Forex CRUD- User Mnagement Delegationt

This commit is contained in:
venbaittech 2025-05-21 11:25:31 +05:30
parent 4559eb7262
commit c560963e50
9 changed files with 1024 additions and 260 deletions

View File

@ -1,33 +1,266 @@
import 'dart:convert';
import 'package:dropdown_search/dropdown_search.dart'; import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import '../../config/apiUrl.dart';
import '../../services/apiService.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_forex.dart'; import '../../widgets/custom_text_forex.dart';
import 'forex_list.dart';
class ForexData extends StatefulWidget { class ForexData extends StatefulWidget {
final Future<List<dynamic>> Function() fetchGetForex;
final bool isDesktop; final bool isDesktop;
final Color? layoutColor; final Color? layoutColor;
const ForexData({super.key, required this.isDesktop, this.layoutColor});
final int? forexId; // <-- Add this
final Map<String, dynamic>? forexData;
const ForexData(
{super.key,
required this.isDesktop,
this.layoutColor,
required this.fetchGetForex,
this.forexId,
this.forexData});
@override @override
ForexDataState createState() => ForexDataState(); ForexDataState createState() => ForexDataState();
} }
class ForexDataState extends State<ForexData> { class ForexDataState extends State<ForexData> {
final ApiService apiService = ApiService();
Map<String, String> countryMap = {};
late List<dynamic>? apiCountryData;
late List<dynamic>? apiAirlineCountryData;
Map<String, dynamic>? apiData;
final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {};
List<dynamic> countryList = []; List<dynamic> countryList = [];
String? selectedCountry; String? selectedCountry;
String? selectedCountryName;
String? selectedCurrency; String? selectedCurrency;
String? selectedDuration; String? selectedDuration;
String? selectedPerdiemAmount; String? selectedPerdiemAmount;
String? userId;
int? forexDataId;
late String isActive = "1";
List<String> dataHeader = [
"country_code",
"country",
"currency",
"perdiemAmount"
];
Map<String, dynamic> forex_Detials() {
final data = {
// "forex_perdiem_id": int.parse(forexId),
"country_code": selectedCountry,
"country_name": selectedCountryName,
"currency": controllers["currency"]?.text,
"perdiem_amount": controllers["perdiemAmount"]?.text,
"is_active": 1,
"created_by": userId,
"is_active": isActive,
};
return data;
}
@override
void initState() {
super.initState();
apiCountryData = null;
apiData = null;
for (var field in dataHeader) {
controllers[field] = TextEditingController();
}
fetchCountries();
if (widget.forexId != null) {
print('Editing Forex ID: ${widget.forexId}');
updateForexDetails();
}
_clearError();
}
void _clearError() {
setState(() {
errorMessages.clear();
});
}
@override
void dispose() {
for (var controller in controllers.values) {
controller.dispose();
}
super.dispose();
}
void updateForexDetails() {
print("Updateeee - ${widget.forexData}");
final data = widget.forexData;
if (data == null) return;
setState(() {
selectedCountry = data['country_code']; // For dropdown
selectedCountryName =
data['country_name']; // For dropdown label or display
selectedCurrency = data['currency']; // Optional if used elsewhere
controllers['currency']?.text = data['currency'] ?? '';
controllers['perdiemAmount']?.text = data['perdiem_amount'].toString();
isActive = data["is_active"];
final forexId = int.tryParse(data['forex_perdiem_id'].toString());
forexDataId = forexId;
});
}
Future<void> fetchCountries() async {
try {
List<dynamic> countries = await apiService.fetchCountryList();
setState(() {
apiCountryData = countries;
});
} catch (e) {
print('Error fetching country list: $e');
}
}
void toggleStatus() {
setState(() {
isActive = isActive == "1" ? "0" : "1";
});
}
bool validateData() {
errorMessages.clear();
final data = {
"country_code": selectedCountry,
"country": selectedCountryName,
"currency": controllers["currency"]?.text,
"perdiemAmount": controllers["perdiemAmount"]?.text,
};
final requiredFields = [
"country_code",
"country",
"currency",
"perdiemAmount"
];
// Check validation for each field
for (String field in requiredFields) {
if (data[field] == null || data[field].toString().trim().isEmpty) {
errorMessages[field] = "Required";
}
}
return errorMessages.isEmpty;
}
Future<void> handleSubmit() async {
userId = await getUserId();
setState(() {
// This triggers UI rebuild with error messages
if (validateData()) {
postForexData();
}
});
final forexData1 = forex_Detials();
print("ForexDAta - $forexData1");
}
Future<void> postForexData({int isActive = 1}) async {
// final remarksData = getData();
final forexData = forex_Detials();
print("forexDataPOSDf - $forexData");
final String apiUrldata;
if (forexDataId != null) {
print("feforexDataId - $forexDataId");
apiUrldata = '$apiUrl/api/updateForexPerdiem/$forexDataId';
forexData["id"] = forexDataId;
forexData["updated_by"] = userId;
} else {
apiUrldata = '$apiUrl/api/createForexPerdiem';
forexData["created_by"] = userId;
}
print("Remarks Data - remarksData");
// final String apiUrldata = '$apiUrl/api/createForexPerdiem';
// api/updateForexPerdiem/39
final token = await getToken(); // Fetch token
if (token == null) {
throw Exception('Token not found. Please log in.');
}
// if (selectedPlanId != null && selectedPlanId!.isNotEmpty) {
// planData['plan_id'] = selectedPlanId; // Add plan_id for update
// }
try {
final uri = Uri.parse(apiUrldata);
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
};
final body = jsonEncode(forexData);
final response = forexDataId != null
? await http.put(uri, headers: headers, body: body)
: await http.post(uri, headers: headers, body: body);
// final response = await http.post(
// Uri.parse(apiUrldata),
// headers: {
// 'Authorization': 'Bearer $token',
// 'Content-Type': 'application/json',
// },
// body: jsonEncode(forexData), // Convert map to JSON
// );
if (response.statusCode == 200 || response.statusCode == 201) {
print("Forex Details Created successfully!");
print("Response: ${response.body}");
// _clearError();
_clearError();
await widget.fetchGetForex();
// dispose();
Navigator.of(context).pop();
} else {
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
}
} catch (e) {
print(" Error submitting plan: $e");
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
late Map<String, String> countryMap; // Mapping country_code -> country_name late Map<String, String> countryMap; // Mapping country_code -> country_name
late List<String> countryCodes; // List of country codes late List<String> countryCodes; // List of country codes
countryList = []; // countryList = [];
// countryList = widget.apiCountryData ?? []; countryList = apiCountryData ?? [];
// Map country codes to country names // Map country codes to country names
countryMap = { countryMap = {
@ -52,13 +285,18 @@ class ForexDataState extends State<ForexData> {
Row( Row(
children: [ children: [
Text( Text(
'Create Forex Details', 'Create Perdiem Amount',
style: GoogleFonts.poppins(fontSize: 18, color: Colors.black), style: GoogleFonts.poppins(fontSize: 15, color: Colors.black),
), ),
const Spacer(), const Spacer(),
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 2),
Divider(
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
const SizedBox(height: 5),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -80,10 +318,23 @@ class ForexDataState extends State<ForexData> {
selectedItem: countryMap[selectedCountry], selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(
backgroundColor: Colors.white,
),
constraints: BoxConstraints(maxHeight: 250),
itemBuilder: (context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 6.0),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search Country...", hintText: "Search Country...",
contentPadding: EdgeInsets.symmetric(horizontal: 10), hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
), ),
), ),
), ),
@ -101,7 +352,7 @@ class ForexDataState extends State<ForexData> {
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
selectedItem ?? "Select Country", selectedItem ?? "Select Country",
style: TextStyle(fontSize: 12), style: GoogleFonts.poppins(fontSize: 11),
), ),
), ),
onChanged: (String? newValue) { onChanged: (String? newValue) {
@ -110,18 +361,19 @@ class ForexDataState extends State<ForexData> {
selectedCountry = countryMap.entries selectedCountry = countryMap.entries
.firstWhere((entry) => entry.value == newValue) .firstWhere((entry) => entry.value == newValue)
.key; .key;
selectedCountryName = newValue;
}); });
}, },
), ),
), ),
), ),
// if (errorMessages["country_code"] != null) ...[ if (errorMessages["country_code"] != null) ...[
// SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
// Text( Text(
// "Select Country", errorMessages["country_code"]!,
// style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
// ), ),
// ], ],
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
@ -129,7 +381,7 @@ class ForexDataState extends State<ForexData> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Currency *", "Currency",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -144,24 +396,26 @@ class ForexDataState extends State<ForexData> {
// ? MediaQuery.of(context).size.width * 0.330 // ? MediaQuery.of(context).size.width * 0.330
// : MediaQuery.of(context).size.width * 0.66, // : MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: Padding( child: TextField(
padding: const EdgeInsets.all(8.0), controller: controllers["currency"],
child: Center( style: const TextStyle(fontSize: 12),
child: Text( decoration: const InputDecoration(
// "cur", labelText: "Currency",
// "${selectedCurrency}", labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
selectedCurrency ?? "Currency", floatingLabelBehavior: FloatingLabelBehavior.never,
// selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency", border: InputBorder.none,
style: const TextStyle( contentPadding: EdgeInsets.symmetric(vertical: 16),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
), ),
), )),
),
),
), ),
if (errorMessages["currency"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["currency"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
], ],
), ),
// if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,), // if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,),
@ -185,33 +439,64 @@ class ForexDataState extends State<ForexData> {
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 35, height: 40,
child: Padding( child: TextField(
padding: const EdgeInsets.all(8.0), controller: controllers["perdiemAmount"],
child: Text( style: const TextStyle(fontSize: 12),
// "amo", decoration: const InputDecoration(
selectedPerdiemAmount ?? "Amount", labelText: "Perdiem Amount",
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
style: const TextStyle( floatingLabelBehavior: FloatingLabelBehavior.never,
fontSize: 12, border: InputBorder.none,
fontWeight: FontWeight.w600, contentPadding: EdgeInsets.symmetric(vertical: 16),
color: Color(0xFF575A74)), ),
// decoration: const InputDecoration( )),
// labelText: "To",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
),
),
),
), ),
if (errorMessages["perdiemAmount"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["perdiemAmount"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
], ],
), ),
SizedBox( SizedBox(
height: 15, height: 15,
), ),
if (forexDataId != null)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Change Status ",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
Tooltip(
message:
isActive == "1" ? "Tap to deactivate" : "Tap to activate",
child: GestureDetector(
onTap: toggleStatus,
child: Text(
isActive == "1" ? "Active" : "Inactive",
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
color: isActive == "1" ? Colors.green : Colors.red,
),
),
),
)
],
),
if (forexDataId != null)
SizedBox(
height: 15,
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
@ -238,8 +523,9 @@ class ForexDataState extends State<ForexData> {
SizedBox( SizedBox(
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
handleSubmit();
// You can get text from commentController.text // You can get text from commentController.text
Navigator.of(context).pop(); // Close the modal // Navigator.of(context).pop(); // Close the modal
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColor, backgroundColor: widget.layoutColor,
@ -247,7 +533,7 @@ class ForexDataState extends State<ForexData> {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
child: Text('Submit', child: Text('Save',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 11, color: Colors.white)), fontSize: 11, color: Colors.white)),
), ),

View File

@ -25,6 +25,9 @@ class ForexDataList extends StatefulWidget {
} }
class ForexDataListState extends State<ForexDataList> { class ForexDataListState extends State<ForexDataList> {
final GlobalKey<ForexDataListState> forexListKey =
GlobalKey<ForexDataListState>();
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
late Future<List<dynamic>> futureForex; late Future<List<dynamic>> futureForex;
@ -62,6 +65,19 @@ class ForexDataListState extends State<ForexDataList> {
// futurePlans = fetchPlans(); // futurePlans = fetchPlans();
} }
Future<List<dynamic>> refreshData() {
print("Calling Refresh Data");
futureForex = fetchGetForex();
return futureForex.then((users) {
setState(() {
allForex = users;
});
return users;
});
}
void loadInitialData() async { void loadInitialData() async {
String? layoutString = await getLayoutColor(); String? layoutString = await getLayoutColor();
String? bodyStringColor = await getBodyColor(); String? bodyStringColor = await getBodyColor();
@ -360,7 +376,7 @@ class ForexDataListState extends State<ForexDataList> {
Row( Row(
children: [ children: [
Text( Text(
'Forex Details', 'Perdiem Amount Details',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: isDesktop ? 16 : 14, fontSize: isDesktop ? 16 : 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -371,7 +387,7 @@ class ForexDataListState extends State<ForexDataList> {
), ),
if (isDesktop) if (isDesktop)
SizedBox( SizedBox(
width: MediaQuery.of(context).size.width * 0.22, width: MediaQuery.of(context).size.width * 0.16,
), ),
if (isDesktop) if (isDesktop)
@ -431,11 +447,8 @@ class ForexDataListState extends State<ForexDataList> {
context: context, context: context,
builder: (context) => ForexData( builder: (context) => ForexData(
isDesktop: isDesktop, isDesktop: isDesktop,
// planId: plan.planId,
// planId: plan
// .planId
// .toString(),
layoutColor: layoutColor!, layoutColor: layoutColor!,
fetchGetForex: refreshData,
// role: // role:
// "Travel Agent" // "Travel Agent"
), ),
@ -446,7 +459,7 @@ class ForexDataListState extends State<ForexDataList> {
MainAxisSize.min, // Ensures content fits nicely MainAxisSize.min, // Ensures content fits nicely
children: [ children: [
Text( Text(
"Add Forex", "Add Perdiem",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11, fontSize: isDesktop ? 13 : 11,
), ),
@ -537,7 +550,7 @@ class ForexDataListState extends State<ForexDataList> {
// ), // ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
"No Plans Available For This User", "No Perdiem Available ",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 20, fontSize: 20,
@ -546,7 +559,7 @@ class ForexDataListState extends State<ForexDataList> {
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Text( Text(
"Please Create Plan", "Please Create Perdiem Amount",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 16, color: Colors.grey), fontSize: 16, color: Colors.grey),
@ -617,6 +630,13 @@ class ForexDataListState extends State<ForexDataList> {
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.w600), fontWeight: FontWeight.w600),
)), )),
DataColumn(
label: Text(
'Status',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600),
)),
DataColumn( DataColumn(
label: Text( label: Text(
'Actions', 'Actions',
@ -658,186 +678,64 @@ class ForexDataListState extends State<ForexDataList> {
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis)), overflow: TextOverflow.ellipsis)),
DataCell( DataCell(
UserActionsMenu( Text(
user: forex, forex['is_active'] == "1"
getUserDetails: (id) => ? 'Active'
apiService.getSingleUser(id), : 'Inactive',
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
// color: forex['is_active'] == "1"
// ? Colors.green
// : Colors.grey,
),
softWrap: true,
overflow: TextOverflow.ellipsis,
), ),
// PopupMenuButton<int>( ),
// color: Colors.white, DataCell(
// padding: EdgeInsets.zero, // UserActionsMenu(
// offset: Offset(0, 30), // user: forex,
// icon: Icon( // getUserDetails: (id) =>
// Icons.more_vert, // apiService.getSingleUser(id),
// color: Color(0xFF475569),
// size: 14,
// ),
// itemBuilder: (context) => [
// CustomPopupMenuEntry(
// child: Container(
// padding: EdgeInsets.symmetric(
// horizontal: 8, vertical: 8),
// child: Row(
// mainAxisSize: MainAxisSize.min,
// mainAxisAlignment:
// MainAxisAlignment.center,
// children: [
// IconButton(
// icon: Icon(
// Icons.remove_red_eye,
// color:
// Color(0xFF475569),
// size: 18),
// onPressed: () async {
// print(
// "USerDAta1 - $user");
// // Fetch the user data properly with await
// Map<String, dynamic>
// usersData =
// await apiService
// .getSingleUser(user[
// 'user_id']
// is String
// ? int.parse(user[
// 'user_id'])
// : user[
// 'user_id']);
//
// print(
// "USerDAta2 - $usersData");
//
// // userSingleData =
// // await apiService
// // .getSingleUser(user[
// // 'user_id']);
//
// context.go(
// "/CreateUserDetails",
// extra: {
// "selectedUser":
// usersData,
// "isViewMode": true
// },
// );
// }),
// IconButton(
// icon: Image.asset(
// 'assets/images/IconsImg/edit.png',
// width: 20,
// height: 15),
// onPressed: () async {
// // Fetch the user data properly with await
// Map<String, dynamic>
// usersData =
// await apiService
// .getSingleUser(user[
// 'user_id']
// is String
// ? int.parse(user[
// 'user_id'])
// : user[
// 'user_id']);
//
// print(
// "USerDAta2 - $usersData");
// context.go(
// "/CreateUserDetails",
// extra: {
// "selectedUser":
// usersData,
// "isViewMode": false
// },
// );
// },
// ),
// ],
// ),
// ),
// ),
// ],
// ),
// Row(
// children: [
// MouseRegion(
// cursor: user['is_active'] == "0"
// ? SystemMouseCursors.forbidden
// : SystemMouseCursors.click,
// child: IconButton(
// icon: Icon(Icons.remove_red_eye,
// size: 18,
// color: user['is_active'] == "0"
// ? Colors.grey
// : Color(0xFF475569)),
// onPressed: user['is_active'] == "0"
// ? null
// : () {
// context.go(
// "/CreateUserDetails",
// extra: {
// "selectedUser": user,
// "isViewMode": true
// },
// );
// },
// ),
// ),
//
// MouseRegion(
// cursor: user['is_active'] == "0"
// ? SystemMouseCursors.forbidden
// : SystemMouseCursors.click,
// child: GestureDetector(
// onTap: user['is_active'] == "0"
// ? null
// : () {
//
// },
// child: Image.asset(
// 'assets/images/IconsImg/edit.png',
// width: 20,
// height: 15),
// ),
// ),
//
// // MouseRegion(
// // cursor: user['is_active'] == "0"
// // ? SystemMouseCursors
// // .forbidden
// // : SystemMouseCursors.click,
// // child: IconButton(
// // icon: Icon(Icons.edit,
// // color:
// // user['is_active'] ==
// // "0"
// // ? Colors.grey
// // : Colors.green),
// // onPressed:
// // user['is_active'] == "0"
// // ? null
// // : () {
// // print(
// // "USER: $user");
// //
// // // final userJson = jsonEncode(
// // // user); // Convert user map to string
// // // final encodedUser =
// // // Uri.encodeComponent(
// // // userJson);
// //
// // context.go(
// // "/CreateUserDetails",
// // extra: {
// // "selectedUser":
// // user,
// // "isViewMode":
// // false
// // },
// // );
// // },
// // ),
// // ),
// ],
// ), // ),
GestureDetector(
child: Image.asset(
'assets/images/IconsImg/edit.png',
width: 20,
height: 15),
onTap: () async {
// final userId = getUserId(user['user_id']);
// final usersData = await getUserDetails(userId);
//
final forexId = int.tryParse(
forex['forex_perdiem_id']
.toString());
if (forexId != null) {
print("ForexId -- $forexId");
final data = await apiService
.getForexDetailsFind(forexId);
print("ForexId -- $data");
showDialog(
context: context,
builder: (context) => ForexData(
isDesktop: isDesktop,
forexId: forexId, // Pass the ID
forexData: data,
layoutColor: layoutColor!,
// fetchGetForex: fetchGetForex,
fetchGetForex: refreshData,
// role:
// "Travel Agent"
),
);
} else {
print("Invalid Forex ID");
}
},
),
), ),
]); ]);
}).toList(), }).toList(),

View File

@ -0,0 +1,56 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:frontend/utils/auth_utils.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/apiUrl.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
import '../../services/apiService.dart';
import '../../utils/pagination.dart';
import '../../widgets/custom_popup.dart';
import '../../widgets/popup_userList_action.dart';
class Template extends StatefulWidget {
@override
TemplateState createState() => TemplateState();
}
class TemplateState extends State<Template> {
final ApiService apiService = ApiService();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
backgroundColor: 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: [Text("Template ")],
),
),
);
});
}
}

View File

@ -23,16 +23,11 @@ class templatesList extends StatefulWidget {
class _templatesListState extends State<templatesList> { class _templatesListState extends State<templatesList> {
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(builder: (context, sizingInfo) {
@ -45,22 +40,17 @@ class _templatesListState extends State<templatesList> {
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: [Text("Template Lsit")],
],
), ),
), ),
); );
}); });
} }
} }

View File

@ -94,6 +94,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
String? selectedFirstApprover; String? selectedFirstApprover;
String? selectedSecondApprover; String? selectedSecondApprover;
String? selectedThirdApprover; String? selectedThirdApprover;
String? selectedSubstituteApprover;
String? selectedFileNames; String? selectedFileNames;
Uint8List? passportDocumentBytes; Uint8List? passportDocumentBytes;
@ -125,6 +126,8 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
"secondApproval", "secondApproval",
"thirdApproval", "thirdApproval",
"employeeCode", "employeeCode",
"delegationStartDate",
"delegationEndDate",
"dateOfIssue", "dateOfIssue",
"dateOfExpiry", "dateOfExpiry",
"changePassword" "changePassword"
@ -151,7 +154,8 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
"postal_code": controllers["postalCode"]?.text, "postal_code": controllers["postalCode"]?.text,
"country_code": selectedCountry, "country_code": selectedCountry,
"employee_code": controllers["employeeCode"]?.text, "employee_code": controllers["employeeCode"]?.text,
"delegation_start_date": controllers["delegationStartDate"]?.text,
"delegation_end_date": controllers["delegationEndDate"]?.text,
"user_type": selectedUserType, "user_type": selectedUserType,
"role_id": selectedRole, "role_id": selectedRole,
"department_id": selectedDepartment, "department_id": selectedDepartment,
@ -161,6 +165,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
"first_approver": selectedFirstApprover, "first_approver": selectedFirstApprover,
"second_approver": selectedSecondApprover, "second_approver": selectedSecondApprover,
"third_approver": selectedThirdApprover, "third_approver": selectedThirdApprover,
"delegated_to_user_id": selectedSubstituteApprover,
"passport_number": controllers["passportNumber"]?.text, "passport_number": controllers["passportNumber"]?.text,
"place_of_issue": controllers["placeOfIssue"]?.text, "place_of_issue": controllers["placeOfIssue"]?.text,
"passport_document": passportFile, "passport_document": passportFile,
@ -213,6 +218,12 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
controllers["employeeCode"]?.text = controllers["employeeCode"]?.text =
apiselectedUser?["employee_code"] ?? ""; apiselectedUser?["employee_code"] ?? "";
controllers["delegationStartDate"]?.text =
apiselectedUser?["delegation_start_date"] ?? "";
controllers["delegationEndDate"]?.text =
apiselectedUser?["delegation_end_date"] ?? "";
controllers["passportNumber"]?.text = controllers["passportNumber"]?.text =
apiselectedUser?["passport_number"] ?? ""; apiselectedUser?["passport_number"] ?? "";
@ -271,6 +282,15 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
selectedThirdApprover = selectedThirdApprover =
apiselectedUser?["third_approver"]?.toString() ?? ""; apiselectedUser?["third_approver"]?.toString() ?? "";
} }
if (apiselectedUser?["delegated_to_user_id"] != null) {
print(
"UPDADele- ${apiselectedUser?["delegated_to_user_id"]?.toString()}");
selectedSubstituteApprover =
apiselectedUser?["delegated_to_user_id"]?.toString() ?? "";
print("UPDADele1- $selectedSubstituteApprover");
}
try { try {
final raw = apiselectedUser!["agent_supported_service_ids"]; final raw = apiselectedUser!["agent_supported_service_ids"];
@ -315,6 +335,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
prepareForNewEntry();
selectedTab = "personal"; selectedTab = "personal";
// WidgetsFlutterBinding.ensureInitialized(); // WidgetsFlutterBinding.ensureInitialized();
@ -510,6 +531,12 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
super.dispose(); super.dispose();
} }
void prepareForNewEntry() {
for (var controller in controllers.values) {
controller.clear();
}
}
void handleNext() async { void handleNext() async {
print("USR Detail Next"); print("USR Detail Next");
printFormData(); printFormData();
@ -595,7 +622,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
"last_name", "last_name",
"email", "email",
"mobile_no", "mobile_no",
"employeeCode" // "employeeCode"
]; ];
if (apiselectedUser == null) { if (apiselectedUser == null) {
@ -750,7 +777,9 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
print("Response body: ${response.body}"); print("Response body: ${response.body}");
if (response.statusCode == 200 || response.statusCode == 201) { if (response.statusCode == 200 || response.statusCode == 201) {
dispose();
print("✅ User submitted successfully!"); print("✅ User submitted successfully!");
print("📨 Response: ${response.body}"); print("📨 Response: ${response.body}");
context.go('/listUser'); context.go('/listUser');
} else { } else {
@ -973,6 +1002,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
selectedFirstApprover: selectedFirstApprover, selectedFirstApprover: selectedFirstApprover,
selectedSecondApprover: selectedSecondApprover, selectedSecondApprover: selectedSecondApprover,
selectedThirdApprover: selectedThirdApprover, selectedThirdApprover: selectedThirdApprover,
selectedSubstituteApprover: selectedSubstituteApprover,
onLevelChanged: (gender) { onLevelChanged: (gender) {
setState(() { setState(() {
selectedLevel = gender; selectedLevel = gender;
@ -998,6 +1028,11 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
selectedThirdApprover = role; selectedThirdApprover = role;
}); });
}, },
onFirstSubsApproverChanged: (role) {
setState(() {
selectedSubstituteApprover = role;
});
},
); );
case "travel": case "travel":
return TravellerDetails( return TravellerDetails(

View File

@ -21,12 +21,14 @@ class OfficeDetails extends StatefulWidget {
final ValueChanged<String?>? onFirstApproverChanged; final ValueChanged<String?>? onFirstApproverChanged;
final ValueChanged<String?>? onSecondApproverChanged; final ValueChanged<String?>? onSecondApproverChanged;
final ValueChanged<String?>? onThirdApproverChanged; final ValueChanged<String?>? onThirdApproverChanged;
final ValueChanged<String?>? onFirstSubsApproverChanged;
final String? selectedLevel; final String? selectedLevel;
final String? selectedDepartment; final String? selectedDepartment;
final String? selectedFirstApprover; final String? selectedFirstApprover;
final String? selectedSecondApprover; final String? selectedSecondApprover;
final String? selectedThirdApprover; final String? selectedThirdApprover;
final String? selectedSubstituteApprover;
const OfficeDetails({ const OfficeDetails({
Key? key, Key? key,
@ -44,6 +46,8 @@ class OfficeDetails extends StatefulWidget {
this.onFirstApproverChanged, this.onFirstApproverChanged,
this.onSecondApproverChanged, this.onSecondApproverChanged,
this.onThirdApproverChanged, this.onThirdApproverChanged,
this.selectedSubstituteApprover,
this.onFirstSubsApproverChanged,
}) : super(key: key); }) : super(key: key);
@override @override
@ -57,6 +61,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
late List<String> countryCodes; // List of country codes late List<String> countryCodes; // List of country codes
late List<dynamic>? apiCountryData; late List<dynamic>? apiCountryData;
bool isResetTrue = false;
late List<dynamic>? apiCostData; late List<dynamic>? apiCostData;
late List<dynamic>? apiRoleData; late List<dynamic>? apiRoleData;
late List<dynamic>? apiUserData; late List<dynamic>? apiUserData;
@ -84,6 +90,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
String? selectedFirstApprover; String? selectedFirstApprover;
String? selectedSecondApprover; String? selectedSecondApprover;
String? selectedThirdApprover; String? selectedThirdApprover;
String? selectedSubstituteApprover;
String? selectedFileNames; String? selectedFileNames;
Uint8List? passportDocumentBytes; Uint8List? passportDocumentBytes;
@ -139,6 +146,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
selectedFirstApprover = widget.selectedFirstApprover; selectedFirstApprover = widget.selectedFirstApprover;
selectedSecondApprover = widget.selectedSecondApprover; selectedSecondApprover = widget.selectedSecondApprover;
selectedThirdApprover = widget.selectedThirdApprover; selectedThirdApprover = widget.selectedThirdApprover;
selectedSubstituteApprover = widget.selectedSubstituteApprover;
fetchDepartment(); fetchDepartment();
fetchUsers(); fetchUsers();
@ -151,6 +159,27 @@ class _OfficeDetailsState extends State<OfficeDetails> {
}); });
} }
// void handleReset() {
// setState(() {
// selectedSubstituteApprover = "";
// widget.controllers["delegationEndDate"]?.text = "";
// widget.controllers["delegationStartDate"]?.text = "";
// });
// }
void handleReset() {
setState(() {
isResetTrue = true;
// selectedSubstituteApprover = null;
// widget.onFirstSubsApproverChanged?.call(null);
// widget.controllers["delegationStartDate"]?.clear();
// widget.controllers["delegationEndDate"]?.clear();
//
// print("Start Date: ${widget.controllers["delegationStartDate"]?.text}");
// print("End Date: ${widget.controllers["delegationEndDate"]?.text}");
});
}
Future<void> fetchUsers() async { Future<void> fetchUsers() async {
try { try {
List<dynamic> users = await apiService.fetchUsers(); List<dynamic> users = await apiService.fetchUsers();
@ -224,6 +253,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
), ),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( SizedBox(
height: 10, height: 10,
@ -234,6 +264,30 @@ class _OfficeDetailsState extends State<OfficeDetails> {
height: 10, height: 10,
), ),
_buildSecondRow(widget.isDesktop), _buildSecondRow(widget.isDesktop),
if (widget.isDesktop)
SizedBox(
height: 10,
),
Divider(
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Delegation",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
],
),
SizedBox(
height: 15,
),
_buildThirdRow(widget.isDesktop),
], ],
), ),
); );
@ -293,6 +347,38 @@ class _OfficeDetailsState extends State<OfficeDetails> {
); );
} }
Widget _buildThirdRow(bool isDesktop) {
return Container(
color: Colors.white,
child: widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildApproverSubstitute1(isDesktop),
Spacer(),
buildDelegationStartDateField(isDesktop),
Spacer(), // Space after Last Name
buildDelegationEndDateField(isDesktop),
SizedBox(
width: 15,
),
buildReset(isDesktop)
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildApproverSubstitute1(isDesktop),
SizedBox(height: 8), // Vertical space
buildDelegationStartDateField(isDesktop),
SizedBox(height: 8),
buildDelegationEndDateField(isDesktop),
buildReset(isDesktop)
],
),
);
}
Widget buildEmpCodeField() { Widget buildEmpCodeField() {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -848,4 +934,344 @@ class _OfficeDetailsState extends State<OfficeDetails> {
// ), // ),
); );
} }
Widget buildApproverSubstitute1(bool isDesktop) {
return Container(
color: Colors.white,
// child: Expanded(
// Allow first column to take available space
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Delegate To",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.black),
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: apiUserData == null
? Center(
child: Transform.scale(
scale: 0.5,
child: CircularProgressIndicator(),
),
)
: DropdownSearch<String>(
// selectedItem: userMap[selectedSubstituteApprover],
selectedItem: selectedSubstituteApprover != null
? userMap[selectedSubstituteApprover]
: null,
enabled: !widget.isViewMode,
popupProps: PopupProps.menu(
showSearchBox: true,
fit: FlexFit.loose, // Allows flexible height
constraints: BoxConstraints(maxHeight: 250),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search User...",
contentPadding:
EdgeInsets.symmetric(horizontal: 10),
),
),
),
items: apiUserData!.map((user) {
return "${user['first_name']} ${user['last_name']}";
}).toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 1,
),
),
),
dropdownBuilder: (context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select",
style: GoogleFonts.poppins(
fontSize: 12,
),
),
),
// onChanged: (String? newValue) {
// setState(() {
// selectedFirstApprover = userMap.entries
// .firstWhere(
// (entry) => entry.value == newValue)
// .key;
//
// // if (selectedCountry!.isNotEmpty) {
// // errorMessages.remove("country_code");
// // }
// });
// widget.onFirstApproverChanged?.call(newValue);
// },
onChanged: (String? newValue) {
if (newValue == null) return;
final approverId = userMap.entries
.firstWhere(
(entry) => entry.value == newValue)
.key;
setState(() {
selectedSubstituteApprover = approverId;
});
widget.onFirstSubsApproverChanged?.call(
approverId); // not newValue, but approverId
},
),
),
),
],
),
],
),
],
),
// ),
);
}
Widget buildDelegationStartDateField(bool isDesktop) {
DateTime? _selectedCheckOutDate;
DateTime? _selectedEndDate;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
// Parse date from notifier if available, else use today
DateTime initialDate;
initialDate = today;
// // Use previously selected date if valid
// if (_selectedCheckOutDate != null &&
// _selectedCheckOutDate!.isAfter(today)) {
// initialDate = _selectedCheckOutDate!;
// }
//
// final pickedDate = await showDatePicker(
// context: context,
// initialDate: initialDate,
// firstDate: initialDate,
// lastDate: DateTime(2100),
// );
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
widget.controllers["delegationStartDate"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
});
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Start Date",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
// onTap: () async{
// _selectCheckOutDate(context);
//
// },
onTap: () async {
await _selectCheckOutDate(context);
},
child: AbsorbPointer(
child: TextField(
controller: widget.controllers["delegationStartDate"],
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle:
const TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
// if (errorMessages["start_date"] != null) ...[
// SizedBox(height: 5), // Space before error message
// Text(
// "Select Start Date",
// style: TextStyle(color: Colors.red, fontSize: 12),
// ),
// ],
],
);
}
Widget buildDelegationEndDateField(bool isDesktop) {
DateTime? _selectedEndDate;
Future<void> _selectForexEndDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
// Parse date from notifier if available, else use today
DateTime initialDate;
initialDate = today;
//
// final pickedDate = await showDatePicker(
// context: context,
// initialDate: initialDate,
// firstDate: initialDate,
// lastDate: DateTime(2100),
// );
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate:
_selectedEndDate != null && _selectedEndDate!.isAfter(today)
? _selectedEndDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedEndDate) {
setState(() {
_selectedEndDate = pickedDate;
widget.controllers["delegationEndDate"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
// textControllers["_forexEndDate"]?.text =
// DateFormat('dd-MM-yyyy').format(initialDate);
});
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"End Date",
style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
width: isDesktop ? MediaQuery.of(context).size.width * 0.18 : null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
// onTap: () async{
// _selectCheckOutDate(context);
//
// },
onTap: () async {
await _selectForexEndDate(context);
},
child: AbsorbPointer(
child: TextField(
controller: widget.controllers["delegationEndDate"],
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle:
const TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
// if (widget.errorMessages["employeeCode"] != null) ...[
// SizedBox(height: 5), // Space before error message
// Text(
// widget.errorMessages["employeeCode"]!,
// style: TextStyle(color: Colors.red, fontSize: 12),
// ),
// ],
],
);
}
Widget buildReset(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"",
style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
),
SizedBox(height: 8),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B),
foregroundColor: Colors.white, // Keep original color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 3),
),
onPressed: () {
handleReset();
},
child: Text(
"Reset",
style: GoogleFonts.poppins(fontSize: 11),
))
],
);
}
} }

View File

@ -370,6 +370,12 @@ class _CustomAppBarState extends State<CustomAppBar> {
break; break;
case '/PolicyList': case '/PolicyList':
context.go('/PolicyList'); context.go('/PolicyList');
case '/getPerdiem':
context.go('/getPerdiem');
case '/templateList':
context.go('/templateList');
case '/template':
context.go('/template');
case '/CreateUserDetails': case '/CreateUserDetails':
context.go( context.go(
"/CreateUserDetails", "/CreateUserDetails",
@ -506,6 +512,13 @@ final List<Map<String, dynamic>> menuItems = [
}, },
{'value': '/group', 'icon': Icons.group, 'label': 'Group'}, {'value': '/group', 'icon': Icons.group, 'label': 'Group'},
{'value': '/PolicyList', 'icon': Icons.policy, 'label': 'Policy'}, {'value': '/PolicyList', 'icon': Icons.policy, 'label': 'Policy'},
{'value': '/getPerdiem', 'icon': Icons.ac_unit_sharp, 'label': 'Forex'},
{
'value': '/templateList',
'icon': Icons.ac_unit_sharp,
'label': 'Template List'
},
{'value': '/template', 'icon': Icons.ac_unit_sharp, 'label': 'Template'},
{ {
'value': '/CreateUserDetails', 'value': '/CreateUserDetails',
'icon': Icons.account_circle, 'icon': Icons.account_circle,

View File

@ -5,6 +5,7 @@ import 'package:frontend/Screens/authentication/login/login_page.dart';
import 'package:frontend/Screens/authentication/loginPage1.dart'; import 'package:frontend/Screens/authentication/loginPage1.dart';
import 'package:frontend/Screens/dashboard/home_page.dart'; import 'package:frontend/Screens/dashboard/home_page.dart';
import 'package:frontend/Screens/forex/forex_list.dart'; import 'package:frontend/Screens/forex/forex_list.dart';
import 'package:frontend/Screens/myTemplates/templatesList.dart';
import 'package:frontend/Screens/organization/orgSetup.dart'; import 'package:frontend/Screens/organization/orgSetup.dart';
import 'package:frontend/Screens/organization/org_List.dart'; import 'package:frontend/Screens/organization/org_List.dart';
import 'package:frontend/Screens/plans/create_plans.dart'; import 'package:frontend/Screens/plans/create_plans.dart';
@ -20,6 +21,7 @@ import '../Screens/allTrips/travel_agent_list.dart';
import '../Screens/approvals/approval_list.dart'; 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/userManagement/create_user/create_user.dart'; import '../Screens/userManagement/create_user/create_user.dart';
final GoRouter router = GoRouter( final GoRouter router = GoRouter(
@ -96,6 +98,18 @@ final GoRouter router = GoRouter(
path: '/group', path: '/group',
builder: (context, state) => GroupList(), builder: (context, state) => GroupList(),
), ),
GoRoute(
path: '/getPerdiem',
builder: (context, state) => ForexDataList(),
),
GoRoute(
path: '/templateList',
builder: (context, state) => templatesList(),
),
GoRoute(
path: '/template',
builder: (context, state) => Template(),
),
GoRoute( GoRoute(
path: '/approvallist', path: '/approvallist',
builder: (context, state) => ApprovalList(), builder: (context, state) => ApprovalList(),

View File

@ -732,4 +732,50 @@ class ApiService {
throw Exception('Failed to load plans'); throw Exception('Failed to load plans');
} }
} }
Future<Map<String, dynamic>> getForexDetailsFind(int userId) async {
print('Single USer 1 - $userId');
// final String apiUrldata = '$apiUrl/api/users/find/$userId';
final String apiUrldata =
'$apiUrl/api/findForexPerdiem?forex_perdiem_id=$userId';
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("forexDat - $data");
if (!data.containsKey('data') || data['data'] is! List) {
throw Exception(
"Invalid response format: 'data' field is missing or not a List");
}
final List<dynamic> forexList = data['data'];
if (forexList.isEmpty) {
throw Exception('No forex data found.');
}
return forexList.first as Map<String, dynamic>;
} catch (e) {
throw Exception('Error parsing response: $e');
}
} else {
throw Exception('Failed to load plans');
}
}
} }