import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:supplier_new/resources/resources_drivers.dart'; import '../common/settings.dart'; class DriverDetailsPage extends StatefulWidget { var driverDetails; var status; DriverDetailsPage({this.driverDetails, this.status}); @override State createState() => _DriverDetailsPageState(); } class _DriverDetailsPageState extends State { // tweak if you want a different size/offset double _avSize = 80; // avatar diameter double _cardHPad = 16; // horizontal padding double _cardTPad = 16; // top inner padding double _avatarOverlap = 28; // how much avatar rises above the card final _nameCtrl = TextEditingController(); final _mobileCtrl = TextEditingController(); final _altMobileCtrl = TextEditingController(); final _locationCtrl = TextEditingController(); final GlobalKey _formKey = GlobalKey(); // Unused in UI but kept if you later need them final _commissionCtrl = TextEditingController(); final _joinDateCtrl = TextEditingController(); bool isLoading=false; // Dropdown state String? _status; // 'available' | 'on delivery' | 'offline' final List _statusOptions = const [ 'available', 'on delivery', 'offline' ]; String? selectedLicense; final List licenseNumbers = const [ 'DL-042019-9876543', 'DL-052020-1234567', 'DL-072021-7654321', ]; String? selectedExperience; // years as string final List yearOptions = List.generate(41, (i) => '$i'); // 0..40 String? _required(String? v, {String field = "This field"}) { if (v == null || v.trim().isEmpty) return "$field is required"; return null; } String? _validatePhone(String? v, {String label = "Phone"}) { if (v == null || v.trim().isEmpty) return "$label is required"; if (v.trim().length != 10) return "$label must be 10 digits"; return null; } String? fitToOption(String? incoming, List options) { if (incoming == null) return null; final inc = incoming.trim(); if (inc.isEmpty) return null; final match = options.firstWhere( (o) => o.toLowerCase() == inc.toLowerCase(), orElse: () => '', ); return match.isEmpty ? null : match; // return the exact option string } Future _refreshDriverDetails() async { try { setState(() => isLoading = true); final updatedDetails = await AppSettings.getDriverDetailsByPhone( _mobileCtrl.text.trim(), ); if (updatedDetails != null) { setState(() { widget.driverDetails = updatedDetails; }); } else { AppSettings.longFailedToast("Failed to fetch updated driver details"); } } catch (e) { debugPrint("⚠️ Error refreshing driver details: $e"); AppSettings.longFailedToast("Error refreshing driver details"); } finally { setState(() => isLoading = false); } } Future _updateDriver() async { // run all validators, including the dropdowns final ok = _formKey.currentState?.validate() ?? false; if (!ok) { setState(() {}); // ensure error texts render return; } // Build payload (adjust keys to your API if needed) final payload = { "name": _nameCtrl.text.trim(), "license_number": selectedLicense ?? "", "address": _locationCtrl.text.trim().isEmpty ? AppSettings.userAddress : _locationCtrl.text.trim(), "supplier_name": AppSettings.userName, "phone": _mobileCtrl.text.trim(), "alternativeContactNumber": _altMobileCtrl.text.trim(), "years_of_experience": selectedExperience ?? "", "status": _status ?? "available", }; try { final bool created = await AppSettings.updateDrivers(payload,_mobileCtrl.text); if (!mounted) return; if (created) { AppSettings.longSuccessToast("Driver Updated successfully"); Navigator.pop(context, true); // close sheet _refreshDriverDetails(); } else { Navigator.pop(context, true); AppSettings.longFailedToast("failed to update driver details"); } } catch (e) { debugPrint("⚠️ addDrivers error: $e"); if (!mounted) return; AppSettings.longFailedToast("Something went wrong"); } } Future _openDriverFormSheet(BuildContext context) async { _nameCtrl.text = widget.driverDetails.driver_name ?? ''; _mobileCtrl.text = widget.driverDetails.phone_number ?? ''; _altMobileCtrl.text = widget.driverDetails.alt_phone_number ?? ''; _locationCtrl.text = widget.driverDetails.address ?? ''; selectedExperience = fitToOption(widget.driverDetails.years_of_experience, yearOptions); selectedLicense = fitToOption(widget.driverDetails.license_number, licenseNumbers); _status = fitToOption(widget.driverDetails.status, _statusOptions); await showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, // style inner container builder: (context) { final bottomInset = MediaQuery.of(context).viewInsets.bottom; return FractionallySizedBox( heightFactor: 0.75, // fixed height (75% of screen) child: Container( decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: Padding( padding: EdgeInsets.fromLTRB(20, 16, 20, 20 + bottomInset), child: Form( key: _formKey, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Handle + close (optional, simple close icon) Row( children: [ Expanded( child: Center( child: Container( width: 86, height: 4, margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: const Color(0xFFE0E0E0), borderRadius: BorderRadius.circular(2), ), ), ), ), ], ), _LabeledField( label: "Driver Name *", child: TextFormField( controller: _nameCtrl, validator: (v) => _required(v, field: "Driver Name"), textCapitalization: TextCapitalization.none, inputFormatters: const [ FirstCharUppercaseFormatter(), // << live first-letter caps ], decoration: InputDecoration( hintText: "Full Name", hintStyle: fontTextStyle( 14, const Color(0xFF939495), FontWeight.w400), border: const OutlineInputBorder(), isDense: true, ), textInputAction: TextInputAction.next, ), ), _LabeledField( label: "Driver License Number *", child: DropdownButtonFormField( value: selectedLicense, items: licenseNumbers .map((t) => DropdownMenuItem(value: t, child: Text(t))) .toList(), onChanged: (v) => setState(() => selectedLicense = v), validator: (v) => v == null || v.isEmpty ? "Driver License required" : null, isExpanded: true, alignment: Alignment.centerLeft, hint: Text( "Select License Number", style: fontTextStyle( 14, const Color(0xFF939495), FontWeight.w400), ), icon: Image.asset('images/downarrow.png', width: 16, height: 16), decoration: const InputDecoration( border: OutlineInputBorder(), isDense: false, contentPadding: EdgeInsets.symmetric( horizontal: 12, vertical: 14), ), ), ), _LabeledField( label: "Years of Experience *", child: DropdownButtonFormField( value: selectedExperience, items: yearOptions .map((t) => DropdownMenuItem(value: t, child: Text(t))) .toList(), onChanged: (v) => setState(() => selectedExperience = v), validator: (v) => v == null || v.isEmpty ? "Experience is required" : null, isExpanded: true, alignment: Alignment.centerLeft, hint: Text( "Years", style: fontTextStyle( 14, const Color(0xFF939495), FontWeight.w400), ), icon: Image.asset('images/downarrow.png', width: 16, height: 16), decoration: const InputDecoration( border: OutlineInputBorder(), isDense: false, contentPadding: EdgeInsets.symmetric( horizontal: 12, vertical: 14), ), ), ), _LabeledField( label: "Phone Number *", child: TextFormField( controller: _mobileCtrl, validator: (v) => _validatePhone(v, label: "Phone Number"), keyboardType: TextInputType.phone, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(10), ], decoration: InputDecoration( hintText: "Mobile Number", hintStyle: fontTextStyle( 14, const Color(0xFF939495), FontWeight.w400), border: const OutlineInputBorder(), isDense: true, ), textInputAction: TextInputAction.next, ), ), _LabeledField( label: "Alternate Phone Number", child: TextFormField( controller: _altMobileCtrl, validator: (v) { if (v == null || v.trim().isEmpty) return null; // optional return _validatePhone(v, label: "Alternate Phone Number"); }, keyboardType: TextInputType.phone, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(10), ], decoration: InputDecoration( hintText: "Mobile Number", hintStyle: fontTextStyle( 14, const Color(0xFF939495), FontWeight.w400), border: const OutlineInputBorder(), isDense: true, ), textInputAction: TextInputAction.next, ), ), _LabeledField( label: "Location *", child: TextFormField( controller: _locationCtrl, validator: (v) => _required(v, field: "Location"), textCapitalization: TextCapitalization.none, inputFormatters: const [ FirstCharUppercaseFormatter(), // << live first-letter caps ], decoration: InputDecoration( hintText: "Area / locality", hintStyle: fontTextStyle( 14, const Color(0xFF939495), FontWeight.w400), border: const OutlineInputBorder(), isDense: true, ), textInputAction: TextInputAction.done, ), ), _LabeledField( label: "Status *", child: DropdownButtonFormField( value: _status, items: _statusOptions .map((s) => DropdownMenuItem(value: s, child: Text(s))) .toList(), onChanged: (v) => setState(() => _status = v), validator: (v) => v == null || v.isEmpty ? "Status is required" : null, isExpanded: true, alignment: Alignment.centerLeft, hint: Text( "Select status", style: fontTextStyle( 14, const Color(0xFF939495), FontWeight.w400), ), icon: const Icon(Icons.keyboard_arrow_down_rounded), decoration: const InputDecoration( border: OutlineInputBorder(), isDense: false, contentPadding: EdgeInsets.symmetric( horizontal: 12, vertical: 14), ), ), ), const SizedBox(height: 20), SizedBox( width: double.infinity, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF8270DB), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(24), ), ), onPressed: (){ _updateDriver(); }, child: Text( "Update", style: fontTextStyle( 14, Colors.white, FontWeight.w600), ), ), ), ], ), ), ), ), ), ); }, ); } showDeleteDriverDialog(BuildContext context) async { return showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return AlertDialog( backgroundColor: Color(0XFFFFFFFF),// Set your desired background color shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), // Optional: Rounded corners ), title: Center( child: Text('Delete Driver?' ,style: fontTextStyle(16,Color(0XFF3B3B3B),FontWeight.w600),), ), content: SingleChildScrollView( child: ListBody( children: [ Container( child: Text('Do u want to delete "${widget.driverDetails.driver_name}"',style: fontTextStyle(14,Color(0XFF101214),FontWeight.w600),), ), ], ), ), actions: [ Center( child: Row( mainAxisSize: MainAxisSize.min, children: [ Expanded(child: GestureDetector( onTap: (){ Navigator.pop(context); }, child: Container( decoration: BoxDecoration( shape: BoxShape.rectangle, color: Color(0XFFFFFFFF), border: Border.all( width: 1, color: Color(0XFF1D7AFC)), borderRadius: BorderRadius.circular( 12, )), alignment: Alignment.center, child: Visibility( visible: true, child: Padding( padding: EdgeInsets.fromLTRB(16,12,16,12), child: Text('Cancel', style: fontTextStyle(12, Color(0XFF1D7AFC), FontWeight.w600)), ), ), ), ),), SizedBox(width:MediaQuery.of(context).size.width * .016,), Expanded(child: GestureDetector( onTap: ()async{ bool status = await AppSettings.deleteDriver(widget.driverDetails.phone_number,); if(status){ AppSettings.longSuccessToast('Driver deleted successfully'); Navigator.of(context).pop(true); Navigator.of(context).pop(true); } else{ Navigator.of(context).pop(true); AppSettings.longFailedToast('Failed to delete driver'); } }, child: Container( decoration: BoxDecoration( shape: BoxShape.rectangle, color: Color(0XFFE2483D), border: Border.all( width: 1, color: Color(0XFFE2483D)), borderRadius: BorderRadius.circular( 12, )), alignment: Alignment.center, child: Visibility( visible: true, child: Padding( padding: EdgeInsets.fromLTRB(16,12,16,12), child: Text( 'Delete', style: fontTextStyle( 12, Color(0XFFFFFFFF), FontWeight.w600)), ), ), ) ),) ], ), ), ], ); }); }, ); } @override Widget build(BuildContext context) { final statusColor = switch (widget.driverDetails.status) { 'available' => const Color(0xFF0A9E04), 'on delivery' => const Color(0xFFD0AE3C), 'offline' => const Color(0xFF939495), _ => Colors.grey, }; return WillPopScope( onWillPop: () async { Navigator.pop(context, true); return false; // prevent default pop since we manually handled it }, child: Scaffold( backgroundColor: Color(0XFFFFFFFF), appBar: AppSettings.supplierAppBarWithActionsText( widget.driverDetails.driver_name.isNotEmpty ? widget.driverDetails.driver_name[0].toUpperCase() + widget.driverDetails.driver_name.substring(1) : '', context), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height:MediaQuery.of(context).size.height * .1,), // 👤 Driver Profile Card Stack( clipBehavior: Clip.none, children: [ // Main card (give extra top padding so text starts below the avatar) Container( padding: EdgeInsets.fromLTRB( _cardHPad, _cardTPad + _avSize - _avatarOverlap, // space for avatar _cardHPad, 16, ), decoration: const BoxDecoration( color: Color(0xFFF3F1FB), borderRadius: BorderRadius.only( topLeft: Radius.circular(24), topRight: Radius.circular(24), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // status chip (just under avatar, aligned left) Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded(child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all(color: statusColor), ), child: Text(widget.driverDetails.status, style: fontTextStyle( 10, statusColor, FontWeight.w400)), ), const SizedBox(height: 6), Text( widget.driverDetails.driver_name, style: fontTextStyle( 20, Color(0XFF2D2E30), FontWeight.w500), ), const SizedBox(height: 2), Text( "+91 "+widget.driverDetails.phone_number, style: fontTextStyle( 12, Color(0XFF646566), FontWeight.w400), ), ], ),), PopupMenuButton( // 🔁 Use `child:` so you can place any widget (your 3-dots image) child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0), child: Image.asset( 'images/popup_menu.png', // your 3-dots image width: 22, height: 22, // If you want to tint it like an icon: color: Color(0XFF939495), // remove if you want original colors colorBlendMode: BlendMode.srcIn, ), ), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), offset: const Offset(0, 40), color: Colors.white, elevation: 4, onSelected: (value) { if (value == 'edit') { _openDriverFormSheet(context); } else if (value == 'disable') { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Disable selected')), ); } else if (value == 'delete') { showDeleteDriverDialog(context); } }, itemBuilder: (context) => [ PopupMenuItem( value: 'edit', child: Row( children: [ Image.asset( 'images/edit.png', width: 20, height: 20, color: Color(0XFF646566), // tint (optional) colorBlendMode: BlendMode.srcIn, ), const SizedBox(width: 12), Text( 'Edit', style: fontTextStyle(14, const Color(0XFF646566), FontWeight.w400), ), ], ), ), PopupMenuItem( value: 'disable', child: Row( children: [ Image.asset( 'images/disable.png', width: 20, height: 20, color: Color(0XFF646566), // tint (optional) colorBlendMode: BlendMode.srcIn, ), const SizedBox(width: 12), Text( 'Disable', style: fontTextStyle(14, const Color(0XFF646566), FontWeight.w400), ), ], ), ), PopupMenuItem( value: 'delete', child: Row( children: [ Image.asset( 'images/delete.png', width: 20, height: 20, color: Color(0XFFE2483D), // red like your example colorBlendMode: BlendMode.srcIn, ), const SizedBox(width: 12), Text( 'Delete', style: fontTextStyle(14, const Color(0xFFE2483D), FontWeight.w400), ), ], ), ), ], ) ], ), const SizedBox(height: 8), // buttons row OutlinedButton( style: OutlinedButton.styleFrom( foregroundColor: Color(0XFF515253), backgroundColor: Color(0xFFF3F1FB), side: const BorderSide( color: Color(0xFF939495), width: 0.5, ), padding: EdgeInsets.symmetric(vertical: 10), // uniform height ), onPressed: () { }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( "Change Status", style: fontTextStyle( 14, const Color(0XFF515253), FontWeight.w500), ), ], ), ), const SizedBox(height: 24), // 🪪 License Card ClipRRect( borderRadius: BorderRadius.circular(12), child: Container( width: double.infinity, decoration: const BoxDecoration( color: Colors.black, ), child: Stack( children: [ // background pattern /*Image.asset( 'images/license_bg.png', fit: BoxFit.cover, width: double.infinity, height: 140, ),*/ Container( height: 140, width: double.infinity, padding: const EdgeInsets.all(16), color: Colors.black.withOpacity(0.3), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "DRIVING LICENSE", style: fontTextStyle(10, const Color(0xFFFFFFFF), FontWeight.w400), ), Spacer(), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.driverDetails.driver_name, style: fontTextStyle(12, const Color(0xFFFFFFFF), FontWeight.w500), ), Text( widget.driverDetails.license_number, style: fontTextStyle(12, const Color(0xFFFFFFFF), FontWeight.w500), ), ], ), Align( alignment: Alignment.bottomRight, child: Text( "Expires on 29/02/2028", style: fontTextStyle(10, const Color(0xFFFFFFFF), FontWeight.w300), ), ), ], ) ], ), ), ], ), ), ), ], ), ), // Floating avatar (top-left, overlapping the card) Positioned( top: -_avatarOverlap, left: _cardHPad, child: ClipRRect( borderRadius: BorderRadius.circular(_avSize / 2), child: Image.asset( 'images/avatar.png', width: _avSize, height: _avSize, fit: BoxFit.cover, ), ), ), // Call icon aligned with the card’s top-right /*Positioned( top: _cardTPad - _avatarOverlap + 4, // aligns near chip row right: _cardHPad - 4, child: IconButton( onPressed: () { // 📞 Call action }, icon: const Icon(Icons.call, color: Color(0xFF4F46E5)), ), ),*/ ], ), const SizedBox(height: 24), Padding(padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "RECENT TRIPS", style:fontTextStyle(10, const Color(0xFF343637), FontWeight.w600), ), const SizedBox(height: 12), _buildTripCard("Drinking Water - 10,000 L", "7:02 PM, 28 Jun 2025"), const SizedBox(height: 8), _buildTripCard("Drinking Water - 10,000 L", "7:02 PM, 28 Jun 2025"), const SizedBox(height: 8), _buildTripCard("Drinking Water - 10,000 L", "7:02 PM, 28 Jun 2025"), ], ),) ], ), ), ), )); } Widget _buildTripCard(String title, String time) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFFFFFFFF), borderRadius: BorderRadius.circular(8), border: Border.all(color: Color(0XFFC3C4C4)), ), child: Row( children: [ Image.asset('images/recent_trips.png', width: 28, height: 28), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style:fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500), ), const SizedBox(height: 2), Text( time, style:fontTextStyle(10, const Color(0xFF939495), FontWeight.w400), ), ], ), ), ], ), ); } } class _LabeledField extends StatelessWidget { final String label; final Widget child; const _LabeledField({required this.label, required this.child}); String _capFirstWord(String input) { if (input.isEmpty) return input; final i = input.indexOf(RegExp(r'\S')); if (i == -1) return input; return input.replaceRange(i, i + 1, input[i].toUpperCase()); } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: 14.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _capFirstWord(label), style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w600), ), const SizedBox(height: 6), child, ], ), ); } }