ui changes

This commit is contained in:
venbaittech 2025-04-22 12:56:30 +05:30
parent e0ed41ad35
commit ceb6aece50
17 changed files with 2302 additions and 1354 deletions

View File

@ -434,26 +434,43 @@ class _ApprovalListState extends State<ApprovalList> {
fontSize: 13, fontSize: 13,
fontFamily: "Archivo", fontFamily: "Archivo",
))), ))),
DataCell(Container( DataCell(
padding: const EdgeInsets.symmetric( Container(
vertical: 4, horizontal: 10), width: double
.infinity, // Set your desired fixed size (equal width and height)
height: 25,
alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: plan.status == "Active" color: plan.statusValue ==
? layoutColor "Partially Approved"
: Colors.grey.shade50, ? 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), borderRadius: BorderRadius.circular(10),
), ),
child: Text( child: Text(
plan.statusValue, plan.statusValue,
textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
color: plan.status == "Active" color: (plan.statusValue ==
"Partially Approved" ||
plan.statusValue == "Approved" ||
plan.statusValue == "Rejected")
? Colors.black
: plan.statusValue == "Completed"
? Colors.white ? Colors.white
: Colors.grey, : Colors.grey,
fontSize: 13, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.w400,
),
),
), ),
), ),
)),
DataCell(Row(children: [ DataCell(Row(children: [
IconButton( IconButton(
icon: const Icon( icon: const Icon(
@ -480,6 +497,17 @@ class _ApprovalListState extends State<ApprovalList> {
// color: Colors.green), // color: Colors.green),
// onPressed: () => viewPlanforApprover(plan.planId, // onPressed: () => viewPlanforApprover(plan.planId,
// isViewMode: false) ), // isViewMode: false) ),
SizedBox(
width: 5,
),
GestureDetector(
onTap: () => (),
child: Image.asset(
'assets/images/IconsImg/delete.png',
width: 20,
height: 15),
),
])), ])),
]); ]);
}).toList(), }).toList(),

View File

@ -396,6 +396,9 @@ class _groupState extends State<Group> {
child: TextField( child: TextField(
controller: controllers["name"], controller: controllers["name"],
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
onChanged: (value) {
_clearError("name");
},
decoration: InputDecoration( decoration: InputDecoration(
labelText: "group name", labelText: "group name",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey), labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
@ -436,6 +439,9 @@ class _groupState extends State<Group> {
height: 40, height: 40,
child: TextField( child: TextField(
controller: controllers["description"], controller: controllers["description"],
onChanged: (value) {
_clearError("description");
},
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "description", labelText: "description",

View File

@ -1,8 +1,12 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/Screens/group/group.dart'; import 'package:frontend/Screens/group/group.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import '../../config/apiUrl.dart';
import '../../routes/custom_appBar.dart'; import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart'; import '../../routes/custom_drawer.dart';
import '../../services/apiService.dart'; import '../../services/apiService.dart';
@ -57,10 +61,59 @@ class _GroupListState extends State<GroupList> {
} }
} }
void deleteGroup(int groupId) { void handleActiveStatus(
setState(() { Map<String, dynamic> groupData,
apiAllGroups?.removeWhere((group) => group['group_id'] == groupId); 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<String, dynamic> 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<void> deleteGroupFromApi(int groupId) async { // Future<void> deleteGroupFromApi(int groupId) async {
@ -192,7 +245,8 @@ class _GroupListState extends State<GroupList> {
final group = apiAllGroups![index]; final group = apiAllGroups![index];
return Card( return Card(
// color: bodyColor, // color: bodyColor,
color: Color(0xFFF5F5F5), // color: Color(0xFFF5F5F5),
color: Colors.white,
margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10), margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10),
child: Padding( child: Padding(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
@ -209,13 +263,31 @@ class _GroupListState extends State<GroupList> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
TextButton( GestureDetector(
onPressed: () { onTap: () {
context.go("/CreateGroup", extra: group); 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),
), ),
], ],
), ),

View File

@ -609,12 +609,15 @@ class _MailSettingState extends State<MailSetting> {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2), side: BorderSide(color: Color(0xFF114D8B), width: 2),
), ),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12),
), ),
onPressed: () { onPressed: () {
handleTestMailSubmit(); handleTestMailSubmit();
}, },
child: Text("Test Email")) child: Text(
"Test Email",
style: TextStyle(fontSize: 12),
))
], ],
), ),
]; ];

View File

@ -344,6 +344,41 @@ class _OrgSetUpState extends State<OrgSetUp> {
} }
Widget buildOrganizationLayout(isDesktop) { 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<void> _pickImage() async { Future<void> _pickImage() async {
final picker = ImagePicker(); final picker = ImagePicker();
final XFile? pickedFile = final XFile? pickedFile =
@ -365,25 +400,29 @@ class _OrgSetUpState extends State<OrgSetUp> {
} }
return Container( 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( decoration: BoxDecoration(
border: isDesktop
? Border.all(
width: 2,
color: Color(0xFFF7F7FB),
)
: null,
color: Colors.white,
// color: Color(0xFFF7F7FB),
// color: Colors.amber, // color: Colors.amber,
color: bodyColor, ),
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: SingleChildScrollView(
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),
child: Column(
children: [
SingleChildScrollView(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
child: Container( child: Column(
children: [
Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
// height: MediaQuery.of(context).size.height * 0.8, // height: MediaQuery.of(context).size.height * 0.8,
color: Colors.white, color: Colors.white,
@ -397,10 +436,11 @@ class _OrgSetUpState extends State<OrgSetUp> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text(
"Create Organization", selectedOrg != null && selectedOrg!.isNotEmpty
? "Update Organization"
: "Create Organization",
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 15, fontWeight: FontWeight.w800),
fontWeight: FontWeight.w600),
), ),
], ],
), ),
@ -432,8 +472,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
), ),
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Enter Organization Name", hintText: "Enter Organization Name",
hintStyle: TextStyle( hintStyle:
fontSize: 14, color: Colors.grey), TextStyle(fontSize: 14, color: Colors.grey),
floatingLabelBehavior: floatingLabelBehavior:
FloatingLabelBehavior.never, FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
@ -463,14 +503,12 @@ class _OrgSetUpState extends State<OrgSetUp> {
width: 50, width: 50,
height: 50, height: 50,
fit: BoxFit.cover, fit: BoxFit.cover,
errorBuilder: (context, error, errorBuilder:
stackTrace) { (context, error, stackTrace) {
return const CircleAvatar( return const CircleAvatar(
radius: 20, radius: 20,
backgroundColor: backgroundColor: Colors.redAccent,
Colors.redAccent, child: Icon(Icons.error, size: 10),
child: Icon(Icons.error,
size: 10),
); );
}, },
), ),
@ -478,8 +516,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
: const CircleAvatar( : const CircleAvatar(
radius: 20, radius: 20,
backgroundColor: Colors.amber, backgroundColor: Colors.amber,
child: Icon(Icons.add_a_photo, child: Icon(Icons.add_a_photo, size: 10),
size: 10),
), ),
), ),
], ],
@ -493,8 +530,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
child: Column( child: Column(
children: [ children: [
Row( Row(
mainAxisAlignment: mainAxisAlignment: MainAxisAlignment.spaceBetween,
MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
"Mail Settings", "Mail Settings",
@ -530,10 +566,11 @@ class _OrgSetUpState extends State<OrgSetUp> {
border: Border.all( border: Border.all(
color: Color(0xFFF5F5F5), color: Color(0xFFF5F5F5),
// color: bodyColor ?? Colors.grey, // color: bodyColor ?? Colors.grey,
width: 1.0, width: 1.5,
), ),
// color: bodyColor, // color: bodyColor,
color: Color(0xFFF5F5F5), color: Colors.white70,
// color: Color(0xFFF5F5F5),
), ),
child: Row( child: Row(
mainAxisAlignment: isDesktop mainAxisAlignment: isDesktop
@ -544,8 +581,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
? MailSetting( ? MailSetting(
isDesktop: isDesktop, isDesktop: isDesktop,
initialMailData: mailConfig, initialMailData: mailConfig,
onMailDataChanged: onMailDataChanged: (updatedData) {
(updatedData) {
// You can setState here or do something else with updatedData // You can setState here or do something else with updatedData
print( print(
"Updated Mail Data: $updatedData"); "Updated Mail Data: $updatedData");
@ -578,14 +614,13 @@ class _OrgSetUpState extends State<OrgSetUp> {
border: Border.all(color: Color(0xFFF4F4FB)), border: Border.all(color: Color(0xFFF4F4FB)),
borderRadius: BorderRadius.circular(1), borderRadius: BorderRadius.circular(1),
// color: bodyColor, // color: bodyColor,
color: Color(0xFFF5F5F5), // color: Color(0xFFF5F5F5),
), color: Colors.white),
padding: EdgeInsets.only( padding:
left: 5, right: 5, top: 15, bottom: 15), EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 5),
child: isDesktop child: isDesktop
? Row( ? Row(
mainAxisAlignment: mainAxisAlignment: MainAxisAlignment.spaceEvenly,
MainAxisAlignment.spaceEvenly,
// mainAxisSize: MainAxisSize.min, // mainAxisSize: MainAxisSize.min,
children: _buildOptions(), children: _buildOptions(),
) )
@ -602,8 +637,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
height: 5, height: 5,
), ),
Column( Row(
crossAxisAlignment: CrossAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Choose Theme", "Choose Theme",
@ -631,8 +666,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
layoutColor = selectedLayoutColor; layoutColor = selectedLayoutColor;
}); });
}, },
onBodyColorSelected: onBodyColorSelected: (Color selectedBodyColor) {
(Color selectedBodyColor) {
setState(() { setState(() {
bodyColor = selectedBodyColor; bodyColor = selectedBodyColor;
}); });
@ -655,28 +689,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
], ],
), ),
), ),
),
], ],
), ),
), ),
),
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),
))
],
),
); );
} }

View File

@ -273,6 +273,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
List<dynamic>? apiCountryData; List<dynamic>? apiCountryData;
List<dynamic>? apiCostData; // Store API response here List<dynamic>? apiCostData; // Store API response here
bool isLoading = true; // Track loading state bool isLoading = true; // Track loading state
String? TripPlanAction;
bool showDomestic = false;
bool showInternational = false;
String? orgId; String? orgId;
String? planUsrId; String? planUsrId;
@ -469,6 +472,24 @@ class CreateNewPlansState extends State<CreateNewPlan> {
} }
} }
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() { void getSelectedPlanFor() {
if (!mounted) return; if (!mounted) return;
@ -492,7 +513,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
void fetchUserDetails() async { void fetchUserDetails() async {
final details = await getUserDetails(); final details = await getUserDetails();
TripPlanAction = await getTripPlanAction();
print("TripPlanAction- $TripPlanAction");
print("details- $details"); print("details- $details");
if (details != null) { if (details != null) {
@ -505,6 +527,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
orgId = await getOrgId(); orgId = await getOrgId();
print("userDetails - $selfId"); print("userDetails - $selfId");
getSelectedPlanFor(); getSelectedPlanFor();
setTripPlanAction();
} }
Future<String?> getToken() async { Future<String?> getToken() async {
@ -692,6 +715,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
} }
final requiredFields = { final requiredFields = {
if (TripPlanAction != "Plan Creation Not Allowed") //
"trip_type": _selectedTripType, "trip_type": _selectedTripType,
"cost_center_id": selectedCostCenterId, "cost_center_id": selectedCostCenterId,
"functional_department": selectedFuncDept, "functional_department": selectedFuncDept,
@ -1193,7 +1217,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Trip Type *", // Your label "Trip Type ( $TripPlanAction )", // Your label
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -1609,6 +1633,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
List<Widget> _buildTripType(bool isMobile) { List<Widget> _buildTripType(bool isMobile) {
return [ return [
if (showDomestic == true)
CustomTextFieldWrapper( CustomTextFieldWrapper(
color: Color(0xFFF4F4FB), color: Color(0xFFF4F4FB),
layoutColor: widget.layoutColor, layoutColor: widget.layoutColor,
@ -1624,8 +1649,10 @@ class CreateNewPlansState extends State<CreateNewPlan> {
Text( Text(
"Domestic", "Domestic",
style: TextStyle( style: TextStyle(
color: _selectedTripType == "1" ? Colors.white : Colors.black, color:
fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null, _selectedTripType == "1" ? Colors.white : Colors.black,
fontWeight:
_selectedTripType == "1" ? FontWeight.w600 : null,
fontSize: 13), fontSize: 13),
), ),
@ -1663,8 +1690,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// : Colors.transparent, // : Colors.transparent,
borderRadius: BorderRadius.circular(4), // Rounded rectangle borderRadius: BorderRadius.circular(4), // Rounded rectangle
border: Border.all( border: Border.all(
color: color: _selectedTripType == "1"
_selectedTripType == "1" ? Colors.white : Colors.black, ? Colors.white
: Colors.black,
width: _selectedTripType == "1" ? 2 : 1, width: _selectedTripType == "1" ? 2 : 1,
), ),
), ),
@ -1677,6 +1705,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
), ),
SizedBox(width: 20), SizedBox(width: 20),
if (showInternational)
CustomTextFieldWrapper( CustomTextFieldWrapper(
color: Color(0xFFF4F4FB), color: Color(0xFFF4F4FB),
layoutColor: widget.layoutColor, layoutColor: widget.layoutColor,
@ -1716,8 +1745,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// : Colors.transparent, // : Colors.transparent,
borderRadius: BorderRadius.circular(4), // Rounded rectangle borderRadius: BorderRadius.circular(4), // Rounded rectangle
border: Border.all( border: Border.all(
color: color: _selectedTripType == "2"
_selectedTripType == "2" ? Colors.white : Colors.black, ? Colors.white
: Colors.black,
width: _selectedTripType == "2" ? 2 : 1, width: _selectedTripType == "2" ? 2 : 1,
), ),
), ),

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:easy_stepper/easy_stepper.dart'; import 'package:easy_stepper/easy_stepper.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/Screens/itnerary_list/accomodation_list.dart'; import 'package:frontend/Screens/itnerary_list/accomodation_list.dart';
@ -54,7 +56,10 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
Map<String, dynamic>? selectedItem; Map<String, dynamic>? selectedItem;
int? selectedIndex; int? selectedIndex;
List<dynamic>? apiAllServices; List<dynamic>? selectedAllServices;
List<Map<String, String>> selectedOrgServiceIds = [];
List<dynamic>? ServicesChoosed;
List<String> filledItineraryKeys = [];
// List<Map<String, dynamic>> miscellaneousList = []; // List<Map<String, dynamic>> miscellaneousList = [];
@ -87,21 +92,98 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
void initState() { void initState() {
super.initState(); super.initState();
handleSelectedPlan(); handleSelectedPlan();
loadAllServices(); updateSelectedServices();
} }
Future<void> loadAllServices() async { Future<void> loadAllServices() async {
try { try {
final result = await apiService.fetchAllServices(); final result = await apiService.fetchAllServices();
setState(() { setState(() {
apiAllServices = result; selectedAllServices = result;
}); });
print("Fetched services: $apiAllServices"); print("Fetched services: $selectedAllServices");
} catch (e) { } catch (e) {
print('Error fetching role list: $e'); print('Error fetching role list: $e');
} }
} }
Future<void> loadOrgSelectedAlServices() async {
try {
final result = await apiService.fetchOrganization();
if (result != null && result is Map<String, dynamic>) {
final rawServices = result['services_ids'];
if (rawServices != null && rawServices is String) {
try {
List<dynamic> decoded = json.decode(rawServices);
List<Map<String, String>> 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<void> 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() { void handleSelectedPlan() {
// Check if selectedPlanData has itinerary data // Check if selectedPlanData has itinerary data
if (hasAnyItineraryData()) { if (hasAnyItineraryData()) {
@ -147,13 +229,30 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
"forex" "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) { for (String key in keys) {
if (widget.selectedPlanData.containsKey(key) && if (widget.selectedPlanData.containsKey(key) &&
widget.selectedPlanData[key] is List && widget.selectedPlanData[key] is List &&
(widget.selectedPlanData[key] as List).isNotEmpty) { (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 return false; // No itinerary data available
} }
@ -544,9 +643,9 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
} }
List<Widget> _buildOptions() { List<Widget> _buildOptions() {
if (apiAllServices == null) return []; if (ServicesChoosed == null) return [];
return apiAllServices!.map((service) { return ServicesChoosed!.map((service) {
return Padding( return Padding(
padding: const EdgeInsets.only(right: 20.0), padding: const EdgeInsets.only(right: 20.0),
child: _buildOption( child: _buildOption(

View File

@ -24,6 +24,7 @@ class _ListPlansState extends State<ListPlans> {
String? userId; String? userId;
String? orgId; String? orgId;
String? token; String? token;
String? TripPlanAction;
Color? layoutColor; Color? layoutColor;
Color? bodyColor; Color? bodyColor;
@ -60,6 +61,7 @@ class _ListPlansState extends State<ListPlans> {
token = await getToken(); token = await getToken();
userId = await getUserId(); userId = await getUserId();
orgId = await getOrgId(); orgId = await getOrgId();
TripPlanAction = await getTripPlanAction();
if (token == null || userId == null) { if (token == null || userId == null) {
print("Token or USerId missing"); print("Token or USerId missing");
@ -290,10 +292,34 @@ class _ListPlansState extends State<ListPlans> {
EdgeInsets.symmetric(horizontal: 20, vertical: 12), EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: () { onPressed: () {
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: { context.go('/createPlan', extra: {
'orgId': orgId, 'orgId': orgId,
}); });
if (!isDesktop) Navigator.pop(context); if (!isDesktop) Navigator.pop(context);
}
}, },
child: Row( child: Row(
mainAxisSize: mainAxisSize:
@ -450,26 +476,43 @@ class _ListPlansState extends State<ListPlans> {
fontSize: 13, fontSize: 13,
fontFamily: "Archivo", fontFamily: "Archivo",
))), ))),
DataCell(Container( DataCell(
padding: const EdgeInsets.symmetric( Container(
vertical: 4, horizontal: 10), width: double
.infinity, // Set your desired fixed size (equal width and height)
height: 25,
alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: plan.status == "Active" color: plan.statusValue ==
? layoutColor "Partially Approved"
: Colors.grey.shade50, ? 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), borderRadius: BorderRadius.circular(10),
), ),
child: Text( child: Text(
plan.statusValue, plan.statusValue,
textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
color: plan.status == "Active" color: (plan.statusValue ==
"Partially Approved" ||
plan.statusValue == "Approved" ||
plan.statusValue == "Rejected")
? Colors.black
: plan.statusValue == "Completed"
? Colors.white ? Colors.white
: Colors.grey, : Colors.grey,
fontSize: 13, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.w400,
),
),
), ),
), ),
)),
DataCell(Row(children: [ DataCell(Row(children: [
IconButton( IconButton(
icon: const Icon( icon: const Icon(
@ -494,6 +537,17 @@ class _ListPlansState extends State<ListPlans> {
// color: Colors.green), // color: Colors.green),
// onPressed: () => viewPlan(plan.planId, // onPressed: () => viewPlan(plan.planId,
// isViewMode: false) ), // isViewMode: false) ),
SizedBox(
width: 5,
),
GestureDetector(
onTap: () => (),
child: Image.asset(
'assets/images/IconsImg/delete.png',
width: 20,
height: 15),
),
])), ])),
]); ]);
}).toList(), }).toList(),

View File

@ -1,5 +1,15 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:math'; 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:flutter/material.dart';
import 'package:frontend/Screens/policy/policyCriteria.dart'; import 'package:frontend/Screens/policy/policyCriteria.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@ -9,12 +19,18 @@ import 'package:responsive_builder/responsive_builder.dart';
import '../../config/apiUrl.dart'; import '../../config/apiUrl.dart';
import '../../routes/custom_appBar.dart'; import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart'; import '../../routes/custom_drawer.dart';
import '../../services/apiService.dart';
import '../../utils/auth_utils.dart'; import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_user_form.dart'; import '../../widgets/custom_user_form.dart';
class Policy extends StatefulWidget { class Policy extends StatefulWidget {
const Policy({super.key}); final Map<String, dynamic>? policy;
const Policy({super.key, required this.policy});
static Policy fromState(GoRouterState state) {
return Policy(policy: state.extra as Map<String, dynamic>?);
}
@override @override
_PolicyState createState() => _PolicyState(); _PolicyState createState() => _PolicyState();
@ -24,17 +40,23 @@ class _PolicyState extends State<Policy> {
final GlobalKey<PolicyCriteriaState> policyCriteriaKey = final GlobalKey<PolicyCriteriaState> policyCriteriaKey =
GlobalKey<PolicyCriteriaState>(); GlobalKey<PolicyCriteriaState>();
final ApiService apiService = ApiService();
Color? layoutColor; Color? layoutColor;
Color? bodyColor; Color? bodyColor;
late String policyType = "domestic"; late String policyType = "domestic";
// int? selectedServiceIndex = 1; // int? selectedServiceIndex = 1;
ValueNotifier<String> selectedServiceIndex = ValueNotifier("1"); ValueNotifier<String> selectedServiceIndex = ValueNotifier("1");
late String selectedService = "Train"; String selectedService = "train";
// ValueNotifier<String> selectedService = ValueNotifier("Train"); // ValueNotifier<String> selectedService = ValueNotifier("Train");
bool isViewMode = false; bool isViewMode = false;
Map<String, String> errorMessages = {};
String? selectedPolicyId;
String? _selectedTripType; String? _selectedTripType;
String? PolicyName; String? PolicyName;
String? SelectedDomestic = "1"; String? SelectedDomestic = "0";
String? SelectedInternational = "0"; String? SelectedInternational = "0";
String? orgId; String? orgId;
String? userId; String? userId;
@ -42,6 +64,11 @@ class _PolicyState extends State<Policy> {
bool showClass = true; bool showClass = true;
bool showCost = true; bool showCost = true;
List<dynamic>? selectedAllServices;
List<Map<String, String>> selectedOrgServiceIds = [];
List<dynamic>? ServicesChoosed;
List<String> filledItineraryKeys = [];
TextEditingController _policyController = TextEditingController(); TextEditingController _policyController = TextEditingController();
List<Map<String, dynamic>>? policy_details = []; List<Map<String, dynamic>>? policy_details = [];
@ -79,13 +106,20 @@ class _PolicyState extends State<Policy> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsFlutterBinding.ensureInitialized();
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
loadinitializeData(); loadinitializeData();
loadInitialData(); loadInitialData();
});
// updateData(); if (widget.policy != null) {
final details =
List<Map<String, dynamic>>.from(widget.policy!['policy_details']);
policyCriteriaKey.currentState?.loadPolicyDetails(details);
}
});
updateSelectedServices();
updateData();
} }
void loadInitialData() async { void loadInitialData() async {
@ -108,7 +142,121 @@ class _PolicyState extends State<Policy> {
userId = await getUserId(); userId = await getUserId();
} }
// void updateData(){} Future<void> loadAllServices() async {
try {
final result = await apiService.fetchAllServices();
setState(() {
selectedAllServices = result;
});
print("Fetched services: $selectedAllServices");
} catch (e) {
print('Error fetching role list: $e');
}
}
Future<void> loadOrgSelectedAlServices() async {
try {
final result = await apiService.fetchOrganization();
if (result != null && result is Map<String, dynamic>) {
final rawServices = result['services_ids'];
if (rawServices != null && rawServices is String) {
try {
List<dynamic> decoded = json.decode(rawServices);
List<Map<String, String>> 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<void> updateSelectedServices() async {
await loadAllServices();
await loadOrgSelectedAlServices();
final selectedIds =
selectedOrgServiceIds.map((e) => e['service_id']).toSet();
if (widget.policy != null) {
final details =
List<Map<String, dynamic>>.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<Map<String, dynamic>>.from(
widget.policy?["policy_details"] ?? [],
);
});
}
}
void handleSubmit() async { void handleSubmit() async {
print("USR Detail Submit - $policyData"); print("USR Detail Submit - $policyData");
@ -120,19 +268,69 @@ class _PolicyState extends State<Policy> {
Map<String, dynamic> data = policyData; Map<String, dynamic> data = policyData;
createPolicyData(data); if (!isValidData(data)) {
print("USERDETAILS : $policyData");
// if (!isValidData(data)) { print("Validation Failed: Required fields are missing.");
setState(() {});
return; // Stop execution if validation fails
} else {
// print("USERDETAILS : $policyData"); // print("USERDETAILS : $policyData");
// print("Validation Failed: Required fields are missing."); // orgId = await getOrgId();
// setState(() {});
// return; // Stop execution if validation fails createPolicyData(data);
// } else { }
// // print("USERDETAILS : $policyData"); }
// // orgId = await getOrgId();
// bool isValidData(Map<String, dynamic> data) {
// createPolicyData(policyData); 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<Map<String, dynamic>>;
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<void> createPolicyData(Map<String, dynamic> policyData) async { Future<void> createPolicyData(Map<String, dynamic> policyData) async {
@ -143,9 +341,12 @@ class _PolicyState extends State<Policy> {
throw Exception('Token not found. Please log in.'); throw Exception('Token not found. Please log in.');
} }
// if (selectedPlanId != null && selectedPlanId!.isNotEmpty) { final int? policyId;
// planData['plan_id'] = selectedPlanId; // Add plan_id for update
// } if (selectedPolicyId != null && selectedPolicyId!.isNotEmpty) {
policyId = int.tryParse(selectedPolicyId!);
policyData['policy_id'] = policyId; // Add only if updating
}
try { try {
final response = await http.post( final response = await http.post(
@ -186,28 +387,17 @@ class _PolicyState extends State<Policy> {
child: Row( child: Row(
children: [ children: [
if (isDesktop) CustomDrawer(isDesktop: true), if (isDesktop) CustomDrawer(isDesktop: true),
Expanded(
child: Container( Expanded(child: buildData(isDesktop, context)),
color: bodyColor, // Expanded(
child: buildPolicyLayout(isDesktop), // 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,10 +436,41 @@ class _PolicyState extends State<Policy> {
}); });
} }
Widget buildPolicyLayout(bool isDesktop) { Widget buildData(bool isDesktop, context) {
return SingleChildScrollView( return Container(
scrollDirection: Axis.vertical, // 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: [
Expanded(
child: Container( 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),
),
),
],
),
);
}
Widget buildPolicyLayout(bool isDesktop) {
return Container(
margin: isDesktop margin: isDesktop
? EdgeInsets.all(10.0) ? EdgeInsets.all(10.0)
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
@ -325,7 +546,9 @@ class _PolicyState extends State<Policy> {
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
controller: _policyController, controller: _policyController,
// enabled: !isViewMode, // enabled: !isViewMode,
onChanged: (value) {}, onChanged: (value) {
_clearError("name");
},
decoration: InputDecoration( decoration: InputDecoration(
labelText: "Policy Name", labelText: "Policy Name",
labelStyle: TextStyle( labelStyle: TextStyle(
@ -339,6 +562,13 @@ class _PolicyState extends State<Policy> {
), ),
), ),
), ),
if (errorMessages["name"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["name"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
], ],
), ),
], ],
@ -360,7 +590,14 @@ class _PolicyState extends State<Policy> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _buildTripType(isDesktop), 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),
),
],
], ],
) )
], ],
@ -374,6 +611,13 @@ class _PolicyState extends State<Policy> {
thickness: 0.2, thickness: 0.2,
color: Colors.grey, 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 isDesktop
? Expanded( ? Expanded(
child: Row( child: Row(
@ -393,7 +637,6 @@ class _PolicyState extends State<Policy> {
), ),
], ],
), ),
),
); );
} }
@ -420,17 +663,24 @@ class _PolicyState extends State<Policy> {
} }
Widget _buildPolicySubCategoryList(bool isDesktop) { Widget _buildPolicySubCategoryList(bool isDesktop) {
List<String> services = [ // List<String> services = [
"Flight", // "Flight",
"Train", // "Train",
"Bus", // "Bus",
"Taxi", // "Taxi",
"Forex", // "Forex",
"Accommodation", // "Accommodation",
"Insurance", // "Insurance",
"Visa", // "Visa",
"Miscellaneous" // "Miscellaneous"
]; // ];
if (ServicesChoosed == null) {
return const Center(child: CircularProgressIndicator());
}
List<String> services =
ServicesChoosed!.map((service) => service['name'].toString()).toList();
return Expanded( return Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
@ -476,10 +726,21 @@ class _PolicyState extends State<Policy> {
: EdgeInsets.only(top: 3, bottom: 3, left: 8, right: 8), : EdgeInsets.only(top: 3, bottom: 3, left: 8, right: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
// color: Colors.blue, // color: Colors.blue,
color: color: isSelected ? Color(0xFF114D8B) : Colors.white,
isSelected ? Color(0xFF114D8B) : Colors.grey.shade100,
// : Color(0xFFEBEBF7), // : Color(0xFFEBEBF7),
borderRadius: BorderRadius.circular(8), 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, alignment: Alignment.center,
child: Text( child: Text(
@ -518,6 +779,7 @@ class _PolicyState extends State<Policy> {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() { setState(() {
policy_details = policyData; policy_details = policyData;
_clearError("policy_details");
}); });
}); });
}, },
@ -566,6 +828,7 @@ class _PolicyState extends State<Policy> {
GestureDetector( GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() {
_clearError("trip_type");
_selectedTripType = "1"; _selectedTripType = "1";
PolicyName = "Domestic Policy"; PolicyName = "Domestic Policy";
SelectedDomestic = "1"; SelectedDomestic = "1";
@ -620,6 +883,7 @@ class _PolicyState extends State<Policy> {
GestureDetector( GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() {
_clearError("trip_type");
_selectedTripType = "2"; _selectedTripType = "2";
PolicyName = "International Policy"; PolicyName = "International Policy";
SelectedDomestic = "0"; SelectedDomestic = "0";

View File

@ -81,6 +81,27 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
} }
} }
void loadPolicyDetails(List<Map<String, dynamic>> 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) { void addOrUpdatePolicy(String serviceId) {
Map<String, dynamic> data = { Map<String, dynamic> data = {
"service_id": serviceId, "service_id": serviceId,
@ -161,7 +182,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
widget.isDesktop ? SizedBox(height: 10) : SizedBox(height: 5), // widget.isDesktop ? SizedBox(height: 10) : SizedBox(height: 5),
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
@ -175,7 +196,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
], ],
), ),
if (widget.isClass!) if (widget.isClass!)
widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5), widget.isDesktop ? SizedBox(height: 5) : SizedBox(height: 5),
Padding( Padding(
padding: const EdgeInsets.only(right: 18.0), padding: const EdgeInsets.only(right: 18.0),
child: Row( child: Row(
@ -231,7 +252,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
// color: Color(0xFFEBEBF7), // color: Color(0xFFEBEBF7),
// ), // ),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
color: Colors.brown.shade200, // color: Colors.brown.shade200,
), ),
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
@ -241,7 +262,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
children: [ children: [
Expanded( Expanded(
child: Container( child: Container(
color: Colors.grey.shade100, // color: Colors.grey.shade100,
width: widget.isDesktop width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.63 ? MediaQuery.of(context).size.width * 0.63
: 600, : 600,
@ -250,10 +271,10 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
Container( Container(
margin: const EdgeInsets.only(right: 0), margin: const EdgeInsets.only(right: 0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey.shade100, // color: Colors.grey.shade100,
border: Border.all( // border: Border.all(
color: Colors.grey.shade100, // color: Colors.grey.shade100,
), // ),
), ),
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 10, bottom: 10, left: 35, right: 35), top: 10, bottom: 10, left: 35, right: 35),
@ -262,20 +283,26 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
? MainAxisAlignment.spaceAround ? MainAxisAlignment.spaceAround
: MainAxisAlignment.spaceBetween, : MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Expanded(
flex: 2,
child: Text(
"Approver Name", "Approver Name",
style: TextStyle( style: TextStyle(
color: Color(0xFF9E9DBD), color: Color(0xFF9E9DBD),
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 12), fontSize: 12),
), ),
Text( ),
Expanded(
flex: 2,
child: Text(
"Notification", "Notification",
style: TextStyle( style: TextStyle(
color: Color(0xFF9E9DBD), color: Color(0xFF9E9DBD),
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 12), fontSize: 12),
), ),
),
Text( Text(
"Select", "Select",
style: TextStyle( style: TextStyle(
@ -286,22 +313,28 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
], ],
), ),
), ),
Divider(
thickness: 0.2,
color: Colors.grey,
),
SizedBox( SizedBox(
height: 200, height: 180,
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
child: Container( child: Container(
// color: Colors.grey, // color: Colors.grey,
margin: const EdgeInsets.only(right: 20), margin: const EdgeInsets.only(
color: Colors.grey.shade100, right: 20, left: 20),
// color: Colors.grey.shade100,
child: Column(children: [ child: Column(children: [
Container( Container(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.spaceAround, MainAxisAlignment.end,
children: [ children: [
Text("Approver 1"), Text("Approver 1"),
Spacer(),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
isFocused: false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
@ -318,6 +351,23 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
.loose, // Allows flexible height .loose, // Allows flexible height
constraints: BoxConstraints( constraints: BoxConstraints(
maxHeight: 250), 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: [ items: [
"Approval", "Approval",
@ -366,6 +416,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
), ),
), ),
), ),
Spacer(),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() {
@ -408,9 +459,10 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.spaceAround, MainAxisAlignment.end,
children: [ children: [
Text("Approver 2"), Text("Approver 2"),
Spacer(),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
isFocused: false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
@ -427,6 +479,23 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
.loose, // Allows flexible height .loose, // Allows flexible height
constraints: BoxConstraints( constraints: BoxConstraints(
maxHeight: 250), 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: [ items: [
"Approval", "Approval",
@ -475,6 +544,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
), ),
), ),
), ),
Spacer(),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() {
@ -522,9 +592,10 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.spaceAround, MainAxisAlignment.end,
children: [ children: [
Text("Approver 3"), Text("Approver 3"),
Spacer(),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
isFocused: false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
@ -541,6 +612,23 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
.loose, // Allows flexible height .loose, // Allows flexible height
constraints: BoxConstraints( constraints: BoxConstraints(
maxHeight: 250), 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: [ items: [
"Approval", "Approval",
@ -589,6 +677,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
), ),
), ),
), ),
Spacer(),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() {

View File

@ -1,8 +1,12 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/Screens/group/group.dart'; import 'package:frontend/Screens/group/group.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import '../../config/apiUrl.dart';
import '../../routes/custom_appBar.dart'; import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart'; import '../../routes/custom_drawer.dart';
import '../../services/apiService.dart'; import '../../services/apiService.dart';
@ -57,10 +61,67 @@ class _PolicyListState extends State<PolicyList> {
} }
} }
void deleteGroup(int groupId) { void handleActiveStatus(
setState(() { Map<String, dynamic> policyData,
apiAllGroups?.removeWhere((group) => group['group_id'] == groupId); 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<String, dynamic> 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<void> deleteGroupFromApi(int groupId) async { // Future<void> deleteGroupFromApi(int groupId) async {
@ -189,10 +250,11 @@ class _PolicyListState extends State<PolicyList> {
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
itemCount: apiAllGroups!.length, itemCount: apiAllGroups!.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final group = apiAllGroups![index]; final policy = apiAllGroups![index];
return Card( return Card(
// color: bodyColor, // color: bodyColor,
color: Color(0xFFF5F5F5), // color: Color(0xFFF5F5F5),
color: Colors.white,
margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10), margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10),
child: Padding( child: Padding(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
@ -203,25 +265,53 @@ class _PolicyListState extends State<PolicyList> {
children: [ children: [
Expanded( Expanded(
flex: 2, flex: 2,
child: Text("Group Name: ${group['name']}", child: Text("Policy Name: ${policy['name']}",
style: TextStyle( style: TextStyle(
fontSize: 13, fontWeight: FontWeight.bold)), fontSize: 13, fontWeight: FontWeight.bold)),
), ),
Expanded(flex: 1, child: Text(" ${group['created_on']}")), Expanded(flex: 1, child: Text(" ${policy['created_on']}")),
Expanded(flex: 1, child: Text("${group['created_by']}")), Expanded(flex: 1, child: Text("${policy['created_by']}")),
], ],
), ),
SizedBox(height: 4), SizedBox(height: 4),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
TextButton( GestureDetector(
onPressed: () { onTap: () async {
context.go("/CreateGroup", extra: group); final rawId = policy['policy_id'];
final intPolicyId = rawId is int
? rawId
: int.tryParse(rawId.toString()) ?? 0;
print("Edit ${group['group_id']} $group"); Map<String, dynamic> 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),
), ),
], ],
), ),

File diff suppressed because it is too large Load Diff

View File

@ -69,6 +69,7 @@ class _CustomDrawerState extends State<CustomDrawer> {
"user_id": userDetails["user_id"].toString(), "user_id": userDetails["user_id"].toString(),
"name": "${userDetails["first_name"]} ${userDetails["last_name"]}", "name": "${userDetails["first_name"]} ${userDetails["last_name"]}",
"email": userDetails["email"] ?? "", "email": userDetails["email"] ?? "",
"role": userDetails["role"] ?? "",
}; };
} catch (e) { } catch (e) {
print("Error decoding user data: $e"); print("Error decoding user data: $e");
@ -99,11 +100,19 @@ class _CustomDrawerState extends State<CustomDrawer> {
selectedOrg!['color'].toString().replaceFirst('0x', ''), selectedOrg!['color'].toString().replaceFirst('0x', ''),
radix: 16)) radix: 16))
: Colors.blue; : 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 // Save to SharedPreferences
await prefs.setString('layout_color', selectedOrg?['layout_color']); await prefs.setString('layout_color', selectedOrg?['layout_color']);
await prefs.setString('body_color', selectedOrg?['color']); await prefs.setString('body_color', selectedOrg?['color']);
await prefs.setString('body_color', selectedOrg?['plan_action']);
print( print(
"Layout Color- ${selectedOrg?['layout_color']} - $layoutColor ---------- bodyColor - $bodyColor"); "Layout Color- ${selectedOrg?['layout_color']} - $layoutColor ---------- bodyColor - $bodyColor");
@ -128,11 +137,26 @@ class _CustomDrawerState extends State<CustomDrawer> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Image.asset( selectedOrg?['logo'] != null
'assets/images/login/travelSpend_Logo.png', ? ClipOval(
width: 160, child: Image.network(
height: 40, selectedOrg!['logo'],
fit: BoxFit.contain, 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,7 +181,9 @@ class _CustomDrawerState extends State<CustomDrawer> {
), ),
], ],
), ),
Row( MouseRegion(
cursor: SystemMouseCursors.click,
child: Row(
children: [ children: [
GestureDetector( GestureDetector(
onTap: () { onTap: () {
@ -185,6 +211,7 @@ class _CustomDrawerState extends State<CustomDrawer> {
), ),
], ],
), ),
),
], ],
), ),
@ -247,47 +274,38 @@ class _CustomDrawerState extends State<CustomDrawer> {
SizedBox( SizedBox(
height: 15, 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'), _buildDrawerItem(context, Icons.home_outlined, 'Home', '/home'),
_buildExpandableItem( _buildDrawerItem(context, Icons.request_page_outlined,
context, 'My Travel Request', '/listPlan'),
Icons.assessment_outlined, _buildDrawerItem(context, Icons.assessment_outlined, 'My Approvals',
'Plans', '/ApprovalList'),
[ if (userData?["role"] != "User")
_buildSubDrawerItem( _buildDrawerItem(context, Icons.account_circle_outlined,
context, 'My Travel Request', '/listPlan'), 'User List', '/listUser'),
_buildSubDrawerItem(context, 'My Approvals', '/ApprovalList') // _buildExpandableItem(
], // context,
'/listPlan'), // Icons.assessment_outlined,
_buildExpandableItem( // 'Plans',
context, // [
Icons.account_circle_outlined, // _buildSubDrawerItem(
'User ', // context, 'My Travel Request', '/listPlan'),
[ // _buildSubDrawerItem(context, 'My Approvals', '/ApprovalList')
_buildSubDrawerItem(context, 'User List', '/listUser'), // ],
// _buildSubDrawerItem(context,'PlanB','/PlanB') // '/listPlan'),
], //
'/listUser'), // _buildExpandableItem(
// context,
// Icons.account_circle_outlined,
// 'User ',
// [
// _buildSubDrawerItem(context, 'User List', '/listUser'),
// // _buildSubDrawerItem(context,'PlanB','/PlanB')
// ],
// '/listUser'),
if (userData?["role"] != "User")
_buildExpandableItem( _buildExpandableItem(
context, context,
Icons.settings_outlined, Icons.settings_outlined,
@ -400,7 +418,7 @@ class _CustomDrawerState extends State<CustomDrawer> {
title: Text( title: Text(
title, title,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF475569), color: Color(0xFF475569),
fontFamily: "Archivo"), fontFamily: "Archivo"),
@ -450,7 +468,7 @@ class _CustomDrawerState extends State<CustomDrawer> {
Text( Text(
title, title,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF475569), color: Color(0xFF475569),
fontFamily: "Archivo", fontFamily: "Archivo",

View File

@ -63,7 +63,10 @@ final GoRouter router = GoRouter(
), ),
GoRoute( GoRoute(
path: '/Policy', path: '/Policy',
builder: (context, state) => Policy(), // builder: (context, state) => Policy(),
pageBuilder: (context, state) => MaterialPage(
child: Policy.fromState(state),
),
), ),
GoRoute( GoRoute(
path: '/PolicyList', path: '/PolicyList',

View File

@ -265,6 +265,44 @@ class ApiService {
} }
} }
Future<Map<String, dynamic>> 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<String, dynamic> 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<Map<String, dynamic>> fetchOrganization() async { Future<Map<String, dynamic>> fetchOrganization() async {
String? orgId = await getOrgId(); String? orgId = await getOrgId();

View File

@ -47,6 +47,21 @@ Future<String?> getOrgId() async {
return null; return null;
} }
Future<String?> getTripPlanAction() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if (userDataString != null) {
try {
final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["plan_action"]?.toString();
} catch (e) {
return null;
}
}
return null;
}
Future<List<Map<String, dynamic>>?> getUserServices() async { Future<List<Map<String, dynamic>>?> getUserServices() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data'); final String? userDataString = prefs.getString('user_data');

View File

@ -47,7 +47,6 @@ dependencies:
image_picker: ^1.1.2 image_picker: ^1.1.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter