think
16px
820px

Report: V2 read-only mode + route flip + legacy Pendirian PP review retirement

Worktree: /home/efran/remote-development/poc-ahu-ai/ahu-ocr-tidyup, branch refactor/tidyup-foundation.

Status: DONE

Commits (chronological)

  1. 6ddbd6a — feat(review-engine): readOnly prop threads ReviewEngine → section renderers
  2. a25c8ac — feat(pp-pendirian-v2): completed/read-only mode for the ReviewData engine page
  3. 24f6f80 — feat(pp-pendirian): flip /review to the V2 engine page, retire the legacy page
  4. 9ae25f6 — feat(pp-pendirian): retire the legacy GET .../review-data endpoint
  5. 3b9e4b2 — docs(pp-review-engine-mutations): fix comments left stale by the legacy page's retirement

(Note: 485c0b3 docs(pembubaran-pp): tenant #2 implementation plan sits between the branch's prior tip and commit 1 — that's a concurrent teammate's commit in this shared worktree, not mine; excluded from the summary above.)

What was done

Scope A — V2 completed/read-only mode (TDD)

  • components/review-engine/types.ts: SectionRendererProps gained readOnly?: boolean.
  • ReviewEngine.tsx: accepts readOnly and forwards it to every section renderer.
  • FieldListSection.tsx: Ubah + Konfirmasi/Unconfirm buttons get disabled={readOnly} (rendered, not hidden — legacy idiom in engine terms).
  • RosterSection.tsx: confirm/unconfirm icon button disabled when readOnly (combined with the existing per-row rowConfirmGuard disable).
  • components/pp/pp-pemilik-manfaat-cards.tsx (BoCard + PpPemilikManfaatCards): confirm/unconfirm button and the KriteriaPicker now also disable on readOnly.
  • PpPendirianReviewPageV2.tsx:
  • readOnly={isCompleted} passed into ReviewEngine.
  • onRevalidate omitted entirely (not just disabled) when completed — ValidationBanner only renders the revalidate button when the prop is present.
  • Pernyataan attestation Checkboxes get disabled={isCompleted} (pre-checked via the existing seed effect).
  • Finalisasi button area replaced with a completed banner (data-testid="pendirian-completed-banner") when isCompleted: emerald card + CheckCircle2 + "Pendirian PP — Selesai" + an "Unduh Packet" download button hitting the same /api/pp/pendirian/:id/packet endpoint the legacy page used — styled after the codebase's existing PeralihanCompletedBanner/Perbaikan completedTitle idiom rather than a bespoke new one.
  • Stepper + ReviewProgressHeader gating left untouched, as instructed.

Scope B — route flip + retirement (after Scope A green)

  • routes.tsx: pp/pendirian/:submissionId/review now renders PpPendirianReviewPageV2; .../review-v2 now renders a new tiny PpPendirianReviewV2Redirect component (components/pp/pp-pendirian-review-v2-redirect.tsx, mirrors the existing IndexRedirect convention: useParams + <Navigate replace>) so old bookmarks land on /review. Legacy import removed.
  • Deleted frontend/src/pages/pp/PpPendirianReviewPage.tsx (confirmed via grep: routes.tsx was its only non-comment consumer) and its dedicated test frontend/src/pages/__tests__/PpPendirianReviewPage.bo.test.tsx.
  • hooks/use-pp-pendirian-review.ts: deleted usePpPendirianReviewData and useFinalizePpPendirian (confirmed sole consumer was the legacy page). Kept everything else — usePpEditField (and its schedulePostEditRefetches helper, confirmed still used by it), usePpConfirmField/usePpUnconfirmField/useSetKbliConfirmed/usePpEditBoField/usePpEditKbli/usePpSetBoKriteria/usePpSetBoConfirmed/usePpDeleteBo/usePpSectionApproval, and all PpReview* types (still consumed by V2 + shared BO components per grep). Removed the now-unused useQuery import.
  • Trimmed the now-dead usePpPendirianReviewData describe block out of hooks/__tests__/use-pp-pendirian-review.test.tsx.
  • Backend backend/src/routes/pp-pendirian.ts: deleted the GET /:id/review-data handler and toReviewField/the now-unused PP_REVIEW_PHASE_RULE_CODES import (both existed only to serve that handler). Kept ppRequiredSections() per the brief, even though it's now unused — its doc comment and the finalize Gate-1 comment were reworded off the retired endpoint (they no longer claim finalize consults it, since it never actually did — the code only checks sectionApprovals["pernyataan"]).
  • Deleted backend/src/review/__tests__/pp-pendirian-parity.test.ts (its sole purpose — parity-guarding the legacy vs. new payload while both endpoints lived — no longer applies) and backend/src/routes/__tests__/pp-pendirian-bo-review-data.test.ts (tested only the deleted route).
  • backend/src/services/__tests__/pp-pendirian-bo-e2e.integration.test.ts: its finalize-flow test fetched .../review-data purely to read requiredSections and approve each one. Since finalize's Gate 1 only ever checks sectionApprovals["pernyataan"] (confirmed by reading the handler), replaced that step with a direct pernyataan approval — same test intent, no dependency on the deleted route.
  • tidyup/tasks.md line 64: PENDIRIAN_PP Phase B line updated to reflect route flipped + legacy endpoint/page retired (2026-07-04).
  • Small follow-up: two doc comments in hooks/use-pp-review-engine-mutations.ts asserted the legacy page "keeps using" the old hooks "untouched" — now false since the page is gone; reworded to past tense (commit 5).

Test evidence

  • Backend: cd backend && bunx tsc --noEmit && bun run test → typecheck clean; 1968 pass, 1 skip, 0 fail (245 files).
  • Frontend: cd frontend && bunx tsc --noEmit && npx vitest run → typecheck clean; 689 tests, 106 files, all pass.
  • New/changed test coverage added this task: ReviewEngine.test.tsx (+2: readOnly passthrough, default-false), FieldListSection.test.tsx (+3: readOnly disables buttons / no-op click / default-false), PpPendirianReviewPageV2.test.tsx (+7: the COMPLETED-status describe block covering field/roster/BO-card/kriteria/pernyataan disabling, no Finalisasi, completed banner, no revalidate button). All were written first and observed failing (10 failing) before implementation, then green.

Deletions verified dead by grep (repo-wide, before deleting)

  • PpPendirianReviewPage (non-V2): only routes.tsx imported it; remaining hits after deletion are comments in unrelated files (use-pp-review-engine-mutations.ts, PpPeralihanPtReviewPage.tsx, lib/currency.ts, ReviewProgressHeader.tsx, backend projector/pp-shape.ts) — left alone as historical design-reference comments, except the two in use-pp-review-engine-mutations.ts that made an actively false current-state claim (fixed, commit 5).
  • usePpPendirianReviewData / useFinalizePpPendirian: only the legacy page imported them.
  • schedulePostEditRefetches: still used by usePpEditField — kept.
  • Backend GET /:id/review-data: only 3 test files referenced the literal route/path; all handled (2 deleted, 1 patched).

Concerns

  • ppRequiredSections() is now genuinely unused dead code (kept per explicit instruction in the brief). Its only caller was the deleted endpoint; the finalize handler never actually called it (Gate 1 only checks pernyataan), despite an old comment implying otherwise — that comment is now corrected. Flagging in case the team wants it actually removed in a follow-up rather than kept indefinitely.
  • This is a shared worktree — a concurrent teammate committed 485c0b3 (Pembubaran PP tenant-2 plan doc) mid-task. It's untouched by my diff and doesn't conflict, just noting it for traceability.