Initial commit for intaleq_admin
This commit is contained in:
@@ -1,103 +1,654 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_admin1/constant/links.dart';
|
||||
import 'package:sefer_admin1/controller/functions/crud.dart';
|
||||
import 'package:sefer_admin1/controller/functions/encrypt_decrypt.dart';
|
||||
import 'package:get_storage/get_storage.dart'; // Ensure get_storage is in pubspec.yaml
|
||||
import 'package:sefer_admin1/controller/functions/wallet.dart';
|
||||
import 'package:sefer_admin1/views/widgets/elevated_btn.dart';
|
||||
import 'package:sefer_admin1/views/widgets/my_scafold.dart';
|
||||
|
||||
import '../../../controller/drivers/driverthebest.dart';
|
||||
import 'alexandria.dart';
|
||||
import 'giza.dart';
|
||||
// --- New Controller to handle the specific JSON URL ---
|
||||
class DriverCacheController extends GetxController {
|
||||
List<dynamic> drivers = [];
|
||||
bool isLoading = false;
|
||||
String lastUpdated = '';
|
||||
String searchQuery = ''; // Search query state
|
||||
|
||||
class DriverTheBest extends StatelessWidget {
|
||||
const DriverTheBest({super.key});
|
||||
// Storage for paid drivers
|
||||
final box = GetStorage();
|
||||
List<String> paidDrivers = [];
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
// Load previously paid drivers from storage
|
||||
var stored = box.read('paid_drivers');
|
||||
if (stored != null) {
|
||||
paidDrivers = List<String>.from(stored.map((e) => e.toString()));
|
||||
}
|
||||
fetchData();
|
||||
}
|
||||
|
||||
Future<void> fetchData() async {
|
||||
isLoading = true;
|
||||
update(); // Notify UI to show loader
|
||||
try {
|
||||
// Using GetConnect to fetch the JSON directly
|
||||
final response = await GetConnect().get(
|
||||
'https://api.intaleq.xyz/intaleq/ride/location/active_drivers_cache.json',
|
||||
);
|
||||
|
||||
if (response.body != null && response.body is Map) {
|
||||
if (response.body['data'] != null) {
|
||||
drivers = List<dynamic>.from(response.body['data']);
|
||||
}
|
||||
if (response.body['last_updated'] != null) {
|
||||
lastUpdated = response.body['last_updated'].toString();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("Error fetching driver cache: $e");
|
||||
} finally {
|
||||
isLoading = false;
|
||||
update(); // Update UI with data
|
||||
}
|
||||
}
|
||||
|
||||
// Update search query
|
||||
void updateSearchQuery(String query) {
|
||||
searchQuery = query;
|
||||
update();
|
||||
}
|
||||
|
||||
// Mark driver as paid and save to storage
|
||||
void markAsPaid(String driverId) {
|
||||
// Validation: Don't mark if ID is invalid
|
||||
if (driverId == 'null' || driverId.isEmpty) return;
|
||||
|
||||
if (!paidDrivers.contains(driverId)) {
|
||||
paidDrivers.add(driverId);
|
||||
box.write('paid_drivers', paidDrivers);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all paid status (Delete Box)
|
||||
void clearPaidStorage() {
|
||||
paidDrivers.clear();
|
||||
box.remove('paid_drivers');
|
||||
update();
|
||||
Get.snackbar(
|
||||
"Storage Cleared",
|
||||
"Paid status history has been reset",
|
||||
backgroundColor: Colors.redAccent,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if driver is already paid
|
||||
bool isDriverPaid(String driverId) {
|
||||
return paidDrivers.contains(driverId);
|
||||
}
|
||||
}
|
||||
|
||||
class DriverTheBestRedesigned extends StatelessWidget {
|
||||
const DriverTheBestRedesigned({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(Driverthebest(), permanent: true);
|
||||
return MyScafolld(
|
||||
title: 'Best Drivers'.tr,
|
||||
body: [
|
||||
GetBuilder<Driverthebest>(builder: (driverthebest) {
|
||||
return driverthebest.driver.isNotEmpty
|
||||
? Column(
|
||||
// Put the new controller
|
||||
final controller = Get.put(DriverCacheController());
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF8FAFC), // slate-50 background
|
||||
body: SafeArea(
|
||||
child: GetBuilder<DriverCacheController>(builder: (ctrl) {
|
||||
if (ctrl.isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
// Filter List based on Search Query
|
||||
List<dynamic> filteredDrivers = ctrl.drivers.where((driver) {
|
||||
if (ctrl.searchQuery.isEmpty) return true;
|
||||
final phone = driver['phone']?.toString() ?? '';
|
||||
// Simple contains check for phone
|
||||
return phone.contains(ctrl.searchQuery);
|
||||
}).toList();
|
||||
|
||||
// Sort by Active Time (Hours) Descending
|
||||
// We use the filtered list for sorting and display
|
||||
filteredDrivers.sort((a, b) {
|
||||
double hoursA = _calculateHoursFromStr(a['active_time']);
|
||||
double hoursB = _calculateHoursFromStr(b['active_time']);
|
||||
return hoursB.compareTo(hoursA);
|
||||
});
|
||||
|
||||
// --- 1. Calculate Stats (Based on ALL drivers, not just filtered, to keep dashboard stable) ---
|
||||
int totalDrivers = ctrl.drivers.length;
|
||||
int eliteCount = 0;
|
||||
int inactiveCount = 0;
|
||||
double maxTime = 0.0;
|
||||
|
||||
for (var driver in ctrl.drivers) {
|
||||
double hours = _calculateHoursFromStr(driver['active_time']);
|
||||
if (hours > maxTime) maxTime = hours;
|
||||
if (hours >= 50) {
|
||||
eliteCount++;
|
||||
} else if (hours < 5) {
|
||||
inactiveCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
// --- 2. Header (Slate-900 style) ---
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF0F172A), // slate-900
|
||||
boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MyElevatedButton(
|
||||
title: 'Giza',
|
||||
onPressed: () {
|
||||
Get.to(() => DriverTheBestGiza());
|
||||
}),
|
||||
MyElevatedButton(
|
||||
title: 'Alexandria',
|
||||
onPressed: () {
|
||||
Get.to(() => DriverTheBestAlexandria());
|
||||
}),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: Get.height * .7,
|
||||
child: ListView.builder(
|
||||
itemCount: driverthebest.driver.length,
|
||||
itemBuilder: (context, index) {
|
||||
final driver = driverthebest.driver[index];
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
child: Text(
|
||||
(int.parse(driver['driver_count']) * 5 / 3600)
|
||||
.toStringAsFixed(
|
||||
0), // Perform division first, then convert to string
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.local_taxi,
|
||||
color: Colors.yellow, size: 24),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Best Drivers Dashboard'.tr,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
title:
|
||||
Text((driver['name_arabic']) ?? 'Unknown Name'),
|
||||
subtitle:
|
||||
Text('Phone: ${(driver['phone']) ?? 'N/A'}'),
|
||||
trailing: IconButton(
|
||||
onPressed: () async {
|
||||
Get.defaultDialog(
|
||||
title:
|
||||
'are you sure to pay to this driver gift'
|
||||
.tr,
|
||||
middleText: '',
|
||||
onConfirm: () async {
|
||||
final wallet =
|
||||
Get.put(WalletController());
|
||||
await wallet.addPaymentToDriver(
|
||||
'200',
|
||||
driver['id'].toString(),
|
||||
driver['token']);
|
||||
await wallet.addSeferWallet(
|
||||
'200', driver['id'].toString());
|
||||
await CRUD().post(
|
||||
link: AppLink.deleteRecord,
|
||||
payload: {
|
||||
'driver_id': driver['id'].toString()
|
||||
});
|
||||
driverthebest.driver.removeAt(index);
|
||||
driverthebest.update();
|
||||
Get.back();
|
||||
},
|
||||
onCancel: () => Get.back());
|
||||
},
|
||||
icon: const Icon(Icons.wallet_giftcard_rounded),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.access_time,
|
||||
color: Colors.grey, size: 12),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
ctrl.lastUpdated.isNotEmpty
|
||||
? 'Updated: ${ctrl.lastUpdated}'
|
||||
: 'Data Live',
|
||||
style: TextStyle(
|
||||
color: Colors.grey[400], fontSize: 12),
|
||||
),
|
||||
);
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
// Action Buttons (Delete Box & Refresh)
|
||||
Row(
|
||||
children: [
|
||||
// Delete Box Icon
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Get.defaultDialog(
|
||||
title: "Reset Paid Status",
|
||||
middleText:
|
||||
"Are you sure you want to clear the list of paid drivers? This cannot be undone.",
|
||||
textConfirm: "Yes, Clear",
|
||||
textCancel: "Cancel",
|
||||
confirmTextColor: Colors.white,
|
||||
buttonColor: Colors.red,
|
||||
onConfirm: () {
|
||||
ctrl.clearPaidStorage();
|
||||
Get.back();
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.delete_forever,
|
||||
color: Colors.redAccent),
|
||||
tooltip: "Clear Paid Storage",
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.white10),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
ctrl.fetchData();
|
||||
},
|
||||
icon: const Icon(Icons.refresh,
|
||||
color: Colors.blueAccent),
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.white10),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// --- 3. Statistics Cards Grid ---
|
||||
SizedBox(
|
||||
height: 100, // Fixed height for cards
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildStatCard('Total',
|
||||
totalDrivers.toString(), Colors.blue)),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: _buildStatCard('Elite',
|
||||
eliteCount.toString(), Colors.amber)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
height: 100,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildStatCard('Inactive',
|
||||
inactiveCount.toString(), Colors.red)),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: _buildStatCard(
|
||||
'Max Time',
|
||||
'${maxTime.toStringAsFixed(1)}h',
|
||||
Colors.green)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// --- 4. Search Bar ---
|
||||
TextField(
|
||||
onChanged: (val) => ctrl.updateSearchQuery(val),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search by phone number...',
|
||||
prefixIcon:
|
||||
const Icon(Icons.search, color: Colors.grey),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.grey.shade200),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// --- 5. Driver List ---
|
||||
if (filteredDrivers.isEmpty)
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Text(
|
||||
ctrl.searchQuery.isNotEmpty
|
||||
? "No drivers found with this number"
|
||||
: "No drivers available",
|
||||
style: TextStyle(color: Colors.grey[400])),
|
||||
))
|
||||
else
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: filteredDrivers.length,
|
||||
separatorBuilder: (c, i) =>
|
||||
const SizedBox(height: 12),
|
||||
itemBuilder: (context, index) {
|
||||
final driver = filteredDrivers[index];
|
||||
return _buildDriverCard(
|
||||
context, driver, index, ctrl);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// --- Helper Methods ---
|
||||
|
||||
// Updated to parse the Arabic string format "5 ساعة 30 دقيقة"
|
||||
double _calculateHoursFromStr(dynamic activeTimeStr) {
|
||||
if (activeTimeStr == null || activeTimeStr is! String) return 0.0;
|
||||
|
||||
try {
|
||||
int hours = 0;
|
||||
int mins = 0;
|
||||
|
||||
// Extract hours
|
||||
final hoursMatch = RegExp(r'(\d+)\s*ساعة').firstMatch(activeTimeStr);
|
||||
if (hoursMatch != null) {
|
||||
hours = int.parse(hoursMatch.group(1) ?? '0');
|
||||
}
|
||||
|
||||
// Extract minutes
|
||||
final minsMatch = RegExp(r'(\d+)\s*دقيقة').firstMatch(activeTimeStr);
|
||||
if (minsMatch != null) {
|
||||
mins = int.parse(minsMatch.group(1) ?? '0');
|
||||
}
|
||||
|
||||
return hours + (mins / 60.0);
|
||||
} catch (e) {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildStatCard(String title, String value, Color color) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border(right: BorderSide(color: color, width: 4)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2))
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(title,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey,
|
||||
fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 4),
|
||||
Text(value,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
color: color.withOpacity(0.8),
|
||||
fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDriverCard(BuildContext context, Map driver, int index,
|
||||
DriverCacheController controller) {
|
||||
double hours = _calculateHoursFromStr(driver['active_time']);
|
||||
String driverId = driver['id']?.toString() ?? 'null';
|
||||
bool isPaid = controller.isDriverPaid(driverId);
|
||||
|
||||
// Determine Status Category (mimicking HTML logic)
|
||||
String statusText;
|
||||
Color statusColor;
|
||||
if (hours >= 50) {
|
||||
statusText = "Elite";
|
||||
statusColor = Colors.amber;
|
||||
} else if (hours >= 20) {
|
||||
statusText = "Stable";
|
||||
statusColor = Colors.green;
|
||||
} else if (hours >= 5) {
|
||||
statusText = "Experimental";
|
||||
statusColor = Colors.blue;
|
||||
} else {
|
||||
statusText = "Inactive";
|
||||
statusColor = Colors.red;
|
||||
}
|
||||
|
||||
// Override colors if paid
|
||||
Color cardBackground = isPaid ? Colors.teal.shade50 : Colors.white;
|
||||
Color borderColor = isPaid ? Colors.teal : Colors.transparent;
|
||||
|
||||
// Calculate progress (max assumed 60 hours for 100% bar)
|
||||
double progress = (hours / 60).clamp(0.0, 1.0);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: cardBackground,
|
||||
border: isPaid ? Border.all(color: borderColor, width: 2) : null,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4))
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
if (isPaid)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.teal,
|
||||
borderRadius: BorderRadius.circular(4)),
|
||||
child: const Text("PAID",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold)),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Avatar
|
||||
CircleAvatar(
|
||||
backgroundColor: statusColor.withOpacity(0.1),
|
||||
radius: 24,
|
||||
child: Text(
|
||||
hours.toStringAsFixed(0),
|
||||
style: TextStyle(
|
||||
color: statusColor, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
|
||||
// Name and Phone
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
driver['name_arabic'] ?? 'Unknown Name',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
color: isPaid
|
||||
? Colors.teal.shade900
|
||||
: const Color(0xFF334155)),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
driver['phone'] ?? 'N/A',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
color: Colors.grey),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
driver['active_time'] ?? '',
|
||||
style: TextStyle(fontSize: 10, color: Colors.grey[400]),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const Center(
|
||||
child: Text('No drivers available.'),
|
||||
);
|
||||
})
|
||||
],
|
||||
isleading: true,
|
||||
),
|
||||
),
|
||||
|
||||
// Status Badge
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: statusColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(color: statusColor.withOpacity(0.2)),
|
||||
),
|
||||
child: Text(
|
||||
statusText,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: statusColor,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Progress Bar
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Performance",
|
||||
style: TextStyle(fontSize: 10, color: Colors.grey[600])),
|
||||
Text("${hours.toStringAsFixed(2)} hrs",
|
||||
style: const TextStyle(
|
||||
fontSize: 10, fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
LinearProgressIndicator(
|
||||
value: progress,
|
||||
backgroundColor: Colors.grey[100],
|
||||
color: statusColor,
|
||||
minHeight: 6,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
const Divider(height: 1),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Actions Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
// Pay Gift Button (The specific request)
|
||||
isPaid
|
||||
? const Text("Payment Completed",
|
||||
style: TextStyle(
|
||||
color: Colors.teal, fontWeight: FontWeight.bold))
|
||||
: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
_showPayDialog(driver, controller);
|
||||
},
|
||||
icon: const Icon(Icons.card_giftcard, size: 16),
|
||||
label: Text("Pay Gift".tr),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.indigo, // Dark blue/purple
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16, vertical: 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showPayDialog(Map driver, DriverCacheController controller) {
|
||||
// Check for valid ID immediately
|
||||
String driverId = driver['driver_id']?.toString() ?? '';
|
||||
String phone = driver['phone']?.toString() ?? '';
|
||||
if (driverId.isEmpty || driverId == 'null') {
|
||||
Get.snackbar("Error", "Cannot pay driver with missing ID",
|
||||
backgroundColor: Colors.red, colorText: Colors.white);
|
||||
return;
|
||||
}
|
||||
|
||||
// Controller for the Amount Field
|
||||
final TextEditingController amountController =
|
||||
TextEditingController(text: '50000');
|
||||
|
||||
Get.defaultDialog(
|
||||
title: 'Confirm Payment',
|
||||
titleStyle: const TextStyle(
|
||||
color: Color(0xFF0F172A), fontWeight: FontWeight.bold),
|
||||
content: Column(
|
||||
children: [
|
||||
const Icon(Icons.wallet_giftcard, size: 50, color: Colors.indigo),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Sending gift to ${driver['name_arabic']}',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
// Amount Field
|
||||
TextFormField(
|
||||
controller: amountController,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Amount (SYP)',
|
||||
border: OutlineInputBorder(),
|
||||
prefixIcon: Icon(Icons.attach_money),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
textConfirm: 'Pay Now',
|
||||
confirmTextColor: Colors.white,
|
||||
buttonColor: Colors.indigo,
|
||||
onConfirm: () async {
|
||||
final wallet = Get.put(WalletController());
|
||||
|
||||
// Get amount from field
|
||||
String amount = amountController.text.trim();
|
||||
if (amount.isEmpty) amount = '0';
|
||||
|
||||
// String driverToken = driver['token'] ?? '';
|
||||
|
||||
// 1. Add Payment
|
||||
await wallet.addDriverWallet('gift_connect', driverId, amount, phone);
|
||||
|
||||
// 2. Add to Sefer Wallet
|
||||
//await wallet.addSeferWallet(amount, driverId);
|
||||
|
||||
// 3. Delete Record via CRUD
|
||||
// await CRUD()
|
||||
// .post(link: AppLink.deleteRecord, payload: {'driver_id': driverId});
|
||||
|
||||
// 4. UI Update & Storage
|
||||
// Mark as paid instead of removing completely, so we can see the color change
|
||||
controller.markAsPaid(driverId);
|
||||
|
||||
Get.back(); // Close Dialog
|
||||
|
||||
Get.snackbar("Success", "Payment of $amount EGP sent to driver",
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
},
|
||||
textCancel: 'Cancel',
|
||||
onCancel: () => Get.back(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user