Handover FE — Transmittal Project Settings
Dua setting baru di level project (Projects.settings) yang memengaruhi perilaku transmittal:
- Blokir download form transmittal selama transmittal masih dalam proses review.
- Reminder deadline (dalam hari) ke checker/approver yang belum merespons, dikirim email oleh cron 08:00 WIB.
Backend sudah jalan di staging. FE perlu menambah 2 kontrol di form pengaturan project dan menangani satu error baru saat download.
1. Endpoint yang dipakai
| Kebutuhan | Endpoint | Permission |
|---|---|---|
| Baca settings | GET /api/projects/:id |
member project |
| Ubah settings | PATCH /api/projects/:projectId |
base.project.update (owner/admin) |
| Request download form transmittal | POST /api/projects/:projectId/reports/transmittal-form/:transmittalId/request-download |
transmittal.download.create |
| Download form PDF langsung | GET /api/projects/:projectId/reports/transmittal-form/:transmittalId/download |
transmittal.report.read |
| Lampiran transmittal (byte statis) | {transmittal}/public/transmittals/... (URL yang sudah dipakai sekarang) |
tidak ada guard terpisah |
Tidak ada endpoint baru. Settings tetap dikirim lewat PATCH project yang sudah ada.
Versi API dikirim lewat header (custom versioning), bukan di path. Pakai base URL + header yang sudah dipakai web saat ini.
2. Bentuk data di settings
Settings adalah satu kolom JSON, bukan kolom per-setting. Grup baru: transmittal_settings.
{
"transmittal_settings": {
"disable_download_when_open": true,
"deadline_reminder_days": 3
}
}
Aturan:
disable_download_when_open—boolean, defaultfalse.deadline_reminder_days—integer 0..365, default0(= mati).nullditerima, artinya unset.- Dua-duanya opsional. Grup
transmittal_settingsopsional. - Key lain di dalam
settingswajib dipertahankan. Backend memakaicatchall, jadi key yang tidak dikenal tidak ditolak dan tidak dibuang.
Bentuk data produksi (penting)
Row lama menyimpan nilai yang sama dua kali: di dalam grup dan di top-level. Contoh nyata:
{
"approver_revoke_time": 2,
"approver_revoke_time_unit": "days",
"transmittal_settings": { "approver_revoke_time": 2, "approver_revoke_time_unit": "days" }
}
Backend membaca nested dulu, lalu fallback ke key dengan nama sama di top-level. Jadi penulisan baru cukup ke dalam transmittal_settings. Duplikat flat lama boleh dibiarkan.
3. Jebakan: PATCH mengganti seluruh kolom settings
settings adalah JSON column dan di-update replace, bukan merge. Kalau FE mengirim:
{ "settings": { "transmittal_settings": { "disable_download_when_open": true } } }
maka seluruh key lain (basis, category, milestone_id, partner_company_id, closing_approver_id, priority, approver_revoke_time, dst.) hilang.
Kontrak untuk FE:
- Ambil
settingsdariGET /projects/:id. - Gabungkan perubahan secara lokal:
settings.transmittal_settings = { ...settings.transmittal_settings, ...perubahan }. - Kirim objek
settingshasil gabungan secara utuh diPATCH. - Jangan mengirim
transmittal_settingssaja.
Contoh payload benar:
{
"settings": {
"basis": "Berdasarkan arahan PT Danantara Asset Management",
"category": "CONSULTANT",
"priority": "MEDIUM",
"milestone_id": "bf083b10-53f8-492c-bc02-c731608d7a62",
"partner_company_id": "12219e63-8762-4a7b-a8f4-70cb9705ffd8",
"closing_approver_id": "33b51ecb-62c2-4ef7-af2a-fc55a00e75bb",
"basis_of_implementation": "Berdasarkan arahan PT Danantara Asset Management",
"transmittal_settings": {
"disable_download_when_open": true,
"deadline_reminder_days": 3
}
}
}
4. Setting A — disable_download_when_open
Label yang disarankan: "Blokir download transmittal selama masih diproses"
Tipe kontrol: toggle/switch
Tersedia di: pengaturan project, hanya untuk owner/admin (base.project.update)
Perilaku backend saat true — berlaku untuk form transmittal dan dokumen yang terikat padanya:
- Diblokir hanya saat transmittal berstatus
OPEN,IN_CHECKING,IN_APPROVAL("masih dalam review"). - Semua status lain tetap bisa diunduh:
DRAFT,SCHEDULED,SENT,REJECTED,REJECTED_WITH_NOTES,REJECTED_RESUBMISSION,APPROVED,APPROVED_WITH_NOTES,ARCHIVE. - Transmittal yang sudah closed (
is_closed/effective_is_closed) tetap bisa diunduh, apa pun statusnya. - Status efektif menang atas status dasar (
effective_status/effective_is_closed), sama seperti logika status lain di aplikasi.
Permukaan download yang ikut diblokir (hanya aksi yang punya tombol download khusus):
| Permukaan | Endpoint |
|---|---|
| Minta download form (antrean) | POST /api/projects/:projectId/reports/transmittal-form/:transmittalId/request-download |
| Download form PDF langsung | GET /api/projects/:projectId/reports/transmittal-form/:transmittalId/download |
| Ambil hasil antrean form | GET /api/projects/:projectId/document-downloads/:downloadId/file (hanya bila record_resource = transmittal) |
| Download dokumen beranotasi (langsung) | POST /api/export/room/:roomId/download |
| Minta download dokumen beranotasi (antrean) | POST /api/export/room/:roomId/request-download |
| Ambil hasil antrean dokumen | GET /api/projects/:projectId/document-downloads/:downloadId/file (annotation, record_resource = room) |
| Unduh ulang hasil export | GET /api/export/record/:exportId/download |
| Byte file dokumen asli (mpp/autodesk, open-in-new-tab) | GET {document}/uploads/projects/{projectId}/documents/{documentId}/{file} |
Gate hanya aktif untuk request yang membawa transmittal_id (lihat bagian berikut). Unduhan dokumen yang sama dari Drive tidak berubah.
Transmittal yang dievaluasi = transmittal yang id-nya dikirim FE. Kalau satu revisi dipakai di beberapa transmittal, transmittal lain tidak memengaruhi hasil.
Wajib dari FE: kirim transmittal_id
Drive dan transmittal memakai URL/endpoint yang sama, jadi FE harus mengirim id transmittal yang sedang dibuka. Nilainya sudah tersedia di preview: document.transmittal.id (preview-document.types.ts:58-62). Tanpa parameter itu, backend mengizinkan (perilaku lama).
Tambahkan ?transmittal_id=<id> pada:
POST /api/export/room/:roomId/download— download dokumen beranotasi (langsung).POST /api/export/room/:roomId/request-download— minta download beranotasi (antrean).GET /api/export/record/:exportId/download— unduh ulang hasil export.GET /api/projects/:projectId/document-downloads/:downloadId/file— ambil hasil antrean, hanya untuk unduhan yang dimulai dari layar transmittal.GET {document}/uploads/projects/...— tombol download mpp/autodesk dan open-in-new-tab dari preview transmittal.
Endpoint form transmittal (request-download, transmittal-form/:transmittalId/download, dan file antreannya) tidak perlu parameter tambahan: route itu memang hanya dipakai transmittal.
Tidak terpengaruh (memang begitu desainnya):
- Preview lampiran approval/komentar (
ApprovalPanel,PreviewCommentsPanel,ClosingTransmitter, halaman inbox) — semuanyawindow.openke file statis, bukan tombol download. Klik kanan → Save as tetap bisa; itu diterima. - File tanda tangan.
- Artefak turunan dokumen: halaman hasil ekstraksi dan preview (
.../{timestamp}_{hash}/...), jadi viewer tetap jalan. - Export laporan/report transmittal (
/reports/transmittal-report/export). - Dokumen di luar transmittal dan aset project lain.
Error baru yang harus ditangani
Endpoint API mengembalikan 409:
HTTP 409
{
"statusCode": 409,
"error": "TRANSMITTAL_DOWNLOAD_LOCKED",
"message": "Download is disabled while this transmittal is still open."
}
Byte file statis (baris terakhir tabel) mengembalikan 403 dengan isi yang sama:
HTTP 403
{
"statusCode": 403,
"error": "TRANSMITTAL_DOWNLOAD_LOCKED",
"message": "Download is disabled while this transmittal is still open."
}
Penanganan FE:
- Branch pada
error === 'TRANSMITTAL_DOWNLOAD_LOCKED', jangan cek status code saja — 409 untuk endpoint API, 403 untuk byte file statis. - Tampilkan pesan yang jelas, bukan toast generik "gagal download". Sarankan: "Download dinonaktifkan selama transmittal masih diproses. Hubungi admin project untuk mengubah pengaturan."
- Jangan retry otomatis. Ini keputusan konfigurasi, bukan error sementara.
- Unduhan mpp/autodesk memakai
fetchlangsung ke URL statis; cekresponse.okdan baca JSON error di atas sebelum menampilkan toast. - "Open in new tab" untuk file dokumen ikut 403 (URL sama dengan tombol download). Ini disengaja.
- Preview lampiran tidak berubah, jadi tidak perlu penanganan
onerrorbaru.
Catatan desain: gate ini bukan batas keamanan, hanya penguncian kenyamanan. Kalau settings atau RPC antar-service gagal, backend memilih mengizinkan download (fail-open). Parameter transmittal_id juga bisa dihilangkan atau diganti dengan id transmittal lain; gate sengaja opt-in supaya Drive tidak ikut tersentuh. Jangan andalkan ini untuk kontrol akses. (/public dan /uploads memang belum punya auth — pekerjaan terpisah.)
Latensi efek
Tidak ada cache. Setiap download yang di-gate membaca status transmittal dan setting project saat itu juga, jadi toggle dan penutupan transmittal langsung berlaku pada request berikutnya. Biayanya satu lookup transmittal + satu RPC settings per klik download — aksi manusia, bukan trafik panas.