# Management Change Duration Minutes Review ## 1. Summary This round starts the migration of training duration handling from decimal hours to total minutes with backward compatibility. The core `training-records` create/edit/review flow now accepts minute-based values, stores both minute and legacy decimal fields, and displays Thai duration text in the main training record UI. ## 2. Database Changes Added new nullable minute-based fields: - `training_records.submitted_minutes` - `training_records.approved_minutes` - `training_policies.total_target_minutes` - `training_policies.k_target_minutes` - `training_policies.s_target_minutes` - `training_policies.a_target_minutes` - `employee_training_targets.total_target_minutes` - `employee_training_targets.k_target_minutes` - `employee_training_targets.s_target_minutes` - `employee_training_targets.a_target_minutes` Migration file: - `drizzle/0012_change_duration_minutes.sql` ## 3. Migration Strategy The migration keeps legacy decimal columns and backfills new minute columns using: - `ROUND(hours * 60)` - `ROUND(approved_hours * 60)` - `ROUND(total_hours * 60)` - `ROUND(k_hours * 60)` - `ROUND(s_hours * 60)` - `ROUND(a_hours * 60)` This allows old code paths to keep working while new UI/API paths move to minutes. ## 4. Duration Picker UI Added: - `src/components/ui/duration-picker.tsx` Behavior: - two dropdowns: `ชั่วโมง` and `นาที` - hour range `00` to `30` - minute step `00`, `15`, `30`, `45` - no AM/PM - no `Date` storage - helper display in `HH:MM` ## 5. Storage Behavior Updated the training record flow so that: - form submit sends `submittedMinutes` - review submit sends `approvedMinutes` - route handlers write new minute fields - route handlers still write decimal `hours` and `approved_hours` for compatibility The compatibility helper is: - `getDurationMinutes(minutesField, legacyHoursField)` ## 6. Display Formatting Added shared helpers in: - `src/features/training-records/utils/time-utils.ts` Helpers: - `formatDurationThai(minutes)` - `formatDurationHHMM(minutes)` - `decimalHoursToMinutes(hours)` - `minutesToDecimalHours(minutes)` - `getDurationMinutes(minutesField, legacyHoursField)` Main updated displays: - training record form - training record review page - training record table - pending review table ## 7. Dashboard Impact Not fully migrated yet. Current state: - the training record source data now exposes `submitted_minutes` and `approved_minutes` - dashboard aggregate logic in `src/features/overview/server/overview-data.ts` still sums legacy decimal hour columns Required next step: - switch aggregate expressions to minute-first calculations with decimal fallback only when minute fields are null ## 8. Reports Impact Not fully migrated yet. Current state: - training record API now exposes minute fields - report dataset and exports still primarily rely on decimal hour aggregations Required next step: - update `src/features/reports/server/report-data.ts` and export formatters to sum/display minutes consistently ## 9. Backward Compatibility Compatibility is preserved by: - keeping old decimal DB columns - still writing `hours` / `approved_hours` - deriving minute display from minute columns first, then decimal fallback This reduces breakage during staged migration. ## 10. 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. ## 11. Known Limitations - Overview, reports, employee directory, notifications, and audit log text are not fully migrated to minute-first calculations yet. - Training policy and employee target UI still use hour-based contracts even though DB minute columns are prepared. - Export Excel/PDF paths were not updated in this round.