ts-tat/lib/Screens/itnerary/miscellaneous.dart
2025-10-27 17:35:57 +05:30

524 lines
17 KiB
Dart

import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class MiscellaneousScreen extends StatefulWidget {
final Map<String, dynamic>? apiData;
final Function(String, bool) onClose;
final Function(Map<String, dynamic>) onSaveMiscellaneous;
final Map<String, dynamic>? selectedItem;
final int? selectedIndex;
final String? loginUser;
MiscellaneousScreen({
required this.onClose,
required this.apiData,
required this.onSaveMiscellaneous,
this.selectedItem,
this.selectedIndex,
required this.loginUser,
});
@override
_MiscellaneousScreenState createState() => _MiscellaneousScreenState();
}
class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Map<String, String?> selectedValues = {};
final FocusNode _tripTypeFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late Map<String, TextEditingController> _controllers;
late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _commentsController = TextEditingController();
bool _hotelNameFocused = false;
bool _commentsFocus = false;
String? selectedSpecialType;
Color layoutColor = Colors.grey;
Map<String, String> errorMessages = {};
Map<String, dynamic> get miscellaneousData {
Map<String, dynamic> data = {
"special_request": selectedSpecialType,
"comments": _commentsController.text,
"created_by": widget.loginUser,
"updated_by": widget.loginUser,
};
if (widget.selectedItem != null) {
if (widget.selectedItem?["indx"] != null &&
widget.selectedItem?["indx"] != 0) {
data["indx"] = widget.selectedItem!["indx"];
} else if (widget.selectedItem?["miscellaneous_id"] != null &&
widget.selectedItem?["miscellaneous_id"] != 0) {
data["miscellaneous_id"] = widget.selectedItem!["miscellaneous_id"];
}
}
return data;
}
@override
void initState() {
super.initState();
if (widget.selectedItem == null) {
_commentsController.clear();
}
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
_hotelNameFocusNode.addListener(() {
setState(() {
_hotelNameFocused = _hotelNameFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
setState(() {
_commentsFocus = _commentsFocusNode.hasFocus;
});
});
_commentsController = TextEditingController(
text: widget.selectedItem?["comments"] ?? "",
);
// Set the selected value if available
if (widget.selectedItem != null &&
widget.selectedItem!["special_request"] != null) {
selectedSpecialType = widget.selectedItem!["special_request"].toString();
}
}
@override
void dispose() {
_tripTypeFocusNode.dispose();
_hotelNameFocusNode.dispose();
_tripTypeController.dispose();
_commentsController.dispose();
super.dispose();
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
// setState(() {
// layoutColor =
// layoutString != null
// ? Color(int.parse(layoutString))
// : Colors.redAccent;
// });
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
}
bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors
// Required fields that must not be empty
List<String> requiredFields = ["special_request", "comments"];
// Check validation for each field
for (String field in requiredFields) {
if (data[field] == null || data[field].toString().trim().isEmpty) {
errorMessages[field] = "Required";
}
}
return errorMessages.isEmpty; // Valid if there are no errors
}
void handleSave() {
print("Handle Save miscellaneousData $miscellaneousData");
Map<String, dynamic> data = miscellaneousData;
if (!isValidData(data)) {
print("Validation Failed: Required fields are missing.");
setState(() {});
return; // Stop execution if validation fails
} else {
widget.onSaveMiscellaneous(miscellaneousData); // Send object to parent
}
widget.onClose("Miscellaneous", false); // Close screen after saving
// widget.onClose(false); // Close screen after saving
// Clear only if this is a new entry
// if (widget.selectedItem == null) {
// _commentsController.clear();
// }
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
),
],
),
),
),
);
},
);
}
List<Widget> _buildAccomadtionForm(bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) {
return [
isDesktop ? Row(children: children) : Column(children: children),
SizedBox(height: 10),
];
}
return [
...buildResponsiveRow(_buildFirstRow(isDesktop)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Special Type *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
isDesktop
? Row(children: _buildTripType(isDesktop))
: Column(children: _buildTripType(isDesktop)),
],
),
];
}
List<Widget> _buildTripType(bool isDesktop) {
List<dynamic> purposeList =
widget.apiData?['miscellaneous_special_request'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
List<DropdownMenuItem<String>> dropdownItems =
purposeList
.map(
(item) => DropdownMenuItem<String>(
value: item['dropdown_key'],
child: Text(item['dropdown_value']),
),
)
.toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text(
"No options available",
style: TextStyle(color: Colors.grey),
),
),
);
}
// Default selected value
selectedSpecialType ??=
dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options";
return [
// CustomTextFieldWrapper(
// isFocused: _isHotelNameFocused,
// isDesktop: isDesktop,
// width:
// isDesktop
// ? MediaQuery.of(context).size.width * 0.34
// : MediaQuery.of(context).size.width * 0.66,
// child: SizedBox(
// height: 40,
// child: DropdownButtonFormField<String>(
// focusNode: _tripTypeFocusNode, // Assign the correct focus node
// value: selectedSpecialType,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ), // Proper padding
// ),
// onChanged:
// purposeList.isNotEmpty
// ? (newValue) {
// setState(() {
// selectedSpecialType = newValue;
// });
//
// print(selectedSpecialType);
// }
// : null,
// items: dropdownItems,
// ),
// ),
// ),
CustomTextFieldItnerarySubWrapper(
isFocused: _hotelNameFocused,
isDesktop: isDesktop,
padding: const EdgeInsets.symmetric(horizontal: 0),
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: null, //MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
width: double.infinity,
child: Focus(
focusNode: _hotelNameFocusNode,
onFocusChange: (hasFocus) {
setState(() {
_hotelNameFocused = hasFocus;
});
},
child: GestureDetector(
//
onTap: () {
// Request focus when user taps
_hotelNameFocusNode?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>(
// focusNode: _taxiReqFocusNode,
items: purposeList.cast<Map<String, dynamic>>(),
selectedItem: purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedSpecialType,
orElse: () => {},
),
itemAsString: (item) => item['dropdown_value'] ?? '',
popupProps: PopupProps.menu(
showSearchBox: false,
fit: FlexFit.loose,
menuProps: const MenuProps(backgroundColor: Colors.white),
itemBuilder: (context, item, isSelected) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
child: Text(
item['dropdown_value'] ?? '',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
);
},
),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4),
borderSide: BorderSide(
color:
(_hotelNameFocused ?? false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(_hotelNameFocused ?? false)
? layoutColor
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor, width: 0.5),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
),
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) {
return Text(
"Select ",
style: GoogleFonts.poppins(
color: Colors.grey,
fontSize: 13,
),
);
}
return Text(
selectedItem['dropdown_value'] ?? '',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
);
},
onChanged:
purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedSpecialType = newValue as String?;
});
print("Updating form data: -> ${newValue ?? ""}");
}
: null,
),
),
),
),
),
if (errorMessages["special_request"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
// CustomTextFieldWrapper
CustomTextFieldItnerarySubWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width:
isDesktop
? MediaQuery.of(context).size.width * 0.34
: null, //MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: TextField(
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')),
],
focusNode: _commentsFocusNode,
controller: _commentsController,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Comments",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
if (errorMessages["comments"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
if (isDesktop) Spacer(),
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
_commentsController.clear();
widget.onClose("Miscellaneous", false);
// widget.onClose(false); // Close the dialog or screen
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Close",
style: GoogleFonts.poppins(color: Colors.white, fontSize: 12),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: GoogleFonts.poppins(color: Colors.white, fontSize: 12),
),
),
];
}
}