think
16px
820px

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.

flowchart TD A["Step-2: user tambah jenis manual"] --> B["selectedJenisPerubahan.manualAdditions
(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: parse manualAdditions (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 manualAdditions eksplisit, bukan diturunkan dari selected − 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: parse manualAdditions valid/invalid; applyManualJenisAdditions union + dedupe.
  • Integrasi: submission dengan manual addition → effectiveJenis memuatnya (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 di detectedJenis. Tidak perlu ubah shape untuk UI.

5. Panel — frontend/src/components/klasifikasi/jenis-perubahan-panel.tsx

  • Selalu render (hapus return null saat 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. deselectReasons tetap di-anchor ke detected (additions tak akan salah masuk deselect).
  • checkedJenisKeys (adaptive doc-requirements) otomatis ikut additions PAD/NONPAD — mis. menambah peralihanSaham memicu syarat Akta Pemindahan Hak. (Efek diinginkan.)

7. Tes frontend

  • Panel: tambah PAD/NONPAD/PPAD, hapus chip tambahan, empty-state render "+".
  • KlasifikasiPage: manualAdditions masuk 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).