{"id":"cmv065j18000fpj2onl2sv7az","slug":"tugas-module","judul":"Tugas (Module)","deskripsi":"CRUD tugas anak + validasi ortu + foto bukti","parentId":"cmv06akyz001hpj2o93e4r5gu","urut":2,"kategori":"backend","tag":"[\"tugas\",\"crud\"]","status":"aktif","versi":null,"konsep":"1. Executive Summary & Core Concept\nTujuan Modul: Sistem manajemen tugas harian + kuis edukatif berbasis AI + Otomatisasi Terjadwal (Scheduled AI Tasks). Ortu atur jadwal rutin, AI generate tugas/kuis otomatis tiap hari berdasarkan profil anak + skala kesulitan. Tanpa intervensi manual tiap hari.\n\n**Visual Mockup (Android Material 3):**\n- [Scheduler Panel](/sketches/006-android-scheduler/index.html) — bottom sheet form, M3 switch, day picker, slider kesulitan\n- [Task List](/sketches/007-android-tasklist/index.html) — card list, soal preview, approve/reject inline, FAB\n\n2. Spesifikasi Teknis & Integrasi Sistem\n- App Ortu: Android Native (Kotlin + Jetpack Compose). Repo: ZX-Parenting-ortu (PUBLIC).\n- App Anak: Android Native (Kotlin + Compose). Repo: zx-agent.\n- Backend: Next.js 16 + Prisma 6 + PostgreSQL. Deploy Coolify (app_id=9, zxparenting.zomet.my.id).\n- Web = ADMIN ONLY. Ortu + anak = APK ONLY. Web sediakan API untuk APK konsumsi.\n- AI Engine: Gemini 2.5 Flash (sudah dipakai F11 AI Insights).\n- Cron: Coolify scheduled task atau Next.js API route + node-cron di container.\n- Timezone: Wajib per-user (Asia/Jakarta default, configurable untuk global market).\n- Multi-bahasa: Soal support bahasa per-ortu setting. Prompt Gemini pakai bahasa dipilih. Default: Indonesia + English.\n- Data privacy: Profil anak minimal dikirim ke Gemini (hanya umur + kelas). Gender/agama tidak dikirim kalau tidak relevan untuk soal.\n- Offline mode: Kuis yang sudah generated bisa dikerjakan offline di APK anak. Soal + opsi sudah ada di DB lokal saat tugas di-push. Jawaban disimpan lokal, sync ke server saat online. Tidak bisa mulai kuis baru yang belum di-download.\n\n3. Schema DB — Table Baru\n\n3a. JadwalTugasAI\nmodel JadwalTugasAI {\n  id          String   @id @default(cuid())\n  anakIds     String   // JSON array [\"anak1\",\"anak2\"] — 1 jadwal bisa untuk multiple anak\n  ortuId      String\n  tema        String\n  kesulitan   Int      @default(3) // skala 1-5\n  jamKirim    String   // format HH:mm\n  hariAktif   String   @default('[1,2,3,4,5]') // ISO weekday: Senin-Jumat\n  rewardToken Int      @default(10)\n  autoApprove Boolean  @default(false)\n  aktif       Boolean  @default(false)\n  maxRetry    Int      @default(3) // max retry per hari per anak, 0 = unlimited\n  notifPref   String   @default('[\"ai_generate\",\"anak_selesai\",\"anak_buka\"]') // ortu pilih notifikasi\n  lastRun     DateTime?\n  createdAt   DateTime @default(now())\n  updatedAt   DateTime @updatedAt\n\n  @@index([ortuId])\n}\nCatatan: 1 jadwal untuk multiple anak (anakIds = JSON array). Cron generate soal terpisah per anak (profil beda = soal beda), tapi 1 entry jadwal.\n\n3b. Tugas\nmodel Tugas {\n  id          String   @id @default(cuid())\n  anakId      String\n  anak        Anak     @relation(fields: [anakId], references: [id], onDelete: Cascade)\n  ortuId      String\n  judul       String\n  deskripsi   String   @default(\"\")\n  type        String   @default(\"manual\") // manual | ai_quiz | ai_scheduled\n  soal        String   @default(\"[]\") // JSON array soal untuk type ai_*\n  status      String   @default(\"pending\") // pending | doing | done | approved | expired\n  rewardToken Int      @default(0)\n  tokenDiklaim Int     @default(0) // token yang sudah diklaim (berdasarkan skor terbaik)\n  jadwalId    String?\n  waktuMulai  DateTime? // saat anak buka tugas\n  waktuExpire DateTime? // waktuMulai + 24jam, auto-expire kalau tidak selesai\n  createdAt   DateTime @default(now())\n  selesaiAt   DateTime?\n  approvedAt  DateTime?\n\n  @@index([anakId])\n  @@index([ortuId])\n  @@index([status])\n}\n\n3c. KuisSession\nmodel KuisSession {\n  id          String   @id @default(cuid())\n  tugasId     String\n  tugas       Tugas    @relation(fields: [tugasId], references: [id], onDelete: Cascade)\n  anakId      String\n  urutanSoal  String   // JSON [3,1,5,2,4] shuffle index soal\n  urutanOpsi  String   // JSON [[2,0,3,1],...] shuffle opsi per soal\n  jawaban     String   @default(\"[]\") // JSON [{soalIdx, opsiIdx}] locked saat dipilih\n  skor        Int?     // 0-100\n  benarCount  Int?\n  totalSoal   Int?\n  durasiDetik Int?     // total waktu pengerjaan\n  status      String   @default(\"aktif\") // aktif | selesai | reviewed\n  mulaiAt     DateTime @default(now())\n  selesaiAt   DateTime?\n\n  @@index([tugasId])\n  @@index([anakId])\n}\n\n4. Prompt Template (Gemini 2.5 Flash)\nSystem: You are an educational quiz generator for children. Generate multiple-choice questions that are child-safe, age-appropriate, and contain NO violence, adult themes, or inappropriate content. Explanations must be simple enough for the target age to understand.\nUser: Generate [jumlah] multiple choice questions about [tema] for a [umur] year old student in [kelas], in [bahasa]. Difficulty level [1-5].\nDifficulty guide:\n  Level 1: Simple recall, single-step, obvious answers.\n  Level 2: Basic application, single operation.\n  Level 3: Multi-step or word problems with context.\n  Level 4: Requires reasoning or combining concepts.\n  Level 5: Complex multi-step, advanced reasoning, near grade-level ceiling.\nReturn JSON array only, no markdown:\n[\n  {\n    \"question\": \"Sari punya 12 permen. Dia memberi 4 permen kepada Andi. Berapa permen Sari sekarang?\",\n    \"options\": [\"6\", \"8\", \"7\", \"9\"],\n    \"answer_index\": 1,\n    \"explanation\": \"12 - 4 = 8. Sari memberi 4 permen, jadi sisanya 8.\"\n  }\n]\nSafety: All content must be child-safe. No violence, adult themes, scary content, or inappropriate language.\nExplanation: Must be simple enough for [umur] year old to understand. Use short sentences and concrete examples.\n\n5. Fitur Utama & Actionable Checklist\nA. Manajemen Tugas Manual (APK Ortu)\n- [x] Form buat tugas: judul, deskripsi, target anak, jumlah token\n- [x] Daftar tugas aktif per anak\n- [x] Approval system: ortu approve tugas selesai -> token masuk\n\nB. AI Quiz Generator (APK Ortu)\n- [x] Form input tema/topik instan\n- [x] Filter profil anak (umur dari tanggalLahir, kelas)\n- [x] Slider kesulitan 1-5 (Pemula -> Expert)\n- [ ] Preview soal sebelum kirim ke anak\n- [ ] API: POST /api/tugas/ai-generate -> Gemini generate soal -> return preview\n- [ ] Soal validation: cek jawaban benar ada, format parse-able, tidak ambig. Retry 1x kalau gagal, skip kalau gagal lagi.\n- [ ] Content moderation: validasi output tidak mengandung konten tidak pantas. Keyword filter + Gemini safety settings.\n- [ ] Soal dedup: hash soal, skip duplikat dalam 30 hari.\n\nC. Scheduled AI Tasks [BARU]\n- [ ] UI Scheduler di APK ortu: pilih anak (multiple), tema, kesulitan, jam, hari aktif\n- [ ] API CRUD: /api/jadwal-tugas-ai (GET, POST, PATCH, DELETE)\n- [ ] Cron worker: tiap menit cek jadwal aktif yang jamKirim = now -> generate via Gemini -> auto-publish ke daftar tugas anak\n- [ ] 1 jadwal multiple anak: cron generate soal terpisah per anak (profil beda = soal beda)\n- [ ] Auto-publish: tugas masuk ke antrian anak tanpa ortu klik manual\n- [ ] Token: tugas AI auto-publish TIDAK auto-claim. Anak kerjakan -> ortu approve -> token masuk. Kecuali ortu set auto-approve per jadwal.\n- [ ] Ortu notifikasi preference: pilih notif saat AI generate / anak selesai / anak buka tugas.\n\nD. Sisi Klien Anak (APK Anak)\n- [ ] Notifikasi tugas baru (manual, instan, terjadwal)\n- [ ] UI pengerjaan soal + submit jawaban\n- [ ] Animasi token/ZX Coin saat approval\n- [ ] Offline mode: kuis yang sudah di-download bisa dikerjakan offline\n\n6. Quiz Flow (Kuis Session)\n\n6a. Mulai Kuis\n- Anak tap tugas kuis -> buat KuisSession (status=aktif)\n- Generate urutanSoal: shuffle index soal (Fisher-Yates)\n- Generate urutanOpsi: shuffle opsi per soal\n- Simpan ke session. Anak lihat urutan acak.\n- Timer mulai: record waktuMulai di Tugas.\n\n6b. Lock Per-Soal\n- Anak tap opsi -> jawaban langsung LOCK, tidak bisa ubah.\n- UI: opsi terpilih highlight, opsi lain disabled.\n- Simpan jawaban ke session.jawaban.\n- Progress bar: \"3/5 terjawab\"\n\n6c. Submit\n- Submit button disabled sampai semua soal dijawab.\n- Submit = final. Tidak ada undo.\n- Hitung skor: (benar / total) x 100.\n- Status session -> selesai. Tugas status -> done.\n- Simpan skor + benarCount + totalSoal + durasiDetik.\n\n6d. Review Mode\n- Setelah submit: tampilkan tiap soal dengan:\n  - Jawaban anak (benar = hijau, salah = merah)\n  - Jawaban benar (highlight)\n  - Pembahasan/explanation text dari Gemini (simple, age-appropriate)\n- Anak tidak bisa mengulang jawab. Review = read-only.\n\n6e. Retry & Token Balance\n- Anak bisa ulang kuis: buat KuisSession baru (urutan + opsi diacak ulang).\n- Token berdasarkan skor: rewardToken x (skor / 100). Misal: 10 token x 80% = 8 token.\n- Max retry per hari: default 3x (configurable per jadwal, 0 = unlimited).\n- Token diklaim berdasarkan skor SESI TERBAIK, bukan sesi terakhir. Misal: coba 1 = 60%, coba 2 = 80%, coba 3 = 70% -> token = rewardToken x 80%.\n- Cap: token tidak bisa melebihi rewardToken. Skor 100% = full rewardToken.\n- Token diklaim sekali per tugas. Setelah diklaim, retry tidak tambah token.\n\n6f. Token & Approval\n- Setelah kuis selesai + skor dihitung -> token = rewardToken x (skorTerbaik/100).\n- Kalau auto-approve aktif: token langsung masuk dompet anak.\n- Kalau tidak: ortu approve dulu -> token masuk.\n- Ortu lihat detail: skor, jawaban salah, pembahasan, jumlah retry.\n\n6g. Time Limit (Opsional)\n- Timer per kuis: default tidak ada timer (santai mode).\n- Ortu bisa enable timer per jadwal: misal 2 menit per soal, total 10 menit untuk 5 soal.\n- Waktu habis -> auto-submit jawaban yang sudah terjawab, soal kosong = salah.\n- Timer hanya berjalan saat app aktif (tidak countdown saat background).\n\n7. Token & Reward Integration\n- Tugas selesai + ortu approve -> token masuk dompet anak.\n- Kuis: token = rewardToken x (skorTerbaik/100). Skor 0 = 0 token.\n- Token cap: maksimal rewardToken per tugas. Retry tidak tambah token.\n- Saldo token terhubung Marketplace (tukar hadiah).\n- Scheduled AI: reward token tetap dari setting jadwal, klaim setelah approval.\n\n8. Adaptive Difficulty (Opsional, Post-MVP)\n- Sistem auto-suggest level: anak 5/5 benar -> suggest naik level.\n- Anak 2/5 -> suggest turun level.\n- Ortu tetap kontrol, sistem hanya suggest.\n\n9. Progress Tracking (Opsional, Post-MVP)\n- Dashboard ortu: rata-rata skor per tema, per minggu.\n- Sari: Matematika 80%, Sains 60%.\n- Grafik sederhana: skor per sesi, trend naik/turun.\n\n10. Streak & Gamification (Opsional, Post-MVP)\n- Streak: 7 hari berturut ngerjain tugas -> bonus token.\n- Badge: Quiz Master (50 kuis selesai), Perfect Score (5/5 10x).\n- Leaderboard optional (opt-in, privacy-aware).\n\n11. Tier Gating\n- FREE_TRIAL: 1 jadwal AI aktif, kesulitan max 3.\n- ZX PRO: 3 jadwal AI aktif, kesulitan 1-5.\n- ZX ELITE: unlimited jadwal AI, kesulitan 1-5, adaptive difficulty, progress tracking, timer mode.\n\n12. Pitfall\n- Gemini rate limit: 15 RPM free tier. Cron harus queue + retry, bukan fire-and-forget.\n- Cost: tiap generate ~1k token output. 100 anak x 1 jadwal/hari = ~100k token/hari. Monitor.\n- Fallback: kalau Gemini down, skip generate hari itu, jangan crash cron.\n- Timezone: jika dijual global, jamKirim harus pakai timezone per-ortu, bukan server time.\n- Auto-publish tanpa approval: pastikan ortu tahu tugas AI masuk otomatis. Notifikasi ke ortu saat AI generate.\n- DB migration: table baru, tidak break existing. Prisma db push auto-sync tiap deploy (Coolify start cmd).\n- Soal validation: Gemini bisa halusinasi jawaban salah atau format rusak. Validasi output sebelum insert.\n- Content moderation: Gemini bisa generate soal tidak pantas. Safety settings + keyword filter + system prompt instruction. Kritikal untuk app anak.\n- Soal dedup: tema sama + level sama hari berurutan -> soal mirip. Hash soal, skip duplikat 30 hari.\n- Data privacy: profil anak (umur, kelas) dikirim ke Gemini. Gender/agama tidak dikirim kalau tidak relevan.\n- Child timeout: tugas kuis tidak dijawab dalam 24 jam -> status expired. Ortu lihat tugas expired di dashboard.\n- FCM: perlu Firebase project + FCM token per device. Setup follow-up.\n- Token exploit: retry unlimited = spam sampai 100%. Solusi: max retry per hari + token dari skor terbaik + cap rewardToken.\n- Explanation readability: pembahasan harus sesuai umur anak. Prompt specify: simple enough for [umur] year old.\n- Offline sync conflict: jawaban offline sync saat online. Kalau tugas sudah expired saat sync, tolak jawaban.\n\n13. Alur Cron\nSetiap menit:\n  1. SELECT * FROM JadwalTugasAI WHERE aktif=true AND hariAktif contains today_weekday\n  2. Filter jamKirim = current HH:mm (timezone ortu)\n  3. Filter lastRun < today (sudah jalan hari ini? skip)\n  4. Untuk tiap jadwal:\n     a. Parse anakIds -> loop per anak\n     b. Fetch profil anak (tanggalLahir -> umur, kelas)\n     c. Call Gemini: generate soal berdasarkan tema + kesulitan + profil + bahasa\n     d. Validate output: parse JSON, cek answer_index valid, cek explanation ada\n     e. Content moderation: keyword filter + safety check\n     f. Dedup check: hash soal, skip kalau duplikat 30 hari\n     g. INSERT ke tabel Tugas (type: ai_scheduled, soal: JSON, rewardToken dari jadwal)\n     h. Update lastRun = now\n     i. Notifikasi push ke APK anak (FCM) — cek notifPref ortu\n     j. Notifikasi push ke APK ortu (info: AI generate tugas baru) — cek notifPref\n  5. Error handling: log + skip, jangan block jadwal lain","createdAt":"2026-10-08T23:29:58.172Z","updatedAt":"2026-10-10T03:56:31.413Z","bagian":[{"id":"cmv065j5j000hpj2othww8xqd","blueprintId":"cmv065j18000fpj2onl2sv7az","judul":"Ringkasan","konten":"Tugas = pekerjaan rumah/aktivitas yang ortu assign ke anak.\n\n**Flow:**\n1. Ortu buat tugas (judul, deskripsi, tokenReward, deadline)\n2. Anak lihat tugas PENDING\n3. Anak kerjakan → kirim foto bukti → status DIKUMPULKAN\n4. Ortu validasi: SELESAI (token masuk) atau DITOLAK\n\n**Status:** PENDING → DIKUMPULKAN → SELESAI/DITOLAK\n\n**Model:** `Tugas` — anakId, judul, deskripsi, tokenReward, fotoBuktiUrl, status, validasiOrtu, deadline\n\n**API:**\n- `GET /api/tugas` — list semua tugas anak ortu\n- `POST /api/tugas` — buat tugas\n- `PATCH /api/tugas/[id]` — validasi (selesai/tolak)\n\n**App ortu:** `LayarTugas.kt` — list tugas per anak, form buat, validasi foto\n**App anak:** lihat tugas, kirim foto bukti","media":"[]","urut":0,"createdAt":"2026-10-08T23:29:58.328Z","updatedAt":"2026-10-08T23:29:58.328Z"},{"id":"cmv065jb7000jpj2o9ysf56nu","blueprintId":"cmv065j18000fpj2onl2sv7az","judul":"Pitfall","konten":"⚠ `deadline` field ada di model+API tapi belum dipakai di UI app ortu\n⚠ Foto bukti: upload mechanism belum fully implemented di app anak","media":"[]","urut":1,"createdAt":"2026-10-08T23:29:58.531Z","updatedAt":"2026-10-08T23:29:58.531Z"}],"children":[],"parent":{"slug":"app-ortu","judul":"App Ortu (Android)"}}