# Prompt: ตรวจสอบและปรับปรุง Filter หน้าประวัติการอบรมให้เป็นมาตรฐานเดียวกับหน้าบทเรียนออนไลน์

## บทบาท

ให้คุณทำหน้าที่เป็น Senior Full-Stack Developer และ Code Reviewer ตรวจสอบระบบปัจจุบันก่อนดำเนินการแก้ไข โดยให้ยึดโครงสร้างเดิมของ Repository, Coding Convention, Component Pattern, Data Fetching Pattern, Permission และ Business Rule ที่ระบบใช้อยู่จริง

ห้ามคาดเดาชื่อไฟล์ Field, Enum, API Parameter หรือสถานะขึ้นมาเอง ต้องตรวจสอบจาก Source Code และ Schema ปัจจุบันก่อนทุกครั้ง

---

# เป้าหมาย

ปรับปรุง Filter ในหน้าประวัติการอบรมของผู้ใช้งาน ให้มีรูปแบบ UI/UX และพฤติกรรมการทำงานเป็นมาตรฐานเดียวกับ Filter:

- หมวดหมู่
- สถานะ

ที่ใช้อยู่ในหน้าบทเรียนออนไลน์

Filter ที่ต้องปรับปรุงในหน้าประวัติการอบรม ได้แก่:

1. ประเภทประวัติการอบรม หรือประเภทการอบรม
2. สถานะประวัติการอบรม

หลังแก้ไขแล้ว Filter ต้อง:

- มีหน้าตาเหมือน Filter ในหน้าบทเรียนออนไลน์
- ใช้ Component Pattern เดียวกัน
- กรองข้อมูลได้จริง
- รองรับการใช้หลาย Filter พร้อมกัน
- ทำงานร่วมกับ Search, Sorting และ Pagination เดิมได้
- รองรับการล้าง Filter
- ไม่กระทบ Permission และ Business Rule เดิม

---

# ขอบเขตงาน

งานนี้ต้องดำเนินการทั้งการตรวจสอบและการแก้ไข โดยแบ่งเป็น 2 ขั้นตอนหลัก

## ขั้นตอนที่ 1: ตรวจสอบระบบเดิม

ตรวจสอบก่อนว่า Filter ในหน้าบทเรียนออนไลน์ถูกสร้างและทำงานอย่างไร

## ขั้นตอนที่ 2: ปรับปรุงหน้าประวัติการอบรม

นำ Pattern ที่ตรวจพบมาใช้กับ Filter ประเภทการอบรมและสถานะของหน้าประวัติการอบรม โดยปรับให้เหมาะสมกับข้อมูลและ Workflow ของ Training Record

---

# Phase 1: ตรวจสอบหน้าบทเรียนออนไลน์

ค้นหาหน้ารายการบทเรียนออนไลน์และ Component ที่เกี่ยวข้อง เช่น:

```text
/dashboard/online-lessons
OnlineLessonTable
OnlineLessonToolbar
OnlineLessonFilters
DataTableToolbar
DataTableFacetedFilter
DataTableSelectFilter
```

ชื่อจริงอาจแตกต่างจากตัวอย่าง ให้ตรวจสอบจาก Repository

ตรวจสอบประเด็นต่อไปนี้:

1. Filter หมวดหมู่ใช้ Component ใด
2. Filter สถานะใช้ Component ใด
3. ใช้ `Select`, `Popover`, `Command`, `FacetedFilter` หรือ Component กลางแบบใด
4. Filter ผูกกับ TanStack Table Column Filter หรือไม่
5. ใช้ Client-side หรือ Server-side Filtering
6. ใช้ URL Search Params หรือ Local State
7. ใช้ React Query, TanStack Query, SWR หรือ Server Component
8. ค่า Filter อยู่ใน Query Key หรือไม่
9. เมื่อเปลี่ยน Filter มีการ Reset Pagination หรือไม่
10. ปุ่มล้าง Filter ทำงานอย่างไร
11. Responsive Layout มีรูปแบบอย่างไร
12. Options และ Label ถูกประกาศไว้ที่ใด
13. ใช้ Constant, Enum หรือ Mapping กลางหรือไม่
14. Width, Height, Placeholder และ Style ใช้ Class ใด
15. มีการแสดงจำนวนค่าที่เลือกหรือ Badge หรือไม่

ให้สรุป Pattern ที่พบก่อนเริ่มแก้ไข

---

# Phase 2: ตรวจสอบหน้าประวัติการอบรม

ค้นหาหน้าประวัติการอบรมของผู้ใช้งาน เช่น:

```text
/dashboard/training-records
/dashboard/my-training-records
/dashboard/user/training-records
```

รวมถึง Component ที่เกี่ยวข้อง เช่น:

```text
TrainingRecordTable
TrainingRecordToolbar
TrainingRecordFilters
UserTrainingRecordTable
DataTableToolbar
```

ตรวจสอบว่า Filter ปัจจุบัน:

- แสดงอยู่ที่ Component ใด
- ใช้ Field ใด
- มี State หรือ Search Params หรือไม่
- ส่งค่าเข้า API หรือไม่
- Backend รองรับ Parameter หรือไม่
- Database Query ใช้ค่าที่ส่งมาหรือไม่
- Filter ปัจจุบันกรองข้อมูลจริงหรือเป็นเพียง UI
- ค่าใน Select ตรงกับ Enum หรือค่าที่เก็บในฐานข้อมูลหรือไม่
- Count และ Pagination ใช้เงื่อนไขเดียวกับ Query ข้อมูลหรือไม่

---

# ตรวจสอบความหมายของ “ประเภทการอบรม”

ห้ามสรุปเองว่าประเภทการอบรมคือ `online/offline`

ให้ตรวจสอบ Field จริงจาก:

- Database Schema
- Prisma Schema
- Drizzle Schema
- SQL Schema
- TypeScript Type
- Zod Schema
- Form Create/Edit
- API Request
- Table Column
- Badge หรือ Label Mapping

ประเภทการอบรมอาจหมายถึง เช่น:

```text
ออนไลน์ / ออฟไลน์
ภายใน / ภายนอก
Internal / External
Online / Onsite
Employee Submitted / Imported
```

ต้องใช้ความหมายและค่าที่ระบบปัจจุบันใช้อยู่จริงเท่านั้น

ตัวอย่างชื่อ Field ที่อาจพบ:

```ts
trainingType;
training_type;
trainingMode;
training_mode;
learningType;
locationType;
```

ห้ามสร้าง Field ใหม่ หากมี Field เดิมรองรับอยู่แล้ว

---

# ตรวจสอบสถานะของประวัติการอบรม

ตรวจสอบสถานะทั้งหมดจาก Workflow จริงของ Training Record

ตัวอย่างสถานะที่อาจมี:

```text
draft
pending
pending_review
approved
rejected
returned
revision_required
cancelled
```

ห้ามนำ Enum หรือสถานะจาก Online Lesson มาใช้โดยตรง เพราะ Workflow ของ Online Lesson และ Training Record อาจแตกต่างกัน

ให้ตรวจสอบจาก:

- Database Enum
- Validation Schema
- Status Badge
- Workflow Service
- API
- Permission
- Transition Logic
- Existing Test

---

# แนวทางการปรับปรุง UI

ให้ปรับ Filter ประเภทการอบรมและสถานะในหน้าประวัติการอบรมให้มีรูปแบบเหมือนกับ Filter หมวดหมู่และสถานะของหน้าบทเรียนออนไลน์

ต้องเหมือนกันในด้าน:

- Component Type
- ความสูง
- Border
- Radius
- Font Size
- Placeholder
- Icon
- Padding
- Hover State
- Focus State
- Disabled State
- Dropdown Width
- Responsive Behavior
- ระยะห่างระหว่างช่อง
- รูปแบบปุ่มล้างตัวกรอง

ตัวอย่าง Layout ที่คาดหวัง:

```text
[ ค้นหาประวัติการอบรม ] [ ประเภทการอบรม ▼ ] [ สถานะ ▼ ] [ ล้างตัวกรอง ]
```

บน Mobile:

```text
[ ค้นหา                     ]
[ ประเภทการอบรม            ]
[ สถานะ                    ]
[ ล้างตัวกรอง              ]
```

ให้ยึด Layout จริงของหน้าบทเรียนออนไลน์เป็นหลัก ไม่ต้องใช้ตัวอย่างนี้หากระบบมีมาตรฐานที่เหมาะสมกว่าอยู่แล้ว

---

# การใช้ Component ร่วมกัน

หากหน้าบทเรียนออนไลน์ใช้ Shared Component อยู่แล้ว เช่น:

```tsx
<DataTableFacetedFilter />
<DataTableSelectFilter />
<DataTableToolbarFilter />
<FilterSelect />
```

ให้นำ Component เดิมมาใช้ในหน้าประวัติการอบรม

ห้ามสร้าง Component ใหม่ที่ทำงานซ้ำกับของเดิมโดยไม่จำเป็น

หาก Component ปัจจุบันผูกเฉพาะ Online Lesson มากเกินไป ให้พิจารณา Refactor แบบปลอดภัยเพื่อให้ใช้ร่วมกันได้ เช่น:

```tsx
<DataTableSelectFilter
  title="ประเภทการอบรม"
  value={trainingType}
  options={trainingTypeOptions}
  onValueChange={handleTrainingTypeChange}
/>
```

การ Refactor ต้อง:

- ไม่ทำให้หน้าบทเรียนออนไลน์เปลี่ยนพฤติกรรม
- ไม่ทำให้ UI เดิมเสีย
- ไม่เปลี่ยน Public API โดยไม่จำเป็น
- ลดโค้ดซ้ำ
- มี Type ที่ชัดเจน

---

# การกำหนด Options

ให้ใช้ Constant หรือ Mapping กลาง หากระบบมีอยู่แล้ว

ตัวอย่างโครงสร้าง:

```ts
export const TRAINING_RECORD_TYPE_OPTIONS = [
  {
    label: "ออนไลน์",
    value: "online",
  },
  {
    label: "ออฟไลน์",
    value: "offline",
  },
] as const;
```

และ:

```ts
export const TRAINING_RECORD_STATUS_OPTIONS = [
  {
    label: "ฉบับร่าง",
    value: "draft",
  },
  {
    label: "รอตรวจสอบ",
    value: "pending",
  },
  {
    label: "อนุมัติแล้ว",
    value: "approved",
  },
] as const;
```

ตัวอย่างนี้ใช้ประกอบเท่านั้น ต้องใช้ค่าจริงจากระบบ

ห้ามกำหนด Enum หรือ Options ซ้ำหลายจุด เช่น:

- Toolbar
- Table Column
- Status Badge
- API Schema
- Form
- Repository

ควรมี Source of Truth เดียวเท่าที่โครงสร้างระบบเอื้ออำนวย

---

# การเชื่อม Filter กับ State

ให้ใช้ State Management Pattern เดียวกับหน้าบทเรียนออนไลน์

หากหน้าบทเรียนออนไลน์ใช้ URL Search Params ให้ใช้ URL Search Params

ตัวอย่าง:

```text
/dashboard/training-records?trainingType=online&status=pending&page=1
```

เมื่อเปลี่ยน Filter:

1. อัปเดต Parameter ที่เกี่ยวข้อง
2. คง Parameter อื่นไว้
3. Reset หน้าเป็นหน้า 1
4. Refetch ข้อมูลตามเงื่อนไขใหม่

ตัวอย่างแนวคิด:

```ts
const handleTrainingTypeChange = (value: string) => {
  const params = new URLSearchParams(searchParams.toString());

  if (!value || value === "all") {
    params.delete("trainingType");
  } else {
    params.set("trainingType", value);
  }

  params.set("page", "1");

  router.replace(`${pathname}?${params.toString()}`);
};
```

ห้าม Copy ตัวอย่างไปใช้ทันที หากหน้าบทเรียนออนไลน์มี Utility หรือ Hook กลางอยู่แล้ว ให้ใช้ของเดิม

---

# การเชื่อม Filter กับ Data Fetching

หากใช้ React Query หรือ TanStack Query ต้องตรวจสอบว่า Filter อยู่ใน Query Key

ตัวอย่าง:

```ts
queryKey: [
  "training-records",
  {
    page,
    limit,
    search,
    trainingType,
    status,
    sort,
  },
];
```

หากใช้ SWR ต้องให้ Filter อยู่ใน Key หรือ URL

ตัวอย่าง:

```ts
const queryString = createQueryString({
  page,
  limit,
  search,
  trainingType,
  status,
});

useSWR(`/api/training-records?${queryString}`, fetcher);
```

เมื่อเปลี่ยน Filter ต้อง Refetch อัตโนมัติ โดยไม่ต้อง Refresh Browser

---

# การส่งค่า Filter ไปยัง API

ตรวจสอบว่า Request ส่งค่า Filter จริง

ตัวอย่าง:

```text
GET /api/training-records?trainingType=online&status=pending
```

ห้ามส่งค่าประเภท:

```text
trainingType=all
status=all
```

ค่าที่หมายถึง “ทั้งหมด” ต้อง:

- ถูกลบออกจาก Query String หรือ
- ถูกแปลงเป็น `undefined`

ตรวจสอบผ่าน:

- Browser Network
- Request Log
- API Handler
- Server Log

---

# API Validation

ตรวจสอบหรือปรับ Query Schema ให้รองรับ Filter ทั้งสอง

ตัวอย่างแนวคิด:

```ts
const trainingRecordQuerySchema = z.object({
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(10),
  search: z.string().trim().optional(),
  trainingType: trainingTypeSchema.optional(),
  status: trainingRecordStatusSchema.optional(),
});
```

ให้ใช้ Schema หรือ Enum เดิมของระบบ

หากค่าไม่ถูกต้อง ควรตอบ Validation Error อย่างชัดเจน ไม่ควร Ignore ค่าเงียบ ๆ

---

# Service และ Repository

ให้ส่ง Filter จาก API ไปยัง Service หรือ Repository อย่างครบถ้วน

ตัวอย่าง:

```ts
const result = await getTrainingRecords({
  employeeId: currentEmployee.id,
  page: query.page,
  limit: query.limit,
  search: query.search,
  trainingType: query.trainingType,
  status: query.status,
});
```

หน้าผู้ใช้งานต้องคงเงื่อนไขเห็นเฉพาะข้อมูลของตนเอง

ตัวอย่างแนวคิด:

```ts
const where = {
  employeeId: currentEmployee.id,
  ...(trainingType && {
    trainingType,
  }),
  ...(status && {
    status,
  }),
};
```

ห้ามแก้ไขจนทำให้ Employee สามารถเห็นข้อมูลของพนักงานคนอื่นได้

---

# Database Query

หากใช้ Prisma ให้สร้าง `where` ชุดเดียว และใช้กับทั้งรายการข้อมูลและการนับจำนวน

ตัวอย่าง:

```ts
const where: Prisma.TrainingRecordWhereInput = {
  employeeId: currentEmployee.id,

  ...(search && {
    OR: [
      {
        courseName: {
          contains: search,
          mode: "insensitive",
        },
      },
    ],
  }),

  ...(trainingType && {
    trainingType,
  }),

  ...(status && {
    status,
  }),
};
```

ใช้กับ:

```ts
const [items, total] = await Promise.all([
  prisma.trainingRecord.findMany({
    where,
    skip,
    take,
    orderBy,
  }),

  prisma.trainingRecord.count({
    where,
  }),
]);
```

หากใช้ Drizzle หรือ SQL ให้ใช้ Parameterized Query ตามมาตรฐานเดิมของระบบ

---

# การใช้หลาย Filter พร้อมกัน

Filter ต้องทำงานแบบ AND

ตัวอย่าง:

```text
ประเภทการอบรม = ออนไลน์
สถานะ = รอตรวจสอบ
```

ผลลัพธ์ต้องเป็น:

```text
trainingType = online
AND
status = pending
```

และต้องสามารถใช้ร่วมกับ Search และ Filter อื่นได้ เช่น:

```text
search = React
AND
trainingType = online
AND
status = pending
AND
employeeId = currentEmployee.id
```

---

# Pagination

เมื่อเปลี่ยนประเภทการอบรมหรือสถานะ ต้อง Reset Pagination กลับหน้าแรก

หากใช้ TanStack Table:

```ts
setPagination((previous) => ({
  ...previous,
  pageIndex: 0,
}));
```

หากใช้ URL Search Params:

```ts
params.set("page", "1");
```

ตรวจสอบว่าจำนวนรายการและจำนวนหน้าหลังกรองถูกต้อง

---

# ปุ่มล้างตัวกรอง

ให้ใช้รูปแบบเดียวกับหน้าบทเรียนออนไลน์

เมื่อมี Filter ทำงาน ต้องแสดงปุ่มล้างตัวกรองตาม Pattern เดิมของระบบ

เมื่อกดล้างตัวกรอง ต้อง:

- ล้างประเภทการอบรม
- ล้างสถานะ
- Reset Pagination
- รักษาหรือล้าง Search ตามพฤติกรรมเดียวกับหน้าบทเรียนออนไลน์
- Refetch ข้อมูลใหม่
- อัปเดต URL หากใช้ Search Params

ห้ามสร้างพฤติกรรมคนละแบบกับหน้าบทเรียนออนไลน์โดยไม่มีเหตุผล

---

# Loading และ Empty State

เมื่อเปลี่ยน Filter:

- ใช้ Loading Pattern เดิมของระบบ
- ไม่ควรแสดงข้อมูลเก่าค้างจนผู้ใช้เข้าใจผิด
- ไม่ควรเกิด Layout Shift รุนแรง
- ต้องรองรับ Error State

หากไม่มีข้อมูลตรงกับเงื่อนไข ให้แสดงข้อความ เช่น:

```text
ไม่พบประวัติการอบรมที่ตรงกับเงื่อนไข
```

และควรมีปุ่มล้างตัวกรอง หาก Pattern เดิมของระบบรองรับ

---

# Responsive

ตรวจสอบทั้ง Desktop, Tablet และ Mobile

อย่างน้อยต้องรองรับ:

```text
320px
375px
768px
1024px
1440px
```

ข้อกำหนด:

- Filter ต้องไม่ล้น Container
- ไม่เกิด Horizontal Scroll โดยไม่จำเป็น
- Mobile ควรแสดงเต็มความกว้าง
- Desktop ควรเรียงใน Toolbar อย่างเหมาะสม
- ปุ่มล้าง Filter ต้องไม่เบียดกับช่องค้นหา
- Dropdown ต้องไม่กว้างเกินหน้าจอ

---

# Accessibility

ตรวจสอบ:

- มี Accessible Label
- ใช้ Keyboard เลือก Filter ได้
- Focus State มองเห็นชัดเจน
- Placeholder เข้าใจได้
- ปุ่มล้าง Filter มีข้อความหรือ `aria-label`
- ไม่ใช้สีอย่างเดียวเพื่อสื่อสถานะ
- Screen Reader สามารถเข้าใจชื่อ Filter ได้

---

# สิ่งที่ห้ามแก้ไขโดยไม่จำเป็น

ห้ามเปลี่ยน Business Rule เดิม เช่น:

- Workflow การอนุมัติ
- Status Transition
- Permission
- สิทธิ์ Employee
- สิทธิ์ HRD
- สิทธิ์ Manager
- การสร้างประวัติการอบรม
- การแก้ไขประวัติการอบรม
- การลบข้อมูล
- Sorting เริ่มต้น
- API Response Structure
- Database Schema

หากพบว่าจำเป็นต้องแก้ Database Schema ต้องหยุดและรายงานก่อน ห้าม Migration โดยอัตโนมัติ

---

# Test Cases

## Test Case 1: ตรวจสอบ UI

1. เปิดหน้าบทเรียนออนไลน์
2. ตรวจสอบ Filter หมวดหมู่และสถานะ
3. เปิดหน้าประวัติการอบรม
4. ตรวจสอบว่า Filter ประเภทการอบรมและสถานะมี UI Pattern เดียวกัน
5. ตรวจสอบ Height, Width, Icon, Placeholder และ Spacing

---

## Test Case 2: Filter ประเภทการอบรม

1. เลือกประเภทแรก
2. ตรวจสอบข้อมูลทุกแถวว่าตรงกับประเภทที่เลือก
3. เลือกประเภทอื่น
4. ตรวจสอบว่าข้อมูลเปลี่ยนจริง
5. เลือก “ทั้งหมด”
6. ตรวจสอบว่าแสดงทุกประเภท

---

## Test Case 3: Filter สถานะ

1. เลือกสถานะรอตรวจสอบ
2. ตรวจสอบว่าทุกแถวตรงกับสถานะ
3. เปลี่ยนเป็นสถานะอนุมัติ
4. ตรวจสอบว่าข้อมูลเปลี่ยน
5. เลือกทุกสถานะ
6. ตรวจสอบว่าแสดงข้อมูลทั้งหมดที่ผู้ใช้มีสิทธิ์เห็น

---

## Test Case 4: ใช้สอง Filter พร้อมกัน

1. เลือกประเภทการอบรม
2. เลือกสถานะ
3. ตรวจสอบว่าทุกรายการตรงทั้งสองเงื่อนไข

---

## Test Case 5: ใช้ร่วมกับ Search

1. กรอกคำค้นหา
2. เลือกประเภทการอบรม
3. เลือกสถานะ
4. ตรวจสอบว่าข้อมูลตรงทุกเงื่อนไข

---

## Test Case 6: Pagination

1. ไปหน้าที่มากกว่า 1
2. เปลี่ยนประเภทหรือสถานะ
3. ตรวจสอบว่ากลับหน้า 1
4. ตรวจสอบจำนวนหน้าและจำนวนรายการ

---

## Test Case 7: Clear Filter

1. เลือกหลาย Filter
2. กดล้างตัวกรอง
3. ตรวจสอบว่า Filter ถูกล้าง
4. ตรวจสอบว่า Pagination กลับหน้าแรก
5. ตรวจสอบว่าข้อมูลกลับมาแสดงครบ

---

## Test Case 8: Refresh Browser

หากใช้ URL Search Params:

1. เลือก Filter
2. Refresh หน้า
3. ตรวจสอบว่า Filter ยังคงอยู่
4. ตรวจสอบว่าข้อมูลยังตรงกับ Filter

---

## Test Case 9: Permission

1. Login เป็น Employee
2. ใช้ Filter หลายรูปแบบ
3. ตรวจสอบว่าเห็นเฉพาะข้อมูลของตนเอง
4. ตรวจสอบว่าไม่สามารถเห็น Training Record ของ Employee อื่นได้

---

## Test Case 10: Empty State

1. เลือกเงื่อนไขที่ไม่มีข้อมูล
2. ตรวจสอบข้อความ Empty State
3. ตรวจสอบปุ่มล้าง Filter
4. ตรวจสอบว่าไม่มี Error ใน Console

---

# Automated Test

หาก Repository มี Test Framework ให้เพิ่มหรือปรับปรุง Test ตาม Pattern เดิม เช่น:

- Unit Test สำหรับ Query Parser
- Unit Test สำหรับ Filter Mapping
- Integration Test สำหรับ API
- Component Test สำหรับ Toolbar
- E2E Test สำหรับ Filter

อย่างน้อยควรครอบคลุม:

```text
trainingType only
status only
trainingType + status
search + trainingType + status
clear filters
pagination reset
permission isolation
```

---

# Validation ก่อนส่งงาน

ต้องรันคำสั่งตามที่ Repository รองรับ เช่น:

```bash
pnpm typecheck
pnpm lint
pnpm test
pnpm build
```

หรือคำสั่งจริงจาก `package.json`

ห้ามแก้ TypeScript Error ด้วย:

```ts
any
@ts-ignore
@ts-expect-error
eslint-disable
```

เว้นแต่มีเหตุผลที่ชัดเจนและได้รับการอธิบายในรายงาน

---

# Acceptance Criteria

งานถือว่าเสร็จเมื่อ:

- Filter ประเภทการอบรมมีรูปแบบเดียวกับ Filter หมวดหมู่ในหน้าบทเรียนออนไลน์
- Filter สถานะมีรูปแบบเดียวกับ Filter สถานะในหน้าบทเรียนออนไลน์
- ใช้ Shared Component เดิม หากมี
- ไม่สร้าง Component ซ้ำโดยไม่จำเป็น
- Filter ประเภทการอบรมกรองข้อมูลได้จริง
- Filter สถานะกรองข้อมูลได้จริง
- ใช้สอง Filter พร้อมกันได้
- ใช้ร่วมกับ Search ได้
- ใช้ร่วมกับ Sorting ได้
- ใช้ร่วมกับ Pagination ได้
- เปลี่ยน Filter แล้ว Reset กลับหน้า 1
- ล้าง Filter ได้
- ค่า UI ตรงกับ Enum และ Database
- API รับและใช้งาน Filter จริง
- Count และ Pagination ใช้เงื่อนไขเดียวกับรายการข้อมูล
- Employee ยังเห็นเฉพาะข้อมูลของตนเอง
- UI รองรับ Responsive
- ไม่มี Regression ที่หน้าบทเรียนออนไลน์
- Type Check ผ่าน
- Lint ผ่าน
- Test ผ่าน
- Build ผ่าน

---

# รูปแบบรายงานผลหลังดำเนินการ

หลังแก้ไขเสร็จ ให้รายงานผลตามหัวข้อต่อไปนี้

## 1. ผลการตรวจสอบก่อนแก้ไข

- หน้าบทเรียนออนไลน์ใช้ Component ใด
- Filter ทำงานผ่าน State, URL หรือ Table State
- หน้าประวัติการอบรมเดิมมีปัญหาอย่างไร
- สาเหตุที่ Filter เดิมไม่เป็นมาตรฐานหรือไม่ทำงาน

## 2. ไฟล์ที่แก้ไข

ระบุ:

```text
path/to/file.tsx
path/to/file.ts
```

พร้อมอธิบายหน้าที่ของแต่ละไฟล์

## 3. การเปลี่ยนแปลง Frontend

อธิบาย:

- Component ที่นำมาใช้
- UI ที่ปรับ
- State หรือ Search Params ที่ปรับ
- การ Reset Pagination
- การล้าง Filter

## 4. การเปลี่ยนแปลง Backend

อธิบาย:

- API Parameter
- Validation
- Service
- Repository
- Database Query

หาก Backend เดิมรองรับอยู่แล้ว ให้ระบุว่าไม่ได้แก้และเพราะเหตุใด

## 5. Field และ Value ที่ใช้จริง

ระบุค่าจริง เช่น:

```text
Field: training_type
Values: online, offline
```

และ:

```text
Field: status
Values: draft, pending, approved
```

ใช้ค่าจริงจากระบบเท่านั้น

## 6. ผลการทดสอบ

ระบุ:

- Manual Test
- Automated Test
- Type Check
- Lint
- Build

## 7. Regression Check

ยืนยันว่า:

- หน้าบทเรียนออนไลน์ยังทำงานเดิม
- Search ยังทำงาน
- Sorting ยังทำงาน
- Pagination ยังทำงาน
- Permission ไม่เปลี่ยน
- Workflow ไม่เปลี่ยน

## 8. ข้อจำกัดหรือประเด็นคงค้าง

หากมีสิ่งที่ยังไม่สามารถดำเนินการได้ ให้รายงานตรงไปตรงมา ห้ามระบุว่างานสมบูรณ์หากยังไม่ได้ตรวจสอบผลจริง

---

# ข้อกำหนดสุดท้าย

- ตรวจสอบโค้ดเดิมก่อนแก้ไข
- ใช้ Pattern เดียวกับหน้าบทเรียนออนไลน์
- ห้ามสร้างมาตรฐานใหม่โดยไม่จำเป็น
- ห้ามสมมติ Enum หรือ Field
- ห้ามแก้ Permission และ Business Rule
- ห้ามแก้เฉพาะ UI โดยไม่ตรวจสอบการกรองข้อมูลจริง
- ห้ามรายงานว่าสำเร็จจากการดูหน้าจอเพียงอย่างเดียว
- ต้องตรวจสอบ Request, API Response และผลลัพธ์จาก Query จริง
- ทำการแก้ไขให้ครบถ้วนทั้ง Frontend และ Backend เฉพาะส่วนที่จำเป็น
