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