Forex CRUD- User Mnagement Delegationt
This commit is contained in:
parent
4559eb7262
commit
c560963e50
@ -1,33 +1,266 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.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 'forex_list.dart';
|
||||
|
||||
class ForexData extends StatefulWidget {
|
||||
final Future<List<dynamic>> Function() fetchGetForex;
|
||||
final bool isDesktop;
|
||||
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
|
||||
ForexDataState createState() => ForexDataState();
|
||||
}
|
||||
|
||||
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 = [];
|
||||
String? selectedCountry;
|
||||
String? selectedCountryName;
|
||||
String? selectedCurrency;
|
||||
String? selectedDuration;
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
late Map<String, String> countryMap; // Mapping country_code -> country_name
|
||||
late List<String> countryCodes; // List of country codes
|
||||
|
||||
countryList = [];
|
||||
// countryList = widget.apiCountryData ?? [];
|
||||
// countryList = [];
|
||||
countryList = apiCountryData ?? [];
|
||||
|
||||
// Map country codes to country names
|
||||
countryMap = {
|
||||
@ -52,13 +285,18 @@ class ForexDataState extends State<ForexData> {
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Create Forex Details',
|
||||
style: GoogleFonts.poppins(fontSize: 18, color: Colors.black),
|
||||
'Create Perdiem Amount',
|
||||
style: GoogleFonts.poppins(fontSize: 15, color: Colors.black),
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(height: 2),
|
||||
Divider(
|
||||
thickness: 0.2,
|
||||
color: Colors.blueGrey.shade100,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -80,10 +318,23 @@ class ForexDataState extends State<ForexData> {
|
||||
selectedItem: countryMap[selectedCountry],
|
||||
popupProps: PopupProps.menu(
|
||||
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(
|
||||
decoration: InputDecoration(
|
||||
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,
|
||||
child: Text(
|
||||
selectedItem ?? "Select Country",
|
||||
style: TextStyle(fontSize: 12),
|
||||
style: GoogleFonts.poppins(fontSize: 11),
|
||||
),
|
||||
),
|
||||
onChanged: (String? newValue) {
|
||||
@ -110,18 +361,19 @@ class ForexDataState extends State<ForexData> {
|
||||
selectedCountry = countryMap.entries
|
||||
.firstWhere((entry) => entry.value == newValue)
|
||||
.key;
|
||||
selectedCountryName = newValue;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
// if (errorMessages["country_code"] != null) ...[
|
||||
// SizedBox(height: 5), // Space before error message
|
||||
// Text(
|
||||
// "Select Country",
|
||||
// style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
// ),
|
||||
// ],
|
||||
if (errorMessages["country_code"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["country_code"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
@ -129,7 +381,7 @@ class ForexDataState extends State<ForexData> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Currency *",
|
||||
"Currency",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
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.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Center(
|
||||
child: Text(
|
||||
// "cur",
|
||||
// "${selectedCurrency}",
|
||||
selectedCurrency ?? "Currency",
|
||||
// selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency",
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
height: 40,
|
||||
child: TextField(
|
||||
controller: controllers["currency"],
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Currency",
|
||||
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
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,),
|
||||
@ -185,33 +439,64 @@ class ForexDataState extends State<ForexData> {
|
||||
isDesktop: widget.isDesktop,
|
||||
color: Colors.transparent,
|
||||
child: SizedBox(
|
||||
height: 35,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
// "amo",
|
||||
selectedPerdiemAmount ?? "Amount",
|
||||
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount",
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
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),
|
||||
// ),
|
||||
),
|
||||
),
|
||||
),
|
||||
height: 40,
|
||||
child: TextField(
|
||||
controller: controllers["perdiemAmount"],
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Perdiem Amount",
|
||||
labelStyle: TextStyle(fontSize: 11, 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(
|
||||
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(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
@ -238,8 +523,9 @@ class ForexDataState extends State<ForexData> {
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSubmit();
|
||||
// You can get text from commentController.text
|
||||
Navigator.of(context).pop(); // Close the modal
|
||||
// Navigator.of(context).pop(); // Close the modal
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.layoutColor,
|
||||
@ -247,7 +533,7 @@ class ForexDataState extends State<ForexData> {
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Text('Submit',
|
||||
child: Text('Save',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 11, color: Colors.white)),
|
||||
),
|
||||
|
||||
@ -25,6 +25,9 @@ class ForexDataList extends StatefulWidget {
|
||||
}
|
||||
|
||||
class ForexDataListState extends State<ForexDataList> {
|
||||
final GlobalKey<ForexDataListState> forexListKey =
|
||||
GlobalKey<ForexDataListState>();
|
||||
|
||||
final ApiService apiService = ApiService();
|
||||
late Future<List<dynamic>> futureForex;
|
||||
|
||||
@ -62,6 +65,19 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
// futurePlans = fetchPlans();
|
||||
}
|
||||
|
||||
Future<List<dynamic>> refreshData() {
|
||||
print("Calling Refresh Data");
|
||||
|
||||
futureForex = fetchGetForex();
|
||||
|
||||
return futureForex.then((users) {
|
||||
setState(() {
|
||||
allForex = users;
|
||||
});
|
||||
return users;
|
||||
});
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
String? bodyStringColor = await getBodyColor();
|
||||
@ -360,7 +376,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Forex Details',
|
||||
'Perdiem Amount Details',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: isDesktop ? 16 : 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -371,7 +387,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
),
|
||||
if (isDesktop)
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.22,
|
||||
width: MediaQuery.of(context).size.width * 0.16,
|
||||
),
|
||||
|
||||
if (isDesktop)
|
||||
@ -431,11 +447,8 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
context: context,
|
||||
builder: (context) => ForexData(
|
||||
isDesktop: isDesktop,
|
||||
// planId: plan.planId,
|
||||
// planId: plan
|
||||
// .planId
|
||||
// .toString(),
|
||||
layoutColor: layoutColor!,
|
||||
fetchGetForex: refreshData,
|
||||
// role:
|
||||
// "Travel Agent"
|
||||
),
|
||||
@ -446,7 +459,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add Forex",
|
||||
"Add Perdiem",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: isDesktop ? 13 : 11,
|
||||
),
|
||||
@ -537,7 +550,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
// ),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"No Plans Available For This User",
|
||||
"No Perdiem Available ",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -546,7 +559,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create Plan",
|
||||
"Please Create Perdiem Amount",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16, color: Colors.grey),
|
||||
@ -617,6 +630,13 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600),
|
||||
)),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'Status',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600),
|
||||
)),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'Actions',
|
||||
@ -658,186 +678,64 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis)),
|
||||
DataCell(
|
||||
UserActionsMenu(
|
||||
user: forex,
|
||||
getUserDetails: (id) =>
|
||||
apiService.getSingleUser(id),
|
||||
Text(
|
||||
forex['is_active'] == "1"
|
||||
? 'Active'
|
||||
: '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,
|
||||
// padding: EdgeInsets.zero,
|
||||
// offset: Offset(0, 30),
|
||||
// icon: Icon(
|
||||
// Icons.more_vert,
|
||||
// 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
|
||||
// // },
|
||||
// // );
|
||||
// // },
|
||||
// // ),
|
||||
// // ),
|
||||
// ],
|
||||
),
|
||||
DataCell(
|
||||
// UserActionsMenu(
|
||||
// user: forex,
|
||||
// getUserDetails: (id) =>
|
||||
// apiService.getSingleUser(id),
|
||||
// ),
|
||||
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(),
|
||||
|
||||
@ -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 ")],
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -23,16 +23,11 @@ class templatesList extends StatefulWidget {
|
||||
class _templatesListState extends State<templatesList> {
|
||||
final ApiService apiService = ApiService();
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -45,22 +40,17 @@ class _templatesListState extends State<templatesList> {
|
||||
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
|
||||
)
|
||||
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: [
|
||||
|
||||
|
||||
],
|
||||
children: [Text("Template Lsit")],
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -94,6 +94,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
String? selectedFirstApprover;
|
||||
String? selectedSecondApprover;
|
||||
String? selectedThirdApprover;
|
||||
String? selectedSubstituteApprover;
|
||||
|
||||
String? selectedFileNames;
|
||||
Uint8List? passportDocumentBytes;
|
||||
@ -125,6 +126,8 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
"secondApproval",
|
||||
"thirdApproval",
|
||||
"employeeCode",
|
||||
"delegationStartDate",
|
||||
"delegationEndDate",
|
||||
"dateOfIssue",
|
||||
"dateOfExpiry",
|
||||
"changePassword"
|
||||
@ -151,7 +154,8 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
"postal_code": controllers["postalCode"]?.text,
|
||||
"country_code": selectedCountry,
|
||||
"employee_code": controllers["employeeCode"]?.text,
|
||||
|
||||
"delegation_start_date": controllers["delegationStartDate"]?.text,
|
||||
"delegation_end_date": controllers["delegationEndDate"]?.text,
|
||||
"user_type": selectedUserType,
|
||||
"role_id": selectedRole,
|
||||
"department_id": selectedDepartment,
|
||||
@ -161,6 +165,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
"first_approver": selectedFirstApprover,
|
||||
"second_approver": selectedSecondApprover,
|
||||
"third_approver": selectedThirdApprover,
|
||||
"delegated_to_user_id": selectedSubstituteApprover,
|
||||
"passport_number": controllers["passportNumber"]?.text,
|
||||
"place_of_issue": controllers["placeOfIssue"]?.text,
|
||||
"passport_document": passportFile,
|
||||
@ -213,6 +218,12 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
controllers["employeeCode"]?.text =
|
||||
apiselectedUser?["employee_code"] ?? "";
|
||||
|
||||
controllers["delegationStartDate"]?.text =
|
||||
apiselectedUser?["delegation_start_date"] ?? "";
|
||||
|
||||
controllers["delegationEndDate"]?.text =
|
||||
apiselectedUser?["delegation_end_date"] ?? "";
|
||||
|
||||
controllers["passportNumber"]?.text =
|
||||
apiselectedUser?["passport_number"] ?? "";
|
||||
|
||||
@ -271,6 +282,15 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
selectedThirdApprover =
|
||||
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 {
|
||||
final raw = apiselectedUser!["agent_supported_service_ids"];
|
||||
|
||||
@ -315,6 +335,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
prepareForNewEntry();
|
||||
selectedTab = "personal";
|
||||
|
||||
// WidgetsFlutterBinding.ensureInitialized();
|
||||
@ -510,6 +531,12 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void prepareForNewEntry() {
|
||||
for (var controller in controllers.values) {
|
||||
controller.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void handleNext() async {
|
||||
print("USR Detail Next");
|
||||
printFormData();
|
||||
@ -595,7 +622,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
"last_name",
|
||||
"email",
|
||||
"mobile_no",
|
||||
"employeeCode"
|
||||
// "employeeCode"
|
||||
];
|
||||
|
||||
if (apiselectedUser == null) {
|
||||
@ -750,7 +777,9 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
print("Response body: ${response.body}");
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
dispose();
|
||||
print("✅ User submitted successfully!");
|
||||
|
||||
print("📨 Response: ${response.body}");
|
||||
context.go('/listUser');
|
||||
} else {
|
||||
@ -973,6 +1002,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
selectedFirstApprover: selectedFirstApprover,
|
||||
selectedSecondApprover: selectedSecondApprover,
|
||||
selectedThirdApprover: selectedThirdApprover,
|
||||
selectedSubstituteApprover: selectedSubstituteApprover,
|
||||
onLevelChanged: (gender) {
|
||||
setState(() {
|
||||
selectedLevel = gender;
|
||||
@ -998,6 +1028,11 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
selectedThirdApprover = role;
|
||||
});
|
||||
},
|
||||
onFirstSubsApproverChanged: (role) {
|
||||
setState(() {
|
||||
selectedSubstituteApprover = role;
|
||||
});
|
||||
},
|
||||
);
|
||||
case "travel":
|
||||
return TravellerDetails(
|
||||
|
||||
@ -21,12 +21,14 @@ class OfficeDetails extends StatefulWidget {
|
||||
final ValueChanged<String?>? onFirstApproverChanged;
|
||||
final ValueChanged<String?>? onSecondApproverChanged;
|
||||
final ValueChanged<String?>? onThirdApproverChanged;
|
||||
final ValueChanged<String?>? onFirstSubsApproverChanged;
|
||||
|
||||
final String? selectedLevel;
|
||||
final String? selectedDepartment;
|
||||
final String? selectedFirstApprover;
|
||||
final String? selectedSecondApprover;
|
||||
final String? selectedThirdApprover;
|
||||
final String? selectedSubstituteApprover;
|
||||
|
||||
const OfficeDetails({
|
||||
Key? key,
|
||||
@ -44,6 +46,8 @@ class OfficeDetails extends StatefulWidget {
|
||||
this.onFirstApproverChanged,
|
||||
this.onSecondApproverChanged,
|
||||
this.onThirdApproverChanged,
|
||||
this.selectedSubstituteApprover,
|
||||
this.onFirstSubsApproverChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -57,6 +61,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
late List<String> countryCodes; // List of country codes
|
||||
late List<dynamic>? apiCountryData;
|
||||
|
||||
bool isResetTrue = false;
|
||||
|
||||
late List<dynamic>? apiCostData;
|
||||
late List<dynamic>? apiRoleData;
|
||||
late List<dynamic>? apiUserData;
|
||||
@ -84,6 +90,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
String? selectedFirstApprover;
|
||||
String? selectedSecondApprover;
|
||||
String? selectedThirdApprover;
|
||||
String? selectedSubstituteApprover;
|
||||
|
||||
String? selectedFileNames;
|
||||
Uint8List? passportDocumentBytes;
|
||||
@ -139,6 +146,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
selectedFirstApprover = widget.selectedFirstApprover;
|
||||
selectedSecondApprover = widget.selectedSecondApprover;
|
||||
selectedThirdApprover = widget.selectedThirdApprover;
|
||||
selectedSubstituteApprover = widget.selectedSubstituteApprover;
|
||||
|
||||
fetchDepartment();
|
||||
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 {
|
||||
try {
|
||||
List<dynamic> users = await apiService.fetchUsers();
|
||||
@ -224,6 +253,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 10,
|
||||
@ -234,6 +264,30 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
height: 10,
|
||||
),
|
||||
_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() {
|
||||
return Column(
|
||||
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),
|
||||
))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,6 +370,12 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
||||
break;
|
||||
case '/PolicyList':
|
||||
context.go('/PolicyList');
|
||||
case '/getPerdiem':
|
||||
context.go('/getPerdiem');
|
||||
case '/templateList':
|
||||
context.go('/templateList');
|
||||
case '/template':
|
||||
context.go('/template');
|
||||
case '/CreateUserDetails':
|
||||
context.go(
|
||||
"/CreateUserDetails",
|
||||
@ -506,6 +512,13 @@ final List<Map<String, dynamic>> menuItems = [
|
||||
},
|
||||
{'value': '/group', 'icon': Icons.group, 'label': 'Group'},
|
||||
{'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',
|
||||
'icon': Icons.account_circle,
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:frontend/Screens/authentication/login/login_page.dart';
|
||||
import 'package:frontend/Screens/authentication/loginPage1.dart';
|
||||
import 'package:frontend/Screens/dashboard/home_page.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/org_List.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/group/group.dart';
|
||||
import '../Screens/group/groupList.dart';
|
||||
import '../Screens/myTemplates/template.dart';
|
||||
import '../Screens/userManagement/create_user/create_user.dart';
|
||||
|
||||
final GoRouter router = GoRouter(
|
||||
@ -96,6 +98,18 @@ final GoRouter router = GoRouter(
|
||||
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) => Template(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/approvallist',
|
||||
builder: (context, state) => ApprovalList(),
|
||||
|
||||
@ -732,4 +732,50 @@ class ApiService {
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user