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,
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<ApprovalList> {
// 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(),

View File

@ -396,6 +396,9 @@ class _groupState extends State<Group> {
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<Group> {
height: 40,
child: TextField(
controller: controllers["description"],
onChanged: (value) {
_clearError("description");
},
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "description",

View File

@ -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<GroupList> {
}
}
void deleteGroup(int groupId) {
setState(() {
apiAllGroups?.removeWhere((group) => group['group_id'] == groupId);
});
void handleActiveStatus(
Map<String, dynamic> 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<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 {
@ -192,7 +245,8 @@ class _GroupListState extends State<GroupList> {
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<GroupList> {
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),
),
],
),

View File

@ -609,12 +609,15 @@ class _MailSettingState extends State<MailSetting> {
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),
))
],
),
];

View File

@ -344,6 +344,41 @@ class _OrgSetUpState extends State<OrgSetUp> {
}
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 {
final picker = ImagePicker();
final XFile? pickedFile =
@ -365,317 +400,297 @@ class _OrgSetUpState extends State<OrgSetUp> {
}
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),
))
],
],
),
),
);
}

View File

@ -273,6 +273,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
List<dynamic>? apiCountryData;
List<dynamic>? 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<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() {
if (!mounted) return;
@ -492,7 +513,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
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<CreateNewPlan> {
orgId = await getOrgId();
print("userDetails - $selfId");
getSelectedPlanFor();
setTripPlanAction();
}
Future<String?> getToken() async {
@ -692,7 +715,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
}
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<CreateNewPlan> {
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<CreateNewPlan> {
List<Widget> _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<String>(
// 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<String>(
// activeColor: Colors.blueAccent,
// contentPadding: EdgeInsets.zero,
// dense: true,
// title: Text("International"),
// value: "2",
// groupValue: _selectedTripType,
// onChanged: widget.isViewMode
// ? null
// : (value) {
// setState(() {
// _selectedTripType = value!;
// });
// },
// ),
),
// Radio<String>(
// 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<String>(
// activeColor: Colors.blueAccent,
// contentPadding: EdgeInsets.zero,
// dense: true,
// title: Text("International"),
// value: "2",
// groupValue: _selectedTripType,
// onChanged: widget.isViewMode
// ? null
// : (value) {
// setState(() {
// _selectedTripType = value!;
// });
// },
// ),
),
];
}

View File

@ -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<DynamicItinerary> {
Map<String, dynamic>? selectedItem;
int? selectedIndex;
List<dynamic>? apiAllServices;
List<dynamic>? selectedAllServices;
List<Map<String, String>> selectedOrgServiceIds = [];
List<dynamic>? ServicesChoosed;
List<String> filledItineraryKeys = [];
// List<Map<String, dynamic>> miscellaneousList = [];
@ -87,21 +92,98 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
void initState() {
super.initState();
handleSelectedPlan();
loadAllServices();
updateSelectedServices();
}
Future<void> 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<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() {
// Check if selectedPlanData has itinerary data
if (hasAnyItineraryData()) {
@ -147,13 +229,30 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
"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<DynamicItinerary> {
}
List<Widget> _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(

View File

@ -24,6 +24,7 @@ class _ListPlansState extends State<ListPlans> {
String? userId;
String? orgId;
String? token;
String? TripPlanAction;
Color? layoutColor;
Color? bodyColor;
@ -60,6 +61,7 @@ class _ListPlansState extends State<ListPlans> {
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<ListPlans> {
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<ListPlans> {
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<ListPlans> {
// 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(),

View File

@ -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<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
_PolicyState createState() => _PolicyState();
@ -24,17 +40,23 @@ class _PolicyState extends State<Policy> {
final GlobalKey<PolicyCriteriaState> policyCriteriaKey =
GlobalKey<PolicyCriteriaState>();
final ApiService apiService = ApiService();
Color? layoutColor;
Color? bodyColor;
late String policyType = "domestic";
// int? selectedServiceIndex = 1;
ValueNotifier<String> selectedServiceIndex = ValueNotifier("1");
late String selectedService = "Train";
String selectedService = "train";
// ValueNotifier<String> selectedService = ValueNotifier("Train");
bool isViewMode = false;
Map<String, String> 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<Policy> {
bool showClass = true;
bool showCost = true;
List<dynamic>? selectedAllServices;
List<Map<String, String>> selectedOrgServiceIds = [];
List<dynamic>? ServicesChoosed;
List<String> filledItineraryKeys = [];
TextEditingController _policyController = TextEditingController();
List<Map<String, dynamic>>? policy_details = [];
@ -79,13 +106,20 @@ class _PolicyState extends State<Policy> {
@override
void initState() {
super.initState();
WidgetsFlutterBinding.ensureInitialized();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadinitializeData();
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 {
@ -108,7 +142,121 @@ class _PolicyState extends State<Policy> {
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 {
print("USR Detail Submit - $policyData");
@ -120,19 +268,69 @@ class _PolicyState extends State<Policy> {
Map<String, dynamic> 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<String, dynamic> 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<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 {
@ -143,9 +341,12 @@ class _PolicyState extends State<Policy> {
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<Policy> {
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<Policy> {
});
}
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<Policy> {
}
Widget _buildPolicySubCategoryList(bool isDesktop) {
List<String> services = [
"Flight",
"Train",
"Bus",
"Taxi",
"Forex",
"Accommodation",
"Insurance",
"Visa",
"Miscellaneous"
];
// List<String> services = [
// "Flight",
// "Train",
// "Bus",
// "Taxi",
// "Forex",
// "Accommodation",
// "Insurance",
// "Visa",
// "Miscellaneous"
// ];
if (ServicesChoosed == null) {
return const Center(child: CircularProgressIndicator());
}
List<String> services =
ServicesChoosed!.map((service) => service['name'].toString()).toList();
return Expanded(
child: SingleChildScrollView(
@ -476,10 +726,21 @@ class _PolicyState extends State<Policy> {
: 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<Policy> {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
policy_details = policyData;
_clearError("policy_details");
});
});
},
@ -566,6 +828,7 @@ class _PolicyState extends State<Policy> {
GestureDetector(
onTap: () {
setState(() {
_clearError("trip_type");
_selectedTripType = "1";
PolicyName = "Domestic Policy";
SelectedDomestic = "1";
@ -620,6 +883,7 @@ class _PolicyState extends State<Policy> {
GestureDetector(
onTap: () {
setState(() {
_clearError("trip_type");
_selectedTripType = "2";
PolicyName = "International Policy";
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) {
Map<String, dynamic> data = {
"service_id": serviceId,
@ -161,7 +182,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
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<PolicyCriteria> {
],
),
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<PolicyCriteria> {
// 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<PolicyCriteria> {
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<PolicyCriteria> {
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<PolicyCriteria> {
? 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<PolicyCriteria> {
],
),
),
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<PolicyCriteria> {
.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<PolicyCriteria> {
),
),
),
Spacer(),
GestureDetector(
onTap: () {
setState(() {
@ -408,9 +459,10 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
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<PolicyCriteria> {
.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<PolicyCriteria> {
),
),
),
Spacer(),
GestureDetector(
onTap: () {
setState(() {
@ -522,9 +592,10 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
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<PolicyCriteria> {
.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<PolicyCriteria> {
),
),
),
Spacer(),
GestureDetector(
onTap: () {
setState(() {

View File

@ -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<PolicyList> {
}
}
void deleteGroup(int groupId) {
setState(() {
apiAllGroups?.removeWhere((group) => group['group_id'] == groupId);
});
void handleActiveStatus(
Map<String, dynamic> 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<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 {
@ -189,10 +250,11 @@ class _PolicyListState extends State<PolicyList> {
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<PolicyList> {
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<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(),
"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<CustomDrawer> {
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<CustomDrawer> {
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<CustomDrawer> {
),
],
),
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<CustomDrawer> {
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<CustomDrawer> {
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<CustomDrawer> {
Text(
title,
style: TextStyle(
fontSize: 14,
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF475569),
fontFamily: "Archivo",

View File

@ -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',

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 {
String? orgId = await getOrgId();

View File

@ -47,6 +47,21 @@ Future<String?> getOrgId() async {
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 {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');

View File

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