# Permission Template Validation Fix Report

## Executive Summary

แก้ปัญหา validation ของการเลือก `Permission Template` ในฟอร์มกำหนดสิทธิ์ผู้ใช้ โดยทำให้ชนิดข้อมูลของ `template_id` สอดคล้องกันตั้งแต่ `Select` บน frontend, TanStack Form state, Zod validation, API payload, route handler และ database ที่ใช้ `integer`

## Root Cause Analysis

สาเหตุหลักคือ `Select` component ส่งค่าออกมาเป็น `string` ตามพฤติกรรมของ Radix Select และ shared `FormSelectField` ของโปรเจกต์ก็ส่งค่าต่อเข้า form state แบบ `string` ตรง ๆ แต่ `userPermissionAssignmentSchema` กำหนด `template_id` เป็น `z.number()`

ผลที่เกิดขึ้นคือ

- เมื่อผู้ใช้เลือกค่าใน dropdown, form state กลายเป็น `string`
- Zod ตรวจ `template_id` เป็น `number` จึงขึ้น error `Invalid input: expected number, received string`
- component ต้องพยายาม `Number(value.template_id)` ตอน submit เพื่อแก้ปลายทาง
- logic ที่หา template ปัจจุบันในหน้า edit มีโอกาส mismatch เพราะ `template.id` เป็น `number` แต่ค่าจาก form อาจเป็น `string`

## Architecture Review

Flow หลังแก้ไขเป็นดังนี้

Frontend
-> `Select` ใช้ `string` เฉพาะใน UI
-> `onValueChange` แปลงค่าเป็น `number` ก่อนเก็บใน form state

Validation
-> `userPermissionAssignmentSchema` รองรับ string ที่หลุดเข้ามาได้ด้วย `z.preprocess`
-> คืนข้อความผู้ใช้ `กรุณาเลือก Permission Template`

API
-> `saveUserPermissionAssignment()` ส่ง `template_id` เป็น `number`

Backend
-> route handler ใช้ schema เดิมตรวจ payload
-> service layer รับ `template_id: number`

Database
-> `user_permission_template_assignments.template_id` และ `permission_templates.id` เป็น `integer`

## Files Modified

- [src/features/permission-management/components/user-permission-sheet.tsx](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/permission-management/components/user-permission-sheet.tsx)
  ปรับ select ของ `template_id` ให้แปลงค่า `string -> number` ก่อนเก็บลง form state และเลิกแปลงซ้ำตอน submit
- [src/features/permission-management/schemas/user-permission-assignment.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/permission-management/schemas/user-permission-assignment.ts)
  ปรับ schema ให้ preprocess ค่า `template_id` และคืนข้อความ validation ที่เป็นมิตรกับผู้ใช้

## Implementation Details

Form

- เปลี่ยน default ของ `template_id` จาก fallback `0` เป็นค่าเดิมหรือ template แรกที่มีอยู่จริง
- เก็บค่าใน TanStack Form เป็น `number | undefined` แทน `string`

Select

- ใช้ custom `form.AppField` แทน shared `FormSelectField` เฉพาะจุดนี้
- แปลง `field.state.value` เป็น `String(...)` เฉพาะตอน bind เข้า `Select`
- แปลงค่ากลับเป็น `Number(value)` ตอน `onValueChange`
- ป้องกันเคส empty value ไม่ให้กลายเป็น `0`

Schema

- ใช้ `z.preprocess` สำหรับ `template_id`
- ถ้าเป็น string ว่าง จะเปลี่ยนเป็น `undefined`
- ถ้าเป็น numeric string จะเปลี่ยนเป็น `number`
- ถ้าไม่ผ่านเงื่อนไข จะได้ข้อความ `กรุณาเลือก Permission Template`

API / Backend

- payload จาก frontend ถูกส่งเป็น `number` แล้ว
- route handler และ service layer ไม่ต้องเปลี่ยน business logic

## Before

- เลือก Permission Template แล้ว form มีโอกาสถือค่าเป็น `string`
- schema คาดหวัง `number`
- ผู้ใช้เห็น error เชิงเทคนิค `Invalid input: expected number, received string`
- หน้า edit อาจหา template ปัจจุบันไม่ตรงชนิดข้อมูล

## After

- ค่า `template_id` ใน form state และ payload เป็น `number`
- schema และ API รับค่าชนิดเดียวกับ database
- ผู้ใช้เห็นข้อความ validation ที่เข้าใจได้
- create และ edit ใช้ logic ชนิดข้อมูลเดียวกันตลอด flow

## Validation Result

ตรวจจากโค้ดและ data flow หลังแก้ไขแล้วได้ผลดังนี้

- Create: เลือก template แล้ว submit จะส่ง `template_id` เป็น `number`
- Edit: ค่าเดิมของ template ถูกโหลดเป็น `number` และ bind กลับเข้า select ได้ถูกต้อง
- Validation: หากไม่มีค่า จะได้ข้อความ `กรุณาเลือก Permission Template`
- API: route handler ยังใช้ schema เดิม แต่รับค่า numeric string ได้อย่างปลอดภัยและ normalize เป็น `number`
- Database: ไม่มีการเปลี่ยน schema และยังสอดคล้องกับ `integer`

## Regression Check

- ไม่เปลี่ยน permission logic
- ไม่เปลี่ยน API endpoint
- ไม่เปลี่ยน database schema
- ไม่เพิ่ม library ใหม่
- ไม่เพิ่ม `any`
- ใช้ shared `Select` component เดิมของระบบ

## Risks

- shared `FormSelectField` ของโปรเจกต์ยังเป็น string-based ตามเดิม ดังนั้นถ้ามีฟอร์มอื่นที่ใช้ select กับ schema แบบ `number` อาจเจอ pattern เดียวกันได้

## Recommendation

- หากอนาคตมีหลายฟอร์มที่ต้องใช้ select กับค่า `number`, ควรเพิ่ม shared numeric select wrapper ที่อยู่บน pattern เดิมของ `tanstack-form`
- ควรเพิ่ม automated test ระดับ component หรือ integration สำหรับ create/edit flow ของ user permission assignment
