diff --git a/lib/Screens/forex/forexDetails.dart b/lib/Screens/forex/forexDetails.dart index ddb6e09..4c6ec99 100644 --- a/lib/Screens/forex/forexDetails.dart +++ b/lib/Screens/forex/forexDetails.dart @@ -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> Function() fetchGetForex; final bool isDesktop; final Color? layoutColor; - const ForexData({super.key, required this.isDesktop, this.layoutColor}); + + final int? forexId; // <-- Add this + final Map? 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 { + final ApiService apiService = ApiService(); + Map countryMap = {}; + late List? apiCountryData; + late List? apiAirlineCountryData; + Map? apiData; + final Map controllers = {}; + Map errorMessages = {}; + List countryList = []; String? selectedCountry; + String? selectedCountryName; String? selectedCurrency; String? selectedDuration; String? selectedPerdiemAmount; + String? userId; + int? forexDataId; + late String isActive = "1"; + + List dataHeader = [ + "country_code", + "country", + "currency", + "perdiemAmount" + ]; + + Map 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 fetchCountries() async { + try { + List 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 handleSubmit() async { + userId = await getUserId(); + + setState(() { + // This triggers UI rebuild with error messages + if (validateData()) { + postForexData(); + } + }); + + final forexData1 = forex_Detials(); + print("ForexDAta - $forexData1"); + } + + Future 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 countryMap; // Mapping country_code -> country_name late List 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 { 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 { 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 { 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 { 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 { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Currency *", + "Currency", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -144,24 +396,26 @@ class ForexDataState extends State { // ? 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 { 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 { 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 { borderRadius: BorderRadius.circular(8), ), ), - child: Text('Submit', + child: Text('Save', style: GoogleFonts.poppins( fontSize: 11, color: Colors.white)), ), diff --git a/lib/Screens/forex/forex_list.dart b/lib/Screens/forex/forex_list.dart index a4cc19d..4538554 100644 --- a/lib/Screens/forex/forex_list.dart +++ b/lib/Screens/forex/forex_list.dart @@ -25,6 +25,9 @@ class ForexDataList extends StatefulWidget { } class ForexDataListState extends State { + final GlobalKey forexListKey = + GlobalKey(); + final ApiService apiService = ApiService(); late Future> futureForex; @@ -62,6 +65,19 @@ class ForexDataListState extends State { // futurePlans = fetchPlans(); } + Future> 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 { 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 { ), 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 { 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 { 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 { // ), 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 { ), 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 { 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 { 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( - // 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 - // 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 - // 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(), diff --git a/lib/Screens/myTemplates/template.dart b/lib/Screens/myTemplates/template.dart index e69de29..5352189 100644 --- a/lib/Screens/myTemplates/template.dart +++ b/lib/Screens/myTemplates/template.dart @@ -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