first commit

This commit is contained in:
Hamza-Ayed
2026-06-09 08:40:31 +03:00
commit d8901e1a87
3161 changed files with 536187 additions and 0 deletions

View File

@@ -0,0 +1,335 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../main.dart';
import '../../widgets/my_scafold.dart';
import '../../widgets/mycircular.dart';
import '../../../controller/admin/captain_admin_controller.dart';
import 'captain_details.dart';
class CaptainsPage extends StatelessWidget {
CaptainsPage({super.key});
final CaptainAdminController captainController =
Get.put(CaptainAdminController());
final TextEditingController searchController = TextEditingController();
String myPhone = box.read(BoxName.adminPhone).toString();
bool isSuperAdmin = false;
@override
Widget build(BuildContext context) {
isSuperAdmin = myPhone == '963942542053' || myPhone == '963992952235';
return MyScafolld(
title: 'Search for Captain'.tr,
isleading: true,
body: [
Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Theme.of(context).primaryColor.withOpacity(0.03),
Colors.white,
],
),
),
child: Column(
children: [
_buildHeaderSection(context),
Expanded(
child: GetBuilder<CaptainAdminController>(
builder: (controller) {
if (controller.isLoading) {
return _buildLoadingState();
}
final message = controller.captainData['message'];
if (message == null) {
return _buildEmptyState();
}
// 🔥 الحل هنا: توحيد الشكل إلى List
final List<dynamic> captains =
message is List ? message : [message];
if (captains.isEmpty) {
return _buildEmptyState();
}
return _buildResultsList(context, captains);
},
),
),
],
),
),
],
);
}
// ================= HEADER =================
Widget _buildHeaderSection(BuildContext context) {
return Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(
Icons.manage_search_rounded,
color: Theme.of(context).primaryColor,
size: 24,
),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Find Captain'.tr,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
Text(
'Search by phone number'.tr,
style: TextStyle(
fontSize: 13,
color: Colors.grey[600],
),
),
],
),
],
),
const SizedBox(height: 20),
_buildModernSearchBar(context),
],
),
);
}
Widget _buildModernSearchBar(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.grey[200]!),
),
child: Row(
children: [
Expanded(
child: TextField(
controller: searchController,
keyboardType: TextInputType.phone,
style: const TextStyle(fontSize: 15),
decoration: InputDecoration(
hintText: '0990000000'.tr,
hintStyle: TextStyle(color: Colors.grey[400], fontSize: 14),
prefixIcon: Icon(Icons.phone_android_rounded,
color: Colors.grey[400], size: 22),
border: InputBorder.none,
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
),
onSubmitted: (_) => _performSearch(),
),
),
Padding(
padding: const EdgeInsets.all(6.0),
child: Material(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(12),
child: InkWell(
onTap: _performSearch,
borderRadius: BorderRadius.circular(12),
child: const Padding(
padding: EdgeInsets.all(12),
child: Icon(Icons.search, color: Colors.white, size: 24),
),
),
),
),
],
),
);
}
void _performSearch() {
final phone = searchController.text.trim();
if (phone.isNotEmpty) {
captainController.find_driver_by_phone(phone);
}
}
// ================= RESULTS =================
Widget _buildResultsList(BuildContext context, List<dynamic> captains) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 12),
child: Row(
children: [
Text(
'Search Results'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
'${captains.length}',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor,
),
),
),
],
),
),
Expanded(
child: ListView.separated(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.fromLTRB(20, 0, 20, 80),
itemCount: captains.length,
separatorBuilder: (_, __) => const SizedBox(height: 12),
itemBuilder: (context, index) {
final captain = captains[index] as Map<String, dynamic>;
return _buildModernCaptainCard(context, captain);
},
),
),
],
);
}
// ================= CARD =================
Widget _buildModernCaptainCard(
BuildContext context, Map<String, dynamic> captain) {
final String fullName =
'${captain['first_name'] ?? ''} ${captain['last_name'] ?? ''}';
final String phone = captain['phone']?.toString() ?? '';
final String? email = captain['email']?.toString();
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(20),
onTap: () {
Get.to(() => const CaptainDetailsPage(),
arguments: {'data': captain});
},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).primaryColor.withOpacity(0.8),
Theme.of(context).primaryColor,
],
),
borderRadius: BorderRadius.circular(16),
),
child: const Icon(
Icons.person_rounded,
color: Colors.white,
size: 28,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
fullName,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 6),
Text(phone),
if (isSuperAdmin && email != null) ...[
const SizedBox(height: 4),
Text(email),
],
],
),
),
],
),
),
),
),
);
}
// ================= STATES =================
Widget _buildLoadingState() {
return const Center(child: MyCircularProgressIndicator());
}
Widget _buildEmptyState() {
return const Center(
child: Text("No captains found"),
);
}
}

View File

@@ -0,0 +1,425 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../controller/admin/captain_admin_controller.dart';
import '../../../controller/firebase/firbase_messge.dart';
import '../../../main.dart'; // Import main to access myPhone
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_scafold.dart';
import '../../widgets/my_textField.dart';
import '../quality/driver_scorecard_page.dart';
import 'form_captain.dart';
class CaptainDetailsPage extends StatelessWidget {
const CaptainDetailsPage({super.key});
@override
Widget build(BuildContext context) {
final Map<String, dynamic> data = Get.arguments['data'];
final controller = Get.find<CaptainAdminController>();
String myPhone = box.read(BoxName.adminPhone).toString();
// Define Super Admin Logic
final bool isSuperAdmin =
myPhone == '963942542053' || myPhone == '963992952235';
return MyScafolld(
title: 'Captain Profile'.tr,
isleading: true,
body: [
SingleChildScrollView(
padding: const EdgeInsets.only(bottom: 40),
child: Column(
children: [
// --- Header Section (Avatar & Name) ---
_buildHeaderSection(context, data),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
// --- Personal Information Card ---
_buildInfoCard(
title: 'Personal Information',
icon: Icons.person,
children: [
_buildDetailTile(
Icons.email_outlined,
'Email',
isSuperAdmin
? data['email']
: _maskEmail(
data['email']) // Mask email for non-super
),
_buildDetailTile(
Icons.phone_iphone,
'Phone',
_formatPhoneNumber(
data['phone'].toString(), isSuperAdmin)),
_buildDetailTile(Icons.transgender, 'Gender',
data['gender'] ?? 'Not specified'),
_buildDetailTile(Icons.cake_outlined, 'Birthdate',
data['birthdate'] ?? 'N/A'),
],
),
const SizedBox(height: 16),
// --- Ride Statistics Card ---
_buildInfoCard(
title: 'Performance & Stats',
icon: Icons.bar_chart_rounded,
children: [
_buildDetailTile(Icons.star_rate_rounded, 'Rating',
'${data['ratingPassenger'] ?? 0.0} / 5.0',
valueColor: Colors.amber[700]),
_buildDetailTile(Icons.directions_car_filled_outlined,
'Total Rides', data['countPassengerRide']),
_buildDetailTile(Icons.cancel_outlined,
'Canceled Rides', data['countPassengerCancel'],
valueColor: Colors.redAccent),
],
),
const SizedBox(height: 30),
// --- Action Buttons ---
_buildActionButtons(
context, controller, data, isSuperAdmin),
],
),
),
],
),
),
],
);
}
// --- Header with Gradient Background ---
Widget _buildHeaderSection(BuildContext context, Map<String, dynamic> data) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 25),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 5),
)
],
borderRadius: const BorderRadius.vertical(bottom: Radius.circular(30)),
),
child: Column(
children: [
CircleAvatar(
radius: 45,
backgroundColor: AppColor.primaryColor.withOpacity(0.1),
child: Text(
data['first_name'] != null
? data['first_name'][0].toUpperCase()
: 'C',
style: TextStyle(
fontSize: 35,
fontWeight: FontWeight.bold,
color: AppColor.primaryColor),
),
),
const SizedBox(height: 12),
Text(
'${data['first_name']} ${data['last_name']}',
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.black87),
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
),
child: Text(
'Active Captain'.tr,
style: const TextStyle(
fontSize: 12,
color: Colors.green,
fontWeight: FontWeight.w600),
),
),
],
),
);
}
Widget _buildInfoCard(
{required String title,
required IconData icon,
required List<Widget> children}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.05),
spreadRadius: 2,
blurRadius: 10)
],
border: Border.all(color: Colors.grey.withOpacity(0.1)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, color: AppColor.primaryColor, size: 22),
const SizedBox(width: 10),
Text(title.tr,
style: const TextStyle(
fontSize: 17, fontWeight: FontWeight.bold)),
],
),
Divider(height: 25, color: Colors.grey.withOpacity(0.2)),
...children,
],
),
);
}
Widget _buildDetailTile(IconData icon, String label, dynamic value,
{Color? valueColor}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8)),
child: Icon(icon, color: Colors.grey[600], size: 18),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label.tr,
style: TextStyle(fontSize: 12, color: Colors.grey[500])),
Text(
value?.toString() ?? 'N/A',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: valueColor ?? Colors.black87),
),
],
),
),
],
),
);
}
Widget _buildActionButtons(
BuildContext context,
CaptainAdminController controller,
Map<String, dynamic> data,
bool isSuperAdmin) {
return Column(
children: [
// Driver Scorecard Button
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton.icon(
icon: const Icon(Icons.analytics_outlined, color: Colors.white),
label: Text("بطاقة الأداء (Scorecard)",
style: const TextStyle(color: Colors.white, fontSize: 16)),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
onPressed: () {
Get.to(() => DriverScorecardPage(driverId: data['id'].toString()));
},
),
),
const SizedBox(height: 16),
// Notification is available for everyone
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton.icon(
icon: const Icon(Icons.notifications_active_outlined,
color: Colors.white),
label: Text("Send Notification".tr,
style: const TextStyle(color: Colors.white, fontSize: 16)),
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
onPressed: () => _showSendNotificationDialog(controller, data),
),
),
// Edit and Delete ONLY for Super Admin
if (isSuperAdmin) ...[
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.edit_note_rounded, size: 20),
label: Text("Edit".tr),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: AppColor.yellowColor,
elevation: 0,
side: BorderSide(color: AppColor.yellowColor),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 12),
),
onPressed: () {
Get.to(() => const FormCaptain(), arguments: {
'isEditMode': true,
'captainData': data,
});
},
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton.icon(
icon: const Icon(Icons.delete_outline_rounded, size: 20),
label: Text("Delete".tr),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red[50],
foregroundColor: Colors.red,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 12),
),
onPressed: () => _showDeleteConfirmation(data),
),
),
],
),
] else ...[
// Message for normal admins
const SizedBox(height: 15),
Text(
"Only Super Admins can edit or delete captains.",
style: TextStyle(
color: Colors.grey[400],
fontSize: 12,
fontStyle: FontStyle.italic),
)
]
],
);
}
// --- Helper Methods ---
String _formatPhoneNumber(String phone, bool isSuperAdmin) {
if (isSuperAdmin) return phone;
if (phone.length <= 4) return phone;
return '${'*' * (phone.length - 4)}${phone.substring(phone.length - 4)}';
}
String _maskEmail(String? email) {
if (email == null || email.isEmpty) return 'N/A';
int atIndex = email.indexOf('@');
if (atIndex <= 1) return email; // Too short to mask
return '${email.substring(0, 2)}****${email.substring(atIndex)}';
}
void _showSendNotificationDialog(
CaptainAdminController controller, Map<String, dynamic> data) {
Get.defaultDialog(
title: 'Send Notification'.tr,
titleStyle: const TextStyle(fontWeight: FontWeight.bold),
content: Form(
key: controller.formCaptainPrizeKey,
child: Column(
children: [
MyTextForm(
controller: controller.titleNotify,
label: 'Title'.tr,
hint: 'Enter notification title'.tr,
type: TextInputType.text,
),
const SizedBox(height: 10),
MyTextForm(
controller: controller.bodyNotify,
label: 'Body'.tr,
hint: 'Enter message body'.tr,
type: TextInputType.text,
),
],
),
),
confirm: SizedBox(
width: 100,
child: MyElevatedButton(
title: 'Send',
onPressed: () {
// Check if key is valid (might be recreated)
if (controller.formCaptainPrizeKey.currentState?.validate() ??
true) {
FirebaseMessagesController().sendNotificationToAnyWithoutData(
controller.titleNotify.text,
controller.bodyNotify.text,
data['passengerToken'] ?? '', // Safety check
'order.wav');
Get.back();
Get.snackbar("Success", "Notification Sent",
backgroundColor: Colors.green.withOpacity(0.2));
}
},
),
),
cancel: TextButton(
onPressed: () => Get.back(),
child: Text('Cancel'.tr, style: const TextStyle(color: Colors.grey))),
);
}
void _showDeleteConfirmation(Map<String, dynamic> user) {
Get.defaultDialog(
title: 'Confirm Deletion'.tr,
titleStyle:
const TextStyle(color: Colors.redAccent, fontWeight: FontWeight.bold),
middleText:
'Are you sure you want to delete ${user['first_name']}? This action cannot be undone.'
.tr,
confirm: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.redAccent),
onPressed: () {
// Call delete function here
// controller.deleteCaptain(user['id']);
Get.back();
Get.snackbar("Deleted", "Captain has been removed",
backgroundColor: Colors.red.withOpacity(0.2));
},
child: Text('Delete'.tr, style: const TextStyle(color: Colors.white)),
),
cancel: TextButton(
onPressed: () => Get.back(),
child: Text('Cancel'.tr, style: const TextStyle(color: Colors.grey))),
);
}
}

View File

@@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controller/drivers/driver_not_active_controller.dart';
class DriverDetailsPage extends StatelessWidget {
final String driverId;
final DriverController controller = Get.find();
DriverDetailsPage({super.key, required this.driverId});
/// Helper function to safely get String values from dynamic map
String safeVal(Map d, String key) {
final v = d[key];
if (v == null || v == false) return '';
return v.toString();
}
@override
Widget build(BuildContext context) {
controller.getDriverDetails(driverId);
return Scaffold(
appBar: AppBar(title: const Text("Driver Details")),
body: GetBuilder<DriverController>(
id: 'driverDetails',
builder: (c) {
if (c.driverDetails.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
final d = c.driverDetails['driver'] as Map;
final docs = c.driverDetails['documents'] as List;
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Name: ${safeVal(d, 'first_name')} ${safeVal(d, 'last_name')}"),
Text("Phone: ${safeVal(d, 'phone')}"),
Text("Email: ${safeVal(d, 'email')}"),
Text("National Number: ${safeVal(d, 'national_number')}"),
Text("Gender: ${safeVal(d, 'gender')}"),
Text("Birthdate: ${safeVal(d, 'birthdate')}"),
Text("Status: ${safeVal(d, 'status')}"),
Text("License Type: ${safeVal(d, 'license_type')}"),
Text("License Categories: ${safeVal(d, 'license_categories')}"),
Text("Issue Date: ${safeVal(d, 'issue_date')}"),
Text("Expiry Date: ${safeVal(d, 'expiry_date')}"),
Text("Address: ${safeVal(d, 'address')}"),
Text("Site: ${safeVal(d, 'site')}"),
Text("Employment Type: ${safeVal(d, 'employmentType')}"),
Text("Marital Status: ${safeVal(d, 'maritalStatus')}"),
const SizedBox(height: 16),
const Text("Documents:",
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
...docs.map((doc) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(safeVal(doc, "doc_type")),
const SizedBox(height: 4),
Image.network(
safeVal(doc, "link"),
height: 200,
fit: BoxFit.contain,
errorBuilder: (ctx, err, st) =>
const Icon(Icons.broken_image),
),
const SizedBox(height: 16),
],
)),
],
),
);
},
),
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/views/admin/captain/register_captain.dart';
import 'package:siro_admin/views/widgets/my_scafold.dart';
import '../../../constant/colors.dart';
import '../../../controller/admin/register_captain_controller.dart';
class DriversCantRegister extends StatelessWidget {
const DriversCantRegister({super.key});
@override
Widget build(BuildContext context) {
Get.put(RegisterCaptainController());
return MyScafolld(
title: 'drivers cant register'.tr,
body: [
GetBuilder<RegisterCaptainController>(builder: (mainController) {
return ListView.builder(
itemCount: mainController.driverNotCompleteRegistration.length,
itemBuilder: (context, index) {
final driver =
mainController.driverNotCompleteRegistration[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
Get.to(() => RegisterCaptain(), arguments: {
"phone_number": driver['phone_number'].toString(),
'driverId': driver['driverId'].toString(),
'email': driver['email'].toString(),
});
},
child: Container(
color: driver['note'] == null
? AppColor.greenColor
: AppColor.accentColor,
child: Column(
children: [
Text(driver['phone_number'].toString()),
Text(driver['driverId'].toString()),
Text(driver['email'].toString()),
],
),
),
),
);
},
);
}),
],
isleading: true);
}
}

View File

@@ -0,0 +1,122 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import '../../../constant/links.dart';
import '../../../constant/style.dart';
import '../../../controller/admin/captain_admin_controller.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_scafold.dart';
import '../../widgets/my_textField.dart';
class FormCaptain extends StatefulWidget {
const FormCaptain({super.key});
@override
State<FormCaptain> createState() => _FormCaptainState();
}
class _FormCaptainState extends State<FormCaptain> {
final CaptainAdminController controller = Get.find();
final _formKey = GlobalKey<FormState>();
late TextEditingController firstNameController;
late TextEditingController lastNameController;
late TextEditingController phoneController;
bool isEditMode = false;
Map<String, dynamic>? captainData;
@override
void initState() {
super.initState();
if (Get.arguments != null && Get.arguments['isEditMode'] == true) {
isEditMode = true;
captainData = Get.arguments['captainData'];
firstNameController =
TextEditingController(text: captainData?['first_name'] ?? '');
lastNameController =
TextEditingController(text: captainData?['last_name'] ?? '');
phoneController =
TextEditingController(text: captainData?['phone'] ?? '');
} else {
firstNameController = TextEditingController();
lastNameController = TextEditingController();
phoneController = TextEditingController();
}
}
@override
void dispose() {
firstNameController.dispose();
lastNameController.dispose();
phoneController.dispose();
super.dispose();
}
Future<void> _saveForm() async {
if (_formKey.currentState!.validate()) {
// Create a map of the updated data
Map<String, dynamic> updatedData = {
'id': captainData?['id'], // Important for the WHERE clause in SQL
// 'first_name': firstNameController.text,
// 'last_name': lastNameController.text,
'phone': phoneController.text,
};
var res = await CRUD()
.post(link: AppLink.updateDriverFromAdmin, payload: updatedData);
if (res != 'failure') {
print('Updating data: $updatedData');
Get.back(); // Go back after saving
Get.snackbar('Success', 'Captain data updated successfully!');
}
// controller.updateCaptain(updatedData);
}
}
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Edit Captain'.tr,
isleading: true,
body: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: ListView(
children: [
MyTextForm(
controller: firstNameController,
label: 'First Name'.tr,
hint: 'Enter first name'.tr,
type: TextInputType.name,
),
const SizedBox(height: 16),
MyTextForm(
controller: lastNameController,
label: 'Last Name'.tr,
hint: 'Enter last name'.tr,
type: TextInputType.name,
),
const SizedBox(height: 16),
MyTextForm(
controller: phoneController,
label: 'Phone Number'.tr,
hint: 'Enter phone number'.tr,
type: TextInputType.phone,
),
const SizedBox(height: 32),
MyElevatedButton(
title: 'Update'.tr,
onPressed: _saveForm,
),
],
),
),
),
],
);
}
}

View File

@@ -0,0 +1,982 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/controller/admin/register_captain_controller.dart';
import '../../../../constant/colors.dart';
import '../../../../constant/links.dart';
import '../../../../constant/style.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_scafold.dart';
import '../../widgets/mycircular.dart';
class RegisterCaptain extends StatelessWidget {
RegisterCaptain({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.put(RegisterCaptainController());
// String text = '';
controller.driveInit();
return MyScafolld(
title: 'Documents check'.tr,
action: GetBuilder<RegisterCaptainController>(builder: (controller) {
return IconButton(
onPressed: () {
controller.isLoading = false;
controller.update();
},
icon: const Icon(Icons.refresh),
);
}),
body: [
GetBuilder<RegisterCaptainController>(builder: (controller) {
return controller.isLoading
? const MyCircularProgressIndicator()
: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
(controller.responseIdCardDriverEgyptBack.isNotEmpty &&
controller.responseIdCardDriverEgyptFront
.isNotEmpty &&
controller.responseIdEgyptFront.isNotEmpty &&
controller.responseIdEgyptBack.isNotEmpty &&
controller
.responseIdEgyptDriverLicense.isNotEmpty
// &&
// controller
// .responseCriminalRecordEgypt.isNotEmpty
)
? MyElevatedButton(
title: 'Next'.tr,
onPressed: () {
controller.addDriverAndCarEgypt();
})
: const SizedBox(),
SizedBox(
height:
(controller.responseIdCardDriverEgyptBack
.isNotEmpty &&
controller.responseIdCardDriverEgyptFront
.isNotEmpty &&
controller
.responseIdEgyptFront.isNotEmpty &&
controller
.responseIdEgyptBack.isNotEmpty &&
controller.responseIdEgyptDriverLicense
.isNotEmpty
// &&
// controller.responseCriminalRecordEgypt
// .isNotEmpty
)
? Get.height * .7
: Get.height * .85,
child: ListView(
children: [
egyptDriverLicense(),
egyptCarLicenceFront(),
egyptCarLicenceBack(),
egyptDriverIDFront(),
egyptDriverIDBack(),
// egyptCriminalRecord(),
],
),
),
],
),
);
}),
],
isleading: true);
}
GetBuilder<RegisterCaptainController> egyptDriverLicense() {
return GetBuilder<RegisterCaptainController>(
builder: (ai) {
if (ai.responseIdEgyptDriverLicense.isNotEmpty) {
final expiryDate = ai.responseIdEgyptDriverLicense['expiry_date'];
// Check if the expiry date is before today
final today = DateTime.now();
// Try parsing the expiry date. If it fails, set it to null.
final expiryDateTime = DateTime.tryParse(expiryDate);
final isExpired =
expiryDateTime != null && expiryDateTime.isBefore(today);
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Driver\'s License'.tr, style: AppStyle.headTitle2),
IconButton(
onPressed: () async {
await ai.allMethodForAI("""
Write a JSON object from the following information extracted from the provided Arabic text:
{
"license_type": "",
"national_number": "",
"name_arabic": "",
"name_english": "",
"firstName": "",
"lastName": "",
"address": "",
"issue_date": "", // Format: YYYY-MM-DD using Latin numerals (0-9)
"expiry_date": "", // Format: YYYY-MM-DD using Latin numerals (0-9)
"employmentType": "",
"license_categories": []
}
Important notes:
1. Ensure all dates are in the format YYYY-MM-DD using Latin (Western) numerals (0-9), not Arabic numerals.
2. The 'license_categories' should be an array, even if there's only one category.
3. Fill in all fields based on the information provided in the Arabic text.
4. If any information is missing, leave the field as an empty string or empty array as appropriate.
""", 'driver_license', ai.driverId); //egypt
},
icon: const Icon(Icons.refresh),
),
],
),
const SizedBox(height: 8.0),
const Divider(color: AppColor.accentColor),
const SizedBox(height: 8.0),
Text(
'${'License Type'.tr}: ${ai.responseIdEgyptDriverLicense['license_type']}',
style: AppStyle.title,
),
const SizedBox(height: 8.0),
Text(
'${'National Number'.tr}: ${ai.responseIdEgyptDriverLicense['national_number']}',
style: AppStyle.title.copyWith(
color: ai.responseIdEgyptDriverLicense[
'national_number'] ==
ai.responseIdEgyptBack['nationalID']
? AppColor.greenColor
: AppColor.redColor),
),
const SizedBox(height: 8.0),
Text(
'${'Name (Arabic)'.tr}: ${ai.responseIdEgyptDriverLicense['name_arabic']}',
),
const SizedBox(height: 8.0),
Text(
'${'Name (English)'.tr}: ${ai.responseIdEgyptDriverLicense['name_english']}',
),
const SizedBox(height: 8.0),
Text(
'${'Address'.tr}: ${ai.responseIdEgyptDriverLicense['address']}',
),
const SizedBox(height: 8.0),
Text(
'${'Issue Date'.tr}: ${ai.responseIdEgyptDriverLicense['issue_date']}',
),
const SizedBox(height: 8.0),
Text(
'${'Expiry Date'.tr}: ${ai.responseIdEgyptDriverLicense['expiry_date']}',
style: AppStyle.title.copyWith(
color:
!isExpired ? AppColor.greenColor : AppColor.redColor,
),
),
const SizedBox(height: 8.0),
Text(
'${'License Categories'.tr}: ${ai.responseIdEgyptDriverLicense['license_categories']}',
),
],
),
),
);
}
return Card(
child: InkWell(
onTap: () async {
await ai.allMethodForAI("""
Write a JSON object from the following information extracted from the provided Arabic text:
{
"license_type": "",
"national_number": "",
"name_arabic": "",
"name_english": "",
"firstName": "",
"lastName": "",
"address": "",
"issue_date": "", // Format: YYYY-MM-DD using Latin numerals (0-9)
"expiry_date": "", // Format: YYYY-MM-DD using Latin numerals (0-9)
"employmentType": "",
"license_categories": []
}
Important notes:
1. Ensure all dates are in the format YYYY-MM-DD using Latin (Western) numerals (0-9), not Arabic numerals.
2. The 'license_categories' should be an array, even if there's only one category.
3. Fill in all fields based on the information provided in the Arabic text.
4. If any information is missing, leave the field as an empty string or empty array as appropriate.
""", 'driver_license', ai.driverId); //egypt
},
child: Column(
children: [
Image.network(
'${AppLink.server}/card_image/driver_license-${ai.driverId}.jpg',
height: Get.height * .25,
width: double.maxFinite,
fit: BoxFit.fitHeight,
),
Text(
'Capture an Image of Your Driver License'.tr,
style: AppStyle.title,
),
],
),
),
);
},
);
}
GetBuilder<RegisterCaptainController> egyptDriverIDBack() {
return GetBuilder<RegisterCaptainController>(
builder: (ai) {
if (ai.responseIdEgyptBack.isNotEmpty) {
final taxExpiryDate = ai.responseIdEgyptBack['expirationDate'];
// Check if the tax expiry date is before today
final today = DateTime.now();
// Try parsing the tax expiry date. If it fails, set it to null.
final taxExpiryDateTime = DateTime.tryParse(taxExpiryDate);
final isExpired =
taxExpiryDateTime != null && taxExpiryDateTime.isBefore(today);
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('ID Documents Back'.tr, style: AppStyle.headTitle2),
IconButton(
onPressed: () async {
await ai.allMethodForAI("""
Write a JSON from the following information extracted from the provided Arabic text:
- nationalID(in Latin numerals)
- issueDate (in format YYYY-MM-DD using Latin numerals)
- occupation
- gender
- religion
- maritalStatus
- fullNameMarital (if maritalStatus is "أعزب", set this to "none")
- expirationDate (in format YYYY-MM-DD using Latin numerals)
Please ensure all date fields use Latin (Western) numerals (0-9) instead of Arabic numerals. For example, use "2023-04-03" instead of "٢٠٢٣-٠٤-٠٣".
""", 'id_back', ai.driverId); //egypt
},
icon: const Icon(Icons.refresh),
),
],
),
const SizedBox(height: 8.0),
const Divider(color: AppColor.accentColor),
const SizedBox(height: 8.0),
// Assuming these keys exist in ai.responseIdEgyptFront
Text(
'${'National ID'.tr}: ${ai.responseIdEgyptBack['nationalID']}',
style: AppStyle.title.copyWith(
color: ai.responseIdEgyptDriverLicense[
'national_number'] ==
ai.responseIdEgyptBack['nationalID']
? AppColor.greenColor
: AppColor.redColor),
),
const SizedBox(height: 8.0),
Text(
'${'Occupation'.tr}: ${ai.responseIdEgyptBack['occupation']}', // Assuming 'occupation' exists
),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Issue Date'.tr}: ${ai.responseIdEgyptBack['issueDate']}', // Assuming 'issueDate' exists
),
Text(
'${'Gender'.tr}: ${ai.responseIdEgyptBack['gender']}', // Assuming 'gender' exists
),
],
),
const SizedBox(height: 8.0),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// '${'Religion'.tr}: ${ai.responseIdEgyptBack['religion']}', // Assuming 'religion' exists
// ),
// Text(
// '${'Marital Status'.tr}: ${ai.responseIdEgyptBack['maritalStatus']}', // Assuming 'maritalStatus' exists
// ),
// ],
// ),
// const SizedBox(height: 8.0),
// Text(
// '${'Full Name (Marital)'.tr}: ${ai.responseIdEgyptBack['fullNameMaritial']}', // Assuming 'fullNameMaritial' exists
// ),
// const SizedBox(height: 8.0),
Text(
'${'Expiration Date'.tr}: ${ai.responseIdEgyptBack['expirationDate']}', // Assuming 'expirationDate' exists
style: AppStyle.title.copyWith(
color: !isExpired
? AppColor.greenColor
: AppColor.redColor),
),
],
),
),
);
}
return Card(
child: InkWell(
onTap: () async {
await ai.allMethodForAI('''
Write a JSON object from the following information extracted from the provided Arabic text:
{
"nationalID": "",//(in Latin numerals)
"issueDate": "", // Format: YYYY-MM-DD using Latin numerals (0-9)
"occupation": "",
"gender": "",
"religion": "",
"maritalStatus": "",
"fullNameMaritial": "", // Set to "none" if maritalStatus is "أعزب"
"expirationDate": "" // Format: YYYY-MM-DD using Latin numerals (0-9)
}
Important notes:
1. Ensure all dates (issueDate and expirationDate) are in the format YYYY-MM-DD using Latin (Western) numerals (0-9), not Arabic numerals.
2. If maritalStatus is "أعزب" (single), set fullNameMaritial to "none".
3. Fill in all fields based on the information provided in the Arabic text.
4. If any information is missing, leave the field as an empty string.
''', 'id_back', ai.driverId); //egypt
},
child: Column(
children: [
Image.network(
'${AppLink.server}/card_image/id_back-${ai.driverId}.jpg',
height: Get.height * .25,
width: double.maxFinite,
fit: BoxFit.fitHeight,
),
Text(
'Capture an Image of Your ID Document Back'.tr,
style: AppStyle.title,
),
],
),
),
);
},
);
}
GetBuilder<RegisterCaptainController> egyptDriverIDFront() {
return GetBuilder<RegisterCaptainController>(
builder: (ai) {
if (ai.responseIdEgyptFront.isNotEmpty) {
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('ID Documents Front'.tr, style: AppStyle.headTitle2),
IconButton(
onPressed: () async {
await ai.allMethodForAI('''
Write a JSON object from the following information extracted from the provided Arabic text:
{
"first_name": "", // The word next to "بطاقة تحقيق الشخصية" (National Identification Card)
"full_name": "", // The full name on the next line after the first name
"address": "", // The complete address spanning the next two lines
"national_number": "", // The National ID number before the last line (convert Arabic numerals to Latin)
"card_id": "", // The card ID in English on the last line
"dob": "" // Year of birth only, in Latin numerals (YYYY format)
}
Important notes:
1. For 'first_name', extract the word immediately following "بطاقة تحقيق الشخصية".
2. 'full_name' should be the complete name found on the line after the first name.
3. 'address' should combine information from two consecutive lines.
4. Convert the 'national_number' from Arabic numerals to Latin numerals (0-9).
5. 'card_id' should be extracted as-is from the last line (it's already in English).
6. For 'dob', include only the year of birth in YYYY format using Latin numerals.
7. If any information is missing, leave the field as an empty string.
''', 'id_front', ai.driverId); //egypt
},
icon: const Icon(Icons.refresh),
),
],
),
const SizedBox(height: 8.0),
const Divider(color: AppColor.accentColor),
const SizedBox(height: 8.0),
// Removed Make, Model, etc. as they are not available
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'First Name'.tr}: ${ai.responseIdEgyptFront['first_name']}',
),
Text(
'${'CardID'.tr}: ${ai.responseIdEgyptFront['card_id']}',
),
],
),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Full Name'.tr}: ${ai.responseIdEgyptFront['full_name']}',
),
Text(
'${'DOB'.tr}: ${ai.responseIdEgyptFront['dob']}',
),
],
),
const SizedBox(height: 8.0),
Text(
'${'Address'.tr}: ${ai.responseIdEgyptFront['address']}',
),
const SizedBox(height: 8.0),
// Text(
// '${'National Number'.tr}: ${ai.responseIdEgyptFront['national_number']}',
// ),
// const SizedBox(height: 8.0),
// Removed Inspection Date as it's not available
],
),
),
);
}
return Card(
child: InkWell(
onTap: () async {
await ai.allMethodForAI(""""
Write a JSON object from the following information extracted from the provided Arabic text:
{
"first_name": "", // The word next to "بطاقة تحقيق الشخصية" (National Identification Card)
"full_name": "", // The full name on the next line after the first name
"address": "", // The complete address spanning the next two lines
"national_number": "", // The National ID number before the last line (convert Arabic numerals to Latin)
"card_id": "", // The card ID in English on the last line
"dob": "" // Year of birth only, in Latin numerals (YYYY format)
}
Important notes:
1. For 'first_name', extract the word immediately following "بطاقة تحقيق الشخصية".
2. 'full_name' should be the complete name found on the line after the first name.
3. 'address' should combine information from two consecutive lines.
4. Convert the 'national_number' from Arabic numerals to Latin numerals (0-9).
5. 'card_id' should be extracted as-is from the last line (it's already in English).
6. For 'dob', include only the year of birth in YYYY format using Latin numerals.
7. If any information is missing, leave the field as an empty string.
""", 'id_front', ai.driverId); //egypt
},
child: Column(
children: [
Image.network(
'${AppLink.server}/card_image/id_front-${ai.driverId}.png',
height: Get.height * .25,
width: double.maxFinite,
fit: BoxFit.fitHeight,
),
Text(
'Capture an Image of Your ID Document front'.tr,
style: AppStyle.title,
),
],
),
),
);
},
);
}
GetBuilder<RegisterCaptainController> egyptCarLicenceFront() {
return GetBuilder<RegisterCaptainController>(
builder: (ai) {
if (ai.responseIdCardDriverEgyptFront.isNotEmpty) {
// No need to access ai.responseIdCardDriverEgyptBack anymore
final licenseExpiryDate = DateTime.parse(
ai.responseIdCardDriverEgyptFront['LicenseExpirationDate']);
// Check if license has expired
final today = DateTime.now();
final isLicenseExpired = licenseExpiryDate.isBefore(today);
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text('Vehicle Details Front'.tr,
style: AppStyle.headTitle2),
IconButton(
onPressed: () async {
ai.allMethodForAI("""
Extract the following details from the provided car license data and format them into a JSON object:
License Expiration Date
Car Plate
Owner
Address
Car License Data:
JSON Format:
{
"LicenseExpirationDate": "YYYY-MM-DD",
"car_plate": "[Car plate number]",//the car plate is line next to line contain 'ادارة مرور' for bot numbers and letters in arabic with partition like| but you remove |
"owner": "[Owner's full name]",
"address": "[Address if available, otherwise 'Not provided']"
}
Important notes:
1. For the LicenseExpirationDate, ensure the date is in YYYY-MM-DD format using Latin numerals (0-9).
2. Replace all occurrences of '|' (pipe character) with a space in all fields.
3. If any information is missing, leave the corresponding field as an empty string.
4. Ensure all text is properly formatted and spaces are used correctly.
Please fill in the JSON object with the extracted information, following these guidelines.
""", 'car_front', ai.driverId);
},
icon: const Icon(Icons.refresh),
),
],
),
const SizedBox(height: 8.0),
const Divider(color: AppColor.accentColor),
const SizedBox(height: 8.0),
// Removed Make, Model, etc. as they are not available
Text(
'${'Plate Number'.tr}: ${ai.responseIdCardDriverEgyptFront['car_plate']}',
),
const SizedBox(height: 8.0),
Text(
'${'Owner Name'.tr}: ${ai.responseIdCardDriverEgyptFront['owner']}',
),
const SizedBox(height: 8.0),
Text(
'${'Address'.tr}: ${ai.responseIdCardDriverEgyptFront['address']}',
),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'License Expiry Date'.tr}: ${licenseExpiryDate.toString().substring(0, 10)}',
style: TextStyle(
color: isLicenseExpired ? Colors.red : Colors.green,
),
),
// Removed Fuel as it's not available
],
),
// Removed Inspection Date as it's not available
],
),
),
);
}
return Card(
child: InkWell(
onTap: () async {
ai.allMethodForAI("""
Extract the following details from the provided car license data and format them into a JSON object:
License Expiration Date
Car Plate
Owner
Address
Car License Data:
JSON Format:
{
"LicenseExpirationDate": "YYYY-MM-DD",
"car_plate": "[Car plate number]",//the car plate is line next to line contain 'ادارة مرور' for bot numbers and letters in arabic with partition like| but you remove |
"owner": "[Owner's full name]",
"address": "[Address if available, otherwise 'Not provided']"
}
Important notes:
1. For the LicenseExpirationDate, ensure the date is in YYYY-MM-DD format using Latin numerals (0-9).
2. Replace all occurrences of '|' (pipe character) with a space in all fields.
3. If any information is missing, leave the corresponding field as an empty string.
4. Ensure all text is properly formatted and spaces are used correctly.
Please fill in the JSON object with the extracted information, following these guidelines.
""", 'car_front', ai.driverId);
},
child: Column(
children: [
Image.network(
'${AppLink.server}/card_image/car_front-${ai.driverId}.jpg',
height: Get.height * .25,
width: double.maxFinite,
fit: BoxFit.fitHeight,
),
Text(
'Capture an Image of Your car license front '.tr,
style: AppStyle.title,
),
],
),
),
);
},
);
}
GetBuilder<RegisterCaptainController> egyptCarLicenceBack() {
return GetBuilder<RegisterCaptainController>(
builder: (ai) {
if (ai.responseIdCardDriverEgyptBack.isNotEmpty) {
// Get the tax expiry date from the response
final taxExpiryDate = ai.responseIdCardDriverEgyptBack['tax_expiry'];
// final displacement = ai.responseIdCardDriverEgyptBack['displacement'];
// if (int.parse(displacement) < 1000) {}
// Get the inspection date from the response
final inspectionDate =
ai.responseIdCardDriverEgyptBack['inspection_date'];
final year = int.parse(inspectionDate.split('-')[0]);
// Set inspectionDateTime to December 31st of the given year
final inspectionDateTime = DateTime(year, 12, 31);
String carBackLicenseExpired =
inspectionDateTime.toString().split(' ')[0];
// Get the current date
final today = DateTime.now();
// Try parsing the tax expiry date. If it fails, set it to null.
final taxExpiryDateTime = DateTime.tryParse(taxExpiryDate ?? '');
final isExpired =
taxExpiryDateTime != null && taxExpiryDateTime.isBefore(today);
// Check if the inspection date is before today
bool isInspectionExpired = inspectionDateTime.isBefore(today);
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Vehicle Details Back'.tr,
style: AppStyle.headTitle2),
IconButton(
onPressed: () async {
ai.allMethodForAI("""
Analyze the extracted car license information and create a JSON object with the following keys:
{
"make": "",
"year": "",
"chassis": "",
"model": "",
"engine": "",
"displacement": "",
"cylinders": "",
"fuel": "",
"color": "",
"color_hex": "",
"inspection_date": "",
"assuranceNumber": "",
"tax_expiry": ""
}
Important notes:
1. For dates (inspection_date and tax_expiry), use the format YYYY-MM-DD with Latin numerals (0-9).
2. Convert the color name to its corresponding hex color code for the 'color_hex' field.
3. Ensure all numeric values (year, displacement, cylinders) are in Latin numerals.
4. If any information is missing, leave the corresponding field as an empty string.
5. Do not include any explanatory text in the JSON fields, only the extracted values.
displacement in the line contain (سم٣ )
Please fill in the JSON object with the extracted information, following these guidelines.
""", 'car_back', ai.driverId);
},
icon: const Icon(Icons.refresh),
),
],
),
const SizedBox(height: 8.0),
const Divider(color: AppColor.accentColor),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Make'.tr}: ${ai.responseIdCardDriverEgyptBack['make']}'),
Text(
'${'Model'.tr}: ${ai.responseIdCardDriverEgyptBack['model']}'),
],
),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Year'.tr}: ${ai.responseIdCardDriverEgyptBack['year']}'),
Text(
'${'Chassis'.tr}: ${ai.responseIdCardDriverEgyptBack['chassis']}'),
],
),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Color'.tr}: ${ai.responseIdCardDriverEgyptBack['color']}'),
Text(
'${'Displacement'.tr}: ${ai.responseIdCardDriverEgyptBack['displacement']} cc'),
],
),
const SizedBox(height: 8.0),
Text(
'${'Fuel'.tr}: ${ai.responseIdCardDriverEgyptBack['fuel']}'),
const SizedBox(height: 8.0),
if (taxExpiryDateTime != null)
Text(
'${'Tax Expiry Date'.tr}: $taxExpiryDate',
style: TextStyle(
color: isExpired ? Colors.red : Colors.green,
),
),
const SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Inspection Date'.tr}: $carBackLicenseExpired',
style: TextStyle(
color:
isInspectionExpired ? Colors.red : Colors.green,
),
),
],
),
],
),
),
);
}
return Card(
child: InkWell(
onTap: () async {
ai.allMethodForAI("""
Analyze the extracted car license information and create a JSON object with the following keys:
{
"make": "",
"year": "",
"chassis": "",
"model": "",
"engine": "",
"displacement": "",
"cylinders": "",
"fuel": "",
"color": "",
"color_hex": "",
"inspection_date": "",
"assuranceNumber": "",
"tax_expiry": ""
}
Important notes:
1. For dates (inspection_date and tax_expiry), use the format YYYY-MM-DD with Latin numerals (0-9).
2. Convert the color name to its corresponding hex color code for the 'color_hex' field.
3. Ensure all numeric values (year, displacement, cylinders) are in Latin numerals.
4. If any information is missing, leave the corresponding field as an empty string.
5. Do not include any explanatory text in the JSON fields, only the extracted values.
Please fill in the JSON object with the extracted information, following these guidelines.
""", 'car_back', ai.driverId);
},
child: Column(
children: [
Image.network(
'${AppLink.server}/card_image/car_back-${ai.driverId}.jpg',
height: Get.height * .25,
width: double.maxFinite,
fit: BoxFit.fitHeight,
),
Text(
'Capture an Image of Your car license back'.tr,
style: AppStyle.title,
),
],
),
),
);
},
);
}
GetBuilder<RegisterCaptainController> egyptCriminalRecord() {
return GetBuilder<RegisterCaptainController>(
builder: (ai) {
if (ai.responseCriminalRecordEgypt.isNotEmpty) {
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Criminal Record'.tr, style: AppStyle.headTitle2),
IconButton(
onPressed: () async {
await ai.allMethodForAI("""
Write a JSON object from the following information extracted from the provided Arabic text:
{
"InspectionResult": "",
"NationalID": "",
"FullName": "",
"IssueDate": "" // Format: YYYY-MM-DD
}
Important notes:
1. For the IssueDate, ensure the date is in YYYY-MM-DD format using Latin numerals (0-9).
2. Add appropriate spaces in all text fields to ensure readability.
3. If any information is missing, leave the corresponding field as an empty string.
4. Ensure all text is properly formatted and spaces are used correctly.
5. Convert any Arabic numerals to Latin numerals (0-9) where applicable.
Please fill in the JSON object with the extracted information, following these guidelines.
""", 'criminalRecord', ai.driverId);
},
icon: const Icon(Icons.refresh),
),
],
),
const SizedBox(height: 8.0),
const Divider(color: AppColor.accentColor),
const SizedBox(height: 8.0),
Text(
'${'InspectionResult'.tr}: ${ai.responseCriminalRecordEgypt['InspectionResult']}'),
const SizedBox(height: 8.0),
Text(
'${'FullName'.tr}: ${ai.responseCriminalRecordEgypt['FullName']}',
style: AppStyle.title.copyWith(
color: ai.responseCriminalRecordEgypt['FullName'] ==
ai.responseIdEgyptDriverLicense['name_arabic']
? AppColor.greenColor
: AppColor.redColor),
),
const SizedBox(height: 8.0),
Text(
'${'NationalID'.tr}: ${ai.responseCriminalRecordEgypt['NationalID']}'),
const SizedBox(height: 8.0),
Text(
'${'IssueDate'.tr}: ${ai.responseCriminalRecordEgypt['IssueDate']}'),
],
),
),
);
}
return Card(
child: InkWell(
onTap: () async {
await ai.allMethodForAI("""
Write a JSON object from the following information extracted from the provided Arabic text:
{
"InspectionResult": "",
"NationalID": "",
"FullName": "",
"IssueDate": "" // Format: YYYY-MM-DD
}
Important notes:
1. For the IssueDate, ensure the date is in YYYY-MM-DD format using Latin numerals (0-9).
2. Add appropriate spaces in all text fields to ensure readability.
3. If any information is missing, leave the corresponding field as an empty string.
4. Ensure all text is properly formatted and spaces are used correctly.
5. Convert any Arabic numerals to Latin numerals (0-9) where applicable.
Please fill in the JSON object with the extracted information, following these guidelines.
""", 'criminalRecord', ai.driverId);
},
child: Column(
children: [
Image.network(
'${AppLink.server}/card_image/6.png',
height: Get.height * .25,
width: double.maxFinite,
fit: BoxFit.fitHeight,
),
Text(
'Capture an Image of Your Criminal Record'.tr,
style: AppStyle.title,
),
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import 'package:siro_admin/constant/box_name.dart';
import '../../../constant/info.dart';
import '../../../constant/char_map.dart';
import '../../../controller/drivers/driver_not_active_controller.dart';
import '../../../controller/functions/encrypt_decrypt.dart';
import '../../../main.dart';
import '../../../print.dart';
import 'driver_details_not_active_page.dart';
class DriversPendingPage extends StatelessWidget {
final DriverController controller = Get.put(DriverController());
DriversPendingPage({super.key});
@override
Widget build(BuildContext context) {
controller.getDriversPending();
Log.print(
': ${X.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs).toString().split(AppInformation.addd)[0]}');
return Scaffold(
appBar: AppBar(title: const Text("Drivers Pending")),
body: GetBuilder<DriverController>(
id: 'drivers',
builder: (c) {
if (c.drivers.isEmpty) {
return Center(
child: const Text('no drivers found yet',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
);
}
return ListView.builder(
itemCount: c.drivers.length,
itemBuilder: (ctx, i) {
final d = c.drivers[i];
return ListTile(
title: Text(d["first_name"] + d['last_name'] ?? ""),
subtitle: Text(d["phone"] ?? ""),
onTap: () {
Get.to(() => DriverDetailsPage(driverId: d["id"].toString()));
},
);
},
);
},
),
);
}
}