Files
Siro/siro_admin/lib/views/admin/admin_home_page.dart
2026-06-11 13:47:40 +03:00

1124 lines
41 KiB
Dart

import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:get/get.dart';
import 'package:siro_admin/views/admin/drivers/driver_gift_check_page.dart';
import 'package:siro_admin/views/admin/drivers/driver_tracker_screen.dart';
import 'package:siro_admin/views/admin/quality/blacklist_page.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/style.dart';
import '../../controller/admin/dashboard_controller.dart';
import '../../controller/admin/static_controller.dart';
import '../../controller/functions/crud.dart';
import '../../controller/notification_controller.dart';
import '../../main.dart';
import '../../print.dart';
import '../invoice/invoice_list_page.dart';
import 'captain/captain.dart';
import 'captain/syrian_driver_not_active.dart';
import 'drivers/monitor_ride.dart';
import 'employee/employee_page.dart';
import 'enceypt/driver_fingerprint_migration.dart';
import 'enceypt/encrypt.dart';
import 'enceypt/fingerprint_migration.dart';
import 'error/error/error_page.dart';
import 'packages.dart';
import 'passenger/passenger.dart';
import 'rides/ride_lookup_page.dart';
import 'server/monitor_server_page.dart';
import 'static/static.dart';
import 'wallet/wallet.dart';
import 'staff/add_staff_page.dart';
import 'staff/pending_admins_page.dart';
import 'dashboard_v2_widget.dart';
import 'static/advanced_analytics_page.dart';
import 'financial/financial_v2_page.dart';
import 'security/audit_logs_page.dart';
class AdminHomePage extends StatefulWidget {
const AdminHomePage({super.key});
@override
State<AdminHomePage> createState() => _AdminHomePageState();
}
class _AdminHomePageState extends State<AdminHomePage>
with SingleTickerProviderStateMixin {
final TextEditingController _messageController = TextEditingController();
final TextEditingController _searchController = TextEditingController();
late AnimationController _pulseController;
late bool isSuperAdmin;
late DashboardController dashboardController;
String _searchQuery = '';
// ══════════════════ DESIGN TOKENS ══════════════════
// --- Unified with AppColor ---
static const Color _bg = AppColor.bg;
static const Color _surface = AppColor.surface;
static const Color _surfaceElevated = AppColor.surfaceElevated;
static const Color _accent = AppColor.accent;
static const Color _accentSoft = AppColor.accentSoft;
static const Color _accentBorder = AppColor.accentBorder;
static const Color _danger = AppColor.danger;
static const Color _warning = AppColor.warning;
static const Color _info = AppColor.info;
static const Color _success = AppColor.success;
static const Color _textPrimary = AppColor.textPrimary;
static const Color _textSecondary = AppColor.textSecondary;
static const Color _divider = AppColor.divider;
@override
void initState() {
super.initState();
_pulseController = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat(reverse: true);
final String role = box.read('admin_role')?.toString() ?? 'admin';
final String myPhone = box.read(BoxName.adminPhone)?.toString() ?? '';
// التحقق من الصلاحيات: إما عن طريق الدور أو عن طريق قائمة أرقام السوبر أدمن التقليدية
isSuperAdmin = (role == 'super_admin') ||
(myPhone == '201023248456' ||
myPhone == '963992952235' ||
myPhone == '963942542053');
Log.print('AdminHomePage: role=$role, isSuperAdmin=$isSuperAdmin');
dashboardController = Get.put(DashboardController());
}
@override
void dispose() {
_pulseController.dispose();
_messageController.dispose();
_searchController.dispose();
super.dispose();
}
// ══════════════════════════════════════════════════════════════
// BUILD
// ══════════════════════════════════════════════════════════════
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: _bg,
body: RefreshIndicator(
onRefresh: () async => await dashboardController.getDashBoard(),
color: _accent,
backgroundColor: _surface,
child: GetBuilder<DashboardController>(
builder: (controller) {
if (controller.dashbord.isEmpty) {
return _buildLoadingState();
}
final data = controller.dashbord[0];
final categories = _getFilteredCategories();
return CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: [
_buildSliverAppBar(controller),
_buildSearchBar(),
if (_searchQuery.isEmpty) const DashboardV2Widget(),
if (_searchQuery.isEmpty)
_buildQuickStatsSection(data, controller),
SliverPadding(
padding: const EdgeInsets.only(bottom: 60),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final category = categories[index];
if (category.items.isEmpty)
return const SizedBox.shrink();
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 450),
child: SlideAnimation(
verticalOffset: 40.0,
child: FadeInAnimation(
child: _buildCategorySection(category),
),
),
);
},
childCount: categories.length,
),
),
),
],
);
},
),
),
);
}
// ══════════════════════════════════════════════════════════════
// LOADING STATE
// ══════════════════════════════════════════════════════════════
Widget _buildLoadingState() {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [_accent.withOpacity(0.3), Colors.transparent],
),
),
child: const Center(
child: SizedBox(
width: 28,
height: 28,
child: CircularProgressIndicator(
color: _accent,
strokeWidth: 2.5,
),
),
),
),
const SizedBox(height: 16),
Text('جاري التحميل...',
style: TextStyle(color: _textSecondary, fontSize: 13)),
],
),
);
}
// ══════════════════════════════════════════════════════════════
// SLIVER APP BAR
// ══════════════════════════════════════════════════════════════
Widget _buildSliverAppBar(DashboardController controller) {
return SliverAppBar(
expandedHeight: 130.0,
floating: true,
pinned: true,
backgroundColor: _bg,
elevation: 0,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: Stack(
fit: StackFit.expand,
children: [
// Aurora gradient background
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFF0D2137),
Color(0xFF0D1117),
Color(0xFF0F1F1A),
],
),
),
),
// Subtle glow orbs
Positioned(
top: -30,
left: -40,
child: _GlowOrb(color: _accent, size: 150, opacity: 0.08),
),
Positioned(
top: -20,
right: -20,
child: _GlowOrb(color: _info, size: 120, opacity: 0.06),
),
// Content
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 18),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildLogo(),
const SizedBox(height: 6),
Text(
isSuperAdmin ? 'Super Admin Panel' : 'Admin Panel',
style: TextStyle(
color: _textSecondary,
fontSize: 11,
letterSpacing: 1.5,
),
),
],
),
),
),
],
),
),
actions: [
_buildHeaderAction(
Icons.refresh_rounded, () => controller.getDashBoard()),
const SizedBox(width: 8),
],
);
}
Widget _buildLogo() {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedBuilder(
animation: _pulseController,
builder: (_, __) {
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [
_accent.withOpacity(0.3 + 0.1 * _pulseController.value),
_accent.withOpacity(0.1),
],
),
border: Border.all(
color:
_accent.withOpacity(0.4 + 0.2 * _pulseController.value),
width: 1,
),
),
child: const Icon(
Icons.admin_panel_settings_rounded,
color: _accent,
size: 18,
),
);
},
),
const SizedBox(width: 10),
ShaderMask(
shaderCallback: (bounds) => const LinearGradient(
colors: [_accent, _info],
).createShader(bounds),
child: const Text(
'Siro Admin',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 20,
letterSpacing: 0.5,
),
),
),
],
);
}
Widget _buildHeaderAction(IconData icon, VoidCallback onTap) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: _surface,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: _divider),
),
child: Icon(icon, color: _textSecondary, size: 18),
),
);
}
// ══════════════════════════════════════════════════════════════
// SEARCH BAR
// ══════════════════════════════════════════════════════════════
Widget _buildSearchBar() {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 4),
child: Container(
decoration: BoxDecoration(
color: _surface,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: _searchQuery.isNotEmpty ? _accentBorder : _divider,
width: _searchQuery.isNotEmpty ? 1.5 : 1,
),
),
child: TextField(
controller: _searchController,
onChanged: (val) => setState(() => _searchQuery = val),
style: const TextStyle(color: _textPrimary, fontSize: 14),
decoration: InputDecoration(
hintText: 'ابحث عن خدمة أو ميزة...',
hintStyle: const TextStyle(color: _textSecondary, fontSize: 13),
prefixIcon:
const Icon(Icons.search_rounded, color: _accent, size: 20),
suffixIcon: _searchQuery.isNotEmpty
? IconButton(
icon: Icon(Icons.close_rounded,
color: _textSecondary, size: 18),
onPressed: () {
setState(() {
_searchQuery = '';
_searchController.clear();
});
},
)
: null,
border: InputBorder.none,
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
),
),
),
),
);
}
// ══════════════════════════════════════════════════════════════
// QUICK STATS SECTION
// ══════════════════════════════════════════════════════════════
Widget _buildQuickStatsSection(dynamic data, DashboardController controller) {
final highlights = [
_HighlightData(
'إجمالي الركاب', data['countPassengers'], Icons.group_rounded, _info),
_HighlightData('إجمالي السائقين', data['countDriver'],
Icons.drive_eta_rounded, _warning),
_HighlightData('رحلات الشهر', data['countRideThisMonth'],
Icons.calendar_today_rounded, const Color(0xFFC792EA)),
if (isSuperAdmin)
_HighlightData('المحفظة', _formatCurrency(data['seferWallet']),
Icons.account_balance_wallet_rounded, _accent),
];
final detailedStats = _getDetailedStats(data, controller);
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(top: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Section label
Padding(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 10),
child: Row(
children: [
Container(
width: 3,
height: 14,
decoration: BoxDecoration(
color: _accent,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(width: 8),
const Text('نظرة عامة',
style: TextStyle(
color: _textSecondary,
fontSize: 11,
fontWeight: FontWeight.w600,
letterSpacing: 1.2,
)),
],
),
),
// Highlight Cards
SizedBox(
height: 108,
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
itemCount: highlights.length,
itemBuilder: (ctx, i) => Padding(
padding: EdgeInsets.only(
right: i < highlights.length - 1 ? 10 : 0),
child: _buildHighlightCard(highlights[i]),
),
),
),
const SizedBox(height: 16),
// Detailed stats strip
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
decoration: BoxDecoration(
color: _surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: _divider),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(detailedStats.length, (i) {
final stat = detailedStats[i];
return Row(
mainAxisSize: MainAxisSize.min,
children: [
_buildDetailStatItem(stat),
if (i < detailedStats.length - 1)
Container(
width: 1,
height: 36,
color: _divider,
),
],
);
}),
),
),
),
),
),
const SizedBox(height: 8),
],
),
),
);
}
Widget _buildHighlightCard(_HighlightData h) {
return Container(
width: 148,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: _surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: h.color.withOpacity(0.2)),
boxShadow: [
BoxShadow(
color: h.color.withOpacity(0.08),
blurRadius: 16,
offset: const Offset(0, 6),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(7),
decoration: BoxDecoration(
color: h.color.withOpacity(0.12),
borderRadius: BorderRadius.circular(9),
),
child: Icon(h.icon, color: h.color, size: 16),
),
Container(
width: 6,
height: 6,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: h.color.withOpacity(0.6),
),
),
],
),
const Spacer(),
Text(
h.value.toString(),
style: const TextStyle(
color: _textPrimary,
fontSize: 20,
fontWeight: FontWeight.w700,
height: 1.1,
),
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 3),
Text(
h.label,
style: const TextStyle(
color: _textSecondary,
fontSize: 10,
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
),
],
),
);
}
Widget _buildDetailStatItem(Map<String, dynamic> stat) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(stat['icon'] as IconData,
color: stat['color'] as Color, size: 20),
const SizedBox(height: 6),
Text(
stat['value'].toString(),
style: const TextStyle(
color: _textPrimary,
fontSize: 14,
fontWeight: FontWeight.bold,
height: 1,
),
),
const SizedBox(height: 3),
Text(
stat['title'] as String,
style: const TextStyle(
color: _textSecondary,
fontSize: 9,
fontWeight: FontWeight.w500,
),
),
],
),
);
}
// ══════════════════════════════════════════════════════════════
// CATEGORY SECTION
// ══════════════════════════════════════════════════════════════
Widget _buildCategorySection(ActionCategory category) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 12),
child: Row(
children: [
Container(
width: 3,
height: 14,
decoration: BoxDecoration(
color: _accent,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(width: 8),
Text(
category.title,
style: const TextStyle(
color: _textPrimary,
fontSize: 15,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
),
),
const SizedBox(width: 10),
Expanded(
child: Container(height: 1, color: _divider),
),
const SizedBox(width: 8),
Text(
'${category.items.length}',
style: const TextStyle(
color: _textSecondary,
fontSize: 11,
fontWeight: FontWeight.w500,
),
),
],
),
),
GridView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 120,
childAspectRatio: 0.88,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: category.items.length,
itemBuilder: (context, index) =>
_buildActionItem(category.items[index]),
),
const SizedBox(height: 8),
],
);
}
Widget _buildActionItem(ActionItem item) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: item.onPressed,
borderRadius: BorderRadius.circular(16),
splashColor: item.color.withOpacity(0.1),
highlightColor: item.color.withOpacity(0.05),
child: Container(
decoration: BoxDecoration(
color: _surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: _divider),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(11),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
item.color.withOpacity(0.20),
item.color.withOpacity(0.08),
],
),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: item.color.withOpacity(0.25)),
boxShadow: [
BoxShadow(
color: item.color.withOpacity(0.15),
blurRadius: 10,
offset: const Offset(0, 3),
),
],
),
child: Icon(item.icon, color: item.color, size: 22),
),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6),
child: Text(
item.title,
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: _textPrimary,
fontSize: 11,
fontWeight: FontWeight.w500,
height: 1.3,
),
),
),
],
),
),
),
);
}
// ══════════════════════════════════════════════════════════════
// DATA HELPERS
// ══════════════════════════════════════════════════════════════
List<ActionCategory> _getFilteredCategories() {
final all = _getAllActionCategories();
if (_searchQuery.isEmpty) return all;
return all
.map((cat) {
final matched = cat.items
.where((item) =>
item.title.toLowerCase().contains(_searchQuery.toLowerCase()))
.toList();
return matched.isEmpty
? null
: ActionCategory(title: cat.title, items: matched);
})
.whereType<ActionCategory>()
.toList();
}
List<ActionCategory> _getAllActionCategories() {
return [
ActionCategory(
title: 'المستخدمين',
items: [
ActionItem('الركاب', Icons.people_outline_rounded, _info,
() => Get.to(() => Passengrs())),
ActionItem('السائقون', Icons.drive_eta_rounded, _warning,
() => Get.to(() => CaptainsPage())),
ActionItem('المراقب', Icons.track_changes_rounded, _danger,
() => Get.to(() => SiroTrackerScreen())),
],
),
ActionCategory(
title: 'إدارة النظام الجديد',
items: [
ActionItem('أكواد الخصم', Icons.confirmation_number_rounded, _accent,
() => Get.toNamed('/promo')),
ActionItem('تعديل الأسعار', Icons.settings_suggest_rounded, _warning,
() => Get.toNamed('/kazan')),
ActionItem('الشكاوى', Icons.report_problem_rounded, _danger,
() => Get.toNamed('/complaints')),
ActionItem('مراجعة الوثائق', Icons.assignment_ind_rounded, _info,
() => Get.toNamed('/driver-docs')),
],
),
ActionCategory(
title: 'العمليات',
items: [
ActionItem('الرحلات', Icons.map_rounded, const Color(0xFF82AAFF),
() => Get.to(() => RidesDashboardScreen())),
if (isSuperAdmin)
ActionItem(
'مراقبة الرحلات',
Icons.remove_red_eye_rounded,
const Color(0xFFC792EA),
() => Get.to(() => RideMonitorScreen())),
ActionItem('الإحصائيات', Icons.bar_chart_rounded, _accent, () async {
await Get.put(StaticController()).getAll();
Get.to(() => const StaticDash());
}),
ActionItem('التحليلات المتقدمة', Icons.analytics_rounded, _info,
() => Get.to(() => const AdvancedAnalyticsPage())),
],
),
ActionCategory(
title: 'الجودة والدعم',
items: [
ActionItem('القائمة السوداء', Icons.block_flipped, _danger,
() => Get.to(() => const BlacklistPage())),
],
),
if (true)
ActionCategory(
title: 'المالية والإدارة',
items: [
ActionItem('الإدارة المالية V2', Icons.account_balance_rounded, _accent,
() => Get.to(() => const FinancialV2Page())),
ActionItem('المحفظة', Icons.account_balance_wallet_rounded, _accent,
() => Get.to(() => Wallet())),
ActionItem('هدية 300', Icons.card_giftcard_rounded, _warning,
() => Get.to(() => DriverGiftCheckPage())),
ActionItem('الفواتير', Icons.receipt_long_rounded,
const Color(0xFF80CBC4), () => Get.to(() => InvoiceListPage())),
ActionItem('الموظفون', Icons.badge_rounded, const Color(0xFFB0BEC5),
() => Get.to(() => EmployeePage())),
ActionItem('موافقة المشرفين', Icons.how_to_reg_rounded, _accent,
() => Get.to(() => const PendingAdminsPage())),
],
),
if (true)
ActionCategory(
title: 'النظام والتواصل',
items: [
ActionItem('سجل العمليات', Icons.admin_panel_settings_rounded,
_danger, () => Get.to(() => const AuditLogsPage())),
ActionItem('واتساب جماعي', Icons.message_rounded,
const Color(0xFF4CAF50), () => _showWhatsAppDialog(context)),
ActionItem(
'إشعار سائقين',
Icons.notifications_active_rounded,
const Color(0xFFFF7043),
() => Get.put(NotificationController())
.sendNotificationDrivers()),
ActionItem(
'إشعار ركاب',
Icons.notification_important_rounded,
const Color(0xFFF06292),
() => Get.put(NotificationController())
.sendNotificationPassengers()),
ActionItem('تسجيل سائق', Icons.person_add_rounded, _info,
() => Get.to(() => DriversPendingPage())),
ActionItem(
'تحديث التطبيق',
Icons.system_update_rounded,
const Color(0xFFA1887F),
() => Get.to(() => PackageUpdateScreen())),
ActionItem('مراقب السيرفر', Icons.dns_rounded, _accent,
() => Get.to(() => ServerMonitorPage())),
ActionItem('سجل الأخطاء', Icons.error_outline_rounded, _danger,
() => Get.to(() => ErrorListPage())),
ActionItem('encrypt fp', Icons.error_outline_rounded, _danger,
() => Get.to(() => FingerprintMigrationTool())),
ActionItem('encrypt fp drivers', Icons.error_outline_rounded,
_danger, () => Get.to(() => DriverFingerprintMigrationTool())),
ActionItem(
'أداة التشفير',
Icons.lock_rounded,
const Color(0xFF9575CD),
() => Get.to(() => EncryptToolPage(
adminToken: box.read(BoxName.adminPhone),
))),
],
),
if (isSuperAdmin)
ActionCategory(
title: 'إدارة الكوادر',
items: [
ActionItem(
'إضافة مدير',
Icons.admin_panel_settings_rounded,
_accent,
() => Get.to(() => const AddStaffPage(role: 'admin')),
),
ActionItem(
'إضافة خدمة عملاء',
Icons.support_agent_rounded,
_info,
() => Get.to(() => const AddStaffPage(role: 'service')),
),
],
),
];
}
List<Map<String, dynamic>> _getDetailedStats(
dynamic data, DashboardController controller) {
return [
if (isSuperAdmin)
{
'title': 'رصيد الرسائل',
'value': controller.creditSMS,
'icon': Icons.sms_rounded,
'color': _info,
},
{
'title': 'مكتملة',
'value': data['completed_rides'],
'icon': Icons.check_circle_rounded,
'color': _success,
},
{
'title': 'ملغاة',
'value': data['cancelled_rides'],
'icon': Icons.cancel_rounded,
'color': _danger,
},
{
'title': 'مدفوعات',
'value': _formatCurrency(data['payments']),
'icon': Icons.attach_money_rounded,
'color': _warning,
},
{
'title': 'Comfort',
'value': data['comfort'],
'icon': Icons.chair_rounded,
'color': const Color(0xFF80CBC4),
},
{
'title': 'Speed',
'value': data['speed'],
'icon': Icons.flash_on_rounded,
'color': const Color(0xFFFFD54F),
},
{
'title': 'Lady',
'value': data['lady'],
'icon': Icons.woman_rounded,
'color': const Color(0xFFF48FB1),
},
];
}
// ══════════════════════════════════════════════════════════════
// WHATSAPP DIALOG
// ══════════════════════════════════════════════════════════════
void _showWhatsAppDialog(BuildContext context) {
Get.dialog(
Dialog(
backgroundColor: Colors.transparent,
child: Container(
decoration: BoxDecoration(
color: _surfaceElevated,
borderRadius: BorderRadius.circular(24),
border: Border.all(color: _divider),
boxShadow: const [
BoxShadow(
color: Colors.black45,
blurRadius: 30,
offset: Offset(0, 12),
),
],
),
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF4CAF50).withAlpha(30), // ~0.12 opacity
shape: BoxShape.circle,
border: Border.all(
color: const Color(0xFF4CAF50).withAlpha(64)), // ~0.25 opacity
),
child: const Icon(Icons.message_rounded,
color: Color(0xFF4CAF50), size: 28),
),
const SizedBox(height: 16),
const Text(
'إرسال واتساب جماعي',
style: TextStyle(
color: _textPrimary,
fontSize: 17,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 4),
const Text(
'سيتم إرسال الرسالة لجميع السائقين',
style: TextStyle(color: _textSecondary, fontSize: 11),
),
const SizedBox(height: 20),
Container(
decoration: BoxDecoration(
color: _bg,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: _divider),
),
child: TextField(
controller: _messageController,
maxLines: 4,
style: const TextStyle(color: _textPrimary, fontSize: 13),
decoration: const InputDecoration(
hintText: 'اكتب رسالتك هنا...',
hintStyle: TextStyle(color: _textSecondary, fontSize: 12),
border: InputBorder.none,
contentPadding: EdgeInsets.all(14),
),
),
),
const SizedBox(height: 20),
Row(
children: [
Expanded(
child: TextButton(
onPressed: () => Get.back(),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: const BorderSide(color: _divider),
),
),
child: const Text(
'إلغاء',
style: TextStyle(color: _textSecondary, fontSize: 13),
),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.send_rounded, size: 16),
label:
const Text('إرسال', style: TextStyle(fontSize: 13)),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF4CAF50),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 0,
),
onPressed: () async {
if (_messageController.text.isNotEmpty) {
Get.back();
var driverPhones = box
.read(BoxName.tokensDrivers)['message'] as List?;
if (driverPhones == null || driverPhones.isEmpty)
return;
Get.snackbar(
'بدأ الإرسال',
'سيتم الإرسال في الخلفية',
backgroundColor:
const Color(0xFF4CAF50).withOpacity(0.15),
colorText: _textPrimary,
borderRadius: 12,
margin: const EdgeInsets.all(16),
icon: const Icon(Icons.check_circle_rounded,
color: Color(0xFF4CAF50)),
);
for (var driverData in driverPhones) {
if (driverData['phone'] != null) {
await CRUD().sendWhatsAppAuth(
driverData['phone'].toString(),
_messageController.text);
await Future.delayed(
Duration(seconds: Random().nextInt(3) + 1));
}
}
_messageController.clear();
}
},
),
),
],
),
],
),
),
),
);
}
String _formatCurrency(dynamic value) {
if (value == null) return '0.0';
return double.tryParse(value.toString())?.toStringAsFixed(1) ?? '0.0';
}
}
// ══════════════════════════════════════════════════════════════
// HELPER WIDGETS
// ══════════════════════════════════════════════════════════════
class _GlowOrb extends StatelessWidget {
final Color color;
final double size;
final double opacity;
const _GlowOrb(
{required this.color, required this.size, required this.opacity});
@override
Widget build(BuildContext context) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [color.withOpacity(opacity), Colors.transparent],
),
),
);
}
}
// ══════════════════════════════════════════════════════════════
// DATA CLASSES
// ══════════════════════════════════════════════════════════════
class _HighlightData {
final String label;
final dynamic value;
final IconData icon;
final Color color;
_HighlightData(this.label, this.value, this.icon, this.color);
}
class ActionItem {
final String title;
final IconData icon;
final Color color;
final VoidCallback onPressed;
ActionItem(this.title, this.icon, this.color, this.onPressed);
}
class ActionCategory {
final String title;
final List<ActionItem> items;
ActionCategory({required this.title, required this.items});
}