531 lines
20 KiB
Dart
531 lines
20 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:tripz_admin/main.dart';
|
|
import 'package:tripz_admin/constant/box_name.dart';
|
|
import 'package:tripz_admin/views/admin/captain/captain.dart';
|
|
import 'package:tripz_admin/views/admin/passenger/passenger.dart';
|
|
import 'package:tripz_admin/views/admin/drivers/driver_tracker_screen.dart';
|
|
import 'package:tripz_admin/views/transit/org_list_page.dart';
|
|
import 'package:tripz_admin/views/admin/static/static.dart';
|
|
import 'package:tripz_admin/controller/admin/static_controller.dart';
|
|
import 'package:tripz_admin/views/admin/drivers/monitor_ride.dart';
|
|
import 'package:tripz_admin/views/admin/rides/ride_lookup_page.dart';
|
|
import 'package:tripz_admin/views/admin/packages.dart';
|
|
import 'package:tripz_admin/views/admin/wallet/wallet.dart';
|
|
import 'package:tripz_admin/views/admin/captain/syrian_driver_not_active.dart';
|
|
import 'package:tripz_admin/views/admin/drivers/driver_gift_check_page.dart';
|
|
import 'package:tripz_admin/views/admin/quality/blacklist_page.dart';
|
|
import 'package:tripz_admin/views/admin/employee/employee_page.dart';
|
|
import 'package:tripz_admin/views/admin/server/monitor_server_page.dart';
|
|
import 'package:tripz_admin/views/admin/error/error/error_page.dart';
|
|
import 'package:tripz_admin/views/invoice/invoice_list_page.dart';
|
|
import 'package:tripz_admin/views/admin/staff/add_staff_page.dart';
|
|
import 'package:tripz_admin/views/admin/staff/pending_admins_page.dart';
|
|
import 'package:tripz_admin/views/admin/static/advanced_analytics_page.dart';
|
|
import 'package:tripz_admin/views/admin/financial/financial_v2_page.dart';
|
|
import 'package:tripz_admin/views/admin/security/audit_logs_page.dart';
|
|
import 'package:tripz_admin/views/admin/analytics/live_analytics_page.dart';
|
|
|
|
class WebSidebar extends StatelessWidget {
|
|
final int selectedIndex;
|
|
final Function(int)? onItemSelected;
|
|
|
|
const WebSidebar({
|
|
Key? key,
|
|
this.selectedIndex = 0,
|
|
this.onItemSelected,
|
|
}) : super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final cs = Theme.of(context).colorScheme;
|
|
final String myPhone = box.read(BoxName.adminPhone)?.toString() ?? '';
|
|
final String role = box.read('admin_role')?.toString() ?? 'admin';
|
|
final bool isSuperAdmin = (role == 'super_admin') ||
|
|
(myPhone == '201023248456' ||
|
|
myPhone == '963992952235' ||
|
|
myPhone == '962798583052');
|
|
|
|
return Container(
|
|
width: 270,
|
|
color: cs.surface,
|
|
child: Column(
|
|
children: [
|
|
_buildLogo(context),
|
|
Divider(color: cs.outline, height: 1),
|
|
Expanded(
|
|
child: ListView(
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
children: [
|
|
_buildNavItem(
|
|
context,
|
|
title: 'اللوحة الرئيسية',
|
|
icon: Icons.dashboard_rounded,
|
|
index: 0,
|
|
onTap: () {
|
|
if (onItemSelected != null) onItemSelected!(0);
|
|
},
|
|
),
|
|
|
|
_buildSectionHeader(context, 'التحليلات والإحصائيات'),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'التحليلات الحية',
|
|
icon: Icons.ssid_chart_rounded,
|
|
index: 101,
|
|
onTap: () => Get.to(() => const LiveAnalyticsPage()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'التحليلات المتقدمة',
|
|
icon: Icons.analytics_rounded,
|
|
index: 102,
|
|
onTap: () => Get.to(() => const AdvancedAnalyticsPage()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'إحصائيات النظام',
|
|
icon: Icons.bar_chart_rounded,
|
|
index: 7,
|
|
onTap: () async {
|
|
await Get.put(StaticController()).getAll();
|
|
Get.to(() => const StaticDash());
|
|
},
|
|
),
|
|
|
|
_buildSectionHeader(context, 'الكباتن والركاب'),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'إدارة الركاب',
|
|
icon: Icons.people_outline_rounded,
|
|
index: 1,
|
|
onTap: () => Get.to(() => Passengrs()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'قائمة الكباتن',
|
|
icon: Icons.drive_eta_rounded,
|
|
index: 2,
|
|
onTap: () => Get.to(() => CaptainsPage()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'طلبات الكباتن المعلقة',
|
|
icon: Icons.person_add_disabled_rounded,
|
|
index: 103,
|
|
onTap: () => Get.to(() => DriversPendingPage()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'مراجعة الوثائق',
|
|
icon: Icons.assignment_ind_rounded,
|
|
index: 104,
|
|
onTap: () => Get.toNamed('/driver-docs'),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'هدايا وعروض الكباتن',
|
|
icon: Icons.card_giftcard_rounded,
|
|
index: 105,
|
|
onTap: () => Get.to(() => const DriverGiftCheckPage()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'القائمة السوداء',
|
|
icon: Icons.block_rounded,
|
|
index: 106,
|
|
onTap: () => Get.to(() => const BlacklistPage()),
|
|
),
|
|
|
|
_buildSectionHeader(context, 'الرحلات والتتبع'),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'شاشة الرحلات',
|
|
icon: Icons.map_rounded,
|
|
index: 5,
|
|
onTap: () => Get.to(() => RidesDashboardScreen()),
|
|
),
|
|
if (isSuperAdmin)
|
|
_buildNavItem(
|
|
context,
|
|
title: 'مراقبة الرحلات الحية',
|
|
icon: Icons.remove_red_eye_rounded,
|
|
index: 6,
|
|
onTap: () => Get.to(() => RideMonitorScreen()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'تتبع الكباتن (المراقب)',
|
|
icon: Icons.track_changes_rounded,
|
|
index: 3,
|
|
onTap: () => Get.to(() => TripzTrackerScreen()),
|
|
),
|
|
|
|
_buildSectionHeader(context, 'نظام مواصلاتي'),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'إدارة المؤسسات والشركات',
|
|
icon: Icons.directions_bus_filled_rounded,
|
|
index: 4,
|
|
onTap: () => Get.to(() => const TransitOrgListPage()),
|
|
),
|
|
|
|
_buildSectionHeader(context, 'المالية والأسعار'),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'التقرير المالي (V2)',
|
|
icon: Icons.account_balance_wallet_rounded,
|
|
index: 108,
|
|
onTap: () => Get.to(() => const FinancialV2Page()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'المحافظ المالية',
|
|
icon: Icons.account_balance_rounded,
|
|
index: 109,
|
|
onTap: () => Get.to(() => Wallet()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'الباقات والاشتراكات',
|
|
icon: Icons.card_membership_rounded,
|
|
index: 110,
|
|
onTap: () => Get.to(() => PackageUpdateScreen()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'الفواتير',
|
|
icon: Icons.receipt_long_rounded,
|
|
index: 111,
|
|
onTap: () => Get.to(() => const InvoiceListPage()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'تسعير الكازان',
|
|
icon: Icons.price_change_rounded,
|
|
index: 9,
|
|
onTap: () => Get.toNamed('/kazan'),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'أكواد الخصم (Promo)',
|
|
icon: Icons.confirmation_number_rounded,
|
|
index: 8,
|
|
onTap: () => Get.toNamed('/promo'),
|
|
),
|
|
|
|
_buildSectionHeader(context, 'التسويق والدعم'),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'حركات التسويق والـ AI',
|
|
icon: Icons.campaign_rounded,
|
|
index: 112,
|
|
onTap: () => Get.toNamed('/marketing'),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'الخريطة الحرارية (Heatmap)',
|
|
icon: Icons.local_fire_department_rounded,
|
|
index: 113,
|
|
onTap: () => Get.toNamed('/heatmap'),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'الذكاء الاجتماعي',
|
|
icon: Icons.psychology_rounded,
|
|
index: 114,
|
|
onTap: () => Get.toNamed('/social-intelligence'),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'إدارة الشكاوى',
|
|
icon: Icons.report_problem_rounded,
|
|
index: 115,
|
|
onTap: () => Get.toNamed('/complaints'),
|
|
),
|
|
|
|
if (isSuperAdmin) ...[
|
|
_buildSectionHeader(context, 'إدارة النظام والأمان'),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'مراقبة السيرفرات',
|
|
icon: Icons.dns_rounded,
|
|
index: 116,
|
|
onTap: () => Get.to(() => ServerMonitorPage()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'سجلات الأمان والعمليات',
|
|
icon: Icons.admin_panel_settings_outlined,
|
|
index: 117,
|
|
onTap: () => Get.to(() => const AuditLogsPage()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'فريق العمل والموظفين',
|
|
icon: Icons.badge_rounded,
|
|
index: 118,
|
|
onTap: () => Get.to(() => EmployeePage()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'إضافة مشرف جديد',
|
|
icon: Icons.person_add_rounded,
|
|
index: 119,
|
|
onTap: () => Get.to(() => const AddStaffPage(role: 'admin')),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'طلبات المشرفين المعلقة',
|
|
icon: Icons.hourglass_top_rounded,
|
|
index: 120,
|
|
onTap: () => Get.to(() => const PendingAdminsPage()),
|
|
),
|
|
_buildNavItem(
|
|
context,
|
|
title: 'أخطاء النظام',
|
|
icon: Icons.bug_report_rounded,
|
|
index: 121,
|
|
onTap: () => Get.to(() => const ErrorListPage()),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
Divider(color: cs.outline, height: 1),
|
|
_buildThemeToggle(context),
|
|
_buildLogoutBtn(context),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildLogo(BuildContext context) {
|
|
final cs = Theme.of(context).colorScheme;
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: cs.primary.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: cs.primary.withValues(alpha: 0.3)),
|
|
),
|
|
child: Icon(Icons.admin_panel_settings_rounded,
|
|
color: cs.primary, size: 28),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'TRIPZ ADMIN',
|
|
style: TextStyle(
|
|
color: cs.onSurface,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 16,
|
|
letterSpacing: 1.1,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
'Web Dashboard',
|
|
style: TextStyle(
|
|
color: cs.onSurfaceVariant,
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
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: cs.primary.withValues(alpha: 0.85),
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.bold,
|
|
letterSpacing: 0.5,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildNavItem(
|
|
BuildContext context, {
|
|
required String title,
|
|
required IconData icon,
|
|
required int index,
|
|
required VoidCallback onTap,
|
|
}) {
|
|
final cs = Theme.of(context).colorScheme;
|
|
final bool isSelected = selectedIndex == index;
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 2),
|
|
child: Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
onTap: onTap,
|
|
borderRadius: BorderRadius.circular(10),
|
|
hoverColor: cs.primary.withValues(alpha: 0.08),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
|
decoration: BoxDecoration(
|
|
color: isSelected
|
|
? cs.primary.withValues(alpha: 0.15)
|
|
: Colors.transparent,
|
|
borderRadius: BorderRadius.circular(10),
|
|
border: isSelected
|
|
? Border.all(color: cs.primary.withValues(alpha: 0.4))
|
|
: null,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
icon,
|
|
size: 20,
|
|
color: isSelected ? cs.primary : cs.onSurfaceVariant,
|
|
),
|
|
const SizedBox(width: 14),
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: TextStyle(
|
|
color: isSelected ? cs.primary : cs.onSurface,
|
|
fontSize: 13.5,
|
|
fontWeight:
|
|
isSelected ? FontWeight.bold : FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
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(
|
|
onTap: () {
|
|
box.erase();
|
|
Get.offAllNamed('/login');
|
|
},
|
|
borderRadius: BorderRadius.circular(10),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
|
decoration: BoxDecoration(
|
|
color: cs.error.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(10),
|
|
border: Border.all(color: cs.error.withValues(alpha: 0.2)),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.logout_rounded, color: cs.error, size: 20),
|
|
const SizedBox(width: 14),
|
|
Text(
|
|
'تسجيل الخروج',
|
|
style: TextStyle(
|
|
color: cs.error,
|
|
fontSize: 13.5,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|