تصميم عالمي جديد، وإصلاح الخرائط ومراقب السيرفرات
This commit is contained in:
@@ -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. */
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user