Commit Graph

228 Commits

Author SHA1 Message Date
a2nr 5cc4ca472b fix(quiz): jangan belah soal saat '#' muncul di dalam code fence
_split_flashcard_parts() pakai state-machine per-baris agar komentar '#'
di dalam blok kode (python/shell/bash/yaml) tidak dianggap heading baru.
Sebelumnya re.split('^#{1,3}\s+') membelah soal menjadi dua, sehingga
opsi meleset ke 'soal hantu' dan pilihan di layar tidak sesuai markdown.
2026-08-30 10:55:06 +07:00
a2nr ac3fdb8dff refactor(routes): remove /api/lesson-progress aliases and standardize tests to /lesson-progress 2026-08-30 04:45:37 +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 77028e58ff feat(velxio): modular standalone orchestration & dynamic tailscale config
- Tambah podman-compose.velxio.yml untuk deployment Velxio standalone
- Tambah command elemes.sh (velxio-run, velxio-runbuild, velxio-stop, velxio-status)
- Template lms-tail.json.template dengan auto-generation via elemes.sh berdasarkan VELXIO_MODE
- Dukungan VELXIO_COMPILER_URL dinamis pada compose utama
2026-08-29 13:28:06 +07:00
a2nr 186161ac96 chore(submodule): update velxio to elemes-v3.0.4 2026-08-29 11:13:41 +07:00
a2nr e3eba57d71 fix(migration): reorder constraint expansion before backfill UPDATE
Migration 0005 gagal karena UPDATE state='in_progress' dijalankan
sebelum constraint lama di-drop. Constraint lama (dari 0001) hanya
mengizinkan 'not_started','completed','scored' sehingga 'in_progress'
ditolak PostgreSQL (CheckViolation).

Fix: pindahkan DROP/CREATE constraint ke sebelum blok UPDATE backfill.
2026-08-29 05:12:11 +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 ac10692b61 feat(prereq): dukung ambang minimum (min: N%) pada prasyarat materi
Tambahkan logika ambang minimum skor (opsional) di daftar prasyarat.
Tanpa ambang, status scored berapa pun (termasuk 0/x) memenuhi syarat
(backward compatible). Dengan ambang `min: N%`, earned/total harus
melebihi ambang untuk membuka kunci materi berikutnya.

Perubahan:
- Parser bullet prasyarat mendukung suffix `min: N%` (contoh: min: 75%)
- is_prerequisite_met: completed → selalu terpenuhi; scored → terpenuhi
  kecuali di bawah ambang; not_started → belum terpenuhi
- routes/lessons.py: 3 titik kunci sekarang pakai get_missing_prerequisites
- latex_test.md: tambahkan prereq quiz_test min: 75% (demo nyata)
- examples/content disinkronkan dengan content
- Tambah test_prerequisite_logic.py (28 unit test) + 3 integration test
2026-08-17 21:57:10 +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 a5aedf66d4 refactor(test): unified test runner, markers, elemes.sh cleanup
- Add pytest.ini with unit/integration/e2e markers + --strict-markers
- Add Makefile as centralized test runner entrypoint
- Add @pytest.mark.integration to 7 DB-dependent test files
- Add @pytest.mark.unit to 6 pure-logic test files
- Refactor elemes.sh: test-* commands delegate to 'python -m pytest -m <marker>'
- Move test_elemes_sh.sh -> scripts/ (path-adjusted), add test-shell Makefile target
- Add test-worker target for compiler_worker/tests (separate PYTHONPATH)
- Update runclearbuild smoke gate to use -m unit + sub-home subset
- Add .github/workflows/ci.yml: test-unit + test-integration on PR to dev
- 239 tests collected (110 unit, 125 integration) — no count reduction
2026-08-17 11:29:29 +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
a2nr 79edcd1596 fix: sync lesson registry termasuk sub-bab (sub-home.md) agar kolom materi muncul di CSV guru
lesson_specs() hanya memindai home.md root sehingga lesson di
bab/<folder>/sub-home.md tidak pernah masuk tabel lessons. Akibatnya kolom
materi sub-bab hilang saat guru import/export siswa CSV.

- services/lesson_service.py: tambah find_all_sub_home_folders() (enumerate
  level-1 folder di CONTENT_DIR berisi sub-home.md)
- services/lesson_registry.py: lesson_specs() gabungkan union root + sub-bab,
  dedupe slug (root priority) — satu lesson entitas
- services/tests/test_lesson_registry.py: test unit + DB integration

Verifikasi: 3 passed, 2 skipped (unit); test_sync_includes_sub_bab_lessons
passed (DB); 58 passed total termasuk test_repositories + student_management
tanpa regresi.
2026-08-14 07:07:25 +07:00
a2nr 432a8c2eca fix: show lesson progress on sub-home /bab pages
The /bab/<folder> endpoint returned raw get_sub_home_data() output, so
lessons had no completed/locked fields and green checkmarks never
appeared. api_bab now reads the student token, injects progress via
get_ordered_lessons_with_learning_objectives (scoped to the folder's
sub-home.md), and computes lock status from prerequisites — matching
/lessons. The bab page load also forwards the student token, and tests
cover progress injection and prerequisite locking.
2026-08-14 05:23:50 +07:00
a2nr 67ed58a327 feat: scope sub-home lesson lists & make sub-home cache mtime-aware
Lengkapi rencana sub-home (fitur bab sudah ada sejak 2f689af) dengan
perilaku sidebar/navigasi yang akurat per sub-folder:

- get_lessons()/get_ordered_lessons_with_learning_objectives() menerima
  source_path opsional sehingga daftar materi dapat diambil dari
  sub-home.md alih-alih home.md root
- api_lesson: ordered_lessons + prev/next di-scope ke sub-home.md bila
  folder memilikinya, fallback ke daftar global home.md
- get_sub_home_data kini mtime-cached (bukan lru_cache) sehingga edit
  sub-home.md langsung terlihat tanpa restart; edit juga meng-invalidate
  find_lesson_file agar materi baru terdeteksi

Testing & docs:
- test_sub_home.py (unit): _read_md_cached mtime, find_sub_home_for_lesson,
  get_sub_home_data refresh, ordered_lessons via source_path
- test_sub_home_api.py (integration): GET /bab/<folder> + scoped
  ordered_lessons di /lesson/<slug>.json
- docs/13-content-sub-home.md (baru), README + docs/02-backend-flask.md
  diperbarui dengan catatan fallback
- elemes.sh: perintah `test` + verifikasi tes sub-home di runclearbuild
2026-08-13 20:08:50 +07:00
a2nr 7e17da46c2 fix(frontend): hide CircuitJS toolbar & menu bar in lesson embeds
Append &hideMenu=true to the circuitjs.html iframe src used by
renderCircuitEmbeds so lesson content embeds render clean without
the toolbar and menu bar. The embed path in CircuitEditor.svelte
is untouched, so the editor keeps its full toolbar.

Verified:
- Bundle contains embed URL (with hideMenu=true) and editor URL
  (without) as separate variants
- svelte-check: 0 errors
- /lesson/rangkaian_dasar returns 200 on the running stack
2026-08-12 07:49:34 +07:00
a2nr 24502380aa feat(quiz): strict focus-loss anti-cheat + audit attempt
Kebijakan strict: kehilangan fokus saat kuis aktif langsung mengakhiri kuis
dengan penalti, satu attempt per kuis tercatat audit, pembahasan disembunyikan
saat focus_lost, badge pelanggaran muncul di laporan guru.

Backend:
- tabel quiz_attempts (migration 0002) + endpoint POST /quiz-attempts/submit
  idempotent dengan unique constraint (satu attempt per token+lesson, 409
  untuk duplikat), bounded termination_reason
- models/repositories/storage: QuizAttempt + record/query pelanggaran untuk
  laporan guru (progress)

Frontend:
- quiz-integrity.ts: policy hub (isFocusLossEvent, terminateQuiz,
  shouldShowQuizReview) + attempt_id via crypto.randomUUID()
- lesson.svelte.ts: finalisasi idempotent; sendBeacon untuk exit path yang
  bisa suspend (focus_lost/page_unload), fetch keepalive untuk reason lain
- QuizTab.svelte: summary menyembunyikan pembahasan saat focus_lost
- progress/+page.svelte: badge pelanggaran focus_lost di laporan guru
- APP_VERSION 26 -> 27

Tests & docs:
- 204 backend test pass (unit host + integrasi container, DB elemes_test)
- 107 frontend test pass, svelte-check 0 error
- docs/12-quiz-integrity.md (baru), docs/07-quiz-authoring.md + README FAQ
  anti-cheat diperbarui
2026-08-11 20:55:39 +07:00
a2nr a14a212cc6 test(elemes): lock all three exportall build tags; note runbuild for unbundled services 2026-08-10 12:03:53 +07:00
a2nr c83a47b357 fix(elemes): align exportall/importall image tags with podman-compose (lms-*, not lms-c-*) 2026-08-10 12:03:10 +07:00
a2nr 5508c2dc7a docs: replace hard-coded container names with service-based podman-compose/label resolution 2026-08-10 11:34:54 +07:00
a2nr c453ddde2d test(elemes): per-command log assertions + cover dbupgrade/dbstatus/dbrestore 2026-08-10 11:29:09 +07:00
a2nr 394eb11082 refactor(elemes): delegate run_compose to run_compose_out; fail dbbackup on pg_dump error 2026-08-10 11:29:09 +07:00
a2nr 3935b7e01e test(elemes): add regression test for dynamic PROJECT_NAME resolution 2026-08-10 11:25:44 +07:00
a2nr 6ba508c7bc fix(verify): skip stale check when image tag unknown (avoid bogus podman images call) 2026-08-10 11:25:44 +07:00
a2nr 0b513b3900 docs(exportall): update image name hints to match compose (lms-backend, lms-frontend, lms-velxio) 2026-08-10 11:23:20 +07:00
a2nr 34ab1cde73 fix(verify): dynamic label-based verification for any PROJECT_NAME 2026-08-10 11:23:13 +07:00
a2nr 8976acd154 fix(dbbackup,dbrestore): use compose_exec for postgres service 2026-08-10 11:22:59 +07:00
a2nr 34dc9c9b90 fix(teacher,dbupgrade,dbstatus): use compose_exec for elemes service 2026-08-10 11:22:50 +07:00
a2nr a0c69ece8e fix(db_init): use compose_exec/compose_restart instead of hard-coded container names 2026-08-10 11:22:41 +07:00