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
+35 -38
View File
@@ -35,6 +35,7 @@ import '../../widgets/office_picker.dart';
import '../../widgets/mono_text.dart';
import '../../widgets/responsive_body.dart';
import '../../theme/app_surfaces.dart';
import '../../theme/app_status_colors.dart';
import '../../widgets/sync_pending_badge.dart';
import '../../widgets/task_assignment_section.dart';
import '../../widgets/typing_dots.dart';
@@ -621,9 +622,9 @@ class _TaskDetailScreenState extends ConsumerState<TaskDetailScreen>
const SizedBox(height: 12),
Row(
children: [
const Icon(
Icon(
Icons.warning_amber_rounded,
color: Colors.orange,
color: AppStatusColors.of(context).warning.color,
),
const SizedBox(width: 6),
Expanded(
@@ -2686,16 +2687,24 @@ class _TaskDetailScreenState extends ConsumerState<TaskDetailScreen>
final files =
_attachments ?? [];
if (files.isEmpty) {
return const Center(
return Center(
child: Padding(
padding: EdgeInsets.all(
padding:
const EdgeInsets.all(
16.0,
),
child: Text(
'No attachments yet',
style: TextStyle(
color: Colors.grey,
),
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(
color: Theme.of(
context,
)
.colorScheme
.onSurfaceVariant,
),
),
),
);
@@ -2722,9 +2731,11 @@ class _TaskDetailScreenState extends ConsumerState<TaskDetailScreen>
).textTheme.bodySmall,
),
trailing: IconButton(
icon: const Icon(
icon: Icon(
Icons.delete,
color: Colors.red,
color: Theme.of(
context,
).colorScheme.error,
),
onPressed: () =>
_deleteTaskAttachment(
@@ -2873,7 +2884,11 @@ class _TaskDetailScreenState extends ConsumerState<TaskDetailScreen>
.colorScheme
.surfaceContainerHighest,
borderRadius:
BorderRadius.circular(16),
BorderRadius.circular(
AppSurfaces.of(
context,
).cardRadius,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -4883,39 +4898,21 @@ class _TaskStatusChip extends StatelessWidget {
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
final (icon, bg, fg) = switch (status) {
'queued' => (
Icons.inbox_rounded,
cs.secondaryContainer,
cs.onSecondaryContainer,
),
'in_progress' => (
Icons.play_arrow_rounded,
cs.primaryContainer,
cs.onPrimaryContainer,
),
'completed' => (
Icons.check_circle_rounded,
cs.tertiaryContainer,
cs.onTertiaryContainer,
),
'cancelled' => (
Icons.cancel_rounded,
cs.errorContainer,
cs.onErrorContainer,
),
_ => (
Icons.help_outline_rounded,
cs.surfaceContainerHighest,
cs.onSurface,
),
final s = AppStatusColors.of(context);
final (IconData icon, StatusSwatch swatch) = switch (status) {
'queued' => (Icons.inbox_rounded, s.neutral),
'in_progress' => (Icons.play_arrow_rounded, s.info),
'completed' => (Icons.check_circle_rounded, s.success),
'cancelled' => (Icons.cancel_rounded, s.danger),
_ => (Icons.help_outline_rounded, s.neutral),
};
final bg = swatch.container;
final fg = swatch.onContainer;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(AppSurfaces.of(context).chipRadius),
),
child: Row(
mainAxisSize: MainAxisSize.min,