Initial commit for intaleq_admin
This commit is contained in:
@@ -1,180 +1,237 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_admin1/constant/colors.dart';
|
||||
import 'package:sefer_admin1/controller/admin/dashboard_controller.dart';
|
||||
import 'package:sefer_admin1/controller/admin/register_captain_controller.dart';
|
||||
import 'package:sefer_admin1/controller/admin/static_controller.dart';
|
||||
import 'package:sefer_admin1/controller/notification_controller.dart';
|
||||
import 'package:sefer_admin1/main.dart';
|
||||
import 'package:sefer_admin1/views/admin/captain/drivers_cant_registe.dart';
|
||||
import 'package:sefer_admin1/views/admin/drivers/driver_tracker_screen.dart';
|
||||
import 'package:sefer_admin1/views/admin/error/error/error_page.dart';
|
||||
import 'package:sefer_admin1/views/widgets/mycircular.dart';
|
||||
|
||||
// Please make sure all these imports are correct for your project structure
|
||||
// تأكد من صحة المسارات
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/links.dart';
|
||||
import '../../constant/style.dart';
|
||||
import '../../controller/functions/crud.dart';
|
||||
import '../invoice/invoice_list_page.dart';
|
||||
import '../widgets/my_scafold.dart';
|
||||
import '../widgets/my_textField.dart';
|
||||
import '../invoice/add_invoice_page.dart';
|
||||
import 'captain/captain.dart';
|
||||
import 'dashboard_widget.dart'; // Assuming DashboardStatCard is here
|
||||
import 'captain/syrian_driver_not_active.dart';
|
||||
import 'drivers/driver_gift_check_page.dart';
|
||||
import 'drivers/driver_the_best.dart';
|
||||
import 'drivers/monitor_ride.dart';
|
||||
import 'employee/employee_page.dart';
|
||||
import 'packages.dart';
|
||||
import 'passenger/passenger.dart';
|
||||
import 'rides/rides.dart';
|
||||
import 'rides/ride_lookup_page.dart';
|
||||
import 'static/static.dart';
|
||||
import 'wallet/wallet.dart';
|
||||
|
||||
class AdminHomePage extends StatelessWidget {
|
||||
AdminHomePage({super.key});
|
||||
|
||||
// Responsive grid column calculation
|
||||
int _calculateCrossAxisCount(BuildContext context) {
|
||||
double screenWidth = MediaQuery.of(context).size.width;
|
||||
if (screenWidth > 1200) return 5; // Large desktops
|
||||
if (screenWidth > 900) return 4; // Desktops
|
||||
if (screenWidth > 600) return 3; // Tablets
|
||||
return 2; // Phones
|
||||
}
|
||||
|
||||
// Helper to format currency
|
||||
String _formatCurrency(dynamic value) {
|
||||
if (value == null) return '\$0.00';
|
||||
final number = double.tryParse(value.toString());
|
||||
if (number != null) return '\$${number.toStringAsFixed(2)}';
|
||||
return '\$0.00';
|
||||
}
|
||||
|
||||
final TextEditingController _messageController = TextEditingController();
|
||||
|
||||
// حساب عدد الأعمدة
|
||||
int _calculateCrossAxisCount(BuildContext context, {bool isSmall = false}) {
|
||||
double screenWidth = MediaQuery.of(context).size.width;
|
||||
if (screenWidth > 1200) return isSmall ? 6 : 5;
|
||||
if (screenWidth > 900) return isSmall ? 5 : 4;
|
||||
if (screenWidth > 600) return isSmall ? 4 : 3;
|
||||
return isSmall ? 3 : 2;
|
||||
}
|
||||
|
||||
String _formatCurrency(dynamic value) {
|
||||
if (value == null) return '0.00';
|
||||
final number = double.tryParse(value.toString());
|
||||
if (number != null) return number.toStringAsFixed(2);
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Make sure DashboardController is initialized
|
||||
final DashboardController dashboardController =
|
||||
Get.put(DashboardController());
|
||||
|
||||
// Action items list with Arabic titles
|
||||
// 1. تحديد هوية المستخدم الحالي
|
||||
String myPhone = box.read(BoxName.adminPhone).toString();
|
||||
|
||||
// 2. تحديد من هو "السوبر أدمن" الذي يرى كل شيء
|
||||
// يمكنك إضافة المزيد من الأرقام هنا باستخدام || أو قائمة
|
||||
bool isSuperAdmin = myPhone == '963942542053' || myPhone == '963992952235';
|
||||
|
||||
// 3. بناء القائمة باستخدام (Collection If)
|
||||
final List<Map<String, dynamic>> actionItems = [
|
||||
// --- عناصر يراها الجميع ---
|
||||
{
|
||||
'title': 'الركاب',
|
||||
'icon': Icons.people_alt_outlined,
|
||||
'onPressed': () => Get.to(() => Passengrs(),
|
||||
transition: Transition.rightToLeftWithFade)
|
||||
'icon': Icons.people_alt_rounded,
|
||||
'color': Colors.blueAccent,
|
||||
'onPressed': () =>
|
||||
Get.to(() => Passengrs(), transition: Transition.fadeIn)
|
||||
},
|
||||
{
|
||||
'title': 'الكباتن',
|
||||
'icon': Icons.sports_motorsports_outlined,
|
||||
'icon': Icons.sports_motorsports_rounded,
|
||||
'color': Colors.orangeAccent,
|
||||
'onPressed': () =>
|
||||
Get.to(() => Captain(), transition: Transition.rightToLeftWithFade)
|
||||
},
|
||||
{
|
||||
'title': 'المحفظة',
|
||||
'icon': Icons.account_balance_wallet_outlined,
|
||||
'onPressed': () =>
|
||||
Get.to(() => Wallet(), transition: Transition.rightToLeftWithFade)
|
||||
Get.to(() => CaptainsPage(), transition: Transition.fadeIn)
|
||||
},
|
||||
{
|
||||
'title': 'الرحلات',
|
||||
'icon': Icons.directions_car_filled_outlined,
|
||||
'icon': Icons.directions_car_filled_rounded,
|
||||
'color': Colors.indigoAccent,
|
||||
'onPressed': () =>
|
||||
Get.to(() => Rides(), transition: Transition.rightToLeftWithFade)
|
||||
Get.to(() => RidesDashboardScreen(), transition: Transition.fadeIn)
|
||||
// Get.to(() => RideLookupPage(), transition: Transition.fadeIn)
|
||||
},
|
||||
{
|
||||
'title': ' الكباتن النشطين',
|
||||
'icon': Icons.directions_car_filled_rounded,
|
||||
'color': Colors.indigoAccent,
|
||||
'onPressed': () =>
|
||||
Get.to(() => IntaleqTrackerScreen(), transition: Transition.fadeIn)
|
||||
},
|
||||
|
||||
// --- عناصر خاصة بالسوبر أدمن فقط (Using Collection If) ---
|
||||
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
'title': 'مراقبة الرحلات'.tr,
|
||||
'icon': Icons.route, // أيقونة مناسبة لمراقبة الرحلات
|
||||
'color': Colors.purpleAccent,
|
||||
'onPressed': () =>
|
||||
Get.to(() => RideMonitorScreen(), transition: Transition.fadeIn),
|
||||
},
|
||||
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
'title': 'المحفظة', // الأمور المالية حساسة
|
||||
'icon': Icons.account_balance_wallet_rounded,
|
||||
'color': Colors.purpleAccent,
|
||||
'onPressed': () =>
|
||||
Get.to(() => Wallet(), transition: Transition.fadeIn)
|
||||
},
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
'title': 'دفع هدية 300'.tr,
|
||||
'icon': Icons.card_giftcard, // أيقونة الهدية
|
||||
'color': Colors.purpleAccent,
|
||||
'onPressed': () => Get.to(() => DriverGiftCheckPage(),
|
||||
transition: Transition.fadeIn),
|
||||
},
|
||||
|
||||
// if (isSuperAdmin)
|
||||
{
|
||||
'title': 'الإحصائيات',
|
||||
'icon': Icons.bar_chart_outlined,
|
||||
'icon': Icons.bar_chart_rounded,
|
||||
'color': Colors.teal,
|
||||
'onPressed': () async {
|
||||
await Get.put(StaticController()).getAll();
|
||||
Get.to(() => const StaticDash());
|
||||
}
|
||||
},
|
||||
{
|
||||
'title': 'إرسال واتساب للسائقين',
|
||||
'icon': Icons.message_outlined,
|
||||
'iconColor': Colors.green.shade600,
|
||||
'onPressed': () => _showWhatsAppDialog(context)
|
||||
},
|
||||
{
|
||||
'title': 'إرسال إشعار للسائقين',
|
||||
'icon': Icons.notifications_active_outlined,
|
||||
'onPressed': () async =>
|
||||
await Get.put(NotificationController()).getTokensDrivers()
|
||||
},
|
||||
{
|
||||
'title': 'إرسال إشعار للركاب',
|
||||
'icon': Icons.notification_important_outlined,
|
||||
'onPressed': () async =>
|
||||
await Get.put(NotificationController()).getTokensPassengers()
|
||||
},
|
||||
{
|
||||
'title': 'تسجيل كابتن جديد',
|
||||
'icon': Icons.person_add_alt_1_outlined,
|
||||
'onPressed': () async {
|
||||
await Get.put(RegisterCaptainController())
|
||||
.getDriverNotCompleteRegistration();
|
||||
Get.to(() => const DriversCantRegister());
|
||||
}
|
||||
},
|
||||
{
|
||||
'title': 'تحديث الباقات',
|
||||
'icon': Icons.inventory_2_outlined,
|
||||
'onPressed': () => Get.to(() => PackageUpdateScreen())
|
||||
},
|
||||
{
|
||||
'title': 'الموظفون',
|
||||
'icon': Icons.badge_outlined,
|
||||
'onPressed': () => Get.to(() => EmployeePage())
|
||||
},
|
||||
{
|
||||
'title': 'أفضل السائقين',
|
||||
'icon': Icons.star_border_purple500_outlined,
|
||||
'onPressed': () => Get.to(() => DriverTheBest())
|
||||
},
|
||||
{
|
||||
'title': 'إضافة فاتورة',
|
||||
'icon': Icons.post_add_outlined,
|
||||
// 'onPressed': () => Get.to(() => AddInvoicePage())
|
||||
'onPressed': () => Get.to(() => InvoiceListPage())
|
||||
},
|
||||
{
|
||||
'title': 'إضافة جهاز كمسؤول',
|
||||
'icon': Icons.admin_panel_settings_outlined,
|
||||
'onPressed': () async => await CRUD()
|
||||
.post(link: AppLink.addAdminUser, payload: {'name': 'b'})
|
||||
},
|
||||
|
||||
// هذا هو الجزء الذي طلبته تحديداً
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
'title': 'واتساب سائقين',
|
||||
'icon': Icons.message_rounded,
|
||||
'color': Colors.green,
|
||||
'onPressed': () => _showWhatsAppDialog(context)
|
||||
},
|
||||
|
||||
// --- عودة للعناصر العامة (أو يمكنك تقييدها أيضاً) ---
|
||||
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
'title': 'إشعار للسائقين',
|
||||
'icon': Icons.notifications_active_rounded,
|
||||
'color': Colors.deepOrange,
|
||||
'onPressed': () async =>
|
||||
await Get.put(NotificationController()).sendNotificationDrivers()
|
||||
},
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
'title': 'إشعار للركاب',
|
||||
'icon': Icons.notification_important_rounded,
|
||||
'color': Colors.pinkAccent,
|
||||
'onPressed': () async => await Get.put(NotificationController())
|
||||
.sendNotificationPassengers()
|
||||
},
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
'title': 'تسجيل كابتن',
|
||||
'icon': Icons.person_add_alt_1_rounded,
|
||||
'color': Colors.cyan,
|
||||
'onPressed': () => Get.to(() => DriversPendingPage())
|
||||
},
|
||||
|
||||
if (isSuperAdmin) // تحديث الباقات للمدير فقط
|
||||
{
|
||||
'title': 'تحديث الباقات',
|
||||
'icon': Icons.inventory_2_rounded,
|
||||
'color': Colors.brown,
|
||||
'onPressed': () => Get.to(() => PackageUpdateScreen())
|
||||
},
|
||||
|
||||
if (isSuperAdmin) // الموظفون للمدير فقط
|
||||
{
|
||||
'title': 'الموظفون',
|
||||
'icon': Icons.badge_rounded,
|
||||
'color': Colors.blueGrey,
|
||||
'onPressed': () => Get.to(() => EmployeePage())
|
||||
},
|
||||
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
'title': 'أفضل السائقين',
|
||||
'icon': Icons.star_rounded,
|
||||
'color': Colors.amber,
|
||||
'onPressed': () => Get.to(() => DriverTheBestRedesigned())
|
||||
},
|
||||
|
||||
if (isSuperAdmin) // الفواتير للمدير فقط
|
||||
{
|
||||
'title': 'الفواتير',
|
||||
'icon': Icons.receipt_long_rounded,
|
||||
'color': Colors.green.shade800,
|
||||
'onPressed': () => Get.to(() => InvoiceListPage())
|
||||
},
|
||||
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
'title': 'سجل الأخطاء',
|
||||
'icon': Icons.error_outline_rounded,
|
||||
'color': Colors.redAccent,
|
||||
'onPressed': () => Get.to(() => ErrorListPage())
|
||||
},
|
||||
];
|
||||
|
||||
return MyScafolld(
|
||||
title: 'لوحة التحكم الرئيسية',
|
||||
action: IconButton(
|
||||
onPressed: () async {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF5F7FA),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await dashboardController.getDashBoard();
|
||||
},
|
||||
icon: const Icon(Icons.refresh, color: AppColor.primaryColor, size: 28),
|
||||
tooltip: 'تحديث',
|
||||
),
|
||||
body: [
|
||||
GetBuilder<DashboardController>(builder: (controller) {
|
||||
child: GetBuilder<DashboardController>(builder: (controller) {
|
||||
if (controller.dashbord.isEmpty) {
|
||||
return const MyCircularProgressIndicator();
|
||||
return const Center(child: MyCircularProgressIndicator());
|
||||
}
|
||||
|
||||
// Main data map for easier access
|
||||
final data = controller.dashbord[0];
|
||||
|
||||
// Stat cards list with Arabic titles
|
||||
// إحصائيات لوحة التحكم
|
||||
final List<Map<String, dynamic>> statCards = [
|
||||
{
|
||||
'title': 'رصيد الرسائل',
|
||||
'value': controller.creditSMS.toString(),
|
||||
'icon': Icons.sms_outlined,
|
||||
'color': Colors.lightBlue
|
||||
},
|
||||
// يمكنك تطبيق نفس المنطق هنا لإخفاء الأرباح عن الموظفين العاديين
|
||||
if (isSuperAdmin)
|
||||
{
|
||||
'title': 'رصيد الرسائل',
|
||||
'value': controller.creditSMS.toString(),
|
||||
'icon': Icons.sms_outlined,
|
||||
'color': Colors.lightBlue
|
||||
},
|
||||
{
|
||||
'title': 'الركاب',
|
||||
'value': data['countPassengers'].toString(),
|
||||
@@ -193,12 +250,15 @@ class AdminHomePage extends StatelessWidget {
|
||||
'icon': Icons.calendar_month_outlined,
|
||||
'color': Colors.purple
|
||||
},
|
||||
{
|
||||
'title': 'متوسط التكلفة',
|
||||
'value': _formatCurrency(data['avg_passenger_price']),
|
||||
'icon': Icons.monetization_on_outlined,
|
||||
'color': Colors.green
|
||||
},
|
||||
|
||||
if (isSuperAdmin) // إخفاء الأمور المالية
|
||||
{
|
||||
'title': 'متوسط التكلفة',
|
||||
'value': _formatCurrency(data['avg_passenger_price']),
|
||||
'icon': Icons.monetization_on_outlined,
|
||||
'color': Colors.green
|
||||
},
|
||||
|
||||
{
|
||||
'title': 'الرحلات المكتملة',
|
||||
'value': data['completed_rides'].toString(),
|
||||
@@ -211,24 +271,29 @@ class AdminHomePage extends StatelessWidget {
|
||||
'icon': Icons.cancel_outlined,
|
||||
'color': AppColor.redColor
|
||||
},
|
||||
|
||||
// if (isSuperAdmin) // إخفاء المدفوعات
|
||||
{
|
||||
'title': 'مدفوعات السائقين',
|
||||
'value': _formatCurrency(data['payments']),
|
||||
'icon': Icons.payments_outlined,
|
||||
'color': Colors.indigo
|
||||
},
|
||||
// if (isSuperAdmin)
|
||||
{
|
||||
'title': 'محفظة انطلق',
|
||||
'value': _formatCurrency(data['seferWallet']),
|
||||
'icon': Icons.account_balance_wallet_outlined,
|
||||
'color': Colors.deepOrange
|
||||
},
|
||||
|
||||
{
|
||||
'title': 'عدد التحويلات',
|
||||
'value': data['transfer_from_count'].toString(),
|
||||
'icon': Icons.swap_horiz_outlined,
|
||||
'color': Colors.brown
|
||||
},
|
||||
// ... بقية العناصر التي لا تحتاج إخفاء
|
||||
{
|
||||
'title': 'رحلات الصباح',
|
||||
'value': data['morning_ride_count'].toString(),
|
||||
@@ -248,43 +313,96 @@ class AdminHomePage extends StatelessWidget {
|
||||
'color': Colors.black87
|
||||
},
|
||||
{
|
||||
'title': 'نوع كومفورت',
|
||||
'title': 'كومفورت',
|
||||
'value': data['comfort'].toString(),
|
||||
'icon': Icons.event_seat_outlined,
|
||||
'color': Colors.cyan
|
||||
},
|
||||
{
|
||||
'title': 'نوع سبيد',
|
||||
'title': 'سبيد',
|
||||
'value': data['speed'].toString(),
|
||||
'icon': Icons.speed_outlined,
|
||||
'color': Colors.red.shade700
|
||||
},
|
||||
{
|
||||
'title': 'نوع ليدي',
|
||||
'title': 'ليدي',
|
||||
'value': data['lady'].toString(),
|
||||
'icon': Icons.woman_2_outlined,
|
||||
'color': Colors.pink
|
||||
},
|
||||
];
|
||||
|
||||
return AnimationLimiter(
|
||||
child: ListView(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
|
||||
children: [
|
||||
// --- Statistics Grid Section ---
|
||||
AnimationLimiter(
|
||||
child: GridView.builder(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: _calculateCrossAxisCount(context),
|
||||
mainAxisSpacing: 12.0,
|
||||
crossAxisSpacing: 12.0,
|
||||
childAspectRatio: 1.8,
|
||||
return CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
expandedHeight: 120.0,
|
||||
floating: true,
|
||||
pinned: true,
|
||||
backgroundColor: AppColor.primaryColor,
|
||||
elevation: 0,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
titlePadding:
|
||||
const EdgeInsets.only(left: 16, right: 16, bottom: 16),
|
||||
title: const Text(
|
||||
'لوحة التحكم',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
background: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
AppColor.primaryColor,
|
||||
AppColor.primaryColor.withOpacity(0.8),
|
||||
],
|
||||
),
|
||||
),
|
||||
itemCount: statCards.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
left: -20,
|
||||
top: -20,
|
||||
child: Icon(Icons.dashboard,
|
||||
size: 150, color: Colors.white.withOpacity(0.1)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () async =>
|
||||
await dashboardController.getDashBoard(),
|
||||
icon: const Icon(Icons.refresh, color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 20, 16, 10),
|
||||
child: Text(
|
||||
"نظرة عامة",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey[800]),
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: _calculateCrossAxisCount(context),
|
||||
mainAxisSpacing: 12.0,
|
||||
crossAxisSpacing: 12.0,
|
||||
childAspectRatio: 1.6,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
final card = statCards[index];
|
||||
return AnimationConfiguration.staggeredGrid(
|
||||
position: index,
|
||||
@@ -292,184 +410,274 @@ class AdminHomePage extends StatelessWidget {
|
||||
columnCount: _calculateCrossAxisCount(context),
|
||||
child: ScaleAnimation(
|
||||
child: FadeInAnimation(
|
||||
child: DashboardStatCard(
|
||||
title: card['title'] as String,
|
||||
value: card['value'].toString(),
|
||||
icon: card['icon'] as IconData,
|
||||
iconColor: card['color'] as Color,
|
||||
valueColor: (card['color'] as Color),
|
||||
child: _buildModernStatCard(
|
||||
title: card['title'],
|
||||
value: card['value'],
|
||||
icon: card['icon'],
|
||||
color: card['color'],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: statCards.length,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
Text("الإجراءات السريعة",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleLarge
|
||||
?.copyWith(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// --- Admin Actions List Section ---
|
||||
AnimationLimiter(
|
||||
child: ListView.builder(
|
||||
itemCount: actionItems.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 24, 16, 10),
|
||||
child: Text(
|
||||
"إدارة النظام",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey[800]),
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount:
|
||||
_calculateCrossAxisCount(context, isSmall: true),
|
||||
mainAxisSpacing: 12.0,
|
||||
crossAxisSpacing: 12.0,
|
||||
childAspectRatio: 1.0,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
final item = actionItems[index];
|
||||
return AnimationConfiguration.staggeredList(
|
||||
return AnimationConfiguration.staggeredGrid(
|
||||
position: index,
|
||||
duration: const Duration(milliseconds: 375),
|
||||
columnCount:
|
||||
_calculateCrossAxisCount(context, isSmall: true),
|
||||
child: SlideAnimation(
|
||||
verticalOffset: 50.0,
|
||||
child: FadeInAnimation(
|
||||
child: AdminActionTile(
|
||||
title: item['title'] as String,
|
||||
icon: item['icon'] as IconData,
|
||||
onPressed: item['onPressed'] as void Function(),
|
||||
iconColor: item['iconColor'] as Color?,
|
||||
child: _buildActionCard(
|
||||
context,
|
||||
title: item['title'],
|
||||
icon: item['icon'],
|
||||
color: item['color'],
|
||||
onTap: item['onPressed'],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: actionItems.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 40)),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// --- بقية الدوال (buildModernStatCard, buildActionCard, showWhatsAppDialog) تبقى كما هي ---
|
||||
Widget _buildModernStatCard({
|
||||
required String title,
|
||||
required String value,
|
||||
required IconData icon,
|
||||
required Color color,
|
||||
}) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.04),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Icon(icon, color: color, size: 20),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
isleading: false,
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey[900],
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionCard(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required IconData icon,
|
||||
required Color color,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return Material(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
elevation: 0,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.grey.shade200),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [color.withOpacity(0.2), color.withOpacity(0.05)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
),
|
||||
child: Icon(icon, size: 28, color: color),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showWhatsAppDialog(BuildContext context) {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
|
||||
title: Text('تأكيد إرسال الرسائل؟'),
|
||||
content: MyTextForm(
|
||||
controller: _messageController,
|
||||
label: 'الرسالة',
|
||||
hint: 'أدخل نص الرسالة هنا',
|
||||
type: TextInputType.text,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
title: const Text('إرسال رسالة جماعية',
|
||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: const Icon(Icons.send, size: 40, color: Colors.green),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
MyTextForm(
|
||||
controller: _messageController,
|
||||
label: 'الرسالة',
|
||||
hint: 'اكتب الرسالة هنا...',
|
||||
type: TextInputType.text,
|
||||
),
|
||||
],
|
||||
),
|
||||
actionsPadding: const EdgeInsets.all(15),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_messageController.clear();
|
||||
Get.back();
|
||||
},
|
||||
child: Text('إلغاء'),
|
||||
child: const Text('إلغاء', style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
ElevatedButton(
|
||||
ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColor.primaryColor),
|
||||
backgroundColor: Colors.green,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
),
|
||||
icon: const Icon(Icons.send, size: 18, color: Colors.white),
|
||||
label: const Text('إرسال', style: TextStyle(color: Colors.white)),
|
||||
onPressed: () async {
|
||||
if (_messageController.text.isNotEmpty) {
|
||||
Get.back(); // Close dialog first
|
||||
Get.back();
|
||||
var driverPhones =
|
||||
box.read(BoxName.tokensDrivers)['message'] as List?;
|
||||
if (driverPhones == null || driverPhones.isEmpty) {
|
||||
Get.snackbar('خطأ', 'لم يتم العثور على أرقام هواتف للسائقين.',
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
Get.snackbar('تنبيه', 'لا توجد بيانات اتصال للسائقين',
|
||||
backgroundColor: Colors.amber.withOpacity(0.5));
|
||||
return;
|
||||
}
|
||||
|
||||
Get.snackbar('جاري الإرسال', 'بدأت عملية الإرسال في الخلفية...',
|
||||
backgroundColor: Colors.blue.withOpacity(0.3));
|
||||
|
||||
for (var driverData in driverPhones) {
|
||||
if (driverData['phone'] != null) {
|
||||
await CRUD().sendWhatsAppAuth(
|
||||
driverData['phone'].toString(),
|
||||
_messageController.text,
|
||||
);
|
||||
// Random delay to avoid being flagged as spam
|
||||
await Future.delayed(
|
||||
Duration(seconds: Random().nextInt(5) + 2));
|
||||
Duration(seconds: Random().nextInt(3) + 1));
|
||||
}
|
||||
}
|
||||
_messageController.clear();
|
||||
Get.snackbar(
|
||||
'نجاح',
|
||||
'تم إرسال الرسائل بنجاح',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green.shade100,
|
||||
colorText: Colors.black,
|
||||
);
|
||||
Get.snackbar('نجاح', 'تمت العملية بنجاح',
|
||||
backgroundColor: Colors.green.withOpacity(0.5));
|
||||
}
|
||||
},
|
||||
child: Text('إرسال', style: TextStyle(color: Colors.white)),
|
||||
),
|
||||
],
|
||||
),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Renamed for clarity and improved design
|
||||
class AdminActionTile extends StatelessWidget {
|
||||
const AdminActionTile({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.onPressed,
|
||||
required this.icon,
|
||||
this.iconColor,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final VoidCallback onPressed;
|
||||
final IconData icon;
|
||||
final Color? iconColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
||||
child: Material(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: InkWell(
|
||||
onTap: onPressed,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 18.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.2))),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 26,
|
||||
color: iconColor ?? AppColor.primaryColor,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: AppStyle.title.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user