Files
alla-tms/docs/management-change-duration-minutes-review.md
2026-07-16 09:53:14 +07:00

4.0 KiB

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.