Admin Daily Ops Dashboard
Apa yang admin lihat & lakukan sehari-hari di landing dashboard.
① Dashboard Landing — 3 Kategori Utama
flowchart LR
Login(["🔑 Admin login
Google OAuth"]) --> Dash[📋 Daily Ops
Dashboard]
Dash --> S1["⚡ Yang perlu
segera ditangani"]
Dash --> S2["📊 Yang perlu
di-monitor"]
Dash --> S3["📅 Operasi
akhir bulan"]
style S1 fill:#fbbf24,color:#422006
style S2 fill:#38bdf8,color:#0c2c4a
style S3 fill:#a855f7,color:#2e1065
② Detail per Kategori
⚡ Yang perlu segera ditangani
- Approval Queue
Sesi baru menunggu di-approve admin. Sesi tidak jalan sampai disetujui. - Anomaly Queue
Sesi mencurigakan: double-booking tutor, atau item yang lewat SLA tanggap (mis. REQUESTED >24 jam). - FM Claim Review
Klaim Force Majeure (sakit/duka/bencana) dari tutor. Review L1 + opsi banding L2 dalam 14 hari.
📊 Yang perlu di-monitor
- Calendar View
Lihat jadwal sesi per tutor / per siswa / global. Drag-friendly reschedule. - Outstanding Aging
Tagihan siswa yang belum dibayar, dikelompokkan: 0-30 / 31-60 / 61-90 / 90+ hari. - Honor Unpaid Reconciliation
Honor tutor yang belum di-transfer. Tool track per sesi + bulk mark paid setelah transfer manual.
📅 Operasi akhir bulan
- Generate Tagihan + Honorarium
Sekali per bulan: sistem aggregate sesi yang sudah COMPLETED → jadi Tagihan siswa + Honorarium tutor. - Mark paid bulk
Tag Settlement.PAID untuk siswa yang sudah upload bukti bayar, atau honor tutor yang sudah ditransfer. - Period Close Checklist
Lock pembukuan bulan ini. Setelah lock: tidak boleh ada transaksi backdated. Re-open butuh approval senior.
② Saat Admin Approve / Reject Sesi (dari Approval Queue)
flowchart TD
Open["Admin klik sesi di Approval Queue"] --> Decide{Approve
atau
Reject?}
Decide -->|✓ Approve| Snap["🔒 Sistem freeze pricing + cancellation policy
(harga + policy sesi ini di-kunci,
tidak berubah meski admin edit master data nanti)"]
Decide -->|✗ Reject| Audit["Reason di-audit
(REJECTED = final, tidak bisa di-undo)"]
Snap --> Notify["Admin notif tutor + siswa
via WhatsApp manual"]
style Snap fill:#fef3c7,color:#422006
style Notify fill:#dbeafe,color:#1e3a8a
Catatan Penting (Bahasa Awam)
- Approval Queue = antrian sesi baru yang menunggu admin review. Sesi tidak akan dijalankan sampai admin approve.
- SLA flag = badge peringatan kalau sesi sudah di antrian >24 jam tanpa direspons admin. Mengingatkan supaya tidak terlewat.
- Snapshot (freeze) = saat di-approve, sistem mencatat harga + cancellation policy yang berlaku pada saat itu ke sesi tersebut. Walaupun admin nanti ubah harga di master data, sesi ini tetap pakai harga lama.
- Notify WhatsApp manual = di iter 1, admin yang chat tutor + siswa langsung. Iter 2 → auto-notif sistem.
- Honor Unpaid Reconciliation = tool untuk admin yang membantu track honor tutor mana yang sudah / belum di-transfer manual ke rekening tutor.
- Istilah teknis: lihat tab 📖 Glossary di paling akhir.
Sesi State Machine (7 states)
Lifecycle sesi dari created sampai terminal state.
stateDiagram-v2
[*] --> REQUESTED: Admin create sesi
REQUESTED --> APPROVED: Admin approve (snapshot pricing + policy)
REQUESTED --> REJECTED: Admin reject (reason audited)
APPROVED --> IN_PROGRESS: Tutor Start (admin proxy iter 1)
IN_PROGRESS --> COMPLETED: 2-step gate (tutor confirm + admin validate)
APPROVED --> CANCELLED: Cancel flow (siswa/tutor/admin)
APPROVED --> RESCHEDULED: Reschedule flow
RESCHEDULED --> REQUESTED: New linked sesi created
REJECTED --> [*]
CANCELLED --> [*]
COMPLETED --> [*]: Settlement UNPAID auto-created
note right of COMPLETED
Sub-states (COMPLETED variants):
NO_SHOW_STUDENT (charge full),
NO_SHOW_TUTOR (escalate cancellation)
end note
State Properties
| State | Meaning | Trigger by |
|---|---|---|
REQUESTED | Sesi diminta, nunggu admin approve | Auto on create |
APPROVED | Admin approve, slot di-lock, pricing+policy snapshot | Admin only |
REJECTED | Admin tolak (terminal) | Admin only |
IN_PROGRESS | Sesi mulai (timestamp logged) | Admin proxy iter 1 |
COMPLETED | Selesai + materi confirmed; 2-step gate | Tutor confirm + admin validate |
CANCELLED | Dibatalin post-APPROVED w/ punishment calc | Siswa/tutor/admin |
RESCHEDULED | Old sesi closed, new linked sesi created | Siswa/tutor/admin |
Booking Happy Path
End-to-end sesi sukses → settlement → invoice.
flowchart TD
Create["Admin create sesi
(siswa + tutor + datetime + mode + Tempat Les)"] --> Req[REQUESTED]
Req --> AdminAppr["Admin approve
(via Approval Queue)"]
AdminAppr --> Snap["Snapshot:
pricingMatrixId
amountStudent / amountTutor
transportFee
cancellationPolicyConfigId"]
Snap --> Apr[APPROVED]
Apr --> Start["Tutor mark Start
(admin proxy iter 1)"]
Start --> InProg[IN_PROGRESS]
InProg --> Materi["Mark attendance +
materi log + foto report (R2)"]
Materi --> AdmVal["Admin validate completion
(2-step COMPLETED gate)"]
AdmVal --> Done[COMPLETED]
Done --> Settl["Settlement auto-created
(siswa + tutor side
UNPAID, snapshot-based)"]
Settl --> EOM[End of month]
EOM --> Tagihan[Tagihan generated]
EOM --> Honor[Honorarium generated]
style Snap fill:#fef3c7,color:#422006
style Req fill:#fbbf24,color:#422006
style Apr fill:#38bdf8,color:#0c2c4a
style InProg fill:#a855f7,color:#2e1065
style Done fill:#34d399,color:#052e1f
Catatan Penting
- Snapshot at APPROVED: 4 hal di-freeze — pricing matrix ID (STUDENT + TUTOR), amount actual, transport fee tier, cancellation policy config ID. Immune dari later change.
- 2-step COMPLETED gate: tutor confirm (admin proxy iter 1) → admin validate. Mencegah accidental complete tanpa verify materi/foto.
- Settlement auto-created: dual entries (siswa-side untuk Tagihan, tutor-side untuk Honorarium).
- Iter 2+: tutor login langsung mark Start / attendance / materi / foto (tanpa admin proxy).
Cancellation: Tutor Cancel (Option b Locked)
Tutor cancel ≤4h: self-sub clean atau escalate kena denda graduated + strike.
flowchart TD
Cancel["TUTOR cancel ≤ 4 jam
via WhatsApp ke admin"] --> Decide{"Option α:
Tutor cari pengganti
SENDIRI dari komunitas?"}
Decide -->|Yes try self-sub| SelfSub["Admin record self-sub attempt"]
SelfSub --> SubOk{Pengganti
accept?}
SubOk -->|SUCCESS| Clean["✓ Tutor original: CLEAN
(no penalty, no strike)
✓ Pengganti: full Honorarium
✓ Siswa: bayar normal
(sesi jalan dengan pengganti)"]
SubOk -->|FAIL| Escalate["Escalate ke XPrivate
(Option β — transfer liability)"]
Decide -->|No / fail| Escalate
Escalate --> XDispatch{XPrivate
dispatch sub?}
XDispatch -->|Yes| SesiJadi["Sesi jadi w/ pengganti
(via Substitute Matching)"]
XDispatch -->|No| SesiCancel["Sesi cancelled
(siswa charge 0)"]
SesiJadi --> Penalty
SesiCancel --> Penalty
Penalty["Tutor original penalty:
• Zero pay sesi (lost opp)
• Denda graduated 100/50/25%
• +1 strike
Denda = REVENUE XPrivate"]
style Clean fill:#34d399,color:#052e1f
style Penalty fill:#f87171,color:#420c0c
Denda Bracket (Configurable Runtime — Default)
| Bracket | Threshold | Denda % | Strike |
|---|---|---|---|
| 1 | ≤ 1 jam | 100% | +1 |
| 2 | ≤ 2 jam | 50% | +1 |
| 3 | ≤ 4 jam | 25% | +1 |
| 4 | > 4 jam | 0% | 0 |
| FM | Force majeure (any) | 0% | 0 |
Bracket runtime configurable via Cancellation Policy Editor (M2). Snapshot at APPROVED — sesi tetap pakai bracket yang berlaku saat di-approve.
Cancellation: Siswa Cancel (Bracket)
Siswa cancel charge + honor proporsional terhadap lead time.
flowchart TD
Cancel["SISWA cancel
via WhatsApp ke admin"] --> AdminEntry["Admin entry cancel di backoffice
+ reason + initiator: SISWA"]
AdminEntry --> CalcEngine["Cancellation calc engine:
cek lead time vs bracket"]
CalcEngine --> Lt1{Lead time
≥ 4 jam?}
Lt1 -->|Yes| NoCharge["Charge siswa: 0
Honor tutor: 0
(sesi free cancel)"]
Lt1 -->|No| Lt2{≥ 2 jam?}
Lt2 -->|Yes| B3["Bracket: charge 25%
Honor 25%"]
Lt2 -->|No| Lt3{≥ 1 jam?}
Lt3 -->|Yes| B2["Bracket: charge 50%
Honor 50%"]
Lt3 -->|No| B1["Bracket: charge 100%
Honor 100%"]
NoCharge --> Logged["Audited + sesi CANCELLED"]
B3 --> Logged
B2 --> Logged
B1 --> Logged
Logged --> Override{Admin
override?}
Override -->|Yes| Manual["Manual override w/ alasan
(audited)"]
Override -->|No| Final[Final settlement entries]
style NoCharge fill:#34d399,color:#052e1f
style B1 fill:#f87171,color:#420c0c
Bracket Default
| Lead time | Charge siswa | Honor tutor (no transport) |
|---|---|---|
| < 1 jam | 100% | 100% |
| < 2 jam | 50% | 50% |
| < 4 jam | 25% | 25% |
| ≥ 4 jam | 0 | 0 |
Transport fee tidak di-bayarkan ke tutor untuk siswa cancel (tutor tidak jadi berangkat). Admin override tetap available dengan audit.
Force Majeure Workflow
L1 review + L2 banding 14d window. Category enum (SAKIT/DUKA/DARURAT/BENCANA/LAINNYA).
flowchart TD
Claim["Tutor claim FM via WhatsApp
(category + evidence)"] --> Entry["Admin entry claim di backoffice
+ evidence upload (R2)"]
Entry --> Status1["Status: PENDING_L1"]
Status1 --> L1{L1 Operator
review}
L1 -->|APPROVE| NoPenalty["Status: APPROVED_L1
✓ No penalty for tutor
✓ Audit log"]
L1 -->|REJECT| Status2["Status: REJECTED_L1
+ normal penalty escalate"]
Status2 --> Banding{Tutor banding
dalam 14 hari?}
Banding -->|No| Final["Penalty stays final
(REJECTED_L1 terminal)"]
Banding -->|Yes| Status3["Status: PENDING_L2"]
Status3 --> L2{L2 Admin
senior review}
L2 -->|UPHOLD| Stayed["Status: UPHELD
Penalty stays final
+ audit"]
L2 -->|OVERTURN| Refund["Status: OVERTURNED
✓ Refund penalty
✓ Audit
✓ Optional comp_notes
(finance off-system condolence)"]
style NoPenalty fill:#34d399,color:#052e1f
style Refund fill:#34d399,color:#052e1f
style Final fill:#f87171,color:#420c0c
style Stayed fill:#f87171,color:#420c0c
FM Categories & Properties
- Categories:
SAKIT·DUKA·DARURAT·BENCANA·LAINNYA - Evidence requirement: surat dokter / surat keterangan instansi resmi (untuk sakit + bencana)
- L1 approver: operator (admin) — case-by-case judgment
- L2 approver: senior admin — banding only
- Appeal window: 14 hari rolling dari REJECTED_L1 timestamp
- comp_notes field: optional — finance bayar belasungkawa off-system bila ada (tidak otomatis)
- Termination blocker: bila ada FM PENDING_L1 atau PENDING_L2 → termination tutor di-block sampai resolved
Substitute Matching (Admin-side)
Admin dispatch via WhatsApp setelah tutor cancel & gagal self-sub.
flowchart TD
XCancel["Tutor X cancel via WhatsApp
→ admin info"] --> CheckSelf{"Tutor X coba
self-sub dulu?"}
CheckSelf -->|Berhasil| Direct["Sesi jalan dengan tutor self-sub
(no penalty for X)"]
CheckSelf -->|Gagal / skip| OpenDetail["Admin open schedule X
klik [Cari Sub]"]
OpenDetail --> ListCand["List candidates sorted by distance:
Filter:
• subject match (Subject + SubjectLevel)
• Slot bebas (tidak overlap sesi)
• status ACTIVE
Display: nama, distance km,
Golongan, last_active"]
ListCand --> Pick["Admin pilih candidate Z"]
Pick --> Dispatch["Dispatch via WhatsApp manual
(template message)"]
Dispatch --> Accept{Z accept?}
Accept -->|Yes| Reassign["Admin update sesi assignment ke Z
(audit log capture)"]
Reassign --> NewSnap["Re-snapshot Honorarium
(berdasarkan Z's Golongan)"]
NewSnap --> Run["Sesi jalan dengan Z
(X kena penalty Option β)"]
Accept -->|No| ListCand
Accept -->|Habis kandidat| Cancel["Sesi cancelled
(siswa charge 0, X kena penalty)"]
style Direct fill:#34d399,color:#052e1f
style Run fill:#34d399,color:#052e1f
style Cancel fill:#f87171,color:#420c0c
Iter 1 Constraints
- No service radius MVP — sortir by distance saja, tanpa hard cutoff (admin judgment)
- Manual dispatch via WhatsApp — tidak ada auto-accept/decline. Admin chat manual, Z respond, admin update.
- Re-snapshot Honorarium — kalau Z's Golongan beda dari X, Honorarium di-recalc. Tagihan siswa tetap sama (STUDENT matrix tidak dipengaruhi).
- Iter 2+: tutor punya app/portal → notif push langsung ke Z, accept/decline in-app, audit lebih lengkap.
Monthly Billing & Honor Cycle
End of month — Tagihan ke siswa + Honorarium ke tutor (snapshot-based).
flowchart TD
EOM[End of month trigger] --> GenT["Generate Tagihan per siswa
(aggregate semua sesi COMPLETED
+ NO_SHOW_STUDENT + cancellation
charges + adjustment lines)"]
EOM --> GenH["Generate Honorarium per tutor
(aggregate sesi by Golongan
+ transport reimburse
+ adjustments + PPh placeholder)"]
GenT --> Share["Tagihan dishare ke siswa
(welcome email + admin WA manual)"]
Share --> Bayar["Siswa bayar offline
(bank transfer / e-wallet)
upload bukti via WA"]
Bayar --> AdmVer["Admin verify bukti
+ mark Settlement.PAID bulk"]
AdmVer --> Aging["Outstanding aging update
(0-30 / 31-60 / 61-90 / 90+)"]
GenH --> Slip["Honor slip PDF generated
(breakdown per tutor per bulan)"]
Slip --> Trf["Finance batch transfer off-system
(bank/e-wallet)"]
Trf --> MarkH["Admin mark
SettlementTeacher.PAID bulk"]
Aging --> WriteOff{Bad debt
review?}
WriteOff -->|Yes| WO["Senior approval +
tax compliance flag +
audit"]
WriteOff -->|No| OK[Move to next cycle]
AdmVer --> Close[Ready for Period Close]
MarkH --> Close
style Close fill:#fef3c7,color:#422006
Key Notes
- Tagihan = STUDENT matrix amount snapshot + transport tier + cancellation charges + adjustment lines. Per-sesi line items visible ke siswa.
- Honorarium = TUTOR matrix amount snapshot (by Golongan) + actual transport reimburse (XPrivate cover delta) + adjustments - PPh withholding. SISWA NEVER SEES.
- Manual reminder broadcast: admin bisa trigger WhatsApp template untuk late-payment siswa atau low-completion tutor.
- Honor reconciliation tool: per-session granular + bulk mark paid (untuk track session yang honor-nya belum di-transfer).
Financial Period Close (M7)
Monthly close checklist + period lock + re-open option.
flowchart TD
Trigger["Finance trigger monthly close"] --> CheckIP{Ada sesi
IN_PROGRESS?}
CheckIP -->|Yes| BlockIP["BLOCK: finalize sesi dulu
(tutor confirm + admin validate)"]
CheckIP -->|No| Checklist["Monthly close checklist UI
(step-through)"]
Checklist --> S1["✓ Bills (Tagihan) generated"]
Checklist --> S2["✓ Payments reconciled
(siswa bayar bukti verified)"]
Checklist --> S3["✓ Honor batch ready
(Honorarium generated)"]
Checklist --> S4["✓ FM cases reviewed
(no PENDING_L1/L2)"]
Checklist --> S5["✓ Adjustments processed
(credit notes / refunds)"]
S1 --> AllGood{Semua
checked?}
S2 --> AllGood
S3 --> AllGood
S4 --> AllGood
S5 --> AllGood
AllGood -->|Yes| Lock["Period LOCK flag set
+ Lock audit (who/when)"]
AllGood -->|No| Wait[Address pending items]
Lock --> Effect["Effect:
• No backdated bookings
• No retroactive mark-paid
• No new adjustments"]
Effect --> ReOpen{Need
re-open?}
ReOpen -->|Yes| Senior["Senior admin approval
+ reason audited
+ unlock"]
ReOpen -->|No| Done[Period closed final]
style BlockIP fill:#f87171,color:#420c0c
style Lock fill:#34d399,color:#052e1f
style Done fill:#34d399,color:#052e1f
Period Close Properties
- IN_PROGRESS blocker: hard guard — period tidak bisa di-close kalau ada sesi belum finalized (mencegah data integrity issue).
- Lock flag: setelah lock, attempt operasi backdated / retroactive akan rejected dengan error UI.
- Re-open option: senior admin only, with reason audit. Untuk correction case yang ditemukan setelah close.
- Reconciliation export: Excel template finance-familiar dengan adjustments column, dipakai untuk validation final sebelum lock.
Tutor Termination Flow (M6)
HR-driven termination dengan FM blocker, termination checklist, archive.
flowchart TD
Reason["Termination reason:
• Strike 3 (auto-trigger)
• HR judgment (manual)
• Tutor resign"] --> Trigger["HR trigger termination via UI"]
Trigger --> CheckFM{FM claim status
PENDING_L1 atau
PENDING_L2?}
CheckFM -->|Yes| Block["⚠ BLOCKED:
Resolve FM claim dulu
(no termination during FM review)"]
CheckFM -->|No| Checklist["Termination Checklist UI
(step-through)"]
Checklist --> S1["1. Final settlement payout
(auto-trigger calc untuk
remaining unpaid sesi)"]
Checklist --> S2["2. Document return tracking"]
Checklist --> S3["3. Access revoke immediate
(revoke OAuth tokens +
login disabled atomic txn)"]
Checklist --> S4["4. Archive flag
(tutor.status=ARCHIVED)"]
S1 --> Final[Final terminated]
S2 --> Final
S3 --> Final
S4 --> Final
Final --> Archive["Tutor data preserved
(read-only via ARCHIVED status)
Historic sesi/settlement tetap query-able"]
Archive --> Reactivate{Reactivation
request?}
Reactivate -->|Yes| Senior2["Senior admin approval
+ audit log
+ ARCHIVED → ACTIVE"]
Reactivate -->|No| End[Termination final]
style Block fill:#f87171,color:#420c0c
style Final fill:#fbbf24,color:#422006
style End fill:#94a3b8,color:#1e293b
Strike Sanction Reference (Triggers Termination)
- Strike 1: Warning + audit log entry
- Strike 2: Final warning + temporary suspend 7 hari
- Strike 3: Termination kerjasama (auto-trigger)
- Strike decay: 90 hari rolling window (oldest expires)
Critical Properties
- FM blocker: legal/fairness protection — jangan terminate tutor yang masih punya pending FM claim (mungkin claim valid → no penalty → no Strike 3).
- Final settlement auto-trigger: legal obligation — semua unpaid sesi yg sudah COMPLETED harus di-payout.
- Access revoke immediate + atomic: same DB transaction dengan termination flag set, untuk konsistensi.
- Archive (bukan delete): data preservation untuk audit trail historical disputes / regulator inquiry.
5D Pricing Matrix Structure
Dua matrix versioned per Tahun Ajaran. Snapshot at APPROVED.
① Matrix Structure (dua matrix terpisah)
flowchart LR
subgraph SM["STUDENT Matrix → Tagihan / sesi"]
direction TB
S1[Subject]
S2["SubjectLevel ?
(optional child)"]
S3[Tingkat]
S4[Segmentasi]
S1 --> SA[("amount/sesi")]
S2 -.-> SA
S3 --> SA
S4 --> SA
end
subgraph TM["TUTOR Matrix → Honorarium / sesi"]
direction TB
T1[Subject]
T2["SubjectLevel ?
(optional child)"]
T3[Tingkat]
T4[Segmentasi]
T5["Golongan
(tutor tier)"]
T1 --> TA[("amount/sesi")]
T2 -.-> TA
T3 --> TA
T4 --> TA
T5 --> TA
end
style SA fill:#38bdf8,color:#0c2c4a
style TA fill:#a855f7,color:#2e1065
② Snapshot Composition at APPROVED (vertical flow)
flowchart TD
SA["amountStudent
← lookup STUDENT matrix"] --> Snap
TA["amountTutor
← lookup TUTOR matrix"] --> Snap
TR["transportFee
← app config bracket per km
(default <20km=Rp10k, >20km=Rp12k)"] --> Snap
MS["mode_surcharge
(optional per-sesi escape hatch)"] --> Snap
Snap["🔒 Snapshot at APPROVED
frozen ke Schedule record:
pricingMatrixIdStudent + pricingMatrixIdTutor
+ amountStudent + amountTutor
+ transportFee + mode_surcharge
+ cancellationPolicyConfigId"]
Snap --> Margin["💵 XPrivate margin (derived per sesi)
= amountStudent − amountTutor − (XPrivate transport share)"]
style SA fill:#38bdf8,color:#0c2c4a
style TA fill:#a855f7,color:#2e1065
style Snap fill:#fef3c7,color:#422006
style Margin fill:#34d399,color:#052e1f
③ Contoh Concrete Rows
| Matrix | Subject | SubjLevel | Tingkat | Segmentasi | Golongan | Amount/sesi |
|---|---|---|---|---|---|---|
| STUDENT | Mandarin | HSK 3 | SMA 10-12 | REGULER | — | Rp 150.000 |
| STUDENT | Mandarin | HSK 3 | SMA 10-12 | INTERNASIONAL | — | Rp 250.000 |
| STUDENT | Piano | — | SD 1-6 | REGULER | — | Rp 120.000 |
| TUTOR | Mandarin | HSK 3 | SMA 10-12 | REGULER | GOL_1 | Rp 120.000 |
| TUTOR | Mandarin | HSK 3 | SMA 10-12 | REGULER | GOL_3 | Rp 80.000 |
| TUTOR | Piano | — | SD 1-6 | REGULER | GOL_2 | Rp 90.000 |
Margin XPrivate per sesi = STUDENT amount − TUTOR amount (untuk row Mandarin/HSK3/SMA/REGULER/GOL_1: Rp 150k − Rp 120k = Rp 30k).
Versioning & Operations
- Versioned per Tahun Ajaran (Jul-Jun cycle) dengan
effective_from/effective_until. - Sparse allowed: admin bisa kosongkan combo. Coverage gap detector warn "Subject X has 60% combos populated".
- Mid-flight override OK: admin bisa edit current matrix (audit-logged).
- Atomic Clone Tahun Ajaran: one-click — set
effective_untilon old + insert new rows w/ %-bump. Audit-tracked. - Hierarchical drill-down UX: Subject → SubjectLevel → matrix per audience (NOT flat 6-column entry).
- CSV import + bulk-edit + per-row validation.
- Snapshot at APPROVED: sesi capture
pricingMatrixId+ frozenamount. Immune dari later config change.
Auth & Identity Landscape (Future-Facing)
Iter 1 admin only ACTIVE. Multi-identity schema DB-READY sejak awal (user constraint: auth = nightmare to change later).
flowchart LR
subgraph A1["Iter 1 ACTIVE"]
AdminLogin["Admin Login
Google OAuth +
Workspace domain restrict
@xprivate.education"]
Session[Session mgmt
timeout + remember-me]
RBAC["Custom flexible RBAC
Role + Permission +
RolePermission + UserRole"]
end
subgraph DR["Iter 1 DB-READY (schema present, deferred activation)"]
UserType["UserType enum:
• ADMIN ✓ (active)
• TUTOR (ready)
• SISWA (ready)
• PARENT (ready)"]
IdProvider["IdentityProvider:
• Google OAuth ✓ (active)
• email/password (ready)
• social login (ready)"]
UserIdentity["UserIdentity
(many-to-many link)"]
TFA["TwoFactorMethod
(2FA TOTP / SMS ready)"]
LoginAudit["LoginAuditEvent
(login events ready)"]
PwdReset["PasswordReset
EmailVerification"]
AccState["AccountState machine:
PENDING_INTERVIEW →
APPROVED → ACTIVE →
SUSPENDED → ARCHIVED"]
end
subgraph A2["Iter 2+ ACTIVATION"]
TutorPortal["Tutor portal login
(basic email/pwd
or magic link)"]
SiswaPortal["Siswa portal login"]
Activate2FA["2FA TOTP activate
(opt-in or mandatory)"]
SelfReg["Self-register flow
(apply → interview →
approved → active)"]
end
AdminLogin --> Session
Session --> RBAC
UserType -.-> TutorPortal
UserType -.-> SiswaPortal
IdProvider -.-> TutorPortal
TFA -.-> Activate2FA
AccState -.-> SelfReg
style AdminLogin fill:#34d399,color:#052e1f
style Session fill:#34d399,color:#052e1f
style RBAC fill:#34d399,color:#052e1f
style UserType fill:#fbbf24,color:#422006
style IdProvider fill:#fbbf24,color:#422006
style UserIdentity fill:#fbbf24,color:#422006
style TFA fill:#fbbf24,color:#422006
style LoginAudit fill:#fbbf24,color:#422006
style PwdReset fill:#fbbf24,color:#422006
style AccState fill:#fbbf24,color:#422006
style TutorPortal fill:#38bdf8,color:#0c2c4a
style SiswaPortal fill:#38bdf8,color:#0c2c4a
style Activate2FA fill:#38bdf8,color:#0c2c4a
style SelfReg fill:#38bdf8,color:#0c2c4a
Why Future-Facing dari Iter 1
- Auth menyentuh setiap entity — siapa user, role, session, permission, identity, MFA, audit. Migration auth pasca-launch = pain.
- Schema cost rendah: tambah tabel/kolom upfront ≪ migrate auth model dengan data sensitif + downtime + retraining.
- Iter 1 admin-only ACTIVE: cuma Google OAuth + RBAC + Session yang dipakai. Sisanya schema dormant.
- Iter 2 ACTIVATION: tutor portal login (basic) supaya tutor mark Start/attendance/materi sendiri (genuine identity audit, bukan admin proxy).
📖 Glossary — Istilah XPrivate Education
Kamus istilah yang dipakai di dokumen ini, scope.md, dan UI sistem. Untuk pembaca non-teknis.
👥 Aktor / Entitas
| Istilah | Artinya (bahasa awam) |
|---|---|
Sesi | 1 unit pengajaran (1 lesson). Default durasi 90 menit, format 1 tutor & 1 siswa. |
Siswa | Pelanggan / murid yang les. (Term formal "siswa", bukan "murid".) |
Tutor | Guru yang mengajar. (Term formal "tutor", bukan "guru".) |
Admin / Operator | Staff XPrivate yang mengelola sistem di iter 1 (1-5 orang). Di iter 1, semua interaksi dengan sistem dilakukan admin. |
📚 Catalog Dimensions (kategori untuk pricing & filtering)
| Istilah | Artinya |
|---|---|
Subject | Mata pelajaran konkret. Contoh: "Bahasa Mandarin", "Piano", "CPNS", "Matematika". |
SubjectLevel | Tingkatan dalam satu Subject. Contoh: HSK 1-6 di Mandarin; Grade 1-8 di Piano. Opsional (tidak semua Subject punya level). |
Tingkat | Tingkat pendidikan siswa. Pilihan: SD 1-6, SMP 7-9, SMA 10-12, Mahasiswa, Umum, Profesional. |
Segmentasi | Tier layanan yang dipilih siswa: REGULER, REGULER_PLUS, INTERNASIONAL. Mempengaruhi harga. |
Kategori | Grouping/kelompok Subject (REGULER_KELAS, PROGRAM_KHUSUS, SERTIFIKASI, BAHASA_ASING, MUSIK, KOMPUTER). Hanya untuk organisasi — TIDAK mempengaruhi harga. |
Golongan | Tier tutor: GOL_1 (paling senior) sampai GOL_4 (paling junior), + NON_GOLONGAN (belum di-tier). Mempengaruhi honor tutor. |
📅 Periode & Versioning
| Istilah | Artinya |
|---|---|
Tahun Ajaran | Periode Jul-Jun (mengikuti siklus pendidikan Indonesia). Contoh: Tahun Ajaran 2026/2027 = Jul 2026 sampai Jun 2027. Pricing matrix di-versioned per Tahun Ajaran. |
Snapshot | Sistem mencatat data harga + cancellation policy yang berlaku saat sesi di-approve, supaya sesi itu pakai aturan lama walau admin edit master data nanti. Mencegah retroactive price change. |
Effective_from / Effective_until | Tanggal mulai & berakhir berlakunya satu baris pricing matrix atau cancellation config. |
Atomic Clone Tahun Ajaran | Operasi 1-klik untuk admin: copy semua pricing dari Tahun Ajaran lama ke Tahun Ajaran baru, dengan opsi naik harga (%) sekaligus. |
💰 Finansial
| Istilah | Artinya |
|---|---|
Tagihan | Invoice yang dikirim ke siswa per bulan (post-paid). Berisi list sesi yang dijalankan + transport + adjustment. |
Honorarium | Honor yang dibayarkan ke tutor per bulan. Siswa TIDAK PERNAH lihat detail Honorarium. |
Settlement | Catatan transaksi per sesi (di sisi siswa = jadi line di Tagihan; di sisi tutor = jadi line di Honorarium). |
STUDENT Matrix | Tabel harga yang dipakai untuk hitung Tagihan siswa per sesi. |
TUTOR Matrix | Tabel harga yang dipakai untuk hitung Honorarium tutor per sesi (include dimensi Golongan). |
Transport bracket | Tier ongkos transport berdasarkan jarak (default: <20km = Rp10.000, >20km = Rp12.000). Tagihan ke siswa pakai tier; tutor reimburse pakai bukti actual; XPrivate cover delta. |
mode_surcharge | Tambahan harga opsional per sesi — kalau ternyata di kasus tertentu online vs offline perlu beda harga. Escape hatch. |
Outstanding Aging | Laporan tagihan yang belum dibayar, dikelompokkan by usia: 0-30 hari, 31-60, 61-90, 90+ hari (overdue). |
Credit Note | Dokumen finansial yang mengurangi Tagihan siswa (untuk refund, dispute resolution, atau koreksi salah charge). |
Bad Debt Write-off | Tagihan yang sudah tidak bisa di-collect ditandai sebagai "kerugian". Butuh approval senior + audit + flag pajak. |
PPh withholding | Potongan pajak penghasilan dari honor tutor. Iter 1: field manual input. Iter 2: engine tax full. |
Period Close | Proses mengunci pembukuan keuangan satu bulan. Setelah locked: tidak bisa ada transaksi backdated atau retroactive mark-paid. |
⏱️ Sesi States (state machine 7 status)
| State | Artinya |
|---|---|
REQUESTED | Sesi baru dibuat admin, menunggu approval. Slot belum di-lock. |
APPROVED | Admin sudah approve, slot tutor di-lock, harga + cancellation policy snapshot. |
REJECTED | Admin tolak. Final — tidak bisa di-undo. Reason di-audit. |
IN_PROGRESS | Sesi sedang berjalan (sudah di-mark Start). |
COMPLETED | Sesi selesai + materi confirmed. Lewat 2-step gate (tutor confirm + admin validate). |
CANCELLED | Dibatalin setelah APPROVED. Bisa kena penalty (tergantung policy bracket). |
RESCHEDULED | Sesi lama di-close, sesi baru linked dibuat dengan jadwal baru. |
NO_SHOW_STUDENT | Sub-state COMPLETED: siswa tidak hadir. Siswa kena charge full. |
NO_SHOW_TUTOR | Sub-state COMPLETED: tutor tidak hadir. Escalate ke cancellation flow. |
⚙️ Operasi & Compliance
| Istilah | Artinya |
|---|---|
Approval Queue | Antrian sesi yang status REQUESTED, menunggu admin approve/reject. |
Anomaly Queue | Antrian sesi/item yang flag oleh sistem sebagai mencurigakan (mis. double-booking, durasi aneh). |
SLA | Service Level Agreement — komitmen waktu tanggap. "SLA flag" = badge warn kalau item lewat threshold tanggap (mis. REQUESTED >24 jam = SLA breach). |
Slot | Window waktu availability tutor (kapan dia bisa ngajar). |
FM (Force Majeure) | Keadaan darurat (sakit, duka, bencana, dll) yang membebaskan tutor dari penalty cancellation. Butuh approval L1 + opsi banding L2 dalam 14 hari. |
Strike | Pelanggaran tutor (mis. cancel mendadak tanpa FM). Akumulasi 3 strike dalam 90 hari → terminate. Strike decay 90 hari rolling. |
Self-sub (Option α) | Tutor cancel ≤4h tapi cari pengganti sendiri dari komunitas tutor XPrivate. Kalau sukses = clean (no penalty). |
Escalate (Option β) | Tutor cancel ≤4h dan tidak/gagal cari pengganti sendiri → XPrivate yang dispatch substitute. Tutor original kena penalty graduated + strike. |
Audit Trail | Log perubahan: siapa lakukan apa, kapan, ke entity mana, sebelum vs sesudah. Immutable (tidak bisa di-edit/delete), retention 7 tahun. |
R2 | Cloudflare object storage (untuk simpan file: foto KTP, kontrak PDF, foto materi/report sesi, bukti bayar). |
Coverage gap detector | Tool yang warn ke admin: "Subject X cuma 60% combos populated" — supaya admin tahu mana pricing yang masih bolong di matrix. |
🧱 Modul (developer-facing reference)
| Modul | Scope |
|---|---|
M1 | User Management — login admin, CRUD admin user, role & privilege |
M2 | Master Data — tutor/siswa profile, catalog dimensions, pricing matrix, cancellation policy editor, app config |
M3 | Schedule Management — create sesi, approval queue, calendar, cancellation engine, FM, substitute matching |
M4 | Reporting — Tagihan, Honorarium, student progress, outstanding aging, export Excel |
M6 | HR Discipline + Termination — strike, suspend, terminate, archive, reactivation |
M7 | Finance Workflow — refund/credit note, period close, reconciliation, PPh placeholder, honor slip PDF |
A1 | Audit Trail (cross-cutting) — middleware logging, immutable, 7y retention |
A2 | Notification Trigger — iter 1 manual welcome email saja, iter 2 multi-channel auto |
🚀 Iteration Strategy
| Istilah | Artinya |
|---|---|
Iter 1 (Admin-MVP) | Iteration pertama: pure backoffice admin. Customer-facing self-service belum ada. Fokus de-risk foundation. |
Iter 2+ | Iteration berikutnya: customer-facing aktivasi (tutor/siswa self-service signup & login, payment gateway, auto-notif, Zoom, in-app chat, dispute UI). |
DB-ready | Schema database sudah didesain support fitur tersebut, tapi UI/feature belum di-implementasi. Tujuan: tidak perlu migration besar saat fitur di-aktivasi nanti. |
Admin proxy | Di iter 1, action yang seharusnya dilakukan tutor (mark Start sesi, attendance, upload materi) di-input admin atas nama tutor — karena tutor belum punya login sendiri. |