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.