commit
This commit is contained in:
342
plans/fix-attachment-view.md
Normal file
342
plans/fix-attachment-view.md
Normal file
@@ -0,0 +1,342 @@
|
||||
# Prompt: Fix Attachment View & Download Behavior (Production Ready)
|
||||
|
||||
## Background
|
||||
|
||||
ปัจจุบันหน้าแสดงรายละเอียดบทเรียนออนไลน์ (Online Lesson Detail) ในส่วน **เอกสารแนบ** มีปุ่ม
|
||||
|
||||
- เปิดไฟล์
|
||||
- ดาวน์โหลด
|
||||
|
||||
แต่เมื่อกด **เปิดไฟล์** ระบบกลับดาวน์โหลดไฟล์ทันที ซึ่งไม่ใช่พฤติกรรมที่ต้องการ
|
||||
|
||||
---
|
||||
|
||||
## ปัญหาที่พบ
|
||||
|
||||
ปัจจุบันทั้งสองปุ่มใช้งานผ่าน URL เดียวกัน
|
||||
|
||||
ตัวอย่าง
|
||||
|
||||
```tsx
|
||||
<Button asChild variant="outline">
|
||||
<Link href={lesson.attachment_url}>
|
||||
เปิดไฟล์
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
<Button asChild>
|
||||
<Link href={lesson.attachment_url}>
|
||||
ดาวน์โหลด
|
||||
</Link>
|
||||
</Button>
|
||||
```
|
||||
|
||||
ทำให้
|
||||
|
||||
- เปิดไฟล์ = ดาวน์โหลด
|
||||
- ดาวน์โหลด = ดาวน์โหลด
|
||||
|
||||
สาเหตุหลักเกิดจาก Response Header ของไฟล์เป็น
|
||||
|
||||
```
|
||||
Content-Disposition: attachment
|
||||
```
|
||||
|
||||
Browser จึงบังคับ Download
|
||||
|
||||
---
|
||||
|
||||
# Objective
|
||||
|
||||
แยกการทำงานของ
|
||||
|
||||
- View Attachment
|
||||
- Download Attachment
|
||||
|
||||
ให้เป็นคนละ Endpoint
|
||||
|
||||
และรองรับการทำงานแบบ Production
|
||||
|
||||
---
|
||||
|
||||
# Requirements
|
||||
|
||||
## 1. สร้าง API สำหรับ View Attachment
|
||||
|
||||
เช่น
|
||||
|
||||
```
|
||||
GET
|
||||
/api/online-lessons/[id]/attachment/view
|
||||
```
|
||||
|
||||
หรือ Route ที่เหมาะสมกับโครงสร้างโปรเจกต์
|
||||
|
||||
หน้าที่
|
||||
|
||||
- ตรวจสอบสิทธิ์การเข้าถึง
|
||||
- ตรวจสอบว่าบทเรียนมีไฟล์แนบ
|
||||
- อ่านไฟล์จาก Storage
|
||||
- ส่งไฟล์กลับด้วย
|
||||
|
||||
```
|
||||
Content-Disposition: inline
|
||||
```
|
||||
|
||||
ไม่ใช่
|
||||
|
||||
```
|
||||
attachment
|
||||
```
|
||||
|
||||
พร้อมส่ง
|
||||
|
||||
```
|
||||
Content-Type
|
||||
```
|
||||
|
||||
ตาม Mime Type จริงของไฟล์
|
||||
|
||||
ตัวอย่าง
|
||||
|
||||
```
|
||||
application/pdf
|
||||
image/png
|
||||
image/jpeg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. สร้าง API สำหรับ Download
|
||||
|
||||
เช่น
|
||||
|
||||
```
|
||||
GET
|
||||
/api/online-lessons/[id]/attachment/download
|
||||
```
|
||||
|
||||
ส่ง Response
|
||||
|
||||
```
|
||||
Content-Disposition: attachment
|
||||
```
|
||||
|
||||
เพื่อให้ Browser ดาวน์โหลดไฟล์
|
||||
|
||||
---
|
||||
|
||||
## 3. Frontend
|
||||
|
||||
ปรับปุ่ม
|
||||
|
||||
จากเดิม
|
||||
|
||||
```
|
||||
lesson.attachment_url
|
||||
```
|
||||
|
||||
ให้เรียก API ใหม่
|
||||
|
||||
### เปิดไฟล์
|
||||
|
||||
```
|
||||
/attachment/view
|
||||
```
|
||||
|
||||
### ดาวน์โหลด
|
||||
|
||||
```
|
||||
/attachment/download
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. ใช้ Anchor แทน Next Link
|
||||
|
||||
สำหรับไฟล์
|
||||
|
||||
ไม่ควรใช้
|
||||
|
||||
```
|
||||
next/link
|
||||
```
|
||||
|
||||
ให้ใช้
|
||||
|
||||
```
|
||||
<a>
|
||||
```
|
||||
|
||||
ร่วมกับ
|
||||
|
||||
```
|
||||
Button asChild
|
||||
```
|
||||
|
||||
ตัวอย่าง
|
||||
|
||||
```
|
||||
<Button asChild variant="outline">
|
||||
<a
|
||||
href="..."
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
เปิดไฟล์
|
||||
</a>
|
||||
</Button>
|
||||
```
|
||||
|
||||
และ
|
||||
|
||||
```
|
||||
<Button asChild>
|
||||
<a href="...">
|
||||
ดาวน์โหลด
|
||||
</a>
|
||||
</Button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Browser Behavior
|
||||
|
||||
ต้องรองรับ
|
||||
|
||||
### PDF
|
||||
|
||||
เปิดใน Tab ใหม่
|
||||
|
||||
### Image
|
||||
|
||||
เปิดใน Browser
|
||||
|
||||
### Text
|
||||
|
||||
เปิดใน Browser
|
||||
|
||||
### Office File
|
||||
|
||||
หาก Browser เปิดไม่ได้
|
||||
|
||||
ให้ Browser ดาวน์โหลดตามปกติ
|
||||
|
||||
---
|
||||
|
||||
## 6. Security
|
||||
|
||||
ตรวจสอบสิทธิ์ก่อนเปิดไฟล์
|
||||
|
||||
ห้ามใช้ Public URL โดยตรง
|
||||
|
||||
Route ต้องตรวจสอบ
|
||||
|
||||
- Authentication
|
||||
- Authorization
|
||||
|
||||
เหมือนการเปิดรายละเอียดบทเรียน
|
||||
|
||||
---
|
||||
|
||||
## 7. Error Handling
|
||||
|
||||
หาก
|
||||
|
||||
- ไม่พบไฟล์
|
||||
- ไม่มีสิทธิ์
|
||||
- Storage Error
|
||||
|
||||
ต้องส่ง Status Code ที่เหมาะสม
|
||||
|
||||
เช่น
|
||||
|
||||
```
|
||||
404
|
||||
403
|
||||
500
|
||||
```
|
||||
|
||||
Frontend ต้องแสดงข้อความแจ้งผู้ใช้
|
||||
|
||||
---
|
||||
|
||||
## 8. Logging
|
||||
|
||||
บันทึก Error ผ่านระบบ Logging ของโปรเจกต์
|
||||
|
||||
ไม่ใช้
|
||||
|
||||
```
|
||||
console.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. UX
|
||||
|
||||
ส่วนเอกสารแนบควรเป็น
|
||||
|
||||
```
|
||||
เอกสารแนบ
|
||||
|
||||
📄 คู่มือการใช้งาน.pdf
|
||||
|
||||
[เปิดไฟล์] [ดาวน์โหลด]
|
||||
```
|
||||
|
||||
หากไม่มีไฟล์
|
||||
|
||||
```
|
||||
เอกสารแนบ
|
||||
|
||||
ไม่มีไฟล์แนบ
|
||||
```
|
||||
|
||||
ไม่ต้องแสดง Dropzone
|
||||
|
||||
---
|
||||
|
||||
## 10. Coding Standard
|
||||
|
||||
ต้องสอดคล้องกับมาตรฐานของโปรเจกต์
|
||||
|
||||
- Next.js App Router
|
||||
- TypeScript Strict
|
||||
- Server Component / Route Handler
|
||||
- shadcn/ui
|
||||
- TailwindCSS
|
||||
- ไม่ใช้ any
|
||||
- ESLint ผ่าน
|
||||
- Type Check ผ่าน
|
||||
|
||||
---
|
||||
|
||||
# Acceptance Criteria
|
||||
|
||||
- เปิดไฟล์สามารถเปิดใน Browser ได้ (PDF / Image)
|
||||
- ดาวน์โหลดทำงานแยกจากเปิดไฟล์
|
||||
- ไม่ใช้ URL Storage ตรง ๆ บน Frontend
|
||||
- ใช้ API กลางของระบบ
|
||||
- ตรวจสอบ Permission ก่อนทุกครั้ง
|
||||
- ไม่มี Regression
|
||||
- ผ่าน Build
|
||||
- ผ่าน Type Check
|
||||
- ผ่าน ESLint
|
||||
|
||||
---
|
||||
|
||||
## Deliverables
|
||||
|
||||
ดำเนินการดังนี้
|
||||
|
||||
1. วิเคราะห์โครงสร้างเดิม
|
||||
2. อธิบายสาเหตุของปัญหา
|
||||
3. แก้ไข Backend
|
||||
4. แก้ไข Frontend
|
||||
5. ตรวจสอบ Security
|
||||
6. ตรวจสอบ UX
|
||||
7. ตรวจสอบ Regression
|
||||
8. สรุปรายการไฟล์ที่แก้ไข
|
||||
9. สรุปผลการทดสอบ
|
||||
10. ยืนยันว่า Build / Type Check / ESLint ผ่านทั้งหมด
|
||||
Reference in New Issue
Block a user