Page Shaking

This commit is contained in:
venbaittech 2025-06-03 18:13:45 +05:30
parent b88973d43c
commit 3e3ef56694
17 changed files with 4375 additions and 2640 deletions

View File

@ -13,6 +13,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../../routes/custom_appBar.dart'; import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart'; import '../../routes/custom_drawer.dart';
import '../../routes/mainLayout.dart';
import '../../services/apiService.dart'; import '../../services/apiService.dart';
import '../../utils/auth_utils.dart'; import '../../utils/auth_utils.dart';
import '../../utils/pagination.dart'; import '../../utils/pagination.dart';
@ -69,12 +70,15 @@ class _ListAllPlansState extends State<ListAllPlans> {
print("allPlans before filtering: $allPlans"); print("allPlans before filtering: $allPlans");
final lowerQuery = query.toLowerCase(); final lowerQuery = query.toLowerCase();
setState(() { setState(() {
filteredPlans = allPlans.where((plan) { filteredPlans =
allPlans.where((plan) {
return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) ||
(plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? false) || (plan.employeeCode?.toLowerCase().contains(lowerQuery) ??
false) ||
(plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) ||
(plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) ||
(plan.travellerName?.toLowerCase().contains(lowerQuery) ?? false) || (plan.travellerName?.toLowerCase().contains(lowerQuery) ??
false) ||
(plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) ||
(plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) ||
(plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false);
@ -88,11 +92,13 @@ class _ListAllPlansState extends State<ListAllPlans> {
String? bodyStringColor = await getBodyColor(); String? bodyStringColor = await getBodyColor();
setState(() { setState(() {
layoutColor = layoutString != null layoutColor =
layoutString != null
? Color(int.parse(layoutString)) ? Color(int.parse(layoutString))
: Colors.redAccent; : Colors.redAccent;
bodyColor = bodyStringColor != null bodyColor =
bodyStringColor != null
? Color(int.parse(bodyStringColor)) ? Color(int.parse(bodyStringColor))
: Colors.white; : Colors.white;
}); });
@ -245,8 +251,10 @@ class _ListAllPlansState extends State<ListAllPlans> {
} }
void deletePlan(String planId) async { void deletePlan(String planId) async {
bool confirmed = bool confirmed = await apiService.showCancelConfirmationDialog(
await apiService.showCancelConfirmationDialog(context, layoutColor); context,
layoutColor,
);
if (confirmed) { if (confirmed) {
try { try {
@ -263,35 +271,48 @@ class _ListAllPlansState extends State<ListAllPlans> {
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; builder: (context, sizingInfo) {
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold( return MainLayout(
backgroundColor: Color(0xFFf5f5f5), isDesktop: isDesktop,
// backgroundColor: Color(0xFFFCFCFC),
// appBar: isDesktop ? null : const CustomAppBar(title: 'Home'),
// drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false),
body: Padding(
padding: isDesktop
? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical: MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding
)
: EdgeInsets.all(0),
child: Row( child: Row(
children: [ children: [
// if (isDesktop) CustomDrawer(isDesktop: true), // if (isDesktop) CustomDrawer(isDesktop: true),
Expanded(child: buildGroupListLayout(isDesktop)) Expanded(child: buildGroupListLayout(isDesktop)),
], ],
), ),
),
); );
});
// return Scaffold(
// backgroundColor: Color(0xFFf5f5f5),
// // backgroundColor: Color(0xFFFCFCFC),
// appBar: CustomAppBar(isDesktop: isDesktop),
// drawer: CustomDrawer(isDesktop: false),
// body: Padding(
// padding:
// isDesktop
// ? EdgeInsets.symmetric(
// horizontal:
// MediaQuery.of(context).size.width *
// 0.1, // 30% of screen width as horizontal padding
// vertical:
// MediaQuery.of(context).size.height *
// 0, // 5% of screen height as vertical padding
// )
// : EdgeInsets.all(0),
// child: Row(
// children: [
// // if (isDesktop) CustomDrawer(isDesktop: true),
// Expanded(child: buildGroupListLayout(isDesktop)),
// ],
// ),
// ),
// );
},
);
} }
Widget buildGroupListLayout(bool isDesktop) { Widget buildGroupListLayout(bool isDesktop) {
@ -322,8 +343,9 @@ class _ListAllPlansState extends State<ListAllPlans> {
String _formatDate(String rawDate) { String _formatDate(String rawDate) {
try { try {
final dateTime = DateTime.parse(rawDate); final dateTime = DateTime.parse(rawDate);
return DateFormat('dd, MMM yyyy HH:mm') return DateFormat(
.format(dateTime); // 24-hour format 'dd, MMM yyyy HH:mm',
).format(dateTime); // 24-hour format
} catch (e) { } catch (e) {
return rawDate; // fallback if parsing fails return rawDate; // fallback if parsing fails
} }
@ -332,11 +354,13 @@ class _ListAllPlansState extends State<ListAllPlans> {
return Container( return Container(
margin: isDesktop ? EdgeInsets.all(10.0) : null, margin: isDesktop ? EdgeInsets.all(10.0) : null,
padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20), padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20),
height: isDesktop height:
isDesktop
? MediaQuery.of(context).size.height * 0.98 ? MediaQuery.of(context).size.height * 0.98
: MediaQuery.of(context).size.height, : MediaQuery.of(context).size.height,
decoration: BoxDecoration( decoration: BoxDecoration(
border: isDesktop border:
isDesktop
? Border.all( ? Border.all(
width: 2, width: 2,
color: Colors.white, color: Colors.white,
@ -382,9 +406,7 @@ class _ListAllPlansState extends State<ListAllPlans> {
], ],
), ),
SizedBox( SizedBox(width: 1),
width: 1,
),
Spacer(), Spacer(),
if (isDesktop) if (isDesktop)
Container( Container(
@ -395,8 +417,10 @@ class _ListAllPlansState extends State<ListAllPlans> {
onChanged: filterPlans, onChanged: filterPlans,
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search...", hintText: "Search...",
hintStyle: hintStyle: TextStyle(
TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)), fontSize: 12,
color: Color(0xFF9E9DBD),
),
prefixIcon: Icon( prefixIcon: Icon(
Icons.search, Icons.search,
color: Color(0xFF9E9DBD), color: Color(0xFF9E9DBD),
@ -408,22 +432,23 @@ class _ListAllPlansState extends State<ListAllPlans> {
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.grey.shade200, width: 0.5), color: Colors.grey.shade200,
width: 0.5,
),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.grey.shade300, width: 1), color: Colors.grey.shade300,
width: 1,
), ),
), ),
style: GoogleFonts.poppins(
fontSize: 12,
), ),
style: GoogleFonts.poppins(fontSize: 12),
), ),
), ),
// SizedBox(width: 16), // SizedBox(width: 16),
Spacer(), Spacer(),
// ElevatedButton( // ElevatedButton(
// style: ElevatedButton.styleFrom( // style: ElevatedButton.styleFrom(
@ -521,8 +546,10 @@ class _ListAllPlansState extends State<ListAllPlans> {
onChanged: filterPlans, onChanged: filterPlans,
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search...", hintText: "Search...",
hintStyle: hintStyle: TextStyle(
TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)), fontSize: 12,
color: Color(0xFF9E9DBD),
),
prefixIcon: Icon( prefixIcon: Icon(
Icons.search, Icons.search,
color: Color(0xFF9E9DBD), color: Color(0xFF9E9DBD),
@ -534,17 +561,19 @@ class _ListAllPlansState extends State<ListAllPlans> {
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.grey.shade200, width: 0.5), color: Colors.grey.shade200,
width: 0.5,
),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.grey.shade300, width: 1), color: Colors.grey.shade300,
width: 1,
), ),
), ),
style: GoogleFonts.poppins(
fontSize: 12,
), ),
style: GoogleFonts.poppins(fontSize: 12),
), ),
), ),
], ],
@ -581,14 +610,17 @@ class _ListAllPlansState extends State<ListAllPlans> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Colors.black54), color: Colors.black54,
),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Text( Text(
"Please Create Trip", "Please Create Trip",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
), ),
], ],
), ),
@ -604,10 +636,13 @@ class _ListAllPlansState extends State<ListAllPlans> {
List<Plan> plans = List<Plan> plans =
searchController.text.isEmpty ? allPlans : filteredPlans; searchController.text.isEmpty ? allPlans : filteredPlans;
plans.sort((a, b) => plans.sort(
int.parse(b.planId).compareTo(int.parse(a.planId))); (a, b) =>
int.parse(b.planId).compareTo(int.parse(a.planId)),
);
List<Plan> paginatedPlans = plans List<Plan> paginatedPlans =
plans
.skip(currentPage * itemsPerPage) .skip(currentPage * itemsPerPage)
.take(itemsPerPage) .take(itemsPerPage)
.toList(); .toList();
@ -623,112 +658,169 @@ class _ListAllPlansState extends State<ListAllPlans> {
columnSpacing: isDesktop ? 24.0 : 16.0, columnSpacing: isDesktop ? 24.0 : 16.0,
border: TableBorder( border: TableBorder(
horizontalInside: BorderSide( horizontalInside: BorderSide(
width: 0.5, color: Colors.grey.shade200), width: 0.5,
color: Colors.grey.shade200,
),
), ),
columns: [ columns: [
DataColumn( DataColumn(
label: Text( label: Text(
'Trip ID', 'Trip ID',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600), fontSize: 13,
)), fontWeight: FontWeight.w600,
),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Trip Name', 'Trip Name',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600), fontSize: 13,
)), fontWeight: FontWeight.w600,
),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Emp Code', 'Emp Code',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600), fontSize: 13,
)), fontWeight: FontWeight.w600,
),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Traveller', 'Traveller',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600), fontSize: 13,
)), fontWeight: FontWeight.w600,
),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Trip Type', 'Trip Type',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600), fontSize: 13,
)), fontWeight: FontWeight.w600,
),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Created On', 'Created On',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600), fontSize: 13,
)), fontWeight: FontWeight.w600,
),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Status', 'Status',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600), fontSize: 13,
)), fontWeight: FontWeight.w600,
),
),
),
DataColumn( DataColumn(
label: Text( label: Text(
'Actions', 'Actions',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontWeight: FontWeight.w600), fontSize: 13,
)), fontWeight: FontWeight.w600,
),
),
),
], ],
rows: paginatedPlans.map((plan) { rows:
return DataRow(cells: [ paginatedPlans.map((plan) {
DataCell(Text(plan.planId, return DataRow(
cells: [
DataCell(
Text(
plan.planId,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
))), ),
DataCell(Text(plan.tripTitle, ),
),
DataCell(
Text(
plan.tripTitle,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
), ),
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis)), overflow: TextOverflow.ellipsis,
DataCell(Text(plan.employeeCode ?? " - ", ),
),
DataCell(
Text(
plan.employeeCode ?? " - ",
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
))), ),
DataCell(Text( ),
),
DataCell(
Text(
plan.userName.isNotEmpty plan.userName.isNotEmpty
? plan.userName ? plan.userName
: plan.travellerName, : plan.travellerName,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
))), ),
DataCell(Text(plan.tripType, ),
),
DataCell(
Text(
plan.tripType,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
))), ),
DataCell(Text(_formatDate(plan.createdOn), ),
),
DataCell(
Text(
_formatDate(plan.createdOn),
// plan.createdOn, // plan.createdOn,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
))), ),
),
),
DataCell( DataCell(
Container( Container(
width: double width:
double
.infinity, // Set your desired fixed size (equal width and height) .infinity, // Set your desired fixed size (equal width and height)
height: 25, height: 25,
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: getStatusColor(plan.statusValue), color: getStatusColor(
borderRadius: BorderRadius.circular(10), plan.statusValue,
),
borderRadius: BorderRadius.circular(
10,
),
), ),
child: Text( child: Text(
plan.statusValue, plan.statusValue,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
color: color: getStatusTextColor(
getStatusTextColor(plan.statusValue), plan.statusValue,
),
fontSize: 12, fontSize: 12,
fontFamily: "Inter", fontFamily: "Inter",
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
@ -748,84 +840,130 @@ class _ListAllPlansState extends State<ListAllPlans> {
color: Color(0xFF475569), color: Color(0xFF475569),
size: 14, size: 14,
), ),
itemBuilder: (context) => [ itemBuilder:
(context) => [
CustomPopupMenuEntry( CustomPopupMenuEntry(
child: Container( child: Container(
padding: EdgeInsets.symmetric( padding:
horizontal: 8, vertical: 8), EdgeInsets.symmetric(
horizontal: 8,
vertical: 8,
),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize:
MainAxisSize.min,
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.center, MainAxisAlignment
.center,
children: [ children: [
IconButton( IconButton(
icon: Icon( icon: Icon(
Icons.remove_red_eye, Icons
color: Color(0xFF475569), .remove_red_eye,
size: 18), color: Color(
tooltip: 'View Trips', 0xFF475569,
),
size: 18,
),
tooltip:
'View Trips',
onPressed: () { onPressed: () {
Navigator.pop( Navigator.pop(
context); // Close popup manually context,
); // Close popup manually
ApiService.viewPlan( ApiService.viewPlan(
context, plan.planId, context,
isViewMode: true); plan.planId,
isViewMode:
true,
);
}, },
), ),
IconButton( IconButton(
icon: Image.asset( icon: Image.asset(
'assets/images/IconsImg/edit.png', 'assets/images/IconsImg/edit.png',
width: 20, width: 20,
height: 15), height: 15,
),
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(
context,
);
ApiService.viewPlan( ApiService.viewPlan(
context, plan.planId, context,
isViewMode: false); plan.planId,
isViewMode:
false,
);
}, },
), ),
IconButton( IconButton(
icon: Icon( icon: Icon(
Icons.cancel_rounded, Icons
size: 18), .cancel_rounded,
tooltip: 'Cancellation Trips', size: 18,
),
tooltip:
'Cancellation Trips',
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(
deletePlan(plan.planId); context,
);
deletePlan(
plan.planId,
);
}, },
), ),
IconButton( IconButton(
icon: Icon(Icons.download, icon: Icon(
color: Color(0xFF114D8B), Icons.download,
size: 18), color: Color(
tooltip: 'Download Trips Detials', 0xFF114D8B,
),
size: 18,
),
tooltip:
'Download Trips Detials',
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(
apiService.getPdfDownload( context,
plan.planId); );
apiService
.getPdfDownload(
plan.planId,
);
}, },
), ),
IconButton( IconButton(
icon: const Icon( icon: const Icon(
Icons.comment, Icons.comment,
color: Color(0xFF475569), color: Color(
0xFF475569,
),
size: 11, size: 11,
), ),
tooltip: 'Trips Comments', tooltip:
'Trips Comments',
onPressed: () { onPressed: () {
showDialog( showDialog(
context: context, context:
builder: (context) => context,
CommentModalList( builder:
(
context,
) => CommentModalList(
// planId: plan.planId, // planId: plan.planId,
planId: plan planId:
.planId plan.planId
.toString(), .toString(),
layoutColorForUser: layoutColorForUser:
layoutColor!, layoutColor!,
role: "Admin"), role:
"Admin",
),
); );
}), },
),
], ],
), ),
), ),
@ -835,7 +973,8 @@ class _ListAllPlansState extends State<ListAllPlans> {
], ],
), ),
), ),
]); ],
);
}).toList(), }).toList(),
), ),
); );
@ -849,8 +988,10 @@ class _ListAllPlansState extends State<ListAllPlans> {
final plan = paginatedPlans[index]; final plan = paginatedPlans[index];
return Card( return Card(
color: Colors.white, color: Colors.white,
margin: margin: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 12, vertical: 6), horizontal: 12,
vertical: 6,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
@ -875,18 +1016,23 @@ class _ListAllPlansState extends State<ListAllPlans> {
children: [ children: [
Container( Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: 8, vertical: 4), horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: getStatusColor( color: getStatusColor(
plan.statusValue), plan.statusValue,
borderRadius: ),
BorderRadius.circular(8), borderRadius: BorderRadius.circular(
8,
),
), ),
child: Text( child: Text(
plan.statusValue, plan.statusValue,
style: TextStyle( style: TextStyle(
color: getStatusTextColor( color: getStatusTextColor(
plan.statusValue), plan.statusValue,
),
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@ -915,11 +1061,14 @@ class _ListAllPlansState extends State<ListAllPlans> {
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.start, CrossAxisAlignment.start,
children: [ children: [
Text(' ${plan.tripTitle}', Text(
' ${plan.tripTitle}',
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontFamily: "Inter", fontFamily: "Inter",
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold,
),
),
], ],
), ),
], ],
@ -934,24 +1083,28 @@ class _ListAllPlansState extends State<ListAllPlans> {
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.start, CrossAxisAlignment.start,
children: [ children: [
Text(' ${plan.tripType}', Text(
' ${plan.tripType}',
style: TextStyle( style: TextStyle(
fontSize: 9, fontSize: 9,
color: Colors.black87, color: Colors.black87,
fontFamily: "Inter", fontFamily: "Inter",
)), ),
),
], ],
), ),
Column( Column(
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.start, CrossAxisAlignment.start,
children: [ children: [
Text('${_formatDate(plan.createdOn)}', Text(
'${_formatDate(plan.createdOn)}',
style: TextStyle( style: TextStyle(
fontSize: 9, fontSize: 9,
color: Colors.black87, color: Colors.black87,
fontFamily: "Inter", fontFamily: "Inter",
)), ),
),
], ],
), ),
], ],
@ -971,7 +1124,8 @@ class _ListAllPlansState extends State<ListAllPlans> {
style: TextStyle( style: TextStyle(
fontSize: 9, fontSize: 9,
fontFamily: "Inter", fontFamily: "Inter",
color: Colors.black87), color: Colors.black87,
),
), ),
], ],
), ),
@ -985,7 +1139,8 @@ class _ListAllPlansState extends State<ListAllPlans> {
style: TextStyle( style: TextStyle(
fontSize: 9, fontSize: 9,
fontFamily: "Inter", fontFamily: "Inter",
color: Colors.black87), color: Colors.black87,
),
), ),
], ],
), ),
@ -1005,14 +1160,17 @@ class _ListAllPlansState extends State<ListAllPlans> {
// mainAxisAlignment: MainAxisAlignment.spaceBetween, // mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: isDesktop child:
isDesktop
? (searchController.text.isNotEmpty && ? (searchController.text.isNotEmpty &&
filteredPlans.isEmpty filteredPlans.isEmpty
? Center( ? Center(
child: Text( child: Text(
"No matches found", "No matches found",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, color: Colors.grey), fontSize: 14,
color: Colors.grey,
),
), ),
) )
: SingleChildScrollView( : SingleChildScrollView(
@ -1025,7 +1183,9 @@ class _ListAllPlansState extends State<ListAllPlans> {
child: Text( child: Text(
"No matches found", "No matches found",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, color: Colors.grey), fontSize: 14,
color: Colors.grey,
),
), ),
) )
: buildMobileCardView(paginatedPlans)), : buildMobileCardView(paginatedPlans)),
@ -1058,7 +1218,7 @@ class _ListAllPlansState extends State<ListAllPlans> {
), ),
); );
}, },
) ),
], ],
), ),
), ),

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
Future<dynamic> showApprovalDialog( Future<dynamic> showApprovalDialog(
BuildContext context, Color layoutColor) async { BuildContext context,
Color layoutColor,
) async {
String selectedAction = ""; // "", "accept", "reject" String selectedAction = ""; // "", "accept", "reject"
String remarks = ""; String remarks = "";
@ -24,13 +26,12 @@ Future<dynamic> showApprovalDialog(
Text( Text(
"To Approve or Reject Trip", "To Approve or Reject Trip",
style: TextStyle( style: TextStyle(
fontFamily: "Inter", fontWeight: FontWeight.w500), fontFamily: "Inter",
fontWeight: FontWeight.w500,
),
), ),
IconButton( IconButton(
icon: const Icon( icon: const Icon(Icons.close, size: 15),
Icons.close,
size: 15,
),
onPressed: () { onPressed: () {
Navigator.pop(context, null); // Close the dialog Navigator.pop(context, null); // Close the dialog
}, },
@ -45,10 +46,12 @@ Future<dynamic> showApprovalDialog(
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: selectedAction == "accept" backgroundColor:
? layoutColor selectedAction == "accept"
? Colors.green
: Colors.grey.shade200, : Colors.grey.shade200,
foregroundColor: selectedAction == "accept" foregroundColor:
selectedAction == "accept"
? Colors.white ? Colors.white
: Colors.black, : Colors.black,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -68,10 +71,12 @@ Future<dynamic> showApprovalDialog(
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: selectedAction == "reject" backgroundColor:
selectedAction == "reject"
? Colors.redAccent ? Colors.redAccent
: Colors.grey.shade200, : Colors.grey.shade200,
foregroundColor: selectedAction == "reject" foregroundColor:
selectedAction == "reject"
? Colors.white ? Colors.white
: Colors.black, : Colors.black,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -122,12 +127,16 @@ Future<dynamic> showApprovalDialog(
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide( borderSide: const BorderSide(
color: Colors.blueGrey, width: 0.5), color: Colors.blueGrey,
width: 0.5,
),
), ),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: borderSide: BorderSide(
BorderSide(color: Colors.blueGrey, width: 0.5), color: Colors.blueGrey,
width: 0.5,
),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
@ -135,7 +144,7 @@ Future<dynamic> showApprovalDialog(
), ),
), ),
), ),
] ],
], ],
), ),
actionsAlignment: MainAxisAlignment.center, actionsAlignment: MainAxisAlignment.center,
@ -187,14 +196,12 @@ Future<dynamic> showApprovalDialog(
Future<bool?> showApproveDialog1(BuildContext context, Color layoutColor) { Future<bool?> showApproveDialog1(BuildContext context, Color layoutColor) {
return showDialog<bool>( return showDialog<bool>(
context: context, context: context,
builder: (context) => AlertDialog( builder:
(context) => AlertDialog(
backgroundColor: Colors.white, backgroundColor: Colors.white,
title: const Text( title: const Text(
"Confirm Approval", "Confirm Approval",
style: TextStyle( style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
fontSize: 18,
fontWeight: FontWeight.bold,
),
), ),
content: const Text("Are you sure you want to approve this plan?"), content: const Text("Are you sure you want to approve this plan?"),
actions: [ actions: [
@ -231,14 +238,17 @@ Future<bool?> showApproveDialog1(BuildContext context, Color layoutColor) {
/// Show confirm dialog for rejection with remarks input /// Show confirm dialog for rejection with remarks input
Future<String?> showRejectDialog1( Future<String?> showRejectDialog1(
BuildContext context, Color layoutColor) async { BuildContext context,
Color layoutColor,
) async {
String remarks = ""; String remarks = "";
final confirmed = await showDialog<bool>( final confirmed = await showDialog<bool>(
context: context, context: context,
builder: (context) { builder: (context) {
return StatefulBuilder( return StatefulBuilder(
builder: (context, setState) => AlertDialog( builder:
(context, setState) => AlertDialog(
backgroundColor: Colors.white, backgroundColor: Colors.white,
contentPadding: const EdgeInsets.all(36), contentPadding: const EdgeInsets.all(36),
// title: const Text("Confirm Rejection"), // title: const Text("Confirm Rejection"),
@ -260,13 +270,20 @@ Future<String?> showRejectDialog1(
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 10, // 👈 Set your desired font size here fontSize: 10, // 👈 Set your desired font size here
color: Colors.grey, color: Colors.grey,
fontFamily: "Inter", // optional if you want consistent font fontFamily:
"Inter", // optional if you want consistent font
), ),
border: OutlineInputBorder( border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueGrey, width: 0.5), borderSide: BorderSide(
color: Colors.blueGrey,
width: 0.5,
),
), ),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueGrey, width: 0.5), borderSide: BorderSide(
color: Colors.blueGrey,
width: 0.5,
),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1), borderSide: BorderSide(color: Colors.grey, width: 1),

View File

@ -11,6 +11,8 @@ import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../../../services/apiService.dart';
class LoginWidget extends StatefulWidget { class LoginWidget extends StatefulWidget {
final bool isDesktop; final bool isDesktop;
final bool isTablet; final bool isTablet;
@ -25,6 +27,9 @@ class LoginWidget extends StatefulWidget {
enum LoginStep { login, forgotEmail, otpReset } enum LoginStep { login, forgotEmail, otpReset }
class _LoginWidgetState extends State<LoginWidget> { class _LoginWidgetState extends State<LoginWidget> {
final ApiService apiService = ApiService();
bool _moved = false;
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final TextEditingController _emailController = TextEditingController(); final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController(); final TextEditingController _passwordController = TextEditingController();
@ -39,6 +44,17 @@ class _LoginWidgetState extends State<LoginWidget> {
// 🔹 Login Step Enum and State Variable // 🔹 Login Step Enum and State Variable
LoginStep _loginStep = LoginStep.login; LoginStep _loginStep = LoginStep.login;
@override
void initState() {
super.initState();
Future.delayed(Duration(milliseconds: 300), () {
setState(() {
_moved = true;
});
});
}
@override @override
void dispose() { void dispose() {
_emailController.dispose(); _emailController.dispose();
@ -54,15 +70,18 @@ class _LoginWidgetState extends State<LoginWidget> {
final parts = token.split('.'); final parts = token.split('.');
if (parts.length != 3) throw Exception('Invalid token format'); if (parts.length != 3) throw Exception('Invalid token format');
final payload = json final payload = json.decode(
.decode(utf8.decode(base64Url.decode(base64Url.normalize(parts[1])))); utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))),
);
final userData = payload['data']; final userData = payload['data'];
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.setString('auth_token', token); await prefs.setString('auth_token', token);
await prefs.setString( await prefs.setString(
'user_data', jsonEncode(userData)); // Store full user data 'user_data',
jsonEncode(userData),
); // Store full user data
if (userData != null) { if (userData != null) {
final pref = await SharedPreferences.getInstance(); final pref = await SharedPreferences.getInstance();
@ -75,6 +94,8 @@ class _LoginWidgetState extends State<LoginWidget> {
print("userData11 - ${userData['role']}"); print("userData11 - ${userData['role']}");
print("userData12 - $userRole"); print("userData12 - $userRole");
} }
apiService.getOrganizationData();
} catch (e) { } catch (e) {
print('Error decoding token: $e'); print('Error decoding token: $e');
} }
@ -89,7 +110,7 @@ class _LoginWidgetState extends State<LoginWidget> {
Uri.parse(url), Uri.parse(url),
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Accept': 'application/json' 'Accept': 'application/json',
}, },
body: jsonEncode({ body: jsonEncode({
'email': _emailController.text.trim(), 'email': _emailController.text.trim(),
@ -145,9 +166,9 @@ class _LoginWidgetState extends State<LoginWidget> {
// ); // );
} }
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text("Error: $e")), context,
); ).showSnackBar(SnackBar(content: Text("Error: $e")));
} }
} }
} }
@ -175,19 +196,27 @@ class _LoginWidgetState extends State<LoginWidget> {
// _clearAllFields(); // _clearAllFields();
}); });
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("OTP sent to your email")), SnackBar(
content: Text("OTP sent to your email"),
backgroundColor: Colors.green,
),
); );
} else { } else {
print(response); print(response);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: content: Text(
Text("${jsonDecode(response.body)['messages']['error']}")), "${jsonDecode(response.body)['messages']['error']}",
),
backgroundColor: Colors.red,
),
); );
} }
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context) ScaffoldMessenger.of(context).showSnackBar(
.showSnackBar(SnackBar(content: Text("Error: $e"))); SnackBar(content: Text("Error: $e"), backgroundColor: Colors.red),
);
} }
} else if (!_isForgotPassword && _showOtpResetFields) { } else if (!_isForgotPassword && _showOtpResetFields) {
print('22'); print('22');
@ -214,20 +243,32 @@ class _LoginWidgetState extends State<LoginWidget> {
_clearAllFields(); _clearAllFields();
}); });
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Password reset successfully")), SnackBar(
content: Text("Password reset successfully"),
backgroundColor: Colors.green,
),
); );
} else { } else {
print('23'); print('23');
print('otp wrong'); print('otp wrong');
final responseBody = json.decode(response.body);
final errorMessage =
responseBody['messages']?['error'] ?? 'An unknown error occurred';
print(errorMessage);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text( content: Text(
"Reset failed: ${jsonDecode(response.body)['message']}")), "Reset failed: $errorMessage",
// "Reset failed: ${jsonDecode(response.body)['message']}",
),
backgroundColor: Colors.red,
),
); );
} }
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context) ScaffoldMessenger.of(context).showSnackBar(
.showSnackBar(SnackBar(content: Text("Error: $e"))); SnackBar(content: Text("Error: $e"), backgroundColor: Colors.red),
);
} }
} else { } else {
// Login flow // Login flow
@ -255,15 +296,15 @@ class _LoginWidgetState extends State<LoginWidget> {
} }
@override @override
/// Layout /// Layout
Widget build(BuildContext context) { Widget build(BuildContext context) {
double formWidth = widget.isTablet ? 400 : 300; double formWidth = widget.isTablet ? 400 : 300;
return Container( return Container(
// color: Color(0xFF114D8B), // color: Color(0xFF114D8B),
color: Color(0xFFf5f5f5), color: Colors.white,
// color: Color(0xFFf5f5f5),
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
child: Row( child: Row(
children: [ children: [
@ -285,8 +326,9 @@ class _LoginWidgetState extends State<LoginWidget> {
// color: Colors.white, // color: Colors.white,
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topRight: Radius.circular(250), // Rounded top-left corner topRight: Radius.circular(250), // Rounded top-left corner
bottomRight: bottomRight: Radius.circular(
Radius.circular(250), // Rounded bottom-left corner 250,
), // Rounded bottom-left corner
), ),
), ),
// child: Padding( // child: Padding(
@ -305,11 +347,15 @@ class _LoginWidgetState extends State<LoginWidget> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.asset( // Image.asset(
'assets/images/login/logoNew.jpg', // 'assets/images/login/logoNew.jpg',
// width: 200, // Optional: control size
// height: 100,
// fit: BoxFit.contain,
// ),
Container(
width: 200, // Optional: control size width: 200, // Optional: control size
height: 100, height: 100,
fit: BoxFit.contain,
), ),
Expanded( Expanded(
child: Container( child: Container(
@ -320,9 +366,10 @@ class _LoginWidgetState extends State<LoginWidget> {
// width: 200, // Optional: control size // width: 200, // Optional: control size
// height: 100, // height: 100,
fit: BoxFit.contain, fit: BoxFit.contain,
)),
), ),
) ),
),
),
], ],
), ),
), ),
@ -332,20 +379,18 @@ class _LoginWidgetState extends State<LoginWidget> {
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
// color: Colors.white, // color: Colors.white,
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(25), // Rounded top-left corner topLeft: Radius.circular(25), // Rounded top-left corner
bottomLeft: Radius.circular(25), // Rounded bottom-left corner bottomLeft: Radius.circular(25), // Rounded bottom-left corner
), ),
), ),
child: Center(
child: Padding( child: Padding(
padding: const EdgeInsets.all(40), padding: const EdgeInsets.all(10),
child: _buildForm(width: formWidth), // Fixed form width child: _buildForm(width: formWidth), // Fixed form width
), ),
), ),
), ),
),
], ],
), ),
); );
@ -353,29 +398,57 @@ class _LoginWidgetState extends State<LoginWidget> {
/// **Reusable Login Form** /// **Reusable Login Form**
Widget _buildForm({required double width}) { Widget _buildForm({required double width}) {
return SizedBox( return Column(
children: [
// width: width,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Image.asset(
'assets/images/login/logoNew.jpg',
width: 180, // Optional: control size
height: 70,
fit: BoxFit.contain,
),
],
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: width, width: width,
child: Form( child: Form(
key: _formKey, key: _formKey,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const SizedBox(height: 2),
Text( Text(
"Sign In", "Sign In",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 18, fontSize: widget.isDesktop ? 20 : 18,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w700,
color: Color(0xFF212121)), color: Colors.green,
// color: Color(0xFF212121),
), ),
const SizedBox(height: 2), ),
const SizedBox(height: 5),
Text( Text(
"Welcome To TripApprovalTool", "Welcome To TripApprovalTool",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Color(0xFF212121)),
color: Color(0xFF212121),
), ),
const SizedBox(height: 18), ),
const SizedBox(height: 10),
/// **Email Field** /// **Email Field**
if (!_isForgotPassword && !_showOtpResetFields) ...[ if (!_isForgotPassword && !_showOtpResetFields) ...[
@ -383,47 +456,61 @@ class _LoginWidgetState extends State<LoginWidget> {
TextFormField( TextFormField(
controller: _emailController, controller: _emailController,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11), fontWeight: FontWeight.w600,
decoration: fontSize: 11,
_inputDecoration("Enter your email address").copyWith( ),
decoration: _inputDecoration(
"Enter your email address",
).copyWith(
prefixIcon: Icon( prefixIcon: Icon(
Icons.email_outlined, Icons.email_outlined,
size: 16, size: 16,
), ),
), ),
validator: (value) => validator:
value == null || value.isEmpty ? 'Required Email' : null, (value) =>
value == null || value.isEmpty
? 'Required Email'
: null,
), ),
const SizedBox(height: 16), const SizedBox(height: 10),
/// **Password Field** /// **Password Field**
_buildLabel("Password"), _buildLabel("Password"),
TextFormField( TextFormField(
controller: _passwordController, controller: _passwordController,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11), fontWeight: FontWeight.w600,
obscureText: _obscureText, fontSize: 11,
decoration: _inputDecoration("Enter your password").copyWith(
prefixIcon: Icon(
Icons.key,
size: 16,
), ),
obscureText: _obscureText,
decoration: _inputDecoration(
"Enter your password",
).copyWith(
prefixIcon: Icon(Icons.key, size: 16),
suffixIcon: IconButton( suffixIcon: IconButton(
icon: Icon( icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility, _obscureText
? Icons.visibility_off
: Icons.visibility,
color: Color(0xFF12B24B), color: Color(0xFF12B24B),
size: 16, size: 16,
), ),
onPressed: () => onPressed:
setState(() => _obscureText = !_obscureText), () => setState(
() => _obscureText = !_obscureText,
), ),
), ),
validator: (value) => ),
value == null || value.isEmpty ? 'Required Password' : null, validator:
(value) =>
value == null || value.isEmpty
? 'Required Password'
: null,
), ),
const SizedBox(height: 20), const SizedBox(height: 10),
/// **Login Button** /// **Login Button**
Row( Row(
@ -432,31 +519,39 @@ class _LoginWidgetState extends State<LoginWidget> {
child: ElevatedButton( child: ElevatedButton(
onPressed: () => _login(context), onPressed: () => _login(context),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF12B24B), // Button color backgroundColor: Color(
foregroundColor: Colors.white, // Text color 0xFF12B24B,
), // Button color
foregroundColor:
Colors.white, // Text color
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 24, vertical: 12), horizontal: 24,
vertical: 12,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18)), borderRadius: BorderRadius.circular(18),
),
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 24, vertical: 10), horizontal: 24,
vertical: 5,
),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
"Sign In", "Sign In",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w800, fontSize: 15), fontWeight: FontWeight.w800,
fontSize: 13.5,
), ),
SizedBox(
width: 3,
), ),
SizedBox(width: 3),
Icon( Icon(
Icons.arrow_forward_sharp, Icons.arrow_forward_sharp,
color: Colors.white, color: Colors.white,
) ),
], ],
), ),
), ),
@ -464,54 +559,69 @@ class _LoginWidgetState extends State<LoginWidget> {
), ),
], ],
), ),
] else if (_isForgotPassword && !_showOtpResetFields) ...[ ] else if (_isForgotPassword &&
!_showOtpResetFields) ...[
_buildLabel("Email Address"), _buildLabel("Email Address"),
TextFormField( TextFormField(
controller: _emailController, controller: _emailController,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11), fontWeight: FontWeight.w600,
decoration: fontSize: 11,
_inputDecoration("Enter your email address").copyWith( ),
decoration: _inputDecoration(
"Enter your email address",
).copyWith(
prefixIcon: Icon( prefixIcon: Icon(
Icons.email_outlined, Icons.email_outlined,
size: 16, size: 16,
), ),
), ),
validator: (value) => validator:
value == null || value.isEmpty ? 'Required Email' : null, (value) =>
value == null || value.isEmpty
? 'Required Email'
: null,
), ),
const SizedBox(height: 20), const SizedBox(height: 10),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: () => _onSubmit(context), onPressed: () => _onSubmit(context),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF12B24B), // Button color backgroundColor: Color(
foregroundColor: Colors.white, // Text color 0xFF12B24B,
), // Button color
foregroundColor:
Colors.white, // Text color
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 24, vertical: 12), horizontal: 24,
vertical: 12,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18)), borderRadius: BorderRadius.circular(18),
),
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 24, vertical: 10), horizontal: 24,
vertical: 5,
),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
"Submit", "Submit",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w800, fontSize: 15), fontWeight: FontWeight.w800,
fontSize: 13.5,
), ),
SizedBox(
width: 3,
), ),
SizedBox(width: 3),
Icon( Icon(
Icons.arrow_forward_sharp, Icons.arrow_forward_sharp,
color: Colors.white, color: Colors.white,
) ),
], ],
), ),
), ),
@ -525,80 +635,103 @@ class _LoginWidgetState extends State<LoginWidget> {
controller: _emailController, controller: _emailController,
readOnly: true, readOnly: true,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11), fontWeight: FontWeight.w600,
decoration: fontSize: 11,
_inputDecoration("Enter your email address").copyWith( ),
decoration: _inputDecoration(
"Enter your email address",
).copyWith(
prefixIcon: Icon( prefixIcon: Icon(
Icons.email_outlined, Icons.email_outlined,
size: 16, size: 16,
), ),
), ),
validator: (value) => validator:
value == null || value.isEmpty ? 'Required Email' : null, (value) =>
value == null || value.isEmpty
? 'Required Email'
: null,
), ),
const SizedBox(height: 16), const SizedBox(height: 10),
_buildLabel("OTP"), _buildLabel("OTP"),
TextFormField( TextFormField(
controller: _otpController, controller: _otpController,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11), fontWeight: FontWeight.w600,
decoration: _inputDecoration("Enter your OTP").copyWith( fontSize: 11,
),
decoration: _inputDecoration(
"Enter your OTP",
).copyWith(
prefixIcon: Icon( prefixIcon: Icon(
Icons.email_outlined, Icons.email_outlined,
size: 16, size: 16,
), ),
), ),
validator: (value) => validator:
value == null || value.isEmpty ? 'Required OTP' : null, (value) =>
value == null || value.isEmpty
? 'Required OTP'
: null,
), ),
const SizedBox(height: 16), const SizedBox(height: 10),
_buildLabel("New Password"), _buildLabel("New Password"),
TextFormField( TextFormField(
controller: _newPasswordController, controller: _newPasswordController,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11), fontWeight: FontWeight.w600,
obscureText: _obscureText, fontSize: 11,
decoration:
_inputDecoration("Enter your new password").copyWith(
prefixIcon: Icon(
Icons.key,
size: 16,
), ),
obscureText: _obscureText,
decoration: _inputDecoration(
"Enter your new password",
).copyWith(
prefixIcon: Icon(Icons.key, size: 16),
suffixIcon: IconButton( suffixIcon: IconButton(
icon: Icon( icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility, _obscureText
? Icons.visibility_off
: Icons.visibility,
color: Color(0xFF12B24B), color: Color(0xFF12B24B),
size: 16, size: 16,
), ),
onPressed: () => onPressed:
setState(() => _obscureText = !_obscureText), () => setState(
() => _obscureText = !_obscureText,
), ),
), ),
validator: (value) => value == null || value.isEmpty ),
validator:
(value) =>
value == null || value.isEmpty
? 'Required New Password' ? 'Required New Password'
: null, : null,
), ),
const SizedBox(height: 16), const SizedBox(height: 10),
_buildLabel("Confirm Password"), _buildLabel("Confirm Password"),
TextFormField( TextFormField(
controller: _confirmPasswordController, controller: _confirmPasswordController,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11), fontWeight: FontWeight.w600,
obscureText: _obscureText, fontSize: 11,
decoration:
_inputDecoration("Enter your confirm password").copyWith(
prefixIcon: Icon(
Icons.key,
size: 16,
), ),
obscureText: _obscureText,
decoration: _inputDecoration(
"Enter your confirm password",
).copyWith(
prefixIcon: Icon(Icons.key, size: 16),
suffixIcon: IconButton( suffixIcon: IconButton(
icon: Icon( icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility, _obscureText
? Icons.visibility_off
: Icons.visibility,
color: Color(0xFF12B24B), color: Color(0xFF12B24B),
size: 16, size: 16,
), ),
onPressed: () => onPressed:
setState(() => _obscureText = !_obscureText), () => setState(
() => _obscureText = !_obscureText,
),
), ),
), ),
validator: (value) { validator: (value) {
@ -611,13 +744,14 @@ class _LoginWidgetState extends State<LoginWidget> {
return null; return null;
}, },
), ),
const SizedBox(height: 20), const SizedBox(height: 10),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
if (!_formKey.currentState!.validate()) return; if (!_formKey.currentState!.validate())
return;
setState(() { setState(() {
_isForgotPassword = false; _isForgotPassword = false;
_showOtpResetFields = true; _showOtpResetFields = true;
@ -626,31 +760,39 @@ class _LoginWidgetState extends State<LoginWidget> {
_onSubmit(context); _onSubmit(context);
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF12B24B), // Button color backgroundColor: Color(
foregroundColor: Colors.white, // Text color 0xFF12B24B,
), // Button color
foregroundColor:
Colors.white, // Text color
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 24, vertical: 12), horizontal: 24,
vertical: 12,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18)), borderRadius: BorderRadius.circular(18),
),
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 24, vertical: 10), horizontal: 24,
vertical: 10,
),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
"Submit", "Submit",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w800, fontSize: 15), fontWeight: FontWeight.w800,
fontSize: 13.5,
), ),
SizedBox(
width: 3,
), ),
SizedBox(width: 3),
Icon( Icon(
Icons.arrow_forward_sharp, Icons.arrow_forward_sharp,
color: Colors.white, color: Colors.white,
) ),
], ],
), ),
), ),
@ -659,7 +801,7 @@ class _LoginWidgetState extends State<LoginWidget> {
], ],
), ),
], ],
const SizedBox(height: 20), const SizedBox(height: 10),
if (!_isForgotPassword && !_showOtpResetFields) if (!_isForgotPassword && !_showOtpResetFields)
Center( Center(
child: TextButton( child: TextButton(
@ -678,7 +820,8 @@ class _LoginWidgetState extends State<LoginWidget> {
color: Color(0xFF212121), // Text color color: Color(0xFF212121), // Text color
decoration: decoration:
TextDecoration.underline, // Underline the text TextDecoration
.underline, // Underline the text
), ),
), ),
), ),
@ -701,13 +844,14 @@ class _LoginWidgetState extends State<LoginWidget> {
color: Color(0xFF212121), // Text color color: Color(0xFF212121), // Text color
decoration: decoration:
TextDecoration.underline, // Underline the text TextDecoration
.underline, // Underline the text
), ),
), ),
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 10),
if (!_isForgotPassword && !_showOtpResetFields) if (!_isForgotPassword && !_showOtpResetFields)
Row( Row(
children: [ children: [
@ -717,10 +861,14 @@ class _LoginWidgetState extends State<LoginWidget> {
handleMS(); handleMS();
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.white, // Button color backgroundColor:
foregroundColor: Colors.black, // Text color Colors.white, // Button color
foregroundColor:
Colors.black, // Text color
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 24, vertical: 5), horizontal: 24,
vertical: 5,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18), borderRadius: BorderRadius.circular(18),
side: BorderSide( side: BorderSide(
@ -731,18 +879,20 @@ class _LoginWidgetState extends State<LoginWidget> {
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 24, vertical: 5), horizontal: 24,
vertical: 3,
),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
"Sign In With Microsoft", "Sign In With Microsoft",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w500, fontSize: 14), fontWeight: FontWeight.w500,
fontSize: 13,
), ),
SizedBox(
width: 3,
), ),
SizedBox(width: 3),
Image.asset( Image.asset(
'assets/images/login/microsoft.png', 'assets/images/login/microsoft.png',
width: 30, // Optional: control size width: 30, // Optional: control size
@ -783,6 +933,13 @@ class _LoginWidgetState extends State<LoginWidget> {
], ],
), ),
), ),
),
],
),
],
),
),
],
); );
} }
@ -797,7 +954,8 @@ class _LoginWidgetState extends State<LoginWidget> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF212121)), color: Color(0xFF212121),
),
), ),
), ),
); );
@ -826,8 +984,10 @@ class _LoginWidgetState extends State<LoginWidget> {
final url = '$apiUrl/auth/mslogin'; final url = '$apiUrl/auth/mslogin';
print(url); print(url);
try { try {
final response = await http final response = await http.get(
.get(Uri.parse(url), headers: {'Content-Type': 'application/json'}); Uri.parse(url),
headers: {'Content-Type': 'application/json'},
);
print("inside try method"); print("inside try method");
if (response.statusCode == 200) { if (response.statusCode == 200) {
final authUrl = json.decode(response.body)['auth_url']; final authUrl = json.decode(response.body)['auth_url'];

View File

@ -24,14 +24,15 @@ class ForexScreen extends StatefulWidget {
final Function(Map<String, dynamic>) onSaveForex; final Function(Map<String, dynamic>) onSaveForex;
final String? loginUser; final String? loginUser;
ForexScreen( ForexScreen({
{required this.onClose, required this.onClose,
this.apiData, this.apiData,
required this.selectedItem, required this.selectedItem,
required this.apiCountryData, required this.apiCountryData,
required this.onSaveForex, required this.onSaveForex,
required this.loginUser, required this.loginUser,
required this.flightData}); required this.flightData,
});
@override @override
_ForexScreenState createState() => _ForexScreenState(); _ForexScreenState createState() => _ForexScreenState();
@ -75,16 +76,18 @@ class _ForexScreenState extends State<ForexScreen> {
"_cash", "_cash",
"_checkForex", "_checkForex",
"_deliveryLocation", "_deliveryLocation",
"_comments" "_comments",
]; ];
String _formatDate(String? date) { String _formatDate(String? date) {
if (date == null || date.isEmpty) return ""; if (date == null || date.isEmpty) return "";
try { try {
DateTime parsedDate = DateTime parsedDate = DateTime.parse(
DateTime.parse(date); // Assuming input is YYYY-MM-DD date,
return DateFormat("dd-MM-yyyy") ); // Assuming input is YYYY-MM-DD
.format(parsedDate); // Convert to DD-MM-YYYY return DateFormat(
"dd-MM-yyyy",
).format(parsedDate); // Convert to DD-MM-YYYY
} catch (e) { } catch (e) {
print("Error formatting date: $e"); print("Error formatting date: $e");
return date; // Return as is if parsing fails return date; // Return as is if parsing fails
@ -146,7 +149,7 @@ class _ForexScreenState extends State<ForexScreen> {
"country_code": selectedCountry, "country_code": selectedCountry,
"start_date": _formatDate(textControllers["_forexStartDate"]?.text), "start_date": _formatDate(textControllers["_forexStartDate"]?.text),
"end_date": _formatDate(textControllers["_forexEndDate"]?.text), "end_date": _formatDate(textControllers["_forexEndDate"]?.text),
"user_id": tripuserId "user_id": tripuserId,
// "currency": selectedCurrency ?? "", // "currency": selectedCurrency ?? "",
}; };
} }
@ -195,10 +198,12 @@ class _ForexScreenState extends State<ForexScreen> {
selectedDuration = responseData["duration"]?.toString() ?? ""; selectedDuration = responseData["duration"]?.toString() ?? "";
selectedQuotedAmount = selectedQuotedAmount =
responseData["perdiem_amount"]?.toString() ?? ""; responseData["perdiem_amount"]?.toString() ?? "";
selectedCardPercent = selectedCardPercent = int.tryParse(
int.tryParse(responseData["card_percentage"]?.toString() ?? ""); responseData["card_percentage"]?.toString() ?? "",
selectedCashPercent = );
int.tryParse(responseData["cash_percentage"]?.toString() ?? ""); selectedCashPercent = int.tryParse(
responseData["cash_percentage"]?.toString() ?? "",
);
textControllers["_cardNumber"]?.text = textControllers["_cardNumber"]?.text =
responseData["forex_card_no"]?.toString() ?? ""; responseData["forex_card_no"]?.toString() ?? "";
@ -249,17 +254,16 @@ class _ForexScreenState extends State<ForexScreen> {
} }
Map<String, String?> getFlightTripDateRange( Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData) { List<Map<String, dynamic>> flightData,
final allTrips = flightData ) {
final allTrips =
flightData
.expand((flight) => flight['trips'] ?? []) .expand((flight) => flight['trips'] ?? [])
.whereType<Map<String, dynamic>>() .whereType<Map<String, dynamic>>()
.toList(); .toList();
if (allTrips.isEmpty) { if (allTrips.isEmpty) {
return { return {'firstTripDate': null, 'lastTripDate': null};
'firstTripDate': null,
'lastTripDate': null,
};
} }
allTrips.sort((a, b) { allTrips.sort((a, b) {
@ -352,18 +356,22 @@ class _ForexScreenState extends State<ForexScreen> {
// Only set controller after value is updated // Only set controller after value is updated
// final parsedDate = // final parsedDate =
// DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); // DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat("dd-MM-yyyy") final parsedDate = DateFormat(
.parse(flightFirstTripDateNotifier.value ?? ''); "dd-MM-yyyy",
).parse(flightFirstTripDateNotifier.value ?? '');
if (parsedDate != null) { if (parsedDate != null) {
textControllers["_forexStartDate"]?.text = textControllers["_forexStartDate"]?.text = DateFormat(
DateFormat('dd-MM-yyyy').format(parsedDate); 'dd-MM-yyyy',
).format(parsedDate);
} }
final parsedEndDate = DateFormat("dd-MM-yyyy") final parsedEndDate = DateFormat(
.parse(flightLastTripDateNotifier.value ?? ''); "dd-MM-yyyy",
).parse(flightLastTripDateNotifier.value ?? '');
if (parsedEndDate != null) { if (parsedEndDate != null) {
textControllers["_forexEndDate"]?.text = textControllers["_forexEndDate"]?.text = DateFormat(
DateFormat('dd-MM-yyyy').format(parsedEndDate); 'dd-MM-yyyy',
).format(parsedEndDate);
} }
}); });
} }
@ -392,8 +400,9 @@ class _ForexScreenState extends State<ForexScreen> {
textControllers["_cardNumber"] = initController("card_number"); textControllers["_cardNumber"] = initController("card_number");
textControllers["_card"] = initController("deposit_on_card"); textControllers["_card"] = initController("deposit_on_card");
textControllers["_cash"] = initController("deposit_on_cash"); textControllers["_cash"] = initController("deposit_on_cash");
textControllers["_deliveryLocation"] = textControllers["_deliveryLocation"] = initController(
initController("delivery_location"); "delivery_location",
);
textControllers["_comments"] = initController("comments"); textControllers["_comments"] = initController("comments");
// Set dropdown values // Set dropdown values
@ -401,9 +410,11 @@ class _ForexScreenState extends State<ForexScreen> {
selectedCurrency = widget.selectedItem!["currency"] as String?; selectedCurrency = widget.selectedItem!["currency"] as String?;
selectedDuration = widget.selectedItem!["duration"] as String?; selectedDuration = widget.selectedItem!["duration"] as String?;
selectedCardPercent = int.tryParse( selectedCardPercent = int.tryParse(
widget.selectedItem!["card_percentage"]?.toString() ?? ""); widget.selectedItem!["card_percentage"]?.toString() ?? "",
);
selectedCashPercent = int.tryParse( selectedCashPercent = int.tryParse(
widget.selectedItem!["cash_percentage"]?.toString() ?? ""); widget.selectedItem!["cash_percentage"]?.toString() ?? "",
);
selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?; selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?;
isChecked = isChecked =
widget.selectedItem!["have_card"] == "1"; // Convert string to bool widget.selectedItem!["have_card"] == "1"; // Convert string to bool
@ -473,7 +484,8 @@ class _ForexScreenState extends State<ForexScreen> {
if (startDateString == null || endDateString == null) return; if (startDateString == null || endDateString == null) return;
print( print(
"Calculate 3 - StarrtDAte: $startDateString --EndDate: $endDateString"); "Calculate 3 - StarrtDAte: $startDateString --EndDate: $endDateString",
);
try { try {
// Parse the dates from string // Parse the dates from string
@ -548,7 +560,8 @@ class _ForexScreenState extends State<ForexScreen> {
if (quotedAmount != null) { if (quotedAmount != null) {
// fifteenPercent = (quotedAmount * 15) ~/ 100; // fifteenPercent = (quotedAmount * 15) ~/ 100;
print("selectedCardPercent - $selectedCashPercent"); print("selectedCardPercent - $selectedCashPercent");
fifteenPercent = (quotedAmount * selectedCashPercent!) ~/ fifteenPercent =
(quotedAmount * selectedCashPercent!) ~/
100; // Calculate 15% (integer division) 100; // Calculate 15% (integer division)
remainingAmount = quotedAmount - fifteenPercent; // Subtract from total remainingAmount = quotedAmount - fifteenPercent; // Subtract from total
@ -587,10 +600,12 @@ class _ForexScreenState extends State<ForexScreen> {
int checkValidAmount = cashAmount + enteredAmount; int checkValidAmount = cashAmount + enteredAmount;
print( print(
"checkValidAmount - $checkValidAmount - $enteredAmount - $cashAmount"); "checkValidAmount - $checkValidAmount - $enteredAmount - $cashAmount",
);
print( print(
"CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount"); "CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount",
);
if (enteredAmount == null || calculateAmnt > qouteAmount!) { if (enteredAmount == null || calculateAmnt > qouteAmount!) {
errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount"; errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount";
@ -619,19 +634,20 @@ class _ForexScreenState extends State<ForexScreen> {
int checkValidAmount = cardAmount! + enteredAmount; int checkValidAmount = cardAmount! + enteredAmount;
print( print(
"checkValidAmountCash - $checkValidAmount -cash- $enteredAmount -Card - $cardAmount - quotedAmount- $quotedAmount"); "checkValidAmountCash - $checkValidAmount -cash- $enteredAmount -Card - $cardAmount - quotedAmount- $quotedAmount",
);
print("Difference - $difference"); print("Difference - $difference");
print('CardAmount - $cardAmount'); print('CardAmount - $cardAmount');
textControllers["_card"]?.text = difference.toString(); textControllers["_card"]?.text = difference.toString();
// if (enteredAmount > fifteenPercent) { if (enteredAmount > fifteenPercent) {
// errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent"; errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent";
// } else if (checkValidAmount == quotedAmount) { } else if (checkValidAmount == quotedAmount) {
// errorMessages["deposit_on_card"] = " "; // Clear error if valid errorMessages["deposit_on_card"] = " "; // Clear error if valid
// } else { } else {
// errorMessages["deposit_on_cash"] = ""; // Clear error if valid errorMessages["deposit_on_cash"] = ""; // Clear error if valid
// } }
// Refresh UI if using StatefulWidget // Refresh UI if using StatefulWidget
setState(() {}); setState(() {});
@ -653,10 +669,12 @@ class _ForexScreenState extends State<ForexScreen> {
void _validateDates() { void _validateDates() {
print("VALiDATING DATES"); print("VALiDATING DATES");
DateTime? startDate = DateTime? startDate = _parseDate(
_parseDate(textControllers["_forexStartDate"]?.text ?? ""); textControllers["_forexStartDate"]?.text ?? "",
DateTime? endDate = );
_parseDate(textControllers["_forexEndDate"]?.text ?? ""); DateTime? endDate = _parseDate(
textControllers["_forexEndDate"]?.text ?? "",
);
if (startDate != null && endDate != null && endDate.isBefore(startDate)) { if (startDate != null && endDate != null && endDate.isBefore(startDate)) {
setState(() { setState(() {
@ -672,9 +690,11 @@ class _ForexScreenState extends State<ForexScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile; bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container( return Container(
// color: Color(0xFFF4F4FB), // color: Color(0xFFF4F4FB),
@ -689,13 +709,14 @@ class _ForexScreenState extends State<ForexScreen> {
child: Center( child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)), child: Column(children: _buildAccomadtionForm(isDesktop)),
), ),
) ),
], ],
), ),
), ),
), ),
); );
}); },
);
} }
List<Widget> _buildAccomadtionForm(bool isDesktop) { List<Widget> _buildAccomadtionForm(bool isDesktop) {
@ -714,9 +735,7 @@ class _ForexScreenState extends State<ForexScreen> {
return [ return [
...buildResponsiveRow(_buildFirstRow(isDesktop)), ...buildResponsiveRow(_buildFirstRow(isDesktop)),
SizedBox( SizedBox(height: 5),
height: 5,
),
// Align( // Align(
// alignment: Alignment.centerLeft, // alignment: Alignment.centerLeft,
// child: Text( // child: Text(
@ -743,9 +762,7 @@ class _ForexScreenState extends State<ForexScreen> {
// Divider( // Divider(
// thickness: 0.3, // thickness: 0.3,
// ), // ),
SizedBox( SizedBox(height: 10),
height: 10,
),
...buildResponsiveRow(_buildSecondRow(isDesktop)), ...buildResponsiveRow(_buildSecondRow(isDesktop)),
...buildResponsiveRow(_buildCardDetailsRow(isDesktop)), ...buildResponsiveRow(_buildCardDetailsRow(isDesktop)),
...buildResponsiveRow(_buildForexCard(isDesktop)), ...buildResponsiveRow(_buildForexCard(isDesktop)),
@ -769,8 +786,9 @@ class _ForexScreenState extends State<ForexScreen> {
initialDate = DateTime.parse(flightFirstTripDateNotifier.value!); initialDate = DateTime.parse(flightFirstTripDateNotifier.value!);
// textControllers["_forexStartDate"]?.text = // textControllers["_forexStartDate"]?.text =
// DateFormat('yyyy-MM-dd').format(initialDate); // DateFormat('yyyy-MM-dd').format(initialDate);
textControllers["_forexStartDate"]?.text = textControllers["_forexStartDate"]?.text = DateFormat(
DateFormat('dd-MM-yyyy').format(initialDate); 'dd-MM-yyyy',
).format(initialDate);
} catch (e) { } catch (e) {
initialDate = today; initialDate = today;
} }
@ -804,8 +822,9 @@ class _ForexScreenState extends State<ForexScreen> {
if (pickedDate != null && pickedDate != _selectedCheckOutDate) { if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() { setState(() {
_selectedCheckOutDate = pickedDate; _selectedCheckOutDate = pickedDate;
textControllers["_forexStartDate"]?.text = textControllers["_forexStartDate"]?.text = DateFormat(
DateFormat('dd-MM-yyyy').format(pickedDate); 'dd-MM-yyyy',
).format(pickedDate);
}); });
} }
} }
@ -854,8 +873,9 @@ class _ForexScreenState extends State<ForexScreen> {
if (pickedDate != null && pickedDate != _selectedEndDate) { if (pickedDate != null && pickedDate != _selectedEndDate) {
setState(() { setState(() {
_selectedEndDate = pickedDate; _selectedEndDate = pickedDate;
textControllers["_forexEndDate"]?.text = textControllers["_forexEndDate"]?.text = DateFormat(
DateFormat('dd-MM-yyyy').format(pickedDate); 'dd-MM-yyyy',
).format(pickedDate);
// textControllers["_forexEndDate"]?.text = // textControllers["_forexEndDate"]?.text =
// DateFormat('dd-MM-yyyy').format(initialDate); // DateFormat('dd-MM-yyyy').format(initialDate);
}); });
@ -871,7 +891,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -888,10 +909,12 @@ class _ForexScreenState extends State<ForexScreen> {
await _selectCheckOutDate(context); await _selectCheckOutDate(context);
if (textControllers["_forexEndDate"]!.text.isNotEmpty) { if (textControllers["_forexEndDate"]!.text.isNotEmpty) {
DateTime? startDate = DateTime? startDate = _parseDate(
_parseDate(textControllers["_forexStartDate"]!.text); textControllers["_forexStartDate"]!.text,
DateTime? endDate = );
_parseDate(textControllers["_forexEndDate"]!.text); DateTime? endDate = _parseDate(
textControllers["_forexEndDate"]!.text,
);
if (startDate != null && if (startDate != null &&
endDate != null && endDate != null &&
@ -914,13 +937,18 @@ class _ForexScreenState extends State<ForexScreen> {
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "Select Date", labelText: "Select Date",
labelStyle: labelStyle: const TextStyle(
const TextStyle(fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16), contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(Icons.calendar_today, suffixIcon: const Icon(
size: 16, color: Colors.grey), Icons.calendar_today,
size: 16,
color: Colors.grey,
),
), ),
), ),
), ),
@ -936,12 +964,7 @@ class _ForexScreenState extends State<ForexScreen> {
], ],
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -950,7 +973,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -964,10 +988,12 @@ class _ForexScreenState extends State<ForexScreen> {
await _selectForexEndDate(context); await _selectForexEndDate(context);
if (textControllers["_forexEndDate"]!.text.isNotEmpty) { if (textControllers["_forexEndDate"]!.text.isNotEmpty) {
DateTime? startDate = DateTime? startDate = _parseDate(
_parseDate(textControllers["_forexStartDate"]!.text); textControllers["_forexStartDate"]!.text,
DateTime? endDate = );
_parseDate(textControllers["_forexEndDate"]!.text); DateTime? endDate = _parseDate(
textControllers["_forexEndDate"]!.text,
);
if (startDate != null && if (startDate != null &&
endDate != null && endDate != null &&
@ -995,8 +1021,11 @@ class _ForexScreenState extends State<ForexScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today, suffixIcon: Icon(
size: 16, color: Colors.grey), Icons.calendar_today,
size: 16,
color: Colors.grey,
),
), ),
), ),
), ),
@ -1013,12 +1042,7 @@ class _ForexScreenState extends State<ForexScreen> {
], ],
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -1027,7 +1051,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -1045,7 +1070,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
// decoration: const InputDecoration( // decoration: const InputDecoration(
// labelText: "To", // labelText: "To",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
@ -1071,7 +1097,7 @@ class _ForexScreenState extends State<ForexScreen> {
// Map country codes to country names // Map country codes to country names
countryMap = { countryMap = {
for (var item in countryList) for (var item in countryList)
item['country_code'] as String: item['country_name'] as String item['country_code'] as String: item['country_name'] as String,
}; };
// Extract only country codes for processing // Extract only country codes for processing
@ -1101,7 +1127,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -1124,12 +1151,11 @@ class _ForexScreenState extends State<ForexScreen> {
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( contentPadding: EdgeInsets.symmetric(horizontal: 1),
horizontal: 1,
), ),
), ),
), dropdownBuilder:
dropdownBuilder: (context, selectedItem) => Align( (context, selectedItem) => Align(
// Center-align selected item // Center-align selected item
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
@ -1140,7 +1166,8 @@ class _ForexScreenState extends State<ForexScreen> {
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() { setState(() {
// Find the country_code based on selected country_name // Find the country_code based on selected country_name
selectedCountry = countryMap.entries selectedCountry =
countryMap.entries
.firstWhere((entry) => entry.value == newValue) .firstWhere((entry) => entry.value == newValue)
.key; .key;
_onCountryChanged(selectedCountry); _onCountryChanged(selectedCountry);
@ -1164,13 +1191,9 @@ class _ForexScreenState extends State<ForexScreen> {
// height: 8, // height: 8,
// ), // ),
if (isDesktop) if (isDesktop)
SizedBox( SizedBox(width: MediaQuery.of(context).size.width * 0.048)
width: MediaQuery.of(context).size.width * 0.048,
)
else else
SizedBox( SizedBox(height: 8),
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@ -1179,7 +1202,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -1202,7 +1226,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
), ),
), ),
@ -1211,12 +1236,7 @@ class _ForexScreenState extends State<ForexScreen> {
], ],
), ),
// if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,), // if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -1225,7 +1245,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -1243,7 +1264,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
// decoration: const InputDecoration( // decoration: const InputDecoration(
// labelText: "To", // labelText: "To",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
@ -1279,7 +1301,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
Container( Container(
@ -1298,8 +1321,9 @@ class _ForexScreenState extends State<ForexScreen> {
}, },
keyboardType: TextInputType.numberWithOptions(decimal: true), keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.allow(RegExp( FilteringTextInputFormatter.allow(
r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal RegExp(r'^\d*\.?\d*$'),
), // Allow only positive numbers with optional decimal
], ],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
@ -1310,7 +1334,7 @@ class _ForexScreenState extends State<ForexScreen> {
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
), ),
) ),
// CustomTextFieldItnerarySubWrapper( // CustomTextFieldItnerarySubWrapper(
// width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null,
// isFocused: focusStates["_transport"] ?? false, // isFocused: focusStates["_transport"] ?? false,
@ -1339,14 +1363,7 @@ class _ForexScreenState extends State<ForexScreen> {
// ), // ),
], ],
), ),
if (isDesktop) if (isDesktop) SizedBox(width: 10) else SizedBox(height: 8),
SizedBox(
width: 10,
)
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -1355,7 +1372,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
Container( Container(
@ -1374,8 +1392,9 @@ class _ForexScreenState extends State<ForexScreen> {
}, },
keyboardType: TextInputType.numberWithOptions(decimal: true), keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.allow(RegExp( FilteringTextInputFormatter.allow(
r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal RegExp(r'^\d*\.?\d*$'),
), // Allow only positive numbers with optional decimal
], ],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
@ -1386,7 +1405,7 @@ class _ForexScreenState extends State<ForexScreen> {
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
), ),
) ),
// CustomTextFieldItnerarySubWrapper( // CustomTextFieldItnerarySubWrapper(
// width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null,
// isFocused: focusStates["_accomodation"] ?? false, // isFocused: focusStates["_accomodation"] ?? false,
@ -1415,14 +1434,7 @@ class _ForexScreenState extends State<ForexScreen> {
// ), // ),
], ],
), ),
if (isDesktop) if (isDesktop) SizedBox(width: 10) else SizedBox(height: 8),
SizedBox(
width: 10,
)
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -1431,14 +1443,14 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
Container( Container(
color: Colors.yellow.shade50, color: Colors.yellow.shade50,
padding: const EdgeInsets.only(left: 10), padding: const EdgeInsets.only(left: 10),
width: width: isDesktop ? MediaQuery.of(context).size.width * 0.06 : null,
isDesktop ? MediaQuery.of(context).size.width * 0.06 : null,
height: 30, height: 30,
child: TextField( child: TextField(
focusNode: focusNodes["_telephone"], focusNode: focusNodes["_telephone"],
@ -1451,8 +1463,9 @@ class _ForexScreenState extends State<ForexScreen> {
}, },
keyboardType: TextInputType.numberWithOptions(decimal: true), keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.allow(RegExp( FilteringTextInputFormatter.allow(
r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal RegExp(r'^\d*\.?\d*$'),
), // Allow only positive numbers with optional decimal
], ],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
@ -1462,7 +1475,8 @@ class _ForexScreenState extends State<ForexScreen> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
)) ),
),
// CustomTextFieldItnerarySubWrapper( // CustomTextFieldItnerarySubWrapper(
// width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.07 : null,
// isFocused: focusStates["_telephone"] ?? false, // isFocused: focusStates["_telephone"] ?? false,
@ -1491,14 +1505,7 @@ class _ForexScreenState extends State<ForexScreen> {
// ), // ),
], ],
), ),
if (isDesktop) if (isDesktop) SizedBox(width: 40) else SizedBox(height: 8),
SizedBox(
width: 40,
)
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -1507,7 +1514,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -1525,7 +1533,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
// decoration: const InputDecoration( // decoration: const InputDecoration(
// labelText: "To", // labelText: "To",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
@ -1545,19 +1554,24 @@ class _ForexScreenState extends State<ForexScreen> {
List<Widget> _buildCardDetailsRow(bool isDesktop) { List<Widget> _buildCardDetailsRow(bool isDesktop) {
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? []; List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems =
.map((item) => DropdownMenuItem<String>( purposeList
.map(
(item) => DropdownMenuItem<String>(
value: item['dropdown_value'], value: item['dropdown_value'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)) ),
)
.toList(); .toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", child: Text(
style: TextStyle(color: Colors.grey)), "No options available",
style: TextStyle(color: Colors.grey),
),
), ),
); );
} }
@ -1574,7 +1588,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -1590,7 +1605,8 @@ class _ForexScreenState extends State<ForexScreen> {
onChanged: (value) { onChanged: (value) {
// errorMessages["deposit_on_cash"] = ""; // errorMessages["deposit_on_cash"] = "";
_validateCashAmount( _validateCashAmount(
value); // Call validation when text changes value,
); // Call validation when text changes
}, },
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Cash", labelText: "Cash",
@ -1613,12 +1629,7 @@ class _ForexScreenState extends State<ForexScreen> {
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -1628,7 +1639,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -1643,7 +1655,8 @@ class _ForexScreenState extends State<ForexScreen> {
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
onChanged: (value) { onChanged: (value) {
_validateCardAmount( _validateCardAmount(
value); // Call validation when text changes value,
); // Call validation when text changes
}, },
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Card", labelText: "Card",
@ -1665,12 +1678,7 @@ class _ForexScreenState extends State<ForexScreen> {
], ],
), ),
if (isDesktop) if (isDesktop) Spacer() else SizedBox(height: 8),
Spacer()
else
SizedBox(
height: 8,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -1680,7 +1688,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -1698,7 +1707,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: const TextStyle( style: const TextStyle(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
), ),
), ),
@ -1826,7 +1836,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
@ -1853,9 +1864,7 @@ class _ForexScreenState extends State<ForexScreen> {
], ],
), ),
if (isDesktop) Spacer(), if (isDesktop) Spacer(),
SizedBox( SizedBox(height: 5),
height: 5,
),
// Actions row remains a Row // Actions row remains a Row
Column( Column(
children: [ children: [
@ -1879,7 +1888,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
@ -1919,16 +1929,17 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: focusStates["_deliveryLocation"] ?? isFocused:
focusStates["_deliveryLocation"] ??
false, // Dropdown doesn't use focus false, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width:
? MediaQuery.of(context).size.width * 0.45 isDesktop ? MediaQuery.of(context).size.width * 0.45 : null,
: null,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
child: TextField( child: TextField(
@ -1955,13 +1966,9 @@ class _ForexScreenState extends State<ForexScreen> {
], ],
), ),
if (isDesktop) if (isDesktop)
SizedBox( SizedBox(width: MediaQuery.of(context).size.width * 0.04)
width: MediaQuery.of(context).size.width * 0.04,
)
else else
SizedBox( SizedBox(height: 8),
height: 8,
),
Column( Column(
children: [ children: [
Row( Row(
@ -1997,7 +2004,8 @@ class _ForexScreenState extends State<ForexScreen> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
], ],
), ),
@ -2015,9 +2023,7 @@ class _ForexScreenState extends State<ForexScreen> {
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
child: Text( child: Text(
@ -2026,7 +2032,6 @@ class _ForexScreenState extends State<ForexScreen> {
), ),
), ),
SizedBox(width: 10), // Space between buttons SizedBox(width: 10), // Space between buttons
// Save Changes Button // Save Changes Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
@ -2034,9 +2039,7 @@ class _ForexScreenState extends State<ForexScreen> {
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Primary color for save backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
child: Text( child: Text(

View File

@ -42,7 +42,7 @@ class _PlaceholdersModalState extends State<PlaceholdersModal> {
content: Container( content: Container(
width: width:
isDesktop isDesktop
? MediaQuery.of(context).size.width * 0.3 ? MediaQuery.of(context).size.width * 0.4
: double.maxFinite, : double.maxFinite,
// Set max height so ListView knows constraints // Set max height so ListView knows constraints
height: 300, height: 300,

View File

@ -642,7 +642,7 @@ class TemplateState extends State<Template> {
), ),
Container( Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
height: 200, height: MediaQuery.of(context).size.height * 0.3,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
border: Border.all(color: Color(0xFFD6D5E6), width: 0.5), border: Border.all(color: Color(0xFFD6D5E6), width: 0.5),

View File

@ -0,0 +1,830 @@
import 'dart:convert';
import 'dart:io' as io show Directory, File;
import 'package:delta_to_html/delta_to_html.dart';
import 'package:flutter/cupertino.dart' as dom;
import 'package:flutter_quill/flutter_quill.dart' hide Text;
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart' hide Text;
import 'package:flutter_quill/quill_delta.dart';
import 'package:flutter_quill/quill_delta.dart' as quill;
import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:frontend/Screens/myTemplates/templateForex.dart'
as _editorFocusNode;
import 'package:frontend/Screens/myTemplates/templateForex.dart'
as _editorScrollController;
import 'package:frontend/Screens/myTemplates/templateForex.dart' as _controller;
import 'package:html2md/html2md.dart' as html2md;
import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart';
import 'package:flutter_quill/flutter_quill.dart' as quill;
import 'package:html/parser.dart' show parse;
import 'package:html/dom.dart' as dom hide Element, Text;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_quill/flutter_quill_internal.dart';
import 'package:flutter_quill/quill_delta.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as path;
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_user_travel.dart';
import 'dialog_placeholders.dart';
class TemplateForex extends StatefulWidget {
final Map<String, dynamic>? templateData;
const TemplateForex({super.key, required this.templateData});
static TemplateForex fromState(GoRouterState state) {
return TemplateForex(templateData: state.extra as Map<String, dynamic>?);
}
@override
TemplateForexState createState() => TemplateForexState();
}
class TemplateForexState extends State<TemplateForex> {
final ApiService apiService = ApiService();
// final QuillController _controller = QuillController.basic();
String? orgId;
String? userId;
Color layoutColor = Colors.redAccent;
Color bodyColor = Colors.white;
final Map<String, TextEditingController> controllers = {};
List<String> dataHeader = ["subject"];
List<String> placeholders = [];
late QuillController _controller = QuillController.basic();
final FocusNode _focusNode = FocusNode();
late int templateId = 0;
late String templateName = "";
late List<Map<String, dynamic>> placeholderList = [];
Map<String, dynamic> get TemplateData {
final data = {
"org_id": orgId,
// "template_id": templateId,
// "template_name": controllers["templateName"]?.text,
"template_id": templateId,
"template_name": templateName,
"subject": controllers["subject"]?.text,
"body_html": DeltaToHTML.encodeJson(
_controller.document.toDelta().toJson(),
),
// "body_html": jsonEncode(_controller.document.toDelta().toJson()),
// "body_html": _controller,
// "body_html": convertQuillDocToHtml(_controller.document),
// convert delta to HTML
"placeholder": jsonEncode(placeholderList),
// "created_by": userId
};
print('start 123');
print(jsonEncode(_controller.document.toDelta().toJson()));
// print(jsonEncode(_controller.document));
// Only add group_id if it's an edit operation
// if (widget.templateData != null && widget.templateData!.containsKey('group_id')) {
// data["template_id"] = templateData;
// }
return data;
}
@override
void initState() {
super.initState();
for (var field in dataHeader) {
controllers[field] = TextEditingController();
}
updateData();
loadinitializeData();
loadInitialData();
}
@override
// void dispose() {
// // controllers.dispose();
// // _editorScrollController.dispose();
// _editorFocusNode.dispose();
// super.dispose();
// }
void loadinitializeData() async {
orgId = await getOrgId();
userId = await getUserId();
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
String? bodyStringColor = await getBodyColor();
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
bodyColor =
bodyStringColor != null
? Color(int.parse(bodyStringColor))
: Colors.white;
});
}
String convertQuillDocToHtml(quill.Document doc) {
final buffer = StringBuffer();
print("convertQuillDocToHtml");
for (final op in doc.toDelta().toList()) {
final insert = op.data;
final attrs = op.attributes ?? {};
if (insert is String) {
var content = insert;
// Handle formatting (bold, italic, etc.)
if (attrs.containsKey('bold')) {
content = '<strong>$content</strong>';
}
if (attrs.containsKey('italic')) {
content = '<em>$content</em>';
}
// Wrap each paragraph with <p>
if (content.trim().isNotEmpty) {
buffer.write('<p>${content.trim()}</p>');
}
}
}
return buffer.toString();
}
String convertQuillDocToHtml2(quill.Document doc) {
final buffer = StringBuffer();
final lines = <String>[];
final delta = doc.toDelta();
String applyStyles(String text, Map<String, dynamic>? attrs) {
if (attrs == null) return text;
if (attrs.containsKey('bold')) {
text = '<strong>$text</strong>';
}
if (attrs.containsKey('italic')) {
text = '<em>$text</em>';
}
return text;
}
for (final op in delta.toList()) {
final insert = op.data;
final attrs = op.attributes;
if (insert is String) {
final parts = insert.split('\n');
for (int i = 0; i < parts.length; i++) {
final part = applyStyles(parts[i], attrs);
lines.add(part);
if (i < parts.length - 1) {
// End of line: wrap accumulated content into <p>
final joined = lines.join('');
if (joined.trim().isNotEmpty) {
buffer.writeln('<p>${joined.trim()}</p>');
}
lines.clear();
}
}
}
}
// Add remaining lines
final joined = lines.join('');
if (joined.trim().isNotEmpty) {
buffer.writeln('<p>${joined.trim()}</p>');
}
return buffer.toString();
}
String extractPlainTextFromHtml(String html) {
final regex = RegExp(r'<p>(.*?)<\/p>', multiLine: true, dotAll: true);
final matches = regex.allMatches(html);
final buffer = StringBuffer();
for (final match in matches) {
final text = match.group(1)?.replaceAll(RegExp(r'<[^>]*>'), '') ?? '';
buffer.writeln(text.trim());
}
return buffer.toString();
}
String decodeHtmlEntities(String text) {
return text
.replaceAll('&nbsp;', ' ')
.replaceAll('&amp;', '&')
.replaceAll('&lt;', '<')
.replaceAll('&gt;', '>')
.replaceAll('&quot;', '"')
.replaceAll('&#39;', "'"); // add more as needed
}
// quill.Document convertSimpleHtmlToQuill(String htmlString) {
// final delta = quill.Delta();
// final doc = html_parser.parse(htmlString);
// final body = doc.body;
//
// void walk(Node node) {
// if (node is Text) {
// delta.insert(node.text);
// } else if (node is Element) {
// switch (node.localName) {
// case 'p':
// node.nodes.forEach(walk);
// delta.insert('\n');
// break;
// case 'br':
// delta.insert('\n');
// break;
// case 'strong':
// case 'b':
// delta.insert(node.text, {'bold': true});
// break;
// case 'em':
// case 'i':
// delta.insert(node.text, {'italic': true});
// break;
// case 'a':
// delta.insert(node.text, {'link': node.attributes['href']});
// break;
// default:
// node.nodes.forEach(walk);
// }
// }
// }
//
// if (body != null) {
// walk(body);
// }
//
// return quill.Document.fromDelta(delta..insert('\n'));
// }
String formatTemplateName(String input) {
return input
.split('_') // split by underscore
.map(
(word) =>
word.isNotEmpty
? '${word[0].toUpperCase()}${word.substring(1)}'
: '',
)
.join(' ');
}
Future<void> updateData() async {
// Ensure apiselectedUser is not null before printing
if (widget.templateData != null) {
print("API Selected User Has Data - ${widget.templateData}");
print(
"API Selected User Has - ${widget.templateData?["templateData"]?["subject"]}",
);
setState(() {
// Wrap in setState to update the UI
controllers["templateName"]?.text =
widget.templateData?["templateData"]?["template_name"] ?? "";
controllers["subject"]?.text =
widget.templateData?["templateData"]?["subject"] ?? "";
final bodyHtml =
widget.templateData?["templateData"]?["body_html"] ?? "";
print("bodyHtml - $bodyHtml");
String html = widget.templateData?["templateData"]?["body_html"] ?? "";
final htmlToDelta = HtmlToDelta();
// Convert the HTML string to Quill Delta format
// This is where the magic happens, but also where complex HTML might be simplified
final quill.Delta initialDelta = htmlToDelta.convert(html);
// Create a Quill Document from the Delta
final quill.Document quillDoc = quill.Document.fromDelta(initialDelta);
// Initialize the QuillController with the converted document
_controller = quill.QuillController(
document: quillDoc,
selection: const TextSelection.collapsed(
offset: 0,
), // Cursor at the start
);
// final plainText = extractPlainTextFromHtml(bodyHtml);
// final decodedText = decodeHtmlEntities(plainText);
// final quillDoc = quill.Document()..insert(0, decodedText);
// _controller = quill.QuillController(
// document: quillDoc,
// selection: const TextSelection.collapsed(offset: 0),
// );
templateName =
widget.templateData?["templateData"]?["template_name"] ?? "";
print("Fetched template_name: $templateName");
final rawPlaceholder =
widget.templateData?["templateData"]?["placeholder"];
if (rawPlaceholder is String) {
// If it's a JSON string, decode it first
placeholderList = List<Map<String, dynamic>>.from(
jsonDecode(rawPlaceholder),
);
} else if (rawPlaceholder is List) {
// If it's already a list (ideal case)
placeholderList = List<Map<String, dynamic>>.from(rawPlaceholder);
}
print("Extracted placeholders: $placeholders");
print("Fetched placeholders: $placeholderList");
templateId =
int.tryParse(
widget.templateData?["templateData"]?["template_id"]
?.toString() ??
'0',
) ??
0;
print("Fetched template_id: $templateId");
// if (widget.group?["international_policy_id"] != null) {
// selectedInternational =
// widget.group!["international_policy_id"].toString();
// }
});
} else {
print("API Selected User Has Data - No data available yet");
}
}
Future<void> handleSubmit() async {
Map<String, dynamic> data = TemplateData;
print('TemplateData - $data');
// final String deltaJsonString = TemplateData?["body_html"] ?? "[]";
//
// print('deltaJsonString $deltaJsonString');
//
// // Decode the JSON string into a list
// final List<dynamic> deltaJson = jsonDecode(deltaJsonString);
//
// print(DeltaToHTML.encodeJson(deltaJson));
//
// // Then convert it to a Quill document
// final doc = Document.fromJson(List<Map<String, dynamic>>.from(deltaJson));
//
// // Set it to the controller
// _controller = QuillController(
// document: doc,
// selection: const TextSelection.collapsed(offset: 0),
// );
// print('doc JSON: ${jsonEncode(doc.toDelta().toJson())}');
// print('doc plain text: ${doc.toPlainText()}');
// print('doc $doc');
setState(() {
updateTemplateData(data);
// This triggers UI rebuild with error messages
// if (validateData()) {
// postGroupData();
// }
});
}
Future<void> updateTemplateData(policyData) async {
final String apiUrldata = '$apiUrl/api/template/update/${templateId}';
final token = await getToken(); // Fetch token
if (token == null) {
throw Exception('Token not found. Please log in.');
}
try {
final response = await http.put(
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}");
context.go('/templateList');
} else {
print("Failed to submit policyData. Status: ${response.statusCode}");
print("Error: ${response.body}");
}
} catch (e) {
print(" Error submitting policyData: $e");
}
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
backgroundColor: const Color(0xFFf5f5f5),
appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false),
body: Padding(
padding:
isDesktop
? EdgeInsets.symmetric(
horizontal:
MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical:
MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding
)
: EdgeInsets.all(0),
child: Row(
children: [
// if (isDesktop) CustomDrawer(isDesktop: true),
Expanded(
child: buildUserTable(
isDesktop,
context,
bodyColor,
layoutColor,
),
),
],
),
),
);
},
);
}
Widget buildUserTable(
bool isDesktop,
context,
Color? bodyColor,
Color layoutColor,
) {
return Container(
margin: isDesktop ? const EdgeInsets.only(top: 5.0, bottom: 10.0) : null,
padding: const EdgeInsets.all(28),
decoration: BoxDecoration(
color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
),
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
formatTemplateName(templateName),
style: GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.w400,
color: Colors.black,
),
),
// SizedBox(height: 10),
// buildTempalteSubject(isDesktop),
//
// SizedBox(height: 10),
buildTempalteBody(isDesktop),
Spacer(),
buildActions(isDesktop),
],
),
),
);
}
Widget buildTempalteSubject(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Subject",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: false,
color: Colors.white,
width: isDesktop ? MediaQuery.of(context).size.width * 0.8 : null,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: controllers["subject"],
onChanged: (value) {
// _clearError("local_id_num");
},
decoration: InputDecoration(
labelText: "Enter the subject",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget buildTempalteBody(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text(
// "Content",
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74),
// ),
// ),
const SizedBox(height: 10),
Container(
color: Color(0xFFFFFEF0),
width: isDesktop ? MediaQuery.of(context).size.width * 0.8 : null,
child: IconTheme(
data: IconThemeData(size: 18), // Set icon size here
child: QuillSimpleToolbar(
controller: _controller,
config: QuillSimpleToolbarConfig(
embedButtons: FlutterQuillEmbeds.toolbarButtons(),
showClipboardPaste: true,
customButtons: [
QuillToolbarCustomButtonOptions(
icon: const Icon(Icons.add_alarm_rounded),
onPressed: () {
_controller.document.insert(
_controller.selection.extentOffset,
TimeStampEmbed(DateTime.now().toString()),
);
_controller.updateSelection(
TextSelection.collapsed(
offset: _controller.selection.extentOffset + 1,
),
ChangeSource.local,
);
},
),
],
buttonOptions: QuillSimpleToolbarButtonOptions(
base: QuillToolbarBaseButtonOptions(
afterButtonPressed: () {
final isDesktop = {
TargetPlatform.linux,
TargetPlatform.windows,
TargetPlatform.macOS,
}.contains(defaultTargetPlatform);
// if (isDesktop) {
// _editorFocusNode.requestFocus();
// }
},
),
linkStyle: QuillToolbarLinkStyleButtonOptions(
validateLink: (link) {
// Treats all links as valid. When launching the URL,
// `https://` is prefixed if the link is incomplete (e.g., `google.com` `https://google.com`)
// however this happens only within the editor.
return true;
},
),
),
),
),
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () async {
final selected = await showDialog(
context: context,
builder:
(context) =>
PlaceholdersModal(placeholders: placeholderList),
);
if (selected != null) {
print("User selected placeholder: $selected");
// You can now insert into a controller or editor
// Ensure the editor is focused
FocusScope.of(context).requestFocus(_focusNode);
final selection = _controller.selection;
final position = selection.baseOffset;
// if (position >= 0) {
// final intPosition = position.toInt();
//
// _controller.document.insert(intPosition, selected);
//
// _controller.updateSelection(
// TextSelection.collapsed(
// offset: intPosition + selected.length,
// ),
// ChangeSource.local,
// );
// }
}
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.grey,
// padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
'Placeholders',
style: GoogleFonts.poppins(fontSize: 13, color: Colors.black),
),
),
],
),
),
Container(
padding: const EdgeInsets.all(16),
height: MediaQuery.of(context).size.height * 0.5,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Color(0xFFD6D5E6), width: 0.5),
),
child: QuillEditor(
controller: _controller,
scrollController: ScrollController(),
focusNode: _focusNode,
config: QuillEditorConfig(
placeholder: 'Start writing your notes...',
padding: const EdgeInsets.all(16),
embedBuilders: [
...FlutterQuillEmbeds.editorBuilders(
imageEmbedConfig: QuillEditorImageEmbedConfig(
imageProviderBuilder: (context, imageUrl) {
// https://pub.dev/packages/flutter_quill_extensions#-image-assets
if (imageUrl.startsWith('assets/')) {
return AssetImage(imageUrl);
}
return null;
},
),
videoEmbedConfig: QuillEditorVideoEmbedConfig(
customVideoBuilder: (videoUrl, readOnly) {
// To load YouTube videos https://github.com/singerdmx/flutter-quill/releases/tag/v10.8.0
return null;
},
),
),
TimeStampEmbedBuilder(),
],
),
),
),
],
);
}
Widget buildActions(bool isDesktop) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
child: ElevatedButton(
onPressed: () {
context.go('/OrganizationSettings');
// You can get text from commentController.text
Navigator.of(context).pop(); // Close the modal
},
style: ElevatedButton.styleFrom(
foregroundColor: layoutColor,
// backgroundColor: widget.layoutColor,
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: layoutColor, width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
'Cancel',
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
color: layoutColor,
),
),
),
),
SizedBox(width: 10),
SizedBox(
child: ElevatedButton(
onPressed: () {
handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
},
style: ElevatedButton.styleFrom(
backgroundColor: layoutColor,
// backgroundColor: widget.layoutColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
'Save',
style: GoogleFonts.poppins(fontSize: 14, color: Colors.white),
),
),
),
],
);
}
}
@override
void dispose() {
_controller.dispose();
_editorScrollController.dispose();
_editorFocusNode.dispose();
}
class TimeStampEmbed extends Embeddable {
const TimeStampEmbed(String value) : super(timeStampType, value);
static const String timeStampType = 'timeStamp';
static TimeStampEmbed fromDocument(Document document) =>
TimeStampEmbed(jsonEncode(document.toDelta().toJson()));
Document get document => Document.fromJson(jsonDecode(data));
}
class TimeStampEmbedBuilder extends EmbedBuilder {
@override
String get key => 'timeStamp';
@override
String toPlainText(Embed node) {
return node.value.data;
}
@override
Widget build(BuildContext context, EmbedContext embedContext) {
return Row(
children: [
const Icon(Icons.access_time_rounded),
Text(embedContext.node.value.data as String),
],
);
}
}

View File

@ -10,6 +10,7 @@ import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/apiUrl.dart'; import '../../config/apiUrl.dart';
import '../../routes/custom_appBar.dart'; import '../../routes/custom_appBar.dart';
@ -66,11 +67,13 @@ class _OrgSetUpState extends State<OrgSetUp> {
String? bodyStringColor = await getBodyColor(); String? bodyStringColor = await getBodyColor();
setState(() { setState(() {
layoutColor = layoutString != null layoutColor =
layoutString != null
? Color(int.parse(layoutString)) ? Color(int.parse(layoutString))
: Colors.redAccent; : Colors.redAccent;
bodyColor = bodyStringColor != null bodyColor =
bodyStringColor != null
? Color(int.parse(bodyStringColor)) ? Color(int.parse(bodyStringColor))
: Colors.white; : Colors.white;
}); });
@ -99,7 +102,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
"created_by": null, "created_by": null,
"updated_by": null, "updated_by": null,
"is_active": 1 "is_active": 1,
// "org_id": orgId, // "org_id": orgId,
// "created_by": userId, // "created_by": userId,
@ -144,10 +147,14 @@ class _OrgSetUpState extends State<OrgSetUp> {
try { try {
print("getUpdatedServices"); print("getUpdatedServices");
final result = await apiService.fetchOrganization(); final prefs = await SharedPreferences.getInstance();
print("UUPdatedServices - $result"); final String? orgDataString = prefs.getString('org_data');
if (orgDataString != null) {
final Map<String, dynamic> orgData = jsonDecode(orgDataString);
print("UUPdatedServices - $orgData");
setState(() { setState(() {
selectedOrg = result; selectedOrg = orgData;
String? rawLogoPath = selectedOrg?['logo']; String? rawLogoPath = selectedOrg?['logo'];
if (rawLogoPath != null && rawLogoPath.contains('/assets')) { if (rawLogoPath != null && rawLogoPath.contains('/assets')) {
@ -158,16 +165,27 @@ class _OrgSetUpState extends State<OrgSetUp> {
_orgNameController.text = selectedOrg?['name']; _orgNameController.text = selectedOrg?['name'];
layoutColor = selectedOrg?['layout_color'] != null layoutColor =
? Color(int.parse( selectedOrg?['layout_color'] != null
selectedOrg!['layout_color'].toString().replaceFirst('0x', ''), ? Color(
radix: 16)) int.parse(
selectedOrg!['layout_color'].toString().replaceFirst(
'0x',
'',
),
radix: 16,
),
)
: Colors.white; : Colors.white;
bodyColor = selectedOrg?['color'] != null bodyColor =
? Color(int.parse( selectedOrg?['color'] != null
? Color(
int.parse(
selectedOrg!['color'].toString().replaceFirst('0x', ''), selectedOrg!['color'].toString().replaceFirst('0x', ''),
radix: 16)) radix: 16,
),
)
: Colors.blue; : Colors.blue;
// Set mail config fields // Set mail config fields
@ -200,12 +218,11 @@ class _OrgSetUpState extends State<OrgSetUp> {
services = []; services = [];
} }
selectedServiceIds = services.map<Map<String, dynamic>>((item) { selectedServiceIds =
services.map<Map<String, dynamic>>((item) {
// force cast or copy to a regular map // force cast or copy to a regular map
final map = Map<String, dynamic>.from(item); final map = Map<String, dynamic>.from(item);
return { return {"service_id": map['service_id'].toString()};
"service_id": map['service_id'].toString(),
};
}).toList(); }).toList();
}); });
@ -213,6 +230,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
print("selectedOrg - $selectedOrg"); print("selectedOrg - $selectedOrg");
print("mailConfig - $mailConfig"); print("mailConfig - $mailConfig");
}
// final result = await apiService.fetchOrganization();
} catch (e) { } catch (e) {
print('Error fetching updatedServices list: $e'); print('Error fetching updatedServices list: $e');
} }
@ -222,10 +242,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
errorMessages.clear(); // Reset errors errorMessages.clear(); // Reset errors
// Required fields that must not be empty // Required fields that must not be empty
List<String> requiredFields = [ List<String> requiredFields = ["name", "description"];
"name",
"description",
];
// Check validation for each field // Check validation for each field
for (String field in requiredFields) { for (String field in requiredFields) {
@ -298,7 +315,26 @@ class _OrgSetUpState extends State<OrgSetUp> {
if (response.statusCode == 200 || response.statusCode == 201) { if (response.statusCode == 200 || response.statusCode == 201) {
print("✅ User submitted successfully!"); print("✅ User submitted successfully!");
print("📨 Response: ${response.body}"); print("📨 Response Organizt Update: ${response.body}");
final data = json.decode(response.body);
if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception(
"Invalid response format: 'data' field is missing or not a Map",
);
}
// Make sure each item is a Map<String, dynamic>
final Map<String, dynamic> orgList = Map<String, dynamic>.from(
data['data'],
);
print(orgList);
await updateOrgDataWithNewValues(orgList);
print("📨 Response Organizt Update:");
// return orgList;
context.go('/listPlan'); context.go('/listPlan');
} else { } else {
print("❌ Submission failed. Status: ${response.statusCode}"); print("❌ Submission failed. Status: ${response.statusCode}");
@ -309,6 +345,27 @@ class _OrgSetUpState extends State<OrgSetUp> {
} }
} }
Future<void> updateOrgDataWithNewValues(Map<String, dynamic> newData) async {
final prefs = await SharedPreferences.getInstance();
final String? orgDataString = prefs.getString('org_data');
Map<String, dynamic> orgData = {};
if (orgDataString != null) {
try {
orgData = jsonDecode(orgDataString);
} catch (e) {
print('❌ Failed to decode org_data: $e');
}
}
// Merge in the new data
orgData.addAll(newData);
// Save back
await prefs.setString('org_data', jsonEncode(orgData));
print("✅ Updated org_data saved.");
}
void handleSubmit() { void handleSubmit() {
print("HandleSubmiy - $orgData"); print("HandleSubmiy - $orgData");
createOrgData(orgData); createOrgData(orgData);
@ -327,8 +384,10 @@ class _OrgSetUpState extends State<OrgSetUp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; builder: (context, sizingInfo) {
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold( return Scaffold(
// backgroundColor: Colors.white, // backgroundColor: Colors.white,
@ -336,23 +395,27 @@ class _OrgSetUpState extends State<OrgSetUp> {
appBar: CustomAppBar(isDesktop: isDesktop), appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false), drawer: CustomDrawer(isDesktop: false),
body: Padding( body: Padding(
padding: isDesktop padding:
isDesktop
? EdgeInsets.symmetric( ? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * horizontal:
MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding 0.1, // 30% of screen width as horizontal padding
vertical: MediaQuery.of(context).size.height * vertical:
MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding 0, // 5% of screen height as vertical padding
) )
: EdgeInsets.all(0), : EdgeInsets.all(0),
child: Row( child: Row(
children: [ children: [
// if (isDesktop) CustomDrawer(isDesktop: true), // if (isDesktop) CustomDrawer(isDesktop: true),
Expanded(child: buildOrganizationLayout(isDesktop)) Expanded(child: buildOrganizationLayout(isDesktop)),
], ],
), ),
), ),
); );
}); },
);
} }
Widget buildOrganizationLayout(isDesktop) { Widget buildOrganizationLayout(isDesktop) {
@ -382,18 +445,26 @@ class _OrgSetUpState extends State<OrgSetUp> {
Container( Container(
padding: const EdgeInsets.all(5), padding: const EdgeInsets.all(5),
color: Colors.white, color: Colors.white,
child: isDesktop child:
isDesktop
? Row( ? Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
// children: [Text("Button")], // children: [Text("Button")],
children: children: _buildSubmit(
_buildSubmit(isDesktop, isViewMode, layoutColor), isDesktop,
isViewMode,
layoutColor,
),
) )
: Row( : Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: children: _buildSubmit(
_buildSubmit(isDesktop, isViewMode, layoutColor), isDesktop,
)) isViewMode,
layoutColor,
),
),
),
], ],
), ),
); );
@ -402,8 +473,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
Widget buildOrgLayout(bool isDesktop) { Widget buildOrgLayout(bool isDesktop) {
Future<void> _pickImage() async { Future<void> _pickImage() async {
final picker = ImagePicker(); final picker = ImagePicker();
final XFile? pickedFile = final XFile? pickedFile = await picker.pickImage(
await picker.pickImage(source: ImageSource.gallery); source: ImageSource.gallery,
);
if (pickedFile != null && kIsWeb) { if (pickedFile != null && kIsWeb) {
try { try {
@ -424,7 +496,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
// margin: isDesktop // margin: isDesktop
// ? EdgeInsets.all(10.0) // ? EdgeInsets.all(10.0)
// : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), // : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
height: isDesktop height:
isDesktop
? MediaQuery.of(context).size.height * 0.98 ? MediaQuery.of(context).size.height * 0.98
: MediaQuery.of(context).size.height, : MediaQuery.of(context).size.height,
// decoration: BoxDecoration( // decoration: BoxDecoration(
@ -446,7 +519,11 @@ class _OrgSetUpState extends State<OrgSetUp> {
children: [ children: [
Container( Container(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 20, right: 20, bottom: 20, top: 5), left: 20,
right: 20,
bottom: 20,
top: 5,
),
// height: MediaQuery.of(context).size.height * 0.8, // height: MediaQuery.of(context).size.height * 0.8,
color: Colors.white, color: Colors.white,
child: Column( child: Column(
@ -463,7 +540,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
? "Update Organization" ? "Update Organization"
: "Create Organization", : "Create Organization",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 15, fontWeight: FontWeight.w500), fontSize: 15,
fontWeight: FontWeight.w500,
),
), ),
], ],
), ),
@ -472,7 +551,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
color: Colors.white, color: Colors.white,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center, // now -> .center , old -> .start crossAxisAlignment:
CrossAxisAlignment
.center, // now -> .center , old -> .start
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.only(top: 1.0), padding: const EdgeInsets.only(top: 1.0),
@ -481,7 +562,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF212121)), color: Color(0xFF212121),
),
), ),
), ),
SizedBox(width: 8), SizedBox(width: 8),
@ -496,7 +578,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Enter Organization Name", hintText: "Enter Organization Name",
hintStyle: GoogleFonts.poppins( hintStyle: GoogleFonts.poppins(
fontSize: 14, color: Colors.grey), fontSize: 14,
color: Colors.grey,
),
floatingLabelBehavior: floatingLabelBehavior:
FloatingLabelBehavior.never, FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
@ -510,7 +594,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
Spacer(), Spacer(),
GestureDetector( GestureDetector(
onTap: _pickImage, onTap: _pickImage,
child: _imageBytes != null child:
_imageBytes != null
? ClipOval( ? ClipOval(
child: Image.memory( child: Image.memory(
_imageBytes!, _imageBytes!,
@ -527,8 +612,11 @@ class _OrgSetUpState extends State<OrgSetUp> {
height: 75, // increased height: 75, // increased
fit: BoxFit.contain, fit: BoxFit.contain,
errorBuilder: errorBuilder: (
(context, error, stackTrace) { context,
error,
stackTrace,
) {
return const CircleAvatar( return const CircleAvatar(
radius: 20, radius: 20,
backgroundColor: Colors.redAccent, backgroundColor: Colors.redAccent,
@ -547,30 +635,33 @@ class _OrgSetUpState extends State<OrgSetUp> {
), ),
), ),
SizedBox( SizedBox(height: 10),
height: 10,
),
Text( Text(
"Services", "Services",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF212121)), color: Color(0xFF212121),
), ),
SizedBox(
height: 10,
), ),
SizedBox(height: 10),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Color(0xFFF4F4FB)), border: Border.all(color: Color(0xFFF4F4FB)),
borderRadius: BorderRadius.circular(1), borderRadius: BorderRadius.circular(1),
// color: bodyColor, // color: bodyColor,
// color: Color(0xFFF5F5F5), // color: Color(0xFFF5F5F5),
color: Colors.white), color: Colors.white,
padding: ),
EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 5), padding: EdgeInsets.only(
child: isDesktop left: 5,
right: 5,
top: 15,
bottom: 5,
),
child:
isDesktop
? Row( ? Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// mainAxisSize: MainAxisSize.min, // mainAxisSize: MainAxisSize.min,
@ -579,15 +670,11 @@ class _OrgSetUpState extends State<OrgSetUp> {
: Expanded( : Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Row( child: Row(children: _buildOptions()),
children: _buildOptions(),
), ),
), ),
), ),
), SizedBox(height: 15),
SizedBox(
height: 15,
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -597,7 +684,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF212121)), color: Color(0xFF212121),
),
), ),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -606,18 +694,26 @@ class _OrgSetUpState extends State<OrgSetUp> {
// color: Color(0xFFF4F4FB), // color: Color(0xFFF4F4FB),
), ),
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: 5, right: 5, top: 15, bottom: 5), left: 5,
child: layoutColor != null && bodyColor != null right: 5,
top: 15,
bottom: 5,
),
child:
layoutColor != null && bodyColor != null
? ColorThemePickerWidget( ? ColorThemePickerWidget(
initialLayoutColor: layoutColor, initialLayoutColor: layoutColor,
initialBodyColor: bodyColor, initialBodyColor: bodyColor,
onLayoutColorSelected: onLayoutColorSelected: (
(Color selectedLayoutColor) { Color selectedLayoutColor,
) {
setState(() { setState(() {
layoutColor = selectedLayoutColor; layoutColor = selectedLayoutColor;
}); });
}, },
onBodyColorSelected: (Color selectedBodyColor) { onBodyColorSelected: (
Color selectedBodyColor,
) {
setState(() { setState(() {
bodyColor = selectedBodyColor; bodyColor = selectedBodyColor;
}); });
@ -628,9 +724,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
Container( Container(
color: Colors.white, color: Colors.white,
child: Column( child: Column(
@ -643,7 +737,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF212121)), color: Color(0xFF212121),
),
), ),
// GestureDetector( // GestureDetector(
@ -660,11 +755,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
// ), // ),
], ],
), ),
// if (showMail)
SizedBox( // if (showMail)
height: 10, SizedBox(height: 10),
),
Container( Container(
// width: double.infinity, // width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -678,7 +771,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
// color: Color(0xFFF5F5F5), // color: Color(0xFFF5F5F5),
), ),
child: Row( child: Row(
mainAxisAlignment: isDesktop mainAxisAlignment:
isDesktop
? MainAxisAlignment.start ? MainAxisAlignment.start
: MainAxisAlignment.center, : MainAxisAlignment.center,
children: [ children: [
@ -688,17 +782,18 @@ class _OrgSetUpState extends State<OrgSetUp> {
initialMailData: mailConfig, initialMailData: mailConfig,
onMailDataChanged: (updatedData) { onMailDataChanged: (updatedData) {
// You can setState here or do something else with updatedData // You can setState here or do something else with updatedData
print( print("Updated Mail Data: $updatedData");
"Updated Mail Data: $updatedData");
mailConfig = updatedData; mailConfig = updatedData;
}, },
) )
: CircularProgressIndicator(), : CircularProgressIndicator(),
], ],
)) ),
),
], ],
)), ),
),
// isDesktop // isDesktop
// ? Row( // ? Row(
// mainAxisAlignment: MainAxisAlignment.end, // mainAxisAlignment: MainAxisAlignment.end,
@ -738,8 +833,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
String serviceId = service['service_id'].toString(); String serviceId = service['service_id'].toString();
// bool isSelected = selectedServiceIds.contains(serviceId); // bool isSelected = selectedServiceIds.contains(serviceId);
bool isSelected = bool isSelected = selectedServiceIds.any(
selectedServiceIds.any((item) => item["service_id"] == serviceId); (item) => item["service_id"] == serviceId,
);
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
@ -747,8 +843,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
String serviceId = service['service_id'].toString(); String serviceId = service['service_id'].toString();
// Check if already selected // Check if already selected
int existingIndex = selectedServiceIds int existingIndex = selectedServiceIds.indexWhere(
.indexWhere((item) => item["service_id"] == serviceId); (item) => item["service_id"] == serviceId,
);
if (existingIndex != -1) { if (existingIndex != -1) {
selectedServiceIds.removeAt(existingIndex); selectedServiceIds.removeAt(existingIndex);
@ -757,24 +854,30 @@ class _OrgSetUpState extends State<OrgSetUp> {
} }
}); });
}, },
child: Row(children: [ child: Row(
children: [
iconUrl.isNotEmpty iconUrl.isNotEmpty
? Image.network( ? Image.network(
iconUrl, iconUrl,
width: 18, width: 18,
height: 18, height: 18,
errorBuilder: (context, error, stackTrace) { errorBuilder: (context, error, stackTrace) {
return Icon(fallbackIcon, return Icon(
size: 18, fallbackIcon,
color: isSelected == name
? Color(0xFF114D8B)
: Color(0xFF475569));
},
)
: Icon(fallbackIcon,
size: 18, size: 18,
color: color:
isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569)), isSelected == name
? Color(0xFF114D8B)
: Color(0xFF475569),
);
},
)
: Icon(
fallbackIcon,
size: 18,
color:
isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569),
),
SizedBox(width: 2), SizedBox(width: 2),
@ -784,7 +887,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
fontSize: 12, fontSize: 12,
color: isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569), color: isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569),
fontWeight: fontWeight:
isSelected == name ? FontWeight.bold : FontWeight.w500), isSelected == name ? FontWeight.bold : FontWeight.w500,
),
// fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)), // fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)),
), ),
@ -796,15 +900,19 @@ class _OrgSetUpState extends State<OrgSetUp> {
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: Border.all(
color: isSelected ? Colors.green : Colors.grey, width: 1), color: isSelected ? Colors.green : Colors.grey,
width: 1,
),
), ),
child: Icon( child: Icon(
Icons.check_circle, Icons.check_circle,
size: 10, size: 10,
color: isSelected ? Colors.green : Colors.grey, color: isSelected ? Colors.green : Colors.grey,
// color: Colors.grey, // color: Colors.grey,
)), ),
]), ),
],
),
); );
} }
@ -848,13 +956,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
onPressed: () { onPressed: () {
context.go('/listPlan'); context.go('/listPlan');
}, },
child: Text( child: Text("Cancel", style: GoogleFonts.poppins(fontSize: 12)),
"Cancel",
style: GoogleFonts.poppins(fontSize: 12),
)),
SizedBox(
width: 20,
), ),
SizedBox(width: 20),
MouseRegion( MouseRegion(
// cursor: widget.isViewMode // cursor: widget.isViewMode
// ? SystemMouseCursors.forbidden // ? SystemMouseCursors.forbidden
@ -873,12 +977,9 @@ class _OrgSetUpState extends State<OrgSetUp> {
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: handleSubmit, // Disable when in view mode onPressed: handleSubmit, // Disable when in view mode
child: Text( child: Text("Submit", style: GoogleFonts.poppins(fontSize: 12)),
"Submit",
style: GoogleFonts.poppins(fontSize: 12),
), ),
), ),
)
]; ];
} }
} }

View File

@ -125,7 +125,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
"employeeCode", "employeeCode",
"dateOfIssue", "dateOfIssue",
"dateOfExpiry", "dateOfExpiry",
"changePassword" "changePassword",
]; ];
Color? layoutColor; Color? layoutColor;
@ -197,7 +197,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
// apiUserData = users; // apiUserData = users;
print("Total users fetched from API: ${users.length}"); print("Total users fetched from API: ${users.length}");
// user["role_id"] != "5" - Travel Agent
apiUserData = users.where((user) => user["role_id"] != "5").toList(); apiUserData = users.where((user) => user["role_id"] != "5").toList();
print("Total users fetched from API1: ${apiUserData?.length}"); print("Total users fetched from API1: ${apiUserData?.length}");
print("APIUSerDATa - $apiUserData"); print("APIUSerDATa - $apiUserData");
@ -206,7 +206,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
userMap = { userMap = {
for (var user in userList) for (var user in userList)
user['user_id'].toString(): user['user_id'].toString():
"${user['first_name']} ${user['last_name']}" "${user['first_name']} ${user['last_name']}",
}; };
userIdsApi = userMap.keys.toList(); userIdsApi = userMap.keys.toList();
}); });
@ -240,7 +240,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
groupMap = { groupMap = {
for (var group in groupList) for (var group in groupList)
group['group_id'].toString(): group['name'].toString().trim() group['group_id'].toString(): group['name'].toString().trim(),
}; };
userIdsApi = groupMap.keys.toList(); userIdsApi = groupMap.keys.toList();
@ -265,23 +265,12 @@ class _OfficeDetailsState extends State<OfficeDetails> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( SizedBox(height: 10),
height: 10,
),
_buildFirstRow(widget.isDesktop), _buildFirstRow(widget.isDesktop),
if (widget.isDesktop) if (widget.isDesktop) SizedBox(height: 10),
SizedBox(
height: 10,
),
_buildSecondRow(widget.isDesktop), _buildSecondRow(widget.isDesktop),
if (widget.isDesktop) if (widget.isDesktop) SizedBox(height: 10),
SizedBox( Divider(thickness: 0.2, color: Colors.blueGrey.shade100),
height: 10,
),
Divider(
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -294,9 +283,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
), ),
], ],
), ),
SizedBox( SizedBox(height: 15),
height: 15,
),
_buildThirdRow(widget.isDesktop), _buildThirdRow(widget.isDesktop),
], ],
), ),
@ -306,7 +293,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
Widget _buildFirstRow(bool isDesktop) { Widget _buildFirstRow(bool isDesktop) {
return Container( return Container(
color: Colors.white, color: Colors.white,
child: widget.isDesktop child:
widget.isDesktop
? Row( ? Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -333,7 +321,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
Widget _buildSecondRow(bool isDesktop) { Widget _buildSecondRow(bool isDesktop) {
return Container( return Container(
color: Colors.white, color: Colors.white,
child: widget.isDesktop child:
widget.isDesktop
? Row( ? Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -360,7 +349,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
Widget _buildThirdRow(bool isDesktop) { Widget _buildThirdRow(bool isDesktop) {
return Container( return Container(
color: Colors.white, color: Colors.white,
child: widget.isDesktop child:
widget.isDesktop
? Row( ? Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -369,10 +359,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
buildDelegationStartDateField(isDesktop), buildDelegationStartDateField(isDesktop),
Spacer(), // Space after Last Name Spacer(), // Space after Last Name
buildDelegationEndDateField(isDesktop), buildDelegationEndDateField(isDesktop),
SizedBox( SizedBox(width: 15),
width: 15, buildReset(isDesktop),
),
buildReset(isDesktop)
], ],
) )
: Column( : Column(
@ -383,7 +371,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
buildDelegationStartDateField(isDesktop), buildDelegationStartDateField(isDesktop),
SizedBox(height: 8), SizedBox(height: 8),
buildDelegationEndDateField(isDesktop), buildDelegationEndDateField(isDesktop),
buildReset(isDesktop) buildReset(isDesktop),
], ],
), ),
); );
@ -398,7 +386,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)) color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
@ -415,8 +404,10 @@ class _OfficeDetailsState extends State<OfficeDetails> {
}, },
decoration: InputDecoration( decoration: InputDecoration(
labelText: "Employee Code", labelText: "Employee Code",
labelStyle: labelStyle: GoogleFonts.poppins(
GoogleFonts.poppins(fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -444,7 +435,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)) color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
@ -458,10 +450,12 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: contentPadding: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 10), // Proper padding horizontal: 10,
), // Proper padding
), ),
onChanged: widget.isViewMode onChanged:
widget.isViewMode
? null ? null
: (newValue) { : (newValue) {
setState(() { setState(() {
@ -469,7 +463,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
}); });
widget.onDepartmentChanged?.call(newValue); widget.onDepartmentChanged?.call(newValue);
}, },
items: apiCostData?.map<DropdownMenuItem<String>>((item) { items:
apiCostData?.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem( return DropdownMenuItem(
value: item['department_id'], // ID as value value: item['department_id'], // ID as value
child: Text(item['name'] ?? "Unknown"), child: Text(item['name'] ?? "Unknown"),
@ -496,9 +491,11 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)) color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
// CustomTextFieldUserWrapper( // CustomTextFieldUserWrapper(
// isFocused: false, // isFocused: false,
// isDesktop: widget.isDesktop, // isDesktop: widget.isDesktop,
@ -542,13 +539,13 @@ class _OfficeDetailsState extends State<OfficeDetails> {
// ), // ),
// ), // ),
// ), // ),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
isFocused: false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: apiAllGroups == null child:
apiAllGroups == null
? Center( ? Center(
child: Transform.scale( child: Transform.scale(
scale: 0.5, scale: 0.5,
@ -566,41 +563,44 @@ class _OfficeDetailsState extends State<OfficeDetails> {
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search Group...", hintText: "Search Group...",
contentPadding: EdgeInsets.symmetric(horizontal: 10), contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), ),
), ),
), ),
),
// items: apiAllGroups!.map((group) { // items: apiAllGroups!.map((group) {
// return "${group['name']} "; // return "${group['name']} ";
// }).toList(), // }).toList(),
items:
items: apiAllGroups!.map((group) { apiAllGroups!.map((group) {
return group['name'].toString().trim(); // <-- trim spaces return group['name']
.toString()
.trim(); // <-- trim spaces
}).toList(), }).toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( contentPadding: EdgeInsets.symmetric(horizontal: 1),
horizontal: 1,
), ),
), ),
), dropdownBuilder:
dropdownBuilder: (context, selectedItem) => Align( (context, selectedItem) => Align(
// Center-align selected item // Center-align selected item
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
selectedItem ?? "Select", selectedItem ?? "Select",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(fontSize: 12),
fontSize: 12,
),
), ),
), ),
onChanged: (String? newValue) { onChanged: (String? newValue) {
if (newValue == null) return; if (newValue == null) return;
final levelId = groupMap.entries final levelId =
groupMap.entries
.firstWhere((entry) => entry.value == newValue) .firstWhere((entry) => entry.value == newValue)
.key; .key;
@ -608,8 +608,9 @@ class _OfficeDetailsState extends State<OfficeDetails> {
selectedLevel = levelId; selectedLevel = levelId;
}); });
widget.onLevelChanged widget.onLevelChanged?.call(
?.call(levelId); // pass the ID not the name levelId,
); // pass the ID not the name
}, },
), ),
), ),
@ -636,8 +637,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)) color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
@ -645,7 +646,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: apiUserData == null child:
apiUserData == null
? Center( ? Center(
child: Transform.scale( child: Transform.scale(
scale: 0.5, scale: 0.5,
@ -662,12 +664,14 @@ class _OfficeDetailsState extends State<OfficeDetails> {
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search User...", hintText: "Search User...",
contentPadding: contentPadding: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 10), horizontal: 10,
), ),
), ),
), ),
items: apiUserData!.map((user) { ),
items:
apiUserData!.map((user) {
return "${user['first_name']} ${user['last_name']}"; return "${user['first_name']} ${user['last_name']}";
}).toList(), }).toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
@ -678,7 +682,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
), ),
), ),
), ),
dropdownBuilder: (context, selectedItem) => Align( dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item // Center-align selected item
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
@ -688,6 +693,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
), ),
), ),
), ),
// onChanged: (String? newValue) { // onChanged: (String? newValue) {
// setState(() { // setState(() {
// selectedFirstApprover = userMap.entries // selectedFirstApprover = userMap.entries
@ -701,13 +707,14 @@ class _OfficeDetailsState extends State<OfficeDetails> {
// }); // });
// widget.onFirstApproverChanged?.call(newValue); // widget.onFirstApproverChanged?.call(newValue);
// }, // },
onChanged: (String? newValue) { onChanged: (String? newValue) {
if (newValue == null) return; if (newValue == null) return;
final approverId = userMap.entries final approverId =
userMap.entries
.firstWhere( .firstWhere(
(entry) => entry.value == newValue) (entry) => entry.value == newValue,
)
.key; .key;
setState(() { setState(() {
@ -715,7 +722,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
}); });
widget.onFirstApproverChanged?.call( widget.onFirstApproverChanged?.call(
approverId); // not newValue, but approverId approverId,
); // not newValue, but approverId
}, },
), ),
), ),
@ -749,8 +757,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)) color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
@ -758,7 +766,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: apiUserData == null child:
apiUserData == null
? Center( ? Center(
child: Transform.scale( child: Transform.scale(
scale: 0.5, scale: 0.5,
@ -775,12 +784,14 @@ class _OfficeDetailsState extends State<OfficeDetails> {
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search User...", hintText: "Search User...",
contentPadding: contentPadding: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 10), horizontal: 10,
), ),
), ),
), ),
items: apiUserData!.map((user) { ),
items:
apiUserData!.map((user) {
return "${user['first_name']} ${user['last_name']}"; return "${user['first_name']} ${user['last_name']}";
}).toList(), }).toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
@ -791,7 +802,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
), ),
), ),
), ),
dropdownBuilder: (context, selectedItem) => Align( dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item // Center-align selected item
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
@ -805,9 +817,11 @@ class _OfficeDetailsState extends State<OfficeDetails> {
onChanged: (String? newValue) { onChanged: (String? newValue) {
if (newValue == null) return; if (newValue == null) return;
final approverId = userMap.entries final approverId =
userMap.entries
.firstWhere( .firstWhere(
(entry) => entry.value == newValue) (entry) => entry.value == newValue,
)
.key; .key;
setState(() { setState(() {
@ -815,7 +829,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
}); });
widget.onSecondApproverChanged?.call( widget.onSecondApproverChanged?.call(
approverId); // not newValue, but approverId approverId,
); // not newValue, but approverId
}, },
// onChanged: (String? newValue) { // onChanged: (String? newValue) {
// setState(() { // setState(() {
@ -845,9 +860,9 @@ class _OfficeDetailsState extends State<OfficeDetails> {
Widget buildApprover3(bool isDesktop) { Widget buildApprover3(bool isDesktop) {
return Container( return Container(
color: Colors.white, color: Colors.white,
// child: Expanded( // child: Expanded(
// Allow second column to take available space // Allow second column to take available space
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -861,8 +876,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)) color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
@ -870,7 +885,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: apiUserData == null child:
apiUserData == null
? Center( ? Center(
child: Transform.scale( child: Transform.scale(
scale: 0.5, scale: 0.5,
@ -887,12 +903,14 @@ class _OfficeDetailsState extends State<OfficeDetails> {
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search User...", hintText: "Search User...",
contentPadding: contentPadding: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 10), horizontal: 10,
), ),
), ),
), ),
items: apiUserData!.map((user) { ),
items:
apiUserData!.map((user) {
return "${user['first_name']} ${user['last_name']}"; return "${user['first_name']} ${user['last_name']}";
}).toList(), }).toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
@ -903,7 +921,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
), ),
), ),
), ),
dropdownBuilder: (context, selectedItem) => Align( dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item // Center-align selected item
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
@ -916,9 +935,11 @@ class _OfficeDetailsState extends State<OfficeDetails> {
onChanged: (String? newValue) { onChanged: (String? newValue) {
if (newValue == null) return; if (newValue == null) return;
final approverId = userMap.entries final approverId =
userMap.entries
.firstWhere( .firstWhere(
(entry) => entry.value == newValue) (entry) => entry.value == newValue,
)
.key; .key;
setState(() { setState(() {
@ -926,7 +947,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
}); });
widget.onThirdApproverChanged?.call( widget.onThirdApproverChanged?.call(
approverId); // not newValue, but approverId approverId,
); // not newValue, but approverId
}, },
// onChanged: (String? newValue) { // onChanged: (String? newValue) {
// setState(() { // setState(() {
@ -972,8 +994,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)) color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
@ -981,7 +1003,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: apiUserData == null child:
apiUserData == null
? Center( ? Center(
child: Transform.scale( child: Transform.scale(
scale: 0.5, scale: 0.5,
@ -990,7 +1013,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
) )
: DropdownSearch<String>( : DropdownSearch<String>(
// selectedItem: userMap[selectedSubstituteApprover], // selectedItem: userMap[selectedSubstituteApprover],
selectedItem: selectedSubstituteApprover != null selectedItem:
selectedSubstituteApprover != null
? userMap[selectedSubstituteApprover] ? userMap[selectedSubstituteApprover]
: null, : null,
enabled: !widget.isViewMode, enabled: !widget.isViewMode,
@ -1001,12 +1025,14 @@ class _OfficeDetailsState extends State<OfficeDetails> {
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search User...", hintText: "Search User...",
contentPadding: contentPadding: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 10), horizontal: 10,
), ),
), ),
), ),
items: apiUserData!.map((user) { ),
items:
apiUserData!.map((user) {
return "${user['first_name']} ${user['last_name']}"; return "${user['first_name']} ${user['last_name']}";
}).toList(), }).toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
@ -1017,7 +1043,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
), ),
), ),
), ),
dropdownBuilder: (context, selectedItem) => Align( dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item // Center-align selected item
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
@ -1027,6 +1054,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
), ),
), ),
), ),
// onChanged: (String? newValue) { // onChanged: (String? newValue) {
// setState(() { // setState(() {
// selectedFirstApprover = userMap.entries // selectedFirstApprover = userMap.entries
@ -1040,13 +1068,14 @@ class _OfficeDetailsState extends State<OfficeDetails> {
// }); // });
// widget.onFirstApproverChanged?.call(newValue); // widget.onFirstApproverChanged?.call(newValue);
// }, // },
onChanged: (String? newValue) { onChanged: (String? newValue) {
if (newValue == null) return; if (newValue == null) return;
final approverId = userMap.entries final approverId =
userMap.entries
.firstWhere( .firstWhere(
(entry) => entry.value == newValue) (entry) => entry.value == newValue,
)
.key; .key;
setState(() { setState(() {
@ -1054,7 +1083,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
}); });
widget.onFirstSubsApproverChanged?.call( widget.onFirstSubsApproverChanged?.call(
approverId); // not newValue, but approverId approverId,
); // not newValue, but approverId
}, },
), ),
), ),
@ -1095,7 +1125,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedCheckOutDate != null && initialDate:
_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today) _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: today, : today,
@ -1106,8 +1137,9 @@ class _OfficeDetailsState extends State<OfficeDetails> {
if (pickedDate != null && pickedDate != _selectedCheckOutDate) { if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() { setState(() {
_selectedCheckOutDate = pickedDate; _selectedCheckOutDate = pickedDate;
widget.controllers["delegationStartDate"]?.text = widget.controllers["delegationStartDate"]?.text = DateFormat(
DateFormat('dd-MM-yyyy').format(pickedDate); 'dd-MM-yyyy',
).format(pickedDate);
if (_selectedEndDate != null && if (_selectedEndDate != null &&
_selectedEndDate!.isBefore(_selectedCheckOutDate!)) { _selectedEndDate!.isBefore(_selectedCheckOutDate!)) {
@ -1126,8 +1158,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)) color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
@ -1149,13 +1181,18 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "Select Date", labelText: "Select Date",
labelStyle: labelStyle: const TextStyle(
const TextStyle(fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16), contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(Icons.calendar_today, suffixIcon: const Icon(
size: 16, color: Colors.grey), Icons.calendar_today,
size: 16,
color: Colors.grey,
),
), ),
), ),
), ),
@ -1179,9 +1216,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
DateTime now = DateTime.now(); DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day); DateTime today = DateTime(now.year, now.month, now.day);
DateTime minDate = _selectedCheckOutDate != null DateTime minDate =
? _selectedCheckOutDate! _selectedCheckOutDate != null ? _selectedCheckOutDate! : today;
: today;
// Parse date from notifier if available, else use today // Parse date from notifier if available, else use today
DateTime initialDate; DateTime initialDate;
@ -1198,8 +1234,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedEndDate != null && initialDate:
_selectedEndDate!.isAfter(minDate) _selectedEndDate != null && _selectedEndDate!.isAfter(minDate)
? _selectedEndDate! ? _selectedEndDate!
: minDate, : minDate,
firstDate: minDate, firstDate: minDate,
@ -1209,8 +1245,9 @@ class _OfficeDetailsState extends State<OfficeDetails> {
if (pickedDate != null && pickedDate != _selectedEndDate) { if (pickedDate != null && pickedDate != _selectedEndDate) {
setState(() { setState(() {
_selectedEndDate = pickedDate; _selectedEndDate = pickedDate;
widget.controllers["delegationEndDate"]?.text = widget.controllers["delegationEndDate"]?.text = DateFormat(
DateFormat('dd-MM-yyyy').format(pickedDate); 'dd-MM-yyyy',
).format(pickedDate);
}); });
} }
} }
@ -1223,8 +1260,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)) color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldUserWrapper( CustomTextFieldUserWrapper(
@ -1247,13 +1284,18 @@ class _OfficeDetailsState extends State<OfficeDetails> {
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "Select Date", labelText: "Select Date",
labelStyle: labelStyle: const TextStyle(
const TextStyle(fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16), contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(Icons.calendar_today, suffixIcon: const Icon(
size: 16, color: Colors.grey), Icons.calendar_today,
size: 16,
color: Colors.grey,
),
), ),
), ),
), ),
@ -1279,7 +1321,10 @@ class _OfficeDetailsState extends State<OfficeDetails> {
Text( Text(
"", "",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.black,
),
), ),
SizedBox(height: 8), SizedBox(height: 8),
ElevatedButton( ElevatedButton(
@ -1296,10 +1341,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
onPressed: () { onPressed: () {
handleReset(); handleReset();
}, },
child: Text( child: Text("Reset", style: GoogleFonts.poppins(fontSize: 11)),
"Reset", ),
style: GoogleFonts.poppins(fontSize: 11),
))
], ],
); );
} }

View File

@ -313,6 +313,8 @@ class _UserListScreenState extends State<UserListScreen> {
(user['role_value']?.toLowerCase().contains(lowerQuery) ?? (user['role_value']?.toLowerCase().contains(lowerQuery) ??
false); false);
}).toList(); }).toList();
currentPage = 0;
}); });
print("filteredPlans: $filteredUsers"); print("filteredPlans: $filteredUsers");
} }

View File

@ -2,9 +2,10 @@ import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; // import 'package:flutter/rendering.dart';
import 'dart:html' as html; import 'dart:html' as html;
import 'package:frontend/config/apiUrl.dart'; // 1 newly added import 'package:frontend/config/apiUrl.dart'; // 1 newly added
import 'package:frontend/services/apiService.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
@ -23,13 +24,14 @@ class MyApp extends StatefulWidget {
} }
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
final ApiService apiService = ApiService();
String? _authCode; String? _authCode;
String? userRole; String? userRole;
bool _isAuthRedirect = false; bool _isAuthRedirect = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
SemanticsBinding.instance.ensureSemantics(); // Safe here // SemanticsBinding.instance.ensureSemantics(); // Safe here
if (kIsWeb) { if (kIsWeb) {
final uri = Uri.parse(html.window.location.href); final uri = Uri.parse(html.window.location.href);
if (uri.path == '/authredirection' && if (uri.path == '/authredirection' &&
@ -119,6 +121,7 @@ class _MyAppState extends State<MyApp> {
print("userData11 - ${userData['role']}"); print("userData11 - ${userData['role']}");
print("userData12 - $userRole"); print("userData12 - $userRole");
} }
apiService.getOrganizationData();
} catch (e) { } catch (e) {
print('Error decoding token: $e'); print('Error decoding token: $e');
} }

View File

@ -1,18 +1,13 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/config/apiUrl.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:shared_preferences/shared_preferences.dart'; // don't forget import 'package:shared_preferences/shared_preferences.dart'; // don't forget
import '../services/apiService.dart'; import '../services/apiService.dart';
import '../utils/auth_utils.dart'; import '../utils/auth_utils.dart';
enum TabSelection { enum TabSelection { dashboard, allTrips, myTrips, myApprovals, allMenu }
dashboard,
allTrips,
myTrips,
myApprovals,
allMenu,
}
class CustomAppBar extends StatefulWidget implements PreferredSizeWidget { class CustomAppBar extends StatefulWidget implements PreferredSizeWidget {
final bool isDesktop; final bool isDesktop;
@ -131,6 +126,64 @@ class _CustomAppBarState extends State<CustomAppBar> {
} }
Future<void> getOrganizationData() async { Future<void> getOrganizationData() async {
try {
print("getUpdatedServices");
final prefs = await SharedPreferences.getInstance();
final String? orgDataString = prefs.getString('org_data');
if (orgDataString != null) {
// final result = await apiService.fetchOrganization();
final Map<String, dynamic> result = jsonDecode(orgDataString);
print("UUPdatedServices - $result");
final prefs = await SharedPreferences.getInstance();
print("UUPdatedServices - $result");
setState(() {
selectedOrg = result;
layoutColor =
selectedOrg?['layout_color'] != null
? Color(int.parse(selectedOrg!['layout_color']))
: Colors.white;
bodyColor =
selectedOrg?['color'] != null
? Color(
int.parse(
selectedOrg!['color'].toString().replaceFirst('0x', ''),
radix: 16,
),
)
: Colors.blue;
String? rawLogoPath = selectedOrg?['logo'];
if (rawLogoPath != null && rawLogoPath.contains('/assets')) {
const baseUrl = apiUrl;
// 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",
);
}
} catch (e) {
print("Error : $e");
}
}
Future<void> getOrganizationData1() async {
try { try {
print("getUpdatedServices"); print("getUpdatedServices");
@ -142,14 +195,19 @@ class _CustomAppBarState extends State<CustomAppBar> {
setState(() { setState(() {
selectedOrg = result; selectedOrg = result;
layoutColor = selectedOrg?['layout_color'] != null layoutColor =
selectedOrg?['layout_color'] != null
? Color(int.parse(selectedOrg!['layout_color'])) ? Color(int.parse(selectedOrg!['layout_color']))
: Colors.white; : Colors.white;
bodyColor = selectedOrg?['color'] != null bodyColor =
? Color(int.parse( selectedOrg?['color'] != null
? Color(
int.parse(
selectedOrg!['color'].toString().replaceFirst('0x', ''), selectedOrg!['color'].toString().replaceFirst('0x', ''),
radix: 16)) radix: 16,
),
)
: Colors.blue; : Colors.blue;
String? rawLogoPath = selectedOrg?['logo']; String? rawLogoPath = selectedOrg?['logo'];
@ -166,22 +224,32 @@ class _CustomAppBarState extends State<CustomAppBar> {
await prefs.setString('body_color', selectedOrg?['plan_action']); 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",
);
} catch (e) { } catch (e) {
print("Error : $e"); print("Error : $e");
} }
} }
// void handleTabChange(TabSelection tab, String route) {
// final currentUri =
// GoRouterState.of(context).uri.toString(); // safer than `.location`
// print("currentUri - $currentUri");
//
// if (currentUri != route) {
// setState(() {
// selectedTab = tab;
// });
// context.go(route);
// }
// }
void handleTabChange(TabSelection tab, String route) { void handleTabChange(TabSelection tab, String route) {
final currentUri = final currentUri = GoRouterState.of(context).uri.toString();
GoRouterState.of(context).uri.toString(); // safer than `.location`
print("currentUri - $currentUri");
if (currentUri != route) { if (currentUri != route) {
setState(() { context.go(route); // 🔄 Let navigation happen
selectedTab = tab; // The tab selection will automatically be updated by didChangeDependencies
});
context.go(route);
} }
} }
@ -225,18 +293,21 @@ class _CustomAppBarState extends State<CustomAppBar> {
titleSpacing: 0, titleSpacing: 0,
title: !widget.isDesktop title:
!widget.isDesktop
? Text('') ? Text('')
: Padding( : Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.05), horizontal: MediaQuery.of(context).size.width * 0.05,
),
child: Row( child: Row(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
// padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 10),
child: selectedOrg?['logo'] != null // padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 10),
child:
selectedOrg?['logo'] != null
? SizedBox( ? SizedBox(
height: 60, height: 60,
child: ClipRect( child: ClipRect(
@ -253,7 +324,8 @@ class _CustomAppBarState extends State<CustomAppBar> {
); );
}, },
), ),
)) ),
)
: const CircleAvatar( : const CircleAvatar(
radius: 20, radius: 20,
// backgroundColor: Colors.white, // backgroundColor: Colors.white,
@ -264,9 +336,7 @@ class _CustomAppBarState extends State<CustomAppBar> {
), ),
), ),
), ),
SizedBox( SizedBox(width: MediaQuery.of(context).size.width * 0.18),
width: MediaQuery.of(context).size.width * 0.18,
),
Container( Container(
width: MediaQuery.of(context).size.width * 0.35, width: MediaQuery.of(context).size.width * 0.35,
child: Row( child: Row(
@ -277,7 +347,9 @@ class _CustomAppBarState extends State<CustomAppBar> {
buildNavItem( buildNavItem(
"Dashboard", "Dashboard",
() => handleTabChange( () => handleTabChange(
TabSelection.dashboard, '/StatusDashboard'), TabSelection.dashboard,
'/StatusDashboard',
),
layoutColor!, layoutColor!,
isSelected: selectedTab == TabSelection.dashboard, isSelected: selectedTab == TabSelection.dashboard,
icon: Icons.dashboard, icon: Icons.dashboard,
@ -291,7 +363,9 @@ class _CustomAppBarState extends State<CustomAppBar> {
buildNavItem( buildNavItem(
"All Trips", "All Trips",
() => handleTabChange( () => handleTabChange(
TabSelection.allTrips, '/listAllPlan'), TabSelection.allTrips,
'/listAllPlan',
),
layoutColor!, layoutColor!,
isSelected: selectedTab == TabSelection.allTrips, isSelected: selectedTab == TabSelection.allTrips,
icon: Icons.format_list_bulleted_rounded, icon: Icons.format_list_bulleted_rounded,
@ -303,7 +377,9 @@ class _CustomAppBarState extends State<CustomAppBar> {
buildNavItem( buildNavItem(
"Trips", "Trips",
() => handleTabChange( () => handleTabChange(
TabSelection.myTrips, '/listTravelAgentPlan'), TabSelection.myTrips,
'/listTravelAgentPlan',
),
layoutColor!, layoutColor!,
// () => context.go('/listTravelAgentPlan'), // () => context.go('/listTravelAgentPlan'),
isSelected: selectedTab == TabSelection.myTrips, isSelected: selectedTab == TabSelection.myTrips,
@ -314,7 +390,9 @@ class _CustomAppBarState extends State<CustomAppBar> {
buildNavItem( buildNavItem(
"My Trips", "My Trips",
() => handleTabChange( () => handleTabChange(
TabSelection.myTrips, '/listPlan'), TabSelection.myTrips,
'/listPlan',
),
layoutColor!, layoutColor!,
// () => context.go('/listPlan'), // () => context.go('/listPlan'),
isSelected: selectedTab == TabSelection.myTrips, isSelected: selectedTab == TabSelection.myTrips,
@ -326,10 +404,13 @@ class _CustomAppBarState extends State<CustomAppBar> {
"My Approvals", "My Approvals",
() => handleTabChange( () => handleTabChange(
TabSelection.myApprovals, '/ApprovalList'), TabSelection.myApprovals,
'/ApprovalList',
),
layoutColor!, layoutColor!,
// () => context.go('/ApprovalList'), // () => context.go('/ApprovalList'),
isSelected: selectedTab == TabSelection.myApprovals, isSelected:
selectedTab == TabSelection.myApprovals,
icon: Icons.verified_outlined, icon: Icons.verified_outlined,
), ),
], ],
@ -342,12 +423,14 @@ class _CustomAppBarState extends State<CustomAppBar> {
actions: [ actions: [
Padding( Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.05), horizontal: MediaQuery.of(context).size.width * 0.05,
),
child: Row( child: Row(
children: [ children: [
// if (userData?["role"] != "User") // if (userData?["role"] != "User")
Builder( Builder(
builder: (context) => PopupMenuButton<String>( builder:
(context) => PopupMenuButton<String>(
color: Colors.white, color: Colors.white,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
offset: const Offset(0, 50), // 👈 shift it 50 pixels down offset: const Offset(0, 50), // 👈 shift it 50 pixels down
@ -394,20 +477,23 @@ class _CustomAppBarState extends State<CustomAppBar> {
// itemBuilder: (BuildContext context) => // itemBuilder: (BuildContext context) =>
// menuItems.map(buildMenuItem).toList(), // menuItems.map(buildMenuItem).toList(),
itemBuilder: (BuildContext context) { itemBuilder: (BuildContext context) {
// final isUser = userData?["role"] == "User"; // final isUser = userData?["role"] == "User";
final role = userData?["role"]; final role = userData?["role"];
List<Map<String, dynamic>> filteredItems; List<Map<String, dynamic>> filteredItems;
if (role == "User") { if (role == "User") {
filteredItems = menuItems filteredItems =
.where((item) => menuItems
.where(
(item) =>
item['value'] == '/CreateUserDetails' || item['value'] == '/CreateUserDetails' ||
item['value'] == '/logout') item['value'] == '/logout',
)
.toList(); .toList();
} else if (role == "Travel Agent") { } else if (role == "Travel Agent") {
filteredItems = menuItems filteredItems =
menuItems
.where((item) => item['value'] == '/logout') .where((item) => item['value'] == '/logout')
.toList(); .toList();
} else { } else {
@ -497,12 +583,12 @@ final List<Map<String, dynamic>> menuItems = [
{ {
'value': '/OrganizationSettings', 'value': '/OrganizationSettings',
'icon': Icons.business, 'icon': Icons.business,
'label': 'Org Management' 'label': 'Org Management',
}, },
{ {
'value': '/listUser', 'value': '/listUser',
'icon': Icons.manage_accounts, 'icon': Icons.manage_accounts,
'label': 'User Management' 'label': 'User Management',
}, },
// {'value': '/group', 'icon': Icons.group, 'label': 'Group'}, // {'value': '/group', 'icon': Icons.group, 'label': 'Group'},
@ -518,7 +604,7 @@ final List<Map<String, dynamic>> menuItems = [
{ {
'value': '/CreateUserDetails', 'value': '/CreateUserDetails',
'icon': Icons.account_circle, 'icon': Icons.account_circle,
'label': 'My Profile' 'label': 'My Profile',
}, },
{'value': '/logout', 'icon': Icons.login_outlined, 'label': 'Logout'}, {'value': '/logout', 'icon': Icons.login_outlined, 'label': 'Logout'},
]; ];
@ -527,12 +613,16 @@ PopupMenuItem<String> buildMenuItem(Map<String, dynamic> item) {
return PopupMenuItem<String>( return PopupMenuItem<String>(
height: 40, // 👈 reduce PopupMenuItem height height: 40, // 👈 reduce PopupMenuItem height
value: item['value'], value: item['value'],
padding: padding: EdgeInsets.symmetric(
EdgeInsets.symmetric(horizontal: 12), // 👈 control left-right spacing horizontal: 12,
), // 👈 control left-right spacing
child: Row( child: Row(
children: [ children: [
Icon(item['icon'], Icon(
size: 18, color: Colors.black87), // 👈 smaller, cleaner icon item['icon'],
size: 18,
color: Colors.black87,
), // 👈 smaller, cleaner icon
SizedBox(width: 10), // 👈 small space between icon and text SizedBox(width: 10), // 👈 small space between icon and text
Text( Text(
item['label'], item['label'],
@ -547,8 +637,13 @@ PopupMenuItem<String> buildMenuItem(Map<String, dynamic> item) {
); );
} }
Widget buildNavItem(String label, VoidCallback onTap, Color? layoutColor, Widget buildNavItem(
{bool isSelected = true, IconData? icon}) { String label,
VoidCallback onTap,
Color? layoutColor, {
bool isSelected = true,
IconData? icon,
}) {
final effectiveColor = final effectiveColor =
isSelected ? (layoutColor ?? Colors.blue) : Colors.black; isSelected ? (layoutColor ?? Colors.blue) : Colors.black;
@ -587,7 +682,8 @@ Widget buildNavItem(String label, VoidCallback onTap, Color? layoutColor,
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut, curve: Curves.easeInOut,
height: 2, height: 2,
width: isSelected width:
isSelected
? 50 ? 50
: 0, // Animate width (make sure isSelected changes) : 0, // Animate width (make sure isSelected changes)
color: effectiveColor, color: effectiveColor,

View File

@ -84,7 +84,14 @@ class _CustomDrawerState extends State<CustomDrawer> {
try { try {
print("getUpdatedServices"); print("getUpdatedServices");
final result = await apiService.fetchOrganization(); final prefs = await SharedPreferences.getInstance();
final String? orgDataString = prefs.getString('org_data');
if (orgDataString != null) {
// final result = await apiService.fetchOrganization();
final Map<String, dynamic> result = jsonDecode(orgDataString);
print("UUPdatedServices - $result");
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
print("UUPdatedServices - $result"); print("UUPdatedServices - $result");
@ -92,14 +99,19 @@ class _CustomDrawerState extends State<CustomDrawer> {
setState(() { setState(() {
selectedOrg = result; selectedOrg = result;
layoutColor = selectedOrg?['layout_color'] != null layoutColor =
selectedOrg?['layout_color'] != null
? Color(int.parse(selectedOrg!['layout_color'])) ? Color(int.parse(selectedOrg!['layout_color']))
: Colors.white; : Colors.white;
bodyColor = selectedOrg?['color'] != null bodyColor =
? Color(int.parse( selectedOrg?['color'] != null
? Color(
int.parse(
selectedOrg!['color'].toString().replaceFirst('0x', ''), selectedOrg!['color'].toString().replaceFirst('0x', ''),
radix: 16)) radix: 16,
),
)
: Colors.blue; : Colors.blue;
String? rawLogoPath = selectedOrg?['logo']; String? rawLogoPath = selectedOrg?['logo'];
@ -116,7 +128,9 @@ class _CustomDrawerState extends State<CustomDrawer> {
await prefs.setString('body_color', selectedOrg?['plan_action']); 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",
);
}
} catch (e) { } catch (e) {
print("Error : $e"); print("Error : $e");
} }
@ -126,7 +140,6 @@ class _CustomDrawerState extends State<CustomDrawer> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget drawerContent = Container( Widget drawerContent = Container(
// color: Colors.white, // color: Colors.white,
child: Container( child: Container(
margin: const EdgeInsets.all(18), margin: const EdgeInsets.all(18),
child: Column( child: Column(
@ -169,29 +182,49 @@ class _CustomDrawerState extends State<CustomDrawer> {
], ],
), ),
), ),
// _buildDrawerItem(context, Icons.home_outlined, 'Home', '/home'), // _buildDrawerItem(context, Icons.home_outlined, 'Home', '/home'),
if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin")
_buildDrawerItem(
context,
Icons.dashboard,
'Dashboard',
'/StatusDashboard',
),
if (userData?["role"] == "Org Admin" || if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin") userData?["role"] == "Travel Admin")
_buildDrawerItem(context, Icons.dashboard, 'Dashboard', _buildDrawerItem(
'/StatusDashboard'), context,
Icons.insights_outlined,
if (userData?["role"] == "Org Admin" || 'All Trips',
userData?["role"] == "Travel Admin") '/listAllPlan',
_buildDrawerItem(context, Icons.insights_outlined, 'All Trips', ),
'/listAllPlan'),
if (userDetails["role"] == "Travel Agent") if (userDetails["role"] == "Travel Agent")
_buildDrawerItem(context, Icons.assessment_outlined, _buildDrawerItem(
'My Approvals', '/listTravelAgentPlan'), context,
Icons.assessment_outlined,
'My Approvals',
'/listTravelAgentPlan',
),
if (userDetails["role"] != "Travel Agent") if (userDetails["role"] != "Travel Agent")
_buildDrawerItem(context, Icons.request_page_outlined, 'My Trips', _buildDrawerItem(
'/listPlan'), context,
Icons.request_page_outlined,
'My Trips',
'/listPlan',
),
if (userDetails["role"] != "Travel Agent") if (userDetails["role"] != "Travel Agent")
_buildDrawerItem(context, Icons.assessment_outlined, _buildDrawerItem(
'My Approvals', '/ApprovalList'), context,
Icons.assessment_outlined,
'My Approvals',
'/ApprovalList',
),
SizedBox(height: MediaQuery.of(context).size.height * 0.5), SizedBox(height: MediaQuery.of(context).size.height * 0.5),
Container( Container(
@ -206,8 +239,10 @@ class _CustomDrawerState extends State<CustomDrawer> {
children: [ children: [
Text( Text(
"Powered by", "Powered by",
style: style: TextStyle(
TextStyle(fontSize: 11, color: Color(0xFF212121)), fontSize: 11,
color: Color(0xFF212121),
),
), ),
Image.asset( Image.asset(
'assets/images/login/logoNew.jpg', 'assets/images/login/logoNew.jpg',
@ -226,7 +261,8 @@ class _CustomDrawerState extends State<CustomDrawer> {
); );
return Drawer( return Drawer(
child: ListView(padding: EdgeInsets.zero, children: [drawerContent])); child: ListView(padding: EdgeInsets.zero, children: [drawerContent]),
);
// if (widget.isDesktop) { // if (widget.isDesktop) {
// // Sidebar for Desktop (always visible)** // // Sidebar for Desktop (always visible)**
@ -244,7 +280,11 @@ class _CustomDrawerState extends State<CustomDrawer> {
/// **Reusable Drawer Item** /// **Reusable Drawer Item**
Widget _buildDrawerItem( Widget _buildDrawerItem(
BuildContext context, IconData icon, String title, String route) { BuildContext context,
IconData icon,
String title,
String route,
) {
String selectedRoute = GoRouterState.of(context).uri.toString(); String selectedRoute = GoRouterState.of(context).uri.toString();
// return Container( // return Container(
@ -287,10 +327,7 @@ class _CustomDrawerState extends State<CustomDrawer> {
return Material( return Material(
color: selectedRoute == route ? bodyColor : Colors.transparent, color: selectedRoute == route ? bodyColor : Colors.transparent,
child: ListTile( child: ListTile(
leading: Icon( leading: Icon(icon, size: 20),
icon,
size: 20,
),
title: Text( title: Text(
title, title,
@ -305,8 +342,8 @@ class _CustomDrawerState extends State<CustomDrawer> {
// color: Color(0xFF475569), // color: Color(0xFF475569),
// fontFamily: "Archivo"), // fontFamily: "Archivo"),
), ),
// tileColor: selectedRoute == route ? Colors.blue.shade50 : null,
// tileColor: selectedRoute == route ? Colors.blue.shade50 : null,
onTap: () async { onTap: () async {
if (route == '/') { if (route == '/') {
// Handle logout separately // Handle logout separately
@ -316,7 +353,8 @@ class _CustomDrawerState extends State<CustomDrawer> {
} else { } else {
context.go(route); context.go(route);
} }
}), },
),
); );
} }
@ -338,11 +376,7 @@ class _CustomDrawerState extends State<CustomDrawer> {
child: ExpansionTile( child: ExpansionTile(
tilePadding: EdgeInsets.symmetric(horizontal: 16), tilePadding: EdgeInsets.symmetric(horizontal: 16),
// childrenPadding: EdgeInsets.only(left: 36), // childrenPadding: EdgeInsets.only(left: 36),
leading: Icon( leading: Icon(icon, size: 20, color: Color(0xFF475569)),
icon,
size: 20,
color: Color(0xFF475569),
),
title: Row( title: Row(
children: [ children: [
// You could manually build this instead of using `leading`, but it's simpler here // You could manually build this instead of using `leading`, but it's simpler here
@ -378,11 +412,7 @@ class _CustomDrawerState extends State<CustomDrawer> {
padding: const EdgeInsets.symmetric(horizontal: 44.0, vertical: 8.0), padding: const EdgeInsets.symmetric(horizontal: 44.0, vertical: 8.0),
child: Row( child: Row(
children: [ children: [
Icon( Icon(Icons.circle_rounded, color: Color(0xFF475569), size: 6),
Icons.circle_rounded,
color: Color(0xFF475569),
size: 6,
),
SizedBox(width: 8), SizedBox(width: 8),
Text( Text(
title, title,
@ -399,20 +429,22 @@ class _CustomDrawerState extends State<CustomDrawer> {
); );
} }
Widget _buildExpandableItem1(BuildContext context, IconData icon, Widget _buildExpandableItem1(
String title, List<Widget> children) { BuildContext context,
IconData icon,
String title,
List<Widget> children,
) {
return ExpansionTile( return ExpansionTile(
leading: Icon( leading: Icon(icon, size: 20),
icon,
size: 20,
),
title: Text( title: Text(
title, title,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF475569), color: Color(0xFF475569),
fontFamily: "Archivo"), fontFamily: "Archivo",
),
), ),
collapsedBackgroundColor: Colors.transparent, collapsedBackgroundColor: Colors.transparent,
shape: const Border(), // Removes top and bottom dividers shape: const Border(), // Removes top and bottom dividers
@ -423,20 +455,20 @@ class _CustomDrawerState extends State<CustomDrawer> {
} }
Widget _buildSubDrawerItem1( Widget _buildSubDrawerItem1(
BuildContext context, String title, String route) { BuildContext context,
String title,
String route,
) {
return ListTile( return ListTile(
leading: Icon( leading: Icon(Icons.circle_rounded, color: Color(0xFF475569), size: 8),
Icons.circle_rounded,
color: Color(0xFF475569),
size: 8,
),
title: Text( title: Text(
title, title,
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Color(0xFF475569), color: Color(0xFF475569),
fontFamily: "Archivo"), fontFamily: "Archivo",
),
), ),
onTap: () { onTap: () {
context.go(route); context.go(route);

View File

@ -16,6 +16,7 @@ import 'package:frontend/Screens/userManagement/create_user/create_user1.dart';
import 'package:frontend/Screens/userManagement/user_List.dart'; import 'package:frontend/Screens/userManagement/user_List.dart';
import 'package:frontend/routes/organizationSetting.dart'; import 'package:frontend/routes/organizationSetting.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../Screens/allTrips/list_all_plans.dart'; import '../Screens/allTrips/list_all_plans.dart';
import '../Screens/allTrips/travel_agent_list.dart'; import '../Screens/allTrips/travel_agent_list.dart';
@ -23,6 +24,7 @@ import '../Screens/approvals/approval_list.dart';
import '../Screens/group/group.dart'; import '../Screens/group/group.dart';
import '../Screens/group/groupList.dart'; import '../Screens/group/groupList.dart';
import '../Screens/myTemplates/template.dart'; import '../Screens/myTemplates/template.dart';
import '../Screens/myTemplates/templateForex.dart';
import '../Screens/myTemplates/templateTest.dart'; import '../Screens/myTemplates/templateTest.dart';
import '../Screens/userManagement/create_user/create_user.dart'; import '../Screens/userManagement/create_user/create_user.dart';
import '../Screens/department/department_list.dart'; import '../Screens/department/department_list.dart';
@ -30,52 +32,57 @@ import '../Screens/costCenter/costCenter_list.dart';
import '../Screens/dashboard/status_dashboard.dart'; import '../Screens/dashboard/status_dashboard.dart';
import '../Screens/hotels/hotels_list.dart'; import '../Screens/hotels/hotels_list.dart';
import '../Screens/traveller/travellerList.dart'; import '../Screens/traveller/travellerList.dart';
import 'mainLayout.dart';
final GoRouter router = GoRouter( final GoRouter router = GoRouter(
routes: [ routes: [
// Public routes without app bar
GoRoute(path: '/', builder: (context, state) => LoginPage()), GoRoute(path: '/', builder: (context, state) => LoginPage()),
// GoRoute(
// path: '/authredirection', // Routes that share the app bar and layout (nested routes)
// builder: (context, state) { ShellRoute(
// final code = state.uri.queryParameters['code']; builder: (context, state, child) {
// return MicrosoftPage(code: code); // Use ResponsiveBuilder here to detect isDesktop and pass to MainLayout
return child;
// return ResponsiveBuilder(
// builder: (context, sizingInfo) {
// bool isDesktop =
// sizingInfo.deviceScreenType == DeviceScreenType.desktop;
//
// return MainLayout(isDesktop: isDesktop, child: child);
// }, // },
// ), // );
},
routes: [
GoRoute(path: '/home', builder: (context, state) => HomePage()), GoRoute(path: '/home', builder: (context, state) => HomePage()),
GoRoute(path: '/listAllPlan', builder: (context, state) => ListAllPlans()), GoRoute(
path: '/listAllPlan',
builder: (context, state) => ListAllPlans(),
),
GoRoute( GoRoute(
path: '/listTravelAgentPlan', path: '/listTravelAgentPlan',
builder: (context, state) => TravelAgentListPlans(), builder: (context, state) => TravelAgentListPlans(),
), ),
GoRoute(path: '/listPlan', builder: (context, state) => ListPlans()), GoRoute(path: '/listPlan', builder: (context, state) => ListPlans()),
GoRoute(path: '/createPlan', builder: (context, state) => CreatePlan()), GoRoute(path: '/createPlan', builder: (context, state) => CreatePlan()),
GoRoute(path: '/allTrips/trips', builder: (context, state) => CreatePlan()), GoRoute(
GoRoute(path: '/approver/plans', builder: (context, state) => CreatePlan()), path: '/allTrips/trips',
GoRoute(path: '/listUser', builder: (context, state) => UserListScreen()), builder: (context, state) => CreatePlan(),
),
GoRoute(
path: '/approver/plans',
builder: (context, state) => CreatePlan(),
),
GoRoute(
path: '/listUser',
builder: (context, state) => UserListScreen(),
),
GoRoute( GoRoute(
path: '/CreateUserDetails', path: '/CreateUserDetails',
builder: (context, state) => CreateUserFormDetials(), builder: (context, state) => CreateUserFormDetials(),
// builder: (context, state) {
// final userParam = state.uri.queryParameters['user'];
//
// final isEditProfile =
// state.uri.queryParameters['isEditProfile'] == 'true';
// final isViewMode = state.uri.queryParameters['isViewMode'] == 'true';
//
// final user = userParam != null
// ? jsonDecode(Uri.decodeComponent(userParam))
// : null;
//
// return CreateUserForm(
// apiselectedUser: user,
// isEditProfile: isEditProfile,
// isViewMode: isViewMode,
// );
// }
), ),
GoRoute( GoRoute(
path: '/Policy', path: '/Policy',
// builder: (context, state) => Policy(),
pageBuilder: pageBuilder:
(context, state) => MaterialPage(child: Policy.fromState(state)), (context, state) => MaterialPage(child: Policy.fromState(state)),
), ),
@ -89,24 +96,38 @@ final GoRouter router = GoRouter(
builder: (context, state) => OrganizationSetting(), builder: (context, state) => OrganizationSetting(),
), ),
GoRoute(path: '/group', builder: (context, state) => GroupList()), GoRoute(path: '/group', builder: (context, state) => GroupList()),
GoRoute(path: '/getPerdiem', builder: (context, state) => ForexDataList()), GoRoute(
path: '/getPerdiem',
builder: (context, state) => ForexDataList(),
),
GoRoute( GoRoute(
path: '/templateList', path: '/templateList',
builder: (context, state) => TemplatesList(), builder: (context, state) => TemplatesList(),
), ),
// GoRoute(
// path: '/template',
// builder: (context, state) => MyHomePage(),
// ),
GoRoute( GoRoute(
path: '/template', path: '/template',
// builder: (context, state) => Template(),
pageBuilder: pageBuilder:
(context, state) => MaterialPage(child: Template.fromState(state)), (context, state) =>
MaterialPage(child: Template.fromState(state)),
),
GoRoute(
path: '/templateForex',
pageBuilder:
(context, state) =>
MaterialPage(child: TemplateForex.fromState(state)),
),
GoRoute(
path: '/approvallist',
builder: (context, state) => ApprovalList(),
),
GoRoute(
path: '/department',
builder: (context, state) => DepartmentList(),
),
GoRoute(
path: '/costcenter',
builder: (context, state) => CostCenterList(),
), ),
GoRoute(path: '/approvallist', builder: (context, state) => ApprovalList()),
GoRoute(path: '/department', builder: (context, state) => DepartmentList()),
GoRoute(path: '/costcenter', builder: (context, state) => CostCenterList()),
GoRoute(path: '/hotels', builder: (context, state) => HotelsDataList()), GoRoute(path: '/hotels', builder: (context, state) => HotelsDataList()),
GoRoute( GoRoute(
path: '/statusdashboard', path: '/statusdashboard',
@ -122,4 +143,102 @@ final GoRouter router = GoRouter(
(context, state) => MaterialPage(child: Group.fromState(state)), (context, state) => MaterialPage(child: Group.fromState(state)),
), ),
], ],
),
],
); );
// final GoRouter router = GoRouter(
// routes: [
// GoRoute(path: '/', builder: (context, state) => LoginPage()),
// // GoRoute(
// // path: '/authredirection',
// // builder: (context, state) {
// // final code = state.uri.queryParameters['code'];
// // return MicrosoftPage(code: code);
// // },
// // ),
// GoRoute(path: '/home', builder: (context, state) => HomePage()),
// GoRoute(path: '/listAllPlan', builder: (context, state) => ListAllPlans()),
// GoRoute(
// path: '/listTravelAgentPlan',
// builder: (context, state) => TravelAgentListPlans(),
// ),
// GoRoute(path: '/listPlan', builder: (context, state) => ListPlans()),
// GoRoute(path: '/createPlan', builder: (context, state) => CreatePlan()),
// GoRoute(path: '/allTrips/trips', builder: (context, state) => CreatePlan()),
// GoRoute(path: '/approver/plans', builder: (context, state) => CreatePlan()),
// GoRoute(path: '/listUser', builder: (context, state) => UserListScreen()),
// GoRoute(
// path: '/CreateUserDetails',
// builder: (context, state) => CreateUserFormDetials(),
// // builder: (context, state) {
// // final userParam = state.uri.queryParameters['user'];
// //
// // final isEditProfile =
// // state.uri.queryParameters['isEditProfile'] == 'true';
// // final isViewMode = state.uri.queryParameters['isViewMode'] == 'true';
// //
// // final user = userParam != null
// // ? jsonDecode(Uri.decodeComponent(userParam))
// // : null;
// //
// // return CreateUserForm(
// // apiselectedUser: user,
// // isEditProfile: isEditProfile,
// // isViewMode: isViewMode,
// // );
// // }
// ),
// GoRoute(
// path: '/Policy',
// // builder: (context, state) => Policy(),
// pageBuilder:
// (context, state) => MaterialPage(child: Policy.fromState(state)),
// ),
// GoRoute(path: '/PolicyList', builder: (context, state) => PolicyList()),
// GoRoute(
// path: '/OrganizationSetup',
// builder: (context, state) => OrgSetUp(),
// ),
// GoRoute(
// path: '/OrganizationSettings',
// builder: (context, state) => OrganizationSetting(),
// ),
// GoRoute(path: '/group', builder: (context, state) => GroupList()),
// GoRoute(path: '/getPerdiem', builder: (context, state) => ForexDataList()),
// GoRoute(
// path: '/templateList',
// builder: (context, state) => TemplatesList(),
// ),
// // GoRoute(
// // path: '/template',
// // builder: (context, state) => MyHomePage(),
// // ),
// GoRoute(
// path: '/template',
// // builder: (context, state) => Template(),
// pageBuilder:
// (context, state) => MaterialPage(child: Template.fromState(state)),
// ),
// GoRoute(
// path: '/templateForex',
// pageBuilder:
// (context, state) =>
// MaterialPage(child: TemplateForex.fromState(state)),
// ),
// GoRoute(path: '/approvallist', builder: (context, state) => ApprovalList()),
// GoRoute(path: '/department', builder: (context, state) => DepartmentList()),
// GoRoute(path: '/costcenter', builder: (context, state) => CostCenterList()),
// GoRoute(path: '/hotels', builder: (context, state) => HotelsDataList()),
// GoRoute(
// path: '/statusdashboard',
// builder: (context, state) => StatusDashboard(),
// ),
// GoRoute(path: '/traveller', builder: (context, state) => TravellerList()),
// GoRoute(
// path: '/CreateGroup',
// pageBuilder:
// (context, state) => MaterialPage(child: Group.fromState(state)),
// ),
// ],
// );

View File

@ -0,0 +1,33 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'custom_appBar.dart';
import 'custom_drawer.dart';
class MainLayout extends StatelessWidget {
final Widget child;
final bool isDesktop;
const MainLayout({required this.child, required this.isDesktop, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFf5f5f5),
appBar: CustomAppBar(isDesktop: isDesktop),
drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
body: Padding(
padding:
isDesktop
? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.1,
vertical: 0,
)
: EdgeInsets.zero,
child: child,
),
);
}
}

View File

@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import '../services/apiService.dart';
import 'custom_appBar.dart'; import 'custom_appBar.dart';
import 'custom_drawer.dart'; import 'custom_drawer.dart';
@ -15,6 +16,8 @@ class OrganizationSetting extends StatefulWidget {
} }
class OrganizationSettingState extends State<OrganizationSetting> { class OrganizationSettingState extends State<OrganizationSetting> {
final ApiService apiService = ApiService();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder( return ResponsiveBuilder(
@ -84,7 +87,7 @@ class OrganizationSettingState extends State<OrganizationSetting> {
'description': 'Create and Edit Perdiem Amount', 'description': 'Create and Edit Perdiem Amount',
}, },
{ {
'value': '/department', 'value': '/forexTexmplate',
'icon': Icons.group_add_outlined, 'icon': Icons.group_add_outlined,
'label': 'Forex Template', 'label': 'Forex Template',
'description': 'Create and Edit Template', 'description': 'Create and Edit Template',
@ -244,9 +247,21 @@ class OrganizationSettingState extends State<OrganizationSetting> {
child: Card( child: Card(
color: Colors.white, color: Colors.white,
child: InkWell( child: InkWell(
onTap: () { onTap: () async {
if (item['value'] as String ==
"/forexTexmplate") {
final data =
await apiService.getForexTemplate();
print("ForexId -- $data");
context.go(
'/templateForex',
extra: {'templateData': data},
);
} else {
final route = item['value'] as String; final route = item['value'] as String;
context.go(route); context.go(route);
}
}, },
child: Padding( child: Padding(
padding: EdgeInsets.all(12), padding: EdgeInsets.all(12),

View File

@ -5,11 +5,32 @@ import 'package:frontend/utils/auth_utils.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:universal_html/html.dart' as html; import 'package:universal_html/html.dart' as html;
import 'package:universal_html/js.dart'; import 'package:universal_html/js.dart';
import '../../config/apiUrl.dart'; import '../../config/apiUrl.dart';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
class ApiService { class ApiService {
Future<void> getOrganizationData() async {
try {
print("getUpdatedServices");
final result = await fetchOrganization();
print("UUPdatedServices - $result");
// Save to local storage
final prefs = await SharedPreferences.getInstance();
final jsonString = jsonEncode(result);
await prefs.setString('org_data', jsonString);
print("✅ Organization data saved to SharedPreferences.");
print("selectedOrg - $result");
} catch (e) {
print('Error fetching updatedServices list: $e');
}
}
Future<List<dynamic>> fetchCountryList() async { Future<List<dynamic>> fetchCountryList() async {
final String apiUrldata = '$apiUrl/api/getcountryMaster'; final String apiUrldata = '$apiUrl/api/getcountryMaster';
final token = await getToken(); final token = await getToken();
@ -33,7 +54,8 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
return data['data']; return data['data'];
@ -68,7 +90,8 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
return data['data']; return data['data'];
@ -136,7 +159,8 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
List<dynamic> plansJson = data['data']; // 'data' is a Map, not a List List<dynamic> plansJson = data['data']; // 'data' is a Map, not a List
@ -190,7 +214,8 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
List<dynamic> plansJson = data['data']; // 'data' is a Map, not a List List<dynamic> plansJson = data['data']; // 'data' is a Map, not a List
@ -239,7 +264,8 @@ class ApiService {
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! Map) { if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a Map"); "Invalid response format: 'data' field is missing or not a Map",
);
} }
Map<String, dynamic> plansJson = Map<String, dynamic> plansJson =
@ -277,7 +303,8 @@ class ApiService {
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
return data['data']; return data['data'];
} catch (e) { } catch (e) {
@ -313,7 +340,8 @@ class ApiService {
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
return data['data']; return data['data'];
} catch (e) { } catch (e) {
@ -349,7 +377,8 @@ class ApiService {
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
return data['data']; return data['data'];
} catch (e) { } catch (e) {
@ -385,7 +414,8 @@ class ApiService {
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
return data['data']; return data['data'];
} catch (e) { } catch (e) {
@ -419,7 +449,8 @@ class ApiService {
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! Map) { if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a Map"); "Invalid response format: 'data' field is missing or not a Map",
);
} }
Map<String, dynamic> plansJson = Map<String, dynamic> plansJson =
@ -460,7 +491,8 @@ class ApiService {
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! Map) { if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a Map"); "Invalid response format: 'data' field is missing or not a Map",
);
} }
// Make sure each item is a Map<String, dynamic> // Make sure each item is a Map<String, dynamic>
@ -527,33 +559,47 @@ class ApiService {
} }
} }
static Future<void> viewPlan(BuildContext context, String planId, static Future<void> viewPlan(
{bool isViewMode = false, bool isMyTrips = false}) async { BuildContext context,
String planId, {
bool isViewMode = false,
bool isMyTrips = false,
}) async {
try { try {
Map<String, dynamic> planData = await getViewPlanEdit(planId); Map<String, dynamic> planData = await getViewPlanEdit(planId);
print("ViewAAA - $planData"); print("ViewAAA - $planData");
context.go(isMyTrips ? '/createPlan' : '/allTrips/trips', context.go(
extra: {'planData': planData, 'isViewMode': isViewMode}); isMyTrips ? '/createPlan' : '/allTrips/trips',
extra: {'planData': planData, 'isViewMode': isViewMode},
);
} catch (e) { } catch (e) {
print("Error fetching plan: $e"); print("Error fetching plan: $e");
} }
} }
static Future<void> viewPlanForApprover(BuildContext context, String planId, static Future<void> viewPlanForApprover(
String? approverId, String? delegaterId, BuildContext context,
{bool isViewMode = false, bool isApprover = true}) async { String planId,
String? approverId,
String? delegaterId, {
bool isViewMode = false,
bool isApprover = true,
}) async {
try { try {
Map<String, dynamic> planData = await getViewPlanEdit(planId); Map<String, dynamic> planData = await getViewPlanEdit(planId);
print("ViewAAA - $planData"); print("ViewAAA - $planData");
context.replace('/approver/plans', extra: { context.replace(
'/approver/plans',
extra: {
'planData': planData, 'planData': planData,
'approverId': approverId, 'approverId': approverId,
'delegaterId': delegaterId, 'delegaterId': delegaterId,
'isViewMode': isViewMode, 'isViewMode': isViewMode,
'isApprover': isApprover, 'isApprover': isApprover,
}); },
);
} catch (e) { } catch (e) {
print("Error fetching plan: $e"); print("Error fetching plan: $e");
} }
@ -589,7 +635,8 @@ class ApiService {
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! Map) { if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a Map"); "Invalid response format: 'data' field is missing or not a Map",
);
} }
// Make sure each item is a Map<String, dynamic> // Make sure each item is a Map<String, dynamic>
@ -635,7 +682,8 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
return data['data']; return data['data'];
@ -671,7 +719,8 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
return data['data']; return data['data'];
@ -709,11 +758,12 @@ class ApiService {
// Create a blob from the response body // Create a blob from the response body
final blob = html.Blob([response.bodyBytes]); final blob = html.Blob([response.bodyBytes]);
// Generate a download URL for the blob // Generate a download URL for the blob
final url = html.Url.createObjectUrlFromBlob(blob); final url = html.Url.createObjectUrlFromBlob(blob);
// Create a link element to trigger the download // Create a link element to trigger the download
final anchor = html.AnchorElement(href: url) final anchor =
html.AnchorElement(href: url)
..setAttribute('download', 'trip_plan_$planId.pdf') ..setAttribute('download', 'trip_plan_$planId.pdf')
..click(); ..click();
@ -772,11 +822,12 @@ class ApiService {
// Create a blob from the response body // Create a blob from the response body
final blob = html.Blob([response.bodyBytes]); final blob = html.Blob([response.bodyBytes]);
// Generate a download URL for the blob // Generate a download URL for the blob
final url = html.Url.createObjectUrlFromBlob(blob); final url = html.Url.createObjectUrlFromBlob(blob);
// Create a link element to trigger the download // Create a link element to trigger the download
final anchor = html.AnchorElement(href: url) final anchor =
html.AnchorElement(href: url)
..setAttribute('download', 'Forex_$forexId.pdf') ..setAttribute('download', 'Forex_$forexId.pdf')
..click(); ..click();
@ -835,7 +886,8 @@ class ApiService {
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! Map) { if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a Map"); "Invalid response format: 'data' field is missing or not a Map",
);
} }
print('Single USer 1'); print('Single USer 1');
@ -880,7 +932,8 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
final List<dynamic> forexList = data['data']; final List<dynamic> forexList = data['data'];
@ -925,7 +978,8 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
final List<Map<String, dynamic>> listData = final List<Map<String, dynamic>> listData =
@ -984,7 +1038,62 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! Map) { if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a Map"); "Invalid response format: 'data' field is missing or not a Map",
);
}
return Map<String, dynamic>.from(data['data']);
} catch (e) {
throw Exception('Error parsing response: $e');
}
} else {
throw Exception('Failed to load department details');
}
}
Future<Map<String, dynamic>> getForexTemplate() async {
final String apiUrldata =
'$apiUrl/api/getForexTemplate?template_name=forex';
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('findout the result');
// print(data.runtimeType);
print(data);
// if (!data.containsKey('data') || data['data'] is! List) {
// throw Exception(
// "Invalid response format: 'data' field is missing or not a List");
// }
//
// final List<Map<String, dynamic>> listData =
// List<Map<String, dynamic>>.from(data['data']);
//
// if (listData.isEmpty) {
// throw Exception("No department found with ID $id");
// }
//
// return listData[0];
if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception(
"Invalid response format: 'data' field is missing or not a Map",
);
} }
return Map<String, dynamic>.from(data['data']); return Map<String, dynamic>.from(data['data']);
@ -997,7 +1106,9 @@ class ApiService {
} }
Future<bool> showCancelConfirmationDialog( Future<bool> showCancelConfirmationDialog(
BuildContext context, Color? layoutColor) async { BuildContext context,
Color? layoutColor,
) async {
return await showDialog<bool>( return await showDialog<bool>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -1006,12 +1117,16 @@ class ApiService {
title: Text( title: Text(
'Cancel Confirmation', 'Cancel Confirmation',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 18, fontWeight: FontWeight.w500), fontSize: 18,
fontWeight: FontWeight.w500,
),
), ),
content: Text( content: Text(
'Do you want to cancel?', 'Do you want to cancel?',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 14.5, fontWeight: FontWeight.w500), fontSize: 14.5,
fontWeight: FontWeight.w500,
),
), ),
actions: [ actions: [
ElevatedButton( ElevatedButton(
@ -1021,10 +1136,11 @@ class ApiService {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide( side: BorderSide(
color: layoutColor ?? Colors.grey, width: 2), color: layoutColor ?? Colors.grey,
width: 2,
), ),
padding: ),
EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: () { onPressed: () {
Navigator.of(context).pop(false); Navigator.of(context).pop(false);
@ -1032,7 +1148,8 @@ class ApiService {
child: Text( child: Text(
"Cancel", "Cancel",
style: GoogleFonts.poppins(fontSize: 12), style: GoogleFonts.poppins(fontSize: 12),
)), ),
),
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: layoutColor, // Keep original color backgroundColor: layoutColor, // Keep original color
@ -1043,16 +1160,17 @@ class ApiService {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide( side: BorderSide(
color: layoutColor ?? Colors.grey, width: 1), color: layoutColor ?? Colors.grey,
width: 1,
),
), ),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: () => Navigator.of(context) onPressed:
.pop(true), // Disable when in view mode () => Navigator.of(
child: Text( context,
"OK", ).pop(true), // Disable when in view mode
style: GoogleFonts.poppins(fontSize: 12), child: Text("OK", style: GoogleFonts.poppins(fontSize: 12)),
),
), ),
], ],
); );
@ -1087,7 +1205,8 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
final List<Map<String, dynamic>> listData = final List<Map<String, dynamic>> listData =
@ -1132,7 +1251,8 @@ class ApiService {
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a List"); "Invalid response format: 'data' field is missing or not a List",
);
} }
final List<Map<String, dynamic>> listData = final List<Map<String, dynamic>> listData =
@ -1174,7 +1294,8 @@ class ApiService {
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! Map) { if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception( throw Exception(
"Invalid response format: 'data' field is missing or not a Map"); "Invalid response format: 'data' field is missing or not a Map",
);
} }
print('Single USer 1'); print('Single USer 1');
@ -1194,7 +1315,6 @@ class ApiService {
Future<Map<String, dynamic>> getTravellerDetailsFind(int id) async { Future<Map<String, dynamic>> getTravellerDetailsFind(int id) async {
final String apiUrldata = '$apiUrl/api/travellers/find?traveller_id=$id'; final String apiUrldata = '$apiUrl/api/travellers/find?traveller_id=$id';
//c //c
final token = await getToken(); final token = await getToken();
@ -1215,7 +1335,9 @@ class ApiService {
final data = json.decode(response.body); final data = json.decode(response.body);
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception("Invalid response format: 'data' field is missing or not a List"); throw Exception(
"Invalid response format: 'data' field is missing or not a List",
);
} }
final List<Map<String, dynamic>> listData = final List<Map<String, dynamic>> listData =
@ -1233,5 +1355,4 @@ class ApiService {
throw Exception('Failed to load Hotel details'); throw Exception('Failed to load Hotel details');
} }
} }
} }