ts-tat/lib/Screens/plans/create_plans.dart
2025-04-04 09:03:37 +05:30

1347 lines
42 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:frontend/Screens/plans/dynamic_itinerary_stepper.dart';
import 'package:go_router/go_router.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import '../../config/apiUrl.dart';
import '../../data/models/plan.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
import '../../widgets/custom_radio_button.dart';
import '../../widgets/custom_text_field.dart';
import '../dialog/user_selection_dialog.dart';
class CreatePlan extends StatefulWidget {
const CreatePlan({super.key});
@override
_CreatePlansState createState() => _CreatePlansState();
}
class _CreatePlansState extends State<CreatePlan> {
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
final args = GoRouterState.of(context).extra as Map<String, dynamic>? ?? {};
// final planData = args?['planData'];
final bool isViewMode = args?['isViewMode'] ?? false;
final Map<String, dynamic> planData = args['planData'] as Map<String, dynamic>? ?? {};
// print("isViewMode: $isViewMode");
// final bool isViewMode = true;
// final planData = GoRouterState.of(context).extra as Map<String, dynamic>? ?? {};
print("RECived palndata");
// print("RECived palndata - ${planData}");
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
Container(
color: Color(0xFFF4F4FB),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16),
child: Row(children: [
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.create_new_folder_outlined,
color: Color(0xFF84869A),
size: 23,
),
),
Text(
isViewMode
? "View Plan"
: (planData.isNotEmpty ? "Update Plan" : "New Plan"),
style: TextStyle(fontSize: 18),
),
],
),
Spacer(),
Container(
color: Color(0xFFE9EBF6),
child: IconButton(
icon: Icon(Icons.close),
onPressed: () {
context.go('/listPlan');
},
),
)
]),
),
Expanded(
child: Container(
color: Colors.white,
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(26.0),
child: CreateNewPlan(isDesktop: isDesktop, selectedPlanData: planData, isViewMode : isViewMode),
),
),
),
)
],
),
);
});
}
}
class CreateNewPlan extends StatefulWidget {
final bool isDesktop;
final bool isViewMode;
final Map<String, dynamic> selectedPlanData;
const CreateNewPlan({super.key, required this.isDesktop, required this.selectedPlanData, required this.isViewMode});
@override
_CreateNewPlansState createState() => _CreateNewPlansState();
}
class _CreateNewPlansState extends State<CreateNewPlan> {
final TextEditingController _tripTitleController = TextEditingController();
final TextEditingController _descriptionController = TextEditingController();
final FocusNode _tripTitleFocusNode = FocusNode();
final FocusNode _descriptionFocusNode = FocusNode(); // Declare FocusNode
bool _isTripTitleFocused = false;
bool _isdescriptionFocused = false;
late String _selectedOption = "Option 1";
// late String? _selectedIsBillable = "Billable";
String? selectedPlanId;
String? userDetails;
String? userName;
String? selfId;
String? otherUserName;
String? selectedplanUserId;
bool? selectedIstravelUser;
Map<String, dynamic>? apiData; // Store API response here
List<dynamic>? apiCountryData;
List<dynamic>? apiCostData; // Store API response here
bool isLoading = true; // Track loading state
String? planUsrId;
String? planTravlrId;
String? _selectedTripType;
String? selectedCostCenterId;
String? _selectedIsBillable ;
String? selectedFuncDept;
String? selectedPurpose;
Map<String, String?> validationErrors = {};
List<Map<String, dynamic>> miscellaneousList = [];
// List<Map<String, dynamic>> miscellaneousList = [{"special_request": 1, "comments": "posta", "indx": 1}];
List<Map<String, dynamic>> visaList = [];
List<Map<String, dynamic>> insuranceList = [];
List<Map<String, dynamic>> accommodationList = [];
List<Map<String, dynamic>> trainList = [];
List<Map<String, dynamic>> busList = [];
List<Map<String, dynamic>> taxiList = [];
List<Map<String, dynamic>> forexList = [];
List<Map<String, dynamic>> flightList = [];
//Getter Method
Map<String, dynamic> get planData => {
"user_id": planUsrId,
"traveller_id": planTravlrId,
"trip_title": _tripTitleController.text,
"trip_type": _selectedTripType,
"cost_center_id": selectedCostCenterId,
"is_billable": _selectedIsBillable,
"purpose_of_travel": selectedPurpose,
"description": _descriptionController.text,
"functional_department": selectedFuncDept,
"so_number": "12345",
"status": "0",
"created_by": selfId,
"updated_by": selfId,
"is_active": "1",
"flight":flightList,
"accomodation": accommodationList,
"bus": busList,
"taxi": taxiList,
"train": trainList,
"visa": visaList,
"forex": forexList,
"insurance": insuranceList,
"miscellaneous": miscellaneousList,
};
// // Function to update miscellaneous list
// void updateMiscellaneousData(List<Map<String, dynamic>> newMiscellaneousList) {
// setState(() {
// miscellaneousList = newMiscellaneousList; // Update miscellaneous data
// });
// print("Updated Miscellaneous Data in CreateNewPlan: $miscellaneousList");
// }
void handleItineraryUpdate(String type, List<Map<String, dynamic>> newList) {
setState(() {
switch (type) {
case "Miscellaneous":
miscellaneousList = newList;
break;
case "Visa":
visaList = newList;
break;
case "Insurance":
insuranceList = newList;
break;
case "Train":
trainList = newList;
break;
case "Bus":
busList = newList;
break;
case "Taxi":
taxiList = newList;
break;
case "Forex":
forexList = newList;
break;
case "Flight":
flightList = newList;
break;
case "Accomodation":
accommodationList = newList;
break;
default:
print("Unknown itinerary type: $type");
}
});
print("Updated $type List: $newList");
}
@override
void initState(){
super.initState();
fetchUserDetails();
fetchPlans();
fetchCostCenter();
fetchCountryList();
// _tripTitleController.addListener(() {
// print("Current Value: ${_tripTitleController.text}");
// });
_tripTitleFocusNode.addListener(() {
setState(() {
_isTripTitleFocused = _tripTitleFocusNode.hasFocus;
});
});
_descriptionFocusNode.addListener(() {
setState(() {
_isdescriptionFocused = _descriptionFocusNode.hasFocus;
});
});
handleUpdateData();
}
@override
void dispose() {
_tripTitleFocusNode.dispose();
_descriptionFocusNode.dispose();
super.dispose();
}
void handleUpdateData() {
if (widget.selectedPlanData != null) {
setState(() {
planUsrId = widget.selectedPlanData['user_id'] ?? '';
_tripTitleController.text = widget.selectedPlanData['trip_title'] ?? '';
_descriptionController.text = widget.selectedPlanData['description'] ?? '';
_selectedTripType = widget.selectedPlanData['trip_type'];
_selectedIsBillable = widget.selectedPlanData['is_billable'] == "1" ? "1" : "2";
// selectedCostCenterId = widget.selectedPlanData['cost_center_id']?.toString() ;
// selectedPurpose = widget.selectedPlanData['purpose_of_travel']?.toString();
// selectedFuncDept =widget.selectedPlanData['functional_department']?.toString();
if (widget.selectedPlanData!["cost_center_id"] != null) {
selectedCostCenterId = widget.selectedPlanData!["cost_center_id"].toString();
}
//
if (widget.selectedPlanData!["purpose_of_travel"] != null) {
selectedPurpose = widget.selectedPlanData!["purpose_of_travel"].toString();
}
if (widget.selectedPlanData!["functional_department"] != null) {
// selectedFuncDept = widget.selectedPlanData!["functional_department"].toString();
selectedFuncDept = widget.selectedPlanData!["functional_department"];
}
// Assign lists from selectedPlanData, ensuring they are properly formatted
flightList = List<Map<String, dynamic>>.from(widget.selectedPlanData['flight'] ?? []);
accommodationList = List<Map<String, dynamic>>.from(widget.selectedPlanData['accomodation'] ?? []);
busList = List<Map<String, dynamic>>.from(widget.selectedPlanData['bus'] ?? []);
taxiList = List<Map<String, dynamic>>.from(widget.selectedPlanData['taxi'] ?? []);
trainList = List<Map<String, dynamic>>.from(widget.selectedPlanData['train'] ?? []);
visaList = List<Map<String, dynamic>>.from(widget.selectedPlanData['visa'] ?? []);
forexList = List<Map<String, dynamic>>.from(widget.selectedPlanData['forex'] ?? []);
insuranceList = List<Map<String, dynamic>>.from(widget.selectedPlanData['insurance'] ?? []);
miscellaneousList = List<Map<String, dynamic>>.from(widget.selectedPlanData['miscellaneous'] ?? []);
});
if (widget.selectedPlanData.containsKey('plan_id') && widget.selectedPlanData['plan_id'] != null) {
print("Plan ID exists: ${widget.selectedPlanData['plan_id']}");
selectedPlanId = widget.selectedPlanData['plan_id']?.toString();
} else {
print("Plan ID is missing or null");
}
print("updatedPlanDAta - $planData");
}
}
void getSelectedPlanFor(){
if (!mounted) return;
setState(() {
if(selectedplanUserId != null){
if(selectedIstravelUser!){
planUsrId = "";
planTravlrId = selectedplanUserId;
}else {
planUsrId = selectedplanUserId;
planTravlrId = "";
}
}
else {
planUsrId = selfId;
planTravlrId = "";
}
});
print("USER ID - $planUsrId , TRAVELLER ID - $planTravlrId");
}
void fetchUserDetails() async {
final details = await getUserDetails();
print("details- $details");
if (details != null) {
setState(() {
userDetails = details.toString(); // Store the full Map
userName = details['name']; // Extract the name
selfId = details['user_id'];
});
}
print("userDetails - $selfId");
getSelectedPlanFor();
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('userId');
}
Future <Map<String,String>?> getUserDetails() async{
final prefs = await SharedPreferences.getInstance();
final userData = prefs.getString('user_data');
if(userData!= null){
final decodedData = jsonDecode(userData);
return {
'user_id': decodedData['user_id'].toString(),
'name': "${decodedData['first_name']} ${decodedData['last_name']}",
};
}
return null;
}
Future<void> fetchPlans() 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<String, dynamic> plansJson = data['data']; // 'data' is a Map, not a List
setState(() {
apiData = plansJson; // Store API response in state
isLoading = false;
});
} catch (e) {
throw Exception('Error parsing response: $e');
}
} else {
throw Exception('Failed to load plans');
}
}
Future<void> 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 <dynamic> 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');
} catch (e) {
throw Exception('Error parsing response: $e');
}
} else {
throw Exception('Failed to load plans');
}
}
Future<void> fetchCountryList() async {
final String apiUrldata = '$apiUrl/api/getcountryMaster';
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("Country - $data");
if (!data.containsKey('data') || data['data'] is!List) {
throw Exception("Invalid response format: 'data' field is missing or not a List");
}
List <dynamic> plansJson = data['data']; // 'data' is a Map, not a List
if (data['data'] is List) {
List<dynamic> plansJson = data['data'];
print("plansJson.length - ${plansJson.length}");
} else {
print("The 'data' key does not contain a list.");
}
setState(() {
apiCountryData = plansJson; // Store API response in state
});
print('plansJSONContry - $plansJson');
} catch (e) {
throw Exception('Error parsing response: $e');
}
} else {
throw Exception('Failed to load plans');
}
}
// Handle Submit
bool validateForm(){
validationErrors.clear(); // Clear previous errors
// Ensure either "user_id" or "traveller_id" is provided
if ((planUsrId == null || planUsrId!.isEmpty) && (planTravlrId == null || planTravlrId!.isEmpty)) {
validationErrors["user_id"] = "Either User ID or Traveller ID is required";
validationErrors["traveller_id"] = "Either User ID or Traveller ID is required";
}
final requiredFields = {
"trip_type": _selectedTripType,
"cost_center_id": selectedCostCenterId,
"functional_department": selectedFuncDept,
"purpose_of_travel": selectedPurpose,
};
for (var entry in requiredFields.entries) {
if (entry.value == null || entry.value!.isEmpty) {
validationErrors[entry.key] = "${entry.key.replaceAll('_', ' ').toUpperCase()} is required";
}
}
return validationErrors.isEmpty; // Returns true if no errors
}
void handleSubmit() {
setState(() {
if(validateForm()){
print("Form submitted successfully: $planData");
postPlanData(planData);
context.go('/listPlan');
}
});
}
Future<void> postPlanData(Map<String, dynamic> planData) async {
final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan';
final token = await getToken(); // Fetch token
if (token == null) {
throw Exception('Token not found. Please log in.');
}
if (selectedPlanId != null && selectedPlanId!.isNotEmpty) {
planData['plan_id'] = selectedPlanId; // Add plan_id for update
}
try {
final response = await http.post(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode(planData), // Convert map to JSON
);
if (response.statusCode == 200) {
print("Plan submitted successfully!");
print("Response: ${response.body}");
} else {
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
}
} catch (e) {
print(" Error submitting plan: $e");
}
}
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text(
// "Plan This Trip For : ${userName} ", // Your label
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74)),
// ),
Text.rich(
TextSpan(
text: "Plan This Trip For: ", // Static text
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74), // Default color
),
children: [
TextSpan(
text: otherUserName ?? userName ?? " ", // Dynamic username
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.blueAccent, // Change this to any color
),
),
],
),
),
SizedBox(height: 7),
isMobile
? SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: _buildPlanTrip(isMobile),
),
)
: Row(
children: _buildPlanTrip(isMobile),
),
],
),
),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Divider(
color: Color(0xFFE6E7F5), // Change color
thickness: 0.5,
),
),
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Trip Title", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isTripTitleFocused,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _tripTitleFocusNode,
controller: _tripTitleController,
style: TextStyle(fontSize: 12),
enabled: !widget.isViewMode,
decoration: InputDecoration(
labelText: "Trip Title",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
],
),
SizedBox(
height: 10,
),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Trip Type *", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
isMobile
? SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: _buildTripType(isMobile),
),
)
: Row(
children: _buildTripType(isMobile),
),
if (validationErrors["trip_type"] != null)
Padding(
padding: EdgeInsets.only(top: 4),
child: Text(
validationErrors["trip_type"]!,
style: TextStyle(color: Colors.red, fontSize: 10),
),
),
],
),
),
],
),
SizedBox(
height: 15,
),
isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildCostIsBillable())
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildCostIsBillable()),
SizedBox(
height: 8,
),
isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildNonDescriptionColumn(),
SizedBox(width: 25),
_buildDescriptionColumn(isDesktop),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildNonDescriptionColumn(),
SizedBox(height: 15),
_buildDescriptionColumn(isDesktop),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Divider(
color: Color(0xFFE6E7F5), // Change color
thickness: 0.5,
),
),
Row(
children: [
Expanded(child: DynamicItinerary(apiData: apiData, apiCountryData: apiCountryData,
onItineraryUpdate: handleItineraryUpdate,loginUser: selfId,selectedPlanData: planData, isViewMode:widget.isViewMode ,
)), // Wrap with Expanded if needed
],
),
SizedBox(height: 15),
isDesktop
? Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _buildSubmit(isDesktop),)
:Row(
mainAxisAlignment: MainAxisAlignment.center,
children: _buildSubmit(isDesktop),)
],
);
});
}
/// Extracted helper function
List<Widget> _buildCostIsBillable() {
List<dynamic> purposeList = apiData?['plan_is_billable'] ?? [];
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Cost Center *", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus
isDesktop: widget.isDesktop,
child: SizedBox(
height: 45, // Set appropriate height
child: DropdownButtonFormField<String>(
value: selectedCostCenterId,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(horizontal: 10), // Proper padding
),
onChanged: widget.isViewMode ? null : (newValue) {
setState(() {
selectedCostCenterId = newValue;
});
},
items:apiCostData?.map<DropdownMenuItem<String>>((item){
return DropdownMenuItem(
value: item['department_id'], // ID as value
child: Text(item['name'] ?? "Unknown"),
);
}).toList(),
),
),
),
],
),
SizedBox(width: 25,height: 5,),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Is Billable ", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
Row(
children: purposeList.map<Widget>((item) {
return Row(
children: [
Radio<String>(
// value: item['dropdown_key'], // Use dropdown_value as value
value: item['dropdown_key'].toString(),// Convert to String
groupValue: _selectedIsBillable,
activeColor: Colors.blueAccent,
onChanged: widget.isViewMode ? null :(value) {
setState(() {
_selectedIsBillable = value;
});
print("SELECBILL - $_selectedIsBillable");
},
),
Text(item['dropdown_value'] ?? ''), // Display dropdown_value
SizedBox(width: 20), // Spacing
],
);
}).toList(),
),
])
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Is Billable ", // Your label
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74)),
// ),
// SizedBox(height: 5),
// Row(
// children: [
// Row(
// children: [
// Radio<String>(
// value: "Billable",
// groupValue: _selectedIsBillable,
// activeColor: Colors.blueAccent,
// onChanged: (value) {
// setState(() {
// _selectedIsBillable = value;
// });
// },
// ),
// Text("Billable"),
// ],
// ),
// SizedBox(width: 20), // Spacing
// Row(
// children: [
// Radio<String>(
// value: "Non Billable",
// groupValue: _selectedIsBillable,
// activeColor: Colors.blueAccent,
// onChanged: (value) {
// setState(() {
// _selectedIsBillable = value;
// });
// },
// ),
// Text("Non Billable"),
// ],
// ),
// ],
// ),
// ],
// )
];
}
List<Widget> _buildPlanTrip(bool isDesktop) {
List<Map<String, String>> options = [
{"title": "Self", "value": "Option 1"},
{"title": "Other Employee", "value": "Option 2"},
{"title": "Others", "value": "Option 3"},
];
return options.map((option) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: CustomTextFieldWrapper(
color: Color(0xFFF4F4FB),
width: option["value"] == "Option 2" ? 175 : 120, // Adjust width conditionally
isFocused: _selectedOption == option["value"],
isDesktop: isDesktop,
child: RadioListTile<String>(
activeColor: Colors.blueAccent,
contentPadding: EdgeInsets.zero,
dense: true,
title: Text(option["title"]!),
value: option["value"]!,
groupValue: _selectedOption,
onChanged: widget.isViewMode ? null : (value) {
setState(() {
_selectedOption = value!;
if(value == 'Option 2' || value == 'Option 3' ){
_showInputDialog(option["title"]!);
}
});
},
),
),
);
}).toList();
}
List<Widget> _buildTripType(bool isMobile) {
return [
CustomTextFieldWrapper(
color: Color(0xFFF4F4FB),
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
width: 120,
isFocused: _selectedTripType == "1",
isDesktop: widget.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"),
value: "1",
groupValue: _selectedTripType,
onChanged: widget.isViewMode ? null : (value) {
setState(() {
_selectedTripType = value!;
});
},
),
),
),
),
SizedBox(width: 20),
CustomTextFieldWrapper(
color: Color(0xFFF4F4FB),
width: 150,
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2),
isFocused: _selectedTripType == "2",
isDesktop: widget.isDesktop,
child: 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!;
});
},
),
),
];
}
Widget _buildNonDescriptionColumn (){
// if (apiData == null) {
// return Center(child: CircularProgressIndicator()); // Show loading indicator
// }
// 'plan_purpose_of_travel' Starts ------------------------------------------------------
List<dynamic> purposeList = apiData?['plan_purpose_of_travel'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
// value: item['dropdown_key'],
value: item['dropdown_key']?.toString(),
// value: item['dropdown_key'].toString(),
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
// value: null,
value: "1",
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Ensure Selected Value Exists in the Dropdown List
List<String> dropdownKeys = dropdownItems.map((e) => e.value ?? "").toList();
selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value.toString() : "No options";
print("Dropdown Purpose List: ${dropdownItems.map((e) => e.value).toList()}");
print("Selected Purpose: $selectedPurpose");
// 'plan_functional_department' Starts ---------------------------------------------
List<dynamic> funcDeptList = apiData?['plan_functional_department'] ?? [];
List<DropdownMenuItem<String>> dropdownFuncDeptItems = funcDeptList
.map((item)=>DropdownMenuItem<String>(
// value: item['dropdown_key'],
value: item['dropdown_key']?.toString(),
child: Text(item['dropdown_value']),
)).toList();
if (dropdownFuncDeptItems.isEmpty) {
dropdownFuncDeptItems.add(
DropdownMenuItem<String>(
// value: null,
value: "1",
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// selectedFuncDept ??= dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value.toString() : null;
selectedFuncDept ??= dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value.toString() : "No options";
print("Dropdown Functional Department List: ${dropdownFuncDeptItems.map((e) => e.value).toList()}");
print("Selected Functional Department: $selectedFuncDept");
return Column(
children: [
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Purpose of Travel *", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus
isDesktop: widget.isDesktop,
child: SizedBox(
height: 45, // Set appropriate height
child: apiData == null
? Center(child: CircularProgressIndicator()) // Show loading inside dropdown
:
DropdownButtonFormField<String>(
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: widget.isViewMode ? null : purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
print("selectedPurpose - $selectedPurpose");
}
: null,
items: dropdownItems,
),
),
),
],
),
],
),
SizedBox(
height: 8,
),
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Functional Department", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus
isDesktop: widget.isDesktop,
child: SizedBox(
height: 45, // Set appropriate height
child:
apiData == null
? Center(child: CircularProgressIndicator()) // Show loading inside dropdown
:DropdownButtonFormField<String>(
value: selectedFuncDept,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: widget.isViewMode ? null : funcDeptList.isNotEmpty
? (newValue) {
setState(() {
selectedFuncDept = newValue;
});
}
: null,
items: dropdownFuncDeptItems,
),
),
),
],
),
],
),
SizedBox(
height: 15,
),
]
);
}
Widget _buildDescriptionColumn(isDesktop){
return Column(
children: [
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Description", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isdescriptionFocused,
width: isDesktop? MediaQuery.of(context).size.width * 0.5 :
MediaQuery.of(context).size.width * 0.85 ,
isDesktop: widget.isDesktop,
child: TextField(
focusNode: _descriptionFocusNode,
controller: _descriptionController,
maxLines: 6,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
enabled: !widget.isViewMode,
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
),
],
),
],
);
}
List<Widget> _buildSubmit(isDesktop){
return[
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.blueAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Colors.blueAccent, width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: (){
context.go('/listPlan');
},
child: Text("Cancel")
),
SizedBox(width: 20,),
MouseRegion(
cursor: widget.isViewMode ? SystemMouseCursors.forbidden : SystemMouseCursors.click,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: widget.isViewMode ? Colors.blueAccent : Colors.blueAccent, // Keep original color
foregroundColor: widget.isViewMode ? Colors.white : Colors.white, // Keep original color
disabledBackgroundColor: Colors.blueAccent, // Ensure color remains when disabled
disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Colors.blueAccent, width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: widget.isViewMode ? null : handleSubmit, // Disable when in view mode
child: Text("Submit"),
),
)
];
}
void _showInputDialog(String title){
showDialog(
context: context,
builder: (BuildContext context){
return UserSelectionDialog(
title:title,
onSubmit: (input,userId,isTraveller){
setState(() {
otherUserName = input;
selectedplanUserId = userId;
selectedIstravelUser = isTraveller;
});
print("USer entered : $otherUserName $userId $isTraveller");
getSelectedPlanFor();
}
);
}
);
}
}