From 41308b2590b6209d31a47395a68df3d667d65d33 Mon Sep 17 00:00:00 2001 From: Venba Team Date: Fri, 4 Apr 2025 09:03:37 +0530 Subject: [PATCH] user management functionalities --- lib/Screens/plans/create_plans.dart | 3 +- lib/Screens/policy/policy.dart | 45 +- lib/Screens/policy/policyCriteria.dart | 157 +++++++ .../create_user/create_user.dart | 383 ++++++++++++------ lib/Screens/userManagement/user_List.dart | 2 - lib/services/apiService.dart | 91 +++++ lib/utils/auth_utils.dart | 6 + 7 files changed, 523 insertions(+), 164 deletions(-) diff --git a/lib/Screens/plans/create_plans.dart b/lib/Screens/plans/create_plans.dart index 80bb76b..8488744 100644 --- a/lib/Screens/plans/create_plans.dart +++ b/lib/Screens/plans/create_plans.dart @@ -335,7 +335,6 @@ class _CreateNewPlansState extends State { } - void getSelectedPlanFor(){ if (!mounted) return; @@ -590,7 +589,7 @@ class _CreateNewPlansState extends State { }); } - Future postPlanData(Map planData) async { + Future postPlanData(Map planData) async { final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan'; final token = await getToken(); // Fetch token diff --git a/lib/Screens/policy/policy.dart b/lib/Screens/policy/policy.dart index 6e8c849..c2f8c40 100644 --- a/lib/Screens/policy/policy.dart +++ b/lib/Screens/policy/policy.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:frontend/Screens/policy/policyCriteria.dart'; import 'package:responsive_builder/responsive_builder.dart'; import '../../routes/custom_appBar.dart'; @@ -203,7 +204,8 @@ class _PolicyState extends State{ return SizedBox( width: 180, - height: isDesktop? 65 : 50, + height: isDesktop ? MediaQuery.of(context).size.height * 0.1 : 50, + child:GestureDetector( onTap :(){ print("Selected Services - $service - $index"); @@ -242,45 +244,10 @@ class _PolicyState extends State{ Widget _buildPolicyCategory(bool isDesktop){ return Expanded( child: Container( - color: Colors.brown.shade100, - // color: Colors.white60, + // color: Colors.brown.shade100, + color: Colors.white60, - - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - - Row( - children: [ - Expanded( - child: Container( - child: Text( - "policy Data 1" - ), - ), - ), - ], - ), - - - Expanded( - child: Container( - color: Colors.brown.shade200, - child: Row( - children: [ - Expanded( - child: Container( - child: Text( - "policy Data 2" - ), - ), - ), - ], - ), - ), - ), - ], - ), + child: PolicyCriteria(isDesktop: isDesktop) ), diff --git a/lib/Screens/policy/policyCriteria.dart b/lib/Screens/policy/policyCriteria.dart index e69de29..dfa6ae8 100644 --- a/lib/Screens/policy/policyCriteria.dart +++ b/lib/Screens/policy/policyCriteria.dart @@ -0,0 +1,157 @@ +import 'package:flutter/material.dart'; + +import '../../widgets/custom_text_field.dart'; +import '../../widgets/custom_user_form.dart'; + +class PolicyCriteria extends StatefulWidget{ + + bool isDesktop; + + PolicyCriteria({super.key, required this.isDesktop}); + @override + _PolicyCriteriaState createState() => _PolicyCriteriaState(); + +} + +class _PolicyCriteriaState extends State{ + + Widget build(BuildContext context){ + return Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + + SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text("Selected Policy Criteria For Flight", + style: TextStyle( + fontSize: 16, + color:Colors.grey, + fontWeight: FontWeight.bold + ) + ,) + ],), + + + SizedBox(height: 15), + Row( + children: [ + Expanded( + child: Container( + // color:Colors.grey, + padding: const EdgeInsets.only(top:5,bottom: 5,left:5, right: 5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Class", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w200, color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12), + // controller: controllers["Fname"], + // enabled: !isViewMode, + onChanged: (value) { }, + decoration: InputDecoration( + labelText: "Class", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + + ], + ), + ], + ), + + SizedBox(height: 10), + + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Cost", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w200, color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12), + // controller: controllers["Fname"], + // enabled: !isViewMode, + onChanged: (value) { }, + decoration: InputDecoration( + labelText: "Cost", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + + ], + ), + ], + ), + + + ], + ), + + + ), + ), + ], + ), + SizedBox(height: 15), + + Expanded( + child: Container( + + decoration: BoxDecoration( + border: Border.all( + color: Color(0xFFEBEBF7), + + ), + borderRadius: BorderRadius.circular(8), + // color: Colors.brown.shade200, + ), + + child: Row( + children: [ + Expanded( + child: Container( + child: Text( + "policy Data 2" + ), + ), + ), + ], + ), + ), + ), + + ], + ); + } + +} \ No newline at end of file diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index 54e1384..8956816 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -44,8 +44,8 @@ class _CreateUserFormState extends State{ // late final List? apiCountryData ; late List? apiCountryData; - - + late List? apiCostData; + late List? apiRoleData; late List? apiUserData; Map? apiselectedUser; @@ -102,20 +102,20 @@ class _CreateUserFormState extends State{ "user_type" : selectedUserType, "role_id" : selectedRole, "department_id": selectedDepartment, - "level_id" : selectedLevel, + "group_id" : selectedLevel, "first_approver": selectedFirstApprover, "second_approver": selectedSecondApprover, "third_approver": selectedThirdApprover, "passport_number": controllers["passportNumber"]?.text, "place_of_issue": controllers["placeOfIssue"]?.text, - "passport_document": selectedFileNames, + "passport_document": base64PDF, "date_of_issue": controllers["dateOfIssue"]?.text, "date_of_expiry": controllers["dateOfExpiry"]?.text, "created_by": userId, "is_active": "1", - "passport_fileData": base64PDF, + // "passport_fileData": base64PDF, }; return data; } @@ -145,10 +145,10 @@ class _CreateUserFormState extends State{ controllers["dateOfExpiry"]?.text = apiselectedUser?["date_of_expiry"] ?? ""; - selectedCountry = apiselectedUser?["country"]?.toString() ?? ""; + selectedCountry = apiselectedUser?["country_code"]?.toString() ?? ""; selectedGender = apiselectedUser?["gender"]?.toString().trim() ?? ""; - selectedFileNames = apiselectedUser?["passport_document"]?.toString().trim() ?? ""; + base64PDF = apiselectedUser?["passport_document"]?.toString().trim() ?? ""; selectedUserType = apiselectedUser?["user_type"]?.toString().trim() ?? ""; selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? ""; selectedDepartment = apiselectedUser?["department_id"]?.toString().trim() ?? ""; @@ -178,6 +178,8 @@ class _CreateUserFormState extends State{ apiCountryData = null; apiUserData = null; apiselectedUser = null; + apiCostData = null; + apiRoleData = null; // Delay accessing context until the widget is fully initialized // WidgetsBinding.instance.addPostFrameCallback((_) { @@ -246,7 +248,9 @@ class _CreateUserFormState extends State{ initializeData(); fetchCountries(); + fetchDepartment(); fetchUsers(); + fetchRoles(); } @@ -261,11 +265,27 @@ class _CreateUserFormState extends State{ } } + + Future fetchDepartment() async { + try { + List department = await apiService.fetchCostCenter(); + setState(() { + apiCostData = department; + }); + } catch (e) { + print('Error fetching department list: $e'); + } + } + Future fetchUsers() async { try { List users = await apiService.fetchUsers(); setState(() { - apiUserData = users; + // apiUserData = users; + + apiUserData = users.where((user) => user["role_id"] == "3").toList(); + + print("APIUSerDATa - $apiUserData"); userList = apiUserData ?? []; userMap = { @@ -280,6 +300,24 @@ class _CreateUserFormState extends State{ } + Future fetchRoles() async { + try { + final response = await apiService.fetchMasterDropdown(); + + if (response is Map && response.containsKey("role")) { + List roleList = response["role"]; // Extract the list + setState(() { + apiRoleData = roleList; + }); + print("APIROLEData - $apiRoleData"); + } else { + print("Error: 'role' key not found or response is not a Map."); + } + } catch (e) { + print('Error fetching role list: $e'); + } + } + void printFormData() { for (var field in dataHeader) { @@ -346,6 +384,7 @@ class _CreateUserFormState extends State{ Map data = userDetials; if (!isValidData(data)) { + print("USERDETAILS : $userDetials"); print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails @@ -362,7 +401,11 @@ class _CreateUserFormState extends State{ errorMessages.clear(); // Reset errors // Required fields that must not be empty - List requiredFields = ["first_name", "last_name","email","password","mobile_no"]; + List requiredFields = ["first_name", "last_name","email","mobile_no"]; + + if (apiselectedUser == null) { + requiredFields.add("password"); + } // Check validation for each field for (String field in requiredFields) { @@ -371,6 +414,7 @@ class _CreateUserFormState extends State{ } } + // Mobile number validation for both mobile_no and alternate_mobile_no if (data["mobile_no"] != null && data["mobile_no"].toString().isNotEmpty) { if (!RegExp(r"^\d{10}$").hasMatch(data["mobile_no"].toString())) { @@ -402,6 +446,8 @@ class _CreateUserFormState extends State{ } + + void pickPDFWeb() { html.FileUploadInputElement uploadInput = html.FileUploadInputElement(); uploadInput.accept = '.pdf'; @@ -416,35 +462,87 @@ class _CreateUserFormState extends State{ print('File picked: ${file.name}'); print('File size: ${file.size} bytes'); + // Ensure the file is a PDF + if (!file.type.contains("pdf")) { + print("Error: Not a PDF file"); + return; + } - // Update the state with the selected file name setState(() { - selectedFileNames = file.name; // Store only one file name + selectedFileNames = file.name; // Store file name passportDocumentBytes = reader.result as Uint8List; // Store file data - // 🔹 Convert file to Base64 - base64PDF = base64Encode(passportDocumentBytes! as List); - // File size check: Ensure the file size does not exceed 3MB - int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes + // 🔹 Convert to Base64 properly + base64PDF = base64Encode(passportDocumentBytes!); - if (file.size > maxFileSize) { - print('Error: File size exceeds 3MB'); - // You can show an error message here if necessary - // For example: - // showError('File size cannot exceed 3MB'); + print('Base64 Length: ${base64PDF!.length}'); + print('Base64 (first 50 chars): ${base64PDF!.substring(0, 50)}'); + + // Ensure Base64 starts with "JVBERi0x" + if (!base64PDF!.startsWith("JVBERi0x")) { + print("Error: Base64 does not start with 'JVBERi0x'"); return; } - print('File size: ${file.size} bytes'); - print('Base64 Data: $base64PDF'); // Debugging + // 🔹 File size check: Ensure it does not exceed 3MB + int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes + if (file.size > maxFileSize) { + print('Error: File size exceeds 3MB'); + return; + } }); - - - }); }); } + + // void pickPDFWeb() { + // html.FileUploadInputElement uploadInput = html.FileUploadInputElement(); + // uploadInput.accept = '.pdf'; + // uploadInput.click(); + // + // uploadInput.onChange.listen((e) { + // final file = uploadInput.files!.first; + // final reader = html.FileReader(); + // + // reader.readAsArrayBuffer(file); + // reader.onLoadEnd.listen((event) { + // print('File picked: ${file.name}'); + // print('File size: ${file.size} bytes'); + // + // + // // Update the state with the selected file name + // setState(() { + // selectedFileNames = file.name; // Store only one file name + // passportDocumentBytes = reader.result as Uint8List; // Store file data + // + // // 🔹 Convert file to Base64 + // base64PDF = base64Encode(passportDocumentBytes! as List); + // // File size check: Ensure the file size does not exceed 3MB + // int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes + // + // if (file.size > maxFileSize) { + // print('Error: File size exceeds 3MB'); + // // You can show an error message here if necessary + // // For example: + // // showError('File size cannot exceed 3MB'); + // return; + // } + // + // print('File size: ${file.size} bytes'); + // print('Base64 Data: $base64PDF'); // Debugging + // }); + // + // + // + // }); + // }); + // } + + + + + Future createUserData(Map userData) async { bool isUpdating = apiselectedUser != null && apiselectedUser!.isNotEmpty; @@ -624,7 +722,6 @@ class _CreateUserFormState extends State{ Text("Personal Details", style: TextStyle(fontSize: 18,fontWeight: FontWeight.bold, // color: Colors.black, color: Color(0xFF8B8FB2) - ), ) ],), @@ -1238,36 +1335,7 @@ class _CreateUserFormState extends State{ children: [ Text("Change Password", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), - CustomTextFieldUserWrapper( - isFocused: false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - style: TextStyle(fontSize: 12), - controller: controllers["password"], - enabled: !isViewMode, - onChanged: (value) { - _clearError("changePassword"); - handleChangePassword(value); - }, - decoration: InputDecoration( - labelText: "Change Password", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - if (errorMessages["password"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["password"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], + ], ), ], @@ -1621,6 +1689,7 @@ class _CreateUserFormState extends State{ children: [ Text("Passport Document", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), + CustomTextFieldUserWrapper( isFocused: false, isDesktop: isDesktop, @@ -1641,6 +1710,101 @@ class _CreateUserFormState extends State{ ), ), ), + SizedBox(height: 10), + if(base64PDF != null) + + // Centers the text + Container( + color: Colors.white, + width : isDesktop + ? MediaQuery.of(context).size.width * 0.25 + : MediaQuery.of(context).size.width * 0.8, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + GestureDetector(onTap: (){ + print('DOWLOAS- $base64PDF '); + + if (base64PDF != null && base64PDF!.isNotEmpty) { + try { + // ✅ Step 1: Clean the Base64 string + String cleanedBase64 = base64PDF! + .replaceAll("\n", "") // Remove newlines + .replaceAll("\r", "") // Remove carriage returns + .replaceAll(" ", "") // Remove spaces + .trim(); // Trim any whitespace + + // ✅ Step 2: Ensure valid Base64 length (multiple of 4) + while (cleanedBase64.length % 4 != 0) { + cleanedBase64 += "_"; // Add '=' padding + } + + // ✅ Step 3: Decode the cleaned Base64 + Uint8List bytes; + try { + bytes = base64Decode(cleanedBase64); + } catch (e) { + print("Base64 decoding failed: $e"); + return; + } + + // ✅ Step 4: Create a Blob for download + final blob = html.Blob([bytes], 'application/pdf'); + final url = html.Url.createObjectUrlFromBlob(blob); + + // ✅ Step 5: Trigger the file download + final anchor = html.AnchorElement(href: url) + ..setAttribute("download", selectedFileNames ?? "document.pdf") + ..style.display = "none"; + + html.document.body!.append(anchor); + anchor.click(); + + // ✅ Step 6: Clean up + anchor.remove(); + html.Url.revokeObjectUrl(url); + + print("Download successful!"); + } catch (e) { + print("Error downloading file: $e"); + } + } else { + print("No file to download."); + } + + } , + + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Colors.green.shade300, + ), + + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + "Download", + style: TextStyle(fontSize: 12, color: Colors.white,fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.end, // Ensures text is centered within the Text widget + ), + SizedBox(width: 5), + Icon(Icons.download ,size: 13, color: Colors.white,) + ], + ), + + + ), + ) + + ], + ), + ), + @@ -1933,7 +2097,7 @@ class _CreateUserFormState extends State{ constraints: BoxConstraints(maxHeight: 250), ), - items: ["Admin", "Manager", "HR"], + items: ["Normal", "Privilege",], dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, @@ -1952,6 +2116,8 @@ class _CreateUserFormState extends State{ // Find the country_code based on selected country_name selectedUserType = newValue; + // print("selectedUserType - $selectedUserType"); + // if (selectedCountry!.isNotEmpty) { // errorMessages.remove("country_code"); // } @@ -1989,43 +2155,32 @@ class _CreateUserFormState extends State{ Text("Role ", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), CustomTextFieldUserWrapper( - isFocused: false, + isFocused: false, // Dropdown doesn't use focus isDesktop: isDesktop, child: SizedBox( - height: 40, - child: DropdownSearch( - selectedItem: selectedRole, - enabled: !isViewMode, - popupProps: PopupProps.menu( - // showSearchBox: true, - fit: FlexFit.loose, // Allows flexible height - constraints: BoxConstraints(maxHeight: 250), + height: 45, // Set appropriate height + child: DropdownButtonFormField( + value: selectedRole, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), - items: ["Role 1", "Role 2", "Role 3"], - 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: TextStyle(fontSize: 12), - ), - ), - onChanged: (String? newValue) { + onChanged: isViewMode ? null : (newValue) { setState(() { - // Find the country_code based on selected country_name - selectedRole = newValue; - - // if (selectedCountry!.isNotEmpty) { - // errorMessages.remove("country_code"); - // } - + selectedRole = newValue; }); }, + + items:apiRoleData?.map>((item){ + return DropdownMenuItem( + value: item['dropdown_key'], // ID as value + child:Text(item['dropdown_value'] ?? "Select") + + ); + }).toList(), + hint: Text("Select"), ), ), ), @@ -2039,7 +2194,7 @@ class _CreateUserFormState extends State{ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Level", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w200, color: Colors.black)), + Text("Group", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), CustomTextFieldUserWrapper( isFocused: false, @@ -2097,44 +2252,30 @@ class _CreateUserFormState extends State{ Text("Department", style: TextStyle(fontSize: 15, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), CustomTextFieldUserWrapper( - isFocused: false, + isFocused: false, // Dropdown doesn't use focus isDesktop: isDesktop, child: SizedBox( - height: 40, - child: DropdownSearch( - selectedItem: selectedDepartment, - enabled: !isViewMode, - popupProps: PopupProps.menu( - // showSearchBox: true, - fit: FlexFit.loose, // Allows flexible height - constraints: BoxConstraints(maxHeight: 250), - + height: 45, // Set appropriate height + child: DropdownButtonFormField( + value: selectedDepartment, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), - items: ["Dep1", "Dep2", "Dep3"], - 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: TextStyle(fontSize: 12), - ), - ), - onChanged: (String? newValue) { + onChanged: isViewMode ? null : (newValue) { setState(() { - // Find the country_code based on selected country_name - selectedDepartment = newValue; - - // if (selectedCountry!.isNotEmpty) { - // errorMessages.remove("country_code"); - // } - + selectedDepartment = newValue; }); }, + items:apiCostData?.map>((item){ + return DropdownMenuItem( + value: item['department_id'], // ID as value + child: Text(item['name'] ?? "Unknown"), + ); + }).toList(), + hint: Text("Select"), ), ), ), diff --git a/lib/Screens/userManagement/user_List.dart b/lib/Screens/userManagement/user_List.dart index de3cf83..32e4e2d 100644 --- a/lib/Screens/userManagement/user_List.dart +++ b/lib/Screens/userManagement/user_List.dart @@ -388,8 +388,6 @@ class _UserListScreenState extends State { SizedBox(width: 10,), Text(user['user_type'] ?? '') ],), ],), - - ], ) ), diff --git a/lib/services/apiService.dart b/lib/services/apiService.dart index 5a383a9..91768b4 100644 --- a/lib/services/apiService.dart +++ b/lib/services/apiService.dart @@ -69,4 +69,95 @@ class ApiService { } + Future fetchCostCenter() async { + final String apiUrldata = '$apiUrl/api/getCostCenterMaster'; + + final token = await getToken(); + + final userId = await getUserId(); + + // print("SUSRTRT- $userId"); + // + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + try { + final data = json.decode(response.body); + print(data); + + if (!data.containsKey('data') || data['data'] is!List) { + throw Exception("Invalid response format: 'data' field is missing or not a List"); + } + + List plansJson = data['data']; // 'data' is a Map, not a List + // setState(() { + // apiCostData = plansJson; // Store API response in state + // if(apiCostData!.isNotEmpty){ + // selectedCostCenterId =apiCostData?.first['department_id']; + // } + + // if (apiCostData != null && apiCostData!.isNotEmpty) { + // selectedCostCenterId ??= apiCostData!.first['department_id']?.toString(); + // } + // }); + + print('plansJSON'); + + return plansJson; + + + } catch (e) { + throw Exception('Error parsing response: $e'); + } + } else { + throw Exception('Failed to load plans'); + } + } + + Future> fetchMasterDropdown() async { + final String apiUrldata = '$apiUrl/api/getDropdownMaster'; + + final token = await getToken(); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json',},); + + if (response.statusCode == 200) { + try { + final data = json.decode(response.body); + print(data); + if (!data.containsKey('data') || data['data'] is! Map) { + throw Exception("Invalid response format: 'data' field is missing or not a Map"); + } + + Map plansJson = data['data']; // 'data' is a Map, not a List + + return plansJson; + + } catch (e) { + throw Exception('Error parsing response: $e'); + } + } else { + throw Exception('Failed to load plans'); + } + } + + } diff --git a/lib/utils/auth_utils.dart b/lib/utils/auth_utils.dart index 241da90..5e35a51 100644 --- a/lib/utils/auth_utils.dart +++ b/lib/utils/auth_utils.dart @@ -4,3 +4,9 @@ Future getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString("auth_token"); } + + +Future getUserId() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('userId'); +} \ No newline at end of file