diff --git a/assets/images/login/logoNew.jpg b/assets/images/login/logoNew.jpg new file mode 100644 index 0000000..e9d8b8e Binary files /dev/null and b/assets/images/login/logoNew.jpg differ diff --git a/lib/Screens/allTrips/list_all_plans.dart b/lib/Screens/allTrips/list_all_plans.dart index 7e7e844..b7e62c2 100644 --- a/lib/Screens/allTrips/list_all_plans.dart +++ b/lib/Screens/allTrips/list_all_plans.dart @@ -383,9 +383,8 @@ class _ListAllPlansState extends State { color: Colors.grey.shade300, width: 1), ), ), - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontFamily: "Inter", ), ), ), @@ -510,9 +509,8 @@ class _ListAllPlansState extends State { color: Colors.grey.shade300, width: 1), ), ), - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontFamily: "Inter", ), ), ), @@ -531,30 +529,37 @@ class _ListAllPlansState extends State { padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, - children: const [ - Icon(Icons.error_outline, + children: [ + const Icon(Icons.error_outline, color: Colors.redAccent, size: 60), - SizedBox(height: 1), - Text("Oops!", - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Colors.redAccent)), - SizedBox(height: 15), - Text("No Trips", - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 20, - fontFamily: "Inter", - fontWeight: FontWeight.w600, - color: Colors.black54)), - SizedBox(height: 10), - Text("Please Create Trip", - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: "Inter", - fontSize: 12, - color: Colors.grey)), + const SizedBox(height: 1), + Text( + "Oops!", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.redAccent), + // style: TextStyle( + // fontSize: 22, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent) + ), + const SizedBox(height: 15), + Text( + "No Trips", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.w600, + color: Colors.black54), + ), + const SizedBox(height: 10), + Text( + "Please Create Trip", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 12, color: Colors.grey), + ), ], ), ), @@ -586,58 +591,55 @@ class _ListAllPlansState extends State { horizontalInside: BorderSide( width: 0.5, color: Colors.grey.shade200), ), - columns: const [ + columns: [ DataColumn( - label: Text('Trip Id', - style: TextStyle( - // color: Colors.grey, - // color: Color(0xFF9E9DBD), - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Trip Name', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Employee Code', - style: TextStyle( - // color: Color(0xFF9E9DBD), - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Traveller', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Trip Type', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Created On', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Status', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Actions', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), ], rows: paginatedPlans.map((plan) { return DataRow(cells: [ diff --git a/lib/Screens/allTrips/travel_agent_list.dart b/lib/Screens/allTrips/travel_agent_list.dart index 4133905..f84a910 100644 --- a/lib/Screens/allTrips/travel_agent_list.dart +++ b/lib/Screens/allTrips/travel_agent_list.dart @@ -435,9 +435,8 @@ class _TravelAgentListPlansState extends State { color: Colors.grey.shade300, width: 1), ), ), - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontFamily: "Inter", ), ), ), @@ -468,7 +467,7 @@ class _TravelAgentListPlansState extends State { fontWeight: FontWeight.bold, color: Colors.redAccent)), SizedBox(height: 20), - Text("No Approvals", + Text("No Trips Pending For Your Approvals", textAlign: TextAlign.center, style: TextStyle( fontSize: 20, @@ -514,58 +513,55 @@ class _TravelAgentListPlansState extends State { horizontalInside: BorderSide( width: 0.5, color: Colors.grey.shade200), ), - columns: const [ + columns: [ DataColumn( - label: Text('Trip Id', - style: TextStyle( - // color: Colors.grey, - // color: Color(0xFF9E9DBD), - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Trip Name', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Employee Code', - style: TextStyle( - // color: Color(0xFF9E9DBD), - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Traveller', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Trip Type', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Created On', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Status', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Actions', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), ], rows: paginatedPlans.map((plan) { return DataRow(cells: [ diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 1d81b88..ec5f626 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -434,9 +434,8 @@ class _ApprovalListState extends State { color: Colors.grey.shade300, width: 1), ), ), - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontFamily: "Inter", ), ), ), @@ -482,9 +481,8 @@ class _ApprovalListState extends State { color: Colors.grey.shade300, width: 1), ), ), - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontFamily: "Inter", ), ), ), @@ -516,7 +514,7 @@ class _ApprovalListState extends State { fontWeight: FontWeight.bold, color: Colors.redAccent)), SizedBox(height: 15), - Text("No Approvals", + Text("No Trips Pending For Your Approvals", textAlign: TextAlign.center, style: TextStyle( fontSize: 20, @@ -554,57 +552,55 @@ class _ApprovalListState extends State { horizontalInside: BorderSide( width: 0.5, color: Colors.grey.shade200), ), - columns: const [ + columns: [ DataColumn( - label: Text('Trip Id', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Trip Name', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Employee Code', - style: TextStyle( - // color: Color(0xFF9E9DBD), - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Traveller', - style: TextStyle( - // color: Color(0xFF9E9DBD), - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Trip Type', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Created On', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Status', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Actions', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), ], rows: paginatedPlans.map((plan) { return DataRow(cells: [ diff --git a/lib/Screens/authentication/login/login_widget.dart b/lib/Screens/authentication/login/login_widget.dart index e70a33c..4bc9c19 100644 --- a/lib/Screens/authentication/login/login_widget.dart +++ b/lib/Screens/authentication/login/login_widget.dart @@ -260,11 +260,11 @@ class _LoginWidgetState extends State { ), ), child: Padding( - padding: const EdgeInsets.only(left: 30), + padding: const EdgeInsets.only(left: 0), child: Align( alignment: Alignment.topLeft, child: Image.asset( - 'assets/images/login/TravelSpendsLogo1.png', + 'assets/images/login/logoNew.jpg', width: 200, // Optional: control size height: 100, fit: BoxFit.contain, @@ -676,36 +676,76 @@ class _LoginWidgetState extends State { const SizedBox(height: 20), if (!_isForgotPassword && !_showOtpResetFields) Row( - mainAxisAlignment: MainAxisAlignment.center, children: [ - Image.asset( - 'assets/images/login/microsoft.png', - width: 30, // Optional: control size - height: 30, - fit: BoxFit.contain, - ), - const SizedBox(width: 10), - Container( - width: 30, // Adjust size - height: 30, - decoration: BoxDecoration( - // Background color - shape: BoxShape.rectangle, - borderRadius: BorderRadius.all(Radius.circular(15)), - border: Border.all( - color: Color(0xFF9E9DBD), width: 1), // Grey outline + Expanded( + child: ElevatedButton( + onPressed: () => (), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, // Button color + foregroundColor: Colors.black, // Text color + padding: const EdgeInsets.symmetric( + horizontal: 24, vertical: 5), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18), + side: BorderSide( + color: Colors.black, // Border color + width: 0.5, // Border width + ), + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 24, vertical: 5), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Sign In With Microsoft", + style: TextStyle( + fontFamily: "Nunito", + fontWeight: FontWeight.w500, + fontSize: 15), + ), + SizedBox( + width: 3, + ), + Image.asset( + 'assets/images/login/microsoft.png', + width: 30, // Optional: control size + height: 30, + fit: BoxFit.contain, + ), + ], + ), + ), ), - - child: Center( - child: Image.asset( - 'assets/images/login/VectorG.png', - width: 20, // Optional: control size - height: 20, - fit: BoxFit.contain, - )), ), ], ), + // Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Container( + // width: 30, // Adjust size + // height: 30, + // decoration: BoxDecoration( + // // Background color + // shape: BoxShape.rectangle, + // borderRadius: BorderRadius.all(Radius.circular(15)), + // border: Border.all( + // color: Color(0xFF9E9DBD), width: 1), // Grey outline + // ), + // + // child: Center( + // child: Image.asset( + // 'assets/images/login/VectorG.png', + // width: 20, // Optional: control size + // height: 20, + // fit: BoxFit.contain, + // )), + // ), + // ], + // ), ], ), ), diff --git a/lib/Screens/group/group.dart b/lib/Screens/group/group.dart index 195e127..aca13cb 100644 --- a/lib/Screens/group/group.dart +++ b/lib/Screens/group/group.dart @@ -323,7 +323,8 @@ class _groupState extends State { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( - backgroundColor: Colors.white, + // backgroundColor: Colors.white, + backgroundColor: Color(0xFFf5f5f5), appBar: CustomAppBar(isDesktop: isDesktop), drawer: CustomDrawer(isDesktop: false), body: Padding( @@ -348,15 +349,11 @@ class _groupState extends State { Widget buildData(bool isDesktop, context) { return Container( - // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), - // decoration: BoxDecoration( - // // color: Colors.amber, - // // color: bodyColor, - // color: Color(0xFFE1F5FE), - // border: Border.all( - // color: Colors.white, - // // color: Color(0xFFF7F7FB), - // width: 3.5)), + margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), child: Column( children: [ Expanded( diff --git a/lib/Screens/group/groupList.dart b/lib/Screens/group/groupList.dart index 8979f07..97abbac 100644 --- a/lib/Screens/group/groupList.dart +++ b/lib/Screens/group/groupList.dart @@ -131,7 +131,8 @@ class _GroupListState extends State { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( - backgroundColor: Colors.white, + // backgroundColor: Colors.white, + backgroundColor: Color(0xFFf5f5f5), appBar: CustomAppBar(isDesktop: isDesktop), drawer: CustomDrawer(isDesktop: false), body: Padding( @@ -156,6 +157,11 @@ class _GroupListState extends State { Widget buildGroupListLayout(bool isDesktop) { return Container( + margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), // decoration: BoxDecoration( // // color: Colors.amber, // // color: bodyColor, diff --git a/lib/Screens/itnerary/accomodations.dart b/lib/Screens/itnerary/accomodations.dart index 5763cf5..f69890f 100644 --- a/lib/Screens/itnerary/accomodations.dart +++ b/lib/Screens/itnerary/accomodations.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -419,7 +420,7 @@ class _AccomodationScreenState extends State { children: [ Text( "Destination", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -467,7 +468,7 @@ class _AccomodationScreenState extends State { children: [ Text( "Hotel Name", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -671,7 +672,7 @@ class _AccomodationScreenState extends State { children: [ Text( "Check-in*", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -723,7 +724,7 @@ class _AccomodationScreenState extends State { children: [ Text( "Time (Check-in)", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -775,7 +776,7 @@ class _AccomodationScreenState extends State { children: [ Text( "Check-out*", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -827,7 +828,7 @@ class _AccomodationScreenState extends State { children: [ Text( "Time (Check-out)", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -878,7 +879,7 @@ class _AccomodationScreenState extends State { children: [ Text( "Comments", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -939,7 +940,7 @@ class _AccomodationScreenState extends State { ), child: Text( "Close", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 14), ), ), SizedBox(width: 10), // Space between buttons @@ -958,7 +959,7 @@ class _AccomodationScreenState extends State { ), child: Text( "Save Changes", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 14), ), ), ]; diff --git a/lib/Screens/itnerary/bus.dart b/lib/Screens/itnerary/bus.dart index 5c62a39..2ceaefe 100644 --- a/lib/Screens/itnerary/bus.dart +++ b/lib/Screens/itnerary/bus.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -273,9 +274,9 @@ class _BusScreenState extends State { children: [ Text( "Trip Type", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -401,9 +402,9 @@ class _BusScreenState extends State { children: [ Text( "From", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -446,9 +447,9 @@ class _BusScreenState extends State { children: [ Text( "To", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -491,9 +492,9 @@ class _BusScreenState extends State { children: [ Text( "Date", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -543,9 +544,9 @@ class _BusScreenState extends State { children: [ Text( "Time", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -594,9 +595,9 @@ class _BusScreenState extends State { children: [ Text( "Comments", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -650,7 +651,7 @@ class _BusScreenState extends State { ), child: Text( "Close", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons @@ -669,7 +670,7 @@ class _BusScreenState extends State { ), child: Text( "Save Changes", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; diff --git a/lib/Screens/itnerary/flights.dart b/lib/Screens/itnerary/flights.dart index 29e6217..3e72ee8 100644 --- a/lib/Screens/itnerary/flights.dart +++ b/lib/Screens/itnerary/flights.dart @@ -1,6 +1,7 @@ import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:frontend/services/apiService.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -731,7 +732,7 @@ class _FlightScreenState extends State { children: [ Text( "Trip Type", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1122,68 +1123,12 @@ class _FlightScreenState extends State { // selectedCountry ??= null; return [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - selectedTripType == "Oneway" ? "Class *" : "Class $index *", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: focusStates["_class${index}Focused"] ?? false, - isDesktop: isDesktop, - // width: isDesktop - // ? MediaQuery.of(context).size.width * 0.34 - // : MediaQuery.of(context).size.width * 0.66, - child: SizedBox( - height: 40, - child: isCountryLoading - ? const Center(child: CircularProgressIndicator()) - : DropdownButtonFormField( - focusNode: focusNodes["_class${index}FocusNode"], - // focusNode: _tripTypeFocusNode, // Assign the correct focus node - // controller: _hotelNameController, - value: selectedClasses[index], - - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding - ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedClasses[index] = newValue; - }); - - print(selectedClasses[index]); - } - : null, - items: dropdownItems, - ), - ), - ), - ], - ), - if (isDesktop) - SizedBox( - width: 20, - ) - else - SizedBox( - height: 8, - ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "From", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1284,7 +1229,9 @@ class _FlightScreenState extends State { ], ), if (isDesktop) - Spacer() + SizedBox( + width: 20, + ) else SizedBox( height: 8, @@ -1294,7 +1241,7 @@ class _FlightScreenState extends State { children: [ Text( "To", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1370,6 +1317,60 @@ class _FlightScreenState extends State { ], ], ), + if (isDesktop) + Spacer() + else + SizedBox( + height: 8, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Class *", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + isFocused: focusStates["_class${index}Focused"] ?? false, + isDesktop: isDesktop, + // width: isDesktop + // ? MediaQuery.of(context).size.width * 0.34 + // : MediaQuery.of(context).size.width * 0.66, + child: SizedBox( + height: 40, + child: isCountryLoading + ? const Center(child: CircularProgressIndicator()) + : DropdownButtonFormField( + focusNode: focusNodes["_class${index}FocusNode"], + // focusNode: _tripTypeFocusNode, // Assign the correct focus node + // controller: _hotelNameController, + value: selectedClasses[index], + + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10), // Proper padding + ), + onChanged: purposeList.isNotEmpty + ? (newValue) { + setState(() { + selectedClasses[index] = newValue; + }); + + print(selectedClasses[index]); + } + : null, + items: dropdownItems, + ), + ), + ), + ], + ), if (isDesktop) Spacer() else @@ -1381,7 +1382,7 @@ class _FlightScreenState extends State { children: [ Text( "Date", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1435,7 +1436,7 @@ class _FlightScreenState extends State { children: [ Text( "Time", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1526,7 +1527,7 @@ class _FlightScreenState extends State { children: [ Text( "Visa Required", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1583,7 +1584,7 @@ class _FlightScreenState extends State { children: [ Text( "Comments", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1721,7 +1722,7 @@ class _FlightScreenState extends State { ), child: Text( "Close", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons @@ -1740,7 +1741,7 @@ class _FlightScreenState extends State { ), child: Text( "Save Changes", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; diff --git a/lib/Screens/itnerary/forex.dart b/lib/Screens/itnerary/forex.dart index 391ca55..435c8d1 100644 --- a/lib/Screens/itnerary/forex.dart +++ b/lib/Screens/itnerary/forex.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -412,13 +413,50 @@ class _ForexScreenState extends State { (data["end_date"]?.isNotEmpty ?? false); } + // Check if all required fields have data + bool _isForexDataDurationComplete() { + final data = getForexData; + return (data["start_date"]?.isNotEmpty ?? false) && + (data["end_date"]?.isNotEmpty ?? false); + } + // Handle field changes void _onFieldChanged() { + if (_isForexDataDurationComplete()) { + CalculateDuration(); + } + if (_isForexDataComplete()) { postgetForexData(getForexData); } } + void CalculateDuration() { + final data = getForexData; + final startDateString = data["start_date"]; + final endDateString = data["end_date"]; + + if (startDateString == null || endDateString == null) return; + + try { + // Parse the dates from string + final startDate = DateFormat('d/M/yyyy').parse(startDateString); + final endDate = DateFormat('d/M/yyyy').parse(endDateString); + + // Calculate difference + final durationInDays = + endDate.difference(startDate).inDays + 1; // +1 to include both days + + // You can now use durationInDays however you want: + print("Duration: $durationInDays days"); + + // Optionally, you might want to update some data: + selectedDuration = durationInDays as String?; + } catch (e) { + print('Error parsing dates: $e'); + } + } + // Handle dropdown change void _onCountryChanged(String? newCountry) { setState(() { @@ -498,6 +536,12 @@ class _ForexScreenState extends State { int? enteredAmount = int.tryParse(value); + int? quotedAmount = int.tryParse(selectedQuotedAmount!); + int? difference = quotedAmount! - enteredAmount!; + + print("Difference - $difference"); + textControllers["_card"]?.text = difference.toString(); + if (enteredAmount == null || enteredAmount > fifteenPercent) { errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent"; } else { @@ -583,43 +627,37 @@ class _ForexScreenState extends State { // _buildSecondRow(isDesktop) // ]; - List rowBuilders = [ - ..._builClassType(isDesktop), // Spread the List - Divider(), - ..._buildSecondRow(isDesktop), // Spread the List - ]; - return [ ...buildResponsiveRow(_buildFirstRow(isDesktop)), SizedBox( height: 5, ), - Align( - alignment: Alignment.centerLeft, - child: Text( - "Forex Details", - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - ), + // Align( + // alignment: Alignment.centerLeft, + // child: Text( + // "Forex Details", + // style: TextStyle( + // fontSize: 13, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74)), + // ), + // ), // SizedBox( // height: 8, // ), - Divider( - thickness: 0.3, - ), - SizedBox( - height: 3, - ), + // Divider( + // thickness: 0.3, + // ), + // SizedBox( + // height: 3, + // ), ...buildResponsiveRow(_builClassType(isDesktop)), - SizedBox( - height: 3, - ), - Divider( - thickness: 0.3, - ), + // SizedBox( + // height: 3, + // ), + // Divider( + // thickness: 0.3, + // ), SizedBox( height: 10, ), @@ -646,6 +684,8 @@ class _ForexScreenState extends State { initialDate = DateTime.parse(flightFirstTripDateNotifier.value!); textControllers["_forexStartDate"]?.text = DateFormat('yyyy-MM-dd').format(initialDate); + textControllers["_forexStartDate"]?.text = + DateFormat('dd-MM-yyyy').format(initialDate); } catch (e) { initialDate = today; } @@ -731,40 +771,19 @@ class _ForexScreenState extends State { _selectedEndDate = pickedDate; textControllers["_forexEndDate"]?.text = DateFormat('yyyy-MM-dd').format(pickedDate); + // textControllers["_forexEndDate"]?.text = + // DateFormat('dd-MM-yyyy').format(initialDate); }); } } - late Map countryMap; // Mapping country_code -> country_name - late List countryCodes; // List of country codes - - countryList = widget.apiCountryData ?? []; - - // Map country codes to country names - countryMap = { - for (var item in countryList) - item['country_code'] as String: item['country_name'] as String - }; - - // Extract only country codes for processing - countryCodes = countryMap.keys.toList(); - - selectedCountry ??= null; - - // // Set default selected value - // if (selectedCountry == null && countryCodes.isNotEmpty) { - // selectedCountry = countryCodes.first; - // } - - // -------------------------- End Selected Country Dropdown -------------------------------------- - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Start Date", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -843,7 +862,7 @@ class _ForexScreenState extends State { children: [ Text( "End Date", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -919,92 +938,19 @@ class _ForexScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Countries", - style: TextStyle( + "Durartion", + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldForexWrapper( - isFocused: focusStates["_countries"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: DropdownSearch( - selectedItem: countryMap[selectedCountry], - popupProps: PopupProps.menu( - showSearchBox: true, // Enables search functionality - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search Country...", - contentPadding: EdgeInsets.symmetric(horizontal: 10), - ), - ), - ), - items: countryMap.values.toList(), - 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 Country", - style: TextStyle(fontSize: 12), - ), - ), - onChanged: (String? newValue) { - setState(() { - // Find the country_code based on selected country_name - selectedCountry = countryMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; - _onCountryChanged(selectedCountry); - }); - }, - ), - ), - ), - if (errorMessages["country_code"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Select Country", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - ]; - } - - List _builClassType(bool isDesktop) { - return [ - // if (isDesktop) Spacer() else SizedBox( - // height: 8, - // ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Durartion", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_durationFocused"] ?? false, isDesktop: isDesktop, color: Colors.transparent, child: SizedBox( - height: 30, + height: 40, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( @@ -1028,11 +974,114 @@ class _ForexScreenState extends State { ), ], ), + ]; + } + + List _builClassType(bool isDesktop) { + late Map countryMap; // Mapping country_code -> country_name + late List countryCodes; // List of country codes + + countryList = widget.apiCountryData ?? []; + + // Map country codes to country names + countryMap = { + for (var item in countryList) + item['country_code'] as String: item['country_name'] as String + }; + + // Extract only country codes for processing + countryCodes = countryMap.keys.toList(); + + selectedCountry ??= null; + + // // Set default selected value + // if (selectedCountry == null && countryCodes.isNotEmpty) { + // selectedCountry = countryCodes.first; + // } + + // -------------------------- End Selected Country Dropdown -------------------------------------- + + return [ + // if (isDesktop) Spacer() else SizedBox( + // height: 8, + // ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Country", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldForexWrapper( + isFocused: focusStates["_countries"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: DropdownSearch( + selectedItem: countryMap[selectedCountry], + popupProps: PopupProps.menu( + showSearchBox: true, // Enables search functionality + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search Country...", + contentPadding: EdgeInsets.symmetric(horizontal: 10), + ), + ), + ), + items: countryMap.values.toList(), + 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 Country", + style: TextStyle(fontSize: 12), + ), + ), + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + selectedCountry = countryMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; + _onCountryChanged(selectedCountry); + }); + }, + ), + ), + ), + if (errorMessages["country_code"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Select Country", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + ], + ), // if (isDesktop)SizedBox(width: 8,) else SizedBox( // height: 8, // ), if (isDesktop) - Spacer() + SizedBox( + width: MediaQuery.of(context).size.width * 0.048, + ) else SizedBox( height: 8, @@ -1042,21 +1091,21 @@ class _ForexScreenState extends State { children: [ Text( "Currency *", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( + CustomTextFieldForexWrapper( isFocused: focusStates["_currencyFocused"] ?? false, isDesktop: isDesktop, color: Colors.transparent, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.330 - : MediaQuery.of(context).size.width * 0.66, + // width: isDesktop + // ? MediaQuery.of(context).size.width * 0.330 + // : MediaQuery.of(context).size.width * 0.66, child: SizedBox( - height: 30, + height: 40, child: Padding( padding: const EdgeInsets.all(8.0), child: Center( @@ -1088,18 +1137,18 @@ class _ForexScreenState extends State { children: [ Text( "Perdiem Amount", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( + CustomTextFieldForexWrapper( isFocused: focusStates["_perdiemAmount"] ?? false, isDesktop: isDesktop, color: Colors.transparent, child: SizedBox( - height: 30, + height: 35, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( @@ -1136,46 +1185,74 @@ class _ForexScreenState extends State { List _buildSecondRow(bool isDesktop) { return [ + if (isDesktop) Spacer(), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Transport", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: focusStates["_transport"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - focusNode: focusNodes["_transport"], - controller: textControllers["_transport"], - onChanged: (value) => _onFieldChangedForOthers(), - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal - ], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Transport", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), + Container( + padding: const EdgeInsets.only(left: 10), + color: Colors.yellow.shade50, + width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null, + height: 30, + child: TextField( + focusNode: focusNodes["_transport"], + controller: textControllers["_transport"], + onChanged: (value) => _onFieldChangedForOthers(), + keyboardType: TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + ], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "0", + labelStyle: TextStyle(fontSize: 12, color: Colors.black), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), - ), + ) + // CustomTextFieldItnerarySubWrapper( + // width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null, + // isFocused: focusStates["_transport"] ?? false, + // isDesktop: isDesktop, + // child: SizedBox( + // height: 40, + // child: TextField( + // focusNode: focusNodes["_transport"], + // controller: textControllers["_transport"], + // onChanged: (value) => _onFieldChangedForOthers(), + // keyboardType: TextInputType.numberWithOptions(decimal: true), + // inputFormatters: [ + // FilteringTextInputFormatter.allow(RegExp( + // r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + // ], + // style: const TextStyle(fontSize: 12), + // decoration: const InputDecoration( + // labelText: "Transport", + // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + // floatingLabelBehavior: FloatingLabelBehavior.never, + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + // ), + // ), + // ), + // ), ], ), if (isDesktop) - Spacer() + SizedBox( + width: 10, + ) else SizedBox( height: 8, @@ -1185,41 +1262,68 @@ class _ForexScreenState extends State { children: [ Text( "Accomodation", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: focusStates["_accomodation"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - focusNode: focusNodes["_accomodation"], - controller: textControllers["_accomodation"], - onChanged: (value) => _onFieldChangedForOthers(), - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal - ], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Accomodation", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), + Container( + padding: const EdgeInsets.only(left: 10), + color: Colors.yellow.shade50, + width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null, + height: 30, + child: TextField( + focusNode: focusNodes["_accomodation"], + controller: textControllers["_accomodation"], + onChanged: (value) => _onFieldChangedForOthers(), + keyboardType: TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + ], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "0", + labelStyle: TextStyle(fontSize: 12, color: Colors.black), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), - ), + ) + // CustomTextFieldItnerarySubWrapper( + // width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null, + // isFocused: focusStates["_accomodation"] ?? false, + // isDesktop: isDesktop, + // child: SizedBox( + // height: 40, + // child: TextField( + // focusNode: focusNodes["_accomodation"], + // controller: textControllers["_accomodation"], + // onChanged: (value) => _onFieldChangedForOthers(), + // keyboardType: TextInputType.numberWithOptions(decimal: true), + // inputFormatters: [ + // FilteringTextInputFormatter.allow(RegExp( + // r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + // ], + // style: const TextStyle(fontSize: 12), + // decoration: const InputDecoration( + // labelText: "Accomodation", + // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + // floatingLabelBehavior: FloatingLabelBehavior.never, + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + // ), + // ), + // ), + // ), ], ), if (isDesktop) - Spacer() + SizedBox( + width: 10, + ) else SizedBox( height: 8, @@ -1229,17 +1333,18 @@ class _ForexScreenState extends State { children: [ Text( "Telephone", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: focusStates["_telephone"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, + Container( + color: Colors.yellow.shade50, + padding: const EdgeInsets.only(left: 10), + width: + isDesktop ? MediaQuery.of(context).size.width * 0.06 : null, + height: 30, child: TextField( focusNode: focusNodes["_telephone"], controller: textControllers["_telephone"], @@ -1251,19 +1356,45 @@ class _ForexScreenState extends State { ], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( - labelText: "Telephone", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + labelText: "0", + labelStyle: TextStyle(fontSize: 12, color: Colors.black), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), - ), - ), - ), + )) + // CustomTextFieldItnerarySubWrapper( + // width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null, + // isFocused: focusStates["_telephone"] ?? false, + // isDesktop: isDesktop, + // child: SizedBox( + // height: 40, + // child: TextField( + // focusNode: focusNodes["_telephone"], + // controller: textControllers["_telephone"], + // onChanged: (value) => _onFieldChangedForOthers(), + // keyboardType: TextInputType.numberWithOptions(decimal: true), + // inputFormatters: [ + // FilteringTextInputFormatter.allow(RegExp( + // r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + // ], + // style: const TextStyle(fontSize: 12), + // decoration: const InputDecoration( + // labelText: "Telephone", + // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + // floatingLabelBehavior: FloatingLabelBehavior.never, + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + // ), + // ), + // ), + // ), ], ), if (isDesktop) - Spacer() + SizedBox( + width: 40, + ) else SizedBox( height: 8, @@ -1273,18 +1404,18 @@ class _ForexScreenState extends State { children: [ Text( "Other Expenses", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( + CustomTextFieldForexWrapper( isFocused: false, isDesktop: isDesktop, color: Colors.transparent, child: SizedBox( - height: 40, + height: 35, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( @@ -1340,17 +1471,17 @@ class _ForexScreenState extends State { children: [ Text( "Cash*", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( + CustomTextFieldForexWrapper( isFocused: focusStates["_cash"] ?? false, isDesktop: isDesktop, child: SizedBox( - height: 40, + height: 35, child: TextField( focusNode: focusNodes["_cash"], controller: textControllers["_cash"], @@ -1382,9 +1513,7 @@ class _ForexScreenState extends State { ), if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.035, - ) + Spacer() else SizedBox( height: 8, @@ -1395,17 +1524,17 @@ class _ForexScreenState extends State { children: [ Text( "Card*", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( + CustomTextFieldForexWrapper( isFocused: focusStates["_card"] ?? false, isDesktop: isDesktop, child: SizedBox( - height: 40, + height: 35, child: TextField( focusNode: focusNodes["_card"], controller: textControllers["_card"], @@ -1436,9 +1565,7 @@ class _ForexScreenState extends State { ), if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.035, - ) + Spacer() else SizedBox( height: 8, @@ -1449,18 +1576,18 @@ class _ForexScreenState extends State { children: [ Text( "Total Amount", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( + CustomTextFieldForexWrapper( isFocused: focusStates["_perdiemAmount"] ?? false, isDesktop: isDesktop, color: Colors.transparent, child: SizedBox( - height: 40, + height: 35, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( @@ -1468,16 +1595,83 @@ class _ForexScreenState extends State { selectedQuotedAmount ?? "0", // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", style: const TextStyle( - fontSize: 12, + fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), ), ), ), + + // Container( + // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, + // // width: MediaQuery.of(context).size.width * 0.035, + // height: + // isDesktop ? MediaQuery.of(context).size.height * 0.09 : null, + // child: Row( + // crossAxisAlignment: CrossAxisAlignment.end, + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // Text( + // "Total Amount", + // style: TextStyle( + // fontSize: 13, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74)), + // ), + // ], + // ), + // ), ], ), - + // if (isDesktop) + // SizedBox( + // width: 35, + // ) + // else + // SizedBox( + // height: 8, + // ), + // + // Column( + // // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // // SizedBox(height: 5), + // Container( + // // width: MediaQuery.of(context).size.width * 0.15, + // height: + // isDesktop ? MediaQuery.of(context).size.height * 0.09 : null, + // padding: isDesktop ? const EdgeInsets.only(left: 12.0) : null, + // child: Row( + // crossAxisAlignment: CrossAxisAlignment.end, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Text( + // // "amo", + // selectedQuotedAmount ?? "0", + // // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", + // style: const TextStyle( + // fontSize: 13, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74)), + // ), + // ], + // ), + // ), + // // CustomTextFieldItnerarySubWrapper( + // // isFocused: focusStates["_perdiemAmount"] ?? false, + // // isDesktop: isDesktop, + // // color: Colors.transparent, + // // child: SizedBox( + // // height: 40, + // // child: Padding( + // // padding: const EdgeInsets.all(8.0), + // // child: + // // ), + // // ), + // // ), + // ], + // ), // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ @@ -1528,7 +1722,7 @@ class _ForexScreenState extends State { children: [ Text( "Comments", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1538,7 +1732,7 @@ class _ForexScreenState extends State { isFocused: focusStates["_comments"] ?? false, // Dropdown doesn't use focus isDesktop: isDesktop, - width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null, + width: isDesktop ? MediaQuery.of(context).size.width * 0.45 : null, child: SizedBox( height: 35, child: TextField( @@ -1581,17 +1775,17 @@ class _ForexScreenState extends State { children: [ Text( "Card Number", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( + CustomTextFieldForexWrapper( isFocused: focusStates["_cardNumber"] ?? false, isDesktop: isDesktop, child: SizedBox( - height: 40, + height: 35, child: TextField( enabled: !isChecked, focusNode: focusNodes["_cardNumber"], @@ -1621,7 +1815,7 @@ class _ForexScreenState extends State { children: [ Text( "Delivery Location", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1632,7 +1826,7 @@ class _ForexScreenState extends State { false, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 + ? MediaQuery.of(context).size.width * 0.45 : null, child: SizedBox( height: 35, @@ -1661,7 +1855,7 @@ class _ForexScreenState extends State { ), if (isDesktop) SizedBox( - width: MediaQuery.of(context).size.width * 0.035, + width: MediaQuery.of(context).size.width * 0.04, ) else SizedBox( @@ -1698,8 +1892,8 @@ class _ForexScreenState extends State { ), ), Text( - "Check If You Don't Have a forex Account", - style: TextStyle( + "Check If You Don't Have a Forex Account", + style: GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1727,7 +1921,7 @@ class _ForexScreenState extends State { ), child: Text( "Close", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons @@ -1746,7 +1940,7 @@ class _ForexScreenState extends State { ), child: Text( "Save Changes", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; diff --git a/lib/Screens/itnerary/insurance.dart b/lib/Screens/itnerary/insurance.dart index 43b0d03..dc91e89 100644 --- a/lib/Screens/itnerary/insurance.dart +++ b/lib/Screens/itnerary/insurance.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -304,9 +305,9 @@ class _InsuranceScreenState extends State { children: [ Text( "Insurance Type", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -522,9 +523,9 @@ class _InsuranceScreenState extends State { children: [ Text( "Insurance Type", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -573,9 +574,9 @@ class _InsuranceScreenState extends State { children: [ Text( "Start Date", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -634,9 +635,9 @@ class _InsuranceScreenState extends State { children: [ Text( "End Date", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -713,9 +714,9 @@ class _InsuranceScreenState extends State { children: [ Text( "Comments", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -774,7 +775,7 @@ class _InsuranceScreenState extends State { ), child: Text( "Close", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons @@ -793,7 +794,7 @@ class _InsuranceScreenState extends State { ), child: Text( "Save Changes", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; diff --git a/lib/Screens/itnerary/miscellaneous.dart b/lib/Screens/itnerary/miscellaneous.dart index f7bbca8..47ec8a8 100644 --- a/lib/Screens/itnerary/miscellaneous.dart +++ b/lib/Screens/itnerary/miscellaneous.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -191,9 +192,9 @@ class _MiscellaneousScreenState extends State { children: [ Text( "Special Type", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -279,9 +280,9 @@ class _MiscellaneousScreenState extends State { children: [ Text( "Comments", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -344,7 +345,7 @@ class _MiscellaneousScreenState extends State { ), child: Text( "Close", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons @@ -363,7 +364,7 @@ class _MiscellaneousScreenState extends State { ), child: Text( "Save Changes", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; diff --git a/lib/Screens/itnerary/taxi.dart b/lib/Screens/itnerary/taxi.dart index 496858f..2e22d8d 100644 --- a/lib/Screens/itnerary/taxi.dart +++ b/lib/Screens/itnerary/taxi.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -279,9 +280,9 @@ class _TaxiScreenState extends State { children: [ Text( "Taxi Required For", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -301,9 +302,9 @@ class _TaxiScreenState extends State { children: [ Text( "Number of Passenger", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -351,9 +352,9 @@ class _TaxiScreenState extends State { children: [ Text( "Car Type", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -507,9 +508,9 @@ class _TaxiScreenState extends State { children: [ Text( "City", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -552,9 +553,9 @@ class _TaxiScreenState extends State { children: [ Text( "Location of Pickup", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -597,9 +598,9 @@ class _TaxiScreenState extends State { children: [ Text( "Date", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -649,9 +650,9 @@ class _TaxiScreenState extends State { children: [ Text( "Time", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -700,9 +701,9 @@ class _TaxiScreenState extends State { children: [ Text( "Comments", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -762,7 +763,7 @@ class _TaxiScreenState extends State { ), child: Text( "Close", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons @@ -781,7 +782,7 @@ class _TaxiScreenState extends State { ), child: Text( "Save Changes", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; diff --git a/lib/Screens/itnerary/train.dart b/lib/Screens/itnerary/train.dart index 7d9ed7a..dba5e9b 100644 --- a/lib/Screens/itnerary/train.dart +++ b/lib/Screens/itnerary/train.dart @@ -1,5 +1,6 @@ import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -363,9 +364,9 @@ class _TrainScreenState extends State { children: [ Text( "Train Number", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -519,9 +520,9 @@ class _TrainScreenState extends State { children: [ Text( "Class *", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -574,9 +575,9 @@ class _TrainScreenState extends State { children: [ Text( "From", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -679,9 +680,9 @@ class _TrainScreenState extends State { children: [ Text( "To", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -751,9 +752,9 @@ class _TrainScreenState extends State { children: [ Text( "Date", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -804,9 +805,9 @@ class _TrainScreenState extends State { children: [ Text( "Time", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -856,9 +857,9 @@ class _TrainScreenState extends State { children: [ Text( "Comments", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -917,7 +918,7 @@ class _TrainScreenState extends State { ), child: Text( "Close", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons @@ -936,7 +937,7 @@ class _TrainScreenState extends State { ), child: Text( "Save Changes", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; diff --git a/lib/Screens/itnerary/visa.dart b/lib/Screens/itnerary/visa.dart index b6f5446..eea08a4 100644 --- a/lib/Screens/itnerary/visa.dart +++ b/lib/Screens/itnerary/visa.dart @@ -1,5 +1,6 @@ import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -440,9 +441,9 @@ class _VisaScreenState extends State { children: [ Text( "Type of Visa", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -497,9 +498,9 @@ class _VisaScreenState extends State { children: [ Text( "Country", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -572,9 +573,9 @@ class _VisaScreenState extends State { children: [ Text( "Start Date", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -631,9 +632,9 @@ class _VisaScreenState extends State { children: [ Text( "Comments", - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, color: Color(0xFF575A74)), ), SizedBox(height: 5), @@ -692,7 +693,7 @@ class _VisaScreenState extends State { ), child: Text( "Close", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons @@ -711,7 +712,7 @@ class _VisaScreenState extends State { ), child: Text( "Save Changes", - style: TextStyle(color: Colors.white, fontSize: 14), + style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; diff --git a/lib/Screens/itnerary_list/accomodation_list.dart b/lib/Screens/itnerary_list/accomodation_list.dart index dc3030f..1db208a 100644 --- a/lib/Screens/itnerary_list/accomodation_list.dart +++ b/lib/Screens/itnerary_list/accomodation_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; class AccomodationListWidget extends StatelessWidget { @@ -200,10 +201,10 @@ class AccomodationListWidget extends StatelessWidget { children: [ Text( item["hotel_name"]!, - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w800, - fontFamily: "Archivo"), + color: Color(0xFF575A74)), ), Spacer(), GestureDetector( @@ -230,29 +231,33 @@ class AccomodationListWidget extends StatelessWidget { flex: 2, child: Text( " DestinationCity", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Check (in) ", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Check (out) ", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Comments", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), ], ) @@ -262,31 +267,35 @@ class AccomodationListWidget extends StatelessWidget { children: [ Expanded( flex: 2, - child: Text(item["destination_city"], - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + child: Text( + item["destination_city"], + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( - "${formatDate(item["checkin_date"]!)} ${formatTime(item["checkin_time"]!)}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + "${formatDate(item["checkin_date"]!)} ${formatTime(item["checkin_time"]!)}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( - "${formatDate(item["checkout_date"]!)} ${formatTime(item["checkout_time"]!)}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + "${formatDate(item["checkout_date"]!)} ${formatTime(item["checkout_time"]!)}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), SizedBox(width: 10), Expanded( @@ -354,18 +363,18 @@ class AccomodationListWidget extends StatelessWidget { padding: EdgeInsets.all(8), preferBelow: false, child: Text(value.substring(0, limit) + "...", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), overflow: TextOverflow.ellipsis), ) : Text( value, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), ], @@ -393,8 +402,7 @@ class AccomodationListWidget extends StatelessWidget { children: [ Text( title, - style: TextStyle( - fontWeight: FontWeight.w600, + style: GoogleFonts.poppins( fontSize: 12, ), ), @@ -430,16 +438,18 @@ class AccomodationListWidget extends StatelessWidget { children: [ Text( title, - style: TextStyle( - fontWeight: FontWeight.w600, + style: GoogleFonts.poppins( fontSize: 12, + fontWeight: FontWeight.w600, ), ), SizedBox(width: 8), Expanded( child: Text( "$date, $time", - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), ), ), ], diff --git a/lib/Screens/itnerary_list/bus_list.dart b/lib/Screens/itnerary_list/bus_list.dart index 3603aca..4bdf283 100644 --- a/lib/Screens/itnerary_list/bus_list.dart +++ b/lib/Screens/itnerary_list/bus_list.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; @@ -201,22 +202,25 @@ class BusListWidget extends StatelessWidget { flex: 3, child: Text( " Planned Trips", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 3, child: Text( " Date", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 3, child: Text( " Comments", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), ], ) @@ -227,21 +231,24 @@ class BusListWidget extends StatelessWidget { children: [ Expanded( flex: 3, - child: Text("${item["from"]} - ${item["to"]} ", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + child: Text( + "${item["from"]} - ${item["to"]} ", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), SizedBox(width: 10), Expanded( flex: 3, child: Text( - "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), SizedBox(width: 10), Expanded( @@ -306,18 +313,18 @@ class BusListWidget extends StatelessWidget { padding: EdgeInsets.all(8), preferBelow: false, child: Text(value.substring(0, limit) + "...", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), overflow: TextOverflow.ellipsis), ) : Text( value, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), ], @@ -345,9 +352,9 @@ class BusListWidget extends StatelessWidget { children: [ Text( title, - style: TextStyle( - fontWeight: FontWeight.w600, + style: GoogleFonts.poppins( fontSize: 12, + fontWeight: FontWeight.w600, ), ), SizedBox(width: 8), @@ -369,7 +376,9 @@ class BusListWidget extends StatelessWidget { ) : Text( value, - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), ), ), ], @@ -382,16 +391,18 @@ class BusListWidget extends StatelessWidget { children: [ Text( title, - style: TextStyle( - fontWeight: FontWeight.w600, + style: GoogleFonts.poppins( fontSize: 12, + fontWeight: FontWeight.w600, ), ), SizedBox(width: 8), Expanded( child: Text( "$date, $time", - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), ), ), ], diff --git a/lib/Screens/itnerary_list/flight_list.dart b/lib/Screens/itnerary_list/flight_list.dart index b60b8cf..550d1ce 100644 --- a/lib/Screens/itnerary_list/flight_list.dart +++ b/lib/Screens/itnerary_list/flight_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import '../../services/apiService.dart'; @@ -245,10 +246,10 @@ class _FlightListWidgetState extends State { children: [ Text( item["trip_type"]?.toString() ?? "N/A", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w800, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w800, + ), ), Spacer(), GestureDetector( @@ -268,34 +269,43 @@ class _FlightListWidgetState extends State { color: Colors.blueGrey.shade50, ), SizedBox(height: 4), - Row( - children: [ - Expanded( - flex: 2, - child: Text( - "Class", - style: TextStyle(fontSize: 11, fontFamily: "Archivo"), - )), - Expanded( - flex: 2, - child: Text( - "Sector", - style: TextStyle(fontSize: 11, fontFamily: "Archivo"), - )), - Expanded( - flex: 2, - child: Text( - "Date", - style: TextStyle(fontSize: 11, fontFamily: "Archivo"), - )), - Expanded( - flex: 2, - child: Text( - "Time", - style: TextStyle(fontSize: 11, fontFamily: "Archivo"), - )), - ], - ), + if (isDesktop) + Row( + children: [ + Expanded( + flex: 2, + child: Text( + "Class", + style: GoogleFonts.poppins( + fontSize: 11, + ), + )), + Expanded( + flex: 4, + child: Text( + "Sector", + style: GoogleFonts.poppins( + fontSize: 11, + ), + )), + Expanded( + flex: 2, + child: Text( + "Date", + style: GoogleFonts.poppins( + fontSize: 11, + ), + )), + Expanded( + flex: 2, + child: Text( + "Time", + style: GoogleFonts.poppins( + fontSize: 11, + ), + )), + ], + ), SizedBox(height: 4), // Trip Rows @@ -318,38 +328,42 @@ class _FlightListWidgetState extends State { child: Text( getRequestForClass(trip["class"].toString()) ?? "N/A", - style: TextStyle( - fontSize: 12, fontWeight: FontWeight.bold), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w700, + ), ), ), Expanded( - flex: 2, + flex: 4, child: Text( "$fromPlaceCountry (from) - (to) $toPlaceCountry", // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w700, + ), ), ), Expanded( flex: 2, child: Text( formatDate(trip["date"] ?? ""), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w700, + ), ), ), Expanded( flex: 2, - child: Text(formatTime(trip["time"] ?? ""), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - fontFamily: "Archivo")), + child: Text( + formatTime(trip["time"] ?? ""), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), ), ], ), @@ -376,7 +390,7 @@ class _FlightListWidgetState extends State { "Class", getRequestForClass(trip["class"].toString()) ?? "N/A"), - _buildKeyValueRow("From - To", + _buildKeyValueRow("Sector", "${trip["from_place"] ?? "N/A"} - ${trip["to_place"] ?? "N/A"}"), _buildKeyValueRow( "Date", formatDate(trip["date"] ?? "")), @@ -405,13 +419,18 @@ class _FlightListWidgetState extends State { width: 80, // adjust width as needed child: Text( "$key:", - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w700, + ), ), ), Expanded( child: Text( value, - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), ), ), ], diff --git a/lib/Screens/itnerary_list/forex_list.dart b/lib/Screens/itnerary_list/forex_list.dart index aa8b1a3..0b7e899 100644 --- a/lib/Screens/itnerary_list/forex_list.dart +++ b/lib/Screens/itnerary_list/forex_list.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; @@ -209,10 +210,10 @@ class ForexListWidget extends StatelessWidget { children: [ Text( getRequestForCountry(item["country_code"]!.toString()), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w700, + ), ), Spacer(), GestureDetector( @@ -239,22 +240,25 @@ class ForexListWidget extends StatelessWidget { flex: 2, child: Text( "Currency", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Duration", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Time Period ", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), // Expanded( // flex: 2, @@ -267,8 +271,9 @@ class ForexListWidget extends StatelessWidget { flex: 2, child: Text( " Comments", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), ], ) @@ -280,10 +285,10 @@ class ForexListWidget extends StatelessWidget { flex: 2, child: Text( item["currency"], - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), SizedBox(width: 10), @@ -291,10 +296,10 @@ class ForexListWidget extends StatelessWidget { flex: 2, child: Text( item["duration"], - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), SizedBox(width: 10), @@ -302,10 +307,10 @@ class ForexListWidget extends StatelessWidget { flex: 2, child: Text( "${formatDate(item["start_date"]!)} - ${formatDate(item["end_date"]!)}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), SizedBox(width: 10), @@ -386,18 +391,18 @@ class ForexListWidget extends StatelessWidget { padding: EdgeInsets.all(8), preferBelow: false, child: Text(value.substring(0, limit) + "...", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), overflow: TextOverflow.ellipsis), ) : Text( value, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), ], @@ -425,9 +430,9 @@ class ForexListWidget extends StatelessWidget { children: [ Text( title, - style: TextStyle( - fontWeight: FontWeight.w600, + style: GoogleFonts.poppins( fontSize: 12, + fontWeight: FontWeight.w600, ), ), SizedBox(width: 8), @@ -444,12 +449,16 @@ class ForexListWidget extends StatelessWidget { padding: EdgeInsets.all(8), preferBelow: false, child: Text(value.substring(0, limit) + "...", - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), overflow: TextOverflow.ellipsis), ) : Text( value, - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), ), ), ], diff --git a/lib/Screens/itnerary_list/insurance_list.dart b/lib/Screens/itnerary_list/insurance_list.dart index e33118d..5b73362 100644 --- a/lib/Screens/itnerary_list/insurance_list.dart +++ b/lib/Screens/itnerary_list/insurance_list.dart @@ -1,6 +1,7 @@ import 'dart:js_interop'; import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; class InsuranceListWidget extends StatelessWidget { @@ -208,22 +209,25 @@ class InsuranceListWidget extends StatelessWidget { flex: 2, child: Text( " InsuranceType", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Time Period ", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Comments", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), ], ) @@ -234,22 +238,24 @@ class InsuranceListWidget extends StatelessWidget { Expanded( flex: 2, child: Text( - getRequestForInsuranceType( - item["type_of_insurance"]!.toString()), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + getRequestForInsuranceType( + item["type_of_insurance"]!.toString()), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( - "${formatDate(item["start_date"]!)} - ${formatDate(item["end_date"]!)}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + "${formatDate(item["start_date"]!)} - ${formatDate(item["end_date"]!)}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), SizedBox(width: 10), Expanded( @@ -314,18 +320,18 @@ class InsuranceListWidget extends StatelessWidget { padding: EdgeInsets.all(8), preferBelow: false, child: Text(value.substring(0, limit) + "...", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), overflow: TextOverflow.ellipsis), ) : Text( value, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), ], @@ -353,7 +359,10 @@ class InsuranceListWidget extends StatelessWidget { children: [ Text( title, - style: TextStyle(fontWeight: FontWeight.w600, fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), SizedBox(width: 8), Expanded( @@ -370,13 +379,17 @@ class InsuranceListWidget extends StatelessWidget { preferBelow: false, child: Text( value.substring(0, limit) + "...", - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), overflow: TextOverflow.ellipsis, ), ) : Text( value, - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), ), ), ], diff --git a/lib/Screens/itnerary_list/miscellaneous_list.dart b/lib/Screens/itnerary_list/miscellaneous_list.dart index 17f3a07..ecd1f1d 100644 --- a/lib/Screens/itnerary_list/miscellaneous_list.dart +++ b/lib/Screens/itnerary_list/miscellaneous_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; class MiscellaneousListWidget extends StatelessWidget { final List> miscellaneousList; @@ -175,15 +176,17 @@ class MiscellaneousListWidget extends StatelessWidget { flex: 2, child: Text( "Special Request", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 3, child: Text( " Comments", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), ], ) @@ -196,19 +199,21 @@ class MiscellaneousListWidget extends StatelessWidget { child: Text( getRequestValue( item["special_request"]?.toString()), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), Expanded( flex: 3, - child: Text(item["comments"], - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + child: Text( + item["comments"], + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), ], ) @@ -262,18 +267,18 @@ class MiscellaneousListWidget extends StatelessWidget { padding: EdgeInsets.all(8), preferBelow: false, child: Text(value.substring(0, limit) + "...", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), overflow: TextOverflow.ellipsis), ) : Text( value, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), ], @@ -301,7 +306,10 @@ class MiscellaneousListWidget extends StatelessWidget { children: [ Text( title, - style: TextStyle(fontWeight: FontWeight.w600, fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), SizedBox(width: 8), Expanded( diff --git a/lib/Screens/itnerary_list/taxi_list.dart b/lib/Screens/itnerary_list/taxi_list.dart index bae6245..468912c 100644 --- a/lib/Screens/itnerary_list/taxi_list.dart +++ b/lib/Screens/itnerary_list/taxi_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; class TaxiListWidget extends StatelessWidget { @@ -201,12 +202,11 @@ class TaxiListWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ Text( - "${getRequestForTaxiClass(item["car_type"]!.toString())} For ${item["no_of_passengers"]!} Passenger", - style: TextStyle( - fontSize: 14, + "${getRequestForTaxiClass(item["car_type"]!.toString())} For ${item["no_of_passengers"]!} Passenger", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w800, - fontFamily: "Archivo"), - ), + )), Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Taxi"), @@ -232,29 +232,33 @@ class TaxiListWidget extends StatelessWidget { flex: 2, child: Text( "City", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Location", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Date", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( "Comments", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), ], ) @@ -265,30 +269,35 @@ class TaxiListWidget extends StatelessWidget { children: [ Expanded( flex: 2, - child: Text("${item["destination_city"]}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), - ), - SizedBox(width: 10), - Expanded( - flex: 2, - child: Text("${item["location_of_pickup"]}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + child: Text( + "${item["destination_city"]}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( - "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + "${item["location_of_pickup"]}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), SizedBox(width: 10), Expanded( @@ -354,18 +363,18 @@ class TaxiListWidget extends StatelessWidget { padding: EdgeInsets.all(8), preferBelow: false, child: Text(value.substring(0, limit) + "...", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), overflow: TextOverflow.ellipsis), ) : Text( value, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), ], @@ -417,7 +426,9 @@ class TaxiListWidget extends StatelessWidget { ) : Text( value, - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), ), ), ], @@ -430,16 +441,18 @@ class TaxiListWidget extends StatelessWidget { children: [ Text( title, - style: TextStyle( - fontWeight: FontWeight.w600, + style: GoogleFonts.poppins( fontSize: 12, + fontWeight: FontWeight.w600, ), ), SizedBox(width: 8), Expanded( child: Text( "$date, $time", - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), ), ), ], diff --git a/lib/Screens/itnerary_list/train_list.dart b/lib/Screens/itnerary_list/train_list.dart index 73d9ac3..01e53ed 100644 --- a/lib/Screens/itnerary_list/train_list.dart +++ b/lib/Screens/itnerary_list/train_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import '../../services/apiService.dart'; @@ -288,10 +289,10 @@ class _TrainListWidgetState extends State { children: [ Text( item["train_no"]?.toString() ?? "N/A", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w800, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w800, + ), ), Spacer(), GestureDetector( @@ -318,29 +319,33 @@ class _TrainListWidgetState extends State { flex: 2, child: Text( " Class", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Planned Trips", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Date", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Comments", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), ], ) @@ -350,33 +355,36 @@ class _TrainListWidgetState extends State { ? Row( children: [ Expanded( - flex: 2, - child: Text( + flex: 2, + child: Text( "${getRequestForTrainClass(item["class"]!.toString())} ", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), - ), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + )), SizedBox(width: 10), Expanded( flex: 2, - child: Text("$fromPlaceCountry - $toPlaceCountry", + child: Text( + "$fromPlaceCountry - $toPlaceCountry", - // "${item["from_station"]!} - ${(item["to_station"])}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"))), + // "${item["from_station"]!} - ${(item["to_station"])}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + )), SizedBox(width: 10), Expanded( flex: 2, child: Text( - "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo")), + "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), SizedBox(width: 10), Expanded( @@ -482,9 +490,9 @@ class _TrainListWidgetState extends State { children: [ Text( title, - style: TextStyle( - fontWeight: FontWeight.w600, + style: GoogleFonts.poppins( fontSize: 12, + fontWeight: FontWeight.w600, ), ), SizedBox(width: 8), @@ -501,12 +509,16 @@ class _TrainListWidgetState extends State { padding: EdgeInsets.all(8), preferBelow: false, child: Text(value.substring(0, limit) + "...", - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), overflow: TextOverflow.ellipsis), ) : Text( value, - style: TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + ), ), ), ], diff --git a/lib/Screens/itnerary_list/visa_list.dart b/lib/Screens/itnerary_list/visa_list.dart index c8d666d..4a0aff9 100644 --- a/lib/Screens/itnerary_list/visa_list.dart +++ b/lib/Screens/itnerary_list/visa_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; class VisaListWidget extends StatelessWidget { @@ -279,29 +280,33 @@ class VisaListWidget extends StatelessWidget { flex: 2, child: Text( " Visa Type", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Country", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Date", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), Expanded( flex: 2, child: Text( " Comments", - style: TextStyle( - fontSize: 11, fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 11, + ), )), ], ) @@ -314,10 +319,10 @@ class VisaListWidget extends StatelessWidget { child: Text( getRequestForVisa( item["type_of_visa"]!.toString()), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), SizedBox(width: 10), @@ -326,10 +331,10 @@ class VisaListWidget extends StatelessWidget { child: Text( getRequestForCountry( item["country_code"]!.toString()), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), SizedBox(width: 10), @@ -337,10 +342,10 @@ class VisaListWidget extends StatelessWidget { flex: 2, child: Text( formatDate(item["start_date"]!), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), ), SizedBox(width: 10), @@ -448,7 +453,10 @@ class VisaListWidget extends StatelessWidget { children: [ Text( title, - style: TextStyle(fontWeight: FontWeight.w600, fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), SizedBox(width: 8), Expanded( diff --git a/lib/Screens/organization/orgSetup.dart b/lib/Screens/organization/orgSetup.dart index 2d1ab3a..4cd4c74 100644 --- a/lib/Screens/organization/orgSetup.dart +++ b/lib/Screens/organization/orgSetup.dart @@ -330,7 +330,8 @@ class _OrgSetUpState extends State { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( - backgroundColor: Colors.white, + // backgroundColor: Colors.white, + backgroundColor: Color(0xFFf5f5f5), appBar: CustomAppBar(isDesktop: isDesktop), drawer: CustomDrawer(isDesktop: false), body: Padding( @@ -341,7 +342,7 @@ class _OrgSetUpState extends State { vertical: MediaQuery.of(context).size.height * 0, // 5% of screen height as vertical padding ) - : EdgeInsets.all(8), + : EdgeInsets.all(0), child: Row( children: [ // if (isDesktop) CustomDrawer(isDesktop: true), @@ -355,6 +356,11 @@ class _OrgSetUpState extends State { Widget buildOrganizationLayout(isDesktop) { return Container( + margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), // decoration: BoxDecoration( // // color: Colors.amber, // color: bodyColor, @@ -368,12 +374,12 @@ class _OrgSetUpState extends State { Expanded( child: Container( // color: Colors.redAccent, - color: bodyColor, + // color: bodyColor, child: buildOrgLayout(isDesktop), ), ), Container( - padding: const EdgeInsets.all(8), + padding: const EdgeInsets.all(5), color: Colors.white, child: isDesktop ? Row( diff --git a/lib/Screens/plans/create_plans.dart b/lib/Screens/plans/create_plans.dart index 47c59d0..e0ec347 100644 --- a/lib/Screens/plans/create_plans.dart +++ b/lib/Screens/plans/create_plans.dart @@ -1237,10 +1237,10 @@ class CreateNewPlansState extends State { fontFamily: "Archivo", fontWeight: FontWeight.w600, fontSize: 11), - decoration: const InputDecoration( + decoration: InputDecoration( hintText: "Please enter remarks...", - hintStyle: TextStyle( - color: Colors.grey, fontSize: 15), + hintStyle: GoogleFonts.poppins( + fontSize: 13, color: Colors.grey), border: InputBorder.none, isDense: true, ), @@ -1263,8 +1263,8 @@ class CreateNewPlansState extends State { ), Text( - "Planning This Trip *", // Your label - style: TextStyle( + "Planning This Trip For*", // Your label + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1281,13 +1281,15 @@ class CreateNewPlansState extends State { children: _buildPlanTrip(isMobile), ), SizedBox(height: 7), - Text(otherUserName ?? userName ?? " ", // Your label - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w400, - color: widget.layoutColor, - )), + Text( + otherUserName ?? userName ?? " ", // Your label + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w400, + color: widget.layoutColor, + ), + ) // Text.rich( // TextSpan( // text: "Trip Planned For : ", // Static text @@ -1393,7 +1395,7 @@ class CreateNewPlansState extends State { children: [ Text( validationErrors["services"]!, - style: TextStyle( + style: GoogleFonts.poppins( color: Colors.red, fontSize: 10, fontWeight: FontWeight.bold), @@ -1463,7 +1465,8 @@ class CreateNewPlansState extends State { children: [ Text( "Trip Type *", // Your label - style: TextStyle( + + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -1488,7 +1491,7 @@ class CreateNewPlansState extends State { horizontal: 10, vertical: 8), child: Text( item['dropdown_value'] ?? '', - style: const TextStyle( + style: GoogleFonts.poppins( fontSize: 12, // 👈 Smaller font size color: Colors.black, ), @@ -1505,14 +1508,16 @@ class CreateNewPlansState extends State { ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { - return const Text( + return Text( "Select Trip Type", - style: TextStyle(color: Colors.grey, fontSize: 13), + style: + GoogleFonts.poppins(color: Colors.grey, fontSize: 13), ); } return Text( selectedItem['dropdown_value'] ?? '', - style: const TextStyle(fontSize: 12, color: Colors.black), + style: + GoogleFonts.poppins(fontSize: 12, color: Colors.black), ); }, selectedItem: tripTypeList.firstWhere( @@ -1558,7 +1563,7 @@ class CreateNewPlansState extends State { padding: EdgeInsets.only(top: 4), child: Text( validationErrors["trip_type"]!, - style: TextStyle(color: Colors.red, fontSize: 10), + style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), ), ), ], @@ -1573,13 +1578,11 @@ class CreateNewPlansState extends State { // height: 5, // ), Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "Is Billable ", // Your label - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), + Text("Is Billable ", // Your label + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74))), SizedBox(height: 5), CustomTextFieldWrapper( @@ -1601,13 +1604,11 @@ class CreateNewPlansState extends State { return Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), - child: Text( - item['dropdown_value'] ?? '', - style: const TextStyle( - fontSize: 12, // 👈 Smaller font size - color: Colors.black, - ), - ), + child: Text(item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + )), ); }, ), @@ -1629,18 +1630,13 @@ class CreateNewPlansState extends State { // items: purposeList.cast>(), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { - return const Text( - "Select Billable", // fallback text - style: TextStyle(fontSize: 12, color: Colors.grey), - ); + return Text("Select Billable", // fallback text + style: GoogleFonts.poppins( + fontSize: 12, color: Colors.grey)); } - return Text( - selectedItem['dropdown_value'] ?? '', - style: const TextStyle( - fontSize: 12, // 👈 Small font size for selected item - color: Colors.black, - ), - ); + return Text(selectedItem['dropdown_value'] ?? '', + style: + GoogleFonts.poppins(fontSize: 12, color: Colors.black)); }, items: purposeList.map>((item) { @@ -1751,7 +1747,7 @@ class CreateNewPlansState extends State { // value: null, value: "1", child: Text("No options available", - style: TextStyle(color: Colors.grey)), + style: GoogleFonts.poppins(color: Colors.grey)), ), ); } @@ -1802,13 +1798,12 @@ class CreateNewPlansState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "Cost Center *", // Your label - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), + Text("Cost Center *", // Your label + + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74))), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: false, // Dropdown doesn't use focus @@ -1831,14 +1826,16 @@ class CreateNewPlansState extends State { fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 250), searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search Cost Center...", - hintStyle: - TextStyle(fontSize: 13, color: Colors.grey), - contentPadding: - EdgeInsets.symmetric(horizontal: 10), - ), - style: TextStyle(fontSize: 13)), + decoration: InputDecoration( + hintText: "Search Cost Center...", + // hintStyle: TextStyle(fontSize: 13, color: Colors.grey), + hintStyle: GoogleFonts.poppins( + fontSize: 13, color: Colors.grey), + contentPadding: + EdgeInsets.symmetric(horizontal: 10), + ), + style: GoogleFonts.poppins(fontSize: 13), + ), menuProps: MenuProps( backgroundColor: Colors.white, ), @@ -1847,9 +1844,9 @@ class CreateNewPlansState extends State { horizontal: 8.0, vertical: 6.0), child: Text( item, - style: TextStyle( - fontSize: - 13), // 👈 Set your desired text size here + style: GoogleFonts.poppins( + fontSize: 13, + ), // 👈 Set your desired text size here ), ), ), @@ -1866,16 +1863,16 @@ class CreateNewPlansState extends State { dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { - return const Text( + return Text( "Select Purpose", // fallback text - style: TextStyle(fontSize: 12, color: Colors.grey), + style: GoogleFonts.poppins( + fontSize: 12, color: Colors.grey), ); } return Text( selectedItem ?? '', - style: const TextStyle( - fontSize: - 12, // 👈 Small font size for selected item + style: GoogleFonts.poppins( + fontSize: 12, color: Colors.black, ), ); @@ -1932,13 +1929,12 @@ class CreateNewPlansState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "Purpose of Trip *", // Your label - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), + Text("Purpose of Trip *", // Your label + + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74))), SizedBox(height: 5), CustomTextFieldWrapper( width: isDesktop @@ -1967,14 +1963,17 @@ class CreateNewPlansState extends State { fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 250), searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search Purpose...", - hintStyle: - TextStyle(fontSize: 13, color: Colors.grey), - contentPadding: - EdgeInsets.symmetric(horizontal: 10), - ), - style: TextStyle(fontSize: 13)), + decoration: InputDecoration( + hintText: "Search Purpose...", + hintStyle: GoogleFonts.poppins( + fontSize: 13, color: Colors.grey), + contentPadding: + EdgeInsets.symmetric(horizontal: 10), + ), + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), menuProps: MenuProps( backgroundColor: Colors.white, ), @@ -1983,9 +1982,9 @@ class CreateNewPlansState extends State { horizontal: 8.0, vertical: 6.0), child: Text( item['dropdown_value'], - style: TextStyle( - fontSize: - 13), // 👈 Set your desired text size here + style: GoogleFonts.poppins( + fontSize: 13, + ), // 👈 Set your desired text size here ), ), ), @@ -2008,14 +2007,15 @@ class CreateNewPlansState extends State { dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { - return const Text( + return Text( "Select Purpose", // fallback text - style: TextStyle(fontSize: 12, color: Colors.grey), + style: GoogleFonts.poppins( + fontSize: 13, color: Colors.grey), ); } return Text( selectedItem['dropdown_value'] ?? '', - style: const TextStyle( + style: GoogleFonts.poppins( fontSize: 12, // 👈 Small font size for selected item color: Colors.black, @@ -2076,7 +2076,7 @@ class CreateNewPlansState extends State { children: [ Text( "Functional Department", // Your label - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), @@ -2111,12 +2111,15 @@ class CreateNewPlansState extends State { searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search department...", - hintStyle: - TextStyle(fontSize: 13, color: Colors.grey), + hintStyle: GoogleFonts.poppins( + fontSize: 13, color: Colors.grey), contentPadding: EdgeInsets.symmetric(horizontal: 10), ), - style: TextStyle(fontSize: 13)), + style: GoogleFonts.poppins( + fontSize: + 13, // 👈 Small font size for selected item + )), menuProps: MenuProps( backgroundColor: Colors.white, ), @@ -2125,9 +2128,10 @@ class CreateNewPlansState extends State { horizontal: 8.0, vertical: 6.0), child: Text( item['dropdown_value'], - style: TextStyle( - fontSize: - 13), // 👈 Set your desired text size here + style: GoogleFonts.poppins( + fontSize: + 13, // 👈 Small font size for selected item + ), // 👈 Set your desired text size here ), ), ), @@ -2157,7 +2161,7 @@ class CreateNewPlansState extends State { } return Text( selectedItem['dropdown_value'] ?? '', - style: const TextStyle( + style: GoogleFonts.poppins( fontSize: 12, // 👈 Small font size for selected item color: Colors.black, @@ -2451,51 +2455,25 @@ class CreateNewPlansState extends State { children: [ Text( "Description", // Your label - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldWrapper( - isFocused: false, - // isFocused: _isdescriptionFocused, - isDesktop: widget.isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.74 - : MediaQuery.of(context).size.width * 0.85, - child: SizedBox( - height: 35, - child: TextField( - // focusNode: _descriptionFocusNode, - controller: _descriptionController, - style: TextStyle(fontSize: 12), - enabled: !widget.isViewMode, - decoration: InputDecoration( - labelText: "Description", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), // CustomTextFieldWrapper( - // isFocused: _isdescriptionFocused, + // isFocused: false, + // // isFocused: _isdescriptionFocused, + // isDesktop: widget.isDesktop, // width: isDesktop // ? MediaQuery.of(context).size.width * 0.4 // : MediaQuery.of(context).size.width * 0.85, - // isDesktop: widget.isDesktop, // child: SizedBox( // height: 35, - // child: - // TextField( - // focusNode: _descriptionFocusNode, + // child: TextField( + // // focusNode: _descriptionFocusNode, // controller: _descriptionController, - // // maxLines: 1, - // // keyboardType: TextInputType.multiline, // style: TextStyle(fontSize: 12), // enabled: !widget.isViewMode, // decoration: InputDecoration( @@ -2503,11 +2481,38 @@ class CreateNewPlansState extends State { // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric(vertical: 1), + // contentPadding: EdgeInsets.symmetric(vertical: 16), // ), // ), // ), // ), + CustomTextFieldWrapper( + isFocused: _isdescriptionFocused, + // width: isDesktop + // ? MediaQuery.of(context).size.width * 0.38 + // : MediaQuery.of(context).size.width * 0.85, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 55, + child: TextField( + focusNode: _descriptionFocusNode, + controller: _descriptionController, + maxLines: 2, + keyboardType: TextInputType.multiline, + style: TextStyle(fontSize: 12), + enabled: !widget.isViewMode, + decoration: InputDecoration( + labelText: "Description", + labelStyle: GoogleFonts.poppins( + fontSize: 12, color: Colors.grey), + // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 1), + ), + ), + ), + ), ], ), ], @@ -2582,8 +2587,9 @@ class CreateNewPlansState extends State { ), enabled: !widget.isViewMode, decoration: InputDecoration( - labelText: "Trip Name", - labelStyle: TextStyle(fontSize: 14, color: Colors.grey), + labelText: "Trip Name *", + + labelStyle: GoogleFonts.poppins(fontSize: 14, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), diff --git a/lib/Screens/plans/dynamic_itinerary_stepper.dart b/lib/Screens/plans/dynamic_itinerary_stepper.dart index 5337a7e..9520eeb 100644 --- a/lib/Screens/plans/dynamic_itinerary_stepper.dart +++ b/lib/Screens/plans/dynamic_itinerary_stepper.dart @@ -7,6 +7,7 @@ import 'package:frontend/Screens/itnerary_list/bus_list.dart'; import 'package:frontend/Screens/itnerary_list/flight_list.dart'; import 'package:frontend/Screens/itnerary_list/taxi_list.dart'; import 'package:frontend/Screens/itnerary_list/train_list.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:responsive_builder/responsive_builder.dart'; import '../../services/apiService.dart'; @@ -931,12 +932,17 @@ class DynamicItineraryState extends State { children: [ Text( name, + + // style: GoogleFonts.poppins( fontSize: 12, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74)) + style: TextStyle( fontSize: 14, color: isOptionSelected ? Color(0xFF114D8B) : Color(0xFF475569), - fontFamily: "Archivo", + fontFamily: "Inter", fontWeight: isOptionSelected ? FontWeight.bold : FontWeight.w500, ), diff --git a/lib/Screens/plans/list_plans.dart b/lib/Screens/plans/list_plans.dart index 0faab5a..88ddcf9 100644 --- a/lib/Screens/plans/list_plans.dart +++ b/lib/Screens/plans/list_plans.dart @@ -408,9 +408,8 @@ class _ListPlansState extends State { color: Colors.grey.shade300, width: 1), ), ), - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontFamily: "Inter", ), ), ), @@ -486,10 +485,10 @@ class _ListPlansState extends State { mainAxisSize: MainAxisSize.min, // Ensures content fits nicely children: [ - Text( - "Add New Trip", - style: TextStyle(fontSize: isDesktop ? 13 : 11), - ), + Text("Add New Trip", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 13 : 11, + )), SizedBox(width: 8), // spacing between icon and text Icon( Icons.add_circle_outline_rounded, @@ -534,9 +533,8 @@ class _ListPlansState extends State { color: Colors.grey.shade300, width: 1), ), ), - style: TextStyle( + style: GoogleFonts.poppins( fontSize: 12, - fontFamily: "Inter", ), ), ), @@ -613,58 +611,55 @@ class _ListPlansState extends State { horizontalInside: BorderSide( width: 0.5, color: Colors.grey.shade200), ), - columns: const [ + columns: [ DataColumn( - label: Text('Trip Id', - style: TextStyle( - // color: Colors.grey, - // color: Color(0xFF9E9DBD), - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Trip Name', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Employee Code', - style: TextStyle( - // color: Color(0xFF9E9DBD), - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Traveller', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Trip Type', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Created On', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Status', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Actions', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600), + )), ], rows: paginatedPlans.map((plan) { return DataRow(cells: [ diff --git a/lib/Screens/policy/policy.dart b/lib/Screens/policy/policy.dart index e8444aa..2a2c849 100644 --- a/lib/Screens/policy/policy.dart +++ b/lib/Screens/policy/policy.dart @@ -383,7 +383,7 @@ class _PolicyState extends State { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( - backgroundColor: Colors.white, + backgroundColor: Color(0xFFf5f5f5), appBar: CustomAppBar(isDesktop: isDesktop), drawer: CustomDrawer(isDesktop: false), body: Padding( @@ -453,6 +453,11 @@ class _PolicyState extends State { Widget buildData(bool isDesktop, context) { return Container( + margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), // decoration: BoxDecoration( // color: Color(0xFFE1F5FE), diff --git a/lib/Screens/policy/policy_list.dart b/lib/Screens/policy/policy_list.dart index 4646778..5b93fc0 100644 --- a/lib/Screens/policy/policy_list.dart +++ b/lib/Screens/policy/policy_list.dart @@ -147,7 +147,7 @@ class _PolicyListState extends State { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( - backgroundColor: Colors.white54, + backgroundColor: Color(0xFFf5f5f5), appBar: CustomAppBar(isDesktop: isDesktop), drawer: CustomDrawer(isDesktop: false), body: Padding( @@ -158,7 +158,7 @@ class _PolicyListState extends State { vertical: MediaQuery.of(context).size.height * 0, // 5% of screen height as vertical padding ) - : EdgeInsets.all(8), + : EdgeInsets.all(0), child: Row( children: [ // if (isDesktop) CustomDrawer(isDesktop: true), @@ -172,6 +172,11 @@ class _PolicyListState extends State { Widget buildGroupList(bool isDesktop) { return Container( + margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, + padding: const EdgeInsets.only(left: 10, right: 10, top: 8), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), // decoration: BoxDecoration( // // color: Colors.amber, // color: Color(0xFFE1F5FE), diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index fcc5f9b..df40e86 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -8,9 +8,13 @@ import 'package:dropdown_search/dropdown_search.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; +import 'package:frontend/Screens/userManagement/create_user/personal_details.dart'; +import 'package:frontend/Screens/userManagement/create_user/traveller_details.dart'; import 'package:frontend/utils/auth_utils.dart'; import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:http_parser/http_parser.dart' as http_parser; import 'package:intl/intl.dart'; @@ -25,20 +29,28 @@ import '../../../services/apiService.dart'; import '../../../widgets/custom_text_field.dart'; import '../../../widgets/custom_text_forex.dart'; import '../../../widgets/custom_user_form.dart'; +import 'office_details.dart'; -class CreateUserForm extends StatefulWidget { +class CreateUserFormDetials extends StatefulWidget { @override - _CreateUserFormState createState() => _CreateUserFormState(); + _CreateUserFormDetialsState createState() => _CreateUserFormDetialsState(); } -class _CreateUserFormState extends State { +class _CreateUserFormDetialsState extends State { final ApiService apiService = ApiService(); + final GlobalKey personalDetailsKey = + GlobalKey(); + + late TabController _tabController; + String? userId; String? orgId; String? token; + late bool isapiselectedUser = false; + // Map to store controllers dynamically final Map controllers = {}; bool isViewMode = false; @@ -60,8 +72,12 @@ class _CreateUserFormState extends State { Map errorMessages = {}; + String? selectedTab; + String? selectedCountry; String? selectedGender; + // String? selectedGender = personalDetailsKey.currentState?.selectedGender; + String? selectedUserType; String? selectedRole; String? selectedLevel; @@ -119,6 +135,8 @@ class _CreateUserFormState extends State { "date_of_birth": controllers["dob"]?.text, "address": controllers["address"]?.text, "gender": selectedGender, + // "gender": personalDetailsKey.currentState?.selectedGender, + "postal_code": controllers["postalCode"]?.text, "country_code": selectedCountry, "employee_code": controllers["employeeCode"]?.text, @@ -153,6 +171,10 @@ class _CreateUserFormState extends State { setState(() { // ✅ Wrap in setState to update the UI + if (apiselectedUser != null && apiselectedUser!.isNotEmpty) { + isapiselectedUser = true; + } + controllers["Fname"]?.text = apiselectedUser?["first_name"] ?? ""; controllers["Lname"]?.text = apiselectedUser?["last_name"] ?? ""; controllers["email"]?.text = apiselectedUser?["email"] ?? ""; @@ -199,6 +221,9 @@ class _CreateUserFormState extends State { if (apiselectedUser?["role_id"] != null) { selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? ""; } + if (apiselectedUser?["group_id"] != null) { + selectedLevel = apiselectedUser?["group_id"]?.toString().trim() ?? ""; + } // selectedDepartment = // apiselectedUser?["department_id"]?.toString().trim() ?? ""; @@ -248,6 +273,8 @@ class _CreateUserFormState extends State { @override void initState() { super.initState(); + selectedTab = "personal"; + WidgetsFlutterBinding.ensureInitialized(); // Step 1: Set 'reloaded' flag before page unload @@ -421,11 +448,6 @@ class _CreateUserFormState extends State { return prefs.getString('auth_token'); } - // Future getUserId() async { - // final prefs = await SharedPreferences.getInstance(); - // return prefs.getString('userId'); - // } - Future getUserId() async { final prefs = await SharedPreferences.getInstance(); final userData = prefs.getString('user_data'); @@ -448,6 +470,43 @@ class _CreateUserFormState extends State { super.dispose(); } + void handleNext() async { + print("USR Detail Submit"); + printFormData(); + + Map data = userDetials; + + if (!isValidData(data)) { + print("USERDETAILS : $userDetials"); + print("Validation Failed: Required fields are missing."); + setState(() {}); + return; // Stop execution if validation fails + } else { + print("USERDETAILS : $userDetials"); + + final tabs = { + "personal": "Personal Details", + "office": "Office Details", + // "travel": "Travel Details", + }; + + final tabKeys = tabs.keys.toList(); // ["personal", "office", "travel"] + + final currentIndex = tabKeys.indexOf(selectedTab ?? "personal"); + + if (currentIndex < tabKeys.length - 1) { + // Move to next tab + setState(() { + selectedTab = tabKeys[currentIndex + 1]; + }); + } else { + // Already at last tab (travel), maybe submit form or show done message + print("All tabs completed!"); + // You can trigger full form submit here + } + } + } + void handleSubmit() async { print("USR Detail Submit"); printFormData(); @@ -663,7 +722,7 @@ class _CreateUserFormState extends State { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( - backgroundColor: Colors.white, + backgroundColor: Color(0xFFf5f5f5), // appBar: isDesktop ? null : const CustomAppBar(title: 'Create User '), // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), appBar: CustomAppBar(isDesktop: isDesktop), @@ -676,15 +735,9 @@ class _CreateUserFormState extends State { vertical: MediaQuery.of(context).size.height * 0, // 5% of screen height as vertical padding ) - : EdgeInsets.all(8), + : EdgeInsets.all(0), child: Row( children: [ - // if (isDesktop) CustomDrawer(isDesktop: true), - // const Expanded(child: Center(child: Text("User Details Content"))), - // Expanded( - // child: _buildUserDetails(isDesktop), - // ), - Expanded(child: buildData(isDesktop, context)), ], ), @@ -695,41 +748,18 @@ class _CreateUserFormState extends State { Widget buildData(bool isDesktop, context) { return Container( - // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), - // decoration: BoxDecoration( - // // color: Colors.amber, - // // color: bodyColor, - // color: Color(0xFFE1F5FE), - // border: Border.all( - // color: Colors.white, - // // color: Color(0xFFF7F7FB), - // width: 3.5)), + margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, + padding: const EdgeInsets.only(left: 10, right: 10, top: 8), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), child: Column( children: [ Expanded( child: Container( - // margin: isDesktop - // ? EdgeInsets.all(10.0) - // : EdgeInsets.only( - // left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), - // padding: const EdgeInsets.all(10), height: isDesktop ? MediaQuery.of(context).size.height * 0.98 : MediaQuery.of(context).size.height, - // decoration: BoxDecoration( - // border: isDesktop - // ? Border.all( - // width: 2, - // color: Colors.white, - // // color: Color(0xFFF7F7FB), - // ) - // : null, - // color: Colors.white, - // // color: Color(0xFFF7F7FB), - // - // // color: Colors.amber, - // ), - // color: bodyColor, child: SingleChildScrollView( child: Padding( padding: EdgeInsets.all(0.0), @@ -745,11 +775,15 @@ class _CreateUserFormState extends State { child: isDesktop ? Row( mainAxisAlignment: MainAxisAlignment.end, - children: _buildSubmit(isDesktop, layoutColor!), + children: selectedTab == "office" + ? _buildSubmit(isDesktop, layoutColor!) + : _buildNext(isDesktop, layoutColor!), ) : Row( mainAxisAlignment: MainAxisAlignment.center, - children: _buildSubmit(isDesktop, layoutColor!), + children: selectedTab == "office" + ? _buildSubmit(isDesktop, layoutColor!) + : _buildNext(isDesktop, layoutColor!), )), ) ], @@ -757,491 +791,41 @@ class _CreateUserFormState extends State { ); } - // Widget buildUserTable(bool isDesktop) { - // return Container( - // margin: const EdgeInsets.only( - // left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), - // decoration: BoxDecoration( - // border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), - // child: Column( - // children: [ - // Container( - // color: Color(0xFFF7F7FB), - // child: Padding( - // padding: const EdgeInsets.all(8.0), - // child: Row( - // children: [ - // Text( - // apiselectedUser != null ? "Profile" : "New User", - // style: TextStyle( - // fontSize: 18, - // fontWeight: FontWeight.w200, - // color: Colors.black), - // ), - // if (isEditProfile) - // IconButton( - // icon: Icon( - // Icons.edit, - // color: Colors.blueAccent, - // size: 18, - // ), - // onPressed: () { - // setState(() { - // isViewMode = !isViewMode; - // }); - // }) - // ], - // ), - // ), - // ), - // SizedBox( - // height: 15, - // ), - // Expanded( - // child: SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: _buildUserDetails( - // isDesktop), // Ensure this returns a scrollable widget - // ), - // ), - // ], - // ), - // ); - // } - Widget _buildUserDetails(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(8.0), - child: Row( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - apiselectedUser != null ? "Profile" : "New User", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w200, - color: Colors.black), - ), - // if (isEditProfile) - // IconButton( - // icon: Icon( - // Icons.edit, - // color: Colors.blueAccent, - // size: 18, - // ), - // onPressed: () { - // setState(() { - // isViewMode = !isViewMode; - // }); - // }) - ], - ), - ), - - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - Icons.filter_1_outlined, - color: Color(0xFF8B8FB2), - size: 16, - ), - SizedBox( - width: 10, - ), - Text( - "Personal Details", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - // color: Colors.black, - color: Color(0xFF8B8FB2)), - ) - ], - ), - SizedBox( - height: 20, - ), - Padding( - padding: const EdgeInsets.only(left: 16.0), - // padding: const EdgeInsets.all(8.0), - // color: Colors.grey, - child: SizedBox( - width: isDesktop - ? MediaQuery.of(context).size.width * 0.7 - : null, - // height: isDesktop? null :MediaQuery.of(context).size.height , - child: isDesktop - ? Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Expanded(child: _buildFirstRowLeftColumn(isDesktop)), - // SizedBox(width: 20), - // Expanded(child: _buildFirstRowRightColumn(isDesktop)), - - _buildFirstRowLeftColumn(isDesktop), - SizedBox(width: 20), - _buildFirstRowRightColumn(isDesktop), - ], - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildFirstRowLeftColumn(isDesktop), - SizedBox(height: 1), - _buildFirstRowRightColumn(isDesktop), - ], - ), - ), - ), - ], - ), - ], - ), - ), - - // SizedBox( - // child: Expanded( - // child: Container( - // margin: const EdgeInsets.only(left: 10.0, right: 15.0), - // child: Divider( - // color: Color(0xFF8B8FB2), - // thickness: 0.5, - // height: 50, - // ), - // ), - // ), - // ), - - SizedBox( - width: double.infinity, // Ensures the divider takes full width - child: Stack( - alignment: Alignment.center, // Centers the icon - children: [ - Divider( - color: Color(0xFF8B8FB2), - thickness: 0.5, - height: 50, - ), - Container( - // padding: EdgeInsets.all(4), - color: Colors.white, // Background to avoid overlapping - child: Row( - mainAxisSize: - MainAxisSize.min, // Prevents row from taking full width - children: [ - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - ], + apiselectedUser != null ? "Profile" : "Create New User", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 15 : 12, + fontWeight: FontWeight.w600, + color: Colors.black, ), ), - ], - ), - ), - - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - Icons.filter_2_outlined, - color: Color(0xFF8B8FB2), - size: 16, - ), - SizedBox( - width: 10, - ), - Text( - "Passport Details", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - // color: Colors.black, - color: Color(0xFF8B8FB2)), - ) - ], - ), - SizedBox( - height: 20, - ), - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: SizedBox( - width: isDesktop - ? MediaQuery.of(context).size.width * 0.7 - : null, - // height: isDesktop? null :MediaQuery.of(context).size.height , - child: isDesktop - ? Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildSecondRowLeftColumn(isDesktop), - SizedBox(width: 20), - _buildSecondRowRightColumn(isDesktop), - ], - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildSecondRowLeftColumn(isDesktop), - SizedBox(height: 1), - _buildSecondRowRightColumn(isDesktop), - ], - ), + SizedBox( + height: 20, + ), + isDesktop + ? buildTabsForUser() + : SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: buildTabsForUser(), ), - ), - ], - ), - ], - ), - ), - - SizedBox( - width: double.infinity, // Ensures the divider takes full width - child: Stack( - alignment: Alignment.center, // Centers the icon - children: [ - Divider( - color: Color(0xFF8B8FB2), - thickness: 0.5, - height: 50, - ), Container( - // padding: EdgeInsets.all(4), - color: Colors.white, // Background to avoid overlapping + // color: Colors.yellow.shade50, + height: MediaQuery.of(context).size.height * 0.67, child: Row( - mainAxisSize: - MainAxisSize.min, // Prevents row from taking full width children: [ - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), + Expanded(child: buildTabContents(isDesktop, isViewMode)), ], ), - ), - ], - ), - ), - - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - Icons.filter_3_outlined, - color: Color(0xFF8B8FB2), - size: 16, - ), - SizedBox( - width: 10, - ), - Text( - "Employee Organization Details", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - // color: Colors.black, - color: Color(0xFF8B8FB2)), - ) - ], - ), - SizedBox( - height: 20, - ), - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: SizedBox( - width: isDesktop - ? MediaQuery.of(context).size.width * 0.7 - : null, - // height: isDesktop? null :MediaQuery.of(context).size.height , - child: isDesktop - ? Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildThirdRowLeftColumn(isDesktop), - SizedBox(width: 20), - _buildThirdRowRightColumn(isDesktop), - ], - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildThirdRowLeftColumn(isDesktop), - SizedBox(height: 1), - _buildThirdRowRightColumn(isDesktop), - ], - ), - ), - ), - ], - ), - ], - ), - ), - - SizedBox( - width: double.infinity, // Ensures the divider takes full width - child: Stack( - alignment: Alignment.center, // Centers the icon - children: [ - Divider( - color: Color(0xFF8B8FB2), - thickness: 0.5, - height: 50, - ), - Container( - // padding: EdgeInsets.all(4), - color: Colors.white, // Background to avoid overlapping - child: Row( - mainAxisSize: - MainAxisSize.min, // Prevents row from taking full width - children: [ - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - ], - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - Icons.filter_4_outlined, - color: Color(0xFF8B8FB2), - size: 16, - ), - SizedBox( - width: 10, - ), - Text( - "Approver", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - // color: Colors.black, - color: Color(0xFF8B8FB2)), - ) - ], - ), - SizedBox( - height: 20, - ), - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: SizedBox( - width: isDesktop - ? MediaQuery.of(context).size.width * 0.7 - : null, - // height: isDesktop? null :MediaQuery.of(context).size.height , - child: isDesktop - ? Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildForthRowLeftColumn(isDesktop), - SizedBox(width: 20), - _buildForthRowRightColumn(isDesktop), - SizedBox(width: 20), - _buildForthRowLastColumn(isDesktop), - ], - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildForthRowLeftColumn(isDesktop), - SizedBox(height: 1), - _buildForthRowRightColumn(isDesktop), - SizedBox(height: 1), - _buildForthRowLastColumn(isDesktop), - ], - ), - ), - ), - ], - ), - ], - ), - ), - - SizedBox( - width: double.infinity, // Ensures the divider takes full width - child: Stack( - alignment: Alignment.center, // Centers the icon - children: [ - Divider( - color: Color(0xFF8B8FB2), - thickness: 0.5, - height: 50, - ), - Container( - // padding: EdgeInsets.all(4), - color: Colors.white, // Background to avoid overlapping - child: Row( - mainAxisSize: - MainAxisSize.min, // Prevents row from taking full width - children: [ - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - Icon(Icons.keyboard_arrow_right_outlined, - color: Colors.blue, size: 28), - SizedBox(width: 1), - ], - ), - ), + ) ], ), ), @@ -1249,1674 +833,163 @@ class _CreateUserFormState extends State { ); } - //--------------------------------- Personal Details --------------------------- - - Widget _buildFirstRowLeftColumn(bool isDesktop) { - return Container( - // color: Colors.yellow, - 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 Name", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["Fname"], - enabled: !isViewMode, - onChanged: (value) { - _clearError("first_name"); - }, - decoration: InputDecoration( - labelText: "FirstName", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - if (errorMessages["first_name"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["first_name"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - ], - ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Gender", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: DropdownButtonFormField( - // value: selectedGender, - value: isViewMode ? null : selectedGender, - onChanged: isViewMode - ? null - : (String? newValue) { - setState(() { - selectedGender = newValue; - print("selectedGender - $selectedGender"); - }); - }, - decoration: InputDecoration( - border: InputBorder.none, - enabled: - !isViewMode, // Disables input when in view mode - ), - style: TextStyle(fontSize: 12, color: Colors.black), - items: [ - DropdownMenuItem(value: "Male", child: Text("Male")), - DropdownMenuItem( - value: "Female", child: Text("Female")), - ], - hint: Text( - selectedGender ?? "Select Gender", - style: TextStyle(fontSize: 12, color: Colors.black), - ), - disabledHint: Text( - selectedGender ?? "Select Gender", - style: TextStyle(fontSize: 12, color: Colors.black), - ), - ), - - // child: DropdownSearch( - // selectedItem: selectedGender, - // // key: ValueKey(selectedGender), - // popupProps: PopupProps.menu( - // fit: FlexFit.loose, // Allows flexible height - // constraints: BoxConstraints(maxHeight: 250), - // - // ), - // items: ["Male", "Female", "Others"], - // 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), - // ), - // ), - // onChanged: isViewMode - // ? null - // : (String? newValue) { - // setState(() { - // // Find the country_code based on selected country_name - // selectedGender = newValue; - // print("selectedGender - $selectedGender"); - // // if (selectedCountry!.isNotEmpty) { - // // errorMessages.remove("country_code"); - // // } - // }); - // }, - // - // - // ), - ), - ), - ], - ), - ], - ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Mobile Number", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["mobileNumber"], - enabled: !isViewMode, - onChanged: (value) { - _clearError("mobile_no"); - }, - keyboardType: - TextInputType.numberWithOptions(decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal - ], - decoration: InputDecoration( - labelText: "Mobile Number", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - if (errorMessages["mobile_no"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["mobile_no"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - ], - ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Email", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["email"], - onChanged: (value) { - _clearError("email"); - }, - enabled: !isViewMode, - decoration: InputDecoration( - labelText: "Email", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - if (errorMessages["email"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["email"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - ], - ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Address", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 110, - child: TextField( - style: TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["address"], - enabled: !isViewMode, - maxLines: 4, - keyboardType: TextInputType.multiline, - decoration: InputDecoration( - labelText: "Address", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric(vertical: 16), - contentPadding: EdgeInsets.symmetric(vertical: 4), - ), - ), - ), - ), - ], - ), - ], - ), - ], - ), - // ), - ); + Widget buildTabContents(isDesktop, isViewMode) { + switch (selectedTab) { + case "personal": + return PersonalDetails( + personalDetailsKey: personalDetailsKey, + isDesktop: isDesktop, // pass isDesktop as a named argument + isViewMode: isViewMode, + controllers: controllers, + errorMessages: errorMessages, + selectedGender: selectedGender, + selectedCountry: selectedCountry, + selectedRole: selectedRole, + apiselectedUser: isapiselectedUser, + onGenderChanged: (gender) { + setState(() { + selectedGender = gender; + }); + }, + onCountryChanged: (country) { + setState(() { + selectedCountry = country; + }); + }, + onRoleChanged: (role) { + setState(() { + selectedRole = role; + }); + }, + ); + case "office": + return OfficeDetails( + isDesktop: isDesktop, // pass isDesktop as a named argument + isViewMode: isViewMode, + controllers: controllers, + errorMessages: errorMessages, + selectedLevel: selectedLevel, + selectedDepartment: selectedDepartment, + selectedFirstApprover: selectedFirstApprover, + selectedSecondApprover: selectedSecondApprover, + selectedThirdApprover: selectedThirdApprover, + onLevelChanged: (gender) { + setState(() { + selectedLevel = gender; + }); + }, + onDepartmentChanged: (country) { + setState(() { + selectedDepartment = country; + }); + }, + onFirstApproverChanged: (role) { + setState(() { + selectedFirstApprover = role; + }); + }, + onSecondApproverChanged: (role) { + setState(() { + selectedSecondApprover = role; + }); + }, + onThirdApproverChanged: (role) { + setState(() { + selectedThirdApprover = role; + }); + }, + ); + case "travel": + return TravellerDetails(); + default: + return PersonalDetails( + personalDetailsKey: personalDetailsKey, + isDesktop: isDesktop, // pass isDesktop as a named argument + isViewMode: isViewMode, + controllers: controllers, + errorMessages: errorMessages, + selectedGender: selectedGender, + selectedCountry: selectedCountry, + apiselectedUser: isapiselectedUser); + } } - Widget _buildFirstRowRightColumn(bool isDesktop) { - DateTime? _selectedDateOfBirth; - - Future _selectCheckDateOfBirth(BuildContext context) async { - DateTime now = DateTime.now(); - DateTime today = DateTime(now.year, now.month, now.day); - - DateTime? pickedDate = await showDatePicker( - context: context, - initialDate: - _selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today) - ? _selectedDateOfBirth! - : today, - firstDate: DateTime(1900), - lastDate: DateTime(2100), - ); - - if (pickedDate != null && pickedDate != _selectedDateOfBirth) { - setState(() { - _selectedDateOfBirth = pickedDate; - controllers["dob"]?.text = - DateFormat('yyyy-MM-dd').format(pickedDate); - }); - } - } - - // ----------------- - - late Map countryMap; // Mapping country_code -> country_name - late List countryCodes; // List of country codes - List countryList = apiCountryData ?? []; - - countryList = apiCountryData ?? []; - - // Map country codes to country names - countryMap = { - for (var item in countryList) - item['country_code'] as String: item['country_name'] as String + Widget buildTabsForUser() { + final tabs = { + "personal": "Personal Details", + "office": "Office Details", + // "travel": "Travel Details", }; - // Extract only country codes for processing - countryCodes = countryMap.keys.toList(); - - selectedCountry ??= null; - - return Container( - // color: Colors.yellow, - 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("Last Name", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["Lname"], - enabled: !isViewMode, - onChanged: (value) { - _clearError("last_name"); - }, - decoration: InputDecoration( - labelText: "LastName", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), + return Row( + crossAxisAlignment: CrossAxisAlignment.end, // important + children: tabs.entries.map((entry) { + final isSelected = selectedTab == entry.key; + return GestureDetector( + onTap: () { + setState(() { + selectedTab = entry.key; + }); + }, + child: Padding( + padding: const EdgeInsets.only(right: 24.0), // space between tabs + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + entry.value, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: isSelected ? Color(0xFF114D8B) : Color(0xFF475569), ), - if (errorMessages["last_name"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["last_name"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - ], - ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Date of Birth", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: GestureDetector( - onTap: isViewMode - ? null - : () async { - await _selectCheckDateOfBirth(context); - if (controllers["dob"]!.text.isNotEmpty) { - setState(() { - // errorMessages.remove("start_date"); - }); - } - }, - child: AbsorbPointer( - child: TextField( - controller: controllers["dob"], - style: const TextStyle( - fontSize: 12, color: Colors.black), - decoration: const InputDecoration( - labelText: "Select Date", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Color(0xFF8B8FB2)), - ), - ), - ), - ), - ), - ), - ], - ), - ], - ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Alternate Mobile Number", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["alternateMobile"], - enabled: !isViewMode, - keyboardType: - TextInputType.numberWithOptions(decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal - ], - decoration: InputDecoration( - labelText: "Alternate Number", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - if (errorMessages["alternate_mobile_no"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["alternate_mobile_no"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - ], - ), - SizedBox(height: 3), - apiselectedUser != null - ? SizedBox() - : Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Password", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: - TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["password"], - enabled: !isViewMode, - onChanged: (value) { - _clearError("password"); - }, - decoration: InputDecoration( - labelText: "Password", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - if (errorMessages["password"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["password"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - ], ), - if (!isDesktop) - SizedBox( - height: 3, + const SizedBox(height: 8), + AnimatedContainer( + duration: Duration(milliseconds: 300), + height: 2, + width: isSelected ? 50 : 0, // small line + color: Color(0xFF114D8B), + ), + ], ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Country", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: DropdownSearch( - selectedItem: countryMap[selectedCountry], - enabled: !isViewMode, - popupProps: PopupProps.menu( - showSearchBox: true, // Enables search functionality - fit: FlexFit.loose, // Allows flexible height - constraints: BoxConstraints(maxHeight: 250), - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search Country...", - contentPadding: - EdgeInsets.symmetric(horizontal: 10), - ), - ), - ), - items: countryMap.values.toList(), - 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 Country", - style: TextStyle(fontSize: 12, color: Colors.black), - ), - ), - onChanged: isViewMode - ? null - : (String? newValue) { - setState(() { - // Find the country_code based on selected country_name - selectedCountry = countryMap.entries - .firstWhere( - (entry) => entry.value == newValue) - .key; + ), + ); + }).toList(), + ); + } - // if (selectedCountry!.isNotEmpty) { - // errorMessages.remove("country_code"); - // } - }); - }, - ), - ), - ), - ], - ), - ], - ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Postal Code", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["postalCode"], - enabled: !isViewMode, - keyboardType: - TextInputType.numberWithOptions(decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal - ], - decoration: InputDecoration( - labelText: "Postal Code", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - ], - ), - ], - ), - if (!isDesktop) - SizedBox( - height: 3, +// ---- Submit --------------------------------------- + + List _buildNext(isDesktop, Color layoutColor) { + return [ + MouseRegion( + cursor: isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + isViewMode ? layoutColor : layoutColor, // Keep original color + foregroundColor: + isViewMode ? Colors.white : Colors.white, // Keep original color + disabledBackgroundColor: + layoutColor, // Ensure color remains when disabled + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: layoutColor, width: 2), ), - ], - ), - // ), - ); + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: + isViewMode ? null : handleNext, // Disable when in view mode + child: Text("Next"), + ), + ) + ]; } - //--------------------------------- Passport Details ----------------------------- - Widget _buildSecondRowLeftColumn(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("Passport Number", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["passportNumber"], - enabled: !isViewMode, - decoration: InputDecoration( - labelText: "Passport Number", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - ], - ), - ], - ), - SizedBox( - height: 3, - ), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Passport Document", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: GestureDetector( - onTap: pickPDFWeb, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 10), - alignment: Alignment.centerLeft, - child: Text( - selectedFileNames ?? "Select Document", - style: TextStyle(fontSize: 12, color: Colors.black), - overflow: TextOverflow - .ellipsis, // Prevents overflow issues - ), - ), - ), - ), - ), - SizedBox(height: 10), - if (passportFile != null || passportFileUrlFromApi != null) - - // Centers the text - Container( - color: Colors.white, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.25 - : MediaQuery.of(context).size.width * 0.8, - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - GestureDetector( - onTap: () { - print('DOWNLOAD - $passportFile'); - - if (passportFile != null) { - try { - // ✅ Step 1: Create a Blob directly from the file - final blob = html.Blob( - [passportFile!], 'application/pdf'); - - // ✅ Step 2: Generate a download URL from the Blob - final url = - html.Url.createObjectUrlFromBlob(blob); - - // ✅ Step 3: Create an invisible anchor to trigger download - final anchor = html.AnchorElement(href: url) - ..setAttribute("download", - selectedFileNames ?? "document.pdf") - ..style.display = "none"; - - // ✅ Step 4: Add anchor to DOM and click it - html.document.body!.append(anchor); - anchor.click(); - - // ✅ Step 5: Clean up - anchor.remove(); - html.Url.revokeObjectUrl(url); - - print("Download triggered successfully!"); - } catch (e) { - print("Error during download: $e"); - } - } else if (passportFileUrlFromApi != null) { - // Trigger file download from the server path - final anchor = html.AnchorElement( - href: passportFileUrlFromApi!) - ..target = 'blank' - ..download = - selectedFileNames ?? "document.pdf" - ..click(); - } else { - print("No file available to download."); - } - }, - child: Container( - padding: const EdgeInsets.all(5), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: Colors.green.shade300, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - "Download", - style: TextStyle( - fontSize: 12, - color: Colors.white, - fontWeight: FontWeight.bold), - overflow: TextOverflow.ellipsis, - textAlign: TextAlign - .end, // Ensures text is centered within the Text widget - ), - SizedBox(width: 5), - Icon( - Icons.download, - size: 13, - color: Colors.white, - ) - ], - ), - ), - ) - ], - ), - ), - - // CustomTextFieldUserWrapper( - // isFocused: false, - // isDesktop: isDesktop, - // child: SizedBox( - // height: 40, - // child: TextField( - // style: TextStyle(fontSize: 12), - // controller: controllers["passportDoc"], - // decoration: InputDecoration( - // labelText: "Select Document", - // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - // floatingLabelBehavior: FloatingLabelBehavior.never, - // border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric(vertical: 16), - // ), - // ), - // ), - // ), - ], - ), - ], - ), - if (!isDesktop) - SizedBox( - height: 3, - ), - ], - ), - // ), - ); - } - - Widget _buildSecondRowRightColumn(bool isDesktop) { - DateTime? _selectedDateOfIssue; - - Future _selectCheckDateOfIssue(BuildContext context) async { - DateTime now = DateTime.now(); - DateTime today = DateTime(now.year, now.month, now.day); - - DateTime? pickedDate = await showDatePicker( - context: context, - initialDate: - _selectedDateOfIssue != null && _selectedDateOfIssue!.isAfter(today) - ? _selectedDateOfIssue! - : today, - firstDate: today, - lastDate: DateTime(2100), - ); - - if (pickedDate != null && pickedDate != _selectedDateOfIssue) { - setState(() { - _selectedDateOfIssue = pickedDate; - controllers["dateOfIssue"]?.text = - DateFormat('yyyy-MM-dd').format(pickedDate); - }); - } - } - - DateTime? _selectedDateOfExpiry; - - Future _selectCheckDateOfExpiry(BuildContext context) async { - DateTime now = DateTime.now(); - DateTime today = DateTime(now.year, now.month, now.day); - - DateTime? pickedDate = await showDatePicker( - context: context, - initialDate: _selectedDateOfExpiry != null && - _selectedDateOfExpiry!.isAfter(today) - ? _selectedDateOfExpiry! - : today, - firstDate: today, - lastDate: DateTime(2100), - ); - - if (pickedDate != null && pickedDate != _selectedDateOfExpiry) { - setState(() { - _selectedDateOfExpiry = pickedDate; - controllers["dateOfExpiry"]?.text = - DateFormat('yyyy-MM-dd').format(pickedDate); - }); - } - } - - 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("Place of Issue", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["placeOfIssue"], - enabled: !isViewMode, - decoration: InputDecoration( - labelText: "Place Of Issue", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - ], - ), - ], - ), - SizedBox( - height: 3, - ), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Date of Issue", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: GestureDetector( - onTap: isViewMode - ? null - : () async { - await _selectCheckDateOfIssue(context); - if (controllers["dateOfIssue"]! - .text - .isNotEmpty) { - setState(() { - // errorMessages.remove("start_date"); - }); - } - }, - child: AbsorbPointer( - child: TextField( - controller: controllers["dateOfIssue"], - style: const TextStyle( - fontSize: 12, color: Colors.black), - decoration: const InputDecoration( - labelText: "Select Date", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Color(0xFF8B8FB2)), - ), - ), - ), - ), - ), - ), - ], - ), - ], - ), - SizedBox( - height: 3, - ), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Date of Expiry", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: GestureDetector( - onTap: isViewMode - ? null - : () async { - await _selectCheckDateOfExpiry(context); - if (controllers["dateOfExpiry"]! - .text - .isNotEmpty) { - setState(() { - // errorMessages.remove("start_date"); - }); - } - }, - child: AbsorbPointer( - child: TextField( - controller: controllers["dateOfExpiry"], - style: const TextStyle( - fontSize: 12, color: Colors.black), - decoration: const InputDecoration( - labelText: "Select Date", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Color(0xFF8B8FB2)), - ), - ), - ), - ), - ), - ), - ], - ), - ], - ), - if (!isDesktop) - SizedBox( - height: 3, - ), - ], - ), - // ), - ); - } - -//----------------------------------- Employee Organization Details ------------------ - Widget _buildThirdRowLeftColumn(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("Employee Code", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12, color: Colors.black), - controller: controllers["employeeCode"], - enabled: !isViewMode, - decoration: InputDecoration( - labelText: "Employee Code", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - ], - ), - ], - ), - // SizedBox(height: 3), - // Row( - // children: [ - // Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text("User Type", - // style: TextStyle( - // fontSize: 12, - // fontWeight: FontWeight.w200, - // color: Colors.black)), - // SizedBox(height: 5), - // CustomTextFieldUserWrapper( - // isFocused: false, - // isDesktop: isDesktop, - // child: SizedBox( - // height: 40, - // child: DropdownSearch( - // selectedItem: selectedUserType, - // enabled: !isViewMode, - // popupProps: PopupProps.menu( - // // showSearchBox: true, - // fit: FlexFit.loose, // Allows flexible height - // constraints: BoxConstraints(maxHeight: 250), - // ), - // items: [ - // "Normal", - // "Privilege", - // ], - // 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), - // ), - // ), - // onChanged: (String? newValue) { - // setState(() { - // // Find the country_code based on selected country_name - // selectedUserType = newValue; - // - // // print("selectedUserType - $selectedUserType"); - // - // // if (selectedCountry!.isNotEmpty) { - // // errorMessages.remove("country_code"); - // // } - // }); - // }, - // ), - // ), - // ), - // ], - // ), - // ], - // ), - - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Role ", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, // Dropdown doesn't use focus - isDesktop: isDesktop, - child: SizedBox( - height: 45, // Set appropriate height - child: DropdownButtonFormField( - value: isViewMode ? null : selectedRole, - style: TextStyle(fontSize: 12, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding - ), - onChanged: isViewMode - ? null - : (newValue) { - setState(() { - selectedRole = newValue; - }); - }, - items: - apiRoleData?.map>((item) { - return DropdownMenuItem( - value: item['dropdown_key'], // ID as value - child: Text(item['dropdown_value'] ?? "Select")); - }).toList(), - hint: Text("Select"), - disabledHint: Text( - selectedRole ?? "Select Role", - style: TextStyle(fontSize: 12, color: Colors.black), - ), - ), - ), - ), - ], - ), - ], - ), - SizedBox(height: 3), - if (!isDesktop) SizedBox(height: 3), - ], - ), - // ), - ); - } - - Widget _buildThirdRowRightColumn(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("Group", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: DropdownSearch( - selectedItem: selectedLevel, - enabled: !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"); - // } - }); - }, - ), - ), - ), - ], - ), - ], - ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Department", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, // Dropdown doesn't use focus - isDesktop: isDesktop, - child: SizedBox( - height: 45, // Set appropriate height - child: DropdownButtonFormField( - value: isViewMode ? null : selectedDepartment, - style: TextStyle(fontSize: 12, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding - ), - onChanged: isViewMode - ? null - : (newValue) { - setState(() { - selectedDepartment = 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: TextStyle(fontSize: 12, color: Colors.black), - ), - ), - ), - ), - ], - ), - ], - ), - ], - ), - - // ), - ); - } - -//----------------------------------- Approver --------------------------------------- - - Widget _buildForthRowLeftColumn(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: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.2 - : MediaQuery.of(context).size.width * 0.8, - child: SizedBox( - height: 40, - child: apiUserData == null - ? Center( - child: Transform.scale( - scale: 0.5, - child: CircularProgressIndicator(), - ), - ) - : DropdownSearch( - selectedItem: userMap[selectedFirstApprover], - enabled: !isViewMode, - popupProps: PopupProps.menu( - showSearchBox: true, - fit: FlexFit.loose, // Allows flexible height - 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: 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), - ), - ), - onChanged: (String? newValue) { - setState(() { - selectedFirstApprover = userMap.entries - .firstWhere( - (entry) => entry.value == newValue) - .key; - - // if (selectedCountry!.isNotEmpty) { - // errorMessages.remove("country_code"); - // } - }); - }, - ), - ), - ), - ], - ), - ], - ), - ], - ), - - // ), - ); - } - - Widget _buildForthRowRightColumn(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: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.2 - : MediaQuery.of(context).size.width * 0.8, - child: SizedBox( - height: 40, - child: apiUserData == null - ? Center( - child: Transform.scale( - scale: 0.5, - child: CircularProgressIndicator(), - ), - ) - : DropdownSearch( - selectedItem: userMap[selectedSecondApprover], - enabled: !isViewMode, - popupProps: PopupProps.menu( - showSearchBox: true, - fit: FlexFit.loose, // Allows flexible height - 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: 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), - ), - ), - onChanged: (String? newValue) { - setState(() { - selectedSecondApprover = userMap.entries - .firstWhere( - (entry) => entry.value == newValue) - .key; - - // if (selectedCountry!.isNotEmpty) { - // errorMessages.remove("country_code"); - // } - }); - }, - ), - ), - ), - ], - ), - ], - ), - ], - ), - // ), - ); - } - - Widget _buildForthRowLastColumn(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: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.2 - : MediaQuery.of(context).size.width * 0.8, - child: SizedBox( - height: 40, - child: apiUserData == null - ? Center( - child: Transform.scale( - scale: 0.5, - child: CircularProgressIndicator(), - ), - ) - : DropdownSearch( - selectedItem: userMap[selectedThirdApprover], - enabled: !isViewMode, - popupProps: PopupProps.menu( - showSearchBox: true, - fit: FlexFit.loose, // Allows flexible height - 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: 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), - ), - ), - onChanged: (String? newValue) { - setState(() { - selectedThirdApprover = userMap.entries - .firstWhere( - (entry) => entry.value == newValue) - .key; - - // if (selectedCountry!.isNotEmpty) { - // errorMessages.remove("country_code"); - // } - }); - }, - ), - ), - ), - ], - ), - ], - ), - ], - ), - - // ), - ); - } - -//----------------------------------- Submit --------------------------------------- - List _buildSubmit(isDesktop, Color layoutColor) { return [ ElevatedButton( diff --git a/lib/Screens/userManagement/create_user/create_user1.dart b/lib/Screens/userManagement/create_user/create_user1.dart new file mode 100644 index 0000000..2bbe08e --- /dev/null +++ b/lib/Screens/userManagement/create_user/create_user1.dart @@ -0,0 +1,2954 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:html' as html; +import 'dart:typed_data'; // Import for Uint8List + +import 'package:bcrypt/bcrypt.dart'; +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:frontend/utils/auth_utils.dart'; +import 'package:go_router/go_router.dart'; +import 'package:http/http.dart' as http; +import 'package:http_parser/http_parser.dart' as http_parser; +import 'package:intl/intl.dart'; +import 'package:responsive_builder/responsive_builder.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http_parser/http_parser.dart'; + +import '../../../config/apiUrl.dart'; +import '../../../routes/custom_appBar.dart'; +import '../../../routes/custom_drawer.dart'; +import '../../../services/apiService.dart'; +import '../../../widgets/custom_text_field.dart'; +import '../../../widgets/custom_text_forex.dart'; +import '../../../widgets/custom_user_form.dart'; + +class CreateUserForm1 extends StatefulWidget { + @override + _CreateUserFormState createState() => _CreateUserFormState(); +} + +class _CreateUserFormState extends State { + final ApiService apiService = ApiService(); + late TabController _tabController; + + String? userId; + String? orgId; + + String? token; + + // Map to store controllers dynamically + final Map controllers = {}; + bool isViewMode = false; + bool isEditProfile = false; + // late final List? apiCountryData ; + + late List? apiCountryData; + late List? apiCostData; + late List? apiRoleData; + late List? apiUserData; + Map? apiselectedUser; + + late List userList; + late Future> futureUsers; + + List? resolvedUserData; + late List userIdsApi; + late Map userMap; + + Map errorMessages = {}; + + String? selectedCountry; + String? selectedGender; + String? selectedUserType; + String? selectedRole; + String? selectedLevel; + String? selectedDepartment; + String? selectedFirstApprover; + String? selectedSecondApprover; + String? selectedThirdApprover; + + String? selectedFileNames; + Uint8List? passportDocumentBytes; + String? passportFileUrlFromApi; + String? base64PDF; + + html.File? passportFile; + + List dataHeader = [ + "Fname", + "Lname", + "email", + "password", + "dob", + "gender", + "mobileNumber", + "alternateMobile", + "address", + "postalCode", + "country", + "passportNumber", + "placeOfIssue", + "passportDoc", + "userType", + "roleId", + "deptId", + "levelId", + "firstApproval", + "secondApproval", + "thirdApproval", + "employeeCode", + "dateOfIssue", + "dateOfExpiry", + "changePassword" + ]; + + Color? layoutColor; + Color? bodyColor; + + Map get userDetials { + Map data = { + "first_name": controllers["Fname"]?.text, + "last_name": controllers["Lname"]?.text, + "email": controllers["email"]?.text, + "password": controllers["password"]?.text, + "mobile_no": controllers["mobileNumber"]?.text, + "alternate_mobile_no": controllers["alternateMobile"]?.text, + "date_of_birth": controllers["dob"]?.text, + "address": controllers["address"]?.text, + "gender": selectedGender, + "postal_code": controllers["postalCode"]?.text, + "country_code": selectedCountry, + "employee_code": controllers["employeeCode"]?.text, + + "user_type": selectedUserType, + "role_id": selectedRole, + "department_id": selectedDepartment, + + "group_id": selectedLevel, + + "first_approver": selectedFirstApprover, + "second_approver": selectedSecondApprover, + "third_approver": selectedThirdApprover, + "passport_number": controllers["passportNumber"]?.text, + "place_of_issue": controllers["placeOfIssue"]?.text, + "passport_document": passportFile, + + "date_of_issue": controllers["dateOfIssue"]?.text, + "date_of_expiry": controllers["dateOfExpiry"]?.text, + "created_by": userId, + "is_active": "1", + "org_id": orgId, + // "passport_fileData": passportFile, + }; + return data; + } + + Future updateData() async { + // Ensure apiselectedUser is not null before printing + if (apiselectedUser != null) { + print("API Selected User Has Data - $widget.apiselectedUser"); + setState(() { + // ✅ Wrap in setState to update the UI + + controllers["Fname"]?.text = apiselectedUser?["first_name"] ?? ""; + controllers["Lname"]?.text = apiselectedUser?["last_name"] ?? ""; + controllers["email"]?.text = apiselectedUser?["email"] ?? ""; + controllers["password"]?.text = ""; + // controllers["password"]?.text = apiselectedUser?["password"] ?? ""; + controllers["mobileNumber"]?.text = apiselectedUser?["mobile_no"] ?? ""; + controllers["alternateMobile"]?.text = + apiselectedUser?["alternate_mobile_no"] ?? ""; + + controllers["dob"]?.text = apiselectedUser?["date_of_birth"] ?? ""; + + controllers["address"]?.text = apiselectedUser?["address"] ?? ""; + controllers["postalCode"]?.text = apiselectedUser?["postal_code"] ?? ""; + controllers["employeeCode"]?.text = + apiselectedUser?["employee_code"] ?? ""; + + controllers["passportNumber"]?.text = + apiselectedUser?["passport_number"] ?? ""; + + controllers["placeOfIssue"]?.text = + apiselectedUser?["place_of_issue"] ?? ""; + + if (apiselectedUser?["date_of_issue"] != null) { + controllers["dateOfIssue"]?.text = + apiselectedUser?["date_of_issue"] ?? ""; + } + + if (apiselectedUser?["date_of_expiry"] != null) { + controllers["dateOfExpiry"]?.text = + apiselectedUser?["date_of_expiry"] ?? ""; + } + + if (apiselectedUser?["country_code"] != null) { + selectedCountry = apiselectedUser?["country_code"]?.toString() ?? ""; + } + if (apiselectedUser?["gender"] != null) { + selectedGender = apiselectedUser?["gender"]?.toString().trim() ?? ""; + } + if (apiselectedUser?["user_type"] != null) { + selectedUserType = + apiselectedUser?["user_type"]?.toString().trim() ?? ""; + } + + if (apiselectedUser?["role_id"] != null) { + selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? ""; + } + // selectedDepartment = + // apiselectedUser?["department_id"]?.toString().trim() ?? ""; + + if (apiselectedUser?["department_id"] != null) { + selectedDepartment = apiselectedUser!["department_id"].toString(); + } + // print( + // "selectedDepartment - $selectedDepartment - ${apiselectedUser?["department_id"]} "); + if (apiselectedUser?["level_id"] != null) { + selectedLevel = apiselectedUser?["level_id"]?.toString().trim() ?? ""; + } + if (apiselectedUser?["first_approver"] != null) { + selectedFirstApprover = + apiselectedUser?["first_approver"]?.toString() ?? ""; + } + + if (apiselectedUser?["second_approver"] != null) { + selectedSecondApprover = + apiselectedUser?["second_approver"]?.toString() ?? ""; + } + + if (apiselectedUser?["third_approver"] != null) { + selectedThirdApprover = + apiselectedUser?["third_approver"]?.toString() ?? ""; + } + + print("Updated selectedGender: $selectedGender"); // Debugging + + // ✅ Load passport document from API + String? apiDocPath = apiselectedUser?["passport_document"]; + if (apiDocPath != null && apiDocPath.isNotEmpty) { + passportFileUrlFromApi = apiDocPath; + selectedFileNames = + apiDocPath.split('/').last; // Extract filename from path + passportFile = null; // No local file selected yet + } else { + passportFileUrlFromApi = null; + selectedFileNames = null; + passportFile = null; + } + }); + } else { + print("API Selected User Has Data - No data available yet"); + } + } + + @override + void initState() { + super.initState(); + + WidgetsFlutterBinding.ensureInitialized(); + + // Step 1: Set 'reloaded' flag before page unload + html.window.onBeforeUnload.listen((event) { + html.window.localStorage['reloaded'] = 'true'; + }); + + // apiCountryData = extraData['apiCountryData']; // Extract apiCountryData + // futureUsers = extraData['apiUserData']; // Extract futureUsers (Future>) + apiCountryData = null; + apiUserData = null; + // apiselectedUser = null; + apiCostData = null; + apiRoleData = null; + + // Initialize controllers for each field + + WidgetsBinding.instance.addPostFrameCallback((_) async { + final wasReloaded = html.window.localStorage['reloaded'] == 'true'; + + if (wasReloaded) { + html.window.localStorage.remove('reloaded'); // Clear it + context.go('/listUser'); // Navigate using go_router + } + + final extraData = + GoRouterState.of(context).extra as Map?; + + if (extraData != null) { + print("extraData: ${extraData['selectedUser']}"); + + setState(() { + // apiCountryData = extraData['apiCountryData']; + // apiUserData = extraData['apiUserData']; + // + // userList = apiUserData ?? []; + // userMap = { + // for (var user in userList) + // user['user_id'].toString(): "${user['first_name']} ${user['last_name']}" + // }; + // userIdsApi = userMap.keys.toList(); + + // Handle selectedUser as a Map (not a List) + apiselectedUser = extraData['selectedUser'] + as Map?; // Cast it as a Map + isViewMode = extraData['isViewMode'] ?? false; + isEditProfile = extraData['isEditProfile'] ?? false; + }); + + print("selectedUser: $apiselectedUser"); + + // Add another post-frame callback to check after setState + // await Future.delayed(Duration( + // milliseconds: 100)); // Optional delay to ensure UI has updated + updateData(); + } + + initializeData(); + fetchCountries(); + fetchDepartment(); + fetchUsers(); + fetchRoles(); + loadInitialData(); + }); + + for (var field in dataHeader) { + controllers[field] = TextEditingController(); + } + } + + Future fetchCountries() async { + try { + List countries = await apiService.fetchCountryList(); + setState(() { + apiCountryData = countries; + }); + } catch (e) { + print('Error fetching country list: $e'); + } + } + + Future fetchDepartment() async { + try { + List department = await apiService.fetchCostCenter(); + setState(() { + apiCostData = department; + }); + } catch (e) { + print('Error fetching department list: $e'); + } + } + + Future fetchUsers() async { + try { + List users = await apiService.fetchUsers(); + setState(() { + // apiUserData = users; + + apiUserData = users.where((user) => user["role_id"] == "4").toList(); + + 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 fetchRoles() async { + try { + final response = await apiService.fetchMasterDropdown(); + + if (response is Map && response.containsKey("role")) { + List roleList = response["role"]; // Extract the list + setState(() { + apiRoleData = roleList; + }); + print("APIROLEData - $apiRoleData"); + } else { + print("Error: 'role' key not found or response is not a Map."); + } + } catch (e) { + print('Error fetching role list: $e'); + } + } + + void printFormData() { + for (var field in dataHeader) { + print("$field: ${controllers[field]?.text}"); + } + } + + 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 initializeData() async { + token = await getToken(); + userId = await getUserId(); + + if (token == null || userId == null) { + print("Token or USerId missing"); + print("Retrieved Token: $token"); + print("Retrieved UserId: $userId"); + return; + } else { + setState(() {}); + } + } + + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('auth_token'); + } + + // Future getUserId() async { + // final prefs = await SharedPreferences.getInstance(); + // return prefs.getString('userId'); + // } + + Future getUserId() async { + final prefs = await SharedPreferences.getInstance(); + final userData = prefs.getString('user_data'); + + if (userData != null) { + final decodedData = jsonDecode(userData); + + // Return the user_id from the decoded data + return decodedData['user_id']?.toString(); + } + return null; + } + + @override + void dispose() { + // Dispose all controllers to prevent memory leaks + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + + void handleSubmit() async { + print("USR Detail Submit"); + printFormData(); + + Map data = userDetials; + + if (!isValidData(data)) { + print("USERDETAILS : $userDetials"); + print("Validation Failed: Required fields are missing."); + setState(() {}); + return; // Stop execution if validation fails + } else { + print("USERDETAILS : $userDetials"); + orgId = await getOrgId(); + + createUserData(userDetials); + } + } + + bool isValidData(Map data) { + errorMessages.clear(); // Reset errors + + // Required fields that must not be empty + List requiredFields = [ + "first_name", + "last_name", + "email", + "mobile_no" + ]; + + if (apiselectedUser == null) { + requiredFields.add("password"); + } + + // Check validation for each field + for (String field in requiredFields) { + if (data[field] == null || data[field].toString().trim().isEmpty) { + errorMessages[field] = "Required"; + } + } + + // Mobile number validation for both mobile_no and alternate_mobile_no + if (data["mobile_no"] != null && data["mobile_no"].toString().isNotEmpty) { + if (!RegExp(r"^\d{10}$").hasMatch(data["mobile_no"].toString())) { + errorMessages["mobile_no"] = + "Enter 10 digits"; // Invalid mobile number format + } + } + + if (data["alternate_mobile_no"] != null && + data["alternate_mobile_no"].toString().isNotEmpty) { + if (!RegExp(r"^\d{10}$") + .hasMatch(data["alternate_mobile_no"].toString())) { + errorMessages["alternate_mobile_no"] = + "Enter 10 digits"; // Invalid mobile number format + } + } + + // Email validation + if (data["email"] != null && data["email"].toString().isNotEmpty) { + if (!RegExp(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") + .hasMatch(data["email"].toString())) { + errorMessages["email"] = "Invalid email format"; // Invalid email format + } + } + return errorMessages.isEmpty; // Valid if there are no errors + } + + void _clearError(String field) { + if (mounted && errorMessages.containsKey(field)) { + setState(() { + errorMessages.remove(field); + }); + } + } + + void pickPDFWeb() { + html.FileUploadInputElement uploadInput = html.FileUploadInputElement(); + uploadInput.accept = '.pdf'; + uploadInput.click(); + + uploadInput.onChange.listen((e) { + final file = uploadInput.files!.first; + + // Ensure the file is a PDF + if (!file.type.contains("pdf")) { + print("Error: Not a PDF file"); + return; + } + + // 🔹 File size check: Ensure it does not exceed 3MB + int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes + if (file.size > maxFileSize) { + print('Error: File size exceeds 3MB'); + return; + } + + setState(() { + selectedFileNames = file.name; + passportFile = file; + passportFileUrlFromApi = null; + }); + + print('PDF File selected: ${file.name}'); + }); + } + + Future createUserData(Map userData) async { + final bool isUpdating = + apiselectedUser != null && apiselectedUser!.isNotEmpty; + final uri = Uri.parse( + isUpdating + ? '$apiUrl/api/users/update/${apiselectedUser?["user_id"]}' + : '$apiUrl/api/users/create', + ); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $token'; + + // If updating, spoof the method Laravel-style + if (isUpdating) { + request.fields['_method'] = 'PUT'; + request.fields['user_id'] = apiselectedUser!["user_id"].toString(); + } + + // Add all non-null and non-empty user data fields + userData.forEach((key, value) { + if (value != null && value.toString().trim().isNotEmpty) { + request.fields[key] = value.toString(); + } + }); + + // Attach file if selected + if (passportFile != null) { + try { + final reader = html.FileReader(); + reader.readAsArrayBuffer(passportFile!); + await reader.onLoad.first; + + final data = reader.result as Uint8List; + + final multipartFile = http.MultipartFile.fromBytes( + 'passport_document', + data, + filename: passportFile!.name, + ); + + request.files.add(multipartFile); + print("📎 File attached: ${passportFile!.name}"); + } catch (e) { + print("❌ Failed to read file: $e"); + } + } else { + print("⚠️ No passport file selected."); + } + + print("🚀 Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + print("✅ User submitted successfully!"); + print("📨 Response: ${response.body}"); + context.go('/listUser'); + } else { + print("❌ Submission failed. Status: ${response.statusCode}"); + print("📨 Body: ${response.body}"); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + void handleChangePassword(String enteredPassword) { + var hashedPassword = apiselectedUser?["password"]; // Get stored bcrypt hash + + print("HAshPAssword - $hashedPassword "); + print("enteredPassword - $enteredPassword "); + + if (hashedPassword != null && hashedPassword.isNotEmpty) { + bool isMatch = + BCrypt.checkpw(enteredPassword, hashedPassword); // Compare passwords + + setState(() { + // ✅ Ensure UI updates + if (isMatch) { + errorMessages["password"] = + "New password is not similar to old password"; + print(" Password match!"); + } else { + print(" Password NOT match!"); + errorMessages + .remove("password"); // Clear error if password is different + } + }); + } else { + print("No stored password found."); + } + } + + @override + Widget build(BuildContext context) { + return ResponsiveBuilder(builder: (context, sizingInfo) { + bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'Create User '), + // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), + appBar: CustomAppBar(isDesktop: isDesktop), + drawer: CustomDrawer(isDesktop: false), + body: Padding( + padding: isDesktop + ? EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * + 0.1, // 30% of screen width as horizontal padding + vertical: MediaQuery.of(context).size.height * + 0, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(0), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + // const Expanded(child: Center(child: Text("User Details Content"))), + // Expanded( + // child: _buildUserDetails(isDesktop), + // ), + + Expanded(child: buildData(isDesktop, context)), + ], + ), + ), + ); + }); + } + + Widget buildData(bool isDesktop, context) { + return Container( + margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, + padding: const EdgeInsets.only(left: 10, right: 10, top: 8), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), + // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), + // decoration: BoxDecoration( + // // color: Colors.amber, + // // color: bodyColor, + // color: Color(0xFFE1F5FE), + // border: Border.all( + // color: Colors.white, + // // color: Color(0xFFF7F7FB), + // width: 3.5)), + child: Column( + children: [ + Expanded( + child: Container( + // margin: isDesktop + // ? EdgeInsets.all(10.0) + // : EdgeInsets.only( + // left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), + // padding: const EdgeInsets.all(10), + height: isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, + // decoration: BoxDecoration( + // border: isDesktop + // ? Border.all( + // width: 2, + // color: Colors.white, + // // color: Color(0xFFF7F7FB), + // ) + // : null, + // color: Colors.white, + // // color: Color(0xFFF7F7FB), + // + // // color: Colors.amber, + // ), + // color: bodyColor, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(0.0), + child: _buildUserDetails(isDesktop), + ), + ), + ), + ), + Container( + color: Colors.white, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.end, + children: _buildSubmit(isDesktop, layoutColor!), + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildSubmit(isDesktop, layoutColor!), + )), + ) + ], + ), + ); + } + + // Widget buildUserTable(bool isDesktop) { + // return Container( + // margin: const EdgeInsets.only( + // left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), + // decoration: BoxDecoration( + // border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), + // child: Column( + // children: [ + // Container( + // color: Color(0xFFF7F7FB), + // child: Padding( + // padding: const EdgeInsets.all(8.0), + // child: Row( + // children: [ + // Text( + // apiselectedUser != null ? "Profile" : "New User", + // style: TextStyle( + // fontSize: 18, + // fontWeight: FontWeight.w200, + // color: Colors.black), + // ), + // if (isEditProfile) + // IconButton( + // icon: Icon( + // Icons.edit, + // color: Colors.blueAccent, + // size: 18, + // ), + // onPressed: () { + // setState(() { + // isViewMode = !isViewMode; + // }); + // }) + // ], + // ), + // ), + // ), + // SizedBox( + // height: 15, + // ), + // Expanded( + // child: SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: _buildUserDetails( + // isDesktop), // Ensure this returns a scrollable widget + // ), + // ), + // ], + // ), + // ); + // } + + Widget _buildUserDetails(bool isDesktop) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Text( + apiselectedUser != null ? "Profile" : "New User", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w200, + color: Colors.black), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.filter_1_outlined, + color: Color(0xFF8B8FB2), + size: 16, + ), + SizedBox( + width: 10, + ), + Text( + "Personal Details", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + // color: Colors.black, + color: Color(0xFF8B8FB2)), + ) + ], + ), + SizedBox( + height: 20, + ), + Padding( + padding: const EdgeInsets.only(left: 16.0), + // padding: const EdgeInsets.all(8.0), + // color: Colors.grey, + child: SizedBox( + width: isDesktop + ? MediaQuery.of(context).size.width * 0.7 + : null, + // height: isDesktop? null :MediaQuery.of(context).size.height , + child: isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildFirstRowLeftColumn(isDesktop), + SizedBox(width: 20), + _buildFirstRowRightColumn(isDesktop), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildFirstRowLeftColumn(isDesktop), + SizedBox(height: 1), + _buildFirstRowRightColumn(isDesktop), + ], + ), + ), + ), + ], + ), + ], + ), + ), + SizedBox( + width: double.infinity, // Ensures the divider takes full width + child: Stack( + alignment: Alignment.center, // Centers the icon + children: [ + Divider( + color: Color(0xFF8B8FB2), + thickness: 0.5, + height: 50, + ), + Container( + // padding: EdgeInsets.all(4), + color: Colors.white, // Background to avoid overlapping + child: Row( + mainAxisSize: + MainAxisSize.min, // Prevents row from taking full width + children: [ + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.filter_2_outlined, + color: Color(0xFF8B8FB2), + size: 16, + ), + SizedBox( + width: 10, + ), + Text( + "Passport Details", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + // color: Colors.black, + color: Color(0xFF8B8FB2)), + ) + ], + ), + SizedBox( + height: 20, + ), + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: SizedBox( + width: isDesktop + ? MediaQuery.of(context).size.width * 0.7 + : null, + // height: isDesktop? null :MediaQuery.of(context).size.height , + child: isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSecondRowLeftColumn(isDesktop), + SizedBox(width: 20), + _buildSecondRowRightColumn(isDesktop), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSecondRowLeftColumn(isDesktop), + SizedBox(height: 1), + _buildSecondRowRightColumn(isDesktop), + ], + ), + ), + ), + ], + ), + ], + ), + ), + SizedBox( + width: double.infinity, // Ensures the divider takes full width + child: Stack( + alignment: Alignment.center, // Centers the icon + children: [ + Divider( + color: Color(0xFF8B8FB2), + thickness: 0.5, + height: 50, + ), + Container( + // padding: EdgeInsets.all(4), + color: Colors.white, // Background to avoid overlapping + child: Row( + mainAxisSize: + MainAxisSize.min, // Prevents row from taking full width + children: [ + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.filter_3_outlined, + color: Color(0xFF8B8FB2), + size: 16, + ), + SizedBox( + width: 10, + ), + Text( + "Employee Organization Details", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + // color: Colors.black, + color: Color(0xFF8B8FB2)), + ) + ], + ), + SizedBox( + height: 20, + ), + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: SizedBox( + width: isDesktop + ? MediaQuery.of(context).size.width * 0.7 + : null, + // height: isDesktop? null :MediaQuery.of(context).size.height , + child: isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildThirdRowLeftColumn(isDesktop), + SizedBox(width: 20), + _buildThirdRowRightColumn(isDesktop), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildThirdRowLeftColumn(isDesktop), + SizedBox(height: 1), + _buildThirdRowRightColumn(isDesktop), + ], + ), + ), + ), + ], + ), + ], + ), + ), + SizedBox( + width: double.infinity, // Ensures the divider takes full width + child: Stack( + alignment: Alignment.center, // Centers the icon + children: [ + Divider( + color: Color(0xFF8B8FB2), + thickness: 0.5, + height: 50, + ), + Container( + // padding: EdgeInsets.all(4), + color: Colors.white, // Background to avoid overlapping + child: Row( + mainAxisSize: + MainAxisSize.min, // Prevents row from taking full width + children: [ + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + ], + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.filter_4_outlined, + color: Color(0xFF8B8FB2), + size: 16, + ), + SizedBox( + width: 10, + ), + Text( + "Approver", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + // color: Colors.black, + color: Color(0xFF8B8FB2)), + ) + ], + ), + SizedBox( + height: 20, + ), + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: SizedBox( + width: isDesktop + ? MediaQuery.of(context).size.width * 0.7 + : null, + // height: isDesktop? null :MediaQuery.of(context).size.height , + child: isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildForthRowLeftColumn(isDesktop), + SizedBox(width: 20), + _buildForthRowRightColumn(isDesktop), + SizedBox(width: 20), + _buildForthRowLastColumn(isDesktop), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildForthRowLeftColumn(isDesktop), + SizedBox(height: 1), + _buildForthRowRightColumn(isDesktop), + SizedBox(height: 1), + _buildForthRowLastColumn(isDesktop), + ], + ), + ), + ), + ], + ), + ], + ), + ), + SizedBox( + width: double.infinity, // Ensures the divider takes full width + child: Stack( + alignment: Alignment.center, // Centers the icon + children: [ + Divider( + color: Color(0xFF8B8FB2), + thickness: 0.5, + height: 50, + ), + Container( + // padding: EdgeInsets.all(4), + color: Colors.white, // Background to avoid overlapping + child: Row( + mainAxisSize: + MainAxisSize.min, // Prevents row from taking full width + children: [ + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + Icon(Icons.keyboard_arrow_right_outlined, + color: Colors.blue, size: 28), + SizedBox(width: 1), + ], + ), + ), + ], + ), + ), + ], + ); + } + + //--------------------------------- Personal Details --------------------------- + + Widget _buildFirstRowLeftColumn(bool isDesktop) { + return Container( + // color: Colors.yellow, + 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 Name", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["Fname"], + enabled: !isViewMode, + onChanged: (value) { + _clearError("first_name"); + }, + decoration: InputDecoration( + labelText: "FirstName", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (errorMessages["first_name"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["first_name"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Gender", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: DropdownButtonFormField( + // value: selectedGender, + value: isViewMode ? null : selectedGender, + onChanged: isViewMode + ? null + : (String? newValue) { + setState(() { + selectedGender = newValue; + print("selectedGender - $selectedGender"); + }); + }, + decoration: InputDecoration( + border: InputBorder.none, + enabled: + !isViewMode, // Disables input when in view mode + ), + style: TextStyle(fontSize: 12, color: Colors.black), + items: [ + DropdownMenuItem(value: "Male", child: Text("Male")), + DropdownMenuItem( + value: "Female", child: Text("Female")), + ], + hint: Text( + selectedGender ?? "Select Gender", + style: TextStyle(fontSize: 12, color: Colors.black), + ), + disabledHint: Text( + selectedGender ?? "Select Gender", + style: TextStyle(fontSize: 12, color: Colors.black), + ), + ), + + // child: DropdownSearch( + // selectedItem: selectedGender, + // // key: ValueKey(selectedGender), + // popupProps: PopupProps.menu( + // fit: FlexFit.loose, // Allows flexible height + // constraints: BoxConstraints(maxHeight: 250), + // + // ), + // items: ["Male", "Female", "Others"], + // 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), + // ), + // ), + // onChanged: isViewMode + // ? null + // : (String? newValue) { + // setState(() { + // // Find the country_code based on selected country_name + // selectedGender = newValue; + // print("selectedGender - $selectedGender"); + // // if (selectedCountry!.isNotEmpty) { + // // errorMessages.remove("country_code"); + // // } + // }); + // }, + // + // + // ), + ), + ), + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Mobile Number", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["mobileNumber"], + enabled: !isViewMode, + onChanged: (value) { + _clearError("mobile_no"); + }, + keyboardType: + TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + ], + decoration: InputDecoration( + labelText: "Mobile Number", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (errorMessages["mobile_no"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["mobile_no"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Email", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["email"], + onChanged: (value) { + _clearError("email"); + }, + enabled: !isViewMode, + decoration: InputDecoration( + labelText: "Email", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (errorMessages["email"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["email"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Address", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 110, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["address"], + enabled: !isViewMode, + maxLines: 4, + keyboardType: TextInputType.multiline, + decoration: InputDecoration( + labelText: "Address", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + contentPadding: EdgeInsets.symmetric(vertical: 4), + ), + ), + ), + ), + ], + ), + ], + ), + ], + ), + // ), + ); + } + + Widget _buildFirstRowRightColumn(bool isDesktop) { + DateTime? _selectedDateOfBirth; + + Future _selectCheckDateOfBirth(BuildContext context) async { + DateTime now = DateTime.now(); + DateTime today = DateTime(now.year, now.month, now.day); + + DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: + _selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today) + ? _selectedDateOfBirth! + : today, + firstDate: DateTime(1900), + lastDate: DateTime(2100), + ); + + if (pickedDate != null && pickedDate != _selectedDateOfBirth) { + setState(() { + _selectedDateOfBirth = pickedDate; + controllers["dob"]?.text = + DateFormat('yyyy-MM-dd').format(pickedDate); + }); + } + } + + // ----------------- + + late Map countryMap; // Mapping country_code -> country_name + late List countryCodes; // List of country codes + List countryList = apiCountryData ?? []; + + countryList = apiCountryData ?? []; + + // Map country codes to country names + countryMap = { + for (var item in countryList) + item['country_code'] as String: item['country_name'] as String + }; + + // Extract only country codes for processing + countryCodes = countryMap.keys.toList(); + + selectedCountry ??= null; + + return Container( + // color: Colors.yellow, + 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("Last Name", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["Lname"], + enabled: !isViewMode, + onChanged: (value) { + _clearError("last_name"); + }, + decoration: InputDecoration( + labelText: "LastName", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (errorMessages["last_name"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["last_name"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Date of Birth", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: isViewMode + ? null + : () async { + await _selectCheckDateOfBirth(context); + if (controllers["dob"]!.text.isNotEmpty) { + setState(() { + // errorMessages.remove("start_date"); + }); + } + }, + child: AbsorbPointer( + child: TextField( + controller: controllers["dob"], + style: const TextStyle( + fontSize: 12, color: Colors.black), + decoration: const InputDecoration( + labelText: "Select Date", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(vertical: 16), + suffixIcon: Icon(Icons.calendar_today, + size: 16, color: Color(0xFF8B8FB2)), + ), + ), + ), + ), + ), + ), + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Alternate Mobile Number", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["alternateMobile"], + enabled: !isViewMode, + keyboardType: + TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + ], + decoration: InputDecoration( + labelText: "Alternate Number", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (errorMessages["alternate_mobile_no"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["alternate_mobile_no"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + ], + ), + SizedBox(height: 3), + apiselectedUser != null + ? SizedBox() + : Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Password", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: + TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["password"], + enabled: !isViewMode, + onChanged: (value) { + _clearError("password"); + }, + decoration: InputDecoration( + labelText: "Password", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (errorMessages["password"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["password"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + ], + ), + if (!isDesktop) + SizedBox( + height: 3, + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Country", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: DropdownSearch( + selectedItem: countryMap[selectedCountry], + enabled: !isViewMode, + popupProps: PopupProps.menu( + showSearchBox: true, // Enables search functionality + fit: FlexFit.loose, // Allows flexible height + constraints: BoxConstraints(maxHeight: 250), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search Country...", + contentPadding: + EdgeInsets.symmetric(horizontal: 10), + ), + ), + ), + items: countryMap.values.toList(), + 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 Country", + style: TextStyle(fontSize: 12, color: Colors.black), + ), + ), + onChanged: isViewMode + ? null + : (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + selectedCountry = countryMap.entries + .firstWhere( + (entry) => entry.value == newValue) + .key; + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Postal Code", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["postalCode"], + enabled: !isViewMode, + keyboardType: + TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + ], + decoration: InputDecoration( + labelText: "Postal Code", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + ], + ), + ], + ), + if (!isDesktop) + SizedBox( + height: 3, + ), + ], + ), + // ), + ); + } + + //--------------------------------- Passport Details ----------------------------- + Widget _buildSecondRowLeftColumn(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("Passport Number", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["passportNumber"], + enabled: !isViewMode, + decoration: InputDecoration( + labelText: "Passport Number", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + ], + ), + ], + ), + SizedBox( + height: 3, + ), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Passport Document", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: pickPDFWeb, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10), + alignment: Alignment.centerLeft, + child: Text( + selectedFileNames ?? "Select Document", + style: TextStyle(fontSize: 12, color: Colors.black), + overflow: TextOverflow + .ellipsis, // Prevents overflow issues + ), + ), + ), + ), + ), + SizedBox(height: 10), + if (passportFile != null || passportFileUrlFromApi != null) + + // Centers the text + Container( + color: Colors.white, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.25 + : MediaQuery.of(context).size.width * 0.8, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + print('DOWNLOAD - $passportFile'); + + if (passportFile != null) { + try { + // ✅ Step 1: Create a Blob directly from the file + final blob = html.Blob( + [passportFile!], 'application/pdf'); + + // ✅ Step 2: Generate a download URL from the Blob + final url = + html.Url.createObjectUrlFromBlob(blob); + + // ✅ Step 3: Create an invisible anchor to trigger download + final anchor = html.AnchorElement(href: url) + ..setAttribute("download", + selectedFileNames ?? "document.pdf") + ..style.display = "none"; + + // ✅ Step 4: Add anchor to DOM and click it + html.document.body!.append(anchor); + anchor.click(); + + // ✅ Step 5: Clean up + anchor.remove(); + html.Url.revokeObjectUrl(url); + + print("Download triggered successfully!"); + } catch (e) { + print("Error during download: $e"); + } + } else if (passportFileUrlFromApi != null) { + // Trigger file download from the server path + final anchor = html.AnchorElement( + href: passportFileUrlFromApi!) + ..target = 'blank' + ..download = + selectedFileNames ?? "document.pdf" + ..click(); + } else { + print("No file available to download."); + } + }, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Colors.green.shade300, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + "Download", + style: TextStyle( + fontSize: 12, + color: Colors.white, + fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + textAlign: TextAlign + .end, // Ensures text is centered within the Text widget + ), + SizedBox(width: 5), + Icon( + Icons.download, + size: 13, + color: Colors.white, + ) + ], + ), + ), + ) + ], + ), + ), + + // CustomTextFieldUserWrapper( + // isFocused: false, + // isDesktop: isDesktop, + // child: SizedBox( + // height: 40, + // child: TextField( + // style: TextStyle(fontSize: 12), + // controller: controllers["passportDoc"], + // decoration: InputDecoration( + // labelText: "Select Document", + // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + // floatingLabelBehavior: FloatingLabelBehavior.never, + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + // ), + // ), + // ), + // ), + ], + ), + ], + ), + if (!isDesktop) + SizedBox( + height: 3, + ), + ], + ), + // ), + ); + } + + Widget _buildSecondRowRightColumn(bool isDesktop) { + DateTime? _selectedDateOfIssue; + + Future _selectCheckDateOfIssue(BuildContext context) async { + DateTime now = DateTime.now(); + DateTime today = DateTime(now.year, now.month, now.day); + + DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: + _selectedDateOfIssue != null && _selectedDateOfIssue!.isAfter(today) + ? _selectedDateOfIssue! + : today, + firstDate: today, + lastDate: DateTime(2100), + ); + + if (pickedDate != null && pickedDate != _selectedDateOfIssue) { + setState(() { + _selectedDateOfIssue = pickedDate; + controllers["dateOfIssue"]?.text = + DateFormat('yyyy-MM-dd').format(pickedDate); + }); + } + } + + DateTime? _selectedDateOfExpiry; + + Future _selectCheckDateOfExpiry(BuildContext context) async { + DateTime now = DateTime.now(); + DateTime today = DateTime(now.year, now.month, now.day); + + DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: _selectedDateOfExpiry != null && + _selectedDateOfExpiry!.isAfter(today) + ? _selectedDateOfExpiry! + : today, + firstDate: today, + lastDate: DateTime(2100), + ); + + if (pickedDate != null && pickedDate != _selectedDateOfExpiry) { + setState(() { + _selectedDateOfExpiry = pickedDate; + controllers["dateOfExpiry"]?.text = + DateFormat('yyyy-MM-dd').format(pickedDate); + }); + } + } + + 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("Place of Issue", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["placeOfIssue"], + enabled: !isViewMode, + decoration: InputDecoration( + labelText: "Place Of Issue", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + ], + ), + ], + ), + SizedBox( + height: 3, + ), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Date of Issue", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: isViewMode + ? null + : () async { + await _selectCheckDateOfIssue(context); + if (controllers["dateOfIssue"]! + .text + .isNotEmpty) { + setState(() { + // errorMessages.remove("start_date"); + }); + } + }, + child: AbsorbPointer( + child: TextField( + controller: controllers["dateOfIssue"], + style: const TextStyle( + fontSize: 12, color: Colors.black), + decoration: const InputDecoration( + labelText: "Select Date", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(vertical: 16), + suffixIcon: Icon(Icons.calendar_today, + size: 16, color: Color(0xFF8B8FB2)), + ), + ), + ), + ), + ), + ), + ], + ), + ], + ), + SizedBox( + height: 3, + ), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Date of Expiry", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: isViewMode + ? null + : () async { + await _selectCheckDateOfExpiry(context); + if (controllers["dateOfExpiry"]! + .text + .isNotEmpty) { + setState(() { + // errorMessages.remove("start_date"); + }); + } + }, + child: AbsorbPointer( + child: TextField( + controller: controllers["dateOfExpiry"], + style: const TextStyle( + fontSize: 12, color: Colors.black), + decoration: const InputDecoration( + labelText: "Select Date", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(vertical: 16), + suffixIcon: Icon(Icons.calendar_today, + size: 16, color: Color(0xFF8B8FB2)), + ), + ), + ), + ), + ), + ), + ], + ), + ], + ), + if (!isDesktop) + SizedBox( + height: 3, + ), + ], + ), + // ), + ); + } + +//----------------------------------- Employee Organization Details ------------------ + Widget _buildThirdRowLeftColumn(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("Employee Code", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["employeeCode"], + enabled: !isViewMode, + decoration: InputDecoration( + labelText: "Employee Code", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + ], + ), + ], + ), + // SizedBox(height: 3), + // Row( + // children: [ + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text("User Type", + // style: TextStyle( + // fontSize: 12, + // fontWeight: FontWeight.w200, + // color: Colors.black)), + // SizedBox(height: 5), + // CustomTextFieldUserWrapper( + // isFocused: false, + // isDesktop: isDesktop, + // child: SizedBox( + // height: 40, + // child: DropdownSearch( + // selectedItem: selectedUserType, + // enabled: !isViewMode, + // popupProps: PopupProps.menu( + // // showSearchBox: true, + // fit: FlexFit.loose, // Allows flexible height + // constraints: BoxConstraints(maxHeight: 250), + // ), + // items: [ + // "Normal", + // "Privilege", + // ], + // 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), + // ), + // ), + // onChanged: (String? newValue) { + // setState(() { + // // Find the country_code based on selected country_name + // selectedUserType = newValue; + // + // // print("selectedUserType - $selectedUserType"); + // + // // if (selectedCountry!.isNotEmpty) { + // // errorMessages.remove("country_code"); + // // } + // }); + // }, + // ), + // ), + // ), + // ], + // ), + // ], + // ), + + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Role ", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, // Dropdown doesn't use focus + isDesktop: isDesktop, + child: SizedBox( + height: 45, // Set appropriate height + child: DropdownButtonFormField( + value: isViewMode ? null : selectedRole, + style: TextStyle(fontSize: 12, color: Colors.black), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10), // Proper padding + ), + onChanged: isViewMode + ? null + : (newValue) { + setState(() { + selectedRole = newValue; + }); + }, + items: + apiRoleData?.map>((item) { + return DropdownMenuItem( + value: item['dropdown_key'], // ID as value + child: Text(item['dropdown_value'] ?? "Select")); + }).toList(), + hint: Text("Select"), + disabledHint: Text( + selectedRole ?? "Select Role", + style: TextStyle(fontSize: 12, color: Colors.black), + ), + ), + ), + ), + ], + ), + ], + ), + SizedBox(height: 3), + if (!isDesktop) SizedBox(height: 3), + ], + ), + // ), + ); + } + + Widget _buildThirdRowRightColumn(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("Group", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: DropdownSearch( + selectedItem: selectedLevel, + enabled: !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"); + // } + }); + }, + ), + ), + ), + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Department", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, // Dropdown doesn't use focus + isDesktop: isDesktop, + child: SizedBox( + height: 45, // Set appropriate height + child: DropdownButtonFormField( + value: isViewMode ? null : selectedDepartment, + style: TextStyle(fontSize: 12, color: Colors.black), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10), // Proper padding + ), + onChanged: isViewMode + ? null + : (newValue) { + setState(() { + selectedDepartment = 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: TextStyle(fontSize: 12, color: Colors.black), + ), + ), + ), + ), + ], + ), + ], + ), + ], + ), + + // ), + ); + } + +//----------------------------------- Approver --------------------------------------- + + Widget _buildForthRowLeftColumn(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: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.2 + : MediaQuery.of(context).size.width * 0.8, + child: SizedBox( + height: 40, + child: apiUserData == null + ? Center( + child: Transform.scale( + scale: 0.5, + child: CircularProgressIndicator(), + ), + ) + : DropdownSearch( + selectedItem: userMap[selectedFirstApprover], + enabled: !isViewMode, + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, // Allows flexible height + 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: 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), + ), + ), + onChanged: (String? newValue) { + setState(() { + selectedFirstApprover = userMap.entries + .firstWhere( + (entry) => entry.value == newValue) + .key; + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + ], + ), + ], + ), + ], + ), + + // ), + ); + } + + Widget _buildForthRowRightColumn(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: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.2 + : MediaQuery.of(context).size.width * 0.8, + child: SizedBox( + height: 40, + child: apiUserData == null + ? Center( + child: Transform.scale( + scale: 0.5, + child: CircularProgressIndicator(), + ), + ) + : DropdownSearch( + selectedItem: userMap[selectedSecondApprover], + enabled: !isViewMode, + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, // Allows flexible height + 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: 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), + ), + ), + onChanged: (String? newValue) { + setState(() { + selectedSecondApprover = userMap.entries + .firstWhere( + (entry) => entry.value == newValue) + .key; + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + ], + ), + ], + ), + ], + ), + // ), + ); + } + + Widget _buildForthRowLastColumn(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: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.2 + : MediaQuery.of(context).size.width * 0.8, + child: SizedBox( + height: 40, + child: apiUserData == null + ? Center( + child: Transform.scale( + scale: 0.5, + child: CircularProgressIndicator(), + ), + ) + : DropdownSearch( + selectedItem: userMap[selectedThirdApprover], + enabled: !isViewMode, + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, // Allows flexible height + 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: 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), + ), + ), + onChanged: (String? newValue) { + setState(() { + selectedThirdApprover = userMap.entries + .firstWhere( + (entry) => entry.value == newValue) + .key; + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + ], + ), + ], + ), + ], + ), + + // ), + ); + } + +//----------------------------------- Submit --------------------------------------- + + List _buildSubmit(isDesktop, Color layoutColor) { + return [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: layoutColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: layoutColor, width: 2), + ), + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: () { + isEditProfile ? context.go('/listPlan') : context.go('/listUser'); + }, + child: isViewMode ? Text("Back") : Text("Cancel")), + SizedBox( + width: 20, + ), + if (!isViewMode) + MouseRegion( + cursor: isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + isViewMode ? layoutColor : layoutColor, // Keep original color + foregroundColor: isViewMode + ? Colors.white + : Colors.white, // Keep original color + disabledBackgroundColor: + layoutColor, // Ensure color remains when disabled + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: layoutColor, width: 2), + ), + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: + isViewMode ? null : handleSubmit, // Disable when in view mode + child: Text("Submit"), + ), + ) + ]; + } +} + +class DatabaseHelper { + // Example: Singleton pattern to ensure only one instance of the class exists + static final DatabaseHelper _instance = DatabaseHelper._internal(); + + // Private constructor to prevent external instantiation + DatabaseHelper._internal(); + + // Public method to access the instance + static DatabaseHelper get instance => _instance; + + // Add your database methods here + Future fetchData() async { + // Implement data fetching logic + } +} diff --git a/lib/Screens/userManagement/create_user/office_details.dart b/lib/Screens/userManagement/create_user/office_details.dart new file mode 100644 index 0000000..02fe444 --- /dev/null +++ b/lib/Screens/userManagement/create_user/office_details.dart @@ -0,0 +1,822 @@ +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:intl/intl.dart'; + +import '../../../services/apiService.dart'; +import '../../../widgets/custom_user_form.dart'; + +class OfficeDetails extends StatefulWidget { + final Map controllers; + final Map errorMessages; + final bool isDesktop; + final bool isViewMode; + + final ValueChanged? onLevelChanged; + final ValueChanged? onDepartmentChanged; + final ValueChanged? onFirstApproverChanged; + final ValueChanged? onSecondApproverChanged; + final ValueChanged? onThirdApproverChanged; + + final String? selectedLevel; + final String? selectedDepartment; + final String? selectedFirstApprover; + final String? selectedSecondApprover; + final String? selectedThirdApprover; + + 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, + }) : 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; + + 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; + 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? selectedFileNames; + Uint8List? passportDocumentBytes; + String? passportFileUrlFromApi; + String? base64PDF; + + html.File? passportFile; + + List dataHeader = [ + "Fname", + "Lname", + "email", + "password", + "dob", + "gender", + "mobileNumber", + "alternateMobile", + "address", + "postalCode", + "country", + "passportNumber", + "placeOfIssue", + "passportDoc", + "userType", + "roleId", + "deptId", + "levelId", + "firstApproval", + "secondApproval", + "thirdApproval", + "employeeCode", + "dateOfIssue", + "dateOfExpiry", + "changePassword" + ]; + + Color? layoutColor; + Color? bodyColor; + + @override + void initState() { + super.initState(); + 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; + + fetchDepartment(); + fetchUsers(); + fetchFindGroup(); + } + + void _clearError(String field) { + setState(() { + widget.errorMessages.remove(field); + }); + } + + Future fetchUsers() async { + try { + List users = await apiService.fetchUsers(); + setState(() { + // apiUserData = users; + + apiUserData = users.where((user) => user["role_id"] == "4").toList(); + + 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 { + List department = await apiService.fetchCostCenter(); + setState(() { + apiCostData = department; + }); + } catch (e) { + print('Error fetching department list: $e'); + } + } + + Future fetchFindGroup() async { + try { + final result = await apiService.fetchAllGroup(); + 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( + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: Colors.blueGrey.shade300, // Set your desired color + width: 0.09, // Set border thickness + ), + ), + ), + child: Column( + children: [ + SizedBox( + height: 10, + ), + _buildFirstRow(widget.isDesktop), + if (widget.isDesktop) + SizedBox( + height: 10, + ), + _buildSecondRow(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 buildEmpCodeField() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Employee Code", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: widget.controllers["employeeCode"], + enabled: !widget.isViewMode, + decoration: InputDecoration( + labelText: "Employee Code", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + ], + ); + } + + Widget buildDepartmentField() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Department", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, // Dropdown doesn't use focus + isDesktop: widget.isDesktop, + child: SizedBox( + height: 45, // Set appropriate height + child: DropdownButtonFormField( + value: widget.isViewMode ? null : selectedDepartment, + style: TextStyle(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: TextStyle(fontSize: 12, color: Colors.black), + ), + ), + ), + ), + ], + ); + } + + Widget buildGroupField() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Group", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + 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: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: apiAllGroups == null + ? Center( + child: Transform.scale( + scale: 0.5, + child: CircularProgressIndicator(), + ), + ) + : DropdownSearch( + selectedItem: groupMap[selectedLevel], + enabled: !widget.isViewMode, + + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, // Allows flexible height + 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: 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), + ), + ), + + 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: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + 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 + 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: 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), + ), + ), + // 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: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + 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 + 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: 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), + ), + ), + + 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: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + 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 + 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: 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), + ), + ), + 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"); + // // } + // }); + // }, + ), + ), + ), + ], + ), + ], + ), + ], + ), + + // ), + ); + } +} diff --git a/lib/Screens/userManagement/create_user/personal_details.dart b/lib/Screens/userManagement/create_user/personal_details.dart new file mode 100644 index 0000000..04f3d66 --- /dev/null +++ b/lib/Screens/userManagement/create_user/personal_details.dart @@ -0,0 +1,977 @@ +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:intl/intl.dart'; + +import '../../../services/apiService.dart'; +import '../../../widgets/custom_user_form.dart'; + +class PersonalDetails extends StatefulWidget { + final GlobalKey personalDetailsKey; + final Map controllers; + final Map errorMessages; + final bool isDesktop; + final bool isViewMode; + final bool apiselectedUser; + + final ValueChanged? onGenderChanged; + final ValueChanged? onCountryChanged; + final ValueChanged? onRoleChanged; + + final String? selectedGender; + final String? selectedCountry; + final String? selectedRole; + + // const PersonalDetails(this.isDesktop, this.isViewMode, {super.key},); + const PersonalDetails({ + Key? key, + this.selectedGender, + this.selectedCountry, + this.selectedRole, + required this.personalDetailsKey, + required this.controllers, + required this.errorMessages, + required this.isDesktop, + required this.isViewMode, + required this.apiselectedUser, + this.onGenderChanged, + this.onCountryChanged, + this.onRoleChanged, + }) : super(key: key); + + @override + PersonalDetailsState createState() => PersonalDetailsState(); +} + +class PersonalDetailsState extends State { + final ApiService apiService = ApiService(); + + late Map countryMap; // Mapping country_code -> country_name + late List countryCodes; // List of country codes + late List? apiCountryData; + late List? apiRoleData; + + String? get selectedGenderValue => selectedGender; + String? get selectedCountryValue => selectedCountry; + String? get selectedRoleValue => selectedRole; + + String? selectedTab; + + String? selectedCountry; + String? selectedGender; + + String? selectedUserType; + String? selectedRole; + String? selectedLevel; + String? selectedDepartment; + String? selectedFirstApprover; + String? selectedSecondApprover; + String? selectedThirdApprover; + + String? selectedFileNames; + Uint8List? passportDocumentBytes; + String? passportFileUrlFromApi; + String? base64PDF; + + html.File? passportFile; + + List dataHeader = [ + "Fname", + "Lname", + "email", + "password", + "dob", + "gender", + "mobileNumber", + "alternateMobile", + "address", + "postalCode", + "country", + "passportNumber", + "placeOfIssue", + "passportDoc", + "userType", + "roleId", + "deptId", + "levelId", + "firstApproval", + "secondApproval", + "thirdApproval", + "employeeCode", + "dateOfIssue", + "dateOfExpiry", + "changePassword" + ]; + + Color? layoutColor; + Color? bodyColor; + + @override + void initState() { + super.initState(); + + apiCountryData = null; + + apiRoleData = null; + selectedGender = widget.selectedGender; + selectedCountry = widget.selectedCountry; + selectedRole = widget.selectedRole; + fetchRoles(); + fetchCountries(); + } + + void _clearError(String field) { + setState(() { + widget.errorMessages.remove(field); + }); + } + + Future fetchRoles() async { + try { + final response = await apiService.fetchMasterDropdown(); + + if (response is Map && response.containsKey("role")) { + List roleList = response["role"]; // Extract the list + setState(() { + apiRoleData = roleList; + }); + print("APIROLEData - $apiRoleData"); + } else { + print("Error: 'role' key not found or response is not a Map."); + } + } catch (e) { + print('Error fetching role list: $e'); + } + } + + Future fetchCountries() async { + try { + List countries = await apiService.fetchCountryList(); + setState(() { + apiCountryData = countries; + }); + } catch (e) { + print('Error fetching country list: $e'); + } + } + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: Colors.blueGrey.shade300, // Set your desired color + width: 0.09, // Set border thickness + ), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 10, + ), + _buildFirstRow(widget.isDesktop), + SizedBox( + height: 10, + ), + _buildSecondRow(widget.isDesktop), + SizedBox( + height: 10, + ), + _buildThirdRow(widget.isDesktop), + SizedBox( + height: 10, + ), + _buildForthRow(widget.isDesktop), + ], + ), + ); + } + + Widget _buildFirstRow(bool isDesktop) { + return Container( + color: Colors.white, + child: widget.isDesktop + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildFirstNameField(), + Spacer(), + buildLastNameField(), + Spacer(), // Space after Last Name + buildGenderField(), + SizedBox( + width: 15, + ), + buildDobField(), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildFirstNameField(), + SizedBox(height: 8), // Vertical space + buildLastNameField(), + SizedBox(height: 8), + buildGenderField(), + SizedBox(height: 8), + buildDobField(), + ], + ), + ); + } + + Widget _buildSecondRow(bool isDesktop) { + List fields = [ + buildEmailField(), + buildMobileField(), + buildAlternateMobileField(), + ]; + return Container( + color: Colors.white, + child: widget.isDesktop + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildEmailField(), + Spacer(), + buildMobileField(), + Spacer(), // Space after Last Name + buildAlternateMobileField(), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildEmailField(), + SizedBox(height: 8), // Vertical space + buildMobileField(), + SizedBox(height: 8), + buildAlternateMobileField(), + ], + ), + ); + } + + Widget _buildThirdRow(bool isDesktop) { + return Container( + color: Colors.white, + child: isDesktop + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildRole(), + SizedBox( + width: 15, + ), + if (!widget.apiselectedUser) buildPassword() + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildRole(), + SizedBox(height: 8), // + if (!widget.apiselectedUser) buildPassword() + ], + ), + ); + } + + Widget _buildForthRow(bool isDesktop) { + return Container( + color: Colors.white, + child: isDesktop + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildAddress(), + SizedBox( + width: 15, + ), + buildCountryField(), + SizedBox( + width: 15, + ), + buildPostalCodeField(), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildAddress(), + SizedBox(height: 8), // + buildCountryField(), + SizedBox(height: 8), // Vertical space + buildPostalCodeField(), + ], + ), + ); + } + + Widget buildFirstNameField() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("First Name", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: widget.controllers["Fname"], + enabled: !widget.isViewMode, + onChanged: (value) { + _clearError("first_name"); + }, + decoration: InputDecoration( + labelText: "FirstName", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (widget.errorMessages["first_name"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + widget.errorMessages["first_name"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ); + } + + Widget buildLastNameField() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Last Name", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: widget.controllers["Lname"], + enabled: !widget.isViewMode, + onChanged: (value) { + _clearError("last_name"); + }, + decoration: InputDecoration( + labelText: "LastName", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (widget.errorMessages["last_name"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + widget.errorMessages["last_name"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ); + } + + Widget buildGenderField() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Gender", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + width: widget.isDesktop + ? MediaQuery.of(context).size.width * 0.12 + : null, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: DropdownButtonFormField( + // value: selectedGender, + value: widget.isViewMode ? null : selectedGender, + onChanged: widget.isViewMode + ? null + : (String? newValue) { + setState(() { + selectedGender = newValue; + print("selectedGender - $selectedGender"); + }); + + widget.onGenderChanged?.call(newValue); + }, + decoration: InputDecoration( + border: InputBorder.none, + enabled: !widget.isViewMode, // Disables input when in view mode + ), + style: TextStyle(fontSize: 12, color: Colors.black), + items: [ + DropdownMenuItem(value: "Male", child: Text("Male")), + DropdownMenuItem(value: "Female", child: Text("Female")), + ], + hint: Text( + selectedGender ?? "Select Gender", + style: TextStyle(fontSize: 12, color: Colors.black), + ), + disabledHint: Text( + selectedGender ?? "Select Gender", + style: TextStyle(fontSize: 12, color: Colors.black), + ), + ), + + // child: DropdownSearch( + // selectedItem: selectedGender, + // // key: ValueKey(selectedGender), + // popupProps: PopupProps.menu( + // fit: FlexFit.loose, // Allows flexible height + // constraints: BoxConstraints(maxHeight: 250), + // + // ), + // items: ["Male", "Female", "Others"], + // 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), + // ), + // ), + // onChanged: isViewMode + // ? null + // : (String? newValue) { + // setState(() { + // // Find the country_code based on selected country_name + // selectedGender = newValue; + // print("selectedGender - $selectedGender"); + // // if (selectedCountry!.isNotEmpty) { + // // errorMessages.remove("country_code"); + // // } + // }); + // }, + // + // + // ), + ), + ), + ], + ); + } + + Widget buildDobField() { + DateTime? _selectedDateOfBirth; + + Future _selectCheckDateOfBirth(BuildContext context) async { + DateTime now = DateTime.now(); + DateTime today = DateTime(now.year, now.month, now.day); + + DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: + _selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today) + ? _selectedDateOfBirth! + : today, + firstDate: DateTime(1900), + lastDate: DateTime(2100), + ); + + if (pickedDate != null && pickedDate != _selectedDateOfBirth) { + setState(() { + _selectedDateOfBirth = pickedDate; + widget.controllers["dob"]?.text = + DateFormat('yyyy-MM-dd').format(pickedDate); + }); + } + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Date of Birth", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + width: widget.isDesktop + ? MediaQuery.of(context).size.width * 0.12 + : null, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: widget.isViewMode + ? null + : () async { + await _selectCheckDateOfBirth(context); + if (widget.controllers["dob"]!.text.isNotEmpty) { + setState(() { + // errorMessages.remove("start_date"); + }); + } + }, + child: AbsorbPointer( + child: TextField( + controller: widget.controllers["dob"], + style: const TextStyle(fontSize: 12, color: Colors.black), + decoration: const InputDecoration( + labelText: "Select Date", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + suffixIcon: Icon(Icons.calendar_today, + size: 16, color: Color(0xFF8B8FB2)), + ), + ), + ), + ), + ), + ), + ], + ); + } + + Widget buildEmailField() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Email", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: widget.controllers["email"], + onChanged: (value) { + _clearError("email"); + }, + enabled: !widget.isViewMode, + decoration: InputDecoration( + labelText: "Email", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (widget.errorMessages["email"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + widget.errorMessages["email"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ); + } + + Widget buildMobileField() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Mobile Number", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: widget.controllers["mobileNumber"], + enabled: !widget.isViewMode, + onChanged: (value) { + _clearError("mobile_no"); + }, + keyboardType: TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + ], + decoration: InputDecoration( + labelText: "Mobile Number", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (widget.errorMessages["mobile_no"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + widget.errorMessages["mobile_no"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ); + } + + Widget buildAlternateMobileField() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Alternate Mobile Number", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: widget.controllers["alternateMobile"], + enabled: !widget.isViewMode, + keyboardType: TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + ], + decoration: InputDecoration( + labelText: "Alternate Number", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (widget.errorMessages["alternate_mobile_no"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + widget.errorMessages["alternate_mobile_no"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ); + } + + Widget buildCountryField() { + late Map countryMap; // Mapping country_code -> country_name + late List countryCodes; // List of country codes + List countryList = apiCountryData ?? []; + + countryList = apiCountryData ?? []; + + // Map country codes to country names + countryMap = { + for (var item in countryList) + item['country_code'] as String: item['country_name'] as String + }; + + // Extract only country codes for processing + countryCodes = countryMap.keys.toList(); + + selectedCountry ??= null; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Country", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: DropdownSearch( + selectedItem: countryMap[selectedCountry], + enabled: !widget.isViewMode, + popupProps: PopupProps.menu( + showSearchBox: true, // Enables search functionality + fit: FlexFit.loose, // Allows flexible height + constraints: BoxConstraints(maxHeight: 200), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search Country...", + contentPadding: EdgeInsets.symmetric(horizontal: 10), + ), + ), + ), + items: countryMap.values.toList(), + 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 Country", + style: TextStyle(fontSize: 12, color: Colors.black), + ), + ), + // onChanged: widget.isViewMode + // ? null + // : (String? newValue) { + // setState(() { + // // Find the country_code based on selected country_name + // selectedCountry = countryMap.entries + // .firstWhere((entry) => entry.value == newValue) + // .key; + // + // // if (selectedCountry!.isNotEmpty) { + // // errorMessages.remove("country_code"); + // // } + // }); + // widget.onCountryChanged?.call(newValue); + // }, + + onChanged: widget.isViewMode + ? null + : (String? newValue) { + if (newValue == null) return; + + final countryCode = countryMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; + + setState(() { + selectedCountry = countryCode; + }); + + widget.onCountryChanged + ?.call(countryCode); // ✅ send code not name + }, + ), + ), + ), + ], + ); + } + + Widget buildPostalCodeField() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Postal Code", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: widget.controllers["postalCode"], + enabled: !widget.isViewMode, + keyboardType: TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + ], + decoration: InputDecoration( + labelText: "Postal Code", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + ], + ); + } + + Widget buildRole() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Role ", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, // Dropdown doesn't use focus + isDesktop: widget.isDesktop, + child: SizedBox( + height: 45, // Set appropriate height + child: DropdownButtonFormField( + value: widget.isViewMode ? null : selectedRole, + style: TextStyle(fontSize: 12, color: Colors.black), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding + ), + onChanged: widget.isViewMode + ? null + : (newValue) { + setState(() { + selectedRole = newValue; + }); + widget.onRoleChanged?.call(newValue); + }, + items: apiRoleData?.map>((item) { + return DropdownMenuItem( + value: item['dropdown_key'], // ID as value + child: Text(item['dropdown_value'] ?? "Select")); + }).toList(), + hint: Text("Select"), + disabledHint: Text( + selectedRole ?? "Select Role", + style: TextStyle(fontSize: 12, color: Colors.black), + ), + ), + ), + ), + ], + ); + } + + Widget buildAddress() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Address", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 80, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: widget.controllers["address"], + enabled: !widget.isViewMode, + maxLines: 2, + keyboardType: TextInputType.multiline, + decoration: InputDecoration( + labelText: "Address", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + contentPadding: EdgeInsets.symmetric(vertical: 4), + ), + ), + ), + ), + ], + ); + } + + Widget buildPassword() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Password", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: widget.controllers["password"], + enabled: !widget.isViewMode, + onChanged: (value) { + _clearError("password"); + }, + decoration: InputDecoration( + labelText: "Password", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (widget.errorMessages["password"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + widget.errorMessages["password"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ); + } + + // apiselectedUser != null + // ? SizedBox() +} diff --git a/lib/Screens/userManagement/create_user/traveller_details.dart b/lib/Screens/userManagement/create_user/traveller_details.dart new file mode 100644 index 0000000..aea541e --- /dev/null +++ b/lib/Screens/userManagement/create_user/traveller_details.dart @@ -0,0 +1,27 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class TravellerDetails extends StatefulWidget { + @override + _TravellerDetailsState createState() => _TravellerDetailsState(); +} + +class _TravellerDetailsState extends State { + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: Colors.blueGrey.shade300, // Set your desired color + width: 0.09, // Set border thickness + ), + ), + ), + child: Column( + children: [Text("Traveller ...........")], + ), + ); + } +} diff --git a/lib/Screens/userManagement/user_List.dart b/lib/Screens/userManagement/user_List.dart index 7e4b71d..5fbe978 100644 --- a/lib/Screens/userManagement/user_List.dart +++ b/lib/Screens/userManagement/user_List.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:frontend/utils/auth_utils.dart'; import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -9,6 +10,8 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../config/apiUrl.dart'; import '../../routes/custom_appBar.dart'; import '../../routes/custom_drawer.dart'; +import '../../utils/pagination.dart'; +import '../../widgets/custom_popup.dart'; class UserListScreen extends StatefulWidget { @override @@ -29,7 +32,7 @@ class _UserListScreenState extends State { TextEditingController searchController = TextEditingController(); int currentPage = 0; - int itemsPerPage = 8; + int itemsPerPage = 10; @override void initState() { @@ -303,7 +306,7 @@ class _UserListScreenState extends State { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( - backgroundColor: Colors.white, + backgroundColor: Color(0xFFf5f5f5), // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), appBar: CustomAppBar(isDesktop: isDesktop), @@ -316,7 +319,7 @@ class _UserListScreenState extends State { vertical: MediaQuery.of(context).size.height * 0, // 5% of screen height as vertical padding ) - : EdgeInsets.all(8), + : EdgeInsets.all(0), child: Row( children: [ // if (isDesktop) CustomDrawer(isDesktop: true), @@ -331,6 +334,11 @@ class _UserListScreenState extends State { Widget buildGroupList(bool isDesktop) { return Container( + margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, + padding: const EdgeInsets.all(1), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), // decoration: BoxDecoration( // // color: Colors.amber, // // color: bodyColor, @@ -370,50 +378,56 @@ class _UserListScreenState extends State { children: [ Row( children: [ - const Text('User List', - style: TextStyle( - fontFamily: "Inter", - fontSize: 16, - fontWeight: FontWeight.w600, - color: Color(0xFF212121))), + Text( + 'Users', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), ], ), - Spacer(), - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterUsers, - decoration: InputDecoration( - hintText: "Search for a User", - hintStyle: TextStyle( - fontSize: 12, color: Color(0xFF9E9DBD)), - prefixIcon: Icon( - Icons.search, - color: Color(0xFF9E9DBD), - size: 18, + if (isDesktop) + SizedBox( + width: MediaQuery.of(context).size.width * 0.28, + ), + + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterUsers, + decoration: InputDecoration( + hintText: "Search for a User", + hintStyle: TextStyle( + fontSize: 12, color: Color(0xFF9E9DBD)), + prefixIcon: Icon( + Icons.search, + color: Color(0xFF9E9DBD), + size: 18, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade200, width: 0.5), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, width: 1), + ), ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), + style: GoogleFonts.poppins( + fontSize: 12, ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: Colors.grey.shade200, width: 0.5), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: Colors.grey.shade300, width: 1), - ), - ), - style: TextStyle( - fontSize: 12, - fontFamily: "Inter", ), ), - ), // SizedBox(width: 16), Spacer(), @@ -443,7 +457,6 @@ class _UserListScreenState extends State { // 'apiUserData': users, // } ); - if (!isDesktop) Navigator.pop(context); }, child: Row( mainAxisSize: @@ -451,7 +464,9 @@ class _UserListScreenState extends State { children: [ Text( "Add New User", - style: TextStyle(fontSize: 13), + style: GoogleFonts.poppins( + fontSize: isDesktop ? 13 : 11, + ), ), SizedBox(width: 8), // spacing between icon and text Icon( @@ -464,6 +479,54 @@ class _UserListScreenState extends State { ), ], ), + + if (!isDesktop) + SizedBox( + height: 5, + ), + isDesktop + ? SizedBox.shrink() + : Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.8, + height: 35, + child: TextField( + controller: searchController, + onChanged: filterUsers, + decoration: InputDecoration( + hintText: "Search for a User", + hintStyle: TextStyle( + fontSize: 12, color: Color(0xFF9E9DBD)), + prefixIcon: Icon( + Icons.search, + color: Color(0xFF9E9DBD), + size: 18, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade200, + width: 0.5), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, width: 1), + ), + ), + style: GoogleFonts.poppins( + fontSize: 12, + ), + ), + ), + // SizedBox(width: 16), + ], + ), const SizedBox(height: 10), FutureBuilder>( future: futureUsers, @@ -478,28 +541,34 @@ class _UserListScreenState extends State { padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, - children: const [ - Icon(Icons.error_outline, + children: [ + const Icon(Icons.error_outline, color: Colors.redAccent, size: 60), - SizedBox(height: 16), - Text("Oops!", - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Colors.redAccent)), - SizedBox(height: 8), - Text("No Plans Available For This User", - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey)), - SizedBox(height: 20), - Text("Please Create Plan", - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16, color: Colors.grey)), - SizedBox(height: 20), + const SizedBox(height: 16), + Text( + "Oops!", + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.redAccent), + ), + const SizedBox(height: 8), + Text( + "No Plans Available For This User", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey), + ), + const SizedBox(height: 20), + Text( + "Please Create Plan", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, color: Colors.grey), + ), + const SizedBox(height: 20), ], ), ), @@ -536,39 +605,42 @@ class _UserListScreenState extends State { horizontalInside: BorderSide( width: 0.5, color: Colors.grey.shade200), ), - columns: const [ + columns: [ DataColumn( - label: Text('UserName', - style: TextStyle( - // color: Color(0xFF9E9DBD), - // color: Color(0xFF9E9DBD), - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'UserName', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Email Id', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Email ID', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Role', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Role', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Status', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600), + )), DataColumn( - label: Text('Actions', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - fontWeight: FontWeight.w600))), + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600), + )), ], rows: paginatedUser.map((user) { String userId = @@ -611,40 +683,46 @@ class _UserListScreenState extends State { fontWeight: FontWeight.w400), ))), DataCell( - Row( - children: [ - MouseRegion( - cursor: user['is_active'] == "0" - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: IconButton( - icon: Icon(Icons.remove_red_eye, - size: 18, - color: user['is_active'] == "0" - ? Colors.grey - : Color(0xFF475569)), - onPressed: user['is_active'] == "0" - ? null - : () { - context.go( - "/CreateUserDetails", - extra: { - "selectedUser": user, - "isViewMode": true - }, - ); - }, - ), - ), - - MouseRegion( - cursor: user['is_active'] == "0" - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: GestureDetector( - onTap: user['is_active'] == "0" - ? null - : () { + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 8, vertical: 8), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon( + Icons.remove_red_eye, + color: + Color(0xFF475569), + size: 18), + onPressed: () { + context.go( + "/CreateUserDetails", + extra: { + "selectedUser": user, + "isViewMode": true + }, + ); + }), + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15), + onPressed: () { context.go( "/CreateUserDetails", extra: { @@ -653,52 +731,95 @@ class _UserListScreenState extends State { }, ); }, - child: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), + ), + ], + ), ), ), - - // MouseRegion( - // cursor: user['is_active'] == "0" - // ? SystemMouseCursors - // .forbidden - // : SystemMouseCursors.click, - // child: IconButton( - // icon: Icon(Icons.edit, - // color: - // user['is_active'] == - // "0" - // ? Colors.grey - // : Colors.green), - // onPressed: - // user['is_active'] == "0" - // ? null - // : () { - // print( - // "USER: $user"); - // - // // final userJson = jsonEncode( - // // user); // Convert user map to string - // // final encodedUser = - // // Uri.encodeComponent( - // // userJson); - // - // context.go( - // "/CreateUserDetails", - // extra: { - // "selectedUser": - // user, - // "isViewMode": - // false - // }, - // ); - // }, - // ), - // ), ], ), + // Row( + // children: [ + // MouseRegion( + // cursor: user['is_active'] == "0" + // ? SystemMouseCursors.forbidden + // : SystemMouseCursors.click, + // child: IconButton( + // icon: Icon(Icons.remove_red_eye, + // size: 18, + // color: user['is_active'] == "0" + // ? Colors.grey + // : Color(0xFF475569)), + // onPressed: user['is_active'] == "0" + // ? null + // : () { + // context.go( + // "/CreateUserDetails", + // extra: { + // "selectedUser": user, + // "isViewMode": true + // }, + // ); + // }, + // ), + // ), + // + // MouseRegion( + // cursor: user['is_active'] == "0" + // ? SystemMouseCursors.forbidden + // : SystemMouseCursors.click, + // child: GestureDetector( + // onTap: user['is_active'] == "0" + // ? null + // : () { + // + // }, + // child: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15), + // ), + // ), + // + // // MouseRegion( + // // cursor: user['is_active'] == "0" + // // ? SystemMouseCursors + // // .forbidden + // // : SystemMouseCursors.click, + // // child: IconButton( + // // icon: Icon(Icons.edit, + // // color: + // // user['is_active'] == + // // "0" + // // ? Colors.grey + // // : Colors.green), + // // onPressed: + // // user['is_active'] == "0" + // // ? null + // // : () { + // // print( + // // "USER: $user"); + // // + // // // final userJson = jsonEncode( + // // // user); // Convert user map to string + // // // final encodedUser = + // // // Uri.encodeComponent( + // // // userJson); + // // + // // context.go( + // // "/CreateUserDetails", + // // extra: { + // // "selectedUser": + // // user, + // // "isViewMode": + // // false + // // }, + // // ); + // // }, + // // ), + // // ), + // ], + // ), ), ]); }).toList(), @@ -707,65 +828,203 @@ class _UserListScreenState extends State { }, ); + Widget buildMobileCardView(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final user = paginatedUser[index]; + return Card( + color: Colors.white, + margin: EdgeInsets.symmetric( + horizontal: 12, vertical: 6), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Status and Employee Code + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + user['role_value'] ?? 'N/A', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.w700), + ), + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 8, vertical: 8), + child: Row( + mainAxisSize: + MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon( + Icons + .remove_red_eye, + color: Color( + 0xFF475569), + size: 18), + onPressed: () { + context.go( + "/CreateUserDetails", + extra: { + "selectedUser": + user, + "isViewMode": true + }, + ); + }), + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15), + onPressed: () { + context.go( + "/CreateUserDetails", + extra: { + "selectedUser": + user, + "isViewMode": false + }, + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), + + SizedBox(height: 2), + // Trip Id and Trip Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "${user['first_name'] ?? ''} ${user['last_name'] ?? ''}", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + fontWeight: FontWeight.w500), + ), + ], + ), + ], + ), + SizedBox(height: 2), + + // Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + user['email'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87), + ), + ], + ), + Spacer(), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () { + handleToggleUserStatus( + user['user_id'], + user['is_active'], + user); + }, + child: Text( + user['is_active'] == "1" + ? "Active" + : "Inactive", + style: TextStyle( + color: + user['is_active'] == + "1" + ? Colors.green + : Colors.grey, + fontFamily: "Inter", + fontSize: 11, + fontWeight: + FontWeight.w400), + )) + ], + ), + ], + ), + // Actions + // Actions + ], + ), + ), + ); + }, + ); + } + return Expanded( child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - isDesktop - ? table - : SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SingleChildScrollView( + Expanded( + child: isDesktop + ? SingleChildScrollView( scrollDirection: Axis.vertical, - child: table, - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - // Previous button with arrow icon - IconButton( - icon: Icon( - Icons.arrow_back_ios_new, - size: 10, - ), - onPressed: currentPage > 0 - ? () { - setState(() { - currentPage--; - }); - } - : null, - ), - SizedBox(width: 2), - - // Page number text - Text( - 'Page ${currentPage + 1}', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w400, - color: Colors.black87, - fontFamily: "Inter", - ), - ), - SizedBox(width: 2), - - // Next button with arrow icon - IconButton( - icon: Icon( - Icons.arrow_forward_ios, - size: 10, - ), - onPressed: (currentPage + 1) * itemsPerPage < - users.length - ? () { - setState(() { - currentPage++; - }); - } - : null, - ), - ], + child: table, // <-- your existing table + ) + : buildMobileCardView(paginatedUser), + ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: users.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, ), ], ), diff --git a/lib/routes/custom_drawer.dart b/lib/routes/custom_drawer.dart index 2218aaa..9fb6fbe 100644 --- a/lib/routes/custom_drawer.dart +++ b/lib/routes/custom_drawer.dart @@ -205,7 +205,7 @@ class _CustomDrawerState extends State { TextStyle(fontSize: 11, color: Color(0xFF212121)), ), Image.asset( - 'assets/images/login/travelSpend_Logo.png', + 'assets/images/login/logoNew.jpg', width: 90, height: 25, fit: BoxFit.contain, diff --git a/lib/routes/custom_router.dart b/lib/routes/custom_router.dart index 11d5689..3ec834d 100644 --- a/lib/routes/custom_router.dart +++ b/lib/routes/custom_router.dart @@ -10,7 +10,7 @@ import 'package:frontend/Screens/plans/create_plans.dart'; import 'package:frontend/Screens/plans/list_plans.dart'; import 'package:frontend/Screens/policy/policy.dart'; import 'package:frontend/Screens/policy/policy_list.dart'; -import 'package:frontend/Screens/userManagement/create_user/create_user.dart'; +import 'package:frontend/Screens/userManagement/create_user/create_user1.dart'; import 'package:frontend/Screens/userManagement/user_List.dart'; import 'package:go_router/go_router.dart'; @@ -19,6 +19,7 @@ import '../Screens/allTrips/travel_agent_list.dart'; import '../Screens/approvals/approval_list.dart'; import '../Screens/group/group.dart'; import '../Screens/group/groupList.dart'; +import '../Screens/userManagement/create_user/create_user.dart'; final GoRouter router = GoRouter( routes: [ @@ -52,7 +53,7 @@ final GoRouter router = GoRouter( ), GoRoute( path: '/CreateUserDetails', - builder: (context, state) => CreateUserForm(), + builder: (context, state) => CreateUserFormDetials(), // builder: (context, state) { // final userParam = state.uri.queryParameters['user']; // diff --git a/lib/services/apiService.dart b/lib/services/apiService.dart index a63f6ee..5fe8db3 100644 --- a/lib/services/apiService.dart +++ b/lib/services/apiService.dart @@ -234,6 +234,42 @@ class ApiService { } } + Future> fetchFindGroup() async { + String? orgId = await getOrgId(); + + final String apiUrldata = '$apiUrl/api/groups/find/$orgId'; + + final token = await getToken(); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + try { + final data = json.decode(response.body); + print(data); + if (!data.containsKey('data') || data['data'] is! List) { + throw Exception( + "Invalid response format: 'data' field is missing or not a List"); + } + return data['data']; + } catch (e) { + throw Exception('Error parsing response: $e'); + } + } else { + throw Exception('Failed to load plans'); + } + } + Future> fetchAllPolicy() async { String? orgId = await getOrgId(); diff --git a/pubspec.yaml b/pubspec.yaml index 5e73fee..757b4b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -77,12 +77,14 @@ flutter: - assets/images/login/travelSpend_Logo.png - assets/images/login/TravelSpendsLogo1.png - assets/images/login/VectorG.png + - assets/images/login/logoNew.jpg - assets/images/login/microsoft.png - assets/images/IconsImg/delete.png - assets/images/IconsImg/edit.png - assets/images/IconsImg/planPdf_icon.png + # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg