import 'dart:io' as html; import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import '../../../services/apiService.dart'; import '../../../utils/auth_utils.dart'; import '../../../widgets/custom_user_form.dart'; class OfficeDetails extends StatefulWidget { final Map controllers; final Map errorMessages; final bool isDesktop; final bool isViewMode; final String? userIdApi; final ValueChanged? onLevelChanged; final ValueChanged? onDepartmentChanged; final ValueChanged? onFirstApproverChanged; final ValueChanged? onSecondApproverChanged; final ValueChanged? onThirdApproverChanged; final ValueChanged? onFirstSubsApproverChanged; final ValueChanged? onExceptionalApproverChanged; final String? selectedLevel; final String? selectedDepartment; final String? selectedFirstApprover; final String? selectedSecondApprover; final String? selectedThirdApprover; final String? selectedSubstituteApprover; final String? selectedExceptionalApprover; const OfficeDetails({ Key? key, this.selectedLevel, this.selectedDepartment, this.selectedFirstApprover, this.selectedSecondApprover, this.selectedThirdApprover, required this.controllers, required this.errorMessages, required this.isDesktop, required this.isViewMode, this.onLevelChanged, this.onDepartmentChanged, this.onFirstApproverChanged, this.onSecondApproverChanged, this.onThirdApproverChanged, this.selectedSubstituteApprover, this.selectedExceptionalApprover, this.onExceptionalApproverChanged, this.onFirstSubsApproverChanged, this.userIdApi, }) : super(key: key); @override OfficeDetailsState createState() => OfficeDetailsState(); } class OfficeDetailsState extends State { final ApiService apiService = ApiService(); late Map countryMap; // Mapping country_code -> country_name late List countryCodes; // List of country codes late List? apiCountryData; bool isResetTrue = false; late List? apiCostData; late List? apiRoleData; late List? apiUserData; late List? apiAllGroups; Map? apiselectedUser; late List userList; late List groupList; late Future> futureUsers; List? resolvedUserData; // String? userIdsApi; late List userIdsApi; late List groupIdsApi; late Map userMap; late Map groupMap; String? selectedTab; String? selectedCountry; String? selectedGender; String? selectedUserType; String? selectedRole; String? selectedLevel; String? selectedDepartment; String? selectedFirstApprover; String? selectedSecondApprover; String? selectedThirdApprover; String? selectedSubstituteApprover; String? selectedExceptionalApprover; String? selectedFileNames; Uint8List? passportDocumentBytes; String? passportFileUrlFromApi; String? base64PDF; DateTime? _selectedCheckOutDate; DateTime? _selectedEndDate; html.File? passportFile; final FocusNode _employeeCodeFocusNode = FocusNode(); final FocusNode _departmentFocusNode = FocusNode(); final FocusNode _groupFocusNode = FocusNode(); final FocusNode _firstApprovalFocusNode = FocusNode(); final FocusNode _secondApprovalFocusNode = FocusNode(); final FocusNode _thirdApprovalFocusNode = FocusNode(); final FocusNode _exceptionalApprovalFocusNode = FocusNode(); final FocusNode _delegateFocusNode = FocusNode(); final FocusNode _startDateFocusNode = FocusNode(); final FocusNode _endDateFocusNode = FocusNode(); bool _employeeCodeFocus = false; bool _departmentFocus = false; bool _groupFocus = false; bool _firstApprovalFocus = false; bool _secondApprovalFocus = false; bool _thirdApprovalFocus = false; bool _exceptionalApprovalFocus = false; bool _delegateFocus = false; bool _startDateFocus = false; bool _endDateFocus = false; List dataHeader = [ "Fname", "Lname", "email", "password", "dob", "gender", "mobileNumber", "alternateMobile", "address", "postalCode", "country", "passportNumber", "placeOfIssue", "passportDoc", "userType", "roleId", "deptId", "levelId", "firstApproval", "secondApproval", "thirdApproval", "exceptional_approver", "employeeCode", "dateOfIssue", "dateOfExpiry", "changePassword", ]; // Color? layoutColor; Color layoutColor = Colors.grey; Color? bodyColor; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { loadInitialData(); _employeeCodeFocusNode.requestFocus(); }); apiCountryData = null; apiUserData = null; apiCostData = null; apiRoleData = null; apiAllGroups = null; apiCountryData = null; selectedLevel = widget.selectedLevel; selectedDepartment = widget.selectedDepartment; selectedFirstApprover = widget.selectedFirstApprover; selectedSecondApprover = widget.selectedSecondApprover; selectedThirdApprover = widget.selectedThirdApprover; selectedSubstituteApprover = widget.selectedSubstituteApprover; selectedExceptionalApprover = widget.selectedExceptionalApprover; fetchDepartment(); fetchUsers(); fetchFindGroup(); _addFocusListener( _employeeCodeFocusNode, (focus) => _employeeCodeFocus = focus, ); _addFocusListener( _departmentFocusNode, (focus) => _departmentFocus = focus, ); _addFocusListener(_groupFocusNode, (focus) => _groupFocus = focus); _addFocusListener( _firstApprovalFocusNode, (focus) => _firstApprovalFocus = focus, ); _addFocusListener( _secondApprovalFocusNode, (focus) => _secondApprovalFocus = focus, ); _addFocusListener( _thirdApprovalFocusNode, (focus) => _thirdApprovalFocus = focus, ); _addFocusListener( _exceptionalApprovalFocusNode, (focus) => _exceptionalApprovalFocus = focus, ); _addFocusListener(_delegateFocusNode, (focus) => _delegateFocus = focus); _addFocusListener(_startDateFocusNode, (focus) => _startDateFocus = focus); _addFocusListener(_endDateFocusNode, (focus) => _endDateFocus = focus); } void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { updateState(node.hasFocus); }); }); } void loadInitialData() async { String? layoutString = await getLayoutColor(); String? bodyStringColor = await getBodyColor(); setState(() { layoutColor = layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; bodyColor = bodyStringColor != null ? Color(int.parse(bodyStringColor)) : Colors.white; }); } Future apiCheckDuplicate( String label, String field, String value, String? userId, ) async { try { // Basic validation: Check mobile number length final response = await apiService.CheckDuplicate( context, label, field, value, userId ?? "", ); if (response.isNotEmpty) { _clearError(field); widget.errorMessages[field] = response['message'] ?? "$label Already Exists"; print("Duplicate found: ${response['message']}"); return; } else { _clearError(field); print("No duplicates found."); } } catch (e) { print("Error in checkDuplicate: $e"); } } void _clearError(String field) { setState(() { widget.errorMessages.remove(field); }); } // void handleReset() { // setState(() { // selectedSubstituteApprover = ""; // widget.controllers["delegationEndDate"]?.text = ""; // widget.controllers["delegationStartDate"]?.text = ""; // }); // } void handleReset() { setState(() { selectedSubstituteApprover = null; widget.onFirstSubsApproverChanged?.call(null); // Notify parent if needed // Clear date selections and fields _selectedCheckOutDate = null; _selectedEndDate = null; widget.controllers["delegationStartDate"]?.text = ""; widget.controllers["delegationEndDate"]?.text = ""; // selectedSubstituteApprover = null; // widget.onFirstSubsApproverChanged?.call(null); // widget.controllers["delegationStartDate"]?.clear(); // widget.controllers["delegationEndDate"]?.clear(); // // print("Start Date: ${widget.controllers["delegationStartDate"]?.text}"); // print("End Date: ${widget.controllers["delegationEndDate"]?.text}"); }); } Future fetchUsers() async { try { print("Test Users"); List users = await apiService.fetchUsers(context); setState(() { // apiUserData = users; print("Total users fetched from API: ${users.length}"); // user["role_id"] != "5" - Travel Agent apiUserData = users.where((user) => user["role_id"] != "5").toList(); print("Total users fetched from API1: ${apiUserData?.length}"); print("APIUSerDATa - $apiUserData"); userList = apiUserData ?? []; userMap = { for (var user in userList) user['user_id'].toString(): "${user['first_name']} ${user['last_name']}", }; userIdsApi = userMap.keys.toList(); }); } catch (e) { print('Error fetching country list: $e'); } } Future fetchDepartment() async { try { print("Test department"); List department = await apiService.fetchDepartmentCostCenter( context, ); setState(() { apiCostData = department; }); print("Test department Completeed- $apiCostData"); } catch (e) { print('Error fetching department list: $e'); } } Future fetchFindGroup() async { try { print("Test Groups"); final result = await apiService.fetchAllGroup(context); setState(() { apiAllGroups = result; groupList = apiAllGroups ?? []; // groupMap = { // for (var group in groupList) // group['group_id'].toString(): "${group['name']}" // }; groupMap = { for (var group in groupList) group['group_id'].toString(): group['name'].toString().trim(), }; userIdsApi = groupMap.keys.toList(); }); print("Fetched Groupss: $apiAllGroups"); } catch (e) { print('Error fetching role list: $e'); } } @override Widget build(BuildContext context) { return Container( height: MediaQuery.of(context).size.height * 0.8, decoration: BoxDecoration( border: Border( top: BorderSide( color: Colors.blueGrey.shade300, // Set your desired color width: 0.09, // Set border thickness ), ), ), child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 20), _buildFirstRow(widget.isDesktop), if (widget.isDesktop) SizedBox(height: 10), _buildSecondRow(widget.isDesktop), if (widget.isDesktop) SizedBox(height: 10), _buildThirdRow(widget.isDesktop), if (widget.isDesktop) SizedBox(height: 10), Divider(thickness: 0.2, color: Colors.blueGrey.shade100), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Delegation", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, ), ), ], ), SizedBox(height: 15), _buildFourthRow(widget.isDesktop), ], ), ), ); } Widget _buildFirstRow(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildEmpCodeField(), Spacer(), buildDepartmentField(), Spacer(), // Space after Last Name buildGroupField(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildEmpCodeField(), SizedBox(height: 8), // Vertical space buildDepartmentField(), SizedBox(height: 8), buildGroupField(), ], ), ); } Widget _buildSecondRow(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildApprover1(isDesktop), Spacer(), buildApprover2(isDesktop), Spacer(), // Space after Last Name buildApprover3(isDesktop), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildApprover1(isDesktop), SizedBox(height: 8), // Vertical space buildApprover2(isDesktop), SizedBox(height: 8), buildApprover3(isDesktop), ], ), ); } Widget _buildThirdRow(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [buildExceptionApprover(isDesktop), Spacer()], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildExceptionApprover(isDesktop), SizedBox(height: 8), // Vertical space ], ), ); } Widget _buildFourthRow(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildApproverSubstitute1(isDesktop), Spacer(), buildDelegationStartDateField(isDesktop), Spacer(), // Space after Last Name buildDelegationEndDateField(isDesktop), SizedBox(width: 16), buildReset(isDesktop), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildApproverSubstitute1(isDesktop), SizedBox(height: 8), // Vertical space buildDelegationStartDateField(isDesktop), SizedBox(height: 8), buildDelegationEndDateField(isDesktop), buildReset(isDesktop), ], ), ); } Widget buildEmpCodeField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Employee Code *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldUserWrapper( isFocused: _employeeCodeFocus, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')), ], focusNode: _employeeCodeFocusNode, controller: widget.controllers["employeeCode"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("employee_code"); final updaterUserId = widget.userIdApi ?? ""; apiCheckDuplicate( "Employee Code", "employee_code", value, updaterUserId, ); }, decoration: InputDecoration( labelText: "Employee Code", labelStyle: GoogleFonts.poppins( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (widget.errorMessages["employee_code"] != null) ...[ SizedBox(height: 5), // Space before error message Text( widget.errorMessages["employee_code"]!, style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], ); } // Widget buildDepartmentFieldOld() { // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text( // "Department", // style: GoogleFonts.poppins( // fontSize: 12, // fontWeight: FontWeight.w600, // color: Color(0xFF575A74), // ), // ), // SizedBox(height: 5), // CustomTextFieldUserWrapper( // isFocused: false, // Dropdown doesn't use focus // isDesktop: widget.isDesktop, // child: SizedBox( // height: 45, // Set appropriate height // child: DropdownButtonFormField( // value: selectedDepartment, // // value: widget.isViewMode ? null : selectedDepartment, // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), // decoration: InputDecoration( // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric( // horizontal: 10, // ), // Proper padding // ), // onChanged: // widget.isViewMode // ? null // : (newValue) { // setState(() { // selectedDepartment = newValue; // }); // widget.onDepartmentChanged?.call(newValue); // }, // // items: // apiCostData?.map>((item) { // return DropdownMenuItem( // // value: item['department_id'], // ID as value // child: Text(item['name'] ?? "Unknown"), // ); // }).toList(), // hint: Text("Select"), // disabledHint: Text( // selectedDepartment ?? "Select Department", // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), // ), // ), // ), // ), // ], // ); // } Widget buildDepartmentField() { // Map department_id to department_name Map departmentMap = { for (var item in apiCostData ?? []) if (item['dropdown_key'] != null && item['dropdown_value'] != null && int.tryParse(item['dropdown_key'].toString()) != 5) item['dropdown_key'].toString(): item['dropdown_value'].toString(), // item['department_id'] as String: item['name'] as String, }; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Department", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldUserWrapper( isFocused: _departmentFocus, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: widget.isDesktop, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: _departmentFocusNode, onFocusChange: (hasFocus) { setState(() { _departmentFocus = hasFocus; }); }, child: GestureDetector( onTap: () { // Request focus when user taps _departmentFocusNode?.requestFocus(); }, child: DropdownSearch( selectedItem: departmentMap[selectedDepartment], enabled: !widget.isViewMode, popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit.loose, menuProps: const MenuProps(backgroundColor: Colors.white), itemBuilder: (context, item, isSelected) => Container( padding: EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), child: Text( item, style: GoogleFonts.poppins(fontSize: 11.5), ), ), constraints: BoxConstraints(maxHeight: 200), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search Department...", contentPadding: EdgeInsets.symmetric(horizontal: 10), ), ), ), items: departmentMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (_departmentFocus) ? (layoutColor) : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_departmentFocus) ? (layoutColor) : Colors.white, width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: (layoutColor ?? Colors.grey), width: 0.5, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10.0, vertical: 8.0, ), // contentPadding: EdgeInsets.symmetric(horizontal: 1), ), ), dropdownBuilder: (context, selectedItem) => Align( alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select Department", style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ), ), onChanged: widget.isViewMode ? null : (String? newValue) { if (newValue == null) return; final departmentId = departmentMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; setState(() { selectedDepartment = departmentId; }); widget.onDepartmentChanged?.call( departmentId, ); // Send department_id }, ), ), ), ), ), ], ); } Widget buildGroupField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Group", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), // CustomTextFieldUserWrapper( // isFocused: false, // isDesktop: widget.isDesktop, // child: SizedBox( // height: 40, // child: DropdownSearch( // selectedItem: selectedLevel, // enabled: !widget.isViewMode, // popupProps: PopupProps.menu( // // showSearchBox: true, // fit: FlexFit.loose, // Allows flexible height // constraints: BoxConstraints(maxHeight: 250), // ), // items: ["Level 1", "Level 2", "Level 3"], // dropdownDecoratorProps: DropDownDecoratorProps( // dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric( // horizontal: 1, // ), // ), // ), // dropdownBuilder: (context, selectedItem) => Align( // // Center-align selected item // alignment: Alignment.centerLeft, // child: Text( // selectedItem ?? "Select", // style: TextStyle(fontSize: 12, color: Colors.black), // ), // ), // onChanged: (String? newValue) { // setState(() { // // Find the country_code based on selected country_name // selectedLevel = newValue; // // // if (selectedCountry!.isNotEmpty) { // // errorMessages.remove("country_code"); // // } // }); // }, // ), // ), // ), CustomTextFieldUserWrapper( isFocused: _groupFocus, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: widget.isDesktop, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: _groupFocusNode, onFocusChange: (hasFocus) { setState(() { _groupFocus = hasFocus; }); }, child: GestureDetector( onTap: () { // Request focus when user taps _groupFocusNode?.requestFocus(); }, child: apiAllGroups == null ? Center( child: Transform.scale( scale: 0.5, child: CircularProgressIndicator(), ), ) : DropdownSearch( selectedItem: groupMap[selectedLevel], enabled: !widget.isViewMode, popupProps: PopupProps.menu( menuProps: const MenuProps( backgroundColor: Colors.white, ), showSearchBox: true, fit: FlexFit.loose, // Allows flexible height itemBuilder: (context, item, isSelected) => Container( padding: EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), child: Text( item, style: GoogleFonts.poppins(fontSize: 11.5), ), ), constraints: BoxConstraints(maxHeight: 250), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search Group...", contentPadding: EdgeInsets.symmetric( horizontal: 10, ), ), ), ), // items: apiAllGroups!.map((group) { // return "${group['name']} "; // }).toList(), items: apiAllGroups!.map((group) { return group['name'] .toString() .trim(); // <-- trim spaces }).toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (_groupFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_groupFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: (layoutColor ?? Colors.grey), width: 0.5, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10.0, vertical: 8.0, ), ), ), dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select", style: GoogleFonts.poppins(fontSize: 12), ), ), onChanged: (String? newValue) { if (newValue == null) return; final levelId = groupMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; setState(() { selectedLevel = levelId; }); widget.onLevelChanged?.call( levelId, ); // ✅ pass the ID not the name }, ), ), ), ), ), ], ); } Widget buildApprover1(bool isDesktop) { return Container( color: Colors.white, // child: Expanded( // Allow first column to take available space child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "First Approver", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldUserWrapper( isFocused: _firstApprovalFocus, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: _firstApprovalFocusNode, onFocusChange: (hasFocus) { setState(() { _firstApprovalFocus = hasFocus; }); }, child: GestureDetector( onTap: () { // Request focus when user taps _firstApprovalFocusNode?.requestFocus(); }, child: apiUserData == null ? Center( child: Transform.scale( scale: 0.5, child: CircularProgressIndicator(), ), ) : DropdownSearch( selectedItem: userMap[selectedFirstApprover], enabled: !widget.isViewMode, popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit .loose, // Allows flexible height menuProps: const MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) => Container( padding: EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), child: Text( item, style: GoogleFonts.poppins( fontSize: 11.5, ), ), ), constraints: BoxConstraints( maxHeight: 250, ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search User...", contentPadding: EdgeInsets.symmetric( horizontal: 10, ), ), ), ), items: apiUserData!.map((user) { return "${user['first_name']} ${user['last_name']}"; }).toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (_firstApprovalFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_firstApprovalFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: (layoutColor ?? Colors.grey), width: 0.5, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10.0, vertical: 8.0, ), ), ), dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select", style: GoogleFonts.poppins( fontSize: 12, ), ), ), // onChanged: (String? newValue) { // setState(() { // selectedFirstApprover = userMap.entries // .firstWhere( // (entry) => entry.value == newValue) // .key; // // // if (selectedCountry!.isNotEmpty) { // // errorMessages.remove("country_code"); // // } // }); // widget.onFirstApproverChanged?.call(newValue); // }, onChanged: (String? newValue) { if (newValue == null) return; final approverId = userMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; setState(() { selectedFirstApprover = approverId; }); widget.onFirstApproverChanged?.call( approverId, ); // ✅ not newValue, but approverId }, ), ), ), ), ), ], ), ], ), ], ), // ), ); } Widget buildApprover2(bool isDesktop) { return Container( color: Colors.white, // child: Expanded( // Allow second column to take available space child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Second Approver", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldUserWrapper( isFocused: _secondApprovalFocus, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: _secondApprovalFocusNode, onFocusChange: (hasFocus) { setState(() { _secondApprovalFocus = hasFocus; }); }, child: GestureDetector( onTap: () { // Request focus when user taps _secondApprovalFocusNode?.requestFocus(); }, child: apiUserData == null ? Center( child: Transform.scale( scale: 0.5, child: CircularProgressIndicator(), ), ) : DropdownSearch( selectedItem: userMap[selectedSecondApprover], enabled: !widget.isViewMode, popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit .loose, // Allows flexible height menuProps: const MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) => Container( padding: EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), child: Text( item, style: GoogleFonts.poppins( fontSize: 11.5, ), ), ), constraints: BoxConstraints( maxHeight: 250, ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search User...", contentPadding: EdgeInsets.symmetric( horizontal: 10, ), ), ), ), items: apiUserData!.map((user) { return "${user['first_name']} ${user['last_name']}"; }).toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (_secondApprovalFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_secondApprovalFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: (layoutColor ?? Colors.grey), width: 0.5, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10.0, vertical: 8.0, ), ), ), dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select", style: GoogleFonts.poppins( fontSize: 12, ), ), ), onChanged: (String? newValue) { if (newValue == null) return; final approverId = userMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; setState(() { selectedSecondApprover = approverId; }); widget.onSecondApproverChanged?.call( approverId, ); // ✅ not newValue, but approverId }, // onChanged: (String? newValue) { // setState(() { // selectedSecondApprover = userMap.entries // .firstWhere( // (entry) => entry.value == newValue) // .key; // // // if (selectedCountry!.isNotEmpty) { // // errorMessages.remove("country_code"); // // } // }); // }, ), ), ), ), ), ], ), ], ), ], ), // ), ); } Widget buildApprover3(bool isDesktop) { return Container( color: Colors.white, // child: Expanded( // Allow second column to take available space child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Third Approver", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldUserWrapper( isFocused: _thirdApprovalFocus, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: _thirdApprovalFocusNode, onFocusChange: (hasFocus) { setState(() { _thirdApprovalFocus = hasFocus; }); }, child: GestureDetector( onTap: () { // Request focus when user taps _thirdApprovalFocusNode?.requestFocus(); }, child: apiUserData == null ? Center( child: Transform.scale( scale: 0.5, child: CircularProgressIndicator(), ), ) : DropdownSearch( selectedItem: userMap[selectedThirdApprover], enabled: !widget.isViewMode, popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit .loose, // Allows flexible height menuProps: const MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) => Container( padding: EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), child: Text( item, style: GoogleFonts.poppins( fontSize: 11.5, ), ), ), constraints: BoxConstraints( maxHeight: 250, ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search User...", contentPadding: EdgeInsets.symmetric( horizontal: 10, ), ), ), ), items: apiUserData!.map((user) { return "${user['first_name']} ${user['last_name']}"; }).toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (_thirdApprovalFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_thirdApprovalFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: (layoutColor ?? Colors.grey), width: 0.5, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10.0, vertical: 8.0, ), ), ), dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select", style: GoogleFonts.poppins( fontSize: 12, ), ), ), onChanged: (String? newValue) { if (newValue == null) return; final approverId = userMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; setState(() { selectedThirdApprover = approverId; }); widget.onThirdApproverChanged?.call( approverId, ); // ✅ not newValue, but approverId }, // onChanged: (String? newValue) { // setState(() { // selectedThirdApprover = userMap.entries // .firstWhere( // (entry) => entry.value == newValue) // .key; // // // if (selectedCountry!.isNotEmpty) { // // errorMessages.remove("country_code"); // // } // }); // }, ), ), ), ), ), ], ), ], ), ], ), // ), ); } Widget buildExceptionApprover(bool isDesktop) { return Container( color: Colors.white, // child: Expanded( // Allow first column to take available space child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Fourth Approver", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldUserWrapper( isFocused: _exceptionalApprovalFocus, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: _exceptionalApprovalFocusNode, onFocusChange: (hasFocus) { setState(() { _exceptionalApprovalFocus = hasFocus; }); }, child: GestureDetector( onTap: () { // Request focus when user taps _exceptionalApprovalFocusNode?.requestFocus(); }, child: apiUserData == null ? Center( child: Transform.scale( scale: 0.5, child: CircularProgressIndicator(), ), ) : DropdownSearch( selectedItem: userMap[selectedExceptionalApprover], enabled: !widget.isViewMode, popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit .loose, // Allows flexible height menuProps: const MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) => Container( padding: EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), child: Text( item, style: GoogleFonts.poppins( fontSize: 11.5, ), ), ), constraints: BoxConstraints( maxHeight: 250, ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search User...", contentPadding: EdgeInsets.symmetric( horizontal: 10, ), ), ), ), items: apiUserData!.map((user) { return "${user['first_name']} ${user['last_name']}"; }).toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular( 8, ), borderSide: BorderSide( color: (_exceptionalApprovalFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_exceptionalApprovalFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: (layoutColor ?? Colors.white), width: 0.5, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10.0, vertical: 8.0, ), ), ), dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select", style: GoogleFonts.poppins( fontSize: 12, ), ), ), onChanged: (String? newValue) { if (newValue == null) return; final approverId = userMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; setState(() { selectedExceptionalApprover = approverId; }); widget.onExceptionalApproverChanged?.call( approverId, ); // ✅ not newValue, but approverId }, ), ), ), ), ), ], ), ], ), ], ), // ), ); } Widget buildApproverSubstitute1(bool isDesktop) { return Container( color: Colors.white, // child: Expanded( // Allow first column to take available space child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Delegate To", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldUserWrapper( isFocused: _delegateFocus, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: _delegateFocusNode, onFocusChange: (hasFocus) { setState(() { _delegateFocus = hasFocus; }); }, child: GestureDetector( onTap: () { _delegateFocusNode?.requestFocus(); }, child: apiUserData == null ? Center( child: Transform.scale( scale: 0.5, child: CircularProgressIndicator(), ), ) : DropdownSearch( // selectedItem: userMap[selectedSubstituteApprover], selectedItem: selectedSubstituteApprover != null ? userMap[selectedSubstituteApprover] : null, enabled: !widget.isViewMode, popupProps: PopupProps.menu( showSearchBox: true, fit: FlexFit .loose, // Allows flexible height menuProps: const MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) => Container( padding: EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), child: Text( item, style: GoogleFonts.poppins( fontSize: 11.5, ), ), ), constraints: BoxConstraints( maxHeight: 133, ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search User...", contentPadding: EdgeInsets.symmetric( horizontal: 10, ), ), ), ), items: apiUserData!.map((user) { return "${user['first_name']} ${user['last_name']}"; }).toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (_delegateFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (_delegateFocus ?? false) ? (layoutColor ?? Colors.white) : Colors.white, width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: (layoutColor ?? Colors.grey), width: 0.5, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10.0, vertical: 8.0, ), ), ), dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select", style: GoogleFonts.poppins( fontSize: 12, ), ), ), // onChanged: (String? newValue) { // setState(() { // selectedFirstApprover = userMap.entries // .firstWhere( // (entry) => entry.value == newValue) // .key; // // // if (selectedCountry!.isNotEmpty) { // // errorMessages.remove("country_code"); // // } // }); // widget.onFirstApproverChanged?.call(newValue); // }, onChanged: (String? newValue) { if (newValue == null) return; final approverId = userMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; setState(() { selectedSubstituteApprover = approverId; }); widget.onFirstSubsApproverChanged?.call( approverId, ); // ✅ not newValue, but approverId }, ), ), ), ), ), ], ), ], ), ], ), // ), ); } Widget buildDelegationStartDateField(bool isDesktop) { Future _selectCheckOutDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); // Parse date from notifier if available, else use today DateTime initialDate; initialDate = today; // // Use previously selected date if valid // if (_selectedCheckOutDate != null && // _selectedCheckOutDate!.isAfter(today)) { // initialDate = _selectedCheckOutDate!; // } // // final pickedDate = await showDatePicker( // context: context, // initialDate: initialDate, // firstDate: initialDate, // lastDate: DateTime(2100), // ); DateTime? pickedDate = await showDatePicker( context: context, initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, lastDate: DateTime(2100), initialEntryMode: DatePickerEntryMode.calendarOnly, ); if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; widget.controllers["delegationStartDate"]?.text = DateFormat( 'dd-MM-yyyy', ).format(pickedDate); if (_selectedEndDate != null && _selectedEndDate!.isBefore(_selectedCheckOutDate!)) { _selectedEndDate = null; widget.controllers["delegationEndDate"]?.text = ''; } }); } } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Start Date", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldUserWrapper( isFocused: _startDateFocus, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: Focus( focusNode: _startDateFocusNode, onKeyEvent: (FocusNode node, KeyEvent event) { // Only act on KeyDown and when Enter is pressed if (event is KeyDownEvent && (event.logicalKey == LogicalKeyboardKey.enter || event.logicalKey == LogicalKeyboardKey.numpadEnter)) { _startDateFocusNode?.requestFocus(); _selectCheckOutDate(context); return KeyEventResult.handled; // Stop default behavior } return KeyEventResult.ignored; // Allow normal handling }, child: GestureDetector( behavior: HitTestBehavior.opaque, onTapDown: (details) { _startDateFocusNode?.requestFocus(); _selectCheckOutDate(context); }, child: AbsorbPointer( child: TextField( controller: widget.controllers["delegationStartDate"], readOnly: true, style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), decoration: InputDecoration( labelText: "Select Date", labelStyle: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon( Icons.calendar_today, size: 16, color: Color(0xFF8B8FB2), ), ), ), ), ), ), // child: GestureDetector( // // onTap: () async{ // // _selectCheckOutDate(context); // // // // }, // onTap: () async { // _startDateFocusNode?.requestFocus(); // await _selectCheckOutDate(context); // }, // child: AbsorbPointer( // child: TextField( // focusNode: _startDateFocusNode, // controller: widget.controllers["delegationStartDate"], // readOnly: true, // style: const TextStyle(fontSize: 12), // decoration: InputDecoration( // labelText: "Select Date", // labelStyle: const TextStyle( // fontSize: 12, // color: Colors.grey, // ), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, // contentPadding: const EdgeInsets.symmetric(vertical: 16), // suffixIcon: const Icon( // Icons.calendar_today, // size: 16, // color: Colors.grey, // ), // ), // ), // ), // ), ), ), // if (errorMessages["start_date"] != null) ...[ // SizedBox(height: 5), // Space before error message // Text( // "Select Start Date", // style: TextStyle(color: Colors.red, fontSize: 12), // ), // ], ], ); } Widget buildDelegationEndDateField(bool isDesktop) { // DateTime? _selectedEndDate; Future _selectForexEndDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); DateTime minDate = _selectedCheckOutDate != null ? _selectedCheckOutDate! : today; // Parse date from notifier if available, else use today DateTime initialDate; initialDate = today; // // final pickedDate = await showDatePicker( // context: context, // initialDate: initialDate, // firstDate: initialDate, // lastDate: DateTime(2100), // ); DateTime? pickedDate = await showDatePicker( context: context, initialDate: _selectedEndDate != null && _selectedEndDate!.isAfter(minDate) ? _selectedEndDate! : minDate, firstDate: minDate, lastDate: DateTime(2100), initialEntryMode: DatePickerEntryMode.calendarOnly, ); if (pickedDate != null && pickedDate != _selectedEndDate) { setState(() { _selectedEndDate = pickedDate; widget.controllers["delegationEndDate"]?.text = DateFormat( 'dd-MM-yyyy', ).format(pickedDate); }); } } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "End Date", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldUserWrapper( width: isDesktop ? MediaQuery.of(context).size.width * 0.2 : null, isFocused: _endDateFocus, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: Focus( focusNode: _endDateFocusNode, onKeyEvent: (FocusNode node, KeyEvent event) { // Only act on KeyDown and when Enter is pressed if (event is KeyDownEvent && (event.logicalKey == LogicalKeyboardKey.enter || event.logicalKey == LogicalKeyboardKey.numpadEnter)) { if (!widget.isViewMode) { _selectForexEndDate(context).then((_) { _endDateFocusNode?.requestFocus(); }); } return KeyEventResult.handled; // Stop default behavior } return KeyEventResult.ignored; // Allow normal handling }, child: GestureDetector( behavior: HitTestBehavior.opaque, onTapDown: (details) { if (!widget.isViewMode) { _selectForexEndDate(context).then((_) { _endDateFocusNode?.requestFocus(); }); } }, child: AbsorbPointer( child: TextField( controller: widget.controllers["delegationEndDate"], readOnly: true, style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), decoration: InputDecoration( labelText: "Select Date", labelStyle: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon( Icons.calendar_today, size: 16, color: Color(0xFF8B8FB2), ), ), ), ), ), ), // child: GestureDetector( // // onTap: () async{ // // _selectCheckOutDate(context); // // // // }, // onTap: () async { // _endDateFocusNode?.requestFocus(); // await _selectForexEndDate(context); // }, // child: AbsorbPointer( // child: TextField( // focusNode: _endDateFocusNode, // controller: widget.controllers["delegationEndDate"], // readOnly: true, // style: const TextStyle(fontSize: 12), // decoration: InputDecoration( // labelText: "Select Date", // labelStyle: const TextStyle( // fontSize: 12, // color: Colors.grey, // ), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, // contentPadding: const EdgeInsets.symmetric(vertical: 16), // suffixIcon: const Icon( // Icons.calendar_today, // size: 16, // color: Colors.grey, // ), // ), // ), // ), // ), ), ), // if (widget.errorMessages["employeeCode"] != null) ...[ // SizedBox(height: 5), // Space before error message // Text( // widget.errorMessages["employeeCode"]!, // style: TextStyle(color: Colors.red, fontSize: 12), // ), // ], ], ); } Widget buildReset(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black, ), ), SizedBox(height: 8), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), foregroundColor: Colors.white, // Keep original color shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.symmetric(horizontal: 10, vertical: 3), ), onPressed: () { handleReset(); }, child: Text("Reset", style: GoogleFonts.poppins(fontSize: 11)), ), ], ); } }