Last active 4 days ago

Revision 025a578f9e8b39c8190a76852d7d10d2a0b4c249

FLOW_BOD_ROLLBACK_FRONTEND.md Raw

BOD Rollback — Panduan Tampilan Frontend

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

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

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.