commit
This commit is contained in:
587
plans/announcement-pin-action.md
Normal file
587
plans/announcement-pin-action.md
Normal 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
|
||||
- รายงานถูกสร้างเรียบร้อย
|
||||
Reference in New Issue
Block a user