From ceb6aece50bb5dc0a3e2d25f442b0356aaff387a Mon Sep 17 00:00:00 2001 From: venbaittech Date: Tue, 22 Apr 2025 12:56:30 +0530 Subject: [PATCH] ui changes --- lib/Screens/approvals/approval_list.dart | 64 +- lib/Screens/group/group.dart | 6 + lib/Screens/group/groupList.dart | 92 +- lib/Screens/organization/mailSettings.dart | 7 +- lib/Screens/organization/orgSetup.dart | 583 ++++---- lib/Screens/plans/create_plans.dart | 300 ++-- .../plans/dynamic_itinerary_stepper.dart | 113 +- lib/Screens/plans/list_plans.dart | 98 +- lib/Screens/policy/policy.dart | 648 ++++++--- lib/Screens/policy/policyCriteria.dart | 143 +- lib/Screens/policy/policy_list.dart | 118 +- .../create_user/create_user.dart | 1251 +++++++++-------- lib/routes/custom_drawer.dart | 174 ++- lib/routes/custom_router.dart | 5 +- lib/services/apiService.dart | 38 + lib/utils/auth_utils.dart | 15 + pubspec.yaml | 1 - 17 files changed, 2302 insertions(+), 1354 deletions(-) diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 72cf230..5a8623b 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -434,26 +434,43 @@ class _ApprovalListState extends State { fontSize: 13, fontFamily: "Archivo", ))), - DataCell(Container( - padding: const EdgeInsets.symmetric( - vertical: 4, horizontal: 10), - decoration: BoxDecoration( - color: plan.status == "Active" - ? layoutColor - : Colors.grey.shade50, - borderRadius: BorderRadius.circular(10), - ), - child: Text( - plan.statusValue, - style: TextStyle( - color: plan.status == "Active" - ? Colors.white - : Colors.grey, - fontSize: 13, - fontWeight: FontWeight.bold, + DataCell( + Container( + width: double + .infinity, // Set your desired fixed size (equal width and height) + height: 25, + alignment: Alignment.center, + decoration: BoxDecoration( + color: plan.statusValue == + "Partially Approved" + ? Colors.yellow.shade100 + : plan.statusValue == "Approved" + ? Colors.green.shade100 + : plan.statusValue == "Completed" + ? Colors.green.shade500 + : plan.statusValue == "Rejected" + ? Colors.red.shade100 + : Colors.grey.shade100, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + plan.statusValue, + textAlign: TextAlign.center, + style: TextStyle( + color: (plan.statusValue == + "Partially Approved" || + plan.statusValue == "Approved" || + plan.statusValue == "Rejected") + ? Colors.black + : plan.statusValue == "Completed" + ? Colors.white + : Colors.grey, + fontSize: 12, + fontWeight: FontWeight.w400, + ), ), ), - )), + ), DataCell(Row(children: [ IconButton( icon: const Icon( @@ -480,6 +497,17 @@ class _ApprovalListState extends State { // color: Colors.green), // onPressed: () => viewPlanforApprover(plan.planId, // isViewMode: false) ), + + SizedBox( + width: 5, + ), + GestureDetector( + onTap: () => (), + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15), + ), ])), ]); }).toList(), diff --git a/lib/Screens/group/group.dart b/lib/Screens/group/group.dart index a314944..c8b1352 100644 --- a/lib/Screens/group/group.dart +++ b/lib/Screens/group/group.dart @@ -396,6 +396,9 @@ class _groupState extends State { child: TextField( controller: controllers["name"], style: TextStyle(fontSize: 12), + onChanged: (value) { + _clearError("name"); + }, decoration: InputDecoration( labelText: "group name", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), @@ -436,6 +439,9 @@ class _groupState extends State { height: 40, child: TextField( controller: controllers["description"], + onChanged: (value) { + _clearError("description"); + }, style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "description", diff --git a/lib/Screens/group/groupList.dart b/lib/Screens/group/groupList.dart index 5e2eff6..fad04aa 100644 --- a/lib/Screens/group/groupList.dart +++ b/lib/Screens/group/groupList.dart @@ -1,8 +1,12 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:frontend/Screens/group/group.dart'; import 'package:go_router/go_router.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'; @@ -57,10 +61,59 @@ class _GroupListState extends State { } } - void deleteGroup(int groupId) { - setState(() { - apiAllGroups?.removeWhere((group) => group['group_id'] == groupId); - }); + 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 deleteGroupFromApi(int groupId) async { @@ -192,7 +245,8 @@ class _GroupListState extends State { final group = apiAllGroups![index]; return Card( // color: bodyColor, - color: Color(0xFFF5F5F5), + // color: Color(0xFFF5F5F5), + color: Colors.white, margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10), child: Padding( padding: const EdgeInsets.all(12.0), @@ -209,13 +263,31 @@ class _GroupListState extends State { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - TextButton( - onPressed: () { + GestureDetector( + onTap: () { context.go("/CreateGroup", extra: group); - - print("Edit ${group['group_id']} $group"); }, - child: Text("Edit"), + 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/organization/mailSettings.dart b/lib/Screens/organization/mailSettings.dart index 48cbf97..635dd62 100644 --- a/lib/Screens/organization/mailSettings.dart +++ b/lib/Screens/organization/mailSettings.dart @@ -609,12 +609,15 @@ class _MailSettingState extends State { borderRadius: BorderRadius.circular(8), side: BorderSide(color: Color(0xFF114D8B), width: 2), ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12), ), onPressed: () { handleTestMailSubmit(); }, - child: Text("Test Email")) + child: Text( + "Test Email", + style: TextStyle(fontSize: 12), + )) ], ), ]; diff --git a/lib/Screens/organization/orgSetup.dart b/lib/Screens/organization/orgSetup.dart index 229e8f2..037f497 100644 --- a/lib/Screens/organization/orgSetup.dart +++ b/lib/Screens/organization/orgSetup.dart @@ -344,6 +344,41 @@ class _OrgSetUpState extends State { } Widget buildOrganizationLayout(isDesktop) { + return Container( + decoration: BoxDecoration( + // color: Colors.amber, + color: bodyColor, + border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Container( + color: bodyColor, + child: buildOrgLayout(isDesktop), + ), + ), + Container( + padding: const EdgeInsets.all(8), + color: Colors.white, + child: isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.end, + // children: [Text("Button")], + children: + _buildSubmit(isDesktop, isViewMode, layoutColor), + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: + _buildSubmit(isDesktop, isViewMode, layoutColor), + )) + ], + ), + ); + } + + Widget buildOrgLayout(bool isDesktop) { Future _pickImage() async { final picker = ImagePicker(); final XFile? pickedFile = @@ -365,317 +400,297 @@ class _OrgSetUpState extends State { } return Container( + margin: isDesktop + ? EdgeInsets.all(10.0) + : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), + height: isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, decoration: BoxDecoration( - // color: Colors.amber, - color: bodyColor, - border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Container( - // color: bodyColor, - // color: Colors.grey, - width: double.infinity, - // height: MediaQuery.of(context).size.height, - padding: const EdgeInsets.all(8), + border: isDesktop + ? Border.all( + width: 2, + color: Color(0xFFF7F7FB), + ) + : null, + color: Colors.white, + // color: Color(0xFFF7F7FB), + + // color: Colors.amber, + ), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + padding: const EdgeInsets.all(20), + // height: MediaQuery.of(context).size.height * 0.8, + color: Colors.white, child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Container( - padding: const EdgeInsets.all(20), - // height: MediaQuery.of(context).size.height * 0.8, - color: Colors.white, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - color: Colors.white, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Create Organization", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600), - ), - ], - ), - ), - Container( - color: Colors.white, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Padding( - padding: EdgeInsets.only(top: 1.0), - child: Text( - "Name:", - style: TextStyle( - fontFamily: "Archivo", - fontSize: 14, - fontWeight: FontWeight.w600, - color: Color(0xFF212121)), - ), - ), - SizedBox(width: 8), - Expanded( - child: TextFormField( - controller: _orgNameController, - style: TextStyle( - fontSize: 16, - color: Color(0xFF114D8B), - ), - decoration: InputDecoration( - hintText: "Enter Organization Name", - hintStyle: TextStyle( - fontSize: 14, color: Colors.grey), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: InputBorder.none, - isDense: true, - // contentPadding: - // EdgeInsets.symmetric(vertical: 14), - ), - // textAlignVertical: TextAlignVertical.center, - ), - ), - Spacer(), - GestureDetector( - onTap: _pickImage, - child: _imageBytes != null - ? ClipOval( - child: Image.memory( - _imageBytes!, - width: 50, - height: 50, - fit: BoxFit.cover, - ), - ) - : selectedOrg?['logo'] != null - ? ClipOval( - child: Image.network( - selectedOrg!['logo'], - width: 50, - height: 50, - fit: BoxFit.cover, - errorBuilder: (context, error, - stackTrace) { - return const CircleAvatar( - radius: 20, - backgroundColor: - Colors.redAccent, - child: Icon(Icons.error, - size: 10), - ); - }, - ), - ) - : const CircleAvatar( - radius: 20, - backgroundColor: Colors.amber, - child: Icon(Icons.add_a_photo, - size: 10), - ), - ), - ], - ), - ), - SizedBox( - height: 5, - ), - Container( - color: Colors.white, - child: Column( - children: [ - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "Mail Settings", - style: TextStyle( - fontFamily: "Archivo", - fontSize: 14, - fontWeight: FontWeight.w600, - color: Color(0xFF212121)), - ), - - // GestureDetector( - // onTap: () { - // setState(() { - // showMail = !showMail; - // }); - // }, - // child: Icon( - // Icons.keyboard_arrow_down_outlined, - // color: Color(0xFF114D8B), - // size: 30, - // ), - // ), - ], - ), - // if (showMail) - - SizedBox( - height: 3, - ), - Container( - // width: double.infinity, - decoration: BoxDecoration( - border: Border.all( - color: Color(0xFFF5F5F5), - // color: bodyColor ?? Colors.grey, - width: 1.0, - ), - // color: bodyColor, - color: Color(0xFFF5F5F5), - ), - child: Row( - mainAxisAlignment: isDesktop - ? MainAxisAlignment.start - : MainAxisAlignment.center, - children: [ - mailConfig['sender_email'] != null - ? MailSetting( - isDesktop: isDesktop, - initialMailData: mailConfig, - onMailDataChanged: - (updatedData) { - // You can setState here or do something else with updatedData - print( - "Updated Mail Data: $updatedData"); - - mailConfig = updatedData; - }, - ) - : CircularProgressIndicator(), - ], - )) - ], - )), - SizedBox( - height: 5, - ), - - Text( - "Services", + Container( + color: Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + selectedOrg != null && selectedOrg!.isNotEmpty + ? "Update Organization" + : "Create Organization", + style: TextStyle( + fontSize: 15, fontWeight: FontWeight.w800), + ), + ], + ), + ), + Container( + color: Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.only(top: 1.0), + child: Text( + "Name:", style: TextStyle( fontFamily: "Archivo", fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF212121)), ), - SizedBox( - height: 5, - ), - Container( - decoration: BoxDecoration( - border: Border.all(color: Color(0xFFF4F4FB)), - borderRadius: BorderRadius.circular(1), - // color: bodyColor, - color: Color(0xFFF5F5F5), + ), + SizedBox(width: 8), + Expanded( + child: TextFormField( + controller: _orgNameController, + style: TextStyle( + fontSize: 16, + color: Color(0xFF114D8B), ), - padding: EdgeInsets.only( - left: 5, right: 5, top: 15, bottom: 15), - child: isDesktop - ? Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - // mainAxisSize: MainAxisSize.min, - children: _buildOptions(), - ) - : Expanded( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: _buildOptions(), - ), - ), + decoration: InputDecoration( + hintText: "Enter Organization Name", + hintStyle: + TextStyle(fontSize: 14, color: Colors.grey), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: InputBorder.none, + isDense: true, + // contentPadding: + // EdgeInsets.symmetric(vertical: 14), + ), + // textAlignVertical: TextAlignVertical.center, + ), + ), + Spacer(), + GestureDetector( + onTap: _pickImage, + child: _imageBytes != null + ? ClipOval( + child: Image.memory( + _imageBytes!, + width: 50, + height: 50, + fit: BoxFit.cover, ), - ), - SizedBox( - height: 5, - ), - - Column( - crossAxisAlignment: CrossAxisAlignment.start, + ) + : selectedOrg?['logo'] != null + ? ClipOval( + child: Image.network( + selectedOrg!['logo'], + width: 50, + height: 50, + fit: BoxFit.cover, + errorBuilder: + (context, error, stackTrace) { + return const CircleAvatar( + radius: 20, + backgroundColor: Colors.redAccent, + child: Icon(Icons.error, size: 10), + ); + }, + ), + ) + : const CircleAvatar( + radius: 20, + backgroundColor: Colors.amber, + child: Icon(Icons.add_a_photo, size: 10), + ), + ), + ], + ), + ), + SizedBox( + height: 5, + ), + Container( + color: Colors.white, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "Choose Theme", + "Mail Settings", style: TextStyle( fontFamily: "Archivo", fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF212121)), ), - Container( - decoration: BoxDecoration( - // border: Border.all(color: Color(0xFFF4F4FB)), - borderRadius: BorderRadius.circular(1), - // color: Color(0xFFF4F4FB), - ), - padding: EdgeInsets.only( - left: 5, right: 5, top: 15, bottom: 5), - child: layoutColor != null && bodyColor != null - ? ColorThemePickerWidget( - initialLayoutColor: layoutColor, - initialBodyColor: bodyColor, - onLayoutColorSelected: - (Color selectedLayoutColor) { - setState(() { - layoutColor = selectedLayoutColor; - }); - }, - onBodyColorSelected: - (Color selectedBodyColor) { - setState(() { - bodyColor = selectedBodyColor; - }); - }, - ) - : CircularProgressIndicator(), - ), + + // GestureDetector( + // onTap: () { + // setState(() { + // showMail = !showMail; + // }); + // }, + // child: Icon( + // Icons.keyboard_arrow_down_outlined, + // color: Color(0xFF114D8B), + // size: 30, + // ), + // ), ], ), + // if (showMail) - // isDesktop - // ? Row( - // mainAxisAlignment: MainAxisAlignment.end, - // children: _buildSubmit(isDesktop), - // ) - // : Row( - // mainAxisAlignment: MainAxisAlignment.center, - // children: _buildSubmit(isDesktop), - // ) + SizedBox( + height: 3, + ), + Container( + // width: double.infinity, + decoration: BoxDecoration( + border: Border.all( + color: Color(0xFFF5F5F5), + // color: bodyColor ?? Colors.grey, + width: 1.5, + ), + // color: bodyColor, + color: Colors.white70, + // color: Color(0xFFF5F5F5), + ), + child: Row( + mainAxisAlignment: isDesktop + ? MainAxisAlignment.start + : MainAxisAlignment.center, + children: [ + mailConfig['sender_email'] != null + ? MailSetting( + isDesktop: isDesktop, + initialMailData: mailConfig, + onMailDataChanged: (updatedData) { + // You can setState here or do something else with updatedData + print( + "Updated Mail Data: $updatedData"); + + mailConfig = updatedData; + }, + ) + : CircularProgressIndicator(), + ], + )) ], - ), - ), + )), + SizedBox( + height: 5, ), + + Text( + "Services", + style: TextStyle( + fontFamily: "Archivo", + fontSize: 14, + fontWeight: FontWeight.w600, + color: Color(0xFF212121)), + ), + SizedBox( + height: 5, + ), + Container( + decoration: BoxDecoration( + border: Border.all(color: Color(0xFFF4F4FB)), + borderRadius: BorderRadius.circular(1), + // color: bodyColor, + // color: Color(0xFFF5F5F5), + color: Colors.white), + padding: + EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 5), + child: isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // mainAxisSize: MainAxisSize.min, + children: _buildOptions(), + ) + : Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: _buildOptions(), + ), + ), + ), + ), + SizedBox( + height: 5, + ), + + Row( + // crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Choose Theme", + style: TextStyle( + fontFamily: "Archivo", + fontSize: 14, + fontWeight: FontWeight.w600, + color: Color(0xFF212121)), + ), + Container( + decoration: BoxDecoration( + // border: Border.all(color: Color(0xFFF4F4FB)), + borderRadius: BorderRadius.circular(1), + // color: Color(0xFFF4F4FB), + ), + padding: EdgeInsets.only( + left: 5, right: 5, top: 15, bottom: 5), + child: layoutColor != null && bodyColor != null + ? ColorThemePickerWidget( + initialLayoutColor: layoutColor, + initialBodyColor: bodyColor, + onLayoutColorSelected: + (Color selectedLayoutColor) { + setState(() { + layoutColor = selectedLayoutColor; + }); + }, + onBodyColorSelected: (Color selectedBodyColor) { + setState(() { + bodyColor = selectedBodyColor; + }); + }, + ) + : CircularProgressIndicator(), + ), + ], + ), + + // isDesktop + // ? Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: _buildSubmit(isDesktop), + // ) + // : Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: _buildSubmit(isDesktop), + // ) ], ), ), - ), - Container( - padding: const EdgeInsets.all(8), - color: Colors.white, - child: isDesktop - ? Row( - mainAxisAlignment: MainAxisAlignment.end, - // children: [Text("Button")], - children: - _buildSubmit(isDesktop, isViewMode, layoutColor), - ) - : Row( - mainAxisAlignment: MainAxisAlignment.center, - children: - _buildSubmit(isDesktop, isViewMode, layoutColor), - )) - ], + ], + ), ), ); } diff --git a/lib/Screens/plans/create_plans.dart b/lib/Screens/plans/create_plans.dart index fafb225..250210e 100644 --- a/lib/Screens/plans/create_plans.dart +++ b/lib/Screens/plans/create_plans.dart @@ -273,6 +273,9 @@ class CreateNewPlansState extends State { List? apiCountryData; List? apiCostData; // Store API response here bool isLoading = true; // Track loading state + String? TripPlanAction; + bool showDomestic = false; + bool showInternational = false; String? orgId; String? planUsrId; @@ -469,6 +472,24 @@ class CreateNewPlansState extends State { } } + void setTripPlanAction() { + setState(() { + if (TripPlanAction == "Plan Creation Not Allowed") { + showDomestic = false; + showInternational = false; + } else if (TripPlanAction == "Only Domestic Plan Creation Allowed") { + showDomestic = true; + showInternational = false; + } else if (TripPlanAction == "Only International Plan Creation Allowed") { + showDomestic = false; + showInternational = true; + } else if (TripPlanAction == "Both Type Plan Creation Allowed") { + showDomestic = true; + showInternational = true; + } + }); + } + void getSelectedPlanFor() { if (!mounted) return; @@ -492,7 +513,8 @@ class CreateNewPlansState extends State { void fetchUserDetails() async { final details = await getUserDetails(); - + TripPlanAction = await getTripPlanAction(); + print("TripPlanAction- $TripPlanAction"); print("details- $details"); if (details != null) { @@ -505,6 +527,7 @@ class CreateNewPlansState extends State { orgId = await getOrgId(); print("userDetails - $selfId"); getSelectedPlanFor(); + setTripPlanAction(); } Future getToken() async { @@ -692,7 +715,8 @@ class CreateNewPlansState extends State { } final requiredFields = { - "trip_type": _selectedTripType, + if (TripPlanAction != "Plan Creation Not Allowed") // + "trip_type": _selectedTripType, "cost_center_id": selectedCostCenterId, "functional_department": selectedFuncDept, "purpose_of_travel": selectedPurpose, @@ -1193,7 +1217,7 @@ class CreateNewPlansState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Trip Type *", // Your label + "Trip Type ( $TripPlanAction )", // Your label style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, @@ -1609,142 +1633,148 @@ class CreateNewPlansState extends State { List _buildTripType(bool isMobile) { return [ - CustomTextFieldWrapper( - color: Color(0xFFF4F4FB), - layoutColor: widget.layoutColor, - borderRadius: BorderRadius.circular(25), - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), - width: 130, - isFocused: _selectedTripType == "1", - isDesktop: widget.isDesktop, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Domestic", - style: TextStyle( - color: _selectedTripType == "1" ? Colors.white : Colors.black, - fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null, - fontSize: 13), - ), - - // Radio( - // activeColor: Colors.blueAccent, - // // contentPadding: EdgeInsets.zero, - // visualDensity: VisualDensity.compact, - // // dense: true, - // value: "1", - // groupValue: _selectedTripType, - // onChanged: widget.isViewMode - // ? null - // : (value) { - // setState(() { - // _selectedTripType = value!; - // }); - // }, - // ), - - GestureDetector( - onTap: widget.isViewMode - ? null - : () { - setState(() { - _selectedTripType = "1"; - }); - }, - child: Container( - width: 15, - height: 15, - decoration: BoxDecoration( - shape: BoxShape.rectangle, - // color: _selectedOption == option["value"] - // ? Colors.blueAccent - // : Colors.transparent, - borderRadius: BorderRadius.circular(4), // Rounded rectangle - border: Border.all( + if (showDomestic == true) + CustomTextFieldWrapper( + color: Color(0xFFF4F4FB), + layoutColor: widget.layoutColor, + borderRadius: BorderRadius.circular(25), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + width: 130, + isFocused: _selectedTripType == "1", + isDesktop: widget.isDesktop, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Domestic", + style: TextStyle( color: _selectedTripType == "1" ? Colors.white : Colors.black, - width: _selectedTripType == "1" ? 2 : 1, - ), - ), - child: _selectedTripType == "1" - ? Icon(Icons.rectangle, size: 8, color: Colors.white) - : null, // Add checkmark if selected + fontWeight: + _selectedTripType == "1" ? FontWeight.w600 : null, + fontSize: 13), ), - ) - ], - ), - ), - SizedBox(width: 20), - CustomTextFieldWrapper( - color: Color(0xFFF4F4FB), - layoutColor: widget.layoutColor, - borderRadius: BorderRadius.circular(25), - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - width: 150, - // padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2), - isFocused: _selectedTripType == "2", - isDesktop: widget.isDesktop, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "International", - style: TextStyle( - fontSize: 13, - color: _selectedTripType == "2" ? Colors.white : Colors.black, - fontWeight: _selectedTripType == "2" ? FontWeight.w600 : null, - ), - ), - GestureDetector( - onTap: widget.isViewMode - ? null - : () { - setState(() { - _selectedTripType = "2"; - }); - }, - child: Container( - width: 15, - height: 15, - decoration: BoxDecoration( - shape: BoxShape.rectangle, - // color: _selectedOption == option["value"] - // ? Colors.blueAccent - // : Colors.transparent, - borderRadius: BorderRadius.circular(4), // Rounded rectangle - border: Border.all( - color: - _selectedTripType == "2" ? Colors.white : Colors.black, - width: _selectedTripType == "2" ? 2 : 1, - ), - ), - child: _selectedTripType == "2" - ? Icon(Icons.rectangle, size: 8, color: Colors.white) - : null, // Add checkmark if selected - ), - ) - ], - ), - // RadioListTile( - // activeColor: Colors.blueAccent, - // contentPadding: EdgeInsets.zero, - // dense: true, - // title: Text("International"), - // value: "2", - // groupValue: _selectedTripType, - // onChanged: widget.isViewMode - // ? null - // : (value) { - // setState(() { - // _selectedTripType = value!; - // }); - // }, - // ), - ), + // Radio( + // activeColor: Colors.blueAccent, + // // contentPadding: EdgeInsets.zero, + // visualDensity: VisualDensity.compact, + // // dense: true, + // value: "1", + // groupValue: _selectedTripType, + // onChanged: widget.isViewMode + // ? null + // : (value) { + // setState(() { + // _selectedTripType = value!; + // }); + // }, + // ), + + GestureDetector( + onTap: widget.isViewMode + ? null + : () { + setState(() { + _selectedTripType = "1"; + }); + }, + child: Container( + width: 15, + height: 15, + decoration: BoxDecoration( + shape: BoxShape.rectangle, + // color: _selectedOption == option["value"] + // ? Colors.blueAccent + // : Colors.transparent, + borderRadius: BorderRadius.circular(4), // Rounded rectangle + border: Border.all( + color: _selectedTripType == "1" + ? Colors.white + : Colors.black, + width: _selectedTripType == "1" ? 2 : 1, + ), + ), + child: _selectedTripType == "1" + ? Icon(Icons.rectangle, size: 8, color: Colors.white) + : null, // Add checkmark if selected + ), + ) + ], + ), + ), + SizedBox(width: 20), + if (showInternational) + CustomTextFieldWrapper( + color: Color(0xFFF4F4FB), + layoutColor: widget.layoutColor, + borderRadius: BorderRadius.circular(25), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + width: 150, + // padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2), + isFocused: _selectedTripType == "2", + isDesktop: widget.isDesktop, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "International", + style: TextStyle( + fontSize: 13, + color: _selectedTripType == "2" ? Colors.white : Colors.black, + fontWeight: _selectedTripType == "2" ? FontWeight.w600 : null, + ), + ), + GestureDetector( + onTap: widget.isViewMode + ? null + : () { + setState(() { + _selectedTripType = "2"; + }); + }, + child: Container( + width: 15, + height: 15, + decoration: BoxDecoration( + shape: BoxShape.rectangle, + // color: _selectedOption == option["value"] + // ? Colors.blueAccent + // : Colors.transparent, + borderRadius: BorderRadius.circular(4), // Rounded rectangle + border: Border.all( + color: _selectedTripType == "2" + ? Colors.white + : Colors.black, + width: _selectedTripType == "2" ? 2 : 1, + ), + ), + child: _selectedTripType == "2" + ? Icon(Icons.rectangle, size: 8, color: Colors.white) + : null, // Add checkmark if selected + ), + ) + ], + ), + + // RadioListTile( + // activeColor: Colors.blueAccent, + // contentPadding: EdgeInsets.zero, + // dense: true, + // title: Text("International"), + // value: "2", + // groupValue: _selectedTripType, + // onChanged: widget.isViewMode + // ? null + // : (value) { + // setState(() { + // _selectedTripType = value!; + // }); + // }, + // ), + ), ]; } diff --git a/lib/Screens/plans/dynamic_itinerary_stepper.dart b/lib/Screens/plans/dynamic_itinerary_stepper.dart index 8733948..b60ceab 100644 --- a/lib/Screens/plans/dynamic_itinerary_stepper.dart +++ b/lib/Screens/plans/dynamic_itinerary_stepper.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:easy_stepper/easy_stepper.dart'; import 'package:flutter/material.dart'; import 'package:frontend/Screens/itnerary_list/accomodation_list.dart'; @@ -54,7 +56,10 @@ class _DynamicItineraryState extends State { Map? selectedItem; int? selectedIndex; - List? apiAllServices; + List? selectedAllServices; + List> selectedOrgServiceIds = []; + List? ServicesChoosed; + List filledItineraryKeys = []; // List> miscellaneousList = []; @@ -87,21 +92,98 @@ class _DynamicItineraryState extends State { void initState() { super.initState(); handleSelectedPlan(); - loadAllServices(); + updateSelectedServices(); } Future loadAllServices() async { try { final result = await apiService.fetchAllServices(); setState(() { - apiAllServices = result; + selectedAllServices = result; }); - print("Fetched services: $apiAllServices"); + print("Fetched services: $selectedAllServices"); } catch (e) { print('Error fetching role list: $e'); } } + Future loadOrgSelectedAlServices() async { + try { + final result = await apiService.fetchOrganization(); + + if (result != null && result is Map) { + final rawServices = result['services_ids']; + + if (rawServices != null && rawServices is String) { + try { + List decoded = json.decode(rawServices); + List> formatted = decoded + .map((e) => {"service_id": e['service_id'].toString()}) + .toList(); + + setState(() { + selectedOrgServiceIds = formatted; + }); + print("selectedOrgServiceIds: ${selectedOrgServiceIds}"); + + for (var service in selectedOrgServiceIds) { + print("service_id: ${service['service_id']}"); + } + } catch (e) { + print("Failed to decode services_ids: $e"); + } + } + } + } catch (e) { + print('Error fetching role list: $e'); + } + } + + Future updateSelectedServices() async { + await loadAllServices(); + await loadOrgSelectedAlServices(); + + if (hasAnyItineraryData()) { + print("SELCTSplanChhose: ${filledItineraryKeys}"); + + final selectedIds = + selectedOrgServiceIds.map((e) => e['service_id']).toSet(); + + // Filter services that match filled keys (name match) and are not already selected + final additionalServices = selectedAllServices!.where((service) { + final name = (service['name'] ?? "").toString().toLowerCase(); + final id = service['service_id'].toString(); + return filledItineraryKeys.contains(name) && !selectedIds.contains(id); + }).toList(); + + final originalFiltered = selectedAllServices! + .where((service) => + selectedIds.contains(service['service_id'].toString())) + .toList(); + + setState(() { + ServicesChoosed = [...originalFiltered, ...additionalServices]; + }); + + print( + "Services chosen based on filled keys + selected: $ServicesChoosed"); + } else { + final selectedIds = + selectedOrgServiceIds.map((e) => e['service_id']).toSet(); + + final filtered = selectedAllServices! + .where((service) => + selectedIds.contains(service['service_id'].toString())) + .toList(); + + setState(() { + ServicesChoosed = filtered; + }); + + print("Filtered Selected Services Chooesed: $ServicesChoosed"); + } + } + void handleSelectedPlan() { // Check if selectedPlanData has itinerary data if (hasAnyItineraryData()) { @@ -147,13 +229,30 @@ class _DynamicItineraryState extends State { "forex" ]; + // for (String key in keys) { + // if (widget.selectedPlanData.containsKey(key) && + // widget.selectedPlanData[key] is List && + // (widget.selectedPlanData[key] as List).isNotEmpty) { + // print("SelectedPLANDFDDSF- ${widget.selectedPlanData}"); + // return true; // At least one list has data + // } + // } + + filledItineraryKeys.clear(); // Clear previous results + for (String key in keys) { if (widget.selectedPlanData.containsKey(key) && widget.selectedPlanData[key] is List && (widget.selectedPlanData[key] as List).isNotEmpty) { - return true; // At least one list has data + filledItineraryKeys.add(key); // Store key with data } } + + if (filledItineraryKeys.isNotEmpty) { + print("Selected keys with data: $filledItineraryKeys"); + return true; + } + return false; // No itinerary data available } @@ -544,9 +643,9 @@ class _DynamicItineraryState extends State { } List _buildOptions() { - if (apiAllServices == null) return []; + if (ServicesChoosed == null) return []; - return apiAllServices!.map((service) { + return ServicesChoosed!.map((service) { return Padding( padding: const EdgeInsets.only(right: 20.0), child: _buildOption( diff --git a/lib/Screens/plans/list_plans.dart b/lib/Screens/plans/list_plans.dart index 58e675b..fee8689 100644 --- a/lib/Screens/plans/list_plans.dart +++ b/lib/Screens/plans/list_plans.dart @@ -24,6 +24,7 @@ class _ListPlansState extends State { String? userId; String? orgId; String? token; + String? TripPlanAction; Color? layoutColor; Color? bodyColor; @@ -60,6 +61,7 @@ class _ListPlansState extends State { token = await getToken(); userId = await getUserId(); orgId = await getOrgId(); + TripPlanAction = await getTripPlanAction(); if (token == null || userId == null) { print("Token or USerId missing"); @@ -290,10 +292,34 @@ class _ListPlansState extends State { EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: () { - context.go('/createPlan', extra: { - 'orgId': orgId, - }); - if (!isDesktop) Navigator.pop(context); + if (TripPlanAction == "Plan Creation Not Allowed") { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text( + "Action Not Allowed", + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold), + ), + content: Text( + "Plan Creation Not Allowed For This User."), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text( + "OK", + style: TextStyle(color: layoutColor), + ), + ), + ], + ), + ); + } else { + context.go('/createPlan', extra: { + 'orgId': orgId, + }); + if (!isDesktop) Navigator.pop(context); + } }, child: Row( mainAxisSize: @@ -450,26 +476,43 @@ class _ListPlansState extends State { fontSize: 13, fontFamily: "Archivo", ))), - DataCell(Container( - padding: const EdgeInsets.symmetric( - vertical: 4, horizontal: 10), - decoration: BoxDecoration( - color: plan.status == "Active" - ? layoutColor - : Colors.grey.shade50, - borderRadius: BorderRadius.circular(10), - ), - child: Text( - plan.statusValue, - style: TextStyle( - color: plan.status == "Active" - ? Colors.white - : Colors.grey, - fontSize: 13, - fontWeight: FontWeight.bold, + DataCell( + Container( + width: double + .infinity, // Set your desired fixed size (equal width and height) + height: 25, + alignment: Alignment.center, + decoration: BoxDecoration( + color: plan.statusValue == + "Partially Approved" + ? Colors.yellow.shade100 + : plan.statusValue == "Approved" + ? Colors.green.shade100 + : plan.statusValue == "Completed" + ? Colors.green.shade500 + : plan.statusValue == "Rejected" + ? Colors.red.shade100 + : Colors.grey.shade100, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + plan.statusValue, + textAlign: TextAlign.center, + style: TextStyle( + color: (plan.statusValue == + "Partially Approved" || + plan.statusValue == "Approved" || + plan.statusValue == "Rejected") + ? Colors.black + : plan.statusValue == "Completed" + ? Colors.white + : Colors.grey, + fontSize: 12, + fontWeight: FontWeight.w400, + ), ), ), - )), + ), DataCell(Row(children: [ IconButton( icon: const Icon( @@ -494,6 +537,17 @@ class _ListPlansState extends State { // color: Colors.green), // onPressed: () => viewPlan(plan.planId, // isViewMode: false) ), + + SizedBox( + width: 5, + ), + GestureDetector( + onTap: () => (), + child: Image.asset( + 'assets/images/IconsImg/delete.png', + width: 20, + height: 15), + ), ])), ]); }).toList(), diff --git a/lib/Screens/policy/policy.dart b/lib/Screens/policy/policy.dart index ce04905..68bfb0a 100644 --- a/lib/Screens/policy/policy.dart +++ b/lib/Screens/policy/policy.dart @@ -1,5 +1,15 @@ import 'dart:convert'; import 'dart:math'; +import 'dart:ui' as html; + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; +import 'dart:html' as html; +import 'dart:ui' as web; + +import 'package:web/web.dart' as web; + import 'package:flutter/material.dart'; import 'package:frontend/Screens/policy/policyCriteria.dart'; import 'package:go_router/go_router.dart'; @@ -9,12 +19,18 @@ 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_text_field.dart'; import '../../widgets/custom_user_form.dart'; class Policy extends StatefulWidget { - const Policy({super.key}); + final Map? policy; + const Policy({super.key, required this.policy}); + + static Policy fromState(GoRouterState state) { + return Policy(policy: state.extra as Map?); + } @override _PolicyState createState() => _PolicyState(); @@ -24,17 +40,23 @@ class _PolicyState extends State { final GlobalKey policyCriteriaKey = GlobalKey(); + final ApiService apiService = ApiService(); + Color? layoutColor; Color? bodyColor; late String policyType = "domestic"; // int? selectedServiceIndex = 1; ValueNotifier selectedServiceIndex = ValueNotifier("1"); - late String selectedService = "Train"; + String selectedService = "train"; // ValueNotifier selectedService = ValueNotifier("Train"); bool isViewMode = false; + + Map errorMessages = {}; + + String? selectedPolicyId; String? _selectedTripType; String? PolicyName; - String? SelectedDomestic = "1"; + String? SelectedDomestic = "0"; String? SelectedInternational = "0"; String? orgId; String? userId; @@ -42,6 +64,11 @@ class _PolicyState extends State { bool showClass = true; bool showCost = true; + List? selectedAllServices; + List> selectedOrgServiceIds = []; + List? ServicesChoosed; + List filledItineraryKeys = []; + TextEditingController _policyController = TextEditingController(); List>? policy_details = []; @@ -79,13 +106,20 @@ class _PolicyState extends State { @override void initState() { super.initState(); + WidgetsFlutterBinding.ensureInitialized(); WidgetsBinding.instance.addPostFrameCallback((_) { loadinitializeData(); loadInitialData(); - }); - // updateData(); + if (widget.policy != null) { + final details = + List>.from(widget.policy!['policy_details']); + policyCriteriaKey.currentState?.loadPolicyDetails(details); + } + }); + updateSelectedServices(); + updateData(); } void loadInitialData() async { @@ -108,7 +142,121 @@ class _PolicyState extends State { userId = await getUserId(); } - // void updateData(){} + Future loadAllServices() async { + try { + final result = await apiService.fetchAllServices(); + setState(() { + selectedAllServices = result; + }); + print("Fetched services: $selectedAllServices"); + } catch (e) { + print('Error fetching role list: $e'); + } + } + + Future loadOrgSelectedAlServices() async { + try { + final result = await apiService.fetchOrganization(); + + if (result != null && result is Map) { + final rawServices = result['services_ids']; + + if (rawServices != null && rawServices is String) { + try { + List decoded = json.decode(rawServices); + List> formatted = decoded + .map((e) => {"service_id": e['service_id'].toString()}) + .toList(); + + setState(() { + selectedOrgServiceIds = formatted; + }); + print("selectedOrgServiceIds: ${selectedOrgServiceIds}"); + + for (var service in selectedOrgServiceIds) { + print("service_id: ${service['service_id']}"); + } + } catch (e) { + print("Failed to decode services_ids: $e"); + } + } + } + } catch (e) { + print('Error fetching role list: $e'); + } + } + + Future updateSelectedServices() async { + await loadAllServices(); + await loadOrgSelectedAlServices(); + + final selectedIds = + selectedOrgServiceIds.map((e) => e['service_id']).toSet(); + + if (widget.policy != null) { + final details = + List>.from(widget.policy!['policy_details']); + + print("Filtered Selected Services - $details"); + + final filtered = selectedAllServices! + .where((service) => + selectedIds.contains(service['service_id'].toString())) + .toList(); + + setState(() { + ServicesChoosed = filtered; + }); + + print("Filtered Selected Services Chooesed1: $ServicesChoosed"); + + if (ServicesChoosed!.isNotEmpty) { + String firstServiceName = ServicesChoosed?.first['name']; + print("✅ First service name selected for filter: $firstServiceName"); + selectedService = firstServiceName; + } + print("Filtered Selected Services Added to Policy: $ServicesChoosed"); + } else { + final filtered = selectedAllServices! + .where((service) => + selectedIds.contains(service['service_id'].toString())) + .toList(); + + setState(() { + ServicesChoosed = filtered; + }); + + print("Filtered Selected Services Chooesed1: $ServicesChoosed"); + + if (ServicesChoosed!.isNotEmpty) { + String firstServiceName = ServicesChoosed?.first['name']; + print("✅ First service name selected for filter: $firstServiceName"); + selectedService = firstServiceName; + } + } + } + + void updateData() { + if (widget.policy != null) { + setState(() { + selectedPolicyId = widget.policy?["policy_id"] ?? ""; + _policyController.text = widget.policy?["name"] ?? ""; + SelectedDomestic = widget.policy?["domestic"] ?? ""; + SelectedInternational = widget.policy?["international"] ?? ""; + + if (SelectedDomestic == "1") { + _selectedTripType = "1"; + } else if (SelectedInternational == "1") { + _selectedTripType = "1"; + } + + /// ✅ Load policy_details list safely + policy_details = List>.from( + widget.policy?["policy_details"] ?? [], + ); + }); + } + } void handleSubmit() async { print("USR Detail Submit - $policyData"); @@ -120,19 +268,69 @@ class _PolicyState extends State { Map data = policyData; - createPolicyData(data); + if (!isValidData(data)) { + print("USERDETAILS : $policyData"); + print("Validation Failed: Required fields are missing."); + setState(() {}); + return; // Stop execution if validation fails + } else { + // print("USERDETAILS : $policyData"); + // orgId = await getOrgId(); - // if (!isValidData(data)) { - // print("USERDETAILS : $policyData"); - // print("Validation Failed: Required fields are missing."); - // setState(() {}); - // return; // Stop execution if validation fails - // } else { - // // print("USERDETAILS : $policyData"); - // // orgId = await getOrgId(); - // - // createPolicyData(policyData); - // } + createPolicyData(data); + } + } + + bool isValidData(Map data) { + errorMessages.clear(); // Reset previous errors + + // Validate required fields + if (data["name"] == null || data["name"].toString().trim().isEmpty) { + errorMessages["name"] = "Policy name is required."; + } + + // Validate that either domestic or international is selected + final domestic = data["domestic"]?.toString() ?? "0"; + final international = data["international"]?.toString() ?? "0"; + + print( + "domestic: ${data["domestic"]}, international: ${data["international"]}"); + + if (domestic != "1" && international != "1") { + errorMessages["trip_type"] = "Please select Domestic or International."; + } + + // Validate at least one policy_detail with valid content + final policyDetails = data["policy_details"] as List>; + + bool hasAtLeastOneDetail = policyDetails.any((service) { + final fieldsToCheck = [ + 'cost', + 'class', + 'a1_action', + 'a2_action', + 'a3_action' + ]; + return fieldsToCheck.any((field) { + final value = service[field]; + return value != null && value.toString().trim().isNotEmpty; + }); + }); + + if (!hasAtLeastOneDetail) { + errorMessages["policy_details"] = + "At least one valid policy detail is required."; + } + + return errorMessages.isEmpty; + } + + void _clearError(String field) { + if (mounted && errorMessages.containsKey(field)) { + setState(() { + errorMessages.remove(field); + }); + } } Future createPolicyData(Map policyData) async { @@ -143,9 +341,12 @@ class _PolicyState extends State { throw Exception('Token not found. Please log in.'); } - // if (selectedPlanId != null && selectedPlanId!.isNotEmpty) { - // planData['plan_id'] = selectedPlanId; // Add plan_id for update - // } + final int? policyId; + + if (selectedPolicyId != null && selectedPolicyId!.isNotEmpty) { + policyId = int.tryParse(selectedPolicyId!); + policyData['policy_id'] = policyId; // Add only if updating + } try { final response = await http.post( @@ -186,28 +387,17 @@ class _PolicyState extends State { child: Row( children: [ if (isDesktop) CustomDrawer(isDesktop: true), - Expanded( - child: Container( - color: bodyColor, - child: buildPolicyLayout(isDesktop), - ), - ), + + Expanded(child: buildData(isDesktop, context)), + // Expanded( + // child: Container( + // color: bodyColor, + // child: buildPolicyLayout(isDesktop), + // ), + // ), ], ), ), - Container( - color: Colors.white, - padding: const EdgeInsets.all(8.0), - child: isDesktop - ? Row( - mainAxisAlignment: MainAxisAlignment.end, - children: _buildSubmit(isDesktop), - ) - : Row( - mainAxisAlignment: MainAxisAlignment.center, - children: _buildSubmit(isDesktop), - ), - ), ], ), @@ -246,153 +436,206 @@ class _PolicyState extends State { }); } - Widget buildPolicyLayout(bool isDesktop) { - return SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Container( - margin: isDesktop - ? EdgeInsets.all(10.0) - : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), - height: isDesktop - ? MediaQuery.of(context).size.height * 0.98 - : MediaQuery.of(context).size.height, - decoration: BoxDecoration( - border: isDesktop - ? Border.all( - width: 2, - color: Color(0xFFF7F7FB), - ) - : null, - color: Colors.white, - // color: Color(0xFFF7F7FB), - + Widget buildData(bool isDesktop, context) { + return Container( + // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), + decoration: BoxDecoration( // color: Colors.amber, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Container( - // color: Color(0xFFF7F7FB), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Container( - padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3), - // color: Colors.white, // Background to avoid overlapping - color: Colors.white, - // color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - "Choose Policy Type", - style: TextStyle( - fontSize: 18, - color: Colors.black, - ), - ), - ], - ), - ), - ], - ), + color: bodyColor, + border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), + child: Column( + children: [ + Expanded( + child: Container( + color: bodyColor, + child: buildPolicyLayout(isDesktop), ), - isDesktop ? SizedBox(height: 0) : SizedBox(height: 5), - Container( - // color: Colors.amber, - // color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, - padding: isDesktop ? const EdgeInsets.only(left: 35) : null, - child: Column( - children: [ - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Policy Name", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12), - controller: _policyController, - // enabled: !isViewMode, - onChanged: (value) {}, - decoration: InputDecoration( - labelText: "Policy Name", - labelStyle: TextStyle( - fontSize: 12, color: Colors.grey), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(vertical: 16), - ), + ), + Container( + color: Colors.white, + padding: const EdgeInsets.all(8.0), + child: isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.end, + children: _buildSubmit(isDesktop), + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildSubmit(isDesktop), + ), + ), + ], + ), + ); + } + + Widget buildPolicyLayout(bool isDesktop) { + return Container( + margin: isDesktop + ? EdgeInsets.all(10.0) + : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), + height: isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, + decoration: BoxDecoration( + border: isDesktop + ? Border.all( + width: 2, + color: Color(0xFFF7F7FB), + ) + : null, + color: Colors.white, + // color: Color(0xFFF7F7FB), + + // color: Colors.amber, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + // color: Color(0xFFF7F7FB), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3), + // color: Colors.white, // Background to avoid overlapping + color: Colors.white, + // color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + "Choose Policy Type", + style: TextStyle( + fontSize: 18, + color: Colors.black, + ), + ), + ], + ), + ), + ], + ), + ), + isDesktop ? SizedBox(height: 0) : SizedBox(height: 5), + Container( + // color: Colors.amber, + // color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, + padding: isDesktop ? const EdgeInsets.only(left: 35) : null, + child: Column( + children: [ + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Policy Name", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12), + controller: _policyController, + // enabled: !isViewMode, + onChanged: (value) { + _clearError("name"); + }, + decoration: InputDecoration( + labelText: "Policy Name", + labelStyle: TextStyle( + fontSize: 12, 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 + Text( + errorMessages["name"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), ], - ), - ], - ), - SizedBox( - height: 5, - ), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Policy Type", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildTripType(isDesktop), - ) - ], - ) - ], - ), - ], - )), - SizedBox( - height: 10, - ), - Divider( - thickness: 0.2, - color: Colors.grey, - ), - isDesktop - ? Expanded( - child: Row( - children: [ - _buildPolicyCategoryList(isDesktop), - _buildPolicyCategory(isDesktop), - ], - ), - ) - : Expanded( - child: Column( - children: [ - _buildPolicyCategoryList(isDesktop), - _buildPolicyCategory(isDesktop), - ], - ), + ], + ), + ], ), + SizedBox( + height: 5, + ), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Policy Type", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildTripType(isDesktop), + ), + if (errorMessages["trip_type"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["trip_type"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ) + ], + ), + ], + )), + SizedBox( + height: 10, + ), + Divider( + thickness: 0.2, + color: Colors.grey, + ), + if (errorMessages["policy_details"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["policy_details"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), ], - ), + isDesktop + ? Expanded( + child: Row( + children: [ + _buildPolicyCategoryList(isDesktop), + _buildPolicyCategory(isDesktop), + ], + ), + ) + : Expanded( + child: Column( + children: [ + _buildPolicyCategoryList(isDesktop), + _buildPolicyCategory(isDesktop), + ], + ), + ), + ], ), ); } @@ -420,17 +663,24 @@ class _PolicyState extends State { } Widget _buildPolicySubCategoryList(bool isDesktop) { - List services = [ - "Flight", - "Train", - "Bus", - "Taxi", - "Forex", - "Accommodation", - "Insurance", - "Visa", - "Miscellaneous" - ]; + // List services = [ + // "Flight", + // "Train", + // "Bus", + // "Taxi", + // "Forex", + // "Accommodation", + // "Insurance", + // "Visa", + // "Miscellaneous" + // ]; + + if (ServicesChoosed == null) { + return const Center(child: CircularProgressIndicator()); + } + + List services = + ServicesChoosed!.map((service) => service['name'].toString()).toList(); return Expanded( child: SingleChildScrollView( @@ -476,10 +726,21 @@ class _PolicyState extends State { : EdgeInsets.only(top: 3, bottom: 3, left: 8, right: 8), decoration: BoxDecoration( // color: Colors.blue, - color: - isSelected ? Color(0xFF114D8B) : Colors.grey.shade100, + color: isSelected ? Color(0xFF114D8B) : Colors.white, // : Color(0xFFEBEBF7), borderRadius: BorderRadius.circular(8), + border: Border.all( + color: Colors.white, // Light grey border + width: 1, + ), + boxShadow: [ + BoxShadow( + color: Colors.grey.withAlpha(90), // Shadow color + blurRadius: 1, // Blur radius + spreadRadius: 1, // Spread radius + offset: Offset(0, 1), // Shadow position + ), + ], ), alignment: Alignment.center, child: Text( @@ -518,6 +779,7 @@ class _PolicyState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { policy_details = policyData; + _clearError("policy_details"); }); }); }, @@ -566,6 +828,7 @@ class _PolicyState extends State { GestureDetector( onTap: () { setState(() { + _clearError("trip_type"); _selectedTripType = "1"; PolicyName = "Domestic Policy"; SelectedDomestic = "1"; @@ -620,6 +883,7 @@ class _PolicyState extends State { GestureDetector( onTap: () { setState(() { + _clearError("trip_type"); _selectedTripType = "2"; PolicyName = "International Policy"; SelectedDomestic = "0"; diff --git a/lib/Screens/policy/policyCriteria.dart b/lib/Screens/policy/policyCriteria.dart index 5d740d7..2e8773f 100644 --- a/lib/Screens/policy/policyCriteria.dart +++ b/lib/Screens/policy/policyCriteria.dart @@ -81,6 +81,27 @@ class PolicyCriteriaState extends State { } } + void loadPolicyDetails(List> details) { + for (var item in details) { + final serviceId = item['service_id'].toString(); + + costController[serviceId] = + TextEditingController(text: item['cost'] ?? ''); + classController[serviceId] = + TextEditingController(text: item['class'] ?? ''); + + FirstApproverAction[serviceId] = item['a1_action']?.toString(); + SecondApproverAction[serviceId] = item['a2_action']?.toString(); + ThirdApproverAction[serviceId] = item['a3_action']?.toString(); + SelectedParallelProcess[serviceId] = + item['parallel_process_from']?.toString() ?? "3"; + } + + policyData = details; + widget.onPolicyDataChanged(policyData); + setState(() {}); + } + void addOrUpdatePolicy(String serviceId) { Map data = { "service_id": serviceId, @@ -161,7 +182,7 @@ class PolicyCriteriaState extends State { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - widget.isDesktop ? SizedBox(height: 10) : SizedBox(height: 5), + // widget.isDesktop ? SizedBox(height: 10) : SizedBox(height: 5), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -175,7 +196,7 @@ class PolicyCriteriaState extends State { ], ), if (widget.isClass!) - widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5), + widget.isDesktop ? SizedBox(height: 5) : SizedBox(height: 5), Padding( padding: const EdgeInsets.only(right: 18.0), child: Row( @@ -231,7 +252,7 @@ class PolicyCriteriaState extends State { // color: Color(0xFFEBEBF7), // ), borderRadius: BorderRadius.circular(8), - color: Colors.brown.shade200, + // color: Colors.brown.shade200, ), child: SingleChildScrollView( scrollDirection: Axis.horizontal, @@ -241,7 +262,7 @@ class PolicyCriteriaState extends State { children: [ Expanded( child: Container( - color: Colors.grey.shade100, + // color: Colors.grey.shade100, width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.63 : 600, @@ -250,11 +271,11 @@ class PolicyCriteriaState extends State { Container( margin: const EdgeInsets.only(right: 0), decoration: BoxDecoration( - color: Colors.grey.shade100, - border: Border.all( - color: Colors.grey.shade100, - ), - ), + // color: Colors.grey.shade100, + // border: Border.all( + // color: Colors.grey.shade100, + // ), + ), padding: const EdgeInsets.only( top: 10, bottom: 10, left: 35, right: 35), child: Row( @@ -262,19 +283,25 @@ class PolicyCriteriaState extends State { ? MainAxisAlignment.spaceAround : MainAxisAlignment.spaceBetween, children: [ - Text( - "Approver Name", - style: TextStyle( - color: Color(0xFF9E9DBD), - fontWeight: FontWeight.bold, - fontSize: 12), + Expanded( + flex: 2, + child: Text( + "Approver Name", + style: TextStyle( + color: Color(0xFF9E9DBD), + fontWeight: FontWeight.bold, + fontSize: 12), + ), ), - Text( - "Notification", - style: TextStyle( - color: Color(0xFF9E9DBD), - fontWeight: FontWeight.bold, - fontSize: 12), + Expanded( + flex: 2, + child: Text( + "Notification", + style: TextStyle( + color: Color(0xFF9E9DBD), + fontWeight: FontWeight.bold, + fontSize: 12), + ), ), Text( "Select", @@ -286,22 +313,28 @@ class PolicyCriteriaState extends State { ], ), ), + Divider( + thickness: 0.2, + color: Colors.grey, + ), SizedBox( - height: 200, + height: 180, child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Container( // color: Colors.grey, - margin: const EdgeInsets.only(right: 20), - color: Colors.grey.shade100, + margin: const EdgeInsets.only( + right: 20, left: 20), + // color: Colors.grey.shade100, child: Column(children: [ Container( padding: const EdgeInsets.all(10), child: Row( mainAxisAlignment: - MainAxisAlignment.spaceAround, + MainAxisAlignment.end, children: [ Text("Approver 1"), + Spacer(), CustomTextFieldUserWrapper( isFocused: false, isDesktop: widget.isDesktop, @@ -318,6 +351,23 @@ class PolicyCriteriaState extends State { .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250), + itemBuilder: (context, item, + isSelected) => + Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 16.0, + vertical: 8.0), + child: Text( + item, + style: TextStyle( + fontSize: + 12, // 👈 Smaller text size here + color: Colors + .black, // You can customize this + ), + ), + ), ), items: [ "Approval", @@ -366,6 +416,7 @@ class PolicyCriteriaState extends State { ), ), ), + Spacer(), GestureDetector( onTap: () { setState(() { @@ -408,9 +459,10 @@ class PolicyCriteriaState extends State { padding: const EdgeInsets.all(10), child: Row( mainAxisAlignment: - MainAxisAlignment.spaceAround, + MainAxisAlignment.end, children: [ Text("Approver 2"), + Spacer(), CustomTextFieldUserWrapper( isFocused: false, isDesktop: widget.isDesktop, @@ -427,6 +479,23 @@ class PolicyCriteriaState extends State { .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250), + itemBuilder: (context, item, + isSelected) => + Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 16.0, + vertical: 8.0), + child: Text( + item, + style: TextStyle( + fontSize: + 12, // 👈 Smaller text size here + color: Colors + .black, // You can customize this + ), + ), + ), ), items: [ "Approval", @@ -475,6 +544,7 @@ class PolicyCriteriaState extends State { ), ), ), + Spacer(), GestureDetector( onTap: () { setState(() { @@ -522,9 +592,10 @@ class PolicyCriteriaState extends State { padding: const EdgeInsets.all(10), child: Row( mainAxisAlignment: - MainAxisAlignment.spaceAround, + MainAxisAlignment.end, children: [ Text("Approver 3"), + Spacer(), CustomTextFieldUserWrapper( isFocused: false, isDesktop: widget.isDesktop, @@ -541,6 +612,23 @@ class PolicyCriteriaState extends State { .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250), + itemBuilder: (context, item, + isSelected) => + Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 16.0, + vertical: 8.0), + child: Text( + item, + style: TextStyle( + fontSize: + 12, // 👈 Smaller text size here + color: Colors + .black, // You can customize this + ), + ), + ), ), items: [ "Approval", @@ -589,6 +677,7 @@ class PolicyCriteriaState extends State { ), ), ), + Spacer(), GestureDetector( onTap: () { setState(() { diff --git a/lib/Screens/policy/policy_list.dart b/lib/Screens/policy/policy_list.dart index 9b5c2d4..4bbc863 100644 --- a/lib/Screens/policy/policy_list.dart +++ b/lib/Screens/policy/policy_list.dart @@ -1,8 +1,12 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:frontend/Screens/group/group.dart'; import 'package:go_router/go_router.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'; @@ -57,10 +61,67 @@ class _PolicyListState extends State { } } - void deleteGroup(int groupId) { - setState(() { - apiAllGroups?.removeWhere((group) => group['group_id'] == groupId); - }); + void handleActiveStatus( + Map policyData, + String policyId, + String currentStatus, + ) async { + print("Toggling user status - $policyId (Current: $currentStatus)"); + + final String apiUrlData = + '$apiUrl/api/policy/createOrUpdate'; // 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"); + + final int? selectedPolicyId; + + if (policyId.isNotEmpty) { + selectedPolicyId = int.tryParse(policyId); + policyData['policy_id'] = selectedPolicyId; // Add only if updating + policyData['is_active'] = newStatus; // Add only if updating + } + + try { + final response = await http.post( + Uri.parse(apiUrlData), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + body: jsonEncode(policyData), // Convert map to JSON + ); + + if (response.statusCode == 200) { + print("policyData submitted successfully!"); + print("Response: ${response.body}"); + + loadAllGroups(); + } else { + print("Failed to submit policyData. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + } + } catch (e) { + print(" Error submitting policyData: $e"); + } + } + + void deletePolicy(Map policydata, policyId, status) { + print("policyId : $policyId"); + print("policystatus: $status"); + print("policysData: $policydata"); + + // handleActiveStatus(groupdata, groupId, status); + print("Calling handleActiveStatus with: id=$policyId, status=$status"); + handleActiveStatus(policydata, policyId.toString(), status.toString()); } // Future deleteGroupFromApi(int groupId) async { @@ -189,10 +250,11 @@ class _PolicyListState extends State { physics: NeverScrollableScrollPhysics(), itemCount: apiAllGroups!.length, itemBuilder: (context, index) { - final group = apiAllGroups![index]; + final policy = apiAllGroups![index]; return Card( // color: bodyColor, - color: Color(0xFFF5F5F5), + // color: Color(0xFFF5F5F5), + color: Colors.white, margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10), child: Padding( padding: const EdgeInsets.all(12.0), @@ -203,25 +265,53 @@ class _PolicyListState extends State { children: [ Expanded( flex: 2, - child: Text("Group Name: ${group['name']}", + child: Text("Policy Name: ${policy['name']}", style: TextStyle( fontSize: 13, fontWeight: FontWeight.bold)), ), - Expanded(flex: 1, child: Text(" ${group['created_on']}")), - Expanded(flex: 1, child: Text("${group['created_by']}")), + Expanded(flex: 1, child: Text(" ${policy['created_on']}")), + Expanded(flex: 1, child: Text("${policy['created_by']}")), ], ), SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - TextButton( - onPressed: () { - context.go("/CreateGroup", extra: group); + GestureDetector( + onTap: () async { + final rawId = policy['policy_id']; + final intPolicyId = rawId is int + ? rawId + : int.tryParse(rawId.toString()) ?? 0; - print("Edit ${group['group_id']} $group"); + Map policyData = + await apiService.getSinglePolicy(intPolicyId); + + print("PolicyDATa: $policyData"); + + context.go("/Policy", extra: policyData); }, - child: Text("Edit"), + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox( + width: 5, + ), + GestureDetector( + onTap: () { + final idStr = policy['policy_id']; + final id = int.tryParse(idStr.toString()); + + if (id == null) { + print("group_id is null"); + return; + } + final status = policy['is_active']; + + deletePolicy(policy, id, status); + }, + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), ), ], ), diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index 5d289a9..c0ff5f7 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -105,6 +105,9 @@ class _CreateUserFormState extends State { "changePassword" ]; + Color? layoutColor; + Color? bodyColor; + Map get userDetials { Map data = { "first_name": controllers["Fname"]?.text, @@ -158,7 +161,9 @@ class _CreateUserFormState extends State { controllers["mobileNumber"]?.text = apiselectedUser?["mobile_no"] ?? ""; controllers["alternateMobile"]?.text = apiselectedUser?["alternate_mobile_no"] ?? ""; + controllers["dob"]?.text = apiselectedUser?["date_of_birth"] ?? ""; + controllers["address"]?.text = apiselectedUser?["address"] ?? ""; controllers["postalCode"]?.text = apiselectedUser?["postal_code"] ?? ""; controllers["employeeCode"]?.text = @@ -166,22 +171,34 @@ class _CreateUserFormState extends State { controllers["passportNumber"]?.text = apiselectedUser?["passport_number"] ?? ""; + controllers["placeOfIssue"]?.text = apiselectedUser?["place_of_issue"] ?? ""; - controllers["dateOfIssue"]?.text = - apiselectedUser?["date_of_issue"] ?? ""; - controllers["dateOfExpiry"]?.text = - apiselectedUser?["date_of_expiry"] ?? ""; + if (apiselectedUser?["date_of_issue"] != null) { + controllers["dateOfIssue"]?.text = + apiselectedUser?["date_of_issue"] ?? ""; + } - selectedCountry = apiselectedUser?["country_code"]?.toString() ?? ""; + if (apiselectedUser?["date_of_expiry"] != null) { + controllers["dateOfExpiry"]?.text = + apiselectedUser?["date_of_expiry"] ?? ""; + } - selectedGender = apiselectedUser?["gender"]?.toString().trim() ?? ""; + if (apiselectedUser?["country_code"] != null) { + selectedCountry = apiselectedUser?["country_code"]?.toString() ?? ""; + } + if (apiselectedUser?["gender"] != null) { + selectedGender = apiselectedUser?["gender"]?.toString().trim() ?? ""; + } + if (apiselectedUser?["user_type"] != null) { + selectedUserType = + apiselectedUser?["user_type"]?.toString().trim() ?? ""; + } - selectedUserType = selectedUserType = - apiselectedUser?["user_type"]?.toString().trim() ?? ""; - - selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? ""; + if (apiselectedUser?["role_id"] != null) { + selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? ""; + } // selectedDepartment = // apiselectedUser?["department_id"]?.toString().trim() ?? ""; @@ -190,15 +207,23 @@ class _CreateUserFormState extends State { } // print( // "selectedDepartment - $selectedDepartment - ${apiselectedUser?["department_id"]} "); + if (apiselectedUser?["level_id"] != null) { + selectedLevel = apiselectedUser?["level_id"]?.toString().trim() ?? ""; + } + if (apiselectedUser?["first_approver"] != null) { + selectedFirstApprover = + apiselectedUser?["first_approver"]?.toString() ?? ""; + } - selectedLevel = apiselectedUser?["level_id"]?.toString().trim() ?? ""; + if (apiselectedUser?["second_approver"] != null) { + selectedSecondApprover = + apiselectedUser?["second_approver"]?.toString() ?? ""; + } - selectedFirstApprover = - apiselectedUser?["first_approver"]?.toString() ?? ""; - selectedSecondApprover = - apiselectedUser?["second_approver"]?.toString() ?? ""; - selectedThirdApprover = - apiselectedUser?["third_approver"]?.toString() ?? ""; + if (apiselectedUser?["third_approver"] != null) { + selectedThirdApprover = + apiselectedUser?["third_approver"]?.toString() ?? ""; + } print("Updated selectedGender: $selectedGender"); // Debugging @@ -241,12 +266,12 @@ class _CreateUserFormState extends State { // Initialize controllers for each field WidgetsBinding.instance.addPostFrameCallback((_) async { - // final wasReloaded = html.window.localStorage['reloaded'] == 'true'; - // - // if (wasReloaded) { - // html.window.localStorage.remove('reloaded'); // Clear it - // context.go('/listUser'); // Navigate using go_router - // } + final wasReloaded = html.window.localStorage['reloaded'] == 'true'; + + if (wasReloaded) { + html.window.localStorage.remove('reloaded'); // Clear it + context.go('/listUser'); // Navigate using go_router + } final extraData = GoRouterState.of(context).extra as Map?; @@ -275,21 +300,22 @@ class _CreateUserFormState extends State { print("selectedUser: $apiselectedUser"); // Add another post-frame callback to check after setState - await Future.delayed(Duration( - milliseconds: 100)); // Optional delay to ensure UI has updated + // await Future.delayed(Duration( + // milliseconds: 100)); // Optional delay to ensure UI has updated updateData(); } + + initializeData(); + fetchCountries(); + fetchDepartment(); + fetchUsers(); + fetchRoles(); + loadInitialData(); }); for (var field in dataHeader) { controllers[field] = TextEditingController(); } - - initializeData(); - fetchCountries(); - fetchDepartment(); - fetchUsers(); - fetchRoles(); } Future fetchCountries() async { @@ -361,6 +387,21 @@ class _CreateUserFormState extends State { } } + void loadInitialData() async { + String? layoutString = await getLayoutColor(); + String? bodyStringColor = await getBodyColor(); + + setState(() { + layoutColor = layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; + + bodyColor = bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; + }); + } + Future initializeData() async { token = await getToken(); userId = await getUserId(); @@ -625,12 +666,17 @@ class _CreateUserFormState extends State { appBar: isDesktop ? null : const CustomAppBar(title: 'Create User '), drawer: isDesktop ? null : CustomDrawer(isDesktop: false), body: Container( + // color: bodyColor, color: Colors.white, child: Row( children: [ if (isDesktop) CustomDrawer(isDesktop: true), // const Expanded(child: Center(child: Text("User Details Content"))), - Expanded(child: buildUserTable(isDesktop)), + // Expanded( + // child: _buildUserDetails(isDesktop), + // ), + + Expanded(child: buildData(isDesktop, context)), ], ), ), @@ -638,65 +684,44 @@ class _CreateUserFormState extends State { }); } - Widget buildUserTable(bool isDesktop) { + Widget buildData(bool isDesktop, context) { return Container( - margin: const EdgeInsets.only( - left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), + // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), decoration: BoxDecoration( + // color: Colors.amber, + color: bodyColor, border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: Column( children: [ - Container( - color: Color(0xFFF7F7FB), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Text( - apiselectedUser != null ? "Profile" : "New User", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w200, - color: Colors.black), - ), - if (isEditProfile) - IconButton( - icon: Icon( - Icons.edit, - color: Colors.blueAccent, - size: 18, - ), - onPressed: () { - setState(() { - isViewMode = !isViewMode; - }); - }) - ], - ), - ), - ), - SizedBox( - height: 15, - ), Expanded( - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: _buildUserDetails( - isDesktop), // Ensure this returns a scrollable widget + child: Container( + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), // rounds all corners + ), + + // color: bodyColor, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(20.0), + child: _buildUserDetails(isDesktop), + ), + ), ), ), Container( color: Colors.white, child: Padding( - padding: const EdgeInsets.all(18.0), + padding: const EdgeInsets.all(8.0), child: isDesktop ? Row( mainAxisAlignment: MainAxisAlignment.end, - children: _buildSubmit(isDesktop), + children: _buildSubmit(isDesktop, layoutColor!), ) : Row( mainAxisAlignment: MainAxisAlignment.center, - children: _buildSubmit(isDesktop), + children: _buildSubmit(isDesktop, layoutColor!), )), ) ], @@ -704,10 +729,89 @@ class _CreateUserFormState extends State { ); } + // Widget buildUserTable(bool isDesktop) { + // return Container( + // margin: const EdgeInsets.only( + // left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), + // decoration: BoxDecoration( + // border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), + // child: Column( + // children: [ + // Container( + // color: Color(0xFFF7F7FB), + // child: Padding( + // padding: const EdgeInsets.all(8.0), + // child: Row( + // children: [ + // Text( + // apiselectedUser != null ? "Profile" : "New User", + // style: TextStyle( + // fontSize: 18, + // fontWeight: FontWeight.w200, + // color: Colors.black), + // ), + // if (isEditProfile) + // IconButton( + // icon: Icon( + // Icons.edit, + // color: Colors.blueAccent, + // size: 18, + // ), + // onPressed: () { + // setState(() { + // isViewMode = !isViewMode; + // }); + // }) + // ], + // ), + // ), + // ), + // SizedBox( + // height: 15, + // ), + // Expanded( + // child: SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: _buildUserDetails( + // isDesktop), // Ensure this returns a scrollable widget + // ), + // ), + // ], + // ), + // ); + // } + Widget _buildUserDetails(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Text( + apiselectedUser != null ? "Profile" : "New User", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w200, + color: Colors.black), + ), + // if (isEditProfile) + // IconButton( + // icon: Icon( + // Icons.edit, + // color: Colors.blueAccent, + // size: 18, + // ), + // onPressed: () { + // setState(() { + // isViewMode = !isViewMode; + // }); + // }) + ], + ), + ), + Padding( padding: const EdgeInsets.all(8.0), child: Row( @@ -720,7 +824,7 @@ class _CreateUserFormState extends State { Icon( Icons.filter_1_outlined, color: Color(0xFF8B8FB2), - size: 18, + size: 16, ), SizedBox( width: 10, @@ -728,7 +832,7 @@ class _CreateUserFormState extends State { Text( "Personal Details", style: TextStyle( - fontSize: 18, + fontSize: 14, fontWeight: FontWeight.bold, // color: Colors.black, color: Color(0xFF8B8FB2)), @@ -835,7 +939,7 @@ class _CreateUserFormState extends State { Icon( Icons.filter_2_outlined, color: Color(0xFF8B8FB2), - size: 18, + size: 16, ), SizedBox( width: 10, @@ -843,7 +947,7 @@ class _CreateUserFormState extends State { Text( "Passport Details", style: TextStyle( - fontSize: 18, + fontSize: 14, fontWeight: FontWeight.bold, // color: Colors.black, color: Color(0xFF8B8FB2)), @@ -931,7 +1035,7 @@ class _CreateUserFormState extends State { Icon( Icons.filter_3_outlined, color: Color(0xFF8B8FB2), - size: 18, + size: 16, ), SizedBox( width: 10, @@ -939,7 +1043,7 @@ class _CreateUserFormState extends State { Text( "Employee Organization Details", style: TextStyle( - fontSize: 18, + fontSize: 14, fontWeight: FontWeight.bold, // color: Colors.black, color: Color(0xFF8B8FB2)), @@ -1026,7 +1130,7 @@ class _CreateUserFormState extends State { Icon( Icons.filter_4_outlined, color: Color(0xFF8B8FB2), - size: 18, + size: 16, ), SizedBox( width: 10, @@ -1034,7 +1138,7 @@ class _CreateUserFormState extends State { Text( "Approver", style: TextStyle( - fontSize: 18, + fontSize: 14, fontWeight: FontWeight.bold, // color: Colors.black, color: Color(0xFF8B8FB2)), @@ -1120,31 +1224,6 @@ class _CreateUserFormState extends State { //--------------------------------- Personal Details --------------------------- Widget _buildFirstRowLeftColumn(bool isDesktop) { - DateTime? _selectedDateOfBirth; - - Future _selectCheckDateOfBirth(BuildContext context) async { - DateTime now = DateTime.now(); - DateTime today = DateTime(now.year, now.month, now.day); - - DateTime? pickedDate = await showDatePicker( - context: context, - initialDate: - _selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today) - ? _selectedDateOfBirth! - : today, - firstDate: DateTime(1900), - lastDate: DateTime(2100), - ); - - if (pickedDate != null && pickedDate != _selectedDateOfBirth) { - setState(() { - _selectedDateOfBirth = pickedDate; - controllers["dob"]?.text = - DateFormat('yyyy-MM-dd').format(pickedDate); - }); - } - } - return Container( // color: Colors.yellow, color: Colors.white, @@ -1160,7 +1239,7 @@ class _CreateUserFormState extends State { children: [ Text("First Name", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -1170,7 +1249,7 @@ class _CreateUserFormState extends State { child: SizedBox( height: 40, child: TextField( - style: TextStyle(fontSize: 12), + style: TextStyle(fontSize: 12, color: Colors.black), controller: controllers["Fname"], enabled: !isViewMode, onChanged: (value) { @@ -1199,52 +1278,6 @@ class _CreateUserFormState extends State { ], ), SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Last Name", - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12), - controller: controllers["Lname"], - enabled: !isViewMode, - onChanged: (value) { - _clearError("last_name"); - }, - decoration: InputDecoration( - labelText: "LastName", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - if (errorMessages["last_name"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["last_name"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - ], - ), - SizedBox(height: 3), Row( children: [ Column( @@ -1252,7 +1285,7 @@ class _CreateUserFormState extends State { children: [ Text("Gender", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -1262,7 +1295,8 @@ class _CreateUserFormState extends State { child: SizedBox( height: 40, child: DropdownButtonFormField( - value: selectedGender, + // value: selectedGender, + value: isViewMode ? null : selectedGender, onChanged: isViewMode ? null : (String? newValue) { @@ -1276,7 +1310,7 @@ class _CreateUserFormState extends State { enabled: !isViewMode, // Disables input when in view mode ), - style: TextStyle(fontSize: 12), + style: TextStyle(fontSize: 12, color: Colors.black), items: [ DropdownMenuItem(value: "Male", child: Text("Male")), DropdownMenuItem( @@ -1284,7 +1318,11 @@ class _CreateUserFormState extends State { ], hint: Text( selectedGender ?? "Select Gender", - style: TextStyle(fontSize: 12), + style: TextStyle(fontSize: 12, color: Colors.black), + ), + disabledHint: Text( + selectedGender ?? "Select Gender", + style: TextStyle(fontSize: 12, color: Colors.black), ), ), @@ -1332,190 +1370,6 @@ class _CreateUserFormState extends State { ], ), SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Date of Birth", - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: GestureDetector( - onTap: isViewMode - ? null - : () async { - await _selectCheckDateOfBirth(context); - if (controllers["dob"]!.text.isNotEmpty) { - setState(() { - // errorMessages.remove("start_date"); - }); - } - }, - child: AbsorbPointer( - child: TextField( - controller: controllers["dob"], - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Select Date", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Color(0xFF8B8FB2)), - ), - ), - ), - ), - ), - ), - ], - ), - ], - ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Email", - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12), - controller: controllers["email"], - onChanged: (value) { - _clearError("email"); - }, - enabled: !isViewMode, - decoration: InputDecoration( - labelText: "Email", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - if (errorMessages["email"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["email"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - ], - ), - SizedBox(height: 3), - apiselectedUser != null - ? SizedBox() - : Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Password", - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12), - controller: controllers["password"], - enabled: !isViewMode, - onChanged: (value) { - _clearError("password"); - }, - decoration: InputDecoration( - labelText: "Password", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: - FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - if (errorMessages["password"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["password"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - ], - ), - if (!isDesktop) - SizedBox( - height: 3, - ), - ], - ), - // ), - ); - } - - Widget _buildFirstRowRightColumn(bool isDesktop) { - late Map countryMap; // Mapping country_code -> country_name - late List countryCodes; // List of country codes - List countryList = apiCountryData ?? []; - - countryList = apiCountryData ?? []; - - // Map country codes to country names - countryMap = { - for (var item in countryList) - item['country_code'] as String: item['country_name'] as String - }; - - // Extract only country codes for processing - countryCodes = countryMap.keys.toList(); - - selectedCountry ??= null; - - return Container( - // color: Colors.yellow, - color: Colors.white, - // child: Expanded( - // Allow second column to take available space - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ Row( children: [ Column( @@ -1523,7 +1377,7 @@ class _CreateUserFormState extends State { children: [ Text("Mobile Number", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -1533,7 +1387,7 @@ class _CreateUserFormState extends State { child: SizedBox( height: 40, child: TextField( - style: TextStyle(fontSize: 12), + style: TextStyle(fontSize: 12, color: Colors.black), controller: controllers["mobileNumber"], enabled: !isViewMode, onChanged: (value) { @@ -1573,9 +1427,9 @@ class _CreateUserFormState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Alternate Mobile Number", + Text("Email", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -1585,7 +1439,250 @@ class _CreateUserFormState extends State { child: SizedBox( height: 40, child: TextField( - style: TextStyle(fontSize: 12), + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["email"], + onChanged: (value) { + _clearError("email"); + }, + enabled: !isViewMode, + decoration: InputDecoration( + labelText: "Email", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (errorMessages["email"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["email"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Address", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 110, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["address"], + enabled: !isViewMode, + maxLines: 4, + keyboardType: TextInputType.multiline, + decoration: InputDecoration( + labelText: "Address", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + contentPadding: EdgeInsets.symmetric(vertical: 4), + ), + ), + ), + ), + ], + ), + ], + ), + ], + ), + // ), + ); + } + + Widget _buildFirstRowRightColumn(bool isDesktop) { + DateTime? _selectedDateOfBirth; + + Future _selectCheckDateOfBirth(BuildContext context) async { + DateTime now = DateTime.now(); + DateTime today = DateTime(now.year, now.month, now.day); + + DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: + _selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today) + ? _selectedDateOfBirth! + : today, + firstDate: DateTime(1900), + lastDate: DateTime(2100), + ); + + if (pickedDate != null && pickedDate != _selectedDateOfBirth) { + setState(() { + _selectedDateOfBirth = pickedDate; + controllers["dob"]?.text = + DateFormat('yyyy-MM-dd').format(pickedDate); + }); + } + } + + // ----------------- + + late Map countryMap; // Mapping country_code -> country_name + late List countryCodes; // List of country codes + List countryList = apiCountryData ?? []; + + countryList = apiCountryData ?? []; + + // Map country codes to country names + countryMap = { + for (var item in countryList) + item['country_code'] as String: item['country_name'] as String + }; + + // Extract only country codes for processing + countryCodes = countryMap.keys.toList(); + + selectedCountry ??= null; + + return Container( + // color: Colors.yellow, + color: Colors.white, + // child: Expanded( + // Allow second column to take available space + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Last Name", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["Lname"], + enabled: !isViewMode, + onChanged: (value) { + _clearError("last_name"); + }, + decoration: InputDecoration( + labelText: "LastName", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (errorMessages["last_name"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["last_name"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Date of Birth", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: isViewMode + ? null + : () async { + await _selectCheckDateOfBirth(context); + if (controllers["dob"]!.text.isNotEmpty) { + setState(() { + // errorMessages.remove("start_date"); + }); + } + }, + child: AbsorbPointer( + child: TextField( + controller: controllers["dob"], + style: const TextStyle( + fontSize: 12, color: Colors.black), + decoration: const InputDecoration( + labelText: "Select Date", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(vertical: 16), + suffixIcon: Icon(Icons.calendar_today, + size: 16, color: Color(0xFF8B8FB2)), + ), + ), + ), + ), + ), + ), + ], + ), + ], + ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Alternate Mobile Number", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), controller: controllers["alternateMobile"], enabled: !isViewMode, keyboardType: @@ -1617,86 +1714,60 @@ class _CreateUserFormState extends State { ], ), SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Address", - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 110, - child: TextField( - style: TextStyle(fontSize: 12), - controller: controllers["address"], - enabled: !isViewMode, - maxLines: 4, - keyboardType: TextInputType.multiline, - decoration: InputDecoration( - labelText: "Address", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric(vertical: 16), - contentPadding: EdgeInsets.symmetric(vertical: 4), + apiselectedUser != null + ? SizedBox() + : Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Password", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: + TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["password"], + enabled: !isViewMode, + onChanged: (value) { + _clearError("password"); + }, + decoration: InputDecoration( + labelText: "Password", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(vertical: 16), + ), + ), + ), ), - ), - ), - ), - ], - ), - ], - ), - SizedBox(height: 3), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Postal Code", - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12), - controller: controllers["postalCode"], - enabled: !isViewMode, - keyboardType: - TextInputType.numberWithOptions(decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + if (errorMessages["password"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["password"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), ], - decoration: InputDecoration( - labelText: "Postal Code", - labelStyle: - TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), + ], ), - ), - ], - ), - ], - ), + ], + ), + if (!isDesktop) + SizedBox( + height: 3, + ), SizedBox(height: 3), Row( children: [ @@ -1705,7 +1776,7 @@ class _CreateUserFormState extends State { children: [ Text("Country", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -1743,7 +1814,7 @@ class _CreateUserFormState extends State { alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select Country", - style: TextStyle(fontSize: 12), + style: TextStyle(fontSize: 12, color: Colors.black), ), ), onChanged: isViewMode @@ -1768,6 +1839,48 @@ class _CreateUserFormState extends State { ), ], ), + SizedBox(height: 3), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Postal Code", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12, color: Colors.black), + controller: controllers["postalCode"], + enabled: !isViewMode, + keyboardType: + TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + ], + decoration: InputDecoration( + labelText: "Postal Code", + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + ], + ), + ], + ), if (!isDesktop) SizedBox( height: 3, @@ -1794,7 +1907,7 @@ class _CreateUserFormState extends State { children: [ Text("Passport Number", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -1804,7 +1917,7 @@ class _CreateUserFormState extends State { child: SizedBox( height: 40, child: TextField( - style: TextStyle(fontSize: 12), + style: TextStyle(fontSize: 12, color: Colors.black), controller: controllers["passportNumber"], enabled: !isViewMode, decoration: InputDecoration( @@ -1832,7 +1945,7 @@ class _CreateUserFormState extends State { children: [ Text("Passport Document", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -2045,7 +2158,7 @@ class _CreateUserFormState extends State { children: [ Text("Place of Issue", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -2055,7 +2168,7 @@ class _CreateUserFormState extends State { child: SizedBox( height: 40, child: TextField( - style: TextStyle(fontSize: 12), + style: TextStyle(fontSize: 12, color: Colors.black), controller: controllers["placeOfIssue"], enabled: !isViewMode, decoration: InputDecoration( @@ -2083,7 +2196,7 @@ class _CreateUserFormState extends State { children: [ Text("Date of Issue", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -2108,7 +2221,8 @@ class _CreateUserFormState extends State { child: AbsorbPointer( child: TextField( controller: controllers["dateOfIssue"], - style: const TextStyle(fontSize: 12), + style: const TextStyle( + fontSize: 12, color: Colors.black), decoration: const InputDecoration( labelText: "Select Date", labelStyle: @@ -2140,7 +2254,7 @@ class _CreateUserFormState extends State { children: [ Text("Date of Expiry", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -2165,7 +2279,8 @@ class _CreateUserFormState extends State { child: AbsorbPointer( child: TextField( controller: controllers["dateOfExpiry"], - style: const TextStyle(fontSize: 12), + style: const TextStyle( + fontSize: 12, color: Colors.black), decoration: const InputDecoration( labelText: "Select Date", labelStyle: @@ -2213,7 +2328,7 @@ class _CreateUserFormState extends State { children: [ Text("Employee Code", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -2223,7 +2338,7 @@ class _CreateUserFormState extends State { child: SizedBox( height: 40, child: TextField( - style: TextStyle(fontSize: 12), + style: TextStyle(fontSize: 12, color: Colors.black), controller: controllers["employeeCode"], enabled: !isViewMode, decoration: InputDecoration( @@ -2241,63 +2356,113 @@ class _CreateUserFormState extends State { ), ], ), - SizedBox(height: 3), + // SizedBox(height: 3), + // Row( + // children: [ + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text("User Type", + // style: TextStyle( + // fontSize: 12, + // fontWeight: FontWeight.w200, + // color: Colors.black)), + // SizedBox(height: 5), + // CustomTextFieldUserWrapper( + // isFocused: false, + // isDesktop: isDesktop, + // child: SizedBox( + // height: 40, + // child: DropdownSearch( + // selectedItem: selectedUserType, + // enabled: !isViewMode, + // popupProps: PopupProps.menu( + // // showSearchBox: true, + // fit: FlexFit.loose, // Allows flexible height + // constraints: BoxConstraints(maxHeight: 250), + // ), + // items: [ + // "Normal", + // "Privilege", + // ], + // dropdownDecoratorProps: DropDownDecoratorProps( + // dropdownSearchDecoration: InputDecoration( + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric( + // horizontal: 1, + // ), + // ), + // ), + // dropdownBuilder: (context, selectedItem) => Align( + // // Center-align selected item + // alignment: Alignment.centerLeft, + // child: Text( + // selectedItem ?? "Select", + // style: TextStyle(fontSize: 12), + // ), + // ), + // onChanged: (String? newValue) { + // setState(() { + // // Find the country_code based on selected country_name + // selectedUserType = newValue; + // + // // print("selectedUserType - $selectedUserType"); + // + // // if (selectedCountry!.isNotEmpty) { + // // errorMessages.remove("country_code"); + // // } + // }); + // }, + // ), + // ), + // ), + // ], + // ), + // ], + // ), + Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("User Type", + Text("Role ", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), CustomTextFieldUserWrapper( - isFocused: false, + isFocused: false, // Dropdown doesn't use focus isDesktop: isDesktop, child: SizedBox( - height: 40, - child: DropdownSearch( - selectedItem: selectedUserType, - enabled: !isViewMode, - popupProps: PopupProps.menu( - // showSearchBox: true, - fit: FlexFit.loose, // Allows flexible height - constraints: BoxConstraints(maxHeight: 250), + height: 45, // Set appropriate height + child: DropdownButtonFormField( + value: isViewMode ? null : selectedRole, + style: TextStyle(fontSize: 12, color: Colors.black), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10), // Proper padding ), - items: [ - "Normal", - "Privilege", - ], - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 1, - ), - ), + onChanged: isViewMode + ? null + : (newValue) { + setState(() { + selectedRole = newValue; + }); + }, + items: + apiRoleData?.map>((item) { + return DropdownMenuItem( + value: item['dropdown_key'], // ID as value + child: Text(item['dropdown_value'] ?? "Select")); + }).toList(), + hint: Text("Select"), + disabledHint: Text( + selectedRole ?? "Select Role", + style: TextStyle(fontSize: 12, color: Colors.black), ), - dropdownBuilder: (context, selectedItem) => Align( - // Center-align selected item - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select", - style: TextStyle(fontSize: 12), - ), - ), - onChanged: (String? newValue) { - setState(() { - // Find the country_code based on selected country_name - selectedUserType = newValue; - - // print("selectedUserType - $selectedUserType"); - - // if (selectedCountry!.isNotEmpty) { - // errorMessages.remove("country_code"); - // } - }); - }, ), ), ), @@ -2305,6 +2470,7 @@ class _CreateUserFormState extends State { ), ], ), + SizedBox(height: 3), if (!isDesktop) SizedBox(height: 3), ], ), @@ -2321,52 +2487,6 @@ class _CreateUserFormState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Role ", - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, // Dropdown doesn't use focus - isDesktop: isDesktop, - child: SizedBox( - height: 45, // Set appropriate height - child: DropdownButtonFormField( - value: selectedRole, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding - ), - onChanged: isViewMode - ? null - : (newValue) { - setState(() { - selectedRole = newValue; - }); - }, - items: - apiRoleData?.map>((item) { - return DropdownMenuItem( - value: item['dropdown_key'], // ID as value - child: Text(item['dropdown_value'] ?? "Select")); - }).toList(), - hint: Text("Select"), - ), - ), - ), - ], - ), - ], - ), - SizedBox(height: 3), Row( children: [ Column( @@ -2374,7 +2494,7 @@ class _CreateUserFormState extends State { children: [ Text("Group", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -2405,7 +2525,7 @@ class _CreateUserFormState extends State { alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select", - style: TextStyle(fontSize: 12), + style: TextStyle(fontSize: 12, color: Colors.black), ), ), onChanged: (String? newValue) { @@ -2433,7 +2553,7 @@ class _CreateUserFormState extends State { children: [ Text("Department", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -2443,8 +2563,8 @@ class _CreateUserFormState extends State { child: SizedBox( height: 45, // Set appropriate height child: DropdownButtonFormField( - value: selectedDepartment, - style: TextStyle(fontSize: 12), + value: isViewMode ? null : selectedDepartment, + style: TextStyle(fontSize: 12, color: Colors.black), decoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric( @@ -2465,6 +2585,10 @@ class _CreateUserFormState extends State { ); }).toList(), hint: Text("Select"), + disabledHint: Text( + selectedDepartment ?? "Select Department", + style: TextStyle(fontSize: 12, color: Colors.black), + ), ), ), ), @@ -2496,7 +2620,7 @@ class _CreateUserFormState extends State { children: [ Text("First Approver", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -2590,7 +2714,7 @@ class _CreateUserFormState extends State { children: [ Text("Second Approver", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -2684,7 +2808,7 @@ class _CreateUserFormState extends State { children: [ Text("Third Approver", style: TextStyle( - fontSize: 15, + fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), @@ -2765,50 +2889,51 @@ class _CreateUserFormState extends State { //----------------------------------- Submit --------------------------------------- - List _buildSubmit(isDesktop) { + List _buildSubmit(isDesktop, Color layoutColor) { return [ ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.white, - foregroundColor: Colors.blueAccent, + foregroundColor: layoutColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Colors.blueAccent, width: 2), + side: BorderSide(color: layoutColor, width: 2), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: () { - context.go('/listUser'); + isEditProfile ? context.go('/home') : context.go('/listUser'); }, - child: Text("Cancel")), + child: isEditProfile ? Text("Back") : Text("Cancel")), SizedBox( width: 20, ), - MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: isViewMode - ? Colors.blueAccent - : Colors.blueAccent, // Keep original color - foregroundColor: - isViewMode ? Colors.white : Colors.white, // Keep original color - disabledBackgroundColor: - Colors.blueAccent, // Ensure color remains when disabled - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Colors.blueAccent, width: 2), + if (!isEditProfile) + MouseRegion( + cursor: isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + isViewMode ? layoutColor : layoutColor, // Keep original color + foregroundColor: isViewMode + ? Colors.white + : Colors.white, // Keep original color + disabledBackgroundColor: + layoutColor, // Ensure color remains when disabled + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: layoutColor, width: 2), + ), + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + onPressed: + isViewMode ? null : handleSubmit, // Disable when in view mode + child: Text("Submit"), ), - onPressed: - isViewMode ? null : handleSubmit, // Disable when in view mode - child: Text("Submit"), - ), - ) + ) ]; } } diff --git a/lib/routes/custom_drawer.dart b/lib/routes/custom_drawer.dart index 261d751..7d6d011 100644 --- a/lib/routes/custom_drawer.dart +++ b/lib/routes/custom_drawer.dart @@ -69,6 +69,7 @@ class _CustomDrawerState extends State { "user_id": userDetails["user_id"].toString(), "name": "${userDetails["first_name"]} ${userDetails["last_name"]}", "email": userDetails["email"] ?? "", + "role": userDetails["role"] ?? "", }; } catch (e) { print("Error decoding user data: $e"); @@ -99,11 +100,19 @@ class _CustomDrawerState extends State { selectedOrg!['color'].toString().replaceFirst('0x', ''), radix: 16)) : Colors.blue; + + String? rawLogoPath = selectedOrg?['logo']; + if (rawLogoPath != null && rawLogoPath.contains('/assets')) { + const baseUrl = "https://apitest.tripapprovaltool.com"; + final assetPath = rawLogoPath.split('/assets').last; + selectedOrg!['logo'] = "$baseUrl/assets$assetPath"; + } }); // Save to SharedPreferences await prefs.setString('layout_color', selectedOrg?['layout_color']); await prefs.setString('body_color', selectedOrg?['color']); + await prefs.setString('body_color', selectedOrg?['plan_action']); print( "Layout Color- ${selectedOrg?['layout_color']} - $layoutColor ---------- bodyColor - $bodyColor"); @@ -128,12 +137,27 @@ class _CustomDrawerState extends State { Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - Image.asset( - 'assets/images/login/travelSpend_Logo.png', - width: 160, - height: 40, - fit: BoxFit.contain, - ), + selectedOrg?['logo'] != null + ? ClipOval( + child: Image.network( + selectedOrg!['logo'], + width: 50, + height: 50, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return const CircleAvatar( + radius: 20, + backgroundColor: Colors.redAccent, + child: Icon(Icons.error, size: 10), + ); + }, + ), + ) + : const CircleAvatar( + radius: 20, + backgroundColor: Colors.amber, + child: Icon(Icons.add_a_photo, size: 10), + ), ], ), @@ -157,33 +181,36 @@ class _CustomDrawerState extends State { ), ], ), - Row( - children: [ - GestureDetector( - onTap: () { - print("ONTAP Custom"); - print("ONTAP Custom- $userDetails "); - context.go( - "/CreateUserDetails", - extra: { - "selectedUser": userDetails, - "isEditProfile": true, - "isViewMode": true - }, - ); - }, - child: Text( - userData?["name"] ?? "N/A", - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w600, - fontFamily: "Archivo", - // color: Color(0xFF12B24B), - color: layoutColor, + MouseRegion( + cursor: SystemMouseCursors.click, + child: Row( + children: [ + GestureDetector( + onTap: () { + print("ONTAP Custom"); + print("ONTAP Custom- $userDetails "); + context.go( + "/CreateUserDetails", + extra: { + "selectedUser": userDetails, + "isEditProfile": true, + "isViewMode": true + }, + ); + }, + child: Text( + userData?["name"] ?? "N/A", + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + fontFamily: "Archivo", + // color: Color(0xFF12B24B), + color: layoutColor, + ), ), ), - ), - ], + ], + ), ), ], ), @@ -247,60 +274,51 @@ class _CustomDrawerState extends State { SizedBox( height: 15, ), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - "Components", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - fontFamily: "Archivo", - color: Colors.black87, - ), - ), - ], - ), - - SizedBox( - height: 10, - ), ], ), ), _buildDrawerItem(context, Icons.home_outlined, 'Home', '/home'), - _buildExpandableItem( + _buildDrawerItem(context, Icons.request_page_outlined, + 'My Travel Request', '/listPlan'), + _buildDrawerItem(context, Icons.assessment_outlined, 'My Approvals', + '/ApprovalList'), + if (userData?["role"] != "User") + _buildDrawerItem(context, Icons.account_circle_outlined, + 'User List', '/listUser'), + // _buildExpandableItem( + // context, + // Icons.assessment_outlined, + // 'Plans', + // [ + // _buildSubDrawerItem( + // context, 'My Travel Request', '/listPlan'), + // _buildSubDrawerItem(context, 'My Approvals', '/ApprovalList') + // ], + // '/listPlan'), + // + // _buildExpandableItem( + // context, + // Icons.account_circle_outlined, + // 'User ', + // [ + // _buildSubDrawerItem(context, 'User List', '/listUser'), + // // _buildSubDrawerItem(context,'PlanB','/PlanB') + // ], + // '/listUser'), + if (userData?["role"] != "User") + _buildExpandableItem( context, - Icons.assessment_outlined, - 'Plans', + Icons.settings_outlined, + 'Settings ', [ _buildSubDrawerItem( - context, 'My Travel Request', '/listPlan'), - _buildSubDrawerItem(context, 'My Approvals', '/ApprovalList') - ], - '/listPlan'), - _buildExpandableItem( - context, - Icons.account_circle_outlined, - 'User ', - [ - _buildSubDrawerItem(context, 'User List', '/listUser'), + context, 'Organization', '/OrganizationSetup'), + _buildSubDrawerItem(context, 'Group', '/group'), + _buildSubDrawerItem(context, 'Policy', '/PolicyList'), // _buildSubDrawerItem(context,'PlanB','/PlanB') ], - '/listUser'), - _buildExpandableItem( - context, - Icons.settings_outlined, - 'Settings ', - [ - _buildSubDrawerItem( - context, 'Organization', '/OrganizationSetup'), - _buildSubDrawerItem(context, 'Group', '/group'), - _buildSubDrawerItem(context, 'Policy', '/PolicyList'), - // _buildSubDrawerItem(context,'PlanB','/PlanB') - ], - '/OrganizationSetup', - ), + '/OrganizationSetup', + ), _buildDrawerItem(context, Icons.login_outlined, 'Logout', '/'), if (widget.isDesktop) Spacer(), Container( @@ -400,7 +418,7 @@ class _CustomDrawerState extends State { title: Text( title, style: TextStyle( - fontSize: 14, + fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF475569), fontFamily: "Archivo"), @@ -450,7 +468,7 @@ class _CustomDrawerState extends State { Text( title, style: TextStyle( - fontSize: 14, + fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF475569), fontFamily: "Archivo", diff --git a/lib/routes/custom_router.dart b/lib/routes/custom_router.dart index 5729da7..c1d9370 100644 --- a/lib/routes/custom_router.dart +++ b/lib/routes/custom_router.dart @@ -63,7 +63,10 @@ final GoRouter router = GoRouter( ), GoRoute( path: '/Policy', - builder: (context, state) => Policy(), + // builder: (context, state) => Policy(), + pageBuilder: (context, state) => MaterialPage( + child: Policy.fromState(state), + ), ), GoRoute( path: '/PolicyList', diff --git a/lib/services/apiService.dart b/lib/services/apiService.dart index d95925f..f6e3b51 100644 --- a/lib/services/apiService.dart +++ b/lib/services/apiService.dart @@ -265,6 +265,44 @@ class ApiService { } } + Future> getSinglePolicy(int policyId) async { + final String apiUrldata = '$apiUrl/api/policy/find/${policyId}'; + + final token = await getToken(); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + try { + final data = json.decode(response.body); + print(data); + if (!data.containsKey('data') || data['data'] is! Map) { + throw Exception( + "Invalid response format: 'data' field is missing or not a Map"); + } + + Map plansJson = + data['data']; // 'data' is a Map, not a List + + return plansJson; + } catch (e) { + throw Exception('Error parsing response: $e'); + } + } else { + throw Exception('Failed to load plans'); + } + } + Future> fetchOrganization() async { String? orgId = await getOrgId(); diff --git a/lib/utils/auth_utils.dart b/lib/utils/auth_utils.dart index 675a840..23d7a82 100644 --- a/lib/utils/auth_utils.dart +++ b/lib/utils/auth_utils.dart @@ -47,6 +47,21 @@ Future getOrgId() async { return null; } +Future getTripPlanAction() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); + return userData["plan_action"]?.toString(); + } catch (e) { + return null; + } + } + return null; +} + Future>?> getUserServices() async { final prefs = await SharedPreferences.getInstance(); final String? userDataString = prefs.getString('user_data'); diff --git a/pubspec.yaml b/pubspec.yaml index 931740b..aed4efa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,7 +47,6 @@ dependencies: image_picker: ^1.1.2 - dev_dependencies: flutter_test: sdk: flutter