From ca6cb7a3fed492e65aec17fb5d776a5c2c47b5e6 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sun, 26 Jul 2026 04:21:42 +0300 Subject: [PATCH] =?UTF-8?q?=D8=AA=D8=B5=D9=85=D9=8A=D9=85=20=D8=B9=D8=A7?= =?UTF-8?q?=D9=84=D9=85=D9=8A=20=D8=AC=D8=AF=D9=8A=D8=AF=D8=8C=20=D9=88?= =?UTF-8?q?=D8=A5=D8=B5=D9=84=D8=A7=D8=AD=20=D8=A7=D9=84=D8=AE=D8=B1=D8=A7?= =?UTF-8?q?=D8=A6=D8=B7=20=D9=88=D9=85=D8=B1=D8=A7=D9=82=D8=A8=20=D8=A7?= =?UTF-8?q?=D9=84=D8=B3=D9=8A=D8=B1=D9=81=D8=B1=D8=A7=D8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/siro-admin/flutter_bootstrap.js | 2 +- siro_admin/lib/constant/colors.dart | 30 +- siro_admin/lib/constant/style.dart | 96 ++- siro_admin/lib/constant/theme.dart | 620 ++++++++++++++++++ siro_admin/lib/main.dart | 36 +- .../lib/views/admin/admin_home_page.dart | 398 +++++------ .../lib/views/admin/dashboard_v2_widget.dart | 129 ++-- siro_admin/lib/views/admin/static/static.dart | 516 +++++++-------- .../lib/views/widgets/glass_container.dart | 49 +- siro_admin/lib/views/widgets/web_sidebar.dart | 164 +++-- 10 files changed, 1367 insertions(+), 673 deletions(-) create mode 100644 siro_admin/lib/constant/theme.dart diff --git a/dashboard/siro-admin/flutter_bootstrap.js b/dashboard/siro-admin/flutter_bootstrap.js index a7e15506..310abb42 100644 --- a/dashboard/siro-admin/flutter_bootstrap.js +++ b/dashboard/siro-admin/flutter_bootstrap.js @@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"6c0baaebf70e0148f485f27d5616b3d3382da7 _flutter.loader.load({ serviceWorkerSettings: { - serviceWorkerVersion: "1228008696" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */ + serviceWorkerVersion: "2460754664" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */ } }); diff --git a/siro_admin/lib/constant/colors.dart b/siro_admin/lib/constant/colors.dart index 156772a0..0a0fc0b7 100644 --- a/siro_admin/lib/constant/colors.dart +++ b/siro_admin/lib/constant/colors.dart @@ -1,38 +1,36 @@ import 'package:flutter/material.dart'; class AppColor { - // --- Core Design Tokens --- - - // Background & Surfaces + AppColor._(); + + // ─── Dark Mode Palette (static fallback / design tokens) ───────────────── static const Color bg = Color(0xFF0A0A0B); static const Color surface = Color(0xFF161618); static const Color surfaceElevated = Color(0xFF222225); static const Color surfaceGlass = Color(0xCC161618); - - // Accents & Branding - static const Color accent = Color(0xFF6366F1); // Indigo / Violet - static const Color accentSoft = Color(0x266366F1); // 15% Opacity - static const Color accentBorder = Color(0x4D6366F1); // 30% Opacity + + static const Color accent = Color(0xFF6366F1); + static const Color accentSoft = Color(0x266366F1); + static const Color accentBorder = Color(0x4D6366F1); static const Color glow = Color(0xFF818CF8); - - // Semantic / State Colors + static const Color danger = Color(0xFFEF4444); static const Color dangerSoft = Color(0x26EF4444); static const Color success = Color(0xFF10B981); static const Color successSoft = Color(0x2610B981); static const Color warning = Color(0xFFF59E0B); + static const Color warningSoft = Color(0x26F59E0B); static const Color info = Color(0xFF3B82F6); - - // Text & Content + static const Color infoSoft = Color(0x263B82F6); + static const Color textPrimary = Color(0xFFF3F4F6); static const Color textSecondary = Color(0xFF9CA3AF); static const Color textMuted = Color(0xFF6B7280); - - // UI Elements + static const Color divider = Color(0xFF2D2D30); static const Color cardShadow = Color(0x66000000); - // --- Legacy Mappings (for temporary compatibility) --- + // ─── Legacy Mappings ───────────────────────────────────────────────────── static const Color primaryColor = bg; static const Color secondaryColor = textPrimary; static const Color accentColor = accent; @@ -40,5 +38,5 @@ class AppColor { static const Color greenColor = success; static const Color blueColor = info; static const Color yellowColor = warning; - static const Color deepPurpleAccent = accent; // Map to accent + static const Color deepPurpleAccent = accent; } diff --git a/siro_admin/lib/constant/style.dart b/siro_admin/lib/constant/style.dart index 694045b1..c607c8e8 100644 --- a/siro_admin/lib/constant/style.dart +++ b/siro_admin/lib/constant/style.dart @@ -4,7 +4,9 @@ import 'package:google_fonts/google_fonts.dart'; import 'colors.dart'; class AppStyle { - // --- Typography --- + AppStyle._(); + + // ─── Static typography (dark mode defaults, backward compatible) ─────────── static TextStyle display = GoogleFonts.inter( fontWeight: FontWeight.w800, @@ -49,7 +51,91 @@ class AppStyle { color: AppColor.accent, ); - // --- Decorations --- + // ─── Context-aware typography (adapts to theme) ─────────────────────────── + + static TextStyle displayFor(BuildContext context) => GoogleFonts.inter( + fontWeight: FontWeight.w800, + fontSize: 32, + color: Theme.of(context).colorScheme.onSurface, + letterSpacing: -1, + ); + + static TextStyle headTitleFor(BuildContext context) => GoogleFonts.cairo( + fontWeight: FontWeight.bold, + fontSize: 24, + color: Theme.of(context).colorScheme.onSurface, + ); + + static TextStyle titleFor(BuildContext context) => GoogleFonts.inter( + fontWeight: FontWeight.w600, + fontSize: 16, + color: Theme.of(context).colorScheme.onSurface, + ); + + static TextStyle subtitleFor(BuildContext context) => GoogleFonts.inter( + fontWeight: FontWeight.w500, + fontSize: 14, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ); + + static TextStyle bodyFor(BuildContext context) => GoogleFonts.inter( + fontWeight: FontWeight.normal, + fontSize: 14, + color: Theme.of(context).colorScheme.onSurface, + ); + + static TextStyle captionFor(BuildContext context) => GoogleFonts.inter( + fontWeight: FontWeight.w400, + fontSize: 12, + color: Theme.of(context).colorScheme.onSurfaceVariant.withValues(alpha: 0.6), + ); + + static TextStyle numberFor(BuildContext context) => GoogleFonts.jetBrainsMono( + fontWeight: FontWeight.bold, + fontSize: 15, + color: Theme.of(context).colorScheme.primary, + ); + + // ─── Context-aware decorations ──────────────────────────────────────────── + + static BoxDecoration cardDecorationFor(BuildContext context) { + final cs = Theme.of(context).colorScheme; + return BoxDecoration( + color: cs.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: cs.outline, width: 1), + boxShadow: [ + BoxShadow( + color: cs.shadow.withValues(alpha: context.isDark ? 0.4 : 0.05), + blurRadius: 20, + offset: const Offset(0, 8), + ), + ], + ); + } + + static BoxDecoration elevatedCardFor(BuildContext context) { + final cs = Theme.of(context).colorScheme; + return BoxDecoration( + color: cs.surfaceContainerHighest, + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: cs.primary.withValues(alpha: context.isDark ? 0.3 : 0.2), + width: 1, + ), + ); + } + + static BoxDecoration glassDecorationFor(BuildContext context) { + final cs = Theme.of(context).colorScheme; + return BoxDecoration( + color: cs.surface.withValues(alpha: context.isDark ? 0.8 : 0.9), + borderRadius: BorderRadius.circular(24), + border: Border.all(color: cs.outline, width: 1), + ); + } + + // ─── Legacy static decorations ──────────────────────────────────────────── static BoxDecoration cardDecoration = BoxDecoration( color: AppColor.surface, @@ -76,8 +162,12 @@ class AppStyle { border: Border.all(color: AppColor.divider, width: 1), ); - // --- Legacy Mappings --- + // ─── Legacy Mappings ───────────────────────────────────────────────────── static TextStyle headTitle2 = headTitle; static BoxDecoration boxDecoration = cardDecoration; static BoxDecoration boxDecoration1 = elevatedCard; } + +extension _Brightness on BuildContext { + bool get isDark => Theme.of(this).brightness == Brightness.dark; +} diff --git a/siro_admin/lib/constant/theme.dart b/siro_admin/lib/constant/theme.dart new file mode 100644 index 00000000..7e620674 --- /dev/null +++ b/siro_admin/lib/constant/theme.dart @@ -0,0 +1,620 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import 'colors.dart'; + +class AppTheme { + AppTheme._(); + + // ─── Dark Theme (Premium SaaS) ───────────────────────────────────────────── + static ThemeData get darkTheme { + final darkScheme = ColorScheme.dark( + brightness: Brightness.dark, + primary: AppColor.accent, + onPrimary: Colors.white, + primaryContainer: AppColor.accentSoft, + onPrimaryContainer: AppColor.accent, + secondary: AppColor.glow, + onSecondary: Colors.white, + secondaryContainer: const Color(0x26818CF8), + onSecondaryContainer: AppColor.glow, + tertiary: AppColor.info, + onTertiary: Colors.white, + surface: AppColor.surface, + onSurface: AppColor.textPrimary, + surfaceContainerHighest: AppColor.surfaceElevated, + surfaceContainerHigh: const Color(0xFF1C1C1F), + surfaceContainerMedium: const Color(0xFF1E1E21), + surfaceContainerLow: const Color(0xFF222225), + surfaceContainer: AppColor.surface, + onSurfaceVariant: AppColor.textSecondary, + error: AppColor.danger, + onError: Colors.white, + errorContainer: AppColor.dangerSoft, + onErrorContainer: AppColor.danger, + outline: AppColor.divider, + outlineVariant: const Color(0xFF3D3D42), + shadow: Colors.black, + scrim: Colors.black54, + inverseSurface: AppColor.textPrimary, + onInverseSurface: AppColor.bg, + inversePrimary: AppColor.glow, + ); + + return ThemeData( + useMaterial3: true, + brightness: Brightness.dark, + fontFamily: GoogleFonts.tajawal().fontFamily, + scaffoldBackgroundColor: AppColor.bg, + colorScheme: darkScheme, + dividerColor: AppColor.divider, + dividerTheme: const DividerThemeData( + color: AppColor.divider, + thickness: 1, + space: 1, + ), + appBarTheme: AppBarTheme( + backgroundColor: AppColor.bg, + elevation: 0, + scrolledUnderElevation: 0, + centerTitle: true, + titleTextStyle: GoogleFonts.tajawal( + color: AppColor.textPrimary, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + iconTheme: const IconThemeData(color: AppColor.textPrimary), + ), + cardTheme: CardThemeData( + color: AppColor.surface, + elevation: 0, + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: const BorderSide(color: AppColor.divider, width: 1), + ), + ), + dialogTheme: DialogThemeData( + backgroundColor: AppColor.surface, + elevation: 24, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + side: const BorderSide(color: AppColor.divider, width: 1), + ), + titleTextStyle: GoogleFonts.tajawal( + color: AppColor.textPrimary, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + contentTextStyle: GoogleFonts.tajawal( + color: AppColor.textSecondary, + fontSize: 14, + ), + ), + bottomSheetTheme: const BottomSheetThemeData( + backgroundColor: AppColor.surface, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + ), + snackBarTheme: SnackBarThemeData( + backgroundColor: AppColor.surfaceElevated, + contentTextStyle: GoogleFonts.tajawal( + color: AppColor.textPrimary, + fontSize: 14, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + behavior: SnackBarBehavior.floating, + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: AppColor.surfaceElevated, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColor.accent, width: 1.5), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColor.danger, width: 1), + ), + labelStyle: const TextStyle(color: AppColor.textSecondary), + hintStyle: const TextStyle(color: AppColor.textMuted), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: AppColor.accent, + foregroundColor: Colors.white, + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + textStyle: GoogleFonts.tajawal( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + outlinedButtonTheme: OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + foregroundColor: AppColor.textPrimary, + side: const BorderSide(color: AppColor.divider), + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + textStyle: GoogleFonts.tajawal( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: AppColor.accent, + textStyle: GoogleFonts.tajawal( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + switchTheme: SwitchThemeData( + thumbColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) return AppColor.success; + return AppColor.textMuted; + }), + trackColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) return AppColor.successSoft; + return AppColor.surfaceElevated; + }), + ), + popupMenuTheme: PopupMenuThemeData( + color: AppColor.surface, + elevation: 8, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: const BorderSide(color: AppColor.divider), + ), + ), + tooltipTheme: TooltipThemeData( + decoration: BoxDecoration( + color: AppColor.surfaceElevated, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: AppColor.divider), + ), + textStyle: GoogleFonts.tajawal( + color: AppColor.textPrimary, + fontSize: 12, + ), + ), + floatingActionButtonTheme: const FloatingActionButtonThemeData( + backgroundColor: AppColor.accent, + foregroundColor: Colors.white, + elevation: 4, + ), + progressIndicatorTheme: const ProgressIndicatorThemeData( + color: AppColor.accent, + linearTrackColor: AppColor.surfaceElevated, + ), + tabBarTheme: TabBarThemeData( + labelColor: AppColor.accent, + unselectedLabelColor: AppColor.textSecondary, + indicatorColor: AppColor.accent, + dividerColor: AppColor.divider, + ), + chipTheme: ChipThemeData( + backgroundColor: AppColor.surfaceElevated, + labelStyle: GoogleFonts.tajawal(color: AppColor.textPrimary, fontSize: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: const BorderSide(color: AppColor.divider), + ), + ), + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.windows: FadeUpwardsPageTransitionsBuilder(), + TargetPlatform.macOS: FadeUpwardsPageTransitionsBuilder(), + TargetPlatform.linux: FadeUpwardsPageTransitionsBuilder(), + TargetPlatform.fuchsia: FadeUpwardsPageTransitionsBuilder(), + TargetPlatform.android: CupertinoPageTransitionsBuilder(), + TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), + }, + ), + ); + } + + // ─── Light Theme (Premium SaaS) ──────────────────────────────────────────── + static ThemeData get lightTheme { + const bgLight = Color(0xFFF8F9FC); + const surfaceLight = Color(0xFFFFFFFF); + const surfaceElevatedLight = Color(0xFFF1F3F9); + const dividerLight = Color(0xFFE2E5ED); + const textPrimaryLight = Color(0xFF111827); + const textSecondaryLight = Color(0xFF6B7280); + const textMutedLight = Color(0xFF9CA3AF); + const accentLight = Color(0xFF6366F1); + const accentSoftLight = Color(0x1A6366F1); + const accentBorderLight = Color(0x336366F1); + const glowLight = Color(0xFF4F46E5); + const dangerLight = Color(0xFFDC2626); + const dangerSoftLight = Color(0x1ADC2626); + const successLight = Color(0xFF059669); + const successSoftLight = Color(0x1A059669); + const warningLight = Color(0xFFD97706); + const infoLight = Color(0xFF2563EB); + + final lightScheme = ColorScheme.light( + brightness: Brightness.light, + primary: accentLight, + onPrimary: Colors.white, + primaryContainer: accentSoftLight, + onPrimaryContainer: accentLight, + secondary: glowLight, + onSecondary: Colors.white, + secondaryContainer: const Color(0x1A4F46E5), + onSecondaryContainer: glowLight, + tertiary: infoLight, + onTertiary: Colors.white, + surface: surfaceLight, + onSurface: textPrimaryLight, + surfaceContainerHighest: surfaceElevatedLight, + surfaceContainerHigh: const Color(0xFFF5F6FA), + surfaceContainerMedium: const Color(0xFFF8F9FC), + surfaceContainerLow: const Color(0xFFFAFBFD), + surfaceContainer: surfaceLight, + onSurfaceVariant: textSecondaryLight, + error: dangerLight, + onError: Colors.white, + errorContainer: dangerSoftLight, + onErrorContainer: dangerLight, + outline: dividerLight, + outlineVariant: const Color(0xFFD1D5DB), + shadow: Colors.black26, + scrim: Colors.black38, + inverseSurface: textPrimaryLight, + onInverseSurface: surfaceLight, + inversePrimary: const Color(0xFFC7D2FE), + ); + + return ThemeData( + useMaterial3: true, + brightness: Brightness.light, + fontFamily: GoogleFonts.tajawal().fontFamily, + scaffoldBackgroundColor: bgLight, + colorScheme: lightScheme, + dividerColor: dividerLight, + dividerTheme: const DividerThemeData( + color: dividerLight, + thickness: 1, + space: 1, + ), + appBarTheme: AppBarTheme( + backgroundColor: bgLight, + elevation: 0, + scrolledUnderElevation: 0, + centerTitle: true, + titleTextStyle: GoogleFonts.tajawal( + color: textPrimaryLight, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + iconTheme: const IconThemeData(color: textPrimaryLight), + ), + cardTheme: CardThemeData( + color: surfaceLight, + elevation: 0, + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: const BorderSide(color: dividerLight, width: 1), + ), + ), + dialogTheme: DialogThemeData( + backgroundColor: surfaceLight, + elevation: 24, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + side: const BorderSide(color: dividerLight, width: 1), + ), + titleTextStyle: GoogleFonts.tajawal( + color: textPrimaryLight, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + contentTextStyle: GoogleFonts.tajawal( + color: textSecondaryLight, + fontSize: 14, + ), + ), + bottomSheetTheme: const BottomSheetThemeData( + backgroundColor: surfaceLight, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + ), + snackBarTheme: SnackBarThemeData( + backgroundColor: surfaceElevatedLight, + contentTextStyle: GoogleFonts.tajawal( + color: textPrimaryLight, + fontSize: 14, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + behavior: SnackBarBehavior.floating, + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: surfaceElevatedLight, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: accentLight, width: 1.5), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: dangerLight, width: 1), + ), + labelStyle: const TextStyle(color: textSecondaryLight), + hintStyle: const TextStyle(color: textMutedLight), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: accentLight, + foregroundColor: Colors.white, + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + textStyle: GoogleFonts.tajawal( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + outlinedButtonTheme: OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + foregroundColor: textPrimaryLight, + side: const BorderSide(color: dividerLight), + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + textStyle: GoogleFonts.tajawal( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: accentLight, + textStyle: GoogleFonts.tajawal( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + switchTheme: SwitchThemeData( + thumbColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) return successLight; + return textMutedLight; + }), + trackColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) return successSoftLight; + return surfaceElevatedLight; + }), + ), + popupMenuTheme: PopupMenuThemeData( + color: surfaceLight, + elevation: 8, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: const BorderSide(color: dividerLight), + ), + ), + tooltipTheme: TooltipThemeData( + decoration: BoxDecoration( + color: surfaceElevatedLight, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: dividerLight), + ), + textStyle: GoogleFonts.tajawal( + color: textPrimaryLight, + fontSize: 12, + ), + ), + floatingActionButtonTheme: const FloatingActionButtonThemeData( + backgroundColor: accentLight, + foregroundColor: Colors.white, + elevation: 4, + ), + progressIndicatorTheme: const ProgressIndicatorThemeData( + color: accentLight, + linearTrackColor: surfaceElevatedLight, + ), + tabBarTheme: TabBarThemeData( + labelColor: accentLight, + unselectedLabelColor: textSecondaryLight, + indicatorColor: accentLight, + dividerColor: dividerLight, + ), + chipTheme: ChipThemeData( + backgroundColor: surfaceElevatedLight, + labelStyle: GoogleFonts.tajawal(color: textPrimaryLight, fontSize: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: const BorderSide(color: dividerLight), + ), + ), + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.windows: FadeUpwardsPageTransitionsBuilder(), + TargetPlatform.macOS: FadeUpwardsPageTransitionsBuilder(), + TargetPlatform.linux: FadeUpwardsPageTransitionsBuilder(), + TargetPlatform.fuchsia: FadeUpwardsPageTransitionsBuilder(), + TargetPlatform.android: CupertinoPageTransitionsBuilder(), + TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), + }, + ), + ); + } +} + +// ─── Context Extensions for Theme Access ────────────────────────────────────── + +extension ThemeExtensions on BuildContext { + ColorScheme get colors => Theme.of(this).colorScheme; + TextTheme get textStyles => Theme.of(this).textTheme; + Brightness get brightness => Theme.of(this).brightness; + bool get isDark => brightness == Brightness.dark; +} + +// ─── Semantic Color Getters (light/dark adaptive) ───────────────────────────── +// These replace direct AppColor.* usage in views. They automatically adapt +// based on the current theme brightness. + +class AppColors { + AppColors._(); + + static Color bg(BuildContext context) => context.isDark + ? const Color(0xFF0A0A0B) + : const Color(0xFFF8F9FC); + + static Color surface(BuildContext context) => context.isDark + ? const Color(0xFF161618) + : const Color(0xFFFFFFFF); + + static Color surfaceElevated(BuildContext context) => context.isDark + ? const Color(0xFF222225) + : const Color(0xFFF1F3F9); + + static Color surfaceGlass(BuildContext context) => context.isDark + ? const Color(0xCC161618) + : const Color(0xCCFFFFFF); + + static Color accent(BuildContext context) => context.isDark + ? const Color(0xFF6366F1) + : const Color(0xFF6366F1); + + static Color accentSoft(BuildContext context) => context.isDark + ? const Color(0x266366F1) + : const Color(0x1A6366F1); + + static Color accentBorder(BuildContext context) => context.isDark + ? const Color(0x4D6366F1) + : const Color(0x336366F1); + + static Color glow(BuildContext context) => context.isDark + ? const Color(0xFF818CF8) + : const Color(0xFF4F46E5); + + static Color danger(BuildContext context) => context.isDark + ? const Color(0xFFEF4444) + : const Color(0xFFDC2626); + + static Color dangerSoft(BuildContext context) => context.isDark + ? const Color(0x26EF4444) + : const Color(0x1ADC2626); + + static Color success(BuildContext context) => context.isDark + ? const Color(0xFF10B981) + : const Color(0xFF059669); + + static Color successSoft(BuildContext context) => context.isDark + ? const Color(0x2610B981) + : const Color(0x1A059669); + + static Color warning(BuildContext context) => context.isDark + ? const Color(0xFFF59E0B) + : const Color(0xFFD97706); + + static Color warningSoft(BuildContext context) => context.isDark + ? const Color(0x26F59E0B) + : const Color(0x1AD97706); + + static Color info(BuildContext context) => context.isDark + ? const Color(0xFF3B82F6) + : const Color(0xFF2563EB); + + static Color infoSoft(BuildContext context) => context.isDark + ? const Color(0x263B82F6) + : const Color(0x1A2563EB); + + static Color textPrimary(BuildContext context) => context.isDark + ? const Color(0xFFF3F4F6) + : const Color(0xFF111827); + + static Color textSecondary(BuildContext context) => context.isDark + ? const Color(0xFF9CA3AF) + : const Color(0xFF6B7280); + + static Color textMuted(BuildContext context) => context.isDark + ? const Color(0xFF6B7280) + : const Color(0xFF9CA3AF); + + static Color divider(BuildContext context) => context.isDark + ? const Color(0xFF2D2D30) + : const Color(0xFFE2E5ED); + + static Color cardShadow(BuildContext context) => context.isDark + ? const Color(0x66000000) + : const Color(0x0D000000); + + // Gradient helpers for charts + static LinearGradient accentGradient(BuildContext context) => LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + accent(context).withValues(alpha: 0.4), + accent(context).withValues(alpha: 0.0), + ], + ); + + static LinearGradient successGradient(BuildContext context) => LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + success(context).withValues(alpha: 0.4), + success(context).withValues(alpha: 0.0), + ], + ); + + static LinearGradient dangerGradient(BuildContext context) => LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + danger(context).withValues(alpha: 0.4), + danger(context).withValues(alpha: 0.0), + ], + ); + + static LinearGradient infoGradient(BuildContext context) => LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + info(context).withValues(alpha: 0.4), + info(context).withValues(alpha: 0.0), + ], + ); +} diff --git a/siro_admin/lib/main.dart b/siro_admin/lib/main.dart index a6b9ace1..69e59992 100644 --- a/siro_admin/lib/main.dart +++ b/siro_admin/lib/main.dart @@ -9,12 +9,11 @@ import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'constant/box_name.dart'; +import 'constant/theme.dart'; import 'controller/firebase/firbase_messge.dart'; import 'controller/functions/encrypt_decrypt.dart'; import 'firebase_options.dart'; import 'models/db_sql.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'constant/colors.dart'; import 'routes.dart'; import 'binding/initial_binding.dart'; import 'package:flutter/foundation.dart' show kIsWeb; @@ -27,7 +26,7 @@ Future backgroundMessageHandler(RemoteMessage message) async { if (message.data.isNotEmpty && message.notification != null) { FirebaseMessagesController().fireBaseTitles(message); } -} // +} DbSql sql = DbSql.instance; @@ -40,7 +39,6 @@ void main() async { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); - // await FirebaseMessagesController().requestFirebaseMessagingPermission(); FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); @@ -54,8 +52,7 @@ void main() async { DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); - } // Enable Crashlytics collection - // FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError; + } runApp(const MainApp()); } @@ -70,30 +67,9 @@ class MainApp extends StatelessWidget { title: 'Siro Admin', locale: const Locale('ar'), fallbackLocale: const Locale('en'), - themeMode: ThemeMode.dark, - darkTheme: ThemeData( - fontFamily: GoogleFonts.tajawal().fontFamily, - brightness: Brightness.dark, - scaffoldBackgroundColor: AppColor.bg, - primaryColor: AppColor.accent, - colorScheme: const ColorScheme.dark( - primary: AppColor.accent, - secondary: AppColor.accent, - surface: AppColor.surface, - error: AppColor.danger, - ), - dividerColor: AppColor.divider, - appBarTheme: AppBarTheme( - backgroundColor: AppColor.bg, - elevation: 0, - centerTitle: true, - titleTextStyle: GoogleFonts.tajawal( - color: AppColor.textPrimary, - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - ), + themeMode: ThemeMode.system, + theme: AppTheme.lightTheme, + darkTheme: AppTheme.darkTheme, initialBinding: InitialBinding(), initialRoute: box.read(BoxName.phoneVerified) == true ? "/" : "/login", getPages: routes, diff --git a/siro_admin/lib/views/admin/admin_home_page.dart b/siro_admin/lib/views/admin/admin_home_page.dart index d4b38880..617bc5a0 100644 --- a/siro_admin/lib/views/admin/admin_home_page.dart +++ b/siro_admin/lib/views/admin/admin_home_page.dart @@ -8,7 +8,7 @@ import 'package:siro_admin/views/admin/drivers/driver_tracker_screen.dart'; import 'package:siro_admin/views/admin/quality/blacklist_page.dart'; import '../../constant/box_name.dart'; -import '../../constant/colors.dart'; +import '../../constant/theme.dart'; import '../../controller/admin/dashboard_controller.dart'; import '../../controller/admin/static_controller.dart'; import '../../controller/functions/crud.dart'; @@ -40,6 +40,7 @@ import 'package:siro_admin/views/widgets/responsive_layout.dart'; import 'package:siro_admin/views/widgets/web_sidebar.dart'; import '../transit/org_list_page.dart'; import 'package:siro_admin/views/widgets/glass_container.dart'; + class AdminHomePage extends StatefulWidget { const AdminHomePage({super.key}); @@ -57,21 +58,6 @@ class _AdminHomePageState extends State late DashboardController dashboardController; String _searchQuery = ''; - // ══════════════════ DESIGN TOKENS ══════════════════ - // --- Unified with AppColor --- - static const Color _bg = AppColor.bg; - static const Color _surface = AppColor.surface; - static const Color _surfaceElevated = AppColor.surfaceElevated; - static const Color _accent = AppColor.accent; - static const Color _accentBorder = AppColor.accentBorder; - static const Color _danger = AppColor.danger; - static const Color _warning = AppColor.warning; - static const Color _info = AppColor.info; - static const Color _success = AppColor.success; - static const Color _textPrimary = AppColor.textPrimary; - static const Color _textSecondary = AppColor.textSecondary; - static const Color _divider = AppColor.divider; - @override void initState() { super.initState(); @@ -83,7 +69,6 @@ class _AdminHomePageState extends State final String role = box.read('admin_role')?.toString() ?? 'admin'; final String myPhone = box.read(BoxName.adminPhone)?.toString() ?? ''; - // التحقق من الصلاحيات: إما عن طريق الدور أو عن طريق قائمة أرقام السوبر أدمن التقليدية isSuperAdmin = (role == 'super_admin') || (myPhone == '201023248456' || myPhone == '963992952235' || @@ -101,36 +86,35 @@ class _AdminHomePageState extends State super.dispose(); } - // ══════════════════════════════════════════════════════════════ - // BUILD - // ══════════════════════════════════════════════════════════════ @override Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; + return ResponsiveLayout( - mobile: _buildMobileScaffold(), + mobile: _buildMobileScaffold(cs), desktop: Scaffold( - backgroundColor: _bg, + backgroundColor: cs.surface, body: Row( children: [ const WebSidebar(selectedIndex: 0), - Expanded(child: _buildDesktopScaffold()), + Expanded(child: _buildDesktopScaffold(cs)), ], ), ), ); } - Widget _buildMobileScaffold() { + Widget _buildMobileScaffold(ColorScheme cs) { return Scaffold( - backgroundColor: _bg, + backgroundColor: cs.surface, body: RefreshIndicator( onRefresh: () async => await dashboardController.getDashBoard(), - color: _accent, - backgroundColor: _surface, + color: cs.primary, + backgroundColor: cs.surfaceContainerHighest, child: GetBuilder( builder: (controller) { if (controller.dashbord.isEmpty) { - return _buildLoadingState(); + return _buildLoadingState(cs); } final data = controller.dashbord[0]; @@ -139,11 +123,11 @@ class _AdminHomePageState extends State return CustomScrollView( physics: const BouncingScrollPhysics(), slivers: [ - _buildSliverAppBar(controller), - _buildSearchBar(), + _buildSliverAppBar(controller, cs), + _buildSearchBar(cs), if (_searchQuery.isEmpty) const DashboardV2Widget(), if (_searchQuery.isEmpty) - _buildQuickStatsSection(data, controller), + _buildQuickStatsSection(data, controller, cs), SliverPadding( padding: const EdgeInsets.only(bottom: 60), sliver: SliverList( @@ -160,7 +144,7 @@ class _AdminHomePageState extends State child: SlideAnimation( verticalOffset: 40.0, child: FadeInAnimation( - child: _buildCategorySection(category), + child: _buildCategorySection(category, cs), ), ), ); @@ -177,21 +161,21 @@ class _AdminHomePageState extends State ); } - Widget _buildDesktopScaffold() { + Widget _buildDesktopScaffold(ColorScheme cs) { return Scaffold( - backgroundColor: _bg, + backgroundColor: cs.surface, body: GetBuilder( builder: (controller) { if (controller.dashbord.isEmpty) { - return _buildLoadingState(); + return _buildLoadingState(cs); } final data = controller.dashbord[0]; return CustomScrollView( physics: const BouncingScrollPhysics(), slivers: [ - _buildSliverAppBar(controller), + _buildSliverAppBar(controller, cs), const DashboardV2Widget(), - _buildQuickStatsSection(data, controller), + _buildQuickStatsSection(data, controller, cs), ], ); }, @@ -199,10 +183,7 @@ class _AdminHomePageState extends State ); } - // ══════════════════════════════════════════════════════════════ - // LOADING STATE - // ══════════════════════════════════════════════════════════════ - Widget _buildLoadingState() { + Widget _buildLoadingState(ColorScheme cs) { return Center( child: Column( mainAxisSize: MainAxisSize.min, @@ -213,15 +194,15 @@ class _AdminHomePageState extends State decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( - colors: [_accent.withOpacity(0.3), Colors.transparent], + colors: [cs.primary.withValues(alpha: 0.3), Colors.transparent], ), ), - child: const Center( + child: Center( child: SizedBox( width: 28, height: 28, child: CircularProgressIndicator( - color: _accent, + color: cs.primary, strokeWidth: 2.5, ), ), @@ -229,53 +210,55 @@ class _AdminHomePageState extends State ), const SizedBox(height: 16), Text('جاري التحميل...', - style: TextStyle(color: _textSecondary, fontSize: 13)), + style: TextStyle(color: cs.onSurfaceVariant, fontSize: 13)), ], ), ); } - // ══════════════════════════════════════════════════════════════ - // SLIVER APP BAR - // ══════════════════════════════════════════════════════════════ - Widget _buildSliverAppBar(DashboardController controller) { + Widget _buildSliverAppBar(DashboardController controller, ColorScheme cs) { + final isDark = Theme.of(context).brightness == Brightness.dark; + return SliverAppBar( expandedHeight: 130.0, floating: true, pinned: true, - backgroundColor: _bg, + backgroundColor: cs.surface, elevation: 0, flexibleSpace: FlexibleSpaceBar( collapseMode: CollapseMode.pin, background: Stack( fit: StackFit.expand, children: [ - // Aurora gradient background Container( - decoration: const BoxDecoration( + decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, - colors: [ - Color(0xFF0D2137), - Color(0xFF0D1117), - Color(0xFF0F1F1A), - ], + colors: isDark + ? [ + cs.primary.withValues(alpha: 0.08), + cs.surface, + cs.tertiary.withValues(alpha: 0.05), + ] + : [ + cs.primary.withValues(alpha: 0.05), + cs.surface, + cs.tertiary.withValues(alpha: 0.03), + ], ), ), ), - // Subtle glow orbs Positioned( top: -30, left: -40, - child: _GlowOrb(color: _accent, size: 150, opacity: 0.08), + child: _GlowOrb(color: cs.primary, size: 150, opacity: 0.08), ), Positioned( top: -20, right: -20, - child: _GlowOrb(color: _info, size: 120, opacity: 0.06), + child: _GlowOrb(color: cs.tertiary, size: 120, opacity: 0.06), ), - // Content Align( alignment: Alignment.bottomCenter, child: Padding( @@ -283,12 +266,12 @@ class _AdminHomePageState extends State child: Column( mainAxisSize: MainAxisSize.min, children: [ - _buildLogo(), + _buildLogo(cs), const SizedBox(height: 6), Text( isSuperAdmin ? 'Super Admin Panel' : 'Admin Panel', style: TextStyle( - color: _textSecondary, + color: cs.onSurfaceVariant, fontSize: 11, letterSpacing: 1.5, ), @@ -302,13 +285,13 @@ class _AdminHomePageState extends State ), actions: [ _buildHeaderAction( - Icons.refresh_rounded, () => controller.getDashBoard()), + Icons.refresh_rounded, () => controller.getDashBoard(), cs), const SizedBox(width: 8), ], ); } - Widget _buildLogo() { + Widget _buildLogo(ColorScheme cs) { return Row( mainAxisSize: MainAxisSize.min, children: [ @@ -321,19 +304,20 @@ class _AdminHomePageState extends State borderRadius: BorderRadius.circular(12), gradient: LinearGradient( colors: [ - _accent.withOpacity(0.3 + 0.1 * _pulseController.value), - _accent.withOpacity(0.1), + cs.primary.withValues( + alpha: 0.3 + 0.1 * _pulseController.value), + cs.primary.withValues(alpha: 0.1), ], ), border: Border.all( - color: - _accent.withOpacity(0.4 + 0.2 * _pulseController.value), + color: cs.primary.withValues( + alpha: 0.4 + 0.2 * _pulseController.value), width: 1, ), ), - child: const Icon( + child: Icon( Icons.admin_panel_settings_rounded, - color: _accent, + color: cs.primary, size: 18, ), ); @@ -341,8 +325,8 @@ class _AdminHomePageState extends State ), const SizedBox(width: 10), ShaderMask( - shaderCallback: (bounds) => const LinearGradient( - colors: [_accent, _info], + shaderCallback: (bounds) => LinearGradient( + colors: [cs.primary, cs.tertiary], ).createShader(bounds), child: const Text( 'Siro Admin', @@ -358,50 +342,48 @@ class _AdminHomePageState extends State ); } - Widget _buildHeaderAction(IconData icon, VoidCallback onTap) { + Widget _buildHeaderAction(IconData icon, VoidCallback onTap, ColorScheme cs) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(10), - border: Border.all(color: _divider), + border: Border.all(color: cs.outline), ), - child: Icon(icon, color: _textSecondary, size: 18), + child: Icon(icon, color: cs.onSurfaceVariant, size: 18), ), ); } - // ══════════════════════════════════════════════════════════════ - // SEARCH BAR - // ══════════════════════════════════════════════════════════════ - Widget _buildSearchBar() { + Widget _buildSearchBar(ColorScheme cs) { + final isFocused = _searchQuery.isNotEmpty; return SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), child: Container( decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(14), border: Border.all( - color: _searchQuery.isNotEmpty ? _accentBorder : _divider, - width: _searchQuery.isNotEmpty ? 1.5 : 1, + color: isFocused ? cs.primary.withValues(alpha: 0.5) : cs.outline, + width: isFocused ? 1.5 : 1, ), ), child: TextField( controller: _searchController, onChanged: (val) => setState(() => _searchQuery = val), - style: const TextStyle(color: _textPrimary, fontSize: 14), + style: TextStyle(color: cs.onSurface, fontSize: 14), decoration: InputDecoration( hintText: 'ابحث عن خدمة أو ميزة...', - hintStyle: const TextStyle(color: _textSecondary, fontSize: 13), + hintStyle: TextStyle(color: cs.onSurfaceVariant, fontSize: 13), prefixIcon: - const Icon(Icons.search_rounded, color: _accent, size: 20), + Icon(Icons.search_rounded, color: cs.primary, size: 20), suffixIcon: _searchQuery.isNotEmpty ? IconButton( icon: Icon(Icons.close_rounded, - color: _textSecondary, size: 18), + color: cs.onSurfaceVariant, size: 18), onPressed: () { setState(() { _searchQuery = ''; @@ -420,23 +402,21 @@ class _AdminHomePageState extends State ); } - // ══════════════════════════════════════════════════════════════ - // QUICK STATS SECTION - // ══════════════════════════════════════════════════════════════ - Widget _buildQuickStatsSection(dynamic data, DashboardController controller) { + Widget _buildQuickStatsSection( + dynamic data, DashboardController controller, ColorScheme cs) { final highlights = [ _HighlightData( - 'إجمالي الركاب', data['countPassengers'], Icons.group_rounded, _info), + 'إجمالي الركاب', data['countPassengers'], Icons.group_rounded, cs.tertiary), _HighlightData('إجمالي السائقين', data['countDriver'], - Icons.drive_eta_rounded, _warning), + Icons.drive_eta_rounded, cs.secondary), _HighlightData('رحلات الشهر', data['countRideThisMonth'], - Icons.calendar_today_rounded, const Color(0xFFC792EA)), + Icons.calendar_today_rounded, cs.primary), if (isSuperAdmin) _HighlightData('المحفظة', _formatCurrency(data['seferWallet']), - Icons.account_balance_wallet_rounded, _accent), + Icons.account_balance_wallet_rounded, cs.primary), ]; - final detailedStats = _getDetailedStats(data, controller); + final detailedStats = _getDetailedStats(data, controller, cs); return SliverToBoxAdapter( child: Padding( @@ -444,7 +424,6 @@ class _AdminHomePageState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Section label Padding( padding: const EdgeInsets.fromLTRB(20, 8, 20, 10), child: Row( @@ -453,14 +432,14 @@ class _AdminHomePageState extends State width: 3, height: 14, decoration: BoxDecoration( - color: _accent, + color: cs.primary, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), - const Text('نظرة عامة', + Text('نظرة عامة', style: TextStyle( - color: _textSecondary, + color: cs.onSurfaceVariant, fontSize: 11, fontWeight: FontWeight.w600, letterSpacing: 1.2, @@ -468,8 +447,6 @@ class _AdminHomePageState extends State ], ), ), - - // Highlight Cards SizedBox( height: 108, child: ListView.builder( @@ -479,21 +456,18 @@ class _AdminHomePageState extends State itemBuilder: (ctx, i) => Padding( padding: EdgeInsets.only( right: i < highlights.length - 1 ? 10 : 0), - child: _buildHighlightCard(highlights[i]), + child: _buildHighlightCard(highlights[i], cs), ), ), ), - const SizedBox(height: 16), - - // Detailed stats strip Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Container( decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(16), - border: Border.all(color: _divider), + border: Border.all(color: cs.outline), ), child: ClipRRect( borderRadius: BorderRadius.circular(16), @@ -505,12 +479,12 @@ class _AdminHomePageState extends State return Row( mainAxisSize: MainAxisSize.min, children: [ - _buildDetailStatItem(stat), + _buildDetailStatItem(stat, cs), if (i < detailedStats.length - 1) Container( width: 1, height: 36, - color: _divider, + color: cs.outline, ), ], ); @@ -520,7 +494,6 @@ class _AdminHomePageState extends State ), ), ), - const SizedBox(height: 8), ], ), @@ -528,14 +501,14 @@ class _AdminHomePageState extends State ); } - Widget _buildHighlightCard(_HighlightData h) { + Widget _buildHighlightCard(_HighlightData h, ColorScheme cs) { return GlassContainer( width: 155, padding: const EdgeInsets.all(16), borderRadius: 16, gradientColors: [ - h.color.withOpacity(0.18), - h.color.withOpacity(0.03), + h.color.withValues(alpha: 0.18), + h.color.withValues(alpha: 0.03), ], child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -546,7 +519,7 @@ class _AdminHomePageState extends State Container( padding: const EdgeInsets.all(7), decoration: BoxDecoration( - color: h.color.withOpacity(0.12), + color: h.color.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(9), ), child: Icon(h.icon, color: h.color, size: 16), @@ -556,7 +529,7 @@ class _AdminHomePageState extends State height: 6, decoration: BoxDecoration( shape: BoxShape.circle, - color: h.color.withOpacity(0.6), + color: h.color.withValues(alpha: 0.6), ), ), ], @@ -564,8 +537,8 @@ class _AdminHomePageState extends State const Spacer(), Text( h.value.toString(), - style: const TextStyle( - color: _textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 20, fontWeight: FontWeight.w700, height: 1.1, @@ -575,8 +548,8 @@ class _AdminHomePageState extends State const SizedBox(height: 3), Text( h.label, - style: const TextStyle( - color: _textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 10, fontWeight: FontWeight.w500, ), @@ -587,7 +560,7 @@ class _AdminHomePageState extends State ); } - Widget _buildDetailStatItem(Map stat) { + Widget _buildDetailStatItem(Map stat, ColorScheme cs) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14), child: Column( @@ -598,8 +571,8 @@ class _AdminHomePageState extends State const SizedBox(height: 6), Text( stat['value'].toString(), - style: const TextStyle( - color: _textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 14, fontWeight: FontWeight.bold, height: 1, @@ -608,8 +581,8 @@ class _AdminHomePageState extends State const SizedBox(height: 3), Text( stat['title'] as String, - style: const TextStyle( - color: _textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 9, fontWeight: FontWeight.w500, ), @@ -619,10 +592,7 @@ class _AdminHomePageState extends State ); } - // ══════════════════════════════════════════════════════════════ - // CATEGORY SECTION - // ══════════════════════════════════════════════════════════════ - Widget _buildCategorySection(ActionCategory category) { + Widget _buildCategorySection(ActionCategory category, ColorScheme cs) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -634,15 +604,15 @@ class _AdminHomePageState extends State width: 3, height: 14, decoration: BoxDecoration( - color: _accent, + color: cs.primary, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), Text( category.title, - style: const TextStyle( - color: _textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 15, fontWeight: FontWeight.w600, letterSpacing: 0.3, @@ -650,13 +620,13 @@ class _AdminHomePageState extends State ), const SizedBox(width: 10), Expanded( - child: Container(height: 1, color: _divider), + child: Container(height: 1, color: cs.outline), ), const SizedBox(width: 8), Text( '${category.items.length}', - style: const TextStyle( - color: _textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 11, fontWeight: FontWeight.w500, ), @@ -676,26 +646,26 @@ class _AdminHomePageState extends State ), itemCount: category.items.length, itemBuilder: (context, index) => - _buildActionItem(category.items[index]), + _buildActionItem(category.items[index], cs), ), const SizedBox(height: 8), ], ); } - Widget _buildActionItem(ActionItem item) { + Widget _buildActionItem(ActionItem item, ColorScheme cs) { return Material( color: Colors.transparent, child: InkWell( onTap: item.onPressed, borderRadius: BorderRadius.circular(16), - splashColor: item.color.withOpacity(0.1), - highlightColor: item.color.withOpacity(0.05), + splashColor: item.color.withValues(alpha: 0.1), + highlightColor: item.color.withValues(alpha: 0.05), child: Container( decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(16), - border: Border.all(color: _divider), + border: Border.all(color: cs.outline), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -707,15 +677,16 @@ class _AdminHomePageState extends State begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ - item.color.withOpacity(0.20), - item.color.withOpacity(0.08), + item.color.withValues(alpha: 0.20), + item.color.withValues(alpha: 0.08), ], ), borderRadius: BorderRadius.circular(12), - border: Border.all(color: item.color.withOpacity(0.25)), + border: + Border.all(color: item.color.withValues(alpha: 0.25)), boxShadow: [ BoxShadow( - color: item.color.withOpacity(0.15), + color: item.color.withValues(alpha: 0.15), blurRadius: 10, offset: const Offset(0, 3), ), @@ -731,8 +702,8 @@ class _AdminHomePageState extends State textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, - style: const TextStyle( - color: _textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 11, fontWeight: FontWeight.w500, height: 1.3, @@ -746,9 +717,6 @@ class _AdminHomePageState extends State ); } - // ══════════════════════════════════════════════════════════════ - // DATA HELPERS - // ══════════════════════════════════════════════════════════════ List _getFilteredCategories() { final all = _getAllActionCategories(); if (_searchQuery.isEmpty) return all; @@ -772,31 +740,31 @@ class _AdminHomePageState extends State ActionCategory( title: 'المستخدمين', items: [ - ActionItem('الركاب', Icons.people_outline_rounded, _info, + ActionItem('الركاب', Icons.people_outline_rounded, const Color(0xFF3B82F6), () => Get.to(() => Passengrs())), - ActionItem('السائقون', Icons.drive_eta_rounded, _warning, + ActionItem('السائقون', Icons.drive_eta_rounded, const Color(0xFFF59E0B), () => Get.to(() => CaptainsPage())), - ActionItem('المراقب', Icons.track_changes_rounded, _danger, + ActionItem('المراقب', Icons.track_changes_rounded, const Color(0xFFEF4444), () => Get.to(() => SiroTrackerScreen())), ], ), ActionCategory( title: 'مواصلاتي', items: [ - ActionItem('المؤسسات', Icons.directions_bus_filled_rounded, _accent, + ActionItem('المؤسسات', Icons.directions_bus_filled_rounded, const Color(0xFF6366F1), () => Get.to(() => const TransitOrgListPage())), ], ), ActionCategory( title: 'إدارة النظام الجديد', items: [ - ActionItem('أكواد الخصم', Icons.confirmation_number_rounded, _accent, + ActionItem('أكواد الخصم', Icons.confirmation_number_rounded, const Color(0xFF6366F1), () => Get.toNamed('/promo')), - ActionItem('تعديل الأسعار', Icons.settings_suggest_rounded, _warning, + ActionItem('تعديل الأسعار', Icons.settings_suggest_rounded, const Color(0xFFF59E0B), () => Get.toNamed('/kazan')), - ActionItem('الشكاوى', Icons.report_problem_rounded, _danger, + ActionItem('الشكاوى', Icons.report_problem_rounded, const Color(0xFFEF4444), () => Get.toNamed('/complaints')), - ActionItem('مراجعة الوثائق', Icons.assignment_ind_rounded, _info, + ActionItem('مراجعة الوثائق', Icons.assignment_ind_rounded, const Color(0xFF3B82F6), () => Get.toNamed('/driver-docs')), ActionItem('استخبارات السوشيال', Icons.insights_rounded, const Color(0xFFC792EA), () => Get.toNamed('/social-intelligence')), @@ -813,11 +781,11 @@ class _AdminHomePageState extends State Icons.remove_red_eye_rounded, const Color(0xFFC792EA), () => Get.to(() => RideMonitorScreen())), - ActionItem('الإحصائيات', Icons.bar_chart_rounded, _accent, () async { + ActionItem('الإحصائيات', Icons.bar_chart_rounded, const Color(0xFF6366F1), () async { await Get.put(StaticController()).getAll(); Get.to(() => const StaticDash()); }), - ActionItem('التحليلات المتقدمة', Icons.analytics_rounded, _info, + ActionItem('التحليلات المتقدمة', Icons.analytics_rounded, const Color(0xFF3B82F6), () => Get.to(() => const AdvancedAnalyticsPage())), ActionItem('لوحة البيانات التفاعلية', Icons.dashboard_customize_rounded, const Color(0xFF00CEC9), @@ -827,7 +795,7 @@ class _AdminHomePageState extends State ActionCategory( title: 'الجودة والدعم', items: [ - ActionItem('القائمة السوداء', Icons.block_flipped, _danger, + ActionItem('القائمة السوداء', Icons.block_flipped, const Color(0xFFEF4444), () => Get.to(() => const BlacklistPage())), ], ), @@ -836,16 +804,16 @@ class _AdminHomePageState extends State title: 'المالية والإدارة', items: [ ActionItem('الإدارة المالية V2', Icons.account_balance_rounded, - _accent, () => Get.to(() => const FinancialV2Page())), - ActionItem('المحفظة', Icons.account_balance_wallet_rounded, _accent, + const Color(0xFF6366F1), () => Get.to(() => const FinancialV2Page())), + ActionItem('المحفظة', Icons.account_balance_wallet_rounded, const Color(0xFF6366F1), () => Get.to(() => Wallet())), - ActionItem('هدية 300', Icons.card_giftcard_rounded, _warning, + ActionItem('هدية 300', Icons.card_giftcard_rounded, const Color(0xFFF59E0B), () => Get.to(() => DriverGiftCheckPage())), ActionItem('الفواتير', Icons.receipt_long_rounded, - const Color(0xFF80CBC4), () => Get.to(() => InvoiceListPage())), - ActionItem('الموظفون', Icons.badge_rounded, const Color(0xFFB0BEC5), + const Color(0xFF10B981), () => Get.to(() => InvoiceListPage())), + ActionItem('الموظفون', Icons.badge_rounded, const Color(0xFF9CA3AF), () => Get.to(() => EmployeePage())), - ActionItem('موافقة المشرفين', Icons.how_to_reg_rounded, _accent, + ActionItem('موافقة المشرفين', Icons.how_to_reg_rounded, const Color(0xFF6366F1), () => Get.to(() => const PendingAdminsPage())), ], ), @@ -854,11 +822,11 @@ class _AdminHomePageState extends State title: 'النظام والتواصل', items: [ ActionItem('سجل العمليات', Icons.admin_panel_settings_rounded, - _danger, () => Get.to(() => const AuditLogsPage())), + const Color(0xFFEF4444), () => Get.to(() => const AuditLogsPage())), ActionItem('واتساب جماعي', Icons.message_rounded, - const Color(0xFF4CAF50), () => _showWhatsAppDialog(context)), + const Color(0xFF25D366), () => _showWhatsAppDialog(context)), ActionItem('أتمتة التسويق', Icons.campaign_rounded, - const Color(0xFF80CBC4), () => Get.toNamed('/marketing')), + const Color(0xFF10B981), () => Get.toNamed('/marketing')), ActionItem( 'إشعار سائقين', Icons.notifications_active_rounded, @@ -871,21 +839,17 @@ class _AdminHomePageState extends State const Color(0xFFF06292), () => Get.put(NotificationController()) .sendNotificationPassengers()), - ActionItem('تسجيل سائق', Icons.person_add_rounded, _info, + ActionItem('تسجيل سائق', Icons.person_add_rounded, const Color(0xFF3B82F6), () => Get.to(() => DriversPendingPage())), ActionItem( 'تحديث التطبيق', Icons.system_update_rounded, - const Color(0xFFA1887F), + const Color(0xFF9CA3AF), () => Get.to(() => PackageUpdateScreen())), - ActionItem('مراقب السيرفر', Icons.dns_rounded, _accent, + ActionItem('مراقب السيرفر', Icons.dns_rounded, const Color(0xFF6366F1), () => Get.to(() => ServerMonitorPage())), - ActionItem('سجل الأخطاء', Icons.error_outline_rounded, _danger, + ActionItem('سجل الأخطاء', Icons.error_outline_rounded, const Color(0xFFEF4444), () => Get.to(() => ErrorListPage())), - // ActionItem('encrypt fp', Icons.error_outline_rounded, _danger, - // () => Get.to(() => FingerprintMigrationTool())), - // ActionItem('encrypt fp drivers', Icons.error_outline_rounded, - // _danger, () => Get.to(() => DriverFingerprintMigrationTool())), ActionItem( 'أداة التشفير', Icons.lock_rounded, @@ -902,13 +866,13 @@ class _AdminHomePageState extends State ActionItem( 'إضافة مدير', Icons.admin_panel_settings_rounded, - _accent, + const Color(0xFF6366F1), () => Get.to(() => const AddStaffPage(role: 'admin')), ), ActionItem( 'إضافة خدمة عملاء', Icons.support_agent_rounded, - _info, + const Color(0xFF3B82F6), () => Get.to(() => const AddStaffPage(role: 'service')), ), ], @@ -917,44 +881,44 @@ class _AdminHomePageState extends State } List> _getDetailedStats( - dynamic data, DashboardController controller) { + dynamic data, DashboardController controller, ColorScheme cs) { return [ if (isSuperAdmin) { 'title': 'رصيد الرسائل', 'value': controller.creditSMS, 'icon': Icons.sms_rounded, - 'color': _info, + 'color': cs.tertiary, }, { 'title': 'مكتملة', 'value': data['completed_rides'], 'icon': Icons.check_circle_rounded, - 'color': _success, + 'color': const Color(0xFF10B981), }, { 'title': 'ملغاة', 'value': data['cancelled_rides'], 'icon': Icons.cancel_rounded, - 'color': _danger, + 'color': const Color(0xFFEF4444), }, { 'title': 'مدفوعات', 'value': _formatCurrency(data['payments']), 'icon': Icons.attach_money_rounded, - 'color': _warning, + 'color': const Color(0xFFF59E0B), }, { 'title': 'Comfort', 'value': data['comfort'], 'icon': Icons.chair_rounded, - 'color': const Color(0xFF80CBC4), + 'color': const Color(0xFF10B981), }, { 'title': 'Speed', 'value': data['speed'], 'icon': Icons.flash_on_rounded, - 'color': const Color(0xFFFFD54F), + 'color': const Color(0xFFFBBF24), }, { 'title': 'Lady', @@ -965,23 +929,22 @@ class _AdminHomePageState extends State ]; } - // ══════════════════════════════════════════════════════════════ - // WHATSAPP DIALOG - // ══════════════════════════════════════════════════════════════ void _showWhatsAppDialog(BuildContext context) { + final cs = Theme.of(context).colorScheme; + Get.dialog( Dialog( backgroundColor: Colors.transparent, child: Container( decoration: BoxDecoration( - color: _surfaceElevated, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(24), - border: Border.all(color: _divider), - boxShadow: const [ + border: Border.all(color: cs.outline), + boxShadow: [ BoxShadow( - color: Colors.black45, + color: cs.shadow.withValues(alpha: 0.3), blurRadius: 30, - offset: Offset(0, 12), + offset: const Offset(0, 12), ), ], ), @@ -992,45 +955,44 @@ class _AdminHomePageState extends State Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: const Color(0xFF4CAF50).withAlpha(30), // ~0.12 opacity + color: const Color(0xFF25D366).withValues(alpha: 0.12), shape: BoxShape.circle, border: Border.all( - color: const Color(0xFF4CAF50) - .withAlpha(64)), // ~0.25 opacity + color: const Color(0xFF25D366).withValues(alpha: 0.25)), ), child: const Icon(Icons.message_rounded, - color: Color(0xFF4CAF50), size: 28), + color: Color(0xFF25D366), size: 28), ), const SizedBox(height: 16), - const Text( + Text( 'إرسال واتساب جماعي', style: TextStyle( - color: _textPrimary, + color: cs.onSurface, fontSize: 17, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 4), - const Text( + Text( 'سيتم إرسال الرسالة لجميع السائقين', - style: TextStyle(color: _textSecondary, fontSize: 11), + style: TextStyle(color: cs.onSurfaceVariant, fontSize: 11), ), const SizedBox(height: 20), Container( decoration: BoxDecoration( - color: _bg, + color: cs.surface, borderRadius: BorderRadius.circular(14), - border: Border.all(color: _divider), + border: Border.all(color: cs.outline), ), child: TextField( controller: _messageController, maxLines: 4, - style: const TextStyle(color: _textPrimary, fontSize: 13), - decoration: const InputDecoration( + style: TextStyle(color: cs.onSurface, fontSize: 13), + decoration: InputDecoration( hintText: 'اكتب رسالتك هنا...', - hintStyle: TextStyle(color: _textSecondary, fontSize: 12), + hintStyle: TextStyle(color: cs.onSurfaceVariant, fontSize: 12), border: InputBorder.none, - contentPadding: EdgeInsets.all(14), + contentPadding: const EdgeInsets.all(14), ), ), ), @@ -1044,12 +1006,12 @@ class _AdminHomePageState extends State padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), - side: const BorderSide(color: _divider), + side: BorderSide(color: cs.outline), ), ), - child: const Text( + child: Text( 'إلغاء', - style: TextStyle(color: _textSecondary, fontSize: 13), + style: TextStyle(color: cs.onSurfaceVariant, fontSize: 13), ), ), ), @@ -1060,7 +1022,7 @@ class _AdminHomePageState extends State label: const Text('إرسال', style: TextStyle(fontSize: 13)), style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF4CAF50), + backgroundColor: const Color(0xFF25D366), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( @@ -1108,9 +1070,6 @@ class _AdminHomePageState extends State } } -// ══════════════════════════════════════════════════════════════ -// HELPER WIDGETS -// ══════════════════════════════════════════════════════════════ class _GlowOrb extends StatelessWidget { final Color color; final double size; @@ -1127,16 +1086,13 @@ class _GlowOrb extends StatelessWidget { decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( - colors: [color.withOpacity(opacity), Colors.transparent], + colors: [color.withValues(alpha: opacity), Colors.transparent], ), ), ); } } -// ══════════════════════════════════════════════════════════════ -// DATA CLASSES -// ══════════════════════════════════════════════════════════════ class _HighlightData { final String label; final dynamic value; diff --git a/siro_admin/lib/views/admin/dashboard_v2_widget.dart b/siro_admin/lib/views/admin/dashboard_v2_widget.dart index 4bea3868..8859a1e6 100644 --- a/siro_admin/lib/views/admin/dashboard_v2_widget.dart +++ b/siro_admin/lib/views/admin/dashboard_v2_widget.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:siro_admin/constant/colors.dart'; +import 'package:siro_admin/constant/theme.dart'; import 'package:siro_admin/controller/admin/dashboard_v2_controller.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:siro_admin/views/widgets/glass_container.dart'; @@ -10,15 +10,17 @@ class DashboardV2Widget extends StatelessWidget { @override Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; + return GetBuilder( init: DashboardV2Controller(), builder: (ctrl) { if (ctrl.isLoading) { - return const SliverToBoxAdapter( + return SliverToBoxAdapter( child: SizedBox( height: 150, child: Center( - child: CircularProgressIndicator(color: AppColor.accent), + child: CircularProgressIndicator(color: cs.primary), ), ), ); @@ -28,16 +30,13 @@ class DashboardV2Widget extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // 1. Real-time stats - _buildSectionTitle('مركز العمليات الحي (Real-time)'), - _buildRealtimeStats(ctrl.realtimeData), - + _buildSectionTitle('مركز العمليات الحي (Real-time)', cs), + _buildRealtimeStats(ctrl.realtimeData, cs), const SizedBox(height: 20), - - // 2. Smart Alerts if (ctrl.smartAlerts.isNotEmpty) ...[ - _buildSectionTitle('التنبيهات الذكية (${ctrl.smartAlerts.length})'), - _buildSmartAlerts(ctrl.smartAlerts), + _buildSectionTitle( + 'التنبيهات الذكية (${ctrl.smartAlerts.length})', cs), + _buildSmartAlerts(ctrl.smartAlerts, cs), const SizedBox(height: 10), ] ], @@ -47,7 +46,7 @@ class DashboardV2Widget extends StatelessWidget { ); } - Widget _buildSectionTitle(String title) { + Widget _buildSectionTitle(String title, ColorScheme cs) { return Padding( padding: const EdgeInsets.fromLTRB(20, 8, 20, 10), child: Row( @@ -56,15 +55,15 @@ class DashboardV2Widget extends StatelessWidget { width: 3, height: 14, decoration: BoxDecoration( - color: AppColor.danger, // Distinct color + color: cs.error, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), Text( title, - style: const TextStyle( - color: AppColor.textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 12, fontWeight: FontWeight.bold, letterSpacing: 0.5, @@ -75,43 +74,43 @@ class DashboardV2Widget extends StatelessWidget { ); } - Widget _buildRealtimeStats(Map data) { + Widget _buildRealtimeStats(Map data, ColorScheme cs) { final stats = [ { 'title': 'رحلات نشطة', 'value': data['active_rides']?.toString() ?? '0', 'icon': Icons.directions_car_rounded, - 'color': AppColor.info, + 'color': cs.tertiary, }, { 'title': 'سائقون أونلاين', 'value': data['online_drivers']?.toString() ?? '0', 'icon': Icons.wifi_tethering, - 'color': AppColor.success, + 'color': const Color(0xFF10B981), }, { 'title': 'إيرادات اليوم', 'value': '${data['revenue_today'] ?? 0}', 'icon': Icons.monetization_on_rounded, - 'color': AppColor.warning, + 'color': const Color(0xFFF59E0B), }, { 'title': 'إيرادات الأمس', 'value': '${data['revenue_yesterday'] ?? 0}', 'icon': Icons.history_rounded, - 'color': Colors.grey, + 'color': cs.onSurfaceVariant, }, { 'title': 'شكاوى مفتوحة', 'value': data['new_complaints']?.toString() ?? '0', 'icon': Icons.warning_rounded, - 'color': AppColor.danger, + 'color': cs.error, }, { 'title': 'رخص تنتهي قريبًا', 'value': data['expiring_licenses']?.toString() ?? '0', 'icon': Icons.sd_card_alert_rounded, - 'color': Colors.orangeAccent, + 'color': const Color(0xFFF97316), }, ]; @@ -125,22 +124,22 @@ class DashboardV2Widget extends StatelessWidget { final stat = stats[i]; return Padding( padding: const EdgeInsets.only(right: 10), - child: _buildStatCard(stat), + child: _buildStatCard(stat, cs), ); }, ), ); } - Widget _buildStatCard(Map stat) { + Widget _buildStatCard(Map stat, ColorScheme cs) { final color = stat['color'] as Color; return GlassContainer( width: 150, padding: const EdgeInsets.all(16), borderRadius: 16, gradientColors: [ - color.withOpacity(0.15), - color.withOpacity(0.05), + color.withValues(alpha: 0.15), + color.withValues(alpha: 0.05), ], child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -151,36 +150,34 @@ class DashboardV2Widget extends StatelessWidget { Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( - color: color.withOpacity(0.15), + color: color.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(8), ), child: Icon(stat['icon'] as IconData, color: color, size: 16), ), const Spacer(), - // Pulsing indicator for active things - if (stat['title'] == 'رحلات نشطة' || stat['title'] == 'سائقون أونلاين') + if (stat['title'] == 'رحلات نشطة' || + stat['title'] == 'سائقون أونلاين') Container( - width: 8, - height: 8, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - boxShadow: [ - BoxShadow( - color: color.withOpacity(0.5), - blurRadius: 4, - spreadRadius: 1, - ) - ] - ), - ), + width: 8, + height: 8, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color, + boxShadow: [ + BoxShadow( + color: color.withValues(alpha: 0.5), + blurRadius: 4, + spreadRadius: 1, + ) + ])), ], ), const Spacer(), Text( stat['value'].toString(), - style: const TextStyle( - color: AppColor.textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 20, fontWeight: FontWeight.bold, ), @@ -188,8 +185,8 @@ class DashboardV2Widget extends StatelessWidget { const SizedBox(height: 2), Text( stat['title'].toString(), - style: const TextStyle( - color: AppColor.textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 10, fontWeight: FontWeight.w500, ), @@ -201,12 +198,12 @@ class DashboardV2Widget extends StatelessWidget { ); } - Widget _buildSmartAlerts(List alerts) { + Widget _buildSmartAlerts(List alerts, ColorScheme cs) { return ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - itemCount: alerts.length > 5 ? 5 : alerts.length, // Show top 5 + itemCount: alerts.length > 5 ? 5 : alerts.length, itemBuilder: (context, index) { final alert = alerts[index]; return AnimationConfiguration.staggeredList( @@ -215,7 +212,7 @@ class DashboardV2Widget extends StatelessWidget { child: SlideAnimation( verticalOffset: 20.0, child: FadeInAnimation( - child: _buildAlertItem(alert), + child: _buildAlertItem(alert, cs), ), ), ); @@ -223,17 +220,17 @@ class DashboardV2Widget extends StatelessWidget { ); } - Widget _buildAlertItem(Map alert) { + Widget _buildAlertItem(Map alert, ColorScheme cs) { Color getSeverityColor(String severity) { switch (severity) { case 'high': - return AppColor.danger; + return cs.error; case 'medium': - return AppColor.warning; + return const Color(0xFFF59E0B); case 'warning': - return Colors.orangeAccent; + return const Color(0xFFF97316); default: - return AppColor.info; + return cs.tertiary; } } @@ -257,12 +254,12 @@ class DashboardV2Widget extends StatelessWidget { margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: AppColor.surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(12), - border: Border.all(color: color.withOpacity(0.4)), + border: Border.all(color: color.withValues(alpha: 0.4)), boxShadow: [ BoxShadow( - color: color.withOpacity(0.05), + color: color.withValues(alpha: 0.05), blurRadius: 5, offset: const Offset(0, 2), ) @@ -274,7 +271,7 @@ class DashboardV2Widget extends StatelessWidget { Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: color.withOpacity(0.1), + color: color.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(10), ), child: Icon(icon, color: color, size: 20), @@ -286,8 +283,8 @@ class DashboardV2Widget extends StatelessWidget { children: [ Text( alert['title'] ?? '', - style: const TextStyle( - color: AppColor.textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 13, fontWeight: FontWeight.bold, ), @@ -295,8 +292,8 @@ class DashboardV2Widget extends StatelessWidget { const SizedBox(height: 4), Text( alert['description'] ?? '', - style: const TextStyle( - color: AppColor.textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 11, ), maxLines: 2, @@ -314,14 +311,14 @@ class DashboardV2Widget extends StatelessWidget { alert['date'] != null ? alert['date'].toString().split(' ')[0] : '', - style: const TextStyle( - color: AppColor.textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 10, ), ), const SizedBox(height: 10), Icon(Icons.arrow_forward_ios_rounded, - color: AppColor.textSecondary, size: 12), + color: cs.onSurfaceVariant, size: 12), ], ), ], diff --git a/siro_admin/lib/views/admin/static/static.dart b/siro_admin/lib/views/admin/static/static.dart index 34dc1c82..0c770b2e 100644 --- a/siro_admin/lib/views/admin/static/static.dart +++ b/siro_admin/lib/views/admin/static/static.dart @@ -3,60 +3,38 @@ import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart' hide TextDirection; +import 'package:siro_admin/constant/theme.dart'; import 'package:siro_admin/controller/admin/static_controller.dart'; import 'notes_driver_page.dart'; -// ══════════════════════════════════════════════════════════════ -// DESIGN TOKENS -// ══════════════════════════════════════════════════════════════ -const Color _bg = Color(0xFF0D1117); -const Color _surface = Color(0xFF161B22); -const Color _surfaceElevated = Color(0xFF1C2333); -const Color _accent = Color(0xFF00D4AA); -const Color _danger = Color(0xFFFF5370); -const Color _warning = Color(0xFFFFCB6B); -const Color _info = Color(0xFF82AAFF); -const Color _purple = Color(0xFFC792EA); -const Color _textPrimary = Color(0xFFE6EDF3); -const Color _textSecondary = Color(0xFF7D8590); -const Color _divider = Color(0xFF21262D); - class StaticDash extends StatelessWidget { const StaticDash({super.key}); @override Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; + final isDark = Theme.of(context).brightness == Brightness.dark; + return Scaffold( - backgroundColor: _bg, + backgroundColor: cs.surface, body: GetBuilder( builder: (c) { return CustomScrollView( physics: const BouncingScrollPhysics(), slivers: [ - // ── App Bar ──────────────────────────────────── - _SliverHeader(controller: c), - + _SliverHeader(controller: c, cs: cs, isDark: isDark), if (c.isLoading) - const SliverFillRemaining(child: _LoadingState()) + SliverFillRemaining(child: _LoadingState(cs: cs)) else ...[ - // ── Date Badge ───────────────────────────── SliverToBoxAdapter( - child: _DateBadge(controller: c), + child: _DateBadge(controller: c, cs: cs), ), - - // ── KPI Row ──────────────────────────────── SliverToBoxAdapter( - child: _KpiRow(controller: c), + child: _KpiRow(controller: c, cs: cs), ), - - // ── Section: Growth ──────────────────────── const SliverToBoxAdapter( - child: _SectionLabel( - 'نظرة عامة على النمو', - Icons.trending_up_rounded, - ), + child: _SectionLabel('نظرة عامة على النمو', Icons.trending_up_rounded), ), - SliverToBoxAdapter( child: AnimationLimiter( child: Column( @@ -67,39 +45,40 @@ class StaticDash extends StatelessWidget { child: FadeInAnimation(child: w), ), children: [ - _DarkChartCard( + _ChartCard( title: 'الركاب', total: c.totalMonthlyPassengers, spots: c.chartDataPassengers, - compareSpots: c.isComparing - ? c.chartDataPassengersCompare - : null, - color: _info, + compareSpots: + c.isComparing ? c.chartDataPassengersCompare : null, + color: cs.tertiary, icon: Icons.groups_rounded, controller: c, + cs: cs, ), const SizedBox(height: 12), - _DarkChartCard( + _ChartCard( title: 'السائقون', total: c.totalMonthlyDrivers, spots: c.chartDataDrivers, - compareSpots: c.isComparing - ? c.chartDataDriversCompare - : null, - color: _warning, + compareSpots: + c.isComparing ? c.chartDataDriversCompare : null, + color: const Color(0xFFF59E0B), icon: Icons.drive_eta_rounded, controller: c, + cs: cs, ), const SizedBox(height: 12), - _DarkChartCard( + _ChartCard( title: 'الرحلات', total: c.totalMonthlyRides, spots: c.chartDataRides, compareSpots: c.isComparing ? c.chartDataRidesCompare : null, - color: _purple, + color: cs.primary, icon: Icons.map_rounded, controller: c, + cs: cs, ), const SizedBox(height: 24), ], @@ -107,19 +86,12 @@ class StaticDash extends StatelessWidget { ), ), ), - - // ── Section: Team Performance ────────────── const SliverToBoxAdapter( - child: _SectionLabel( - 'أداء فريق العمل', - Icons.workspaces_filled, - ), + child: _SectionLabel('أداء فريق العمل', Icons.workspaces_filled), ), - SliverToBoxAdapter( child: Column( children: [ - // Activations _DynamicMultiLineCard( title: 'تفعيل السائقين', subtitle: 'Activations', @@ -129,9 +101,9 @@ class StaticDash extends StatelessWidget { getCompareSpots: (emp) => c.employeeDataCompare[emp.name]?.notesSpots ?? [], getTotal: (emp) => emp.totalNotes, + cs: cs, ), const SizedBox(height: 12), - // Calls _DynamicMultiLineCard( title: 'عدد المكالمات', subtitle: 'Calls', @@ -142,38 +114,28 @@ class StaticDash extends StatelessWidget { c.employeeDataCompare[emp.name]?.callsSpots ?? [], getTotal: (emp) => emp.totalCalls, onTap: () => Get.to(() => const DailyNotesView()), + cs: cs, ), const SizedBox(height: 24), ], ), ), - - // ── Section: Employee Leaderboard ────────── if (c.employmentStatsList.isNotEmpty) ...[ const SliverToBoxAdapter( - child: _SectionLabel( - 'لوحة الصدارة', - Icons.emoji_events_rounded, - ), + child: _SectionLabel('لوحة الصدارة', Icons.emoji_events_rounded), ), SliverToBoxAdapter( - child: _EmployeeLeaderboard(stats: c.employmentStatsList), + child: _EmployeeLeaderboard(stats: c.employmentStatsList, cs: cs), ), const SliverToBoxAdapter(child: SizedBox(height: 24)), ], - - // ── Section: Registration Follow-up ──────── const SliverToBoxAdapter( - child: _SectionLabel( - 'متابعة التسجيل', - Icons.assignment_turned_in_rounded, - ), + child: _SectionLabel('متابعة التسجيل', Icons.assignment_turned_in_rounded), ), - SliverToBoxAdapter( child: Column( children: [ - _DarkChartCard( + _ChartCard( title: 'سائقين بعد الاتصال', total: c.staticList.isNotEmpty && c.staticList[0]['totalMonthlyMatchingNotes'] != @@ -185,9 +147,10 @@ class StaticDash extends StatelessWidget { compareSpots: c.isComparing ? c.chartDataDriversMatchingNotesCompare : null, - color: _accent, + color: const Color(0xFF10B981), icon: Icons.phone_in_talk_rounded, controller: c, + cs: cs, ), const SizedBox(height: 60), ], @@ -202,12 +165,13 @@ class StaticDash extends StatelessWidget { } } -// ══════════════════════════════════════════════════════════════ -// SLIVER HEADER -// ══════════════════════════════════════════════════════════════ +// ─── SLIVER HEADER ─────────────────────────────────────────────────────────── + class _SliverHeader extends StatelessWidget { final StaticController controller; - const _SliverHeader({required this.controller}); + final ColorScheme cs; + final bool isDark; + const _SliverHeader({required this.controller, required this.cs, required this.isDark}); @override Widget build(BuildContext context) { @@ -215,28 +179,30 @@ class _SliverHeader extends StatelessWidget { expandedHeight: 100, pinned: true, floating: true, - backgroundColor: _bg, + backgroundColor: cs.surface, elevation: 0, leading: GestureDetector( onTap: () => Get.back(), child: Container( margin: const EdgeInsets.all(10), decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(10), - border: Border.all(color: _divider), + border: Border.all(color: cs.outline), ), - child: const Icon(Icons.arrow_back_ios_new_rounded, - color: _textSecondary, size: 16), + child: Icon(Icons.arrow_back_ios_new_rounded, + color: cs.onSurfaceVariant, size: 16), ), ), flexibleSpace: FlexibleSpaceBar( background: Container( - decoration: const BoxDecoration( + decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, - colors: [Color(0xFF0F1F1A), _bg], + colors: isDark + ? [cs.primary.withValues(alpha: 0.06), cs.surface] + : [cs.primary.withValues(alpha: 0.04), cs.surface], ), ), child: Align( @@ -249,17 +215,17 @@ class _SliverHeader extends StatelessWidget { Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: _accent.withOpacity(0.12), + color: cs.primary.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(10), - border: Border.all(color: _accent.withOpacity(0.25)), + border: Border.all(color: cs.primary.withValues(alpha: 0.25)), ), - child: const Icon(Icons.bar_chart_rounded, - color: _accent, size: 18), + child: Icon(Icons.bar_chart_rounded, + color: cs.primary, size: 18), ), const SizedBox(width: 10), ShaderMask( - shaderCallback: (b) => const LinearGradient( - colors: [_accent, _info], + shaderCallback: (b) => LinearGradient( + colors: [cs.primary, cs.tertiary], ).createShader(b), child: const Text( 'لوحة الإحصائيات', @@ -283,29 +249,29 @@ class _SliverHeader extends StatelessWidget { margin: const EdgeInsets.only(right: 16, top: 10, bottom: 10), padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(10), - border: Border.all(color: _divider), + border: Border.all(color: cs.outline), ), - child: const Icon(Icons.refresh_rounded, - color: _textSecondary, size: 18), + child: Icon(Icons.refresh_rounded, + color: cs.onSurfaceVariant, size: 18), ), ), ], bottom: PreferredSize( preferredSize: const Size.fromHeight(1), - child: Container(height: 1, color: _divider), + child: Container(height: 1, color: cs.outline), ), ); } } -// ══════════════════════════════════════════════════════════════ -// DATE BADGE -// ══════════════════════════════════════════════════════════════ +// ─── DATE BADGE ────────────────────────────────────────────────────────────── + class _DateBadge extends StatelessWidget { final StaticController controller; - const _DateBadge({required this.controller}); + final ColorScheme cs; + const _DateBadge({required this.controller, required this.cs}); @override Widget build(BuildContext context) { @@ -317,20 +283,20 @@ class _DateBadge extends StatelessWidget { Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 7), decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), - border: Border.all(color: _divider), + border: Border.all(color: cs.outline), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.calendar_today_rounded, - color: _accent, size: 13), + Icon(Icons.calendar_today_rounded, + color: cs.primary, size: 13), const SizedBox(width: 7), Text( controller.currentDateString, - style: const TextStyle( - color: _textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 12, fontWeight: FontWeight.w600, ), @@ -343,9 +309,9 @@ class _DateBadge extends StatelessWidget { Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 7), decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.grey.withOpacity(0.2)), + border: Border.all(color: cs.outline.withValues(alpha: 0.5)), ), child: Row( mainAxisSize: MainAxisSize.min, @@ -353,16 +319,16 @@ class _DateBadge extends StatelessWidget { Container( width: 6, height: 6, - decoration: const BoxDecoration( + decoration: BoxDecoration( shape: BoxShape.circle, - color: _textSecondary, + color: cs.onSurfaceVariant, ), ), const SizedBox(width: 7), Text( controller.compareDateString, - style: const TextStyle( - color: _textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 12, fontWeight: FontWeight.w500), ), @@ -376,22 +342,22 @@ class _DateBadge extends StatelessWidget { } } -// ══════════════════════════════════════════════════════════════ -// KPI ROW -// ══════════════════════════════════════════════════════════════ +// ─── KPI ROW ───────────────────────────────────────────────────────────────── + class _KpiRow extends StatelessWidget { final StaticController controller; - const _KpiRow({required this.controller}); + final ColorScheme cs; + const _KpiRow({required this.controller, required this.cs}); @override Widget build(BuildContext context) { final items = [ _KpiItem('الركاب', controller.totalMonthlyPassengers, - Icons.groups_rounded, _info), + Icons.groups_rounded, cs.tertiary), _KpiItem('السائقون', controller.totalMonthlyDrivers, - Icons.drive_eta_rounded, _warning), + Icons.drive_eta_rounded, const Color(0xFFF59E0B)), _KpiItem( - 'الرحلات', controller.totalMonthlyRides, Icons.map_rounded, _purple), + 'الرحلات', controller.totalMonthlyRides, Icons.map_rounded, cs.primary), ]; return Padding( @@ -413,9 +379,9 @@ class _KpiRow extends StatelessWidget { return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(14), - border: Border.all(color: item.color.withOpacity(0.2)), + border: Border.all(color: item.color.withValues(alpha: 0.2)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -424,8 +390,8 @@ class _KpiRow extends StatelessWidget { const SizedBox(height: 8), Text( item.value, - style: const TextStyle( - color: _textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 20, fontWeight: FontWeight.w800, height: 1, @@ -433,7 +399,7 @@ class _KpiRow extends StatelessWidget { ), const SizedBox(height: 3), Text(item.label, - style: const TextStyle(color: _textSecondary, fontSize: 10)), + style: TextStyle(color: cs.onSurfaceVariant, fontSize: 10)), ], ), ); @@ -447,9 +413,8 @@ class _KpiItem { const _KpiItem(this.label, this.value, this.icon, this.color); } -// ══════════════════════════════════════════════════════════════ -// SECTION LABEL -// ══════════════════════════════════════════════════════════════ +// ─── SECTION LABEL ─────────────────────────────────────────────────────────── + class _SectionLabel extends StatelessWidget { final String text; final IconData icon; @@ -457,6 +422,7 @@ class _SectionLabel extends StatelessWidget { @override Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; return Padding( padding: const EdgeInsets.fromLTRB(20, 24, 20, 12), child: Row( @@ -465,17 +431,17 @@ class _SectionLabel extends StatelessWidget { width: 3, height: 16, decoration: BoxDecoration( - color: _accent, + color: cs.primary, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 10), - Icon(icon, size: 16, color: _textSecondary), + Icon(icon, size: 16, color: cs.onSurfaceVariant), const SizedBox(width: 8), Text( text, - style: const TextStyle( - color: _textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 15, fontWeight: FontWeight.w700, letterSpacing: 0.3, @@ -487,18 +453,18 @@ class _SectionLabel extends StatelessWidget { } } -// ══════════════════════════════════════════════════════════════ -// DARK SINGLE-LINE CHART CARD -// ══════════════════════════════════════════════════════════════ -class _DarkChartCard extends StatelessWidget { +// ─── CHART CARD (Theme-aware, premium) ─────────────────────────────────────── + +class _ChartCard extends StatelessWidget { final String title, total; final List spots; final List? compareSpots; final Color color; final IconData icon; final StaticController controller; + final ColorScheme cs; - const _DarkChartCard({ + const _ChartCard({ required this.title, required this.total, required this.spots, @@ -506,6 +472,7 @@ class _DarkChartCard extends StatelessWidget { required this.color, required this.icon, required this.controller, + required this.cs, }); @override @@ -521,12 +488,12 @@ class _DarkChartCard extends StatelessWidget { child: Container( height: 340, decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), - border: Border.all(color: color.withOpacity(0.15)), + border: Border.all(color: color.withValues(alpha: 0.15)), boxShadow: [ BoxShadow( - color: color.withOpacity(0.06), + color: color.withValues(alpha: 0.06), blurRadius: 20, offset: const Offset(0, 8), ), @@ -534,7 +501,6 @@ class _DarkChartCard extends StatelessWidget { ), child: Column( children: [ - // Header Padding( padding: const EdgeInsets.all(18), child: Row( @@ -544,9 +510,9 @@ class _DarkChartCard extends StatelessWidget { Container( padding: const EdgeInsets.all(9), decoration: BoxDecoration( - color: color.withOpacity(0.12), + color: color.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(10), - border: Border.all(color: color.withOpacity(0.25)), + border: Border.all(color: color.withValues(alpha: 0.25)), ), child: Icon(icon, color: color, size: 18), ), @@ -555,13 +521,13 @@ class _DarkChartCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, - style: const TextStyle( - color: _textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 14, fontWeight: FontWeight.w700)), Text('الإجمالي: $total', - style: const TextStyle( - color: _textSecondary, fontSize: 11)), + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 11)), ]), ]), if (controller.isComparing) @@ -570,18 +536,17 @@ class _DarkChartCard extends StatelessWidget { width: 16, height: 2, decoration: BoxDecoration( - color: _textSecondary, + color: cs.onSurfaceVariant, borderRadius: BorderRadius.circular(1), )), const SizedBox(width: 5), - const Text('سابق', - style: - TextStyle(color: _textSecondary, fontSize: 10)), + Text('سابق', + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 10)), ]), ], ), ), - // Chart Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(4, 0, 12, 10), @@ -592,23 +557,21 @@ class _DarkChartCard extends StatelessWidget { maxX: days > 0 ? days : 1, minY: 0, maxY: maxY, - lineTouchData: _tooltipData(controller, color), - gridData: _gridData(interval), - titlesData: - _titlesData(interval, xInterval, days, controller), + lineTouchData: _tooltipData(controller, color, cs), + gridData: _gridData(interval, cs), + titlesData: _titlesData(interval, xInterval, days, controller, cs), borderData: FlBorderData(show: false), lineBarsData: [ if (compareSpots != null && compareSpots!.isNotEmpty) - _line(compareSpots!, _textSecondary, - isDashed: true, isStep: true, width: 1.5), - _line(spots, color, isStep: true, width: 2.5), + _line(compareSpots!, cs.onSurfaceVariant, + isDashed: true, isStep: true, width: 1.5, cs: cs), + _line(spots, color, isStep: true, width: 2.5, cs: cs), ], )), ), ), ), - // Control Bar - _ControlBar(controller: controller, accentColor: color), + _ControlBar(controller: controller, accentColor: color, cs: cs), ], ), ), @@ -616,9 +579,8 @@ class _DarkChartCard extends StatelessWidget { } } -// ══════════════════════════════════════════════════════════════ -// DYNAMIC MULTI-LINE CHART CARD 🔥 Uses dynamic employee data -// ══════════════════════════════════════════════════════════════ +// ─── DYNAMIC MULTI-LINE CHART CARD ────────────────────────────────────────── + class _DynamicMultiLineCard extends StatelessWidget { final String title, subtitle; final IconData icon; @@ -627,6 +589,7 @@ class _DynamicMultiLineCard extends StatelessWidget { final List Function(EmployeeChartData) getCompareSpots; final int Function(EmployeeChartData) getTotal; final VoidCallback? onTap; + final ColorScheme cs; const _DynamicMultiLineCard({ required this.title, @@ -637,12 +600,12 @@ class _DynamicMultiLineCard extends StatelessWidget { required this.getCompareSpots, required this.getTotal, this.onTap, + required this.cs, }); @override Widget build(BuildContext context) { final employees = controller.employeeData.values.toList(); - // Sort by total descending for legend ordering employees.sort((a, b) => getTotal(b).compareTo(getTotal(a))); final allSpots = employees.expand((e) => getSpots(e)).toList(); @@ -655,12 +618,12 @@ class _DynamicMultiLineCard extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16), child: Container( decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), - border: Border.all(color: _divider), + border: Border.all(color: cs.outline), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.15), + color: cs.shadow.withValues(alpha: 0.08), blurRadius: 15, offset: const Offset(0, 6), ), @@ -668,7 +631,6 @@ class _DynamicMultiLineCard extends StatelessWidget { ), child: Column( children: [ - // ── Header ────────────────────────────────────── InkWell( onTap: onTap, borderRadius: @@ -682,48 +644,47 @@ class _DynamicMultiLineCard extends StatelessWidget { Container( padding: const EdgeInsets.all(9), decoration: BoxDecoration( - color: _accent.withOpacity(0.10), + color: cs.primary.withValues(alpha: 0.10), borderRadius: BorderRadius.circular(10), - border: Border.all(color: _accent.withOpacity(0.20)), + border: Border.all( + color: cs.primary.withValues(alpha: 0.20)), ), - child: Icon(icon, color: _accent, size: 18), + child: Icon(icon, color: cs.primary, size: 18), ), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, - style: const TextStyle( - color: _textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 14, fontWeight: FontWeight.w700)), Text(subtitle, - style: const TextStyle( - color: _textSecondary, fontSize: 11)), + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 11)), if (onTap != null) - const Text( + Text( 'اضغط لعرض التفاصيل اليومية', style: TextStyle( - color: _accent, + color: cs.primary, fontSize: 9, fontWeight: FontWeight.w600), ), ]), ]), if (onTap != null) - const Icon(Icons.arrow_forward_ios_rounded, - size: 14, color: _textSecondary), + Icon(Icons.arrow_forward_ios_rounded, + size: 14, color: cs.onSurfaceVariant), ], ), ), ), - - // ── Dynamic Legend ────────────────────────────── if (employees.isEmpty) - const Padding( - padding: EdgeInsets.symmetric(vertical: 20), + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), child: Text('لا توجد بيانات', - style: TextStyle(color: _textSecondary)), + style: TextStyle(color: cs.onSurfaceVariant)), ) else ...[ Padding( @@ -737,14 +698,12 @@ class _DynamicMultiLineCard extends StatelessWidget { name: emp.name.toUpperCase(), color: emp.color, total: getTotal(emp), + cs: cs, ); }).toList(), ), ), - const SizedBox(height: 8), - - // ── Chart ─────────────────────────────────── SizedBox( height: 240, child: Padding( @@ -756,27 +715,26 @@ class _DynamicMultiLineCard extends StatelessWidget { maxX: days > 0 ? days : 1, minY: 0, maxY: maxY, - lineTouchData: _tooltipData(controller, _accent), - gridData: _gridData(interval), - titlesData: - _titlesData(interval, xInterval, days, controller), + lineTouchData: _tooltipData(controller, cs.primary, cs), + gridData: _gridData(interval, cs), + titlesData: _titlesData(interval, xInterval, days, controller, cs), borderData: FlBorderData(show: false), lineBarsData: [ - // Compare lines (dashed) if (controller.isComparing) ...employees.map((emp) => _line( getCompareSpots(emp), - emp.color.withOpacity(0.25), + emp.color.withValues(alpha: 0.25), isDashed: true, isStep: false, width: 1.5, + cs: cs, )), - // Main lines ...employees.map((emp) => _line( getSpots(emp), emp.color, isStep: false, width: 2.5, + cs: cs, )), ], )), @@ -784,8 +742,7 @@ class _DynamicMultiLineCard extends StatelessWidget { ), ), ], - - _ControlBar(controller: controller, accentColor: _accent), + _ControlBar(controller: controller, accentColor: cs.primary, cs: cs), ], ), ), @@ -793,24 +750,24 @@ class _DynamicMultiLineCard extends StatelessWidget { } } -// ══════════════════════════════════════════════════════════════ -// LEGEND CHIP -// ══════════════════════════════════════════════════════════════ +// ─── LEGEND CHIP ───────────────────────────────────────────────────────────── + class _LegendChip extends StatelessWidget { final String name; final Color color; final int total; + final ColorScheme cs; const _LegendChip( - {required this.name, required this.color, required this.total}); + {required this.name, required this.color, required this.total, required this.cs}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( - color: color.withOpacity(0.08), + color: color.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(20), - border: Border.all(color: color.withOpacity(0.25)), + border: Border.all(color: color.withValues(alpha: 0.25)), ), child: Row( mainAxisSize: MainAxisSize.min, @@ -826,8 +783,8 @@ class _LegendChip extends StatelessWidget { color: color, fontSize: 10, fontWeight: FontWeight.w700)), const SizedBox(width: 5), Text('$total', - style: const TextStyle( - color: _textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 10, fontWeight: FontWeight.w500)), ], @@ -836,12 +793,12 @@ class _LegendChip extends StatelessWidget { } } -// ══════════════════════════════════════════════════════════════ -// EMPLOYEE LEADERBOARD 🔥 Fully dynamic -// ══════════════════════════════════════════════════════════════ +// ─── EMPLOYEE LEADERBOARD ─────────────────────────────────────────────────── + class _EmployeeLeaderboard extends StatelessWidget { final List stats; - const _EmployeeLeaderboard({required this.stats}); + final ColorScheme cs; + const _EmployeeLeaderboard({required this.stats, required this.cs}); @override Widget build(BuildContext context) { @@ -852,14 +809,13 @@ class _EmployeeLeaderboard extends StatelessWidget { child: Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), - border: Border.all(color: _divider), + border: Border.all(color: cs.outline), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Header Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -867,41 +823,39 @@ class _EmployeeLeaderboard extends StatelessWidget { Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: _warning.withOpacity(0.12), + color: const Color(0xFFF59E0B).withValues(alpha: 0.12), borderRadius: BorderRadius.circular(9), - border: Border.all(color: _warning.withOpacity(0.25)), + border: Border.all( + color: const Color(0xFFF59E0B).withValues(alpha: 0.25)), ), child: const Icon(Icons.emoji_events_rounded, - color: _warning, size: 16), + color: Color(0xFFF59E0B), size: 16), ), const SizedBox(width: 10), - const Column( + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('لوحة الصدارة', style: TextStyle( - color: _textPrimary, + color: cs.onSurface, fontSize: 14, fontWeight: FontWeight.w700)), Text('إجمالي الإدخالات حسب الموظف', - style: - TextStyle(color: _textSecondary, fontSize: 10)), + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 10)), ], ), ]), Text( '${stats.fold(0, (s, e) => s + e.count)} إجمالي', - style: const TextStyle( - color: _textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 11, fontWeight: FontWeight.w600), ), ], ), - const SizedBox(height: 20), - - // Rank rows ...List.generate(stats.length, (i) { final stat = stats[i]; final pct = maxCount > 0 ? stat.count / maxCount : 0.0; @@ -911,8 +865,7 @@ class _EmployeeLeaderboard extends StatelessWidget { children: [ Row( children: [ - // Rank Badge - _RankBadge(rank: i + 1, color: stat.color), + _RankBadge(rank: i + 1, color: stat.color, cs: cs), const SizedBox(width: 12), Expanded( child: Column( @@ -932,8 +885,8 @@ class _EmployeeLeaderboard extends StatelessWidget { ), Text( '${stat.count}', - style: const TextStyle( - color: _textPrimary, + style: TextStyle( + color: cs.onSurface, fontSize: 14, fontWeight: FontWeight.w800, fontFamily: 'monospace', @@ -942,12 +895,11 @@ class _EmployeeLeaderboard extends StatelessWidget { ], ), const SizedBox(height: 6), - // Progress bar ClipRRect( borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: pct.toDouble(), - backgroundColor: _divider, + backgroundColor: cs.outline, valueColor: AlwaysStoppedAnimation(stat.color), minHeight: 5, @@ -972,29 +924,36 @@ class _EmployeeLeaderboard extends StatelessWidget { class _RankBadge extends StatelessWidget { final int rank; final Color color; - const _RankBadge({required this.rank, required this.color}); + final ColorScheme cs; + const _RankBadge({required this.rank, required this.color, required this.cs}); @override Widget build(BuildContext context) { final isTop3 = rank <= 3; - final medalColors = [_warning, _textSecondary, const Color(0xFFCD7F32)]; + final medalColors = [ + const Color(0xFFF59E0B), + cs.onSurfaceVariant, + const Color(0xFFCD7F32) + ]; return Container( width: 32, height: 32, decoration: BoxDecoration( - color: isTop3 ? medalColors[rank - 1].withOpacity(0.12) : _divider, + color: isTop3 + ? medalColors[rank - 1].withValues(alpha: 0.12) + : cs.outline, shape: BoxShape.circle, border: Border.all( color: isTop3 - ? medalColors[rank - 1].withOpacity(0.4) + ? medalColors[rank - 1].withValues(alpha: 0.4) : Colors.transparent), ), child: Center( child: Text( '$rank', style: TextStyle( - color: isTop3 ? medalColors[rank - 1] : _textSecondary, + color: isTop3 ? medalColors[rank - 1] : cs.onSurfaceVariant, fontSize: 12, fontWeight: FontWeight.w800, ), @@ -1004,22 +963,23 @@ class _RankBadge extends StatelessWidget { } } -// ══════════════════════════════════════════════════════════════ -// SHARED CONTROL BAR -// ══════════════════════════════════════════════════════════════ +// ─── CONTROL BAR ───────────────────────────────────────────────────────────── + class _ControlBar extends StatelessWidget { final StaticController controller; final Color accentColor; - const _ControlBar({required this.controller, required this.accentColor}); + final ColorScheme cs; + const _ControlBar( + {required this.controller, required this.accentColor, required this.cs}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration( - color: _bg, + color: cs.surface, borderRadius: const BorderRadius.vertical(bottom: Radius.circular(20)), - border: const Border(top: BorderSide(color: _divider)), + border: Border(top: BorderSide(color: cs.outline)), ), child: Row( children: [ @@ -1042,12 +1002,10 @@ class _ControlBar extends StatelessWidget { ), builder: (ctx, child) => Theme( data: Theme.of(ctx).copyWith( - colorScheme: const ColorScheme.dark( - primary: _accent, - onPrimary: _bg, - surface: _surfaceElevated, - onSurface: _textPrimary, - ), + colorScheme: Theme.of(ctx).colorScheme.copyWith( + primary: accentColor, + surface: cs.surfaceContainerHighest, + ), ), child: child!, ), @@ -1061,9 +1019,9 @@ class _ControlBar extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), decoration: BoxDecoration( - color: _surface, + color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(9), - border: Border.all(color: _divider), + border: Border.all(color: cs.outline), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, @@ -1073,8 +1031,8 @@ class _ControlBar extends StatelessWidget { const SizedBox(width: 7), Text( _formatRange(controller), - style: const TextStyle( - color: _textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 11, fontWeight: FontWeight.w600, ), @@ -1093,13 +1051,13 @@ class _ControlBar extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), decoration: BoxDecoration( color: controller.isComparing - ? _danger.withOpacity(0.10) - : _surface, + ? cs.error.withValues(alpha: 0.10) + : cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(9), border: Border.all( color: controller.isComparing - ? _danger.withOpacity(0.3) - : _divider), + ? cs.error.withValues(alpha: 0.3) + : cs.outline), ), child: Row( children: [ @@ -1108,13 +1066,13 @@ class _ControlBar extends StatelessWidget { ? Icons.visibility_off_outlined : Icons.compare_arrows_rounded, size: 14, - color: controller.isComparing ? _danger : accentColor, + color: controller.isComparing ? cs.error : accentColor, ), const SizedBox(width: 5), Text( controller.isComparing ? 'إلغاء' : 'مقارنة', style: TextStyle( - color: controller.isComparing ? _danger : accentColor, + color: controller.isComparing ? cs.error : accentColor, fontSize: 11, fontWeight: FontWeight.w600, ), @@ -1135,11 +1093,11 @@ class _ControlBar extends StatelessWidget { } } -// ══════════════════════════════════════════════════════════════ -// LOADING STATE -// ══════════════════════════════════════════════════════════════ +// ─── LOADING STATE ─────────────────────────────────────────────────────────── + class _LoadingState extends StatelessWidget { - const _LoadingState(); + final ColorScheme cs; + const _LoadingState({required this.cs}); @override Widget build(BuildContext context) { @@ -1151,29 +1109,29 @@ class _LoadingState extends StatelessWidget { width: 40, height: 40, child: CircularProgressIndicator( - color: _accent, + color: cs.primary, strokeWidth: 2, - backgroundColor: _accent.withOpacity(0.1), + backgroundColor: cs.primary.withValues(alpha: 0.1), ), ), const SizedBox(height: 16), - const Text('جاري تحميل الإحصائيات...', - style: TextStyle(color: _textSecondary, fontSize: 13)), + Text('جاري تحميل الإحصائيات...', + style: TextStyle(color: cs.onSurfaceVariant, fontSize: 13)), ], ), ); } } -// ══════════════════════════════════════════════════════════════ -// CHART HELPERS (pure functions) -// ══════════════════════════════════════════════════════════════ +// ─── CHART HELPERS (World-class premium) ───────────────────────────────────── + LineChartBarData _line( List spots, Color color, { bool isDashed = false, bool isStep = false, double width = 2.5, + required ColorScheme cs, }) { return LineChartBarData( spots: spots, @@ -1183,7 +1141,10 @@ LineChartBarData _line( color: color, barWidth: width + 1, isStrokeCapRound: !isStep, - shadow: Shadow(color: color.withOpacity(0.4), blurRadius: 8, offset: const Offset(0, 4)), + shadow: Shadow( + color: color.withValues(alpha: 0.4), + blurRadius: 8, + offset: const Offset(0, 4)), dotData: const FlDotData(show: false), dashArray: isDashed ? [5, 5] : null, lineChartStepData: const LineChartStepData(stepDirection: 0.5), @@ -1192,18 +1153,22 @@ LineChartBarData _line( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [color.withOpacity(0.35), color.withOpacity(0.0)], + colors: [ + color.withValues(alpha: 0.35), + color.withValues(alpha: 0.0), + ], ), ), ); } -LineTouchData _tooltipData(StaticController c, Color accentColor) { +LineTouchData _tooltipData( + StaticController c, Color accentColor, ColorScheme cs) { return LineTouchData( handleBuiltInTouches: true, touchTooltipData: LineTouchTooltipData( - getTooltipColor: (_) => _surfaceElevated, - tooltipBorder: const BorderSide(color: _divider), + getTooltipColor: (_) => cs.surfaceContainerHighest, + tooltipBorder: BorderSide(color: cs.outline), tooltipPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), tooltipRoundedRadius: 10, getTooltipItems: (spots) { @@ -1216,8 +1181,8 @@ LineTouchData _tooltipData(StaticController c, Color accentColor) { } return LineTooltipItem( '$dateStr\n', - const TextStyle( - color: _textSecondary, + TextStyle( + color: cs.onSurfaceVariant, fontWeight: FontWeight.w600, fontSize: 11), children: [ @@ -1236,13 +1201,13 @@ LineTouchData _tooltipData(StaticController c, Color accentColor) { ); } -FlGridData _gridData(double interval) { +FlGridData _gridData(double interval, ColorScheme cs) { return FlGridData( show: true, drawVerticalLine: false, horizontalInterval: interval, - getDrawingHorizontalLine: (_) => - const FlLine(color: Color(0xFF21262D), strokeWidth: 1), + getDrawingHorizontalLine: (_) => FlLine( + color: cs.outline.withValues(alpha: 0.5), strokeWidth: 1), ); } @@ -1251,6 +1216,7 @@ FlTitlesData _titlesData( double xInterval, double days, StaticController c, + ColorScheme cs, ) { return FlTitlesData( show: true, @@ -1269,8 +1235,8 @@ FlTitlesData _titlesData( padding: const EdgeInsets.only(top: 6), child: Text( DateFormat('d/M', 'en').format(date), - style: const TextStyle( - color: _textSecondary, + style: TextStyle( + color: cs.onSurfaceVariant, fontSize: 9, fontWeight: FontWeight.w600), ), @@ -1285,7 +1251,7 @@ FlTitlesData _titlesData( reservedSize: 36, getTitlesWidget: (value, _) => Text( _formatNum(value), - style: const TextStyle(color: _textSecondary, fontSize: 9), + style: TextStyle(color: cs.onSurfaceVariant, fontSize: 9), ), ), ), diff --git a/siro_admin/lib/views/widgets/glass_container.dart b/siro_admin/lib/views/widgets/glass_container.dart index df4700f0..396d143f 100644 --- a/siro_admin/lib/views/widgets/glass_container.dart +++ b/siro_admin/lib/views/widgets/glass_container.dart @@ -10,6 +10,9 @@ class GlassContainer extends StatelessWidget { final double borderRadius; final List? gradientColors; final double blur; + final double opacity; + final bool showBorder; + final Color? borderColor; const GlassContainer({ super.key, @@ -20,15 +23,35 @@ class GlassContainer extends StatelessWidget { this.margin, this.borderRadius = 16.0, this.gradientColors, - this.blur = 10.0, + this.blur = 20.0, + this.opacity = 0.1, + this.showBorder = true, + this.borderColor, }); @override Widget build(BuildContext context) { - final defaultGradient = [ - Colors.white.withOpacity(0.1), - Colors.white.withOpacity(0.05), - ]; + final isDark = Theme.of(context).brightness == Brightness.dark; + final cs = Theme.of(context).colorScheme; + + final defaultGradient = isDark + ? [ + Colors.white.withValues(alpha: opacity), + Colors.white.withValues(alpha: opacity * 0.5), + ] + : [ + Colors.white.withValues(alpha: 0.7), + Colors.white.withValues(alpha: 0.4), + ]; + + final effectiveBorderColor = borderColor ?? + (isDark + ? Colors.white.withValues(alpha: 0.15) + : Colors.white.withValues(alpha: 0.5)); + + final shadowColor = isDark + ? Colors.black.withValues(alpha: 0.3) + : cs.shadow.withValues(alpha: 0.08); return Container( width: width, @@ -38,10 +61,11 @@ class GlassContainer extends StatelessWidget { borderRadius: BorderRadius.circular(borderRadius), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.1), - blurRadius: 20, - spreadRadius: 1, - ) + color: shadowColor, + blurRadius: 24, + spreadRadius: 0, + offset: const Offset(0, 8), + ), ], ), child: ClipRRect( @@ -52,10 +76,9 @@ class GlassContainer extends StatelessWidget { padding: padding, decoration: BoxDecoration( borderRadius: BorderRadius.circular(borderRadius), - border: Border.all( - color: Colors.white.withOpacity(0.2), - width: 1.5, - ), + border: showBorder + ? Border.all(color: effectiveBorderColor, width: 1) + : null, gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, diff --git a/siro_admin/lib/views/widgets/web_sidebar.dart b/siro_admin/lib/views/widgets/web_sidebar.dart index 47f747d3..310b5272 100644 --- a/siro_admin/lib/views/widgets/web_sidebar.dart +++ b/siro_admin/lib/views/widgets/web_sidebar.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:siro_admin/constant/colors.dart'; +import 'package:siro_admin/constant/theme.dart'; import 'package:siro_admin/main.dart'; import 'package:siro_admin/constant/box_name.dart'; import 'package:siro_admin/views/admin/captain/captain.dart'; @@ -40,6 +40,8 @@ class WebSidebar extends StatelessWidget { @override Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; + final isDark = Theme.of(context).brightness == Brightness.dark; final String myPhone = box.read(BoxName.adminPhone)?.toString() ?? ''; final String role = box.read('admin_role')?.toString() ?? 'admin'; final bool isSuperAdmin = (role == 'super_admin') || @@ -49,11 +51,11 @@ class WebSidebar extends StatelessWidget { return Container( width: 270, - color: AppColor.surface, + color: cs.surface, child: Column( children: [ - _buildLogo(), - const Divider(color: AppColor.divider, height: 1), + _buildLogo(context), + Divider(color: cs.outline, height: 1), Expanded( child: ListView( padding: const EdgeInsets.symmetric(vertical: 12), @@ -68,8 +70,7 @@ class WebSidebar extends StatelessWidget { }, ), - // ─── التحليلات والإحصائيات ────────────────────────────── - _buildSectionHeader('التحليلات والإحصائيات'), + _buildSectionHeader(context, 'التحليلات والإحصائيات'), _buildNavItem( context, title: 'التحليلات الحية', @@ -95,8 +96,7 @@ class WebSidebar extends StatelessWidget { }, ), - // ─── إدارة الكباتن والركاب ────────────────────────────── - _buildSectionHeader('الكباتن والركاب'), + _buildSectionHeader(context, 'الكباتن والركاب'), _buildNavItem( context, title: 'إدارة الركاب', @@ -140,8 +140,7 @@ class WebSidebar extends StatelessWidget { onTap: () => Get.to(() => const BlacklistPage()), ), - // ─── الرحلات والمراقبة ────────────────────────────── - _buildSectionHeader('الرحلات والتتبع'), + _buildSectionHeader(context, 'الرحلات والتتبع'), _buildNavItem( context, title: 'شاشة الرحلات', @@ -157,13 +156,6 @@ class WebSidebar extends StatelessWidget { index: 6, onTap: () => Get.to(() => RideMonitorScreen()), ), - // _buildNavItem( - // context, - // title: 'البحث عن رحلة', - // icon: Icons.search_rounded, - // index: 107, - // onTap: () => Get.to(() => RideLookupPage()), - // ), _buildNavItem( context, title: 'تتبع الكباتن (المراقب)', @@ -172,8 +164,7 @@ class WebSidebar extends StatelessWidget { onTap: () => Get.to(() => SiroTrackerScreen()), ), - // ─── مواصلاتي والشركات ────────────────────────────── - _buildSectionHeader('نظام مواصلاتي'), + _buildSectionHeader(context, 'نظام مواصلاتي'), _buildNavItem( context, title: 'إدارة المؤسسات والشركات', @@ -182,8 +173,7 @@ class WebSidebar extends StatelessWidget { onTap: () => Get.to(() => const TransitOrgListPage()), ), - // ─── المالية والاشتراكات ────────────────────────────── - _buildSectionHeader('المالية والأسعار'), + _buildSectionHeader(context, 'المالية والأسعار'), _buildNavItem( context, title: 'التقرير المالي (V2)', @@ -227,11 +217,10 @@ class WebSidebar extends StatelessWidget { onTap: () => Get.toNamed('/promo'), ), - // ─── التسويق والدعم الفني ────────────────────────────── - _buildSectionHeader('التسويق والدعم'), + _buildSectionHeader(context, 'التسويق والدعم'), _buildNavItem( context, - title: 'حركية التسويق والـ AI', + title: 'حركات التسويق والـ AI', icon: Icons.campaign_rounded, index: 112, onTap: () => Get.toNamed('/marketing'), @@ -258,9 +247,8 @@ class WebSidebar extends StatelessWidget { onTap: () => Get.toNamed('/complaints'), ), - // ─── إدارة النظام والأمان ────────────────────────────── if (isSuperAdmin) ...[ - _buildSectionHeader('إدارة النظام والأمان'), + _buildSectionHeader(context, 'إدارة النظام والأمان'), _buildNavItem( context, title: 'مراقبة السيرفرات', @@ -307,14 +295,16 @@ class WebSidebar extends StatelessWidget { ], ), ), - const Divider(color: AppColor.divider, height: 1), + Divider(color: cs.outline, height: 1), + _buildThemeToggle(context), _buildLogoutBtn(context), ], ), ); } - Widget _buildLogo() { + Widget _buildLogo(BuildContext context) { + final cs = Theme.of(context).colorScheme; return Container( padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20), child: Row( @@ -322,12 +312,12 @@ class WebSidebar extends StatelessWidget { Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: AppColor.accent.withOpacity(0.1), + color: cs.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), - border: Border.all(color: AppColor.accent.withOpacity(0.3)), + border: Border.all(color: cs.primary.withValues(alpha: 0.3)), ), - child: const Icon(Icons.admin_panel_settings_rounded, - color: AppColor.accent, size: 28), + child: Icon(Icons.admin_panel_settings_rounded, + color: cs.primary, size: 28), ), const SizedBox(width: 12), Column( @@ -336,7 +326,7 @@ class WebSidebar extends StatelessWidget { Text( 'SIRO ADMIN', style: TextStyle( - color: AppColor.textPrimary, + color: cs.onSurface, fontWeight: FontWeight.bold, fontSize: 16, letterSpacing: 1.1, @@ -346,7 +336,7 @@ class WebSidebar extends StatelessWidget { Text( 'Web Dashboard', style: TextStyle( - color: AppColor.textSecondary, + color: cs.onSurfaceVariant, fontSize: 11, ), ), @@ -357,13 +347,14 @@ class WebSidebar extends StatelessWidget { ); } - Widget _buildSectionHeader(String title) { + Widget _buildSectionHeader(BuildContext context, String title) { + final cs = Theme.of(context).colorScheme; return Padding( padding: const EdgeInsets.only(right: 20, left: 20, top: 18, bottom: 6), child: Text( title, style: TextStyle( - color: AppColor.accent.withOpacity(0.85), + color: cs.primary.withValues(alpha: 0.85), fontSize: 11, fontWeight: FontWeight.bold, letterSpacing: 0.5, @@ -379,6 +370,7 @@ class WebSidebar extends StatelessWidget { required int index, required VoidCallback onTap, }) { + final cs = Theme.of(context).colorScheme; final bool isSelected = selectedIndex == index; return Padding( @@ -388,16 +380,16 @@ class WebSidebar extends StatelessWidget { child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(10), - hoverColor: AppColor.accent.withOpacity(0.08), + hoverColor: cs.primary.withValues(alpha: 0.08), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( color: isSelected - ? AppColor.accent.withOpacity(0.15) + ? cs.primary.withValues(alpha: 0.15) : Colors.transparent, borderRadius: BorderRadius.circular(10), border: isSelected - ? Border.all(color: AppColor.accent.withOpacity(0.4)) + ? Border.all(color: cs.primary.withValues(alpha: 0.4)) : null, ), child: Row( @@ -405,15 +397,14 @@ class WebSidebar extends StatelessWidget { Icon( icon, size: 20, - color: isSelected ? AppColor.accent : AppColor.textSecondary, + color: isSelected ? cs.primary : cs.onSurfaceVariant, ), const SizedBox(width: 14), Expanded( child: Text( title, style: TextStyle( - color: - isSelected ? AppColor.accent : AppColor.textPrimary, + color: isSelected ? cs.primary : cs.onSurface, fontSize: 13.5, fontWeight: isSelected ? FontWeight.bold : FontWeight.w500, @@ -428,7 +419,84 @@ class WebSidebar extends StatelessWidget { ); } + Widget _buildThemeToggle(BuildContext context) { + final cs = Theme.of(context).colorScheme; + final isDark = Theme.of(context).brightness == Brightness.dark; + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + final newMode = isDark ? ThemeMode.light : ThemeMode.dark; + Get.changeThemeMode(newMode); + }, + borderRadius: BorderRadius.circular(10), + hoverColor: cs.primary.withValues(alpha: 0.08), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + decoration: BoxDecoration( + color: cs.surfaceContainerHighest, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: cs.outline), + ), + child: Row( + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: Icon( + isDark ? Icons.light_mode_rounded : Icons.dark_mode_rounded, + key: ValueKey(isDark), + size: 20, + color: isDark ? const Color(0xFFFBBF24) : cs.primary, + ), + ), + const SizedBox(width: 14), + Expanded( + child: Text( + isDark ? 'الوضع الفاتح' : 'الوضع الداكن', + style: TextStyle( + color: cs.onSurface, + fontSize: 13.5, + fontWeight: FontWeight.w500, + ), + ), + ), + AnimatedContainer( + duration: const Duration(milliseconds: 300), + width: 44, + height: 24, + decoration: BoxDecoration( + color: isDark + ? cs.primary.withValues(alpha: 0.3) + : cs.outline, + borderRadius: BorderRadius.circular(12), + ), + child: AnimatedAlign( + duration: const Duration(milliseconds: 300), + alignment: isDark ? Alignment.centerRight : Alignment.centerLeft, + child: Container( + width: 20, + height: 20, + margin: const EdgeInsets.symmetric(horizontal: 2), + decoration: BoxDecoration( + color: isDark ? cs.primary : cs.onSurface, + shape: BoxShape.circle, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } + Widget _buildLogoutBtn(BuildContext context) { + final cs = Theme.of(context).colorScheme; return Container( padding: const EdgeInsets.all(16), child: InkWell( @@ -440,18 +508,18 @@ class WebSidebar extends StatelessWidget { child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( - color: Colors.red.withOpacity(0.1), + color: cs.error.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(10), - border: Border.all(color: Colors.red.withOpacity(0.2)), + border: Border.all(color: cs.error.withValues(alpha: 0.2)), ), - child: const Row( + child: Row( children: [ - Icon(Icons.logout_rounded, color: Colors.redAccent, size: 20), - SizedBox(width: 14), + Icon(Icons.logout_rounded, color: cs.error, size: 20), + const SizedBox(width: 14), Text( 'تسجيل الخروج', style: TextStyle( - color: Colors.redAccent, + color: cs.error, fontSize: 13.5, fontWeight: FontWeight.bold, ),