import 'dart:convert'; 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 'costCenter_list.dart'; class CostCenterData extends StatefulWidget { final Future> Function() fetchGetCostCenter; final bool isDesktop; final Color? layoutColor; final int? costcenterId; // <-- Add this final Map? costcenterData; const CostCenterData({ super.key, required this.isDesktop, this.layoutColor, required this.fetchGetCostCenter, this.costcenterId, this.costcenterData, }); @override CostCenterDataState createState() => CostCenterDataState(); } class CostCenterDataState extends State { final ApiService apiService = ApiService(); Map? apiData; // final Map focusNodes = { // "name": FocusNode(), // "description": FocusNode(), // }; Map focusNodes = {}; Map focusStates = {}; final Map controllers = {}; Map errorMessages = {}; String? selectedName; String? selectedDescription; String? userId; int? costcenterDataId; late String isActive = "1"; bool isDisable = false; List dataHeader = ["name", "description"]; Map costcenterDetails() { final data = { // "cost_center_id": int.parse(costcenterId), "name": controllers["name"]?.text, "description": controllers["description"]?.text, "created_by": userId, "is_active": isActive, }; return data; } @override void initState() { super.initState(); apiData = null; for (var field in dataHeader) { controllers[field] = TextEditingController(); focusNodes["${field}FocusNode"] = FocusNode(); focusStates["${field}Focused"] = false; } for (var key in focusNodes.keys) { _addFocusListener(focusNodes[key]!, (focus) { setState(() { focusStates[key.replaceFirst("FocusNode", "Focused")] = focus; }); }); } if (widget.costcenterId != null) { print('Editing D ID: ${widget.costcenterId}'); updateCostCenterDetails(); } } void _clearError() { setState(() { errorMessages.clear(); }); } @override void dispose() { for (var controller in controllers.values) { controller.dispose(); } for (var node in focusNodes.values) { node.dispose(); } super.dispose(); } void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { updateState(node.hasFocus); }); }); } void updateCostCenterDetails() { print("Inside Update Function - ${widget.costcenterData}"); final data = widget.costcenterData; if (data == null) return; setState(() { controllers['name']?.text = data['name'] ?? ''; controllers['description']?.text = data['description'].toString(); isActive = data["is_active"]; final costcenterId = int.tryParse(data['cost_center_id'].toString()); costcenterDataId = costcenterId; }); } void toggleStatus() { setState(() { isActive = isActive == "1" ? "0" : "1"; }); } bool validateData() { errorMessages.clear(); final data = { "name": controllers["name"]?.text, "description": controllers["description"]?.text, }; final requiredFields = ["name", "description"]; bool hasFocused = false; // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field]!.trim().isEmpty) { errorMessages[field] = "Required"; // if (!hasFocused) { // focusNodes[field]?.requestFocus(); // hasFocused = true; // } } } return errorMessages.isEmpty; } Future handleSubmit() async { userId = await getUserId(); setState(() { isDisable = true; // This triggers UI rebuild with error messages if (validateData()) { postCostCenterData(); } else { isDisable = false; } }); final costcenterData1 = costcenterDetails(); print("submit data - $costcenterData1"); } Future postCostCenterData({int isActive = 1}) async { // final remarksData = getData(); final costcenterData = costcenterDetails(); print("initially value of the CostCenter - $costcenterData"); final String apiUrldata; if (costcenterDataId != null) { print("for edit costcenter id - $costcenterDataId"); apiUrldata = '$apiUrl/api/updateCostCenter/$costcenterDataId'; costcenterData["cost_center_id"] = costcenterDataId.toString(); costcenterData["updated_by"] = userId; (costcenterData.containsKey("created_by")) ? costcenterData.remove("created_by") : ''; } else { print("for add CostCenter id - null"); apiUrldata = '$apiUrl/api/createCostCenter'; print("called apiUrl - $apiUrldata"); costcenterData["created_by"] = userId; } print("recently CostCenter data - $costcenterData"); final token = await getToken(); // Fetch token if (token == null) { throw Exception('Token not found. Please log in.'); } try { final uri = Uri.parse(apiUrldata); final headers = { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', 'app-signature': 'ts-traveltool-2025-signature-123456', }; final body = jsonEncode(costcenterData); final response = costcenterDataId != null ? await http.put(uri, headers: headers, body: body) : await http.post(uri, headers: headers, body: body); switch (response.statusCode) { case 200: print("Update - Response: ${response.body}"); _clearError(); widget.fetchGetCostCenter(); if (context.mounted) { Navigator.of(context).pop(); // Close modal only if mounted } setState(() { isDisable = false; }); break; case 201: print("Save - Response: ${response.body}"); _clearError(); await widget.fetchGetCostCenter(); if (context.mounted) { Navigator.of(context).pop(); // Close modal only if mounted } setState(() { isDisable = false; }); break; case 403: print("403-FORB"); await apiService.logout(context); break; // throw Exception('Failed to load users'); default: print("Failed to submit costcenter. Status: ${response.statusCode}"); print("Error: ${response.body}"); setState(() { isDisable = false; }); } } catch (e) { print(" Error submitting plan: $e"); setState(() { isDisable = false; }); } } @override Widget build(BuildContext context) { return AlertDialog( backgroundColor: Colors.white, contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30), // contentPadding: const EdgeInsets.fromLTRB(24, 20, 24, 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), content: Column( mainAxisSize: MainAxisSize.min, children: [ // Row 1: Title + Edit + Delete buttons Row( children: [ Text( (costcenterDataId != null) ? 'Edit CostCenter' : 'Create CostCenter', style: GoogleFonts.poppins(fontSize: 15, color: Colors.black), ), const Spacer(), ], ), const SizedBox(height: 2), Divider(thickness: 0.2, color: Colors.blueGrey.shade100), const SizedBox(height: 5), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Name *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( isFocused: focusStates["nameFocused"] ?? false, isDesktop: widget.isDesktop, color: Colors.transparent, child: SizedBox( height: 40, child: TextField( controller: controllers["name"], focusNode: focusNodes["nameFocusNode"], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Name", labelStyle: TextStyle(fontSize: 11, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["name"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["name"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), SizedBox(height: 15), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Description *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldForexWrapper( isFocused: focusStates["descriptionFocused"] ?? false, isDesktop: widget.isDesktop, color: Colors.transparent, child: SizedBox( height: 100, child: TextField( controller: controllers["description"], focusNode: focusNodes["descriptionFocusNode"], style: const TextStyle(fontSize: 12), maxLines: null, expands: true, decoration: const InputDecoration( labelText: "Description", labelStyle: TextStyle(fontSize: 11, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["description"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["description"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), SizedBox(height: 15), if (costcenterDataId != 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.grey, ), ), ), ), ], ), if (costcenterDataId != null) SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ // SizedBox( // child: ElevatedButton( // onPressed: () { // // You can get text from commentController.text // Navigator.of(context).pop(); // Close the modal // }, // style: ElevatedButton.styleFrom( // backgroundColor: widget.layoutColor, // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.circular(8), // ), // ), // child: Text('Cancel', // style: GoogleFonts.poppins( // fontSize: 13, color: Colors.white)), // ), // ), // SizedBox( // width: 10, // ), SizedBox( child: ElevatedButton( onPressed: isDisable ? null : () async { setState(() { isDisable = true; }); await handleSubmit(); // You can get text from commentController.text // Navigator.of(context).pop(); // Close the modal // Optional: re-enable only on error // setState(() { // isDisable = false; // }); }, // onPressed: () { // handleSubmit(); // // You can get text from commentController.text // // Navigator.of(context).pop(); // Close the modal // }, style: ElevatedButton.styleFrom( backgroundColor: widget.layoutColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: Text( 'Save', style: GoogleFonts.poppins( fontSize: 11, color: Colors.white, ), ), ), ), ], ), // : SizedBox.shrink(), ], ), ); } }