تصميم عالمي جديد، وإصلاح الخرائط ومراقب السيرفرات

This commit is contained in:
Hamza-Ayed
2026-07-26 04:21:42 +03:00
parent f325ffce42
commit ca6cb7a3fe
10 changed files with 1367 additions and 673 deletions
+1 -1
View File
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"6c0baaebf70e0148f485f27d5616b3d3382da7
_flutter.loader.load({ _flutter.loader.load({
serviceWorkerSettings: { 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. */
} }
}); });
+14 -16
View File
@@ -1,38 +1,36 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AppColor { class AppColor {
// --- Core Design Tokens --- AppColor._();
// Background & Surfaces // ─── Dark Mode Palette (static fallback / design tokens) ─────────────────
static const Color bg = Color(0xFF0A0A0B); static const Color bg = Color(0xFF0A0A0B);
static const Color surface = Color(0xFF161618); static const Color surface = Color(0xFF161618);
static const Color surfaceElevated = Color(0xFF222225); static const Color surfaceElevated = Color(0xFF222225);
static const Color surfaceGlass = Color(0xCC161618); static const Color surfaceGlass = Color(0xCC161618);
// Accents & Branding static const Color accent = Color(0xFF6366F1);
static const Color accent = Color(0xFF6366F1); // Indigo / Violet static const Color accentSoft = Color(0x266366F1);
static const Color accentSoft = Color(0x266366F1); // 15% Opacity static const Color accentBorder = Color(0x4D6366F1);
static const Color accentBorder = Color(0x4D6366F1); // 30% Opacity
static const Color glow = Color(0xFF818CF8); static const Color glow = Color(0xFF818CF8);
// Semantic / State Colors
static const Color danger = Color(0xFFEF4444); static const Color danger = Color(0xFFEF4444);
static const Color dangerSoft = Color(0x26EF4444); static const Color dangerSoft = Color(0x26EF4444);
static const Color success = Color(0xFF10B981); static const Color success = Color(0xFF10B981);
static const Color successSoft = Color(0x2610B981); static const Color successSoft = Color(0x2610B981);
static const Color warning = Color(0xFFF59E0B); static const Color warning = Color(0xFFF59E0B);
static const Color warningSoft = Color(0x26F59E0B);
static const Color info = Color(0xFF3B82F6); static const Color info = Color(0xFF3B82F6);
static const Color infoSoft = Color(0x263B82F6);
// Text & Content
static const Color textPrimary = Color(0xFFF3F4F6); static const Color textPrimary = Color(0xFFF3F4F6);
static const Color textSecondary = Color(0xFF9CA3AF); static const Color textSecondary = Color(0xFF9CA3AF);
static const Color textMuted = Color(0xFF6B7280); static const Color textMuted = Color(0xFF6B7280);
// UI Elements
static const Color divider = Color(0xFF2D2D30); static const Color divider = Color(0xFF2D2D30);
static const Color cardShadow = Color(0x66000000); static const Color cardShadow = Color(0x66000000);
// --- Legacy Mappings (for temporary compatibility) --- // ─── Legacy Mappings ─────────────────────────────────────────────────────
static const Color primaryColor = bg; static const Color primaryColor = bg;
static const Color secondaryColor = textPrimary; static const Color secondaryColor = textPrimary;
static const Color accentColor = accent; static const Color accentColor = accent;
@@ -40,5 +38,5 @@ class AppColor {
static const Color greenColor = success; static const Color greenColor = success;
static const Color blueColor = info; static const Color blueColor = info;
static const Color yellowColor = warning; static const Color yellowColor = warning;
static const Color deepPurpleAccent = accent; // Map to accent static const Color deepPurpleAccent = accent;
} }
+93 -3
View File
@@ -4,7 +4,9 @@ import 'package:google_fonts/google_fonts.dart';
import 'colors.dart'; import 'colors.dart';
class AppStyle { class AppStyle {
// --- Typography --- AppStyle._();
// ─── Static typography (dark mode defaults, backward compatible) ───────────
static TextStyle display = GoogleFonts.inter( static TextStyle display = GoogleFonts.inter(
fontWeight: FontWeight.w800, fontWeight: FontWeight.w800,
@@ -49,7 +51,91 @@ class AppStyle {
color: AppColor.accent, 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( static BoxDecoration cardDecoration = BoxDecoration(
color: AppColor.surface, color: AppColor.surface,
@@ -76,8 +162,12 @@ class AppStyle {
border: Border.all(color: AppColor.divider, width: 1), border: Border.all(color: AppColor.divider, width: 1),
); );
// --- Legacy Mappings --- // ─── Legacy Mappings ─────────────────────────────────────────────────────
static TextStyle headTitle2 = headTitle; static TextStyle headTitle2 = headTitle;
static BoxDecoration boxDecoration = cardDecoration; static BoxDecoration boxDecoration = cardDecoration;
static BoxDecoration boxDecoration1 = elevatedCard; static BoxDecoration boxDecoration1 = elevatedCard;
} }
extension _Brightness on BuildContext {
bool get isDark => Theme.of(this).brightness == Brightness.dark;
}
+620
View File
@@ -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),
],
);
}
+6 -30
View File
@@ -9,12 +9,11 @@ import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/date_symbol_data_local.dart';
import 'constant/box_name.dart'; import 'constant/box_name.dart';
import 'constant/theme.dart';
import 'controller/firebase/firbase_messge.dart'; import 'controller/firebase/firbase_messge.dart';
import 'controller/functions/encrypt_decrypt.dart'; import 'controller/functions/encrypt_decrypt.dart';
import 'firebase_options.dart'; import 'firebase_options.dart';
import 'models/db_sql.dart'; import 'models/db_sql.dart';
import 'package:google_fonts/google_fonts.dart';
import 'constant/colors.dart';
import 'routes.dart'; import 'routes.dart';
import 'binding/initial_binding.dart'; import 'binding/initial_binding.dart';
import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/foundation.dart' show kIsWeb;
@@ -27,7 +26,7 @@ Future<void> backgroundMessageHandler(RemoteMessage message) async {
if (message.data.isNotEmpty && message.notification != null) { if (message.data.isNotEmpty && message.notification != null) {
FirebaseMessagesController().fireBaseTitles(message); FirebaseMessagesController().fireBaseTitles(message);
} }
} // }
DbSql sql = DbSql.instance; DbSql sql = DbSql.instance;
@@ -40,7 +39,6 @@ void main() async {
await Firebase.initializeApp( await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform, options: DefaultFirebaseOptions.currentPlatform,
); );
// await FirebaseMessagesController().requestFirebaseMessagingPermission();
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
@@ -54,8 +52,7 @@ void main() async {
DeviceOrientation.portraitUp, DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown, DeviceOrientation.portraitDown,
]); ]);
} // Enable Crashlytics collection }
// FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
runApp(const MainApp()); runApp(const MainApp());
} }
@@ -70,30 +67,9 @@ class MainApp extends StatelessWidget {
title: 'Siro Admin', title: 'Siro Admin',
locale: const Locale('ar'), locale: const Locale('ar'),
fallbackLocale: const Locale('en'), fallbackLocale: const Locale('en'),
themeMode: ThemeMode.dark, themeMode: ThemeMode.system,
darkTheme: ThemeData( theme: AppTheme.lightTheme,
fontFamily: GoogleFonts.tajawal().fontFamily, darkTheme: AppTheme.darkTheme,
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,
),
),
),
initialBinding: InitialBinding(), initialBinding: InitialBinding(),
initialRoute: box.read(BoxName.phoneVerified) == true ? "/" : "/login", initialRoute: box.read(BoxName.phoneVerified) == true ? "/" : "/login",
getPages: routes, getPages: routes,
+177 -221
View File
@@ -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 'package:siro_admin/views/admin/quality/blacklist_page.dart';
import '../../constant/box_name.dart'; import '../../constant/box_name.dart';
import '../../constant/colors.dart'; import '../../constant/theme.dart';
import '../../controller/admin/dashboard_controller.dart'; import '../../controller/admin/dashboard_controller.dart';
import '../../controller/admin/static_controller.dart'; import '../../controller/admin/static_controller.dart';
import '../../controller/functions/crud.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 'package:siro_admin/views/widgets/web_sidebar.dart';
import '../transit/org_list_page.dart'; import '../transit/org_list_page.dart';
import 'package:siro_admin/views/widgets/glass_container.dart'; import 'package:siro_admin/views/widgets/glass_container.dart';
class AdminHomePage extends StatefulWidget { class AdminHomePage extends StatefulWidget {
const AdminHomePage({super.key}); const AdminHomePage({super.key});
@@ -57,21 +58,6 @@ class _AdminHomePageState extends State<AdminHomePage>
late DashboardController dashboardController; late DashboardController dashboardController;
String _searchQuery = ''; 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 @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -83,7 +69,6 @@ class _AdminHomePageState extends State<AdminHomePage>
final String role = box.read('admin_role')?.toString() ?? 'admin'; final String role = box.read('admin_role')?.toString() ?? 'admin';
final String myPhone = box.read(BoxName.adminPhone)?.toString() ?? ''; final String myPhone = box.read(BoxName.adminPhone)?.toString() ?? '';
// التحقق من الصلاحيات: إما عن طريق الدور أو عن طريق قائمة أرقام السوبر أدمن التقليدية
isSuperAdmin = (role == 'super_admin') || isSuperAdmin = (role == 'super_admin') ||
(myPhone == '201023248456' || (myPhone == '201023248456' ||
myPhone == '963992952235' || myPhone == '963992952235' ||
@@ -101,36 +86,35 @@ class _AdminHomePageState extends State<AdminHomePage>
super.dispose(); super.dispose();
} }
// ══════════════════════════════════════════════════════════════
// BUILD
// ══════════════════════════════════════════════════════════════
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
return ResponsiveLayout( return ResponsiveLayout(
mobile: _buildMobileScaffold(), mobile: _buildMobileScaffold(cs),
desktop: Scaffold( desktop: Scaffold(
backgroundColor: _bg, backgroundColor: cs.surface,
body: Row( body: Row(
children: [ children: [
const WebSidebar(selectedIndex: 0), const WebSidebar(selectedIndex: 0),
Expanded(child: _buildDesktopScaffold()), Expanded(child: _buildDesktopScaffold(cs)),
], ],
), ),
), ),
); );
} }
Widget _buildMobileScaffold() { Widget _buildMobileScaffold(ColorScheme cs) {
return Scaffold( return Scaffold(
backgroundColor: _bg, backgroundColor: cs.surface,
body: RefreshIndicator( body: RefreshIndicator(
onRefresh: () async => await dashboardController.getDashBoard(), onRefresh: () async => await dashboardController.getDashBoard(),
color: _accent, color: cs.primary,
backgroundColor: _surface, backgroundColor: cs.surfaceContainerHighest,
child: GetBuilder<DashboardController>( child: GetBuilder<DashboardController>(
builder: (controller) { builder: (controller) {
if (controller.dashbord.isEmpty) { if (controller.dashbord.isEmpty) {
return _buildLoadingState(); return _buildLoadingState(cs);
} }
final data = controller.dashbord[0]; final data = controller.dashbord[0];
@@ -139,11 +123,11 @@ class _AdminHomePageState extends State<AdminHomePage>
return CustomScrollView( return CustomScrollView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
slivers: [ slivers: [
_buildSliverAppBar(controller), _buildSliverAppBar(controller, cs),
_buildSearchBar(), _buildSearchBar(cs),
if (_searchQuery.isEmpty) const DashboardV2Widget(), if (_searchQuery.isEmpty) const DashboardV2Widget(),
if (_searchQuery.isEmpty) if (_searchQuery.isEmpty)
_buildQuickStatsSection(data, controller), _buildQuickStatsSection(data, controller, cs),
SliverPadding( SliverPadding(
padding: const EdgeInsets.only(bottom: 60), padding: const EdgeInsets.only(bottom: 60),
sliver: SliverList( sliver: SliverList(
@@ -160,7 +144,7 @@ class _AdminHomePageState extends State<AdminHomePage>
child: SlideAnimation( child: SlideAnimation(
verticalOffset: 40.0, verticalOffset: 40.0,
child: FadeInAnimation( child: FadeInAnimation(
child: _buildCategorySection(category), child: _buildCategorySection(category, cs),
), ),
), ),
); );
@@ -177,21 +161,21 @@ class _AdminHomePageState extends State<AdminHomePage>
); );
} }
Widget _buildDesktopScaffold() { Widget _buildDesktopScaffold(ColorScheme cs) {
return Scaffold( return Scaffold(
backgroundColor: _bg, backgroundColor: cs.surface,
body: GetBuilder<DashboardController>( body: GetBuilder<DashboardController>(
builder: (controller) { builder: (controller) {
if (controller.dashbord.isEmpty) { if (controller.dashbord.isEmpty) {
return _buildLoadingState(); return _buildLoadingState(cs);
} }
final data = controller.dashbord[0]; final data = controller.dashbord[0];
return CustomScrollView( return CustomScrollView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
slivers: [ slivers: [
_buildSliverAppBar(controller), _buildSliverAppBar(controller, cs),
const DashboardV2Widget(), const DashboardV2Widget(),
_buildQuickStatsSection(data, controller), _buildQuickStatsSection(data, controller, cs),
], ],
); );
}, },
@@ -199,10 +183,7 @@ class _AdminHomePageState extends State<AdminHomePage>
); );
} }
// ══════════════════════════════════════════════════════════════ Widget _buildLoadingState(ColorScheme cs) {
// LOADING STATE
// ══════════════════════════════════════════════════════════════
Widget _buildLoadingState() {
return Center( return Center(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -213,15 +194,15 @@ class _AdminHomePageState extends State<AdminHomePage>
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
gradient: RadialGradient( 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( child: SizedBox(
width: 28, width: 28,
height: 28, height: 28,
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: _accent, color: cs.primary,
strokeWidth: 2.5, strokeWidth: 2.5,
), ),
), ),
@@ -229,53 +210,55 @@ class _AdminHomePageState extends State<AdminHomePage>
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text('جاري التحميل...', Text('جاري التحميل...',
style: TextStyle(color: _textSecondary, fontSize: 13)), style: TextStyle(color: cs.onSurfaceVariant, fontSize: 13)),
], ],
), ),
); );
} }
// ══════════════════════════════════════════════════════════════ Widget _buildSliverAppBar(DashboardController controller, ColorScheme cs) {
// SLIVER APP BAR final isDark = Theme.of(context).brightness == Brightness.dark;
// ══════════════════════════════════════════════════════════════
Widget _buildSliverAppBar(DashboardController controller) {
return SliverAppBar( return SliverAppBar(
expandedHeight: 130.0, expandedHeight: 130.0,
floating: true, floating: true,
pinned: true, pinned: true,
backgroundColor: _bg, backgroundColor: cs.surface,
elevation: 0, elevation: 0,
flexibleSpace: FlexibleSpaceBar( flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin, collapseMode: CollapseMode.pin,
background: Stack( background: Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
// Aurora gradient background
Container( Container(
decoration: const BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: isDark
Color(0xFF0D2137), ? [
Color(0xFF0D1117), cs.primary.withValues(alpha: 0.08),
Color(0xFF0F1F1A), 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( Positioned(
top: -30, top: -30,
left: -40, left: -40,
child: _GlowOrb(color: _accent, size: 150, opacity: 0.08), child: _GlowOrb(color: cs.primary, size: 150, opacity: 0.08),
), ),
Positioned( Positioned(
top: -20, top: -20,
right: -20, right: -20,
child: _GlowOrb(color: _info, size: 120, opacity: 0.06), child: _GlowOrb(color: cs.tertiary, size: 120, opacity: 0.06),
), ),
// Content
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Padding( child: Padding(
@@ -283,12 +266,12 @@ class _AdminHomePageState extends State<AdminHomePage>
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
_buildLogo(), _buildLogo(cs),
const SizedBox(height: 6), const SizedBox(height: 6),
Text( Text(
isSuperAdmin ? 'Super Admin Panel' : 'Admin Panel', isSuperAdmin ? 'Super Admin Panel' : 'Admin Panel',
style: TextStyle( style: TextStyle(
color: _textSecondary, color: cs.onSurfaceVariant,
fontSize: 11, fontSize: 11,
letterSpacing: 1.5, letterSpacing: 1.5,
), ),
@@ -302,13 +285,13 @@ class _AdminHomePageState extends State<AdminHomePage>
), ),
actions: [ actions: [
_buildHeaderAction( _buildHeaderAction(
Icons.refresh_rounded, () => controller.getDashBoard()), Icons.refresh_rounded, () => controller.getDashBoard(), cs),
const SizedBox(width: 8), const SizedBox(width: 8),
], ],
); );
} }
Widget _buildLogo() { Widget _buildLogo(ColorScheme cs) {
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -321,19 +304,20 @@ class _AdminHomePageState extends State<AdminHomePage>
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [
_accent.withOpacity(0.3 + 0.1 * _pulseController.value), cs.primary.withValues(
_accent.withOpacity(0.1), alpha: 0.3 + 0.1 * _pulseController.value),
cs.primary.withValues(alpha: 0.1),
], ],
), ),
border: Border.all( border: Border.all(
color: color: cs.primary.withValues(
_accent.withOpacity(0.4 + 0.2 * _pulseController.value), alpha: 0.4 + 0.2 * _pulseController.value),
width: 1, width: 1,
), ),
), ),
child: const Icon( child: Icon(
Icons.admin_panel_settings_rounded, Icons.admin_panel_settings_rounded,
color: _accent, color: cs.primary,
size: 18, size: 18,
), ),
); );
@@ -341,8 +325,8 @@ class _AdminHomePageState extends State<AdminHomePage>
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
ShaderMask( ShaderMask(
shaderCallback: (bounds) => const LinearGradient( shaderCallback: (bounds) => LinearGradient(
colors: [_accent, _info], colors: [cs.primary, cs.tertiary],
).createShader(bounds), ).createShader(bounds),
child: const Text( child: const Text(
'Siro Admin', 'Siro Admin',
@@ -358,50 +342,48 @@ class _AdminHomePageState extends State<AdminHomePage>
); );
} }
Widget _buildHeaderAction(IconData icon, VoidCallback onTap) { Widget _buildHeaderAction(IconData icon, VoidCallback onTap, ColorScheme cs) {
return GestureDetector( return GestureDetector(
onTap: onTap, onTap: onTap,
child: Container( child: Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _surface, color: cs.surfaceContainerHighest,
borderRadius: BorderRadius.circular(10), 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),
), ),
); );
} }
// ══════════════════════════════════════════════════════════════ Widget _buildSearchBar(ColorScheme cs) {
// SEARCH BAR final isFocused = _searchQuery.isNotEmpty;
// ══════════════════════════════════════════════════════════════
Widget _buildSearchBar() {
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: Padding( child: Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), padding: const EdgeInsets.fromLTRB(16, 12, 16, 4),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: _surface, color: cs.surfaceContainerHighest,
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(14),
border: Border.all( border: Border.all(
color: _searchQuery.isNotEmpty ? _accentBorder : _divider, color: isFocused ? cs.primary.withValues(alpha: 0.5) : cs.outline,
width: _searchQuery.isNotEmpty ? 1.5 : 1, width: isFocused ? 1.5 : 1,
), ),
), ),
child: TextField( child: TextField(
controller: _searchController, controller: _searchController,
onChanged: (val) => setState(() => _searchQuery = val), onChanged: (val) => setState(() => _searchQuery = val),
style: const TextStyle(color: _textPrimary, fontSize: 14), style: TextStyle(color: cs.onSurface, fontSize: 14),
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'ابحث عن خدمة أو ميزة...', hintText: 'ابحث عن خدمة أو ميزة...',
hintStyle: const TextStyle(color: _textSecondary, fontSize: 13), hintStyle: TextStyle(color: cs.onSurfaceVariant, fontSize: 13),
prefixIcon: prefixIcon:
const Icon(Icons.search_rounded, color: _accent, size: 20), Icon(Icons.search_rounded, color: cs.primary, size: 20),
suffixIcon: _searchQuery.isNotEmpty suffixIcon: _searchQuery.isNotEmpty
? IconButton( ? IconButton(
icon: Icon(Icons.close_rounded, icon: Icon(Icons.close_rounded,
color: _textSecondary, size: 18), color: cs.onSurfaceVariant, size: 18),
onPressed: () { onPressed: () {
setState(() { setState(() {
_searchQuery = ''; _searchQuery = '';
@@ -420,23 +402,21 @@ class _AdminHomePageState extends State<AdminHomePage>
); );
} }
// ══════════════════════════════════════════════════════════════ Widget _buildQuickStatsSection(
// QUICK STATS SECTION dynamic data, DashboardController controller, ColorScheme cs) {
// ══════════════════════════════════════════════════════════════
Widget _buildQuickStatsSection(dynamic data, DashboardController controller) {
final highlights = [ final highlights = [
_HighlightData( _HighlightData(
'إجمالي الركاب', data['countPassengers'], Icons.group_rounded, _info), 'إجمالي الركاب', data['countPassengers'], Icons.group_rounded, cs.tertiary),
_HighlightData('إجمالي السائقين', data['countDriver'], _HighlightData('إجمالي السائقين', data['countDriver'],
Icons.drive_eta_rounded, _warning), Icons.drive_eta_rounded, cs.secondary),
_HighlightData('رحلات الشهر', data['countRideThisMonth'], _HighlightData('رحلات الشهر', data['countRideThisMonth'],
Icons.calendar_today_rounded, const Color(0xFFC792EA)), Icons.calendar_today_rounded, cs.primary),
if (isSuperAdmin) if (isSuperAdmin)
_HighlightData('المحفظة', _formatCurrency(data['seferWallet']), _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( return SliverToBoxAdapter(
child: Padding( child: Padding(
@@ -444,7 +424,6 @@ class _AdminHomePageState extends State<AdminHomePage>
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Section label
Padding( Padding(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 10), padding: const EdgeInsets.fromLTRB(20, 8, 20, 10),
child: Row( child: Row(
@@ -453,14 +432,14 @@ class _AdminHomePageState extends State<AdminHomePage>
width: 3, width: 3,
height: 14, height: 14,
decoration: BoxDecoration( decoration: BoxDecoration(
color: _accent, color: cs.primary,
borderRadius: BorderRadius.circular(2), borderRadius: BorderRadius.circular(2),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
const Text('نظرة عامة', Text('نظرة عامة',
style: TextStyle( style: TextStyle(
color: _textSecondary, color: cs.onSurfaceVariant,
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
letterSpacing: 1.2, letterSpacing: 1.2,
@@ -468,8 +447,6 @@ class _AdminHomePageState extends State<AdminHomePage>
], ],
), ),
), ),
// Highlight Cards
SizedBox( SizedBox(
height: 108, height: 108,
child: ListView.builder( child: ListView.builder(
@@ -479,21 +456,18 @@ class _AdminHomePageState extends State<AdminHomePage>
itemBuilder: (ctx, i) => Padding( itemBuilder: (ctx, i) => Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
right: i < highlights.length - 1 ? 10 : 0), right: i < highlights.length - 1 ? 10 : 0),
child: _buildHighlightCard(highlights[i]), child: _buildHighlightCard(highlights[i], cs),
), ),
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
// Detailed stats strip
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: _surface, color: cs.surfaceContainerHighest,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: _divider), border: Border.all(color: cs.outline),
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
@@ -505,12 +479,12 @@ class _AdminHomePageState extends State<AdminHomePage>
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
_buildDetailStatItem(stat), _buildDetailStatItem(stat, cs),
if (i < detailedStats.length - 1) if (i < detailedStats.length - 1)
Container( Container(
width: 1, width: 1,
height: 36, height: 36,
color: _divider, color: cs.outline,
), ),
], ],
); );
@@ -520,7 +494,6 @@ class _AdminHomePageState extends State<AdminHomePage>
), ),
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
], ],
), ),
@@ -528,14 +501,14 @@ class _AdminHomePageState extends State<AdminHomePage>
); );
} }
Widget _buildHighlightCard(_HighlightData h) { Widget _buildHighlightCard(_HighlightData h, ColorScheme cs) {
return GlassContainer( return GlassContainer(
width: 155, width: 155,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
borderRadius: 16, borderRadius: 16,
gradientColors: [ gradientColors: [
h.color.withOpacity(0.18), h.color.withValues(alpha: 0.18),
h.color.withOpacity(0.03), h.color.withValues(alpha: 0.03),
], ],
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -546,7 +519,7 @@ class _AdminHomePageState extends State<AdminHomePage>
Container( Container(
padding: const EdgeInsets.all(7), padding: const EdgeInsets.all(7),
decoration: BoxDecoration( decoration: BoxDecoration(
color: h.color.withOpacity(0.12), color: h.color.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(9), borderRadius: BorderRadius.circular(9),
), ),
child: Icon(h.icon, color: h.color, size: 16), child: Icon(h.icon, color: h.color, size: 16),
@@ -556,7 +529,7 @@ class _AdminHomePageState extends State<AdminHomePage>
height: 6, height: 6,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: h.color.withOpacity(0.6), color: h.color.withValues(alpha: 0.6),
), ),
), ),
], ],
@@ -564,8 +537,8 @@ class _AdminHomePageState extends State<AdminHomePage>
const Spacer(), const Spacer(),
Text( Text(
h.value.toString(), h.value.toString(),
style: const TextStyle( style: TextStyle(
color: _textPrimary, color: cs.onSurface,
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
height: 1.1, height: 1.1,
@@ -575,8 +548,8 @@ class _AdminHomePageState extends State<AdminHomePage>
const SizedBox(height: 3), const SizedBox(height: 3),
Text( Text(
h.label, h.label,
style: const TextStyle( style: TextStyle(
color: _textSecondary, color: cs.onSurfaceVariant,
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@@ -587,7 +560,7 @@ class _AdminHomePageState extends State<AdminHomePage>
); );
} }
Widget _buildDetailStatItem(Map<String, dynamic> stat) { Widget _buildDetailStatItem(Map<String, dynamic> stat, ColorScheme cs) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14), padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
child: Column( child: Column(
@@ -598,8 +571,8 @@ class _AdminHomePageState extends State<AdminHomePage>
const SizedBox(height: 6), const SizedBox(height: 6),
Text( Text(
stat['value'].toString(), stat['value'].toString(),
style: const TextStyle( style: TextStyle(
color: _textPrimary, color: cs.onSurface,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
height: 1, height: 1,
@@ -608,8 +581,8 @@ class _AdminHomePageState extends State<AdminHomePage>
const SizedBox(height: 3), const SizedBox(height: 3),
Text( Text(
stat['title'] as String, stat['title'] as String,
style: const TextStyle( style: TextStyle(
color: _textSecondary, color: cs.onSurfaceVariant,
fontSize: 9, fontSize: 9,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@@ -619,10 +592,7 @@ class _AdminHomePageState extends State<AdminHomePage>
); );
} }
// ══════════════════════════════════════════════════════════════ Widget _buildCategorySection(ActionCategory category, ColorScheme cs) {
// CATEGORY SECTION
// ══════════════════════════════════════════════════════════════
Widget _buildCategorySection(ActionCategory category) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -634,15 +604,15 @@ class _AdminHomePageState extends State<AdminHomePage>
width: 3, width: 3,
height: 14, height: 14,
decoration: BoxDecoration( decoration: BoxDecoration(
color: _accent, color: cs.primary,
borderRadius: BorderRadius.circular(2), borderRadius: BorderRadius.circular(2),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
category.title, category.title,
style: const TextStyle( style: TextStyle(
color: _textPrimary, color: cs.onSurface,
fontSize: 15, fontSize: 15,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
letterSpacing: 0.3, letterSpacing: 0.3,
@@ -650,13 +620,13 @@ class _AdminHomePageState extends State<AdminHomePage>
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
child: Container(height: 1, color: _divider), child: Container(height: 1, color: cs.outline),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'${category.items.length}', '${category.items.length}',
style: const TextStyle( style: TextStyle(
color: _textSecondary, color: cs.onSurfaceVariant,
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@@ -676,26 +646,26 @@ class _AdminHomePageState extends State<AdminHomePage>
), ),
itemCount: category.items.length, itemCount: category.items.length,
itemBuilder: (context, index) => itemBuilder: (context, index) =>
_buildActionItem(category.items[index]), _buildActionItem(category.items[index], cs),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
], ],
); );
} }
Widget _buildActionItem(ActionItem item) { Widget _buildActionItem(ActionItem item, ColorScheme cs) {
return Material( return Material(
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: InkWell(
onTap: item.onPressed, onTap: item.onPressed,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
splashColor: item.color.withOpacity(0.1), splashColor: item.color.withValues(alpha: 0.1),
highlightColor: item.color.withOpacity(0.05), highlightColor: item.color.withValues(alpha: 0.05),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: _surface, color: cs.surfaceContainerHighest,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: _divider), border: Border.all(color: cs.outline),
), ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -707,15 +677,16 @@ class _AdminHomePageState extends State<AdminHomePage>
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: [
item.color.withOpacity(0.20), item.color.withValues(alpha: 0.20),
item.color.withOpacity(0.08), item.color.withValues(alpha: 0.08),
], ],
), ),
borderRadius: BorderRadius.circular(12), 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: [
BoxShadow( BoxShadow(
color: item.color.withOpacity(0.15), color: item.color.withValues(alpha: 0.15),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 3), offset: const Offset(0, 3),
), ),
@@ -731,8 +702,8 @@ class _AdminHomePageState extends State<AdminHomePage>
textAlign: TextAlign.center, textAlign: TextAlign.center,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: TextStyle(
color: _textPrimary, color: cs.onSurface,
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
height: 1.3, height: 1.3,
@@ -746,9 +717,6 @@ class _AdminHomePageState extends State<AdminHomePage>
); );
} }
// ══════════════════════════════════════════════════════════════
// DATA HELPERS
// ══════════════════════════════════════════════════════════════
List<ActionCategory> _getFilteredCategories() { List<ActionCategory> _getFilteredCategories() {
final all = _getAllActionCategories(); final all = _getAllActionCategories();
if (_searchQuery.isEmpty) return all; if (_searchQuery.isEmpty) return all;
@@ -772,31 +740,31 @@ class _AdminHomePageState extends State<AdminHomePage>
ActionCategory( ActionCategory(
title: 'المستخدمين', title: 'المستخدمين',
items: [ items: [
ActionItem('الركاب', Icons.people_outline_rounded, _info, ActionItem('الركاب', Icons.people_outline_rounded, const Color(0xFF3B82F6),
() => Get.to(() => Passengrs())), () => Get.to(() => Passengrs())),
ActionItem('السائقون', Icons.drive_eta_rounded, _warning, ActionItem('السائقون', Icons.drive_eta_rounded, const Color(0xFFF59E0B),
() => Get.to(() => CaptainsPage())), () => Get.to(() => CaptainsPage())),
ActionItem('المراقب', Icons.track_changes_rounded, _danger, ActionItem('المراقب', Icons.track_changes_rounded, const Color(0xFFEF4444),
() => Get.to(() => SiroTrackerScreen())), () => Get.to(() => SiroTrackerScreen())),
], ],
), ),
ActionCategory( ActionCategory(
title: 'مواصلاتي', title: 'مواصلاتي',
items: [ items: [
ActionItem('المؤسسات', Icons.directions_bus_filled_rounded, _accent, ActionItem('المؤسسات', Icons.directions_bus_filled_rounded, const Color(0xFF6366F1),
() => Get.to(() => const TransitOrgListPage())), () => Get.to(() => const TransitOrgListPage())),
], ],
), ),
ActionCategory( ActionCategory(
title: 'إدارة النظام الجديد', title: 'إدارة النظام الجديد',
items: [ items: [
ActionItem('أكواد الخصم', Icons.confirmation_number_rounded, _accent, ActionItem('أكواد الخصم', Icons.confirmation_number_rounded, const Color(0xFF6366F1),
() => Get.toNamed('/promo')), () => Get.toNamed('/promo')),
ActionItem('تعديل الأسعار', Icons.settings_suggest_rounded, _warning, ActionItem('تعديل الأسعار', Icons.settings_suggest_rounded, const Color(0xFFF59E0B),
() => Get.toNamed('/kazan')), () => Get.toNamed('/kazan')),
ActionItem('الشكاوى', Icons.report_problem_rounded, _danger, ActionItem('الشكاوى', Icons.report_problem_rounded, const Color(0xFFEF4444),
() => Get.toNamed('/complaints')), () => Get.toNamed('/complaints')),
ActionItem('مراجعة الوثائق', Icons.assignment_ind_rounded, _info, ActionItem('مراجعة الوثائق', Icons.assignment_ind_rounded, const Color(0xFF3B82F6),
() => Get.toNamed('/driver-docs')), () => Get.toNamed('/driver-docs')),
ActionItem('استخبارات السوشيال', Icons.insights_rounded, const Color(0xFFC792EA), ActionItem('استخبارات السوشيال', Icons.insights_rounded, const Color(0xFFC792EA),
() => Get.toNamed('/social-intelligence')), () => Get.toNamed('/social-intelligence')),
@@ -813,11 +781,11 @@ class _AdminHomePageState extends State<AdminHomePage>
Icons.remove_red_eye_rounded, Icons.remove_red_eye_rounded,
const Color(0xFFC792EA), const Color(0xFFC792EA),
() => Get.to(() => RideMonitorScreen())), () => Get.to(() => RideMonitorScreen())),
ActionItem('الإحصائيات', Icons.bar_chart_rounded, _accent, () async { ActionItem('الإحصائيات', Icons.bar_chart_rounded, const Color(0xFF6366F1), () async {
await Get.put(StaticController()).getAll(); await Get.put(StaticController()).getAll();
Get.to(() => const StaticDash()); Get.to(() => const StaticDash());
}), }),
ActionItem('التحليلات المتقدمة', Icons.analytics_rounded, _info, ActionItem('التحليلات المتقدمة', Icons.analytics_rounded, const Color(0xFF3B82F6),
() => Get.to(() => const AdvancedAnalyticsPage())), () => Get.to(() => const AdvancedAnalyticsPage())),
ActionItem('لوحة البيانات التفاعلية', Icons.dashboard_customize_rounded, ActionItem('لوحة البيانات التفاعلية', Icons.dashboard_customize_rounded,
const Color(0xFF00CEC9), const Color(0xFF00CEC9),
@@ -827,7 +795,7 @@ class _AdminHomePageState extends State<AdminHomePage>
ActionCategory( ActionCategory(
title: 'الجودة والدعم', title: 'الجودة والدعم',
items: [ items: [
ActionItem('القائمة السوداء', Icons.block_flipped, _danger, ActionItem('القائمة السوداء', Icons.block_flipped, const Color(0xFFEF4444),
() => Get.to(() => const BlacklistPage())), () => Get.to(() => const BlacklistPage())),
], ],
), ),
@@ -836,16 +804,16 @@ class _AdminHomePageState extends State<AdminHomePage>
title: 'المالية والإدارة', title: 'المالية والإدارة',
items: [ items: [
ActionItem('الإدارة المالية V2', Icons.account_balance_rounded, ActionItem('الإدارة المالية V2', Icons.account_balance_rounded,
_accent, () => Get.to(() => const FinancialV2Page())), const Color(0xFF6366F1), () => Get.to(() => const FinancialV2Page())),
ActionItem('المحفظة', Icons.account_balance_wallet_rounded, _accent, ActionItem('المحفظة', Icons.account_balance_wallet_rounded, const Color(0xFF6366F1),
() => Get.to(() => Wallet())), () => Get.to(() => Wallet())),
ActionItem('هدية 300', Icons.card_giftcard_rounded, _warning, ActionItem('هدية 300', Icons.card_giftcard_rounded, const Color(0xFFF59E0B),
() => Get.to(() => DriverGiftCheckPage())), () => Get.to(() => DriverGiftCheckPage())),
ActionItem('الفواتير', Icons.receipt_long_rounded, ActionItem('الفواتير', Icons.receipt_long_rounded,
const Color(0xFF80CBC4), () => Get.to(() => InvoiceListPage())), const Color(0xFF10B981), () => Get.to(() => InvoiceListPage())),
ActionItem('الموظفون', Icons.badge_rounded, const Color(0xFFB0BEC5), ActionItem('الموظفون', Icons.badge_rounded, const Color(0xFF9CA3AF),
() => Get.to(() => EmployeePage())), () => Get.to(() => EmployeePage())),
ActionItem('موافقة المشرفين', Icons.how_to_reg_rounded, _accent, ActionItem('موافقة المشرفين', Icons.how_to_reg_rounded, const Color(0xFF6366F1),
() => Get.to(() => const PendingAdminsPage())), () => Get.to(() => const PendingAdminsPage())),
], ],
), ),
@@ -854,11 +822,11 @@ class _AdminHomePageState extends State<AdminHomePage>
title: 'النظام والتواصل', title: 'النظام والتواصل',
items: [ items: [
ActionItem('سجل العمليات', Icons.admin_panel_settings_rounded, ActionItem('سجل العمليات', Icons.admin_panel_settings_rounded,
_danger, () => Get.to(() => const AuditLogsPage())), const Color(0xFFEF4444), () => Get.to(() => const AuditLogsPage())),
ActionItem('واتساب جماعي', Icons.message_rounded, ActionItem('واتساب جماعي', Icons.message_rounded,
const Color(0xFF4CAF50), () => _showWhatsAppDialog(context)), const Color(0xFF25D366), () => _showWhatsAppDialog(context)),
ActionItem('أتمتة التسويق', Icons.campaign_rounded, ActionItem('أتمتة التسويق', Icons.campaign_rounded,
const Color(0xFF80CBC4), () => Get.toNamed('/marketing')), const Color(0xFF10B981), () => Get.toNamed('/marketing')),
ActionItem( ActionItem(
'إشعار سائقين', 'إشعار سائقين',
Icons.notifications_active_rounded, Icons.notifications_active_rounded,
@@ -871,21 +839,17 @@ class _AdminHomePageState extends State<AdminHomePage>
const Color(0xFFF06292), const Color(0xFFF06292),
() => Get.put(NotificationController()) () => Get.put(NotificationController())
.sendNotificationPassengers()), .sendNotificationPassengers()),
ActionItem('تسجيل سائق', Icons.person_add_rounded, _info, ActionItem('تسجيل سائق', Icons.person_add_rounded, const Color(0xFF3B82F6),
() => Get.to(() => DriversPendingPage())), () => Get.to(() => DriversPendingPage())),
ActionItem( ActionItem(
'تحديث التطبيق', 'تحديث التطبيق',
Icons.system_update_rounded, Icons.system_update_rounded,
const Color(0xFFA1887F), const Color(0xFF9CA3AF),
() => Get.to(() => PackageUpdateScreen())), () => Get.to(() => PackageUpdateScreen())),
ActionItem('مراقب السيرفر', Icons.dns_rounded, _accent, ActionItem('مراقب السيرفر', Icons.dns_rounded, const Color(0xFF6366F1),
() => Get.to(() => ServerMonitorPage())), () => Get.to(() => ServerMonitorPage())),
ActionItem('سجل الأخطاء', Icons.error_outline_rounded, _danger, ActionItem('سجل الأخطاء', Icons.error_outline_rounded, const Color(0xFFEF4444),
() => Get.to(() => ErrorListPage())), () => 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( ActionItem(
'أداة التشفير', 'أداة التشفير',
Icons.lock_rounded, Icons.lock_rounded,
@@ -902,13 +866,13 @@ class _AdminHomePageState extends State<AdminHomePage>
ActionItem( ActionItem(
'إضافة مدير', 'إضافة مدير',
Icons.admin_panel_settings_rounded, Icons.admin_panel_settings_rounded,
_accent, const Color(0xFF6366F1),
() => Get.to(() => const AddStaffPage(role: 'admin')), () => Get.to(() => const AddStaffPage(role: 'admin')),
), ),
ActionItem( ActionItem(
'إضافة خدمة عملاء', 'إضافة خدمة عملاء',
Icons.support_agent_rounded, Icons.support_agent_rounded,
_info, const Color(0xFF3B82F6),
() => Get.to(() => const AddStaffPage(role: 'service')), () => Get.to(() => const AddStaffPage(role: 'service')),
), ),
], ],
@@ -917,44 +881,44 @@ class _AdminHomePageState extends State<AdminHomePage>
} }
List<Map<String, dynamic>> _getDetailedStats( List<Map<String, dynamic>> _getDetailedStats(
dynamic data, DashboardController controller) { dynamic data, DashboardController controller, ColorScheme cs) {
return [ return [
if (isSuperAdmin) if (isSuperAdmin)
{ {
'title': 'رصيد الرسائل', 'title': 'رصيد الرسائل',
'value': controller.creditSMS, 'value': controller.creditSMS,
'icon': Icons.sms_rounded, 'icon': Icons.sms_rounded,
'color': _info, 'color': cs.tertiary,
}, },
{ {
'title': 'مكتملة', 'title': 'مكتملة',
'value': data['completed_rides'], 'value': data['completed_rides'],
'icon': Icons.check_circle_rounded, 'icon': Icons.check_circle_rounded,
'color': _success, 'color': const Color(0xFF10B981),
}, },
{ {
'title': 'ملغاة', 'title': 'ملغاة',
'value': data['cancelled_rides'], 'value': data['cancelled_rides'],
'icon': Icons.cancel_rounded, 'icon': Icons.cancel_rounded,
'color': _danger, 'color': const Color(0xFFEF4444),
}, },
{ {
'title': 'مدفوعات', 'title': 'مدفوعات',
'value': _formatCurrency(data['payments']), 'value': _formatCurrency(data['payments']),
'icon': Icons.attach_money_rounded, 'icon': Icons.attach_money_rounded,
'color': _warning, 'color': const Color(0xFFF59E0B),
}, },
{ {
'title': 'Comfort', 'title': 'Comfort',
'value': data['comfort'], 'value': data['comfort'],
'icon': Icons.chair_rounded, 'icon': Icons.chair_rounded,
'color': const Color(0xFF80CBC4), 'color': const Color(0xFF10B981),
}, },
{ {
'title': 'Speed', 'title': 'Speed',
'value': data['speed'], 'value': data['speed'],
'icon': Icons.flash_on_rounded, 'icon': Icons.flash_on_rounded,
'color': const Color(0xFFFFD54F), 'color': const Color(0xFFFBBF24),
}, },
{ {
'title': 'Lady', 'title': 'Lady',
@@ -965,23 +929,22 @@ class _AdminHomePageState extends State<AdminHomePage>
]; ];
} }
// ══════════════════════════════════════════════════════════════
// WHATSAPP DIALOG
// ══════════════════════════════════════════════════════════════
void _showWhatsAppDialog(BuildContext context) { void _showWhatsAppDialog(BuildContext context) {
final cs = Theme.of(context).colorScheme;
Get.dialog( Get.dialog(
Dialog( Dialog(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: _surfaceElevated, color: cs.surfaceContainerHighest,
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
border: Border.all(color: _divider), border: Border.all(color: cs.outline),
boxShadow: const [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black45, color: cs.shadow.withValues(alpha: 0.3),
blurRadius: 30, blurRadius: 30,
offset: Offset(0, 12), offset: const Offset(0, 12),
), ),
], ],
), ),
@@ -992,45 +955,44 @@ class _AdminHomePageState extends State<AdminHomePage>
Container( Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF4CAF50).withAlpha(30), // ~0.12 opacity color: const Color(0xFF25D366).withValues(alpha: 0.12),
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: Border.all(
color: const Color(0xFF4CAF50) color: const Color(0xFF25D366).withValues(alpha: 0.25)),
.withAlpha(64)), // ~0.25 opacity
), ),
child: const Icon(Icons.message_rounded, child: const Icon(Icons.message_rounded,
color: Color(0xFF4CAF50), size: 28), color: Color(0xFF25D366), size: 28),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
const Text( Text(
'إرسال واتساب جماعي', 'إرسال واتساب جماعي',
style: TextStyle( style: TextStyle(
color: _textPrimary, color: cs.onSurface,
fontSize: 17, fontSize: 17,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
const Text( Text(
'سيتم إرسال الرسالة لجميع السائقين', 'سيتم إرسال الرسالة لجميع السائقين',
style: TextStyle(color: _textSecondary, fontSize: 11), style: TextStyle(color: cs.onSurfaceVariant, fontSize: 11),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: _bg, color: cs.surface,
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(14),
border: Border.all(color: _divider), border: Border.all(color: cs.outline),
), ),
child: TextField( child: TextField(
controller: _messageController, controller: _messageController,
maxLines: 4, maxLines: 4,
style: const TextStyle(color: _textPrimary, fontSize: 13), style: TextStyle(color: cs.onSurface, fontSize: 13),
decoration: const InputDecoration( decoration: InputDecoration(
hintText: 'اكتب رسالتك هنا...', hintText: 'اكتب رسالتك هنا...',
hintStyle: TextStyle(color: _textSecondary, fontSize: 12), hintStyle: TextStyle(color: cs.onSurfaceVariant, fontSize: 12),
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.all(14), contentPadding: const EdgeInsets.all(14),
), ),
), ),
), ),
@@ -1044,12 +1006,12 @@ class _AdminHomePageState extends State<AdminHomePage>
padding: const EdgeInsets.symmetric(vertical: 12), padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), 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<AdminHomePage>
label: label:
const Text('إرسال', style: TextStyle(fontSize: 13)), const Text('إرسال', style: TextStyle(fontSize: 13)),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF4CAF50), backgroundColor: const Color(0xFF25D366),
foregroundColor: Colors.white, foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12), padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@@ -1108,9 +1070,6 @@ class _AdminHomePageState extends State<AdminHomePage>
} }
} }
// ══════════════════════════════════════════════════════════════
// HELPER WIDGETS
// ══════════════════════════════════════════════════════════════
class _GlowOrb extends StatelessWidget { class _GlowOrb extends StatelessWidget {
final Color color; final Color color;
final double size; final double size;
@@ -1127,16 +1086,13 @@ class _GlowOrb extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
gradient: RadialGradient( gradient: RadialGradient(
colors: [color.withOpacity(opacity), Colors.transparent], colors: [color.withValues(alpha: opacity), Colors.transparent],
), ),
), ),
); );
} }
} }
// ══════════════════════════════════════════════════════════════
// DATA CLASSES
// ══════════════════════════════════════════════════════════════
class _HighlightData { class _HighlightData {
final String label; final String label;
final dynamic value; final dynamic value;
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.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:siro_admin/controller/admin/dashboard_v2_controller.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:siro_admin/views/widgets/glass_container.dart'; import 'package:siro_admin/views/widgets/glass_container.dart';
@@ -10,15 +10,17 @@ class DashboardV2Widget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
return GetBuilder<DashboardV2Controller>( return GetBuilder<DashboardV2Controller>(
init: DashboardV2Controller(), init: DashboardV2Controller(),
builder: (ctrl) { builder: (ctrl) {
if (ctrl.isLoading) { if (ctrl.isLoading) {
return const SliverToBoxAdapter( return SliverToBoxAdapter(
child: SizedBox( child: SizedBox(
height: 150, height: 150,
child: Center( child: Center(
child: CircularProgressIndicator(color: AppColor.accent), child: CircularProgressIndicator(color: cs.primary),
), ),
), ),
); );
@@ -28,16 +30,13 @@ class DashboardV2Widget extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// 1. Real-time stats _buildSectionTitle('مركز العمليات الحي (Real-time)', cs),
_buildSectionTitle('مركز العمليات الحي (Real-time)'), _buildRealtimeStats(ctrl.realtimeData, cs),
_buildRealtimeStats(ctrl.realtimeData),
const SizedBox(height: 20), const SizedBox(height: 20),
// 2. Smart Alerts
if (ctrl.smartAlerts.isNotEmpty) ...[ if (ctrl.smartAlerts.isNotEmpty) ...[
_buildSectionTitle('التنبيهات الذكية (${ctrl.smartAlerts.length})'), _buildSectionTitle(
_buildSmartAlerts(ctrl.smartAlerts), 'التنبيهات الذكية (${ctrl.smartAlerts.length})', cs),
_buildSmartAlerts(ctrl.smartAlerts, cs),
const SizedBox(height: 10), const SizedBox(height: 10),
] ]
], ],
@@ -47,7 +46,7 @@ class DashboardV2Widget extends StatelessWidget {
); );
} }
Widget _buildSectionTitle(String title) { Widget _buildSectionTitle(String title, ColorScheme cs) {
return Padding( return Padding(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 10), padding: const EdgeInsets.fromLTRB(20, 8, 20, 10),
child: Row( child: Row(
@@ -56,15 +55,15 @@ class DashboardV2Widget extends StatelessWidget {
width: 3, width: 3,
height: 14, height: 14,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColor.danger, // Distinct color color: cs.error,
borderRadius: BorderRadius.circular(2), borderRadius: BorderRadius.circular(2),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
title, title,
style: const TextStyle( style: TextStyle(
color: AppColor.textSecondary, color: cs.onSurfaceVariant,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
letterSpacing: 0.5, letterSpacing: 0.5,
@@ -75,43 +74,43 @@ class DashboardV2Widget extends StatelessWidget {
); );
} }
Widget _buildRealtimeStats(Map<String, dynamic> data) { Widget _buildRealtimeStats(Map<String, dynamic> data, ColorScheme cs) {
final stats = [ final stats = [
{ {
'title': 'رحلات نشطة', 'title': 'رحلات نشطة',
'value': data['active_rides']?.toString() ?? '0', 'value': data['active_rides']?.toString() ?? '0',
'icon': Icons.directions_car_rounded, 'icon': Icons.directions_car_rounded,
'color': AppColor.info, 'color': cs.tertiary,
}, },
{ {
'title': 'سائقون أونلاين', 'title': 'سائقون أونلاين',
'value': data['online_drivers']?.toString() ?? '0', 'value': data['online_drivers']?.toString() ?? '0',
'icon': Icons.wifi_tethering, 'icon': Icons.wifi_tethering,
'color': AppColor.success, 'color': const Color(0xFF10B981),
}, },
{ {
'title': 'إيرادات اليوم', 'title': 'إيرادات اليوم',
'value': '${data['revenue_today'] ?? 0}', 'value': '${data['revenue_today'] ?? 0}',
'icon': Icons.monetization_on_rounded, 'icon': Icons.monetization_on_rounded,
'color': AppColor.warning, 'color': const Color(0xFFF59E0B),
}, },
{ {
'title': 'إيرادات الأمس', 'title': 'إيرادات الأمس',
'value': '${data['revenue_yesterday'] ?? 0}', 'value': '${data['revenue_yesterday'] ?? 0}',
'icon': Icons.history_rounded, 'icon': Icons.history_rounded,
'color': Colors.grey, 'color': cs.onSurfaceVariant,
}, },
{ {
'title': 'شكاوى مفتوحة', 'title': 'شكاوى مفتوحة',
'value': data['new_complaints']?.toString() ?? '0', 'value': data['new_complaints']?.toString() ?? '0',
'icon': Icons.warning_rounded, 'icon': Icons.warning_rounded,
'color': AppColor.danger, 'color': cs.error,
}, },
{ {
'title': 'رخص تنتهي قريبًا', 'title': 'رخص تنتهي قريبًا',
'value': data['expiring_licenses']?.toString() ?? '0', 'value': data['expiring_licenses']?.toString() ?? '0',
'icon': Icons.sd_card_alert_rounded, '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]; final stat = stats[i];
return Padding( return Padding(
padding: const EdgeInsets.only(right: 10), padding: const EdgeInsets.only(right: 10),
child: _buildStatCard(stat), child: _buildStatCard(stat, cs),
); );
}, },
), ),
); );
} }
Widget _buildStatCard(Map<String, dynamic> stat) { Widget _buildStatCard(Map<String, dynamic> stat, ColorScheme cs) {
final color = stat['color'] as Color; final color = stat['color'] as Color;
return GlassContainer( return GlassContainer(
width: 150, width: 150,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
borderRadius: 16, borderRadius: 16,
gradientColors: [ gradientColors: [
color.withOpacity(0.15), color.withValues(alpha: 0.15),
color.withOpacity(0.05), color.withValues(alpha: 0.05),
], ],
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -151,36 +150,34 @@ class DashboardV2Widget extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(6), padding: const EdgeInsets.all(6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: color.withOpacity(0.15), color: color.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Icon(stat['icon'] as IconData, color: color, size: 16), child: Icon(stat['icon'] as IconData, color: color, size: 16),
), ),
const Spacer(), const Spacer(),
// Pulsing indicator for active things if (stat['title'] == 'رحلات نشطة' ||
if (stat['title'] == 'رحلات نشطة' || stat['title'] == 'سائقون أونلاين') stat['title'] == 'سائقون أونلاين')
Container( Container(
width: 8, width: 8,
height: 8, height: 8,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: color, color: color,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: color.withOpacity(0.5), color: color.withValues(alpha: 0.5),
blurRadius: 4, blurRadius: 4,
spreadRadius: 1, spreadRadius: 1,
) )
] ])),
),
),
], ],
), ),
const Spacer(), const Spacer(),
Text( Text(
stat['value'].toString(), stat['value'].toString(),
style: const TextStyle( style: TextStyle(
color: AppColor.textPrimary, color: cs.onSurface,
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -188,8 +185,8 @@ class DashboardV2Widget extends StatelessWidget {
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
stat['title'].toString(), stat['title'].toString(),
style: const TextStyle( style: TextStyle(
color: AppColor.textSecondary, color: cs.onSurfaceVariant,
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@@ -201,12 +198,12 @@ class DashboardV2Widget extends StatelessWidget {
); );
} }
Widget _buildSmartAlerts(List<dynamic> alerts) { Widget _buildSmartAlerts(List<dynamic> alerts, ColorScheme cs) {
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
itemCount: alerts.length > 5 ? 5 : alerts.length, // Show top 5 itemCount: alerts.length > 5 ? 5 : alerts.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final alert = alerts[index]; final alert = alerts[index];
return AnimationConfiguration.staggeredList( return AnimationConfiguration.staggeredList(
@@ -215,7 +212,7 @@ class DashboardV2Widget extends StatelessWidget {
child: SlideAnimation( child: SlideAnimation(
verticalOffset: 20.0, verticalOffset: 20.0,
child: FadeInAnimation( child: FadeInAnimation(
child: _buildAlertItem(alert), child: _buildAlertItem(alert, cs),
), ),
), ),
); );
@@ -223,17 +220,17 @@ class DashboardV2Widget extends StatelessWidget {
); );
} }
Widget _buildAlertItem(Map<String, dynamic> alert) { Widget _buildAlertItem(Map<String, dynamic> alert, ColorScheme cs) {
Color getSeverityColor(String severity) { Color getSeverityColor(String severity) {
switch (severity) { switch (severity) {
case 'high': case 'high':
return AppColor.danger; return cs.error;
case 'medium': case 'medium':
return AppColor.warning; return const Color(0xFFF59E0B);
case 'warning': case 'warning':
return Colors.orangeAccent; return const Color(0xFFF97316);
default: default:
return AppColor.info; return cs.tertiary;
} }
} }
@@ -257,12 +254,12 @@ class DashboardV2Widget extends StatelessWidget {
margin: const EdgeInsets.only(bottom: 10), margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColor.surface, color: cs.surfaceContainerHighest,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all(color: color.withOpacity(0.4)), border: Border.all(color: color.withValues(alpha: 0.4)),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: color.withOpacity(0.05), color: color.withValues(alpha: 0.05),
blurRadius: 5, blurRadius: 5,
offset: const Offset(0, 2), offset: const Offset(0, 2),
) )
@@ -274,7 +271,7 @@ class DashboardV2Widget extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: color.withOpacity(0.1), color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: Icon(icon, color: color, size: 20), child: Icon(icon, color: color, size: 20),
@@ -286,8 +283,8 @@ class DashboardV2Widget extends StatelessWidget {
children: [ children: [
Text( Text(
alert['title'] ?? '', alert['title'] ?? '',
style: const TextStyle( style: TextStyle(
color: AppColor.textPrimary, color: cs.onSurface,
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -295,8 +292,8 @@ class DashboardV2Widget extends StatelessWidget {
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
alert['description'] ?? '', alert['description'] ?? '',
style: const TextStyle( style: TextStyle(
color: AppColor.textSecondary, color: cs.onSurfaceVariant,
fontSize: 11, fontSize: 11,
), ),
maxLines: 2, maxLines: 2,
@@ -314,14 +311,14 @@ class DashboardV2Widget extends StatelessWidget {
alert['date'] != null alert['date'] != null
? alert['date'].toString().split(' ')[0] ? alert['date'].toString().split(' ')[0]
: '', : '',
style: const TextStyle( style: TextStyle(
color: AppColor.textSecondary, color: cs.onSurfaceVariant,
fontSize: 10, fontSize: 10,
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Icon(Icons.arrow_forward_ios_rounded, Icon(Icons.arrow_forward_ios_rounded,
color: AppColor.textSecondary, size: 12), color: cs.onSurfaceVariant, size: 12),
], ],
), ),
], ],
File diff suppressed because it is too large Load Diff
@@ -10,6 +10,9 @@ class GlassContainer extends StatelessWidget {
final double borderRadius; final double borderRadius;
final List<Color>? gradientColors; final List<Color>? gradientColors;
final double blur; final double blur;
final double opacity;
final bool showBorder;
final Color? borderColor;
const GlassContainer({ const GlassContainer({
super.key, super.key,
@@ -20,15 +23,35 @@ class GlassContainer extends StatelessWidget {
this.margin, this.margin,
this.borderRadius = 16.0, this.borderRadius = 16.0,
this.gradientColors, this.gradientColors,
this.blur = 10.0, this.blur = 20.0,
this.opacity = 0.1,
this.showBorder = true,
this.borderColor,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final defaultGradient = [ final isDark = Theme.of(context).brightness == Brightness.dark;
Colors.white.withOpacity(0.1), final cs = Theme.of(context).colorScheme;
Colors.white.withOpacity(0.05),
]; 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( return Container(
width: width, width: width,
@@ -38,10 +61,11 @@ class GlassContainer extends StatelessWidget {
borderRadius: BorderRadius.circular(borderRadius), borderRadius: BorderRadius.circular(borderRadius),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.1), color: shadowColor,
blurRadius: 20, blurRadius: 24,
spreadRadius: 1, spreadRadius: 0,
) offset: const Offset(0, 8),
),
], ],
), ),
child: ClipRRect( child: ClipRRect(
@@ -52,10 +76,9 @@ class GlassContainer extends StatelessWidget {
padding: padding, padding: padding,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadius), borderRadius: BorderRadius.circular(borderRadius),
border: Border.all( border: showBorder
color: Colors.white.withOpacity(0.2), ? Border.all(color: effectiveBorderColor, width: 1)
width: 1.5, : null,
),
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
+116 -48
View File
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.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/main.dart';
import 'package:siro_admin/constant/box_name.dart'; import 'package:siro_admin/constant/box_name.dart';
import 'package:siro_admin/views/admin/captain/captain.dart'; import 'package:siro_admin/views/admin/captain/captain.dart';
@@ -40,6 +40,8 @@ class WebSidebar extends StatelessWidget {
@override @override
Widget build(BuildContext context) { 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 myPhone = box.read(BoxName.adminPhone)?.toString() ?? '';
final String role = box.read('admin_role')?.toString() ?? 'admin'; final String role = box.read('admin_role')?.toString() ?? 'admin';
final bool isSuperAdmin = (role == 'super_admin') || final bool isSuperAdmin = (role == 'super_admin') ||
@@ -49,11 +51,11 @@ class WebSidebar extends StatelessWidget {
return Container( return Container(
width: 270, width: 270,
color: AppColor.surface, color: cs.surface,
child: Column( child: Column(
children: [ children: [
_buildLogo(), _buildLogo(context),
const Divider(color: AppColor.divider, height: 1), Divider(color: cs.outline, height: 1),
Expanded( Expanded(
child: ListView( child: ListView(
padding: const EdgeInsets.symmetric(vertical: 12), padding: const EdgeInsets.symmetric(vertical: 12),
@@ -68,8 +70,7 @@ class WebSidebar extends StatelessWidget {
}, },
), ),
// ─── التحليلات والإحصائيات ────────────────────────────── _buildSectionHeader(context, 'التحليلات والإحصائيات'),
_buildSectionHeader('التحليلات والإحصائيات'),
_buildNavItem( _buildNavItem(
context, context,
title: 'التحليلات الحية', title: 'التحليلات الحية',
@@ -95,8 +96,7 @@ class WebSidebar extends StatelessWidget {
}, },
), ),
// ─── إدارة الكباتن والركاب ────────────────────────────── _buildSectionHeader(context, 'الكباتن والركاب'),
_buildSectionHeader('الكباتن والركاب'),
_buildNavItem( _buildNavItem(
context, context,
title: 'إدارة الركاب', title: 'إدارة الركاب',
@@ -140,8 +140,7 @@ class WebSidebar extends StatelessWidget {
onTap: () => Get.to(() => const BlacklistPage()), onTap: () => Get.to(() => const BlacklistPage()),
), ),
// ─── الرحلات والمراقبة ────────────────────────────── _buildSectionHeader(context, 'الرحلات والتتبع'),
_buildSectionHeader('الرحلات والتتبع'),
_buildNavItem( _buildNavItem(
context, context,
title: 'شاشة الرحلات', title: 'شاشة الرحلات',
@@ -157,13 +156,6 @@ class WebSidebar extends StatelessWidget {
index: 6, index: 6,
onTap: () => Get.to(() => RideMonitorScreen()), onTap: () => Get.to(() => RideMonitorScreen()),
), ),
// _buildNavItem(
// context,
// title: 'البحث عن رحلة',
// icon: Icons.search_rounded,
// index: 107,
// onTap: () => Get.to(() => RideLookupPage()),
// ),
_buildNavItem( _buildNavItem(
context, context,
title: 'تتبع الكباتن (المراقب)', title: 'تتبع الكباتن (المراقب)',
@@ -172,8 +164,7 @@ class WebSidebar extends StatelessWidget {
onTap: () => Get.to(() => SiroTrackerScreen()), onTap: () => Get.to(() => SiroTrackerScreen()),
), ),
// ─── مواصلاتي والشركات ────────────────────────────── _buildSectionHeader(context, 'نظام مواصلاتي'),
_buildSectionHeader('نظام مواصلاتي'),
_buildNavItem( _buildNavItem(
context, context,
title: 'إدارة المؤسسات والشركات', title: 'إدارة المؤسسات والشركات',
@@ -182,8 +173,7 @@ class WebSidebar extends StatelessWidget {
onTap: () => Get.to(() => const TransitOrgListPage()), onTap: () => Get.to(() => const TransitOrgListPage()),
), ),
// ─── المالية والاشتراكات ────────────────────────────── _buildSectionHeader(context, 'المالية والأسعار'),
_buildSectionHeader('المالية والأسعار'),
_buildNavItem( _buildNavItem(
context, context,
title: 'التقرير المالي (V2)', title: 'التقرير المالي (V2)',
@@ -227,11 +217,10 @@ class WebSidebar extends StatelessWidget {
onTap: () => Get.toNamed('/promo'), onTap: () => Get.toNamed('/promo'),
), ),
// ─── التسويق والدعم الفني ────────────────────────────── _buildSectionHeader(context, 'التسويق والدعم'),
_buildSectionHeader('التسويق والدعم'),
_buildNavItem( _buildNavItem(
context, context,
title: 'حركية التسويق والـ AI', title: 'حركات التسويق والـ AI',
icon: Icons.campaign_rounded, icon: Icons.campaign_rounded,
index: 112, index: 112,
onTap: () => Get.toNamed('/marketing'), onTap: () => Get.toNamed('/marketing'),
@@ -258,9 +247,8 @@ class WebSidebar extends StatelessWidget {
onTap: () => Get.toNamed('/complaints'), onTap: () => Get.toNamed('/complaints'),
), ),
// ─── إدارة النظام والأمان ──────────────────────────────
if (isSuperAdmin) ...[ if (isSuperAdmin) ...[
_buildSectionHeader('إدارة النظام والأمان'), _buildSectionHeader(context, 'إدارة النظام والأمان'),
_buildNavItem( _buildNavItem(
context, context,
title: 'مراقبة السيرفرات', 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), _buildLogoutBtn(context),
], ],
), ),
); );
} }
Widget _buildLogo() { Widget _buildLogo(BuildContext context) {
final cs = Theme.of(context).colorScheme;
return Container( return Container(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20), padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Row( child: Row(
@@ -322,12 +312,12 @@ class WebSidebar extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColor.accent.withOpacity(0.1), color: cs.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12), 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, child: Icon(Icons.admin_panel_settings_rounded,
color: AppColor.accent, size: 28), color: cs.primary, size: 28),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Column( Column(
@@ -336,7 +326,7 @@ class WebSidebar extends StatelessWidget {
Text( Text(
'SIRO ADMIN', 'SIRO ADMIN',
style: TextStyle( style: TextStyle(
color: AppColor.textPrimary, color: cs.onSurface,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 16, fontSize: 16,
letterSpacing: 1.1, letterSpacing: 1.1,
@@ -346,7 +336,7 @@ class WebSidebar extends StatelessWidget {
Text( Text(
'Web Dashboard', 'Web Dashboard',
style: TextStyle( style: TextStyle(
color: AppColor.textSecondary, color: cs.onSurfaceVariant,
fontSize: 11, 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( return Padding(
padding: const EdgeInsets.only(right: 20, left: 20, top: 18, bottom: 6), padding: const EdgeInsets.only(right: 20, left: 20, top: 18, bottom: 6),
child: Text( child: Text(
title, title,
style: TextStyle( style: TextStyle(
color: AppColor.accent.withOpacity(0.85), color: cs.primary.withValues(alpha: 0.85),
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
letterSpacing: 0.5, letterSpacing: 0.5,
@@ -379,6 +370,7 @@ class WebSidebar extends StatelessWidget {
required int index, required int index,
required VoidCallback onTap, required VoidCallback onTap,
}) { }) {
final cs = Theme.of(context).colorScheme;
final bool isSelected = selectedIndex == index; final bool isSelected = selectedIndex == index;
return Padding( return Padding(
@@ -388,16 +380,16 @@ class WebSidebar extends StatelessWidget {
child: InkWell( child: InkWell(
onTap: onTap, onTap: onTap,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
hoverColor: AppColor.accent.withOpacity(0.08), hoverColor: cs.primary.withValues(alpha: 0.08),
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected color: isSelected
? AppColor.accent.withOpacity(0.15) ? cs.primary.withValues(alpha: 0.15)
: Colors.transparent, : Colors.transparent,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
border: isSelected border: isSelected
? Border.all(color: AppColor.accent.withOpacity(0.4)) ? Border.all(color: cs.primary.withValues(alpha: 0.4))
: null, : null,
), ),
child: Row( child: Row(
@@ -405,15 +397,14 @@ class WebSidebar extends StatelessWidget {
Icon( Icon(
icon, icon,
size: 20, size: 20,
color: isSelected ? AppColor.accent : AppColor.textSecondary, color: isSelected ? cs.primary : cs.onSurfaceVariant,
), ),
const SizedBox(width: 14), const SizedBox(width: 14),
Expanded( Expanded(
child: Text( child: Text(
title, title,
style: TextStyle( style: TextStyle(
color: color: isSelected ? cs.primary : cs.onSurface,
isSelected ? AppColor.accent : AppColor.textPrimary,
fontSize: 13.5, fontSize: 13.5,
fontWeight: fontWeight:
isSelected ? FontWeight.bold : FontWeight.w500, 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) { Widget _buildLogoutBtn(BuildContext context) {
final cs = Theme.of(context).colorScheme;
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: InkWell( child: InkWell(
@@ -440,18 +508,18 @@ class WebSidebar extends StatelessWidget {
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1), color: cs.error.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(10), 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: [ children: [
Icon(Icons.logout_rounded, color: Colors.redAccent, size: 20), Icon(Icons.logout_rounded, color: cs.error, size: 20),
SizedBox(width: 14), const SizedBox(width: 14),
Text( Text(
'تسجيل الخروج', 'تسجيل الخروج',
style: TextStyle( style: TextStyle(
color: Colors.redAccent, color: cs.error,
fontSize: 13.5, fontSize: 13.5,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),