ts-tat/lib/Screens/policy/policy.dart
2025-07-29 15:24:58 +05:30

1731 lines
55 KiB
Dart

import 'dart:convert';
import 'dart:math';
import 'dart:ui' as html;
import 'dart:async';
import 'dart:html' as html;
import 'dart:typed_data';
import 'package:fluttertoast/fluttertoast.dart';
import 'dart:ui' as web;
import 'package:google_fonts/google_fonts.dart';
import 'package:reorderables/reorderables.dart';
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<String, dynamic>? policy;
const Policy({super.key, required this.policy});
static Policy fromState(GoRouterState state) {
return Policy(policy: state.extra as Map<String, dynamic>?);
}
@override
_PolicyState createState() => _PolicyState();
}
class _PolicyState extends State<Policy> {
final GlobalKey<PolicyCriteriaState> policyCriteriaKey =
GlobalKey<PolicyCriteriaState>();
final ApiService apiService = ApiService();
// List<String> services = [];
List<Map<String, dynamic>> services = [];
String? servicesJson;
Color? layoutColor;
Color? bodyColor;
late String policyType = "domestic";
// String selectedServiceDBValue = '1';
List<int> serviceDBValueList = [];
ValueNotifier<String> selectedServiceIndex = ValueNotifier("1");
ValueNotifier<String> selectedServicePriorityIndex = ValueNotifier("1");
ValueNotifier<String> selectedServiceDBValue = ValueNotifier("0");
String? selectedService;
// ValueNotifier<String> selectedService = ValueNotifier("Train");
bool isViewMode = false;
bool isDisable = false;
bool isSubmitting = false;
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
Map<String, String> errorMessages = {};
String? selectedPolicyId;
String? _selectedTripType;
String? PolicyName;
String? SelectedDomestic = "0";
String? SelectedInternational = "0";
String? orgId;
String? userId;
bool showClass = true;
bool showCost = false; // bool showCost = true; // on 21st june
List<dynamic>? selectedAllServices;
List<Map<String, String>> selectedOrgServiceIds = [];
List<dynamic>? ServicesChoosed;
List<String> filledItineraryKeys = [];
TextEditingController _policyController = TextEditingController();
List<Map<String, dynamic>>? policy_details = [];
Map<String, dynamic> get policyData {
List<Map<String, dynamic>> 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<String, dynamic> data = {
"name": _policyController.text,
"domestic": SelectedDomestic,
"international": SelectedInternational,
"services_ids": services,
"is_active": "1",
"org_id": orgId,
"created_by": userId,
"policy_details": policyDetails,
};
return data;
}
@override
void initState() {
super.initState();
_checkAuthAndLoadData();
focusNodes["policyFocusNode"] = FocusNode();
focusStates["policyFocused"] = false;
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
}
// for (var field in dataHeader) {
// focusNodes["${field}FocusNode"] = FocusNode();
// focusStates["${field}Focused"] = false;
// }
// WidgetsFlutterBinding.ensureInitialized();
// WidgetsBinding.instance.addPostFrameCallback((_) {
// loadinitializeData();
//
// updateSelectedServices();
// updateData();
//
// loadInitialData();
//
// if (widget.policy != null) {
// final details = List<Map<String, dynamic>>.from(
// widget.policy!['policy_details'],
// );
// policyCriteriaKey.currentState?.loadPolicyDetails(details);
//
// policyCriteriaKey.currentState?.fetchTrainFlightClass();
// }
// });
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void _checkAuthAndLoadData() async {
final String? token = await getToken(); // Your async function to get token
if (token == null || token.isEmpty) {
// Token doesn't exist → redirect to login
context.go(
"/",
); // or use: router.go("/") if you're using `GoRouter` directly
return;
}
if (!mounted) return;
try {
loadinitializeData();
await updateSelectedServices();
updateData();
loadInitialData();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.policy != null) {
final details = List<Map<String, dynamic>>.from(
widget.policy!['policy_details'],
);
policyCriteriaKey.currentState?.loadPolicyDetails(details);
}
});
// if (widget.policy != null) {
// print("185");
// final details = List<Map<String, dynamic>>.from(
// widget.policy!['policy_details'],
// );
// policyCriteriaKey.currentState?.loadPolicyDetails(details);
//
// policyCriteriaKey.currentState?.fetchTrainFlightClass();
// }
} catch (e) {
print("group : $e");
}
}
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<void> loadAllServices() async {
try {
final result = await apiService.fetchAllServices();
setState(() {
selectedAllServices = result;
});
print("Fetched services: $selectedAllServices");
} catch (e) {
print('Error fetching role list: $e');
}
}
Future<void> loadOrgSelectedAlServices() async {
try {
final result = await apiService.fetchOrganization();
if (result != null && result is Map<String, dynamic>) {
final rawServices = result['services_ids'];
if (rawServices != null && rawServices is String) {
try {
List<dynamic> decoded = json.decode(rawServices);
List<Map<String, String>> 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<void> updateSelectedServices() async {
print("started");
await loadAllServices();
await loadOrgSelectedAlServices();
print("III-end");
final selectedIds =
selectedOrgServiceIds.map((e) => e['service_id']).toSet();
if (widget.policy != null) {
print("inside if");
print(widget.policy);
final details = List<Map<String, dynamic>>.from(
widget.policy!['policy_details'],
);
print("****details-$details");
print(
"UUFiltered Selected Services - ${widget.policy!['services_ids']} ",
);
final exisitingService = List<Map<String, dynamic>>.from(
jsonDecode(widget.policy!['services_ids']),
);
print("exisitingService ${exisitingService}");
final filtered =
exisitingService!
.where(
(service) =>
selectedIds.contains(service['service_id'].toString()),
)
.toList();
setState(() {
ServicesChoosed = filtered;
});
print("298 ServicesChoosed ${ServicesChoosed}");
if (ServicesChoosed!.isNotEmpty) {
print("Filtered Selected Services ChooesedIpfa1: $ServicesChoosed");
ServicesChoosed!.sort(
(a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0),
);
String firstServiceName = ServicesChoosed?.first['name'];
String firstServiceId = ServicesChoosed?.first['service_id'];
print("✅ 306 First service name selected for filter: $firstServiceName - $firstServiceId");
selectedService = firstServiceName;
if (selectedServiceDBValue.value == "0") {
setState(() {
selectedServiceDBValue.value = firstServiceId.toString();
});
}
List<int> serviceDBvalue = ServicesChoosed!
.map((item) => int.parse(item['service_id'].toString()))
.toList();
serviceDBValueList = serviceDBvalue;
}
print(widget.policy);
// updateData();
if (widget.policy != null && widget.policy!['services_ids'] != null) {
final decoded = jsonDecode(widget.policy!['services_ids']);
setState(() {
services =
List<Map<String, dynamic>>.from(decoded)
.map(
(service) => {
'service_id': service['service_id'].toString(),
'name': service['name'].toString(),
'order': service['order'].toString(),
},
)
.toList();
});
print("✅ Loaded services from policy (decoded): $services");
}
print("Filtered Selected Services Added to Policy: $ServicesChoosed");
}
else {
final filtered =
selectedAllServices!
.where(
(service) =>
selectedIds.contains(service['service_id'].toString()),
)
.toList();
print("ServicesChoosedYY: $ServicesChoosed");
setState(() {
ServicesChoosed = filtered;
// services = ServicesChoosed!
// .map((service) => service['name'].toString())
// .toList();
// ServicesChoosed = filtered;
services =
ServicesChoosed!
.map(
(service) => {
'service_id': service['service_id'].toString(),
'name':
service['name'].toString(), // ✅ no space before 'name'
'order': service['order'].toString(),
},
)
.toList();
});
print("Filtered Selected Services Chooesed1: $ServicesChoosed");
if (ServicesChoosed!.isNotEmpty) {
policyCriteriaKey.currentState?.fetchTrainFlightClass();
ServicesChoosed!.sort(
(a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0),
);
String firstServiceName = ServicesChoosed?.first['name'];
String firstServiceId = ServicesChoosed?.first['service_id'];
print("✅ 363 First service name selected for filter: $firstServiceName - $firstServiceId");
selectedService = firstServiceName;
if (selectedServiceDBValue.value == "0") {
setState(() {
selectedServiceDBValue.value = firstServiceId.toString();
});
}
List<int> serviceDBvalue = ServicesChoosed!
.map((item) => int.parse(item['service_id'].toString()))
.toList();
serviceDBValueList = serviceDBvalue;
}
}
}
void updateData() {
print("updateData");
// if (widget.policy != null && selectedService != null && selectedServiceDBValue.value != "0") {
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 = "2";
}
/// ✅ Load policy_details list safely
policy_details = List<Map<String, dynamic>>.from(
widget.policy?["policy_details"] ?? [],
);
// print("policy_details ${policy_details}");
});
}
}
// void handleSubmit() async {
// setState(() { isDisable = true; });
// print("Services - $services");
// print("USR Detail Submit - $policyData");
//
// policyCriteriaKey.currentState?.saveCurrentPolicy(services);
//
// // Now the full data is ready in policyDataFromChild
// print("Submitting full policyData: $policyData");
//
// Map<String, dynamic> data = policyData;
//
// if (!isValidData(data)) {
// print("USERDETAILS : $policyData");
// print("Validation Failed: Required fields are missing.");
// setState(() { isDisable = false; });
// return; // Stop execution if validation fails
// } else {
// // print("USERDETAILS : $policyData");
// // orgId = await getOrgId();
//
// if(isDisable == true){ createPolicyData(data); }
// setState(() { isDisable = false; });
// }
// }
void handleSubmit() async {
if (isSubmitting || isDisable) return;
isSubmitting = true;
setState(() {
isDisable = true;
});
policyCriteriaKey.currentState?.saveCurrentPolicy(services);
// Now the full data is ready in policyDataFromChild
print("Submitting full policyData: $policyData");
Map<String, dynamic> data = policyData;
if (!isValidData(data)) {
print("Validation Failed");
isSubmitting = false;
setState(() => isDisable = false);
return;
}
bool result = false;
try {
result = await createPolicyData(data);
} catch (e) {
print("Unexpected error during policy creation: $e");
}
if (result) {
// ✅ Only navigate on success
context.go('/PolicyList');
} else {
// ❌ Show error, allow retry
Fluttertoast.showToast(
msg: "Failed to submit policy. Please try again.",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 2,
backgroundColor: Colors.redAccent,
textColor: Colors.white,
fontSize: 18.0,
webBgColor: "linear-gradient(to right, #28a745, #28a745)",
);
}
isSubmitting = false;
setState(() {
isDisable = false; // Allow retry
});
}
bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset previous errors
// Validate required fields
if (data["name"] == null || data["name"].toString().trim().isEmpty) {
errorMessages["name"] = "Required"; // "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"] =
"Required"; // "Please select Domestic or International.";
}
// Validate at least one policy_detail with valid content
final policyDetails = data["policy_details"] as List<Map<String, dynamic>>;
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"] =
"Required"; // "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<bool> createPolicyData(Map<String, dynamic> policyData) async {
final String apiUrldata = '$apiUrl/api/policy/createOrUpdate';
final token = await getToken(); // Fetch token
if (token == null) {
print('Token not found. Please log in.');
// throw Exception('Token not found. Please log in.');
return false;
}
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}");
return true;
} else {
print("Failed to submit policyData. Status: ${response.statusCode}");
print("Error: ${response.body}");
return false;
}
} catch (e) {
print(" Error submitting policyData: $e");
return false;
}
}
@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(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: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
),
// 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: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74),
// ),
// ),
// ],
),
),
],
),
),
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.1, color: Colors.grey),
if (errorMessages["policy_details"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["policy_details"]!,
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
),
],
isDesktop
? Padding(
padding: const EdgeInsets.only(left: 30.0, right: 98.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Approval Criteria",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
Text(
"Service Priority",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
],
),
)
: SizedBox.shrink(),
isDesktop
? Container(
// color: Colors.yellow.shade50,
height: MediaQuery.of(context).size.height * 0.54,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
_buildPolicyOrdering(isDesktop),
_buildPolicyCategory(isDesktop),
],
),
Column(children: [_buildPolicyCategoryList(isDesktop)]),
],
),
),
)
: Expanded(
child: Container(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
_buildPolicyCategoryList(isDesktop),
_buildPolicyOrdering(isDesktop),
_buildPolicyCategory(isDesktop),
],
),
),
),
),
// isDesktop
// ? Expanded(
// child: Row(
// children: [
// _buildPolicyCategory(isDesktop),
// _buildPolicyCategoryList(isDesktop),
// ],
// ),
// )
// : Expanded(
// child: Column(
// children: [
// _buildPolicyCategoryList(isDesktop),
// _buildPolicyCategory(isDesktop),
// ],
// ),
// ),
],
),
);
}
// Widget _buildServiceSelect(bool isDesktop){
// return [];
// }
Widget _buildPolicyNameField(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Policy Name *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: focusStates["policyFocused"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12),
focusNode: focusNodes["policyFocusNode"],
controller: _policyController,
onChanged: (value) => _clearError("name"),
decoration: InputDecoration(
labelText: "Policy Name",
labelStyle: GoogleFonts.poppins(
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: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
),
],
],
);
}
Widget _buildPolicyTypeField(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Policy Type *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: _buildTripType(isDesktop),
),
if (errorMessages["trip_type"] != null) ...[
SizedBox(height: 5),
Text(
errorMessages["trip_type"]!,
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
),
],
],
);
}
Widget _buildPolicyCategoryList(bool isDesktop) {
return Container(
margin: const EdgeInsets.only(top: 8.0),
// color: Colors.white,
height: isDesktop ? 350 : null,
// height: isDesktop ? MediaQuery.of(context).size.height * 0.55 : null,
// color: Colors.blueGrey.shade200,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
decoration: BoxDecoration(
border: Border.all(color: Colors.blueGrey.shade100, width: 0.35),
),
child:
isDesktop
? Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [_buildPolicySubCategoryList(isDesktop)],
),
)
: Column(
children: [
Container(
// color: Colors.amber,
child: _buildPolicySubCategoryList(isDesktop),
// child: Text("FAta"),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.start,
// children: [_buildPolicySubCategoryList(isDesktop)],
// ),
),
],
),
);
}
Widget _buildPolicySubCategoryList(bool isDesktop) {
if (services.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
if (isDesktop) {
// 🖥 Desktop (vertical ReorderableListView)
return Expanded(
child: ReorderableListView(
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) newIndex -= 1;
final item = services.removeAt(oldIndex);
services.insert(newIndex, item);
});
},
children: List.generate(services.length, (i) {
return SizedBox(
key: ValueKey(services[i]['service_id']),
width: 185,
height: 35,
// height: MediaQuery.of(context).size.height * 0.025,
child: _buildServiceTile(services[i], i.toString()),
);
}),
),
);
} else {
// 📱 Mobile (horizontal ReorderableWrap)
return Container(
// color: Colors.red,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Service Ordering"),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ReorderableWrap(
spacing: 8,
runSpacing: 8,
direction: Axis.horizontal,
needsLongPressDraggable: true,
onReorder: (oldIndex, newIndex) {
setState(() {
final item = services.removeAt(oldIndex);
services.insert(newIndex, item);
});
},
children: List.generate(services.length, (i) {
return SizedBox(
key: ValueKey(services[i]),
height: isDesktop ? 10 : 45,
// child: _buildServiceTile(services[i]['service_id'], i.toString()),
child: _buildServiceTile(services[i], i.toString()),
);
}),
),
),
],
),
);
}
}
Widget _buildServiceTile(Map<String, dynamic> service, String index) {
// bool isSelected = selectedServiceIndex.value == index;
bool isSelected = selectedServicePriorityIndex.value == index;
String name = service['name']; // or 'service_id', as needed
return GestureDetector(
onTap: () {
setState(() {
// selectedServiceIndex.value = index;
selectedServicePriorityIndex.value = index;
});
},
child: Container(
margin: const EdgeInsets.all(5),
decoration: BoxDecoration(
// color: Colors.yellow.shade50,
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.white),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
alignment: Alignment.center,
child: Text(
name,
style: GoogleFonts.poppins(color: Colors.black, fontSize: 12),
),
),
);
}
Widget _buildServiceTile1(Map<String, dynamic> service, String index) {
// bool isSelected = selectedServiceIndex.value == index;
// bool isSelected = selectedServiceIndex.value == index;
// String name = service['service_id']
bool isSelected = selectedServiceIndex.value == index;
String name = service['name'];
return GestureDetector(
onTap: () {
setState(() {
selectedServiceIndex.value = index;
});
},
child: Container(
height: 5,
// padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 2),
margin: const EdgeInsets.all(1),
decoration: BoxDecoration(
color: Colors.white,
// color: isSelected ? const Color(0xFF114D8B) : Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.white),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
alignment: Alignment.center,
child: Text(
name,
style: GoogleFonts.poppins(
color: Colors.black,
// color: isSelected ? Colors.white : Colors.black87,
fontSize: 12,
// fontWeight: FontWeight.w100,
),
),
),
);
}
Widget _buildPolicyOrdering(bool isDesktop) {
return Container(
// height: 50,
// color: Colors.white,
// color: Colors.yellow.shade200,
padding: isDesktop ? const EdgeInsets.only(left: 30, top: 8) : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.62 : null,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.75 : null,
child:
isDesktop
? Container(
// color: Colors.amber,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [_buildPolicyServiceOrdering(isDesktop)],
),
)
: Container(
// color: Colors.amber,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [_buildPolicyServiceOrdering(isDesktop)],
),
),
);
}
Widget _buildPolicyServiceOrdering(bool isDesktop) {
if (ServicesChoosed == null) {
return const Center(child: CircularProgressIndicator());
}
// Sort services by 'order'
ServicesChoosed!.sort(
(a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0),
);
List<String> services =
ServicesChoosed!.map((service) => service['name'].toString()).toList();
// List<int> serviceDBvalue =
// ServicesChoosed!.map((service) => int.parse(service['service_id'].toString())).toList();
List<int> serviceDBvalue = ServicesChoosed!
.map((item) => int.parse(item['service_id'].toString()))
.toList();
print("1093");
print(services);
print(serviceDBvalue);
return Container(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Flex(
direction: Axis.horizontal,
children:
services.asMap().entries.map((entry) {
print("1182 ${entry}");
int i = entry.key;
int index = i + 1;
String service = entry.value;
int serviceId = serviceDBvalue[i];
bool isSelected =
selectedServiceIndex.value == index.toString();
return SizedBox(
// width: isDesktop ? 40 : 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("1201 Selected Services - $service - Tab Index - $index Service Id- $serviceId");
setState(() {
selectedServiceIndex.value = index.toString();
selectedServiceDBValue.value = serviceId.toString();
selectedService = service;
print(
" selectedServiceIndex.value - ${selectedServiceIndex.value}",
);
// policyCriteriaKey.currentState?.fieldForPolicy();
// policyCriteriaKey.currentState
// ?.addOrUpdatePolicy(selectedServiceIndex.value);
print("policyselectedService - $selectedService");
if (selectedService == "Flight" ||
selectedService == "Train" || selectedService == "Accomodation") {
showClass = true;
showCost = false;
// int serviceCode = selectedService == "Flight" ? 1 : 2;
print("policyselectedService1 - $selectedService");
policyCriteriaKey.currentState
?.fetchTrainFlightClass();
}
// else if (selectedService == "Accommodation") {
// showClass = true;
// showCost = false;
// }
else {
showClass = false;
showCost = false;
}
});
},
child: Container(
margin: const EdgeInsets.all(5),
padding:
isDesktop
? const EdgeInsets.all(8)
: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 3,
),
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
service,
style: GoogleFonts.poppins(
color:
isSelected
? const Color(0xFF114D8B)
: Colors.black87,
fontSize: 13,
fontWeight:
isSelected
? FontWeight.bold
: FontWeight.w500,
decoration:
TextDecoration
.none, // remove built-in underline
),
),
if (isSelected)
const SizedBox(
height: 1,
), // spacing between text and underline
if (isSelected)
Container(
height: 2,
width: 30, // or based on text width
color: const Color(0xFF114D8B),
),
],
),
),
),
);
}).toList(),
),
),
);
}
Widget _buildPolicySubCategoryList1(bool isDesktop) {
// List<String> services = [
// "Flight",
// "Train",
// "Bus",
// "Taxi",
// "Forex",
// "Accommodation",
// "Insurance",
// "Visa",
// "Miscellaneous"
// ];
if (ServicesChoosed == null) {
return const Center(child: CircularProgressIndicator());
}
List<String> 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: GoogleFonts.poppins(
color: isSelected ? Colors.white : Colors.black87,
fontSize: 13,
fontWeight:
isSelected ? FontWeight.bold : FontWeight.w100,
),
),
),
),
);
}).toList(),
),
),
);
}
Widget _buildPolicyCategory(bool isDesktop) {
print("1408 $selectedServiceIndex");
print("1409 $selectedService");
print("1410 ${selectedServiceDBValue.value}");
return Container(
margin: isDesktop ? EdgeInsets.all(5) : null,
// color: Colors.brown.shade100,
// child: Text("data"),
// color: Colors.white60,
child: (selectedService != null && selectedServiceDBValue.value != "0")
? PolicyCriteria(
key: policyCriteriaKey,
isDesktop: isDesktop,
isClass: showClass,
isCost: showCost,
selectedTabNotifier: selectedServiceIndex,
selectedIdNotifier: selectedServiceDBValue,
selectedService: selectedService!,
serviceDBValueList: serviceDBValueList, // 👈 Pass the array here
userId: userId,
onPolicyDataChanged: (List<Map<String, dynamic>>? policyData) {
print("🟢 policyData received from child: $policyData");
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
policy_details = policyData;
_clearError("policy_details");
});
});
},
) : const Center(child: CircularProgressIndicator()),
);
}
List<Widget> _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: GoogleFonts.poppins(
color: _selectedTripType == "1" ? Colors.black : 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.green : Colors.black,
width: _selectedTripType == "1" ? 2 : 1,
),
),
child:
_selectedTripType == "1"
? Icon(Icons.rectangle, size: 8, color: Colors.green)
: 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: GoogleFonts.poppins(
fontSize: 13,
color: _selectedTripType == "2" ? Colors.black : 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.green : Colors.black,
width: _selectedTripType == "2" ? 2 : 1,
),
),
child:
_selectedTripType == "2"
? Icon(Icons.rectangle, size: 8, color: Colors.green)
: null, // Add checkmark if selected
),
),
],
),
// RadioListTile<String>(
// activeColor: Colors.blueAccent,
// contentPadding: EdgeInsets.zero,
// dense: true,
// title: Text("International"),
// value: "2",
// groupValue: _selectedTripType,
// onChanged: widget.isViewMode
// ? null
// : (value) {
// setState(() {
// _selectedTripType = value!;
// });
// },
// ),
),
];
}
List<Widget> _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", style: GoogleFonts.poppins(fontSize: 10)),
),
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
isViewMode || isDisable ? null : handleSubmit,
child: Text("Submit", style: GoogleFonts.poppins(fontSize: 10)),
),
),
];
}
List<Widget> _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<String>(
activeColor: Colors.blueAccent,
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
dense: true,
title: Text("Domestic", style: GoogleFonts.poppins(fontSize: 12)),
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<String>(
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";
});
},
),
),
),
),
];
}
}