Commit Graph

156 Commits

Author SHA1 Message Date
a2nr 3c2546c818 fix(content-editor): diagnostik upload asset + kelengkapan bundle
- upload_asset: deteksi format dari magic bytes saat PIL menolak, log exception asli

- contentEditor.ts: import bundle stop di failure pertama (informasi utuh tetap
  tersedia); guard frontend
- verify_import_export.py: probe PNG 1x1 valid (PNG palsu ditolak PIL)
- test_content_editor_routes.py: coverage pesan error + AVIF
- bundle: elemes_content_fixed.zip (electron_flow_sparkfun.png 404 nginx dibuang)
2026-10-03 21:47:05 +07:00
a2nr bb8ca50650 fix(upload): review — CSP SVG, boundary 5 MiB, regression guard 502/413
Hasil review pasca-fix upload asset:

Keamanan:
- Mitigasi stored-XSS via SVG (route /assets/* publik, same-origin):
  CSP 'default-src none; sandbox' saat serve SVG — di backend
  (send_assets) DAN di proxy SvelteKit (proxy hanya meneruskan
  content-type/set-cookie, jadi CSP backend tak sampai ke browser).
- Deteksi 413 proxy kini ber-test status code + pesan.

Testing:
- Regression guard: error jaringan biasa tetap 502, bukan 413.
- 413 via status code saja (tanpa regex pesan) tetap 413.
- Boundary 5 MiB: file TEPAT 5 MiB lolos; 5 MiB+1 ditolak (cabang
  len(raw) — SVG dipakai karena dikecualikan validasi PIL).
- CSP: SVG dapat header, raster tidak.
- E2E script (verify_asset_upload.py) memakai file asli
  mr-edu/assets/ilustrasi_beban.jpeg + test 413 terintegrasi.

Reuseability:
- Komentar silang-referensi _MAX_ASSET_BYTES (sumber) dengan pesan 413
  proxy dan BODY_SIZE_LIMIT compose.
2026-10-03 08:06:29 +07:00
a2nr 8fd39d6e1a fix(frontend): upload asset lolos proxy — BODY_SIZE_LIMIT 8M + proxy 413 jelas
Body upload (JSON base64, ~7 MiB untuk file 5 MiB) ditolak default
adapter-node 512K sebelum sampai backend Flask, lalu error itu
terlapor sebagai 502 'Backend tidak tersedia'.

- podman-compose: BODY_SIZE_LIMIT=8M untuk elemes-frontend
- hooks.server: SvelteKitError body-size (413) dipisah dari kegagalan
  koneksi — balas 413 'Ukuran file terlalu besar' bukan 502
- test: proxy balas 413 untuk body melebihi limit
2026-10-03 05:16:41 +07:00
a2nr ed7e7d1b26 feat: tombol export/import bundle di content editor 2026-10-01 12:11:58 +07:00
a2nr 961d2a0ac2 feat: extractBundle — unzip client-side via fflate dengan validasi entry 2026-10-01 12:09:12 +07:00
a2nr 796fd47800 feat: exportBundle + importBundle per-file via endpoint existing 2026-10-01 12:08:07 +07:00
a2nr f1f0b57189 chore: tambah fflate untuk unzip client-side 2026-10-01 12:06:24 +07:00
a2nr ba1e7240f3 feat(offline-db): implement offline progress persistence and sync manager for lessons
- Add getProgress and getAllProgress to local IndexedDB SyncManager
- Support opportunistic background sync for lesson progress and quizzes
- Connect LessonManager to IndexedDB progress cache for offline lesson completion status
- Disable SSR for /lesson and /bab routes to ensure consistent client-side local storage
2026-09-16 22:19:53 +07:00
a2nr 53b704c7de fix(teacher): align student and teacher counters on progress tree view
- Count only distinct students for sub-chapter folder completion badge
- Separate teacher attempts from student counter on lesson header with a dedicated '+Y guru' badge
- Differentiate teacher rows with teacher icon and dashed styling
2026-09-16 20:15:20 +07:00
a2nr f95365a6e3 feat(teacher): refactor progress into tree view and separate classroom management
- Redesign /teacher/progress into a folder-like tree view (sub-bab -> lesson -> completed students)
- Create /teacher/classroom for student management (import/export CSV, add, bulk delete)
- Enrich /progress-report.json API response with sub_bab, sub_bab_title, and sub_bab_list
- Add Classroom navigation item to teacher navbar menu
2026-09-15 04:51:35 +07:00
a2nr c765399715 feat(playground): implement synchronous interactive input for client-side C and Python
- Unbuffer C stdout via setbuf(stdout, NULL) so prompts flush before blocking on input
- Break Emscripten FS.read loop with justFinishedLine and upgrade float/%f to double/%lf for PicoC compatibility
- Provide reactive waitingForInput state with auto-focus, dynamic placeholder, and highlighted send button in ConsolePanel
- Enable full multi-step interactive stdin flow in browser execution mode via SharedArrayBuffer and Atomics
2026-09-14 04:13:08 +07:00
a2nr 0decd6ce86 fix(playground): resolve mobile layout overlap and missing console input
- Lock viewport and relative navbar in +layout.svelte on both mobile and desktop
- Remove static min-height and overflow-y overrides on mobile in +page.svelte
- Auto-collapse file tree and compact code toolbar on mobile
- Add safe-area padding and 200px mobile height in ConsolePanel.svelte
- Bump APP_VERSION to 32
2026-09-13 15:53:02 +07:00
a2nr c1cb60be1e feat(code-runner): implement client-side C and Python execution with offline mirror and COEP iframe embedding
- Add client-side code runner via Web Workers (PicoC for C, MicroPython WASM for Python)
- Support interactive stdin with SharedArrayBuffer and Atomics
- Implement offline database mirror using Dexie.js with opportunistic auto-sync
- Add execution mode toggle in Playground (Browser vs Server)
- Configure COOP, COEP, and CORP security headers in SvelteKit server hook
- Fix Velxio and Flowchart Nginx headers and add credentialless attribute to iframes
- Mount Nginx configs in podman-compose.yml for velxio and flowchart services
- Add unit and E2E Playwright integration tests (100% passed)
2026-09-13 10:14:51 +07:00
a2nr d0e4645c36 fix(playground): resolve desktop layout overlap and show velxio toolbar 2026-09-11 12:55:35 +07:00
a2nr 1f4217be76 fix(quiz): enforce 75% passing threshold, sync server progress, and persist attempt review
- Only show celebration overlay and top bar completed checkmark if quiz score meets 75% threshold (>= 0.75) and exercises are done
- Return is_done, state, and composite_percent in POST /lesson-progress for exercise and quiz submissions
- Synchronize frontend LessonManager state with authoritative server is_done response
- Add has_quiz_attempt check in routes/lessons.py and token_service to recognize existing attempts even when composite state is in_progress
- Update QuizTab to display persistent attempt review, score status, and explanation instead of retake prompt on page load/refresh
- Make QuizQuestionView finish handler async, await finishQuiz(), and disable submission buttons to prevent navigation race conditions
- Update local ordered_lessons completion state dynamically upon passing
- Add unit and integration tests covering response payload attributes and has_quiz_attempt API field
- Bump APP_VERSION to 30
2026-09-09 07:33:52 +07:00
a2nr 9bfecfbcab feat(student-management): sync imported progress with composite model and add inline expected format to CSV headers
- Sync imported progress (completed, scored, done, reset) to composite progress model and recompute composite_percent
- Prevent caching of /progress-report.json with Cache-Control: no-store and frontend cache-busting timestamp
- Provide inline expected format guides in exported CSV lesson headers (e.g. [completed], [<earned>/<total>], [done:<1|0>:<earned>/N])
- Support robust slug extraction from formatted headers in CSV roundtrip import with full backward compatibility
- Add example composite lesson (exercise + quiz) in examples/content/dasar/composite_test.md
- Add comprehensive unit and integration tests covering progress synchronization, header formatting, and roundtrip workflows
2026-09-08 19:01:33 +07:00
a2nr 17713303bb fix(review): address code review findings for dev to master merge
- Optimize N+1 queries in get_all_students_progress via bulk fetch
- Backfill legacy progress in migration 0005 and startup sync
- Use arrayBuffer in hooks.server proxy for binary payload safety
- Restrict postMessage targetOrigin from wildcard to window.location.origin
- Harden iframe polling in TeacherVelxioEditor and reduce frequency
- Improve markdown section extractor line-boundary detection
- Clean up child subprocesses on exception in CLI runner
- Add console.error in contentEditor catch blocks and parameterize repro script
- Configure sequential vitest execution to optimize test runs
2026-09-06 18:08:58 +07:00
a2nr 7d590e0f95 fix(content-editor): sync and auto-save velxio arduino draft on publish
- Auto-save dirty draft before publishing in ContentEditorManager so in-memory
  Velxio circuit/code changes are never lost when clicking Publish directly
- Add resolveRestoredContent and baseline tracking to velxio-manager so lesson
  view immediately reflects updated server code instead of stale localStorage
- Invalidate in-memory lesson and markdown caches on draft publish and file operations
- Update teacher content page publish button disabled condition to check saving state
2026-09-06 16:17:35 +07:00
a2nr e4ab058078 feat(velxio): improve teacher arduino toolbar, add expected_wiring template, and harden deployer board detection
- Add EXPECTED_WIRING block to set-arduino template and standalone expected-wiring feature in teacher content editor
- Reposition reload button to the far left inline with status dot, and shorten status text to 'Siap' / 'Menghubungkan...'
- Decouple transfer from save: replace 'Simpan ke Dokumen' with 'Pindahkan Hasil' and centralize document saving to top header save button
- Dynamically detect board FQBN from circuit in DeployTab with arduino:avr:uno fallback
2026-09-06 05:10:13 +07:00
a2nr 812ede0acc test(velxio): add comprehensive unit test suites for velxio-manager, hooks redirect, and backend markdown parsing 2026-09-05 18:06:19 +07:00
a2nr 032e7f29ae fix(teacher): fix CSS height collapse and flex layout in content editor arduino tab 2026-09-05 17:32:21 +07:00
a2nr 21ffb65e05 fix(frontend): handle /editor gracefully and enable desktopLayout for teacher arduino tab 2026-09-05 17:03:15 +07:00
a2nr c3fc8dc8f1 feat(teacher): integrate full Velxio editor with bi-directional circuit and code sync 2026-09-05 16:50:31 +07:00
a2nr da6d7e25d3 fix(quiz): pakai state eksplisit + keyed each agar opsi render konsisten
currentQuizQuestion dijadikan $state (bukan getter) yang di-sync lewat
_syncCurrentQuestion() tiap ganti soal, dan {#each} opsi memakai :key
option.id. Mencegah opsi 'hilang' 1 frame saat navigasi mcq<->flashcard.
Tambah test integritas shuffle (opsi tetap lengkap & id konsisten).
2026-08-30 10:55:14 +07:00
a2nr 41c434f064 feat(progress): composite scoring, roundtrip CSV grammar, and report export
- Integrate diagnostic and evaluation MCQ scoring in quiz sessions
- Extend round-trip CSV grammar with `done:<ex>:<earned>/<total>` and `RESET`
- Fix completed count aggregation in repositories to include 'done' state
- Update progress report CSV export to output composite scores per lesson
- Add comprehensive test coverage for scoring, CSV round-trip, and exports
2026-08-30 04:12:23 +07:00
a2nr 630f004834 fix(content-editor): slide preview mounting, reactivity loop fix & template sidebar
- Mount SlideCarousel secara dinamis di LessonContentView (#slide-mount-point)
- Fix infinite reactivity loop (effect_update_depth_exceeded) dengan menggunakan plain variable bukan $state untuk handle slideComponent
- Tambah auto-rendering (math, code, flowchart, circuit) saat slide berubah/expand di SlideCarousel
- Defensive try-catch unmount/mount dan eksplisit type ReturnType<typeof mount>
- Update sidebar feature template di /teacher/content (Layout, Set Soal bundle, Inline)
2026-08-29 13:28:13 +07:00
a2nr 467b146988 feat: unified lesson progress — composite score & /api/lesson-progress
- composite score: exercise 70% + quiz 30%, done ≥75% (env LESSON_*)
- endpoint /api/lesson-progress menggantikan /track-progress + /quiz-attempts
- state baru 'done'/'in_progress'; deprecated per-lesson min:N% -> global threshold
- progress_status emit composite; teacher report single composite column
- frontend: submitLessonProgress/lessonProgressBeacon/fetchLessonProgress, QuizTab fix, SW url
- migration 0005; test baru: composite, lesson-progress routes, composite flow
2026-08-29 00:22:10 +07:00
a2nr 9087a77096 refactor(markdown): konsolidasi polyfill nodeCompat & hapus dependency dompurify
- CLN-01: Verifikasi dan konsolidasi polyfill worker_threads.markAsUncloneable ke helper nodeCompat.ts
- CLN-01: Dokumentasi mekanisme require-time (node-polyfill.cjs) vs import-time (nodeCompat.ts)
- CLN-02: Hapus dependency dompurify dan @types/dompurify yang tidak terpakai dari package.json
- Verifikasi seluruh test suite (13 test files, 165 tests) tetap 100% lulus
2026-08-28 11:12:39 +07:00
a2nr 2d557df061 fix(markdown): sanitasi DOMPurify, perbaikan parser flashcard & kompatibilitas SSR Node 20
- Mengganti sanitizer manual dengan isomorphic-dompurify dan hook whitelist/blocklist (BF-01 & BF-02)
- Memperbaiki parsing penjelasan flashcard non-MCQ agar mencari blockquote di seluruh body (BF-03)
- Menambahkan node-polyfill.cjs dan konfigurasi NODE_OPTIONS untuk mengatasi incompatibilitas worker_threads.markAsUncloneable pada Node 20 runtime (SSR blank screen fix)
- Menambahkan unit test keamanan embed (BF-04) dan parity tests dengan fixture backend (BF-05)
- Mendokumentasikan deduplikasi quiz tab pada detectActiveTabs (BF-06)
2026-08-28 05:24:44 +07:00
a2nr bb04245d85 feat: migrate markdown preview rendering to client-side SvelteKit 2026-08-26 19:32:28 +07:00
a2nr db4d15ba50 fix(quiz): render LaTeX/KaTeX pada opsi, pembahasan, & preview guru
- QuizTab: ganti use:renderMath (jalan sekali pas mount) dengan $effect
  yang jalan ulang autoRenderMath tiap konten kuis berubah — cover opsi
  MCQ aktif, review live, dan review hasil-load async (setelah refresh).
- QuizPreviewReadonly (content editor guru): tambah $effect autoRenderMath
  agar LaTeX di soal/opsi/penjelasan ke-render di pratinjau kunci jawaban.

autoRenderMath idempoten sehingga aman dijalankan berulang tanpa dobel render.
2026-08-23 13:15:54 +07:00
a2nr f78eaaca11 fix: regresi hapus folder tidak kosong, tambah test contentEditor
- Perbaiki pemanggilan deleteEntry di +page.svelte baris 531: argumen
  force=true tidak lagi diteruskan dengan benar setelah signature
  fungsi berubah (confirmCritical ditambah di tengah)

- Koreksi komentar dengan karakter Mandarin di content_editor.py baris 488

- Tambah test unit contentEditor.test.ts dengan 5 kasus:
  - deleteEntry force delete
  - deleteEntry confirm_critical untuk file kritis
  - deleteEntry default parameter
  - renameEntry standar
  - renameEntry confirm_critical

Fixes: hapus folder dengan isi tidak lagi gagal 409.
All tests: 119 passed.
2026-08-23 10:43:18 +07:00
a2nr 1659063784 fix(playground): view tab Arduino/Flowchart/Circuit mengikuti ruang kosong & tambah link Playground di menu burger
- playground +page: jadikan main.container flex-column + .playground-page flex:1
  agar memenuhi ruang sisa setelah navbar (sebelumnya height:100% resolve ke
  tinggi intrinsik karena parent display:block)
- pg-content: tambah flex-direction:column supaya flex:1 grow tinggi bukan lebar
- override :global .flowchart-container & .circuit-wrapper jadi height:100%;flex:1;
  min-height:0 (hilangkan min-height:400px / height:550px fix) agar melar
- Navbar: tambah link Playground di dropdown burger tepat di bawah Tema Gelap
- terverifikasi Playwright desktop(1280x800) & mobile(390x780): semua tab 98-100%
2026-08-23 09:27:11 +07:00
anggoro ffceb519be feat(content-editor): konfirmasi ganda file navigasi kritis & hapus narrow sidebar
- Tambah PROTECTED_BASENAMES (home.md, sub-home.md) dengan konfirmasi
  kedua saat rename/delete agar tidak merusak navigasi siswa (tidak
  bisa di-undo). Diteruskan ke renameEntry/deleteEntry di contentEditor.ts.
- Hapus fitur featureSidebarNarrow + CSS terkait (tidak dipakai).
- Footer & backend: penyesuaian rute/test content editor.
2026-08-23 08:18:02 +07:00
anggoro a1ccef2d71 feat(content-editor): tambah template Exp Wiring & buat iframe Velxio mengisi area
- Tambah FEATURE_TEMPLATES 'expected-wiring' (grup Penilaian) untuk
  menyisipkan blok ---EXPECTED_WIRING--- yang dipakai velxio-evaluator
  grading wiring (sudah diekstrak backend lesson_service.py).
- Perbaiki iframe Velxio tab Arduino tidak dinamis: tambah height:100%
  pada .velxio-tab-content agar mengisi tinggi area kosong di desktop & mobile.
2026-08-23 08:17:42 +07:00
a2nr c90a8787f7 feat(teacher-nav): tambah Edit Content & Progress di navbar, pindah route /progress -> /teacher/progress, perbaiki toolbar editor
- Navbar: item 'Edit Content' (/teacher/content) & 'Progress' (/teacher/progress)
  muncul hanya saat akun guru login, ditaruh di atas divider login/logout
- Pindahkan route /progress -> /teacher/progress (git mv) agar konsisten dg /teacher/content
- Hapus link Progress di Footer (sudah ada di navbar)
- Editor content: tombol Fitur (toggle sidebar) dipindah ke header samping Simpan/Publish,
  Simpan/Publish diubah jadi icon, hapus tombol panah floating di tepi & di header sidebar,
  tombol dock/undock & minimize dipindah ke pojok kanan (mode desktop)
- Bump APP_VERSION 28 -> 29
2026-08-22 21:30:06 +07:00
a2nr f7fa1c70c2 fix(content-editor): bersihkan tab saat pindah asset & perbaiki upload gagal 500
- Reset previewActiveTabs/previewSlides saat pindah ke asset & saat hapus
  file aktif, agar tab dinamis (C/Python/Circuit/Arduino/Flowchart) tidak
  menetap setelah berpindah konteks.
- Ganti Pillow Image.verify() dengan Image.load() di validasi upload asset.
  verify() false-positive CRC pada chunk IDAT PNG valid -> backend 500.
  load() cukup membuktikan file adalah gambar yang bisa di-decode.
2026-08-22 09:39:46 +07:00
a2nr daa813f511 feat(content-editor): remove Preview tab, load Circuit/Velxio/Flowchart components
- Remove Preview tab (preview already shown in left content panel)
- Circuit tab loads actual CircuitEditor component with extracted initial_circuit
- Velxio tab loads velxio iframe with embed mode
- Flowchart tab loads flowchart iframe
- C/Python tabs remain as read-only CodeEditor with extracted code
2026-08-21 23:46:15 +07:00
a2nr fbc8a3fb03 feat(content-editor): dynamic tabs for C/Python/circuit preview
Add dynamic chrome tabs based on previewActiveTabs from markdown parsing.
When teacher writes ---INITIAL_CODE--- or ---INITIAL_PYTHON--- blocks,
the corresponding C/Python tab appears with read-only code preview.

- Widen activeTab type to string for dynamic tab IDs
- Extract code blocks client-side from raw markdown body
- Render chrome tabs dynamically from allTabs derived state
- Auto-switch away from invalid tabs when preview updates
2026-08-21 23:27:37 +07:00
a2nr 0d299055b0 fix(content-editor): reuse WorkspaceHeader icons & CSS, make editor fill parent
- Replace inline SVGs with Unicode icons from WorkspaceHeader (⊞ ▽ △ ◀)
- Use shared .panel-btn / .panel-actions / .btn-float-toggle CSS classes
- Match .resize-handle and .panel-header styling from lesson page
- Make CodeEditor dynamically fill workspace at any height (flex + height:100%)
- Override .cm-editor max-height in content-editor to remove fixed 60vh cap
2026-08-21 22:26:36 +07:00
a2nr b75eb06346 feat(content-editor): dockable/floating workspace, mobile view modes, asset preview
- Floating/dockable editor panel with drag & resize handles
- Mobile view modes: hidden → 50% → 100% toggle cycle
- Mobile tree-first: show file tree when no file selected, back button to return
- Asset file preview with image display and copy-path button
- Touch support for drag and resize on mobile
- Fix missing closing div in asset preview section
2026-08-21 19:03:53 +07:00
a2nr 084eb71a71 feat(content-editor): tambah editor konten guru dengan draft & publish
Backend:
- Model ContentDraft + migration 0004_content_drafts
- CRUD draft: save, reload, preview, publish, discard
- Tree ops: create file/folder, rename, delete dengan draft cleanup
- Refactor _render_markdown_string() agar bisa render dari buffer
- Refactor teacher_auth ke modul shared (teacher_auth.py)
- Blueprint routes/content_editor.py (17 endpoint)
- Permission error handling di semua write operations
- Tambah Pillow ke requirements.txt
- Mount content/ & assets/ dari :ro ke :rw

Frontend:
- Page /teacher/content dengan panel tree + editor + preview
- ContentFileTree, LessonContentView, QuizPreviewReadonly components
- contentEditor.ts service layer & contentEditor.svelte.ts state manager
- CodeEditor support @codemirror/lang-markdown + sync code prop
- Tambah @codemirror/lang-markdown dependency
2026-08-21 15:55:21 +07:00
a2nr 16031fa728 fix(docs/api): hilangkan sync berulang di api_reference + UI API Reference di /docs
- routes/docs.py: pakai current_app, bukan create_app() per request — tidak
  lagi memicu DB sync ulang di setiap hit /docs/api-reference (dead code
  route_modules/rule_map dihapus)
- frontend: entry statis API Reference di sidebar /docs, fetch lazy
  /api/docs/api-reference, render method badge + auth + docstring
- test: 4 test yatim di test_teacher_bootstrap diberi marker unit;
  Makefile test-stats kini menampilkan count per marker + orphan (harus 0)
- docs_service: hapus dead code _read_md_cached, sederhanakan cache-check
  _parse_frontmatter (satu getmtime, kondisi jelas)
- lesson_service: gabung pemanggilan bleach.clean dobel jadi satu dengan
  css_sanitizer opsional — hilangkan NoCssSanitizerWarning (7x)
- conftest: fixture autouse _isolate_database lewati isolasi untuk test unit
  supaya make test-unit tetap jalan tanpa DB walau DATABASE_URL diset

Verifikasi: unit 114 passed, full suite 239 passed (0 warnings) dengan
Postgres test, svelte-check 0 error, vitest 114 passed, docs-validate ok.
2026-08-17 16:37:30 +07:00
a2nr 429b23e9b8 fix(docs): frontend load must fetch /api/docs (not /docs) for SSR proxy
- hooks.server.ts only proxies /api/* to Flask (strips /api prefix)
- load() fetching /docs (no /api) caused SSR recursion/500
- Revert to /api/docs so proxy reaches Flask /docs route correctly
- selectDoc client fetch already uses /api/docs (correct)
2026-08-17 13:05:59 +07:00
a2nr d243df898a fix(docs): correct API route prefix — strip /api to match Flask proxy convention
- Flask docs routes changed from /api/docs to /docs (no /api prefix)
  because hooks.server.ts proxies /api/* by stripping /api, so /api/docs
  reached Flask as /docs (404). Other routes (lessons, help) also lack /api.
- Frontend load functions now fetch /docs and /docs/<slug> (not /api/docs)
- Fixes 500 on /docs SSR (proxy ECONNREFUSED / 404 from stripped route)
2026-08-17 12:40:48 +07:00
a2nr 9c2563a2bf feat(docs): docs viewer, API reference generator, markdown rendering
- Add YAML frontmatter (title/order/category) to 13 docs/*.md files
- Add services/docs_service.py: mtime-cache, frontmatter parsing w/ fallback,
  bleach sanitize, path traversal protection
- Add routes/docs.py: GET /api/docs, GET /api/docs/<slug>, GET /api/docs/api-reference
- Register docs_bp in app.py
- Add frontend /docs + /docs/[slug] routes (sidebar nav + markdown render)
- Add frontend/src/types/docs.ts interfaces
- Add link from /help page to /docs viewer
- Add scripts/validate_docs.py: frontmatter + broken link validation
- Add elemes.sh docs-validate command

Endpoints documented in API reference: /quiz-attempts/<lesson>,
/sessions, /bab/<folder>, /compile/sessions, /lesson/<slug>.json + all 30 routes
2026-08-17 11:45:05 +07:00
a2nr 25269fca08 fix(quiz/progress): perbaiki temuan audit — btn:disabled, css mati, indentasi 2026-08-17 09:23:05 +07:00
a2nr 9aef612f01 feat(quiz/progress): pindah navigator ke quiz view, tampilkan guru di /progress, tambah 1 siswa, fix breakdown eval/diag
- Task 1: pindahkan .quiz-navigator & .quiz-controls dari QuizTab ke
  QuizQuestionView; refactor props jadi mgr: LessonManager; CSS ke lesson.css.
  Navigator+kontrol kuis selalu terlihat di mobile walau workspace hidden.
- Task 2: get_all_students_progress tidak filter role guru; field role per row;
  badge 'Guru', bulk-action & empty-state & ringkasan hanya hitung siswa.
- Task 3: endpoint POST /students/add (reuse StudentRoundTripRow +
  find_student_import_conflicts + run_student_import); validate_single_student_input;
  AddStudentDialog + addStudent(); import/export/bulk-delete CSV lama tetap ada.
- Fix (Opsi A): breakdown eval/diag hanya hitung MCQ (flashcard netral) —
  selaraskan dengan skor resmi FE; perbaiki tampilan eval:4/6 yg membingungkan.
  Tambah field type ke QuizAnswerPayload.

Test: backend 239 passed (termasuk test baru teacher-in-report, add-student,
breakdown-excludes-flashcards); svelte-check 0 error; build sukses.
version.ts 27->28.
2026-08-17 08:32:42 +07:00
a2nr 2d5f100808 feat(quiz): perbaikan refresh quiz + enhancement UI kuis
- QUIZ-01: beforeunload hanya kirim beacon (tanpa finalisasi state) sehingga
  Cancel dialog membatalkan reload dan quiz lanjut; beforeNavigate tidak lagi
  memfinalisasi untuk tipe 'leave' (full-page unload/reload); review
  disembunyikan untuk page_unload (display policy anti-eksploitasi)
- QUIZ-02: opsi MCQ tampil 2-kolom grid (single-col untuk <=2 pilihan,
  fallback 1 kolom di mobile sempit)
- QUIZ-03: hapus info "soal X dari Y / X dijawab" + progress bar dari
  workspace dan question view
- QUIZ-04: opsi tinggi workspace mobile hide/30%/50%/70%/full dengan
  stepUp/stepDown untuk swipe & tombol resize
- QUIZ-05: workspace flashcard hanya 2 tombol "Sudah Mengerti"/"Tidak
  Mengerti" (tracking understood), indikator di summary review

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-16 19:17:42 +07:00
a2nr 58cb35a01a feat(quiz): tag kategori diagnostik + breakdown evaluasi/diagnostik di kuis & report guru
- Backend parser (_parse_flashcards): deteksi marker ::diagnostic di baris sendiri -> category='diagnostik' (default evaluasi), strip dari tampilan.
- Persistensi: kolom answers_json (migration 0003) simpan ringkasan per-soal {question_id, selected_option_id, is_correct, category}.
- quiz_attempts: validasi/serialize answers_json + endpoint GET /quiz-attempts/<lesson> untuk review-after-refresh.
- quiz-session: hitung evalCorrect/evalTotal/diagCorrect/diagTotal/diagUnmastered; skor resmi = evaluasi saja.
- Frontend: QuizTab badge per-soal + breakdown 'Belum dikuasai'; progress guru tampil badge Eval/Diag per lesson.
- Content: pindahkan ::diagnostic ke baris sendiri (fix kerusakan fenced code/blockquote dari edit sebelumnya).
- Tests: unit parser kategori + payload answers.
2026-08-15 09:33:23 +07:00