import 'dart:convert'; import 'dart:math'; import 'dart:ui' as html; import 'dart:async'; import 'dart:html' as html; import 'dart:typed_data'; import 'dart:html' as html; import 'dart:ui' as web; import 'package:web/web.dart' as web; import 'package:flutter/material.dart'; import 'package:frontend/Screens/policy/policyCriteria.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:responsive_builder/responsive_builder.dart'; import '../../config/apiUrl.dart'; import '../../routes/custom_appBar.dart'; import '../../routes/custom_drawer.dart'; import '../../services/apiService.dart'; import '../../utils/auth_utils.dart'; import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_user_form.dart'; class Policy extends StatefulWidget { final Map? policy; const Policy({super.key, required this.policy}); static Policy fromState(GoRouterState state) { return Policy(policy: state.extra as Map?); } @override _PolicyState createState() => _PolicyState(); } class _PolicyState extends State { final GlobalKey policyCriteriaKey = GlobalKey(); final ApiService apiService = ApiService(); Color? layoutColor; Color? bodyColor; late String policyType = "domestic"; // int? selectedServiceIndex = 1; ValueNotifier selectedServiceIndex = ValueNotifier("1"); String selectedService = "train"; // ValueNotifier selectedService = ValueNotifier("Train"); bool isViewMode = false; Map errorMessages = {}; String? selectedPolicyId; String? _selectedTripType; String? PolicyName; String? SelectedDomestic = "0"; String? SelectedInternational = "0"; String? orgId; String? userId; bool showClass = true; bool showCost = true; List? selectedAllServices; List> selectedOrgServiceIds = []; List? ServicesChoosed; List filledItineraryKeys = []; TextEditingController _policyController = TextEditingController(); List>? policy_details = []; Map get policyData { List> policyDetails = policy_details!.where((service) { // Only check these specific fields for emptiness final fieldsToCheck = [ 'cost', 'class', 'a1_action', 'a2_action', 'a3_action' ]; // If any of the important fields has a value, keep it return fieldsToCheck.any((field) { final value = service[field]; return value != null && value.toString().trim().isNotEmpty; }); }).toList(); Map data = { "name": _policyController.text, "domestic": SelectedDomestic, "international": SelectedInternational, "is_active": "1", "org_id": orgId, "created_by": userId, "policy_details": policyDetails, }; return data; } @override void initState() { super.initState(); // WidgetsFlutterBinding.ensureInitialized(); WidgetsBinding.instance.addPostFrameCallback((_) { loadinitializeData(); updateSelectedServices(); updateData(); loadInitialData(); if (widget.policy != null) { final details = List>.from(widget.policy!['policy_details']); policyCriteriaKey.currentState?.loadPolicyDetails(details); policyCriteriaKey.currentState?.fetchTrainFlightClass(); } }); } void loadInitialData() async { String? layoutString = await getLayoutColor(); String? bodyStringColor = await getBodyColor(); setState(() { layoutColor = layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; bodyColor = bodyStringColor != null ? Color(int.parse(bodyStringColor)) : Colors.white; }); } void loadinitializeData() async { orgId = await getOrgId(); userId = await getUserId(); } Future loadAllServices() async { try { final result = await apiService.fetchAllServices(); setState(() { selectedAllServices = result; }); print("Fetched services: $selectedAllServices"); } catch (e) { print('Error fetching role list: $e'); } } Future loadOrgSelectedAlServices() async { try { final result = await apiService.fetchOrganization(); if (result != null && result is Map) { final rawServices = result['services_ids']; if (rawServices != null && rawServices is String) { try { List decoded = json.decode(rawServices); List> formatted = decoded .map((e) => {"service_id": e['service_id'].toString()}) .toList(); setState(() { selectedOrgServiceIds = formatted; }); print("selectedOrgServiceIds: ${selectedOrgServiceIds}"); for (var service in selectedOrgServiceIds) { print("service_id: ${service['service_id']}"); } } catch (e) { print("Failed to decode services_ids: $e"); } } } } catch (e) { print('Error fetching role list: $e'); } } Future updateSelectedServices() async { await loadAllServices(); await loadOrgSelectedAlServices(); final selectedIds = selectedOrgServiceIds.map((e) => e['service_id']).toSet(); if (widget.policy != null) { final details = List>.from(widget.policy!['policy_details']); print("Filtered Selected Services - $details"); final filtered = selectedAllServices! .where((service) => selectedIds.contains(service['service_id'].toString())) .toList(); setState(() { ServicesChoosed = filtered; }); print("Filtered Selected Services Chooesed1: $ServicesChoosed"); if (ServicesChoosed!.isNotEmpty) { String firstServiceName = ServicesChoosed?.first['name']; print("✅ First service name selected for filter: $firstServiceName"); selectedService = firstServiceName; } print("Filtered Selected Services Added to Policy: $ServicesChoosed"); } else { final filtered = selectedAllServices! .where((service) => selectedIds.contains(service['service_id'].toString())) .toList(); setState(() { ServicesChoosed = filtered; }); print("Filtered Selected Services Chooesed1: $ServicesChoosed"); if (ServicesChoosed!.isNotEmpty) { String firstServiceName = ServicesChoosed?.first['name']; print("✅ First service name selected for filter: $firstServiceName"); selectedService = firstServiceName; policyCriteriaKey.currentState?.fetchTrainFlightClass(); } } } void updateData() { if (widget.policy != null) { setState(() { selectedPolicyId = widget.policy?["policy_id"] ?? ""; _policyController.text = widget.policy?["name"] ?? ""; SelectedDomestic = widget.policy?["domestic"] ?? ""; SelectedInternational = widget.policy?["international"] ?? ""; if (SelectedDomestic == "1") { _selectedTripType = "1"; } else if (SelectedInternational == "1") { _selectedTripType = "1"; } /// ✅ Load policy_details list safely policy_details = List>.from( widget.policy?["policy_details"] ?? [], ); }); } } void handleSubmit() async { print("USR Detail Submit - $policyData"); policyCriteriaKey.currentState?.saveCurrentPolicy(); // Now the full data is ready in policyDataFromChild print("Submitting full policyData: $policyData"); Map data = policyData; if (!isValidData(data)) { print("USERDETAILS : $policyData"); print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails } else { // print("USERDETAILS : $policyData"); // orgId = await getOrgId(); createPolicyData(data); } } bool isValidData(Map data) { errorMessages.clear(); // Reset previous errors // Validate required fields if (data["name"] == null || data["name"].toString().trim().isEmpty) { errorMessages["name"] = "Policy name is required."; } // Validate that either domestic or international is selected final domestic = data["domestic"]?.toString() ?? "0"; final international = data["international"]?.toString() ?? "0"; print( "domestic: ${data["domestic"]}, international: ${data["international"]}"); if (domestic != "1" && international != "1") { errorMessages["trip_type"] = "Please select Domestic or International."; } // Validate at least one policy_detail with valid content final policyDetails = data["policy_details"] as List>; bool hasAtLeastOneDetail = policyDetails.any((service) { final fieldsToCheck = [ 'cost', 'class', 'a1_action', 'a2_action', 'a3_action' ]; return fieldsToCheck.any((field) { final value = service[field]; return value != null && value.toString().trim().isNotEmpty; }); }); if (!hasAtLeastOneDetail) { errorMessages["policy_details"] = "At least one valid policy detail is required."; } return errorMessages.isEmpty; } void _clearError(String field) { if (mounted && errorMessages.containsKey(field)) { setState(() { errorMessages.remove(field); }); } } Future createPolicyData(Map policyData) async { final String apiUrldata = '$apiUrl/api/policy/createOrUpdate'; final token = await getToken(); // Fetch token if (token == null) { throw Exception('Token not found. Please log in.'); } final int? policyId; if (selectedPolicyId != null && selectedPolicyId!.isNotEmpty) { policyId = int.tryParse(selectedPolicyId!); policyData['policy_id'] = policyId; // Add only if updating } try { final response = await http.post( Uri.parse(apiUrldata), headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', }, body: jsonEncode(policyData), // Convert map to JSON ); if (response.statusCode == 200) { print("policyData submitted successfully!"); print("Response: ${response.body}"); context.go('/PolicyList'); } else { print("Failed to submit policyData. Status: ${response.statusCode}"); print("Error: ${response.body}"); } } catch (e) { print(" Error submitting policyData: $e"); } } @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( backgroundColor: Colors.white, appBar: const CustomAppBar(), 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(8), child: Column( children: [ Expanded( child: Row( children: [ // if (isDesktop) CustomDrawer(isDesktop: true), Expanded(child: buildData(isDesktop, context)), // Expanded( // child: Container( // color: bodyColor, // child: buildPolicyLayout(isDesktop), // ), // ), ], ), ), ], ), ), // Row( // children: [ // if (isDesktop) CustomDrawer(isDesktop: true), // Expanded( // child: Column( // children: [ // Expanded( // child: Container( // // height: MediaQuery.of(context).size.height * 0.8, // color: bodyColor, // child: buildPolicyLayout(isDesktop)), // ), // ], // ), // ), // Container( // color: Colors.white, // child: Padding( // padding: const EdgeInsets.all(8.0), // child: isDesktop // ? Row( // mainAxisAlignment: MainAxisAlignment.end, // children: _buildSubmit(isDesktop), // ) // : Row( // mainAxisAlignment: MainAxisAlignment.center, // children: _buildSubmit(isDesktop), // )), // ) // ], // ), ); }); } Widget buildData(bool isDesktop, context) { return Container( // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), // decoration: BoxDecoration( // color: Color(0xFFE1F5FE), // // color: bodyColor, // border: Border.all( // color: Colors.white, // // color: Color(0xFFF7F7FB), // // width: 3.5)), child: Column( children: [ Expanded( child: Container( // color: bodyColor, // color: Color(0xFFE1F5FE), child: buildPolicyLayout(isDesktop), ), ), Container( color: Colors.white, padding: const EdgeInsets.all(8.0), child: isDesktop ? Row( mainAxisAlignment: MainAxisAlignment.end, children: _buildSubmit(isDesktop), ) : Row( mainAxisAlignment: MainAxisAlignment.center, children: _buildSubmit(isDesktop), ), ), ], ), ); } Widget buildPolicyLayout(bool isDesktop) { return Container( // margin: isDesktop // ? EdgeInsets.all(10.0) // : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), height: isDesktop ? MediaQuery.of(context).size.height * 0.98 : MediaQuery.of(context).size.height, // decoration: BoxDecoration( // border: isDesktop // ? Border.all( // width: 2, // color: Colors.white, // // color: Color(0xFFF7F7FB), // ) // : null, // color: Colors.white, // // color: Color(0xFFF7F7FB), // // // color: Colors.amber, // ), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( // color: Color(0xFFF7F7FB), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3), // color: Colors.white, // Background to avoid overlapping color: Colors.white, // color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( "Choose Policy Type", style: TextStyle( fontSize: 18, color: Colors.black, ), ), ], ), ), ], ), ), isDesktop ? SizedBox(height: 0) : SizedBox(height: 5), Container( padding: isDesktop ? const EdgeInsets.only(left: 35) : null, child: isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded(child: _buildPolicyNameField(isDesktop)), Spacer(), Expanded(child: _buildPolicyTypeField(isDesktop)), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildPolicyNameField(isDesktop), SizedBox(height: 20), _buildPolicyTypeField(isDesktop), ], ), ), SizedBox( height: 10, ), Divider( thickness: 0.2, color: Colors.grey, ), if (errorMessages["policy_details"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["policy_details"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], isDesktop ? Expanded( child: Row( children: [ _buildPolicyCategoryList(isDesktop), _buildPolicyCategory(isDesktop), ], ), ) : Expanded( child: Column( children: [ _buildPolicyCategoryList(isDesktop), _buildPolicyCategory(isDesktop), ], ), ), ], ), ); } Widget _buildPolicyNameField(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Policy Name", style: TextStyle( fontSize: 12, 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: _policyController, onChanged: (value) => _clearError("name"), decoration: InputDecoration( labelText: "Policy Name", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["name"] != null) ...[ SizedBox(height: 5), Text(errorMessages["name"]!, style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ); } Widget _buildPolicyTypeField(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Policy Type", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w200, color: Colors.black)), SizedBox(height: 5), Row( mainAxisAlignment: MainAxisAlignment.start, children: _buildTripType(isDesktop), ), if (errorMessages["trip_type"] != null) ...[ SizedBox(height: 5), Text(errorMessages["trip_type"]!, style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ); } Widget _buildPolicyCategoryList(bool isDesktop) { return Container( color: Colors.white, // color: Colors.blueGrey.shade200, width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, child: isDesktop ? Column( mainAxisAlignment: MainAxisAlignment.start, children: [_buildPolicySubCategoryList(isDesktop)], ) : Container( // color: Colors.amber, child: Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [_buildPolicySubCategoryList(isDesktop)], ), ), ), ); } Widget _buildPolicySubCategoryList(bool isDesktop) { // List services = [ // "Flight", // "Train", // "Bus", // "Taxi", // "Forex", // "Accommodation", // "Insurance", // "Visa", // "Miscellaneous" // ]; if (ServicesChoosed == null) { return const Center(child: CircularProgressIndicator()); } List services = ServicesChoosed!.map((service) => service['name'].toString()).toList(); return Expanded( child: SingleChildScrollView( scrollDirection: isDesktop ? Axis.vertical : Axis.horizontal, child: Flex( direction: isDesktop ? Axis.vertical : Axis.horizontal, children: services.asMap().entries.map((entry) { int index = entry.key + 1; String service = entry.value; bool isSelected = selectedServiceIndex.value == index.toString(); return SizedBox( width: isDesktop ? 180 : null, height: isDesktop ? max((MediaQuery.of(context).size.height * 0.075), 10) : 45, // max((MediaQuery.of(context).size.height * 0.09), 10) child: GestureDetector( onTap: () { print("Selected Services - $service - $index"); setState(() { selectedServiceIndex.value = index.toString(); selectedService = service; if (selectedService == "Flight" || selectedService == "Train") { showClass = true; showCost = true; int serviceCode = selectedService == "Flight" ? 1 : 2; policyCriteriaKey.currentState?.fetchTrainFlightClass(); } else if (selectedService == "Accommodation") { showClass = true; showCost = false; } else { showClass = false; showCost = false; } }); }, child: Container( margin: EdgeInsets.all(5), padding: isDesktop ? EdgeInsets.all(8) : EdgeInsets.only(top: 3, bottom: 3, left: 8, right: 8), decoration: BoxDecoration( // color: Colors.blue, color: isSelected ? Color(0xFF114D8B) : Colors.white, // : Color(0xFFEBEBF7), borderRadius: BorderRadius.circular(8), border: Border.all( color: Colors.white, // Light grey border width: 1, ), boxShadow: [ BoxShadow( color: Colors.grey.withAlpha(90), // Shadow color blurRadius: 1, // Blur radius spreadRadius: 1, // Spread radius offset: Offset(0, 1), // Shadow position ), ], ), alignment: Alignment.center, child: Text( service, style: TextStyle( color: isSelected ? Colors.white : Colors.black87, fontSize: 13, fontWeight: isSelected ? FontWeight.bold : FontWeight.w100), ), ), )); }).toList(), ), ), ); } Widget _buildPolicyCategory(bool isDesktop) { return Expanded( child: Container( margin: EdgeInsets.all(10), // color: Colors.brown.shade100, // color: Colors.white60, child: PolicyCriteria( key: policyCriteriaKey, isDesktop: isDesktop, isClass: showClass, isCost: showCost, selectedTabNotifier: selectedServiceIndex, selectedService: selectedService, userId: userId, onPolicyDataChanged: (List>? policyData) { print("🟢 policyData received from child: $policyData"); WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { policy_details = policyData; _clearError("policy_details"); }); }); }, )), ); } List _buildTripType(bool isDesktop) { return [ CustomTextFieldWrapper( color: Color(0xFFF4F4FB), layoutColor: layoutColor, borderRadius: BorderRadius.circular(25), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), width: 130, isFocused: _selectedTripType == "1", isDesktop: isDesktop, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Domestic", style: TextStyle( color: _selectedTripType == "1" ? Colors.white : Colors.black, fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null, fontSize: 13), ), GestureDetector( onTap: () { setState(() { _clearError("trip_type"); _selectedTripType = "1"; PolicyName = "Domestic Policy"; SelectedDomestic = "1"; SelectedInternational = "0"; }); }, child: Container( width: 15, height: 15, decoration: BoxDecoration( shape: BoxShape.rectangle, // color: _selectedOption == option["value"] // ? Colors.blueAccent // : Colors.transparent, borderRadius: BorderRadius.circular(4), // Rounded rectangle border: Border.all( color: _selectedTripType == "1" ? Colors.white : Colors.black, width: _selectedTripType == "1" ? 2 : 1, ), ), child: _selectedTripType == "1" ? Icon(Icons.rectangle, size: 8, color: Colors.white) : null, // Add checkmark if selected ), ) ], ), ), SizedBox(width: 20), CustomTextFieldWrapper( color: Color(0xFFF4F4FB), layoutColor: layoutColor, borderRadius: BorderRadius.circular(25), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), width: 150, // padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2), isFocused: _selectedTripType == "2", isDesktop: isDesktop, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "International", style: TextStyle( fontSize: 13, color: _selectedTripType == "2" ? Colors.white : Colors.black, fontWeight: _selectedTripType == "2" ? FontWeight.w600 : null, ), ), GestureDetector( onTap: () { setState(() { _clearError("trip_type"); _selectedTripType = "2"; PolicyName = "International Policy"; SelectedDomestic = "0"; SelectedInternational = "1"; }); }, child: Container( width: 15, height: 15, decoration: BoxDecoration( shape: BoxShape.rectangle, // color: _selectedOption == option["value"] // ? Colors.blueAccent // : Colors.transparent, borderRadius: BorderRadius.circular(4), // Rounded rectangle border: Border.all( color: _selectedTripType == "2" ? Colors.white : Colors.black, width: _selectedTripType == "2" ? 2 : 1, ), ), child: _selectedTripType == "2" ? Icon(Icons.rectangle, size: 8, color: Colors.white) : null, // Add checkmark if selected ), ) ], ), // RadioListTile( // activeColor: Colors.blueAccent, // contentPadding: EdgeInsets.zero, // dense: true, // title: Text("International"), // value: "2", // groupValue: _selectedTripType, // onChanged: widget.isViewMode // ? null // : (value) { // setState(() { // _selectedTripType = value!; // }); // }, // ), ), ]; } List _buildSubmit(isDesktop) { return [ ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.white, foregroundColor: layoutColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: layoutColor ?? Colors.grey, width: 2), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: () { context.go('/PolicyList'); }, child: Text("Cancel")), SizedBox( width: 20, ), MouseRegion( cursor: isViewMode ? SystemMouseCursors.forbidden : SystemMouseCursors.click, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: isViewMode ? layoutColor : layoutColor, // Keep original color foregroundColor: isViewMode ? Colors.white : Colors.white, // Keep original color disabledBackgroundColor: layoutColor, // Ensure color remains when disabled disabledForegroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: layoutColor ?? Colors.grey, width: 2), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: isViewMode ? null : handleSubmit, // Disable when in view mode child: Text("Submit"), ), ) ]; } List _buildTripType1(bool isDesktop) { return [ CustomTextFieldWrapper( color: Color(0xFFF4F4FB), padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), width: isDesktop ? 170 : 140, isFocused: _selectedTripType == "1", isDesktop: isDesktop, child: SizedBox( height: 35, child: Material( color: Colors.transparent, child: RadioListTile( activeColor: Colors.blueAccent, contentPadding: EdgeInsets.zero, visualDensity: VisualDensity.compact, dense: true, title: Text("Domestic"), value: "1", groupValue: _selectedTripType, onChanged: (value) { setState(() { _selectedTripType = value!; PolicyName = "Domestic Policy"; SelectedDomestic = "1"; SelectedInternational = "0"; }); }, ), ), ), ), isDesktop ? SizedBox(width: 28) : SizedBox(width: 15), CustomTextFieldWrapper( color: Color(0xFFF4F4FB), padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), width: isDesktop ? 180 : 180, isFocused: _selectedTripType == "2", isDesktop: isDesktop, child: SizedBox( height: 35, child: Material( color: Colors.transparent, child: RadioListTile( activeColor: Colors.blueAccent, contentPadding: EdgeInsets.zero, visualDensity: VisualDensity.compact, dense: true, title: Text("International"), value: "2", groupValue: _selectedTripType, onChanged: (value) { setState(() { _selectedTripType = value!; PolicyName = "International Policy"; SelectedDomestic = "0"; SelectedInternational = "1"; }); }, ), ), ), ), ]; } }