# Prompt: ตรวจสอบและแก้ไข Permission Logic สำหรับบทเรียนออนไลน์สถานะ Returned/Rejected ## บทบาท ให้คุณทำหน้าที่เป็น Senior Full-Stack Developer, Authorization Reviewer และ Code Auditor ตรวจสอบระบบสิทธิ์และ Workflow ของโมดูลบทเรียนออนไลน์ก่อนดำเนินการแก้ไข ต้องตรวจสอบ Source Code, Permission Definitions, Route Guards, Action Menu, Form Submission, API Authorization และ Workflow Transition ที่ระบบใช้อยู่จริง ห้ามคาดเดาชื่อสถานะ Permission Route หรือ Function ขึ้นมาเอง ให้ยืนยันจากโค้ดจริงก่อนทุกครั้ง --- # ปัญหาปัจจุบัน ในหน้าบทเรียนออนไลน์ของเจ้าหน้าที่ เมื่อบทเรียนถูกผู้ตรวจสอบส่งกลับมาแก้ไข รายการมีสถานะประเภท: ```text returned rejected revision_required changes_requested ``` หรือสถานะจริงที่ระบบใช้อยู่ เจ้าหน้าที่ซึ่งเป็นเจ้าของหรือผู้สร้างบทเรียนควรสามารถแก้ไขงานของตนเองและส่งตรวจสอบใหม่ได้ แต่ปัจจุบันระบบกลับตรวจสอบสิทธิ์ด้วย Permission: ```text online_lesson:revision ``` ทำให้เจ้าหน้าที่ทั่วไปที่ไม่มี Permission ดังกล่าว: - ไม่เห็นปุ่มแก้ไข - ไม่สามารถเข้าหน้าแก้ไข - หรือเปิดหน้าแก้ไขได้แต่ไม่สามารถบันทึก - ไม่สามารถส่งตรวจสอบใหม่ - ถูกปฏิเสธจาก Frontend หรือ Backend Authorization จากการวิเคราะห์เบื้องต้น ปัญหาไม่น่าเกิดจากตัว Form โดยตรง แต่เกิดจากการตีความ Permission ผิดหน้าที่ --- # หลักการ Permission ที่ถูกต้อง ให้แยกความหมายของ Permission อย่างชัดเจนดังนี้ ## `online_lesson:revision` Permission นี้ควรเป็นสิทธิ์ของฝั่งผู้ตรวจสอบหรือผู้อนุมัติ สำหรับการดำเนินการ: ```text ส่งบทเรียนกลับให้เจ้าหน้าที่แก้ไข ``` ตัวอย่าง Action: ```text pending_review → returned ``` หรือ Transition จริงของระบบ Permission นี้ไม่ควรถูกใช้เป็นเงื่อนไขหลักในการอนุญาตให้เจ้าของบทเรียนแก้ไขรายการที่ถูกส่งกลับ --- ## `online_lesson:edit_draft` Permission นี้ควรเป็นสิทธิ์ของฝั่งผู้สร้างหรือเจ้าหน้าที่ สำหรับการ: - แก้ไขบทเรียนฉบับร่างของตนเอง - แก้ไขบทเรียนของตนเองที่ถูกส่งกลับ - บันทึกการแก้ไข - ส่งตรวจสอบใหม่ ตัวอย่างสถานะที่ควรแก้ไขได้: ```text draft returned ``` หรือสถานะจริงของระบบ เช่น: ```text draft revision_required rejected ``` ต้องตรวจสอบ Workflow จริงก่อนกำหนดรายการสถานะ --- # เป้าหมาย ปรับ Authorization Logic ให้เจ้าของหรือผู้สร้างบทเรียนที่ถูกส่งกลับสามารถแก้ไขงานของตนเองได้ด้วย Permission ฝั่งผู้สร้าง เช่น: ```text online_lesson:edit_draft ``` โดยยังคงให้: ```text online_lesson:revision ``` เป็น Permission สำหรับผู้ตรวจสอบในการส่งบทเรียนกลับให้แก้ไขเท่านั้น หลังแก้ไขแล้วต้องได้พฤติกรรมดังนี้: 1. เจ้าหน้าที่ที่มี `online_lesson:edit_draft` สามารถแก้ไขบทเรียนของตนเองในสถานะ Draft 2. เจ้าหน้าที่ที่มี `online_lesson:edit_draft` สามารถแก้ไขบทเรียนของตนเองในสถานะ Returned 3. เจ้าหน้าที่ไม่จำเป็นต้องมี `online_lesson:revision` เพื่อแก้ไขงานที่ถูกส่งกลับ 4. ผู้ตรวจสอบยังต้องมี `online_lesson:revision` เพื่อกดส่งกลับ 5. ผู้ใช้งานต้องไม่สามารถแก้ไขบทเรียนของบุคคลอื่น เว้นแต่มี Permission ระดับจัดการที่ระบบกำหนดไว้ 6. บทเรียนสถานะ Pending, Approved, Published หรือสถานะล็อกอื่น ต้องยังไม่สามารถแก้ไขได้ตาม Business Rule เดิม 7. Frontend และ Backend ต้องใช้กฎเดียวกัน --- # ขอบเขตการตรวจสอบ ตรวจสอบ Flow ทั้งหมดต่อไปนี้: ```text Permission Definitions → Role / Permission Template → Navigation / Action Visibility → Action Menu → Edit Button → Edit Page Guard → Form Initialization → Update API / Server Action → Workflow Service → Status Transition → Ownership Check → Audit Log → Resubmission ``` ค้นหาไฟล์หรือ Function ที่เกี่ยวข้อง เช่น: ```text permissions.ts permission-constants.ts online-lesson-permissions.ts authorization.ts requirePermission.ts canEditOnlineLesson.ts isOnlineLessonEditable.ts OnlineLessonActionMenu OnlineLessonCellAction OnlineLessonEditPage OnlineLessonForm updateOnlineLesson submitOnlineLessonForReview returnOnlineLessonForRevision ``` ชื่อจริงอาจแตกต่าง ให้ตรวจสอบจาก Repository --- # Phase 1: ตรวจสอบ Permission Definition ตรวจสอบว่า Permission ต่อไปนี้ถูกประกาศไว้ที่ใด: ```text online_lesson:create online_lesson:edit_draft online_lesson:submit online_lesson:review online_lesson:approve online_lesson:revision online_lesson:publish online_lesson:manage ``` ให้ยืนยันความหมายจริงจาก: - Permission Seed - Permission Template - Permission Description - Role Mapping - UI Permission Matrix - Authorization Helper - Existing Tests - Documentation ตรวจสอบว่า `online_lesson:revision` ถูกใช้ในตำแหน่งใดบ้าง และมีจุดใดนำไปใช้ผิดวัตถุประสงค์ เช่น: ```ts canEdit = hasPermission("online_lesson:revision") && lesson.status === "returned"; ``` หรือ: ```ts requirePermission("online_lesson:revision"); ``` ภายใน Update API ให้จัดทำรายการ Usage ทั้งหมดก่อนแก้ไข --- # Phase 2: ตรวจสอบสถานะ Workflow จริง ตรวจสอบ Enum หรือ Constant ของสถานะบทเรียนออนไลน์จาก Source of Truth จริง เช่น: ```ts draft; pending_review; returned; approved; rejected; scheduled; published; archived; ``` ต้องแยกให้ชัดเจนว่า: - สถานะใดหมายถึง “ส่งกลับให้แก้ไข” - สถานะใดหมายถึง “ปฏิเสธถาวร” - สถานะใดอนุญาตให้ส่งใหม่ - สถานะใดไม่อนุญาตให้แก้ไขอีก ห้ามเหมารวม `returned` และ `rejected` ว่าแก้ไขได้เหมือนกันโดยอัตโนมัติ หาก Workflow ปัจจุบันกำหนดว่า: ```text returned = แก้ไขและส่งใหม่ได้ rejected = ปิดรายการ ไม่สามารถส่งใหม่ได้ ``` ให้รองรับเฉพาะ `returned` หากระบบใช้ `rejected` ในความหมายว่า “ส่งกลับให้แก้ไข” จริง ให้ยึดตาม Business Rule ปัจจุบัน แต่ควรบันทึกข้อสังเกตไว้ในรายงาน --- # Phase 3: สร้างหรือปรับกฎกลางสำหรับการแก้ไข ควรมี Authorization Helper กลางสำหรับตรวจสอบสิทธิ์การแก้ไข เช่น: ```ts canEditOnlineLesson({ actor, lesson, }); ``` หรือใช้ Function เดิมที่มีอยู่แล้ว แนวคิดที่ควรได้: ```ts type CanEditOnlineLessonInput = { actorUserId: string; actorEmployeeId?: string; permissions: string[]; lesson: { createdBy: string; status: OnlineLessonStatus; }; }; export function canEditOnlineLesson(input: CanEditOnlineLessonInput): boolean { const isOwner = input.lesson.createdBy === input.actorUserId; const hasEditDraftPermission = input.permissions.includes( "online_lesson:edit_draft", ); const isEditableStatus = [ OnlineLessonStatus.DRAFT, OnlineLessonStatus.RETURNED, ].includes(input.lesson.status); return isOwner && hasEditDraftPermission && isEditableStatus; } ``` ตัวอย่างนี้เป็นเพียงแนวคิด ต้องใช้ Field และ Type จริงของระบบ หากระบบมีสิทธิ์ระดับจัดการ เช่น: ```text online_lesson:manage online_lesson:edit_all ``` ให้รองรับตาม Business Rule เดิม เช่น: ```ts const canManageAll = hasPermission("online_lesson:manage"); ``` แต่ห้ามเพิ่มสิทธิ์ใหม่โดยไม่จำเป็น --- # หลักการ Ownership ผู้สร้างหรือเจ้าของบทเรียนควรแก้ไขได้เฉพาะบทเรียนของตนเอง เว้นแต่มี Permission ระดับจัดการทั้งหมด ต้องตรวจสอบ Field ที่ใช้ระบุเจ้าของจริง เช่น: ```ts createdBy; created_by; ownerId; owner_id; authorId; submittedBy; employeeId; ``` ห้ามตรวจ Ownership ด้วยชื่อหรืออีเมล หากระบบมี ID ที่ชัดเจน ตัวอย่างแนวคิด: ```ts const isOwner = lesson.createdByUserId === session.user.id; ``` หรือ: ```ts const isOwner = lesson.createdByEmployeeId === currentEmployee.id; ``` ให้ใช้ Identifier เดียวกับระบบปัจจุบัน --- # จุดที่ต้องแก้ไขให้สอดคล้องกัน ## 1. Action Menu และปุ่มแก้ไข ตรวจสอบเงื่อนไขการแสดงปุ่มแก้ไข ตัวอย่างปัญหา: ```ts const canEdit = lesson.status === "draft" || hasPermission("online_lesson:revision"); ``` หรือ: ```ts const canEditReturned = hasPermission("online_lesson:revision"); ``` ให้เปลี่ยนไปใช้ Helper กลาง เช่น: ```ts const canEdit = canEditOnlineLesson({ actor, lesson, }); ``` ปุ่มแก้ไขต้องแสดงเมื่อ: - เป็นเจ้าของบทเรียน - มี `online_lesson:edit_draft` - สถานะอยู่ในรายการที่แก้ไขได้ --- ## 2. Edit Page Guard ตรวจสอบ Guard ของหน้าแก้ไข เช่น: ```ts if ( lesson.status !== "draft" && !hasPermission("online_lesson:revision") ) { redirect(...); } ``` ให้ปรับเป็นกฎเดียวกับ Action Menu ตัวอย่าง: ```ts if (!canEditOnlineLesson({ actor, lesson })) { forbidden(); } ``` ห้ามให้ Frontend แสดงปุ่มได้ แต่ Route Guard ปฏิเสธ --- ## 3. Update API หรือ Server Action Backend ต้องตรวจสอบกฎเดียวกันเสมอ ห้ามอาศัยเฉพาะการซ่อนปุ่มฝั่ง Frontend ตรวจสอบ Logic เช่น: ```ts requirePermission("online_lesson:revision"); ``` ภายใน Update API สำหรับสถานะ Returned ให้เปลี่ยนเป็น: ```ts requirePermission("online_lesson:edit_draft"); ``` ร่วมกับ: ```ts requireOwnership(...) ``` และ Status Check ตัวอย่างแนวคิด: ```ts const lesson = await findOnlineLessonById(id); if (!lesson) { throw new NotFoundError(); } const allowed = canEditOnlineLesson({ actor, lesson, }); if (!allowed) { throw new ForbiddenError("คุณไม่มีสิทธิ์แก้ไขบทเรียนนี้"); } ``` --- ## 4. Form Initialization ตรวจสอบว่า Form ไม่ได้ Disable Field ทั้งหมดเมื่อสถานะไม่ใช่ Draft ตัวอย่างปัญหา: ```ts const isReadOnly = lesson.status !== "draft"; ``` ควรเปลี่ยนเป็น: ```ts const isReadOnly = !canEdit; ``` หาก Form รับ Prop เช่น: ```tsx ``` ให้ส่งค่าจาก Authorization Helper กลาง --- ## 5. ปุ่มบันทึกฉบับร่าง เมื่อบทเรียนสถานะ Returned ถูกแก้ไขแล้วกดบันทึกฉบับร่าง ต้องกำหนด Transition ตาม Workflow เดิม แนวทางที่แนะนำ: ```text returned → save draft → returned ``` เหตุผลคือยังเป็นงานที่ถูกส่งกลับและยังไม่ส่งเข้าตรวจสอบใหม่ แต่หากระบบปัจจุบันกำหนดว่า: ```text returned → save draft → draft ``` ให้ยึด Business Rule ที่ผ่านการอนุมัติแล้ว ห้ามเปลี่ยน Status โดยไม่ตรวจสอบ Workflow จริง --- ## 6. ปุ่มส่งตรวจสอบใหม่ เมื่อเจ้าหน้าที่แก้ไขเสร็จและส่งตรวจสอบอีกครั้ง ควรเกิด Transition เช่น: ```text returned → pending_review ``` ต้องใช้ Permission ฝั่งผู้สร้าง เช่น: ```text online_lesson:submit ``` หรือ Permission จริงของระบบ ห้ามใช้: ```text online_lesson:revision ``` สำหรับการส่งตรวจสอบใหม่ --- ## 7. Action “ส่งกลับให้แก้ไข” ฝั่งผู้ตรวจสอบต้องยังใช้ Permission: ```text online_lesson:revision ``` สำหรับ Transition เช่น: ```text pending_review → returned ``` ต้องตรวจสอบ: - ผู้ตรวจสอบมี Permission - สถานะปัจจุบันถูกต้อง - มีเหตุผลหรือ Comment ตาม Validation เดิม - บันทึกผู้ส่งกลับ - บันทึกวันที่ - Audit Log - Notification ไปยังเจ้าของบทเรียน การแก้ครั้งนี้ห้ามลดระดับความปลอดภัยของ Action ฝั่งผู้ตรวจสอบ --- # Permission Matrix ที่คาดหวัง ตรวจสอบและปรับให้ได้พฤติกรรมตามแนวคิดนี้ โดยใช้ชื่อ Permission จริงของระบบ | Action | เจ้าของบทเรียน | ผู้ตรวจสอบ | Permission หลัก | | ----------------------- | -------------: | ------------: | -------------------------- | | สร้างบทเรียน | ได้ | ตามสิทธิ์ | `online_lesson:create` | | แก้ไข Draft ของตนเอง | ได้ | ไม่เกี่ยวข้อง | `online_lesson:edit_draft` | | แก้ไข Returned ของตนเอง | ได้ | ไม่เกี่ยวข้อง | `online_lesson:edit_draft` | | ส่งตรวจสอบ | ได้ | ไม่เกี่ยวข้อง | `online_lesson:submit` | | ตรวจสอบบทเรียน | ไม่ได้ | ได้ | `online_lesson:review` | | ส่งกลับให้แก้ไข | ไม่ได้ | ได้ | `online_lesson:revision` | | อนุมัติ | ไม่ได้ | ได้ | `online_lesson:approve` | | เผยแพร่ | ตาม Workflow | ตามสิทธิ์ | `online_lesson:publish` | ตารางนี้เป็นแนวทาง ต้องยึด Permission จริงในระบบ --- # แยก Permission กับ Status Transition ห้ามตรวจเพียง Permission อย่างเดียว การอนุญาต Action ต้องประกอบด้วย: ```text Permission AND Ownership AND Current Status AND Allowed Transition ``` ตัวอย่าง: ```ts const canEdit = hasPermission("online_lesson:edit_draft") && isOwner && isEditableStatus; ``` ตัวอย่างการส่งกลับ: ```ts const canReturnForRevision = hasPermission("online_lesson:revision") && lesson.status === "pending_review"; ``` --- # ตรวจสอบการใช้ Permission ที่ผิดทั้งหมด ค้นหาทั้ง Repository ด้วยคำ: ```text online_lesson:revision ``` ตรวจสอบทุกตำแหน่งว่าเป็นการใช้งานสำหรับ: ```text ผู้ตรวจสอบส่งกลับให้แก้ไข ``` จริงหรือไม่ ตำแหน่งที่อาจผิด: - ปุ่มแก้ไข Returned - Edit Page Guard - Update API - Form Read-only - Save Draft Action - Resubmit Action - Navigation - Permission Matrix Description - Automated Tests - Seed Data - UI Description หากพบว่า Permission Description อธิบายคลุมเครือ เช่น: ```text แก้ไขบทเรียน ``` ให้ปรับคำอธิบายให้ชัดเจน เช่น: ```text ส่งบทเรียนกลับให้ผู้สร้างแก้ไข ``` โดยไม่เปลี่ยน Permission Key หากไม่จำเป็น --- # การจัดการ Rejected ต้องตรวจสอบให้ชัดเจนว่าสถานะ `rejected` หมายถึงอะไร ## กรณี Rejected คือปฏิเสธถาวร ห้ามให้เจ้าของแก้ไขหรือส่งใหม่โดยอัตโนมัติ พฤติกรรม: ```text rejected → locked ``` อาจต้องสร้างรายการใหม่แทน ตาม Business Rule ## กรณี Rejected ถูกใช้แทน Returned ให้พิจารณารองรับการแก้ไข แต่ต้องรายงานว่า Naming ของสถานะอาจไม่สอดคล้องกับความหมายทางธุรกิจ ห้ามเปลี่ยนชื่อ Enum หรือทำ Database Migration ภายในงานนี้ เว้นแต่ได้รับคำสั่งเพิ่มเติม --- # Cache และ UI Refresh หลังแก้ไขหรือส่งตรวจสอบใหม่ ต้องอัปเดตหน้ารายการทันที ตรวจสอบ Data Fetching Pattern จริง เช่น: - TanStack Query - SWR - Server Component - Server Action หลัง Mutation สำเร็จ ต้อง Invalidate หรือ Revalidate Query ที่เกี่ยวข้อง เช่น: ```text online-lessons online-lesson-detail online-lesson-counts review-queue notifications ``` ห้ามใช้: ```ts window.location.reload(); ``` ห้ามใช้ `setTimeout()` เพื่อแก้ปัญหา Refresh --- # Error Message ปรับข้อความ Error ให้ตรงกับกฎจริง ตัวอย่างข้อความที่ควรหลีกเลี่ยง: ```text สามารถแก้ไขได้เฉพาะสถานะฉบับร่างเท่านั้น ``` หาก Returned แก้ไขได้แล้ว ควรเป็น: ```text สามารถแก้ไขได้เฉพาะบทเรียนฉบับร่างหรือบทเรียนที่ถูกส่งกลับให้แก้ไข ``` หรือใช้ข้อความตามมาตรฐานระบบ กรณีไม่ใช่เจ้าของ: ```text คุณไม่มีสิทธิ์แก้ไขบทเรียนนี้ ``` กรณีสถานะไม่อนุญาต: ```text ไม่สามารถแก้ไขบทเรียนในสถานะปัจจุบันได้ ``` ไม่ควรเปิดเผยรายละเอียด Permission ภายในเกินจำเป็นต่อผู้ใช้ทั่วไป --- # Audit Log ตรวจสอบว่าการแก้ไข Returned และการส่งตรวจสอบใหม่บันทึก Audit Log ตามมาตรฐานเดิม เช่น: ```text online_lesson.updated online_lesson.resubmitted ``` ควรบันทึก: - Lesson ID - ผู้ดำเนินการ - สถานะก่อนหน้า - สถานะหลัง - วันที่เวลา - Field ที่แก้ไข หากระบบรองรับ - Revision Number หากมี ห้ามลบประวัติการส่งกลับหรือเหตุผลเดิม --- # Notification เมื่อเจ้าหน้าที่ส่งบทเรียนที่แก้ไขแล้วเข้าตรวจสอบใหม่ ให้ตรวจสอบ Notification ตาม Workflow เดิม เช่น: - แจ้งผู้ตรวจสอบ - แจ้ง HRD Manager - อัปเดต In-app Notification - Email หากระบบรองรับ ห้ามส่ง Notification ซ้ำจากการกดปุ่มหลายครั้ง --- # Test Cases ## Test Case 1: เจ้าของแก้ไข Draft 1. Login เป็นเจ้าหน้าที่ที่มี `online_lesson:edit_draft` 2. เปิดบทเรียน Draft ที่ตนเองสร้าง 3. ต้องเห็นปุ่มแก้ไข 4. เข้าหน้าแก้ไขได้ 5. บันทึกได้สำเร็จ --- ## Test Case 2: เจ้าของแก้ไข Returned 1. ผู้ตรวจสอบส่งบทเรียนกลับ 2. Login เป็นเจ้าของบทเรียน 3. เจ้าของไม่มี `online_lesson:revision` 4. เจ้าของมี `online_lesson:edit_draft` 5. ต้องเห็นปุ่มแก้ไข 6. เข้าหน้าแก้ไขได้ 7. บันทึกได้ 8. ไม่ได้รับ Forbidden Error --- ## Test Case 3: เจ้าของไม่มี Edit Draft Permission 1. Login เป็นเจ้าของบทเรียน 2. ไม่มี `online_lesson:edit_draft` 3. บทเรียนอยู่สถานะ Returned 4. ต้องไม่เห็นปุ่มแก้ไข 5. เข้าหน้า Edit โดย URL ต้องถูกปฏิเสธ 6. เรียก Update API โดยตรงต้องได้ Forbidden --- ## Test Case 4: ผู้ใช้พยายามแก้บทเรียนคนอื่น 1. Login เป็นเจ้าหน้าที่ A 2. มี `online_lesson:edit_draft` 3. เปิดบทเรียน Returned ของเจ้าหน้าที่ B 4. ต้องไม่เห็นปุ่มแก้ไข 5. Route Guard ต้องปฏิเสธ 6. API ต้องปฏิเสธ --- ## Test Case 5: ผู้ตรวจสอบส่งกลับ 1. Login เป็นผู้ตรวจสอบ 2. มี `online_lesson:revision` 3. เปิดบทเรียน Pending Review 4. สามารถส่งกลับให้แก้ไขได้ 5. สถานะเปลี่ยนเป็น Returned 6. เจ้าของได้รับ Notification --- ## Test Case 6: เจ้าหน้าที่ไม่มี Revision แต่แก้ Returned ได้ 1. เจ้าหน้าที่มี: - `online_lesson:edit_draft` - ไม่มี `online_lesson:revision` 2. บทเรียนเป็นของตนเอง 3. สถานะ Returned 4. ต้องแก้ไขและบันทึกได้ Test Case นี้เป็น Acceptance Test หลักของงาน --- ## Test Case 7: ส่งตรวจสอบใหม่ 1. เจ้าของแก้ไข Returned 2. กดส่งตรวจสอบใหม่ 3. ใช้ Permission ฝั่ง Submit 4. สถานะเปลี่ยนเป็น Pending Review 5. รายการออกจาก Returned Filter 6. ไปปรากฏใน Pending Review 7. ผู้ตรวจสอบได้รับ Notification --- ## Test Case 8: Pending Review ต้องแก้ไม่ได้ 1. เจ้าหน้าที่เป็นเจ้าของบทเรียน 2. มี `online_lesson:edit_draft` 3. สถานะ Pending Review 4. ต้องไม่เห็นปุ่มแก้ไข 5. Route และ API ต้องปฏิเสธ --- ## Test Case 9: Approved/Published ต้องแก้ไม่ได้ ทดสอบสถานะ: ```text approved published scheduled archived ``` หรือสถานะจริงของระบบ เจ้าของที่มี `online_lesson:edit_draft` ต้องไม่สามารถแก้ไขได้ เว้นแต่มี Business Rule หรือ Permission ระดับจัดการที่รองรับอยู่แล้ว --- ## Test Case 10: Rejected ตรวจสอบตามความหมายจริง: - หาก Rejected ถาวร ต้องแก้ไม่ได้ - หาก Rejected หมายถึงส่งกลับ ต้องแก้ได้ตามกฎเดียวกับ Returned ให้รายงานผลชัดเจน --- ## Test Case 11: Direct API Access ทดสอบเรียก API โดยไม่ผ่าน UI: - เจ้าของ + Edit Draft + Returned → สำเร็จ - เจ้าของ + ไม่มี Edit Draft → Forbidden - ไม่ใช่เจ้าของ + Edit Draft → Forbidden - Revision อย่างเดียวแต่ไม่ใช่ผู้สร้าง → ต้องไม่สามารถใช้ Update API ของเจ้าของได้ เว้นแต่มี Manage Permission --- ## Test Case 12: Permission Regression ตรวจสอบว่า: - ผู้ตรวจสอบยังส่งกลับได้ - เจ้าหน้าที่ยังส่งตรวจสอบได้ - ผู้อนุมัติยังอนุมัติได้ - ผู้เผยแพร่ยังเผยแพร่ได้ - ไม่มี Permission ใดได้รับสิทธิ์เกินเดิม --- # Automated Tests หาก Repository มี Test Framework ให้เพิ่มหรือปรับปรุง Test ตาม Pattern เดิม อย่างน้อยต้องครอบคลุม Authorization Matrix: ```text owner + edit_draft + draft = allow owner + edit_draft + returned = allow owner + edit_draft + pending = deny owner + revision + returned แต่ไม่มี edit_draft = deny edit reviewer + revision + pending = allow return non-owner + edit_draft + returned = deny manage permission + supported status = follow existing rule ``` เพิ่ม Test สำหรับ: - Action visibility - Route Guard - Update API - Resubmit Action - Workflow Transition - Permission Description หากมี Snapshot หรือ Seed Test --- # Validation ก่อนส่งงาน รันคำสั่งจริงจาก `package.json` เช่น: ```bash pnpm typecheck pnpm lint pnpm test pnpm build ``` ห้ามแก้ Type Error ด้วย: ```ts any @ts-ignore @ts-expect-error eslint-disable ``` เว้นแต่มีเหตุผลจำเป็นและอธิบายในรายงาน --- # Acceptance Criteria งานถือว่าเสร็จเมื่อ: - เจ้าของบทเรียน Returned สามารถแก้ไขได้ด้วย `online_lesson:edit_draft` - เจ้าของไม่จำเป็นต้องมี `online_lesson:revision` - `online_lesson:revision` ใช้สำหรับ Action ส่งกลับของผู้ตรวจสอบ - Action Menu แสดงปุ่มถูกต้อง - Edit Page Guard ใช้กฎเดียวกัน - Form ไม่ถูก Read-only ผิดพลาด - Update API ใช้กฎเดียวกัน - Ownership ถูกตรวจสอบ - Draft และ Returned แก้ไขได้ตามกฎ - Pending, Approved และ Published ยังถูกล็อก - Rejected ทำงานตาม Business Rule จริง - สามารถบันทึกฉบับร่างหลังถูกส่งกลับ - สามารถส่งตรวจสอบใหม่ - Cache และหน้ารายการอัปเดตทันที - Audit Log ถูกต้อง - Notification ไม่ซ้ำ - Permission ฝั่งผู้ตรวจสอบไม่ถูกลดระดับ - ไม่มีการเปิดให้แก้ไขบทเรียนของคนอื่น - Type Check ผ่าน - Lint ผ่าน - Test ผ่าน - Build ผ่าน --- # รูปแบบรายงานผลหลังแก้ไข หลังดำเนินการเสร็จ ให้รายงานตามหัวข้อต่อไปนี้ ## 1. สาเหตุของปัญหา ระบุจุดที่ใช้ `online_lesson:revision` ผิดหน้าที่ พร้อม Path และ Function ที่เกี่ยวข้อง ## 2. ความหมาย Permission หลังตรวจสอบ สรุปว่าแต่ละ Permission ใช้ทำอะไร โดยเฉพาะ: ```text online_lesson:edit_draft online_lesson:revision online_lesson:submit ``` ## 3. สถานะที่แก้ไขได้ ระบุสถานะจริง เช่น: ```text draft returned ``` และอธิบาย Rejected ว่ามีความหมายอย่างไร ## 4. Ownership Rule ระบุ Field ที่ใช้ตรวจสอบเจ้าของบทเรียน ## 5. ไฟล์ที่แก้ไข ระบุ Path และหน้าที่ของแต่ละไฟล์ ## 6. Authorization Helper อธิบาย Helper กลางที่สร้างหรือปรับปรุง และตำแหน่งที่นำไปใช้ ## 7. Frontend Changes อธิบาย: - Action Menu - Edit Button - Form Read-only - Error Message - Cache Refresh ## 8. Backend Changes อธิบาย: - API Authorization - Status Check - Ownership Check - Workflow Transition ## 9. Permission Matrix สรุปผลลัพธ์ของแต่ละ Role หรือ Permission Combination ## 10. ผลการทดสอบ รายงาน: - Manual Test - Authorization Test - Automated Test - Type Check - Lint - Build ## 11. Regression Check ยืนยันว่า: - ผู้ตรวจสอบยังส่งกลับได้ - เจ้าหน้าที่แก้เฉพาะงานตนเอง - Pending และ Published ยังล็อก - Permission อื่นไม่เปลี่ยน - หน้า List และ Action Menu อัปเดตถูกต้อง ## 12. ประเด็นคงค้าง หากพบว่า Naming ของ `rejected` หรือ Permission Description ยังคลุมเครือ ให้รายงานแยก ห้ามทำ Migration หรือเปลี่ยน Permission Key โดยไม่ได้รับคำสั่ง --- # ข้อกำหนดสุดท้าย - ตรวจสอบโค้ดจริงก่อนแก้ไข - แยกสิทธิ์ “แก้ไขงานของผู้สร้าง” ออกจากสิทธิ์ “ส่งกลับของผู้ตรวจสอบ” - ใช้ `online_lesson:edit_draft` สำหรับเจ้าของบทเรียน Draft/Returned - ใช้ `online_lesson:revision` สำหรับผู้ตรวจสอบส่งกลับ - ต้องตรวจ Ownership ทุกครั้ง - Frontend และ Backend ต้องใช้กฎเดียวกัน - ห้ามแก้เฉพาะการซ่อนหรือแสดงปุ่ม - ห้ามอาศัย Frontend Authorization เพียงอย่างเดียว - ห้ามให้ `revision` กลายเป็นสิทธิ์แก้ไขบทเรียนของบุคคลอื่น - ห้ามเพิ่ม Permission ใหม่หากของเดิมรองรับอยู่แล้ว - ห้ามเปลี่ยน Database Schema หรือ Permission Key โดยไม่จำเป็น - ห้ามใช้ `window.location.reload()` เพื่อแก้ UI - ต้องมี Test ครอบคลุม Permission Matrix - ต้องยืนยันว่าเจ้าหน้าที่ที่ไม่มี `online_lesson:revision` แต่มี `online_lesson:edit_draft` สามารถแก้ไขบทเรียน Returned ของตนเองได้จริง