Rencana: Tambah Jenis Perubahan Manual di Step-2
Tujuan: user bisa menambahkan jenis perubahan yang tidak terdeteksi otomatis, pada tahap unggah (Step-2). Additions harus benar-benar berefek — muncul di extraction (read-only) dan terkirim ke SABH — bukan sekadar preview.
Keputusan yang sudah disepakati:
- Tambah hanya di Step-2 (upload). Extraction dibiarkan (sudah read-only setelah submit).
- Kategori yang bisa ditambah: PAD + NONPAD (dari katalog) + PPAD (ketik nomor pasal manual).
- Tombol "+" selalu ada walau 0 terdeteksi; judul group hanya muncul bila ada isi.
Kenapa wajib menyentuh backend (temuan inti)
Pilihan Step-2 saat ini adalah subset ketat dari deteksi pipeline Step-3. Di backend/src/services/jenis-selection.ts:
// getEffectiveJenis(selected, detected)
pad: selected.pad.filter(x => detected.pad.includes(x)) // tak terdeteksi → DIBUANG
Dipakai ~10 tempat (processor, projector review, submit gate, cross-validator, flow-context). Jadi kalau hanya UI, jenis tambahan hilang diam-diam begitu deteksi pipeline masuk. Solusi: perlakukan additions sebagai "deteksi yang diakui user" → union ke changeTypeResult.checkedItems di titik tulis pipeline, sehingga semua konsumen hilir otomatis melihatnya.
(dipersist saat create submission)"] B --> C["Pipeline: detectChangeTypes → checkedItems"] C --> D["applyManualJenisAdditions
union additions ke checkedItems"] D --> E["changeTypeResult tersimpan"] E --> F["getEffectiveJenis / appearingSectionKeys
(~10 konsumen)"] F --> G["Kartu Jenis Perubahan (extraction, read-only)"] F --> H["Submit gate + cross-validator + payload SABH"]
Perubahan Backend
1. Perluas tipe & parser selection — backend/src/services/jenis-selection.ts
- Tambah field opsional pada
SelectedJenisPerubahan:
ts manualAdditions?: { pad: string[]; ppad: string[]; nonpad: string[] }; parseSelectedJenisPerubahanInput: parsemanualAdditions(opsional, tiap sub-array string; default[]). Validasi PAD/NONPAD termasuk anggota katalog kanonik (CANONICAL_PAD_KEYS/CANONICAL_NONPAD_KEYS); PPAD bebas (string non-kosong).- Helper baru:
ts export function applyManualJenisAdditions( checkedItems: DetectedCheckedItems, additions?: { pad: string[]; ppad: string[]; nonpad: string[] }, ): DetectedCheckedItems
Union + dedupe (PPAD case-insensitive). Murni, mudah dites.
2. Union di 4 titik tulis changeTypeResult (pola identik: setelah detectChangeTypes(...))
| File | Baris | Catatan |
|---|---|---|
services/perubahan-processor.ts |
~558 | re-detect perubahan |
services/akuisisi-processor.ts |
~581 | setelah forcePeralihanOn |
flow-engine/processor.ts |
~251 | deteksi awal pipeline |
flow-engine/rematch.ts |
~84 | re-detect flow generik |
Di tiap titik, muat submission.selectedJenisPerubahan.manualAdditions lalu:
changeTypeResult.checkedItems = applyManualJenisAdditions(changeTypeResult.checkedItems, additions);
(Untuk mengurangi duplikasi, bungkus jadi satu helper mergeAdditionsForSubmission(db, submissionId, changeTypeResult).)
Kenapa
manualAdditionseksplisit, bukan diturunkan dariselected − detected? Agar tidak bentrok dengan semantik "stale selection di-drop saat re-ekstraksi". Additions adalah pernyataan user yang harus persist lintas re-ekstraksi.
3. Tes backend
jenis-selection: parsemanualAdditionsvalid/invalid;applyManualJenisAdditionsunion + dedupe.- Integrasi: submission dengan manual addition →
effectiveJenismemuatnya (tidak dibuang).
Perubahan Frontend
4. Store — frontend/src/store/klasifikasi.ts
- Additions tetap tinggal di
jenisSelection.pad/ppad/nonpad(himpunan tercentang). Yang "tambahan" = anggota selection yang tidak ada didetectedJenis. Tidak perlu ubah shape untuk UI.
5. Panel — frontend/src/components/klasifikasi/jenis-perubahan-panel.tsx
- Selalu render (hapus
return nullsaat 0 deteksi). Header ringkas + tombol "+ Tambah" (kanan atas). - Popover tambah (pakai
ui/popover+ui/command): - Group PAD & NONPAD: daftar katalog yang belum ada (detected ∪ added), klik untuk tambah.
- Bagian PPAD: input teks "Ketik nomor pasal (mis. Pasal 5)" + tombol Tambah. Kalau teks pencarian tak cocok katalog, tawarkan "Tambah sebagai PPAD: '…'".
- Chip:
- Terdeteksi → toggle (subset) seperti sekarang.
- Tambahan manual → tampil tercentang + tombol "×" untuk hapus; penanda visual halus (mis. ring beda / label "ditambah").
- Group header per kategori hanya muncul bila ada ≥1 chip.
6. Submit — frontend/src/pages/KlasifikasiPage.tsx
- Saat menyusun
selectedJenisPerubahan, hitung:
ts manualAdditions = { pad: selection.pad.filter(k => !detected.pad.includes(k)), nonpad: selection.nonpad.filter(k => !detected.nonpad.includes(k)), ppad: selection.ppad.filter(k => !detected.ppad?.some(d => d.toLowerCase()===k.toLowerCase())), }
lampirkan ke payload.deselectReasonstetap di-anchor kedetected(additions tak akan salah masuk deselect). checkedJenisKeys(adaptive doc-requirements) otomatis ikut additions PAD/NONPAD — mis. menambahperalihanSahammemicu syarat Akta Pemindahan Hak. (Efek diinginkan.)
7. Tes frontend
- Panel: tambah PAD/NONPAD/PPAD, hapus chip tambahan, empty-state render "+".
- KlasifikasiPage:
manualAdditionsmasuk payload submit.
Ringkasan file tersentuh
Backend (4 + 1): jenis-selection.ts, perubahan-processor.ts, akuisisi-processor.ts, flow-engine/processor.ts, flow-engine/rematch.ts (+ tes).
Frontend (3): store/klasifikasi.ts, jenis-perubahan-panel.tsx, KlasifikasiPage.tsx (+ tes).
Risiko utama: union additions ke universe deteksi = mereka ikut submit gate & cross-validator. Item PAD/NONPAD dibatasi ke katalog kanonik agar tak ada key liar yang lolos ke logika validasi. PPAD bebas (memang string pasal). Extraction read-only tetap seperti sekarang; hanya bertambah item yang tampil.
Perkiraan: backend inti kecil-terukur (1 helper + 4 sisipan 1-baris), frontend panel adalah bagian terbesar (popover + chip states).