135dika / HANDOVER_FE_PROGRESS_EDIT.md

Last active 1 week ago

Like 0
HANDOVER_FE_PROGRESS_EDIT.md Raw

Handover FE — Edit Progress Action Plan

Backend modules_base, feature/version/1/action-plans. Dokumen ini untuk tim frontend. Semua perubahan backend sudah selesai dan teruji; tidak ada yang perlu ditunggu.


1. Yang berubah, singkat

Dulu progress yang salah harus dihapus lalu submit ulang. Sekarang ada satu endpoint revisi yang bisa mengubah nilai, lampiran, dan (untuk admin) status.

Ada dua form berbeda di satu endpoint, dibedakan oleh status entry:

Siapa Kapan Form Kuota Tercatat
Member (submitter) entry PENDING nilai + lampiran tidak ada tidak
OWNER / ADMIN entry sudah direspons nilai + lampiran + status 3x ya

Poin penting untuk FE:

  • Tombol edit hanya untuk submission terakhir. Field canEdit sudah dihitung server.
  • Member boleh revisi berkali-kali tanpa isi alasan.
  • Admin wajib isi alasan; satu submit = satu unit dari kuota 3x, walaupun nilainya banyak.
  • Selalu kirim multipart/form-data, walaupun tidak ada file.

2. Endpoint

Base: /v1/projects/:projectId/action-plans

2.1 Revisi (satu endpoint, tiga varian path)

PATCH :activityId/progress/:progressId
PATCH :activityId/tasks/:taskId/progress/:progressId
PATCH :activityId/tasks/:taskId/subtasks/:subtaskId/progress/:progressId

Content-Type: multipart/form-data

2.2 Riwayat revisi

GET :activityId/progress/:progressId/edits
GET :activityId/tasks/:taskId/progress/:progressId/edits
GET :activityId/tasks/:taskId/subtasks/:subtaskId/progress/:progressId/edits

Hanya berisi revisi admin. Koreksi member sebelum approval tidak pernah muncul di sini.


3. Field yang dikirim

Field Tipe Boleh dipakai Catatan
progress number 0–100 member & admin kirim workVolume saja → persentase dihitung server
workVolume number member & admin
description string ≤ 5000 member & admin
createdAt date member & admin boleh backdate, tidak boleh masa depan
attachments[] file member & admin bisa lebih dari satu, diulang per file
removeImageIds string | diulang member & admin pilih per id, bukan ganti semua
document file PDF member & admin ganti PDF dengan upload
documentSource upload | document member & admin sumber PDF
documentRef JSON string member & admin {"documentId","versionId","revisionId"}
companyId uuid member & admin wajib kalau documentSource=document
removeDocument 'true'/'false' member & admin string, bukan boolean
status enum admin saja APPROVED / APPROVED_WITH_NOTES / REJECTED
reason string ≤ 500 admin wajib, member opsional

Minimal satu aksi harus dikirim. Request kosong → 400 Nothing to update.

Tipe file yang diterima: image/jpeg, image/png, image/gif, image/webp, image/svg+xml, application/pdf. Selain itu → 400 File type not allowed: <mime>.

3.1 PDF: upload atau ambil dari modules_document

Payload-nya sama dengan form submit progress (POST .../progress). Kalau dokumen diambil dari modul dokumen, jangan kirim file — kirim referensinya:

form.append('documentSource', 'document');
form.append(
  'documentRef',
  JSON.stringify({ documentId, versionId, revisionId })
);
form.append('companyId', currentUser.companyId);

Server memvalidasi tiga hal: pemanggil memang anggota company itu di project ini, dokumen itu punya company, dan company-nya sama. Dokumen tanpa company ditolak (fail closed).

Kalau mau ganti PDF kembali ke upload biasa, kirim document dan jangan kirim documentSource/documentRef — server menimpa baris dokumen yang ada.


4. Contoh request

4.1 Member membetulkan persentase + salah foto

curl -X PATCH "$BASE/:activityId/progress/:progressId" \
  -H "Authorization: Bearer $TOKEN" \
  -F 'progress=55' \
  -F 'removeImageIds=img_123' \
  -F 'attachments=@sesudah.jpg'

Tanpa reason. Tidak dihitung kuota, tidak masuk riwayat.

4.2 Admin mengubah status + nilai + lampiran sekaligus

curl -X PATCH "$BASE/:activityId/progress/:progressId" \
  -H "Authorization: Bearer $TOKEN" \
  -F 'status=APPROVED_WITH_NOTES' \
  -F 'progress=60' \
  -F 'removeImageIds=img_456' \
  -F 'attachments=@revisi.jpg' \
  -F 'reason=koreksi hasil survey lapangan'

Satu request = satu unit kuota.

4.3 Admin mengubah status saja

curl -X PATCH "$BASE/:activityId/progress/:progressId" \
  -H "Authorization: Bearer $TOKEN" \
  -F 'status=REJECTED' \
  -F 'reason=salah input volume'

5. Alur UI yang diharapkan

5.1 Member

  1. Tampilkan tombol edit hanya bila entry.canEdit === true dan entry.status === 'PENDING'.
  2. Form: persentase/volume, deskripsi, tanggal, kelola gambar (pilih yang dihapus per thumbnail), ganti PDF.
  3. Tidak perlu input alasan, tidak perlu menampilkan sisa kuota.
  4. Setelah sukses, entry kembali PENDING dengan nilai baru — tidak ada baris riwayat baru.

5.2 Admin

  1. Tampilkan tombol edit bila entry.canEdit === true.
  2. Satu form: status, persentase/volume, deskripsi, tanggal, gambar, PDF, alasan (wajib).
  3. Tampilkan sisa kuota dari entry.editCount / entry.maxEdits ("Revisi 2 dari 3").
  4. Bila canEdit === false karena kuota habis, disable tombol dan jelaskan batasnya.
  5. Status yang boleh dipilih: APPROVED, APPROVED_WITH_NOTES, REJECTED — termasuk membatalkan penolakan (REJECTED → APPROVED).

5.3 Riwayat

Tampilkan GET .../edits di panel "Riwayat revisi": siapa, kapan, alasan, dan diff sebelum/sesudah dari oldData/newData (status, values, attachments).


6. Response

6.1 Field tambahan pada entry progress

{
  "id": "…",
  "status": "PENDING",
  "editCount": 1,
  "maxEdits": 3,
  "canEdit": true,
  "canEditAttachments": true
}
Field Arti
editCount Revisi admin yang sudah terpakai (koreksi member tidak dihitung)
maxEdits Batas revisi admin (3)
canEdit true hanya bila entry adalah submission terakhir dan (PENDING atau editCount < 3)
canEditAttachments Alias canEdit, dipertahankan untuk kompatibilitas

Catatan: canEdit sudah memperhitungkan "entry terakhir", jadi entry lama selalu false. Server tetap menolak edit pada entry lama sebagai lapis kedua.

6.2 Riwayat revisi

[
  {
    "id": "…",
    "revisionNo": 2,
    "kind": "MIXED",
    "oldData": {
      "status": "APPROVED",
      "values": { "progress": 40, "workVolume": null, "description": "lama", "createdAt": "2026-02-01T00:00:00.000Z" },
      "attachments": {
        "images": [{ "id": "…", "originalName": "a.jpg" }],
        "document": { "originalName": "laporan.pdf" }
      }
    },
    "newData": {
      "status": "APPROVED_WITH_NOTES",
      "values": { "progress": 55, "workVolume": null, "description": "baru", "createdAt": "2026-02-01T00:00:00.000Z" },
      "attachments": {
        "images": [{ "id": "…", "originalName": "b.jpg" }],
        "document": null
      }
    },
    "reason": "koreksi hasil survey lapangan",
    "editedBy": "uuid",
    "editedByName": "Budi",
    "editedAt": "2026-11-02T03:00:00.000Z"
  }
]

kind:

Nilai Arti
VALUES nilai berubah (dengan atau tanpa perubahan status)
STATUS hanya status
ATTACHMENTS hanya lampiran
MIXED dua kelompok atau lebih dalam satu request

7. Error yang akan ditemui

HTTP Message Kapan Saran di UI
400 Progress entry not found id tidak ada / bukan project ini tutup form
403 Only the submitter can edit the progress while it is pending admin buka form member sembunyikan form
403 Only an owner or admin can change the progress status member kirim status jangan kirim field itu
403 You do not have required role perform action member mencoba edit entry pasca-approval sembunyikan tombol
400 Progress can only be edited 3 times kuota admin habis tampilkan batas
400 reason is required when an owner or admin edits a progress admin tanpa alasan validasi di form
400 Nothing to update: provide values, attachments, status, or a removal submit kosong disable tombol submit
400 Only the latest progress entry can be edited entry sudah disusul submission baru refresh list
400 Progress must be >= current progress (X%). New: Y% nilai di bawah progress yang sudah disetujui tampilkan floor X
400 Progress must be between 0 and 100 angka di luar rentang clamp input
400 createdAt cannot be in the future tanggal > hari ini batasi datepicker
400 createdAt must be >= previous progress date (…) tanggal lebih awal dari entry sebelumnya tampilkan tanggal minimal
400 Activity is on hold: waiting for its prerequisite to be approved and closed node sedang hold (task/subtask juga bisa) disable form
400 companyId is required when attaching a document-source document documentSource=document tanpa companyId selalu kirim
400 Invalid documentRef: must be valid JSON documentRef bukan JSON stringify objek
400 documentRef must contain documentId, versionId, and revisionId field kurang cek selektor dokumen
400 Referenced document not found dokumen/revisi tidak ada muat ulang daftar dokumen
400 Referenced document has no company, so it cannot be verified dokumen tanpa company blokir pemilihan dokumen itu
403 Referenced document does not belong to your company dokumen milik company lain filter daftar dokumen per company
400 File type not allowed: <mime> tipe file tidak didukung batasi accept
400 Cannot add progress: previous progress is still pending approval submit baru saat masih ada PENDING nonaktifkan tombol tambah

8. Jebakan yang mudah terlewat

  1. Selalu multipart/form-data, walaupun hanya mengirim progress. Body JSON tidak dibaca interceptor upload.
  2. removeDocument dan documentSource bukan boolean — removeDocument harus string 'true'/'false'.
  3. removeImageIds per id. Kalau thumbnail dihapus semua, kirim semua id-nya, jangan kirim array kosong berharap server menghapus semua.
  4. Jangan kirim field yang tidak berubah. description yang dikirim ulang dengan nilai lama tetap dianggap perubahan nilai (dan untuk admin, memakan satu kuota).
  5. Kuota dihitung per request, bukan per field. Gabungkan semua koreksi dalam satu submit.
  6. Status berubah = approvedBy ditulis ulang ke admin yang menyimpan. Ini disengaja.
  7. canEdit tidak menggantikan role check. Untuk member, syarat tombol edit = canEdit && status === 'PENDING' && createdBy === currentUserId.
  8. Entry lama tidak bisa diedit. Kalau list menampilkan riwayat panjang, sembunyikan aksi edit pada semua entry kecuali canEdit === true.
  9. Koreksi member tidak tercatat. Jangan mengharapkan riwayat untuk entry PENDING; panel riwayat akan kosong atau berisi revisi admin sebelumnya.
  10. editCount bukan total revisi. Revisi admin pada baris yang sama bisa berlanjut setelah approval berikutnya me-reset kuota ke 0.

9. Migrasi & data lama

Dua migrasi Prisma harus sudah jalan di environment target (prisma migrate deploy):

20261102100000_v1_action_plan_progress_edits            → kolom editCount, soft-remove images, tabel revisi
20261102110000_v1_action_plan_progress_amended_created_by → backfill createdBy row amended

Tidak ada perubahan bentuk response lama selain field tambahan di §6.1. Field lama tetap ada.


10. Belum ada (jangan diharapkan)

  • Notifikasi saat progress/lampiran direvisi.
  • Riwayat revisi untuk koreksi member (memang tidak disimpan).
  • Endpoint bulk edit; revisi tetap satu per entry.

11. File backend terkait

File Isi
progress.service.ts updateProgress (semua aturan), listProgressEdits, latestEditableId, resolveDocumentRef
action-plans.controller.ts 3 route PATCH .../progress/:progressId, parseProgressEditBody
action-plans.dto.ts UpdateProgressSchema / UpdateProgressDto
.markdown/PROGRESS_EDIT.md Dokumen desain lengkap (termasuk aturan internal)