135dika revised this gist 4 days ago. Go to revision
1 file changed, 2 insertions, 3 deletions
FLOW_BOD_ROLLBACK_FRONTEND.md
| @@ -1,12 +1,11 @@ | |||
| 1 | 1 | # BOD Rollback — Panduan Tampilan Frontend | |
| 2 | 2 | ||
| 3 | - | Dokumen ini untuk **tim frontend**. Menjawab: *"mas ketika 'Testing' di-rollback ke 'Testing 2', apa yang seharusnya terjadi di layar?"* | |
| 4 | 3 | ||
| 5 | 4 | 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. | |
| 6 | 5 | ||
| 7 | 6 | --- | |
| 8 | 7 | ||
| 9 | - | ## 1. Jawaban singkat (bisa langsung dikirim ke frontend) | |
| 8 | + | ## 1. Jawaban singkat | |
| 10 | 9 | ||
| 11 | 10 | > Ketika kamu rollback **"Testing"** ke **"Testing 2"**: | |
| 12 | 11 | > 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. | |
| @@ -121,7 +120,7 @@ Frontend boleh menampilkan status ini apa adanya; urutan cascade dijamin backend | |||
| 121 | 120 | ||
| 122 | 121 | --- | |
| 123 | 122 | ||
| 124 | - | ## 5. Contoh payload detail item (English, sesuai API — untuk referensi field render) | |
| 123 | + | ## 5. Contoh payload detail item | |
| 125 | 124 | ||
| 126 | 125 | `GET /api/v1/projects/:projectId/action-plans/:activityId` → `data` (potongan): | |
| 127 | 126 | ||
135dika revised this gist 4 days ago. Go to revision
1 file changed, 16 insertions, 13 deletions
FLOW_BOD_ROLLBACK_FRONTEND.md
| @@ -1,14 +1,16 @@ | |||
| 1 | 1 | # BOD Rollback — Panduan Tampilan Frontend | |
| 2 | 2 | ||
| 3 | + | Dokumen ini untuk **tim frontend**. Menjawab: *"mas ketika 'Testing' di-rollback ke 'Testing 2', apa yang seharusnya terjadi di layar?"* | |
| 4 | + | ||
| 3 | 5 | 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. | |
| 4 | 6 | ||
| 5 | 7 | --- | |
| 6 | 8 | ||
| 7 | - | ## 1. Jawaban singkat | |
| 9 | + | ## 1. Jawaban singkat (bisa langsung dikirim ke frontend) | |
| 8 | 10 | ||
| 9 | 11 | > Ketika kamu rollback **"Testing"** ke **"Testing 2"**: | |
| 10 | 12 | > 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. | |
| 11 | - | > 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 (leaf rantai-awal jadi **Need Approval** + progress terakhir jadi **PENDING**; yang masih punya prereq tetap **NOT_STARTED**). | |
| 13 | + | > 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**. | |
| 12 | 14 | > 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. | |
| 13 | 15 | > 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. | |
| 14 | 16 | > 5. Semua ini tercatat di **log aktivitas** per item (siapa vote apa, item mana yang dibuka/dikunci/dilepas). | |
| @@ -27,7 +29,7 @@ Semua behavior di bawah sudah terimplementasi di backend. Frontend tinggal **mer | |||
| 27 | 29 | | `bodReason` | alasan dari vote rollback pertama | | |
| 28 | 30 | | Detail **BOD approvals** | daftar nama BOD + decision (`APPROVE`/`REVISE`/`ROLLBACK`) + reason + `rollbackTargetName` = "Testing 2" + `respondedAt` | | |
| 29 | 31 | | `rollbackOptions` | terkunci ke **1 pilihan**: "Testing 2" | | |
| 30 | - | | Progress milik Testing (leaf) | **TIDAK diubah** — tetap `APPROVED` 100% (nilai/approvedBy/approvedAt utuh). Flip baru terjadi saat "Testing 2" ditutup lagi (tahap T2—T3) | | |
| 32 | + | | 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) | | |
| 31 | 33 | | Tombol aksi | **disabled** (on hold) sampai "Testing 2" selesai lagi | | |
| 32 | 34 | ||
| 33 | 35 | ### Item yang di-rollback — **"Testing"** (item B) — setelah "Testing 2" di-approve & closed | |
| @@ -35,7 +37,7 @@ Semua behavior di bawah sudah terimplementasi di backend. Frontend tinggal **mer | |||
| 35 | 37 | | Bentuk B | Nilai / tampilan | | |
| 36 | 38 | |---|---| | |
| 37 | 39 | | 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` | | |
| 38 | - | | B non-leaf | root: `WAITING_APPROVAL` + unpause; leaf rantai-awal (tanpa prereq / prereq rantai awal): progress terakhir `APPROVED` → **`PENDING`** (Need Approval) + notif ke Owner/Admin; leaf yang masih menunggu prereq: tetap **NOT_STARTED** abu-abu; blocker subtree: tetap pause | | |
| 40 | + | | 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 | | |
| 39 | 41 | ||
| 40 | 42 | ### Target rollback — **"Testing 2"** (item X) | |
| 41 | 43 | ||
| @@ -57,7 +59,7 @@ Semua behavior di bawah sudah terimplementasi di backend. Frontend tinggal **mer | |||
| 57 | 59 | ||
| 58 | 60 | ### Anak-anak "Testing" (B) — saat resolve | |
| 59 | 61 | ||
| 60 | - | Seluruh subtree B: **NOT_STARTED** abu-abu (`rollbackPaused = true`), `approvalStatus = WAITING_APPROVAL`, progress tetap 100%, tombol write **disabled**. Restart baru terjadi saat "Testing 2" ditutup lagi — lalu mengikuti aturan anak di atas (leaf rantai-awal → Need Approval + flip; punya prereq → NOT_STARTED lanjut). | |
| 62 | + | 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). | |
| 61 | 63 | ||
| 62 | 64 | > Aturan display yang dipakai backend (bisa dipakai juga untuk highlight): | |
| 63 | 65 | > - `bodResult === 'REJECTED_ROLLBACK'` **atau** `rollbackPaused === true` → item tampil **NOT_STARTED** (override). | |
| @@ -76,22 +78,22 @@ Ini bagian yang paling sering mengejutkan: **rollback tidak hanya menyentuh Test | |||
| 76 | 78 | | Belum **CLOSED** | tampil **NOT_STARTED**; dikunci | mengikuti alur prereq-nya; lanjut dari status terakhir | | |
| 77 | 79 | | Anak-anak konsumen (subtree) | ikut **NOT_STARTED** | release berurutan: anak baru aktif setelah konsumennya ditutup lagi | | |
| 78 | 80 | ||
| 79 | - | **Item B (yang di-rollback) tidak masuk kategori konsumen** — B di-hold dengan `rollbackPaused = true` + `bodResult = REJECTED_ROLLBACK` saat resolve, lalu subtree-nya di-restart (leaf rantai-awal di-flip) ketika "Testing 2" ditutup lagi. | |
| 81 | + | **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. | |
| 80 | 82 | ||
| 81 | 83 | Alur release (rantai `Testing 2 → A → P → Q` + subtree B): | |
| 82 | 84 | ||
| 83 | 85 | ``` | |
| 84 | 86 | "Testing 2" closed lagi | |
| 85 | - | ├─► A → Waiting Approval ← mulainya di sini (+ flip leaf rantai-awal subtree B) | |
| 87 | + | ├─► A → Waiting Approval ← mulainya di sini (subtree B ikut restart tanpa flip) | |
| 86 | 88 | ├─► B leaf → Waiting Approval (Completed & Waiting; tidak di-flip; flip bila VP REJECT) | |
| 87 | - | └─► B non-leaf → anak leaf rantai-awal → Need Approval (progress PENDING) | |
| 89 | + | └─► B non-leaf → anak leaf rantai-awal → Waiting Approval (Completed & Waiting; tidak di-flip) | |
| 88 | 90 | A di-approve & closed | |
| 89 | 91 | └─► P → Waiting Approval | |
| 90 | 92 | P di-approve & closed | |
| 91 | 93 | └─► Q → Waiting Approval (dan seterusnya) | |
| 92 | 94 | ``` | |
| 93 | 95 | ||
| 94 | - | Timing-nya **bertahap**, bukan serentak. Notifikasi saat release mengikuti routing baku: item yang jadi **Need Approval** (leaf yang di-flip) → notif ke **Owner/Admin**; item yang jadi **Completed & Waiting Approval** → notif ke **VP**-nya. | |
| 96 | + | 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**. | |
| 95 | 97 | ||
| 96 | 98 | ### Read-only / disabled saat `rollbackPaused = true` | |
| 97 | 99 | ||
| @@ -110,8 +112,8 @@ Semua action berikut mengembalikan `400 ... is on hold: waiting for its prerequi | |||
| 110 | 112 | | Waktu | Yang tampil | | |
| 111 | 113 | |---|---| | |
| 112 | 114 | | **T0 — BOD vote masuk** | List detail "Testing": badge BOD Approval; tiap BOD yang sudah vote tampil di `bodApprovals` (beri indikator sudah/blm vote) | | |
| 113 | - | | **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`** (belum di-flip), semua aksi disabled. Konsumen jadi **NOT_STARTED** abu-abu; notifikasi *Rolled Back by BOD* ke VP; log aktivitas terisi per item | | |
| 114 | - | | **T2 — "Testing 2" di-approve lagi** | "Testing 2" → CLOSED; konsumen A → **Waiting Approval**; subtree B mulai di-restart — leaf rantai-awal B → **Need Approval** (progress terakhir `PENDING`), akar B leaf → **Waiting Approval** tanpa flip (notif *Ready for Approval*) | | |
| 115 | + | | **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 | | |
| 116 | + | | **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) | | |
| 115 | 117 | | **T3 — A di-approve** | A → CLOSED; P → **Waiting Approval**; dst | | |
| 116 | 118 | | **T4 — "Testing" di-approve ulang** | "Testing" → CLOSED (handover normal); selesai | | |
| 117 | 119 | ||
| @@ -119,7 +121,7 @@ Frontend boleh menampilkan status ini apa adanya; urutan cascade dijamin backend | |||
| 119 | 121 | ||
| 120 | 122 | --- | |
| 121 | 123 | ||
| 122 | - | ## 5. Contoh payload detail item | |
| 124 | + | ## 5. Contoh payload detail item (English, sesuai API — untuk referensi field render) | |
| 123 | 125 | ||
| 124 | 126 | `GET /api/v1/projects/:projectId/action-plans/:activityId` → `data` (potongan): | |
| 125 | 127 | ||
| @@ -164,7 +166,7 @@ Item konsumen (A) yang sedang di-hold: | |||
| 164 | 166 | } | |
| 165 | 167 | ``` | |
| 166 | 168 | ||
| 167 | - | Baris progress (leaf "Testing" — item B — **setelah rollback resolve**; belum di-flip, baru di-flip saat "Testing 2" closed): | |
| 169 | + | Baris progress (leaf "Testing" — item B — **setelah rollback resolve**): tidak pernah di-flip; hanya berubah bila VP REJECT: | |
| 168 | 170 | ||
| 169 | 171 | ```json | |
| 170 | 172 | { | |
| @@ -207,3 +209,4 @@ Baris progress (leaf "Testing 2" — item X — setelah rollback resolve; langsu | |||
| 207 | 209 | ||
| 208 | 210 | --- | |
| 209 | 211 | ||
| 212 | + | *Referensi lanjutan (backend internals, boleh diabaikan frontend): `.markdown/FLOW_BOD_REJECT_ROLLBACK.md`.* | |
135dika revised this gist 4 days ago. Go to revision
1 file changed, 209 insertions
FLOW_BOD_ROLLBACK_FRONTEND.md(file created)
| @@ -0,0 +1,209 @@ | |||
| 1 | + | # BOD Rollback — Panduan Tampilan Frontend | |
| 2 | + | ||
| 3 | + | 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. | |
| 4 | + | ||
| 5 | + | --- | |
| 6 | + | ||
| 7 | + | ## 1. Jawaban singkat | |
| 8 | + | ||
| 9 | + | > Ketika kamu rollback **"Testing"** ke **"Testing 2"**: | |
| 10 | + | > 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. | |
| 11 | + | > 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 (leaf rantai-awal jadi **Need Approval** + progress terakhir jadi **PENDING**; yang masih punya prereq tetap **NOT_STARTED**). | |
| 12 | + | > 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. | |
| 13 | + | > 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. | |
| 14 | + | > 5. Semua ini tercatat di **log aktivitas** per item (siapa vote apa, item mana yang dibuka/dikunci/dilepas). | |
| 15 | + | ||
| 16 | + | --- | |
| 17 | + | ||
| 18 | + | ## 2. Apa yang berubah di UI sesaat setelah rollback resolve | |
| 19 | + | ||
| 20 | + | ### Item yang di-rollback — **"Testing"** (item B) — sesaat setelah resolve | |
| 21 | + | ||
| 22 | + | | Elemen UI | Nilai / tampilan | | |
| 23 | + | |---|---| | |
| 24 | + | | Chip status item | **NOT_STARTED** (`bodResult = REJECTED_ROLLBACK` **atau** `rollbackPaused = true`) | | |
| 25 | + | | `approvalStatus` (badge approval) | **Waiting Approval** | | |
| 26 | + | | `bodResult` | `REJECTED_ROLLBACK` | | |
| 27 | + | | `bodReason` | alasan dari vote rollback pertama | | |
| 28 | + | | Detail **BOD approvals** | daftar nama BOD + decision (`APPROVE`/`REVISE`/`ROLLBACK`) + reason + `rollbackTargetName` = "Testing 2" + `respondedAt` | | |
| 29 | + | | `rollbackOptions` | terkunci ke **1 pilihan**: "Testing 2" | | |
| 30 | + | | Progress milik Testing (leaf) | **TIDAK diubah** — tetap `APPROVED` 100% (nilai/approvedBy/approvedAt utuh). Flip baru terjadi saat "Testing 2" ditutup lagi (tahap T2—T3) | | |
| 31 | + | | Tombol aksi | **disabled** (on hold) sampai "Testing 2" selesai lagi | | |
| 32 | + | ||
| 33 | + | ### Item yang di-rollback — **"Testing"** (item B) — setelah "Testing 2" di-approve & closed | |
| 34 | + | ||
| 35 | + | | Bentuk B | Nilai / tampilan | | |
| 36 | + | |---|---| | |
| 37 | + | | 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` | | |
| 38 | + | | B non-leaf | root: `WAITING_APPROVAL` + unpause; leaf rantai-awal (tanpa prereq / prereq rantai awal): progress terakhir `APPROVED` → **`PENDING`** (Need Approval) + notif ke Owner/Admin; leaf yang masih menunggu prereq: tetap **NOT_STARTED** abu-abu; blocker subtree: tetap pause | | |
| 39 | + | ||
| 40 | + | ### Target rollback — **"Testing 2"** (item X) | |
| 41 | + | ||
| 42 | + | | Elemen UI | Nilai / tampilan | | |
| 43 | + | |---|---| | |
| 44 | + | | Chip status item | **Need Approval** (karena progress terakhirnya `PENDING`) — *atau* **NOT_STARTED** jika "Testing 2" sendiri masih menunggu prerequisite-nya | | |
| 45 | + | | `approvalStatus` | **Waiting Approval** | | |
| 46 | + | | `bodResult` | `null` (bukan item yang di-rollback) | | |
| 47 | + | | Progress terakhir (leaf) | `APPROVED` → **`PENDING`** | | |
| 48 | + | | Progress perantara | tidak berubah | | |
| 49 | + | ||
| 50 | + | ### Anak-anak "Testing 2" (X) — semua level, live (restart penuh saat resolve) | |
| 51 | + | ||
| 52 | + | | Kondisi anak | Tampilan | | |
| 53 | + | |---|---| | |
| 54 | + | | Leaf (tidak punya anak) & tidak memblokir | chip **Need Approval**, progress terakhir `PENDING`, `approvalStatus` **Waiting Approval** | | |
| 55 | + | | Non-leaf & tidak memblokir | chip **Need Approval** (atau normal sesuai derived), `approvalStatus` **Waiting Approval**, progress tetap | | |
| 56 | + | | Dipakai sebagai prerequisite oleh sibling live lain (blocker) | seluruh subtree tampil **NOT_STARTED** + `rollbackPaused = true`; tombol write **disable**; baru terbuka saat prereq pemicunya selesai | | |
| 57 | + | ||
| 58 | + | ### Anak-anak "Testing" (B) — saat resolve | |
| 59 | + | ||
| 60 | + | Seluruh subtree B: **NOT_STARTED** abu-abu (`rollbackPaused = true`), `approvalStatus = WAITING_APPROVAL`, progress tetap 100%, tombol write **disabled**. Restart baru terjadi saat "Testing 2" ditutup lagi — lalu mengikuti aturan anak di atas (leaf rantai-awal → Need Approval + flip; punya prereq → NOT_STARTED lanjut). | |
| 61 | + | ||
| 62 | + | > Aturan display yang dipakai backend (bisa dipakai juga untuk highlight): | |
| 63 | + | > - `bodResult === 'REJECTED_ROLLBACK'` **atau** `rollbackPaused === true` → item tampil **NOT_STARTED** (override). | |
| 64 | + | > - `bodResult === 'REJECTED_REVISE'` → tampil "Rejected by BOD" (`bodDisplayStatus = REJECTED_BY_BOD`). | |
| 65 | + | > - `approvalStatus === 'BOD_APPROVAL'` → tampil "BOD Approval". | |
| 66 | + | ||
| 67 | + | --- | |
| 68 | + | ||
| 69 | + | ## 3. Konsumen downstream — item lain yang prereq-nya = "Testing 2" | |
| 70 | + | ||
| 71 | + | Ini bagian yang paling sering mengejutkan: **rollback tidak hanya menyentuh Testing & Testing 2**. Semua item same-level yang *prerequisite langsungnya* "Testing 2" ikut terkunci. | |
| 72 | + | ||
| 73 | + | | Kondisi konsumen | Tampilan saat rollback | Saat "Testing 2" selesai lagi | | |
| 74 | + | |---|---|---| | |
| 75 | + | | Sudah **CLOSED** (approved) | tampil **NOT_STARTED** (`rollbackPaused = true`); data & progress **tetap utuh**; semua aksi **disabled** | `approvalStatus` → **Waiting Approval** (progress tidak di-flip; VP tinggal approve ulang) | | |
| 76 | + | | Belum **CLOSED** | tampil **NOT_STARTED**; dikunci | mengikuti alur prereq-nya; lanjut dari status terakhir | | |
| 77 | + | | Anak-anak konsumen (subtree) | ikut **NOT_STARTED** | release berurutan: anak baru aktif setelah konsumennya ditutup lagi | | |
| 78 | + | ||
| 79 | + | **Item B (yang di-rollback) tidak masuk kategori konsumen** — B di-hold dengan `rollbackPaused = true` + `bodResult = REJECTED_ROLLBACK` saat resolve, lalu subtree-nya di-restart (leaf rantai-awal di-flip) ketika "Testing 2" ditutup lagi. | |
| 80 | + | ||
| 81 | + | Alur release (rantai `Testing 2 → A → P → Q` + subtree B): | |
| 82 | + | ||
| 83 | + | ``` | |
| 84 | + | "Testing 2" closed lagi | |
| 85 | + | ├─► A → Waiting Approval ← mulainya di sini (+ flip leaf rantai-awal subtree B) | |
| 86 | + | ├─► B leaf → Waiting Approval (Completed & Waiting; tidak di-flip; flip bila VP REJECT) | |
| 87 | + | └─► B non-leaf → anak leaf rantai-awal → Need Approval (progress PENDING) | |
| 88 | + | A di-approve & closed | |
| 89 | + | └─► P → Waiting Approval | |
| 90 | + | P di-approve & closed | |
| 91 | + | └─► Q → Waiting Approval (dan seterusnya) | |
| 92 | + | ``` | |
| 93 | + | ||
| 94 | + | Timing-nya **bertahap**, bukan serentak. Notifikasi saat release mengikuti routing baku: item yang jadi **Need Approval** (leaf yang di-flip) → notif ke **Owner/Admin**; item yang jadi **Completed & Waiting Approval** → notif ke **VP**-nya. | |
| 95 | + | ||
| 96 | + | ### Read-only / disabled saat `rollbackPaused = true` | |
| 97 | + | ||
| 98 | + | 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: | |
| 99 | + | ||
| 100 | + | - edit data item (PATCH) | |
| 101 | + | - approval respond, re-request, BOD respond | |
| 102 | + | - submit / respond progress | |
| 103 | + | - simpan plans (PUT) | |
| 104 | + | - tambah/hapus member | |
| 105 | + | ||
| 106 | + | --- | |
| 107 | + | ||
| 108 | + | ## 4. Urutan tampilan di layar dari waktu ke waktu | |
| 109 | + | ||
| 110 | + | | Waktu | Yang tampil | | |
| 111 | + | |---|---| | |
| 112 | + | | **T0 — BOD vote masuk** | List detail "Testing": badge BOD Approval; tiap BOD yang sudah vote tampil di `bodApprovals` (beri indikator sudah/blm vote) | | |
| 113 | + | | **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`** (belum di-flip), semua aksi disabled. Konsumen jadi **NOT_STARTED** abu-abu; notifikasi *Rolled Back by BOD* ke VP; log aktivitas terisi per item | | |
| 114 | + | | **T2 — "Testing 2" di-approve lagi** | "Testing 2" → CLOSED; konsumen A → **Waiting Approval**; subtree B mulai di-restart — leaf rantai-awal B → **Need Approval** (progress terakhir `PENDING`), akar B leaf → **Waiting Approval** tanpa flip (notif *Ready for Approval*) | | |
| 115 | + | | **T3 — A di-approve** | A → CLOSED; P → **Waiting Approval**; dst | | |
| 116 | + | | **T4 — "Testing" di-approve ulang** | "Testing" → CLOSED (handover normal); selesai | | |
| 117 | + | ||
| 118 | + | Frontend boleh menampilkan status ini apa adanya; urutan cascade dijamin backend (data + log tersedia per item). | |
| 119 | + | ||
| 120 | + | --- | |
| 121 | + | ||
| 122 | + | ## 5. Contoh payload detail item | |
| 123 | + | ||
| 124 | + | `GET /api/v1/projects/:projectId/action-plans/:activityId` → `data` (potongan): | |
| 125 | + | ||
| 126 | + | ```json | |
| 127 | + | { | |
| 128 | + | "id": "activity-testing-b", | |
| 129 | + | "name": "Testing", | |
| 130 | + | "approvalStatus": "WAITING_APPROVAL", | |
| 131 | + | "approvalReason": null, | |
| 132 | + | "bodRoundId": "round-1", | |
| 133 | + | "bodResult": "REJECTED_ROLLBACK", | |
| 134 | + | "bodReason": "hasil pengujian tidak sesuai", | |
| 135 | + | "bodDisplayStatus": "NOT_STARTED", | |
| 136 | + | "rollbackPaused": true, | |
| 137 | + | "approverId": "user-vp-1", | |
| 138 | + | "approverName": "Fajar", | |
| 139 | + | "bodApprovals": [ | |
| 140 | + | { | |
| 141 | + | "userId": "bod-1", | |
| 142 | + | "name": "Hendra", | |
| 143 | + | "decision": "ROLLBACK", | |
| 144 | + | "reason": "hasil pengujian tidak sesuai", | |
| 145 | + | "rollbackItemId": "activity-testing2-x", | |
| 146 | + | "rollbackTargetName": "Testing 2", | |
| 147 | + | "respondedAt": "2026-09-10T03:00:00.000Z" | |
| 148 | + | } | |
| 149 | + | ], | |
| 150 | + | "rollbackOptions": [{ "id": "activity-testing2-x", "name": "Testing 2" }] | |
| 151 | + | } | |
| 152 | + | ``` | |
| 153 | + | ||
| 154 | + | Item konsumen (A) yang sedang di-hold: | |
| 155 | + | ||
| 156 | + | ```json | |
| 157 | + | { | |
| 158 | + | "id": "activity-a", | |
| 159 | + | "name": "A", | |
| 160 | + | "approvalStatus": "CLOSED", | |
| 161 | + | "bodResult": null, | |
| 162 | + | "bodDisplayStatus": "NOT_STARTED", | |
| 163 | + | "rollbackPaused": true | |
| 164 | + | } | |
| 165 | + | ``` | |
| 166 | + | ||
| 167 | + | Baris progress (leaf "Testing" — item B — **setelah rollback resolve**; belum di-flip, baru di-flip saat "Testing 2" closed): | |
| 168 | + | ||
| 169 | + | ```json | |
| 170 | + | { | |
| 171 | + | "id": "progress-1", | |
| 172 | + | "progress": 100, | |
| 173 | + | "status": "APPROVED", | |
| 174 | + | "approvedBy": "user-vp-1", | |
| 175 | + | "approvedAt": "2026-09-09T09:00:00.000Z", | |
| 176 | + | "description": "…" | |
| 177 | + | } | |
| 178 | + | ``` | |
| 179 | + | ||
| 180 | + | Baris progress (leaf "Testing 2" — item X — setelah rollback resolve; langsung di-flip): | |
| 181 | + | ||
| 182 | + | ```json | |
| 183 | + | { | |
| 184 | + | "id": "progress-2", | |
| 185 | + | "progress": 100, | |
| 186 | + | "status": "PENDING", | |
| 187 | + | "approvedBy": null, | |
| 188 | + | "approvedAt": null, | |
| 189 | + | "description": "…" | |
| 190 | + | } | |
| 191 | + | ``` | |
| 192 | + | ||
| 193 | + | --- | |
| 194 | + | ||
| 195 | + | ## 6. Mapping singkat field → tampilan | |
| 196 | + | ||
| 197 | + | | Field | Tampilan frontend | | |
| 198 | + | |---|---| | |
| 199 | + | | `status` (derived) | chip utama item | | |
| 200 | + | | `bodDisplayStatus = NOT_STARTED` | tampil NOT_STARTED walau `approvalStatus` lain | | |
| 201 | + | | `bodDisplayStatus = REJECTED_BY_BOD` | chip/notice "Rejected by BOD" | | |
| 202 | + | | `bodDisplayStatus = BOD_APPROVAL` | badge "BOD Approval" (menunggu vote) | | |
| 203 | + | | `approvalStatus = WAITING_APPROVAL` | aksi approve VP aktif | | |
| 204 | + | | `rollbackPaused = true` | abu-abu + disable semua aksi + helper "on hold" | | |
| 205 | + | | progress row `status = PENDING` | badge PENDING di list progress | | |
| 206 | + | | `bodApprovals[].rollbackTargetName` | label target rollback di detail | | |
| 207 | + | ||
| 208 | + | --- | |
| 209 | + | ||