ui changes
This commit is contained in:
parent
e0ed41ad35
commit
ceb6aece50
@ -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(),
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -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),
|
||||
))
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
@ -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),
|
||||
))
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -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!;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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(() {
|
||||
|
||||
@ -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
@ -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",
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -47,7 +47,6 @@ dependencies:
|
||||
image_picker: ^1.1.2
|
||||
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
Loading…
Reference in New Issue
Block a user