Update: 2026-07-26 02:51:53
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jwt_decoder/jwt_decoder.dart';
|
||||
@@ -185,7 +186,7 @@ class OtpHelper extends GetxController {
|
||||
await box.write(BoxName.phoneVerified, true);
|
||||
await box.write('admin_password', password);
|
||||
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||
try {
|
||||
FirebaseMessaging.instance.subscribeToTopic('admin_alerts');
|
||||
} catch (e) {
|
||||
@@ -247,7 +248,7 @@ class OtpHelper extends GetxController {
|
||||
String token = r(rawJwt.toString()).split(AppInformation.addd)[0];
|
||||
if (!JwtDecoder.isExpired(token)) {
|
||||
Log.print('Valid JWT found, skipping login.php (no OTP needed)');
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||
try {
|
||||
FirebaseMessaging.instance.subscribeToTopic('admin_alerts');
|
||||
} catch (e) {
|
||||
@@ -291,7 +292,7 @@ class OtpHelper extends GetxController {
|
||||
} else if (response['jwt'] != null) {
|
||||
box.write(BoxName.jwt, c(response['jwt']));
|
||||
}
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||
try {
|
||||
FirebaseMessaging.instance.subscribeToTopic('admin_alerts');
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
@@ -43,7 +44,9 @@ class FirebaseMessagesController extends GetxController {
|
||||
FirebaseMessaging messaging = FirebaseMessaging.instance;
|
||||
|
||||
// Check if the platform is Android
|
||||
if (Platform.isAndroid) {
|
||||
if (kIsWeb) {
|
||||
await messaging.requestPermission();
|
||||
} else if (Platform.isAndroid) {
|
||||
// Request permission for Android
|
||||
await messaging.requestPermission();
|
||||
} else if (Platform.isIOS) {
|
||||
@@ -248,7 +251,9 @@ class FirebaseMessagesController extends GetxController {
|
||||
}
|
||||
});
|
||||
|
||||
FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) {
|
||||
FirebaseMessaging.instance
|
||||
.getInitialMessage()
|
||||
.then((RemoteMessage? message) {
|
||||
if (message != null && message.data['type'] == 'marketing_report') {
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
Get.toNamed('/social-intelligence');
|
||||
@@ -259,7 +264,9 @@ class FirebaseMessagesController extends GetxController {
|
||||
|
||||
void fireBaseTitles(RemoteMessage message) {
|
||||
if (message.notification!.title! == 'Order') {
|
||||
if (Platform.isAndroid) {
|
||||
if (kIsWeb) {
|
||||
// Handle web push payload
|
||||
} else if (Platform.isAndroid) {
|
||||
// NotificationController().showNotification('Order', '', 'order');
|
||||
}
|
||||
var myListString = message.data['DriverList'];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
@@ -29,7 +30,10 @@ class CRUD {
|
||||
return;
|
||||
}
|
||||
|
||||
dev = Platform.isAndroid ? 'android' : 'ios';
|
||||
// Note: We send 'android' instead of 'web' because the backend's loginAdmin.php
|
||||
// explicitly validates the audience against 'allowedDriver1' (android) and 'allowedDriver2' (ios).
|
||||
// Sending 'web' will cause a 400 Bad Request error.
|
||||
dev = kIsWeb ? 'android' : (Platform.isAndroid ? 'android' : 'ios');
|
||||
|
||||
var payload = {
|
||||
'id': 'admin',
|
||||
@@ -261,7 +265,7 @@ class CRUD {
|
||||
var payload = {
|
||||
'id': box.read(BoxName.driverID) ?? '1',
|
||||
'password': AK.passnpassenger,
|
||||
'aud': '${Env.allowedWallet}${Platform.isAndroid ? 'android' : 'ios'}',
|
||||
'aud': '${Env.allowedWallet}${kIsWeb ? 'android' : (Platform.isAndroid ? 'android' : 'ios')}',
|
||||
'fingerPrint': fingerPrint
|
||||
};
|
||||
var fallbackRes = await _client.post(
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:jailbreak_root_detection/jailbreak_root_detection.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import '../../main.dart';
|
||||
import '../../print.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
|
||||
class DeviceHelper {
|
||||
static Future<String> getDeviceFingerprint() async {
|
||||
@@ -15,11 +16,15 @@ class DeviceHelper {
|
||||
Map<String, dynamic> deviceData;
|
||||
|
||||
try {
|
||||
if (kIsWeb) {
|
||||
WebBrowserInfo webInfo = await deviceInfoPlugin.webBrowserInfo;
|
||||
return '${webInfo.vendor}_${webInfo.browserName.name}';
|
||||
}
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
// Fetch Android-specific device information
|
||||
AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
|
||||
deviceData = androidInfo.toMap(); // Convert to a map for easier access
|
||||
// Log.print('deviceData: ${jsonEncode(deviceData)}');
|
||||
} else if (Platform.isIOS) {
|
||||
// Fetch iOS-specific device information
|
||||
IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo;
|
||||
@@ -29,7 +34,7 @@ class DeviceHelper {
|
||||
MacOsDeviceInfo macInfo = await deviceInfoPlugin.macOsInfo;
|
||||
deviceData = macInfo.toMap();
|
||||
} else {
|
||||
throw UnsupportedError('Unsupported platform');
|
||||
return 'unknown_platform';
|
||||
}
|
||||
|
||||
String deviceId = 'unknown';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http/io_client.dart' as http_io;
|
||||
@@ -25,6 +26,10 @@ class SslPinning {
|
||||
];
|
||||
|
||||
static http.Client createPinnedClient() {
|
||||
if (kIsWeb) {
|
||||
return http.Client();
|
||||
}
|
||||
|
||||
final httpClient = HttpClient()
|
||||
..badCertificateCallback =
|
||||
(X509Certificate cert, String host, int port) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
import 'constant/colors.dart';
|
||||
import 'routes.dart';
|
||||
import 'binding/initial_binding.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
|
||||
final box = GetStorage();
|
||||
const storage = FlutterSecureStorage();
|
||||
@@ -35,7 +36,7 @@ void main() async {
|
||||
await GetStorage.init();
|
||||
await initializeDateFormatting('ar', null);
|
||||
await EncryptionHelper.initialize();
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
|
||||
@@ -36,8 +36,9 @@ import 'static/advanced_analytics_page.dart';
|
||||
import 'financial/financial_v2_page.dart';
|
||||
import 'security/audit_logs_page.dart';
|
||||
import 'analytics/live_analytics_page.dart';
|
||||
import 'package:siro_admin/views/widgets/responsive_layout.dart';
|
||||
import 'package:siro_admin/views/widgets/web_sidebar.dart';
|
||||
import '../transit/org_list_page.dart';
|
||||
|
||||
class AdminHomePage extends StatefulWidget {
|
||||
const AdminHomePage({super.key});
|
||||
|
||||
@@ -104,6 +105,21 @@ class _AdminHomePageState extends State<AdminHomePage>
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveLayout(
|
||||
mobile: _buildMobileScaffold(),
|
||||
desktop: Scaffold(
|
||||
backgroundColor: _bg,
|
||||
body: Row(
|
||||
children: [
|
||||
const WebSidebar(selectedIndex: 0),
|
||||
Expanded(child: _buildDesktopScaffold()),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMobileScaffold() {
|
||||
return Scaffold(
|
||||
backgroundColor: _bg,
|
||||
body: RefreshIndicator(
|
||||
@@ -160,6 +176,28 @@ class _AdminHomePageState extends State<AdminHomePage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDesktopScaffold() {
|
||||
return Scaffold(
|
||||
backgroundColor: _bg,
|
||||
body: GetBuilder<DashboardController>(
|
||||
builder: (controller) {
|
||||
if (controller.dashbord.isEmpty) {
|
||||
return _buildLoadingState();
|
||||
}
|
||||
final data = controller.dashbord[0];
|
||||
return CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
_buildSliverAppBar(controller),
|
||||
const DashboardV2Widget(),
|
||||
_buildQuickStatsSection(data, controller),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// LOADING STATE
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ResponsiveLayout extends StatelessWidget {
|
||||
final Widget mobile;
|
||||
final Widget desktop;
|
||||
|
||||
const ResponsiveLayout({
|
||||
Key? key,
|
||||
required this.mobile,
|
||||
required this.desktop,
|
||||
}) : super(key: key);
|
||||
|
||||
static bool isDesktop(BuildContext context) {
|
||||
return MediaQuery.of(context).size.width >= 900; // standard desktop breakpoint
|
||||
}
|
||||
|
||||
static bool isMobile(BuildContext context) {
|
||||
return MediaQuery.of(context).size.width < 900;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
if (constraints.maxWidth >= 900) {
|
||||
return desktop;
|
||||
} else {
|
||||
return mobile;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:siro_admin/constant/colors.dart';
|
||||
import 'package:siro_admin/main.dart';
|
||||
import 'package:siro_admin/constant/box_name.dart';
|
||||
import 'package:siro_admin/views/admin/captain/captain.dart';
|
||||
import 'package:siro_admin/views/admin/passenger/passenger.dart';
|
||||
import 'package:siro_admin/views/admin/drivers/driver_tracker_screen.dart';
|
||||
import 'package:siro_admin/views/transit/org_list_page.dart';
|
||||
import 'package:siro_admin/views/admin/rides/rides.dart';
|
||||
import 'package:siro_admin/views/admin/static/static.dart';
|
||||
import 'package:siro_admin/controller/admin/static_controller.dart';
|
||||
import 'package:siro_admin/views/admin/drivers/monitor_ride.dart';
|
||||
import 'package:siro_admin/views/admin/rides/ride_lookup_page.dart';
|
||||
import 'package:siro_admin/views/admin/packages.dart';
|
||||
import 'package:siro_admin/views/admin/wallet/wallet.dart';
|
||||
import 'package:siro_admin/views/admin/captain/syrian_driver_not_active.dart';
|
||||
import 'package:siro_admin/views/admin/drivers/driver_gift_check_page.dart';
|
||||
import 'package:siro_admin/views/admin/quality/blacklist_page.dart';
|
||||
import 'package:siro_admin/views/admin/employee/employee_page.dart';
|
||||
import 'package:siro_admin/views/admin/server/monitor_server_page.dart';
|
||||
import 'package:siro_admin/views/admin/error/error/error_page.dart';
|
||||
import 'package:siro_admin/views/invoice/invoice_list_page.dart';
|
||||
import 'package:siro_admin/views/admin/staff/add_staff_page.dart';
|
||||
import 'package:siro_admin/views/admin/staff/pending_admins_page.dart';
|
||||
import 'package:siro_admin/views/admin/static/advanced_analytics_page.dart';
|
||||
import 'package:siro_admin/views/admin/financial/financial_v2_page.dart';
|
||||
import 'package:siro_admin/views/admin/security/audit_logs_page.dart';
|
||||
import 'package:siro_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 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: AppColor.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
_buildLogo(),
|
||||
const Divider(color: AppColor.divider, 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('التحليلات والإحصائيات'),
|
||||
_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('الكباتن والركاب'),
|
||||
_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('الرحلات والتتبع'),
|
||||
_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.search_rounded,
|
||||
// index: 107,
|
||||
// onTap: () => Get.to(() => RideLookupPage()),
|
||||
// ),
|
||||
_buildNavItem(
|
||||
context,
|
||||
title: 'تتبع الكباتن (المراقب)',
|
||||
icon: Icons.track_changes_rounded,
|
||||
index: 3,
|
||||
onTap: () => Get.to(() => SiroTrackerScreen()),
|
||||
),
|
||||
|
||||
// ─── مواصلاتي والشركات ──────────────────────────────
|
||||
_buildSectionHeader('نظام مواصلاتي'),
|
||||
_buildNavItem(
|
||||
context,
|
||||
title: 'إدارة المؤسسات والشركات',
|
||||
icon: Icons.directions_bus_filled_rounded,
|
||||
index: 4,
|
||||
onTap: () => Get.to(() => const TransitOrgListPage()),
|
||||
),
|
||||
|
||||
// ─── المالية والاشتراكات ──────────────────────────────
|
||||
_buildSectionHeader('المالية والأسعار'),
|
||||
_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('التسويق والدعم'),
|
||||
_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('إدارة النظام والأمان'),
|
||||
_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()),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(color: AppColor.divider, height: 1),
|
||||
_buildLogoutBtn(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLogo() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.accent.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColor.accent.withOpacity(0.3)),
|
||||
),
|
||||
child: const Icon(Icons.admin_panel_settings_rounded,
|
||||
color: AppColor.accent, size: 28),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'SIRO ADMIN',
|
||||
style: TextStyle(
|
||||
color: AppColor.textPrimary,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
letterSpacing: 1.1,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'Web Dashboard',
|
||||
style: TextStyle(
|
||||
color: AppColor.textSecondary,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionHeader(String title) {
|
||||
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),
|
||||
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 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: AppColor.accent.withOpacity(0.08),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? AppColor.accent.withOpacity(0.15)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: isSelected
|
||||
? Border.all(color: AppColor.accent.withOpacity(0.4))
|
||||
: null,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 20,
|
||||
color: isSelected ? AppColor.accent : AppColor.textSecondary,
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color:
|
||||
isSelected ? AppColor.accent : AppColor.textPrimary,
|
||||
fontSize: 13.5,
|
||||
fontWeight:
|
||||
isSelected ? FontWeight.bold : FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLogoutBtn(BuildContext context) {
|
||||
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: Colors.red.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: Colors.red.withOpacity(0.2)),
|
||||
),
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.logout_rounded, color: Colors.redAccent, size: 20),
|
||||
SizedBox(width: 14),
|
||||
Text(
|
||||
'تسجيل الخروج',
|
||||
style: TextStyle(
|
||||
color: Colors.redAccent,
|
||||
fontSize: 13.5,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user