# Prompt: Move Announcement Pin Action Out of Create/Edit Form ## Objective ปรับปรุงการจัดการฟังก์ชัน **ปักหมุดประกาศ** โดยนำตัวเลือกปักหมุดออกจากหน้าสร้างและหน้าแก้ไขประกาศ แล้วเปลี่ยนให้เป็น Action ที่ใช้งานหลังจากประกาศได้รับการเผยแพร่แล้ว เป้าหมายคือ: - ลดความซับซ้อนของแบบฟอร์มสร้างประกาศ - แยกการจัดการเนื้อหาออกจากการจัดลำดับการแสดงผล - ทำให้ Workflow ของประกาศชัดเจน - จำกัดการปักหมุดตามสถานะและ Permission - ใช้โครงสร้างเดิมของระบบให้มากที่สุด --- # Existing Structure Review ก่อนเริ่มแก้ไข ให้ตรวจสอบโครงสร้างเดิมที่เกี่ยวข้องทั้งหมด เช่น: - Announcement database schema - `is_pinned`, `pinned_at`, `pinned_by` หรือ Field ที่มีความหมายใกล้เคียง - Create/Edit announcement form - Announcement list - Announcement detail - Action menu - API routes - Server actions - Query และ Sorting - Permission - Audit log - Publish workflow หากระบบมีโครงสร้างรองรับการปักหมุดอยู่แล้ว ให้ Reuse และปรับเฉพาะ UI/Workflow ตามความเหมาะสม ใช้หลัก: > Reuse > Extend > Refactor > Create New ห้ามสร้าง Field, API หรือ Business Logic ซ้ำโดยไม่จำเป็น --- # Scope ดำเนินการดังนี้: 1. นำตัวเลือกปักหมุดออกจากหน้าสร้างประกาศ 2. นำตัวเลือกปักหมุดออกจากหน้าแก้ไขประกาศ 3. เพิ่ม Action ปักหมุดในหน้า Announcement List 4. เพิ่ม Action ปักหมุดในหน้า Announcement Detail หากเหมาะสม 5. จำกัด Action ตาม Status และ Permission 6. ปรับ Query การเรียงประกาศ 7. เพิ่ม Audit Log หากระบบรองรับ 8. ตรวจสอบ Regression ของ Workflow เดิม --- # Create Announcement Form นำ Section หรือ Field ต่อไปนี้ออกจากหน้าสร้างประกาศ: ```text ปักหมุดประกาศ ประกาศที่ปักหมุดจะแสดงด้านบนของรายการ ``` หน้าสร้างประกาศควรมีเฉพาะ: - หัวข้อประกาศ - รายละเอียดประกาศ - วันเริ่มแสดง - วันสิ้นสุดการแสดง - ไฟล์แนบ - ยกเลิก - บันทึกร่าง - ส่งตรวจสอบ ห้ามส่งค่า `isPinned` หรือค่าที่เกี่ยวข้องจาก Create Form เว้นแต่โครงสร้าง API เดิมกำหนดเป็น Required ให้ปรับเป็นค่า Default ที่ปลอดภัย เช่น `false` ประกาศใหม่ต้องมีค่าเริ่มต้น: ```text is_pinned = false ``` --- # Edit Announcement Form นำตัวเลือกปักหมุดออกจากหน้าแก้ไขประกาศเช่นเดียวกัน หน้า Edit ใช้สำหรับแก้ไขเนื้อหาและช่วงเวลาการแสดงผลเท่านั้น ห้ามให้การบันทึกหน้า Edit เปลี่ยนสถานะการปักหมุดโดยไม่ได้ตั้งใจ --- # Announcement List เพิ่มสถานะการปักหมุดให้มองเห็นได้ในรายการ เช่น: - ไอคอน Pin ข้างชื่อประกาศ - Badge `ปักหมุด` - คอลัมน์การแสดงผล หากเหมาะกับ Layout เดิม ตัวอย่าง: ```text 📌 ประกาศวันหยุดประจำปี ``` หรือ: ```text [ปักหมุด] ประกาศวันหยุดประจำปี ``` หลีกเลี่ยงการเพิ่มคอลัมน์ใหม่ หากทำให้ตารางกว้างเกินไป ให้ใช้ไอคอนหรือ Badge ภายในคอลัมน์ชื่อประกาศแทน --- # Action Menu ใน Action Menu ของแต่ละประกาศ เพิ่มคำสั่งตามสถานะ: หากยังไม่ปักหมุด: ```text ปักหมุดประกาศ ``` หากปักหมุดอยู่แล้ว: ```text ยกเลิกการปักหมุด ``` ใช้ไอคอนที่สื่อความหมาย เช่น: - `Pin` - `PinOff` Action ต้องมี `aria-label` และข้อความภาษาไทยที่ชัดเจน --- # Announcement Detail หากหน้า Detail มีชุด Action อยู่แล้ว ให้เพิ่ม: ```text ปักหมุดประกาศ ``` หรือ: ```text ยกเลิกการปักหมุด ``` ไม่จำเป็นต้องเพิ่ม หากจะทำให้ Action ซ้ำซ้อนและหน้า List รองรับการใช้งานครบถ้วนแล้ว ให้เลือกแนวทางที่สอดคล้องกับ Pattern ของระบบเดิม --- # Status Rules อนุญาตให้ปักหมุดเฉพาะประกาศที่มีสถานะ: ```text Published ``` และควรอยู่ในเงื่อนไข: - วันเริ่มแสดงมาถึงแล้ว หรือระบบอนุญาตให้ปักหมุดล่วงหน้าอย่างชัดเจน - วันสิ้นสุดยังไม่ผ่านมา - ไม่ถูก Archive - ไม่ถูก Soft Delete ห้ามปักหมุดประกาศสถานะ: - Draft - Pending Review - Rejected - Approved but not Published - Inactive - Archived - Deleted - Expired หากระบบใช้ชื่อ Status ต่างจากนี้ ให้ Mapping กับ Enum เดิมของโปรเจกต์ ห้ามสร้าง Enum ซ้ำ --- # Automatic Unpin Rules ระบบต้องยกเลิกการปักหมุดอัตโนมัติ หรือไม่นำประกาศนั้นมาแสดงเป็นปักหมุด เมื่อ: - ประกาศถูก Unpublish - ประกาศถูก Archive - ประกาศถูก Soft Delete - ประกาศหมดอายุ - วันสิ้นสุดการแสดงผลผ่านไปแล้ว เลือกแนวทางให้สอดคล้องกับ Architecture เดิม: 1. อัปเดต `is_pinned = false` เมื่อสถานะเปลี่ยน 2. หรือ Filter ออกจาก Pinned Query โดยไม่เปลี่ยนข้อมูลเดิม ควรเลือกวิธีที่ลดความเสี่ยงของข้อมูลไม่สอดคล้องกัน และอธิบายไว้ในรายงาน --- # Multiple Pinned Announcements รองรับการปักหมุดหลายประกาศ ลำดับการแสดงผล: 1. ประกาศที่ปักหมุดก่อน 2. ภายในกลุ่มปักหมุด เรียงตาม `pinned_at DESC` 3. หากไม่มี `pinned_at` ให้ใช้ `published_at DESC` 4. ประกาศทั่วไปเรียงตามมาตรฐานเดิมของระบบ ตัวอย่าง Query Logic: ```text is_pinned DESC pinned_at DESC published_at DESC created_at DESC ``` ให้ปรับตาม Field ที่มีอยู่จริงในระบบ --- # Confirmation Dialog ก่อนปักหมุด แสดง Dialog: ```text ยืนยันการปักหมุดประกาศ ประกาศนี้จะแสดงอยู่ด้านบนของรายการประกาศ คุณต้องการดำเนินการต่อหรือไม่ ``` ปุ่ม: ```text ยกเลิก ปักหมุดประกาศ ``` ก่อนยกเลิกการปักหมุด แสดง Dialog: ```text ยืนยันการยกเลิกปักหมุด ประกาศนี้จะกลับไปเรียงตามลำดับประกาศทั่วไป คุณต้องการดำเนินการต่อหรือไม่ ``` ปุ่ม: ```text ยกเลิก ยกเลิกการปักหมุด ``` หาก Pattern เดิมของระบบไม่ใช้ Confirm Dialog กับ Action ลักษณะนี้ สามารถใช้ Action ทันทีพร้อม Toast ได้ แต่ต้องรักษามาตรฐานเดียวกับ Feature อื่น --- # Permission ใช้ Permission-first Architecture เท่านั้น เพิ่มหรือ Reuse Permission: ```text announcement:pin ``` Permission นี้ครอบคลุม: - ปักหมุดประกาศ - ยกเลิกการปักหมุด ทุก API หรือ Server Action ต้องตรวจสอบ Permission ฝั่ง Server ห้ามตรวจสอบเฉพาะฝั่ง Client ผู้ไม่มี Permission: - ไม่เห็น Action - เรียก API โดยตรงไม่ได้ - ได้รับ Response ที่เหมาะสม เช่น `403 Forbidden` หากมี Permission เดิมที่มีความหมายตรงกัน ให้ Reuse และไม่สร้างซ้ำ --- # API and Server Action หากมี API เดิมรองรับ ให้ขยายของเดิม หากไม่มี ให้เพิ่มเฉพาะ Endpoint หรือ Server Action ที่จำเป็น เช่น: ```text POST /api/announcements/:id/pin POST /api/announcements/:id/unpin ``` หรือใช้รูปแบบ Mutation ตาม Architecture เดิม การปักหมุดต้องอัปเดต: ```text is_pinned = true pinned_at = current timestamp pinned_by = current user ``` การยกเลิกปักหมุดต้องอัปเดต: ```text is_pinned = false pinned_at = null pinned_by = null ``` หาก Schema เดิมไม่มี `pinned_at` หรือ `pinned_by` ให้ประเมินก่อนเพิ่ม โดยเพิ่มเฉพาะเมื่อจำเป็นต่อ Sorting และ Audit เท่านั้น --- # Audit Log หากระบบมี Audit Log อยู่แล้ว ให้บันทึก Action: ```text ANNOUNCEMENT_PINNED ANNOUNCEMENT_UNPINNED ``` ข้อมูลควรประกอบด้วย: - Announcement ID - Announcement title - Actor - Action - Previous value - New value - Timestamp ห้ามสร้างระบบ Audit ใหม่ หากมีโครงสร้างกลางอยู่แล้ว --- # Toast Messages ปักหมุดสำเร็จ: ```text ปักหมุดประกาศเรียบร้อยแล้ว ``` ยกเลิกสำเร็จ: ```text ยกเลิกการปักหมุดเรียบร้อยแล้ว ``` กรณีสถานะไม่รองรับ: ```text สามารถปักหมุดได้เฉพาะประกาศที่เผยแพร่แล้วเท่านั้น ``` กรณีประกาศหมดอายุ: ```text ไม่สามารถปักหมุดประกาศที่หมดอายุแล้วได้ ``` กรณีไม่มีสิทธิ์: ```text คุณไม่มีสิทธิ์จัดการการปักหมุดประกาศ ``` --- # Loading State ขณะดำเนินการ: - Disable Action ที่กำลังประมวลผล - ป้องกันการกดซ้ำ - แสดง Loading Indicator ตามมาตรฐานระบบ - ไม่ปิดเมนูหรือ Dialog แบบทำให้ผู้ใช้ไม่ทราบผลลัพธ์ หลังสำเร็จ: - Refresh หรือ Invalidate Query เฉพาะข้อมูลที่เกี่ยวข้อง - ห้าม Reload ทั้งหน้าโดยไม่จำเป็น --- # Accessibility ตรวจสอบ: - Action มีชื่อที่ Screen Reader เข้าใจได้ - ไอคอนไม่เป็นตัวสื่อความหมายเพียงอย่างเดียว - Dialog Focus Management ถูกต้อง - Keyboard Navigation ใช้งานได้ - Badge และสถานะไม่ใช้สีเพียงอย่างเดียว --- # Responsive หน้า List และ Action Menu ต้องใช้งานได้บน: - Desktop - Tablet - Mobile ห้ามให้ Badge หรือไอคอนปักหมุดทำให้ชื่อประกาศถูกบีบจนอ่านไม่ได้ --- # Business Logic Restrictions ห้ามเปลี่ยน: - Approval workflow - Draft workflow - Submit for review workflow - Publish workflow - Schedule publish workflow - Versioning ของเนื้อหา - File attachment logic - Existing validation ที่ไม่เกี่ยวข้อง - Existing permission ที่ไม่เกี่ยวข้อง งานนี้ต้องเปลี่ยนเฉพาะ Workflow และ UI ของการปักหมุดประกาศ --- # Testing เพิ่มหรือปรับ Test Cases ให้ครอบคลุม: ## Functional - Create announcement แล้ว `is_pinned` เป็น `false` - หน้า Create ไม่มีตัวเลือกปักหมุด - หน้า Edit ไม่มีตัวเลือกปักหมุด - Published announcement สามารถปักหมุดได้ - Pinned announcement สามารถยกเลิกได้ - Draft ไม่สามารถปักหมุดได้ - Pending Review ไม่สามารถปักหมุดได้ - Archived ไม่สามารถปักหมุดได้ - Expired ไม่สามารถปักหมุดได้ - Unpublished announcement ไม่ถูกแสดงเป็นปักหมุด - หลายประกาศปักหมุดเรียงถูกต้อง ## Permission - มี `announcement:pin` เห็นและใช้ Action ได้ - ไม่มี Permission ไม่เห็น Action - เรียก Server Action โดยไม่มี Permission ต้องถูกปฏิเสธ ## UI - Badge หรือ Pin icon แสดงถูกต้อง - Loading State ทำงาน - Toast ถูกต้อง - Confirm Dialog ถูกต้อง - Mobile ใช้งานได้ ## Regression - Create Draft ยังทำงาน - Submit Review ยังทำงาน - Approve ยังทำงาน - Publish ยังทำงาน - Schedule Publish ยังทำงาน - Edit Announcement ยังทำงาน - Pagination/Search/Filter ยังทำงาน --- # Deliverables เมื่อดำเนินการเสร็จ ให้สร้างเอกสาร: ```text docs/announcement-pin-action-enhancement-report.md ``` ประกอบด้วย: ## 1. Existing Structure Review - โครงสร้างเดิมที่พบ - Field เดิมที่ Reuse - API/Action เดิมที่ Reuse - Component เดิมที่ Reuse - ส่วนที่ต้อง Refactor หรือ Extend ## 2. Files Changed ระบุไฟล์ทั้งหมดที่แก้ไข ## 3. UI Changes - สิ่งที่นำออกจาก Create/Edit - Action ที่เพิ่มใน List/Detail - Badge หรือสถานะที่เพิ่ม ## 4. Business Rules - Status ที่ปักหมุดได้ - Automatic Unpin - Sorting - Multiple Pinned Announcements ## 5. Permission - Permission ที่ใช้ - การตรวจสอบ Client/Server ## 6. Database and API Changes - Field ที่เพิ่มหรือปรับ - Migration - Endpoint หรือ Server Action ## 7. Audit Log Action ที่บันทึก ## 8. Testing Result - Functional - Permission - Responsive - Regression ## 9. Remaining Improvements เช่น: - Pin expiration - Pin priority - Scheduled pinning - Limit maximum pinned announcements - Drag-and-drop pinned ordering --- # Success Criteria งานถือว่าเสร็จเมื่อ: - หน้าสร้างไม่มีตัวเลือกปักหมุด - หน้าแก้ไขไม่มีตัวเลือกปักหมุด - ปักหมุดจากหน้า List หรือ Detail ได้ - ปักหมุดได้เฉพาะประกาศที่เผยแพร่แล้ว - Permission-first ทำงานครบ - Server-side authorization ทำงาน - Sorting ของประกาศปักหมุดถูกต้อง - Automatic Unpin หรือ Filtering ทำงานถูกต้อง - ไม่มีผลกระทบต่อ Approval/Publish Workflow - TypeScript ไม่มี Error - ESLint ผ่าน - Build ผ่าน - Tests ผ่าน - ไม่มี Regression - รายงานถูกสร้างเรียบร้อย