Last active 4 days ago

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 +
Newer Older