Files
tasq/lib/utils/app_time.dart
T
redz1029 3cb980b629 QA hardening: security RLS fixes, Flutter 3.47.5 upgrade, UI/validation fixes
Security — enforce write authorization server-side (was UI/RPC-only):
- it_service_requests RLS: block cross-office read/edit + self-approve (QA-015)
- pass_slips RLS: owner can complete but not self-approve (QA-046)
- swap_requests RLS: scope select/update to participants + admin (QA-047)
- storage: tighten it_service_attachments + task_attachments write/delete (QA-027)
- admin_user_management edge function: allow programmers to manage users (QA-016)

Fixes:
- workforce generator "uncovered shifts" false alarms (QA-043/044)
- network-map VLAN + New-location dialog validation, disabled-until-valid (QA-048)
- de-flake time-of-day-dependent dashboard metrics test (QA-045)

Toolchain:
- upgrade to Flutter 3.47.5 / Dart 3.13.4; font_awesome_flutter 11.0.0,
  flutter_quill 11.6.0, pdfrx 2.6.5; clear resulting deprecations (QA-002)

analyze clean; 139 tests pass; web build succeeds. Report + evidence in docs/qa/.

Note: also carries the in-progress Brick model cleanup already present in the
working tree. QA-001 (AI keys public in the build) is deferred by owner decision.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-09-26 11:52:16 +08:00

100 lines
2.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
class AppTime {
static bool _initialized = false;
static void initialize({String location = 'Asia/Manila'}) {
if (_initialized) return;
tz.initializeTimeZones();
tz.setLocalLocation(tz.getLocation(location));
_initialized = true;
}
static DateTime now() {
return tz.TZDateTime.now(tz.local);
}
static DateTime nowUtc() {
return now().toUtc();
}
static DateTime toAppTime(DateTime value) {
final utc = value.isUtc ? value : value.toUtc();
return tz.TZDateTime.from(utc, tz.local);
}
static DateTime parse(String value) {
return toAppTime(DateTime.parse(value));
}
/// "Just now", "5m ago", "3h ago", "2d ago", then a short date after a week.
static String relative(DateTime value) {
final diff = now().difference(value);
if (diff.inMinutes < 1) return 'Just now';
if (diff.inMinutes < 60) return '${diff.inMinutes}m ago';
if (diff.inHours < 24) return '${diff.inHours}h ago';
if (diff.inDays < 7) return '${diff.inDays}d ago';
return formatDate(value);
}
/// Converts a [DateTime] into a human-readable short date string.
///
/// Example: **Jan 05, 2025**. This matches the format previously used by
/// `_formatDate` helpers across multiple screens.
static String formatDate(DateTime value) {
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
final month = months[value.month - 1];
final day = value.day.toString().padLeft(2, '0');
return '$month $day, ${value.year}';
}
/// Formats a [DateTimeRange] as ``start - end`` using [formatDate].
static String formatDateRange(DateTimeRange range) {
return '${formatDate(range.start)} - ${formatDate(range.end)}';
}
/// Renders a [DateTime] in 12‑hour clock notation with AM/PM suffix.
///
/// Example: **08:30 PM**. Used primarily in workforce-related screens.
/// Creates a [DateTime] in the app timezone (Asia/Manila) from date/time components.
///
/// Use this instead of [DateTime] constructor when building a Manila-aware
/// timestamp from separate year/month/day/hour/minute values so that
/// `.toUtc()` correctly accounts for the +08:00 offset.
static DateTime fromComponents({
required int year,
required int month,
required int day,
int hour = 0,
int minute = 0,
}) {
return tz.TZDateTime(tz.local, year, month, day, hour, minute);
}
static String formatTime(DateTime value) {
final rawHour = value.hour;
final hour = (rawHour % 12 == 0 ? 12 : rawHour % 12).toString().padLeft(
2,
'0',
);
final minute = value.minute.toString().padLeft(2, '0');
final suffix = rawHour >= 12 ? 'PM' : 'AM';
return '$hour:$minute $suffix';
}
}