From b3eb4051bf9857b55570fd7a9fa02b3e69405792 Mon Sep 17 00:00:00 2001 From: venba-Inspriron-3558 Date: Sat, 7 Jun 2025 10:18:09 +0530 Subject: [PATCH 1/5] symbols only --- lib/Screens/costCenter/costCenter_list.dart | 2 +- lib/Screens/forex/forexDetails.dart | 4 ++-- lib/Screens/forex/forex_list.dart | 6 +++--- lib/Screens/group/groupDetails.dart | 8 ++++---- lib/Screens/group/groupList.dart | 4 ++-- lib/Screens/hotels/hotelsDetails.dart | 4 ++-- lib/Screens/itnerary/accomodations.dart | 10 +++++----- lib/Screens/itnerary/bus.dart | 8 ++++---- lib/Screens/itnerary/forex.dart | 10 ++++++++-- lib/Screens/itnerary/insurance.dart | 6 +++--- lib/Screens/itnerary/miscellaneous.dart | 8 ++++---- lib/Screens/itnerary/taxi.dart | 10 +++++----- lib/Screens/itnerary/train.dart | 10 +++++----- lib/Screens/itnerary/visa.dart | 10 +++++----- lib/Screens/policy/policy_list.dart | 7 ++++--- 15 files changed, 57 insertions(+), 50 deletions(-) diff --git a/lib/Screens/costCenter/costCenter_list.dart b/lib/Screens/costCenter/costCenter_list.dart index af92cdb..1970b8b 100644 --- a/lib/Screens/costCenter/costCenter_list.dart +++ b/lib/Screens/costCenter/costCenter_list.dart @@ -336,7 +336,7 @@ class CostCenterListState extends State { MainAxisSize.min, // Ensures content fits nicely children: [ Text( - "Add CostCenter", + "Add Cost Center", style: GoogleFonts.poppins( fontSize: isDesktop ? 13 : 11, ), diff --git a/lib/Screens/forex/forexDetails.dart b/lib/Screens/forex/forexDetails.dart index 44c60bc..78002e5 100644 --- a/lib/Screens/forex/forexDetails.dart +++ b/lib/Screens/forex/forexDetails.dart @@ -396,7 +396,7 @@ class ForexDataState extends State { ), searchFieldProps: TextFieldProps( decoration: InputDecoration( - hintText: "Search Country...", + hintText: "Search ...", hintStyle: GoogleFonts.poppins(fontSize: 11), contentPadding: EdgeInsets.symmetric(horizontal: 4), ), @@ -414,7 +414,7 @@ class ForexDataState extends State { // Center-align selected item alignment: Alignment.centerLeft, child: Text( - selectedItem ?? "Select Country", + selectedItem ?? "Select ", style: GoogleFonts.poppins(fontSize: 11), ), ), diff --git a/lib/Screens/forex/forex_list.dart b/lib/Screens/forex/forex_list.dart index 64daa91..2d99fc6 100644 --- a/lib/Screens/forex/forex_list.dart +++ b/lib/Screens/forex/forex_list.dart @@ -746,9 +746,9 @@ class ForexDataListState extends State { style: TextStyle( fontSize: 13, fontFamily: "Inter", - // color: forex['is_active'] == "1" - // ? Colors.green - // : Colors.grey, + color: forex['is_active'] == "1" + ? Colors.green + : Colors.grey, ), softWrap: true, overflow: TextOverflow.ellipsis, diff --git a/lib/Screens/group/groupDetails.dart b/lib/Screens/group/groupDetails.dart index fc3156b..9f0cb19 100644 --- a/lib/Screens/group/groupDetails.dart +++ b/lib/Screens/group/groupDetails.dart @@ -407,7 +407,7 @@ class GroupDataState extends State { ), searchFieldProps: TextFieldProps( decoration: InputDecoration( - hintText: "Select Policy For International", + hintText: "Search ...", hintStyle: GoogleFonts.poppins(fontSize: 11), contentPadding: EdgeInsets.symmetric(horizontal: 4), ), @@ -425,7 +425,7 @@ class GroupDataState extends State { // Center-align selected item alignment: Alignment.centerLeft, child: Text( - selectedItem ?? "Select Policy For International", + selectedItem ?? "Select", style: GoogleFonts.poppins(fontSize: 11), ), ), @@ -481,7 +481,7 @@ class GroupDataState extends State { ), searchFieldProps: TextFieldProps( decoration: InputDecoration( - hintText: "Select Policy For Domestic...", + hintText: "Search ...", hintStyle: GoogleFonts.poppins(fontSize: 11), contentPadding: EdgeInsets.symmetric(horizontal: 4), ), @@ -499,7 +499,7 @@ class GroupDataState extends State { // Center-align selected item alignment: Alignment.centerLeft, child: Text( - selectedItem ?? "Select Policy For Domestic", + selectedItem ?? "Select ", style: GoogleFonts.poppins(fontSize: 11), ), ), diff --git a/lib/Screens/group/groupList.dart b/lib/Screens/group/groupList.dart index d653297..92c43af 100644 --- a/lib/Screens/group/groupList.dart +++ b/lib/Screens/group/groupList.dart @@ -353,7 +353,7 @@ class _GroupListState extends State { MainAxisSize.min, // Ensures content fits nicely children: [ Text( - "Add New Group", + "Add Group", style: GoogleFonts.poppins( fontSize: isDesktop ? 13 : 11, ), @@ -927,7 +927,7 @@ class _GroupListState extends State { ? Center( child: Text( "No matches found", - style: GoogleFonts.poppins( + style: GoogwqleFonts.poppins( fontSize: 14, color: Colors.grey, ), diff --git a/lib/Screens/hotels/hotelsDetails.dart b/lib/Screens/hotels/hotelsDetails.dart index e759d09..76eb5f9 100644 --- a/lib/Screens/hotels/hotelsDetails.dart +++ b/lib/Screens/hotels/hotelsDetails.dart @@ -418,7 +418,7 @@ class HotelsDataState extends State { ), searchFieldProps: TextFieldProps( decoration: InputDecoration( - hintText: "Search Country...", + hintText: "Search ...", hintStyle: GoogleFonts.poppins(fontSize: 11), contentPadding: EdgeInsets.symmetric(horizontal: 4), ), @@ -436,7 +436,7 @@ class HotelsDataState extends State { // Center-align selected item alignment: Alignment.centerLeft, child: Text( - selectedItem ?? "Select Country", + selectedItem ?? "Select ", style: GoogleFonts.poppins(fontSize: 11), ), ), diff --git a/lib/Screens/itnerary/accomodations.dart b/lib/Screens/itnerary/accomodations.dart index 2d085f1..8493ff7 100644 --- a/lib/Screens/itnerary/accomodations.dart +++ b/lib/Screens/itnerary/accomodations.dart @@ -437,7 +437,7 @@ class _AccomodationScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Destination", + "Destination *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -485,7 +485,7 @@ class _AccomodationScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Hotel Name", + "Hotel Name *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -695,7 +695,7 @@ class _AccomodationScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Check-in*", + "Check-in *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -747,7 +747,7 @@ class _AccomodationScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Time (Check-in)", + "Time (Check-in) *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -851,7 +851,7 @@ class _AccomodationScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Time (Check-out)", + "Time (Check-out) *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, diff --git a/lib/Screens/itnerary/bus.dart b/lib/Screens/itnerary/bus.dart index 6738e33..a8990d8 100644 --- a/lib/Screens/itnerary/bus.dart +++ b/lib/Screens/itnerary/bus.dart @@ -401,7 +401,7 @@ class _BusScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "From", + "From *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -446,7 +446,7 @@ class _BusScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "To", + "To *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -491,7 +491,7 @@ class _BusScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Date", + "Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -543,7 +543,7 @@ class _BusScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Time", + "Time *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, diff --git a/lib/Screens/itnerary/forex.dart b/lib/Screens/itnerary/forex.dart index 0d93906..7acd3e7 100644 --- a/lib/Screens/itnerary/forex.dart +++ b/lib/Screens/itnerary/forex.dart @@ -1154,7 +1154,7 @@ class _ForexScreenState extends State { showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( - hintText: "Search Country...", + hintText: "Search ...", contentPadding: EdgeInsets.symmetric(horizontal: 10), ), ), @@ -1171,7 +1171,7 @@ class _ForexScreenState extends State { // Center-align selected item alignment: Alignment.centerLeft, child: Text( - selectedItem ?? "Select Country", + selectedItem ?? "Select ", style: TextStyle(fontSize: 12), ), ), @@ -1620,6 +1620,10 @@ class _ForexScreenState extends State { // keyboardType: TextInputType.number, onChanged: (value) { // errorMessages["deposit_on_cash"] = ""; + errorMessages.remove("deposit_on_cash"); + errorMessages.remove("deposit_on_card"); + // errorMessages["deposit_on_card"] = ""; + _validateCashAmount( value, ); // Call validation when text changes @@ -1673,6 +1677,8 @@ class _ForexScreenState extends State { FilteringTextInputFormatter.digitsOnly, // ✅ Only allow digits ], onChanged: (value) { + errorMessages.remove("deposit_on_cash"); + errorMessages.remove("deposit_on_card"); _validateCardAmount( value, ); // Call validation when text changes diff --git a/lib/Screens/itnerary/insurance.dart b/lib/Screens/itnerary/insurance.dart index d1a903c..5998146 100644 --- a/lib/Screens/itnerary/insurance.dart +++ b/lib/Screens/itnerary/insurance.dart @@ -529,7 +529,7 @@ class _InsuranceScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Insurance Type", + "Insurance Type *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -580,7 +580,7 @@ class _InsuranceScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Start Date", + "Start Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -641,7 +641,7 @@ class _InsuranceScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "End Date", + "End Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, diff --git a/lib/Screens/itnerary/miscellaneous.dart b/lib/Screens/itnerary/miscellaneous.dart index 47ec8a8..93029f3 100644 --- a/lib/Screens/itnerary/miscellaneous.dart +++ b/lib/Screens/itnerary/miscellaneous.dart @@ -114,7 +114,7 @@ class _MiscellaneousScreenState extends State { // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { - errorMessages[field] = "This field is required"; + errorMessages[field] = "Required"; } } @@ -191,7 +191,7 @@ class _MiscellaneousScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Special Type", + "Special Type *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -266,7 +266,7 @@ class _MiscellaneousScreenState extends State { if (errorMessages["special_request"] != null) ...[ SizedBox(height: 5), // Space before error message Text( - "Select Type", + "Required", style: TextStyle(color: Colors.red, fontSize: 12), ), ], @@ -279,7 +279,7 @@ class _MiscellaneousScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Comments", + "Comments *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, diff --git a/lib/Screens/itnerary/taxi.dart b/lib/Screens/itnerary/taxi.dart index 969ba4e..077563b 100644 --- a/lib/Screens/itnerary/taxi.dart +++ b/lib/Screens/itnerary/taxi.dart @@ -524,7 +524,7 @@ class _TaxiScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "City", + "City *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -562,7 +562,7 @@ class _TaxiScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Pickup Location", + "Pickup Location *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -600,7 +600,7 @@ class _TaxiScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Date", + "Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -648,7 +648,7 @@ class _TaxiScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Time", + "Time *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -700,7 +700,7 @@ class _TaxiScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Comments", + "Comments *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, diff --git a/lib/Screens/itnerary/train.dart b/lib/Screens/itnerary/train.dart index 3f2e5d8..3c7c755 100644 --- a/lib/Screens/itnerary/train.dart +++ b/lib/Screens/itnerary/train.dart @@ -369,7 +369,7 @@ class _TrainScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Train Number", + "Train Number *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -584,7 +584,7 @@ class _TrainScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "From", + "From *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -694,7 +694,7 @@ class _TrainScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "To", + "To *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -767,7 +767,7 @@ class _TrainScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Date", + "Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -816,7 +816,7 @@ class _TrainScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Time", + "Time *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, diff --git a/lib/Screens/itnerary/visa.dart b/lib/Screens/itnerary/visa.dart index b8de533..1a81a43 100644 --- a/lib/Screens/itnerary/visa.dart +++ b/lib/Screens/itnerary/visa.dart @@ -442,7 +442,7 @@ class _VisaScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Type of Visa", + "Type of Visa *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -499,7 +499,7 @@ class _VisaScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Country", + "Country *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, @@ -518,7 +518,7 @@ class _VisaScreenState extends State { showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( - hintText: "Search Country...", + hintText: "Search ...", contentPadding: EdgeInsets.symmetric(horizontal: 10), ), ), @@ -536,7 +536,7 @@ class _VisaScreenState extends State { // Center-align selected item alignment: Alignment.centerLeft, child: Text( - selectedItem ?? "Select Country", + selectedItem ?? "Select ", style: TextStyle(fontSize: 12), ), ), @@ -574,7 +574,7 @@ class _VisaScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Start Date", + "Start Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, diff --git a/lib/Screens/policy/policy_list.dart b/lib/Screens/policy/policy_list.dart index 6c69199..8b0a94c 100644 --- a/lib/Screens/policy/policy_list.dart +++ b/lib/Screens/policy/policy_list.dart @@ -322,7 +322,8 @@ class _PolicyListState extends State { controller: searchController, onChanged: filterPolicy, decoration: InputDecoration( - hintText: "Search for a Policy", + hintText: "Search ...", + hintStyle: TextStyle( fontSize: 12, color: Color(0xFF9E9DBD), @@ -383,7 +384,7 @@ class _PolicyListState extends State { MainAxisSize.min, // Ensures content fits nicely children: [ Text( - "Add New Policy", + "Add Policy", style: GoogleFonts.poppins( fontSize: isDesktop ? 13 : 11, ), @@ -413,7 +414,7 @@ class _PolicyListState extends State { controller: searchController, onChanged: filterPolicy, decoration: InputDecoration( - hintText: "Search for a Policy", + hintText: "Search ...", hintStyle: TextStyle( fontSize: 12, color: Color(0xFF9E9DBD), From 20b74c5f5c586fcc85f898f26bbbded105568f7c Mon Sep 17 00:00:00 2001 From: venba-Inspriron-3558 Date: Sat, 7 Jun 2025 10:23:07 +0530 Subject: [PATCH 2/5] grouplist missing --- lib/Screens/group/groupList.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Screens/group/groupList.dart b/lib/Screens/group/groupList.dart index 92c43af..02e0ff4 100644 --- a/lib/Screens/group/groupList.dart +++ b/lib/Screens/group/groupList.dart @@ -927,7 +927,7 @@ class _GroupListState extends State { ? Center( child: Text( "No matches found", - style: GoogwqleFonts.poppins( + style: GoogleFonts.poppins( fontSize: 14, color: Colors.grey, ), From b1d19aec8458a307ac27c5e878ae55343d0b0959 Mon Sep 17 00:00:00 2001 From: SurendarSuri30 Date: Sat, 7 Jun 2025 14:23:12 +0530 Subject: [PATCH 3/5] user mangement client chnages --- lib/Screens/allTrips/list_all_plans.dart | 849 +++--- lib/Screens/allTrips/travel_agent_list.dart | 916 +++--- lib/Screens/approvals/approval_dialogs.dart | 241 +- lib/Screens/approvals/approval_list.dart | 1254 ++++---- .../authentication/login/login_widget.dart | 1121 ++++--- lib/Screens/costCenter/costCenterDetails.dart | 164 +- lib/Screens/costCenter/costCenter_list.dart | 1219 ++++---- lib/Screens/dashboard/status_dashboard.dart | 125 +- lib/Screens/department/departmentDetails.dart | 164 +- lib/Screens/department/department_list.dart | 1241 ++++---- lib/Screens/dialog/user_selection_dialog.dart | 390 ++- lib/Screens/forex/forexDetails.dart | 307 +- lib/Screens/forex/forex_list.dart | 1357 ++++----- lib/Screens/group/groupDetails.dart | 264 +- lib/Screens/group/groupList.dart | 1116 +++++-- lib/Screens/group/groupListBackUp.dart | 433 +++ lib/Screens/hotels/hotelsDetails.dart | 300 +- lib/Screens/hotels/hotels_list.dart | 1185 ++++---- lib/Screens/itnerary/flights.dart | 1045 ++++--- lib/Screens/itnerary/forex.dart | 792 ++--- lib/Screens/itnerary/taxi.dart | 353 ++- lib/Screens/itnerary/train.dart | 505 ++-- lib/Screens/itnerary/visa.dart | 121 +- .../itnerary_list/accomodation_list.dart | 20 +- lib/Screens/itnerary_list/bus_list.dart | 28 +- lib/Screens/itnerary_list/flight_list.dart | 166 +- lib/Screens/itnerary_list/forex_list.dart | 20 +- lib/Screens/itnerary_list/insurance_list.dart | 48 +- .../itnerary_list/miscellaneous_list.dart | 259 +- lib/Screens/itnerary_list/taxi_list.dart | 353 +-- lib/Screens/itnerary_list/train_list.dart | 20 +- lib/Screens/itnerary_list/visa_list.dart | 28 +- lib/Screens/myTemplates/Tempale | 562 ++++ lib/Screens/myTemplates/assets.dart | 3 +- lib/Screens/myTemplates/custom_toolbar.dart | 56 +- .../myTemplates/dialog_placeholders.dart | 88 + .../src/common/default_image_insert.dart | 30 - .../src/common/default_video_insert.dart | 30 - .../src/common/extensions/attribute.dart | 12 - .../src/common/extensions/controller_ext.dart | 37 +- .../src/common/image_video_utils.dart | 122 - .../common/utils/dart_ui/dart_ui_fake.dart | 43 - .../common/utils/dart_ui/dart_ui_real.dart | 1 - .../element_utils/element_shared_utils.dart | 84 - .../utils/element_utils/element_utils.dart | 106 - .../element_utils/element_web_utils.dart | 60 - .../src/common/utils/patterns.dart | 17 - .../myTemplates/src/common/utils/string.dart | 30 - .../myTemplates/src/common/utils/utils.dart | 30 - .../myTemplates/src/common/utils/web/web.dart | 1 - .../src/common/utils/web/web_real.dart | 46 - .../src/common/utils/web/web_stub.dart | 19 - .../src/editor/image/config/image_config.dart | 166 +- .../editor/image/config/image_web_config.dart | 12 +- .../src/editor/image/image_embed.dart | 78 +- .../src/editor/image/image_embed_types.dart | 68 +- .../src/editor/image/image_load_utils.dart | 36 - .../src/editor/image/image_menu.dart | 246 -- .../src/editor/image/image_save_utils.dart | 254 -- .../src/editor/image/image_web_embed.dart | 65 +- .../src/editor/image/widgets/image.dart | 186 -- .../editor/image/widgets/image_resizer.dart | 126 - .../src/editor/video/config/video_config.dart | 47 +- .../editor/video/config/video_web_config.dart | 7 +- .../src/editor/video/video_embed.dart | 56 +- .../src/editor/video/video_web_embed.dart | 56 +- .../src/editor/video/widgets/video_app.dart | 122 - .../src/editor/video/youtube_video_url.dart | 32 - .../myTemplates/src/flutter_quill_embeds.dart | 107 +- .../src/toolbar/camera/camera_button.dart | 133 +- .../src/toolbar/camera/camera_types.dart | 40 +- .../toolbar/camera/config/camera_config.dart | 29 +- .../toolbar/camera/select_camera_action.dart | 52 - .../toolbar/image/config/image_config.dart | 40 +- .../src/toolbar/image/image_button.dart | 136 +- .../toolbar/image/select_image_source.dart | 59 - .../src/toolbar/quill_simple_toolbar_api.dart | 12 - .../src/toolbar/video/config/video.dart | 51 +- .../toolbar/video/config/video_config.dart | 33 +- .../toolbar/video/select_video_source.dart | 57 - .../src/toolbar/video/video_button.dart | 135 +- lib/Screens/myTemplates/src/tst | 244 -- lib/Screens/myTemplates/template.dart | 607 +++- lib/Screens/myTemplates/templateForex.dart | 830 ++++++ lib/Screens/myTemplates/templateTest.dart | 37 + lib/Screens/organization/orgSetup.dart | 750 +++-- lib/Screens/organization/themeColor.dart | 105 +- lib/Screens/plans/create_plans.dart | 2542 ++++++++-------- .../plans/dynamic_itinerary_stepper.dart | 582 ++-- lib/Screens/plans/list_plans.dart | 1270 ++++---- lib/Screens/policy/policy.dart | 1000 ++++--- lib/Screens/policy/policyCriteria.dart | 72 +- lib/Screens/policy/policyListBackup.dart | 428 +++ lib/Screens/policy/policy_list.dart | 1168 ++++++-- lib/Screens/traveller/travellerDetails.dart | 542 ++++ lib/Screens/traveller/travellerList.dart | 848 ++++++ .../create_user/change_password.dart | 383 +++ .../create_user/create_user.dart | 565 ++-- .../create_user/office_details.dart | 1297 +++++---- .../create_user/personal_details.dart | 1213 +++++--- .../create_user/traveller_details.dart | 2591 +++++++++++------ lib/Screens/userManagement/user_List.dart | 1496 +++++----- lib/app.dart | 246 +- lib/config/apiUrl.dart | 5 +- lib/data/models/plan.dart | 7 + lib/main.dart | 2 + lib/routes/custom_appBar.dart | 609 ++-- lib/routes/custom_drawer.dart | 273 +- lib/routes/custom_router.dart | 331 ++- lib/routes/mainLayout.dart | 33 + lib/routes/organizationSetting.dart | 389 ++- lib/services/apiService.dart | 442 ++- lib/widgets/popup_listPlan_action.dart | 21 +- lib/widgets/popup_userList_action.dart | 19 +- lib/widgets/saving_loader.dart | 35 + pubspec.lock | 70 +- pubspec.yaml | 26 +- 117 files changed, 24402 insertions(+), 16898 deletions(-) create mode 100644 lib/Screens/group/groupListBackUp.dart create mode 100644 lib/Screens/myTemplates/Tempale create mode 100644 lib/Screens/myTemplates/dialog_placeholders.dart delete mode 100644 lib/Screens/myTemplates/src/common/default_image_insert.dart delete mode 100644 lib/Screens/myTemplates/src/common/default_video_insert.dart delete mode 100644 lib/Screens/myTemplates/src/common/extensions/attribute.dart delete mode 100644 lib/Screens/myTemplates/src/common/image_video_utils.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/dart_ui/dart_ui_fake.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/dart_ui/dart_ui_real.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/element_utils/element_shared_utils.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/element_utils/element_utils.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/element_utils/element_web_utils.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/patterns.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/string.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/utils.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/web/web.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/web/web_real.dart delete mode 100644 lib/Screens/myTemplates/src/common/utils/web/web_stub.dart delete mode 100644 lib/Screens/myTemplates/src/editor/image/image_load_utils.dart delete mode 100644 lib/Screens/myTemplates/src/editor/image/image_menu.dart delete mode 100644 lib/Screens/myTemplates/src/editor/image/image_save_utils.dart delete mode 100644 lib/Screens/myTemplates/src/editor/image/widgets/image.dart delete mode 100644 lib/Screens/myTemplates/src/editor/image/widgets/image_resizer.dart delete mode 100644 lib/Screens/myTemplates/src/editor/video/widgets/video_app.dart delete mode 100644 lib/Screens/myTemplates/src/editor/video/youtube_video_url.dart delete mode 100644 lib/Screens/myTemplates/src/toolbar/camera/select_camera_action.dart delete mode 100644 lib/Screens/myTemplates/src/toolbar/image/select_image_source.dart delete mode 100644 lib/Screens/myTemplates/src/toolbar/quill_simple_toolbar_api.dart delete mode 100644 lib/Screens/myTemplates/src/toolbar/video/select_video_source.dart delete mode 100644 lib/Screens/myTemplates/src/tst create mode 100644 lib/Screens/myTemplates/templateForex.dart create mode 100644 lib/Screens/myTemplates/templateTest.dart create mode 100644 lib/Screens/policy/policyListBackup.dart create mode 100644 lib/Screens/traveller/travellerDetails.dart create mode 100644 lib/Screens/traveller/travellerList.dart create mode 100644 lib/Screens/userManagement/create_user/change_password.dart create mode 100644 lib/routes/mainLayout.dart create mode 100644 lib/widgets/saving_loader.dart diff --git a/lib/Screens/allTrips/list_all_plans.dart b/lib/Screens/allTrips/list_all_plans.dart index 6e8c628..536b13f 100644 --- a/lib/Screens/allTrips/list_all_plans.dart +++ b/lib/Screens/allTrips/list_all_plans.dart @@ -13,6 +13,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../routes/custom_appBar.dart'; import '../../routes/custom_drawer.dart'; +import '../../routes/mainLayout.dart'; import '../../services/apiService.dart'; import '../../utils/auth_utils.dart'; import '../../utils/pagination.dart'; @@ -62,7 +63,6 @@ class _ListAllPlansState extends State { // }); // }); }); - // futurePlans = fetchPlans(); } @@ -70,16 +70,20 @@ class _ListAllPlansState extends State { print("allPlans before filtering: $allPlans"); final lowerQuery = query.toLowerCase(); setState(() { - filteredPlans = allPlans.where((plan) { - return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); - }).toList(); + filteredPlans = + allPlans.where((plan) { + return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); + }).toList(); + currentPage = 0; }); print("filteredPlans: $filteredPlans"); } @@ -89,13 +93,15 @@ class _ListAllPlansState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -202,6 +208,7 @@ class _ListAllPlansState extends State { if (response.statusCode == 200) { final data = json.decode(response.body); + // List plansJson = []; List plansJson = data['data']; return plansJson.map((json) => Plan.fromJson(json)).toList(); } else { @@ -246,8 +253,10 @@ class _ListAllPlansState extends State { } void deletePlan(String planId) async { - bool confirmed = - await apiService.showCancelConfirmationDialog(context, layoutColor); + bool confirmed = await apiService.showCancelConfirmationDialog( + context, + layoutColor, + ); if (confirmed) { try { @@ -264,35 +273,41 @@ class _ListAllPlansState extends State { } Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Scaffold( - backgroundColor: Color(0xFFf5f5f5), - // backgroundColor: Color(0xFFFCFCFC), + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // backgroundColor: Color(0xFFFCFCFC), - // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), - // 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), - Expanded(child: buildGroupListLayout(isDesktop)) - ], + // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), + // 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), + Expanded(child: buildGroupListLayout(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupListLayout(bool isDesktop) { @@ -323,8 +338,9 @@ class _ListAllPlansState extends State { String _formatDate(String rawDate) { try { final dateTime = DateTime.parse(rawDate); - return DateFormat('dd, MMM yyyy HH:mm') - .format(dateTime); // 24-hour format + return DateFormat( + 'dd, MMM yyyy HH:mm', + ).format(dateTime); // 24-hour format } catch (e) { return rawDate; // fallback if parsing fails } @@ -333,17 +349,19 @@ class _ListAllPlansState extends State { return Container( margin: isDesktop ? EdgeInsets.all(10.0) : null, padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20), - height: isDesktop - ? MediaQuery.of(context).size.height * 0.98 - : MediaQuery.of(context).size.height, + 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, + border: + isDesktop + ? Border.all( + width: 2, + color: Colors.white, + // color: Color(0xFFF7F7FB), + ) + : null, color: isDesktop ? Colors.white : Color(0xFFFCFCFC), // color: Color(0xFFF7F7FB), @@ -383,9 +401,7 @@ class _ListAllPlansState extends State { ], ), - SizedBox( - width: 1, - ), + SizedBox(width: 1), Spacer(), if (isDesktop) Container( @@ -396,8 +412,10 @@ class _ListAllPlansState extends State { onChanged: filterPlans, decoration: InputDecoration( hintText: "Search...", - hintStyle: - TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)), + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), @@ -409,22 +427,23 @@ class _ListAllPlansState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade200, width: 0.5), + color: Colors.grey.shade200, + width: 0.5, + ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade300, width: 1), + color: Colors.grey.shade300, + width: 1, + ), ), ), - style: GoogleFonts.poppins( - fontSize: 12, - ), + style: GoogleFonts.poppins(fontSize: 12), ), ), // SizedBox(width: 16), - Spacer(), // ElevatedButton( // style: ElevatedButton.styleFrom( @@ -522,8 +541,10 @@ class _ListAllPlansState extends State { onChanged: filterPlans, decoration: InputDecoration( hintText: "Search...", - hintStyle: - TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)), + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), @@ -535,17 +556,19 @@ class _ListAllPlansState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade200, width: 0.5), + color: Colors.grey.shade200, + width: 0.5, + ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade300, width: 1), + color: Colors.grey.shade300, + width: 1, + ), ), ), - style: GoogleFonts.poppins( - fontSize: 12, - ), + style: GoogleFonts.poppins(fontSize: 12), ), ), ], @@ -558,6 +581,8 @@ class _ListAllPlansState extends State { } else if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) { + final adjHgt = MediaQuery.of(context).size.height; + return Center( child: Padding( padding: const EdgeInsets.all(16.0), @@ -575,21 +600,15 @@ class _ListAllPlansState extends State { // color: Colors.redAccent), // // ), - const SizedBox(height: 15), + SizedBox(height: adjHgt / 4), Text( - "No Trips", + " No Trips Found", 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), + fontSize: 20, + fontWeight: FontWeight.w500, + color: Colors.black54, + ), ), ], ), @@ -605,13 +624,16 @@ class _ListAllPlansState extends State { List plans = searchController.text.isEmpty ? allPlans : filteredPlans; - plans.sort((a, b) => - int.parse(b.planId).compareTo(int.parse(a.planId))); + plans.sort( + (a, b) => + int.parse(b.planId).compareTo(int.parse(a.planId)), + ); - List paginatedPlans = plans - .skip(currentPage * itemsPerPage) - .take(itemsPerPage) - .toList(); + List paginatedPlans = + plans + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); Widget table = LayoutBuilder( builder: (context, constraints) { @@ -624,216 +646,326 @@ class _ListAllPlansState extends State { columnSpacing: isDesktop ? 24.0 : 16.0, border: TableBorder( horizontalInside: BorderSide( - width: 0.5, color: Colors.grey.shade200), + width: 0.5, + color: Colors.grey.shade200, + ), ), columns: [ DataColumn( - label: Text( - 'Trip ID', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Trip Name', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Emp Code', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Traveller', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Trip Type', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Created On', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Actions', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - ], - rows: paginatedPlans.map((plan) { - return DataRow(cells: [ - DataCell(Text(plan.planId, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(plan.tripTitle, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell(Text(plan.employeeCode ?? " - ", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text( - plan.userName.isNotEmpty - ? plan.userName - : plan.travellerName, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(plan.tripType, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(_formatDate(plan.createdOn), - // plan.createdOn, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell( - Container( - width: double - .infinity, // Set your desired fixed size (equal width and height) - height: 25, - alignment: Alignment.center, - decoration: BoxDecoration( - color: getStatusColor(plan.statusValue), - borderRadius: BorderRadius.circular(10), - ), - child: Text( - plan.statusValue, - textAlign: TextAlign.center, - style: TextStyle( - color: - getStatusTextColor(plan.statusValue), - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.w400, - ), - ), + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, ), ), - DataCell( - Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, + ), + DataColumn( + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + paginatedPlans.map((plan) { + return DataRow( + cells: [ + DataCell( + Text( + plan.planId, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), ), - 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: () { - Navigator.pop( - context); // Close popup manually - ApiService.viewPlan( - context, plan.planId, - isViewMode: true); - }, - ), - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - onPressed: () { - Navigator.pop(context); - ApiService.viewPlan( - context, plan.planId, - isViewMode: false); - }, - ), - IconButton( - icon: Icon( - Icons.cancel_rounded, - size: 18), - onPressed: () { - Navigator.pop(context); - deletePlan(plan.planId); - }, - ), - IconButton( - icon: Icon(Icons.download, - color: Color(0xFF114D8B), - size: 18), - onPressed: () { - Navigator.pop(context); - apiService.getPdfDownload( - plan.planId); - }, - ), - IconButton( - icon: const Icon( - Icons.comment, - color: Color(0xFF475569), - size: 11, - ), - onPressed: () { - showDialog( - context: context, - builder: (context) => - CommentModalList( - // planId: plan.planId, - planId: plan - .planId - .toString(), - layoutColorForUser: - layoutColor!, - role: "Admin"), - ); - }), - ], - ), + ), + DataCell( + Text( + plan.tripTitle, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + plan.employeeCode ?? " - ", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.userName.isNotEmpty + ? plan.userName + : plan.travellerName, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.tripType, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + _formatDate(plan.createdOn), + // plan.createdOn, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Container( + width: + double + .infinity, // Set your desired fixed size (equal width and height) + height: 25, + alignment: Alignment.center, + decoration: BoxDecoration( + color: getStatusColor( + plan.statusValue, + ), + borderRadius: BorderRadius.circular( + 10, ), ), - ], + child: Text( + plan.statusValue, + textAlign: TextAlign.center, + style: TextStyle( + color: getStatusTextColor( + plan.statusValue, + ), + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), + ), + ), + ), + DataCell( + Row( + children: [ + 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, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + true, + ); + }, + ), + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + false, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + deletePlan( + plan.planId, + ); + }, + ), + IconButton( + icon: Icon( + Icons.download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Trip Comments', + onPressed: () { + showDialog( + context: + context, + builder: + ( + context, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId + .toString(), + layoutColorForUser: + layoutColor!, + role: + "Admin", + ), + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), ), ], - ), - ), - ]); - }).toList(), + ); + }).toList(), ), ); }, @@ -846,8 +978,10 @@ class _ListAllPlansState extends State { final plan = paginatedPlans[index]; return Card( color: Colors.white, - margin: - EdgeInsets.symmetric(horizontal: 12, vertical: 6), + margin: EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), @@ -872,18 +1006,23 @@ class _ListAllPlansState extends State { children: [ Container( padding: EdgeInsets.symmetric( - horizontal: 8, vertical: 4), + horizontal: 8, + vertical: 4, + ), decoration: BoxDecoration( color: getStatusColor( - plan.statusValue), - borderRadius: - BorderRadius.circular(8), + plan.statusValue, + ), + borderRadius: BorderRadius.circular( + 8, + ), ), child: Text( plan.statusValue, style: TextStyle( color: getStatusTextColor( - plan.statusValue), + plan.statusValue, + ), fontSize: 10, fontWeight: FontWeight.w600, ), @@ -912,11 +1051,14 @@ class _ListAllPlansState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(' ${plan.tripTitle}', - style: TextStyle( - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.bold)), + Text( + ' ${plan.tripTitle}', + style: TextStyle( + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.bold, + ), + ), ], ), ], @@ -931,24 +1073,28 @@ class _ListAllPlansState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(' ${plan.tripType}', - style: TextStyle( - fontSize: 9, - color: Colors.black87, - fontFamily: "Inter", - )), + Text( + ' ${plan.tripType}', + style: TextStyle( + fontSize: 9, + color: Colors.black87, + fontFamily: "Inter", + ), + ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('${_formatDate(plan.createdOn)}', - style: TextStyle( - fontSize: 9, - color: Colors.black87, - fontFamily: "Inter", - )), + Text( + '${_formatDate(plan.createdOn)}', + style: TextStyle( + fontSize: 9, + color: Colors.black87, + fontFamily: "Inter", + ), + ), ], ), ], @@ -966,9 +1112,10 @@ class _ListAllPlansState extends State { ? '${plan.userName}' : '${plan.travellerName}', style: TextStyle( - fontSize: 9, - fontFamily: "Inter", - color: Colors.black87), + fontSize: 9, + fontFamily: "Inter", + color: Colors.black87, + ), ), ], ), @@ -980,9 +1127,10 @@ class _ListAllPlansState extends State { Text( plan.employeeCode ?? 'No Data', style: TextStyle( - fontSize: 9, - fontFamily: "Inter", - color: Colors.black87), + fontSize: 9, + fontFamily: "Inter", + color: Colors.black87, + ), ), ], ), @@ -1002,30 +1150,35 @@ class _ListAllPlansState extends State { // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: isDesktop - ? (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, color: Colors.grey), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) - : (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, color: Colors.grey), - ), - ) - : buildMobileCardView(paginatedPlans)), + child: + isDesktop + ? (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView(paginatedPlans)), // child: isDesktop // ? SingleChildScrollView( @@ -1055,7 +1208,7 @@ class _ListAllPlansState extends State { ), ); }, - ) + ), ], ), ), diff --git a/lib/Screens/allTrips/travel_agent_list.dart b/lib/Screens/allTrips/travel_agent_list.dart index 721efe8..ad48751 100644 --- a/lib/Screens/allTrips/travel_agent_list.dart +++ b/lib/Screens/allTrips/travel_agent_list.dart @@ -69,16 +69,19 @@ class _TravelAgentListPlansState extends State { print("allPlans before filtering: $allPlans"); final lowerQuery = query.toLowerCase(); setState(() { - filteredPlans = allPlans.where((plan) { - return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); - }).toList(); + filteredPlans = + allPlans.where((plan) { + return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); + }).toList(); }); print("filteredPlans: $filteredPlans"); } @@ -88,13 +91,15 @@ class _TravelAgentListPlansState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -195,7 +200,8 @@ class _TravelAgentListPlansState extends State { if (response.statusCode == 200) { final data = json.decode(response.body); - List plansJson = data['data']; + List plansJson = []; + // List plansJson = data['data']; return plansJson.map((json) => Plan.fromJson(json)).toList(); } else { throw Exception('Failed to load plans'); @@ -239,8 +245,10 @@ class _TravelAgentListPlansState extends State { } void deletePlan(String planId) async { - bool confirmed = - await apiService.showCancelConfirmationDialog(context, layoutColor); + bool confirmed = await apiService.showCancelConfirmationDialog( + context, + layoutColor, + ); if (confirmed) { try { @@ -257,33 +265,39 @@ class _TravelAgentListPlansState extends State { } Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Scaffold( - backgroundColor: Color(0xFFf5f5f5), - // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), - // 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), - Expanded(child: buildGroupListLayout(isDesktop)) - ], + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), + // 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), + Expanded(child: buildGroupListLayout(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupListLayout(bool isDesktop) { @@ -313,8 +327,9 @@ class _TravelAgentListPlansState extends State { String _formatDate(String rawDate) { try { final dateTime = DateTime.parse(rawDate); - return DateFormat('dd, MMM yyyy HH:mm') - .format(dateTime); // 24-hour format + return DateFormat( + 'dd, MMM yyyy HH:mm', + ).format(dateTime); // 24-hour format } catch (e) { return rawDate; // fallback if parsing fails } @@ -323,17 +338,19 @@ class _TravelAgentListPlansState extends State { return Container( margin: isDesktop ? EdgeInsets.all(10.0) : null, padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20), - height: isDesktop - ? MediaQuery.of(context).size.height * 0.98 - : MediaQuery.of(context).size.height, + 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, + border: + isDesktop + ? Border.all( + width: 2, + color: Colors.white, + // color: Color(0xFFF7F7FB), + ) + : null, color: isDesktop ? Colors.white : Color(0xFFFCFCFC), // color: Color(0xFFF7F7FB), @@ -382,8 +399,10 @@ class _TravelAgentListPlansState extends State { onChanged: filterPlans, decoration: InputDecoration( hintText: "Search...", - hintStyle: - TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)), + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), @@ -395,23 +414,23 @@ class _TravelAgentListPlansState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade200, width: 0.5), + color: Colors.grey.shade200, + width: 0.5, + ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade300, width: 1), + color: Colors.grey.shade300, + width: 1, + ), ), ), - style: TextStyle( - fontSize: 12, - fontFamily: "Inter", - ), + style: TextStyle(fontSize: 12, fontFamily: "Inter"), ), ), // SizedBox(width: 16), - Spacer(), // ElevatedButton( // style: ElevatedButton.styleFrom( @@ -447,8 +466,10 @@ class _TravelAgentListPlansState extends State { onChanged: filterPlans, decoration: InputDecoration( hintText: "Search...", - hintStyle: - TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)), + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), @@ -460,17 +481,19 @@ class _TravelAgentListPlansState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade200, width: 0.5), + color: Colors.grey.shade200, + width: 0.5, + ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade300, width: 1), + color: Colors.grey.shade300, + width: 1, + ), ), ), - style: GoogleFonts.poppins( - fontSize: 12, - ), + style: GoogleFonts.poppins(fontSize: 12), ), ), ], @@ -479,6 +502,8 @@ class _TravelAgentListPlansState extends State { FutureBuilder>( future: futurePlans, builder: (context, snapshot) { + final adjHgt = MediaQuery.of(context).size.height; + if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError || @@ -490,7 +515,7 @@ class _TravelAgentListPlansState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - SizedBox(height: 20), + // SizedBox(height: 20), // Icon(Icons.error_outline, // color: Colors.redAccent, size: 60), // SizedBox(height: 1), @@ -499,21 +524,23 @@ class _TravelAgentListPlansState extends State { // fontSize: 22, // fontWeight: FontWeight.bold, // color: Colors.redAccent)), - SizedBox(height: 20), isDesktop ? SizedBox( - width: - MediaQuery.of(context).size.width * 5.5, - ) + width: + MediaQuery.of(context).size.width * 5.5, + ) : SizedBox.shrink(), + SizedBox(height: adjHgt / 4), - Text("No Trips Pending For Your Approvals", - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 20, - fontFamily: "Inter", - fontWeight: FontWeight.w600, - color: Colors.black54)), + Text( + " No Trips Found", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.w500, + color: Colors.black54, + ), + ), SizedBox(height: 10), // Text("Please Create Trip", // textAlign: TextAlign.center, @@ -536,13 +563,16 @@ class _TravelAgentListPlansState extends State { List plans = searchController.text.isEmpty ? allPlans : filteredPlans; - plans.sort((a, b) => - int.parse(b.planId).compareTo(int.parse(a.planId))); + plans.sort( + (a, b) => + int.parse(b.planId).compareTo(int.parse(a.planId)), + ); - List paginatedPlans = plans - .skip(currentPage * itemsPerPage) - .take(itemsPerPage) - .toList(); + List paginatedPlans = + plans + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); Widget table = LayoutBuilder( builder: (context, constraints) { @@ -555,260 +585,355 @@ class _TravelAgentListPlansState extends State { columnSpacing: isDesktop ? 24.0 : 16.0, border: TableBorder( horizontalInside: BorderSide( - width: 0.5, color: Colors.grey.shade200), + width: 0.5, + color: Colors.grey.shade200, + ), ), columns: [ DataColumn( - label: Text( - 'Trip ID', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Trip Name', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Emp Code', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Traveller', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Trip Type', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Created On', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Actions', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - ], - rows: paginatedPlans.map((plan) { - return DataRow(cells: [ - DataCell(Text(plan.planId, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - - DataCell(Text(plan.tripTitle, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - - DataCell(Text(plan.employeeCode ?? " - ", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text( - plan.userName.isNotEmpty - ? plan.userName - : plan.travellerName, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(plan.tripType, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(_formatDate(plan.createdOn), - // plan.createdOn, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell( - Container( - padding: const EdgeInsets.all(3), - // width: - // 150, // Set your desired fixed size (equal width and height) - width: double - .infinity, // Set your desired fixed size (equal width and height) - height: 25, - alignment: Alignment.center, - decoration: BoxDecoration( - color: getStatusColor(plan.statusValue), - borderRadius: BorderRadius.circular(10), - ), - child: Text( - plan.statusValue, - textAlign: TextAlign.center, - style: TextStyle( - color: - getStatusTextColor(plan.statusValue), - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.w400, - ), - ), + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, ), ), + ), + DataColumn( + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + paginatedPlans.map((plan) { + return DataRow( + cells: [ + DataCell( + Text( + plan.planId, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), - DataCell( - Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon(Icons.more_vert, - color: Color(0xFF475569)), - 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: 16), - onPressed: () { - Navigator.pop( - context); // Close popup manually - ApiService.viewPlan( - context, plan.planId, - isViewMode: true); - }, - ), - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - onPressed: () { - Navigator.pop(context); - ApiService.viewPlan( - context, plan.planId, - isViewMode: false); - }, - ), - IconButton( - icon: Icon( - Icons.cancel_rounded, - size: 18), - onPressed: () { - Navigator.pop(context); - deletePlan(plan.planId); - }, - ), - IconButton( - icon: Icon(Icons.download, - color: Color(0xFF114D8B), - size: 18), - onPressed: () { - Navigator.pop(context); - apiService.getPdfDownload( - plan.planId); - }, - ), - IconButton( - icon: const Icon( - Icons.comment, - color: Color(0xFF475569), - size: 11, - ), - onPressed: () { - showDialog( - context: context, - builder: (context) => - CommentModal( - // planId: plan.planId, - planId: plan - .planId - .toString(), - layoutColorForUser: - layoutColor!, - role: - "Travel Agent"), - ); - }), - ], - ), + DataCell( + Text( + plan.tripTitle, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + + DataCell( + Text( + plan.employeeCode ?? " - ", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.userName.isNotEmpty + ? plan.userName + : plan.travellerName, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.tripType, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + _formatDate(plan.createdOn), + // plan.createdOn, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Container( + padding: const EdgeInsets.all(3), + // width: + // 150, // Set your desired fixed size (equal width and height) + width: + double + .infinity, // Set your desired fixed size (equal width and height) + height: 25, + alignment: Alignment.center, + decoration: BoxDecoration( + color: getStatusColor( + plan.statusValue, + ), + borderRadius: BorderRadius.circular( + 10, ), ), - ], + child: Text( + plan.statusValue, + textAlign: TextAlign.center, + style: TextStyle( + color: getStatusTextColor( + plan.statusValue, + ), + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), + ), + ), ), - ], - ), - ), - // DataCell(Row(children: [ - // IconButton( - // icon: const Icon( - // Icons.remove_red_eye, - // color: Color(0xFF475569), - // size: 18, - // ), - // onPressed: () => ApiService.viewPlan( - // context, plan.planId, - // isViewMode: true)), - // GestureDetector( - // onTap: () => ApiService.viewPlan( - // context, plan.planId, - // isViewMode: false), - // child: Image.asset( - // 'assets/images/IconsImg/edit.png', - // width: 20, - // height: 15), - // ), - // SizedBox( - // width: 5, - // ), - // GestureDetector( - // onTap: () => deletePlan(plan.planId), - // child: Image.asset( - // 'assets/images/IconsImg/delete.png', - // width: 20, - // height: 15), - // ), - // IconButton( - // icon: const Icon( - // Icons.download, - // color: Color(0xFF475569), - // size: 18, - // ), - // onPressed: () { - // apiService.getPdfDownload(plan.planId); - // }), - // ])), - ]); - }).toList(), + DataCell( + Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + ), + 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: 16, + ), + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + true, + ); + }, + ), + // IconButton( + // icon: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15), + // onPressed: () { + // Navigator.pop(context); + // ApiService.viewPlan( + // context, plan.planId, + // isViewMode: false); + // }, + // ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + onPressed: () { + Navigator.pop( + context, + ); + deletePlan( + plan.planId, + ); + }, + ), + IconButton( + icon: Icon( + Icons.download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + onPressed: () { + Navigator.pop( + context, + ); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + onPressed: () { + showDialog( + context: + context, + builder: + ( + context, + ) => CommentModal( + // planId: plan.planId, + planId: + plan.planId + .toString(), + layoutColorForUser: + layoutColor!, + role: + "Travel Agent", + ), + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), + ), + + // DataCell(Row(children: [ + // IconButton( + // icon: const Icon( + // Icons.remove_red_eye, + // color: Color(0xFF475569), + // size: 18, + // ), + // onPressed: () => ApiService.viewPlan( + // context, plan.planId, + // isViewMode: true)), + // GestureDetector( + // onTap: () => ApiService.viewPlan( + // context, plan.planId, + // isViewMode: false), + // child: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15), + // ), + // SizedBox( + // width: 5, + // ), + // GestureDetector( + // onTap: () => deletePlan(plan.planId), + // child: Image.asset( + // 'assets/images/IconsImg/delete.png', + // width: 20, + // height: 15), + // ), + // IconButton( + // icon: const Icon( + // Icons.download, + // color: Color(0xFF475569), + // size: 18, + // ), + // onPressed: () { + // apiService.getPdfDownload(plan.planId); + // }), + // ])), + ], + ); + }).toList(), ), ); }, @@ -821,8 +946,10 @@ class _TravelAgentListPlansState extends State { final plan = paginatedPlans[index]; return Card( color: Colors.white, - margin: - EdgeInsets.symmetric(horizontal: 12, vertical: 6), + margin: EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), @@ -839,7 +966,9 @@ class _TravelAgentListPlansState extends State { children: [ Container( padding: EdgeInsets.symmetric( - horizontal: 8, vertical: 4), + horizontal: 8, + vertical: 4, + ), decoration: BoxDecoration( color: getStatusColor(plan.statusValue), borderRadius: BorderRadius.circular(8), @@ -848,7 +977,8 @@ class _TravelAgentListPlansState extends State { plan.statusValue, style: TextStyle( color: getStatusTextColor( - plan.statusValue), + plan.statusValue, + ), fontSize: 10, fontWeight: FontWeight.w600, ), @@ -871,11 +1001,14 @@ class _TravelAgentListPlansState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(' ${plan.tripTitle}', - style: TextStyle( - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.bold)), + Text( + ' ${plan.tripTitle}', + style: TextStyle( + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.bold, + ), + ), ], ), ], @@ -890,24 +1023,28 @@ class _TravelAgentListPlansState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(' ${plan.tripType}', - style: TextStyle( - fontSize: 9, - color: Colors.black87, - fontFamily: "Inter", - )), + Text( + ' ${plan.tripType}', + style: TextStyle( + fontSize: 9, + color: Colors.black87, + fontFamily: "Inter", + ), + ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('${_formatDate(plan.createdOn)}', - style: TextStyle( - fontSize: 9, - color: Colors.black87, - fontFamily: "Inter", - )), + Text( + '${_formatDate(plan.createdOn)}', + style: TextStyle( + fontSize: 9, + color: Colors.black87, + fontFamily: "Inter", + ), + ), ], ), ], @@ -925,9 +1062,10 @@ class _TravelAgentListPlansState extends State { ? '${plan.userName}' : '${plan.travellerName}', style: TextStyle( - fontSize: 9, - fontFamily: "Inter", - color: Colors.black87), + fontSize: 9, + fontFamily: "Inter", + color: Colors.black87, + ), ), ], ), @@ -939,9 +1077,10 @@ class _TravelAgentListPlansState extends State { Text( plan.employeeCode ?? 'No Data', style: TextStyle( - fontSize: 9, - fontFamily: "Inter", - color: Colors.black87), + fontSize: 9, + fontFamily: "Inter", + color: Colors.black87, + ), ), ], ), @@ -963,30 +1102,35 @@ class _TravelAgentListPlansState extends State { // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: isDesktop - ? (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, color: Colors.grey), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) - : (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, color: Colors.grey), - ), - ) - : buildMobileCardView(paginatedPlans)), + child: + isDesktop + ? (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView(paginatedPlans)), // child: isDesktop // ? SingleChildScrollView( @@ -1016,7 +1160,7 @@ class _TravelAgentListPlansState extends State { ), ); }, - ) + ), ], ), ), diff --git a/lib/Screens/approvals/approval_dialogs.dart b/lib/Screens/approvals/approval_dialogs.dart index 14c145e..d8b772c 100644 --- a/lib/Screens/approvals/approval_dialogs.dart +++ b/lib/Screens/approvals/approval_dialogs.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; Future showApprovalDialog( - BuildContext context, Color layoutColor) async { + BuildContext context, + Color layoutColor, +) async { String selectedAction = ""; // "", "accept", "reject" String remarks = ""; @@ -24,13 +26,12 @@ Future showApprovalDialog( Text( "To Approve or Reject Trip", style: TextStyle( - fontFamily: "Inter", fontWeight: FontWeight.w500), + fontFamily: "Inter", + fontWeight: FontWeight.w500, + ), ), IconButton( - icon: const Icon( - Icons.close, - size: 15, - ), + icon: const Icon(Icons.close, size: 15), onPressed: () { Navigator.pop(context, null); // Close the dialog }, @@ -45,12 +46,14 @@ Future showApprovalDialog( Expanded( child: ElevatedButton( style: ElevatedButton.styleFrom( - backgroundColor: selectedAction == "accept" - ? layoutColor - : Colors.grey.shade200, - foregroundColor: selectedAction == "accept" - ? Colors.white - : Colors.black, + backgroundColor: + selectedAction == "accept" + ? Colors.green + : Colors.grey.shade200, + foregroundColor: + selectedAction == "accept" + ? Colors.white + : Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), @@ -68,12 +71,14 @@ Future showApprovalDialog( Expanded( child: ElevatedButton( style: ElevatedButton.styleFrom( - backgroundColor: selectedAction == "reject" - ? Colors.redAccent - : Colors.grey.shade200, - foregroundColor: selectedAction == "reject" - ? Colors.white - : Colors.black, + backgroundColor: + selectedAction == "reject" + ? Colors.redAccent + : Colors.grey.shade200, + foregroundColor: + selectedAction == "reject" + ? Colors.white + : Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), @@ -122,12 +127,16 @@ Future showApprovalDialog( border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide( - color: Colors.blueGrey, width: 0.5), + color: Colors.blueGrey, + width: 0.5, + ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), - borderSide: - BorderSide(color: Colors.blueGrey, width: 0.5), + borderSide: BorderSide( + color: Colors.blueGrey, + width: 0.5, + ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), @@ -135,7 +144,7 @@ Future showApprovalDialog( ), ), ), - ] + ], ], ), actionsAlignment: MainAxisAlignment.center, @@ -187,94 +196,14 @@ Future showApprovalDialog( Future showApproveDialog1(BuildContext context, Color layoutColor) { return showDialog( context: context, - builder: (context) => AlertDialog( - backgroundColor: Colors.white, - title: const Text( - "Confirm Approval", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - content: const Text("Are you sure you want to approve this plan?"), - actions: [ - 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: () => Navigator.pop(context, false), - child: const Text("Cancel"), - ), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: layoutColor, - foregroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: layoutColor, width: 2), - ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - ), - onPressed: () => Navigator.pop(context, true), - child: const Text("OK"), - ), - ], - ), - ); -} - -/// Show confirm dialog for rejection with remarks input -Future showRejectDialog1( - BuildContext context, Color layoutColor) async { - String remarks = ""; - - final confirmed = await showDialog( - context: context, - builder: (context) { - return StatefulBuilder( - builder: (context, setState) => AlertDialog( + builder: + (context) => AlertDialog( backgroundColor: Colors.white, - contentPadding: const EdgeInsets.all(36), - // title: const Text("Confirm Rejection"), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text( - "Confirm Rejection", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 16), - const Text("Please enter reason for rejection."), - const SizedBox(height: 10), - TextField( - maxLines: 3, - onChanged: (value) => remarks = value, - decoration: const InputDecoration( - hintText: "Remarks...", - hintStyle: TextStyle( - fontSize: 10, // 👈 Set your desired font size here - color: Colors.grey, - fontFamily: "Inter", // optional if you want consistent font - ), - border: OutlineInputBorder( - borderSide: BorderSide(color: Colors.blueGrey, width: 0.5), - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.blueGrey, width: 0.5), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey, width: 1), - ), - ), - ), - ], + title: const Text( + "Confirm Approval", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), + content: const Text("Are you sure you want to approve this plan?"), actions: [ ElevatedButton( style: ElevatedButton.styleFrom( @@ -299,14 +228,102 @@ Future showRejectDialog1( ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), - onPressed: () { - if (remarks.trim().isEmpty) return; - Navigator.pop(context, true); - }, + onPressed: () => Navigator.pop(context, true), child: const Text("OK"), ), ], ), + ); +} + +/// Show confirm dialog for rejection with remarks input +Future showRejectDialog1( + BuildContext context, + Color layoutColor, +) async { + String remarks = ""; + + final confirmed = await showDialog( + context: context, + builder: (context) { + return StatefulBuilder( + builder: + (context, setState) => AlertDialog( + backgroundColor: Colors.white, + contentPadding: const EdgeInsets.all(36), + // title: const Text("Confirm Rejection"), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + "Confirm Rejection", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 16), + const Text("Please enter reason for rejection."), + const SizedBox(height: 10), + TextField( + maxLines: 3, + onChanged: (value) => remarks = value, + decoration: const InputDecoration( + hintText: "Remarks...", + hintStyle: TextStyle( + fontSize: 10, // 👈 Set your desired font size here + color: Colors.grey, + fontFamily: + "Inter", // optional if you want consistent font + ), + border: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.blueGrey, + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.blueGrey, + width: 0.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.grey, width: 1), + ), + ), + ), + ], + ), + actions: [ + 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: () => Navigator.pop(context, false), + child: const Text("Cancel"), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: layoutColor, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: layoutColor, width: 2), + ), + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: () { + if (remarks.trim().isEmpty) return; + Navigator.pop(context, true); + }, + child: const Text("OK"), + ), + ], + ), ); }, ); diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 0cda2f5..0c464b4 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -43,8 +43,6 @@ class _ApprovalListState extends State { List filteredPlans = []; TextEditingController searchController = TextEditingController(); - late List plansJson; - @override void initState() { super.initState(); @@ -63,13 +61,15 @@ class _ApprovalListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -186,7 +186,7 @@ class _ApprovalListState extends State { if (response.statusCode == 200) { final data = json.decode(response.body); - plansJson = data['data']; + List plansJson = data['data']; return plansJson.map((json) => Plan.fromJson(json)).toList(); } else { throw Exception('Failed to load plans'); @@ -239,8 +239,10 @@ class _ApprovalListState extends State { // print("Error fetching plan: $e"); // } - bool confirmed = - await apiService.showCancelConfirmationDialog(context, layoutColor); + bool confirmed = await apiService.showCancelConfirmationDialog( + context, + layoutColor, + ); if (confirmed) { try { @@ -282,17 +284,37 @@ class _ApprovalListState extends State { } } - void viewPlanforApprover(String planId, - {bool isViewMode = false, bool isApprover = true}) async { + void viewPlanforApprover( + String planId, + String? approverId, + String? delegaterId, { + bool isViewMode = false, + bool isApprover = true, + }) async { try { Map planData = await getViewPlan(planId); print("ViewAAA - $planData"); - context.go('/createPlan', extra: { - 'planData': planData, - 'isViewMode': isViewMode, - 'isApprover': isApprover - }); + + context.replace( + '/approver/plans', + extra: { + 'planData': planData, + 'approverId': approverId, + 'delegaterId': delegaterId, + 'isViewMode': isViewMode, + 'isApprover': isApprover, + }, + ); + + // context.go( + // '/createPlan', + // extra: { + // 'planData': planData, + // 'isViewMode': isViewMode, + // 'isApprover': isApprover, + // }, + // ); } catch (e) { print("Error fetching plan: $e"); } @@ -314,48 +336,58 @@ class _ApprovalListState extends State { print("allPlans before filtering: $allPlans"); final lowerQuery = query.toLowerCase(); setState(() { - filteredPlans = allPlans.where((plan) { - return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); - }).toList(); + filteredPlans = + allPlans.where((plan) { + return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); + }).toList(); + currentPage = 0; }); print("filteredPlans: $filteredPlans"); } Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Scaffold( - backgroundColor: Color(0xFFf5f5f5), - // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), - // 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), - Expanded(child: buildGroupListLayout(isDesktop)) - ], + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), + // 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), + Expanded(child: buildGroupListLayout(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupListLayout(bool isDesktop) { @@ -382,11 +414,14 @@ class _ApprovalListState extends State { // } // } + final adjHgt = MediaQuery.of(context).size.height; + String _formatDate(String rawDate) { try { final dateTime = DateTime.parse(rawDate); - return DateFormat('dd, MMM yyyy HH:mm') - .format(dateTime); // 24-hour format + return DateFormat( + 'dd, MMM yyyy HH:mm', + ).format(dateTime); // 24-hour format } catch (e) { return rawDate; // fallback if parsing fails } @@ -395,17 +430,19 @@ class _ApprovalListState extends State { return Container( margin: isDesktop ? EdgeInsets.all(10.0) : null, padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20), - height: isDesktop - ? MediaQuery.of(context).size.height * 0.98 - : MediaQuery.of(context).size.height, + 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, + border: + isDesktop + ? Border.all( + width: 2, + color: Colors.white, + // color: Color(0xFFF7F7FB), + ) + : null, color: isDesktop ? Colors.white : Color(0xFFFCFCFC), // color: Color(0xFFF7F7FB), @@ -443,9 +480,7 @@ class _ApprovalListState extends State { ], ), if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.23, - ), + SizedBox(width: MediaQuery.of(context).size.width * 0.23), if (isDesktop) Container( @@ -456,8 +491,10 @@ class _ApprovalListState extends State { onChanged: filterPlans, decoration: InputDecoration( hintText: "Search...", - hintStyle: - TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)), + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), @@ -469,69 +506,72 @@ class _ApprovalListState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade200, width: 0.5), + color: Colors.grey.shade200, + width: 0.5, + ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade300, width: 1), + color: Colors.grey.shade300, + width: 1, + ), ), ), - style: GoogleFonts.poppins( - fontSize: 12, - ), + style: GoogleFonts.poppins(fontSize: 12), ), ), // SizedBox(width: 16), ], ), - if (!isDesktop) - SizedBox( - height: 5, - ), + 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: filterPlans, - decoration: InputDecoration( - hintText: "Search...", - 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), + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.8, + height: 35, + child: TextField( + controller: searchController, + onChanged: filterPlans, + decoration: InputDecoration( + hintText: "Search...", + 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, ), ), - style: GoogleFonts.poppins( - fontSize: 12, + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, + ), ), ), + style: GoogleFonts.poppins(fontSize: 12), ), - // SizedBox(width: 16), - ], - ), + ), + // SizedBox(width: 16), + ], + ), const SizedBox(height: 10), FutureBuilder>( @@ -547,7 +587,9 @@ class _ApprovalListState extends State { padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, - children: const [ + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ // Icon(Icons.error_outline, // color: Colors.redAccent, size: 60), // SizedBox(height: 1), @@ -556,14 +598,18 @@ class _ApprovalListState extends State { // fontSize: 22, // fontWeight: FontWeight.bold, // color: Colors.redAccent)), - SizedBox(height: 15), - Text("No Trips Pending For Your Approvals", - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 20, - fontFamily: "Inter", - fontWeight: FontWeight.w600, - color: Colors.black54)), + SizedBox(height: adjHgt / 4), + Text( + " No Trips Pending For Your Approvals", + + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + // fontFamily: "Inter", + fontWeight: FontWeight.w500, + color: Colors.black54, + ), + ), ], ), ), @@ -579,297 +625,414 @@ class _ApprovalListState extends State { List plans = searchController.text.isEmpty ? allPlans : filteredPlans; - plans.sort((a, b) => - int.parse(b.planId).compareTo(int.parse(a.planId))); + plans.sort( + (a, b) => + int.parse(b.planId).compareTo(int.parse(a.planId)), + ); - List paginatedPlans = plans - .skip(currentPage * itemsPerPage) - .take(itemsPerPage) - .toList(); + List paginatedPlans = + plans + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); Widget table = LayoutBuilder( builder: (context, constraints) { double minWidth = isDesktop ? constraints.maxWidth : 1300; return ConstrainedBox( - constraints: BoxConstraints( - minWidth: minWidth, - ), + constraints: BoxConstraints(minWidth: minWidth), child: DataTable( dividerThickness: 0.5, columnSpacing: isDesktop ? 24.0 : 16.0, border: TableBorder( horizontalInside: BorderSide( - width: 0.5, color: Colors.grey.shade200), + width: 0.5, + color: Colors.grey.shade200, + ), ), columns: [ DataColumn( - label: Text( - 'Trip ID', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Trip Name', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Emp Code', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Traveller', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Trip Type', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Created On', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Actions', - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600), - )), - ], - rows: paginatedPlans.map((plan) { - return DataRow(cells: [ - DataCell(Text(plan.planId, - style: TextStyle( - fontSize: 13, - fontFamily: "Archivo", - ))), - - DataCell(Text(plan.tripTitle, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell(Text(plan.employeeCode ?? " - ", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text( - plan.userName.isNotEmpty - ? plan.userName - : plan.travellerName, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(plan.tripType, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - - DataCell(Text( - - // plan.createdOn, - _formatDate(plan.createdOn), - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell( - Container( - width: double - .infinity, // Set your desired fixed size (equal width and height) - height: 25, - alignment: Alignment.center, - decoration: BoxDecoration( - color: getStatusColor(plan.statusValue), - borderRadius: BorderRadius.circular(8), - ), - - child: Text( - plan.statusValue, - textAlign: TextAlign.center, - style: TextStyle( - color: - getStatusTextColor(plan.statusValue), - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.w400, - ), - ), + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, ), ), - - DataCell( - Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, + ), + DataColumn( + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + paginatedPlans.map((plan) { + return DataRow( + cells: [ + DataCell( + Text( + plan.planId, + style: TextStyle( + fontSize: 13, + fontFamily: "Archivo", + ), ), - 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: () { - Navigator.pop( - context); // Close popup manually - viewPlanforApprover( - plan.planId, - isViewMode: true, - isApprover: true); - }), - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - onPressed: () { - Navigator.pop(context); - ApiService - .viewPlanForApprover( - context, - plan.planId, - isViewMode: false, - isApprover: true); - }, - ), - IconButton( - icon: Icon( - Icons.cancel_rounded, - size: 18), - onPressed: () { - Navigator.pop(context); - deletePlan(plan.planId); - }, - ), - IconButton( - icon: Icon(Icons.download, - color: Color(0xFF114D8B), - size: 18), - onPressed: () { - Navigator.pop(context); - apiService.getPdfDownload( - plan.planId); - }, - ), - IconButton( - icon: const Icon( - Icons.comment, - color: Color(0xFF475569), - size: 11, - ), - onPressed: () { - showDialog( - context: context, - builder: (context) => - CommentModalList( - // planId: plan.planId, - planId: plan - .planId - .toString(), - layoutColorForUser: - layoutColor!, - role: - "Approver"), - ); - }), - ], - ), + ), + + DataCell( + Text( + plan.tripTitle, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + plan.employeeCode ?? " - ", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.userName.isNotEmpty + ? plan.userName + : plan.travellerName, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.tripType, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + + DataCell( + Text( + // plan.createdOn, + _formatDate(plan.createdOn), + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Container( + width: + double + .infinity, // Set your desired fixed size (equal width and height) + height: 25, + alignment: Alignment.center, + decoration: BoxDecoration( + color: getStatusColor( + plan.statusValue, + ), + borderRadius: BorderRadius.circular( + 8, ), ), - ], + + child: Text( + plan.statusValue, + textAlign: TextAlign.center, + style: TextStyle( + color: getStatusTextColor( + plan.statusValue, + ), + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), + ), + ), ), + + DataCell( + Row( + children: [ + 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, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + viewPlanforApprover( + plan.planId, + plan.approverId, + plan.delegaterId, + isViewMode: + true, + isApprover: + true, + ); + }, + ), + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + print( + "Approver Edit : ${plan.approverId}", + ); + print( + "Approver Edit2 : ${plan.delegaterId}", + ); + Navigator.pop( + context, + ); + ApiService.viewPlanForApprover( + context, + plan.planId, + plan.approverId, + plan.delegaterId, + isViewMode: + false, + isApprover: + true, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + deletePlan( + plan.planId, + ); + }, + ), + IconButton( + icon: Icon( + Icons.download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Approver Comment', + onPressed: () { + showDialog( + context: + context, + builder: + ( + context, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId + .toString(), + layoutColorForUser: + layoutColor!, + role: + "Approver", + ), + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), + ), + // DataCell(Row(children: [ + // IconButton( + // icon: const Icon( + // Icons.remove_red_eye, + // color: Color(0xFF475569), + // size: 18, + // ), + // onPressed: () => viewPlanforApprover( + // plan.planId, + // isViewMode: true, + // isApprover: true)), + // + // GestureDetector( + // onTap: () => ApiService.viewPlanForApprover( + // context, plan.planId, + // isViewMode: false, isApprover: true), + // child: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15), + // ), + // + // // IconButton( + // // icon: const Icon(Icons.edit, + // // color: Colors.green), + // // onPressed: () => viewPlanforApprover(plan.planId, + // // isViewMode: false) ), + // + // SizedBox( + // width: 5, + // ), + // GestureDetector( + // onTap: () => deletePlan(plan.planId), + // child: Image.asset( + // 'assets/images/IconsImg/delete.png', + // width: 20, + // height: 15), + // ), + // + // IconButton( + // icon: const Icon( + // Icons.download, + // color: Color(0xFF475569), + // size: 18, + // ), + // onPressed: () { + // apiService.getPdfDownload(plan.planId); + // }), + // ])), ], - ), - ), - // DataCell(Row(children: [ - // IconButton( - // icon: const Icon( - // Icons.remove_red_eye, - // color: Color(0xFF475569), - // size: 18, - // ), - // onPressed: () => viewPlanforApprover( - // plan.planId, - // isViewMode: true, - // isApprover: true)), - // - // GestureDetector( - // onTap: () => ApiService.viewPlanForApprover( - // context, plan.planId, - // isViewMode: false, isApprover: true), - // child: Image.asset( - // 'assets/images/IconsImg/edit.png', - // width: 20, - // height: 15), - // ), - // - // // IconButton( - // // icon: const Icon(Icons.edit, - // // color: Colors.green), - // // onPressed: () => viewPlanforApprover(plan.planId, - // // isViewMode: false) ), - // - // SizedBox( - // width: 5, - // ), - // GestureDetector( - // onTap: () => deletePlan(plan.planId), - // child: Image.asset( - // 'assets/images/IconsImg/delete.png', - // width: 20, - // height: 15), - // ), - // - // IconButton( - // icon: const Icon( - // Icons.download, - // color: Color(0xFF475569), - // size: 18, - // ), - // onPressed: () { - // apiService.getPdfDownload(plan.planId); - // }), - // ])), - ]); - }).toList(), + ); + }).toList(), ), ); }, @@ -882,8 +1045,10 @@ class _ApprovalListState extends State { final plan = paginatedPlans[index]; return Card( color: Colors.white, - margin: - EdgeInsets.symmetric(horizontal: 12, vertical: 6), + margin: EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), @@ -900,7 +1065,9 @@ class _ApprovalListState extends State { children: [ Container( padding: EdgeInsets.symmetric( - horizontal: 8, vertical: 4), + horizontal: 8, + vertical: 4, + ), decoration: BoxDecoration( color: getStatusColor(plan.statusValue), borderRadius: BorderRadius.circular(8), @@ -909,7 +1076,8 @@ class _ApprovalListState extends State { plan.statusValue, style: TextStyle( color: getStatusTextColor( - plan.statusValue), + plan.statusValue, + ), fontSize: 10, fontWeight: FontWeight.w600, ), @@ -924,90 +1092,126 @@ class _ApprovalListState extends State { 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( + 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: () { - Navigator.pop( - context); // Close popup manually - viewPlanforApprover( + color: Color( + 0xFF475569, + ), + size: 18, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + viewPlanforApprover( plan.planId, + plan.approverId, + plan.delegaterId, isViewMode: true, - isApprover: true); - }), - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - onPressed: () { - Navigator.pop(context); - ApiService - .viewPlanForApprover( - context, - plan.planId, - isViewMode: false, - isApprover: true); - }, - ), - IconButton( - icon: Icon( - Icons.cancel_rounded, - size: 18), - onPressed: () { - Navigator.pop(context); - deletePlan(plan.planId); - }, - ), - IconButton( - icon: Icon(Icons.download, - color: Color(0xFF114D8B), - size: 18), - onPressed: () { - Navigator.pop(context); - apiService.getPdfDownload( - plan.planId); - }, - ), - IconButton( - icon: const Icon( - Icons.comment, - color: Color(0xFF475569), - size: 11, + isApprover: true, + ); + }, ), - onPressed: () { - showDialog( - context: context, - builder: (context) => - CommentModalList( + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + Navigator.pop(context); + ApiService.viewPlanForApprover( + context, + plan.planId, + plan.approverId, + plan.delegaterId, + isViewMode: false, + isApprover: true, + ); + }, + ), + IconButton( + icon: Icon( + Icons.cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop(context); + deletePlan(plan.planId); + }, + ), + IconButton( + icon: Icon( + Icons.download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The Trip Details', + onPressed: () { + Navigator.pop(context); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: 'Trip Comments', + onPressed: () { + showDialog( + context: context, + builder: + ( + context, + ) => CommentModalList( // planId: plan.planId, - planId: plan - .planId - .toString(), + planId: + plan.planId + .toString(), layoutColorForUser: layoutColor!, role: - "Approver"), - ); - }), - ], + "Approver", + ), + ); + }, + ), + ], + ), + ), ), - ), - ), - ], + ], ), ], ), @@ -1020,11 +1224,14 @@ class _ApprovalListState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(' ${plan.tripTitle}', - style: TextStyle( - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.bold)), + Text( + ' ${plan.tripTitle}', + style: TextStyle( + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.bold, + ), + ), ], ), ], @@ -1039,24 +1246,28 @@ class _ApprovalListState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(' ${plan.tripType}', - style: TextStyle( - fontSize: 9, - color: Colors.black87, - fontFamily: "Inter", - )), + Text( + ' ${plan.tripType}', + style: TextStyle( + fontSize: 9, + color: Colors.black87, + fontFamily: "Inter", + ), + ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('${_formatDate(plan.createdOn)}', - style: TextStyle( - fontSize: 9, - color: Colors.black87, - fontFamily: "Inter", - )), + Text( + '${_formatDate(plan.createdOn)}', + style: TextStyle( + fontSize: 9, + color: Colors.black87, + fontFamily: "Inter", + ), + ), ], ), ], @@ -1074,9 +1285,10 @@ class _ApprovalListState extends State { ? '${plan.userName}' : '${plan.travellerName}', style: TextStyle( - fontSize: 9, - fontFamily: "Inter", - color: Colors.black87), + fontSize: 9, + fontFamily: "Inter", + color: Colors.black87, + ), ), ], ), @@ -1088,9 +1300,10 @@ class _ApprovalListState extends State { Text( plan.employeeCode ?? 'No Data', style: TextStyle( - fontSize: 9, - fontFamily: "Inter", - color: Colors.black87), + fontSize: 9, + fontFamily: "Inter", + color: Colors.black87, + ), ), ], ), @@ -1111,30 +1324,35 @@ class _ApprovalListState extends State { // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: isDesktop - ? (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, color: Colors.grey), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) - : (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, color: Colors.grey), - ), - ) - : buildMobileCardView(paginatedPlans)), + child: + isDesktop + ? (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView(paginatedPlans)), // child: isDesktop // ? SingleChildScrollView( @@ -1164,7 +1382,7 @@ class _ApprovalListState extends State { ), ); }, - ) + ), ], ), ), diff --git a/lib/Screens/authentication/login/login_widget.dart b/lib/Screens/authentication/login/login_widget.dart index 5ccf7d7..e8a72fd 100644 --- a/lib/Screens/authentication/login/login_widget.dart +++ b/lib/Screens/authentication/login/login_widget.dart @@ -11,12 +11,14 @@ import 'package:go_router/go_router.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../../../services/apiService.dart'; + class LoginWidget extends StatefulWidget { final bool isDesktop; final bool isTablet; const LoginWidget({Key? key, required this.isDesktop, required this.isTablet}) - : super(key: key); + : super(key: key); @override _LoginWidgetState createState() => _LoginWidgetState(); @@ -25,6 +27,9 @@ class LoginWidget extends StatefulWidget { enum LoginStep { login, forgotEmail, otpReset } class _LoginWidgetState extends State { + final ApiService apiService = ApiService(); + bool _moved = false; + final _formKey = GlobalKey(); final TextEditingController _emailController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); @@ -39,6 +44,17 @@ class _LoginWidgetState extends State { // 🔹 Login Step Enum and State Variable LoginStep _loginStep = LoginStep.login; + @override + void initState() { + super.initState(); + + Future.delayed(Duration(milliseconds: 300), () { + setState(() { + _moved = true; + }); + }); + } + @override void dispose() { _emailController.dispose(); @@ -54,15 +70,18 @@ class _LoginWidgetState extends State { final parts = token.split('.'); if (parts.length != 3) throw Exception('Invalid token format'); - final payload = json - .decode(utf8.decode(base64Url.decode(base64Url.normalize(parts[1])))); + final payload = json.decode( + utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))), + ); final userData = payload['data']; final prefs = await SharedPreferences.getInstance(); await prefs.setString('auth_token', token); await prefs.setString( - 'user_data', jsonEncode(userData)); // Store full user data + 'user_data', + jsonEncode(userData), + ); // Store full user data if (userData != null) { final pref = await SharedPreferences.getInstance(); @@ -75,6 +94,8 @@ class _LoginWidgetState extends State { print("userData11 - ${userData['role']}"); print("userData12 - $userRole"); } + + await apiService.getOrganizationData(); } catch (e) { print('Error decoding token: $e'); } @@ -89,7 +110,7 @@ class _LoginWidgetState extends State { Uri.parse(url), headers: { 'Content-Type': 'application/json', - 'Accept': 'application/json' + 'Accept': 'application/json', }, body: jsonEncode({ 'email': _emailController.text.trim(), @@ -135,6 +156,8 @@ class _LoginWidgetState extends State { backgroundColor: Colors.red, ), ); + _emailController.clear(); + _passwordController.clear(); // Fluttertoast.showToast( // msg: "Login Failed: $errorMessage", // toastLength: Toast.LENGTH_LONG, @@ -145,9 +168,9 @@ class _LoginWidgetState extends State { // ); } } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Error: $e")), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text("Error: $e"))); } } } @@ -175,19 +198,27 @@ class _LoginWidgetState extends State { // _clearAllFields(); }); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("OTP sent to your email")), + SnackBar( + content: Text("OTP sent to your email"), + backgroundColor: Colors.green, + ), ); } else { print(response); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: - Text("${jsonDecode(response.body)['messages']['error']}")), + content: Text( + "${jsonDecode(response.body)['messages']['error']}", + ), + + backgroundColor: Colors.red, + ), ); } } catch (e) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("Error: $e"))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Error: $e"), backgroundColor: Colors.red), + ); } } else if (!_isForgotPassword && _showOtpResetFields) { print('22'); @@ -214,20 +245,32 @@ class _LoginWidgetState extends State { _clearAllFields(); }); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Password reset successfully")), + SnackBar( + content: Text("Password reset successfully"), + backgroundColor: Colors.green, + ), ); } else { print('23'); print('otp wrong'); + final responseBody = json.decode(response.body); + final errorMessage = + responseBody['messages']?['error'] ?? 'An unknown error occurred'; + print(errorMessage); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - "Reset failed: ${jsonDecode(response.body)['message']}")), + content: Text( + "Reset failed: $errorMessage", + // "Reset failed: ${jsonDecode(response.body)['message']}", + ), + backgroundColor: Colors.red, + ), ); } } catch (e) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("Error: $e"))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Error: $e"), backgroundColor: Colors.red), + ); } } else { // Login flow @@ -255,15 +298,15 @@ class _LoginWidgetState extends State { } @override - /// Layout Widget build(BuildContext context) { double formWidth = widget.isTablet ? 400 : 300; return Container( // color: Color(0xFF114D8B), - color: Color(0xFFf5f5f5), + color: Colors.white, + // color: Color(0xFFf5f5f5), padding: const EdgeInsets.all(10), child: Row( children: [ @@ -272,21 +315,14 @@ class _LoginWidgetState extends State { flex: 2, child: Container( decoration: BoxDecoration( - // image: DecorationImage( - // image: AssetImage( - // 'assets/images/login/login_travel.png', - // ), - // // fit: BoxFit.fill - // fit: BoxFit.contain - // // fit: BoxFit.cover, // or BoxFit.contain, BoxFit.fill, etc. - // ), color: Color(0xFFE6F0FA), // color: Color(0xFFF0F7FF), // color: Colors.white, borderRadius: BorderRadius.only( topRight: Radius.circular(250), // Rounded top-left corner - bottomRight: - Radius.circular(250), // Rounded bottom-left corner + bottomRight: Radius.circular( + 250, + ), // Rounded bottom-left corner ), ), // child: Padding( @@ -305,24 +341,29 @@ class _LoginWidgetState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset( - 'assets/images/login/logoNew.jpg', + // Image.asset( + // 'assets/images/login/logoNew.jpg', + // width: 200, // Optional: control size + // height: 100, + // fit: BoxFit.contain, + // ), + Container( width: 200, // Optional: control size height: 100, - fit: BoxFit.contain, ), Expanded( child: Container( child: Align( - // alignment: Alignment.bottomRight, - child: Image.asset( - 'assets/images/login/login_travel.png', - // width: 200, // Optional: control size - // height: 100, - fit: BoxFit.contain, - )), + // alignment: Alignment.bottomRight, + child: Image.asset( + 'assets/images/login/login_travel.png', + // width: 200, // Optional: control size + // height: 100, + fit: BoxFit.contain, + ), + ), ), - ) + ), ], ), ), @@ -332,17 +373,15 @@ class _LoginWidgetState extends State { child: Container( decoration: BoxDecoration( // color: Colors.white, - borderRadius: BorderRadius.only( topLeft: Radius.circular(25), // Rounded top-left corner bottomLeft: Radius.circular(25), // Rounded bottom-left corner ), ), - child: Center( - child: Padding( - padding: const EdgeInsets.all(40), - child: _buildForm(width: formWidth), // Fixed form width - ), + + child: Padding( + padding: const EdgeInsets.all(10), + child: _buildForm(width: formWidth), // Fixed form width ), ), ), @@ -353,436 +392,587 @@ class _LoginWidgetState extends State { /// **Reusable Login Form** Widget _buildForm({required double width}) { - return SizedBox( - width: width, - child: Form( - key: _formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "Sign In", - style: GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w500, - color: Color(0xFF212121)), - ), - const SizedBox(height: 2), - Text( - "Welcome To TripApprovalTool", - style: GoogleFonts.poppins( - fontSize: 11, - fontWeight: FontWeight.w400, - color: Color(0xFF212121)), - ), - const SizedBox(height: 18), - - /// **Email Field** - if (!_isForgotPassword && !_showOtpResetFields) ...[ - _buildLabel("Email Address"), - TextFormField( - controller: _emailController, - style: GoogleFonts.poppins( - fontWeight: FontWeight.w600, fontSize: 11), - decoration: - _inputDecoration("Enter your email address").copyWith( - prefixIcon: Icon( - Icons.email_outlined, - size: 16, - ), - ), - validator: (value) => - value == null || value.isEmpty ? 'Required Email' : null, - ), - - const SizedBox(height: 16), - - /// **Password Field** - _buildLabel("Password"), - TextFormField( - controller: _passwordController, - style: GoogleFonts.poppins( - fontWeight: FontWeight.w600, fontSize: 11), - obscureText: _obscureText, - decoration: _inputDecoration("Enter your password").copyWith( - prefixIcon: Icon( - Icons.key, - size: 16, - ), - suffixIcon: IconButton( - icon: Icon( - _obscureText ? Icons.visibility_off : Icons.visibility, - color: Color(0xFF12B24B), - size: 16, - ), - onPressed: () => - setState(() => _obscureText = !_obscureText), - ), - ), - validator: (value) => - value == null || value.isEmpty ? 'Required Password' : null, - ), - - const SizedBox(height: 20), - - /// **Login Button** + return Column( + children: [ + // width: width, + // Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // + // ], + // ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Expanded( - child: ElevatedButton( - onPressed: () => _login(context), - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF12B24B), // Button color - foregroundColor: Colors.white, // Text color - padding: const EdgeInsets.symmetric( - horizontal: 24, vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(18)), - ), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 24, vertical: 10), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "Sign In", + SizedBox( + width: width, + child: Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + 'assets/images/login/logoNew.jpg', + width: 180, // Optional: control size + height: 70, + fit: BoxFit.contain, + ), + const SizedBox(height: 2), + Text( + "Sign In", + style: GoogleFonts.poppins( + fontSize: widget.isDesktop ? 20 : 18, + fontWeight: FontWeight.w700, + color: Colors.green, + // color: Color(0xFF212121), + ), + ), + const SizedBox(height: 5), + Text( + "Welcome To TripApprovalTool", + style: GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w400, + + color: Color(0xFF212121), + ), + ), + const SizedBox(height: 10), + + /// **Email Field** + if (!_isForgotPassword && !_showOtpResetFields) ...[ + _buildLabel("Email Address"), + TextFormField( + controller: _emailController, style: GoogleFonts.poppins( - fontWeight: FontWeight.w800, fontSize: 15), + fontWeight: FontWeight.w600, + fontSize: 11, + ), + decoration: _inputDecoration( + "Enter your email address", + ).copyWith( + prefixIcon: Icon( + Icons.email_outlined, + size: 16, + ), + ), + validator: + (value) => + value == null || value.isEmpty + ? 'Required Email' + : null, ), - SizedBox( - width: 3, - ), - Icon( - Icons.arrow_forward_sharp, - color: Colors.white, - ) - ], - ), - ), - ), - ), - ], - ), - ] else if (_isForgotPassword && !_showOtpResetFields) ...[ - _buildLabel("Email Address"), - TextFormField( - controller: _emailController, - style: GoogleFonts.poppins( - fontWeight: FontWeight.w600, fontSize: 11), - decoration: - _inputDecoration("Enter your email address").copyWith( - prefixIcon: Icon( - Icons.email_outlined, - size: 16, - ), - ), - validator: (value) => - value == null || value.isEmpty ? 'Required Email' : null, - ), - const SizedBox(height: 20), - Row( - children: [ - Expanded( - child: ElevatedButton( - onPressed: () => _onSubmit(context), - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF12B24B), // Button color - foregroundColor: Colors.white, // Text color - padding: const EdgeInsets.symmetric( - horizontal: 24, vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(18)), - ), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 24, vertical: 10), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "Submit", + + const SizedBox(height: 10), + + /// **Password Field** + _buildLabel("Password"), + // TextFormField( + // controller: _passwordController, + // style: GoogleFonts.poppins( + // fontWeight: FontWeight.w600, + // fontSize: 11, + // ), + // obscureText: _obscureText, + // + // decoration: _inputDecoration( + // "Enter your password", + // ).copyWith( + // prefixIcon: Icon(Icons.key, size: 16), + // suffixIcon: IconButton( + // icon: Icon( + // _obscureText + // ? Icons.visibility_off + // : Icons.visibility, + // color: Color(0xFF12B24B), + // size: 16, + // ), + // + // onPressed: + // () => setState( + // () => _obscureText = !_obscureText, + // ), + // ), + // ), + // + // validator: + // (value) => + // value == null || value.isEmpty + // ? 'Required Password' + // : null, + // ), + TextFormField( + controller: _passwordController, style: GoogleFonts.poppins( - fontWeight: FontWeight.w800, fontSize: 15), + fontWeight: FontWeight.w600, + fontSize: 11, + ), + obscureText: _obscureText, + textInputAction: TextInputAction.done, + decoration: _inputDecoration( + "Enter your password", + ).copyWith( + prefixIcon: Icon(Icons.key, size: 16), + suffixIcon: IconButton( + icon: Icon( + _obscureText + ? Icons.visibility_off + : Icons.visibility, + color: Color(0xFF12B24B), + size: 16, + ), + onPressed: + () => setState( + () => _obscureText = !_obscureText, + ), + ), + ), + onFieldSubmitted: (_) { + if (_formKey.currentState!.validate()) { + _login(context); + } + }, + validator: + (value) => + value == null || value.isEmpty + ? 'Required Password' + : null, ), - SizedBox( - width: 3, + const SizedBox(height: 10), + + /// **Login Button** + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () => _login(context), + style: ElevatedButton.styleFrom( + backgroundColor: Color( + 0xFF12B24B, + ), // Button color + foregroundColor: + Colors.white, // Text color + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18), + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 5, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Sign In", + style: GoogleFonts.poppins( + fontWeight: FontWeight.w800, + fontSize: 13.5, + ), + ), + SizedBox(width: 3), + Icon( + Icons.arrow_forward_sharp, + color: Colors.white, + ), + ], + ), + ), + ), + ), + ], ), - Icon( - Icons.arrow_forward_sharp, - color: Colors.white, - ) - ], - ), - ), - ), - ), - ], - ), - ] else ...[ - _buildLabel("Email Address"), - TextFormField( - controller: _emailController, - readOnly: true, - style: GoogleFonts.poppins( - fontWeight: FontWeight.w600, fontSize: 11), - decoration: - _inputDecoration("Enter your email address").copyWith( - prefixIcon: Icon( - Icons.email_outlined, - size: 16, - ), - ), - validator: (value) => - value == null || value.isEmpty ? 'Required Email' : null, - ), - const SizedBox(height: 16), - _buildLabel("OTP"), - TextFormField( - controller: _otpController, - style: GoogleFonts.poppins( - fontWeight: FontWeight.w600, fontSize: 11), - decoration: _inputDecoration("Enter your OTP").copyWith( - prefixIcon: Icon( - Icons.email_outlined, - size: 16, - ), - ), - validator: (value) => - value == null || value.isEmpty ? 'Required OTP' : null, - ), - const SizedBox(height: 16), - _buildLabel("New Password"), - TextFormField( - controller: _newPasswordController, - style: GoogleFonts.poppins( - fontWeight: FontWeight.w600, fontSize: 11), - obscureText: _obscureText, - decoration: - _inputDecoration("Enter your new password").copyWith( - prefixIcon: Icon( - Icons.key, - size: 16, - ), - suffixIcon: IconButton( - icon: Icon( - _obscureText ? Icons.visibility_off : Icons.visibility, - color: Color(0xFF12B24B), - size: 16, - ), - onPressed: () => - setState(() => _obscureText = !_obscureText), - ), - ), - validator: (value) => value == null || value.isEmpty - ? 'Required New Password' - : null, - ), - const SizedBox(height: 16), - _buildLabel("Confirm Password"), - TextFormField( - controller: _confirmPasswordController, - style: GoogleFonts.poppins( - fontWeight: FontWeight.w600, fontSize: 11), - obscureText: _obscureText, - decoration: - _inputDecoration("Enter your confirm password").copyWith( - prefixIcon: Icon( - Icons.key, - size: 16, - ), - suffixIcon: IconButton( - icon: Icon( - _obscureText ? Icons.visibility_off : Icons.visibility, - color: Color(0xFF12B24B), - size: 16, - ), - onPressed: () => - setState(() => _obscureText = !_obscureText), - ), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Required New Password'; - } - if (value != _newPasswordController.text) { - return 'Passwords do not match'; - } - return null; - }, - ), - const SizedBox(height: 20), - Row( - children: [ - Expanded( - child: ElevatedButton( - onPressed: () { - if (!_formKey.currentState!.validate()) return; - setState(() { - _isForgotPassword = false; - _showOtpResetFields = true; - _passwordController.clear(); - }); - _onSubmit(context); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF12B24B), // Button color - foregroundColor: Colors.white, // Text color - padding: const EdgeInsets.symmetric( - horizontal: 24, vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(18)), - ), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 24, vertical: 10), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "Submit", + ] else if (_isForgotPassword && + !_showOtpResetFields) ...[ + _buildLabel("Email Address"), + TextFormField( + controller: _emailController, style: GoogleFonts.poppins( - fontWeight: FontWeight.w800, fontSize: 15), + fontWeight: FontWeight.w600, + fontSize: 11, + ), + decoration: _inputDecoration( + "Enter your email address", + ).copyWith( + prefixIcon: Icon( + Icons.email_outlined, + size: 16, + ), + ), + validator: + (value) => + value == null || value.isEmpty + ? 'Required Email' + : null, ), - SizedBox( - width: 3, + const SizedBox(height: 10), + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () => _onSubmit(context), + style: ElevatedButton.styleFrom( + backgroundColor: Color( + 0xFF12B24B, + ), // Button color + foregroundColor: + Colors.white, // Text color + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18), + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 5, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Submit", + style: GoogleFonts.poppins( + fontWeight: FontWeight.w800, + fontSize: 13.5, + ), + ), + SizedBox(width: 3), + Icon( + Icons.arrow_forward_sharp, + color: Colors.white, + ), + ], + ), + ), + ), + ), + ], + ), + ] else ...[ + _buildLabel("Email Address"), + TextFormField( + controller: _emailController, + readOnly: true, + style: GoogleFonts.poppins( + fontWeight: FontWeight.w600, + fontSize: 11, + ), + decoration: _inputDecoration( + "Enter your email address", + ).copyWith( + prefixIcon: Icon( + Icons.email_outlined, + size: 16, + ), + ), + validator: + (value) => + value == null || value.isEmpty + ? 'Required Email' + : null, + ), + const SizedBox(height: 10), + _buildLabel("OTP"), + TextFormField( + controller: _otpController, + style: GoogleFonts.poppins( + fontWeight: FontWeight.w600, + fontSize: 11, + ), + decoration: _inputDecoration( + "Enter your OTP", + ).copyWith( + prefixIcon: Icon( + Icons.email_outlined, + size: 16, + ), + ), + validator: + (value) => + value == null || value.isEmpty + ? 'Required OTP' + : null, + ), + const SizedBox(height: 10), + _buildLabel("New Password"), + TextFormField( + controller: _newPasswordController, + style: GoogleFonts.poppins( + fontWeight: FontWeight.w600, + fontSize: 11, + ), + obscureText: _obscureText, + decoration: _inputDecoration( + "Enter your new password", + ).copyWith( + prefixIcon: Icon(Icons.key, size: 16), + suffixIcon: IconButton( + icon: Icon( + _obscureText + ? Icons.visibility_off + : Icons.visibility, + color: Color(0xFF12B24B), + size: 16, + ), + onPressed: + () => setState( + () => _obscureText = !_obscureText, + ), + ), + ), + validator: + (value) => + value == null || value.isEmpty + ? 'Required New Password' + : null, + ), + const SizedBox(height: 10), + _buildLabel("Confirm Password"), + TextFormField( + controller: _confirmPasswordController, + style: GoogleFonts.poppins( + fontWeight: FontWeight.w600, + fontSize: 11, + ), + obscureText: _obscureText, + decoration: _inputDecoration( + "Enter your confirm password", + ).copyWith( + prefixIcon: Icon(Icons.key, size: 16), + suffixIcon: IconButton( + icon: Icon( + _obscureText + ? Icons.visibility_off + : Icons.visibility, + color: Color(0xFF12B24B), + size: 16, + ), + onPressed: + () => setState( + () => _obscureText = !_obscureText, + ), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Required New Password'; + } + if (value != _newPasswordController.text) { + return 'Passwords do not match'; + } + return null; + }, + ), + const SizedBox(height: 10), + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + if (!_formKey.currentState!.validate()) + return; + setState(() { + _isForgotPassword = false; + _showOtpResetFields = true; + _passwordController.clear(); + }); + _onSubmit(context); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Color( + 0xFF12B24B, + ), // Button color + foregroundColor: + Colors.white, // Text color + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18), + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 10, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Submit", + style: GoogleFonts.poppins( + fontWeight: FontWeight.w800, + fontSize: 13.5, + ), + ), + SizedBox(width: 3), + Icon( + Icons.arrow_forward_sharp, + color: Colors.white, + ), + ], + ), + ), + ), + ), + ], ), - Icon( - Icons.arrow_forward_sharp, - color: Colors.white, - ) ], - ), + const SizedBox(height: 10), + if (!_isForgotPassword && !_showOtpResetFields) + Center( + child: TextButton( + onPressed: () { + setState(() { + _isForgotPassword = true; + _showOtpResetFields = false; + _passwordController.clear(); + }); + }, + child: Text( + "Forgot Password", + style: GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w300, + + color: Color(0xFF212121), // Text color + decoration: + TextDecoration + .underline, // Underline the text + ), + ), + ), + ), + if (_isForgotPassword || _showOtpResetFields) + Center( + child: TextButton( + onPressed: () { + setState(() { + _isForgotPassword = false; + _showOtpResetFields = false; + _clearAllFields(); + }); + }, + child: Text( + "Back to Login", + style: GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w300, + + color: Color(0xFF212121), // Text color + decoration: + TextDecoration + .underline, // Underline the text + ), + ), + ), + ), + + const SizedBox(height: 10), + if (!_isForgotPassword && !_showOtpResetFields) + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + handleMS(); + }, + 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: 3, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Sign In With Microsoft", + style: GoogleFonts.poppins( + fontWeight: FontWeight.w500, + fontSize: 13, + ), + ), + SizedBox(width: 3), + Image.asset( + 'assets/images/login/microsoft.png', + width: 30, // Optional: control size + height: 30, + 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, + // )), + // ), + // ], + // ), + ], ), ), ), ], ), ], - const SizedBox(height: 20), - if (!_isForgotPassword && !_showOtpResetFields) - Center( - child: TextButton( - onPressed: () { - setState(() { - _isForgotPassword = true; - _showOtpResetFields = false; - _passwordController.clear(); - }); - }, - child: Text( - "Forgot Password", - style: GoogleFonts.poppins( - fontSize: 11, - fontWeight: FontWeight.w300, - - color: Color(0xFF212121), // Text color - decoration: - TextDecoration.underline, // Underline the text - ), - ), - ), - ), - if (_isForgotPassword || _showOtpResetFields) - Center( - child: TextButton( - onPressed: () { - setState(() { - _isForgotPassword = false; - _showOtpResetFields = false; - _clearAllFields(); - }); - }, - child: Text( - "Back to Login", - style: GoogleFonts.poppins( - fontSize: 11, - fontWeight: FontWeight.w300, - - color: Color(0xFF212121), // Text color - decoration: - TextDecoration.underline, // Underline the text - ), - ), - ), - ), - - const SizedBox(height: 20), - if (!_isForgotPassword && !_showOtpResetFields) - Row( - children: [ - Expanded( - child: ElevatedButton( - onPressed: () { - handleMS(); - }, - 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: GoogleFonts.poppins( - fontWeight: FontWeight.w500, fontSize: 14), - ), - SizedBox( - width: 3, - ), - Image.asset( - 'assets/images/login/microsoft.png', - width: 30, // Optional: control size - height: 30, - 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, - // )), - // ), - // ], - // ), - ], + ), ), - ), + ], ); } @@ -795,9 +985,10 @@ class _LoginWidgetState extends State { child: Text( text, style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF212121)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF212121), + ), ), ), ); @@ -826,8 +1017,10 @@ class _LoginWidgetState extends State { final url = '$apiUrl/auth/mslogin'; print(url); try { - final response = await http - .get(Uri.parse(url), headers: {'Content-Type': 'application/json'}); + final response = await http.get( + Uri.parse(url), + headers: {'Content-Type': 'application/json'}, + ); print("inside try method"); if (response.statusCode == 200) { final authUrl = json.decode(response.body)['auth_url']; diff --git a/lib/Screens/costCenter/costCenterDetails.dart b/lib/Screens/costCenter/costCenterDetails.dart index 0068d63..12d24d3 100644 --- a/lib/Screens/costCenter/costCenterDetails.dart +++ b/lib/Screens/costCenter/costCenterDetails.dart @@ -19,13 +19,14 @@ class CostCenterData extends StatefulWidget { final int? costcenterId; // <-- Add this final Map? costcenterData; - const CostCenterData( - {super.key, - required this.isDesktop, - this.layoutColor, - required this.fetchGetCostCenter, - this.costcenterId, - this.costcenterData}); + const CostCenterData({ + super.key, + required this.isDesktop, + this.layoutColor, + required this.fetchGetCostCenter, + this.costcenterId, + this.costcenterData, + }); @override CostCenterDataState createState() => CostCenterDataState(); @@ -49,10 +50,7 @@ class CostCenterDataState extends State { int? costcenterDataId; late String isActive = "1"; - List dataHeader = [ - "name", - "description", - ]; + List dataHeader = ["name", "description"]; Map costcenterDetails() { final data = { @@ -69,7 +67,6 @@ class CostCenterDataState extends State { void initState() { super.initState(); - apiData = null; for (var field in dataHeader) { controllers[field] = TextEditingController(); @@ -110,7 +107,6 @@ class CostCenterDataState extends State { }); } - void toggleStatus() { setState(() { isActive = isActive == "1" ? "0" : "1"; @@ -171,7 +167,9 @@ class CostCenterDataState extends State { apiUrldata = '$apiUrl/api/updateCostCenter/$costcenterDataId'; costcenterData["cost_center_id"] = costcenterDataId.toString(); costcenterData["updated_by"] = userId; - (costcenterData.containsKey("created_by")) ? costcenterData.remove("created_by") : '' ; + (costcenterData.containsKey("created_by")) + ? costcenterData.remove("created_by") + : ''; } else { print("for add CostCenter id - null"); apiUrldata = '$apiUrl/api/createCostCenter'; @@ -193,10 +191,10 @@ class CostCenterDataState extends State { }; final body = jsonEncode(costcenterData); - final response = costcenterDataId != null - ? await http.put(uri, headers: headers, body: body) - : await http.post(uri, headers: headers, body: body); - + final response = + costcenterDataId != null + ? await http.put(uri, headers: headers, body: body) + : await http.post(uri, headers: headers, body: body); switch (response.statusCode) { case 200: @@ -217,7 +215,6 @@ class CostCenterDataState extends State { print("Failed to submit costcenter. Status: ${response.statusCode}"); print("Error: ${response.body}"); } - } catch (e) { print(" Error submitting plan: $e"); } @@ -225,7 +222,6 @@ class CostCenterDataState extends State { @override Widget build(BuildContext context) { - return AlertDialog( backgroundColor: Colors.white, contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30), @@ -238,27 +234,27 @@ class CostCenterDataState extends State { Row( children: [ Text( - (costcenterDataId != null) ? 'Edit CostCenter' : 'Create CostCenter', + (costcenterDataId != null) + ? 'Edit CostCenter' + : 'Create CostCenter', style: GoogleFonts.poppins(fontSize: 15, color: Colors.black), ), const Spacer(), ], ), const SizedBox(height: 2), - Divider( - thickness: 0.2, - color: Colors.blueGrey.shade100, - ), + Divider(thickness: 0.2, color: Colors.blueGrey.shade100), const SizedBox(height: 5), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Name", + "Name *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -266,19 +262,20 @@ class CostCenterDataState extends State { isDesktop: widget.isDesktop, color: Colors.transparent, child: SizedBox( - height: 40, - child: TextField( - controller: controllers["name"], - focusNode: focusNodes["name"], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Name", - labelStyle: TextStyle(fontSize: 11, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - )), + height: 40, + child: TextField( + controller: controllers["name"], + focusNode: focusNodes["name"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Name", + labelStyle: TextStyle(fontSize: 11, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), ), if (errorMessages["name"] != null) ...[ SizedBox(height: 5), // Space before error message @@ -289,18 +286,17 @@ class CostCenterDataState extends State { ], ], ), - SizedBox( - height: 15, - ), + SizedBox(height: 15), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Description", + "Description *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -334,41 +330,37 @@ class CostCenterDataState extends State { ], ], ), - SizedBox( - height: 15, - ), + SizedBox(height: 15), if (costcenterDataId != null) - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Change Status ", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Change Status ", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), ), - Tooltip( - message: - isActive == "1" ? "Tap to deactivate" : "Tap to activate", - child: GestureDetector( - onTap: toggleStatus, - child: Text( - isActive == "1" ? "Active" : "Inactive", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - color: isActive == "1" ? Colors.green : Colors.red, - ), + ), + Tooltip( + message: + isActive == "1" ? "Tap to deactivate" : "Tap to activate", + child: GestureDetector( + onTap: toggleStatus, + child: Text( + isActive == "1" ? "Active" : "Inactive", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + color: isActive == "1" ? Colors.green : Colors.grey, ), ), - ) - ], - ), - if (costcenterDataId != null) - SizedBox( - height: 15, + ), + ), + ], ), + if (costcenterDataId != null) SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -405,16 +397,20 @@ class CostCenterDataState extends State { borderRadius: BorderRadius.circular(8), ), ), - child: Text('Save', - style: GoogleFonts.poppins( - fontSize: 11, color: Colors.white)), + child: Text( + 'Save', + style: GoogleFonts.poppins( + fontSize: 11, + color: Colors.white, + ), + ), ), ), ], - ) + ), // : SizedBox.shrink(), ], ), ); } -} \ No newline at end of file +} diff --git a/lib/Screens/costCenter/costCenter_list.dart b/lib/Screens/costCenter/costCenter_list.dart index 79b102d..af92cdb 100644 --- a/lib/Screens/costCenter/costCenter_list.dart +++ b/lib/Screens/costCenter/costCenter_list.dart @@ -25,7 +25,7 @@ class CostCenterList extends StatefulWidget { class CostCenterListState extends State { final GlobalKey costCenterListKey = - GlobalKey(); + GlobalKey(); final ApiService apiService = ApiService(); late Future> futureCostCenter; @@ -67,13 +67,15 @@ class CostCenterListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -97,8 +99,7 @@ class CostCenterListState extends State { } Future> fetchGetCostCenter() async { - - final String apiUrlData = '$apiUrl/api/getCostCenterMaster'; + final String apiUrlData = '$apiUrl/api/getCostCenterMaster?for=table_view'; final String? token = await getToken(); @@ -143,51 +144,60 @@ class CostCenterListState extends State { print("all before filtering: $query"); final lowerQuery = query.toLowerCase(); setState(() { - filteredCostCenter = allCostCenter.where((object) { - final isActiveStatus = - object['is_active'] == "1" ? "active" : "inactive"; - return (object['cost_center_id']?.toLowerCase().contains(lowerQuery) ?? - false) || - (object['name']?.toLowerCase().contains(lowerQuery) ?? - false) || - (object['description']?.toLowerCase().contains(lowerQuery) ?? false) || - (isActiveStatus.contains(lowerQuery)); - }).toList(); + filteredCostCenter = + allCostCenter.where((object) { + final isActiveStatus = + object['is_active'] == "1" ? "active" : "inactive"; + return (object['cost_center_id']?.toLowerCase().contains( + lowerQuery, + ) ?? + false) || + (object['name']?.toLowerCase().contains(lowerQuery) ?? false) || + (object['description']?.toLowerCase().contains(lowerQuery) ?? + false) || + (isActiveStatus.contains(lowerQuery)); + }).toList(); + currentPage = 0; }); print("filteredCostCenter: $filteredCostCenter"); - } @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Scaffold( - backgroundColor: Color(0xFFf5f5f5), - // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), - // 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 Page Content"))), - Expanded(child: buildGroupList(isDesktop)), - ], + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), + // 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 Page Content"))), + Expanded(child: buildGroupList(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupList(bool isDesktop) { @@ -215,98 +225,103 @@ class CostCenterListState extends State { // ? 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, + height: + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, child: Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + color: Colors.white, padding: const EdgeInsets.all(10.0), - child: Container( - color: Colors.white, - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Divider( - // thickness: 0.2, // how "thick" the line is - // color: Colors.grey, // optional - // ), Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Text( - 'CostCenter Details', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - ], + Text( + 'Cost Center Details', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), ), - if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.16, - ), + ], + ), + if (isDesktop) + SizedBox(width: MediaQuery.of(context).size.width * 0.16), - if (isDesktop) - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterCostCenter, - decoration: InputDecoration( - hintText: "Search ...", - 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), - ), + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterCostCenter, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), - style: GoogleFonts.poppins( - fontSize: 12, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, ), ), ), - // SizedBox(width: 16), - Spacer(), + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + // SizedBox(width: 16), + Spacer(), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF114D8B), - foregroundColor: Colors.white, - disabledBackgroundColor: Color(0xFF114D8B), - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: - BorderSide(color: Color(0xFF114D8B), width: 2), - ), - padding: EdgeInsets.symmetric( - horizontal: 20, vertical: 12), - ), - onPressed: () async { - showDialog( - context: context, - builder: (context) => CostCenterData( + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: EdgeInsets.symmetric( + horizontal: 20, + vertical: 12, + ), + ), + onPressed: () async { + showDialog( + context: context, + builder: + (context) => CostCenterData( isDesktop: isDesktop, layoutColor: layoutColor!, fetchGetCostCenter: refreshData, @@ -314,37 +329,34 @@ class CostCenterListState extends State { // role: // "Travel Agent" ), - ); - }, - child: Row( - mainAxisSize: + ); + }, + child: Row( + mainAxisSize: MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add CostCenter", - style: GoogleFonts.poppins( - fontSize: isDesktop ? 13 : 11, - ), - ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, - ), - ], + children: [ + Text( + "Add CostCenter", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 13 : 11, + ), ), - ), - ], - ), - - if (!isDesktop) - SizedBox( - height: 5, + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], ), - isDesktop - ? SizedBox.shrink() - : Row( + ), + ], + ), + + if (!isDesktop) SizedBox(height: 5), + isDesktop + ? SizedBox.shrink() + : Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( @@ -356,7 +368,9 @@ class CostCenterListState extends State { decoration: InputDecoration( hintText: "Search ...", hintStyle: TextStyle( - fontSize: 12, color: Color(0xFF9E9DBD)), + fontSize: 12, + color: Color(0xFF9E9DBD), + ), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), @@ -368,470 +382,535 @@ class CostCenterListState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade200, - width: 0.5), + color: Colors.grey.shade200, + width: 0.5, + ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: Colors.grey.shade300, width: 1), + color: Colors.grey.shade300, + width: 1, + ), ), ), - style: GoogleFonts.poppins( - fontSize: 12, - ), + style: GoogleFonts.poppins(fontSize: 12), ), ), // SizedBox(width: 16), ], ), - const SizedBox(height: 10), - FutureBuilder>( - future: futureCostCenter, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError || - !snapshot.hasData || - snapshot.data!.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // const Icon(Icons.error_outline, - // color: Colors.redAccent, size: 60), - // const SizedBox(height: 16), - // Text( - // "Oops!", - // style: GoogleFonts.poppins( - // fontSize: 20, - // fontWeight: FontWeight.bold, - // color: Colors.redAccent), - // ), - const SizedBox(height: 8), - Text( - "No CostCenter Available ", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey), - ), - const SizedBox(height: 20), - Text( - "Please Create CostCenter Details", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 16, color: Colors.grey), - ), - const SizedBox(height: 20), - ], + const SizedBox(height: 10), + FutureBuilder>( + future: futureCostCenter, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // const Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // const SizedBox(height: 16), + // Text( + // "Oops!", + // style: GoogleFonts.poppins( + // fontSize: 20, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent), + // ), + const SizedBox(height: 8), + Text( + "No CostCenter Available ", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), ), - ), - ); - } - /* Here collect the list to displayed the data in table or card Used */ - List object = - filteredCostCenter.isNotEmpty ? filteredCostCenter : allCostCenter; + const SizedBox(height: 20), + Text( + "Please Create CostCenter Details", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + ], + ), + ), + ); + } + /* Here collect the list to displayed the data in table or card Used */ + List object = + filteredCostCenter.isNotEmpty + ? filteredCostCenter + : allCostCenter; - /* List is Sorting here */ - object.sort((a, b) { - DateTime dateA = DateTime.parse(a['created_on']); - DateTime dateB = DateTime.parse(b['created_on']); + /* List is Sorting here */ + object.sort((a, b) { + DateTime dateA = DateTime.parse(a['created_on']); + DateTime dateB = DateTime.parse(b['created_on']); - return dateB - .compareTo(dateA); // Descending: newest first - }); + return dateB.compareTo(dateA); // Descending: newest first + }); - /* For pagination for list ... */ - List paginatedCostCenter = object + /* For pagination for list ... */ + List paginatedCostCenter = + object .skip(currentPage * itemsPerPage) .take(itemsPerPage) .toList(); - /* Table ... */ - Widget table = LayoutBuilder( - builder: (context, constraints) { - double minWidth = - isDesktop ? constraints.maxWidth : 1300; + /* Table ... */ + Widget table = LayoutBuilder( + builder: (context, constraints) { + double minWidth = isDesktop ? constraints.maxWidth : 1300; - return ConstrainedBox( - constraints: BoxConstraints(minWidth: minWidth), - child: DataTable( - dividerThickness: 0.5, - columnSpacing: isDesktop ? 24.0 : 16.0, - border: TableBorder( - horizontalInside: BorderSide( - width: 0.5, color: Colors.grey.shade200), + return ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + dividerThickness: 0.5, + columnSpacing: isDesktop ? 24.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, + ), + ), + columns: [ + DataColumn( + label: Text( + 'Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), ), - columns: [ - DataColumn( - label: Text( - 'Name', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Description', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Actions', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - ], - rows: paginatedCostCenter.map((tableObject) { - String costcenterId = tableObject['cost_center_id'] - .toString(); // Get user ID - bool isSelected = selectedCostCenterId == costcenterId; + ), + DataColumn( + label: Text( + 'Description', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + paginatedCostCenter.map((tableObject) { + String costcenterId = + tableObject['cost_center_id'] + .toString(); // Get user ID + bool isSelected = + selectedCostCenterId == costcenterId; - return DataRow(cells: [ - DataCell(Text(tableObject['name'] ?? '', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(tableObject['description'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", + return DataRow( + cells: [ + DataCell( + Text( + tableObject['name'] ?? '', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell( - Text( - tableObject['is_active'] == "1" - ? 'Active' - : 'Inactive', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - color: tableObject['is_active'] == "1" ? Colors.green : Colors.red, - ), - softWrap: true, - overflow: TextOverflow.ellipsis, ), - ), - DataCell( - // UserActionsMenu( - // user: forex, - // getUserDetails: (id) => - // apiService.getSingleUser(id), - // ), + DataCell( + Text( + tableObject['description'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + tableObject['is_active'] == "1" + ? 'Active' + : 'Inactive', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + color: + tableObject['is_active'] == "1" + ? Colors.green + : Colors.grey, + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + // UserActionsMenu( + // user: forex, + // getUserDetails: (id) => + // apiService.getSingleUser(id), + // ), + GestureDetector( + child: Tooltip( + message: 'Edit CostCenter Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + onTap: () async { + // final userId = getUserId(user['user_id']); + // final usersData = await getUserDetails(userId); + // + final costcenterId = int.tryParse( + tableObject['cost_center_id'] + .toString(), + ); + + if (costcenterId != null) { + print( + "Table cell - costcenter Id -- $costcenterId", + ); + final data = await apiService + .getCostCenterDetailsFind( + costcenterId, + ); + print("CostCenterId -- $data"); + + showDialog( + context: context, + builder: + (context) => CostCenterData( + isDesktop: isDesktop, + costcenterId: + costcenterId, // Pass the ID + costcenterData: data, + layoutColor: layoutColor!, + // fetchGetForex: fetchGetForex, + fetchGetCostCenter: + refreshData, + // role: + // "Travel Agent" + ), + ); + } else { + print("Invalid ID"); + } + }, + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + + /* Card ... */ + Widget buildMobileCardView(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final cardObject = 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( + cardObject['name'] ?? 'N/A', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.w700, + ), + ), + GestureDetector( - child: Image.asset( + child: Tooltip( + message: 'Edit CostCenter Details', + child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, - height: 15), + height: 15, + ), + ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); // final costcenterId = int.tryParse( - tableObject['cost_center_id'] - .toString()); + cardObject['cost_center_id'] + .toString(), + ); if (costcenterId != null) { - print("Table cell - costcenter Id -- $costcenterId"); - final data = await apiService.getCostCenterDetailsFind(costcenterId); + print( + "costcenterId -- $costcenterId", + ); + final data = await apiService + .getCostCenterDetailsFind( + costcenterId, + ); print("CostCenterId -- $data"); showDialog( context: context, - builder: (context) => CostCenterData( - isDesktop: isDesktop, - costcenterId: costcenterId, // Pass the ID - costcenterData: data, - layoutColor: layoutColor!, - // fetchGetForex: fetchGetForex, - fetchGetCostCenter: refreshData, - // role: - // "Travel Agent" - ), + builder: + (context) => CostCenterData( + isDesktop: isDesktop, + costcenterId: + costcenterId, // Pass the ID + costcenterData: data, + layoutColor: layoutColor!, + // fetchGetCostCenter: fetchGetCostCenter, + fetchGetCostCenter: + refreshData, + // role: + // "Travel Agent" + ), ); } else { print("Invalid ID"); } }, ), - ), - ]); - }).toList(), - ), - ); - }, - ); - - /* Card ... */ - Widget buildMobileCardView(List paginatedUser) { - return ListView.builder( - itemCount: paginatedUser.length, - itemBuilder: (context, index) { - final cardObject = 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( - cardObject['name'] ?? 'N/A', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - fontWeight: FontWeight.w700), - ), - - GestureDetector( - child: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - onTap: () async { - // final userId = getUserId(user['user_id']); - // final usersData = await getUserDetails(userId); - // - final costcenterId = int.tryParse( - cardObject['cost_center_id'] - .toString()); - - if (costcenterId != null) { - print("costcenterId -- $costcenterId"); - final data = await apiService - .getCostCenterDetailsFind(costcenterId); - print("CostCenterId -- $data"); - - showDialog( - context: context, - builder: (context) => CostCenterData( - isDesktop: isDesktop, - costcenterId:costcenterId, // Pass the ID - costcenterData:data, - layoutColor:layoutColor!, - // fetchGetCostCenter: fetchGetCostCenter, - fetchGetCostCenter: refreshData, - // role: - // "Travel Agent" - ), - ); - } else { - print("Invalid ID"); - } - }, - ), - // 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: () { - // print( - // "USerDAta - $user"); - // // dynamic usersData = apiService - // // .getSingleUser(user[ - // // 'user_id'] - // // is String - // // ? int.parse(user[ - // // 'user_id']) - // // : user[ - // // 'user_id']); - // // - // // print( - // // "USerDAta - $usersData"); - // - // 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 - // Name - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - cardObject['description'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - SizedBox( - width: 10, - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - cardObject['description'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - ], - ), - // Actions - // Actions + // 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: () { + // print( + // "USerDAta - $user"); + // // dynamic usersData = apiService + // // .getSingleUser(user[ + // // 'user_id'] + // // is String + // // ? int.parse(user[ + // // 'user_id']) + // // : user[ + // // 'user_id']); + // // + // // print( + // // "USerDAta - $usersData"); + // + // 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 + // }, + // ); + // }, + // ), + // ], + // ), + // ), + // ), + // ], + // ), ], ), - ), - ); - }, - ); - } - - return Expanded( - child: Column( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: isDesktop - ? (searchController.text.isNotEmpty && - filteredCostCenter.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) - : (searchController.text.isNotEmpty && - filteredCostCenter.isEmpty - ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), - ), - ) - : buildMobileCardView(paginatedCostCenter)), - ), - // Expanded( - // child: isDesktop - // ? SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: table, // <-- your existing table - // ) - // : buildMobileCardView(paginatedCostCenter), - // ), - PaginationControls( - currentPage: currentPage, - itemsPerPage: itemsPerPage, - totalItems: object.length, - activeColor: layoutColor, // your theme color - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, - onItemsPerPageChanged: (items) { - setState(() { - itemsPerPage = items; - currentPage = 0; - }); - }, + SizedBox(height: 2), + // Trip Id and Trip Name + // Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + cardObject['description'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + SizedBox(width: 10), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + cardObject['description'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + ], + ), + // Actions + // Actions + ], ), - ], + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop + ? (searchController.text.isNotEmpty && + filteredCostCenter.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredCostCenter.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView( + paginatedCostCenter, + )), ), - ); - }, - ) - ]), - )), + // Expanded( + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedCostCenter), + // ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: object.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), ); } -} \ No newline at end of file +} diff --git a/lib/Screens/dashboard/status_dashboard.dart b/lib/Screens/dashboard/status_dashboard.dart index 23227f1..9ec8522 100644 --- a/lib/Screens/dashboard/status_dashboard.dart +++ b/lib/Screens/dashboard/status_dashboard.dart @@ -195,65 +195,78 @@ class StatusDashboardState extends State { 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: 10, // 5% of screen height as vertical padding - ) - : EdgeInsets.all(0), - child:Container( - // padding: const EdgeInsets.all(10.0), - decoration: BoxDecoration( - color: isDesktop ? Colors.white : const Color(0xFFFCFCFC), - borderRadius: BorderRadius.circular(12), // 👈 Set your desired radius - ), - // color: isDesktop ? Colors.white : Color(0xFFFCFCFC), - child: Row( - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.all(30.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Wrap( - spacing: 10, - runSpacing: 10, - children: typeBasedCount.map((item) { - double cardWidth = isDesktop - ? (MediaQuery.of(context).size.width * 0.75 - 10) / 2 // 80% width padding adjusted - : MediaQuery.of(context).size.width - 24; // full width with padding + padding: isDesktop + ? EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.1, // 30% of screen width as horizontal padding + vertical: 10, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(0), + child : LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( // Only needed if child layout depends on height + child: Container( + decoration: BoxDecoration( + color: isDesktop ? Colors.white : const Color(0xFFFCFCFC), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(30.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Wrap( + spacing: 10, + runSpacing: 10, + children: typeBasedCount.map((item) { + double cardWidth = isDesktop + ? (MediaQuery.of(context).size.width * 0.75 - 10) / 2 // 80% width padding adjusted + : MediaQuery.of(context).size.width - 24; // full width with padding - return SizedBox( - width: cardWidth, - child: buildInfoCard(item['value'], item['count'], cardWidth), - ); - }).toList(), + return SizedBox( + width: cardWidth, + child: buildInfoCard(item['value'], item['count'], cardWidth), + ); + }).toList(), + ), + const SizedBox(height: 20), + Wrap( + spacing: 10, + runSpacing: 10, + children: statusBasedCount.map((item) { + double cardWidth = isDesktop + ? (MediaQuery.of(context).size.width * 0.90 - 50) / 6 // desktop layout: 6 cards per row + : MediaQuery.of(context).size.width - 24; // mobile: full width + + return SizedBox( + width: cardWidth, + child: buildInfoCard(item['value'], item['count'], cardWidth), + ); + }).toList(), + ), + + ], + ), + ), + ), + ], + ), ), - const SizedBox(height: 20), - Wrap( - spacing: 10, - runSpacing: 10, - children: statusBasedCount.map((item) { - double cardWidth = isDesktop - ? (MediaQuery.of(context).size.width * 0.90 - 50) / 6 // desktop layout: 6 cards per row - : MediaQuery.of(context).size.width - 24; // mobile: full width - - return SizedBox( - width: cardWidth, - child: buildInfoCard(item['value'], item['count'], cardWidth), - ); - }).toList(), - ), - - ], + ), ), - ), - ), - ], - ), - ) + ); + }, + ) + + + ), ); }); diff --git a/lib/Screens/department/departmentDetails.dart b/lib/Screens/department/departmentDetails.dart index 078e395..faa0ca3 100644 --- a/lib/Screens/department/departmentDetails.dart +++ b/lib/Screens/department/departmentDetails.dart @@ -19,13 +19,14 @@ class DepartmentData extends StatefulWidget { final int? departmentId; // <-- Add this final Map? departmentData; - const DepartmentData( - {super.key, - required this.isDesktop, - this.layoutColor, - required this.fetchGetDepartment, - this.departmentId, - this.departmentData}); + const DepartmentData({ + super.key, + required this.isDesktop, + this.layoutColor, + required this.fetchGetDepartment, + this.departmentId, + this.departmentData, + }); @override DepartmentDataState createState() => DepartmentDataState(); @@ -49,10 +50,7 @@ class DepartmentDataState extends State { int? departmentDataId; late String isActive = "1"; - List dataHeader = [ - "name", - "description", - ]; + List dataHeader = ["name", "description"]; Map departmentDetails() { final data = { @@ -69,7 +67,6 @@ class DepartmentDataState extends State { void initState() { super.initState(); - apiData = null; for (var field in dataHeader) { controllers[field] = TextEditingController(); @@ -110,7 +107,6 @@ class DepartmentDataState extends State { }); } - void toggleStatus() { setState(() { isActive = isActive == "1" ? "0" : "1"; @@ -171,7 +167,9 @@ class DepartmentDataState extends State { apiUrldata = '$apiUrl/api/updateDepartment/$departmentDataId'; departmentData["department_id"] = departmentDataId.toString(); departmentData["updated_by"] = userId; - (departmentData.containsKey("created_by")) ? departmentData.remove("created_by") : '' ; + (departmentData.containsKey("created_by")) + ? departmentData.remove("created_by") + : ''; } else { print("for add Department id - null"); apiUrldata = '$apiUrl/api/createDepartment'; @@ -193,10 +191,10 @@ class DepartmentDataState extends State { }; final body = jsonEncode(departmentData); - final response = departmentDataId != null - ? await http.put(uri, headers: headers, body: body) - : await http.post(uri, headers: headers, body: body); - + final response = + departmentDataId != null + ? await http.put(uri, headers: headers, body: body) + : await http.post(uri, headers: headers, body: body); switch (response.statusCode) { case 200: @@ -217,7 +215,6 @@ class DepartmentDataState extends State { print("Failed to submit department. Status: ${response.statusCode}"); print("Error: ${response.body}"); } - } catch (e) { print(" Error submitting plan: $e"); } @@ -225,7 +222,6 @@ class DepartmentDataState extends State { @override Widget build(BuildContext context) { - return AlertDialog( backgroundColor: Colors.white, contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30), @@ -238,27 +234,27 @@ class DepartmentDataState extends State { Row( children: [ Text( - (departmentDataId != null) ? 'Edit Department' : 'Create Department', + (departmentDataId != null) + ? 'Edit Department' + : 'Create Department', style: GoogleFonts.poppins(fontSize: 15, color: Colors.black), ), const Spacer(), ], ), const SizedBox(height: 2), - Divider( - thickness: 0.2, - color: Colors.blueGrey.shade100, - ), + Divider(thickness: 0.2, color: Colors.blueGrey.shade100), const SizedBox(height: 5), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Name", + "Name *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -266,19 +262,20 @@ class DepartmentDataState extends State { isDesktop: widget.isDesktop, color: Colors.transparent, child: SizedBox( - height: 40, - child: TextField( - controller: controllers["name"], - focusNode: focusNodes["name"], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Name", - labelStyle: TextStyle(fontSize: 11, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - )), + height: 40, + child: TextField( + controller: controllers["name"], + focusNode: focusNodes["name"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Name", + labelStyle: TextStyle(fontSize: 11, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), ), if (errorMessages["name"] != null) ...[ SizedBox(height: 5), // Space before error message @@ -289,18 +286,17 @@ class DepartmentDataState extends State { ], ], ), - SizedBox( - height: 15, - ), + SizedBox(height: 15), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Description", + "Description *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -334,41 +330,37 @@ class DepartmentDataState extends State { ], ], ), - SizedBox( - height: 15, - ), + SizedBox(height: 15), if (departmentDataId != null) - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Change Status ", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Change Status ", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), ), - Tooltip( - message: - isActive == "1" ? "Tap to deactivate" : "Tap to activate", - child: GestureDetector( - onTap: toggleStatus, - child: Text( - isActive == "1" ? "Active" : "Inactive", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - color: isActive == "1" ? Colors.green : Colors.red, - ), + ), + Tooltip( + message: + isActive == "1" ? "Tap to deactivate" : "Tap to activate", + child: GestureDetector( + onTap: toggleStatus, + child: Text( + isActive == "1" ? "Active" : "Inactive", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + color: isActive == "1" ? Colors.green : Colors.red, ), ), - ) - ], - ), - if (departmentDataId != null) - SizedBox( - height: 15, + ), + ), + ], ), + if (departmentDataId != null) SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -405,16 +397,20 @@ class DepartmentDataState extends State { borderRadius: BorderRadius.circular(8), ), ), - child: Text('Save', - style: GoogleFonts.poppins( - fontSize: 11, color: Colors.white)), + child: Text( + 'Save', + style: GoogleFonts.poppins( + fontSize: 11, + color: Colors.white, + ), + ), ), ), ], - ) + ), // : SizedBox.shrink(), ], ), ); } -} \ No newline at end of file +} diff --git a/lib/Screens/department/department_list.dart b/lib/Screens/department/department_list.dart index 63e0965..f7b63b1 100644 --- a/lib/Screens/department/department_list.dart +++ b/lib/Screens/department/department_list.dart @@ -67,13 +67,15 @@ class DepartmentListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -97,7 +99,7 @@ class DepartmentListState extends State { } Future> fetchGetDepartment() async { - final String apiUrlData = '$apiUrl/api/getDepartmentList'; + final String apiUrlData = '$apiUrl/api/getDepartmentList?for=table_view'; final String? token = await getToken(); @@ -141,50 +143,60 @@ class DepartmentListState extends State { print("all before filtering: $query"); final lowerQuery = query.toLowerCase(); setState(() { - filteredDepartment = allDepartment.where((object) { - final isActiveStatus = - object['is_active'] == "1" ? "active" : "inactive"; - return (object['department_id']?.toLowerCase().contains(lowerQuery) ?? - false) || - (object['name']?.toLowerCase().contains(lowerQuery) ?? false) || - (object['description']?.toLowerCase().contains(lowerQuery) ?? - false) || - (isActiveStatus.contains(lowerQuery)); - }).toList(); + filteredDepartment = + allDepartment.where((object) { + final isActiveStatus = + object['is_active'] == "1" ? "active" : "inactive"; + return (object['department_id']?.toLowerCase().contains( + lowerQuery, + ) ?? + false) || + (object['name']?.toLowerCase().contains(lowerQuery) ?? false) || + (object['description']?.toLowerCase().contains(lowerQuery) ?? + false) || + (isActiveStatus.contains(lowerQuery)); + }).toList(); + currentPage = 0; }); print("filteredDepartment: $filteredDepartment"); } @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Scaffold( - backgroundColor: Color(0xFFf5f5f5), - // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), - // 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 Page Content"))), - Expanded(child: buildGroupList(isDesktop)), - ], + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), + // 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 Page Content"))), + Expanded(child: buildGroupList(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupList(bool isDesktop) { @@ -212,98 +224,103 @@ class DepartmentListState extends State { // ? 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, + height: + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, child: Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + color: Colors.white, padding: const EdgeInsets.all(10.0), - child: Container( - color: Colors.white, - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Divider( - // thickness: 0.2, // how "thick" the line is - // color: Colors.grey, // optional - // ), Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Text( - 'Department Details', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - ], + Text( + 'Department Details', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), ), - if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.16, - ), + ], + ), + if (isDesktop) + SizedBox(width: MediaQuery.of(context).size.width * 0.16), - if (isDesktop) - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterDepartment, - decoration: InputDecoration( - hintText: "Search ...", - 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), - ), + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterDepartment, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), - style: GoogleFonts.poppins( - fontSize: 12, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, ), ), ), - // SizedBox(width: 16), - Spacer(), + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + // SizedBox(width: 16), + Spacer(), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF114D8B), - foregroundColor: Colors.white, - disabledBackgroundColor: Color(0xFF114D8B), - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: - BorderSide(color: Color(0xFF114D8B), width: 2), - ), - padding: EdgeInsets.symmetric( - horizontal: 20, vertical: 12), - ), - onPressed: () async { - showDialog( - context: context, - builder: (context) => DepartmentData( + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: EdgeInsets.symmetric( + horizontal: 20, + vertical: 12, + ), + ), + onPressed: () async { + showDialog( + context: context, + builder: + (context) => DepartmentData( isDesktop: isDesktop, layoutColor: layoutColor!, fetchGetDepartment: refreshData, @@ -311,338 +328,380 @@ class DepartmentListState extends State { // role: // "Travel Agent" ), - ); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add Department", - style: GoogleFonts.poppins( - fontSize: isDesktop ? 13 : 11, + ); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add Department", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 13 : 11, + ), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], + ), + ), + ], + ), + + 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: filterDepartment, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, + 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: futureDepartment, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // const Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // const SizedBox(height: 16), + // Text( + // "Oops!", + // style: GoogleFonts.poppins( + // fontSize: 20, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent), + // ), + const SizedBox(height: 8), + Text( + "No Department Available ", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + Text( + "Please Create Department Details", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), ], ), ), - ], - ), - - 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: filterDepartment, - decoration: InputDecoration( - hintText: "Search ...", - 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: futureDepartment, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError || - !snapshot.hasData || - snapshot.data!.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // const Icon(Icons.error_outline, - // color: Colors.redAccent, size: 60), - // const SizedBox(height: 16), - // Text( - // "Oops!", - // style: GoogleFonts.poppins( - // fontSize: 20, - // fontWeight: FontWeight.bold, - // color: Colors.redAccent), - // ), - const SizedBox(height: 8), - Text( - "No Department Available ", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey), - ), - const SizedBox(height: 20), - Text( - "Please Create Department Details", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 16, color: Colors.grey), - ), - const SizedBox(height: 20), - ], - ), - ), - ); - } - /* Here collect the list to displayed the data in table or card Used */ - List object = filteredDepartment.isNotEmpty + ); + } + /* Here collect the list to displayed the data in table or card Used */ + List object = + filteredDepartment.isNotEmpty ? filteredDepartment : allDepartment; - /* List is Sorting here */ - object.sort((a, b) { - DateTime dateA = DateTime.parse(a['created_on']); - DateTime dateB = DateTime.parse(b['created_on']); + /* List is Sorting here */ + object.sort((a, b) { + DateTime dateA = DateTime.parse(a['created_on']); + DateTime dateB = DateTime.parse(b['created_on']); - return dateB - .compareTo(dateA); // Descending: newest first - }); + return dateB.compareTo(dateA); // Descending: newest first + }); - /* For pagination for list ... */ - List paginatedDepartment = object + /* For pagination for list ... */ + List paginatedDepartment = + object .skip(currentPage * itemsPerPage) .take(itemsPerPage) .toList(); - /* Table ... */ - Widget table = LayoutBuilder( - builder: (context, constraints) { - double minWidth = - isDesktop ? constraints.maxWidth : 1300; + /* Table ... */ + Widget table = LayoutBuilder( + builder: (context, constraints) { + double minWidth = isDesktop ? constraints.maxWidth : 1300; - return ConstrainedBox( - constraints: BoxConstraints(minWidth: minWidth), - child: DataTable( - dividerThickness: 0.5, - columnSpacing: isDesktop ? 24.0 : 16.0, - border: TableBorder( - horizontalInside: BorderSide( - width: 0.5, color: Colors.grey.shade200), + return ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + dividerThickness: 0.5, + columnSpacing: isDesktop ? 24.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, + ), + ), + columns: [ + DataColumn( + label: Text( + 'Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), ), - columns: [ - DataColumn( - label: Text( - 'Name', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Description', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Actions', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - ], - rows: paginatedDepartment.map((tableObject) { + ), + DataColumn( + label: Text( + 'Description', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + paginatedDepartment.map((tableObject) { String departmentId = tableObject['department_id'] .toString(); // Get user ID bool isSelected = selectedDepartmentId == departmentId; - return DataRow(cells: [ - DataCell(Text(tableObject['name'] ?? '', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell( - Text(tableObject['description'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell( - Text( - tableObject['is_active'] == "1" - ? 'Active' - : 'Inactive', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - color: tableObject['is_active'] == "1" - ? Colors.green - : Colors.red, + return DataRow( + cells: [ + DataCell( + Text( + tableObject['name'] ?? '', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), ), - softWrap: true, - overflow: TextOverflow.ellipsis, ), - ), - DataCell( - // UserActionsMenu( - // user: forex, - // getUserDetails: (id) => - // apiService.getSingleUser(id), - // ), + DataCell( + Text( + tableObject['description'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + tableObject['is_active'] == "1" + ? 'Active' + : 'Inactive', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + color: + tableObject['is_active'] == "1" + ? Colors.green + : Colors.grey, + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + // UserActionsMenu( + // user: forex, + // getUserDetails: (id) => + // apiService.getSingleUser(id), + // ), + GestureDetector( + child: Tooltip( + message: 'Edit Department Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + onTap: () async { + // final userId = getUserId(user['user_id']); + // final usersData = await getUserDetails(userId); + // + final departmentId = int.tryParse( + tableObject['department_id'] + .toString(), + ); + + if (departmentId != null) { + print( + "Table cell - department Id -- $departmentId", + ); + final data = await apiService + .getDepartmentDetailsFind( + departmentId, + ); + print("DepartmentId -- $data"); + + showDialog( + context: context, + builder: + (context) => DepartmentData( + isDesktop: isDesktop, + departmentId: + departmentId, // Pass the ID + departmentData: data, + layoutColor: layoutColor!, + // fetchGetForex: fetchGetForex, + fetchGetDepartment: + refreshData, + // role: + // "Travel Agent" + ), + ); + } else { + print("Invalid ID"); + } + }, + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + + /* Card ... */ + Widget buildMobileCardView(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final cardObject = 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( + cardObject['name'] ?? 'N/A', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.w700, + ), + ), + GestureDetector( - child: Image.asset( + child: Tooltip( + message: 'Edit Department Details', + child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, - height: 15), + height: 15, + ), + ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); // final departmentId = int.tryParse( - tableObject['department_id'] - .toString()); + cardObject['department_id'] + .toString(), + ); if (departmentId != null) { print( - "Table cell - department Id -- $departmentId"); + "departmentId -- $departmentId", + ); final data = await apiService .getDepartmentDetailsFind( - departmentId); + departmentId, + ); print("DepartmentId -- $data"); showDialog( context: context, - builder: (context) => - DepartmentData( - isDesktop: isDesktop, - departmentId: - departmentId, // Pass the ID - departmentData: data, - layoutColor: layoutColor!, - // fetchGetForex: fetchGetForex, - fetchGetDepartment: refreshData, - // role: - // "Travel Agent" - ), - ); - } else { - print("Invalid ID"); - } - }, - ), - ), - ]); - }).toList(), - ), - ); - }, - ); - - /* Card ... */ - Widget buildMobileCardView(List paginatedUser) { - return ListView.builder( - itemCount: paginatedUser.length, - itemBuilder: (context, index) { - final cardObject = 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( - cardObject['name'] ?? 'N/A', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - fontWeight: FontWeight.w700), - ), - - GestureDetector( - child: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - onTap: () async { - // final userId = getUserId(user['user_id']); - // final usersData = await getUserDetails(userId); - // - final departmentId = int.tryParse( - cardObject['department_id'] - .toString()); - - if (departmentId != null) { - print( - "departmentId -- $departmentId"); - final data = await apiService - .getDepartmentDetailsFind( - departmentId); - print("DepartmentId -- $data"); - - showDialog( - context: context, - builder: (context) => - DepartmentData( + builder: + (context) => DepartmentData( isDesktop: isDesktop, departmentId: departmentId, // Pass the ID @@ -654,197 +713,203 @@ class DepartmentListState extends State { // role: // "Travel Agent" ), - ); - } else { - print("Invalid ID"); - } - }, - ), - // 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: () { - // print( - // "USerDAta - $user"); - // // dynamic usersData = apiService - // // .getSingleUser(user[ - // // 'user_id'] - // // is String - // // ? int.parse(user[ - // // 'user_id']) - // // : user[ - // // 'user_id']); - // // - // // print( - // // "USerDAta - $usersData"); - // - // 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 - // }, - // ); - // }, - // ), - // ], - // ), - // ), - // ), - // ], - // ), - ], + ); + } else { + print("Invalid ID"); + } + }, ), - - SizedBox(height: 2), - // Trip Id and Trip Name - // Name - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - cardObject['description'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - SizedBox( - width: 10, - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - cardObject['description'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - ], - ), - // Actions - // Actions + // 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: () { + // print( + // "USerDAta - $user"); + // // dynamic usersData = apiService + // // .getSingleUser(user[ + // // 'user_id'] + // // is String + // // ? int.parse(user[ + // // 'user_id']) + // // : user[ + // // 'user_id']); + // // + // // print( + // // "USerDAta - $usersData"); + // + // 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 + // }, + // ); + // }, + // ), + // ], + // ), + // ), + // ), + // ], + // ), ], ), - ), - ); - }, - ); - } - return Expanded( - child: Column( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: isDesktop + SizedBox(height: 2), + // Trip Id and Trip Name + // Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + cardObject['description'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + SizedBox(width: 10), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + cardObject['description'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + ], + ), + // Actions + // Actions + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop ? (searchController.text.isNotEmpty && filteredDepartment.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) + scrollDirection: Axis.vertical, + child: table, + )) : (searchController.text.isNotEmpty && filteredDepartment.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : buildMobileCardView( - paginatedDepartment)), - ), - // Expanded( - // child: isDesktop - // ? SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: table, // <-- your existing table - // ) - // : buildMobileCardView(paginatedDepartment), - // ), - PaginationControls( - currentPage: currentPage, - itemsPerPage: itemsPerPage, - totalItems: object.length, - activeColor: layoutColor, // your theme color - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, - onItemsPerPageChanged: (items) { - setState(() { - itemsPerPage = items; - currentPage = 0; - }); - }, - ), - ], + paginatedDepartment, + )), ), - ); - }, - ) - ]), - )), + // Expanded( + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedDepartment), + // ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: object.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), ); } } diff --git a/lib/Screens/dialog/user_selection_dialog.dart b/lib/Screens/dialog/user_selection_dialog.dart index 9427b0c..2495a8b 100644 --- a/lib/Screens/dialog/user_selection_dialog.dart +++ b/lib/Screens/dialog/user_selection_dialog.dart @@ -100,11 +100,13 @@ class _UserSelectionDialogState extends State { } } else { throw Exception( - "Unexpected response format: Expected a List but got ${responseBody.runtimeType}"); + "Unexpected response format: Expected a List but got ${responseBody.runtimeType}", + ); } } else { throw Exception( - 'Failed to load users. Status Code: ${response.statusCode}'); + 'Failed to load users. Status Code: ${response.statusCode}', + ); } } catch (e) { print("Error fetching users: $e"); @@ -138,56 +140,63 @@ class _UserSelectionDialogState extends State { List travellerList = responseBody['data']; setState(() { - _traveller = travellerList - .map((user) => SearchTraveler.fromJson(user)) - .toList(); + _traveller = + travellerList + .map((user) => SearchTraveler.fromJson(user)) + .toList(); _filteredTraveller = List.from(_traveller); }); print("Users fetched: ${_users.length}"); for (var travvelr in _traveller) { print( - "${travvelr.firstName} ${travvelr.lastName} ${travvelr.mobileNo}"); + "${travvelr.firstName} ${travvelr.lastName} ${travvelr.mobileNo}", + ); } } else { throw Exception( - "Unexpected response format: Expected a List but got ${responseBody.runtimeType}"); + "Unexpected response format: Expected a List but got ${responseBody.runtimeType}", + ); } } else { throw Exception( - 'Failed to load users. Status Code: ${response.statusCode}'); + 'Failed to load users. Status Code: ${response.statusCode}', + ); } } catch (e) { print("Error fetching traveller: $e"); } } - void _filterUsers1(String query) { - print("Filtering users..."); - setState(() { - if (query.isEmpty) { - _filteredUsers = List.from(_users); - } else { - _filteredUsers = _users.where((user) { - List searchFields = [ - "${user.firstName} ${user.lastName}".toLowerCase(), - user.email.toLowerCase() ?? "", - user.userId.toLowerCase() ?? "", - user.mobileNo ?? "", - user.alternateMobileNo ?? "" - ]; - - return searchFields - .any((field) => field.contains(query.toLowerCase())); - }).toList(); - } - }); - - print("Filtered Users:"); - for (var user in _filteredUsers) { - print("${user.firstName} ${user.lastName}"); - } - } + // void _filterUsers1(String query) { + // print("Filtering users..."); + // setState(() { + // if (query.isEmpty) { + // _filteredUsers = List.from(_users); + // } else { + // _filteredUsers = + // _users.where((user) { + // List searchFields = [ + // "${user.firstName} ${user.lastName}".toLowerCase(), + // user.email.toLowerCase() ?? "", + // user.empCode?.toLowerCase() ?? "", + // user.userId.toLowerCase() ?? "", + // user.mobileNo ?? "", + // user.alternateMobileNo ?? "", + // ]; + // + // return searchFields.any( + // (field) => field.contains(query.toLowerCase()), + // ); + // }).toList(); + // } + // }); + // + // print("Filtered Users:"); + // for (var user in _filteredUsers) { + // print("${user.firstName} ${user.lastName}"); + // } + // } void _filterUsers(String query) { print("Filtering _filterUsersTravellers..."); @@ -200,19 +209,23 @@ class _UserSelectionDialogState extends State { ]; } else { _filteredList = [ - ..._users.where((user) { - print("usersLLL : ${user}"); + ..._users + .where((user) { + print("usersLLL : ${user}"); - List searchFields = [ - "${user.firstName} ${user.lastName}".toLowerCase(), - user.email.toLowerCase() ?? "", - user.userId.toLowerCase() ?? "", - user.mobileNo ?? "", - user.alternateMobileNo ?? "" - ]; - return searchFields - .any((field) => field.contains(query.toLowerCase())); - }).map((user) => {"type": "user", "data": user}), + List searchFields = [ + "${user.firstName} ${user.lastName}".toLowerCase(), + user.email.toLowerCase() ?? "", + user.userId.toLowerCase() ?? "", + user.mobileNo ?? "", + user.alternateMobileNo ?? "", + user.empCode?.toLowerCase() ?? "", + ]; + return searchFields.any( + (field) => field.contains(query.toLowerCase()), + ); + }) + .map((user) => {"type": "user", "data": user}), ]; } }); @@ -221,7 +234,8 @@ class _UserSelectionDialogState extends State { for (var item in _filteredList) { var user = item["data"]; print( - "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); + "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}", + ); } } @@ -236,16 +250,19 @@ class _UserSelectionDialogState extends State { ]; } else { _filteredList = [ - ..._traveller.where((traveller) { - List searchFields = [ - "${traveller.firstName} ${traveller.lastName}".toLowerCase(), - traveller.email.toLowerCase() ?? "", - traveller.travellerId.toLowerCase() ?? "", - traveller.mobileNo ?? "", - ]; - return searchFields - .any((field) => field.contains(query.toLowerCase())); - }).map((traveller) => {"type": "traveller", "data": traveller}), + ..._traveller + .where((traveller) { + List searchFields = [ + "${traveller.firstName} ${traveller.lastName}".toLowerCase(), + traveller.email.toLowerCase() ?? "", + traveller.travellerId.toLowerCase() ?? "", + traveller.mobileNo ?? "", + ]; + return searchFields.any( + (field) => field.contains(query.toLowerCase()), + ); + }) + .map((traveller) => {"type": "traveller", "data": traveller}), ]; } }); @@ -254,7 +271,8 @@ class _UserSelectionDialogState extends State { for (var item in _filteredList) { var user = item["data"]; print( - "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); + "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}", + ); } } @@ -266,32 +284,39 @@ class _UserSelectionDialogState extends State { if (query.isEmpty) { _filteredList = [ ..._users.map((user) => {"type": "user", "data": user}), - ..._traveller - .map((traveller) => {"type": "traveller", "data": traveller}), + ..._traveller.map( + (traveller) => {"type": "traveller", "data": traveller}, + ), ]; } else { _filteredList = [ - ..._users.where((user) { - List searchFields = [ - "${user.firstName} ${user.lastName}".toLowerCase(), - user.email.toLowerCase() ?? "", - user.userId.toLowerCase() ?? "", - user.mobileNo ?? "", - user.alternateMobileNo ?? "" - ]; - return searchFields - .any((field) => field.contains(query.toLowerCase())); - }).map((user) => {"type": "user", "data": user}), - ..._traveller.where((traveller) { - List searchFields = [ - "${traveller.firstName} ${traveller.lastName}".toLowerCase(), - traveller.email.toLowerCase() ?? "", - traveller.travellerId.toLowerCase() ?? "", - traveller.mobileNo ?? "", - ]; - return searchFields - .any((field) => field.contains(query.toLowerCase())); - }).map((traveller) => {"type": "traveller", "data": traveller}), + ..._users + .where((user) { + List searchFields = [ + "${user.firstName} ${user.lastName}".toLowerCase(), + user.email.toLowerCase() ?? "", + user.userId.toLowerCase() ?? "", + user.mobileNo ?? "", + user.alternateMobileNo ?? "", + ]; + return searchFields.any( + (field) => field.contains(query.toLowerCase()), + ); + }) + .map((user) => {"type": "user", "data": user}), + ..._traveller + .where((traveller) { + List searchFields = [ + "${traveller.firstName} ${traveller.lastName}".toLowerCase(), + traveller.email.toLowerCase() ?? "", + traveller.travellerId.toLowerCase() ?? "", + traveller.mobileNo ?? "", + ]; + return searchFields.any( + (field) => field.contains(query.toLowerCase()), + ); + }) + .map((traveller) => {"type": "traveller", "data": traveller}), ]; } }); @@ -300,7 +325,8 @@ class _UserSelectionDialogState extends State { for (var item in _filteredList) { var user = item["data"]; print( - "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); + "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}", + ); } } @@ -323,11 +349,15 @@ class _UserSelectionDialogState extends State { mainAxisSize: MainAxisSize.min, // Ensures content doesn't expand unnecessarily children: [ - widget.title == "Others" - ? Text("Please Select Other User", - style: GoogleFonts.poppins(fontSize: 14)) - : Text("Please Select Other Employee", - style: GoogleFonts.poppins(fontSize: 14)), + widget.title == "Others (Non Employee)" + ? Text( + "Please Select Other User", + style: GoogleFonts.poppins(fontSize: 14), + ) + : Text( + "Please Select Other Employee", + style: GoogleFonts.poppins(fontSize: 14), + ), SizedBox(height: 10), // Search Field @@ -337,18 +367,21 @@ class _UserSelectionDialogState extends State { setState(() { _showTravellerForm = false; }); - widget.title == "Others" + widget.title == "Others (Non Employee)" ? _filterTravellers(query) : _filterUsers(query); }, style: GoogleFonts.poppins(fontSize: 12), decoration: InputDecoration( hintText: "Search for a user", - hintStyle: - GoogleFonts.poppins(fontSize: 14, color: Colors.grey), + hintStyle: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), prefixIcon: Icon(Icons.search), - border: - OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.grey.shade200, width: 1), // borderSide: BorderSide(color: Color(0xFFF5F5F5), width: 2), @@ -362,13 +395,17 @@ class _UserSelectionDialogState extends State { SizedBox(height: 10), - if (widget.title == "Others") ...[ + if (widget.title == "Others (Non Employee)") ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text("or create a new traveler", - style: GoogleFonts.poppins( - fontSize: 14, color: Color(0xFF575A74))), + Text( + "or create a new traveler", + style: GoogleFonts.poppins( + fontSize: 14, + color: Color(0xFF575A74), + ), + ), TextButton( onPressed: () { setState(() { @@ -376,9 +413,13 @@ class _UserSelectionDialogState extends State { _searchController.clear(); }); }, - child: Text("Create", - style: GoogleFonts.poppins( - fontSize: 14, color: widget.layoutColorForUser)), + child: Text( + "Create", + style: GoogleFonts.poppins( + fontSize: 14, + color: widget.layoutColorForUser, + ), + ), ), ], ), @@ -389,17 +430,20 @@ class _UserSelectionDialogState extends State { // User List or Message _searchController.text.isNotEmpty ? SizedBox( - height: 300, // Limit height to avoid overflow - // child: _filteredUsers.isEmpty - child: _filteredList.isEmpty - ? Center( + height: 300, // Limit height to avoid overflow + // child: _filteredUsers.isEmpty + child: + _filteredList.isEmpty + ? Center( child: Text( "No users found", style: GoogleFonts.poppins( - fontSize: 14, color: Colors.grey), + fontSize: 14, + color: Colors.grey, + ), ), ) - : ListView.builder( + : ListView.builder( // itemCount: _filteredUsers.length, itemCount: _filteredList.length, itemBuilder: (context, index) { @@ -411,7 +455,8 @@ class _UserSelectionDialogState extends State { item["type"]; // "user" or "traveller" if (user is Map) { print( - "userLsirer - ${jsonEncode(user)}"); // pretty JSON-like string + "userLsirer - ${jsonEncode(user)}", + ); // pretty JSON-like string } else { print("userLsirer - $user"); // fallback } @@ -420,37 +465,42 @@ class _UserSelectionDialogState extends State { "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}", style: GoogleFonts.poppins(fontSize: 11), ), - subtitle: userType == "user" - ? Text( - "Employee ID: ${user.empCode ?? ""} ", - // "Employee ID: ${userType == "user" ? user.userId : user.travellerId}", - style: - GoogleFonts.poppins(fontSize: 10), - ) - : Text( - "Mobile : ${user.mobileNo ?? ""} ", - // "Employee ID: ${userType == "user" ? user.userId : user.travellerId}", - style: - GoogleFonts.poppins(fontSize: 10), - ), + subtitle: + userType == "user" + ? Text( + "Employee ID: ${user.empCode ?? ""} ", + // "Employee ID: ${userType == "user" ? user.userId : user.travellerId}", + style: GoogleFonts.poppins( + fontSize: 10, + ), + ) + : Text( + "Mobile : ${user.mobileNo ?? ""} ", + // "Employee ID: ${userType == "user" ? user.userId : user.travellerId}", + style: GoogleFonts.poppins( + fontSize: 10, + ), + ), onTap: () { String selectedUser = "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"; setState(() { _searchController.text = selectedUser; - userIdSelected = userType == "user" - ? user.userId - : user.travellerId; + userIdSelected = + userType == "user" + ? user.userId + : user.travellerId; isTraveller = userType == "traveller"; }); print( - "Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId}," - " isTraveller: $userIdSelected"); + "Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId}," + " isTraveller: $userIdSelected", + ); }, ); }, ), - ) + ) : SizedBox.shrink(), // Traveler Form @@ -462,10 +512,16 @@ class _UserSelectionDialogState extends State { padding: const EdgeInsets.all(16.0), child: TravelerForm( formKey: _formKey, - onSubmit: (String fullName, String travellerId, - bool isTraveller) { - widget.onSubmit(fullName, travellerId, - isTraveller); // Pass the data up + onSubmit: ( + String fullName, + String travellerId, + bool isTraveller, + ) { + widget.onSubmit( + fullName, + travellerId, + isTraveller, + ); // Pass the data up }, firstNameController: TextEditingController(), lastNameController: TextEditingController(), @@ -487,7 +543,9 @@ class _UserSelectionDialogState extends State { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide( - color: widget.layoutColorForUser, width: 2), + color: widget.layoutColorForUser, + width: 2, + ), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), @@ -508,15 +566,21 @@ class _UserSelectionDialogState extends State { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide( - color: widget.layoutColorForUser, width: 2), + color: widget.layoutColorForUser, + width: 2, + ), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: () { print( - "Submitting: ${_searchController.text}, ID: $userIdSelected"); + "Submitting: ${_searchController.text}, ID: $userIdSelected", + ); widget.onSubmit( - _searchController.text, userIdSelected, isTraveller); + _searchController.text, + userIdSelected, + isTraveller, + ); Navigator.pop(context); }, child: Text( @@ -542,14 +606,15 @@ class TravelerForm extends StatefulWidget { final GlobalKey formKey; final void Function(String, String, bool) onSubmit; - TravelerForm( - {required this.formKey, - required this.orgId, - required this.firstNameController, - required this.lastNameController, - required this.emailController, - required this.mobileController, - required this.onSubmit}); + TravelerForm({ + required this.formKey, + required this.orgId, + required this.firstNameController, + required this.lastNameController, + required this.emailController, + required this.mobileController, + required this.onSubmit, + }); @override _TravelerFormState createState() => _TravelerFormState(); @@ -582,8 +647,9 @@ class _TravelerFormState extends State { if (value == null || value.isEmpty) { return 'Email is required'; } - if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$') - .hasMatch(value)) { + if (!RegExp( + r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', + ).hasMatch(value)) { return 'Enter a valid email address'; } return null; @@ -642,7 +708,8 @@ class _TravelerFormState extends State { String lastName = travellerData["last_name"]; print( - "Traveller Added: ID: $travellerId, Name: $firstName $lastName"); + "Traveller Added: ID: $travellerId, Name: $firstName $lastName", + ); // // Pass data to callback // widget.onSubmit("$firstName $lastName", travellerId, true); @@ -658,21 +725,22 @@ class _TravelerFormState extends State { SnackBar( content: Text( "Traveller added successfully!", - style: - GoogleFonts.poppins(color: Colors.white), // ✅ Set text color + style: GoogleFonts.poppins( + color: Colors.white, + ), // ✅ Set text color ), backgroundColor: Colors.green, ), ); } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Error: ${response.body}")), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text("Error: ${response.body}"))); } } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Failed to connect to server.")), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text("Failed to connect to server."))); } } @@ -697,8 +765,10 @@ class _TravelerFormState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text("Create Traveler", - style: GoogleFonts.poppins(color: Colors.black54)), + Text( + "Create Traveler", + style: GoogleFonts.poppins(color: Colors.black54), + ), SizedBox(height: 7), Expanded( child: SingleChildScrollView( @@ -714,13 +784,17 @@ class _TravelerFormState extends State { onPressed: () { widget.formKey.currentState?.reset(); }, - child: Text("Clear", - style: GoogleFonts.poppins(color: Colors.grey)), + child: Text( + "Clear", + style: GoogleFonts.poppins(color: Colors.grey), + ), ), TextButton( onPressed: () => _onSubmit(context), - child: Text("Add", - style: GoogleFonts.poppins(color: Color(0xFF114D8B))), + child: Text( + "Add", + style: GoogleFonts.poppins(color: Color(0xFF114D8B)), + ), ), ], ), diff --git a/lib/Screens/forex/forexDetails.dart b/lib/Screens/forex/forexDetails.dart index 45230f8..44c60bc 100644 --- a/lib/Screens/forex/forexDetails.dart +++ b/lib/Screens/forex/forexDetails.dart @@ -21,13 +21,14 @@ class ForexData extends StatefulWidget { final int? forexId; // <-- Add this final Map? forexData; - const ForexData( - {super.key, - required this.isDesktop, - this.layoutColor, - required this.fetchGetForex, - this.forexId, - this.forexData}); + const ForexData({ + super.key, + required this.isDesktop, + this.layoutColor, + required this.fetchGetForex, + this.forexId, + this.forexData, + }); @override ForexDataState createState() => ForexDataState(); @@ -61,7 +62,7 @@ class ForexDataState extends State { "currency", "perdiemAmount", "cash", - "card" + "card", ]; Map forex_Detials() { @@ -197,7 +198,7 @@ class ForexDataState extends State { "currency", "perdiemAmount", "cash_percentage", - "card_percentage" + "card_percentage", ]; // Check validation for each field @@ -273,9 +274,10 @@ class ForexDataState extends State { }; final body = jsonEncode(forexData); - final response = forexDataId != null - ? await http.put(uri, headers: headers, body: body) - : await http.post(uri, headers: headers, body: body); + final response = + forexDataId != null + ? await http.put(uri, headers: headers, body: body) + : await http.post(uri, headers: headers, body: body); // final response = await http.post( // Uri.parse(apiUrldata), @@ -326,7 +328,7 @@ class ForexDataState extends State { // Map country codes to country names countryMap = { for (var item in countryList) - item['country_code'] as String: item['country_name'] as String + item['country_code'] as String: item['country_name'] as String, }; // Extract only country codes for processing @@ -355,21 +357,19 @@ class ForexDataState extends State { ], ), const SizedBox(height: 2), - Divider( - thickness: 0.2, - color: Colors.blueGrey.shade100, - ), + Divider(thickness: 0.2, color: Colors.blueGrey.shade100), const SizedBox(height: 5), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Country", + "Country *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -381,18 +381,19 @@ class ForexDataState extends State { selectedItem: countryMap[selectedCountry], popupProps: PopupProps.menu( showSearchBox: true, // Enables search functionality - menuProps: const MenuProps( - backgroundColor: Colors.white, - ), + menuProps: const MenuProps(backgroundColor: Colors.white), constraints: BoxConstraints(maxHeight: 250), - itemBuilder: (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 6.0), - child: Text( - item, - style: GoogleFonts.poppins(fontSize: 11.5), - ), - ), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search Country...", @@ -405,25 +406,25 @@ class ForexDataState extends State { dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 1, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: + (context, selectedItem) => Align( + // Center-align selected item + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select Country", + style: GoogleFonts.poppins(fontSize: 11), + ), ), - ), - ), - dropdownBuilder: (context, selectedItem) => Align( - // Center-align selected item - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select Country", - style: GoogleFonts.poppins(fontSize: 11), - ), - ), onChanged: (String? newValue) { setState(() { // Find the country_code based on selected country_name - selectedCountry = countryMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; + selectedCountry = + countryMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; selectedCountryName = newValue; }); }, @@ -444,11 +445,12 @@ class ForexDataState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Currency", + "Currency *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -459,18 +461,19 @@ class ForexDataState extends State { // ? MediaQuery.of(context).size.width * 0.330 // : MediaQuery.of(context).size.width * 0.66, child: SizedBox( - height: 40, - child: TextField( - controller: controllers["currency"], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Currency", - labelStyle: TextStyle(fontSize: 11, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - )), + height: 40, + child: TextField( + controller: controllers["currency"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Currency", + labelStyle: TextStyle(fontSize: 11, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), ), if (errorMessages["currency"] != null) ...[ SizedBox(height: 5), // Space before error message @@ -481,11 +484,9 @@ class ForexDataState extends State { ], ], ), - // if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,), - SizedBox( - height: 10, - ), + // if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,), + SizedBox(height: 10), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -493,38 +494,43 @@ class ForexDataState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Cash", + "Cash (%) *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( isFocused: false, isDesktop: widget.isDesktop, color: Colors.transparent, - width: widget.isDesktop - ? MediaQuery.of(context).size.width * 0.09 - : MediaQuery.of(context).size.width * 0.66, + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.09 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( - height: 40, - child: TextField( - controller: controllers["cash"], - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Cash", - labelStyle: - TextStyle(fontSize: 11, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), + height: 40, + child: TextField( + controller: controllers["cash"], + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Cash", + labelStyle: TextStyle( + fontSize: 11, + color: Colors.grey, ), - )), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), ), if (errorMessages["cash_percentage"] != null) ...[ SizedBox(height: 5), // Space before error message @@ -540,38 +546,43 @@ class ForexDataState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Card", + "Card (%) *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( isFocused: false, isDesktop: widget.isDesktop, color: Colors.transparent, - width: widget.isDesktop - ? MediaQuery.of(context).size.width * 0.09 - : MediaQuery.of(context).size.width * 0.66, + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.09 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( - height: 40, - child: TextField( - controller: controllers["card"], - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Card", - labelStyle: - TextStyle(fontSize: 11, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), + height: 40, + child: TextField( + controller: controllers["card"], + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Card", + labelStyle: TextStyle( + fontSize: 11, + color: Colors.grey, ), - )), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), ), if (errorMessages["card_percentage"] != null) ...[ SizedBox(height: 5), // Space before error message @@ -581,21 +592,20 @@ class ForexDataState extends State { ), ], ], - ) + ), ], ), - SizedBox( - height: 10, - ), + SizedBox(height: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Perdiem Amount", + "Perdiem Amount *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -603,18 +613,19 @@ class ForexDataState extends State { isDesktop: widget.isDesktop, color: Colors.transparent, child: SizedBox( - height: 40, - child: TextField( - controller: controllers["perdiemAmount"], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Perdiem Amount", - labelStyle: TextStyle(fontSize: 11, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - )), + height: 40, + child: TextField( + controller: controllers["perdiemAmount"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Perdiem Amount", + labelStyle: TextStyle(fontSize: 11, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), ), if (errorMessages["perdiemAmount"] != null) ...[ SizedBox(height: 5), // Space before error message @@ -625,9 +636,7 @@ class ForexDataState extends State { ], ], ), - SizedBox( - height: 15, - ), + SizedBox(height: 15), if (forexDataId != null) Row( @@ -636,9 +645,10 @@ class ForexDataState extends State { Text( "Change Status ", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), Tooltip( message: @@ -654,13 +664,10 @@ class ForexDataState extends State { ), ), ), - ) + ), ], ), - if (forexDataId != null) - SizedBox( - height: 15, - ), + if (forexDataId != null) SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -697,13 +704,17 @@ class ForexDataState extends State { borderRadius: BorderRadius.circular(8), ), ), - child: Text('Save', - style: GoogleFonts.poppins( - fontSize: 11, color: Colors.white)), + child: Text( + 'Save', + style: GoogleFonts.poppins( + fontSize: 11, + color: Colors.white, + ), + ), ), ), ], - ) + ), // : SizedBox.shrink(), ], ), diff --git a/lib/Screens/forex/forex_list.dart b/lib/Screens/forex/forex_list.dart index 58dae9b..64daa91 100644 --- a/lib/Screens/forex/forex_list.dart +++ b/lib/Screens/forex/forex_list.dart @@ -83,13 +83,15 @@ class ForexDataListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -100,7 +102,7 @@ class ForexDataListState extends State { Future> fetchGetForex() async { orgId = await getOrgId(); - final String apiUrlData = '$apiUrl/api/getForexPerdiemList'; + final String apiUrlData = '$apiUrl/api/getForexPerdiemList?for=table_view'; final String? token = await getToken(); @@ -151,7 +153,8 @@ class ForexDataListState extends State { if (!data.containsKey('data') || data['data'] is! List) { throw Exception( - "Invalid response format: 'data' field is missing or not a List"); + "Invalid response format: 'data' field is missing or not a List", + ); } List plansJson = data['data']; // 'data' is a Map, not a List @@ -180,7 +183,10 @@ class ForexDataListState extends State { } Future createUserData( - Map userData, String userId, String newStatus) async { + Map userData, + String userId, + String newStatus, + ) async { final uri = Uri.parse('$apiUrl/api/users/update/$userId'); final String? token = await getToken(); @@ -230,8 +236,11 @@ class ForexDataListState extends State { } } - void handleToggleUserStatus(String userId, String currentStatus, - Map userData) async { + void handleToggleUserStatus( + String userId, + String currentStatus, + Map userData, + ) async { print("Toggling user status - $userId (Current: $currentStatus)"); final String apiUrlData = @@ -274,7 +283,7 @@ class ForexDataListState extends State { // } } -// Refresh user list after update + // Refresh user list after update void refreshUserList() { setState(() { futureForex = fetchGetForex(); // Re-fetch users after status update @@ -282,73 +291,65 @@ class ForexDataListState extends State { }); } - void filterForex1(String query) { - print("allUsers before filtering: $query"); - final lowerQuery = query.toLowerCase(); - setState(() { - filteredForex = allForex.where((forex) { - return (forex['country_code']?.toLowerCase().contains(lowerQuery) ?? - false) || - (forex['country_name']?.toLowerCase().contains(lowerQuery) ?? - false) || - (forex['currency']?.toLowerCase().contains(lowerQuery) ?? false) || - (forex['perdiem_amount']?.toLowerCase().contains(lowerQuery) ?? - false); - }).toList(); - }); - print("filteredPlans: $filteredForex"); - } - void filterForex(String query) { print("allForex before filtering: $query"); final lowerQuery = query.toLowerCase(); setState(() { - filteredForex = allForex.where((forex) { - final isActiveStatus = - forex['is_active'] == "1" ? "active" : "inactive"; - return (forex['country_code']?.toLowerCase().contains(lowerQuery) ?? - false) || - (forex['country_name']?.toLowerCase().contains(lowerQuery) ?? - false) || - (forex['currency']?.toLowerCase().contains(lowerQuery) ?? false) || - (forex['perdiem_amount']?.toLowerCase().contains(lowerQuery) ?? - false) || - (isActiveStatus.contains(lowerQuery)); - }).toList(); + filteredForex = + allForex.where((forex) { + final isActiveStatus = + forex['is_active'] == "1" ? "active" : "inactive"; + return (forex['country_code']?.toLowerCase().contains(lowerQuery) ?? + false) || + (forex['country_name']?.toLowerCase().contains(lowerQuery) ?? + false) || + (forex['currency']?.toLowerCase().contains(lowerQuery) ?? + false) || + (forex['perdiem_amount']?.toLowerCase().contains(lowerQuery) ?? + false) || + (isActiveStatus.contains(lowerQuery)); + }).toList(); + currentPage = 0; }); print("filteredForex: $filteredForex"); } @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Scaffold( - backgroundColor: Color(0xFFf5f5f5), - // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), - // 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 Page Content"))), - Expanded(child: buildGroupList(isDesktop)), - ], + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), + // 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 Page Content"))), + Expanded(child: buildGroupList(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupList(bool isDesktop) { @@ -376,363 +377,492 @@ class ForexDataListState extends State { // ? 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, + height: + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, child: Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + color: Colors.white, padding: const EdgeInsets.all(10.0), - child: Container( - color: Colors.white, - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Divider( - // thickness: 0.2, // how "thick" the line is - // color: Colors.grey, // optional - // ), Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Text( - 'Perdiem Amount Details', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - ], + Text( + 'Perdiem Amount Details', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), ), - if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.16, - ), + ], + ), + if (isDesktop) + SizedBox(width: MediaQuery.of(context).size.width * 0.16), - if (isDesktop) - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterForex, - decoration: InputDecoration( - hintText: "Search ...", - 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), - ), + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterForex, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), - style: GoogleFonts.poppins( - fontSize: 12, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, ), ), ), - // SizedBox(width: 16), - Spacer(), + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + // SizedBox(width: 16), + Spacer(), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF114D8B), - foregroundColor: Colors.white, - disabledBackgroundColor: Color(0xFF114D8B), - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: - BorderSide(color: Color(0xFF114D8B), width: 2), - ), - padding: EdgeInsets.symmetric( - horizontal: 20, vertical: 12), - ), - onPressed: () async { - showDialog( - context: context, - builder: (context) => ForexData( + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: EdgeInsets.symmetric( + horizontal: 20, + vertical: 12, + ), + ), + onPressed: () async { + showDialog( + context: context, + builder: + (context) => ForexData( isDesktop: isDesktop, layoutColor: layoutColor!, fetchGetForex: refreshData, // role: // "Travel Agent" ), - ); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add Perdiem", - style: GoogleFonts.poppins( - fontSize: isDesktop ? 13 : 11, + ); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add Perdiem", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 13 : 11, + ), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], + ), + ), + ], + ), + + 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: filterForex, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, + 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: futureForex, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // const Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // const SizedBox(height: 16), + // Text( + // "Oops!", + // style: GoogleFonts.poppins( + // fontSize: 20, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent), + // ), + const SizedBox(height: 8), + Text( + "No Perdiem Available ", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + Text( + "Please Create Perdiem Amount", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), ], ), ), - ], - ), + ); + } - 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: filterForex, - decoration: InputDecoration( - hintText: "Search ...", - 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: futureForex, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError || - !snapshot.hasData || - snapshot.data!.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // const Icon(Icons.error_outline, - // color: Colors.redAccent, size: 60), - // const SizedBox(height: 16), - // Text( - // "Oops!", - // style: GoogleFonts.poppins( - // fontSize: 20, - // fontWeight: FontWeight.bold, - // color: Colors.redAccent), - // ), - const SizedBox(height: 8), - Text( - "No Perdiem Available ", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey), - ), - const SizedBox(height: 20), - Text( - "Please Create Perdiem Amount", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 16, color: Colors.grey), - ), - const SizedBox(height: 20), - ], - ), - ), - ); - } + List forex = + filteredForex.isNotEmpty ? filteredForex : allForex; - List forex = - filteredForex.isNotEmpty ? filteredForex : allForex; + forex.sort((a, b) { + DateTime dateA = DateTime.parse(a['created_on']); + DateTime dateB = DateTime.parse(b['created_on']); - forex.sort((a, b) { - DateTime dateA = DateTime.parse(a['created_on']); - DateTime dateB = DateTime.parse(b['created_on']); + return dateB.compareTo(dateA); // Descending: newest first + }); - return dateB - .compareTo(dateA); // Descending: newest first - }); - - List paginatedForex = forex + List paginatedForex = + forex .skip(currentPage * itemsPerPage) .take(itemsPerPage) .toList(); - Widget table = LayoutBuilder( - builder: (context, constraints) { - double minWidth = - isDesktop ? constraints.maxWidth : 1300; + Widget table = LayoutBuilder( + builder: (context, constraints) { + double minWidth = isDesktop ? constraints.maxWidth : 1300; - return ConstrainedBox( - constraints: BoxConstraints(minWidth: minWidth), - child: DataTable( - dividerThickness: 0.5, - columnSpacing: isDesktop ? 24.0 : 16.0, - border: TableBorder( - horizontalInside: BorderSide( - width: 0.5, color: Colors.grey.shade200), + return ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + dividerThickness: 0.5, + columnSpacing: isDesktop ? 24.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, + ), + ), + columns: [ + DataColumn( + label: Text( + 'Country Code', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), ), - columns: [ - DataColumn( - label: Text( - 'Country Code', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Country', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Currency', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Perdiem Amount', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Actions', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - ], - rows: paginatedForex.map((forex) { - String forexId = forex['forex_perdiem_id'] - .toString(); // Get user ID + ), + DataColumn( + label: Text( + 'Country', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Currency', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Perdiem Amount', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + paginatedForex.map((forex) { + String forexId = + forex['forex_perdiem_id'] + .toString(); // Get user ID bool isSelected = selectedUserId == forexId; - return DataRow(cells: [ - DataCell( - Text("${forex['country_code'] ?? ''}", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(forex['country_name'] ?? '', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(forex['currency'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", + return DataRow( + cells: [ + DataCell( + Text( + "${forex['country_code'] ?? ''}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell( - Text(forex['perdiem_amount'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell( - Text( - forex['is_active'] == "1" - ? 'Active' - : 'Inactive', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - // color: forex['is_active'] == "1" - // ? Colors.green - // : Colors.grey, - ), - softWrap: true, - overflow: TextOverflow.ellipsis, ), - ), - DataCell( - // UserActionsMenu( - // user: forex, - // getUserDetails: (id) => - // apiService.getSingleUser(id), - // ), + DataCell( + Text( + forex['country_name'] ?? '', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + forex['currency'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + forex['perdiem_amount'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + forex['is_active'] == "1" + ? 'Active' + : 'Inactive', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + // color: forex['is_active'] == "1" + // ? Colors.green + // : Colors.grey, + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + // UserActionsMenu( + // user: forex, + // getUserDetails: (id) => + // apiService.getSingleUser(id), + // ), + GestureDetector( + child: Tooltip( + message: 'Edit ForEx Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + onTap: () async { + // final userId = getUserId(user['user_id']); + // final usersData = await getUserDetails(userId); + // + final forexId = int.tryParse( + forex['forex_perdiem_id'] + .toString(), + ); + + if (forexId != null) { + print("ForexId -- $forexId"); + final data = await apiService + .getForexDetailsFind(forexId); + print("ForexId -- $data"); + + showDialog( + context: context, + builder: + (context) => ForexData( + isDesktop: isDesktop, + forexId: + forexId, // Pass the ID + forexData: data, + layoutColor: layoutColor!, + // fetchGetForex: fetchGetForex, + fetchGetForex: refreshData, + // role: + // "Travel Agent" + ), + ); + } else { + print("Invalid Forex ID"); + } + }, + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + + Widget buildMobileCardView(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final forex = 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( + forex['country_name'] ?? 'N/A', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.w700, + ), + ), + GestureDetector( - child: Image.asset( + child: Tooltip( + message: 'Edit Forex Details', + child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, - height: 15), + height: 15, + ), + ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); // final forexId = int.tryParse( - forex['forex_perdiem_id'] - .toString()); + forex['forex_perdiem_id'].toString(), + ); if (forexId != null) { print("ForexId -- $forexId"); @@ -742,83 +872,8 @@ class ForexDataListState extends State { showDialog( context: context, - builder: (context) => ForexData( - isDesktop: isDesktop, - forexId: forexId, // Pass the ID - forexData: data, - layoutColor: layoutColor!, - // fetchGetForex: fetchGetForex, - fetchGetForex: refreshData, - // role: - // "Travel Agent" - ), - ); - } else { - print("Invalid Forex ID"); - } - }, - ), - ), - ]); - }).toList(), - ), - ); - }, - ); - - Widget buildMobileCardView(List paginatedUser) { - return ListView.builder( - itemCount: paginatedUser.length, - itemBuilder: (context, index) { - final forex = 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( - forex['country_name'] ?? 'N/A', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - fontWeight: FontWeight.w700), - ), - - GestureDetector( - child: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - onTap: () async { - // final userId = getUserId(user['user_id']); - // final usersData = await getUserDetails(userId); - // - final forexId = int.tryParse( - forex['forex_perdiem_id'] - .toString()); - - if (forexId != null) { - print("ForexId -- $forexId"); - final data = await apiService - .getForexDetailsFind(forexId); - print("ForexId -- $data"); - - showDialog( - context: context, - builder: (context) => ForexData( + builder: + (context) => ForexData( isDesktop: isDesktop, forexId: forexId, // Pass the ID @@ -829,215 +884,221 @@ class ForexDataListState extends State { // role: // "Travel Agent" ), - ); - } else { - print("Invalid Forex ID"); - } - }, - ), - // 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: () { - // print( - // "USerDAta - $user"); - // // dynamic usersData = apiService - // // .getSingleUser(user[ - // // 'user_id'] - // // is String - // // ? int.parse(user[ - // // 'user_id']) - // // : user[ - // // 'user_id']); - // // - // // print( - // // "USerDAta - $usersData"); - // - // 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 - // }, - // ); - // }, - // ), - // ], - // ), - // ), - // ), - // ], - // ), - ], + ); + } else { + print("Invalid Forex ID"); + } + }, ), - - SizedBox(height: 2), - // Trip Id and Trip Name - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "${forex['country_code'] ?? ''} ", - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.w500), - ), - ], - ), - ], - ), - SizedBox(height: 2), - - // Name - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - forex['currency'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - SizedBox( - width: 10, - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - forex['perdiem_amount'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - ], - ), - // Actions - // Actions + // 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: () { + // print( + // "USerDAta - $user"); + // // dynamic usersData = apiService + // // .getSingleUser(user[ + // // 'user_id'] + // // is String + // // ? int.parse(user[ + // // 'user_id']) + // // : user[ + // // 'user_id']); + // // + // // print( + // // "USerDAta - $usersData"); + // + // 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 + // }, + // ); + // }, + // ), + // ], + // ), + // ), + // ), + // ], + // ), ], ), - ), - ); - }, - ); - } - return Expanded( - child: Column( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: isDesktop + SizedBox(height: 2), + // Trip Id and Trip Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "${forex['country_code'] ?? ''} ", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ], + ), + SizedBox(height: 2), + + // Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + forex['currency'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + SizedBox(width: 10), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + forex['perdiem_amount'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + ], + ), + // Actions + // Actions + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop ? (searchController.text.isNotEmpty && filteredForex.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) + scrollDirection: Axis.vertical, + child: table, + )) : (searchController.text.isNotEmpty && filteredForex.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : buildMobileCardView(paginatedForex)), - ), - // Expanded( - // child: isDesktop - // ? SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: table, // <-- your existing table - // ) - // : buildMobileCardView(paginatedUser), - // ), - PaginationControls( - currentPage: currentPage, - itemsPerPage: itemsPerPage, - totalItems: forex.length, - activeColor: layoutColor, // your theme color - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, - onItemsPerPageChanged: (items) { - setState(() { - itemsPerPage = items; - currentPage = 0; - }); - }, - ), - ], ), - ); - }, - ) - ]), - )), + // Expanded( + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedUser), + // ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: forex.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), ); } } diff --git a/lib/Screens/group/groupDetails.dart b/lib/Screens/group/groupDetails.dart index f2ebb58..fc3156b 100644 --- a/lib/Screens/group/groupDetails.dart +++ b/lib/Screens/group/groupDetails.dart @@ -21,13 +21,14 @@ class GroupData extends StatefulWidget { final int? groupId; // <-- Add this final Map? groupData; - const GroupData( - {super.key, - required this.isDesktop, - this.layoutColor, - required this.fetchGetGroup, - this.groupId, - this.groupData}); + const GroupData({ + super.key, + required this.isDesktop, + this.layoutColor, + required this.fetchGetGroup, + this.groupId, + this.groupData, + }); @override GroupDataState createState() => GroupDataState(); @@ -46,7 +47,6 @@ class GroupDataState extends State { final Map controllers = {}; Map errorMessages = {}; - List domesticList = []; List internationalList = []; @@ -75,12 +75,12 @@ class GroupDataState extends State { Map group_Detials() { final data = { - "name":controllers["name"]?.text, - "description":controllers["description"]?.text, - "domestic_policy_id":selectedDomesticPolicyID, - "international_policy_id":selectedInternationalPolicyID, - "domestic_policy_name":selectedDomesticPolicyName, - "international_policy_name":selectedInternationalPolicyName, + "name": controllers["name"]?.text, + "description": controllers["description"]?.text, + "domestic_policy_id": selectedDomesticPolicyID, + "international_policy_id": selectedInternationalPolicyID, + "domestic_policy_name": selectedDomesticPolicyName, + "international_policy_name": selectedInternationalPolicyName, "is_active": isActive, }; return data; @@ -166,23 +166,19 @@ class GroupDataState extends State { }); } - bool validateData() { errorMessages.clear(); final data = { - "name":controllers["name"]?.text, - "description":controllers["description"]?.text, - "domestic_policy_id":selectedDomesticPolicyID, - "international_policy_id":selectedInternationalPolicyID, - "domestic_policy_name":selectedDomesticPolicyName, - "international_policy_name":selectedInternationalPolicyName, + "name": controllers["name"]?.text, + "description": controllers["description"]?.text, + "domestic_policy_id": selectedDomesticPolicyID, + "international_policy_id": selectedInternationalPolicyID, + "domestic_policy_name": selectedDomesticPolicyName, + "international_policy_name": selectedInternationalPolicyName, }; - final requiredFields = [ - "name", - "description", - ]; + final requiredFields = ["name", "description"]; // Check validation for each field for (String field in requiredFields) { @@ -240,9 +236,10 @@ class GroupDataState extends State { }; final body = jsonEncode(groupData); - final response = groupDataId != null - ? await http.put(uri, headers: headers, body: body) - : await http.post(uri, headers: headers, body: body); + final response = + groupDataId != null + ? await http.put(uri, headers: headers, body: body) + : await http.post(uri, headers: headers, body: body); if (response.statusCode == 200 || response.statusCode == 201) { print("Group Details Created successfully!"); @@ -288,8 +285,8 @@ class GroupDataState extends State { // Map id to names DomesticMap = { for (var object in domesticList) - object['policy_id'] as String: object['name'] as String - }; + object['policy_id'] as String: object['name'] as String, + }; // print("domestic -- map--$DomesticMap"); @@ -302,7 +299,7 @@ class GroupDataState extends State { InternationalMap = { for (var item in internationalList) - item['policy_id'] as String: item['name'] as String + item['policy_id'] as String: item['name'] as String, }; // Extract only id for processing @@ -330,20 +327,18 @@ class GroupDataState extends State { ], ), const SizedBox(height: 2), - Divider( - thickness: 0.2, - color: Colors.blueGrey.shade100, - ), + Divider(thickness: 0.2, color: Colors.blueGrey.shade100), const SizedBox(height: 5), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Name", + "Name *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -351,18 +346,19 @@ class GroupDataState extends State { isDesktop: widget.isDesktop, color: Colors.transparent, child: SizedBox( - height: 40, - child: TextField( - controller: controllers["name"], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Name", - labelStyle: TextStyle(fontSize: 11, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - )), + height: 40, + child: TextField( + controller: controllers["name"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Name", + labelStyle: TextStyle(fontSize: 11, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), ), if (errorMessages["name"] != null) ...[ SizedBox(height: 5), // Space before error message @@ -380,9 +376,10 @@ class GroupDataState extends State { Text( "Select Policy For International", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -391,21 +388,23 @@ class GroupDataState extends State { child: SizedBox( height: 40, child: DropdownSearch( - selectedItem: InternationalMap[selectedInternationalPolicyID], + selectedItem: + InternationalMap[selectedInternationalPolicyID], popupProps: PopupProps.menu( showSearchBox: true, // Enables search functionality - menuProps: const MenuProps( - backgroundColor: Colors.white, - ), + menuProps: const MenuProps(backgroundColor: Colors.white), constraints: BoxConstraints(maxHeight: 250), - itemBuilder: (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 6.0), - child: Text( - item, - style: GoogleFonts.poppins(fontSize: 11.5), - ), - ), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Select Policy For International", @@ -418,25 +417,25 @@ class GroupDataState extends State { dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 1, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: + (context, selectedItem) => Align( + // Center-align selected item + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select Policy For International", + style: GoogleFonts.poppins(fontSize: 11), + ), ), - ), - ), - dropdownBuilder: (context, selectedItem) => Align( - // Center-align selected item - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select Policy For International", - style: GoogleFonts.poppins(fontSize: 11), - ), - ), onChanged: (String? newValue) { setState(() { // Find the country_code based on selected country_name - selectedInternationalPolicyID = InternationalMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; + selectedInternationalPolicyID = + InternationalMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; selectedInternationalPolicyName = newValue; }); }, @@ -452,9 +451,10 @@ class GroupDataState extends State { Text( "Select Policy For Domestic", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -466,18 +466,19 @@ class GroupDataState extends State { selectedItem: DomesticMap[selectedDomesticPolicyID], popupProps: PopupProps.menu( showSearchBox: true, // Enables search functionality - menuProps: const MenuProps( - backgroundColor: Colors.white, - ), + menuProps: const MenuProps(backgroundColor: Colors.white), constraints: BoxConstraints(maxHeight: 250), - itemBuilder: (context, object, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 6.0), - child: Text( - object, - style: GoogleFonts.poppins(fontSize: 11.5), - ), - ), + itemBuilder: + (context, object, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + object, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Select Policy For Domestic...", @@ -490,25 +491,25 @@ class GroupDataState extends State { dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 1, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: + (context, selectedItem) => Align( + // Center-align selected item + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select Policy For Domestic", + style: GoogleFonts.poppins(fontSize: 11), + ), ), - ), - ), - dropdownBuilder: (context, selectedItem) => Align( - // Center-align selected item - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select Policy For Domestic", - style: GoogleFonts.poppins(fontSize: 11), - ), - ), onChanged: (String? newValue) { setState(() { // Find the country_code based on selected country_name - selectedDomesticPolicyID = DomesticMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; + selectedDomesticPolicyID = + DomesticMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; selectedDomesticPolicyName = newValue; }); }, @@ -522,11 +523,12 @@ class GroupDataState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Description", + "Description *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -559,9 +561,7 @@ class GroupDataState extends State { ], ], ), - SizedBox( - height: 15, - ), + SizedBox(height: 15), if (groupDataId != null) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -569,13 +569,14 @@ class GroupDataState extends State { Text( "Change Status ", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), Tooltip( message: - isActive == "1" ? "Tap to deactivate" : "Tap to activate", + isActive == "1" ? "Tap to deactivate" : "Tap to activate", child: GestureDetector( onTap: toggleStatus, child: Text( @@ -587,13 +588,10 @@ class GroupDataState extends State { ), ), ), - ) + ), ], ), - if (groupDataId != null) - SizedBox( - height: 15, - ), + if (groupDataId != null) SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -630,16 +628,20 @@ class GroupDataState extends State { borderRadius: BorderRadius.circular(8), ), ), - child: Text('Save', - style: GoogleFonts.poppins( - fontSize: 11, color: Colors.white)), + child: Text( + 'Save', + style: GoogleFonts.poppins( + fontSize: 11, + color: Colors.white, + ), + ), ), ), ], - ) + ), // : SizedBox.shrink(), ], ), ); } -} \ No newline at end of file +} diff --git a/lib/Screens/group/groupList.dart b/lib/Screens/group/groupList.dart index e7591c0..d653297 100644 --- a/lib/Screens/group/groupList.dart +++ b/lib/Screens/group/groupList.dart @@ -1,17 +1,18 @@ import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:frontend/Screens/group/group.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'; import '../../config/apiUrl.dart'; import '../../routes/custom_appBar.dart'; import '../../routes/custom_drawer.dart'; import '../../services/apiService.dart'; import '../../utils/auth_utils.dart'; +import '../../utils/pagination.dart'; import 'groupDetails.dart'; class GroupList extends StatefulWidget { @@ -22,18 +23,43 @@ class GroupList extends StatefulWidget { class _GroupListState extends State { final ApiService apiService = ApiService(); - List? apiAllGroups; + late Future> futureGroups; + + late Map groupSingleData; + List? apiCountryData; + String? selectedGroupId; + String? orgId; + Color? layoutColor; Color? bodyColor; + List allGroups = []; + List filteredGroups = []; + TextEditingController searchController = TextEditingController(); + + int currentPage = 0; + int itemsPerPage = 10; + + List? apiAllGroups; + @override void initState() { super.initState(); + futureGroups = fetchGroups(); + + futureGroups.then((object) { + setState(() { + allGroups = object; + }); + }); WidgetsBinding.instance.addPostFrameCallback((_) { - loadAllGroups(); loadInitialData(); + fetchGroups(); + loadAllGroups(); }); + + // futurePlans = fetchPlans(); } void loadInitialData() async { @@ -41,37 +67,76 @@ class _GroupListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('auth_token'); + } + + Future> fetchGroups() async { + final result = await apiService.fetchAllGroup(); + return result; // Returning raw JSON list + } + Future loadAllGroups() async { try { final result = await apiService.fetchAllGroup(); setState(() { - apiAllGroups = result; + allGroups = result; }); - print("Fetched services: $apiAllGroups"); + print("Fetched services: $allGroups"); } catch (e) { print('Error fetching role list: $e'); } } + void filterGroups(String query) { + final lowerQuery = query.toLowerCase(); + setState(() { + filteredGroups = + allGroups.where((object) { + final isActiveStatus = + object['is_active'] == "1" ? "active" : "inactive"; + return (object['group_id']?.toLowerCase().contains(lowerQuery) ?? + false) || + (object['name']?.toLowerCase().contains(lowerQuery) ?? false) || + (object['domestic_policy_name']?.toLowerCase().contains( + lowerQuery, + ) ?? + false) || + (object['international_policy_name']?.toLowerCase().contains( + lowerQuery, + ) ?? + false) || + (object['description']?.toLowerCase().contains(lowerQuery) ?? + false) || + (isActiveStatus.contains(lowerQuery)); + }).toList(); + currentPage = 0; + }); + print("filteredGroups: $filteredGroups"); + } + void handleActiveStatus( Map groupData, String groupId, String currentStatus, ) async { - print("Toggling user status - $groupId (Current: $currentStatus)"); + print("Toggling group status - $groupId (Current: $currentStatus)"); final String apiUrlData = - '$apiUrl/api/groups/update/$groupId'; // API for updating user + '$apiUrl/api/groups/update/$groupId'; // API for updating group final String? token = await getToken(); if (token == null) { @@ -92,19 +157,19 @@ class _GroupListState extends State { 'Content-Type': 'application/json', }, body: jsonEncode({ - "is_active": newStatus // Set new status dynamically + "is_active": newStatus, // Set new status dynamically }), ); if (response.statusCode == 200 || response.statusCode == 201) { - print("User status updated successfully to $newStatus!"); - loadAllGroups(); // Refresh users list after update + print("Group status updated successfully to $newStatus!"); + loadAllGroups(); // Refresh groups list after update } else { - print("Failed to update user status. Status: ${response.statusCode}"); + print("Failed to update group status. Status: ${response.statusCode}"); print("Error: ${response.body}"); } } catch (e) { - print("Error updating user status: $e"); + print("Error updating group status: $e"); } } @@ -122,49 +187,40 @@ class _GroupListState extends State { loadAllGroups(); } - // Future deleteGroupFromApi(int groupId) async { - // try { - // await apiService.deleteGroup(groupId); // your delete API call - // deleteGroup(groupId); // remove from UI list - // } catch (e) { - // print('Error deleting group: $e'); - // } - // }' - @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Scaffold( - // backgroundColor: Colors.white, - backgroundColor: Color(0xFFf5f5f5), - 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(8), - child: Row( - children: [ - // if (isDesktop) CustomDrawer(isDesktop: true), - Expanded(child: buildGroupListLayout(isDesktop)) - ], + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + 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: [Expanded(child: buildGroupList(isDesktop))]), ), - ), - ); - }); + ); + }, + ); } - Widget buildGroupListLayout(bool isDesktop) { + Widget buildGroupList(bool isDesktop) { return Container( margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, - padding: const EdgeInsets.all(10), + padding: const EdgeInsets.all(1), decoration: BoxDecoration( color: isDesktop ? Colors.white : Color(0xFFFCFCFC), ), @@ -176,259 +232,751 @@ class _GroupListState extends State { // color: Colors.white, // // color: Color(0xFFF7F7FB), // width: 3.5)), - child: buildGroupData(isDesktop), + child: buildGroupTable(isDesktop), ); } - // Widget buildGroupListView(bool isDesktop) { - // return Container( - // child: Text("DAta"), - // ); - // } - - Widget buildGroupData(isDesktop) { + Widget buildGroupTable(bool isDesktop) { return 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, - // ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + height: + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, + + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + color: Colors.white, + padding: const EdgeInsets.all(10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'Group List', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), + Row( + children: [ + Text( + 'Group', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + ], ), - IconButton( - icon: const Icon(Icons.keyboard_arrow_down), - onPressed: () {}, - ), - ], - ), - ElevatedButton( - style: ElevatedButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Color(0xFF114D8B), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - // side: BorderSide(color: , width: 1), - ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - ), - onPressed: () async { - // List users = await futureUsers; - // context.go('/CreateGroup'); - showDialog( - context: context, - builder: (context) => GroupData( - isDesktop: isDesktop, - groupId: null, - layoutColor: layoutColor!, - fetchGetGroup: refreshData - ), - ); + 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: filterGroups, + decoration: InputDecoration( + hintText: "Search ...", + 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), + Spacer(), - }, - child: Row( - children: [ - Text('New Group', style: GoogleFonts.poppins(fontSize: 12)), - SizedBox( - width: 5, + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: EdgeInsets.symmetric( + horizontal: 20, + vertical: 12, + ), ), - Icon( - Icons.add_circle_outline_rounded, - color: Colors.white, - ), - ], - ), - ), - ], - ), - SizedBox( - height: 5, - ), - Row( - children: [ - Expanded( - child: Container( - // height: MediaQuery.of(context).size.height * 0.89, - padding: const EdgeInsets.all(10), - // margin: const EdgeInsets.only(bottom: 10), - color: Colors.white, - // color: Colors.red.shade100, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( + onPressed: () async { + // List groups = await futureGroups; + // context.go('/CreateGroup'); + showDialog( + context: context, + builder: + (context) => GroupData( + isDesktop: isDesktop, + groupId: null, + layoutColor: layoutColor!, + fetchGetGroup: refreshData, + ), + ); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely children: [ - buildGroupListView(isDesktop), + Text( + "Add New Group", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 13 : 11, + ), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), ], ), ), - ), + ], + ), + + 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: filterGroups, + decoration: InputDecoration( + hintText: "Search ...", + 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: futureGroups, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 8), + Text( + "No Group Found", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + Text( + "Please Create Group", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + ], + ), + ), + ); + } + + List groups = + filteredGroups.isNotEmpty ? filteredGroups : allGroups; + + groups.sort((a, b) { + DateTime dateA = DateTime.parse(a['created_on']); + DateTime dateB = DateTime.parse(b['created_on']); + + return dateB.compareTo(dateA); // Descending: newest first + }); + + List paginatedGroup = + groups + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); + + Widget table = LayoutBuilder( + builder: (context, constraints) { + double minWidth = isDesktop ? constraints.maxWidth : 1300; + + return ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + dividerThickness: 0.5, + columnSpacing: isDesktop ? 24.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, + ), + ), + columns: [ + DataColumn( + label: Text( + 'Group Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Domestic Policy', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'International Policy', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Description', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + paginatedGroup.map((group) { + String groupId = + group['group_id'] + .toString(); // Get group ID + bool isSelected = selectedGroupId == groupId; + + return DataRow( + cells: [ + DataCell( + Text( + "${group['name'] ?? ''}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + "${group['domestic_policy_name'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + "${group['international_policy_name'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + "${group['description'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + group['is_active'] == "1" + ? "Active" + : "Inactive", + style: TextStyle( + color: + group['is_active'] == "1" + ? Colors.green + : Colors.grey, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), + ), + ), + DataCell( + Row( + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + GestureDetector( + onTap: () async { + if (group['group_id'] != null) { + final newGroupID = int.tryParse( + group['group_id'].toString(), + ); + if (newGroupID != null) { + final data = await apiService + .getGroupDetailsFind( + newGroupID, + ); // ✅ Always an int + showDialog( + context: context, + builder: + (context) => GroupData( + isDesktop: isDesktop, + groupId: newGroupID, + // Pass the ID + groupData: data, + layoutColor: + layoutColor!, + fetchGetGroup: + refreshData, + ), + ); + } + } else { + print( + "something went wrong check properly", + ); + } + }, + + // onTap: () { + // context.go("/CreateGroup", extra: group); + // }, + child: Tooltip( + message: 'Edit Group Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + ), + SizedBox(width: 5), + GestureDetector( + onTap: () { + final idStr = group['group_id']; + final id = int.tryParse( + idStr.toString(), + ); + + if (id == null) { + print("group_id is null"); + return; + } + final status = group['is_active']; + // print("GroupId : ${group['group_id']} "); + deleteGroup(group, id, status); + }, + child: Tooltip( + message: 'Delete Group Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), + ), + ], + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + Widget buildMobileCardView(List paginatedGroup) { + return ListView.builder( + itemCount: paginatedGroup.length, + itemBuilder: (context, index) { + final object = paginatedGroup[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: [ + // Row 1: Policy Name and Actions + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: RichText( + text: TextSpan( + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black, + ), + children: [ + TextSpan( + text: "Group Name: ", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: + "${object['name'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal, + ), + ), + ], + ), + ), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () async { + if (object['group_id'] != null) { + final newGroupID = int.tryParse( + object['group_id'].toString(), + ); + if (newGroupID != null) { + final data = await apiService + .getGroupDetailsFind( + newGroupID, + ); // ✅ Always an int + showDialog( + context: context, + builder: + (context) => GroupData( + isDesktop: isDesktop, + groupId: newGroupID, + // Pass the ID + groupData: data, + layoutColor: + layoutColor!, + fetchGetGroup: + refreshData, + ), + ); + } + } else { + print( + "something went wrong check properly", + ); + } + }, + + // onTap: () { + // context.go("/CreateGroup", extra: group); + // }, + child: Tooltip( + message: 'Edit Group Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + ), + SizedBox(width: 5), + GestureDetector( + onTap: () { + final idStr = object['group_id']; + final id = int.tryParse( + idStr.toString(), + ); + + if (id == null) { + print("group_id is null"); + return; + } + final status = object['is_active']; + // print("GroupId : ${group['group_id']} "); + deleteGroup(object, id, status); + }, + child: Tooltip( + message: 'Edit Group Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), + ), + ], + ), + ], + ), + + SizedBox(height: 8), // Spacing + // Row 2: + RichText( + text: TextSpan( + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black, + ), + children: [ + TextSpan( + text: "Domestic Policy: ", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: + "${object['domestic_policy_name'] ?? 'N/A'}", + + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal, + ), + ), + ], + ), + ), + SizedBox(height: 8), // Spacing + // Row 3: + RichText( + text: TextSpan( + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black, + ), + children: [ + TextSpan( + text: "International Policy: ", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: + "${object['international_policy_name'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal, + ), + ), + ], + ), + ), + SizedBox(height: 8), // Spacing + // Row 4: + RichText( + text: TextSpan( + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black, + ), + children: [ + TextSpan( + text: "Description: ", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + TextSpan( + text: + "${object['description'] ?? 'N/A'}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + fontWeight: FontWeight.normal, + ), + ), + ], + ), + ), + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop + ? (searchController.text.isNotEmpty && + filteredGroups.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + paginatedGroup.isEmpty + ? Center( + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView(paginatedGroup)), + ), + + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: groups.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, ), ], - ) - ], + ), + ), ), ); } - - Widget buildGroupListView(bool isDesktop) { - if (apiAllGroups == null || apiAllGroups!.isEmpty) { - return Center(child: Text("No groups found.")); - } - - return ListView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: apiAllGroups!.length, - itemBuilder: (context, index) { - final group = apiAllGroups![index]; - return Card( - // color: bodyColor, - // color: Color(0xFFF5F5F5), - color: Colors.white, - margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - child: Text("Group Name", - style: GoogleFonts.poppins( - fontSize: 11.5, fontWeight: FontWeight.w400)), - ), - Expanded( - child: Text("Domestic Policy", - style: GoogleFonts.poppins( - fontSize: 11.5, fontWeight: FontWeight.w400)), - ), - Expanded( - child: Text("International Policy", - style: GoogleFonts.poppins( - fontSize: 11.5, fontWeight: FontWeight.w400)), - ), - Expanded( - child: Text("Description", - style: GoogleFonts.poppins( - fontSize: 11.5, fontWeight: FontWeight.w400)), - ), - ], - ), - SizedBox(height: 4), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text("${group['name']}", - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600)), - ), - Expanded( - child: Text("${group['domestic_policy_name'] ?? 'N/A'}", - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600)), - ), - Expanded( - child: Text("${group['international_policy_name']}", - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600))), - Expanded( - child: Text("${group['description'] ?? 'N/A'}", - style: GoogleFonts.poppins( - fontSize: 13, fontWeight: FontWeight.w600))), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - GestureDetector( - onTap: () async { - - if (group['group_id'] != null) { - final newGroupID = int.tryParse(group['group_id'].toString()); - if (newGroupID != null) { - final data = await apiService.getGroupDetailsFind(newGroupID); // ✅ Always an int - showDialog( - context: context, - builder: (context) => GroupData( - isDesktop: isDesktop, - groupId: newGroupID, // Pass the ID - groupData: data, - layoutColor: layoutColor!, - fetchGetGroup: refreshData - ), - ); - - - - } - - } else { - print("something went wrong check properly"); - } - }, - - // onTap: () { - // context.go("/CreateGroup", extra: group); - // }, - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), - ), - SizedBox( - width: 5, - ), - GestureDetector( - onTap: () { - final idStr = group['group_id']; - final id = int.tryParse(idStr.toString()); - - if (id == null) { - print("group_id is null"); - return; - } - final status = group['is_active']; - // print("GroupId : ${group['group_id']} "); - deleteGroup(group, id, status); - }, - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), - ), - ], - ), - ], - ), - ), - ); - }, - ); - } } diff --git a/lib/Screens/group/groupListBackUp.dart b/lib/Screens/group/groupListBackUp.dart new file mode 100644 index 0000000..ab02409 --- /dev/null +++ b/lib/Screens/group/groupListBackUp.dart @@ -0,0 +1,433 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:frontend/Screens/group/group.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 '../../config/apiUrl.dart'; +import '../../routes/custom_appBar.dart'; +import '../../routes/custom_drawer.dart'; +import '../../services/apiService.dart'; +import '../../utils/auth_utils.dart'; +import 'groupDetails.dart'; + +class GroupListBackUp extends StatefulWidget { + @override + _GroupListBackUpState createState() => _GroupListBackUpState(); +} + +class _GroupListBackUpState extends State { + final ApiService apiService = ApiService(); + + List? apiAllGroups; + Color? layoutColor; + Color? bodyColor; + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + loadAllGroups(); + loadInitialData(); + }); + } + + 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 loadAllGroups() async { + try { + final result = await apiService.fetchAllGroup(); + setState(() { + apiAllGroups = result; + }); + print("Fetched services: $apiAllGroups"); + } catch (e) { + print('Error fetching role list: $e'); + } + } + + void handleActiveStatus( + Map groupData, + String groupId, + String currentStatus, + ) async { + print("Toggling user status - $groupId (Current: $currentStatus)"); + + final String apiUrlData = + '$apiUrl/api/groups/update/$groupId'; // API for updating user + final String? token = await getToken(); + + if (token == null) { + print("Error: Token not found"); + return; + } + + // Toggle status: If active ("1"), set to inactive ("0"); otherwise, activate ("1") + String newStatus = (currentStatus == "1") ? "0" : "1"; + + print("STatus 1 - $newStatus"); + + try { + final response = await http.put( + Uri.parse(apiUrlData), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + body: jsonEncode({ + "is_active": newStatus // Set new status dynamically + }), + ); + + if (response.statusCode == 200 || response.statusCode == 201) { + print("User status updated successfully to $newStatus!"); + loadAllGroups(); // Refresh users list after update + } else { + print("Failed to update user status. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + } + } catch (e) { + print("Error updating user status: $e"); + } + } + + void deleteGroup(Map groupdata, groupId, status) { + print("GroupId : $groupId"); + print("Groupstatus: $status"); + print("GroupsData: $groupdata"); + + // handleActiveStatus(groupdata, groupId, status); + print("Calling handleActiveStatus with: id=$groupId, status=$status"); + handleActiveStatus(groupdata, groupId.toString(), status.toString()); + } + + Future refreshData() async { + loadAllGroups(); + } + + // Future deleteGroupFromApi(int groupId) async { + // try { + // await apiService.deleteGroup(groupId); // your delete API call + // deleteGroup(groupId); // remove from UI list + // } catch (e) { + // print('Error deleting group: $e'); + // } + // }' + + @override + Widget build(BuildContext context) { + return ResponsiveBuilder(builder: (context, sizingInfo) { + bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + + return Scaffold( + // backgroundColor: Colors.white, + backgroundColor: Color(0xFFf5f5f5), + 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(8), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + Expanded(child: buildGroupListLayout(isDesktop)) + ], + ), + ), + ); + }); + } + + 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, + // color: Color(0xFFE1F5FE), + // border: Border.all( + // color: Colors.white, + // // color: Color(0xFFF7F7FB), + // width: 3.5)), + child: buildGroupData(isDesktop), + ); + } + + // Widget buildGroupListView(bool isDesktop) { + // return Container( + // child: Text("DAta"), + // ); + // } + + Widget buildGroupData(isDesktop) { + return 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, + // ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Text( + 'Group List', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + IconButton( + icon: const Icon(Icons.keyboard_arrow_down), + onPressed: () {}, + ), + ], + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Color(0xFF114D8B), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: , width: 1), + ), + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: () async { + // List users = await futureUsers; + // context.go('/CreateGroup'); + showDialog( + context: context, + builder: (context) => GroupData( + isDesktop: isDesktop, + groupId: null, + layoutColor: layoutColor!, + fetchGetGroup: refreshData + ), + ); + + + }, + child: Row( + children: [ + Text('New Group', style: GoogleFonts.poppins(fontSize: 12)), + SizedBox( + width: 5, + ), + Icon( + Icons.add_circle_outline_rounded, + color: Colors.white, + ), + ], + ), + ), + ], + ), + SizedBox( + height: 5, + ), + Row( + children: [ + Expanded( + child: Container( + height: MediaQuery.of(context).size.height * 0.75, + padding: const EdgeInsets.all(10), + // margin: const EdgeInsets.only(bottom: 10), + color: Colors.white, + // color: Colors.red.shade100, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + buildGroupListView(isDesktop), + ], + ), + ), + ), + ), + ], + ) + ], + ), + ); + } + + Widget buildGroupListView(bool isDesktop) { + if (apiAllGroups == null || apiAllGroups!.isEmpty) { + return Center(child: Text("No groups found.")); + } + + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: apiAllGroups!.length, + itemBuilder: (context, index) { + final group = apiAllGroups![index]; + return Card( + // color: bodyColor, + // color: Color(0xFFF5F5F5), + color: Colors.white, + margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text("Group Name", + style: GoogleFonts.poppins( + fontSize: 11.5, fontWeight: FontWeight.w400)), + ), + Expanded( + child: Text("Domestic Policy", + style: GoogleFonts.poppins( + fontSize: 11.5, fontWeight: FontWeight.w400)), + ), + Expanded( + child: Text("International Policy", + style: GoogleFonts.poppins( + fontSize: 11.5, fontWeight: FontWeight.w400)), + ), + Expanded( + child: Text("Description", + style: GoogleFonts.poppins( + fontSize: 11.5, fontWeight: FontWeight.w400)), + ), + ], + ), + SizedBox(height: 4), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text("${group['name']}", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600)), + ), + Expanded( + child: Text("${group['domestic_policy_name'] ?? 'N/A'}", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600)), + ), + Expanded( + child: Text("${group['international_policy_name']}", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600))), + Expanded( + child: Text("${group['description'] ?? 'N/A'}", + style: GoogleFonts.poppins( + fontSize: 13, fontWeight: FontWeight.w600))), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () async { + if (group['group_id'] != null) { + final newGroupID = int.tryParse( + group['group_id'].toString()); + if (newGroupID != null) { + final data = await apiService.getGroupDetailsFind( + newGroupID); // ✅ Always an int + showDialog( + context: context, + builder: (context) => + GroupData( + isDesktop: isDesktop, + groupId: newGroupID, + // Pass the ID + groupData: data, + layoutColor: layoutColor!, + fetchGetGroup: refreshData + ), + ); + } + } else { + print("something went wrong check properly"); + } + }, + + // onTap: () { + // context.go("/CreateGroup", extra: group); + // }, + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox( + width: 5, + ), + GestureDetector( + onTap: () { + final idStr = group['group_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("group_id is null"); + return; + } + final status = group['is_active']; + // print("GroupId : ${group['group_id']} "); + deleteGroup(group, id, status); + }, + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), + ), + ], + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/Screens/hotels/hotelsDetails.dart b/lib/Screens/hotels/hotelsDetails.dart index 9c3e4de..e759d09 100644 --- a/lib/Screens/hotels/hotelsDetails.dart +++ b/lib/Screens/hotels/hotelsDetails.dart @@ -20,13 +20,14 @@ class HotelsData extends StatefulWidget { final int? hotelsId; // <-- Add this final Map? hotelsData; - const HotelsData( - {super.key, - required this.isDesktop, - this.layoutColor, - required this.fetchGetHotels, - this.hotelsId, - this.hotelsData}); + const HotelsData({ + super.key, + required this.isDesktop, + this.layoutColor, + required this.fetchGetHotels, + this.hotelsId, + this.hotelsData, + }); @override HotelsDataState createState() => HotelsDataState(); @@ -110,7 +111,8 @@ class HotelsDataState extends State { if (data == null) return; setState(() { selectedCountry = data['country_code']; // For dropdown - selectedCountryName = data['country_name']; // For dropdown label or display + selectedCountryName = + data['country_name']; // For dropdown label or display controllers['city']?.text = data['city'] ?? ''; controllers['hotel_chain']?.text = data['hotel_chain'] ?? ''; controllers['hotel_name']?.text = data['hotel_name'] ?? ''; @@ -148,7 +150,12 @@ class HotelsDataState extends State { "city": controllers["city"]?.text, }; - final requiredFields = ["hotel_name","hotel_chain","country_code","city"]; + final requiredFields = [ + "hotel_name", + "hotel_chain", + "country_code", + "city", + ]; // Check validation for each field for (String field in requiredFields) { @@ -174,7 +181,6 @@ class HotelsDataState extends State { } Future postHotelsData({int isActive = 1}) async { - final hotelsData = hotels_Details(); final String apiUrldata; @@ -184,16 +190,20 @@ class HotelsDataState extends State { apiUrldata = '$apiUrl/api/updateHotels/$hotelsDataId'; hotelsData["hotel_id"] = hotelsDataId.toString(); hotelsData["updated_by"] = userId; - (hotelsData.containsKey("created_by")) ? hotelsData.remove("created_by") : '' ; - (hotelsData.containsKey("country_name")) ? hotelsData.remove("country_name") : '' ; - - + (hotelsData.containsKey("created_by")) + ? hotelsData.remove("created_by") + : ''; + (hotelsData.containsKey("country_name")) + ? hotelsData.remove("country_name") + : ''; } else { print("for add Hotel id - null"); apiUrldata = '$apiUrl/api/createHotels'; print("called apiUrl - $apiUrldata"); hotelsData["created_by"] = userId; - (hotelsData.containsKey("country_name")) ? hotelsData.remove("country_name") : '' ; + (hotelsData.containsKey("country_name")) + ? hotelsData.remove("country_name") + : ''; } final token = await getToken(); // Fetch token @@ -210,9 +220,10 @@ class HotelsDataState extends State { }; final body = jsonEncode(hotelsData); - final response = hotelsDataId != null - ? await http.put(uri, headers: headers, body: body) - : await http.post(uri, headers: headers, body: body); + final response = + hotelsDataId != null + ? await http.put(uri, headers: headers, body: body) + : await http.post(uri, headers: headers, body: body); if (response.statusCode == 200 || response.statusCode == 201) { print("Hotels Details Created successfully!"); @@ -254,7 +265,7 @@ class HotelsDataState extends State { // Map country codes to country names countryMap = { for (var item in countryList) - item['country_code'] as String: item['country_name'] as String + item['country_code'] as String: item['country_name'] as String, }; // Extract only country codes for processing @@ -281,20 +292,18 @@ class HotelsDataState extends State { ], ), const SizedBox(height: 2), - Divider( - thickness: 0.2, - color: Colors.blueGrey.shade100, - ), + Divider(thickness: 0.2, color: Colors.blueGrey.shade100), const SizedBox(height: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Hotel Name", + "Hotel Name *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -302,18 +311,19 @@ class HotelsDataState extends State { isDesktop: widget.isDesktop, color: Colors.transparent, child: SizedBox( - height: 40, - child: TextField( - controller: controllers["hotel_name"], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Hotel Name", - labelStyle: TextStyle(fontSize: 11, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - )), + height: 40, + child: TextField( + controller: controllers["hotel_name"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Hotel Name", + labelStyle: TextStyle(fontSize: 11, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), ), if (errorMessages["hotel_name"] != null) ...[ SizedBox(height: 5), // Space before error message @@ -329,11 +339,12 @@ class HotelsDataState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Hotel Chain", + "Hotel Chain *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -341,18 +352,19 @@ class HotelsDataState extends State { isDesktop: widget.isDesktop, color: Colors.transparent, child: SizedBox( - height: 40, - child: TextField( - controller: controllers["hotel_chain"], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Hotel Chain", - labelStyle: TextStyle(fontSize: 11, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - )), + height: 40, + child: TextField( + controller: controllers["hotel_chain"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Hotel Chain", + labelStyle: TextStyle(fontSize: 11, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), ), if (errorMessages["hotel_chain"] != null) ...[ SizedBox(height: 5), // Space before error message @@ -363,55 +375,23 @@ class HotelsDataState extends State { ], ], ), - SizedBox(height: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "City", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldForexWrapper( - isFocused: false, - isDesktop: widget.isDesktop, - color: Colors.transparent, - child: SizedBox( - height: 40, - child: TextField( - controller: controllers["city"], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "City", - labelStyle: TextStyle(fontSize: 11, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - )), - ), - if (errorMessages["city"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["city"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), + + // - It has been observed that many of the dropdowns have overlapping issues, causing label names to be hidden - just copied searchable dropdown - still not completed (user mangement screen only ) master page except policy - my trips - flight taxi train insurance, visa misscenllo color white size padding data + // - Delete option is not working in the policy list page - completed + // - Label Names for all the modules should be set bold as it is looking like normal text in user management compared to trips page - completed + // - In the masters org mangement search option not working for traveller - particular 4 master page - - issues occur - commpleted - email master working fine, traveller master working fine, amount master working fine, group - completed + // - QC- Authentication - - completed - ask to check const SizedBox(height: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Country", + "Country *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -423,18 +403,19 @@ class HotelsDataState extends State { selectedItem: countryMap[selectedCountry], popupProps: PopupProps.menu( showSearchBox: true, // Enables search functionality - menuProps: const MenuProps( - backgroundColor: Colors.white, - ), + menuProps: const MenuProps(backgroundColor: Colors.white), constraints: BoxConstraints(maxHeight: 250), - itemBuilder: (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 6.0), - child: Text( - item, - style: GoogleFonts.poppins(fontSize: 11.5), - ), - ), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search Country...", @@ -447,25 +428,25 @@ class HotelsDataState extends State { dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 1, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: + (context, selectedItem) => Align( + // Center-align selected item + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select Country", + style: GoogleFonts.poppins(fontSize: 11), + ), ), - ), - ), - dropdownBuilder: (context, selectedItem) => Align( - // Center-align selected item - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select Country", - style: GoogleFonts.poppins(fontSize: 11), - ), - ), onChanged: (String? newValue) { setState(() { // Find the country_code based on selected country_name - selectedCountry = countryMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; + selectedCountry = + countryMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; selectedCountryName = newValue; }); }, @@ -481,7 +462,48 @@ class HotelsDataState extends State { ], ], ), - SizedBox( height: 15 ), + const SizedBox(height: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "City *", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldForexWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + color: Colors.transparent, + child: SizedBox( + height: 40, + child: TextField( + controller: controllers["city"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "City", + labelStyle: TextStyle(fontSize: 11, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (errorMessages["city"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["city"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + SizedBox(height: 10), // if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,), if (hotelsDataId != null) Row( @@ -490,13 +512,14 @@ class HotelsDataState extends State { Text( "Change Status ", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), Tooltip( message: - isActive == "1" ? "Tap to deactivate" : "Tap to activate", + isActive == "1" ? "Tap to deactivate" : "Tap to activate", child: GestureDetector( onTap: toggleStatus, child: Text( @@ -508,13 +531,10 @@ class HotelsDataState extends State { ), ), ), - ) + ), ], ), - if (hotelsDataId != null) - SizedBox( - height: 15, - ), + if (hotelsDataId != null) SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -551,13 +571,17 @@ class HotelsDataState extends State { borderRadius: BorderRadius.circular(8), ), ), - child: Text('Save', - style: GoogleFonts.poppins( - fontSize: 11, color: Colors.white)), + child: Text( + 'Save', + style: GoogleFonts.poppins( + fontSize: 11, + color: Colors.white, + ), + ), ), ), ], - ) + ), // : SizedBox.shrink(), ], ), diff --git a/lib/Screens/hotels/hotels_list.dart b/lib/Screens/hotels/hotels_list.dart index 281eba6..b3ffcce 100644 --- a/lib/Screens/hotels/hotels_list.dart +++ b/lib/Screens/hotels/hotels_list.dart @@ -83,13 +83,15 @@ class HotelsDataListState extends State { String? bodyStringColor = await getBodyColor(); setState(() { - layoutColor = layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; - bodyColor = bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -100,7 +102,7 @@ class HotelsDataListState extends State { Future> fetchGetHotels() async { orgId = await getOrgId(); - final String apiUrlData = '$apiUrl/api/getHotels'; + final String apiUrlData = '$apiUrl/api/getHotels?for=table_view'; final String? token = await getToken(); @@ -150,7 +152,8 @@ class HotelsDataListState extends State { if (!data.containsKey('data') || data['data'] is! List) { throw Exception( - "Invalid response format: 'data' field is missing or not a List"); + "Invalid response format: 'data' field is missing or not a List", + ); } List plansJson = data['data']; // 'data' is a Map, not a List @@ -174,8 +177,7 @@ class HotelsDataListState extends State { } } - -// Refresh user list after update + // Refresh user list after update void refreshUserList() { setState(() { futureHotels = fetchGetHotels(); // Re-fetch users after status update @@ -187,53 +189,64 @@ class HotelsDataListState extends State { print("allHotels before filtering: $query"); final lowerQuery = query.toLowerCase(); setState(() { - filteredHotels = allHotels.where((hotels) { - final isActiveStatus = - hotels['is_active'] == "1" ? "active" : "inactive"; - return (hotels['country_code']?.toLowerCase().contains(lowerQuery) ?? - false) || - (hotels['country_name']?.toLowerCase().contains(lowerQuery) ?? - false) || - (hotels['city']?.toLowerCase().contains(lowerQuery) ?? false) || - (hotels['hotel_chain']?.toLowerCase().contains(lowerQuery) ?? false) || - (hotels['hotel_name']?.toLowerCase().contains(lowerQuery) ?? - false) || - (isActiveStatus.contains(lowerQuery)); - }).toList(); + filteredHotels = + allHotels.where((hotels) { + final isActiveStatus = + hotels['is_active'] == "1" ? "active" : "inactive"; + return (hotels['country_code']?.toLowerCase().contains( + lowerQuery, + ) ?? + false) || + (hotels['country_name']?.toLowerCase().contains(lowerQuery) ?? + false) || + (hotels['city']?.toLowerCase().contains(lowerQuery) ?? false) || + (hotels['hotel_chain']?.toLowerCase().contains(lowerQuery) ?? + false) || + (hotels['hotel_name']?.toLowerCase().contains(lowerQuery) ?? + false) || + (isActiveStatus.contains(lowerQuery)); + }).toList(); + currentPage = 0; }); print("filteredHotels: $filteredHotels"); } @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Scaffold( - backgroundColor: Color(0xFFf5f5f5), - // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), - // 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 Page Content"))), - Expanded(child: buildGroupList(isDesktop)), - ], + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), + // 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 Page Content"))), + Expanded(child: buildGroupList(isDesktop)), + ], + ), ), - ), - ); - }); + ); + }, + ); } Widget buildGroupList(bool isDesktop) { @@ -261,359 +274,492 @@ class HotelsDataListState extends State { // ? 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, + height: + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, child: Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + color: Colors.white, padding: const EdgeInsets.all(10.0), - child: Container( - color: Colors.white, - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Divider( - // thickness: 0.2, // how "thick" the line is - // color: Colors.grey, // optional - // ), Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Text( - 'Hotel Details', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - ], + Text( + 'Hotel Details', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), ), - if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.16, - ), + ], + ), + if (isDesktop) + SizedBox(width: MediaQuery.of(context).size.width * 0.16), - if (isDesktop) - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterHotels, - decoration: InputDecoration( - hintText: "Search ...", - 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), - ), + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterHotels, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), - style: GoogleFonts.poppins( - fontSize: 12, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, ), ), ), - // SizedBox(width: 16), - Spacer(), + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + // SizedBox(width: 16), + Spacer(), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF114D8B), - foregroundColor: Colors.white, - disabledBackgroundColor: Color(0xFF114D8B), - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: - BorderSide(color: Color(0xFF114D8B), width: 2), - ), - padding: EdgeInsets.symmetric( - horizontal: 20, vertical: 12), - ), - onPressed: () async { - showDialog( - context: context, - builder: (context) => HotelsData( + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: EdgeInsets.symmetric( + horizontal: 20, + vertical: 12, + ), + ), + onPressed: () async { + showDialog( + context: context, + builder: + (context) => HotelsData( isDesktop: isDesktop, layoutColor: layoutColor!, fetchGetHotels: refreshData, // role: // "Travel Agent" ), - ); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add Hotels", - style: GoogleFonts.poppins( - fontSize: isDesktop ? 13 : 11, + ); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add Hotels", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 13 : 11, + ), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], + ), + ), + ], + ), + + 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: filterHotels, + decoration: InputDecoration( + hintText: "Search ...", + 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, ), ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, + 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: futureHotels, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // const Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // const SizedBox(height: 16), + // Text( + // "Oops!", + // style: GoogleFonts.poppins( + // fontSize: 20, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent), + // ), + const SizedBox(height: 8), + Text( + "No Hotels Available ", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + Text( + "Please Create Hotels", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), ], ), ), - ], - ), + ); + } - 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: filterHotels, - decoration: InputDecoration( - hintText: "Search ...", - 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: futureHotels, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError || - !snapshot.hasData || - snapshot.data!.isEmpty) { - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // const Icon(Icons.error_outline, - // color: Colors.redAccent, size: 60), - // const SizedBox(height: 16), - // Text( - // "Oops!", - // style: GoogleFonts.poppins( - // fontSize: 20, - // fontWeight: FontWeight.bold, - // color: Colors.redAccent), - // ), - const SizedBox(height: 8), - Text( - "No Hotels Available ", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey), - ), - const SizedBox(height: 20), - Text( - "Please Create Hotels", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 16, color: Colors.grey), - ), - const SizedBox(height: 20), - ], - ), - ), - ); - } + List hotels = + filteredHotels.isNotEmpty ? filteredHotels : allHotels; - List hotels = - filteredHotels.isNotEmpty ? filteredHotels : allHotels; + hotels.sort((a, b) { + DateTime dateA = DateTime.parse(a['created_on']); + DateTime dateB = DateTime.parse(b['created_on']); - hotels.sort((a, b) { - DateTime dateA = DateTime.parse(a['created_on']); - DateTime dateB = DateTime.parse(b['created_on']); + return dateB.compareTo(dateA); // Descending: newest first + }); - return dateB - .compareTo(dateA); // Descending: newest first - }); - - List paginatedHotels = hotels + List paginatedHotels = + hotels .skip(currentPage * itemsPerPage) .take(itemsPerPage) .toList(); - Widget table = LayoutBuilder( - builder: (context, constraints) { - double minWidth = - isDesktop ? constraints.maxWidth : 1300; + Widget table = LayoutBuilder( + builder: (context, constraints) { + double minWidth = isDesktop ? constraints.maxWidth : 1300; - return ConstrainedBox( - constraints: BoxConstraints(minWidth: minWidth), - child: DataTable( - dividerThickness: 0.5, - columnSpacing: isDesktop ? 24.0 : 16.0, - border: TableBorder( - horizontalInside: BorderSide( - width: 0.5, color: Colors.grey.shade200), + return ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + dividerThickness: 0.5, + columnSpacing: isDesktop ? 24.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, + ), + ), + columns: [ + DataColumn( + label: Text( + 'Hotel Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), ), - columns: [ - DataColumn( - label: Text( - 'Hotel Name', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Hotel Chain', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'City', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Country', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - DataColumn( - label: Text( - 'Actions', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600), - )), - ], - rows: paginatedHotels.map((hotels) { - String hotelsId = hotels['hotel_id'] - .toString(); // Get user ID + ), + DataColumn( + label: Text( + 'Hotel Chain', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'City', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Country', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + paginatedHotels.map((hotels) { + String hotelsId = + hotels['hotel_id'] + .toString(); // Get user ID bool isSelected = selectedUserId == hotelsId; - return DataRow(cells: [ - DataCell(Text(hotels['hotel_name'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", + return DataRow( + cells: [ + DataCell( + Text( + hotels['hotel_name'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell(Text(hotels['hotel_chain'] ?? '', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell(Text(hotels['city'] ?? 'N/A', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis)), - DataCell(Text(hotels['country_name'] ?? '', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ))), - DataCell( - Text( - hotels['is_active'] == "1" - ? 'Active' - : 'Inactive', - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - color: hotels['is_active'] == "1" ? Colors.green : Colors.red, - ), - softWrap: true, - overflow: TextOverflow.ellipsis, ), - ), - DataCell( - // UserActionsMenu( - // user: hotels, - // getUserDetails: (id) => - // apiService.getSingleUser(id), - // ), + DataCell( + Text( + hotels['hotel_chain'] ?? '', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + hotels['city'] ?? 'N/A', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + hotels['country_name'] ?? '', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + hotels['is_active'] == "1" + ? 'Active' + : 'Inactive', + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + color: + hotels['is_active'] == "1" + ? Colors.green + : Colors.grey, + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + // UserActionsMenu( + // user: hotels, + // getUserDetails: (id) => + // apiService.getSingleUser(id), + // ), + GestureDetector( + child: Tooltip( + message: 'Edit Hotel Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), + onTap: () async { + // final userId = getUserId(user['user_id']); + // final usersData = await getUserDetails(userId); + // + final hotelsId = int.tryParse( + hotels['hotel_id'].toString(), + ); + + if (hotelsId != null) { + print("HotelsId -- $hotelsId"); + final data = await apiService + .getHotelsDetailsFind(hotelsId); + print("HotelsId -- $data"); + + showDialog( + context: context, + builder: + (context) => HotelsData( + isDesktop: isDesktop, + hotelsId: + hotelsId, // Pass the ID + hotelsData: data, + layoutColor: layoutColor!, + // fetchGetHotels: fetchGetHotels, + fetchGetHotels: refreshData, + // role: + // "Travel Agent" + ), + ); + } else { + print("Invalid Hotels ID"); + } + }, + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + + Widget buildMobileCardView(List paginatedUser) { + return ListView.builder( + itemCount: paginatedUser.length, + itemBuilder: (context, index) { + final hotels = 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( + hotels['hotel_name'] ?? 'N/A', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.w700, + ), + ), + GestureDetector( - child: Image.asset( + child: Tooltip( + message: 'Edit Hotel Details', + child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, - height: 15), + height: 15, + ), + ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); // final hotelsId = int.tryParse( - hotels['hotel_id'] - .toString()); + hotels['hotel_id'].toString(), + ); if (hotelsId != null) { print("HotelsId -- $hotelsId"); @@ -623,83 +769,8 @@ class HotelsDataListState extends State { showDialog( context: context, - builder: (context) => HotelsData( - isDesktop: isDesktop, - hotelsId: hotelsId, // Pass the ID - hotelsData: data, - layoutColor: layoutColor!, - // fetchGetHotels: fetchGetHotels, - fetchGetHotels: refreshData, - // role: - // "Travel Agent" - ), - ); - } else { - print("Invalid Hotels ID"); - } - }, - ), - ), - ]); - }).toList(), - ), - ); - }, - ); - - Widget buildMobileCardView(List paginatedUser) { - return ListView.builder( - itemCount: paginatedUser.length, - itemBuilder: (context, index) { - final hotels = 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( - hotels['hotel_name'] ?? 'N/A', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - fontWeight: FontWeight.w700), - ), - - GestureDetector( - child: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15), - onTap: () async { - // final userId = getUserId(user['user_id']); - // final usersData = await getUserDetails(userId); - // - final hotelsId = int.tryParse( - hotels['hotel_id'] - .toString()); - - if (hotelsId != null) { - print("HotelsId -- $hotelsId"); - final data = await apiService - .getHotelsDetailsFind(hotelsId); - print("HotelsId -- $data"); - - showDialog( - context: context, - builder: (context) => HotelsData( + builder: + (context) => HotelsData( isDesktop: isDesktop, hotelsId: hotelsId, // Pass the ID @@ -710,142 +781,150 @@ class HotelsDataListState extends State { // role: // "Travel Agent" ), - ); - } else { - print("Invalid Hotels ID"); - } - }, - ), - ], + ); + } else { + print("Invalid Hotels ID"); + } + }, ), - - SizedBox(height: 2), - // Trip Id and Trip Name - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "${hotels['hotel_chain'] ?? ''} ", - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.w500), - ), - ], - ), - ], - ), - SizedBox(height: 2), - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "${hotels['city'] ?? ''} ", - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.w500), - ), - ], - ), - ], - ), - SizedBox(height: 2), - Row( - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - hotels['country_name'] ?? '', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87), - ), - ], - ), - ], - ), - // ** ], ), - ), - ); - }, - ); - } - return Expanded( - child: Column( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: isDesktop + SizedBox(height: 2), + // Trip Id and Trip Name + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "${hotels['hotel_chain'] ?? ''} ", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ], + ), + SizedBox(height: 2), + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "${hotels['city'] ?? ''} ", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ], + ), + SizedBox(height: 2), + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + hotels['country_name'] ?? '', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + ], + ), + ], + ), + // ** + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop ? (searchController.text.isNotEmpty && filteredHotels.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) + scrollDirection: Axis.vertical, + child: table, + )) : (searchController.text.isNotEmpty && filteredHotels.isEmpty ? Center( - child: Text( - "No matches found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey), + child: Text( + "No matches found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, ), - ) + ), + ) : buildMobileCardView(paginatedHotels)), - ), - // Expanded( - // child: isDesktop - // ? SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: table, // <-- your existing table - // ) - // : buildMobileCardView(paginatedUser), - // ), - PaginationControls( - currentPage: currentPage, - itemsPerPage: itemsPerPage, - totalItems: hotels.length, - activeColor: layoutColor, // your theme color - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, - onItemsPerPageChanged: (items) { - setState(() { - itemsPerPage = items; - currentPage = 0; - }); - }, - ), - ], ), - ); - }, - ) - ]), - )), + // Expanded( + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedUser), + // ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: hotels.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), ); } } diff --git a/lib/Screens/itnerary/flights.dart b/lib/Screens/itnerary/flights.dart index b4ca591..111a598 100644 --- a/lib/Screens/itnerary/flights.dart +++ b/lib/Screens/itnerary/flights.dart @@ -21,19 +21,19 @@ class FlightScreen extends StatefulWidget { final Map? selectedItem; final ValueNotifier tripTypeNotifier; - FlightScreen( - {Key? key, - required this.apiData, - required this.loginUser, - required this.onClose, - required this.onSaveFlight, - required this.selectedItem, - required this.flightData, - required this.hasAction, - this.tripType, - required this.tripTypeNotifier, - this.apiDataForClass}) - : super(key: key); + FlightScreen({ + Key? key, + required this.apiData, + required this.loginUser, + required this.onClose, + required this.onSaveFlight, + required this.selectedItem, + required this.flightData, + required this.hasAction, + this.tripType, + required this.tripTypeNotifier, + this.apiDataForClass, + }) : super(key: key); @override FlightScreenState createState() => FlightScreenState(); @@ -70,7 +70,7 @@ class FlightScreenState extends State { "_date", "_visa", "_time", - "_comments" + "_comments", ]; Map focusNodes = {}; @@ -148,14 +148,18 @@ class FlightScreenState extends State { // Loop through each row and add listeners to clear errors for (int i = 1; i <= rowCount; i++) { - textControllers["_from${i}Controller"] - ?.addListener(() => _clearError("from_place_$i")); - textControllers["_to${i}Controller"] - ?.addListener(() => _clearError("to_place_$i")); - textControllers["_date${i}Controller"] - ?.addListener(() => _clearError("date_$i")); - textControllers["_time${i}Controller"] - ?.addListener(() => _clearError("time_$i")); + textControllers["_from${i}Controller"]?.addListener( + () => _clearError("from_place_$i"), + ); + textControllers["_to${i}Controller"]?.addListener( + () => _clearError("to_place_$i"), + ); + textControllers["_date${i}Controller"]?.addListener( + () => _clearError("date_$i"), + ); + textControllers["_time${i}Controller"]?.addListener( + () => _clearError("time_$i"), + ); } // loadCountryList(); @@ -178,17 +182,16 @@ class FlightScreenState extends State { } Map getFlightTripDateRange( - List> flightData) { - final allTrips = flightData - .expand((flight) => flight['trips'] ?? []) - .whereType>() - .toList(); + List> flightData, + ) { + final allTrips = + flightData + .expand((flight) => flight['trips'] ?? []) + .whereType>() + .toList(); if (allTrips.isEmpty) { - return { - 'firstTripDate': null, - 'lastTripDate': null, - }; + return {'firstTripDate': null, 'lastTripDate': null}; } allTrips.sort((a, b) { @@ -293,9 +296,10 @@ class FlightScreenState extends State { print("Text Controllers KeysII: ${textControllers.keys.toList()}"); // Determine the row count based on selectedTripType - int rowCount = selectedTripType == "Roundtrip" - ? 2 - : selectedTripType == "Multitrip" + int rowCount = + selectedTripType == "Roundtrip" + ? 2 + : selectedTripType == "Multitrip" ? multiTripRowCount : 1; @@ -476,10 +480,12 @@ class FlightScreenState extends State { // TextEditingController(text: trip["from_place"]); // textControllers["_to${index}Controller"] = // TextEditingController(text: trip["to_place"]); - textControllers["_date${index}Controller"] = - TextEditingController(text: trip["date"]); - textControllers["_time${index}Controller"] = - TextEditingController(text: trip["time"]); + textControllers["_date${index}Controller"] = TextEditingController( + text: trip["date"], + ); + textControllers["_time${index}Controller"] = TextEditingController( + text: trip["time"], + ); // Check if editing and flight_trip_id exists for this trip if (widget.selectedItem != null && @@ -541,10 +547,9 @@ class FlightScreenState extends State { final currDateTime = format.parse("$currDateStr $currTimeStr"); if (!currDateTime.isAfter(prevDateTime)) { - errorMessages["time_$index"] = "Must be after previous time"; + errorMessages["time_$index"] = "30 mins gap required"; } else if (currDateTime.difference(prevDateTime).inMinutes < 30) { - errorMessages["time_$index"] = - "Must be least 30 mins after previous time"; + errorMessages["time_$index"] = "30 mins gap required"; } else { errorMessages.remove("time_$index"); } @@ -665,32 +670,34 @@ class FlightScreenState extends State { @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isMobile = sizingInfo.isMobile; - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isMobile = sizingInfo.isMobile; + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Container( - // color: Color(0xFFF4F4FB), - // color: Color(0xFFF9F9F9), // Slightly lighter than white - - child: Form( - key: _formKey, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(20.0), - child: Center( - child: Column(children: _buildAccomadtionForm(isDesktop)), + return Container( + // color: Color(0xFFF4F4FB), + // color: Color(0xFFF9F9F9), // Slightly lighter than white + child: Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 30.0), + child: Center( + child: Column(children: _buildAccomadtionForm(isDesktop)), + ), ), - ) - ], + ], + ), ), ), - ), - ); - }); + ); + }, + ); } List _buildAccomadtionForm(bool isDesktop) { @@ -703,14 +710,14 @@ class FlightScreenState extends State { List> rowBuilders = [ // _builClassType(isDesktop, 1), - _buildSecondRow(isDesktop, 1) + _buildSecondRow(isDesktop, 1), ]; List> rowRoundBuilders = [ // _builClassType(isDesktop, 1), _buildSecondRow(isDesktop, 1), // _builClassType(isDesktop, 2), - _buildSecondRow(isDesktop, 2) + _buildSecondRow(isDesktop, 2), ]; print("Trip Type Selected: $selectedTripType"); @@ -740,7 +747,6 @@ class FlightScreenState extends State { // ...List.generate(multiTripRowCount, (index) => // buildResponsiveRow(_builClassType(isDesktop, index + 1) + _buildSecondRow(isDesktop, index + 1)) // ).expand((row) => row), - if (selectedTripType == "Multitrip") Align( alignment: Alignment.centerRight, @@ -809,41 +815,42 @@ class FlightScreenState extends State { Text( "Trip Type", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), isDesktop ? Row(children: _buildTripType(isDesktop)) - : Column(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)), ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), ]; } List _buildTripType(bool isDesktop) { List purposeList = widget.apiData?['flight_trip_type'] ?? []; - List> dropdownItems = purposeList - .map((item) => DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_value'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -858,9 +865,10 @@ class FlightScreenState extends State { height: 40, width: double.infinity, child: DropdownSearch( - items: purposeList - .map((item) => item['dropdown_value'] as String) - .toList(), + items: + purposeList + .map((item) => item['dropdown_value'] as String) + .toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, @@ -876,29 +884,35 @@ class FlightScreenState extends State { errorMessages.clear(); }); print( - "Updating form data: Flight -> trip_type -> $selectedTripType"); + "Updating form data: Flight -> trip_type -> $selectedTripType", + ); _initializeFields(); }, selectedItem: selectedTripType, - dropdownBuilder: (context, selectedItem) => Align( - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select", - style: TextStyle(fontSize: 12), - ), - ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select", + style: TextStyle(fontSize: 12), + ), + ), popupProps: PopupProps.menu( constraints: BoxConstraints(maxHeight: 100), menuProps: MenuProps(backgroundColor: Colors.white), - itemBuilder: (context, item, isSelected) => Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0), - child: Text( - item, - style: TextStyle( - fontSize: 13), // Custom text size for dropdown items - ), - ), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item, + style: TextStyle( + fontSize: 13, + ), // Custom text size for dropdown items + ), + ), ), ), @@ -999,9 +1013,9 @@ class FlightScreenState extends State { return [ Container( padding: const EdgeInsets.all(10), + // padding: const EdgeInsets.only(left: 10, right: 10), // color: Colors.white, - child: Text( "Trip ${index}", style: TextStyle( @@ -1012,17 +1026,14 @@ class FlightScreenState extends State { ), ), SizedBox( - width: isDesktop - ? MediaQuery.of(context).size.width * 0.58 - : 80, // Ensure full width + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.58 + : 80, // Ensure full width child: Stack( alignment: Alignment.center, // Centers the icon children: [ - Divider( - color: Color(0xFF8B8FB2), - thickness: 0.5, - height: 20, - ), + Divider(color: Color(0xFF8B8FB2), thickness: 0.5, height: 20), Container( // padding: EdgeInsets.all(4), color: Colors.white, // Background to avoid overlapping @@ -1064,7 +1075,6 @@ class FlightScreenState extends State { // ], // ), // ), - Container( // color: Colors.white, // padding: const EdgeInsets.only(left: 10, right: 10), @@ -1076,7 +1086,7 @@ class FlightScreenState extends State { color: Colors.blueAccent, iconSize: 20, ), - ) + ), ]; } @@ -1085,19 +1095,24 @@ class FlightScreenState extends State { List purposeList = widget.apiDataForClass?['flight_class'] ?? []; - List> dropdownItems = purposeList - .map((item) => DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -1120,8 +1135,9 @@ class FlightScreenState extends State { flightLastTripDateNotifier.value != null && flightLastTripDateNotifier.value!.isNotEmpty) { try { - final tripDate = DateFormat('dd-MM-yyyy') - .parseStrict(flightLastTripDateNotifier.value!); + final tripDate = DateFormat( + 'dd-MM-yyyy', + ).parseStrict(flightLastTripDateNotifier.value!); if (tripDate.isAfter(today)) { firstDate = tripDate; } @@ -1133,8 +1149,9 @@ class FlightScreenState extends State { textControllers["_date${index - 1}Controller"]?.text; if (previousDateString != null && previousDateString.isNotEmpty) { try { - final previousDate = - DateFormat('dd-MM-yyyy').parseStrict(previousDateString); + final previousDate = DateFormat( + 'dd-MM-yyyy', + ).parseStrict(previousDateString); if (previousDate.isAfter(today)) { firstDate = previousDate; } @@ -1144,10 +1161,11 @@ class FlightScreenState extends State { } } - DateTime initialDate = _selectedCheckOutDate != null && - _selectedCheckOutDate!.isAfter(firstDate) - ? _selectedCheckOutDate! - : firstDate; + DateTime initialDate = + _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(firstDate) + ? _selectedCheckOutDate! + : firstDate; DateTime? pickedDate = await showDatePicker( context: context, @@ -1160,14 +1178,18 @@ class FlightScreenState extends State { setState(() { _selectedCheckOutDate = pickedDate; // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); - textControllers["_date${index}Controller"]?.text = - DateFormat('dd-MM-yyyy').format(pickedDate); + textControllers["_date${index}Controller"]?.text = DateFormat( + 'dd-MM-yyyy', + ).format(pickedDate); }); } } Future _selectCheckOutTime( - BuildContext context, int index, VoidCallback onPicked) async { + BuildContext context, + int index, + VoidCallback onPicked, + ) async { TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: _selectedCheckOutTime ?? TimeOfDay.now(), @@ -1179,8 +1201,13 @@ class FlightScreenState extends State { // Formatting time to HH:mm (24-hour format) final now = DateTime.now(); final formattedTime = DateFormat('HH:mm').format( - DateTime(now.year, now.month, now.day, pickedTime.hour, - pickedTime.minute), + DateTime( + now.year, + now.month, + now.day, + pickedTime.hour, + pickedTime.minute, + ), ); // _timeController.text = formattedTime; textControllers["_time${index}Controller"]?.text = formattedTime; @@ -1208,50 +1235,57 @@ class FlightScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "From", + "From*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - // isFocused: _fromFocus, - isFocused: focusStates["_from${index}Focused"] ?? false, - // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: isCountryLoading - ? Center(child: CircularProgressIndicator()) - : DropdownSearch( - selectedItem: selectedFrom[index] != null - ? countryMap[selectedFrom[index]] - : null, + // isFocused: _fromFocus, + isFocused: focusStates["_from${index}Focused"] ?? false, + // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: + isCountryLoading + ? Center(child: CircularProgressIndicator()) + : DropdownSearch( + selectedItem: + selectedFrom[index] != null + ? countryMap[selectedFrom[index]] + : null, popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 220), showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search...", - contentPadding: EdgeInsets.symmetric( - horizontal: 10, vertical: 1), + decoration: InputDecoration( + hintText: "Search...", + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 1, ), - style: TextStyle(fontSize: 12)), - menuProps: MenuProps( - backgroundColor: Colors.white, - ), - itemBuilder: (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 6.0), - child: Text( - item, - style: TextStyle( - fontSize: - 13), // 👈 Set your desired text size here ), + style: TextStyle(fontSize: 12), ), + menuProps: MenuProps(backgroundColor: Colors.white), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item, + style: TextStyle( + fontSize: 13, + ), // 👈 Set your desired text size here + ), + ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( @@ -1260,109 +1294,110 @@ class FlightScreenState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 1), ), ), - dropdownBuilder: (context, selectedItem) => Align( - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select", - style: TextStyle(fontSize: 12), - ), - ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select", + style: TextStyle(fontSize: 12), + ), + ), onChanged: (String? newValue) { setState(() { // selectedFrom[index] = countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; - selectedFrom[index] = countryMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; + selectedFrom[index] = + countryMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; print(selectedFrom[index]); }); }, ), - ) + ), - // child: SizedBox( - // height: 40, - // child: TextField( - // // focusNode: _fromFocusNode, - // focusNode: focusNodes["_from${index}FocusNode"], - // controller: textControllers["_from${index}Controller"], - // style: const TextStyle(fontSize: 12), - // decoration: const InputDecoration( - // labelText: "From", - // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - // floatingLabelBehavior: FloatingLabelBehavior.never, - // border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric(vertical: 16), - // ), - // ), - // ), - ), + // child: SizedBox( + // height: 40, + // child: TextField( + // // focusNode: _fromFocusNode, + // focusNode: focusNodes["_from${index}FocusNode"], + // controller: textControllers["_from${index}Controller"], + // style: const TextStyle(fontSize: 12), + // decoration: const InputDecoration( + // labelText: "From", + // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + // floatingLabelBehavior: FloatingLabelBehavior.never, + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + // ), + // ), + // ), + ), if (errorMessages["from_place_$index"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - SizedBox( - width: 20, - ) - else - SizedBox( - height: 8, - ), + if (isDesktop) SizedBox(width: 20) else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "To", + "To*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - isFocused: focusStates["_to${index}Focused"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: isCountryLoading - ? Center(child: CircularProgressIndicator()) - : DropdownSearch( - selectedItem: selectedTo[index] != null - ? countryMap[selectedTo[index]] - : null, + isFocused: focusStates["_to${index}Focused"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: + isCountryLoading + ? Center(child: CircularProgressIndicator()) + : DropdownSearch( + selectedItem: + selectedTo[index] != null + ? countryMap[selectedTo[index]] + : null, popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 220), showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search...", - contentPadding: EdgeInsets.symmetric( - horizontal: 10, vertical: 1), + decoration: InputDecoration( + hintText: "Search...", + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 1, ), - style: TextStyle(fontSize: 12)), - menuProps: MenuProps( - backgroundColor: Colors.white, - ), - itemBuilder: (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 6.0), - child: Text( - item, - style: TextStyle( - fontSize: - 13), // 👈 Set your desired text size here ), + style: TextStyle(fontSize: 12), ), + menuProps: MenuProps(backgroundColor: Colors.white), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item, + style: TextStyle( + fontSize: 13, + ), // 👈 Set your desired text size here + ), + ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( @@ -1371,102 +1406,194 @@ class FlightScreenState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 1), ), ), - dropdownBuilder: (context, selectedItem) => Align( - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select Country", - style: TextStyle(fontSize: 12), - ), - ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select Country", + style: TextStyle(fontSize: 12), + ), + ), onChanged: (String? newValue) { setState(() { - selectedTo[index] = countryMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; + selectedTo[index] = + countryMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; print(selectedTo[index]); }); }, ), - )), + ), + ), if (errorMessages["to_place_$index"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + 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: + // isFlightClassLoading + // ? 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, + // ), + // ), + // ), + // ], + // ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Class *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + 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: isFlightClassLoading - ? 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 + height: 35, + width: double.infinity, + child: DropdownSearch>( + items: purposeList.cast>(), + selectedItem: purposeList.firstWhere( + (item) => item['dropdown_key'] == selectedClasses[index], + orElse: () => {}, + ), + itemAsString: (item) => item['dropdown_value'] ?? '', + popupProps: PopupProps.menu( + showSearchBox: false, + fit: FlexFit.loose, + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: (context, item, isSelected) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedClasses[index] = newValue; - }); - - print(selectedClasses[index]); - } - : null, - items: dropdownItems, + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ), + ); + }, + ), + dropdownDecoratorProps: const DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, ), + ), + ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || selectedItem.isEmpty) { + return Text( + "Select Class", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 13, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ); + }, + onChanged: + purposeList.isNotEmpty + ? (Map? newValue) { + setState(() { + selectedClasses[index] = newValue?['dropdown_key']; + print("Selected Class: ${selectedClasses[index]}"); + }); + } + : null, + ), ), ), + if (errorMessages["class_$index"] != null) + Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + errorMessages["class_$index"]!, + style: GoogleFonts.poppins(fontSize: 12, color: Colors.red), + ), + ), ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Date", + "Date*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -1489,8 +1616,11 @@ class FlightScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -1499,28 +1629,21 @@ class FlightScreenState extends State { ), if (errorMessages["date_$index"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Time", + "Time*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -1538,7 +1661,8 @@ class FlightScreenState extends State { _selectCheckOutTime(context, index, () { validateTimeDifference(index); setState( - () {}); // ✅ Force rebuild to show the error immediately + () {}, + ); // ✅ Force rebuild to show the error immediately }); }, child: AbsorbPointer( @@ -1554,8 +1678,11 @@ class FlightScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.access_time, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -1579,13 +1706,9 @@ class FlightScreenState extends State { onPressed: () { removeTrip(index); }, - icon: Icon( - Icons.close, - color: Colors.redAccent, - size: 20, - ), + icon: Icon(Icons.close, color: Colors.redAccent, size: 20), ), - ) + ), ]; } @@ -1594,12 +1717,15 @@ class FlightScreenState extends State { widget.apiData?['flight_visa_available'] ?? []; // Default selected value - List> dropdownItems = visa_available - .map((item) => DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + visa_available + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); selectedvisa_available ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; @@ -1608,78 +1734,168 @@ class FlightScreenState extends State { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } return [ + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // "Visa Required", + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74), + // ), + // ), + // SizedBox(height: 5), + // CustomTextFieldItnerarySubWrapper( + // // isFocused: _tripTypeFocused, + // isFocused: focusStates["_visa1Focused"] ?? false, + // isDesktop: isDesktop, + // // width: isDesktop + // // ? MediaQuery.of(context).size.width * 0.34 + // // : MediaQuery.of(context).size.width * 0.66, + // child: SizedBox( + // height: 40, + // child: DropdownButtonFormField( + // // focusNode: _tripTypeFocusNode, // Assign the correct focus node + // focusNode: focusNodes["_visa1FocusNode"], + // value: selectedvisa_available, + // style: TextStyle(fontSize: 12), + // decoration: InputDecoration( + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric( + // horizontal: 10, + // ), // Proper padding + // ), + // onChanged: + // visa_available.isNotEmpty + // ? (newValue) { + // setState(() { + // selectedvisa_available = newValue; + // // selectedTripType = "Oneway"; + // // Reset `multiTripRowCount` when switching away from Multitrip + // }); + // print( + // "Updating form data: Flight -> trip_type -> $selectedvisa_available", + // ); + // + // // _initializeRows(); + // } + // : null, + // + // items: dropdownItems, + // ), + // ), + // ), + // ], + // ), + //********// Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Visa Required", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - // isFocused: _tripTypeFocused, + // use same wrapper as class isFocused: focusStates["_visa1Focused"] ?? false, isDesktop: isDesktop, - // width: isDesktop - // ? MediaQuery.of(context).size.width * 0.34 - // : MediaQuery.of(context).size.width * 0.66, child: SizedBox( - height: 40, - child: DropdownButtonFormField( - // focusNode: _tripTypeFocusNode, // Assign the correct focus node - focusNode: focusNodes["_visa1FocusNode"], - value: selectedvisa_available, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding + height: 35, + width: double.infinity, + child: DropdownSearch>( + items: visa_available.cast>(), + selectedItem: visa_available.firstWhere( + (item) => item['dropdown_key'] == selectedvisa_available, + orElse: () => {}, ), - onChanged: visa_available.isNotEmpty - ? (newValue) { - setState(() { - selectedvisa_available = newValue; - // selectedTripType = "Oneway"; - // Reset `multiTripRowCount` when switching away from Multitrip - }); - print( - "Updating form data: Flight -> trip_type -> $selectedvisa_available"); - - // _initializeRows(); - } - : null, - - items: dropdownItems, + itemAsString: (item) => item['dropdown_value'] ?? '', + popupProps: PopupProps.menu( + showSearchBox: false, + fit: FlexFit.loose, + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: (context, item, isSelected) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, + ), + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ), + ); + }, + ), + dropdownDecoratorProps: const DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + ), + ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || selectedItem.isEmpty) { + return Text( + "Select Option", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 13, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ); + }, + onChanged: + visa_available.isNotEmpty + ? (Map? newValue) { + setState(() { + selectedvisa_available = newValue?['dropdown_key']; + print("Selected Visa: $selectedvisa_available"); + }); + } + : null, ), ), ), ], ), - if (isDesktop) - SizedBox( - width: 20, - ), - SizedBox( - height: 5, - ), + if (isDesktop) SizedBox(width: 20), + SizedBox(height: 5), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Comments", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( @@ -1705,9 +1921,7 @@ class FlightScreenState extends State { ], ), if (isDesktop) Spacer(), - SizedBox( - height: 5, - ), + SizedBox(height: 5), Column( children: [ Row( @@ -1724,12 +1938,15 @@ class FlightScreenState extends State { widget.apiData?['flight_visa_available'] ?? []; // Default selected value - List> dropdownItems = visa_available - .map((item) => DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + visa_available + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); selectedvisa_available ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; @@ -1738,8 +1955,10 @@ class FlightScreenState extends State { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -1751,18 +1970,20 @@ class FlightScreenState extends State { Text( "Visa Required", style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), - CustomTextFieldWrapper( + CustomTextFieldItnerarySubWrapper( // isFocused: _tripTypeFocused, isFocused: focusStates["_visa1Focused"] ?? false, isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + // width: + // isDesktop + // ? MediaQuery.of(context).size.width * 0.34 + // : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: DropdownButtonFormField( @@ -1772,22 +1993,25 @@ class FlightScreenState extends State { style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), // Proper padding ), - onChanged: visa_available.isNotEmpty - ? (newValue) { - setState(() { - selectedvisa_available = newValue; - // selectedTripType = "Oneway"; - // Reset `multiTripRowCount` when switching away from Multitrip - }); - print( - "Updating form data: Flight -> trip_type -> $selectedvisa_available"); + onChanged: + visa_available.isNotEmpty + ? (newValue) { + setState(() { + selectedvisa_available = newValue; + // selectedTripType = "Oneway"; + // Reset `multiTripRowCount` when switching away from Multitrip + }); + print( + "Updating form data: Flight -> trip_type -> $selectedvisa_available", + ); - // _initializeRows(); - } - : null, + // _initializeRows(); + } + : null, items: dropdownItems, ), @@ -1807,9 +2031,7 @@ class FlightScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( @@ -1818,7 +2040,6 @@ class FlightScreenState extends State { ), ), SizedBox(width: 10), // Space between buttons - // Save Changes Button ElevatedButton( onPressed: () { @@ -1826,9 +2047,7 @@ class FlightScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( diff --git a/lib/Screens/itnerary/forex.dart b/lib/Screens/itnerary/forex.dart index 1368b1b..e08c86a 100644 --- a/lib/Screens/itnerary/forex.dart +++ b/lib/Screens/itnerary/forex.dart @@ -24,14 +24,15 @@ class ForexScreen extends StatefulWidget { final Function(Map) onSaveForex; final String? loginUser; - ForexScreen( - {required this.onClose, - this.apiData, - required this.selectedItem, - required this.apiCountryData, - required this.onSaveForex, - required this.loginUser, - required this.flightData}); + ForexScreen({ + required this.onClose, + this.apiData, + required this.selectedItem, + required this.apiCountryData, + required this.onSaveForex, + required this.loginUser, + required this.flightData, + }); @override _ForexScreenState createState() => _ForexScreenState(); @@ -75,16 +76,18 @@ class _ForexScreenState extends State { "_cash", "_checkForex", "_deliveryLocation", - "_comments" + "_comments", ]; String _formatDate(String? date) { if (date == null || date.isEmpty) return ""; try { - DateTime parsedDate = - DateTime.parse(date); // Assuming input is YYYY-MM-DD - return DateFormat("dd-MM-yyyy") - .format(parsedDate); // Convert to DD-MM-YYYY + DateTime parsedDate = DateTime.parse( + date, + ); // Assuming input is YYYY-MM-DD + return DateFormat( + "dd-MM-yyyy", + ).format(parsedDate); // Convert to DD-MM-YYYY } catch (e) { print("Error formatting date: $e"); return date; // Return as is if parsing fails @@ -146,7 +149,7 @@ class _ForexScreenState extends State { "country_code": selectedCountry, "start_date": _formatDate(textControllers["_forexStartDate"]?.text), "end_date": _formatDate(textControllers["_forexEndDate"]?.text), - "user_id": tripuserId + "user_id": tripuserId, // "currency": selectedCurrency ?? "", }; } @@ -195,10 +198,12 @@ class _ForexScreenState extends State { selectedDuration = responseData["duration"]?.toString() ?? ""; selectedQuotedAmount = responseData["perdiem_amount"]?.toString() ?? ""; - selectedCardPercent = - int.tryParse(responseData["card_percentage"]?.toString() ?? ""); - selectedCashPercent = - int.tryParse(responseData["cash_percentage"]?.toString() ?? ""); + selectedCardPercent = int.tryParse( + responseData["card_percentage"]?.toString() ?? "", + ); + selectedCashPercent = int.tryParse( + responseData["cash_percentage"]?.toString() ?? "", + ); textControllers["_cardNumber"]?.text = responseData["forex_card_no"]?.toString() ?? ""; @@ -218,8 +223,6 @@ class _ForexScreenState extends State { } bool isValidForexData(Map data) { - errorMessages.clear(); // Reset errors - // Required fields that must not be empty List requiredFields = [ "start_date", @@ -227,7 +230,7 @@ class _ForexScreenState extends State { "country_code", "deposit_on_card", "deposit_on_cash", - // "card_number" + // "card_number", ]; // If have_card is "1", then delivery_location is required @@ -241,25 +244,30 @@ class _ForexScreenState extends State { // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { - errorMessages[field] = "This field is required"; + errorMessages[field] = "Required"; } } - return errorMessages.isEmpty; // Valid if there are no errors + return errorMessages.values.every((msg) => msg.trim().isEmpty); + + // if (hasErrors) { + // print("At least one error message is present."); + // } + // + // return errorMessages.isEmpty; // Valid if there are no errors } Map getFlightTripDateRange( - List> flightData) { - final allTrips = flightData - .expand((flight) => flight['trips'] ?? []) - .whereType>() - .toList(); + List> flightData, + ) { + final allTrips = + flightData + .expand((flight) => flight['trips'] ?? []) + .whereType>() + .toList(); if (allTrips.isEmpty) { - return { - 'firstTripDate': null, - 'lastTripDate': null, - }; + return {'firstTripDate': null, 'lastTripDate': null}; } allTrips.sort((a, b) { @@ -282,7 +290,7 @@ class _ForexScreenState extends State { Map data = forexData; - if (!isValidForexData(data)) { + if (!isValidForexData(data) && errorMessages.isNotEmpty) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails @@ -352,18 +360,22 @@ class _ForexScreenState extends State { // ✅ Only set controller after value is updated // final parsedDate = // DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); - final parsedDate = DateFormat("dd-MM-yyyy") - .parse(flightFirstTripDateNotifier.value ?? ''); + final parsedDate = DateFormat( + "dd-MM-yyyy", + ).parse(flightFirstTripDateNotifier.value ?? ''); if (parsedDate != null) { - textControllers["_forexStartDate"]?.text = - DateFormat('dd-MM-yyyy').format(parsedDate); + textControllers["_forexStartDate"]?.text = DateFormat( + 'dd-MM-yyyy', + ).format(parsedDate); } - final parsedEndDate = DateFormat("dd-MM-yyyy") - .parse(flightLastTripDateNotifier.value ?? ''); + final parsedEndDate = DateFormat( + "dd-MM-yyyy", + ).parse(flightLastTripDateNotifier.value ?? ''); if (parsedEndDate != null) { - textControllers["_forexEndDate"]?.text = - DateFormat('dd-MM-yyyy').format(parsedEndDate); + textControllers["_forexEndDate"]?.text = DateFormat( + 'dd-MM-yyyy', + ).format(parsedEndDate); } }); } @@ -392,8 +404,9 @@ class _ForexScreenState extends State { textControllers["_cardNumber"] = initController("card_number"); textControllers["_card"] = initController("deposit_on_card"); textControllers["_cash"] = initController("deposit_on_cash"); - textControllers["_deliveryLocation"] = - initController("delivery_location"); + textControllers["_deliveryLocation"] = initController( + "delivery_location", + ); textControllers["_comments"] = initController("comments"); // Set dropdown values @@ -401,9 +414,11 @@ class _ForexScreenState extends State { selectedCurrency = widget.selectedItem!["currency"] as String?; selectedDuration = widget.selectedItem!["duration"] as String?; selectedCardPercent = int.tryParse( - widget.selectedItem!["card_percentage"]?.toString() ?? ""); + widget.selectedItem!["card_percentage"]?.toString() ?? "", + ); selectedCashPercent = int.tryParse( - widget.selectedItem!["cash_percentage"]?.toString() ?? ""); + widget.selectedItem!["cash_percentage"]?.toString() ?? "", + ); selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?; isChecked = widget.selectedItem!["have_card"] == "1"; // Convert string to bool @@ -473,7 +488,8 @@ class _ForexScreenState extends State { if (startDateString == null || endDateString == null) return; print( - "Calculate 3 - StarrtDAte: $startDateString --EndDate: $endDateString"); + "Calculate 3 - StarrtDAte: $startDateString --EndDate: $endDateString", + ); try { // Parse the dates from string @@ -548,7 +564,8 @@ class _ForexScreenState extends State { if (quotedAmount != null) { // fifteenPercent = (quotedAmount * 15) ~/ 100; print("selectedCardPercent - $selectedCashPercent"); - fifteenPercent = (quotedAmount * selectedCashPercent!) ~/ + fifteenPercent = + (quotedAmount * selectedCashPercent!) ~/ 100; // Calculate 15% (integer division) remainingAmount = quotedAmount - fifteenPercent; // Subtract from total @@ -587,10 +604,12 @@ class _ForexScreenState extends State { int checkValidAmount = cashAmount + enteredAmount; print( - "checkValidAmount - $checkValidAmount - $enteredAmount - $cashAmount"); + "checkValidAmount - $checkValidAmount - $enteredAmount - $cashAmount", + ); print( - "CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount"); + "CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount", + ); if (enteredAmount == null || calculateAmnt > qouteAmount!) { errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount"; @@ -598,7 +617,7 @@ class _ForexScreenState extends State { errorMessages["deposit_on_card"] = "Enter Valid Amount"; // Clear error if valid } else { - errorMessages["deposit_on_card"] = ""; // Clear error if valid + errorMessages.remove("deposit_on_card"); // Clear error if valid } // Refresh UI if using StatefulWidget @@ -606,7 +625,8 @@ class _ForexScreenState extends State { } void _validateCashAmount(String value) { - errorMessages["deposit_on_card"] = " "; + // errorMessages["deposit_on_card"] = " "; + errorMessages.remove("deposit_on_cash"); print("_validateCashAmount - $value - $fifteenPercent"); int? enteredAmount = int.tryParse(value); @@ -619,19 +639,27 @@ class _ForexScreenState extends State { int checkValidAmount = cardAmount! + enteredAmount; print( - "checkValidAmountCash - $checkValidAmount -cash- $enteredAmount -Card - $cardAmount - quotedAmount- $quotedAmount"); + "checkValidAmountCash - $checkValidAmount -cash- $enteredAmount -Card - $cardAmount - quotedAmount- $quotedAmount", + ); print("Difference - $difference"); print('CardAmount - $cardAmount'); textControllers["_card"]?.text = difference.toString(); - // if (enteredAmount > fifteenPercent) { - // errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent"; - // } else if (checkValidAmount == quotedAmount) { - // errorMessages["deposit_on_card"] = " "; // Clear error if valid - // } else { - // errorMessages["deposit_on_cash"] = ""; // Clear error if valid - // } + if (enteredAmount > fifteenPercent) { + if (checkValidAmount > 0) { + textControllers["_card"]?.text = "0"; + errorMessages["deposit_on_card"] = + "Enter Valid Amount"; // Clear error if valid + } + errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent"; + } else if (checkValidAmount == quotedAmount) { + errorMessages.remove("deposit_on_cash"); + // errorMessages["deposit_on_card"] = " "; // Clear error if valid + } else { + // errorMessages["deposit_on_cash"] = ""; // Clear error if valid + errorMessages.remove("deposit_on_cash"); + } // Refresh UI if using StatefulWidget setState(() {}); @@ -653,10 +681,12 @@ class _ForexScreenState extends State { void _validateDates() { print("VALiDATING DATES"); - DateTime? startDate = - _parseDate(textControllers["_forexStartDate"]?.text ?? ""); - DateTime? endDate = - _parseDate(textControllers["_forexEndDate"]?.text ?? ""); + DateTime? startDate = _parseDate( + textControllers["_forexStartDate"]?.text ?? "", + ); + DateTime? endDate = _parseDate( + textControllers["_forexEndDate"]?.text ?? "", + ); if (startDate != null && endDate != null && endDate.isBefore(startDate)) { setState(() { @@ -672,30 +702,33 @@ class _ForexScreenState extends State { @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isMobile = sizingInfo.isMobile; - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isMobile = sizingInfo.isMobile; + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Container( - // color: Color(0xFFF4F4FB), - child: Form( - key: _formKey, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(28.0), - child: Center( - child: Column(children: _buildAccomadtionForm(isDesktop)), + return Container( + // color: Color(0xFFF4F4FB), + child: Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(28.0), + child: Center( + child: Column(children: _buildAccomadtionForm(isDesktop)), + ), ), - ) - ], + ], + ), ), ), - ), - ); - }); + ); + }, + ); } List _buildAccomadtionForm(bool isDesktop) { @@ -714,9 +747,7 @@ class _ForexScreenState extends State { return [ ...buildResponsiveRow(_buildFirstRow(isDesktop)), - SizedBox( - height: 5, - ), + SizedBox(height: 5), // Align( // alignment: Alignment.centerLeft, // child: Text( @@ -743,9 +774,7 @@ class _ForexScreenState extends State { // Divider( // thickness: 0.3, // ), - SizedBox( - height: 10, - ), + SizedBox(height: 10), ...buildResponsiveRow(_buildSecondRow(isDesktop)), ...buildResponsiveRow(_buildCardDetailsRow(isDesktop)), ...buildResponsiveRow(_buildForexCard(isDesktop)), @@ -769,8 +798,9 @@ 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); + textControllers["_forexStartDate"]?.text = DateFormat( + 'dd-MM-yyyy', + ).format(initialDate); } catch (e) { initialDate = today; } @@ -804,8 +834,9 @@ class _ForexScreenState extends State { if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; - textControllers["_forexStartDate"]?.text = - DateFormat('dd-MM-yyyy').format(pickedDate); + textControllers["_forexStartDate"]?.text = DateFormat( + 'dd-MM-yyyy', + ).format(pickedDate); }); } } @@ -854,8 +885,9 @@ class _ForexScreenState extends State { if (pickedDate != null && pickedDate != _selectedEndDate) { setState(() { _selectedEndDate = pickedDate; - textControllers["_forexEndDate"]?.text = - DateFormat('dd-MM-yyyy').format(pickedDate); + textControllers["_forexEndDate"]?.text = DateFormat( + 'dd-MM-yyyy', + ).format(pickedDate); // textControllers["_forexEndDate"]?.text = // DateFormat('dd-MM-yyyy').format(initialDate); }); @@ -867,11 +899,12 @@ class _ForexScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Start Date", + "Start Date *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -888,10 +921,12 @@ class _ForexScreenState extends State { await _selectCheckOutDate(context); if (textControllers["_forexEndDate"]!.text.isNotEmpty) { - DateTime? startDate = - _parseDate(textControllers["_forexStartDate"]!.text); - DateTime? endDate = - _parseDate(textControllers["_forexEndDate"]!.text); + DateTime? startDate = _parseDate( + textControllers["_forexStartDate"]!.text, + ); + DateTime? endDate = _parseDate( + textControllers["_forexEndDate"]!.text, + ); if (startDate != null && endDate != null && @@ -914,13 +949,18 @@ class _ForexScreenState extends State { style: const TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Select Date", - labelStyle: - const TextStyle(fontSize: 12, color: Colors.grey), + labelStyle: const TextStyle( + fontSize: 12, + color: Colors.grey, + ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(vertical: 16), - suffixIcon: const Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: const Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -930,27 +970,23 @@ class _ForexScreenState extends State { if (errorMessages["start_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( - "Select Start Date", + errorMessages["start_date"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "End Date", + "End Date *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -964,10 +1000,12 @@ class _ForexScreenState extends State { await _selectForexEndDate(context); if (textControllers["_forexEndDate"]!.text.isNotEmpty) { - DateTime? startDate = - _parseDate(textControllers["_forexStartDate"]!.text); - DateTime? endDate = - _parseDate(textControllers["_forexEndDate"]!.text); + DateTime? startDate = _parseDate( + textControllers["_forexStartDate"]!.text, + ); + DateTime? endDate = _parseDate( + textControllers["_forexEndDate"]!.text, + ); if (startDate != null && endDate != null && @@ -995,8 +1033,11 @@ class _ForexScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -1013,21 +1054,17 @@ class _ForexScreenState extends State { ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Duration", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -1043,9 +1080,10 @@ class _ForexScreenState extends State { // selectedDuration?.isNotEmpty == true ? selectedDuration! : "Duration", selectedDuration ?? "Duration", style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), // decoration: const InputDecoration( // labelText: "To", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), @@ -1071,7 +1109,7 @@ class _ForexScreenState extends State { // Map country codes to country names countryMap = { for (var item in countryList) - item['country_code'] as String: item['country_name'] as String + item['country_code'] as String: item['country_name'] as String, }; // Extract only country codes for processing @@ -1097,11 +1135,12 @@ class _ForexScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Country", + "Country*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -1124,25 +1163,25 @@ class _ForexScreenState extends State { dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 1, + 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), + ), ), - ), - ), - 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; + selectedCountry = + countryMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; _onCountryChanged(selectedCountry); }); }, @@ -1152,7 +1191,7 @@ class _ForexScreenState extends State { if (errorMessages["country_code"] != null) ...[ SizedBox(height: 5), // Space before error message Text( - "Select Country", + errorMessages["country_code"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], @@ -1164,22 +1203,19 @@ class _ForexScreenState extends State { // height: 8, // ), if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.048, - ) + SizedBox(width: MediaQuery.of(context).size.width * 0.048) else - SizedBox( - height: 8, - ), + SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( "Currency *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -1200,9 +1236,10 @@ class _ForexScreenState extends State { selectedCurrency ?? "Currency", // selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency", style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), ), ), @@ -1211,21 +1248,17 @@ class _ForexScreenState extends State { ], ), // if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Perdiem Amount", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -1241,9 +1274,10 @@ class _ForexScreenState extends State { selectedPerdiemAmount ?? "Amount", // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), // decoration: const InputDecoration( // labelText: "To", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), @@ -1277,9 +1311,10 @@ class _ForexScreenState extends State { Text( "Transport", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), Container( @@ -1298,8 +1333,9 @@ class _ForexScreenState extends State { }, keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + FilteringTextInputFormatter.allow( + RegExp(r'^\d*\.?\d*$'), + ), // Allow only positive numbers with optional decimal ], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( @@ -1310,7 +1346,7 @@ class _ForexScreenState extends State { contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), - ) + ), // CustomTextFieldItnerarySubWrapper( // width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null, // isFocused: focusStates["_transport"] ?? false, @@ -1339,23 +1375,17 @@ class _ForexScreenState extends State { // ), ], ), - if (isDesktop) - SizedBox( - width: 10, - ) - else - SizedBox( - height: 8, - ), + if (isDesktop) SizedBox(width: 10) else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Accomodation", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), Container( @@ -1374,8 +1404,9 @@ class _ForexScreenState extends State { }, keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + FilteringTextInputFormatter.allow( + RegExp(r'^\d*\.?\d*$'), + ), // Allow only positive numbers with optional decimal ], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( @@ -1386,7 +1417,7 @@ class _ForexScreenState extends State { contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), - ) + ), // CustomTextFieldItnerarySubWrapper( // width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null, // isFocused: focusStates["_accomodation"] ?? false, @@ -1415,54 +1446,49 @@ class _ForexScreenState extends State { // ), ], ), - if (isDesktop) - SizedBox( - width: 10, - ) - else - SizedBox( - height: 8, - ), + if (isDesktop) SizedBox(width: 10) else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Telephone", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), 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"], - onChanged: (value) { - setState(() { - userEdited = true; - }); - _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), - ), - )) + 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"], + onChanged: (value) { + setState(() { + userEdited = true; + }); + _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["_telephone"] ?? false, @@ -1491,23 +1517,17 @@ class _ForexScreenState extends State { // ), ], ), - if (isDesktop) - SizedBox( - width: 40, - ) - else - SizedBox( - height: 8, - ), + if (isDesktop) SizedBox(width: 40) else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Other Expenses", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -1523,9 +1543,10 @@ class _ForexScreenState extends State { // focusNode: _toFocusNode, // controller: _toController, style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), // decoration: const InputDecoration( // labelText: "To", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), @@ -1545,19 +1566,24 @@ class _ForexScreenState extends State { List _buildCardDetailsRow(bool isDesktop) { List purposeList = widget.apiData?['flight_class'] ?? []; - List> dropdownItems = purposeList - .map((item) => DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_value'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -1572,9 +1598,10 @@ class _ForexScreenState extends State { Text( "Cash*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -1585,12 +1612,21 @@ class _ForexScreenState extends State { child: TextField( focusNode: focusNodes["_cash"], controller: textControllers["_cash"], - style: const TextStyle(fontSize: 12), keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, // ✅ Only allow digits + ], + style: const TextStyle(fontSize: 12), + // keyboardType: TextInputType.number, onChanged: (value) { // errorMessages["deposit_on_cash"] = ""; + errorMessages.remove("deposit_on_cash"); + errorMessages.remove("deposit_on_card"); + // errorMessages["deposit_on_card"] = ""; + _validateCashAmount( - value); // Call validation when text changes + value, + ); // Call validation when text changes }, decoration: const InputDecoration( labelText: "Cash", @@ -1613,12 +1649,7 @@ class _ForexScreenState extends State { ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1626,9 +1657,10 @@ class _ForexScreenState extends State { Text( "Card*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -1641,9 +1673,15 @@ class _ForexScreenState extends State { controller: textControllers["_card"], style: const TextStyle(fontSize: 12), keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, // ✅ Only allow digits + ], onChanged: (value) { + errorMessages.remove("deposit_on_cash"); + errorMessages.remove("deposit_on_card"); _validateCardAmount( - value); // Call validation when text changes + value, + ); // Call validation when text changes }, decoration: const InputDecoration( labelText: "Card", @@ -1665,12 +1703,7 @@ class _ForexScreenState extends State { ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1678,9 +1711,10 @@ class _ForexScreenState extends State { Text( "Total Amount", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( @@ -1696,9 +1730,10 @@ class _ForexScreenState extends State { selectedQuotedAmount ?? "0", // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", style: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 13, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), ), ), @@ -1824,9 +1859,10 @@ class _ForexScreenState extends State { Text( "Comments", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( @@ -1853,9 +1889,7 @@ class _ForexScreenState extends State { ], ), if (isDesktop) Spacer(), - SizedBox( - height: 5, - ), + SizedBox(height: 5), // Actions row remains a Row Column( children: [ @@ -1872,96 +1906,94 @@ class _ForexScreenState extends State { return [ !isChecked ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Card Number", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Card Number*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), ), - SizedBox(height: 5), - CustomTextFieldForexWrapper( - isFocused: focusStates["_cardNumber"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 35, - child: TextField( - enabled: !isChecked, - focusNode: focusNodes["_cardNumber"], - controller: textControllers["_cardNumber"], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Card Number", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), + ), + SizedBox(height: 5), + CustomTextFieldForexWrapper( + isFocused: focusStates["_cardNumber"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 35, + child: TextField( + enabled: !isChecked, + focusNode: focusNodes["_cardNumber"], + controller: textControllers["_cardNumber"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Card Number", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), - if (errorMessages["card_number"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], + ), + if (errorMessages["card_number"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), ], - ) + ], + ) : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Delivery Location", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Delivery Location", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), ), - SizedBox(height: 5), - CustomTextFieldWrapper( - isFocused: focusStates["_deliveryLocation"] ?? - false, // Dropdown doesn't use focus - isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.45 - : null, - child: SizedBox( - height: 35, - child: TextField( - focusNode: focusNodes["_deliveryLocation"], - controller: textControllers["_deliveryLocation"], - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - labelText: "Location", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), + ), + SizedBox(height: 5), + CustomTextFieldWrapper( + isFocused: + focusStates["_deliveryLocation"] ?? + false, // Dropdown doesn't use focus + isDesktop: isDesktop, + width: + isDesktop ? MediaQuery.of(context).size.width * 0.45 : null, + child: SizedBox( + height: 35, + child: TextField( + focusNode: focusNodes["_deliveryLocation"], + controller: textControllers["_deliveryLocation"], + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + labelText: "Location", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), - if (errorMessages["delivery_location"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], + ), + if (errorMessages["delivery_location"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), ], - ), + ], + ), if (isDesktop) - SizedBox( - width: MediaQuery.of(context).size.width * 0.04, - ) + SizedBox(width: MediaQuery.of(context).size.width * 0.04) else - SizedBox( - height: 8, - ), + SizedBox(height: 8), Column( children: [ Row( @@ -1995,9 +2027,10 @@ class _ForexScreenState extends State { Text( "Check If You Don't Have a Forex Account", style: GoogleFonts.poppins( - fontSize: 10, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 10, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), ], ), @@ -2015,9 +2048,7 @@ class _ForexScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( @@ -2026,7 +2057,6 @@ class _ForexScreenState extends State { ), ), SizedBox(width: 10), // Space between buttons - // Save Changes Button ElevatedButton( onPressed: () { @@ -2034,9 +2064,7 @@ class _ForexScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( diff --git a/lib/Screens/itnerary/taxi.dart b/lib/Screens/itnerary/taxi.dart index 6549298..969ba4e 100644 --- a/lib/Screens/itnerary/taxi.dart +++ b/lib/Screens/itnerary/taxi.dart @@ -14,12 +14,13 @@ class TaxiScreen extends StatefulWidget { final Map? selectedItem; final String? loginUser; - TaxiScreen( - {required this.onClose, - this.apiData, - required this.onSavetaxi, - required this.selectedItem, - required this.loginUser}); + TaxiScreen({ + required this.onClose, + this.apiData, + required this.onSavetaxi, + required this.selectedItem, + required this.loginUser, + }); @override _TaxiScreenState createState() => _TaxiScreenState(); @@ -97,13 +98,17 @@ class _TaxiScreenState extends State { super.initState(); _addFocusListener( - _destinationFocusNode, (focus) => _destinationFocus = focus); + _destinationFocusNode, + (focus) => _destinationFocus = focus, + ); _addFocusListener(_locationFocusNode, (focus) => _locationFocus = focus); _addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus); _addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus); _addFocusListener(_taxiReqFocusNode, (focus) => _taxiReqFocused = focus); _addFocusListener( - _numPassengerFocusNode, (focus) => _numPassengerFocus = focus); + _numPassengerFocusNode, + (focus) => _numPassengerFocus = focus, + ); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); _destinationController = initController("destination_city"); @@ -168,7 +173,7 @@ class _TaxiScreenState extends State { "location_of_pickup", "no_of_passengers", "date", - "time" + "time", ]; // Check validation for each field @@ -199,30 +204,33 @@ class _TaxiScreenState extends State { @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isMobile = sizingInfo.isMobile; - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isMobile = sizingInfo.isMobile; + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Container( - // color: Color(0xFFF4F4FB), - child: Form( - key: _formKey, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(28.0), - child: Center( - child: Column(children: _buildAccomadtionForm(isDesktop)), + return Container( + // color: Color(0xFFF4F4FB), + child: Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(28.0), + child: Center( + child: Column(children: _buildAccomadtionForm(isDesktop)), + ), ), - ) - ], + ], + ), ), ), - ), - ); - }); + ); + }, + ); } List _buildAccomadtionForm(bool isDesktop) { @@ -235,7 +243,7 @@ class _TaxiScreenState extends State { List> rowBuilders = [ // _builClassType(isDesktop), - _buildSecondRow(isDesktop) + _buildSecondRow(isDesktop), ]; return [ @@ -253,19 +261,24 @@ class _TaxiScreenState extends State { List _buildFirstRow(isDesktop) { List purposeList = widget.apiData?['taxt_car_type'] ?? []; - List> dropdownItems = purposeList - .map((item) => DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -281,31 +294,28 @@ class _TaxiScreenState extends State { Text( "Taxi Required For", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), isDesktop ? Row(children: _buildTripType(isDesktop)) - : Column(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)), ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Number of Passenger", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -319,8 +329,9 @@ class _TaxiScreenState extends State { style: const TextStyle(fontSize: 12), keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + FilteringTextInputFormatter.allow( + RegExp(r'^\d*\.?\d*$'), + ), // Allow only positive numbers with optional decimal ], decoration: const InputDecoration( labelText: "Number of Passenger", @@ -334,28 +345,21 @@ class _TaxiScreenState extends State { ), if (errorMessages["no_of_passengers"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Car Type", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -369,18 +373,21 @@ class _TaxiScreenState extends State { style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), // Proper padding ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedCarType = newValue; - }); - print( - "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - } - : null, + onChanged: + purposeList.isNotEmpty + ? (newValue) { + setState(() { + selectedCarType = newValue; + }); + print( + "Updating form data: Flight -> trip_type -> ${newValue ?? ""}", + ); + } + : null, items: dropdownItems, ), @@ -388,31 +395,31 @@ class _TaxiScreenState extends State { ), ], ), - if (isDesktop) - SizedBox.shrink() - else - SizedBox( - height: 8, - ), + if (isDesktop) SizedBox.shrink() else SizedBox(height: 8), ]; } List _buildTripType(bool isDesktop) { List purposeList = widget.apiData?['taxi_car_required_for'] ?? []; - List> dropdownItems = purposeList - .map((item) => DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -425,9 +432,10 @@ class _TaxiScreenState extends State { CustomTextFieldWrapper( isFocused: _taxiReqFocused, isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: DropdownButtonFormField( @@ -436,18 +444,21 @@ class _TaxiScreenState extends State { style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), // Proper padding ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedReqTaxi = newValue; - }); - print( - "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - } - : null, + onChanged: + purposeList.isNotEmpty + ? (newValue) { + setState(() { + selectedReqTaxi = newValue; + }); + print( + "Updating form data: Flight -> trip_type -> ${newValue ?? ""}", + ); + } + : null, items: dropdownItems, ), @@ -466,10 +477,11 @@ class _TaxiScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - initialDate: _selectedCheckOutDate != null && - _selectedCheckOutDate!.isAfter(today) - ? _selectedCheckOutDate! - : today, + initialDate: + _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) + ? _selectedCheckOutDate! + : today, firstDate: today, lastDate: DateTime(2100), ); @@ -494,8 +506,13 @@ class _TaxiScreenState extends State { // Formatting time to HH:mm (24-hour format) final now = DateTime.now(); final formattedTime = DateFormat('HH:mm').format( - DateTime(now.year, now.month, now.day, pickedTime.hour, - pickedTime.minute), + DateTime( + now.year, + now.month, + now.day, + pickedTime.hour, + pickedTime.minute, + ), ); _timeController.text = formattedTime; }); @@ -509,9 +526,10 @@ class _TaxiScreenState extends State { Text( "City", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -535,28 +553,21 @@ class _TaxiScreenState extends State { ), if (errorMessages["destination_city"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Location of Pickup", + "Pickup Location", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -569,7 +580,7 @@ class _TaxiScreenState extends State { controller: _locationController, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( - labelText: "Location of Pickup", + labelText: "Pickup Location", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, @@ -580,28 +591,21 @@ class _TaxiScreenState extends State { ), if (errorMessages["location_of_pickup"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Date", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -622,8 +626,11 @@ class _TaxiScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -632,28 +639,21 @@ class _TaxiScreenState extends State { ), if (errorMessages["date"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Time", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -674,8 +674,11 @@ class _TaxiScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.access_time, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -684,10 +687,7 @@ class _TaxiScreenState extends State { ), if (errorMessages["time"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), @@ -702,17 +702,19 @@ class _TaxiScreenState extends State { Text( "Comments", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, @@ -733,9 +735,7 @@ class _TaxiScreenState extends State { ], ), if (isDesktop) Spacer(), - SizedBox( - height: 5, - ), + SizedBox(height: 5), Column( children: [ Row( @@ -756,9 +756,7 @@ class _TaxiScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( @@ -767,7 +765,6 @@ class _TaxiScreenState extends State { ), ), SizedBox(width: 10), // Space between buttons - // Save Changes Button ElevatedButton( onPressed: () { @@ -775,9 +772,7 @@ class _TaxiScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( diff --git a/lib/Screens/itnerary/train.dart b/lib/Screens/itnerary/train.dart index e2ddd7d..3f2e5d8 100644 --- a/lib/Screens/itnerary/train.dart +++ b/lib/Screens/itnerary/train.dart @@ -17,14 +17,15 @@ class TrainScreen extends StatefulWidget { final String? loginUser; final String? tripType; - TrainScreen( - {required this.onClose, - this.apiData, - required this.onSavetrain, - required this.selectedItem, - required this.loginUser, - this.apiDataForClass, - this.tripType}); + TrainScreen({ + required this.onClose, + this.apiData, + required this.onSavetrain, + required this.selectedItem, + required this.loginUser, + this.apiDataForClass, + this.tripType, + }); @override _TrainScreenState createState() => _TrainScreenState(); @@ -232,7 +233,7 @@ class _TrainScreenState extends State { "from_station", "to_station", "date", - "time" + "time", ]; // Check validation for each field @@ -287,51 +288,54 @@ class _TrainScreenState extends State { @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isMobile = sizingInfo.isMobile; - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isMobile = sizingInfo.isMobile; + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; - return Container( - // color: Color(0xFFF4F4FB), - child: Form( - key: _formKey, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - // Align( - // alignment: Alignment.centerRight, - // child: InkWell( - // onTap: () { - // widget.onClose(false); - // }, - // child: Icon( - // Icons.close, - // size: 18, - // color: Color(0xFF575A74), - // ), - // ), - // ), - // Text("Train Booking List", - // style: TextStyle( - // fontSize: 18, - // fontWeight: FontWeight.bold, - // color: Color(0xFF575A74))), - // SizedBox( - // height: 6, - // ), - Padding( - padding: const EdgeInsets.all(28.0), - child: Center( - child: Column(children: _buildAccomadtionForm(isDesktop)), + return Container( + // color: Color(0xFFF4F4FB), + child: Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + // Align( + // alignment: Alignment.centerRight, + // child: InkWell( + // onTap: () { + // widget.onClose(false); + // }, + // child: Icon( + // Icons.close, + // size: 18, + // color: Color(0xFF575A74), + // ), + // ), + // ), + // Text("Train Booking List", + // style: TextStyle( + // fontSize: 18, + // fontWeight: FontWeight.bold, + // color: Color(0xFF575A74))), + // SizedBox( + // height: 6, + // ), + Padding( + padding: const EdgeInsets.all(28.0), + child: Center( + child: Column(children: _buildAccomadtionForm(isDesktop)), + ), ), - ) - ], + ], + ), ), ), - ), - ); - }); + ); + }, + ); } List _buildAccomadtionForm(bool isDesktop) { @@ -344,7 +348,7 @@ class _TrainScreenState extends State { List> rowBuilders = [ _builClassType(isDesktop), - _buildSecondRow(isDesktop) + _buildSecondRow(isDesktop), ]; return [ @@ -367,9 +371,10 @@ class _TrainScreenState extends State { Text( "Train Number", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), isDesktop @@ -377,38 +382,35 @@ class _TrainScreenState extends State { : Column(children: _buildTripType(isDesktop)), if (errorMessages["train_no"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), ]; } List _buildTripType(bool isDesktop) { List purposeList = widget.apiData?['flight_trip_type'] ?? []; - List> dropdownItems = purposeList - .map((item) => DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_value'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -455,10 +457,11 @@ class _TrainScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - initialDate: _selectedCheckOutDate != null && - _selectedCheckOutDate!.isAfter(today) - ? _selectedCheckOutDate! - : today, + initialDate: + _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) + ? _selectedCheckOutDate! + : today, firstDate: today, lastDate: DateTime(2100), ); @@ -483,8 +486,13 @@ class _TrainScreenState extends State { // Formatting time to HH:mm (24-hour format) final now = DateTime.now(); final formattedTime = DateFormat('HH:mm').format( - DateTime(now.year, now.month, now.day, pickedTime.hour, - pickedTime.minute), + DateTime( + now.year, + now.month, + now.day, + pickedTime.hour, + pickedTime.minute, + ), ); _timeController.text = formattedTime; }); @@ -495,19 +503,24 @@ class _TrainScreenState extends State { List purposeList = widget.apiDataForClass?['train_class'] ?? []; - List> dropdownItems = purposeList - .map((item) => DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )) - .toList(); + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -523,9 +536,10 @@ class _TrainScreenState extends State { Text( "Class *", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -543,86 +557,91 @@ class _TrainScreenState extends State { style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), // Proper padding ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedClass = newValue; - }); - } - : null, + onChanged: + purposeList.isNotEmpty + ? (newValue) { + setState(() { + selectedClass = newValue; + }); + } + : null, items: dropdownItems, ), ), ), if (errorMessages["class"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "From", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - isFocused: _fromFocus, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: isCountryLoading - ? Center(child: CircularProgressIndicator()) - : DropdownSearch( + isFocused: _fromFocus, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: + isCountryLoading + ? Center(child: CircularProgressIndicator()) + : DropdownSearch( // selectedItem: selectedFrom != null // ? countryMap[selectedFrom] // : null, - - selectedItem: selectedFrom != null - ? countryMap[ - selectedFrom] // get the display value from code - : null, + selectedItem: + selectedFrom != null + ? countryMap[selectedFrom] // get the display value from code + : null, popupProps: PopupProps.menu( + menuProps: MenuProps(backgroundColor: Colors.white), + constraints: BoxConstraints(maxHeight: 230), showSearchBox: true, searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search ...", - contentPadding: - EdgeInsets.symmetric(horizontal: 10), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), ), ), ), + items: countryMap.values.toList(), + dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 1), - ), - ), - dropdownBuilder: (context, selectedItem) => Align( - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select", - style: TextStyle(fontSize: 12), ), ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select", + style: TextStyle(fontSize: 12), + ), + ), + // onChanged: (String? newValue) { // setState(() { // // selectedFrom[index] = countryMap.entries @@ -636,56 +655,51 @@ class _TrainScreenState extends State { // print(selectedFrom); // }); // }, - onChanged: (String? newValue) { setState(() { - selectedFrom = countryMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; + selectedFrom = + countryMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; }); }, ), - ) - // child: SizedBox( - // height: 40, - // child: TextField( - // focusNode: _fromFocusNode, - // controller: _fromController, - // style: const TextStyle(fontSize: 12), - // decoration: const InputDecoration( - // labelText: "From", - // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - // floatingLabelBehavior: FloatingLabelBehavior.never, - // border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric(vertical: 16), - // ), - // ), - // ), - ), + ), + // child: SizedBox( + // height: 40, + // child: TextField( + // focusNode: _fromFocusNode, + // controller: _fromController, + // style: const TextStyle(fontSize: 12), + // decoration: const InputDecoration( + // labelText: "From", + // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + // floatingLabelBehavior: FloatingLabelBehavior.never, + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + // ), + // ), + // ), + ), if (errorMessages["from_station"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "To", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -693,71 +707,72 @@ class _TrainScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: isCountryLoading - ? Center(child: CircularProgressIndicator()) - : DropdownSearch( - selectedItem: selectedTo != null - ? countryMap[ - selectedTo] // get the display value from code - : null, - popupProps: PopupProps.menu( - showSearchBox: true, - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search ...", - contentPadding: - EdgeInsets.symmetric(horizontal: 10), + child: + isCountryLoading + ? Center(child: CircularProgressIndicator()) + : DropdownSearch( + selectedItem: + selectedTo != null + ? countryMap[selectedTo] // get the display value from code + : null, + popupProps: PopupProps.menu( + menuProps: MenuProps(backgroundColor: Colors.white), + constraints: BoxConstraints(maxHeight: 230), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search ...", + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), + ), ), ), - ), - items: countryMap.values.toList(), - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 1), + items: countryMap.values.toList(), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select", + style: TextStyle(fontSize: 12), + ), + ), + onChanged: (String? newValue) { + setState(() { + selectedTo = + countryMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; + }); + }, ), - dropdownBuilder: (context, selectedItem) => Align( - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select", - style: TextStyle(fontSize: 12), - ), - ), - onChanged: (String? newValue) { - setState(() { - selectedTo = countryMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; - }); - }, - ), ), ), if (errorMessages["to_station"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Date", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -779,8 +794,11 @@ class _TrainScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -789,28 +807,21 @@ class _TrainScreenState extends State { ), if (errorMessages["date"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Time", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -832,8 +843,11 @@ class _TrainScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.access_time, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -842,10 +856,7 @@ class _TrainScreenState extends State { ), if (errorMessages["time"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), @@ -860,9 +871,10 @@ class _TrainScreenState extends State { Text( "Comments", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( @@ -890,9 +902,7 @@ class _TrainScreenState extends State { ], ), if (isDesktop) Spacer(), - SizedBox( - height: 5, - ), + SizedBox(height: 5), Column( children: [ Row( @@ -900,7 +910,7 @@ class _TrainScreenState extends State { children: _handleAction(isDesktop), ), ], - ) + ), ]; } @@ -913,9 +923,7 @@ class _TrainScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( @@ -924,7 +932,6 @@ class _TrainScreenState extends State { ), ), SizedBox(width: 10), // Space between buttons - // Save Changes Button ElevatedButton( onPressed: () { @@ -932,9 +939,7 @@ class _TrainScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( diff --git a/lib/Screens/itnerary/visa.dart b/lib/Screens/itnerary/visa.dart index b8de533..0aa6316 100644 --- a/lib/Screens/itnerary/visa.dart +++ b/lib/Screens/itnerary/visa.dart @@ -438,63 +438,6 @@ class _VisaScreenState extends State { selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Type of Visa", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldWrapper( - isFocused: _tripTypeFocused, - isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, - child: SizedBox( - height: 40, - child: DropdownButtonFormField( - focusNode: _tripTypeFocusNode, // Assign the correct focus node - value: selectedPurpose, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding - ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedPurpose = newValue; - }); - print( - "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - } - : null, - - items: dropdownItems, - ), - ), - ), - if (errorMessages["type_of_visa"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -509,7 +452,10 @@ class _VisaScreenState extends State { CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, - width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, + // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: DropdownSearch( @@ -564,6 +510,65 @@ class _VisaScreenState extends State { ], ], ), + + if (isDesktop) + Spacer() + else + SizedBox( + height: 8, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Type of Visa", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldWrapper( + isFocused: _tripTypeFocused, + isDesktop: isDesktop, + width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, + // width: isDesktop + // ? MediaQuery.of(context).size.width * 0.34 + // : MediaQuery.of(context).size.width * 0.66, + child: SizedBox( + height: 40, + child: DropdownButtonFormField( + focusNode: _tripTypeFocusNode, // Assign the correct focus node + value: selectedPurpose, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding + ), + onChanged: purposeList.isNotEmpty + ? (newValue) { + setState(() { + selectedPurpose = newValue; + }); + print( + "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); + } + : null, + + items: dropdownItems, + ), + ), + ), + if (errorMessages["type_of_visa"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), if (isDesktop) Spacer() else diff --git a/lib/Screens/itnerary_list/accomodation_list.dart b/lib/Screens/itnerary_list/accomodation_list.dart index 432e160..6599b5c 100644 --- a/lib/Screens/itnerary_list/accomodation_list.dart +++ b/lib/Screens/itnerary_list/accomodation_list.dart @@ -245,14 +245,26 @@ class AccomodationListWidget extends StatelessWidget { Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Accomodation"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Accomodation Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteAccommodation(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Accommodation Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/itnerary_list/bus_list.dart b/lib/Screens/itnerary_list/bus_list.dart index 4e20724..e4e097a 100644 --- a/lib/Screens/itnerary_list/bus_list.dart +++ b/lib/Screens/itnerary_list/bus_list.dart @@ -214,16 +214,36 @@ class BusListWidget extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ + Text( + "${item["from"]} - ${item["to"]}", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w800, + ), + ), + Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Bus"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Edit Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteBus(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Bus Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/itnerary_list/flight_list.dart b/lib/Screens/itnerary_list/flight_list.dart index 1c837d8..aaa5b84 100644 --- a/lib/Screens/itnerary_list/flight_list.dart +++ b/lib/Screens/itnerary_list/flight_list.dart @@ -85,7 +85,8 @@ class _FlightListWidgetState extends State { return AlertDialog( title: const Text('Select Trip Type'), content: const Text( - 'Please select a trip type before adding a flight.'), + 'Please select a trip type before adding a flight.', + ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), @@ -118,16 +119,18 @@ class _FlightListWidgetState extends State { // style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), // ), MouseRegion( - cursor: widget.isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, + cursor: + widget.isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, child: GestureDetector( - onTap: widget.isViewMode - ? null - : () { - checkClass(); - print("New data"); - }, + onTap: + widget.isViewMode + ? null + : () { + checkClass(); + print("New data"); + }, child: Row( mainAxisSize: MainAxisSize.min, // Ensures content fits nicely @@ -141,7 +144,7 @@ class _FlightListWidgetState extends State { Icons.add_circle_sharp, size: 30, color: Color(0xFF114D8B), - ) + ), ], ), ), @@ -149,9 +152,9 @@ class _FlightListWidgetState extends State { ], ), const SizedBox(height: 16), - // Scroll behavior based on device - _buildData(context, isDesktop) + // Scroll behavior based on device + _buildData(context, isDesktop), ], ), ), @@ -224,8 +227,11 @@ class _FlightListWidgetState extends State { if (hour == 24 && minute == 0) { // 24:00 is treated as 00:00 on the next day - dateTime = DateTime(now.year, now.month, now.day) - .add(const Duration(days: 1)); + dateTime = DateTime( + now.year, + now.month, + now.day, + ).add(const Duration(days: 1)); } else { if (hour < 0 || hour > 23 || minute < 0 || minute > 59) { throw FormatException("Invalid hour or minute"); @@ -245,7 +251,21 @@ class _FlightListWidgetState extends State { itemCount: filteredList.length, itemBuilder: (context, index) { final item = filteredList[index]; - + String? tripTypeName = ''; + switch (item["trip_type"]?.toString()) { + case "Roundtrip": + tripTypeName = "Round Trip"; + break; + case "Multitrip": + tripTypeName = "Multi-Trip"; + break; + case "Oneway": + tripTypeName = "One-Way"; + break; + default: + tripTypeName = item["trip_type"]?.toString(); + break; + } return Container( margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), decoration: BoxDecoration( @@ -281,7 +301,7 @@ class _FlightListWidgetState extends State { Row( children: [ Text( - item["trip_type"]?.toString() ?? "N/A", + tripTypeName ?? "N/A", style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w800, @@ -290,70 +310,76 @@ class _FlightListWidgetState extends State { Spacer(), GestureDetector( onTap: () => widget.onOpen(true, item, "Flight"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Flight Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => widget.onDeleteFlight(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Flight Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), - Divider( - color: Colors.blueGrey.shade50, - ), + Divider(color: Colors.blueGrey.shade50), SizedBox(height: 4), if (isDesktop) Row( children: [ Expanded( - flex: 2, - child: Text( - "Class", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), + flex: 2, + child: Text( + "Class", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), Expanded( - flex: 4, - child: Text( - "Sector", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), + flex: 4, + child: Text( + "Sector", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), Expanded( - flex: 2, - child: Text( - "Date", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), + flex: 2, + child: Text( + "Date", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), Expanded( - flex: 2, - child: Text( - "Time", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), + flex: 2, + child: Text( + "Time", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), ], ), SizedBox(height: 4), - // Trip Rows + // Trip Rows if (isDesktop) if (item["trips"] != null && item["trips"].isNotEmpty) ...item["trips"].map((trip) { String fromPlaceCountry = countryMap[trip["from_place"]?.toString()] ?? - "Unknown Country"; + "Unknown Country"; String toPlaceCountry = countryMap[trip["to_place"]?.toString()] ?? - "Unknown Country"; + "Unknown Country"; return Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), @@ -373,7 +399,7 @@ class _FlightListWidgetState extends State { Expanded( flex: 4, child: Text( - "$fromPlaceCountry (from) - (to) $toPlaceCountry", + "$fromPlaceCountry (From) - (To) $toPlaceCountry", // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", style: GoogleFonts.poppins( fontSize: 12, @@ -423,15 +449,22 @@ class _FlightListWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildKeyValueRow( - "Class", - getRequestForClass(trip["class"].toString()) ?? - "N/A"), - _buildKeyValueRow("Sector", - "${trip["from_place"] ?? "N/A"} - ${trip["to_place"] ?? "N/A"}"), + "Class", + getRequestForClass(trip["class"].toString()) ?? + "N/A", + ), _buildKeyValueRow( - "Date", formatDate(trip["date"] ?? "")), + "Sector", + "${trip["from_place"] ?? "N/A"} - ${trip["to_place"] ?? "N/A"}", + ), _buildKeyValueRow( - "Time", formatTime(trip["time"] ?? "")), + "Date", + formatDate(trip["date"] ?? ""), + ), + _buildKeyValueRow( + "Time", + formatTime(trip["time"] ?? ""), + ), ], ), ), @@ -462,12 +495,7 @@ class _FlightListWidgetState extends State { ), ), Expanded( - child: Text( - value, - style: GoogleFonts.poppins( - fontSize: 12, - ), - ), + child: Text(value, style: GoogleFonts.poppins(fontSize: 12)), ), ], ), diff --git a/lib/Screens/itnerary_list/forex_list.dart b/lib/Screens/itnerary_list/forex_list.dart index 3b826ed..da9c9a4 100644 --- a/lib/Screens/itnerary_list/forex_list.dart +++ b/lib/Screens/itnerary_list/forex_list.dart @@ -261,14 +261,26 @@ class ForexListWidget extends StatelessWidget { Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Forex"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Forex Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteForex(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Forex Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), item['forex_id'] != null ? IconButton( diff --git a/lib/Screens/itnerary_list/insurance_list.dart b/lib/Screens/itnerary_list/insurance_list.dart index e512edb..9e0b1de 100644 --- a/lib/Screens/itnerary_list/insurance_list.dart +++ b/lib/Screens/itnerary_list/insurance_list.dart @@ -192,16 +192,36 @@ class InsuranceListWidget extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ + Text( + getRequestForInsuranceType(item["type_of_insurance"]!.toString()), + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w800, + ), + ), + Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Insurance"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Insurance Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteInsurance(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Insurance Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), @@ -437,14 +457,26 @@ class InsuranceListWidget extends StatelessWidget { children: [ GestureDetector( onTap: () => onOpen(true, item, "Insurance"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Insurance Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteInsurance(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Insurance Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), IconButton( icon: Icon(Icons.keyboard_arrow_down_outlined, diff --git a/lib/Screens/itnerary_list/miscellaneous_list.dart b/lib/Screens/itnerary_list/miscellaneous_list.dart index ecd1f1d..e9a2279 100644 --- a/lib/Screens/itnerary_list/miscellaneous_list.dart +++ b/lib/Screens/itnerary_list/miscellaneous_list.dart @@ -9,14 +9,15 @@ class MiscellaneousListWidget extends StatelessWidget { final Function(String, bool) onAddNew; final bool isViewMode; - const MiscellaneousListWidget( - {super.key, - required this.miscellaneousList, - required this.onOpen, - required this.onDeleteMiscellaneous, - required this.apiData, - required this.onAddNew, - required this.isViewMode}); + const MiscellaneousListWidget({ + super.key, + required this.miscellaneousList, + required this.onOpen, + required this.onDeleteMiscellaneous, + required this.apiData, + required this.onAddNew, + required this.isViewMode, + }); @override Widget build(BuildContext context) { @@ -34,16 +35,18 @@ class MiscellaneousListWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, + cursor: + isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, child: GestureDetector( - onTap: isViewMode - ? null - : () { - print("New data"); - onAddNew("Miscellaneous", true); - }, + onTap: + isViewMode + ? null + : () { + print("New data"); + onAddNew("Miscellaneous", true); + }, child: Row( mainAxisSize: MainAxisSize.min, // Ensures content fits nicely @@ -57,7 +60,7 @@ class MiscellaneousListWidget extends StatelessWidget { Icons.add_circle_sharp, size: 30, color: Color(0xFF114D8B), - ) + ), ], ), ), @@ -95,7 +98,7 @@ class MiscellaneousListWidget extends StatelessWidget { ), const SizedBox(height: 16), // Scroll behavior based on device - _buildData(context, isDesktop) + _buildData(context, isDesktop), ], ), ), @@ -152,81 +155,99 @@ class MiscellaneousListWidget extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ + Text( + getRequestValue(item["special_request"]?.toString()), + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w800, + ), + ), + Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Miscellaneous"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Miscellaneous Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( - onTap: () => onOpen(true, item, "Miscellaneous"), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + // onTap: () => onOpen(true, item, "Miscellaneous"), + onTap: () => onDeleteMiscellaneous(item), + child: Tooltip( + message: 'Delete Miscellaneous Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), - Divider( - color: Colors.blueGrey.shade50, - ), + Divider(color: Colors.blueGrey.shade50), SizedBox(height: 4), isDesktop ? Row( - children: [ - Expanded( - flex: 2, - child: Text( - "Special Request", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), - Expanded( - flex: 3, - child: Text( - " Comments", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), - ], - ) + children: [ + Expanded( + flex: 2, + child: Text( + "Special Request", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + Expanded( + flex: 3, + child: Text( + " Comments", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + ], + ) : SizedBox.shrink(), isDesktop ? Row( - children: [ - Expanded( - flex: 2, - child: Text( - getRequestValue( - item["special_request"]?.toString()), - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), + children: [ + Expanded( + flex: 2, + child: Text( + getRequestValue( + item["special_request"]?.toString(), + ), + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, ), ), - Expanded( - flex: 3, - child: Text( - item["comments"], - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), + ), + Expanded( + flex: 3, + child: Text( + item["comments"], + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, ), ), - ], - ) + ), + ], + ) : Column( - children: [ - _buildRow( - "Special Request:", - getRequestValue( - item["special_request"]?.toString())), - SizedBox(width: 10), - _buildRow("Comments:", item["comments"] ?? "N/A"), - ], - ), + children: [ + _buildRow( + "Special Request:", + getRequestValue(item["special_request"]?.toString()), + ), + SizedBox(width: 10), + _buildRow("Comments:", item["comments"] ?? "N/A"), + ], + ), ], ), ), @@ -255,31 +276,35 @@ class MiscellaneousListWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: exceedsLimit - ? Tooltip( - message: wrapText(value, 50), - decoration: BoxDecoration( - color: Colors.grey.shade200, // Black background - borderRadius: BorderRadius.circular(6), - ), - textStyle: - TextStyle(color: Colors.black), // Tooltip text color - padding: EdgeInsets.all(8), - preferBelow: false, - child: Text(value.substring(0, limit) + "...", + child: + exceedsLimit + ? Tooltip( + message: wrapText(value, 50), + decoration: BoxDecoration( + color: Colors.grey.shade200, // Black background + borderRadius: BorderRadius.circular(6), + ), + textStyle: TextStyle( + color: Colors.black, + ), // Tooltip text color + padding: EdgeInsets.all(8), + preferBelow: false, + child: Text( + value.substring(0, limit) + "...", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), - overflow: TextOverflow.ellipsis), - ) - : Text( - value, - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, + overflow: TextOverflow.ellipsis, + ), + ) + : Text( + value, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), - ), ), ], ); @@ -306,36 +331,36 @@ class MiscellaneousListWidget extends StatelessWidget { children: [ Text( title, - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), + style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600), ), SizedBox(width: 8), Expanded( - child: exceedsLimit - ? Tooltip( - message: wrapText(value, 50), - decoration: BoxDecoration( - color: Colors.grey.shade200, // Black background - borderRadius: BorderRadius.circular(6), - ), - textStyle: TextStyle( - color: Colors.black, fontSize: 12), // Tooltip text color - padding: EdgeInsets.all(8), - preferBelow: false, - child: Text( - value.substring(0, limit) + "...", - overflow: TextOverflow.ellipsis, - ), - ) - : Text(value), + child: + exceedsLimit + ? Tooltip( + message: wrapText(value, 50), + decoration: BoxDecoration( + color: Colors.grey.shade200, // Black background + borderRadius: BorderRadius.circular(6), + ), + textStyle: TextStyle( + color: Colors.black, + fontSize: 12, + ), // Tooltip text color + padding: EdgeInsets.all(8), + preferBelow: false, + child: Text( + value.substring(0, limit) + "...", + overflow: TextOverflow.ellipsis, + ), + ) + : Text(value), ), ], ); } -// + // // Widget _buildData(BuildContext context) { // return Container( // width: MediaQuery.of(context).size.width, diff --git a/lib/Screens/itnerary_list/taxi_list.dart b/lib/Screens/itnerary_list/taxi_list.dart index 3f3d4b6..8059f4d 100644 --- a/lib/Screens/itnerary_list/taxi_list.dart +++ b/lib/Screens/itnerary_list/taxi_list.dart @@ -11,14 +11,15 @@ class TaxiListWidget extends StatelessWidget { final Function(String, bool) onAddNew; final bool isViewMode; - const TaxiListWidget( - {super.key, - required this.taxiList, - required this.apiData, - required this.onOpen, - required this.onDeleteTaxi, - required this.onAddNew, - required this.isViewMode}); + const TaxiListWidget({ + super.key, + required this.taxiList, + required this.apiData, + required this.onOpen, + required this.onDeleteTaxi, + required this.onAddNew, + required this.isViewMode, + }); @override Widget build(BuildContext context) { @@ -36,17 +37,19 @@ class TaxiListWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, + cursor: + isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, child: GestureDetector( - onTap: isViewMode - ? null - : () { - print("New data"); - onAddNew("Taxi", true); - }, + onTap: + isViewMode + ? null + : () { + print("New data"); + onAddNew("Taxi", true); + }, child: Row( mainAxisSize: MainAxisSize.min, // Ensures content fits nicely @@ -60,7 +63,7 @@ class TaxiListWidget extends StatelessWidget { Icons.add_circle_sharp, size: 30, color: Color(0xFF114D8B), - ) + ), ], ), ), @@ -99,9 +102,9 @@ class TaxiListWidget extends StatelessWidget { ], ), const SizedBox(height: 16), - // Scroll behavior based on device - _buildData(context, isDesktop) + // Scroll behavior based on device + _buildData(context, isDesktop), ], ), ), @@ -187,8 +190,11 @@ class TaxiListWidget extends StatelessWidget { if (hour == 24 && minute == 0) { // 24:00 is treated as 00:00 on the next day - dateTime = DateTime(now.year, now.month, now.day) - .add(const Duration(days: 1)); + dateTime = DateTime( + now.year, + now.month, + now.day, + ).add(const Duration(days: 1)); } else { if (hour < 0 || hour > 23 || minute < 0 || minute > 59) { throw FormatException("Invalid hour or minute"); @@ -238,127 +244,136 @@ class TaxiListWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ Text( - "${getRequestForTaxiClass(item["car_type"]!.toString())} For ${item["no_of_passengers"]!} Passenger", - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w800, - )), + "${getRequestForTaxiClass(item["car_type"]!.toString())} For ${item["no_of_passengers"]!} Passenger", + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w800, + ), + ), Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Taxi"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Taxi Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( - onTap: () => (item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + onTap: () => onDeleteTaxi(item), + child: Tooltip( + message: 'Delete Taxi Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), - Divider( - color: Colors.blueGrey.shade50, - ), + Divider(color: Colors.blueGrey.shade50), SizedBox(height: 4), isDesktop ? Row( - children: [ - Expanded( - flex: 2, - child: Text( - "City", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), - Expanded( - flex: 2, - child: Text( - " Location", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), - Expanded( - flex: 2, - child: Text( - " Date", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), - Expanded( - flex: 2, - child: Text( - "Comments", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), - ], - ) + children: [ + Expanded( + flex: 2, + child: Text( + "City", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + Expanded( + flex: 2, + child: Text( + " Location", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + Expanded( + flex: 2, + child: Text( + " Date", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + Expanded( + flex: 2, + child: Text( + "Comments", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + ], + ) : SizedBox.shrink(), SizedBox(height: 4), isDesktop ? Row( - children: [ - Expanded( - flex: 2, - child: Text( - "${item["destination_city"]}", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), + children: [ + Expanded( + flex: 2, + child: Text( + "${item["destination_city"]}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, ), ), - SizedBox(width: 10), - Expanded( - flex: 2, - child: Text( - "${item["location_of_pickup"]}", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + "${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( + flex: 2, + child: Text( + "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, ), ), - SizedBox(width: 10), - Expanded( - flex: 2, - child: _buildComments( - " Comments:", item["comments"] ?? "N/A"), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: _buildComments( + " Comments:", + item["comments"] ?? "N/A", ), - ], - ) + ), + ], + ) : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildRow("City:", item["destination_city"]), - SizedBox(width: 10), - _buildRow("Pickup:", item["location_of_pickup"]!), - SizedBox(width: 10), - _buildDateTimeRow( - "Date:", - formatDate(item["date"]!), - formatTime(item["time"]!), - ), - _buildRow("TaxiFor:", item["car_required_for"]!), - _buildRow("Comments:", item["comments"] ?? "N/A"), - ], - ) + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildRow("City:", item["destination_city"]), + SizedBox(width: 10), + _buildRow("Pickup:", item["location_of_pickup"]!), + SizedBox(width: 10), + _buildDateTimeRow( + "Date:", + formatDate(item["date"]!), + formatTime(item["time"]!), + ), + _buildRow("TaxiFor:", item["car_required_for"]!), + _buildRow("Comments:", item["comments"] ?? "N/A"), + ], + ), ], ), ), @@ -387,31 +402,35 @@ class TaxiListWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: exceedsLimit - ? Tooltip( - message: wrapText(value, 50), - decoration: BoxDecoration( - color: Colors.grey.shade200, // Black background - borderRadius: BorderRadius.circular(6), - ), - textStyle: - TextStyle(color: Colors.black), // Tooltip text color - padding: EdgeInsets.all(8), - preferBelow: false, - child: Text(value.substring(0, limit) + "...", + child: + exceedsLimit + ? Tooltip( + message: wrapText(value, 50), + decoration: BoxDecoration( + color: Colors.grey.shade200, // Black background + borderRadius: BorderRadius.circular(6), + ), + textStyle: TextStyle( + color: Colors.black, + ), // Tooltip text color + padding: EdgeInsets.all(8), + preferBelow: false, + child: Text( + value.substring(0, limit) + "...", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), - overflow: TextOverflow.ellipsis), - ) - : Text( - value, - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, + overflow: TextOverflow.ellipsis, + ), + ) + : Text( + value, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), - ), ), ], ); @@ -438,34 +457,30 @@ class TaxiListWidget extends StatelessWidget { children: [ Text( title, - style: TextStyle( - fontWeight: FontWeight.w600, - fontSize: 12, - ), + style: TextStyle(fontWeight: FontWeight.w600, fontSize: 12), ), SizedBox(width: 8), Expanded( - child: exceedsLimit - ? Tooltip( - message: wrapText(value, 50), - decoration: BoxDecoration( - color: Colors.grey.shade200, // Black background - borderRadius: BorderRadius.circular(6), - ), - textStyle: - TextStyle(color: Colors.black), // Tooltip text color - padding: EdgeInsets.all(8), - preferBelow: false, - child: Text(value.substring(0, limit) + "...", + child: + exceedsLimit + ? Tooltip( + message: wrapText(value, 50), + decoration: BoxDecoration( + color: Colors.grey.shade200, // Black background + borderRadius: BorderRadius.circular(6), + ), + textStyle: TextStyle( + color: Colors.black, + ), // Tooltip text color + padding: EdgeInsets.all(8), + preferBelow: false, + child: Text( + value.substring(0, limit) + "...", style: TextStyle(fontSize: 12), - overflow: TextOverflow.ellipsis), - ) - : Text( - value, - style: GoogleFonts.poppins( - fontSize: 12, - ), - ), + overflow: TextOverflow.ellipsis, + ), + ) + : Text(value, style: GoogleFonts.poppins(fontSize: 12)), ), ], ); @@ -477,19 +492,11 @@ class TaxiListWidget extends StatelessWidget { children: [ Text( title, - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), + style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600), ), SizedBox(width: 8), Expanded( - child: Text( - "$date, $time", - style: GoogleFonts.poppins( - fontSize: 12, - ), - ), + child: Text("$date, $time", style: GoogleFonts.poppins(fontSize: 12)), ), ], ); diff --git a/lib/Screens/itnerary_list/train_list.dart b/lib/Screens/itnerary_list/train_list.dart index cdacc3c..bf3913f 100644 --- a/lib/Screens/itnerary_list/train_list.dart +++ b/lib/Screens/itnerary_list/train_list.dart @@ -333,14 +333,26 @@ class _TrainListWidgetState extends State { Spacer(), GestureDetector( onTap: () => widget.onOpen(true, item, "Train"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Train Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => widget.onDeleteTrain(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Train Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/itnerary_list/visa_list.dart b/lib/Screens/itnerary_list/visa_list.dart index cd7a441..9429a8e 100644 --- a/lib/Screens/itnerary_list/visa_list.dart +++ b/lib/Screens/itnerary_list/visa_list.dart @@ -291,16 +291,36 @@ class VisaListWidget extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ + Text( + getRequestForVisa(item["type_of_visa"]!.toString()), + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w800, + ), + ), + Spacer(), GestureDetector( onTap: () => onOpen(true, item, "Visa"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + child: Tooltip( + message: 'Edit Visa Details', + child: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + ), ), SizedBox(width: 10), GestureDetector( onTap: () => onDeleteMiscellaneous(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + child: Tooltip( + message: 'Delete Visa Details', + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15, + ), + ), ), ], ), diff --git a/lib/Screens/myTemplates/Tempale b/lib/Screens/myTemplates/Tempale new file mode 100644 index 0000000..660b989 --- /dev/null +++ b/lib/Screens/myTemplates/Tempale @@ -0,0 +1,562 @@ +import 'dart:convert'; +import 'dart:io' as io show Directory, File; +import 'package:flutter/cupertino.dart' as dom; +import 'package:flutter_quill/flutter_quill.dart' hide Text; +import 'package:flutter/material.dart'; +import 'package:flutter_quill/flutter_quill.dart' hide Text; +import 'package:flutter_quill/quill_delta.dart'; +import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart'; +import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart'; + +import 'package:flutter_quill/flutter_quill.dart' as quill; +import 'package:html/parser.dart' show parse; +import 'package:html/dom.dart' as dom hide Element; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_quill/flutter_quill.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_quill/flutter_quill_internal.dart'; +import 'package:flutter_quill/quill_delta.dart'; + +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:path/path.dart' as path; +import 'package:responsive_builder/responsive_builder.dart'; + +import '../../config/apiUrl.dart'; +import '../../routes/custom_appBar.dart'; +import '../../routes/custom_drawer.dart'; +import '../../services/apiService.dart'; +import '../../utils/auth_utils.dart'; +import '../../widgets/custom_user_travel.dart'; + +class Template extends StatefulWidget { + final Map? templateData; + + const Template({super.key, required this.templateData}); + + static Template fromState(GoRouterState state) { + return Template(templateData: state.extra as Map?); + } + + @override + TemplateState createState() => TemplateState(); +} + +class TemplateState extends State