Last active 4 days ago

Revision 65aaa7e93a5e7db537c56da13b3cffe0efce26fd

FLOW_BOD_ROLLBACK_FRONTEND.md Raw

BOD Rollback — Panduan Tampilan Frontend

Dokumen ini untuk tim frontend. Menjawab: "mas ketika 'Testing' di-rollback ke 'Testing 2', apa yang seharusnya terjadi di layar?"

Semua behavior di bawah sudah terimplementasi di backend. Frontend tinggal merender field yang sudah ada di payload item (approvalStatus, bodResult, bodDisplayStatus, rollbackPaused, bodApprovals[], rollbackOptions[], dan status progress per baris). Contoh payload ada di §5.


1. Jawaban singkat (bisa langsung dikirim ke frontend)

Ketika kamu rollback "Testing" ke "Testing 2":

  1. Testing 2 beserta semua anaknya dibuka ulang untuk approval. Progress terakhir tiap item paling bawah (leaf) berubah jadi PENDING / menunggu approval. Item yang masih menunggu prerequisite-nya tidak ikut dibuka — tampil NOT_STARTED abu-abu, dan tidak bisa disentuh sampai prereqnya selesai.
  2. Testing (item yang di-rollback) tidak di-flip saat itu juga: seluruh subtree Testing tampil NOT_STARTED (rollbackPaused = true), data & progress tetap utuh (masih 100%), approvalStatus jadi Waiting Approval, tombol aksi disabled. Setelah "Testing 2" di-approve & ditutup lagi, barulah subtree Testing dibuka ulang bertahap — semua node (akar leaf, non-leaf, leaf rantai-awal) jadi Waiting Approval tanpa flip progress (Completed & Waiting, notif ke VP); yang masih punya prereq / blocker tetap NOT_STARTED.
  3. Item lain yang prerequisite-nya = "Testing 2" (konsumen downstream) ikut dikunci: tampil NOT_STARTED, data tidak diubah, semua tombol edit/approve disable sampai "Testing 2" di-approve & ditutup lagi.
  4. Setelah "Testing 2" selesai di-approve ulang (VP + BOD bila wajib), konsumennya & subtree Testing melepas satu-satu secara berurutan: si A jadi Waiting Approval, lalu anak/rantai berikutnya setelah A ditutup, dst.
  5. Semua ini tercatat di log aktivitas per item (siapa vote apa, item mana yang dibuka/dikunci/dilepas).

2. Apa yang berubah di UI sesaat setelah rollback resolve

Item yang di-rollback — "Testing" (item B) — sesaat setelah resolve

Elemen UI Nilai / tampilan
Chip status item NOT_STARTED (bodResult = REJECTED_ROLLBACK atau rollbackPaused = true)
approvalStatus (badge approval) Waiting Approval
bodResult REJECTED_ROLLBACK
bodReason alasan dari vote rollback pertama
Detail BOD approvals daftar nama BOD + decision (APPROVE/REVISE/ROLLBACK) + reason + rollbackTargetName = "Testing 2" + respondedAt
rollbackOptions terkunci ke 1 pilihan: "Testing 2"
Progress milik Testing (leaf) TIDAK diubah — tetap APPROVED 100% (nilai/approvedBy/approvedAt utuh). Tidak pernah di-flip di sisi B; flip progress hanya terjadi bila VP REJECT (atau bila item ini yang dipilih jadi target rollback X)
Tombol aksi disabled (on hold) sampai "Testing 2" selesai lagi

Item yang di-rollback — "Testing" (item B) — setelah "Testing 2" di-approve & closed

Bentuk B Nilai / tampilan
B leaf (akar) approvalStatus = WAITING_APPROVAL + rollbackPaused = false; progress tidak di-flip (Completed & Waiting) → tombol approve VP aktif; bila VP REJECT barulah progress terakhir jadi PENDING
B non-leaf root: WAITING_APPROVAL + unpause; leaf rantai-awal (tanpa prereq / prereq rantai awal) & non-leaf: WAITING_APPROVAL + unpause tanpa flip (Completed & Waiting; notif ke VP); leaf yang masih menunggu prereq: tetap NOT_STARTED abu-abu; blocker subtree: tetap pause

Target rollback — "Testing 2" (item X)

Elemen UI Nilai / tampilan
Chip status item Need Approval (karena progress terakhirnya PENDING) — atau NOT_STARTED jika "Testing 2" sendiri masih menunggu prerequisite-nya
approvalStatus Waiting Approval
bodResult null (bukan item yang di-rollback)
Progress terakhir (leaf) APPROVEDPENDING
Progress perantara tidak berubah

Anak-anak "Testing 2" (X) — semua level, live (restart penuh saat resolve)

Kondisi anak Tampilan
Leaf (tidak punya anak) & tidak memblokir chip Need Approval, progress terakhir PENDING, approvalStatus Waiting Approval
Non-leaf & tidak memblokir chip Need Approval (atau normal sesuai derived), approvalStatus Waiting Approval, progress tetap
Dipakai sebagai prerequisite oleh sibling live lain (blocker) seluruh subtree tampil NOT_STARTED + rollbackPaused = true; tombol write disable; baru terbuka saat prereq pemicunya selesai

Anak-anak "Testing" (B) — saat resolve

Seluruh subtree B: NOT_STARTED abu-abu (rollbackPaused = true), approvalStatus = WAITING_APPROVAL, progress tetap 100%, tombol write disabled. Buka ulang baru terjadi saat "Testing 2" ditutup lagi — lalu mengikuti aturan di atas (semua node → Waiting Approval tanpa flip; punya prereq → NOT_STARTED lanjut).

Aturan display yang dipakai backend (bisa dipakai juga untuk highlight):

  • bodResult === 'REJECTED_ROLLBACK' atau rollbackPaused === true → item tampil NOT_STARTED (override).
  • bodResult === 'REJECTED_REVISE' → tampil "Rejected by BOD" (bodDisplayStatus = REJECTED_BY_BOD).
  • approvalStatus === 'BOD_APPROVAL' → tampil "BOD Approval".

3. Konsumen downstream — item lain yang prereq-nya = "Testing 2"

Ini bagian yang paling sering mengejutkan: rollback tidak hanya menyentuh Testing & Testing 2. Semua item same-level yang prerequisite langsungnya "Testing 2" ikut terkunci.

Kondisi konsumen Tampilan saat rollback Saat "Testing 2" selesai lagi
Sudah CLOSED (approved) tampil NOT_STARTED (rollbackPaused = true); data & progress tetap utuh; semua aksi disabled approvalStatusWaiting Approval (progress tidak di-flip; VP tinggal approve ulang)
Belum CLOSED tampil NOT_STARTED; dikunci mengikuti alur prereq-nya; lanjut dari status terakhir
Anak-anak konsumen (subtree) ikut NOT_STARTED release berurutan: anak baru aktif setelah konsumennya ditutup lagi

Item B (yang di-rollback) tidak masuk kategori konsumen — B di-hold dengan rollbackPaused = true + bodResult = REJECTED_ROLLBACK saat resolve, lalu subtree-nya dibuka ulang bertahap (tanpa flip) ketika "Testing 2" ditutup lagi.

Alur release (rantai Testing 2 → A → P → Q + subtree B):

"Testing 2" closed lagi
   ├─► A        → Waiting Approval        ← mulainya di sini (subtree B ikut restart tanpa flip)
   ├─► B leaf   → Waiting Approval (Completed & Waiting; tidak di-flip; flip bila VP REJECT)
   └─► B non-leaf → anak leaf rantai-awal → Waiting Approval (Completed & Waiting; tidak di-flip)
A di-approve & closed
   └─► P        → Waiting Approval
P di-approve & closed
   └─► Q        → Waiting Approval   (dan seterusnya)

Timing-nya bertahap, bukan serentak. Notifikasi saat release mengikuti routing baku: item yang jadi Completed & Waiting Approval → notif ke VP-nya — seluruh node B-side masuk kategori ini (tanpa flip, progress tetap 100%). Flip progress hanya terjadi di sisi target X saat resolve; item yang di-flip (jadi Need Approval) → notif ke Owner/Admin.

Read-only / disabled saat rollbackPaused = true

Semua action berikut mengembalikan 400 ... is on hold: waiting for its prerequisite to be approved and closed — frontend sebaiknya men-disable tombol ini di item/subtree yang di-hold:

  • edit data item (PATCH)
  • approval respond, re-request, BOD respond
  • submit / respond progress
  • simpan plans (PUT)
  • tambah/hapus member

4. Urutan tampilan di layar dari waktu ke waktu

Waktu Yang tampil
T0 — BOD vote masuk List detail "Testing": badge BOD Approval; tiap BOD yang sudah vote tampil di bodApprovals (beri indikator sudah/blm vote)
T1 — resolve rollback "Testing 2" + subtree X: chip NOT_STARTED / Need Approval; progress leaf X jadi PENDING. "Testing" + subtree B: NOT_STARTED abu-abu (rollbackPaused = true), progress tetap 100% APPROVED (tidak di-flip), semua aksi disabled. Konsumen jadi NOT_STARTED abu-abu; notifikasi Rolled Back by BOD ke VP; log aktivitas terisi per item
T2 — "Testing 2" di-approve lagi "Testing 2" → CLOSED; konsumen A → Waiting Approval; subtree B mulai di-restart — semua node B → Waiting Approval tanpa flip (Completed & Waiting, notif Ready for Approval ke VP)
T3 — A di-approve A → CLOSED; P → Waiting Approval; dst
T4 — "Testing" di-approve ulang "Testing" → CLOSED (handover normal); selesai

Frontend boleh menampilkan status ini apa adanya; urutan cascade dijamin backend (data + log tersedia per item).


5. Contoh payload detail item (English, sesuai API — untuk referensi field render)

GET /api/v1/projects/:projectId/action-plans/:activityIddata (potongan):

{
  "id": "activity-testing-b",
  "name": "Testing",
  "approvalStatus": "WAITING_APPROVAL",
  "approvalReason": null,
  "bodRoundId": "round-1",
  "bodResult": "REJECTED_ROLLBACK",
  "bodReason": "hasil pengujian tidak sesuai",
  "bodDisplayStatus": "NOT_STARTED",
  "rollbackPaused": true,
  "approverId": "user-vp-1",
  "approverName": "Fajar",
  "bodApprovals": [
    {
      "userId": "bod-1",
      "name": "Hendra",
      "decision": "ROLLBACK",
      "reason": "hasil pengujian tidak sesuai",
      "rollbackItemId": "activity-testing2-x",
      "rollbackTargetName": "Testing 2",
      "respondedAt": "2026-09-10T03:00:00.000Z"
    }
  ],
  "rollbackOptions": [{ "id": "activity-testing2-x", "name": "Testing 2" }]
}

Item konsumen (A) yang sedang di-hold:

{
  "id": "activity-a",
  "name": "A",
  "approvalStatus": "CLOSED",
  "bodResult": null,
  "bodDisplayStatus": "NOT_STARTED",
  "rollbackPaused": true
}

Baris progress (leaf "Testing" — item B — setelah rollback resolve): tidak pernah di-flip; hanya berubah bila VP REJECT:

{
  "id": "progress-1",
  "progress": 100,
  "status": "APPROVED",
  "approvedBy": "user-vp-1",
  "approvedAt": "2026-09-09T09:00:00.000Z",
  "description": "…"
}

Baris progress (leaf "Testing 2" — item X — setelah rollback resolve; langsung di-flip):

{
  "id": "progress-2",
  "progress": 100,
  "status": "PENDING",
  "approvedBy": null,
  "approvedAt": null,
  "description": "…"
}

6. Mapping singkat field → tampilan

Field Tampilan frontend
status (derived) chip utama item
bodDisplayStatus = NOT_STARTED tampil NOT_STARTED walau approvalStatus lain
bodDisplayStatus = REJECTED_BY_BOD chip/notice "Rejected by BOD"
bodDisplayStatus = BOD_APPROVAL badge "BOD Approval" (menunggu vote)
approvalStatus = WAITING_APPROVAL aksi approve VP aktif
rollbackPaused = true abu-abu + disable semua aksi + helper "on hold"
progress row status = PENDING badge PENDING di list progress
bodApprovals[].rollbackTargetName label target rollback di detail

Referensi lanjutan (backend internals, boleh diabaikan frontend): .markdown/FLOW_BOD_REJECT_ROLLBACK.md.