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
+21 -14
View File
@@ -13,6 +13,8 @@ import '../../providers/teams_provider.dart';
import '../../utils/app_time.dart';
import '../../utils/snackbar.dart';
import '../../widgets/app_breakpoints.dart';
import '../../theme/app_status_colors.dart';
import '../../theme/app_surfaces.dart';
import '../../widgets/m3_card.dart';
import '../../widgets/mono_text.dart';
import '../../widgets/profile_avatar.dart';
@@ -231,24 +233,25 @@ class _ItJobChecklistTabState extends ConsumerState<ItJobChecklistTab> {
}
Widget _buildSkeletonTile(BuildContext context, bool isWide) {
final bone = Theme.of(context).colorScheme.surfaceContainerHighest;
return M3Card.outlined(
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
Container(width: 60, height: 14, color: Colors.grey),
Container(width: 60, height: 14, color: bone),
const SizedBox(width: 12),
Expanded(
child: Container(height: 14, color: Colors.grey),
child: Container(height: 14, color: bone),
),
if (isWide) ...[
const SizedBox(width: 12),
Container(width: 80, height: 22, color: Colors.grey),
Container(width: 80, height: 22, color: bone),
const SizedBox(width: 12),
Container(width: 120, height: 14, color: Colors.grey),
Container(width: 120, height: 14, color: bone),
],
const SizedBox(width: 12),
Container(width: 24, height: 24, color: Colors.grey),
Container(width: 24, height: 24, color: bone),
],
),
),
@@ -292,20 +295,21 @@ class _StatsCard extends StatelessWidget {
),
);
final status = AppStatusColors.of(context);
final submittedChip = _StatChip(
icon: Icons.check_circle_outline,
label: 'Submitted',
count: submitted,
color: cs.primary,
bgColor: cs.primaryContainer,
color: status.success.onContainer,
bgColor: status.success.container,
);
final pendingChip = _StatChip(
icon: Icons.pending_outlined,
label: 'Pending',
count: pending,
color: cs.tertiary,
bgColor: cs.tertiaryContainer,
color: status.warning.onContainer,
bgColor: status.warning.container,
);
return M3Card.filled(
@@ -399,7 +403,7 @@ class _StatChip extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(AppSurfaces.of(context).chipRadius),
),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -878,11 +882,14 @@ class _ItJobTileState extends ConsumerState<_ItJobTile> {
_isChecked ? cs.primaryContainer.withValues(alpha: 0.18) : null;
// Status badge
final statusSwatch = _isChecked
? AppStatusColors.of(context).success
: AppStatusColors.of(context).warning;
final statusBadge = Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: _isChecked ? cs.primaryContainer : cs.tertiaryContainer,
borderRadius: BorderRadius.circular(20),
color: statusSwatch.container,
borderRadius: BorderRadius.circular(AppSurfaces.of(context).chipRadius),
),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -892,13 +899,13 @@ class _ItJobTileState extends ConsumerState<_ItJobTile> {
? Icons.check_circle_outline
: Icons.hourglass_empty_outlined,
size: 12,
color: _isChecked ? cs.primary : cs.tertiary,
color: statusSwatch.onContainer,
),
const SizedBox(width: 4),
Text(
_isChecked ? 'Submitted' : 'Pending',
style: tt.labelSmall?.copyWith(
color: _isChecked ? cs.primary : cs.tertiary,
color: statusSwatch.onContainer,
fontWeight: FontWeight.w600,
),
),