You are a Senior Full Stack Engineer.

Project:
Training Management System (TMS)

Current task:
Change training hours storage and display from decimal hours to duration minutes.

Requirement:
Training duration must be stored and calculated as hours and minutes, not decimal hours.

Important constraints:

- Do NOT use AM/PM.
- Do NOT use clock TimePicker.
- Do NOT store Date object.
- Keep Thai UI.
- Keep responsive/mobile behavior.
- Minimal-impact changes only.

Business examples:

- 01:45 = 1 ชั่วโมง 45 นาที
- 00:15 = 15 นาที
- 02:00 = 2 ชั่วโมง
- 30:00 = 30 ชั่วโมง

Recommended storage:
Store duration as total minutes.

Examples:

- 01:45 -> 105
- 00:15 -> 15
- 02:00 -> 120
- 30:00 -> 1800

Part 1: Database

Check current fields:

- submittedHours
- approvedHours
- targetHours
- kHours
- sHours
- aHours

If current DB stores decimal hours, add new minute-based fields if needed:

training_records:

- submittedMinutes integer
- approvedMinutes integer

employee_training_targets:

- totalTargetMinutes integer
- kTargetMinutes integer
- sTargetMinutes integer
- aTargetMinutes integer

training_policy:

- totalTargetMinutes integer
- kTargetMinutes integer
- sTargetMinutes integer
- aTargetMinutes integer

Important:
Do not drop old decimal fields yet.
Keep old fields for compatibility during migration.

Migration:

- Convert old decimal hours to minutes:
  minutes = ROUND(hours \* 60)

Part 2: Duration Picker UI

Create or update component:

src/components/ui/duration-picker.tsx

Props:

- valueMinutes: number | null
- onChange: (minutes: number | null) => void
- maxHours?: number
- minuteStep?: number
- disabled?: boolean

UI:

- Two dropdowns:
  1. ชั่วโมง
  2. นาที

Hour options:

- 00 to 30

Minute options:

- 00
- 15
- 30
- 45

Display:

- --:-- when empty
- 00:15
- 01:45
- 02:00
- 30:00

Do not display:

- AM
- PM

Part 3: Form Behavior

In Create/Edit Training Record form:

Field:

- จำนวนชั่วโมงอบรม \*

Use DurationPicker.

Save:

- submittedMinutes

Validation:

- required
- > 0 minutes
- <= 1800 minutes

Thai messages:

- กรุณาระบุจำนวนชั่วโมงอบรม
- จำนวนชั่วโมงอบรมต้องมากกว่า 0 นาที
- จำนวนชั่วโมงอบรมต้องไม่เกิน 30 ชั่วโมง

Part 4: Display Formatting

Create helper functions:

formatDurationThai(minutes: number): string

Examples:

- 15 -> 15 นาที
- 60 -> 1 ชั่วโมง
- 105 -> 1 ชั่วโมง 45 นาที
- 120 -> 2 ชั่วโมง
- 1800 -> 30 ชั่วโมง

formatDurationHHMM(minutes: number): string

Examples:

- 15 -> 00:15
- 105 -> 01:45
- 120 -> 02:00

Part 5: Calculations

All dashboard/report calculations should use minutes:

- approvedMinutes
- pendingMinutes
- rejectedMinutes
- targetMinutes

When summing:

- SUM(minutes)

Do not sum decimal hours.

Part 6: K/S/A Targets

Targets should support minutes too.

Examples:

- K = 12 ชั่วโมง 30 นาที -> 750 minutes
- S = 10 ชั่วโมง -> 600 minutes
- A = 7 ชั่วโมง 30 นาที -> 450 minutes

Part 7: Backward Compatibility

If minute field is null:

- fallback to old decimal hour field:
  minutes = ROUND(hours \* 60)

Use helper:
getDurationMinutes(minutesField, legacyHoursField)

Part 8: Update all UI locations

Update:

- Training Record table
- Training Detail
- HRD Review
- Dashboard
- K/S/A Progress
- Employee Directory
- Reports
- Export Excel
- Export PDF

Display examples:

- 1 ชั่วโมง 45 นาที
- 15 นาที
- 2 ชั่วโมง

Part 9: Output Review File

Create:
docs/management-change-duration-minutes-review.md

Include:

1. Summary
2. Database Changes
3. Migration Strategy
4. Duration Picker UI
5. Storage Behavior
6. Display Formatting
7. Dashboard Impact
8. Reports Impact
9. Backward Compatibility
10. Manual Test Checklist
11. Known Limitations

Manual Test Checklist:

- 00:15 saves 15 minutes.
- 01:45 saves 105 minutes.
- 02:00 saves 120 minutes.
- 30:00 saves 1800 minutes.
- 00:00 is blocked.
- More than 30:00 is blocked.
- Training history shows 1 ชั่วโมง 45 นาที.
- Dashboard sums minutes correctly.
- Reports export minutes correctly.
- Legacy decimal hours still display correctly.
- No AM/PM appears anywhere.

Return:

- Complete code changes with file paths.
- Migration command if required.
- Review file.
