This commit is contained in:
2026-07-16 09:53:14 +07:00
parent 0fc112a2e8
commit 29cec708a3
1236 changed files with 212848 additions and 0 deletions

View File

@@ -0,0 +1,587 @@
# 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
- รายงานถูกสร้างเรียบร้อย