# 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.
