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
+22 -35
View File
@@ -29,6 +29,7 @@ import '../../widgets/responsive_body.dart';
import '../../widgets/tasq_adaptive_list.dart';
import '../../widgets/typing_dots.dart';
import '../../theme/app_surfaces.dart';
import '../../theme/app_status_colors.dart';
import '../../utils/snackbar.dart';
import '../../widgets/app_breakpoints.dart';
import '../../widgets/app_page_header.dart';
@@ -523,10 +524,10 @@ class _TasksListScreenState extends ConsumerState<TasksListScreen>
if (task.status == 'completed' &&
task.hasIncompleteDetails) ...[
const SizedBox(width: 4),
const Icon(
Icon(
Icons.warning_amber_rounded,
size: 16,
color: Colors.orange,
color: AppStatusColors.of(context).warning.color,
),
],
],
@@ -620,10 +621,10 @@ class _TasksListScreenState extends ConsumerState<TasksListScreen>
if (task.status == 'completed' &&
task.hasIncompleteDetails) ...[
const SizedBox(width: 4),
const Icon(
Icon(
Icons.warning_amber_rounded,
size: 16,
color: Colors.orange,
color: AppStatusColors.of(context).warning.color,
),
],
if (showTyping) ...[
@@ -1353,26 +1354,14 @@ class _StatusSummaryCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final background = switch (status) {
'critical' => scheme.errorContainer,
'queued' => scheme.surfaceContainerHighest,
'in_progress' => scheme.secondaryContainer,
'completed' => scheme.primaryContainer,
_ => scheme.surfaceContainerHigh,
};
final foreground = switch (status) {
'critical' => scheme.onErrorContainer,
'queued' => scheme.onSurfaceVariant,
'in_progress' => scheme.onSecondaryContainer,
'completed' => scheme.onPrimaryContainer,
_ => scheme.onSurfaceVariant,
};
final swatch = AppStatusColors.of(context).byKind(_taskStatusKind(status));
final background = swatch.container;
final foreground = swatch.onContainer;
final label = status.replaceAll('_', ' ').toUpperCase();
return Material(
color: background,
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(AppSurfaces.of(context).chipRadius),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: Row(
@@ -1401,6 +1390,16 @@ class _StatusSummaryCard extends StatelessWidget {
}
}
/// Maps a task status to a semantic [StatusKind] (red=critical, blue=in
/// progress, green=completed, grey=queued).
StatusKind _taskStatusKind(String status) => switch (status) {
'critical' => StatusKind.danger,
'in_progress' => StatusKind.info,
'completed' => StatusKind.success,
'queued' => StatusKind.neutral,
_ => StatusKind.neutral,
};
String _assignedAgent(Map<String, Profile> profileById, String? userId) {
if (userId == null || userId.isEmpty) {
return 'Unassigned';
@@ -1422,21 +1421,9 @@ class _StatusBadge extends StatelessWidget {
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final background = switch (status) {
'critical' => scheme.errorContainer,
'queued' => scheme.surfaceContainerHighest,
'in_progress' => scheme.secondaryContainer,
'completed' => scheme.primaryContainer,
_ => scheme.surfaceContainerHighest,
};
final foreground = switch (status) {
'critical' => scheme.onErrorContainer,
'queued' => scheme.onSurfaceVariant,
'in_progress' => scheme.onSecondaryContainer,
'completed' => scheme.onPrimaryContainer,
_ => scheme.onSurfaceVariant,
};
final swatch = AppStatusColors.of(context).byKind(_taskStatusKind(status));
final background = swatch.container;
final foreground = swatch.onContainer;
return Badge(
backgroundColor: background,