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
decoration: BoxDecoration( .infinity, // Set your desired fixed size (equal width and height)
color: plan.status == "Active" height: 25,
? layoutColor alignment: Alignment.center,
: Colors.grey.shade50, decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), color: plan.statusValue ==
), "Partially Approved"
child: Text( ? Colors.yellow.shade100
plan.statusValue, : plan.statusValue == "Approved"
style: TextStyle( ? Colors.green.shade100
color: plan.status == "Active" : plan.statusValue == "Completed"
? Colors.white ? Colors.green.shade500
: Colors.grey, : plan.statusValue == "Rejected"
fontSize: 13, ? Colors.red.shade100
fontWeight: FontWeight.bold, : 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: [ 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,317 +400,297 @@ 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(
// color: Colors.amber, border: isDesktop
color: bodyColor, ? Border.all(
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), width: 2,
child: Column( color: Color(0xFFF7F7FB),
mainAxisAlignment: MainAxisAlignment.spaceBetween, )
children: [ : null,
Expanded( color: Colors.white,
child: Container( // color: Color(0xFFF7F7FB),
// color: bodyColor,
// color: Colors.grey, // color: Colors.amber,
width: double.infinity, ),
// height: MediaQuery.of(context).size.height, child: SingleChildScrollView(
padding: const EdgeInsets.all(8), 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( child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SingleChildScrollView( Container(
scrollDirection: Axis.vertical, color: Colors.white,
child: Container( child: Row(
padding: const EdgeInsets.all(20), mainAxisAlignment: MainAxisAlignment.center,
// height: MediaQuery.of(context).size.height * 0.8, children: [
color: Colors.white, Text(
child: Column( selectedOrg != null && selectedOrg!.isNotEmpty
mainAxisAlignment: MainAxisAlignment.start, ? "Update Organization"
crossAxisAlignment: CrossAxisAlignment.start, : "Create Organization",
children: [ style: TextStyle(
Container( fontSize: 15, fontWeight: FontWeight.w800),
color: Colors.white, ),
child: Row( ],
mainAxisAlignment: MainAxisAlignment.center, ),
children: [ ),
Text( Container(
"Create Organization", color: Colors.white,
style: TextStyle( child: Row(
fontSize: 16, mainAxisAlignment: MainAxisAlignment.spaceBetween,
fontWeight: FontWeight.w600), crossAxisAlignment: CrossAxisAlignment.start,
), children: [
], const Padding(
), padding: EdgeInsets.only(top: 1.0),
), child: Text(
Container( "Name:",
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",
style: TextStyle( style: TextStyle(
fontFamily: "Archivo", fontFamily: "Archivo",
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF212121)), color: Color(0xFF212121)),
), ),
SizedBox( ),
height: 5, SizedBox(width: 8),
), Expanded(
Container( child: TextFormField(
decoration: BoxDecoration( controller: _orgNameController,
border: Border.all(color: Color(0xFFF4F4FB)), style: TextStyle(
borderRadius: BorderRadius.circular(1), fontSize: 16,
// color: bodyColor, color: Color(0xFF114D8B),
color: Color(0xFFF5F5F5),
), ),
padding: EdgeInsets.only( decoration: InputDecoration(
left: 5, right: 5, top: 15, bottom: 15), hintText: "Enter Organization Name",
child: isDesktop hintStyle:
? Row( TextStyle(fontSize: 14, color: Colors.grey),
mainAxisAlignment: floatingLabelBehavior:
MainAxisAlignment.spaceEvenly, FloatingLabelBehavior.never,
// mainAxisSize: MainAxisSize.min, border: InputBorder.none,
children: _buildOptions(), isDense: true,
) // contentPadding:
: Expanded( // EdgeInsets.symmetric(vertical: 14),
child: SingleChildScrollView( ),
scrollDirection: Axis.horizontal, // textAlignVertical: TextAlignVertical.center,
child: Row( ),
children: _buildOptions(), ),
), Spacer(),
), GestureDetector(
onTap: _pickImage,
child: _imageBytes != null
? ClipOval(
child: Image.memory(
_imageBytes!,
width: 50,
height: 50,
fit: BoxFit.cover,
), ),
), )
SizedBox( : selectedOrg?['logo'] != null
height: 5, ? ClipOval(
), child: Image.network(
selectedOrg!['logo'],
Column( width: 50,
crossAxisAlignment: CrossAxisAlignment.start, 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: [ children: [
Text( Text(
"Choose Theme", "Mail Settings",
style: TextStyle( style: TextStyle(
fontFamily: "Archivo", fontFamily: "Archivo",
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF212121)), color: Color(0xFF212121)),
), ),
Container(
decoration: BoxDecoration( // GestureDetector(
// border: Border.all(color: Color(0xFFF4F4FB)), // onTap: () {
borderRadius: BorderRadius.circular(1), // setState(() {
// color: Color(0xFFF4F4FB), // showMail = !showMail;
), // });
padding: EdgeInsets.only( // },
left: 5, right: 5, top: 15, bottom: 5), // child: Icon(
child: layoutColor != null && bodyColor != null // Icons.keyboard_arrow_down_outlined,
? ColorThemePickerWidget( // color: Color(0xFF114D8B),
initialLayoutColor: layoutColor, // size: 30,
initialBodyColor: bodyColor, // ),
onLayoutColorSelected: // ),
(Color selectedLayoutColor) {
setState(() {
layoutColor = selectedLayoutColor;
});
},
onBodyColorSelected:
(Color selectedBodyColor) {
setState(() {
bodyColor = selectedBodyColor;
});
},
)
: CircularProgressIndicator(),
),
], ],
), ),
// if (showMail)
// isDesktop SizedBox(
// ? Row( height: 3,
// mainAxisAlignment: MainAxisAlignment.end, ),
// children: _buildSubmit(isDesktop), Container(
// ) // width: double.infinity,
// : Row( decoration: BoxDecoration(
// mainAxisAlignment: MainAxisAlignment.center, border: Border.all(
// children: _buildSubmit(isDesktop), 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>? 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,7 +715,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
} }
final requiredFields = { final requiredFields = {
"trip_type": _selectedTripType, if (TripPlanAction != "Plan Creation Not Allowed") //
"trip_type": _selectedTripType,
"cost_center_id": selectedCostCenterId, "cost_center_id": selectedCostCenterId,
"functional_department": selectedFuncDept, "functional_department": selectedFuncDept,
"purpose_of_travel": selectedPurpose, "purpose_of_travel": selectedPurpose,
@ -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,142 +1633,148 @@ class CreateNewPlansState extends State<CreateNewPlan> {
List<Widget> _buildTripType(bool isMobile) { List<Widget> _buildTripType(bool isMobile) {
return [ return [
CustomTextFieldWrapper( if (showDomestic == true)
color: Color(0xFFF4F4FB), CustomTextFieldWrapper(
layoutColor: widget.layoutColor, color: Color(0xFFF4F4FB),
borderRadius: BorderRadius.circular(25), layoutColor: widget.layoutColor,
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), borderRadius: BorderRadius.circular(25),
width: 130, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
isFocused: _selectedTripType == "1", width: 130,
isDesktop: widget.isDesktop, isFocused: _selectedTripType == "1",
child: Row( isDesktop: widget.isDesktop,
mainAxisSize: MainAxisSize.min, child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisSize: MainAxisSize.min,
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Text( children: [
"Domestic", Text(
style: TextStyle( "Domestic",
color: _selectedTripType == "1" ? Colors.white : Colors.black, style: TextStyle(
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(
color: color:
_selectedTripType == "1" ? Colors.white : Colors.black, _selectedTripType == "1" ? Colors.white : Colors.black,
width: _selectedTripType == "1" ? 2 : 1, fontWeight:
), _selectedTripType == "1" ? FontWeight.w600 : null,
), fontSize: 13),
child: _selectedTripType == "1"
? Icon(Icons.rectangle, size: 8, color: Colors.white)
: null, // Add checkmark if selected
), ),
)
],
),
),
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>( // Radio<String>(
// activeColor: Colors.blueAccent, // activeColor: Colors.blueAccent,
// contentPadding: EdgeInsets.zero, // // contentPadding: EdgeInsets.zero,
// dense: true, // visualDensity: VisualDensity.compact,
// title: Text("International"), // // dense: true,
// value: "2", // value: "1",
// groupValue: _selectedTripType, // groupValue: _selectedTripType,
// onChanged: widget.isViewMode // onChanged: widget.isViewMode
// ? null // ? null
// : (value) { // : (value) {
// setState(() { // setState(() {
// _selectedTripType = value!; // _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: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: () {
context.go('/createPlan', extra: { if (TripPlanAction == "Plan Creation Not Allowed") {
'orgId': orgId, showDialog(
}); context: context,
if (!isDesktop) Navigator.pop(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( 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
decoration: BoxDecoration( .infinity, // Set your desired fixed size (equal width and height)
color: plan.status == "Active" height: 25,
? layoutColor alignment: Alignment.center,
: Colors.grey.shade50, decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), color: plan.statusValue ==
), "Partially Approved"
child: Text( ? Colors.yellow.shade100
plan.statusValue, : plan.statusValue == "Approved"
style: TextStyle( ? Colors.green.shade100
color: plan.status == "Active" : plan.statusValue == "Completed"
? Colors.white ? Colors.green.shade500
: Colors.grey, : plan.statusValue == "Rejected"
fontSize: 13, ? Colors.red.shade100
fontWeight: FontWeight.bold, : 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: [ 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");
print("Validation Failed: Required fields are missing.");
setState(() {});
return; // Stop execution if validation fails
} else {
// print("USERDETAILS : $policyData");
// orgId = await getOrgId();
// if (!isValidData(data)) { createPolicyData(data);
// print("USERDETAILS : $policyData"); }
// print("Validation Failed: Required fields are missing."); }
// setState(() {});
// return; // Stop execution if validation fails bool isValidData(Map<String, dynamic> data) {
// } else { errorMessages.clear(); // Reset previous errors
// // print("USERDETAILS : $policyData");
// // orgId = await getOrgId(); // Validate required fields
// if (data["name"] == null || data["name"].toString().trim().isEmpty) {
// createPolicyData(policyData); 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,153 +436,206 @@ 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),
child: Container( decoration: BoxDecoration(
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, // color: Colors.amber,
), color: bodyColor,
child: Column( border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
mainAxisAlignment: MainAxisAlignment.start, child: Column(
children: [ children: [
Container( Expanded(
// color: Color(0xFFF7F7FB), child: Container(
child: Column( color: bodyColor,
mainAxisAlignment: MainAxisAlignment.start, child: buildPolicyLayout(isDesktop),
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( Container(
// color: Colors.amber, color: Colors.white,
// color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, padding: const EdgeInsets.all(8.0),
padding: isDesktop ? const EdgeInsets.only(left: 35) : null, child: isDesktop
child: Column( ? Row(
children: [ mainAxisAlignment: MainAxisAlignment.end,
Row( children: _buildSubmit(isDesktop),
children: [ )
Column( : Row(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: _buildSubmit(isDesktop),
Text("Policy Name", ),
style: TextStyle( ),
fontSize: 12, ],
fontWeight: FontWeight.w200, ),
color: Colors.black)), );
SizedBox(height: 5), }
CustomTextFieldUserWrapper(
isFocused: false, Widget buildPolicyLayout(bool isDesktop) {
isDesktop: isDesktop, return Container(
child: SizedBox( margin: isDesktop
height: 40, ? EdgeInsets.all(10.0)
child: TextField( : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
style: TextStyle(fontSize: 12), height: isDesktop
controller: _policyController, ? MediaQuery.of(context).size.height * 0.98
// enabled: !isViewMode, : MediaQuery.of(context).size.height,
onChanged: (value) {}, decoration: BoxDecoration(
decoration: InputDecoration( border: isDesktop
labelText: "Policy Name", ? Border.all(
labelStyle: TextStyle( width: 2,
fontSize: 12, color: Colors.grey), color: Color(0xFFF7F7FB),
floatingLabelBehavior: )
FloatingLabelBehavior.never, : null,
border: InputBorder.none, color: Colors.white,
contentPadding: // color: Color(0xFFF7F7FB),
EdgeInsets.symmetric(vertical: 16),
), // 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) { 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,11 +271,11 @@ 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),
child: Row( child: Row(
@ -262,19 +283,25 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
? MainAxisAlignment.spaceAround ? MainAxisAlignment.spaceAround
: MainAxisAlignment.spaceBetween, : MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Expanded(
"Approver Name", flex: 2,
style: TextStyle( child: Text(
color: Color(0xFF9E9DBD), "Approver Name",
fontWeight: FontWeight.bold, style: TextStyle(
fontSize: 12), color: Color(0xFF9E9DBD),
fontWeight: FontWeight.bold,
fontSize: 12),
),
), ),
Text( Expanded(
"Notification", flex: 2,
style: TextStyle( child: Text(
color: Color(0xFF9E9DBD), "Notification",
fontWeight: FontWeight.bold, style: TextStyle(
fontSize: 12), color: Color(0xFF9E9DBD),
fontWeight: FontWeight.bold,
fontSize: 12),
),
), ),
Text( Text(
"Select", "Select",
@ -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,12 +137,27 @@ 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,33 +181,36 @@ class _CustomDrawerState extends State<CustomDrawer> {
), ),
], ],
), ),
Row( MouseRegion(
children: [ cursor: SystemMouseCursors.click,
GestureDetector( child: Row(
onTap: () { children: [
print("ONTAP Custom"); GestureDetector(
print("ONTAP Custom- $userDetails "); onTap: () {
context.go( print("ONTAP Custom");
"/CreateUserDetails", print("ONTAP Custom- $userDetails ");
extra: { context.go(
"selectedUser": userDetails, "/CreateUserDetails",
"isEditProfile": true, extra: {
"isViewMode": true "selectedUser": userDetails,
}, "isEditProfile": true,
); "isViewMode": true
}, },
child: Text( );
userData?["name"] ?? "N/A", },
style: TextStyle( child: Text(
fontSize: 13, userData?["name"] ?? "N/A",
fontWeight: FontWeight.w600, style: TextStyle(
fontFamily: "Archivo", fontSize: 13,
// color: Color(0xFF12B24B), fontWeight: FontWeight.w600,
color: layoutColor, fontFamily: "Archivo",
// color: Color(0xFF12B24B),
color: layoutColor,
),
), ),
), ),
), ],
], ),
), ),
], ],
), ),
@ -247,60 +274,51 @@ 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,
'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, context,
Icons.assessment_outlined, Icons.settings_outlined,
'Plans', 'Settings ',
[ [
_buildSubDrawerItem( _buildSubDrawerItem(
context, 'My Travel Request', '/listPlan'), context, 'Organization', '/OrganizationSetup'),
_buildSubDrawerItem(context, 'My Approvals', '/ApprovalList') _buildSubDrawerItem(context, 'Group', '/group'),
], _buildSubDrawerItem(context, 'Policy', '/PolicyList'),
'/listPlan'),
_buildExpandableItem(
context,
Icons.account_circle_outlined,
'User ',
[
_buildSubDrawerItem(context, 'User List', '/listUser'),
// _buildSubDrawerItem(context,'PlanB','/PlanB') // _buildSubDrawerItem(context,'PlanB','/PlanB')
], ],
'/listUser'), '/OrganizationSetup',
_buildExpandableItem( ),
context,
Icons.settings_outlined,
'Settings ',
[
_buildSubDrawerItem(
context, 'Organization', '/OrganizationSetup'),
_buildSubDrawerItem(context, 'Group', '/group'),
_buildSubDrawerItem(context, 'Policy', '/PolicyList'),
// _buildSubDrawerItem(context,'PlanB','/PlanB')
],
'/OrganizationSetup',
),
_buildDrawerItem(context, Icons.login_outlined, 'Logout', '/'), _buildDrawerItem(context, Icons.login_outlined, 'Logout', '/'),
if (widget.isDesktop) Spacer(), if (widget.isDesktop) Spacer(),
Container( Container(
@ -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