Files
Siro/siro_service/lib/controller/mainController/pages/review_driver_page.dart

563 lines
19 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/controller/mainController/review_driver_controller.dart';
import 'package:siro_service/views/widgets/elevated_btn.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
class ReviewDriverPage extends StatelessWidget {
const ReviewDriverPage({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.put(ReviewDriverController());
return MyScaffold(
title: 'Review Driver'.tr,
isleading: true,
body: [
Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
_buildTabBar(controller),
Expanded(child: _buildTabBarView(controller)),
_buildBottomActions(controller, context),
],
);
}),
],
);
}
Widget _buildTabBar(ReviewDriverController c) {
final keys = c.docUrls.keys.toList();
return SizedBox(
height: 64,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
itemCount: keys.length,
itemBuilder: (context, index) {
final key = keys[index];
final isSelected = c.currentTabIndex.value == index;
return GestureDetector(
onTap: () => c.currentTabIndex.value = index,
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 3),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: isSelected
? AppColor.primaryColor
: AppColor.primaryLight,
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
c.tabIcons[key] ?? Icons.image,
size: 16,
color: isSelected ? Colors.white : AppColor.primaryColor,
),
const SizedBox(width: 4),
Text(
c.tabLabels[key] ?? key,
style: TextStyle(
fontSize: 11,
fontWeight:
isSelected ? FontWeight.bold : FontWeight.normal,
color:
isSelected ? Colors.white : AppColor.primaryColor,
),
),
],
),
),
);
},
),
);
}
Widget _buildTabBarView(ReviewDriverController c) {
return Obx(() {
final index = c.currentTabIndex.value;
final keys = c.docUrls.keys.toList();
final key = keys[index];
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Country indicator badge
Container(
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: AppColor.primaryLight,
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.public, size: 16, color: AppColor.primaryColor),
const SizedBox(width: 6),
Text(
c.country.value.isNotEmpty
? 'Country: ${c.country.value}'
: 'Detecting country...',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: AppColor.primaryColor,
),
),
],
),
),
_buildDocumentImage(c.docUrls[key]!.value),
const SizedBox(height: 16),
_buildFormFieldsForTab(c, key),
],
),
);
});
}
Widget _buildDocumentImage(String url) {
if (url.isEmpty) {
return Container(
height: 180,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.image_not_supported, size: 48, color: Colors.grey[400]),
const SizedBox(height: 8),
Text('No image available', style: TextStyle(color: Colors.grey[500])),
],
),
),
);
}
return GestureDetector(
onTap: () => _showImageFullscreen(url),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
url,
height: 200,
fit: BoxFit.contain,
width: double.infinity,
errorBuilder: (_, __, ___) => Container(
height: 180,
color: Colors.grey[200],
child: Center(
child: Text('Failed to load image',
style: TextStyle(color: Colors.grey[500])),
),
),
),
),
);
}
void _showImageFullscreen(String url) {
Get.dialog(
Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.black,
iconTheme: const IconThemeData(color: Colors.white),
),
body: Center(
child: InteractiveViewer(
child: Image.network(url, fit: BoxFit.contain),
),
),
),
);
}
Widget _buildFormFieldsForTab(ReviewDriverController c, String tabKey) {
final fields = c.getFieldsForTab(tabKey);
// Special layouts for specific tabs
if (tabKey == 'criminal_record') {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${c.country.value} - Criminal Record',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const Divider(),
Card(
color: Colors.green[50],
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.check_circle, color: Colors.green[700], size: 32),
const SizedBox(width: 12),
Expanded(
child: Text(
_getCriminalRecordHint(c.country.value),
style: TextStyle(color: Colors.green[800]),
),
),
],
),
),
),
...fields.map((f) => _buildFieldFromConfig(c, f)),
],
);
}
if (tabKey == 'profile_picture') {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Profile Photo',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const Divider(),
Card(
color: Colors.blue[50],
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.face, color: Colors.blue[700], size: 32),
const SizedBox(width: 12),
Expanded(
child: Text(
'Verify the profile photo matches the person in the ID '
'and Driver License photos above.',
style: TextStyle(color: Colors.blue[800]),
),
),
],
),
),
),
const SizedBox(height: 12),
...fields.map((f) => _buildFieldFromConfig(c, f)),
],
);
}
// Dynamic fields for all other tabs
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${c.country.value} - ${c.tabLabels[tabKey] ?? tabKey}',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const Divider(),
...fields.map((f) => _buildFieldFromConfig(c, f)),
],
);
}
String _getCriminalRecordHint(String country) {
switch (country) {
case 'Syria':
return 'Review the "لا حكم عليه" document. Verify name matches driver.';
case 'Jordan':
return 'Review the "عدم محكومية" document. Verify name matches driver.';
case 'Egypt':
return 'Review the "فيش وتشبيه" document. Verify name matches driver.';
default:
return 'Review the criminal record document. Verify name matches driver.';
}
}
Widget _buildFieldFromConfig(ReviewDriverController c, List<dynamic> fieldDef) {
if (fieldDef.length < 6) return const SizedBox();
final key = fieldDef[0] as String;
final label = fieldDef[1] as String;
final isDate = fieldDef[2] as bool;
final isGender = fieldDef[3] as bool;
final isColor = fieldDef[4] as bool;
final isFuel = fieldDef[5] as bool;
if (isGender) return _buildGenderDropdown(c, label);
if (isColor) return _buildColorDropdown(c, label);
if (isFuel) return _buildFuelDropdown(c, label);
final controller = _getController(c, key);
if (controller == null) return const SizedBox();
if (isDate) {
return _buildTextField(
label: label,
controller: controller,
icon: Icons.event,
onTap: () => c.selectDate(Get.context!, controller),
);
}
// Determine icon based on common field names
final icon = _getFieldIcon(key, label);
return _buildTextField(
label: label,
controller: controller,
icon: icon,
);
}
IconData? _getFieldIcon(String key, String label) {
final l = label.toLowerCase();
if (l.contains('national')) return Icons.fingerprint;
if (l.contains('phone')) return Icons.phone;
if (l.contains('email')) return Icons.email;
if (l.contains('address')) return Icons.location_on;
if (l.contains('birth')) return Icons.cake;
if (l.contains('father') || l.contains('mother')) return Icons.people;
if (l.contains('license') || l.contains('category')) return Icons.card_membership;
if (l.contains('plate')) return Icons.confirmation_number;
if (l.contains('owner')) return Icons.person_search;
if (l.contains('make') || l.contains('model')) return Icons.directions_car;
if (l.contains('year')) return Icons.calendar_today;
if (l.contains('vin')) return Icons.confirmation_number;
if (l.contains('governorate') || l.contains('place')) return Icons.location_city;
if (l.contains('blood')) return Icons.water_drop;
if (l.contains('spouse') || l.contains('marital')) return Icons.people;
if (l.contains('occupation')) return Icons.work;
if (l.contains('restriction')) return Icons.block;
if (l.contains('engine') || l.contains('capacity')) return Icons.settings;
if (l.contains('first name') || l.contains('last name')) return Icons.person;
return null;
}
TextEditingController? _getController(ReviewDriverController c, String key) {
switch (key) {
case 'firstNameController': return c.firstNameController;
case 'lastNameController': return c.lastNameController;
case 'phoneController': return c.phoneController;
case 'emailController': return c.emailController;
case 'siteController': return c.siteController;
case 'nationalNumberController': return c.nationalNumberController;
case 'birthdateController': return c.birthdateController;
case 'addressController': return c.addressController;
case 'licenseCategoriesController': return c.licenseCategoriesController;
case 'licenseTypeController': return c.licenseTypeController;
case 'expiryDateController': return c.expiryDateController;
case 'licenseIssueDateController': return c.licenseIssueDateController;
case 'ownerController': return c.ownerController;
case 'carPlateController': return c.carPlateController;
case 'vinController': return c.vinController;
case 'carLicenseExpiryDateController': return c.carLicenseExpiryDateController;
case 'makeController': return c.makeController;
case 'modelController': return c.modelController;
case 'yearController': return c.yearController;
case 'fatherNameController': return c.fatherNameController;
case 'motherNameController': return c.motherNameController;
case 'birthPlaceController': return c.birthPlaceController;
case 'bloodTypeController': return c.bloodTypeController;
case 'maritalStatusController': return c.maritalStatusController;
case 'spouseNameController': return c.spouseNameController;
case 'idIssueDateController': return c.idIssueDateController;
case 'idExpiryDateController': return c.idExpiryDateController;
case 'licenseNumberController': return c.licenseNumberController;
case 'licenseCategoryController': return c.licenseCategoryController;
case 'restrictionsController': return c.restrictionsController;
case 'governorateController': return c.governorateController;
case 'occupationController': return c.occupationController;
case 'religionController': return c.religionController;
case 'engineCapacityController': return c.engineCapacityController;
case 'passengerCapacityController': return c.passengerCapacityController;
default: return null;
}
}
Widget _buildTextField({
required String label,
required TextEditingController controller,
IconData? icon,
TextInputType keyboardType = TextInputType.text,
VoidCallback? onTap,
String? hint,
int maxLines = 1,
}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: TextFormField(
controller: controller,
keyboardType: keyboardType,
maxLines: maxLines,
readOnly: onTap != null,
onTap: onTap,
decoration: InputDecoration(
labelText: label.tr,
hintText: hint,
prefixIcon: icon != null
? Icon(icon, color: AppColor.secondaryColor)
: null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
),
),
);
}
Widget _buildGenderDropdown(ReviewDriverController c, String label) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Obx(
() => DropdownButtonFormField<String>(
value: c.selectedGender.value.isEmpty
? null
: c.selectedGender.value,
isExpanded: true,
decoration: InputDecoration(
labelText: label.tr,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
),
items: ['Male', 'Female'].map((v) {
return DropdownMenuItem(value: v, child: Text(v.tr));
}).toList(),
onChanged: (v) {
if (v != null) c.selectedGender.value = v;
},
),
),
);
}
Widget _buildColorDropdown(ReviewDriverController c, String label) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Obx(
() => DropdownButtonFormField<String>(
value: c.colorHex.value.isEmpty ? null : c.colorHex.value,
isExpanded: true,
decoration: InputDecoration(
labelText: label.tr,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
),
items: ReviewDriverController.kCarColorOptions.map((opt) {
return DropdownMenuItem<String>(
value: opt['hex'],
child: Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: c.hexToColor(opt['hex']!),
shape: BoxShape.circle,
border: Border.all(color: Colors.black12),
),
),
const SizedBox(width: 10),
Text(opt['key']!.tr),
],
),
);
}).toList(),
onChanged: (hex) {
if (hex != null) c.updateColorSelection(hex);
},
),
),
);
}
Widget _buildFuelDropdown(ReviewDriverController c, String label) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Obx(
() => DropdownButtonFormField<String>(
value: ReviewDriverController.kFuelOptions
.contains(c.selectedFuel.value)
? c.selectedFuel.value
: null,
isExpanded: true,
decoration: InputDecoration(
labelText: label.tr,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
),
items: ReviewDriverController.kFuelOptions.map((v) {
return DropdownMenuItem(value: v, child: Text(v));
}).toList(),
onChanged: (v) {
if (v != null) c.selectedFuel.value = v;
},
),
),
);
}
Widget _buildBottomActions(ReviewDriverController c, BuildContext context) {
return Obx(() {
final saving = c.isSaving.value;
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, -4),
),
],
),
child: Row(
children: [
Expanded(
child: MyElevatedButton(
title: 'Save'.tr,
onPressed: () { c.saveChanges(); },
kolor: AppColor.blueColor,
loading: saving,
),
),
const SizedBox(width: 8),
Expanded(
child: MyElevatedButton(
title: 'Activate'.tr,
onPressed: () { c.activateDriver(); },
kolor: AppColor.greenColor,
loading: saving,
),
),
const SizedBox(width: 8),
Expanded(
child: MyElevatedButton(
title: 'Reject'.tr,
onPressed: () { c.showRejectDialog(); },
kolor: AppColor.redColor,
loading: saving,
),
),
],
),
);
});
}
}