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

This commit is contained in:
Hamza-Ayed
2026-07-26 04:21:42 +03:00
parent f325ffce42
commit ca6cb7a3fe
10 changed files with 1367 additions and 673 deletions
@@ -10,6 +10,9 @@ class GlassContainer extends StatelessWidget {
final double borderRadius;
final List<Color>? gradientColors;
final double blur;
final double opacity;
final bool showBorder;
final Color? borderColor;
const GlassContainer({
super.key,
@@ -20,15 +23,35 @@ class GlassContainer extends StatelessWidget {
this.margin,
this.borderRadius = 16.0,
this.gradientColors,
this.blur = 10.0,
this.blur = 20.0,
this.opacity = 0.1,
this.showBorder = true,
this.borderColor,
});
@override
Widget build(BuildContext context) {
final defaultGradient = [
Colors.white.withOpacity(0.1),
Colors.white.withOpacity(0.05),
];
final isDark = Theme.of(context).brightness == Brightness.dark;
final cs = Theme.of(context).colorScheme;
final defaultGradient = isDark
? [
Colors.white.withValues(alpha: opacity),
Colors.white.withValues(alpha: opacity * 0.5),
]
: [
Colors.white.withValues(alpha: 0.7),
Colors.white.withValues(alpha: 0.4),
];
final effectiveBorderColor = borderColor ??
(isDark
? Colors.white.withValues(alpha: 0.15)
: Colors.white.withValues(alpha: 0.5));
final shadowColor = isDark
? Colors.black.withValues(alpha: 0.3)
: cs.shadow.withValues(alpha: 0.08);
return Container(
width: width,
@@ -38,10 +61,11 @@ class GlassContainer extends StatelessWidget {
borderRadius: BorderRadius.circular(borderRadius),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
spreadRadius: 1,
)
color: shadowColor,
blurRadius: 24,
spreadRadius: 0,
offset: const Offset(0, 8),
),
],
),
child: ClipRRect(
@@ -52,10 +76,9 @@ class GlassContainer extends StatelessWidget {
padding: padding,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadius),
border: Border.all(
color: Colors.white.withOpacity(0.2),
width: 1.5,
),
border: showBorder
? Border.all(color: effectiveBorderColor, width: 1)
: null,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
+116 -48
View File
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/constant/colors.dart';
import 'package:siro_admin/constant/theme.dart';
import 'package:siro_admin/main.dart';
import 'package:siro_admin/constant/box_name.dart';
import 'package:siro_admin/views/admin/captain/captain.dart';
@@ -40,6 +40,8 @@ class WebSidebar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
final String myPhone = box.read(BoxName.adminPhone)?.toString() ?? '';
final String role = box.read('admin_role')?.toString() ?? 'admin';
final bool isSuperAdmin = (role == 'super_admin') ||
@@ -49,11 +51,11 @@ class WebSidebar extends StatelessWidget {
return Container(
width: 270,
color: AppColor.surface,
color: cs.surface,
child: Column(
children: [
_buildLogo(),
const Divider(color: AppColor.divider, height: 1),
_buildLogo(context),
Divider(color: cs.outline, height: 1),
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 12),
@@ -68,8 +70,7 @@ class WebSidebar extends StatelessWidget {
},
),
// ─── التحليلات والإحصائيات ──────────────────────────────
_buildSectionHeader('التحليلات والإحصائيات'),
_buildSectionHeader(context, 'التحليلات والإحصائيات'),
_buildNavItem(
context,
title: 'التحليلات الحية',
@@ -95,8 +96,7 @@ class WebSidebar extends StatelessWidget {
},
),
// ─── إدارة الكباتن والركاب ──────────────────────────────
_buildSectionHeader('الكباتن والركاب'),
_buildSectionHeader(context, 'الكباتن والركاب'),
_buildNavItem(
context,
title: 'إدارة الركاب',
@@ -140,8 +140,7 @@ class WebSidebar extends StatelessWidget {
onTap: () => Get.to(() => const BlacklistPage()),
),
// ─── الرحلات والمراقبة ──────────────────────────────
_buildSectionHeader('الرحلات والتتبع'),
_buildSectionHeader(context, 'الرحلات والتتبع'),
_buildNavItem(
context,
title: 'شاشة الرحلات',
@@ -157,13 +156,6 @@ class WebSidebar extends StatelessWidget {
index: 6,
onTap: () => Get.to(() => RideMonitorScreen()),
),
// _buildNavItem(
// context,
// title: 'البحث عن رحلة',
// icon: Icons.search_rounded,
// index: 107,
// onTap: () => Get.to(() => RideLookupPage()),
// ),
_buildNavItem(
context,
title: 'تتبع الكباتن (المراقب)',
@@ -172,8 +164,7 @@ class WebSidebar extends StatelessWidget {
onTap: () => Get.to(() => SiroTrackerScreen()),
),
// ─── مواصلاتي والشركات ──────────────────────────────
_buildSectionHeader('نظام مواصلاتي'),
_buildSectionHeader(context, 'نظام مواصلاتي'),
_buildNavItem(
context,
title: 'إدارة المؤسسات والشركات',
@@ -182,8 +173,7 @@ class WebSidebar extends StatelessWidget {
onTap: () => Get.to(() => const TransitOrgListPage()),
),
// ─── المالية والاشتراكات ──────────────────────────────
_buildSectionHeader('المالية والأسعار'),
_buildSectionHeader(context, 'المالية والأسعار'),
_buildNavItem(
context,
title: 'التقرير المالي (V2)',
@@ -227,11 +217,10 @@ class WebSidebar extends StatelessWidget {
onTap: () => Get.toNamed('/promo'),
),
// ─── التسويق والدعم الفني ──────────────────────────────
_buildSectionHeader('التسويق والدعم'),
_buildSectionHeader(context, 'التسويق والدعم'),
_buildNavItem(
context,
title: 'حركية التسويق والـ AI',
title: 'حركات التسويق والـ AI',
icon: Icons.campaign_rounded,
index: 112,
onTap: () => Get.toNamed('/marketing'),
@@ -258,9 +247,8 @@ class WebSidebar extends StatelessWidget {
onTap: () => Get.toNamed('/complaints'),
),
// ─── إدارة النظام والأمان ──────────────────────────────
if (isSuperAdmin) ...[
_buildSectionHeader('إدارة النظام والأمان'),
_buildSectionHeader(context, 'إدارة النظام والأمان'),
_buildNavItem(
context,
title: 'مراقبة السيرفرات',
@@ -307,14 +295,16 @@ class WebSidebar extends StatelessWidget {
],
),
),
const Divider(color: AppColor.divider, height: 1),
Divider(color: cs.outline, height: 1),
_buildThemeToggle(context),
_buildLogoutBtn(context),
],
),
);
}
Widget _buildLogo() {
Widget _buildLogo(BuildContext context) {
final cs = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Row(
@@ -322,12 +312,12 @@ class WebSidebar extends StatelessWidget {
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: AppColor.accent.withOpacity(0.1),
color: cs.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.accent.withOpacity(0.3)),
border: Border.all(color: cs.primary.withValues(alpha: 0.3)),
),
child: const Icon(Icons.admin_panel_settings_rounded,
color: AppColor.accent, size: 28),
child: Icon(Icons.admin_panel_settings_rounded,
color: cs.primary, size: 28),
),
const SizedBox(width: 12),
Column(
@@ -336,7 +326,7 @@ class WebSidebar extends StatelessWidget {
Text(
'SIRO ADMIN',
style: TextStyle(
color: AppColor.textPrimary,
color: cs.onSurface,
fontWeight: FontWeight.bold,
fontSize: 16,
letterSpacing: 1.1,
@@ -346,7 +336,7 @@ class WebSidebar extends StatelessWidget {
Text(
'Web Dashboard',
style: TextStyle(
color: AppColor.textSecondary,
color: cs.onSurfaceVariant,
fontSize: 11,
),
),
@@ -357,13 +347,14 @@ class WebSidebar extends StatelessWidget {
);
}
Widget _buildSectionHeader(String title) {
Widget _buildSectionHeader(BuildContext context, String title) {
final cs = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.only(right: 20, left: 20, top: 18, bottom: 6),
child: Text(
title,
style: TextStyle(
color: AppColor.accent.withOpacity(0.85),
color: cs.primary.withValues(alpha: 0.85),
fontSize: 11,
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
@@ -379,6 +370,7 @@ class WebSidebar extends StatelessWidget {
required int index,
required VoidCallback onTap,
}) {
final cs = Theme.of(context).colorScheme;
final bool isSelected = selectedIndex == index;
return Padding(
@@ -388,16 +380,16 @@ class WebSidebar extends StatelessWidget {
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(10),
hoverColor: AppColor.accent.withOpacity(0.08),
hoverColor: cs.primary.withValues(alpha: 0.08),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color: isSelected
? AppColor.accent.withOpacity(0.15)
? cs.primary.withValues(alpha: 0.15)
: Colors.transparent,
borderRadius: BorderRadius.circular(10),
border: isSelected
? Border.all(color: AppColor.accent.withOpacity(0.4))
? Border.all(color: cs.primary.withValues(alpha: 0.4))
: null,
),
child: Row(
@@ -405,15 +397,14 @@ class WebSidebar extends StatelessWidget {
Icon(
icon,
size: 20,
color: isSelected ? AppColor.accent : AppColor.textSecondary,
color: isSelected ? cs.primary : cs.onSurfaceVariant,
),
const SizedBox(width: 14),
Expanded(
child: Text(
title,
style: TextStyle(
color:
isSelected ? AppColor.accent : AppColor.textPrimary,
color: isSelected ? cs.primary : cs.onSurface,
fontSize: 13.5,
fontWeight:
isSelected ? FontWeight.bold : FontWeight.w500,
@@ -428,7 +419,84 @@ class WebSidebar extends StatelessWidget {
);
}
Widget _buildThemeToggle(BuildContext context) {
final cs = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
final newMode = isDark ? ThemeMode.light : ThemeMode.dark;
Get.changeThemeMode(newMode);
},
borderRadius: BorderRadius.circular(10),
hoverColor: cs.primary.withValues(alpha: 0.08),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color: cs.surfaceContainerHighest,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: cs.outline),
),
child: Row(
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: Icon(
isDark ? Icons.light_mode_rounded : Icons.dark_mode_rounded,
key: ValueKey(isDark),
size: 20,
color: isDark ? const Color(0xFFFBBF24) : cs.primary,
),
),
const SizedBox(width: 14),
Expanded(
child: Text(
isDark ? 'الوضع الفاتح' : 'الوضع الداكن',
style: TextStyle(
color: cs.onSurface,
fontSize: 13.5,
fontWeight: FontWeight.w500,
),
),
),
AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: 44,
height: 24,
decoration: BoxDecoration(
color: isDark
? cs.primary.withValues(alpha: 0.3)
: cs.outline,
borderRadius: BorderRadius.circular(12),
),
child: AnimatedAlign(
duration: const Duration(milliseconds: 300),
alignment: isDark ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
width: 20,
height: 20,
margin: const EdgeInsets.symmetric(horizontal: 2),
decoration: BoxDecoration(
color: isDark ? cs.primary : cs.onSurface,
shape: BoxShape.circle,
),
),
),
),
],
),
),
),
),
);
}
Widget _buildLogoutBtn(BuildContext context) {
final cs = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.all(16),
child: InkWell(
@@ -440,18 +508,18 @@ class WebSidebar extends StatelessWidget {
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
color: cs.error.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.red.withOpacity(0.2)),
border: Border.all(color: cs.error.withValues(alpha: 0.2)),
),
child: const Row(
child: Row(
children: [
Icon(Icons.logout_rounded, color: Colors.redAccent, size: 20),
SizedBox(width: 14),
Icon(Icons.logout_rounded, color: cs.error, size: 20),
const SizedBox(width: 14),
Text(
'تسجيل الخروج',
style: TextStyle(
color: Colors.redAccent,
color: cs.error,
fontSize: 13.5,
fontWeight: FontWeight.bold,
),