itinerary changes
This commit is contained in:
parent
15992c4008
commit
5c628282c6
@ -6,6 +6,7 @@ import '../../widgets/custom_text_field.dart';
|
|||||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||||
|
|
||||||
class AccomodationScreen extends StatefulWidget {
|
class AccomodationScreen extends StatefulWidget {
|
||||||
|
final List<Map<String, dynamic>> flightData;
|
||||||
final Function(bool) onClose; // Callback function
|
final Function(bool) onClose; // Callback function
|
||||||
final Function(Map<String, dynamic>) onSaveAccomadation;
|
final Function(Map<String, dynamic>) onSaveAccomadation;
|
||||||
final Map<String, dynamic>? selectedItem;
|
final Map<String, dynamic>? selectedItem;
|
||||||
@ -15,13 +16,17 @@ class AccomodationScreen extends StatefulWidget {
|
|||||||
{required this.onClose,
|
{required this.onClose,
|
||||||
required this.onSaveAccomadation,
|
required this.onSaveAccomadation,
|
||||||
required this.selectedItem,
|
required this.selectedItem,
|
||||||
required this.loginUser});
|
required this.loginUser,
|
||||||
|
required this.flightData});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_AccomodationScreenState createState() => _AccomodationScreenState();
|
_AccomodationScreenState createState() => _AccomodationScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AccomodationScreenState extends State<AccomodationScreen> {
|
class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||||
|
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||||
|
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||||
|
|
||||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
final FocusNode _destinationFocusNode = FocusNode();
|
final FocusNode _destinationFocusNode = FocusNode();
|
||||||
@ -116,6 +121,30 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
_checkInTimeController.addListener(() => _clearError("checkin_time"));
|
_checkInTimeController.addListener(() => _clearError("checkin_time"));
|
||||||
_checkOutController.addListener(() => _clearError("checkout_date"));
|
_checkOutController.addListener(() => _clearError("checkout_date"));
|
||||||
_checkOutTimeController.addListener(() => _clearError("checkout_time"));
|
_checkOutTimeController.addListener(() => _clearError("checkout_time"));
|
||||||
|
|
||||||
|
flightFirstTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
flightLastTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
final result = getFlightTripDateRange(widget.flightData);
|
||||||
|
flightFirstTripDateNotifier.value = result['firstTripDate'];
|
||||||
|
flightLastTripDateNotifier.value = result['lastTripDate'];
|
||||||
|
|
||||||
|
// ✅ Only set controller after value is updated
|
||||||
|
final parsedDate =
|
||||||
|
DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
|
||||||
|
if (parsedDate != null) {
|
||||||
|
_checkInController.text = DateFormat('yyyy-MM-dd').format(parsedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Check and set default times if empty
|
||||||
|
if (_checkInTimeController.text.isEmpty) {
|
||||||
|
_checkInTimeController.text = '14:00'; // 2 PM
|
||||||
|
}
|
||||||
|
if (_checkOutTimeController.text.isEmpty) {
|
||||||
|
_checkOutTimeController.text = '12:00'; // 12 PM
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -139,6 +168,35 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String?> getFlightTripDateRange(
|
||||||
|
List<Map<String, dynamic>> flightData) {
|
||||||
|
final allTrips = flightData
|
||||||
|
.expand((flight) => flight['trips'] ?? [])
|
||||||
|
.whereType<Map<String, dynamic>>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (allTrips.isEmpty) {
|
||||||
|
return {
|
||||||
|
'firstTripDate': null,
|
||||||
|
'lastTripDate': null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
allTrips.sort((a, b) {
|
||||||
|
final aDate = DateTime.tryParse(a['date'] ?? '') ?? DateTime(1900);
|
||||||
|
final bDate = DateTime.tryParse(b['date'] ?? '') ?? DateTime(1900);
|
||||||
|
return aDate.compareTo(bDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
final firstTrip = allTrips.first;
|
||||||
|
final lastTrip = allTrips.last;
|
||||||
|
|
||||||
|
return {
|
||||||
|
'firstTripDate': firstTrip['date'],
|
||||||
|
'lastTripDate': lastTrip['date'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
bool isValidData(Map<String, dynamic> data) {
|
bool isValidData(Map<String, dynamic> data) {
|
||||||
errorMessages.clear(); // Reset errors
|
errorMessages.clear(); // Reset errors
|
||||||
|
|
||||||
@ -159,6 +217,55 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Additional validation: checkout_date >= checkin_date
|
||||||
|
final checkIn = data["checkin_date"];
|
||||||
|
final checkInTime = data["checkin_time"];
|
||||||
|
final checkOut = data["checkout_date"];
|
||||||
|
final checkOutTime = data["checkout_time"];
|
||||||
|
|
||||||
|
if (checkIn != null &&
|
||||||
|
checkOut != null &&
|
||||||
|
checkIn.toString().isNotEmpty &&
|
||||||
|
checkOut.toString().isNotEmpty) {
|
||||||
|
try {
|
||||||
|
final checkInDate = DateTime.parse(checkIn);
|
||||||
|
final checkOutDate = DateTime.parse(checkOut);
|
||||||
|
|
||||||
|
if (checkOutDate.isBefore(checkInDate)) {
|
||||||
|
errorMessages["checkout_date"] =
|
||||||
|
"Check-out date cannot be before check-in date";
|
||||||
|
} else if (checkOutDate.isAtSameMomentAs(checkInDate)) {
|
||||||
|
// If dates are same, check the times
|
||||||
|
if (checkInTime != null &&
|
||||||
|
checkOutTime != null &&
|
||||||
|
checkInTime.toString().isNotEmpty &&
|
||||||
|
checkOutTime.toString().isNotEmpty) {
|
||||||
|
try {
|
||||||
|
final checkInDateTime =
|
||||||
|
DateTime.parse("${checkIn}T${checkInTime}");
|
||||||
|
final checkOutDateTime =
|
||||||
|
DateTime.parse("${checkOut}T${checkOutTime}");
|
||||||
|
|
||||||
|
if (!checkOutDateTime.isAfter(checkInDateTime)) {
|
||||||
|
errorMessages["checkout_time"] =
|
||||||
|
"Check-out must be after check-in time";
|
||||||
|
} else {
|
||||||
|
final difference = checkOutDateTime.difference(checkInDateTime);
|
||||||
|
if (difference.inMinutes < 30) {
|
||||||
|
errorMessages["checkout_time"] =
|
||||||
|
"Check-out must be at least 30 minutes after check-in";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
errorMessages["checkout_time"] = "Invalid time format";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
errorMessages["checkout_date"] = "Invalid date format";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return errorMessages.isEmpty; // Valid if there are no errors
|
return errorMessages.isEmpty; // Valid if there are no errors
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,35 +292,13 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Color(0xFFF4F4FB),
|
// color: Color(0xFFF4F4FB),
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
print("Close icon clicked");
|
|
||||||
widget.onClose(false);
|
|
||||||
},
|
|
||||||
child: Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 18,
|
|
||||||
color: Color(0xFF575A74),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text("Accomodation Booking",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF575A74))),
|
|
||||||
SizedBox(
|
|
||||||
height: 6,
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(28.0),
|
padding: const EdgeInsets.all(28.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -256,17 +341,26 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
: Column(
|
: Column(
|
||||||
children: _buildThirdRow(isDesktop),
|
children: _buildThirdRow(isDesktop),
|
||||||
),
|
),
|
||||||
|
|
||||||
SizedBox(height: 10),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: _handleAction(isDesktop),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildFirstRow(isDesktop) {
|
List<Widget> _buildFirstRow(isDesktop) {
|
||||||
return [
|
return [
|
||||||
|
// Column(
|
||||||
|
// children: [
|
||||||
|
// ValueListenableBuilder<String?>(
|
||||||
|
// valueListenable: flightFirstTripDateNotifier,
|
||||||
|
// builder: (context, value, child) =>
|
||||||
|
// Text("First Trip Date: ${value ?? 'Not available'}"),
|
||||||
|
// ),
|
||||||
|
// ValueListenableBuilder<String?>(
|
||||||
|
// valueListenable: flightLastTripDateNotifier,
|
||||||
|
// builder: (context, value, child) =>
|
||||||
|
// Text("Last Trip Date: ${value ?? 'Not available'}"),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -368,16 +462,41 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
DateTime today = DateTime(now.year, now.month, now.day);
|
DateTime today = DateTime(now.year, now.month, now.day);
|
||||||
|
|
||||||
DateTime? pickedDate = await showDatePicker(
|
// Parse date from notifier if available, else use today
|
||||||
|
DateTime initialDate;
|
||||||
|
if (flightFirstTripDateNotifier.value != null) {
|
||||||
|
try {
|
||||||
|
initialDate = DateTime.parse(flightFirstTripDateNotifier.value!);
|
||||||
|
} catch (e) {
|
||||||
|
initialDate = today;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initialDate = today;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use previously selected date if valid
|
||||||
|
if (_selectedCheckInDate != null &&
|
||||||
|
_selectedCheckInDate!.isAfter(today)) {
|
||||||
|
initialDate = _selectedCheckInDate!;
|
||||||
|
}
|
||||||
|
|
||||||
|
final pickedDate = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate:
|
initialDate: initialDate,
|
||||||
_selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today)
|
firstDate: initialDate,
|
||||||
? _selectedCheckInDate!
|
|
||||||
: today,
|
|
||||||
firstDate: today,
|
|
||||||
lastDate: DateTime(2100),
|
lastDate: DateTime(2100),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// DateTime? pickedDate = await showDatePicker(
|
||||||
|
// context: context,
|
||||||
|
// initialDate:
|
||||||
|
// _selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today)
|
||||||
|
// ? _selectedCheckInDate!
|
||||||
|
// : today,
|
||||||
|
// firstDate: today,
|
||||||
|
// lastDate: DateTime(2100),
|
||||||
|
// );
|
||||||
|
|
||||||
if (pickedDate != null && pickedDate != _selectedCheckInDate) {
|
if (pickedDate != null && pickedDate != _selectedCheckInDate) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedCheckInDate = pickedDate;
|
_selectedCheckInDate = pickedDate;
|
||||||
@ -410,17 +529,57 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
DateTime? _selectedCheckOutDate;
|
DateTime? _selectedCheckOutDate;
|
||||||
TimeOfDay? _selectedCheckOutTime;
|
TimeOfDay? _selectedCheckOutTime;
|
||||||
|
|
||||||
|
// Future<void> _selectCheckOutDate(BuildContext context) async {
|
||||||
|
// DateTime now = DateTime.now();
|
||||||
|
// DateTime today = DateTime(now.year, now.month, now.day);
|
||||||
|
//
|
||||||
|
// DateTime? pickedDate = await showDatePicker(
|
||||||
|
// context: context,
|
||||||
|
// initialDate: _selectedCheckOutDate != null &&
|
||||||
|
// _selectedCheckOutDate!.isAfter(today)
|
||||||
|
// ? _selectedCheckOutDate!
|
||||||
|
// : today,
|
||||||
|
// firstDate: today,
|
||||||
|
// lastDate: DateTime(2100),
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
||||||
|
// setState(() {
|
||||||
|
// _selectedCheckOutDate = pickedDate;
|
||||||
|
// _checkOutController.text =
|
||||||
|
// DateFormat('yyyy-MM-dd').format(pickedDate);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
Future<void> _selectCheckOutDate(BuildContext context) async {
|
Future<void> _selectCheckOutDate(BuildContext context) async {
|
||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
DateTime today = DateTime(now.year, now.month, now.day);
|
DateTime today = DateTime(now.year, now.month, now.day);
|
||||||
|
|
||||||
DateTime? pickedDate = await showDatePicker(
|
DateTime? checkInDate;
|
||||||
|
try {
|
||||||
|
checkInDate = DateTime.parse(_checkInController.text);
|
||||||
|
} catch (e) {
|
||||||
|
checkInDate = today;
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Ensure at least today is used
|
||||||
|
// DateTime firstDate = checkInDate.isAfter(today) ? checkInDate : today;
|
||||||
|
// DateTime initialDate = _selectedCheckOutDate != null &&
|
||||||
|
// _selectedCheckOutDate!.isAfter(firstDate)
|
||||||
|
// ? _selectedCheckOutDate!
|
||||||
|
// : firstDate;
|
||||||
|
|
||||||
|
DateTime firstDate = checkInDate;
|
||||||
|
DateTime initialDate = _selectedCheckOutDate != null &&
|
||||||
|
_selectedCheckOutDate!.isAfter(firstDate)
|
||||||
|
? _selectedCheckOutDate!
|
||||||
|
: firstDate;
|
||||||
|
|
||||||
|
final pickedDate = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: _selectedCheckOutDate != null &&
|
initialDate: initialDate,
|
||||||
_selectedCheckOutDate!.isAfter(today)
|
firstDate: firstDate,
|
||||||
? _selectedCheckOutDate!
|
|
||||||
: today,
|
|
||||||
firstDate: today,
|
|
||||||
lastDate: DateTime(2100),
|
lastDate: DateTime(2100),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -678,23 +837,36 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
width: isDesktop
|
width: isDesktop
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
? MediaQuery.of(context).size.width * 0.34
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
: MediaQuery.of(context).size.width * 0.66,
|
||||||
child: TextField(
|
child: SizedBox(
|
||||||
focusNode: _commentsFocusNode,
|
height: 40,
|
||||||
controller: _commentsController,
|
child: TextField(
|
||||||
maxLines: 6,
|
focusNode: _commentsFocusNode,
|
||||||
keyboardType: TextInputType.multiline,
|
controller: _commentsController,
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Description",
|
labelText: "Comments",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
if (isDesktop) Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: _handleAction(isDesktop),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -199,34 +199,34 @@ class _BusScreenState extends State<BusScreen> {
|
|||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Color(0xFFF4F4FB),
|
// color: Colors.white,
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Align(
|
// Align(
|
||||||
alignment: Alignment.centerRight,
|
// alignment: Alignment.centerRight,
|
||||||
child: InkWell(
|
// child: InkWell(
|
||||||
onTap: () {
|
// onTap: () {
|
||||||
widget.onClose(false);
|
// widget.onClose(false);
|
||||||
},
|
// },
|
||||||
child: Icon(
|
// child: Icon(
|
||||||
Icons.close,
|
// Icons.close,
|
||||||
size: 18,
|
// size: 18,
|
||||||
color: Color(0xFF575A74),
|
// color: Color(0xFF575A74),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
Text("Bus Booking List",
|
// Text("Bus Booking List",
|
||||||
style: TextStyle(
|
// style: TextStyle(
|
||||||
fontSize: 18,
|
// fontSize: 18,
|
||||||
fontWeight: FontWeight.bold,
|
// fontWeight: FontWeight.bold,
|
||||||
color: Color(0xFF575A74))),
|
// color: Color(0xFF575A74))),
|
||||||
SizedBox(
|
// SizedBox(
|
||||||
height: 6,
|
// height: 6,
|
||||||
),
|
// ),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(28.0),
|
padding: const EdgeInsets.all(28.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -263,10 +263,6 @@ class _BusScreenState extends State<BusScreen> {
|
|||||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||||
|
|
||||||
// Actions row remains a Row
|
// Actions row remains a Row
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: _handleAction(isDesktop),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,26 +603,34 @@ class _BusScreenState extends State<BusScreen> {
|
|||||||
CustomTextFieldWrapper(
|
CustomTextFieldWrapper(
|
||||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
width: isDesktop
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null,
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
child: SizedBox(
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
focusNode: _commentsFocusNode,
|
focusNode: _commentsFocusNode,
|
||||||
controller: _buscommentsController,
|
controller: _buscommentsController,
|
||||||
maxLines: 6,
|
style: TextStyle(fontSize: 12),
|
||||||
keyboardType: TextInputType.multiline,
|
decoration: InputDecoration(
|
||||||
style: TextStyle(fontSize: 12),
|
labelText: "Comments",
|
||||||
decoration: InputDecoration(
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
labelText: "Comments",
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
border: InputBorder.none,
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
border: InputBorder.none,
|
),
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
if (isDesktop) Spacer(),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: _handleAction(isDesktop),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'package:dropdown_search/dropdown_search.dart';
|
import 'package:dropdown_search/dropdown_search.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:frontend/services/apiService.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:responsive_builder/responsive_builder.dart';
|
import 'package:responsive_builder/responsive_builder.dart';
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ import '../../widgets/custom_text_field.dart';
|
|||||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||||
|
|
||||||
class FlightScreen extends StatefulWidget {
|
class FlightScreen extends StatefulWidget {
|
||||||
|
final List<Map<String, dynamic>> flightData;
|
||||||
final Map<String, dynamic>? apiData;
|
final Map<String, dynamic>? apiData;
|
||||||
final String? loginUser;
|
final String? loginUser;
|
||||||
final Function(bool) onClose;
|
final Function(bool) onClose;
|
||||||
@ -19,15 +21,23 @@ class FlightScreen extends StatefulWidget {
|
|||||||
required this.loginUser,
|
required this.loginUser,
|
||||||
required this.onClose,
|
required this.onClose,
|
||||||
required this.onSaveFlight,
|
required this.onSaveFlight,
|
||||||
required this.selectedItem});
|
required this.selectedItem,
|
||||||
|
required this.flightData});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_FlightScreenState createState() => _FlightScreenState();
|
_FlightScreenState createState() => _FlightScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FlightScreenState extends State<FlightScreen> {
|
class _FlightScreenState extends State<FlightScreen> {
|
||||||
|
ApiService apiService = ApiService();
|
||||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||||
|
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||||
|
|
||||||
|
String? selectedCountry;
|
||||||
|
List<Map<String, dynamic>> countryList = [];
|
||||||
|
|
||||||
Map<String, String?> selectedValues = {};
|
Map<String, String?> selectedValues = {};
|
||||||
|
|
||||||
String? selectedTripType;
|
String? selectedTripType;
|
||||||
@ -60,6 +70,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
// _initializeRows();
|
// _initializeRows();
|
||||||
// List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
|
// List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
|
||||||
// selectedTripType ??= purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
|
// selectedTripType ??= purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
|
||||||
@ -109,6 +120,56 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
textControllers["_time${i}Controller"]
|
textControllers["_time${i}Controller"]
|
||||||
?.addListener(() => _clearError("time_$i"));
|
?.addListener(() => _clearError("time_$i"));
|
||||||
}
|
}
|
||||||
|
// loadCountryList();
|
||||||
|
|
||||||
|
flightFirstTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
flightLastTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
final result = getFlightTripDateRange(widget.flightData);
|
||||||
|
flightFirstTripDateNotifier.value = result['firstTripDate'];
|
||||||
|
flightLastTripDateNotifier.value = result['lastTripDate'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String?> getFlightTripDateRange(
|
||||||
|
List<Map<String, dynamic>> flightData) {
|
||||||
|
final allTrips = flightData
|
||||||
|
.expand((flight) => flight['trips'] ?? [])
|
||||||
|
.whereType<Map<String, dynamic>>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (allTrips.isEmpty) {
|
||||||
|
return {
|
||||||
|
'firstTripDate': null,
|
||||||
|
'lastTripDate': null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
allTrips.sort((a, b) {
|
||||||
|
final aDate = DateTime.tryParse(a['date'] ?? '') ?? DateTime(1900);
|
||||||
|
final bDate = DateTime.tryParse(b['date'] ?? '') ?? DateTime(1900);
|
||||||
|
return aDate.compareTo(bDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
final firstTrip = allTrips.first;
|
||||||
|
final lastTrip = allTrips.last;
|
||||||
|
|
||||||
|
return {
|
||||||
|
'firstTripDate': firstTrip['date'],
|
||||||
|
'lastTripDate': lastTrip['date'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadCountryList() async {
|
||||||
|
final result = await apiService.fetchFlightsCountryList();
|
||||||
|
|
||||||
|
if (result is List) {
|
||||||
|
countryList =
|
||||||
|
result.map((item) => Map<String, dynamic>.from(item)).toList();
|
||||||
|
} else {
|
||||||
|
countryList = []; // fallback or throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int getRowCount() {
|
int getRowCount() {
|
||||||
@ -444,34 +505,15 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Color(0xFFF4F4FB),
|
// color: Color(0xFFF4F4FB),
|
||||||
|
// color: Color(0xFFF9F9F9), // Slightly lighter than white
|
||||||
|
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
widget.onClose(false);
|
|
||||||
},
|
|
||||||
child: Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 18,
|
|
||||||
color: Color(0xFF575A74),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text("Flight Booking",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF575A74))),
|
|
||||||
SizedBox(
|
|
||||||
height: 6,
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(28.0),
|
padding: const EdgeInsets.all(28.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -486,13 +528,6 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// void _addNewRow() {
|
|
||||||
// setState(() {
|
|
||||||
// rowBuilders.add(_builClassType(false));
|
|
||||||
// controllers.add(TextEditingController());
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
List<Widget> _buildAccomadtionForm(bool isDesktop) {
|
List<Widget> _buildAccomadtionForm(bool isDesktop) {
|
||||||
List<Widget> buildResponsiveRow(List<Widget> children) {
|
List<Widget> buildResponsiveRow(List<Widget> children) {
|
||||||
return [
|
return [
|
||||||
@ -502,14 +537,14 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<List<Widget>> rowBuilders = [
|
List<List<Widget>> rowBuilders = [
|
||||||
_builClassType(isDesktop, 1),
|
// _builClassType(isDesktop, 1),
|
||||||
_buildSecondRow(isDesktop, 1)
|
_buildSecondRow(isDesktop, 1)
|
||||||
];
|
];
|
||||||
|
|
||||||
List<List<Widget>> rowRoundBuilders = [
|
List<List<Widget>> rowRoundBuilders = [
|
||||||
_builClassType(isDesktop, 1),
|
// _builClassType(isDesktop, 1),
|
||||||
_buildSecondRow(isDesktop, 1),
|
_buildSecondRow(isDesktop, 1),
|
||||||
_builClassType(isDesktop, 2),
|
// _builClassType(isDesktop, 2),
|
||||||
_buildSecondRow(isDesktop, 2)
|
_buildSecondRow(isDesktop, 2)
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -527,11 +562,11 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
|
|
||||||
if (selectedTripType == "Multitrip")
|
if (selectedTripType == "Multitrip")
|
||||||
...List.generate(multiTripRowCount, (index) {
|
...List.generate(multiTripRowCount, (index) {
|
||||||
List<Widget> firstRow = _builClassType(isDesktop, index + 1);
|
// List<Widget> firstRow = _builClassType(isDesktop, index + 1);
|
||||||
List<Widget> secondRow = _buildSecondRow(isDesktop, index + 1);
|
List<Widget> secondRow = _buildSecondRow(isDesktop, index + 1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...buildResponsiveRow(firstRow), // Row 1
|
// ...buildResponsiveRow(firstRow), // Row 1
|
||||||
...buildResponsiveRow(secondRow), // Row 2
|
...buildResponsiveRow(secondRow), // Row 2
|
||||||
];
|
];
|
||||||
}).expand((row) => row),
|
}).expand((row) => row),
|
||||||
@ -546,13 +581,12 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Colors.blueAccent,
|
backgroundColor: Colors.green,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
side: BorderSide(color: Colors.blueAccent, width: 2),
|
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -591,18 +625,14 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"Add Trip",
|
"Add Trip",
|
||||||
style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold),
|
style: TextStyle(color: Colors.white, fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
...buildResponsiveRow(_buildvisa(isDesktop)),
|
// ...buildResponsiveRow(_buildvisa(isDesktop)),
|
||||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||||
// Actions row remains a Row
|
// Actions row remains a Row
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: _handleAction(isDesktop),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,9 +688,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
// isFocused: _tripTypeFocused,
|
// isFocused: _tripTypeFocused,
|
||||||
isFocused: focusStates["_tripType1Focused"] ?? false,
|
isFocused: focusStates["_tripType1Focused"] ?? false,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
width: isDesktop
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@ -887,7 +915,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _builClassType(bool isDesktop, int index) {
|
List<Widget> _buildSecondRow(bool isDesktop, int index) {
|
||||||
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
|
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
|
||||||
|
|
||||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||||
@ -910,96 +938,54 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
// Default selected value
|
// Default selected value
|
||||||
selectedClasses[index] ??=
|
selectedClasses[index] ??=
|
||||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
return [
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (selectedTripType == "Multitrip")
|
|
||||||
isDesktop
|
|
||||||
?
|
|
||||||
// SizedBox(
|
|
||||||
// width: MediaQuery.of(context).size.width * 0.89,
|
|
||||||
// child:
|
|
||||||
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
// Spacer(flex: 2),
|
|
||||||
|
|
||||||
// _buildDelete(isDesktop, index)
|
|
||||||
|
|
||||||
// Spacer(flex: 2),
|
|
||||||
|
|
||||||
..._buildDelete(isDesktop, index)
|
|
||||||
])
|
|
||||||
// )
|
|
||||||
: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
|
||||||
// _buildDelete(isDesktop, index)
|
|
||||||
..._buildDelete(isDesktop, index)
|
|
||||||
]),
|
|
||||||
SizedBox(height: 20),
|
|
||||||
Text(
|
|
||||||
"Class $index *",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF575A74)),
|
|
||||||
),
|
|
||||||
SizedBox(height: 5),
|
|
||||||
CustomTextFieldWrapper(
|
|
||||||
isFocused: focusStates["_class${index}Focused"] ?? false,
|
|
||||||
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: focusNodes["_class${index}FocusNode"],
|
|
||||||
// focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
|
||||||
// controller: _hotelNameController,
|
|
||||||
value: selectedClasses[index],
|
|
||||||
style: TextStyle(fontSize: 12),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: InputBorder.none,
|
|
||||||
contentPadding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
|
||||||
),
|
|
||||||
onChanged: purposeList.isNotEmpty
|
|
||||||
? (newValue) {
|
|
||||||
setState(() {
|
|
||||||
selectedClasses[index] = newValue;
|
|
||||||
});
|
|
||||||
|
|
||||||
print(selectedClasses[index]);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
items: dropdownItems,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> _buildSecondRow(bool isDesktop, int index) {
|
|
||||||
DateTime? _selectedCheckOutDate;
|
DateTime? _selectedCheckOutDate;
|
||||||
TimeOfDay? _selectedCheckOutTime;
|
TimeOfDay? _selectedCheckOutTime;
|
||||||
|
|
||||||
Future<void> _selectCheckOutDate(BuildContext context) async {
|
Future<void> _selectCheckOutDate(BuildContext context) async {
|
||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
DateTime today = DateTime(now.year, now.month, now.day);
|
DateTime today = DateTime(now.year, now.month, now.day);
|
||||||
|
// Determine the minimum date (firstDate) based on previous index if available
|
||||||
|
DateTime firstDate = today;
|
||||||
|
|
||||||
|
if (index == 1 &&
|
||||||
|
flightLastTripDateNotifier.value != null &&
|
||||||
|
flightLastTripDateNotifier.value!.isNotEmpty) {
|
||||||
|
try {
|
||||||
|
final tripDate = DateFormat('yyyy-MM-dd')
|
||||||
|
.parseStrict(flightLastTripDateNotifier.value!);
|
||||||
|
if (tripDate.isAfter(today)) {
|
||||||
|
firstDate = tripDate;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// handle parse error if needed
|
||||||
|
}
|
||||||
|
} else if (index > 1) {
|
||||||
|
final previousDateString =
|
||||||
|
textControllers["_date${index - 1}Controller"]?.text;
|
||||||
|
if (previousDateString != null && previousDateString.isNotEmpty) {
|
||||||
|
try {
|
||||||
|
final previousDate =
|
||||||
|
DateFormat('yyyy-MM-dd').parseStrict(previousDateString);
|
||||||
|
if (previousDate.isAfter(today)) {
|
||||||
|
firstDate = previousDate;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// handle parse error if necessary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime initialDate = _selectedCheckOutDate != null &&
|
||||||
|
_selectedCheckOutDate!.isAfter(firstDate)
|
||||||
|
? _selectedCheckOutDate!
|
||||||
|
: firstDate;
|
||||||
|
|
||||||
DateTime? pickedDate = await showDatePicker(
|
DateTime? pickedDate = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: _selectedCheckOutDate != null &&
|
initialDate: initialDate,
|
||||||
_selectedCheckOutDate!.isAfter(today)
|
firstDate: firstDate,
|
||||||
? _selectedCheckOutDate!
|
|
||||||
: today,
|
|
||||||
firstDate: today,
|
|
||||||
lastDate: DateTime(2100),
|
lastDate: DateTime(2100),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1034,7 +1020,73 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
late Map<String, String> countryMap; // Mapping country_code -> country_name
|
||||||
|
late List<String> countryCodes; // List of country codes
|
||||||
|
|
||||||
|
countryMap = {
|
||||||
|
for (var item in countryList)
|
||||||
|
item['country_code'] as String: item['country_name'] as String
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extract only country codes for processing
|
||||||
|
countryCodes = countryMap.keys.toList();
|
||||||
|
|
||||||
|
selectedCountry ??= null;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Class $index *",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF575A74)),
|
||||||
|
),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
CustomTextFieldItnerarySubWrapper(
|
||||||
|
isFocused: focusStates["_class${index}Focused"] ?? false,
|
||||||
|
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: focusNodes["_class${index}FocusNode"],
|
||||||
|
// focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
||||||
|
// controller: _hotelNameController,
|
||||||
|
value: selectedClasses[index],
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||||
|
),
|
||||||
|
onChanged: purposeList.isNotEmpty
|
||||||
|
? (newValue) {
|
||||||
|
setState(() {
|
||||||
|
selectedClasses[index] = newValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
print(selectedClasses[index]);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
items: dropdownItems,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
SizedBox(
|
||||||
|
width: 20,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -1051,6 +1103,51 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
isFocused: focusStates["_from${index}Focused"] ?? false,
|
isFocused: focusStates["_from${index}Focused"] ?? false,
|
||||||
// isFocused: focusStates["_from${fieldIndex}Focused"] ?? false,
|
// isFocused: focusStates["_from${fieldIndex}Focused"] ?? false,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
|
// child: SizedBox(
|
||||||
|
// height: 40,
|
||||||
|
// child: DropdownSearch<String>(
|
||||||
|
// selectedItem: countryMap[selectedCountry],
|
||||||
|
// popupProps: PopupProps.menu(
|
||||||
|
// showSearchBox: true, // Enables search functionality
|
||||||
|
// searchFieldProps: TextFieldProps(
|
||||||
|
// decoration: InputDecoration(
|
||||||
|
// hintText: "Search Country...",
|
||||||
|
// contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// items: countryMap.values.toList(),
|
||||||
|
// dropdownDecoratorProps: DropDownDecoratorProps(
|
||||||
|
// dropdownSearchDecoration: InputDecoration(
|
||||||
|
// border: InputBorder.none,
|
||||||
|
// contentPadding: EdgeInsets.symmetric(
|
||||||
|
// horizontal: 1,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// dropdownBuilder: (context, selectedItem) => Align(
|
||||||
|
// // Center-align selected item
|
||||||
|
// alignment: Alignment.centerLeft,
|
||||||
|
// child: Text(
|
||||||
|
// selectedItem ?? "Select Country",
|
||||||
|
// style: TextStyle(fontSize: 12),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// onChanged: (String? newValue) {
|
||||||
|
// setState(() {
|
||||||
|
// // Find the country_code based on selected country_name
|
||||||
|
// selectedCountry = countryMap.entries
|
||||||
|
// .firstWhere((entry) => entry.value == newValue)
|
||||||
|
// .key;
|
||||||
|
//
|
||||||
|
// if (selectedCountry!.isNotEmpty) {
|
||||||
|
// errorMessages.remove("country_code");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
@ -1142,6 +1239,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
CustomTextFieldItnerarySubWrapper(
|
CustomTextFieldItnerarySubWrapper(
|
||||||
isFocused: focusStates["_date${index}Focused"] ?? false,
|
isFocused: focusStates["_date${index}Focused"] ?? false,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -1195,6 +1293,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
CustomTextFieldItnerarySubWrapper(
|
CustomTextFieldItnerarySubWrapper(
|
||||||
isFocused: focusStates["_timeFocused"] ?? false,
|
isFocused: focusStates["_timeFocused"] ?? false,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.08 : null,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -1206,7 +1305,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
controller: textControllers["_time${index}Controller"],
|
controller: textControllers["_time${index}Controller"],
|
||||||
style: const TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: 12),
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: "Select Time",
|
labelText: "Time",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
@ -1228,11 +1327,106 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
if (selectedTripType == "Multitrip")
|
||||||
|
Container(
|
||||||
|
// color: Colors.blueGrey,
|
||||||
|
// padding: const EdgeInsets.only(top: 50, bottom: 50),
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
removeTrip(index);
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: Colors.redAccent,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildThirdRow(bool isDesktop) {
|
List<Widget> _buildThirdRow(bool isDesktop) {
|
||||||
|
List<dynamic> visa_available =
|
||||||
|
widget.apiData?['flight_visa_available'] ?? [];
|
||||||
|
// Default selected value
|
||||||
|
|
||||||
|
List<DropdownMenuItem<String>> dropdownItems = visa_available
|
||||||
|
.map((item) => DropdownMenuItem<String>(
|
||||||
|
value: item['dropdown_key'],
|
||||||
|
child: Text(item['dropdown_value']),
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
selectedvisa_available ??=
|
||||||
|
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||||
|
|
||||||
|
if (dropdownItems.isEmpty) {
|
||||||
|
dropdownItems.add(
|
||||||
|
DropdownMenuItem<String>(
|
||||||
|
value: null,
|
||||||
|
child: Text("No options available",
|
||||||
|
style: TextStyle(color: Colors.grey)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Visa Required",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF575A74)),
|
||||||
|
),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
CustomTextFieldItnerarySubWrapper(
|
||||||
|
// isFocused: _tripTypeFocused,
|
||||||
|
isFocused: focusStates["_visa1Focused"] ?? false,
|
||||||
|
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
|
||||||
|
focusNode: focusNodes["_visa1FocusNode"],
|
||||||
|
value: selectedvisa_available,
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||||
|
),
|
||||||
|
onChanged: visa_available.isNotEmpty
|
||||||
|
? (newValue) {
|
||||||
|
setState(() {
|
||||||
|
selectedvisa_available = newValue;
|
||||||
|
// selectedTripType = "Oneway";
|
||||||
|
// Reset `multiTripRowCount` when switching away from Multitrip
|
||||||
|
});
|
||||||
|
print(
|
||||||
|
"Updating form data: Flight -> trip_type -> $selectedvisa_available");
|
||||||
|
|
||||||
|
// _initializeRows();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
|
||||||
|
items: dropdownItems,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
SizedBox(
|
||||||
|
width: 20,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -1247,27 +1441,37 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
CustomTextFieldWrapper(
|
CustomTextFieldWrapper(
|
||||||
isFocused: focusStates["_comments1Focused"] ?? false,
|
isFocused: focusStates["_comments1Focused"] ?? false,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
width: isDesktop
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
|
||||||
child: TextField(
|
child: TextField(
|
||||||
focusNode: focusNodes["_comments1FocusNode"],
|
focusNode: focusNodes["_comments1FocusNode"],
|
||||||
// controller: _commentsController,
|
// controller: _commentsController,
|
||||||
controller: textControllers["_comments1Controller"],
|
controller: textControllers["_comments1Controller"],
|
||||||
maxLines: 6,
|
// maxLines: 6,
|
||||||
keyboardType: TextInputType.multiline,
|
// keyboardType: TextInputType.multiline,
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Description",
|
labelText: "Comments",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
// contentPadding: EdgeInsets.symmetric(vertical: 1),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
if (isDesktop) Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: _handleAction(isDesktop),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import '../../widgets/custom_text_itnerary_sub.dart';
|
|||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
class ForexScreen extends StatefulWidget {
|
class ForexScreen extends StatefulWidget {
|
||||||
|
final List<Map<String, dynamic>> flightData;
|
||||||
final Map<String, dynamic>? apiData;
|
final Map<String, dynamic>? apiData;
|
||||||
final Function(bool) onClose;
|
final Function(bool) onClose;
|
||||||
final Map<String, dynamic>? selectedItem;
|
final Map<String, dynamic>? selectedItem;
|
||||||
@ -27,7 +28,8 @@ class ForexScreen extends StatefulWidget {
|
|||||||
required this.selectedItem,
|
required this.selectedItem,
|
||||||
required this.apiCountryData,
|
required this.apiCountryData,
|
||||||
required this.onSaveForex,
|
required this.onSaveForex,
|
||||||
required this.loginUser});
|
required this.loginUser,
|
||||||
|
required this.flightData});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ForexScreenState createState() => _ForexScreenState();
|
_ForexScreenState createState() => _ForexScreenState();
|
||||||
@ -36,6 +38,9 @@ class ForexScreen extends StatefulWidget {
|
|||||||
class _ForexScreenState extends State<ForexScreen> {
|
class _ForexScreenState extends State<ForexScreen> {
|
||||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||||
|
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||||
|
|
||||||
Map<String, String?> selectedValues = {};
|
Map<String, String?> selectedValues = {};
|
||||||
bool isChecked = false; // State variable for checkbox
|
bool isChecked = false; // State variable for checkbox
|
||||||
|
|
||||||
@ -106,6 +111,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
"deposit_on_cash": textControllers["_cash"]?.text,
|
"deposit_on_cash": textControllers["_cash"]?.text,
|
||||||
"delivery_location": textControllers["_deliveryLocation"]?.text,
|
"delivery_location": textControllers["_deliveryLocation"]?.text,
|
||||||
"comments": textControllers["_comments"]?.text,
|
"comments": textControllers["_comments"]?.text,
|
||||||
|
"total": selectedQuotedAmount,
|
||||||
"created_by": widget.loginUser,
|
"created_by": widget.loginUser,
|
||||||
"updated_by": widget.loginUser,
|
"updated_by": widget.loginUser,
|
||||||
};
|
};
|
||||||
@ -220,6 +226,35 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
return errorMessages.isEmpty; // Valid if there are no errors
|
return errorMessages.isEmpty; // Valid if there are no errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String?> getFlightTripDateRange(
|
||||||
|
List<Map<String, dynamic>> flightData) {
|
||||||
|
final allTrips = flightData
|
||||||
|
.expand((flight) => flight['trips'] ?? [])
|
||||||
|
.whereType<Map<String, dynamic>>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (allTrips.isEmpty) {
|
||||||
|
return {
|
||||||
|
'firstTripDate': null,
|
||||||
|
'lastTripDate': null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
allTrips.sort((a, b) {
|
||||||
|
final aDate = DateTime.tryParse(a['date'] ?? '') ?? DateTime(1900);
|
||||||
|
final bDate = DateTime.tryParse(b['date'] ?? '') ?? DateTime(1900);
|
||||||
|
return aDate.compareTo(bDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
final firstTrip = allTrips.first;
|
||||||
|
final lastTrip = allTrips.last;
|
||||||
|
|
||||||
|
return {
|
||||||
|
'firstTripDate': firstTrip['date'],
|
||||||
|
'lastTripDate': lastTrip['date'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void handleSave() {
|
void handleSave() {
|
||||||
print("Handle Save forexData $forexData");
|
print("Handle Save forexData $forexData");
|
||||||
|
|
||||||
@ -280,6 +315,30 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
textControllers["_forexEndDate"]?.addListener(_onFieldChanged);
|
textControllers["_forexEndDate"]?.addListener(_onFieldChanged);
|
||||||
|
|
||||||
handleUpdatedField();
|
handleUpdatedField();
|
||||||
|
|
||||||
|
flightFirstTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
flightLastTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
final result = getFlightTripDateRange(widget.flightData);
|
||||||
|
flightFirstTripDateNotifier.value = result['firstTripDate'];
|
||||||
|
flightLastTripDateNotifier.value = result['lastTripDate'];
|
||||||
|
|
||||||
|
// ✅ Only set controller after value is updated
|
||||||
|
final parsedDate =
|
||||||
|
DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
|
||||||
|
if (parsedDate != null) {
|
||||||
|
textControllers["_forexStartDate"]?.text =
|
||||||
|
DateFormat('yyyy-MM-dd').format(parsedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
final parsedEndDate =
|
||||||
|
DateTime.tryParse(flightLastTripDateNotifier.value ?? '');
|
||||||
|
if (parsedEndDate != null) {
|
||||||
|
textControllers["_forexEndDate"]?.text =
|
||||||
|
DateFormat('yyyy-MM-dd').format(parsedEndDate);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleUpdatedField() {
|
void handleUpdatedField() {
|
||||||
@ -468,34 +527,13 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Color(0xFFF4F4FB),
|
// color: Color(0xFFF4F4FB),
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
widget.onClose(false);
|
|
||||||
},
|
|
||||||
child: Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 18,
|
|
||||||
color: Color(0xFF575A74),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text("Forex List",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF575A74))),
|
|
||||||
SizedBox(
|
|
||||||
height: 6,
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(28.0),
|
padding: const EdgeInsets.all(28.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -532,48 +570,43 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||||
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 28,
|
height: 5,
|
||||||
),
|
),
|
||||||
Text(
|
Align(
|
||||||
"Forex Details",
|
alignment: Alignment.centerLeft,
|
||||||
style: TextStyle(
|
child: Text(
|
||||||
fontSize: 14,
|
"Forex Details",
|
||||||
fontWeight: FontWeight.bold,
|
style: TextStyle(
|
||||||
color: Color(0xFF575A74)),
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF575A74)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// SizedBox(
|
||||||
|
// height: 8,
|
||||||
|
// ),
|
||||||
|
Divider(
|
||||||
|
thickness: 0.3,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 8,
|
height: 3,
|
||||||
),
|
),
|
||||||
Divider(),
|
|
||||||
SizedBox(
|
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
|
|
||||||
...buildResponsiveRow(_builClassType(isDesktop)),
|
...buildResponsiveRow(_builClassType(isDesktop)),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 8,
|
height: 3,
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
thickness: 0.3,
|
||||||
),
|
),
|
||||||
Divider(),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 28,
|
height: 10,
|
||||||
),
|
),
|
||||||
...buildResponsiveRow(_buildSecondRow(isDesktop)),
|
...buildResponsiveRow(_buildSecondRow(isDesktop)),
|
||||||
|
|
||||||
...buildResponsiveRow(_buildCardDetailsRow(isDesktop)),
|
...buildResponsiveRow(_buildCardDetailsRow(isDesktop)),
|
||||||
|
|
||||||
...buildResponsiveRow(_buildFprexCard(isDesktop)),
|
...buildResponsiveRow(_buildFprexCard(isDesktop)),
|
||||||
|
|
||||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||||
|
|
||||||
...buildResponsiveRow(_buildCommetsRow(isDesktop)),
|
...buildResponsiveRow(_buildCommetsRow(isDesktop)),
|
||||||
|
|
||||||
// Actions row remains a Row
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: _handleAction(isDesktop),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,16 +618,43 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
DateTime today = DateTime(now.year, now.month, now.day);
|
DateTime today = DateTime(now.year, now.month, now.day);
|
||||||
|
|
||||||
DateTime? pickedDate = await showDatePicker(
|
// Parse date from notifier if available, else use today
|
||||||
|
DateTime initialDate;
|
||||||
|
if (flightFirstTripDateNotifier.value != null) {
|
||||||
|
try {
|
||||||
|
initialDate = DateTime.parse(flightFirstTripDateNotifier.value!);
|
||||||
|
textControllers["_forexStartDate"]?.text =
|
||||||
|
DateFormat('yyyy-MM-dd').format(initialDate);
|
||||||
|
} catch (e) {
|
||||||
|
initialDate = today;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initialDate = today;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use previously selected date if valid
|
||||||
|
if (_selectedCheckOutDate != null &&
|
||||||
|
_selectedCheckOutDate!.isAfter(today)) {
|
||||||
|
initialDate = _selectedCheckOutDate!;
|
||||||
|
}
|
||||||
|
|
||||||
|
final pickedDate = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: _selectedCheckOutDate != null &&
|
initialDate: initialDate,
|
||||||
_selectedCheckOutDate!.isAfter(today)
|
firstDate: initialDate,
|
||||||
? _selectedCheckOutDate!
|
|
||||||
: today,
|
|
||||||
firstDate: today,
|
|
||||||
lastDate: DateTime(2100),
|
lastDate: DateTime(2100),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// DateTime? pickedDate = await showDatePicker(
|
||||||
|
// context: context,
|
||||||
|
// initialDate: _selectedCheckOutDate != null &&
|
||||||
|
// _selectedCheckOutDate!.isAfter(today)
|
||||||
|
// ? _selectedCheckOutDate!
|
||||||
|
// : today,
|
||||||
|
// firstDate: today,
|
||||||
|
// lastDate: DateTime(2100),
|
||||||
|
// );
|
||||||
|
|
||||||
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedCheckOutDate = pickedDate;
|
_selectedCheckOutDate = pickedDate;
|
||||||
@ -610,16 +670,41 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
DateTime today = DateTime(now.year, now.month, now.day);
|
DateTime today = DateTime(now.year, now.month, now.day);
|
||||||
|
|
||||||
DateTime? pickedDate = await showDatePicker(
|
// Parse date from notifier if available, else use today
|
||||||
|
DateTime initialDate;
|
||||||
|
if (flightLastTripDateNotifier.value != null) {
|
||||||
|
try {
|
||||||
|
initialDate = DateTime.parse(flightLastTripDateNotifier.value!);
|
||||||
|
} catch (e) {
|
||||||
|
initialDate = today;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initialDate = today;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use previously selected date if valid
|
||||||
|
if (_selectedCheckOutDate != null &&
|
||||||
|
_selectedCheckOutDate!.isAfter(today)) {
|
||||||
|
initialDate = _selectedCheckOutDate!;
|
||||||
|
}
|
||||||
|
|
||||||
|
final pickedDate = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate:
|
initialDate: initialDate,
|
||||||
_selectedEndDate != null && _selectedEndDate!.isAfter(today)
|
firstDate: initialDate,
|
||||||
? _selectedEndDate!
|
|
||||||
: today,
|
|
||||||
firstDate: today,
|
|
||||||
lastDate: DateTime(2100),
|
lastDate: DateTime(2100),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// DateTime? pickedDate = await showDatePicker(
|
||||||
|
// context: context,
|
||||||
|
// initialDate:
|
||||||
|
// _selectedEndDate != null && _selectedEndDate!.isAfter(today)
|
||||||
|
// ? _selectedEndDate!
|
||||||
|
// : today,
|
||||||
|
// firstDate: today,
|
||||||
|
// lastDate: DateTime(2100),
|
||||||
|
// );
|
||||||
|
|
||||||
if (pickedDate != null && pickedDate != _selectedEndDate) {
|
if (pickedDate != null && pickedDate != _selectedEndDate) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedEndDate = pickedDate;
|
_selectedEndDate = pickedDate;
|
||||||
@ -657,7 +742,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Forex Start Date",
|
"Start Date",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@ -736,7 +821,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Forex End Date",
|
"End Date",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@ -898,7 +983,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 30,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -906,7 +991,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
// selectedDuration?.isNotEmpty == true ? selectedDuration! : "Duration",
|
// selectedDuration?.isNotEmpty == true ? selectedDuration! : "Duration",
|
||||||
selectedDuration ?? "Duration",
|
selectedDuration ?? "Duration",
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Color(0xFF575A74)),
|
color: Color(0xFF575A74)),
|
||||||
// decoration: const InputDecoration(
|
// decoration: const InputDecoration(
|
||||||
@ -950,7 +1035,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
? MediaQuery.of(context).size.width * 0.330
|
? MediaQuery.of(context).size.width * 0.330
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
: MediaQuery.of(context).size.width * 0.66,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 30,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -960,7 +1045,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
selectedCurrency ?? "Currency",
|
selectedCurrency ?? "Currency",
|
||||||
// selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency",
|
// selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency",
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Color(0xFF575A74)),
|
color: Color(0xFF575A74)),
|
||||||
),
|
),
|
||||||
@ -993,7 +1078,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 30,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -1001,7 +1086,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
selectedPerdiemAmount ?? "Amount",
|
selectedPerdiemAmount ?? "Amount",
|
||||||
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount",
|
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount",
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Color(0xFF575A74)),
|
color: Color(0xFF575A74)),
|
||||||
// decoration: const InputDecoration(
|
// decoration: const InputDecoration(
|
||||||
@ -1186,7 +1271,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
// focusNode: _toFocusNode,
|
// focusNode: _toFocusNode,
|
||||||
// controller: _toController,
|
// controller: _toController,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Color(0xFF575A74)),
|
color: Color(0xFF575A74)),
|
||||||
// decoration: const InputDecoration(
|
// decoration: const InputDecoration(
|
||||||
@ -1358,7 +1443,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
selectedQuotedAmount ?? "0",
|
selectedQuotedAmount ?? "0",
|
||||||
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount",
|
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount",
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Color(0xFF575A74)),
|
color: Color(0xFF575A74)),
|
||||||
),
|
),
|
||||||
@ -1524,26 +1609,38 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
isFocused:
|
isFocused:
|
||||||
focusStates["_comments"] ?? false, // Dropdown doesn't use focus
|
focusStates["_comments"] ?? false, // Dropdown doesn't use focus
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
width: isDesktop
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null,
|
||||||
? MediaQuery.of(context).size.width * 0.330
|
child: SizedBox(
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
height: 35,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
focusNode: focusNodes["_comments"],
|
focusNode: focusNodes["_comments"],
|
||||||
controller: textControllers["_comments"],
|
controller: textControllers["_comments"],
|
||||||
maxLines: 3,
|
style: TextStyle(fontSize: 12),
|
||||||
keyboardType: TextInputType.multiline,
|
decoration: InputDecoration(
|
||||||
style: TextStyle(fontSize: 12),
|
labelText: "Comments",
|
||||||
decoration: InputDecoration(
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
labelText: "Comments",
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
border: InputBorder.none,
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
border: InputBorder.none,
|
),
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
if (isDesktop) Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
// Actions row remains a Row
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: _handleAction(isDesktop),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import '../../widgets/custom_text_field.dart';
|
|||||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||||
|
|
||||||
class InsuranceScreen extends StatefulWidget {
|
class InsuranceScreen extends StatefulWidget {
|
||||||
|
final List<Map<String, dynamic>> flightData;
|
||||||
final Map<String, dynamic>? apiData;
|
final Map<String, dynamic>? apiData;
|
||||||
final Function(bool) onClose;
|
final Function(bool) onClose;
|
||||||
final Function(Map<String, dynamic>) onSaveInsurance;
|
final Function(Map<String, dynamic>) onSaveInsurance;
|
||||||
@ -17,7 +18,8 @@ class InsuranceScreen extends StatefulWidget {
|
|||||||
required this.apiData,
|
required this.apiData,
|
||||||
required this.onSaveInsurance,
|
required this.onSaveInsurance,
|
||||||
required this.selectedItem,
|
required this.selectedItem,
|
||||||
required this.loginUser});
|
required this.loginUser,
|
||||||
|
required this.flightData});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_InsuranceScreenState createState() => _InsuranceScreenState();
|
_InsuranceScreenState createState() => _InsuranceScreenState();
|
||||||
@ -28,6 +30,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
|
|
||||||
Map<String, String?> selectedValues = {};
|
Map<String, String?> selectedValues = {};
|
||||||
|
|
||||||
|
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||||
|
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||||
|
|
||||||
final FocusNode _tripTypeFocusNode = FocusNode();
|
final FocusNode _tripTypeFocusNode = FocusNode();
|
||||||
final FocusNode _hotelNameFocusNode = FocusNode();
|
final FocusNode _hotelNameFocusNode = FocusNode();
|
||||||
final FocusNode _fromFocusNode = FocusNode();
|
final FocusNode _fromFocusNode = FocusNode();
|
||||||
@ -105,6 +110,58 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
selectedInsuranceType =
|
selectedInsuranceType =
|
||||||
widget.selectedItem!["type_of_insurance"].toString();
|
widget.selectedItem!["type_of_insurance"].toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flightFirstTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
flightLastTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
final result = getFlightTripDateRange(widget.flightData);
|
||||||
|
flightFirstTripDateNotifier.value = result['firstTripDate'];
|
||||||
|
flightLastTripDateNotifier.value = result['lastTripDate'];
|
||||||
|
|
||||||
|
// ✅ Only set controller after value is updated
|
||||||
|
final parsedDate =
|
||||||
|
DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
|
||||||
|
if (parsedDate != null) {
|
||||||
|
_startdateController.text = DateFormat('yyyy-MM-dd').format(parsedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
final parsedEndDate =
|
||||||
|
DateTime.tryParse(flightLastTripDateNotifier.value ?? '');
|
||||||
|
if (parsedEndDate != null) {
|
||||||
|
_endDateController.text =
|
||||||
|
DateFormat('yyyy-MM-dd').format(parsedEndDate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String?> getFlightTripDateRange(
|
||||||
|
List<Map<String, dynamic>> flightData) {
|
||||||
|
final allTrips = flightData
|
||||||
|
.expand((flight) => flight['trips'] ?? [])
|
||||||
|
.whereType<Map<String, dynamic>>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (allTrips.isEmpty) {
|
||||||
|
return {
|
||||||
|
'firstTripDate': null,
|
||||||
|
'lastTripDate': null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
allTrips.sort((a, b) {
|
||||||
|
final aDate = DateTime.tryParse(a['date'] ?? '') ?? DateTime(1900);
|
||||||
|
final bDate = DateTime.tryParse(b['date'] ?? '') ?? DateTime(1900);
|
||||||
|
return aDate.compareTo(bDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
final firstTrip = allTrips.first;
|
||||||
|
final lastTrip = allTrips.last;
|
||||||
|
|
||||||
|
return {
|
||||||
|
'firstTripDate': firstTrip['date'],
|
||||||
|
'lastTripDate': lastTrip['date'],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||||
@ -132,6 +189,25 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Additional validation: checkout_date >= checkin_date
|
||||||
|
final checkIn = data["start_date"];
|
||||||
|
final checkOut = data["end_date"];
|
||||||
|
|
||||||
|
if (checkIn != null &&
|
||||||
|
checkOut != null &&
|
||||||
|
checkIn.toString().isNotEmpty &&
|
||||||
|
checkOut.toString().isNotEmpty) {
|
||||||
|
try {
|
||||||
|
final checkInDate = DateTime.parse(checkIn);
|
||||||
|
final checkOutDate = DateTime.parse(checkOut);
|
||||||
|
if (checkOutDate.isBefore(checkInDate)) {
|
||||||
|
errorMessages["end_date"] = "EndDate date cannot be before StartDate";
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
errorMessages["end_date"] = "Invalid date format";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return errorMessages.isEmpty; // Valid if there are no errors
|
return errorMessages.isEmpty; // Valid if there are no errors
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,34 +251,13 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Color(0xFFF4F4FB),
|
// color: Color(0xFFF4F4FB),
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
widget.onClose(false);
|
|
||||||
},
|
|
||||||
child: Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 18,
|
|
||||||
color: Color(0xFF575A74),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text("Insurance Booking List",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF575A74))),
|
|
||||||
SizedBox(
|
|
||||||
height: 6,
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(28.0),
|
padding: const EdgeInsets.all(28.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -231,7 +286,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
// ...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||||
|
|
||||||
// Iterate over rowBuilders and wrap each in a responsive container
|
// Iterate over rowBuilders and wrap each in a responsive container
|
||||||
...rowBuilders.expand((row) => buildResponsiveRow(row)),
|
...rowBuilders.expand((row) => buildResponsiveRow(row)),
|
||||||
@ -239,10 +294,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||||
|
|
||||||
// Actions row remains a Row
|
// Actions row remains a Row
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: _handleAction(isDesktop),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,6 +389,33 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildSecondRow(bool isDesktop) {
|
List<Widget> _buildSecondRow(bool isDesktop) {
|
||||||
|
List<dynamic> purposeList =
|
||||||
|
widget.apiData?['insurance_type_of_insurance'] ?? [];
|
||||||
|
// 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
|
||||||
|
selectedInsuranceType ??=
|
||||||
|
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
DateTime? _selectedCheckOutDate;
|
DateTime? _selectedCheckOutDate;
|
||||||
TimeOfDay? _selectedCheckOutTime;
|
TimeOfDay? _selectedCheckOutTime;
|
||||||
|
|
||||||
@ -345,16 +423,41 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
DateTime today = DateTime(now.year, now.month, now.day);
|
DateTime today = DateTime(now.year, now.month, now.day);
|
||||||
|
|
||||||
DateTime? pickedDate = await showDatePicker(
|
// Parse date from notifier if available, else use today
|
||||||
|
DateTime initialDate;
|
||||||
|
if (flightFirstTripDateNotifier.value != null) {
|
||||||
|
try {
|
||||||
|
initialDate = DateTime.parse(flightFirstTripDateNotifier.value!);
|
||||||
|
} catch (e) {
|
||||||
|
initialDate = today;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initialDate = today;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use previously selected date if valid
|
||||||
|
if (_selectedCheckOutDate != null &&
|
||||||
|
_selectedCheckOutDate!.isAfter(today)) {
|
||||||
|
initialDate = _selectedCheckOutDate!;
|
||||||
|
}
|
||||||
|
|
||||||
|
final pickedDate = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: _selectedCheckOutDate != null &&
|
initialDate: initialDate,
|
||||||
_selectedCheckOutDate!.isAfter(today)
|
firstDate: initialDate,
|
||||||
? _selectedCheckOutDate!
|
|
||||||
: today,
|
|
||||||
firstDate: today,
|
|
||||||
lastDate: DateTime(2100),
|
lastDate: DateTime(2100),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// DateTime? pickedDate = await showDatePicker(
|
||||||
|
// context: context,
|
||||||
|
// initialDate: _selectedCheckOutDate != null &&
|
||||||
|
// _selectedCheckOutDate!.isAfter(today)
|
||||||
|
// ? _selectedCheckOutDate!
|
||||||
|
// : today,
|
||||||
|
// firstDate: today,
|
||||||
|
// lastDate: DateTime(2100),
|
||||||
|
// );
|
||||||
|
|
||||||
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedCheckOutDate = pickedDate;
|
_selectedCheckOutDate = pickedDate;
|
||||||
@ -368,16 +471,43 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
DateTime today = DateTime(now.year, now.month, now.day);
|
DateTime today = DateTime(now.year, now.month, now.day);
|
||||||
|
|
||||||
DateTime? pickedDate = await showDatePicker(
|
DateTime? checkInDate;
|
||||||
|
try {
|
||||||
|
checkInDate = DateTime.parse(_startdateController.text);
|
||||||
|
} catch (e) {
|
||||||
|
checkInDate = today;
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Ensure at least today is used
|
||||||
|
// DateTime firstDate = checkInDate.isAfter(today) ? checkInDate : today;
|
||||||
|
// DateTime initialDate = _selectedCheckOutDate != null &&
|
||||||
|
// _selectedCheckOutDate!.isAfter(firstDate)
|
||||||
|
// ? _selectedCheckOutDate!
|
||||||
|
// : firstDate;
|
||||||
|
|
||||||
|
DateTime firstDate = checkInDate;
|
||||||
|
DateTime initialDate = _selectedCheckOutDate != null &&
|
||||||
|
_selectedCheckOutDate!.isAfter(firstDate)
|
||||||
|
? _selectedCheckOutDate!
|
||||||
|
: firstDate;
|
||||||
|
|
||||||
|
final pickedDate = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: _selectedCheckOutDate != null &&
|
initialDate: initialDate,
|
||||||
_selectedCheckOutDate!.isAfter(today)
|
firstDate: firstDate,
|
||||||
? _selectedCheckOutDate!
|
|
||||||
: today,
|
|
||||||
firstDate: today,
|
|
||||||
lastDate: DateTime(2100),
|
lastDate: DateTime(2100),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// DateTime? pickedDate = await showDatePicker(
|
||||||
|
// context: context,
|
||||||
|
// initialDate: _selectedCheckOutDate != null &&
|
||||||
|
// _selectedCheckOutDate!.isAfter(today)
|
||||||
|
// ? _selectedCheckOutDate!
|
||||||
|
// : today,
|
||||||
|
// firstDate: today,
|
||||||
|
// lastDate: DateTime(2100),
|
||||||
|
// );
|
||||||
|
|
||||||
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedCheckOutDate = pickedDate;
|
_selectedCheckOutDate = pickedDate;
|
||||||
@ -387,6 +517,57 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Insurance Type",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF575A74)),
|
||||||
|
),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
CustomTextFieldWrapper(
|
||||||
|
isFocused: _isHotelNameFocused,
|
||||||
|
isDesktop: isDesktop,
|
||||||
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 40,
|
||||||
|
child: DropdownButtonFormField<String>(
|
||||||
|
focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
||||||
|
value: selectedInsuranceType,
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||||
|
),
|
||||||
|
onChanged: purposeList.isNotEmpty
|
||||||
|
? (newValue) {
|
||||||
|
setState(() {
|
||||||
|
selectedInsuranceType = newValue;
|
||||||
|
if (selectedInsuranceType!.isNotEmpty) {
|
||||||
|
errorMessages.remove("type_of_insurance");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
print(selectedInsuranceType);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
|
||||||
|
items: dropdownItems,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
Spacer()
|
||||||
|
else
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -401,9 +582,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
CustomTextFieldWrapper(
|
CustomTextFieldWrapper(
|
||||||
isFocused: _dateFocus,
|
isFocused: _dateFocus,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
width: isDesktop
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -464,9 +643,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
CustomTextFieldWrapper(
|
CustomTextFieldWrapper(
|
||||||
isFocused: _dateFocus,
|
isFocused: _dateFocus,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
width: isDesktop
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -548,23 +725,36 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
|||||||
width: isDesktop
|
width: isDesktop
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
? MediaQuery.of(context).size.width * 0.34
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
: MediaQuery.of(context).size.width * 0.66,
|
||||||
child: TextField(
|
child: SizedBox(
|
||||||
focusNode: _commentsFocusNode,
|
height: 40,
|
||||||
controller: _insuranceCommentsController,
|
child: TextField(
|
||||||
maxLines: 6,
|
focusNode: _commentsFocusNode,
|
||||||
keyboardType: TextInputType.multiline,
|
controller: _insuranceCommentsController,
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Description",
|
labelText: "Comments",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
if (isDesktop) Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: _handleAction(isDesktop),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -147,35 +147,12 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
|||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Color(0xFFF4F4FB),
|
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
_commentsController.clear();
|
|
||||||
widget.onClose(false);
|
|
||||||
},
|
|
||||||
child: Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 18,
|
|
||||||
color: Color(0xFF575A74),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text("Miscellaneous Booking List",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF575A74))),
|
|
||||||
SizedBox(
|
|
||||||
height: 6,
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(28.0),
|
padding: const EdgeInsets.all(28.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -204,10 +181,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
|||||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||||
|
|
||||||
// Actions row remains a Row
|
// Actions row remains a Row
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: _handleAction(isDesktop),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,12 +202,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
|||||||
: Column(children: _buildTripType(isDesktop))
|
: Column(children: _buildTripType(isDesktop))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (isDesktop)
|
|
||||||
Spacer()
|
|
||||||
else
|
|
||||||
SizedBox(
|
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,18 +291,19 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
|||||||
width: isDesktop
|
width: isDesktop
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
? MediaQuery.of(context).size.width * 0.34
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
: MediaQuery.of(context).size.width * 0.66,
|
||||||
child: TextField(
|
child: SizedBox(
|
||||||
focusNode: _commentsFocusNode,
|
height: 40,
|
||||||
controller: _commentsController,
|
child: TextField(
|
||||||
maxLines: 6,
|
focusNode: _commentsFocusNode,
|
||||||
keyboardType: TextInputType.multiline,
|
controller: _commentsController,
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Comments",
|
labelText: "Comments",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -347,7 +315,15 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
if (isDesktop) Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: _handleAction(isDesktop),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -203,34 +203,13 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
|||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Color(0xFFF4F4FB),
|
// color: Color(0xFFF4F4FB),
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
widget.onClose(false);
|
|
||||||
},
|
|
||||||
child: Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 18,
|
|
||||||
color: Color(0xFF575A74),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text("Taxi Booking List",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF575A74))),
|
|
||||||
SizedBox(
|
|
||||||
height: 6,
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(28.0),
|
padding: const EdgeInsets.all(28.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -267,10 +246,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
|||||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||||
|
|
||||||
// Actions row remains a Row
|
// Actions row remains a Row
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: _handleAction(isDesktop),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,23 +713,36 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
|||||||
? MediaQuery.of(context).size.width * 0.34
|
? MediaQuery.of(context).size.width * 0.34
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
: MediaQuery.of(context).size.width * 0.66,
|
||||||
|
|
||||||
child: TextField(
|
child: SizedBox(
|
||||||
focusNode: _commentsFocusNode,
|
height: 40,
|
||||||
controller: _taxiCommentsController,
|
child: TextField(
|
||||||
maxLines: 6,
|
focusNode: _commentsFocusNode,
|
||||||
keyboardType: TextInputType.multiline,
|
controller: _taxiCommentsController,
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Description",
|
labelText: "Comments",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
if (isDesktop) Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: _handleAction(isDesktop),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -212,34 +212,34 @@ class _TrainScreenState extends State<TrainScreen> {
|
|||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Color(0xFFF4F4FB),
|
// color: Color(0xFFF4F4FB),
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Align(
|
// Align(
|
||||||
alignment: Alignment.centerRight,
|
// alignment: Alignment.centerRight,
|
||||||
child: InkWell(
|
// child: InkWell(
|
||||||
onTap: () {
|
// onTap: () {
|
||||||
widget.onClose(false);
|
// widget.onClose(false);
|
||||||
},
|
// },
|
||||||
child: Icon(
|
// child: Icon(
|
||||||
Icons.close,
|
// Icons.close,
|
||||||
size: 18,
|
// size: 18,
|
||||||
color: Color(0xFF575A74),
|
// color: Color(0xFF575A74),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
Text("Train Booking List",
|
// Text("Train Booking List",
|
||||||
style: TextStyle(
|
// style: TextStyle(
|
||||||
fontSize: 18,
|
// fontSize: 18,
|
||||||
fontWeight: FontWeight.bold,
|
// fontWeight: FontWeight.bold,
|
||||||
color: Color(0xFF575A74))),
|
// color: Color(0xFF575A74))),
|
||||||
SizedBox(
|
// SizedBox(
|
||||||
height: 6,
|
// height: 6,
|
||||||
),
|
// ),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(28.0),
|
padding: const EdgeInsets.all(28.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -276,10 +276,6 @@ class _TrainScreenState extends State<TrainScreen> {
|
|||||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||||
|
|
||||||
// Actions row remains a Row
|
// Actions row remains a Row
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: _handleAction(isDesktop),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,9 +341,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
|||||||
CustomTextFieldWrapper(
|
CustomTextFieldWrapper(
|
||||||
isFocused: _trainNoFocused,
|
isFocused: _trainNoFocused,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
width: isDesktop
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
@ -368,80 +362,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _builClassType(bool isDesktop) {
|
List<Widget> _builClassType(bool isDesktop) {
|
||||||
List<dynamic> purposeList = widget.apiData?['train_class'] ?? [];
|
return [];
|
||||||
|
|
||||||
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
|
|
||||||
selectedClass ??=
|
|
||||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
|
||||||
|
|
||||||
return [
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Class *",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF575A74)),
|
|
||||||
),
|
|
||||||
SizedBox(height: 5),
|
|
||||||
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: _hotelNameFocusNode, // Assign the correct focus node
|
|
||||||
// controller: _hotelNameController,
|
|
||||||
value: selectedClass,
|
|
||||||
style: TextStyle(fontSize: 12),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: InputBorder.none,
|
|
||||||
contentPadding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
|
||||||
),
|
|
||||||
onChanged: purposeList.isNotEmpty
|
|
||||||
? (newValue) {
|
|
||||||
setState(() {
|
|
||||||
selectedClass = newValue;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
items: dropdownItems,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (errorMessages["class"] != null) ...[
|
|
||||||
SizedBox(height: 5), // Space before error message
|
|
||||||
Text(
|
|
||||||
"Required",
|
|
||||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildSecondRow(bool isDesktop) {
|
List<Widget> _buildSecondRow(bool isDesktop) {
|
||||||
@ -490,7 +411,87 @@ class _TrainScreenState extends State<TrainScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------
|
||||||
|
|
||||||
|
List<dynamic> purposeList = widget.apiData?['train_class'] ?? [];
|
||||||
|
|
||||||
|
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
|
||||||
|
selectedClass ??=
|
||||||
|
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Class *",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF575A74)),
|
||||||
|
),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
CustomTextFieldItnerarySubWrapper(
|
||||||
|
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: _hotelNameFocusNode, // Assign the correct focus node
|
||||||
|
// controller: _hotelNameController,
|
||||||
|
value: selectedClass,
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||||
|
),
|
||||||
|
onChanged: purposeList.isNotEmpty
|
||||||
|
? (newValue) {
|
||||||
|
setState(() {
|
||||||
|
selectedClass = newValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
items: dropdownItems,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (errorMessages["class"] != null) ...[
|
||||||
|
SizedBox(height: 5), // Space before error message
|
||||||
|
Text(
|
||||||
|
"Required",
|
||||||
|
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
Spacer()
|
||||||
|
else
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -595,6 +596,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
|||||||
CustomTextFieldItnerarySubWrapper(
|
CustomTextFieldItnerarySubWrapper(
|
||||||
isFocused: _dateFocus,
|
isFocused: _dateFocus,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -647,6 +649,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
|||||||
CustomTextFieldItnerarySubWrapper(
|
CustomTextFieldItnerarySubWrapper(
|
||||||
isFocused: _timeFocus,
|
isFocused: _timeFocus,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.08 : null,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -698,25 +701,38 @@ class _TrainScreenState extends State<TrainScreen> {
|
|||||||
CustomTextFieldWrapper(
|
CustomTextFieldWrapper(
|
||||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
width: isDesktop
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
child: SizedBox(
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
focusNode: _commentsFocusNode,
|
focusNode: _commentsFocusNode,
|
||||||
controller: _trainCommentsController,
|
controller: _trainCommentsController,
|
||||||
maxLines: 6,
|
// maxLines: 6,
|
||||||
keyboardType: TextInputType.multiline,
|
// keyboardType: TextInputType.multiline,
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Comments",
|
labelText: "Comments",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
if (isDesktop) Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: _handleAction(isDesktop),
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import '../../widgets/custom_text_field.dart';
|
|||||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||||
|
|
||||||
class VisaScreen extends StatefulWidget {
|
class VisaScreen extends StatefulWidget {
|
||||||
|
final List<Map<String, dynamic>> flightData;
|
||||||
final Map<String, dynamic>? apiData;
|
final Map<String, dynamic>? apiData;
|
||||||
final List<dynamic>? apiCountryData;
|
final List<dynamic>? apiCountryData;
|
||||||
|
|
||||||
@ -21,7 +22,8 @@ class VisaScreen extends StatefulWidget {
|
|||||||
this.apiData,
|
this.apiData,
|
||||||
required this.selectedItem,
|
required this.selectedItem,
|
||||||
required this.apiCountryData,
|
required this.apiCountryData,
|
||||||
required this.loginUser});
|
required this.loginUser,
|
||||||
|
required this.flightData});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_VisaScreenState createState() => _VisaScreenState();
|
_VisaScreenState createState() => _VisaScreenState();
|
||||||
@ -30,6 +32,9 @@ class VisaScreen extends StatefulWidget {
|
|||||||
class _VisaScreenState extends State<VisaScreen> {
|
class _VisaScreenState extends State<VisaScreen> {
|
||||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||||
|
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||||
|
|
||||||
Map<String, String?> selectedValues = {};
|
Map<String, String?> selectedValues = {};
|
||||||
|
|
||||||
List<dynamic> countryList = [];
|
List<dynamic> countryList = [];
|
||||||
@ -105,6 +110,22 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
// selectedPurpose = widget.selectedItem!["selectedCountry"].toString();
|
// selectedPurpose = widget.selectedItem!["selectedCountry"].toString();
|
||||||
selectedCountry = widget.selectedItem!["country_code"] as String?;
|
selectedCountry = widget.selectedItem!["country_code"] as String?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flightFirstTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
flightLastTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
final result = getFlightTripDateRange(widget.flightData);
|
||||||
|
flightFirstTripDateNotifier.value = result['firstTripDate'];
|
||||||
|
flightLastTripDateNotifier.value = result['lastTripDate'];
|
||||||
|
|
||||||
|
// ✅ Only set controller after value is updated
|
||||||
|
final parsedDate =
|
||||||
|
DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
|
||||||
|
if (parsedDate != null) {
|
||||||
|
_dateController.text = DateFormat('yyyy-MM-dd').format(parsedDate);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||||
@ -115,6 +136,35 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String?> getFlightTripDateRange(
|
||||||
|
List<Map<String, dynamic>> flightData) {
|
||||||
|
final allTrips = flightData
|
||||||
|
.expand((flight) => flight['trips'] ?? [])
|
||||||
|
.whereType<Map<String, dynamic>>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (allTrips.isEmpty) {
|
||||||
|
return {
|
||||||
|
'firstTripDate': null,
|
||||||
|
'lastTripDate': null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
allTrips.sort((a, b) {
|
||||||
|
final aDate = DateTime.tryParse(a['date'] ?? '') ?? DateTime(1900);
|
||||||
|
final bDate = DateTime.tryParse(b['date'] ?? '') ?? DateTime(1900);
|
||||||
|
return aDate.compareTo(bDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
final firstTrip = allTrips.first;
|
||||||
|
final lastTrip = allTrips.last;
|
||||||
|
|
||||||
|
return {
|
||||||
|
'firstTripDate': firstTrip['date'],
|
||||||
|
'lastTripDate': lastTrip['date'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_tripTypeFocusNode.dispose();
|
_tripTypeFocusNode.dispose();
|
||||||
@ -171,34 +221,12 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Color(0xFFF4F4FB),
|
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
widget.onClose(false);
|
|
||||||
},
|
|
||||||
child: Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 18,
|
|
||||||
color: Color(0xFF575A74),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text("Visa Registration",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF575A74))),
|
|
||||||
SizedBox(
|
|
||||||
height: 6,
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(28.0),
|
padding: const EdgeInsets.all(28.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -232,38 +260,11 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||||
|
|
||||||
// Actions row remains a Row
|
// Actions row remains a Row
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: _handleAction(isDesktop),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildFirstRow(isDesktop) {
|
List<Widget> _buildFirstRow(isDesktop) {
|
||||||
return [
|
return [
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Type of Visa",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF575A74)),
|
|
||||||
),
|
|
||||||
SizedBox(height: 5),
|
|
||||||
isDesktop
|
|
||||||
? Row(children: _buildTripType(isDesktop))
|
|
||||||
: Column(children: _buildTripType(isDesktop)),
|
|
||||||
if (errorMessages["type_of_visa"] != null) ...[
|
|
||||||
SizedBox(height: 5), // Space before error message
|
|
||||||
Text(
|
|
||||||
"Required",
|
|
||||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (isDesktop)
|
if (isDesktop)
|
||||||
Spacer()
|
Spacer()
|
||||||
else
|
else
|
||||||
@ -274,62 +275,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildTripType(bool isDesktop) {
|
List<Widget> _buildTripType(bool isDesktop) {
|
||||||
List<dynamic> purposeList = widget.apiData?['visa_type_of_visa'] ?? [];
|
return [];
|
||||||
|
|
||||||
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
|
|
||||||
selectedPurpose ??=
|
|
||||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
|
||||||
|
|
||||||
return [
|
|
||||||
CustomTextFieldWrapper(
|
|
||||||
isFocused: _tripTypeFocused,
|
|
||||||
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: selectedPurpose,
|
|
||||||
style: TextStyle(fontSize: 12),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: InputBorder.none,
|
|
||||||
contentPadding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
|
||||||
),
|
|
||||||
onChanged: purposeList.isNotEmpty
|
|
||||||
? (newValue) {
|
|
||||||
setState(() {
|
|
||||||
selectedPurpose = newValue;
|
|
||||||
});
|
|
||||||
print(
|
|
||||||
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
|
|
||||||
items: dropdownItems,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildSecondRow(bool isDesktop) {
|
List<Widget> _buildSecondRow(bool isDesktop) {
|
||||||
@ -380,16 +326,41 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
DateTime today = DateTime(now.year, now.month, now.day);
|
DateTime today = DateTime(now.year, now.month, now.day);
|
||||||
|
|
||||||
DateTime? pickedDate = await showDatePicker(
|
// Parse date from notifier if available, else use today
|
||||||
|
DateTime initialDate;
|
||||||
|
if (flightFirstTripDateNotifier.value != null) {
|
||||||
|
try {
|
||||||
|
initialDate = DateTime.parse(flightFirstTripDateNotifier.value!);
|
||||||
|
} catch (e) {
|
||||||
|
initialDate = today;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initialDate = today;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use previously selected date if valid
|
||||||
|
if (_selectedCheckOutDate != null &&
|
||||||
|
_selectedCheckOutDate!.isAfter(today)) {
|
||||||
|
initialDate = _selectedCheckOutDate!;
|
||||||
|
}
|
||||||
|
|
||||||
|
final pickedDate = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: _selectedCheckOutDate != null &&
|
initialDate: initialDate,
|
||||||
_selectedCheckOutDate!.isAfter(today)
|
firstDate: initialDate,
|
||||||
? _selectedCheckOutDate!
|
|
||||||
: today,
|
|
||||||
firstDate: today,
|
|
||||||
lastDate: DateTime(2100),
|
lastDate: DateTime(2100),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// DateTime? pickedDate = await showDatePicker(
|
||||||
|
// context: context,
|
||||||
|
// initialDate: _selectedCheckOutDate != null &&
|
||||||
|
// _selectedCheckOutDate!.isAfter(today)
|
||||||
|
// ? _selectedCheckOutDate!
|
||||||
|
// : today,
|
||||||
|
// firstDate: today,
|
||||||
|
// lastDate: DateTime(2100),
|
||||||
|
// );
|
||||||
|
|
||||||
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedCheckOutDate = pickedDate;
|
_selectedCheckOutDate = pickedDate;
|
||||||
@ -398,7 +369,88 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
List<dynamic> purposeList = widget.apiData?['visa_type_of_visa'] ?? [];
|
||||||
|
|
||||||
|
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
|
||||||
|
selectedPurpose ??=
|
||||||
|
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||||
return [
|
return [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Type of Visa",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF575A74)),
|
||||||
|
),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
CustomTextFieldWrapper(
|
||||||
|
isFocused: _tripTypeFocused,
|
||||||
|
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: selectedPurpose,
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||||
|
),
|
||||||
|
onChanged: purposeList.isNotEmpty
|
||||||
|
? (newValue) {
|
||||||
|
setState(() {
|
||||||
|
selectedPurpose = newValue;
|
||||||
|
});
|
||||||
|
print(
|
||||||
|
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
|
||||||
|
items: dropdownItems,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (errorMessages["type_of_visa"] != null) ...[
|
||||||
|
SizedBox(height: 5), // Space before error message
|
||||||
|
Text(
|
||||||
|
"Required",
|
||||||
|
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (isDesktop)
|
||||||
|
Spacer()
|
||||||
|
else
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -413,9 +465,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
CustomTextFieldWrapper(
|
CustomTextFieldWrapper(
|
||||||
isFocused: _isHotelNameFocused,
|
isFocused: _isHotelNameFocused,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
width: isDesktop
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: DropdownSearch<String>(
|
child: DropdownSearch<String>(
|
||||||
@ -490,9 +540,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
CustomTextFieldWrapper(
|
CustomTextFieldWrapper(
|
||||||
isFocused: _dateFocus,
|
isFocused: _dateFocus,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
width: isDesktop
|
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -554,23 +602,36 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
width: isDesktop
|
width: isDesktop
|
||||||
? MediaQuery.of(context).size.width * 0.34
|
? MediaQuery.of(context).size.width * 0.34
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
: MediaQuery.of(context).size.width * 0.66,
|
||||||
child: TextField(
|
child: SizedBox(
|
||||||
focusNode: _commentsFocusNode,
|
height: 40,
|
||||||
controller: _visaCommentsController,
|
child: TextField(
|
||||||
maxLines: 6,
|
focusNode: _commentsFocusNode,
|
||||||
keyboardType: TextInputType.multiline,
|
controller: _visaCommentsController,
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Comments",
|
labelText: "Comments",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
if (isDesktop) Spacer(),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: _handleAction(isDesktop),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,13 +9,14 @@ class AccomodationListWidget extends StatelessWidget {
|
|||||||
final Function(String, bool) onAddNew;
|
final Function(String, bool) onAddNew;
|
||||||
final bool isViewMode;
|
final bool isViewMode;
|
||||||
|
|
||||||
const AccomodationListWidget(
|
const AccomodationListWidget({
|
||||||
{super.key,
|
super.key,
|
||||||
required this.accommodationList,
|
required this.accommodationList,
|
||||||
required this.onOpen,
|
required this.onOpen,
|
||||||
required this.onDeleteAccommodation,
|
required this.onDeleteAccommodation,
|
||||||
required this.onAddNew,
|
required this.onAddNew,
|
||||||
required this.isViewMode});
|
required this.isViewMode,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -24,6 +25,7 @@ class AccomodationListWidget extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -31,28 +33,17 @@ class AccomodationListWidget extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
// Text(
|
||||||
"Accomodation Booking List",
|
// "Accomodation Booking List",
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
// style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
),
|
// ),
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: isViewMode
|
cursor: isViewMode
|
||||||
? SystemMouseCursors.forbidden
|
? SystemMouseCursors.forbidden
|
||||||
: SystemMouseCursors.click,
|
: SystemMouseCursors.click,
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
child: GestureDetector(
|
||||||
backgroundColor: Color(0xFF114D8B),
|
onTap: isViewMode
|
||||||
foregroundColor: Colors.white,
|
|
||||||
disabledBackgroundColor: Color(0xFF114D8B),
|
|
||||||
disabledForegroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
|
||||||
),
|
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
),
|
|
||||||
onPressed: isViewMode
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
print("New data");
|
print("New data");
|
||||||
@ -62,20 +53,73 @@ class AccomodationListWidget extends StatelessWidget {
|
|||||||
mainAxisSize:
|
mainAxisSize:
|
||||||
MainAxisSize.min, // Ensures content fits nicely
|
MainAxisSize.min, // Ensures content fits nicely
|
||||||
children: [
|
children: [
|
||||||
Text(
|
// Text(
|
||||||
"Add New",
|
// "Add New",
|
||||||
style: TextStyle(fontSize: 13),
|
// style: TextStyle(fontSize: 13),
|
||||||
),
|
// ),
|
||||||
SizedBox(width: 8), // spacing between icon and text
|
// SizedBox(width: 8), // spacing between icon and text
|
||||||
Icon(
|
Icon(
|
||||||
Icons.add_circle_outline_rounded,
|
Icons.add_circle_sharp,
|
||||||
size: 15,
|
size: 30,
|
||||||
color: Colors.white,
|
color: Color(0xFF114D8B),
|
||||||
),
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// print("New data");
|
||||||
|
//
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
// children: [
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
|
// MouseRegion(
|
||||||
|
// cursor: isViewMode
|
||||||
|
// ? SystemMouseCursors.forbidden
|
||||||
|
// : SystemMouseCursors.click,
|
||||||
|
// child: ElevatedButton(
|
||||||
|
// style: ElevatedButton.styleFrom(
|
||||||
|
// backgroundColor: Color(0xFF114D8B),
|
||||||
|
// foregroundColor: Colors.white,
|
||||||
|
// disabledBackgroundColor: Color(0xFF114D8B),
|
||||||
|
// disabledForegroundColor: Colors.white,
|
||||||
|
// shape: RoundedRectangleBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(8),
|
||||||
|
// side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||||
|
// ),
|
||||||
|
// padding:
|
||||||
|
// EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
|
// ),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// print("New data");
|
||||||
|
// onAddNew("Accomodation", true);
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize:
|
||||||
|
// MainAxisSize.min, // Ensures content fits nicely
|
||||||
|
// children: [
|
||||||
|
// // spacing between icon and text
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|||||||
@ -31,6 +31,7 @@ class BusListWidget extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -38,28 +39,12 @@ class BusListWidget extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
|
||||||
"Bus Booking List",
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: isViewMode
|
cursor: isViewMode
|
||||||
? SystemMouseCursors.forbidden
|
? SystemMouseCursors.forbidden
|
||||||
: SystemMouseCursors.click,
|
: SystemMouseCursors.click,
|
||||||
child: ElevatedButton(
|
child: GestureDetector(
|
||||||
style: ElevatedButton.styleFrom(
|
onTap: isViewMode
|
||||||
backgroundColor: Color(0xFF114D8B),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
disabledBackgroundColor: Color(0xFF114D8B),
|
|
||||||
disabledForegroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
|
||||||
),
|
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
),
|
|
||||||
onPressed: isViewMode
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
print("New data");
|
print("New data");
|
||||||
@ -69,19 +54,50 @@ class BusListWidget extends StatelessWidget {
|
|||||||
mainAxisSize:
|
mainAxisSize:
|
||||||
MainAxisSize.min, // Ensures content fits nicely
|
MainAxisSize.min, // Ensures content fits nicely
|
||||||
children: [
|
children: [
|
||||||
Text(
|
// Text(
|
||||||
"Add New",
|
// "Add New",
|
||||||
style: TextStyle(fontSize: 13),
|
// style: TextStyle(fontSize: 13),
|
||||||
),
|
// ),
|
||||||
SizedBox(width: 8), // spacing between icon and text
|
// SizedBox(width: 8), // spacing between icon and text
|
||||||
Icon(
|
Icon(
|
||||||
Icons.add_circle_outline_rounded,
|
Icons.add_circle_sharp,
|
||||||
size: 15,
|
size: 30,
|
||||||
color: Colors.white,
|
color: Color(0xFF114D8B),
|
||||||
),
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// child: ElevatedButton(
|
||||||
|
// style: ElevatedButton.styleFrom(
|
||||||
|
// backgroundColor: Color(0xFF114D8B),
|
||||||
|
// foregroundColor: Colors.white,
|
||||||
|
// disabledBackgroundColor: Color(0xFF114D8B),
|
||||||
|
// disabledForegroundColor: Colors.white,
|
||||||
|
// shape: RoundedRectangleBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(8),
|
||||||
|
// side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||||
|
// ),
|
||||||
|
// padding:
|
||||||
|
// EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
|
// ),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// print("New data");
|
||||||
|
// onAddNew("Bus", true);
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize:
|
||||||
|
// MainAxisSize.min, // Ensures content fits nicely
|
||||||
|
// children: [
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -26,6 +26,7 @@ class FlightListWidget extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -33,28 +34,16 @@ class FlightListWidget extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
// Text(
|
||||||
"Flight Booking List",
|
// "Flight Booking List",
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
// style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
),
|
// ),
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: isViewMode
|
cursor: isViewMode
|
||||||
? SystemMouseCursors.forbidden
|
? SystemMouseCursors.forbidden
|
||||||
: SystemMouseCursors.click,
|
: SystemMouseCursors.click,
|
||||||
child: ElevatedButton(
|
child: GestureDetector(
|
||||||
style: ElevatedButton.styleFrom(
|
onTap: isViewMode
|
||||||
backgroundColor: Color(0xFF114D8B),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
disabledBackgroundColor: Color(0xFF114D8B),
|
|
||||||
disabledForegroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
|
||||||
),
|
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
),
|
|
||||||
onPressed: isViewMode
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
print("New data");
|
print("New data");
|
||||||
@ -64,16 +53,16 @@ class FlightListWidget extends StatelessWidget {
|
|||||||
mainAxisSize:
|
mainAxisSize:
|
||||||
MainAxisSize.min, // Ensures content fits nicely
|
MainAxisSize.min, // Ensures content fits nicely
|
||||||
children: [
|
children: [
|
||||||
Text(
|
// Text(
|
||||||
"Add New",
|
// "Add New",
|
||||||
style: TextStyle(fontSize: 13),
|
// style: TextStyle(fontSize: 13),
|
||||||
),
|
// ),
|
||||||
SizedBox(width: 8), // spacing between icon and text
|
// SizedBox(width: 8), // spacing between icon and text
|
||||||
Icon(
|
Icon(
|
||||||
Icons.add_circle_outline_rounded,
|
Icons.add_circle_sharp,
|
||||||
size: 15,
|
size: 30,
|
||||||
color: Colors.white,
|
color: Color(0xFF114D8B),
|
||||||
),
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -30,6 +30,7 @@ class ForexListWidget extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -37,28 +38,13 @@ class ForexListWidget extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
|
||||||
"Forex List",
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: isViewMode
|
cursor: isViewMode
|
||||||
? SystemMouseCursors.forbidden
|
? SystemMouseCursors.forbidden
|
||||||
: SystemMouseCursors.click,
|
: SystemMouseCursors.click,
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
child: GestureDetector(
|
||||||
backgroundColor: Color(0xFF114D8B),
|
onTap: isViewMode
|
||||||
foregroundColor: Colors.white,
|
|
||||||
disabledBackgroundColor: Color(0xFF114D8B),
|
|
||||||
disabledForegroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
|
||||||
),
|
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
),
|
|
||||||
onPressed: isViewMode
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
print("New data");
|
print("New data");
|
||||||
@ -68,20 +54,72 @@ class ForexListWidget extends StatelessWidget {
|
|||||||
mainAxisSize:
|
mainAxisSize:
|
||||||
MainAxisSize.min, // Ensures content fits nicely
|
MainAxisSize.min, // Ensures content fits nicely
|
||||||
children: [
|
children: [
|
||||||
Text(
|
// Text(
|
||||||
"Add New",
|
// "Add New",
|
||||||
style: TextStyle(fontSize: 13),
|
// style: TextStyle(fontSize: 13),
|
||||||
),
|
// ),
|
||||||
SizedBox(width: 8), // spacing between icon and text
|
// SizedBox(width: 8), // spacing between icon and text
|
||||||
Icon(
|
Icon(
|
||||||
Icons.add_circle_outline_rounded,
|
Icons.add_circle_sharp,
|
||||||
size: 15,
|
size: 30,
|
||||||
color: Colors.white,
|
color: Color(0xFF114D8B),
|
||||||
),
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// print("New data");
|
||||||
|
//
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
// children: [
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
|
// MouseRegion(
|
||||||
|
// cursor: isViewMode
|
||||||
|
// ? SystemMouseCursors.forbidden
|
||||||
|
// : SystemMouseCursors.click,
|
||||||
|
// child: ElevatedButton(
|
||||||
|
// style: ElevatedButton.styleFrom(
|
||||||
|
// backgroundColor: Color(0xFF114D8B),
|
||||||
|
// foregroundColor: Colors.white,
|
||||||
|
// disabledBackgroundColor: Color(0xFF114D8B),
|
||||||
|
// disabledForegroundColor: Colors.white,
|
||||||
|
// shape: RoundedRectangleBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(8),
|
||||||
|
// side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||||
|
// ),
|
||||||
|
// padding:
|
||||||
|
// EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
|
// ),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// print("New data");
|
||||||
|
// onAddNew("Forex", true);
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize:
|
||||||
|
// MainAxisSize.min, // Ensures content fits nicely
|
||||||
|
// children: [
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|||||||
@ -28,6 +28,7 @@ class InsuranceListWidget extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -35,28 +36,12 @@ class InsuranceListWidget extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
|
||||||
"Insurance Booking List",
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: isViewMode
|
cursor: isViewMode
|
||||||
? SystemMouseCursors.forbidden
|
? SystemMouseCursors.forbidden
|
||||||
: SystemMouseCursors.click,
|
: SystemMouseCursors.click,
|
||||||
child: ElevatedButton(
|
child: GestureDetector(
|
||||||
style: ElevatedButton.styleFrom(
|
onTap: isViewMode
|
||||||
backgroundColor: Color(0xFF114D8B),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
disabledBackgroundColor: Color(0xFF114D8B),
|
|
||||||
disabledForegroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
|
||||||
),
|
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
),
|
|
||||||
onPressed: isViewMode
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
print("New data");
|
print("New data");
|
||||||
@ -66,19 +51,50 @@ class InsuranceListWidget extends StatelessWidget {
|
|||||||
mainAxisSize:
|
mainAxisSize:
|
||||||
MainAxisSize.min, // Ensures content fits nicely
|
MainAxisSize.min, // Ensures content fits nicely
|
||||||
children: [
|
children: [
|
||||||
Text(
|
// Text(
|
||||||
"Add New",
|
// "Add New",
|
||||||
style: TextStyle(fontSize: 13),
|
// style: TextStyle(fontSize: 13),
|
||||||
),
|
// ),
|
||||||
SizedBox(width: 8), // spacing between icon and text
|
// SizedBox(width: 8), // spacing between icon and text
|
||||||
Icon(
|
Icon(
|
||||||
Icons.add_circle_outline_rounded,
|
Icons.add_circle_sharp,
|
||||||
size: 15,
|
size: 30,
|
||||||
color: Colors.white,
|
color: Color(0xFF114D8B),
|
||||||
),
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// child: ElevatedButton(
|
||||||
|
// style: ElevatedButton.styleFrom(
|
||||||
|
// backgroundColor: Color(0xFF114D8B),
|
||||||
|
// foregroundColor: Colors.white,
|
||||||
|
// disabledBackgroundColor: Color(0xFF114D8B),
|
||||||
|
// disabledForegroundColor: Colors.white,
|
||||||
|
// shape: RoundedRectangleBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(8),
|
||||||
|
// side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||||
|
// ),
|
||||||
|
// padding:
|
||||||
|
// EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
|
// ),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// print("New data");
|
||||||
|
// onAddNew("Insurance", true);
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize:
|
||||||
|
// MainAxisSize.min, // Ensures content fits nicely
|
||||||
|
// children: [
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -24,6 +24,7 @@ class MiscellaneousListWidget extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -31,42 +32,63 @@ class MiscellaneousListWidget extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
|
||||||
"Miscellaneous List",
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: isViewMode
|
cursor: isViewMode
|
||||||
? SystemMouseCursors.forbidden
|
? SystemMouseCursors.forbidden
|
||||||
: SystemMouseCursors.click,
|
: SystemMouseCursors.click,
|
||||||
child: ElevatedButton(
|
child: GestureDetector(
|
||||||
style: ElevatedButton.styleFrom(
|
onTap: isViewMode
|
||||||
backgroundColor: Color(0xFF114D8B),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
disabledBackgroundColor: Color(0xFF114D8B),
|
|
||||||
disabledForegroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
|
||||||
),
|
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
),
|
|
||||||
onPressed: isViewMode
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
|
print("New data");
|
||||||
onAddNew("Miscellaneous", true);
|
onAddNew("Miscellaneous", true);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize:
|
||||||
|
MainAxisSize.min, // Ensures content fits nicely
|
||||||
children: [
|
children: [
|
||||||
Text("Add New", style: TextStyle(fontSize: 13)),
|
// Text(
|
||||||
SizedBox(width: 8),
|
// "Add New",
|
||||||
Icon(Icons.add_circle_outline_rounded,
|
// style: TextStyle(fontSize: 13),
|
||||||
size: 15, color: Colors.white),
|
// ),
|
||||||
|
// SizedBox(width: 8), // spacing between icon and text
|
||||||
|
Icon(
|
||||||
|
Icons.add_circle_sharp,
|
||||||
|
size: 30,
|
||||||
|
color: Color(0xFF114D8B),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// child: ElevatedButton(
|
||||||
|
// style: ElevatedButton.styleFrom(
|
||||||
|
// backgroundColor: Color(0xFF114D8B),
|
||||||
|
// foregroundColor: Colors.white,
|
||||||
|
// disabledBackgroundColor: Color(0xFF114D8B),
|
||||||
|
// disabledForegroundColor: Colors.white,
|
||||||
|
// shape: RoundedRectangleBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(8),
|
||||||
|
// side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||||
|
// ),
|
||||||
|
// padding:
|
||||||
|
// EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
|
// ),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// onAddNew("Miscellaneous", true);
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
// children: [
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -26,6 +26,7 @@ class TaxiListWidget extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -33,28 +34,13 @@ class TaxiListWidget extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
|
||||||
"Taxi Booking List",
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: isViewMode
|
cursor: isViewMode
|
||||||
? SystemMouseCursors.forbidden
|
? SystemMouseCursors.forbidden
|
||||||
: SystemMouseCursors.click,
|
: SystemMouseCursors.click,
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
child: GestureDetector(
|
||||||
backgroundColor: Color(0xFF114D8B),
|
onTap: isViewMode
|
||||||
foregroundColor: Colors.white,
|
|
||||||
disabledBackgroundColor: Color(0xFF114D8B),
|
|
||||||
disabledForegroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
|
||||||
),
|
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
),
|
|
||||||
onPressed: isViewMode
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
print("New data");
|
print("New data");
|
||||||
@ -64,19 +50,50 @@ class TaxiListWidget extends StatelessWidget {
|
|||||||
mainAxisSize:
|
mainAxisSize:
|
||||||
MainAxisSize.min, // Ensures content fits nicely
|
MainAxisSize.min, // Ensures content fits nicely
|
||||||
children: [
|
children: [
|
||||||
Text(
|
// Text(
|
||||||
"Add New",
|
// "Add New",
|
||||||
style: TextStyle(fontSize: 13),
|
// style: TextStyle(fontSize: 13),
|
||||||
),
|
// ),
|
||||||
SizedBox(width: 8), // spacing between icon and text
|
// SizedBox(width: 8), // spacing between icon and text
|
||||||
Icon(
|
Icon(
|
||||||
Icons.add_circle_outline_rounded,
|
Icons.add_circle_sharp,
|
||||||
size: 15,
|
size: 30,
|
||||||
color: Colors.white,
|
color: Color(0xFF114D8B),
|
||||||
),
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// child: ElevatedButton(
|
||||||
|
// style: ElevatedButton.styleFrom(
|
||||||
|
// backgroundColor: Color(0xFF114D8B),
|
||||||
|
// foregroundColor: Colors.white,
|
||||||
|
// disabledBackgroundColor: Color(0xFF114D8B),
|
||||||
|
// disabledForegroundColor: Colors.white,
|
||||||
|
// shape: RoundedRectangleBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(8),
|
||||||
|
// side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||||
|
// ),
|
||||||
|
// padding:
|
||||||
|
// EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
|
// ),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// print("New data");
|
||||||
|
// onAddNew("Taxi", true);
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize:
|
||||||
|
// MainAxisSize.min, // Ensures content fits nicely
|
||||||
|
// children: [
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -26,6 +26,7 @@ class TrainListWidget extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -33,50 +34,87 @@ class TrainListWidget extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
|
||||||
"Train Booking List",
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: isViewMode
|
cursor: isViewMode
|
||||||
? SystemMouseCursors.forbidden
|
? SystemMouseCursors.forbidden
|
||||||
: SystemMouseCursors.click,
|
: SystemMouseCursors.click,
|
||||||
child: ElevatedButton(
|
child: MouseRegion(
|
||||||
style: ElevatedButton.styleFrom(
|
cursor: isViewMode
|
||||||
backgroundColor: Color(0xFF114D8B),
|
? SystemMouseCursors.forbidden
|
||||||
foregroundColor: Colors.white,
|
: SystemMouseCursors.click,
|
||||||
disabledBackgroundColor: Color(0xFF114D8B),
|
|
||||||
disabledForegroundColor: Colors.white,
|
child: GestureDetector(
|
||||||
shape: RoundedRectangleBorder(
|
onTap: isViewMode
|
||||||
borderRadius: BorderRadius.circular(8),
|
? null
|
||||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
: () {
|
||||||
|
print("New data");
|
||||||
|
onAddNew("Train", true);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize:
|
||||||
|
MainAxisSize.min, // Ensures content fits nicely
|
||||||
|
children: [
|
||||||
|
// Text(
|
||||||
|
// "Add New",
|
||||||
|
// style: TextStyle(fontSize: 13),
|
||||||
|
// ),
|
||||||
|
// SizedBox(width: 8), // spacing between icon and text
|
||||||
|
Icon(
|
||||||
|
Icons.add_circle_sharp,
|
||||||
|
size: 30,
|
||||||
|
color: Color(0xFF114D8B),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
),
|
|
||||||
onPressed: isViewMode
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
print("New data");
|
|
||||||
onAddNew("Train", true);
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize:
|
|
||||||
MainAxisSize.min, // Ensures content fits nicely
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Add New",
|
|
||||||
style: TextStyle(fontSize: 13),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8), // spacing between icon and text
|
|
||||||
Icon(
|
|
||||||
Icons.add_circle_outline_rounded,
|
|
||||||
size: 15,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// print("New data");
|
||||||
|
//
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
// children: [
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
|
// child: ElevatedButton(
|
||||||
|
// style: ElevatedButton.styleFrom(
|
||||||
|
// backgroundColor: Color(0xFF114D8B),
|
||||||
|
// foregroundColor: Colors.white,
|
||||||
|
// disabledBackgroundColor: Color(0xFF114D8B),
|
||||||
|
// disabledForegroundColor: Colors.white,
|
||||||
|
// shape: RoundedRectangleBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(8),
|
||||||
|
// side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||||
|
// ),
|
||||||
|
// padding:
|
||||||
|
// EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
|
// ),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// print("New data");
|
||||||
|
// onAddNew("Train", true);
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize:
|
||||||
|
// MainAxisSize.min, // Ensures content fits nicely
|
||||||
|
// children: [
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -28,6 +28,7 @@ class VisaListWidget extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@ -35,43 +36,51 @@ class VisaListWidget extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
|
||||||
"Visa List",
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: isViewMode
|
cursor: isViewMode
|
||||||
? SystemMouseCursors.forbidden
|
? SystemMouseCursors.forbidden
|
||||||
: SystemMouseCursors.click,
|
: SystemMouseCursors.click,
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
child: GestureDetector(
|
||||||
backgroundColor: Color(0xFF114D8B),
|
onTap: isViewMode
|
||||||
foregroundColor: Colors.white,
|
|
||||||
disabledBackgroundColor: Color(0xFF114D8B),
|
|
||||||
disabledForegroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
|
||||||
),
|
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
||||||
),
|
|
||||||
onPressed: isViewMode
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
print("New data");
|
print("New data");
|
||||||
onAddNew("Visa", true);
|
onAddNew("Visa", true);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize:
|
||||||
|
MainAxisSize.min, // Ensures content fits nicely
|
||||||
children: [
|
children: [
|
||||||
Text("Add New", style: TextStyle(fontSize: 13)),
|
// Text(
|
||||||
SizedBox(width: 8),
|
// "Add New",
|
||||||
Icon(Icons.add_circle_outline_rounded,
|
// style: TextStyle(fontSize: 13),
|
||||||
size: 15, color: Colors.white),
|
// ),
|
||||||
|
// SizedBox(width: 8), // spacing between icon and text
|
||||||
|
Icon(
|
||||||
|
Icons.add_circle_sharp,
|
||||||
|
size: 30,
|
||||||
|
color: Color(0xFF114D8B),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// onPressed: isViewMode
|
||||||
|
// ? null
|
||||||
|
// : () {
|
||||||
|
// print("New data");
|
||||||
|
//
|
||||||
|
// },
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
// children: [
|
||||||
|
// Icon(
|
||||||
|
// Icons.add_circle_sharp,
|
||||||
|
// size: 30,
|
||||||
|
// color: Color(0xFF114D8B),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -75,19 +75,6 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
"Forex": [],
|
"Forex": [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Store form values for each tab
|
|
||||||
final Map<String, Map<String, String>> formData = {
|
|
||||||
"Flight": {}, // Stores data for the Flight tab
|
|
||||||
"Train": {}, // Stores data for the Train tab
|
|
||||||
"Taxi": {}, // Stores data for the Car tab
|
|
||||||
"Bus": {}, // Stores data for the Bus tab
|
|
||||||
"Insurance": {}, // Stores data for the Bus tab
|
|
||||||
"Accomodation": {}, // Stores data for the Accomodation tab
|
|
||||||
"Miscellaneous": {},
|
|
||||||
"Forex": {},
|
|
||||||
"Visa": {},
|
|
||||||
};
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -101,7 +88,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
selectedAllServices = result;
|
selectedAllServices = result;
|
||||||
});
|
});
|
||||||
print("Fetched services: $selectedAllServices");
|
print("Fetched services Order: $selectedAllServices");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error fetching role list: $e');
|
print('Error fetching role list: $e');
|
||||||
}
|
}
|
||||||
@ -161,8 +148,12 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
selectedIds.contains(service['service_id'].toString()))
|
selectedIds.contains(service['service_id'].toString()))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
// setState(() {
|
||||||
|
// ServicesChoosed = [...originalFiltered, ...additionalServices];
|
||||||
|
// });
|
||||||
setState(() {
|
setState(() {
|
||||||
ServicesChoosed = [...originalFiltered, ...additionalServices];
|
ServicesChoosed = [...originalFiltered, ...additionalServices]
|
||||||
|
..sort((a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0));
|
||||||
});
|
});
|
||||||
|
|
||||||
print(
|
print(
|
||||||
@ -177,9 +168,14 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
ServicesChoosed = filtered;
|
ServicesChoosed = filtered
|
||||||
|
..sort((a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// setState(() {
|
||||||
|
// ServicesChoosed = filtered;
|
||||||
|
// });
|
||||||
|
|
||||||
print("Filtered Selected Services Chooesed: $ServicesChoosed");
|
print("Filtered Selected Services Chooesed: $ServicesChoosed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -287,21 +283,6 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
Widget selectedWidget;
|
Widget selectedWidget;
|
||||||
Widget selectedListWidget;
|
Widget selectedListWidget;
|
||||||
|
|
||||||
// void updateFormData(String tab, String key, String value) {
|
|
||||||
// setState(() {
|
|
||||||
// formData[tab]![key] = value; // Update the stored data
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
void updateFormData(String tab, String key, String value) {
|
|
||||||
setState(() {
|
|
||||||
if (!formData.containsKey(tab)) {
|
|
||||||
formData[tab] = {}; // Initialize if null
|
|
||||||
}
|
|
||||||
formData[tab]![key] = value; // Update the stored data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleItineraryUpdate(String type, Map<String, dynamic> newData) {
|
void handleItineraryUpdate(String type, Map<String, dynamic> newData) {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (!itineraryData.containsKey(type)) {
|
if (!itineraryData.containsKey(type)) {
|
||||||
@ -488,12 +469,13 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
break;
|
break;
|
||||||
case "Accomodation":
|
case "Accomodation":
|
||||||
selectedListWidget = AccomodationListWidget(
|
selectedListWidget = AccomodationListWidget(
|
||||||
accommodationList: itineraryData["Accomodation"]!,
|
accommodationList: itineraryData["Accomodation"]!,
|
||||||
onOpen: handleEdit,
|
onOpen: handleEdit,
|
||||||
onAddNew: handlecreateNewPlan,
|
onAddNew: handlecreateNewPlan,
|
||||||
isViewMode: widget.isViewMode,
|
isViewMode: widget.isViewMode,
|
||||||
onDeleteAccommodation: (data) =>
|
onDeleteAccommodation: (data) =>
|
||||||
handleItinerarydelete("Accomodation", data));
|
handleItinerarydelete("Accomodation", data),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case "Miscellaneous":
|
case "Miscellaneous":
|
||||||
selectedListWidget = MiscellaneousListWidget(
|
selectedListWidget = MiscellaneousListWidget(
|
||||||
@ -545,11 +527,13 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
break;
|
break;
|
||||||
case "Insurance":
|
case "Insurance":
|
||||||
selectedWidget = InsuranceScreen(
|
selectedWidget = InsuranceScreen(
|
||||||
onClose: handleClose,
|
onClose: handleClose,
|
||||||
apiData: widget.apiData,
|
apiData: widget.apiData,
|
||||||
loginUser: widget.loginUser,
|
loginUser: widget.loginUser,
|
||||||
onSaveInsurance: (data) => handleItineraryUpdate("Insurance", data),
|
onSaveInsurance: (data) => handleItineraryUpdate("Insurance", data),
|
||||||
selectedItem: selectedItem);
|
selectedItem: selectedItem,
|
||||||
|
flightData: itineraryData["Flight"]!,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Visa":
|
case "Visa":
|
||||||
@ -560,6 +544,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
loginUser: widget.loginUser,
|
loginUser: widget.loginUser,
|
||||||
onSaveVisa: (data) => handleItineraryUpdate("Visa", data),
|
onSaveVisa: (data) => handleItineraryUpdate("Visa", data),
|
||||||
selectedItem: selectedItem,
|
selectedItem: selectedItem,
|
||||||
|
flightData: itineraryData["Flight"]!,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "Miscellaneous":
|
case "Miscellaneous":
|
||||||
@ -580,66 +565,169 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
onSaveAccomadation: (data) =>
|
onSaveAccomadation: (data) =>
|
||||||
handleItineraryUpdate("Accomodation", data),
|
handleItineraryUpdate("Accomodation", data),
|
||||||
selectedItem: selectedItem,
|
selectedItem: selectedItem,
|
||||||
|
flightData: itineraryData["Flight"]!,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "Forex":
|
case "Forex":
|
||||||
selectedWidget = ForexScreen(
|
selectedWidget = ForexScreen(
|
||||||
onClose: handleClose,
|
onClose: handleClose,
|
||||||
apiData: widget.apiData,
|
apiData: widget.apiData,
|
||||||
loginUser: widget.loginUser,
|
loginUser: widget.loginUser,
|
||||||
apiCountryData: widget.apiCountryData,
|
apiCountryData: widget.apiCountryData,
|
||||||
onSaveForex: (data) => handleItineraryUpdate("Forex", data),
|
onSaveForex: (data) => handleItineraryUpdate("Forex", data),
|
||||||
selectedItem: selectedItem);
|
selectedItem: selectedItem,
|
||||||
|
flightData: itineraryData["Flight"]!,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case "Flight":
|
case "Flight":
|
||||||
default:
|
default:
|
||||||
selectedWidget = FlightScreen(
|
selectedWidget = FlightScreen(
|
||||||
onClose: handleClose,
|
onClose: handleClose,
|
||||||
loginUser: widget.loginUser,
|
loginUser: widget.loginUser,
|
||||||
onSaveFlight: (data) => handleItineraryUpdate("Flight", data),
|
onSaveFlight: (data) => handleItineraryUpdate("Flight", data),
|
||||||
apiData: widget.apiData,
|
apiData: widget.apiData,
|
||||||
selectedItem: selectedItem);
|
selectedItem: selectedItem,
|
||||||
|
flightData: itineraryData["Flight"]!,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||||
|
// bool isMobile = sizingInfo.isMobile;
|
||||||
|
// bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
//
|
||||||
|
// return Column(
|
||||||
|
// // mainAxisSize: MainAxisSize.min,
|
||||||
|
// children: [
|
||||||
|
// Transform.translate(
|
||||||
|
// offset: Offset(0, 20),
|
||||||
|
// child: Container(
|
||||||
|
// decoration: BoxDecoration(
|
||||||
|
// border: Border(
|
||||||
|
// bottom: BorderSide(color: Color(0xFFF4F4FB), width: 2)),
|
||||||
|
// borderRadius: BorderRadius.circular(1),
|
||||||
|
// color: Color(0xFFF4F4FB),
|
||||||
|
// ),
|
||||||
|
// margin: EdgeInsets.symmetric(
|
||||||
|
// horizontal: MediaQuery.of(context).size.width *
|
||||||
|
// 0.05, // 30% of screen width as horizontal padding
|
||||||
|
// vertical: MediaQuery.of(context).size.height *
|
||||||
|
// 0, // 5% of screen height as vertical padding
|
||||||
|
// ),
|
||||||
|
// child: isMobile
|
||||||
|
// ? Expanded(
|
||||||
|
// child: SingleChildScrollView(
|
||||||
|
// scrollDirection: Axis.horizontal,
|
||||||
|
// child: Row(
|
||||||
|
// children: _buildOptions(),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// : Row(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
// // mainAxisSize: MainAxisSize.min,
|
||||||
|
// children: _buildOptions(),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Container(
|
||||||
|
// decoration: BoxDecoration(
|
||||||
|
// // border:
|
||||||
|
// // Border(bottom: BorderSide(color: Colors.black, width: 2)),
|
||||||
|
//
|
||||||
|
// border: Border.all(color: Colors.green, width: 1.5),
|
||||||
|
// borderRadius: BorderRadius.circular(1),
|
||||||
|
// color: Colors.yellowAccent.shade100,
|
||||||
|
// ),
|
||||||
|
// margin: EdgeInsets.only(top: 0),
|
||||||
|
// child: Column(
|
||||||
|
// // mainAxisSize: MainAxisSize.min,
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
// children: [
|
||||||
|
// SizedBox(height: 2),
|
||||||
|
// isSelected ? selectedWidget : selectedListWidget,
|
||||||
|
// // TrainScreen()
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
//
|
||||||
|
// ],
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
|
||||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||||
bool isMobile = sizingInfo.isMobile;
|
bool isMobile = sizingInfo.isMobile;
|
||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
|
||||||
|
|
||||||
return Column(
|
return Stack(
|
||||||
// mainAxisSize: MainAxisSize.min,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
|
// Second container (yellow box)
|
||||||
Container(
|
Container(
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
top: 40), // Push it down to make room for the tab bar
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
color: Colors.white, // Card background
|
||||||
bottom: BorderSide(color: Color(0xFFF4F4FB), width: 2)),
|
// color: Colors.yellow.shade50, // Card background
|
||||||
borderRadius: BorderRadius.circular(1),
|
// color: Color(0xFFF9F9F9), // Slightly lighter than white
|
||||||
// color: Color(0xFFF4F4FB),
|
// border: Border.all(color: Color(0xFFE6E7F5), width: 1.3),
|
||||||
|
border: Border.all(color: Color(0xFFE6E7F5), width: 1.2),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
// color: Color(0x0D000000), // 5% opacity black
|
||||||
|
color: Colors.black12, // 5% opacity black
|
||||||
|
blurRadius: 5,
|
||||||
|
offset: Offset(0, 0.2),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
// padding: EdgeInsets.all(10),
|
child: Column(
|
||||||
child: isMobile
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
? Expanded(
|
children: [
|
||||||
child: SingleChildScrollView(
|
SizedBox(height: 2),
|
||||||
|
isSelected ? selectedWidget : selectedListWidget,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// First container (tab bar) — positioned above
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: MediaQuery.of(context).size.width * 0.05,
|
||||||
|
right: MediaQuery.of(context).size.width * 0.05,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white, // Card background
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
|
||||||
|
// color: Color(0xFFE6E7F5)
|
||||||
|
// border: Border.all(color: Colors.black12, width: 1.3),
|
||||||
|
border: Border.all(color: Color(0xFFE6E7F5), width: 1.2),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black12,
|
||||||
|
// color: Color(0x0D000000), // 5% opacity black
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: Offset(0, 0.2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: isMobile
|
||||||
|
? SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: _buildOptions(),
|
children: _buildOptions(),
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: _buildOptions(),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
// mainAxisSize: MainAxisSize.min,
|
|
||||||
children: _buildOptions(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
// mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
SizedBox(height: 2),
|
|
||||||
isSelected ? selectedWidget : selectedListWidget,
|
|
||||||
// TrainScreen()
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -703,7 +791,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 10),
|
||||||
child: Row(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
iconUrl.isNotEmpty
|
iconUrl.isNotEmpty
|
||||||
? Image.network(
|
? Image.network(
|
||||||
@ -713,7 +801,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
errorBuilder: (context, error, stackTrace) {
|
errorBuilder: (context, error, stackTrace) {
|
||||||
return Icon(
|
return Icon(
|
||||||
fallbackIcon,
|
fallbackIcon,
|
||||||
size: 18,
|
size: 25,
|
||||||
color: isOptionSelected
|
color: isOptionSelected
|
||||||
? Color(0xFF114D8B)
|
? Color(0xFF114D8B)
|
||||||
: Color(0xFF475569),
|
: Color(0xFF475569),
|
||||||
@ -722,24 +810,30 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
)
|
)
|
||||||
: Icon(
|
: Icon(
|
||||||
fallbackIcon,
|
fallbackIcon,
|
||||||
size: 18,
|
size: 25,
|
||||||
color: isOptionSelected
|
color: isOptionSelected
|
||||||
? Color(0xFF114D8B)
|
? Color(0xFF114D8B)
|
||||||
: Color(0xFF475569),
|
: Color(0xFF475569),
|
||||||
),
|
),
|
||||||
SizedBox(width: 2),
|
SizedBox(height: 2),
|
||||||
Text(
|
Row(
|
||||||
name,
|
children: [
|
||||||
style: TextStyle(
|
Text(
|
||||||
fontSize: 14,
|
name,
|
||||||
color: isOptionSelected ? Color(0xFF114D8B) : Color(0xFF475569),
|
style: TextStyle(
|
||||||
fontFamily: "Archivo",
|
fontSize: 14,
|
||||||
fontWeight:
|
color: isOptionSelected
|
||||||
isOptionSelected ? FontWeight.bold : FontWeight.w500,
|
? Color(0xFF114D8B)
|
||||||
),
|
: Color(0xFF475569),
|
||||||
|
fontFamily: "Archivo",
|
||||||
|
fontWeight:
|
||||||
|
isOptionSelected ? FontWeight.bold : FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 4),
|
||||||
|
if (hasData) Icon(Icons.circle, size: 8, color: Colors.green),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
SizedBox(width: 4),
|
|
||||||
if (hasData) Icon(Icons.circle, size: 8, color: Colors.green),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -24,6 +24,9 @@ class ListPlans extends StatefulWidget {
|
|||||||
class _ListPlansState extends State<ListPlans> {
|
class _ListPlansState extends State<ListPlans> {
|
||||||
final ApiService apiService = ApiService();
|
final ApiService apiService = ApiService();
|
||||||
|
|
||||||
|
int currentPage = 0;
|
||||||
|
int itemsPerPage = 8;
|
||||||
|
|
||||||
late Future<List<Plan>> futurePlans;
|
late Future<List<Plan>> futurePlans;
|
||||||
String? userId;
|
String? userId;
|
||||||
String? orgId;
|
String? orgId;
|
||||||
@ -285,7 +288,7 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
margin: isDesktop
|
margin: isDesktop
|
||||||
? EdgeInsets.all(10.0)
|
? EdgeInsets.all(10.0)
|
||||||
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
|
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
|
||||||
padding: const EdgeInsets.all(10),
|
// padding: const EdgeInsets.all(10),
|
||||||
height: isDesktop
|
height: isDesktop
|
||||||
? MediaQuery.of(context).size.height * 0.98
|
? MediaQuery.of(context).size.height * 0.98
|
||||||
: MediaQuery.of(context).size.height,
|
: MediaQuery.of(context).size.height,
|
||||||
@ -305,7 +308,7 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(1.0),
|
padding: const EdgeInsets.all(1.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(10.0),
|
// padding: const EdgeInsets.all(10.0),
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@ -356,6 +359,7 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
// SizedBox(width: 16),
|
// SizedBox(width: 16),
|
||||||
|
|
||||||
Spacer(),
|
Spacer(),
|
||||||
// ElevatedButton(
|
// ElevatedButton(
|
||||||
// style: ElevatedButton.styleFrom(
|
// style: ElevatedButton.styleFrom(
|
||||||
@ -486,6 +490,11 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
plans.sort((a, b) =>
|
plans.sort((a, b) =>
|
||||||
int.parse(b.planId).compareTo(int.parse(a.planId)));
|
int.parse(b.planId).compareTo(int.parse(a.planId)));
|
||||||
|
|
||||||
|
List<Plan> paginatedPlans = plans
|
||||||
|
.skip(currentPage * itemsPerPage)
|
||||||
|
.take(itemsPerPage)
|
||||||
|
.toList();
|
||||||
|
|
||||||
Widget table = LayoutBuilder(
|
Widget table = LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
double minWidth = isDesktop ? constraints.maxWidth : 1300;
|
double minWidth = isDesktop ? constraints.maxWidth : 1300;
|
||||||
@ -508,13 +517,13 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
fontFamily: "Archivo",
|
fontFamily: "Archivo",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.bold))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('UserName',
|
label: Text('Trip Name',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
color: Color(0xFF9E9DBD),
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Archivo",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.bold))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Trip Title',
|
label: Text('UserName',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
color: Color(0xFF9E9DBD),
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Archivo",
|
||||||
@ -544,21 +553,13 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
fontFamily: "Archivo",
|
fontFamily: "Archivo",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.bold))),
|
||||||
],
|
],
|
||||||
rows: plans.map((plan) {
|
rows: paginatedPlans.map((plan) {
|
||||||
return DataRow(cells: [
|
return DataRow(cells: [
|
||||||
DataCell(Text(plan.planId,
|
DataCell(Text(plan.planId,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Archivo",
|
||||||
))),
|
))),
|
||||||
DataCell(Text(
|
|
||||||
plan.userName.isNotEmpty
|
|
||||||
? plan.userName
|
|
||||||
: plan.travellerName,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
fontFamily: "Archivo",
|
|
||||||
))),
|
|
||||||
DataCell(Text(plan.tripTitle,
|
DataCell(Text(plan.tripTitle,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
@ -566,6 +567,14 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
),
|
),
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
overflow: TextOverflow.ellipsis)),
|
overflow: TextOverflow.ellipsis)),
|
||||||
|
DataCell(Text(
|
||||||
|
plan.userName.isNotEmpty
|
||||||
|
? plan.userName
|
||||||
|
: plan.travellerName,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Archivo",
|
||||||
|
))),
|
||||||
DataCell(Text(plan.tripType,
|
DataCell(Text(plan.tripType,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
@ -663,15 +672,66 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: isDesktop
|
child: Column(
|
||||||
? table
|
children: [
|
||||||
: SingleChildScrollView(
|
isDesktop
|
||||||
scrollDirection: Axis.horizontal,
|
? table
|
||||||
child: SingleChildScrollView(
|
: SingleChildScrollView(
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.horizontal,
|
||||||
child: table,
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
child: table,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
// Previous button with arrow icon
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_back_ios_new,
|
||||||
|
size: 10,
|
||||||
|
),
|
||||||
|
onPressed: currentPage > 0
|
||||||
|
? () {
|
||||||
|
setState(() {
|
||||||
|
currentPage--;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
SizedBox(width: 2),
|
||||||
|
|
||||||
|
// Page number text
|
||||||
|
Text(
|
||||||
|
'Page ${currentPage + 1}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 2),
|
||||||
|
|
||||||
|
// Next button with arrow icon
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 10,
|
||||||
|
),
|
||||||
|
onPressed: (currentPage + 1) * itemsPerPage <
|
||||||
|
plans.length
|
||||||
|
? () {
|
||||||
|
setState(() {
|
||||||
|
currentPage++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@ -42,7 +42,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
|||||||
// Map to store controllers dynamically
|
// Map to store controllers dynamically
|
||||||
final Map<String, TextEditingController> controllers = {};
|
final Map<String, TextEditingController> controllers = {};
|
||||||
bool isViewMode = false;
|
bool isViewMode = false;
|
||||||
bool isEditProfile = true;
|
bool isEditProfile = false;
|
||||||
// late final List<dynamic>? apiCountryData ;
|
// late final List<dynamic>? apiCountryData ;
|
||||||
|
|
||||||
late List<dynamic>? apiCountryData;
|
late List<dynamic>? apiCountryData;
|
||||||
@ -2932,11 +2932,11 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
isEditProfile ? context.go('/listPlan') : context.go('/listUser');
|
isEditProfile ? context.go('/listPlan') : context.go('/listUser');
|
||||||
},
|
},
|
||||||
child: isEditProfile ? Text("Back") : Text("Cancel")),
|
child: isViewMode ? Text("Back") : Text("Cancel")),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
),
|
),
|
||||||
if (!isEditProfile)
|
if (!isViewMode)
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: isViewMode
|
cursor: isViewMode
|
||||||
? SystemMouseCursors.forbidden
|
? SystemMouseCursors.forbidden
|
||||||
|
|||||||
31
lib/app.dart
31
lib/app.dart
@ -1,10 +1,37 @@
|
|||||||
|
// import 'package:flutter/material.dart';
|
||||||
|
// import 'package:frontend/routes/custom_router.dart';
|
||||||
|
//
|
||||||
|
// class MyApp extends StatelessWidget {
|
||||||
|
// const MyApp({super.key});
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// return MaterialApp.router(
|
||||||
|
// title: 'TRIP MANAGEMENT',
|
||||||
|
// routerConfig: router,
|
||||||
|
// debugShowCheckedModeBanner: false,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:frontend/routes/custom_router.dart';
|
import 'package:frontend/routes/custom_router.dart';
|
||||||
|
|
||||||
|
class MyApp extends StatefulWidget {
|
||||||
class MyApp extends StatelessWidget {
|
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyApp> createState() => _MyAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyAppState extends State<MyApp> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
SemanticsBinding.instance.ensureSemantics(); // ✅ Safe here
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
import 'app.dart';
|
import 'app.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|||||||
@ -138,6 +138,7 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
|||||||
return AppBar(
|
return AppBar(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
surfaceTintColor: Colors.white,
|
surfaceTintColor: Colors.white,
|
||||||
|
|
||||||
// elevation: 3,
|
// elevation: 3,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
// leading: showBackButton
|
// leading: showBackButton
|
||||||
@ -147,176 +148,209 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
|||||||
// )
|
// )
|
||||||
// : null,
|
// : null,
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: Row(
|
title: Padding(
|
||||||
children: [
|
padding: EdgeInsets.symmetric(
|
||||||
Padding(
|
horizontal: MediaQuery.of(context).size.width * 0.05),
|
||||||
padding: const EdgeInsets.all(10),
|
child: Row(
|
||||||
child: selectedOrg?['logo'] != null
|
children: [
|
||||||
? ClipRect(
|
Padding(
|
||||||
child: Image.network(
|
padding: const EdgeInsets.all(10),
|
||||||
selectedOrg!['logo'],
|
// padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 10),
|
||||||
width: 100,
|
|
||||||
height: 50,
|
child: selectedOrg?['logo'] != null
|
||||||
fit: BoxFit.contain,
|
? ClipRect(
|
||||||
errorBuilder: (context, error, stackTrace) {
|
child: Image.network(
|
||||||
return const CircleAvatar(
|
selectedOrg!['logo'],
|
||||||
radius: 20,
|
width: 100,
|
||||||
backgroundColor: Colors.redAccent,
|
height: 50,
|
||||||
child: Icon(Icons.error, size: 10),
|
fit: BoxFit.contain,
|
||||||
);
|
errorBuilder: (context, error, stackTrace) {
|
||||||
},
|
return const CircleAvatar(
|
||||||
),
|
radius: 20,
|
||||||
)
|
backgroundColor: Colors.redAccent,
|
||||||
: const CircleAvatar(
|
child: Icon(Icons.error, size: 10),
|
||||||
radius: 20,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
child: Icon(
|
|
||||||
Icons.add_a_photo,
|
|
||||||
size: 10,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
MouseRegion(
|
|
||||||
cursor: SystemMouseCursors.click,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
print("ONTAP Custom");
|
|
||||||
print("ONTAP Custom- $userDetails ");
|
|
||||||
context.go(
|
|
||||||
"/CreateUserDetails",
|
|
||||||
extra: {
|
|
||||||
"selectedUser": userDetails,
|
|
||||||
"isEditProfile": true,
|
|
||||||
"isViewMode": true
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Text(
|
|
||||||
userData?["name"] ?? "N/A",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontFamily: "Archivo",
|
|
||||||
// color: Color(0xFF12B24B),
|
|
||||||
color: layoutColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
)
|
||||||
),
|
: const CircleAvatar(
|
||||||
|
radius: 20,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
child: Icon(
|
||||||
|
Icons.add_a_photo,
|
||||||
|
size: 10,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.click,
|
||||||
|
onEnter: (_) {
|
||||||
|
setState(() {
|
||||||
|
_myTravelRequestColor =
|
||||||
|
Colors.blue; // Change color on hover
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onExit: (_) {
|
||||||
|
setState(() {
|
||||||
|
_myTravelRequestColor =
|
||||||
|
Color(0xFF475569); // Revert color when hover ends
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
context.go('/listPlan');
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
"My Travel Request",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
// color: Color(0xFF475569),
|
||||||
|
color: _myTravelRequestColor,
|
||||||
|
fontFamily: "Archivo"),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 25),
|
||||||
|
MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.click,
|
||||||
|
onEnter: (_) {
|
||||||
|
setState(() {
|
||||||
|
_myApprovalsColor = Colors.blue; // Change color on hover
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onExit: (_) {
|
||||||
|
setState(() {
|
||||||
|
_myApprovalsColor =
|
||||||
|
Color(0xFF475569); // Revert color when hover ends
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
context.go('/ApprovalList');
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
"My Approvals",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: _myApprovalsColor,
|
||||||
|
// color: Color(0xFF475569),
|
||||||
|
fontFamily: "Archivo"),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
Row(
|
Padding(
|
||||||
children: [
|
padding: EdgeInsets.symmetric(
|
||||||
MouseRegion(
|
horizontal: MediaQuery.of(context).size.width * 0.05),
|
||||||
cursor: SystemMouseCursors.click,
|
child: Column(
|
||||||
onEnter: (_) {
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
setState(() {
|
children: [
|
||||||
_myTravelRequestColor = Colors.blue; // Change color on hover
|
Text(
|
||||||
});
|
userData?["name"] ?? "N/A",
|
||||||
},
|
style: TextStyle(
|
||||||
onExit: (_) {
|
fontSize: 13,
|
||||||
setState(() {
|
fontWeight: FontWeight.w600,
|
||||||
_myTravelRequestColor =
|
fontFamily: "Archivo",
|
||||||
Color(0xFF475569); // Revert color when hover ends
|
// color: Color(0xFF12B24B),
|
||||||
});
|
color: layoutColor,
|
||||||
},
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
context.go('/listPlan');
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
"My Travel Request",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
// color: Color(0xFF475569),
|
|
||||||
color: _myTravelRequestColor,
|
|
||||||
fontFamily: "Archivo"),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 25),
|
|
||||||
MouseRegion(
|
|
||||||
cursor: SystemMouseCursors.click,
|
|
||||||
onEnter: (_) {
|
|
||||||
setState(() {
|
|
||||||
_myApprovalsColor = Colors.blue; // Change color on hover
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onExit: (_) {
|
|
||||||
setState(() {
|
|
||||||
_myApprovalsColor =
|
|
||||||
Color(0xFF475569); // Revert color when hover ends
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
context.go('/ApprovalList');
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
"My Approvals",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: _myApprovalsColor,
|
|
||||||
// color: Color(0xFF475569),
|
|
||||||
fontFamily: "Archivo"),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
if (userData?["role"] != "User")
|
|
||||||
Builder(
|
|
||||||
builder: (context) => PopupMenuButton<String>(
|
|
||||||
color: Colors.white,
|
|
||||||
icon: const Icon(Icons.settings, color: Colors.black87),
|
|
||||||
offset: const Offset(0, 50), // 👈 shift it 50 pixels down
|
|
||||||
onSelected: (String value) {
|
|
||||||
switch (value) {
|
|
||||||
case '/OrganizationSetup':
|
|
||||||
context.go('/OrganizationSetup');
|
|
||||||
break;
|
|
||||||
case '/listUser':
|
|
||||||
context.go('/listUser');
|
|
||||||
break;
|
|
||||||
case '/group':
|
|
||||||
context.go('/group');
|
|
||||||
break;
|
|
||||||
case '/PolicyList':
|
|
||||||
context.go('/PolicyList');
|
|
||||||
case '/CreateUserDetails':
|
|
||||||
context.go(
|
|
||||||
"/CreateUserDetails",
|
|
||||||
extra: {
|
|
||||||
"selectedUser": userDetails,
|
|
||||||
"isEditProfile": true,
|
|
||||||
"isViewMode": true
|
|
||||||
},
|
|
||||||
);
|
|
||||||
case '/logout':
|
|
||||||
context.go('/');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
itemBuilder: (BuildContext context) =>
|
|
||||||
menuItems.map(buildMenuItem).toList(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
SizedBox(
|
||||||
],
|
height: 1,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
// if (userData?["role"] != "User")
|
||||||
|
Builder(
|
||||||
|
builder: (context) => PopupMenuButton<String>(
|
||||||
|
color: Colors.white,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
// icon: const Icon(Icons.arrow_drop_down,
|
||||||
|
// size: 20, color: Colors.black87),
|
||||||
|
offset: const Offset(0, 50), // 👈 shift it 50 pixels down
|
||||||
|
onSelected: (String value) {
|
||||||
|
switch (value) {
|
||||||
|
case '/OrganizationSetup':
|
||||||
|
context.go('/OrganizationSetup');
|
||||||
|
break;
|
||||||
|
case '/listUser':
|
||||||
|
context.go('/listUser');
|
||||||
|
break;
|
||||||
|
case '/group':
|
||||||
|
context.go('/group');
|
||||||
|
break;
|
||||||
|
case '/PolicyList':
|
||||||
|
context.go('/PolicyList');
|
||||||
|
case '/CreateUserDetails':
|
||||||
|
context.go(
|
||||||
|
"/CreateUserDetails",
|
||||||
|
extra: {
|
||||||
|
"selectedUser": userDetails,
|
||||||
|
"isEditProfile": true,
|
||||||
|
"isViewMode": true
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case '/logout':
|
||||||
|
context.go('/');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// itemBuilder: (BuildContext context) =>
|
||||||
|
// menuItems.map(buildMenuItem).toList(),
|
||||||
|
|
||||||
|
itemBuilder: (BuildContext context) {
|
||||||
|
final isUser = userData?["role"] == "User";
|
||||||
|
final filteredItems = isUser
|
||||||
|
? menuItems
|
||||||
|
.where((item) =>
|
||||||
|
item['value'] == '/CreateUserDetails' ||
|
||||||
|
item['value'] == '/logout')
|
||||||
|
.toList()
|
||||||
|
: menuItems;
|
||||||
|
|
||||||
|
return filteredItems.map(buildMenuItem).toList();
|
||||||
|
},
|
||||||
|
child: MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.click,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
userData?["role"] ?? "Role",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w300,
|
||||||
|
fontFamily: "Archivo",
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
Icons.arrow_drop_down,
|
||||||
|
size: 20,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
@ -339,7 +373,11 @@ final List<Map<String, dynamic>> menuItems = [
|
|||||||
'icon': Icons.business,
|
'icon': Icons.business,
|
||||||
'label': 'Organization'
|
'label': 'Organization'
|
||||||
},
|
},
|
||||||
{'value': '/listUser', 'icon': Icons.manage_accounts, 'label': 'Users'},
|
{
|
||||||
|
'value': '/listUser',
|
||||||
|
'icon': Icons.manage_accounts,
|
||||||
|
'label': 'User Management'
|
||||||
|
},
|
||||||
{'value': '/group', 'icon': Icons.group, 'label': 'Group'},
|
{'value': '/group', 'icon': Icons.group, 'label': 'Group'},
|
||||||
{'value': '/PolicyList', 'icon': Icons.policy, 'label': 'Policy'},
|
{'value': '/PolicyList', 'icon': Icons.policy, 'label': 'Policy'},
|
||||||
{
|
{
|
||||||
@ -352,10 +390,25 @@ final List<Map<String, dynamic>> menuItems = [
|
|||||||
|
|
||||||
PopupMenuItem<String> buildMenuItem(Map<String, dynamic> item) {
|
PopupMenuItem<String> buildMenuItem(Map<String, dynamic> item) {
|
||||||
return PopupMenuItem<String>(
|
return PopupMenuItem<String>(
|
||||||
|
height: 40, // 👈 reduce PopupMenuItem height
|
||||||
value: item['value'],
|
value: item['value'],
|
||||||
child: ListTile(
|
padding:
|
||||||
leading: Icon(item['icon'], size: 18),
|
EdgeInsets.symmetric(horizontal: 12), // 👈 control left-right spacing
|
||||||
title: Text(item['label'], style: const TextStyle(fontSize: 13)),
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(item['icon'],
|
||||||
|
size: 18, color: Colors.black87), // 👈 smaller, cleaner icon
|
||||||
|
SizedBox(width: 10), // 👈 small space between icon and text
|
||||||
|
Text(
|
||||||
|
item['label'],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Roboto",
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -124,8 +124,8 @@ class _CustomDrawerState extends State<CustomDrawer> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget drawerContent = Container(
|
Widget drawerContent = Container(
|
||||||
color: Colors.white,
|
// color: Colors.white,
|
||||||
// color: Color(0xFFF3F3FA),
|
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.all(18),
|
margin: const EdgeInsets.all(18),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
@ -447,4 +447,41 @@ class ApiService {
|
|||||||
throw Exception('Failed to load organizations');
|
throw Exception('Failed to load organizations');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flight From - To
|
||||||
|
|
||||||
|
Future<List<dynamic>> fetchFlightsCountryList() async {
|
||||||
|
final String apiUrldata = '$apiUrl/api/getAirportCodeMaster';
|
||||||
|
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("Country - $data");
|
||||||
|
|
||||||
|
if (!data.containsKey('data') || data['data'] is! List) {
|
||||||
|
throw Exception(
|
||||||
|
"Invalid response format: 'data' field is missing or not a List");
|
||||||
|
}
|
||||||
|
|
||||||
|
return data['data'];
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Error parsing response: $e');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to load country list');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user