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';
|
||||
|
||||
class AccomodationScreen extends StatefulWidget {
|
||||
final List<Map<String, dynamic>> flightData;
|
||||
final Function(bool) onClose; // Callback function
|
||||
final Function(Map<String, dynamic>) onSaveAccomadation;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
@ -15,13 +16,17 @@ class AccomodationScreen extends StatefulWidget {
|
||||
{required this.onClose,
|
||||
required this.onSaveAccomadation,
|
||||
required this.selectedItem,
|
||||
required this.loginUser});
|
||||
required this.loginUser,
|
||||
required this.flightData});
|
||||
|
||||
@override
|
||||
_AccomodationScreenState createState() => _AccomodationScreenState();
|
||||
}
|
||||
|
||||
class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
final FocusNode _destinationFocusNode = FocusNode();
|
||||
@ -116,6 +121,30 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
_checkInTimeController.addListener(() => _clearError("checkin_time"));
|
||||
_checkOutController.addListener(() => _clearError("checkout_date"));
|
||||
_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
|
||||
@ -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) {
|
||||
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
|
||||
}
|
||||
|
||||
@ -185,35 +292,13 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Container(
|
||||
color: Color(0xFFF4F4FB),
|
||||
// color: Color(0xFFF4F4FB),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
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: const EdgeInsets.all(28.0),
|
||||
child: Center(
|
||||
@ -256,17 +341,26 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
: Column(
|
||||
children: _buildThirdRow(isDesktop),
|
||||
),
|
||||
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: _handleAction(isDesktop),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildFirstRow(isDesktop) {
|
||||
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(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -368,16 +462,41 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
DateTime now = DateTime.now();
|
||||
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,
|
||||
initialDate:
|
||||
_selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today)
|
||||
? _selectedCheckInDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
initialDate: initialDate,
|
||||
firstDate: initialDate,
|
||||
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) {
|
||||
setState(() {
|
||||
_selectedCheckInDate = pickedDate;
|
||||
@ -410,17 +529,57 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
DateTime? _selectedCheckOutDate;
|
||||
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 {
|
||||
DateTime now = DateTime.now();
|
||||
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,
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
initialDate: initialDate,
|
||||
firstDate: firstDate,
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
|
||||
@ -678,23 +837,36 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _commentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _commentsController,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
if (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;
|
||||
|
||||
return Container(
|
||||
color: Color(0xFFF4F4FB),
|
||||
// color: Colors.white,
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
widget.onClose(false);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
size: 18,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text("Bus Booking List",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF575A74))),
|
||||
SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
// Align(
|
||||
// alignment: Alignment.centerRight,
|
||||
// child: InkWell(
|
||||
// onTap: () {
|
||||
// widget.onClose(false);
|
||||
// },
|
||||
// child: Icon(
|
||||
// Icons.close,
|
||||
// size: 18,
|
||||
// color: Color(0xFF575A74),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Text("Bus Booking List",
|
||||
// style: TextStyle(
|
||||
// fontSize: 18,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Color(0xFF575A74))),
|
||||
// SizedBox(
|
||||
// height: 6,
|
||||
// ),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(28.0),
|
||||
child: Center(
|
||||
@ -263,10 +263,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||
|
||||
// Actions row remains a Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: _handleAction(isDesktop),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@ -607,26 +603,34 @@ class _BusScreenState extends State<BusScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _buscommentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _buscommentsController,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
if (isDesktop) Spacer(),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: _handleAction(isDesktop),
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend/services/apiService.dart';
|
||||
import 'package:intl/intl.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';
|
||||
|
||||
class FlightScreen extends StatefulWidget {
|
||||
final List<Map<String, dynamic>> flightData;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final String? loginUser;
|
||||
final Function(bool) onClose;
|
||||
@ -19,15 +21,23 @@ class FlightScreen extends StatefulWidget {
|
||||
required this.loginUser,
|
||||
required this.onClose,
|
||||
required this.onSaveFlight,
|
||||
required this.selectedItem});
|
||||
required this.selectedItem,
|
||||
required this.flightData});
|
||||
|
||||
@override
|
||||
_FlightScreenState createState() => _FlightScreenState();
|
||||
}
|
||||
|
||||
class _FlightScreenState extends State<FlightScreen> {
|
||||
ApiService apiService = ApiService();
|
||||
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 = {};
|
||||
|
||||
String? selectedTripType;
|
||||
@ -60,6 +70,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// _initializeRows();
|
||||
// List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
|
||||
// selectedTripType ??= purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
|
||||
@ -109,6 +120,56 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
textControllers["_time${i}Controller"]
|
||||
?.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() {
|
||||
@ -444,34 +505,15 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Container(
|
||||
color: Color(0xFFF4F4FB),
|
||||
// color: Color(0xFFF4F4FB),
|
||||
// color: Color(0xFFF9F9F9), // Slightly lighter than white
|
||||
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
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: const EdgeInsets.all(28.0),
|
||||
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> buildResponsiveRow(List<Widget> children) {
|
||||
return [
|
||||
@ -502,14 +537,14 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
}
|
||||
|
||||
List<List<Widget>> rowBuilders = [
|
||||
_builClassType(isDesktop, 1),
|
||||
// _builClassType(isDesktop, 1),
|
||||
_buildSecondRow(isDesktop, 1)
|
||||
];
|
||||
|
||||
List<List<Widget>> rowRoundBuilders = [
|
||||
_builClassType(isDesktop, 1),
|
||||
// _builClassType(isDesktop, 1),
|
||||
_buildSecondRow(isDesktop, 1),
|
||||
_builClassType(isDesktop, 2),
|
||||
// _builClassType(isDesktop, 2),
|
||||
_buildSecondRow(isDesktop, 2)
|
||||
];
|
||||
|
||||
@ -527,11 +562,11 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
|
||||
if (selectedTripType == "Multitrip")
|
||||
...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);
|
||||
|
||||
return [
|
||||
...buildResponsiveRow(firstRow), // Row 1
|
||||
// ...buildResponsiveRow(firstRow), // Row 1
|
||||
...buildResponsiveRow(secondRow), // Row 2
|
||||
];
|
||||
}).expand((row) => row),
|
||||
@ -546,13 +581,12 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
alignment: Alignment.centerRight,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blueAccent,
|
||||
backgroundColor: Colors.green,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
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: () {
|
||||
setState(() {
|
||||
@ -591,18 +625,14 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
},
|
||||
child: Text(
|
||||
"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)),
|
||||
// Actions row remains a Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: _handleAction(isDesktop),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@ -658,9 +688,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
// isFocused: _tripTypeFocused,
|
||||
isFocused: focusStates["_tripType1Focused"] ?? false,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
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<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
@ -910,96 +938,54 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
// Default selected value
|
||||
selectedClasses[index] ??=
|
||||
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;
|
||||
TimeOfDay? _selectedCheckOutTime;
|
||||
|
||||
Future<void> _selectCheckOutDate(BuildContext context) async {
|
||||
DateTime now = DateTime.now();
|
||||
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(
|
||||
context: context,
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
initialDate: initialDate,
|
||||
firstDate: firstDate,
|
||||
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 [
|
||||
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(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -1051,6 +1103,51 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
isFocused: focusStates["_from${index}Focused"] ?? false,
|
||||
// isFocused: focusStates["_from${fieldIndex}Focused"] ?? false,
|
||||
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(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
@ -1142,6 +1239,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: focusStates["_date${index}Focused"] ?? false,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
@ -1195,6 +1293,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: focusStates["_timeFocused"] ?? false,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.08 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
@ -1206,7 +1305,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
controller: textControllers["_time${index}Controller"],
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Select Time",
|
||||
labelText: "Time",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
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<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 [
|
||||
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(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -1247,27 +1441,37 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: focusStates["_comments1Focused"] ?? false,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
|
||||
child: TextField(
|
||||
focusNode: focusNodes["_comments1FocusNode"],
|
||||
// controller: _commentsController,
|
||||
controller: textControllers["_comments1Controller"],
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
// maxLines: 6,
|
||||
// keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
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;
|
||||
|
||||
class ForexScreen extends StatefulWidget {
|
||||
final List<Map<String, dynamic>> flightData;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(bool) onClose;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
@ -27,7 +28,8 @@ class ForexScreen extends StatefulWidget {
|
||||
required this.selectedItem,
|
||||
required this.apiCountryData,
|
||||
required this.onSaveForex,
|
||||
required this.loginUser});
|
||||
required this.loginUser,
|
||||
required this.flightData});
|
||||
|
||||
@override
|
||||
_ForexScreenState createState() => _ForexScreenState();
|
||||
@ -36,6 +38,9 @@ class ForexScreen extends StatefulWidget {
|
||||
class _ForexScreenState extends State<ForexScreen> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||
|
||||
Map<String, String?> selectedValues = {};
|
||||
bool isChecked = false; // State variable for checkbox
|
||||
|
||||
@ -106,6 +111,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
"deposit_on_cash": textControllers["_cash"]?.text,
|
||||
"delivery_location": textControllers["_deliveryLocation"]?.text,
|
||||
"comments": textControllers["_comments"]?.text,
|
||||
"total": selectedQuotedAmount,
|
||||
"created_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
|
||||
}
|
||||
|
||||
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() {
|
||||
print("Handle Save forexData $forexData");
|
||||
|
||||
@ -280,6 +315,30 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
textControllers["_forexEndDate"]?.addListener(_onFieldChanged);
|
||||
|
||||
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() {
|
||||
@ -468,34 +527,13 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Container(
|
||||
color: Color(0xFFF4F4FB),
|
||||
// color: Color(0xFFF4F4FB),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
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: const EdgeInsets.all(28.0),
|
||||
child: Center(
|
||||
@ -532,48 +570,43 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
|
||||
return [
|
||||
...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||
|
||||
SizedBox(
|
||||
height: 28,
|
||||
height: 5,
|
||||
),
|
||||
Text(
|
||||
"Forex Details",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF575A74)),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
"Forex Details",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
),
|
||||
// SizedBox(
|
||||
// height: 8,
|
||||
// ),
|
||||
Divider(
|
||||
thickness: 0.3,
|
||||
),
|
||||
SizedBox(
|
||||
height: 8,
|
||||
height: 3,
|
||||
),
|
||||
Divider(),
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
...buildResponsiveRow(_builClassType(isDesktop)),
|
||||
SizedBox(
|
||||
height: 8,
|
||||
height: 3,
|
||||
),
|
||||
Divider(
|
||||
thickness: 0.3,
|
||||
),
|
||||
Divider(),
|
||||
SizedBox(
|
||||
height: 28,
|
||||
height: 10,
|
||||
),
|
||||
...buildResponsiveRow(_buildSecondRow(isDesktop)),
|
||||
|
||||
...buildResponsiveRow(_buildCardDetailsRow(isDesktop)),
|
||||
|
||||
...buildResponsiveRow(_buildFprexCard(isDesktop)),
|
||||
|
||||
...buildResponsiveRow(_buildThirdRow(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 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,
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
initialDate: initialDate,
|
||||
firstDate: initialDate,
|
||||
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) {
|
||||
setState(() {
|
||||
_selectedCheckOutDate = pickedDate;
|
||||
@ -610,16 +670,41 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
DateTime now = DateTime.now();
|
||||
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,
|
||||
initialDate:
|
||||
_selectedEndDate != null && _selectedEndDate!.isAfter(today)
|
||||
? _selectedEndDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
initialDate: initialDate,
|
||||
firstDate: initialDate,
|
||||
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) {
|
||||
setState(() {
|
||||
_selectedEndDate = pickedDate;
|
||||
@ -657,7 +742,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Forex Start Date",
|
||||
"Start Date",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -736,7 +821,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Forex End Date",
|
||||
"End Date",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -898,7 +983,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
isDesktop: isDesktop,
|
||||
color: Colors.transparent,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
height: 30,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
@ -906,7 +991,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
// selectedDuration?.isNotEmpty == true ? selectedDuration! : "Duration",
|
||||
selectedDuration ?? "Duration",
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
// 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.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
height: 30,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Center(
|
||||
@ -960,7 +1045,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
selectedCurrency ?? "Currency",
|
||||
// selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency",
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
@ -993,7 +1078,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
isDesktop: isDesktop,
|
||||
color: Colors.transparent,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
height: 30,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
@ -1001,7 +1086,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
selectedPerdiemAmount ?? "Amount",
|
||||
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount",
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
// decoration: const InputDecoration(
|
||||
@ -1186,7 +1271,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
// focusNode: _toFocusNode,
|
||||
// controller: _toController,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
// decoration: const InputDecoration(
|
||||
@ -1358,7 +1443,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
selectedQuotedAmount ?? "0",
|
||||
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount",
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
@ -1524,26 +1609,38 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
isFocused:
|
||||
focusStates["_comments"] ?? false, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.330
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: focusNodes["_comments"],
|
||||
controller: textControllers["_comments"],
|
||||
maxLines: 3,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null,
|
||||
child: SizedBox(
|
||||
height: 35,
|
||||
child: TextField(
|
||||
focusNode: focusNodes["_comments"],
|
||||
controller: textControllers["_comments"],
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
if (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';
|
||||
|
||||
class InsuranceScreen extends StatefulWidget {
|
||||
final List<Map<String, dynamic>> flightData;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String, dynamic>) onSaveInsurance;
|
||||
@ -17,7 +18,8 @@ class InsuranceScreen extends StatefulWidget {
|
||||
required this.apiData,
|
||||
required this.onSaveInsurance,
|
||||
required this.selectedItem,
|
||||
required this.loginUser});
|
||||
required this.loginUser,
|
||||
required this.flightData});
|
||||
|
||||
@override
|
||||
_InsuranceScreenState createState() => _InsuranceScreenState();
|
||||
@ -28,6 +30,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
|
||||
Map<String, String?> selectedValues = {};
|
||||
|
||||
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||
|
||||
final FocusNode _tripTypeFocusNode = FocusNode();
|
||||
final FocusNode _hotelNameFocusNode = FocusNode();
|
||||
final FocusNode _fromFocusNode = FocusNode();
|
||||
@ -105,6 +110,58 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
selectedInsuranceType =
|
||||
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) {
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -175,34 +251,13 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Container(
|
||||
color: Color(0xFFF4F4FB),
|
||||
// color: Color(0xFFF4F4FB),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
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: const EdgeInsets.all(28.0),
|
||||
child: Center(
|
||||
@ -231,7 +286,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
];
|
||||
|
||||
return [
|
||||
...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||
// ...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||
|
||||
// Iterate over rowBuilders and wrap each in a responsive container
|
||||
...rowBuilders.expand((row) => buildResponsiveRow(row)),
|
||||
@ -239,10 +294,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||
|
||||
// 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<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;
|
||||
TimeOfDay? _selectedCheckOutTime;
|
||||
|
||||
@ -345,16 +423,41 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
DateTime now = DateTime.now();
|
||||
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,
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
initialDate: initialDate,
|
||||
firstDate: initialDate,
|
||||
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) {
|
||||
setState(() {
|
||||
_selectedCheckOutDate = pickedDate;
|
||||
@ -368,16 +471,43 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
DateTime now = DateTime.now();
|
||||
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,
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
initialDate: initialDate,
|
||||
firstDate: firstDate,
|
||||
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) {
|
||||
setState(() {
|
||||
_selectedCheckOutDate = pickedDate;
|
||||
@ -387,6 +517,57 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
}
|
||||
|
||||
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(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -401,9 +582,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _dateFocus,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
@ -464,9 +643,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _dateFocus,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
@ -548,23 +725,36 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _insuranceCommentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _insuranceCommentsController,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
if (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;
|
||||
|
||||
return Container(
|
||||
color: Color(0xFFF4F4FB),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
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: const EdgeInsets.all(28.0),
|
||||
child: Center(
|
||||
@ -204,10 +181,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||
|
||||
// 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))
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
else
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@ -324,18 +291,19 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _commentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _commentsController,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -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;
|
||||
|
||||
return Container(
|
||||
color: Color(0xFFF4F4FB),
|
||||
// color: Color(0xFFF4F4FB),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
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: const EdgeInsets.all(28.0),
|
||||
child: Center(
|
||||
@ -267,10 +246,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||
|
||||
// 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.66,
|
||||
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _taxiCommentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _taxiCommentsController,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
if (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;
|
||||
|
||||
return Container(
|
||||
color: Color(0xFFF4F4FB),
|
||||
// color: Color(0xFFF4F4FB),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
widget.onClose(false);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
size: 18,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text("Train Booking List",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF575A74))),
|
||||
SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
// Align(
|
||||
// alignment: Alignment.centerRight,
|
||||
// child: InkWell(
|
||||
// onTap: () {
|
||||
// widget.onClose(false);
|
||||
// },
|
||||
// child: Icon(
|
||||
// Icons.close,
|
||||
// size: 18,
|
||||
// color: Color(0xFF575A74),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Text("Train Booking List",
|
||||
// style: TextStyle(
|
||||
// fontSize: 18,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Color(0xFF575A74))),
|
||||
// SizedBox(
|
||||
// height: 6,
|
||||
// ),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(28.0),
|
||||
child: Center(
|
||||
@ -276,10 +276,6 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||
|
||||
// Actions row remains a Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: _handleAction(isDesktop),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@ -345,9 +341,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _trainNoFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
@ -368,80 +362,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
}
|
||||
|
||||
List<Widget> _builClassType(bool isDesktop) {
|
||||
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 [
|
||||
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),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
];
|
||||
return [];
|
||||
}
|
||||
|
||||
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 [
|
||||
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(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -595,6 +596,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _dateFocus,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
@ -647,6 +649,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _timeFocus,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.08 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
@ -698,25 +701,38 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _trainCommentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _trainCommentsController,
|
||||
// maxLines: 6,
|
||||
// keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (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';
|
||||
|
||||
class VisaScreen extends StatefulWidget {
|
||||
final List<Map<String, dynamic>> flightData;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final List<dynamic>? apiCountryData;
|
||||
|
||||
@ -21,7 +22,8 @@ class VisaScreen extends StatefulWidget {
|
||||
this.apiData,
|
||||
required this.selectedItem,
|
||||
required this.apiCountryData,
|
||||
required this.loginUser});
|
||||
required this.loginUser,
|
||||
required this.flightData});
|
||||
|
||||
@override
|
||||
_VisaScreenState createState() => _VisaScreenState();
|
||||
@ -30,6 +32,9 @@ class VisaScreen extends StatefulWidget {
|
||||
class _VisaScreenState extends State<VisaScreen> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||
|
||||
Map<String, String?> selectedValues = {};
|
||||
|
||||
List<dynamic> countryList = [];
|
||||
@ -105,6 +110,22 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
// selectedPurpose = widget.selectedItem!["selectedCountry"].toString();
|
||||
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) {
|
||||
@ -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
|
||||
void dispose() {
|
||||
_tripTypeFocusNode.dispose();
|
||||
@ -171,34 +221,12 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Container(
|
||||
color: Color(0xFFF4F4FB),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
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: const EdgeInsets.all(28.0),
|
||||
child: Center(
|
||||
@ -232,38 +260,11 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||
|
||||
// Actions row remains a Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: _handleAction(isDesktop),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildFirstRow(isDesktop) {
|
||||
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)
|
||||
Spacer()
|
||||
else
|
||||
@ -274,62 +275,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
}
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop) {
|
||||
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 [
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
return [];
|
||||
}
|
||||
|
||||
List<Widget> _buildSecondRow(bool isDesktop) {
|
||||
@ -380,16 +326,41 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
DateTime now = DateTime.now();
|
||||
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,
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
initialDate: initialDate,
|
||||
firstDate: initialDate,
|
||||
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) {
|
||||
setState(() {
|
||||
_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 [
|
||||
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(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -413,9 +465,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: DropdownSearch<String>(
|
||||
@ -490,9 +540,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _dateFocus,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
@ -554,23 +602,36 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _visaCommentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _visaCommentsController,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
if (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 bool isViewMode;
|
||||
|
||||
const AccomodationListWidget(
|
||||
{super.key,
|
||||
required this.accommodationList,
|
||||
required this.onOpen,
|
||||
required this.onDeleteAccommodation,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode});
|
||||
const AccomodationListWidget({
|
||||
super.key,
|
||||
required this.accommodationList,
|
||||
required this.onOpen,
|
||||
required this.onDeleteAccommodation,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -24,6 +25,7 @@ class AccomodationListWidget extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@ -31,28 +33,17 @@ class AccomodationListWidget extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Accomodation Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
// Text(
|
||||
// "Accomodation Booking List",
|
||||
// style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
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
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
@ -62,20 +53,73 @@ class AccomodationListWidget extends StatelessWidget {
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
// 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,
|
||||
),
|
||||
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),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
),
|
||||
// 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),
|
||||
|
||||
@ -31,6 +31,7 @@ class BusListWidget extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@ -38,28 +39,12 @@ class BusListWidget extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Bus Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
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
|
||||
child: GestureDetector(
|
||||
onTap: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
@ -69,19 +54,50 @@ class BusListWidget extends StatelessWidget {
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
// 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,
|
||||
),
|
||||
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("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(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@ -33,28 +34,16 @@ class FlightListWidget extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Flight Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
// Text(
|
||||
// "Flight Booking List",
|
||||
// style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
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
|
||||
child: GestureDetector(
|
||||
onTap: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
@ -64,16 +53,16 @@ class FlightListWidget extends StatelessWidget {
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
// 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,
|
||||
),
|
||||
Icons.add_circle_sharp,
|
||||
size: 30,
|
||||
color: Color(0xFF114D8B),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -30,6 +30,7 @@ class ForexListWidget extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@ -37,28 +38,13 @@ class ForexListWidget extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Forex List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
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
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
@ -68,20 +54,72 @@ class ForexListWidget extends StatelessWidget {
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
// 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,
|
||||
),
|
||||
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),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
),
|
||||
// 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),
|
||||
|
||||
@ -28,6 +28,7 @@ class InsuranceListWidget extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@ -35,28 +36,12 @@ class InsuranceListWidget extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Insurance Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
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
|
||||
child: GestureDetector(
|
||||
onTap: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
@ -66,19 +51,50 @@ class InsuranceListWidget extends StatelessWidget {
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
// 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,
|
||||
),
|
||||
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("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(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@ -31,42 +32,63 @@ class MiscellaneousListWidget extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Miscellaneous List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
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
|
||||
child: GestureDetector(
|
||||
onTap: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Miscellaneous", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text("Add New", style: TextStyle(fontSize: 13)),
|
||||
SizedBox(width: 8),
|
||||
Icon(Icons.add_circle_outline_rounded,
|
||||
size: 15, color: Colors.white),
|
||||
// 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),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
// 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(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@ -33,28 +34,13 @@ class TaxiListWidget extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Taxi Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
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
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
@ -64,19 +50,50 @@ class TaxiListWidget extends StatelessWidget {
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
// 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,
|
||||
),
|
||||
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("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(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@ -33,50 +34,87 @@ class TrainListWidget extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Train Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
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),
|
||||
child: MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: 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_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(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@ -35,43 +36,51 @@ class VisaListWidget extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Visa List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
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
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Visa", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text("Add New", style: TextStyle(fontSize: 13)),
|
||||
SizedBox(width: 8),
|
||||
Icon(Icons.add_circle_outline_rounded,
|
||||
size: 15, color: Colors.white),
|
||||
// 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),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
// 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": [],
|
||||
};
|
||||
|
||||
// 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
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -101,7 +88,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
setState(() {
|
||||
selectedAllServices = result;
|
||||
});
|
||||
print("Fetched services: $selectedAllServices");
|
||||
print("Fetched services Order: $selectedAllServices");
|
||||
} catch (e) {
|
||||
print('Error fetching role list: $e');
|
||||
}
|
||||
@ -161,8 +148,12 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
selectedIds.contains(service['service_id'].toString()))
|
||||
.toList();
|
||||
|
||||
// setState(() {
|
||||
// ServicesChoosed = [...originalFiltered, ...additionalServices];
|
||||
// });
|
||||
setState(() {
|
||||
ServicesChoosed = [...originalFiltered, ...additionalServices];
|
||||
ServicesChoosed = [...originalFiltered, ...additionalServices]
|
||||
..sort((a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0));
|
||||
});
|
||||
|
||||
print(
|
||||
@ -177,9 +168,14 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
.toList();
|
||||
|
||||
setState(() {
|
||||
ServicesChoosed = filtered;
|
||||
ServicesChoosed = filtered
|
||||
..sort((a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0));
|
||||
});
|
||||
|
||||
// setState(() {
|
||||
// ServicesChoosed = filtered;
|
||||
// });
|
||||
|
||||
print("Filtered Selected Services Chooesed: $ServicesChoosed");
|
||||
}
|
||||
}
|
||||
@ -287,21 +283,6 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
Widget selectedWidget;
|
||||
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) {
|
||||
setState(() {
|
||||
if (!itineraryData.containsKey(type)) {
|
||||
@ -488,12 +469,13 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
break;
|
||||
case "Accomodation":
|
||||
selectedListWidget = AccomodationListWidget(
|
||||
accommodationList: itineraryData["Accomodation"]!,
|
||||
onOpen: handleEdit,
|
||||
onAddNew: handlecreateNewPlan,
|
||||
isViewMode: widget.isViewMode,
|
||||
onDeleteAccommodation: (data) =>
|
||||
handleItinerarydelete("Accomodation", data));
|
||||
accommodationList: itineraryData["Accomodation"]!,
|
||||
onOpen: handleEdit,
|
||||
onAddNew: handlecreateNewPlan,
|
||||
isViewMode: widget.isViewMode,
|
||||
onDeleteAccommodation: (data) =>
|
||||
handleItinerarydelete("Accomodation", data),
|
||||
);
|
||||
break;
|
||||
case "Miscellaneous":
|
||||
selectedListWidget = MiscellaneousListWidget(
|
||||
@ -545,11 +527,13 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
break;
|
||||
case "Insurance":
|
||||
selectedWidget = InsuranceScreen(
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
loginUser: widget.loginUser,
|
||||
onSaveInsurance: (data) => handleItineraryUpdate("Insurance", data),
|
||||
selectedItem: selectedItem);
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
loginUser: widget.loginUser,
|
||||
onSaveInsurance: (data) => handleItineraryUpdate("Insurance", data),
|
||||
selectedItem: selectedItem,
|
||||
flightData: itineraryData["Flight"]!,
|
||||
);
|
||||
break;
|
||||
|
||||
case "Visa":
|
||||
@ -560,6 +544,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
loginUser: widget.loginUser,
|
||||
onSaveVisa: (data) => handleItineraryUpdate("Visa", data),
|
||||
selectedItem: selectedItem,
|
||||
flightData: itineraryData["Flight"]!,
|
||||
);
|
||||
break;
|
||||
case "Miscellaneous":
|
||||
@ -580,66 +565,169 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
onSaveAccomadation: (data) =>
|
||||
handleItineraryUpdate("Accomodation", data),
|
||||
selectedItem: selectedItem,
|
||||
flightData: itineraryData["Flight"]!,
|
||||
);
|
||||
break;
|
||||
case "Forex":
|
||||
selectedWidget = ForexScreen(
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
loginUser: widget.loginUser,
|
||||
apiCountryData: widget.apiCountryData,
|
||||
onSaveForex: (data) => handleItineraryUpdate("Forex", data),
|
||||
selectedItem: selectedItem);
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
loginUser: widget.loginUser,
|
||||
apiCountryData: widget.apiCountryData,
|
||||
onSaveForex: (data) => handleItineraryUpdate("Forex", data),
|
||||
selectedItem: selectedItem,
|
||||
flightData: itineraryData["Flight"]!,
|
||||
);
|
||||
break;
|
||||
case "Flight":
|
||||
default:
|
||||
selectedWidget = FlightScreen(
|
||||
onClose: handleClose,
|
||||
loginUser: widget.loginUser,
|
||||
onSaveFlight: (data) => handleItineraryUpdate("Flight", data),
|
||||
apiData: widget.apiData,
|
||||
selectedItem: selectedItem);
|
||||
onClose: handleClose,
|
||||
loginUser: widget.loginUser,
|
||||
onSaveFlight: (data) => handleItineraryUpdate("Flight", data),
|
||||
apiData: widget.apiData,
|
||||
selectedItem: selectedItem,
|
||||
flightData: itineraryData["Flight"]!,
|
||||
);
|
||||
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) {
|
||||
bool isMobile = sizingInfo.isMobile;
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
// Second container (yellow box)
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: 40), // Push it down to make room for the tab bar
|
||||
padding: EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(color: Color(0xFFF4F4FB), width: 2)),
|
||||
borderRadius: BorderRadius.circular(1),
|
||||
// color: Color(0xFFF4F4FB),
|
||||
color: Colors.white, // Card background
|
||||
// color: Colors.yellow.shade50, // Card background
|
||||
// color: Color(0xFFF9F9F9), // Slightly lighter than white
|
||||
// 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: isMobile
|
||||
? Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
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,
|
||||
child: Row(
|
||||
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),
|
||||
child: Row(
|
||||
child: Column(
|
||||
children: [
|
||||
iconUrl.isNotEmpty
|
||||
? Image.network(
|
||||
@ -713,7 +801,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Icon(
|
||||
fallbackIcon,
|
||||
size: 18,
|
||||
size: 25,
|
||||
color: isOptionSelected
|
||||
? Color(0xFF114D8B)
|
||||
: Color(0xFF475569),
|
||||
@ -722,24 +810,30 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
)
|
||||
: Icon(
|
||||
fallbackIcon,
|
||||
size: 18,
|
||||
size: 25,
|
||||
color: isOptionSelected
|
||||
? Color(0xFF114D8B)
|
||||
: Color(0xFF475569),
|
||||
),
|
||||
SizedBox(width: 2),
|
||||
Text(
|
||||
name,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: isOptionSelected ? Color(0xFF114D8B) : Color(0xFF475569),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight:
|
||||
isOptionSelected ? FontWeight.bold : FontWeight.w500,
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
name,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: isOptionSelected
|
||||
? 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> {
|
||||
final ApiService apiService = ApiService();
|
||||
|
||||
int currentPage = 0;
|
||||
int itemsPerPage = 8;
|
||||
|
||||
late Future<List<Plan>> futurePlans;
|
||||
String? userId;
|
||||
String? orgId;
|
||||
@ -285,7 +288,7 @@ class _ListPlansState extends State<ListPlans> {
|
||||
margin: isDesktop
|
||||
? EdgeInsets.all(10.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
|
||||
? MediaQuery.of(context).size.height * 0.98
|
||||
: MediaQuery.of(context).size.height,
|
||||
@ -305,7 +308,7 @@ class _ListPlansState extends State<ListPlans> {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(1.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
// padding: const EdgeInsets.all(10.0),
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@ -356,6 +359,7 @@ class _ListPlansState extends State<ListPlans> {
|
||||
),
|
||||
),
|
||||
// SizedBox(width: 16),
|
||||
|
||||
Spacer(),
|
||||
// ElevatedButton(
|
||||
// style: ElevatedButton.styleFrom(
|
||||
@ -486,6 +490,11 @@ class _ListPlansState extends State<ListPlans> {
|
||||
plans.sort((a, b) =>
|
||||
int.parse(b.planId).compareTo(int.parse(a.planId)));
|
||||
|
||||
List<Plan> paginatedPlans = plans
|
||||
.skip(currentPage * itemsPerPage)
|
||||
.take(itemsPerPage)
|
||||
.toList();
|
||||
|
||||
Widget table = LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
double minWidth = isDesktop ? constraints.maxWidth : 1300;
|
||||
@ -508,13 +517,13 @@ class _ListPlansState extends State<ListPlans> {
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('UserName',
|
||||
label: Text('Trip Name',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Trip Title',
|
||||
label: Text('UserName',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
@ -544,21 +553,13 @@ class _ListPlansState extends State<ListPlans> {
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
],
|
||||
rows: plans.map((plan) {
|
||||
rows: paginatedPlans.map((plan) {
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(plan.planId,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Text(
|
||||
plan.userName.isNotEmpty
|
||||
? plan.userName
|
||||
: plan.travellerName,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Text(plan.tripTitle,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
@ -566,6 +567,14 @@ class _ListPlansState extends State<ListPlans> {
|
||||
),
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis)),
|
||||
DataCell(Text(
|
||||
plan.userName.isNotEmpty
|
||||
? plan.userName
|
||||
: plan.travellerName,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Text(plan.tripType,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
@ -663,15 +672,66 @@ class _ListPlansState extends State<ListPlans> {
|
||||
);
|
||||
|
||||
return Expanded(
|
||||
child: isDesktop
|
||||
? table
|
||||
: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: table,
|
||||
child: Column(
|
||||
children: [
|
||||
isDesktop
|
||||
? table
|
||||
: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
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
|
||||
final Map<String, TextEditingController> controllers = {};
|
||||
bool isViewMode = false;
|
||||
bool isEditProfile = true;
|
||||
bool isEditProfile = false;
|
||||
// late final List<dynamic>? apiCountryData ;
|
||||
|
||||
late List<dynamic>? apiCountryData;
|
||||
@ -2932,11 +2932,11 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
onPressed: () {
|
||||
isEditProfile ? context.go('/listPlan') : context.go('/listUser');
|
||||
},
|
||||
child: isEditProfile ? Text("Back") : Text("Cancel")),
|
||||
child: isViewMode ? Text("Back") : Text("Cancel")),
|
||||
SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
if (!isEditProfile)
|
||||
if (!isViewMode)
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
|
||||
33
lib/app.dart
33
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/rendering.dart';
|
||||
import 'package:frontend/routes/custom_router.dart';
|
||||
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
class MyApp extends StatefulWidget {
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp.router(
|
||||
@ -13,4 +40,4 @@ class MyApp extends StatelessWidget {
|
||||
debugShowCheckedModeBanner: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'app.dart';
|
||||
|
||||
void main() {
|
||||
|
||||
@ -138,6 +138,7 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
||||
return AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.white,
|
||||
|
||||
// elevation: 3,
|
||||
automaticallyImplyLeading: false,
|
||||
// leading: showBackButton
|
||||
@ -147,176 +148,209 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
||||
// )
|
||||
// : null,
|
||||
titleSpacing: 0,
|
||||
title: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: selectedOrg?['logo'] != null
|
||||
? ClipRect(
|
||||
child: Image.network(
|
||||
selectedOrg!['logo'],
|
||||
width: 100,
|
||||
height: 50,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return const CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundColor: Colors.redAccent,
|
||||
child: Icon(Icons.error, size: 10),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: const CircleAvatar(
|
||||
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
|
||||
},
|
||||
title: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: MediaQuery.of(context).size.width * 0.05),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 10),
|
||||
|
||||
child: selectedOrg?['logo'] != null
|
||||
? ClipRect(
|
||||
child: Image.network(
|
||||
selectedOrg!['logo'],
|
||||
width: 100,
|
||||
height: 50,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return const CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundColor: Colors.redAccent,
|
||||
child: Icon(Icons.error, size: 10),
|
||||
);
|
||||
},
|
||||
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: [
|
||||
Row(
|
||||
children: [
|
||||
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"),
|
||||
)),
|
||||
),
|
||||
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(),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: MediaQuery.of(context).size.width * 0.05),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
userData?["name"] ?? "N/A",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: "Archivo",
|
||||
// color: Color(0xFF12B24B),
|
||||
color: layoutColor,
|
||||
),
|
||||
),
|
||||
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(
|
||||
@ -339,7 +373,11 @@ final List<Map<String, dynamic>> menuItems = [
|
||||
'icon': Icons.business,
|
||||
'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': '/PolicyList', 'icon': Icons.policy, 'label': 'Policy'},
|
||||
{
|
||||
@ -352,10 +390,25 @@ final List<Map<String, dynamic>> menuItems = [
|
||||
|
||||
PopupMenuItem<String> buildMenuItem(Map<String, dynamic> item) {
|
||||
return PopupMenuItem<String>(
|
||||
height: 40, // 👈 reduce PopupMenuItem height
|
||||
value: item['value'],
|
||||
child: ListTile(
|
||||
leading: Icon(item['icon'], size: 18),
|
||||
title: Text(item['label'], style: const TextStyle(fontSize: 13)),
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 12), // 👈 control left-right spacing
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
Widget drawerContent = Container(
|
||||
color: Colors.white,
|
||||
// color: Color(0xFFF3F3FA),
|
||||
// color: Colors.white,
|
||||
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(18),
|
||||
child: Column(
|
||||
|
||||
@ -447,4 +447,41 @@ class ApiService {
|
||||
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