UI: Cyber/IT design overhaul with semantic status tokens (Material 3)

Re-theme TasQ with a refined enterprise "Cyber/IT" identity on Material 3.
Functionality is unchanged — only color/shape tokens and a few state widgets.

Foundation:
- Hand-tuned light + dark ColorSchemes (deep-slate surfaces, azure/teal/violet
  accents), tightened 12-16dp shape language, flat hairline-outlined cards.
- New AppStatusColors theme extension (success/warning/danger/info/neutral,
  per-brightness) replacing scattered raw Colors.green/orange/red/grey so status
  color is consistent and legible in dark mode.
- New TechChip mono primitive; StatusPill.kind; M3Card hairline variants;
  tightened AppSurfaces radii.

Per-screen:
- Tokenized status colors + radii across dashboard, tickets, tasks, IT service
  requests, attendance, whereabouts, network map, reports charts, auth, and
  shared/admin screens. Added _ticketStatusKind/_taskStatusKind/_isrStatusKind
  mappers so status carries meaning consistently.
- Fixed white pie-slice borders for dark mode; dropped ad-hoc dialog radius
  overrides in favor of the theme default. Categorical palettes (leave-type
  icons, team color picker, distribution charts) intentionally preserved.

Verified: flutter analyze lib clean; flutter build web green; visual QA in
light and dark (dashboard, tickets, attendance).

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-09-26 15:13:47 +08:00
parent 3cb980b629
commit 57377b1b21
28 changed files with 986 additions and 470 deletions
+24 -22
View File
@@ -35,6 +35,7 @@ import 'logbook_day_activity.dart';
import 'work_log_tab.dart';
import '../../theme/m3_motion.dart';
import '../../theme/app_surfaces.dart';
import '../../theme/app_status_colors.dart';
import '../../utils/app_time.dart';
import '../../utils/device_security.dart';
import '../../utils/location_permission.dart';
@@ -787,7 +788,7 @@ class _CheckInTabState extends ConsumerState<_CheckInTab> {
} else if (diff.inMinutes <= 5) {
timeColor = colors.error;
} else if (diff.inMinutes <= 15) {
timeColor = Colors.orange;
timeColor = AppStatusColors.of(context).warning.color;
} else if (diff.inMinutes <= 30) {
timeColor = colors.tertiary;
}
@@ -847,7 +848,7 @@ class _CheckInTabState extends ConsumerState<_CheckInTab> {
: Icons.cancel,
size: 16,
color: _insideGeofence || hasGeofenceOverride
? Colors.green
? AppStatusColors.of(context).success.color
: colors.error,
),
const SizedBox(width: 6),
@@ -859,7 +860,7 @@ class _CheckInTabState extends ConsumerState<_CheckInTab> {
: 'Outside geofence',
style: theme.textTheme.bodySmall?.copyWith(
color: _insideGeofence || hasGeofenceOverride
? Colors.green
? AppStatusColors.of(context).success.color
: colors.error,
fontWeight: FontWeight.w500,
),
@@ -1863,11 +1864,11 @@ class _CheckInTabState extends ConsumerState<_CheckInTab> {
bg = colors.secondaryContainer;
fg = colors.onSecondaryContainer;
case 'Early Out':
bg = Colors.orange.shade100;
fg = Colors.orange.shade900;
bg = AppStatusColors.of(context).warning.container;
fg = AppStatusColors.of(context).warning.onContainer;
case 'Noon Break':
bg = Colors.blue.shade100;
fg = Colors.blue.shade900;
bg = AppStatusColors.of(context).info.container;
fg = AppStatusColors.of(context).info.onContainer;
case 'Absent':
bg = colors.errorContainer;
fg = colors.onErrorContainer;
@@ -2841,13 +2842,14 @@ class _DateGroupTile extends StatelessWidget {
final colors = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
final status = AppStatusColors.of(context);
final statusColor = group.status == 'Absent'
? Colors.red
? status.danger.color
: group.status == 'On duty'
? Colors.orange
? status.warning.color
: group.status == 'On Leave'
? Colors.teal
: Colors.green;
? status.info.color
: status.success.color;
return Card(
margin: const EdgeInsets.fromLTRB(12, 8, 12, 0),
@@ -2960,10 +2962,10 @@ class _DateGroupTile extends StatelessWidget {
String statusLabel;
switch (slip.status) {
case 'approved':
statusColor = Colors.green;
statusColor = AppStatusColors.of(context).success.color;
statusLabel = 'Approved';
case 'pending':
statusColor = Colors.orange;
statusColor = AppStatusColors.of(context).warning.color;
statusLabel = 'Pending';
case 'rejected':
statusColor = colors.error;
@@ -3081,7 +3083,7 @@ class _DateGroupTile extends StatelessWidget {
? Chip(
label: Text(entry.isLeave ? 'On Leave' : 'Absent'),
backgroundColor: entry.isLeave
? Colors.teal.withValues(alpha: 0.15)
? AppStatusColors.of(context).info.container
: colors.errorContainer,
)
: entry.status == 'On duty'
@@ -3107,7 +3109,7 @@ class _DateGroupTile extends StatelessWidget {
switch (entry.verificationStatus) {
case 'verified':
icon = Icons.verified;
color = Colors.green;
color = AppStatusColors.of(context).success.color;
tooltip = 'Verified';
case 'unverified' || 'skipped':
icon = Icons.warning_amber_rounded;
@@ -3115,7 +3117,7 @@ class _DateGroupTile extends StatelessWidget {
tooltip = 'Unverified';
default:
icon = Icons.hourglass_bottom;
color = Colors.orange;
color = AppStatusColors.of(context).warning.color;
tooltip = 'Pending';
}
final badge = Tooltip(
@@ -3526,7 +3528,7 @@ class _CheckInOutMapTab extends StatelessWidget {
title: 'Check In',
lat: entry.checkInLat,
lng: entry.checkInLng,
markerColor: Colors.green,
markerColor: AppStatusColors.of(context).success.color,
),
),
const SizedBox(width: 12),
@@ -3536,7 +3538,7 @@ class _CheckInOutMapTab extends StatelessWidget {
title: 'Check Out',
lat: entry.checkOutLat,
lng: entry.checkOutLng,
markerColor: Colors.red,
markerColor: AppStatusColors.of(context).danger.color,
),
),
],
@@ -5830,7 +5832,7 @@ class _PassSlipTabState extends ConsumerState<_PassSlipTab> {
IconData statusIcon;
switch (slip.status) {
case 'approved':
statusColor = Colors.green;
statusColor = AppStatusColors.of(context).success.color;
statusIcon = Icons.check_circle;
case 'rejected':
statusColor = colors.error;
@@ -5839,7 +5841,7 @@ class _PassSlipTabState extends ConsumerState<_PassSlipTab> {
statusColor = colors.primary;
statusIcon = Icons.task_alt;
default:
statusColor = Colors.orange;
statusColor = AppStatusColors.of(context).warning.color;
statusIcon = Icons.hourglass_top;
}
@@ -6248,7 +6250,7 @@ class _LeaveTabState extends ConsumerState<_LeaveTab> {
IconData statusIcon;
switch (leave.status) {
case 'approved':
statusColor = Colors.teal;
statusColor = AppStatusColors.of(context).success.color;
statusIcon = Icons.check_circle;
case 'rejected':
statusColor = colors.error;
@@ -6257,7 +6259,7 @@ class _LeaveTabState extends ConsumerState<_LeaveTab> {
statusColor = colors.onSurfaceVariant;
statusIcon = Icons.remove_circle_outline;
default:
statusColor = Colors.orange;
statusColor = AppStatusColors.of(context).warning.color;
statusIcon = Icons.hourglass_top;
}