commit
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# Training Record Attachment UI Refactor Report
|
||||
|
||||
## Scope
|
||||
|
||||
- Refactor attachment UI in training record create, edit, and review flows
|
||||
- Remove duplicate upload area from the attachment list section
|
||||
- Keep existing API, permissions, validation, and workflow behavior unchanged
|
||||
|
||||
## Files Changed
|
||||
|
||||
- `src/components/file-uploader.tsx`
|
||||
- `src/features/training-records/components/training-record-attachment-list.tsx`
|
||||
- `src/features/training-records/components/training-record-certificate-manager.tsx`
|
||||
- `src/features/training-records/components/training-record-form.tsx`
|
||||
|
||||
## Before vs After
|
||||
|
||||
- Before: form page showed one upload area in the form and another upload-capable attachment manager after save
|
||||
- Before: attachment section title used wording that looked like another upload area
|
||||
- After: upload UI appears only in editable create/draft/revision flows
|
||||
- After: attachment section is a compact read-focused list titled `ไฟล์ที่แนบ`
|
||||
- After: empty state is reduced to a simple inline message with no oversized placeholder card
|
||||
|
||||
## Refactoring Summary
|
||||
|
||||
- Added a shared `TrainingRecordAttachmentList` component for compact attachment display
|
||||
- Reused the shared list in the review page through `TrainingRecordCertificateManager`
|
||||
- Extended `FileUploader` with an optional `showFileList` flag so the dropzone can stay upload-only where needed
|
||||
|
||||
## Functional Verification
|
||||
|
||||
- Create: upload area visible, attachment list visible, pending files shown in list, no duplicate dropzone
|
||||
- Edit Draft / Needs Revision: upload area visible, existing files downloadable, deletable, and new pending files shown in the same list
|
||||
- Waiting / Approved / Rejected view: attachment list remains readable and download-only, no upload dropzone shown
|
||||
|
||||
## Responsive Testing
|
||||
|
||||
- Layout uses stacked action buttons on small screens and inline actions on larger screens
|
||||
- Attachment rows wrap long filenames and avoid horizontal overflow
|
||||
|
||||
## Regression Testing
|
||||
|
||||
- Business logic for upload, delete, and permission checks remains in existing mutations and route handlers
|
||||
- Submit workflow changes from the previous task are preserved
|
||||
|
||||
## Risks
|
||||
|
||||
- Pending local files are labeled `รอบันทึก` until the record save/upload sequence completes
|
||||
- Download action still opens the existing file URL in a new tab, matching current behavior
|
||||
|
||||
## Recommendations
|
||||
|
||||
- If users want a stronger distinction between “pending local files” and “saved attachments”, we can add a small badge style later without touching the workflow
|
||||
135
docs/reports/training-record-submit-workflow-review.md
Normal file
135
docs/reports/training-record-submit-workflow-review.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Training Record Submit Workflow Review
|
||||
|
||||
## Executive Summary
|
||||
|
||||
ปัญหาหลักเกิดจาก submit flow ฝั่งฟอร์มเรียกเปลี่ยนสถานะรายการเป็น `pending` ก่อนอัปโหลด certificate เสร็จในบางเส้นทาง โดย backend อนุญาตให้อัปโหลดได้เฉพาะตอนสถานะ `draft` หรือ `needs_revision` เท่านั้น จึงเกิด error ระหว่าง submit
|
||||
|
||||
## Current Workflow
|
||||
|
||||
ก่อนแก้ไข โฟลว์หลักใน `training-record-form.tsx` เป็นดังนี้
|
||||
|
||||
- Edit Draft Submit:
|
||||
`PATCH training record (submit)` -> `POST certificates`
|
||||
- Create Submit:
|
||||
`POST training record (submit)` -> `POST certificates`
|
||||
|
||||
ผลคือเมื่อ record ถูกเปลี่ยนเป็น `pending` แล้ว route upload จะปฏิเสธทันที
|
||||
|
||||
## Expected Workflow
|
||||
|
||||
โฟลว์ที่ต้องการและที่แก้แล้วคือ
|
||||
|
||||
- Submit:
|
||||
`validate` -> `save editable data as draft` -> `upload certificates` -> `update status to pending` -> `redirect`
|
||||
- Save Draft:
|
||||
`save draft` -> `upload certificates` -> `redirect`
|
||||
|
||||
## Execution Timeline
|
||||
|
||||
### Edit + Submit
|
||||
|
||||
1. validate form
|
||||
2. `PATCH /api/training-records/[id]` ด้วย `submissionMode=draft`
|
||||
3. `POST /api/training-records/[id]/certificates`
|
||||
4. `PATCH /api/training-records/[id]` ด้วย `submissionMode=submit`
|
||||
5. invalidate query จาก mutations
|
||||
6. `router.push('/dashboard/training-records')`
|
||||
|
||||
### Create + Submit
|
||||
|
||||
1. validate form
|
||||
2. `POST /api/training-records` ด้วย `submissionMode=draft`
|
||||
3. `POST /api/training-records/[id]/certificates`
|
||||
4. `PATCH /api/training-records/[id]` ด้วย `submissionMode=submit`
|
||||
5. invalidate query จาก mutations
|
||||
6. redirect
|
||||
|
||||
## API Call Sequence
|
||||
|
||||
- `POST /api/training-records`
|
||||
- `PATCH /api/training-records/[id]`
|
||||
- `POST /api/training-records/[id]/certificates`
|
||||
- `DELETE /api/training-records/[id]/certificates/[certificateId]`
|
||||
|
||||
ลำดับใหม่ยืนยันว่า upload เกิดก่อน status transition ไป `pending`
|
||||
|
||||
## React Query Flow
|
||||
|
||||
- ใช้ `createTrainingRecordMutation`
|
||||
- ใช้ `updateTrainingRecordMutation`
|
||||
- ใช้ `uploadCertificatesMutation`
|
||||
- แต่ละ mutation invalidate query ตาม key เดิมของ feature
|
||||
- ไม่มี `Promise.all` หรือ upload background
|
||||
|
||||
## Async Analysis
|
||||
|
||||
- flow ใช้ `await` แบบลำดับตรง
|
||||
- ไม่มี parallel mutation ที่เสี่ยงชนกัน
|
||||
- submit จะไม่ไปขั้นเปลี่ยนสถานะถ้า upload ล้มเหลว
|
||||
|
||||
## Race Condition Analysis
|
||||
|
||||
ก่อนแก้มี race เชิงลำดับธุรกิจ ไม่ใช่ race จาก concurrency จริง:
|
||||
|
||||
- route update เปลี่ยน status เป็น `pending`
|
||||
- route upload ถูกเรียกทีหลัง
|
||||
- backend reject เพราะ record ไม่อยู่ในสถานะ editable แล้ว
|
||||
|
||||
หลังแก้ upload เกิดก่อน status update จึงปิดช่องนี้
|
||||
|
||||
## Upload Component Review
|
||||
|
||||
- ฟอร์มหลักใช้ `FileUploader` เก็บไฟล์ไว้ใน `pendingFiles`
|
||||
- upload จริงเกิดใน `onSubmit`
|
||||
- `TrainingRecordCertificateManager` ใช้สำหรับจัดการไฟล์ของรายการที่บันทึกแล้ว
|
||||
- ไม่พบ auto-upload บน mount
|
||||
|
||||
## Root Cause
|
||||
|
||||
root cause คือ submit flow ใน [training-record-form.tsx](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/training-records/components/training-record-form.tsx) ส่ง record ไปสถานะ `pending` ก่อนเรียก route upload ในบางเส้นทาง ขณะที่ business rule ของ route upload ใน [route.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/training-records/[id]/certificates/route.ts) อนุญาตเฉพาะ `draft` หรือ `needs_revision`
|
||||
|
||||
หลักฐานจาก source:
|
||||
|
||||
- ฟอร์มเดิมเรียก create/update ด้วย `submissionMode=submit` ก่อน upload
|
||||
- route upload ตรวจ `canEmployeeManageEditableRecord(...)`
|
||||
- helper ฝั่ง server อนุญาตเฉพาะสถานะ editable
|
||||
|
||||
## Files Modified
|
||||
|
||||
- `src/features/training-records/components/training-record-form.tsx`
|
||||
ปรับลำดับ submit ให้ save-as-draft ก่อน upload แล้วค่อย final submit
|
||||
- `src/features/training-records/server/training-record-data.ts`
|
||||
ปรับ shared editable guard ให้เช็กสิทธิ์จาก employee mapping ได้ครบขึ้น
|
||||
- `src/app/api/training-records/[id]/route.ts`
|
||||
ใช้ shared editable guard แบบใหม่และอัปเดตข้อความ error
|
||||
- `src/app/api/training-records/[id]/certificates/route.ts`
|
||||
ใช้ shared editable guard แบบใหม่และอัปเดตข้อความ error
|
||||
- `src/app/api/training-records/[id]/certificates/[certificateId]/route.ts`
|
||||
ใช้ shared editable guard แบบใหม่และอัปเดตข้อความ error
|
||||
- `src/features/training-records/components/training-record-certificate-manager.tsx`
|
||||
ปรับ helper text ให้ตรงกับสถานะ editable จริง
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
- Employee:
|
||||
ผลดีคือ submit draft/revision พร้อม upload ทำงานสอดคล้องกับ backend มากขึ้น
|
||||
- HRD:
|
||||
pending queue จะเห็นเฉพาะรายการที่ผ่าน upload flow แล้ว
|
||||
- Admin:
|
||||
ไม่มีการเปลี่ยน schema หรือ permission model
|
||||
|
||||
## Regression Test Result
|
||||
|
||||
ตรวจเช็กเชิงโค้ดและ lint แล้ว
|
||||
|
||||
- editable guard ถูกใช้กับ route แก้ไข/อัปโหลด/ลบ
|
||||
- submit flow ในฟอร์มถูกย้ายเป็น upload ก่อน status update
|
||||
- `npx oxlint` ผ่านสำหรับไฟล์ที่แก้
|
||||
|
||||
ยังไม่ได้รัน E2E/manual test ครบทุก case ในเอกสาร
|
||||
|
||||
## Remaining Issues
|
||||
|
||||
- ปุ่ม submit เดิมในฟอร์มยังถูกซ่อนไว้ ควรลบออกในรอบ cleanup
|
||||
- success message บางจุดยังไม่แยก wording ระหว่าง draft กับ submit แบบครบทุกสถานะ
|
||||
- ยังไม่ได้ทำ manual regression ครบทั้ง 10 case ตาม checklist
|
||||
Reference in New Issue
Block a user