Initial commit for service

This commit is contained in:
Hamza-Ayed
2026-01-20 23:36:57 +03:00
parent 66ae6c0ddb
commit ad2511bd96
34 changed files with 3757 additions and 4761 deletions

View File

@@ -92,7 +92,7 @@ class AddCarForm extends StatelessWidget {
AppLink.uploadEgypt, carData['id'], 'car_front');
},
child: Image.network(
'https://sefer.click/sefer/card_image/car_front-${carData['id']}.jpg',
'${AppLink.server}/card_image/car_front-${carData['id']}.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
@@ -100,7 +100,7 @@ class AddCarForm extends StatelessWidget {
StackTrace? stackTrace) {
// If the image fails to load, use the _copy version
return Image.network(
'https://sefer.click/sefer/card_image/car_front-${carData['id']}_copy.jpg',
'${AppLink.server}/card_image/car_front-${carData['id']}_copy.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
@@ -114,7 +114,7 @@ class AddCarForm extends StatelessWidget {
AppLink.uploadEgypt, carData['id'], 'car_back');
},
child: Image.network(
'https://sefer.click/sefer/card_image/car_back-${carData['id']}.jpg',
'${AppLink.server}/card_image/car_back-${carData['id']}.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
@@ -122,7 +122,7 @@ class AddCarForm extends StatelessWidget {
StackTrace? stackTrace) {
// If the image fails to load, use the _copy version
return Image.network(
'https://sefer.click/sefer/card_image/car_back-${carData['id']}_copy.jpg',
'${AppLink.server}/card_image/car_back-${carData['id']}_copy.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,

View File

@@ -1,18 +1,20 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:service/views/widgets/my_scafold.dart';
import '../main_controller.dart';
class DriverPage extends StatelessWidget {
DriverPage({super.key});
MainController mainController = MainController();
final MainController mainController = Get.find<MainController>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: GetBuilder<MainController>(builder: (mainController) {
Map data = mainController.driverData['message'][0];
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('${data['first_name']} ${data['last_name']}'),
@@ -21,16 +23,11 @@ class DriverPage extends StatelessWidget {
child: CupertinoScrollbar(
child: ListView(
children: [
_buildDriverInfoSection(
mainController.driverData['message'][0]),
_buildStatisticsSection(
mainController.driverData['message'][0]),
_buildCarInfoSection(mainController.driverData['message'][0]),
_buildLicenseInfoSection(
mainController.driverData['message'][0]),
_buildBankInfoSection(
mainController.driverData['message'][0]),
// buildCarInfo(mainController.driverData['message'][0]),
_buildDriverInfoSection(data),
_buildStatisticsSection(data),
_buildCarInfoSection(data),
_buildLicenseInfoSection(data),
_buildBankInfoSection(data),
],
),
),
@@ -40,32 +37,83 @@ class DriverPage extends StatelessWidget {
);
}
Widget _buildDriverInfoSection(Map<String, dynamic> data) {
// ============================================================
// REUSABLE EDIT ROW
// ============================================================
Widget _buildEditableRow(String label, String key, Map data) {
return CupertinoListTile(
title: Text(label),
trailing: Text(
data[key].toString(),
style: const TextStyle(color: CupertinoColors.systemGrey),
),
onTap: () => _openEditSheet(label, key, data[key]),
);
}
void _openEditSheet(String label, String key, dynamic value) {
final TextEditingController controller =
TextEditingController(text: value.toString());
Get.bottomSheet(
CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("Edit $label"),
trailing: GestureDetector(
child: const Text(
"Save",
style: TextStyle(color: CupertinoColors.activeBlue),
),
onTap: () {
mainController.updateDriverField(key, controller.text);
Get.back();
},
),
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: CupertinoTextField(
controller: controller,
autofocus: true,
),
),
),
),
);
}
// ============================================================
// SECTIONS
// ============================================================
Widget _buildDriverInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Driver Information'.tr),
children: [
_buildInfoRow('Name'.tr, data['name_arabic'].toString()),
_buildInfoRow('Name (English)'.tr, data['name_english'].toString()),
_buildInfoRow('Phone'.tr, data['phone'].toString()),
_buildInfoRow('Email'.tr, data['email'].toString()),
_buildInfoRow('Gender'.tr, data['gender'].toString()),
_buildInfoRow('Birthdate'.tr, data['birthdate'].toString()),
_buildInfoRow('National Number'.tr, data['national_number'].toString()),
_buildInfoRow('Religion'.tr, data['religion'].toString()),
_buildInfoRow('Occupation'.tr, data['occupation'].toString()),
_buildInfoRow('Education'.tr, data['education'].toString()),
_buildEditableRow('Name Arabic'.tr, 'name_arabic', data),
// _buildEditableRow('Name English'.tr, 'name_english', data),
_buildEditableRow('Phone'.tr, 'phone', data),
_buildEditableRow('Email'.tr, 'email', data),
_buildEditableRow('Gender'.tr, 'gender', data),
_buildEditableRow('Birthdate'.tr, 'birthdate', data),
_buildEditableRow('National Number'.tr, 'national_number', data),
// _buildEditableRow('Religion'.tr, 'religion', data),
// _buildEditableRow('Occupation'.tr, 'occupation', data),
// _buildEditableRow('Education'.tr, 'education', data),
],
);
}
Widget _buildStatisticsSection(Map<String, dynamic> data) {
Widget _buildStatisticsSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Driver Statistics'.tr),
children: [
_buildInfoRow('Total Rides'.tr, data['countRide'].toString()),
_buildInfoRow('Average Rating'.tr, data['rating'].toString()),
_buildInfoRow('Total Payments'.tr, '\$${data['totalPayment']}'),
_buildInfoRow('Wallet Balance'.tr, '\$${data['totalDriverWallet']}'),
_buildInfoRow('Total Payments'.tr, data['totalPayment'].toString()),
_buildInfoRow(
'Wallet Balance'.tr, data['totalDriverWallet'].toString()),
_buildInfoRow('Complaints'.tr, data['countComplaint'].toString()),
_buildInfoRow('Scam Reports'.tr, data['countScam'].toString()),
_buildInfoRow(
@@ -76,53 +124,55 @@ class DriverPage extends StatelessWidget {
);
}
Widget _buildCarInfoSection(Map<String, dynamic> data) {
return CupertinoListSection.insetGrouped(
header: Text('Vehicle Information'.tr),
children: [
_buildInfoRow('VIN'.tr, data['vin'].toString()),
_buildInfoRow('Plate Number'.tr, data['car_plate'].toString()),
_buildInfoRow('Make'.tr, data['make'].toString()),
_buildInfoRow('Model'.tr, data['model'].toString()),
_buildInfoRow('Year'.tr, data['year'].toString()),
_buildInfoRow('Color'.tr, data['color'].toString()),
_buildInfoRow('Fuel Type'.tr, data['fuel'].toString()),
_buildInfoRow('Displacement'.tr, data['displacement'].toString()),
_buildInfoRow(
'Registration Date'.tr, data['registration_date'].toString()),
_buildInfoRow('Expiration Date'.tr, data['expiration_date'].toString()),
],
);
}
Widget _buildLicenseInfoSection(Map<String, dynamic> data) {
return CupertinoListSection.insetGrouped(
header: Text('License Information'.tr),
children: [
_buildInfoRow('License Type'.tr, data['license_type'].toString()),
_buildInfoRow('Card ID'.tr, data['card_id'].toString()),
_buildInfoRow('Issue Date'.tr, data['issue_date'].toString()),
_buildInfoRow('Expiry Date'.tr, data['expiry_date'].toString()),
_buildInfoRow('Categories'.tr, data['license_categories'].toString()),
],
);
}
Widget _buildBankInfoSection(Map<String, dynamic> data) {
return CupertinoListSection.insetGrouped(
header: Text('Bank Information'.tr),
children: [
_buildInfoRow('Account Number'.tr, data['accountBank'].toString()),
_buildInfoRow('Bank Code'.tr, data['bankCode'].toString()),
],
);
}
// Read-only row widget
Widget _buildInfoRow(String label, String value) {
return CupertinoListTile(
title: Text(label),
trailing: Text(value,
style: const TextStyle(color: CupertinoColors.systemGrey)),
trailing: Text(
value,
style: const TextStyle(color: CupertinoColors.systemGrey),
),
);
}
Widget _buildCarInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Vehicle Information'.tr),
children: [
// _buildEditableRow('VIN'.tr, 'vin', data),
_buildEditableRow('Plate Number'.tr, 'car_plate', data),
_buildEditableRow('Make'.tr, 'make', data),
_buildEditableRow('Model'.tr, 'model', data),
_buildEditableRow('Year'.tr, 'year', data),
_buildEditableRow('Color'.tr, 'color', data),
_buildEditableRow('Fuel Type'.tr, 'fuel', data),
// _buildEditableRow('Displacement'.tr, 'displacement', data),
_buildEditableRow('Registration Date'.tr, 'registration_date', data),
_buildEditableRow('Expiration Date'.tr, 'expiration_date', data),
],
);
}
Widget _buildLicenseInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('License Information'.tr),
children: [
_buildEditableRow('License Type'.tr, 'license_type', data),
_buildEditableRow('Card ID'.tr, 'card_id', data),
_buildEditableRow('Issue Date'.tr, 'issue_date', data),
_buildEditableRow('Expiry Date'.tr, 'expiry_date', data),
_buildEditableRow('Categories'.tr, 'license_categories', data),
],
);
}
Widget _buildBankInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Bank Information'.tr),
children: [
_buildEditableRow('Account Number'.tr, 'accountBank', data),
_buildEditableRow('Bank Code'.tr, 'bankCode', data),
],
);
}
}

View File

@@ -2,9 +2,10 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:service/constant/colors.dart';
import 'package:service/controller/functions/encrypt_decrypt.dart';
import 'package:service/controller/mainController/main_controller.dart';
import 'package:service/views/widgets/my_scafold.dart';
import 'package:service/main.dart';
import 'package:service/constant/box_name.dart';
import 'registration_captain_page.dart';
@@ -14,201 +15,204 @@ class DriversCantRegister extends StatelessWidget {
@override
Widget build(BuildContext context) {
Get.put(MainController());
// Unified action button (WhatsApp - Edit - etc)
Widget buildActionButton({
required IconData icon,
required Color color,
required VoidCallback onPressed,
}) {
return CupertinoButton(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
onPressed: onPressed,
child: Icon(icon, color: color, size: 28),
);
}
return MyScaffold(
title: 'Drivers Cant Register'.tr,
title: 'Drivers Want Register'.tr,
isleading: true,
body: [
GetBuilder<MainController>(builder: (mainController) {
return Column(
children: [
// Search
Padding(
padding: const EdgeInsets.all(8.0),
padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
child: CupertinoSearchTextField(
keyboardType: TextInputType.phone,
onChanged: (value) => mainController.searchDrivers(value),
placeholder: 'Search by phone number'.tr,
),
),
// List
Expanded(
child: ListView.builder(
itemCount: mainController.filteredDrivers.length,
itemBuilder: (context, index) {
final driver = mainController.filteredDrivers[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: driver['note'] == null
? AppColor.greenColor
: AppColor.greyColor,
child: CupertinoFormSection(
header: Text(
'Driver ID: ${driver['driverId']}',
),
children: [
InkWell(
onTap: () => mainController
.makePhoneCall(driver['phone_number']),
child: Container(
height: 40,
color: driver['note'] != null
? AppColor.greenColor
: AppColor.greyColor,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
child: mainController.filteredDrivers.isEmpty
? Center(
child: Text(
'No drivers found'.tr,
style:
TextStyle(color: Colors.grey[600], fontSize: 16),
),
)
: ListView.builder(
itemCount: mainController.filteredDrivers.length,
itemBuilder: (context, index) {
final driver = mainController.filteredDrivers[index];
final notesController =
TextEditingController(text: driver['note'] ?? '');
return Card(
margin: const EdgeInsets.symmetric(
horizontal: 12.0, vertical: 8.0),
elevation: 3,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(
color: driver['note'] != null
? AppColor.secondaryColor
: Colors.transparent,
width: 2.5,
),
),
child: Padding(
padding:
const EdgeInsets.fromLTRB(16, 12, 16, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Text(
'Driver Phone: ${driver['first_name'] ?? ''} ${driver['last_name'] ?? ''}',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.black87,
),
),
const Divider(height: 20),
// Phone Row
Row(
children: [
Text((driver['phone_number'])),
IconButton(
const Icon(
CupertinoIcons.phone_fill,
color: AppColor.primaryColor,
size: 22,
),
const SizedBox(width: 12),
Expanded(
child: InkWell(
onTap: () {
mainController.makePhoneCall(
driver['phone_number']);
},
child: Text(
driver['phone_number'] ?? 'N/A',
style: const TextStyle(
fontSize: 17,
letterSpacing: 1.2,
),
),
),
),
// WhatsApp button
buildActionButton(
icon: Icons.send,
color: const Color(0xFF25D366),
onPressed: () {
String message = "مرحباً،\n\n"
"نلاحظ أنك لم تكمل عملية التسجيل في خدمة Tripz درايفر. نود تذكيرك بأن إكمال التسجيل يتيح لك فرصة الانضمام إلى فريق Tripz والاستفادة من خدماتنا المتنوعة.\n\n"
"إذا كنت بحاجة إلى أي مساعدة أو لديك أي استفسارات، لا تتردد في الاتصال بنا. نحن هنا لمساعدتك.\n\n"
"للاتصال بنا، يرجى الاتصال على الرقم التالي: +20 101 880 5430\n\n"
"مع تحيات فريق Tripz.";
"يظهر لدينا في نظام تطبيق *انطلق* أنك لم تكمل عملية التسجيل بعد.\n"
"ندعوك لإكمال التسجيل للاستفادة من مزايا التطبيق والبدء بالعمل معنا.\n\n"
"إذا احتجت لأي مساعدة، تواصل معنا على خدمة العملاء:\n"
"+963 952 475 742\n\n"
"+963 952 475 740\n\n"
"فريق انطلق يتمنى لك يوماً سعيداً.";
mainController.launchCommunication(
'whatsapp',
'${driver['phone_number']}',
message);
'whatsapp',
driver['phone_number'],
message,
);
},
icon: const Icon(
Icons.send_time_extension_sharp,
color: AppColor.secondaryColor,
),
),
IconButton(
// Edit button → go to registration form
buildActionButton(
icon: CupertinoIcons
.pencil_ellipsis_rectangle,
color: AppColor.gold,
onPressed: () {
Get.to(() => RegisterCaptain(),
arguments: {
"phone_number":
driver['phone_number']
.toString(),
'driverId': driver['driverId']
.toString(),
'email':
driver['email'].toString(),
});
Get.to(
() => RegisterCaptain(),
arguments: {
"phone": driver['phone_number'],
"driverId": driver['driverId'],
},
);
},
),
buildActionButton(
icon: CupertinoIcons
.pencil_ellipsis_rectangle,
color: AppColor.redColor,
onPressed: () {
mainController
.deleteDriverNotCompleteRegistration(
driver['phone_number']);
},
icon: const Icon(
Icons.save,
color: AppColor.gold,
),
),
],
)
// CupertinoFormRow(
// prefix: Text('Phone Number'.tr),
// child: CupertinoTextFormFieldRow(
// initialValue: driver['phone_number'],
// readOnly: true,
// placeholder: 'Phone Number'.tr,
// ),
// ),
),
),
CupertinoFormRow(
prefix: Text('Created At'.tr),
child: CupertinoTextFormFieldRow(
initialValue: driver['created_at'],
readOnly: true,
placeholder: 'Created At',
),
),
CupertinoFormRow(
prefix: Text('Status'.tr),
child: GestureDetector(
onTap: () {
showCupertinoModalPopup<void>(
context: Get.context!,
builder: (BuildContext context) =>
Container(
height: 216,
padding: const EdgeInsets.only(top: 6.0),
margin: EdgeInsets.only(
bottom: MediaQuery.of(context)
.viewInsets
.bottom,
),
color: CupertinoColors.systemBackground
.resolveFrom(context),
child: SafeArea(
top: false,
child: CupertinoPicker(
magnification: 1.22,
squeeze: 1.2,
useMagnifier: true,
itemExtent: 32.0,
scrollController:
FixedExtentScrollController(
initialItem: mainController
.selectedStatus
.indexOf(mainController
.selectedStatus),
),
onSelectedItemChanged:
(int selectedItem) {
mainController.setSelectedStatus(
mainController
.statusOptions[selectedItem]
.tr);
},
children: List<Widget>.generate(
mainController.statusOptions
.length, (int index) {
return Center(
child: Text(mainController
.statusOptions[index].tr),
);
}),
),
),
),
);
},
child: CupertinoFormRow(
child: Text(
mainController.selectedStatus.tr,
style: TextStyle(
color: CupertinoColors.label
.resolveFrom(Get.context!)),
),
),
),
),
CupertinoFormRow(
prefix: Text('Notes'.tr),
child: CupertinoTextFormFieldRow(
cursorColor: AppColor.blueColor,
controller: mainController.notesController,
placeholder:
driver['note'] ?? "Additional comments".tr,
),
),
CupertinoButton(
child: Text('Save Notes'.tr),
onPressed: () {
// Save the notes for the driver
String notes =
mainController.notesController.text == ''
? mainController.selectedStatus
.toString()
: mainController.notesController.text;
mainController
.saveNoteForDriverNotCompleteRegistration(
driver['phone_number'],
'girls name',
notes);
print(
'Notes for driver ${driver['id']}: $notes');
},
const SizedBox(height: 16),
// Notes box
CupertinoTextField(
controller: notesController,
placeholder: "Additional comments".tr,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
maxLines: 2,
),
const SizedBox(height: 12),
// Save button
Align(
alignment: AlignmentDirectional.centerEnd,
child: CupertinoButton(
padding: const EdgeInsets.symmetric(
horizontal: 20),
color: AppColor.secondaryColor,
onPressed: () {
mainController
.saveNoteForDriverNotCompleteRegistration(
driver['phone_number'],
box.read(BoxName.employeename) ??
'none',
notesController.text,
);
},
child: Text('Save Notes'.tr),
),
),
],
),
),
],
),
);
},
),
);
},
),
),
],
);

View File

@@ -0,0 +1,485 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:service/constant/colors.dart';
// Importa tu nuevo controlador de servicio
import 'package:service/controller/mainController/ragister_service_controller.dart';
import 'package:service/views/widgets/elevated_btn.dart';
import 'package:service/views/widgets/my_scafold.dart';
// El import del antiguo controlador ya no es necesario
// import '../registration_captain_controller.dart';
class RegisterCaptain extends StatelessWidget {
const RegisterCaptain({super.key});
@override
Widget build(BuildContext context) {
// Instancia el NUEVO controlador
final controller = Get.put(RegisterCaptainServiceController());
return MyScaffold(
title: 'Syrian Documents Check'.tr,
isleading: true,
body: [
Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: StepIndicator(
currentStep: controller.currentPageIndex.value,
totalSteps: 4,
),
),
Expanded(
child: PageView(
controller: controller.pageController,
onPageChanged: (index) {
controller.currentPageIndex.value = index;
},
children: [
// <-- PÁGINAS REACTIVADAS CON EL NUEVO CONTROLADOR -->
_buildSyrianDriverLicenseFront(context, controller),
_buildSyrianDriverLicenseBack(context, controller),
_buildSyrianCarLicenseFront(context, controller),
_buildSyrianCarLicenseBack(controller),
],
),
),
_buildNavigationControls(controller),
],
);
}),
],
);
}
// Este método ya estaba usando el controlador correcto
Widget _buildNavigationControls(RegisterCaptainServiceController controller) {
bool isLastPage = controller.currentPageIndex.value == 3;
bool isFirstPage = controller.currentPageIndex.value == 0;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (!isFirstPage)
MyElevatedButton(
title: 'Back'.tr,
onPressed: controller.previousPage,
kolor: Colors.grey,
),
if (isFirstPage) const Spacer(),
MyElevatedButton(
title: isLastPage ? 'Save and Activate'.tr : 'Next'.tr,
onPressed: isLastPage
? controller.updateAndActivateSyrianDriver
: controller.nextPage,
kolor: isLastPage ? AppColor.greenColor : AppColor.primaryColor,
),
],
),
);
}
Widget _buildTextField({
required String label,
required TextEditingController controller,
IconData? icon,
TextInputType keyboardType = TextInputType.text,
VoidCallback? onTap,
String? hintText,
}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
controller: controller,
keyboardType: keyboardType,
readOnly: onTap != null,
onTap: onTap,
decoration: InputDecoration(
labelText: label.tr,
hintText: hintText,
prefixIcon:
icon != null ? Icon(icon, color: AppColor.secondaryColor) : null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: AppColor.secondaryColor, width: 2),
),
),
),
);
}
Widget _buildPageContent({
required RxString imageUrl,
required List<Widget> formFields,
}) {
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ClipRRect(
// borderRadius: BorderRadius.circular(12),
// child: Image.network(
// imageUrl.value,
// fit: BoxFit.fitWidth,
// errorBuilder: (context, error, stackTrace) {
// return Container(
// height: 200,
// color: Colors.grey[200],
// child: Center(
// child: Text(
// 'Image not available'.tr,
// style: const TextStyle(color: Colors.grey),
// ),
// ),
// );
// },
// ),
// ),
// const SizedBox(height: 20),
const Divider(),
...formFields,
],
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildColorDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.colorHex.value.isEmpty
? null
: controller.colorHex.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Car Color'.tr,
prefixIcon: Icon(Icons.color_lens, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
items: RegisterCaptainServiceController.kCarColorOptions.map((opt) {
final hex = opt['hex']!;
final key = opt['key']!;
return DropdownMenuItem<String>(
value: hex,
child: Row(
children: [
Container(
width: 18,
height: 18,
decoration: BoxDecoration(
color: controller.hexToColor(hex),
shape: BoxShape.circle,
border: Border.all(color: Colors.black12),
),
),
const SizedBox(width: 12),
Expanded(child: Text(key.tr)),
],
),
);
}).toList(),
onChanged: (hex) {
if (hex != null) {
controller.updateColorSelection(hex);
}
},
),
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildGenderDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.selectedGender.value.isEmpty
? null
: controller.selectedGender.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Gender'.tr,
prefixIcon: Icon(Icons.wc, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
items: ['Male', 'Female'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedGender.value = newValue;
}
},
),
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildFuelDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
value: RegisterCaptainServiceController.kFuelOptions
.contains(controller.selectedFuel.value)
? controller.selectedFuel.value
: null,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Fuel Type'.tr,
prefixIcon:
Icon(Icons.local_gas_station, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
items:
RegisterCaptainServiceController.kFuelOptions.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedFuel.value = newValue;
}
},
),
),
);
}
// --- PAGE 1: Driver License Front ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianDriverLicenseFront(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_front']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'First Name',
controller: controller.firstNameController,
keyboardType: TextInputType.name),
),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Last Name',
controller: controller.lastNameController,
keyboardType: TextInputType.name),
),
],
),
_buildTextField(
label: 'Phone Number',
controller: controller.phoneController,
hintText: 'e.g., 963992952235',
icon: Icons.phone,
keyboardType: TextInputType.phone,
),
_buildTextField(
label: 'Place of Registration',
controller: controller.siteController,
icon: Icons.location_city),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'National Number',
controller: controller.nationalNumberController,
keyboardType: TextInputType.number,
icon: Icons.fingerprint),
),
const SizedBox(width: 8),
Expanded(child: _buildGenderDropdown(controller)),
],
),
_buildTextField(
label: 'Birthdate',
controller: controller.birthdateController,
icon: Icons.cake,
onTap: () =>
controller.selectDate(context, controller.birthdateController)),
],
);
}
// --- PAGE 2: Driver License Back ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianDriverLicenseBack(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_back']!,
formFields: [
_buildTextField(
label: 'License Category',
controller: controller.licenseCategoriesController),
_buildTextField(
label: 'Expiry Date',
controller: controller.expiryDateController,
icon: Icons.event_busy,
onTap: () => controller.selectDate(
context, controller.expiryDateController)),
],
);
}
// --- PAGE 3: Car License Front ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianCarLicenseFront(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_front']!,
formFields: [
_buildTextField(
label: 'Owner Name',
controller: controller.ownerController,
keyboardType: TextInputType.name,
icon: Icons.person_search),
Row(
children: [
Expanded(child: _buildColorDropdown(controller)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Car Plate',
controller: controller.carPlateController,
),
),
],
),
// _buildTextField(
// label: 'VIN',
// controller: controller.vinController,
// icon: Icons.confirmation_number),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'License Issue Date',
controller: controller.licenseIssueDateController,
onTap: () => controller.selectDate(
context, controller.licenseIssueDateController))),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'License Expiry Date',
controller: controller.carLicenseExpiryDateController,
onTap: () => controller.selectDate(
context, controller.carLicenseExpiryDateController))),
],
),
],
);
}
// --- PAGE 4: Car License Back ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianCarLicenseBack(
RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_back']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Make',
controller: controller.makeController,
)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Model',
controller: controller.modelController,
)),
],
),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Year',
controller: controller.yearController,
keyboardType: TextInputType.number)),
const SizedBox(width: 8),
Expanded(child: _buildFuelDropdown(controller)),
],
),
],
);
}
}
class StepIndicator extends StatelessWidget {
final int currentStep;
final int totalSteps;
const StepIndicator({
super.key,
required this.currentStep,
required this.totalSteps,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
'${'Step'.tr} ${currentStep + 1} ${'of'.tr} $totalSteps',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black54),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(totalSteps, (index) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
width: 30,
height: 8,
decoration: BoxDecoration(
color: index <= currentStep
? AppColor.primaryColor
: Colors.grey.shade300,
borderRadius: BorderRadius.circular(4),
),
);
}),
),
],
);
}
}

View File

@@ -28,12 +28,12 @@ class PassengersCantRegister extends StatelessWidget {
header: Text('Passenger ID: ${passenger['id']}'),
children: [
InkWell(
onTap: () => mainController
.makePhoneCall(passenger['phone_number']),
onTap: () =>
mainController.makePhoneCall(passenger['phone']),
child: CupertinoFormRow(
prefix: Text('Phone Number'.tr),
prefix: Text(' phone'.tr),
child: CupertinoTextFormFieldRow(
initialValue: ((passenger['phone_number'])),
initialValue: ((passenger['phone'])),
readOnly: true,
placeholder: 'Phone Number'.tr,
),

View File

@@ -9,92 +9,109 @@ class PassengersPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetBuilder<MainController>(builder: (mainController) {
Map data = mainController.passengerData['message'][0];
return MyScaffold(
title: (data['first_name']?.toString() ?? '') +
' ' +
(data['last_name']?.toString() ?? ''),
isleading: true,
body: [
ListView(
children: [
_buildPersonalInfoCard(data),
_buildLatestRideCard(data),
_buildWalletInfoCard(data),
],
),
],
);
});
return GetBuilder<MainController>(
builder: (mainController) {
final data = _extractPassengerData(mainController);
return MyScaffold(
title: data != null
? '${data['first_name'] ?? ''} ${data['last_name'] ?? ''}'
: 'Passenger Not Found'.tr,
isleading: true,
body: [
if (data != null)
ListView(
children: [
_buildPersonalInfoCard(data),
_buildLatestRideCard(data),
_buildWalletInfoCard(data),
],
)
else
const Center(
child: Padding(
padding: EdgeInsets.all(24.0),
child: Text(
'No passenger data available.',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
),
),
),
],
);
},
);
}
/// Helper method to extract the passenger data safely.
Map? _extractPassengerData(MainController controller) {
final passengerData = controller.passengerData;
if (passengerData == null ||
passengerData['message'] == null ||
passengerData['message'].isEmpty) {
return null;
}
return passengerData['message'][0];
}
Widget _buildPersonalInfoCard(Map data) {
return Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Personal Information'.tr,
style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
_buildInfoRow('Phone'.tr, data['phone']?.toString() ?? 'N/A'),
_buildInfoRow('Email'.tr, data['email']?.toString() ?? 'N/A'),
_buildInfoRow('Gender'.tr, data['gender']?.toString() ?? 'N/A'),
_buildInfoRow(
'Birthdate'.tr, data['birthdate']?.toString() ?? 'N/A'),
_buildInfoRow(
'Education'.tr, data['education']?.toString() ?? 'N/A'),
_buildInfoRow(
'Employment'.tr, data['employmentType']?.toString() ?? 'N/A'),
_buildInfoRow('Marital Status'.tr,
data['maritalStatus']?.toString() ?? 'N/A'),
],
),
),
return _buildCard(
title: 'Personal Information'.tr,
children: [
_buildInfoRow('Phone'.tr, data['phone']),
_buildInfoRow('Email'.tr, data['email']),
_buildInfoRow('Gender'.tr, data['gender']),
_buildInfoRow('Birthdate'.tr, data['birthdate']),
// _buildInfoRow('Education'.tr, data['education']),
_buildInfoRow('Employment'.tr, data['employmentType']),
_buildInfoRow('Marital Status'.tr, data['maritalStatus']),
],
);
}
Widget _buildLatestRideCard(Map data) {
return Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Latest Ride'.tr,
style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
_buildInfoRow('Ride ID'.tr, data['ride_id']?.toString() ?? 'N/A'),
_buildInfoRow('Date'.tr, data['ride_date']?.toString() ?? 'N/A'),
_buildInfoRow(
'Start Time'.tr, data['ride_time']?.toString() ?? 'N/A'),
_buildInfoRow(
'End Time'.tr, data['ride_endtime']?.toString() ?? 'N/A'),
_buildInfoRow(
'Price'.tr, '\$${data['price']?.toString() ?? 'N/A'}'),
_buildInfoRow(
'Status'.tr, data['ride_status']?.toString() ?? 'N/A'),
_buildInfoRow('Payment Method'.tr,
data['ride_payment_method']?.toString() ?? 'N/A'),
_buildInfoRow('Car Type'.tr, data['car_type']?.toString() ?? 'N/A'),
_buildInfoRow(
'Distance'.tr,
data['distance'] != null
? '${data['distance'].toStringAsFixed(2)} km'
: 'N/A'),
],
),
),
return _buildCard(
title: 'Latest Ride'.tr,
children: [
_buildInfoRow('Ride ID'.tr, data['ride_id']),
_buildInfoRow('Date'.tr, data['ride_date']),
_buildInfoRow('Start Time'.tr, data['ride_time']),
_buildInfoRow('End Time'.tr, data['ride_endtime']),
_buildInfoRow(
'Price'.tr, data['price'] != null ? '\$${data['price']}' : null),
_buildInfoRow('Status'.tr, data['ride_status']),
_buildInfoRow('Payment Method'.tr, data['ride_payment_method']),
_buildInfoRow('Car Type'.tr, data['car_type']),
_buildInfoRow(
'Distance'.tr,
data['distance'] != null
? '${(data['distance'] as num).toStringAsFixed(2)} km'
: null),
],
);
}
Widget _buildWalletInfoCard(Map data) {
return _buildCard(
title: 'Wallet Information'.tr,
children: [
_buildInfoRow(
'Wallet Balance'.tr,
data['passenger_wallet_balance'] != null
? '\$${data['passenger_wallet_balance']}'
: null),
_buildInfoRow(
'Last Payment Amount'.tr,
data['passenger_payment_amount'] != null
? '\$${data['passenger_payment_amount']}'
: null),
_buildInfoRow(
'Last Payment Method'.tr, data['passenger_payment_method']),
],
);
}
Widget _buildCard({required String title, required List<Widget> children}) {
return Card(
margin: const EdgeInsets.all(16),
child: Padding(
@@ -102,30 +119,26 @@ class PassengersPage extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Wallet Information'.tr,
Text(title,
style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
_buildInfoRow('Wallet Balance'.tr,
'\$${data['passenger_wallet_balance']?.toString() ?? 'N/A'}'),
_buildInfoRow('Last Payment Amount'.tr,
'\$${data['passenger_payment_amount']?.toString() ?? 'N/A'}'),
_buildInfoRow('Last Payment Method'.tr,
data['passenger_payment_method']?.toString() ?? 'N/A'),
...children,
],
),
),
);
}
Widget _buildInfoRow(String label, String value) {
Widget _buildInfoRow(String label, dynamic value) {
final displayValue = value?.toString() ?? 'N/A';
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
Text(value),
Flexible(child: Text(displayValue, overflow: TextOverflow.ellipsis)),
],
),
);

View File

@@ -1,4 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:service/constant/colors.dart';
import 'package:service/constant/style.dart';
@@ -12,21 +13,35 @@ class WelcomeCall extends StatelessWidget {
@override
Widget build(BuildContext context) {
Get.put(MainController());
final controller = Get.put(MainController());
return MyScaffold(
title: 'Welcome Drivers'.tr,
isleading: true,
body: [
GetBuilder<MainController>(builder: (mainController) {
return Expanded(
child: CupertinoScrollbar(
child: ListView.builder(
itemCount: mainController.newDriverRegister.length,
itemBuilder: (context, index) {
final driver = mainController.newDriverRegister[index];
return DriverCard(driver: driver);
},
final drivers = mainController.newDriverRegister;
if (drivers.isEmpty) {
return const Padding(
padding: EdgeInsets.all(32.0),
child: Center(
child: Text(
'No new drivers found.',
style: TextStyle(fontSize: 18),
),
),
);
}
return CupertinoScrollbar(
child: ListView.builder(
padding: const EdgeInsets.only(bottom: 20),
itemCount: drivers.length,
itemBuilder: (context, index) {
final driver = drivers[index];
return DriverCard(driver: driver);
},
),
);
}),
@@ -39,9 +54,22 @@ class DriverCard extends StatelessWidget {
final Map<String, dynamic> driver;
const DriverCard({super.key, required this.driver});
Widget buildActionButton({
required IconData icon,
required Color color,
required VoidCallback onPressed,
}) {
return CupertinoButton(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
onPressed: onPressed,
child: Icon(icon, color: color, size: 28),
);
}
@override
Widget build(BuildContext context) {
final controller = Get.find<MainController>();
return CupertinoCard(
margin: const EdgeInsets.all(16.0),
child: Padding(
@@ -49,38 +77,37 @@ class DriverCard extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// حالة التلوين حسب isCall
Container(
decoration: AppStyle.boxDecoration1.copyWith(
color: driver['isCall'].toString() == '1'
? AppColor.greenColor
: AppColor.accentColor),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 2),
child: Text(
'Driver Information'.tr,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
),
color: driver['isCall'].toString() == '1'
? AppColor.greenColor
: AppColor.accentColor,
),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
child: Text(
'Driver Information'.tr,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
),
),
const SizedBox(height: 16),
InfoText('Name'.tr, driver['name_arabic'].toString()),
InfoText('Phone'.tr, driver['phone'].toString()),
InfoText('Email'.tr, driver['email'].toString()),
InfoText('License Type'.tr, driver['license_type'].toString()),
const SizedBox(height: 12),
InfoText(
'License Categories'.tr, driver['license_categories'] ?? ''),
InfoText(
'National Number'.tr, driver['national_number'].toString()),
InfoText('Occupation'.tr, driver['occupation'].toString()),
const SizedBox(height: 16),
Text(
'Notes:'.tr,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
),
'Name'.tr, driver['first_name'] + ' ' + driver['last_name']),
InfoText('Phone'.tr, driver['phone']),
InfoText('Email'.tr, driver['email']),
InfoText('License Type'.tr, driver['license_type']),
InfoText('License Categories'.tr, driver['license_categories']),
InfoText('National Number'.tr, driver['national_number']),
InfoText('Occupation'.tr, driver['occupation']),
const SizedBox(height: 12),
Text('Notes:'.tr,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle),
const SizedBox(height: 8),
CupertinoTextField(
controller: Get.find<MainController>().notesController,
controller: controller.notesController,
placeholder: driver['notes'] ?? 'Enter notes here...'.tr,
maxLines: 3,
padding: const EdgeInsets.all(12.0),
@@ -89,25 +116,55 @@ class DriverCard extends StatelessWidget {
borderRadius: BorderRadius.circular(8.0),
),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: MyElevatedButton(
title: 'Call Driver'.tr,
onPressed: () {
Get.find<MainController>()
.makePhoneCall(driver['phone'].toString());
})),
CupertinoButton(
onPressed: () async {
await Get.find<MainController>().addWelcomeCall(
driver['id'].toString(),
);
},
child: Text('Save Changes'.tr),
Expanded(
child: MyElevatedButton(
title: 'Call Driver'.tr,
onPressed: () {
controller.makePhoneCall(driver['phone'].toString());
},
),
),
const SizedBox(width: 16),
Expanded(
child: CupertinoButton(
onPressed: () async {
await controller.addWelcomeCall(driver['id'].toString());
},
child: Text('Save Changes'.tr),
),
),
Expanded(
child: CupertinoButton(
onPressed: () async {
final phone = driver['phone'];
if (phone == null || phone.toString().isEmpty) {
Get.snackbar("خطأ", "لا يوجد رقم هاتف لهذا السائق");
return;
}
String message = "مرحباً،\n\n"
"يعطيك العافية أستاذ. نرحب بك في شركة *انطلق* للنقل الذكي.\n"
"نود تعريفك بالتطبيق ومميزاته لتتمكن من الاستفادة الكاملة وبدء العمل معنا بسهولة.\n\n"
"لأي استفسار أو مساعدة، يمكنك التواصل معنا على الأرقام التالية:\n\n"
"+963 952 475 742\n"
"+963 952 475 740\n"
"+963 952 475 734\n\n"
"فريق انطلق يتمنى لك تجربة موفقة ويوم سعيد.";
Get.find<MainController>().launchCommunication(
'whatsapp',
phone,
message,
);
},
child: Text('send'.tr),
),
),
],
),
@@ -120,16 +177,17 @@ class DriverCard extends StatelessWidget {
class InfoText extends StatelessWidget {
final String label;
final String value;
final dynamic value;
const InfoText(this.label, this.value, {super.key});
@override
Widget build(BuildContext context) {
final display = value?.toString() ?? 'N/A';
return Padding(
padding: const EdgeInsets.only(bottom: 4.0),
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Text(
'$label: $value',
'$label: $display',
style: CupertinoTheme.of(context).textTheme.textStyle,
),
);