ui changes

This commit is contained in:
venbaittech 2025-04-25 10:58:14 +05:30
parent d59aaf90bc
commit 36d3b365be
33 changed files with 4081 additions and 1476 deletions

View File

@ -67,14 +67,28 @@ Future<String?> showRejectDialog(
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
const Text("Please enter remarks to reject the plan."),
const Text("Please enter reason for rejection."),
const SizedBox(height: 10),
TextField(
maxLines: 3,
onChanged: (value) => remarks = value,
decoration: const InputDecoration(
hintText: "Remarks...",
border: OutlineInputBorder(),
hintStyle: TextStyle(
fontSize: 12, // 👈 Set your desired font size here
color: Colors.grey,
fontFamily:
"Archivo", // optional if you want consistent font
),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueGrey, width: 0.5),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueGrey, width: 0.5),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1),
),
),
),
],

View File

@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:frontend/config/apiUrl.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -275,13 +276,23 @@ class _ApprovalListState extends State<ApprovalList> {
return Container(
decoration: BoxDecoration(
// color: Colors.amber,
color: bodyColor,
// color: bodyColor,
color: Color(0xFFE1F5FE),
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: buildTableLayout(isDesktop),
);
}
Widget buildTableLayout(isDesktop) {
String _formatDate(String rawDate) {
try {
final dateTime = DateTime.parse(rawDate);
return DateFormat('dd MMM yy : hh a').format(dateTime);
} catch (e) {
return rawDate; // fallback if parsing fails
}
}
return Container(
margin: isDesktop
? EdgeInsets.all(10.0)
@ -414,7 +425,9 @@ class _ApprovalListState extends State<ApprovalList> {
double minWidth = isDesktop ? constraints.maxWidth : 1300;
return ConstrainedBox(
constraints: BoxConstraints(minWidth: minWidth),
constraints: BoxConstraints(
minWidth: minWidth,
),
child: DataTable(
dividerThickness: 0.5,
columnSpacing: isDesktop ? 24.0 : 16.0,
@ -494,7 +507,10 @@ class _ApprovalListState extends State<ApprovalList> {
fontSize: 13,
fontFamily: "Archivo",
))),
DataCell(Text(plan.createdOn,
DataCell(Text(
// plan.createdOn,
_formatDate(plan.createdOn),
style: TextStyle(
fontSize: 13,
fontFamily: "Archivo",

View File

@ -191,6 +191,10 @@ class _LoginWidgetState extends State<LoginWidget> {
_buildLabel("Email Address"),
TextFormField(
controller: _emailController,
style: TextStyle(
fontFamily: "Archivo",
fontWeight: FontWeight.w600,
fontSize: 11),
decoration: _inputDecoration("Enter your email address").copyWith(
prefixIcon: Icon(
Icons.email_outlined,
@ -207,6 +211,10 @@ class _LoginWidgetState extends State<LoginWidget> {
_buildLabel("Password"),
TextFormField(
controller: _passwordController,
style: TextStyle(
fontFamily: "Archivo",
fontWeight: FontWeight.w600,
fontSize: 11),
obscureText: _obscureText,
decoration: _inputDecoration("Enter your password").copyWith(
prefixIcon: Icon(

View File

@ -78,14 +78,13 @@ class _groupState extends State<Group> {
WidgetsBinding.instance.addPostFrameCallback((_) {
loadAllServices();
for (var field in dataHeader) {
controllers[field] = TextEditingController();
}
updateData();
loadInitialData();
});
for (var field in dataHeader) {
controllers[field] = TextEditingController();
}
updateData();
}
void loadInitialData() async {
@ -342,13 +341,15 @@ class _groupState extends State<Group> {
// margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0),
decoration: BoxDecoration(
// color: Colors.amber,
color: bodyColor,
// color: bodyColor,
color: Color(0xFFE1F5FE),
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: Column(
children: [
Expanded(
child: Container(
color: bodyColor,
// color: bodyColor,
color: Color(0xFFE1F5FE),
child: buildOrganizationLayout(isDesktop),
),
),
@ -386,6 +387,7 @@ class _groupState extends State<Group> {
)
: null,
color: Colors.white,
// color: Color(0xFFF7F7FB),
// color: Colors.amber,

View File

@ -148,7 +148,8 @@ class _GroupListState extends State<GroupList> {
return Container(
decoration: BoxDecoration(
// color: Colors.amber,
color: bodyColor,
// color: bodyColor,
color: Color(0xFFE1F5FE),
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: buildGroupData(isDesktop),
);

View File

@ -725,7 +725,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),

View File

@ -657,7 +657,7 @@ class _BusScreenState extends State<BusScreen> {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),

View File

@ -628,6 +628,7 @@ class _FlightScreenState extends State<FlightScreen> {
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
isExpanded: true,
// focusNode: _tripTypeFocusNode, // Assign the correct focus node
focusNode: focusNodes["_tripType1FocusNode"],
value: selectedTripType,
@ -1292,7 +1293,7 @@ class _FlightScreenState extends State<FlightScreen> {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),

View File

@ -201,7 +201,7 @@ class _ForexScreenState extends State<ForexScreen> {
"country_code",
"deposit_on_card",
"deposit_on_cash",
"card_number"
// "card_number"
];
// If have_card is "1", then delivery_location is required
@ -932,7 +932,7 @@ class _ForexScreenState extends State<ForexScreen> {
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Currency *",
@ -946,19 +946,24 @@ class _ForexScreenState extends State<ForexScreen> {
isFocused: focusStates["_currencyFocused"] ?? false,
isDesktop: isDesktop,
color: Colors.transparent,
width: isDesktop
? MediaQuery.of(context).size.width * 0.330
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
// "cur",
// "${selectedCurrency}",
selectedCurrency ?? "Currency",
// selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency",
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
child: Center(
child: Text(
// "cur",
// "${selectedCurrency}",
selectedCurrency ?? "Currency",
// selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency",
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
),
),
),
@ -1012,46 +1017,13 @@ class _ForexScreenState extends State<ForexScreen> {
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
// if (isDesktop)
// Spacer()
// else
// SizedBox(
// height: 8,
// ),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Quoted Amount",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_perdiemAmount"] ?? false,
isDesktop: isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
// "amo",
selectedQuotedAmount ?? "0",
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount",
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
),
),
),
],
),
// if (isDesktop)Spacer() else SizedBox(height: 8,),
];
}
@ -1268,7 +1240,7 @@ class _ForexScreenState extends State<ForexScreen> {
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_cash"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
@ -1321,7 +1293,7 @@ class _ForexScreenState extends State<ForexScreen> {
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_card"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
@ -1355,6 +1327,47 @@ class _ForexScreenState extends State<ForexScreen> {
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Total Amount",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_perdiemAmount"] ?? false,
isDesktop: isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
// "amo",
selectedQuotedAmount ?? "0",
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount",
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
@ -1406,19 +1419,20 @@ class _ForexScreenState extends State<ForexScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Card Number*",
"Card Number",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_cardNumber"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
enabled: !isChecked,
focusNode: focusNodes["_cardNumber"],
controller: textControllers["_cardNumber"],
style: const TextStyle(fontSize: 12),
@ -1463,7 +1477,7 @@ class _ForexScreenState extends State<ForexScreen> {
false, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.464
? MediaQuery.of(context).size.width * 0.330
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: focusNodes["_deliveryLocation"],
@ -1511,7 +1525,7 @@ class _ForexScreenState extends State<ForexScreen> {
focusStates["_comments"] ?? false, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.464
? MediaQuery.of(context).size.width * 0.330
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: focusNodes["_comments"],
@ -1547,6 +1561,10 @@ class _ForexScreenState extends State<ForexScreen> {
onChanged: (bool? value) {
setState(() {
isChecked = value!;
if (isChecked) {
textControllers["_cardNumber"]
?.clear(); // Clear the value when isChecked is true
}
});
},
),
@ -1589,7 +1607,7 @@ class _ForexScreenState extends State<ForexScreen> {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),

View File

@ -595,7 +595,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),

View File

@ -379,7 +379,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),

View File

@ -785,7 +785,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),

View File

@ -748,7 +748,7 @@ class _TrainScreenState extends State<TrainScreen> {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),

View File

@ -601,7 +601,7 @@ class _VisaScreenState extends State<VisaScreen> {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
backgroundColor: Color(0xFF114D8B), // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class AccomodationListWidget extends StatelessWidget {
final List<Map<String, dynamic>> accommodationList;
@ -18,149 +19,386 @@ class AccomodationListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
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),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header with title and button
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
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
? null
: () {
print("New data");
onAddNew("Accomodation", 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,
),
],
),
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: [
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,
),
],
),
),
),
],
),
const SizedBox(height: 16),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(
color: Colors.black12), // Only horizontal lines
),
columns: const [
DataColumn(label: Text('#')),
DataColumn(label: Text('Destination City')),
DataColumn(label: Text('Hotel Name')),
DataColumn(label: Text('CheckIn Date')),
DataColumn(label: Text('CheckOut Date')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
],
),
),
],
const SizedBox(height: 16),
// Scroll behavior based on device
_buildData(context, isDesktop)
],
),
),
);
}
List<DataRow> _buildDataRows() {
Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList =
accommodationList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) {
final Map<String, dynamic> item = entry.value;
String formatDate(String dateString) {
try {
DateTime date = DateTime.parse(dateString);
return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25
} catch (e) {
return "Invalid Date";
}
}
return DataRow(cells: [
DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
DataCell(Text(item["destination_city"]!)),
DataCell(Text(item["hotel_name"]!)),
DataCell(Text(item["checkin_date"]!)),
DataCell(Text(item["checkout_date"]!)),
DataCell(Row(
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Accomodation"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteAccommodation(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
IconButton(
icon: Icon(Icons.keyboard_arrow_down_outlined,
size: 28, color: Color(0xFF475569)),
onPressed: () {
// Expand logic
},
),
],
))
String formatTime(String timeString) {
try {
final parts = timeString.split(':');
final now = DateTime.now();
final dateTime = DateTime(
now.year,
now.month,
now.day,
int.parse(parts[0]),
int.parse(parts[1]),
);
return DateFormat.jm().format(dateTime); // e.g., 12:16 PM or 12 AM
} catch (e) {
return "Invalid Time";
}
}
// Row(
// children: [
// IconButton(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
// onPressed: () {
// // View action
// },
// ),
// IconButton(
// icon: Icon(Icons.edit, color: Colors.green),
// onPressed: () {
// onOpen(true, item, "Accomodation");
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteAccommodation(item);
// },
// ),
// ],
// )),
//
]);
}).toList();
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
return Container(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
// color: Colors.orange.shade50,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 3,
offset: Offset(0, 1),
),
],
border: Border(
top: BorderSide(
color: Colors.white70, // Change color to match your theme
width: 2,
),
),
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
item["hotel_name"]!,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
fontFamily: "Archivo"),
),
Spacer(),
GestureDetector(
onTap: () => onOpen(true, item, "Accomodation"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteAccommodation(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
],
),
Divider(
color: Colors.blueGrey.shade50,
),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
" DestinationCity",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Check (in) ",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Check (out) ",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Comments",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
],
)
: SizedBox.shrink(),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(item["destination_city"],
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text(
"${formatDate(item["checkin_date"]!)} ${formatTime(item["checkin_time"]!)}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text(
"${formatDate(item["checkout_date"]!)} ${formatTime(item["checkout_time"]!)}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: _buildComments(
"Comments:", item["comments"] ?? "N/A"),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildRow("City:", item["destination_city"]),
SizedBox(width: 10),
SizedBox(width: 10),
_buildDateTimeRow(
"Check-in:",
formatDate(item["checkin_date"]!),
item["checkin_time"]!,
),
SizedBox(width: 10),
_buildRow(
"Date(out):", formatDate(item["checkout_date"]!)),
SizedBox(width: 10),
_buildRow("Comments:", item["comments"] ?? "N/A"),
],
)
],
),
),
);
},
);
}
Widget _buildComments(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 20,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
],
);
}
Widget _buildRow(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 10,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
SizedBox(width: 8),
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(fontSize: 12),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(fontSize: 12),
),
),
],
);
}
Widget _buildDateTimeRow(String title, String date, String time) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
SizedBox(width: 8),
Expanded(
child: Text(
"$date, $time",
style: TextStyle(fontSize: 12),
),
),
],
);
}
}

View File

@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -25,161 +26,359 @@ class BusListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header with title and button
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
? null
: () {
print("New data");
onAddNew("Bus", 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,
),
],
),
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: [
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,
),
],
),
),
),
],
),
const SizedBox(height: 16),
Container(
// color: Colors.blueGrey,
width: double.infinity,
child: Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
// child: Expanded(
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(
color: Colors.black12), // Only horizontal lines
),
columns: const [
// DataColumn(label: Text('#')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Date')),
DataColumn(label: Text('Time')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
// )
),
],
),
),
],
const SizedBox(height: 16),
// Scroll behavior based on device
_buildData(context, isDesktop)
],
),
),
);
}
List<DataRow> _buildDataRows() {
Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList =
busList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) {
final Map<String, dynamic> item = entry.value;
String formatDate(String dateString) {
try {
DateTime date = DateTime.parse(dateString);
return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25
} catch (e) {
return "Invalid Date";
}
}
return DataRow(cells: [
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
DataCell(Text(item["from"]!)),
DataCell(Text(item["to"]!)),
DataCell(Text(item["date"]!)),
DataCell(Text(item["time"]!)),
DataCell(Row(
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Bus"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteBus(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
IconButton(
icon: Icon(Icons.keyboard_arrow_down_outlined,
size: 28, color: Color(0xFF475569)),
onPressed: () {
// Expand logic
},
),
],
)
String formatTime(String timeString) {
try {
final parts = timeString.split(':');
final now = DateTime.now();
final dateTime = DateTime(
now.year,
now.month,
now.day,
int.parse(parts[0]),
int.parse(parts[1]),
);
return DateFormat.jm().format(dateTime); // e.g., 12:16 PM or 12 AM
} catch (e) {
return "Invalid Time";
}
}
// Row(
// children: [
// IconButton(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
// onPressed: () {
// // View action
// },
// ),
// IconButton(
// icon: Icon(Icons.edit, color: Colors.green),
// onPressed: () {
// onOpen(true, item, "Bus");
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteBus(item);
// },
// ),
// ],
// )
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
return Container(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
// color: Colors.orange.shade50,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 3,
offset: Offset(0, 1),
),
],
border: Border(
top: BorderSide(
color: Colors.white70, // Change color to match your theme
width: 2,
),
),
]);
}).toList();
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Bus"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteBus(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
],
),
Divider(
color: Colors.blueGrey.shade50,
),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 3,
child: Text(
" Planned Trips",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 3,
child: Text(
" Date",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 3,
child: Text(
" Comments",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
],
)
: SizedBox.shrink(),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 3,
child: Text("${item["from"]} - ${item["to"]} ",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 3,
child: Text(
"${formatDate(item["date"]!)} ${formatTime(item["time"]!)}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 3,
child: _buildComments(
"Comments:", item["comments"] ?? "N/A"),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildRow("From:", item["from"]),
SizedBox(width: 10),
_buildRow("To:", item["to"]!),
SizedBox(width: 10),
_buildDateTimeRow(
"Date:",
formatDate(item["date"]!),
formatTime(item["time"]!),
),
_buildRow("Comments:", item["comments"] ?? "N/A"),
],
)
],
),
),
);
},
);
}
Widget _buildComments(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 30,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
],
);
}
Widget _buildRow(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 10,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
SizedBox(width: 8),
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(fontSize: 12),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(fontSize: 12),
),
),
],
);
}
Widget _buildDateTimeRow(String title, String date, String time) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
SizedBox(width: 8),
Expanded(
child: Text(
"$date, $time",
style: TextStyle(fontSize: 12),
),
),
],
);
}
}

View File

@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class FlightListWidget extends StatelessWidget {
final List<Map<String, dynamic>> flightList;
final Function(bool, Map<String, dynamic>, String) onOpen;
final Function(Map<String, dynamic>) onDeleteFlight;
final Map<String, dynamic>? apiData;
final Function(String, bool) onAddNew;
final bool isViewMode;
@ -14,161 +16,324 @@ class FlightListWidget extends StatelessWidget {
required this.onOpen,
required this.onDeleteFlight,
required this.onAddNew,
required this.apiData,
required this.isViewMode});
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
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),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header with title and button
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
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
? null
: () {
print("New data");
onAddNew("Flight", 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,
),
],
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: isViewMode
? null
: () {
print("New data");
onAddNew("Flight", 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,
),
],
),
),
],
),
const SizedBox(height: 16),
// Scroll behavior based on device
_buildData(context, isDesktop)
],
),
),
);
}
Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList =
flightList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList");
List<dynamic> visatypeList = apiData?['flight_class'] ?? [];
String getRequestForClass(String? specialRequestKey) {
if (specialRequestKey == null) return "N/A";
return visatypeList
.firstWhere(
(element) =>
element["dropdown_key"].toString() == specialRequestKey,
orElse: () => {"dropdown_value": "N/A"},
)["dropdown_value"]
.toString();
}
String formatDate(String dateString) {
try {
DateTime date = DateTime.parse(dateString);
return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25
} catch (e) {
return "Invalid Date";
}
}
String formatTime(String timeString) {
try {
final parts = timeString.split(':');
final now = DateTime.now();
final dateTime = DateTime(
now.year,
now.month,
now.day,
int.parse(parts[0]),
int.parse(parts[1]),
);
return DateFormat.jm().format(dateTime); // e.g., 12:16 PM or 12 AM
} catch (e) {
return "Invalid Time";
}
}
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
return Container(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
// color: Colors.orange.shade50,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 3,
offset: Offset(0, 1),
),
],
),
const SizedBox(height: 16),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
// width: 1000,
width: MediaQuery.of(context).size.width,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(
color: Colors.black12), // Only horizontal lines
),
columns: const [
// DataColumn(label: Text('#')),
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
border: Border(
top: BorderSide(
color: Colors.white70, // Change color to match your theme
width: 2,
),
),
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
item["trip_type"]?.toString() ?? "N/A",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
fontFamily: "Archivo"),
),
Spacer(),
GestureDetector(
onTap: () => onOpen(true, item, "Flight"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteFlight(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
],
),
Divider(
color: Colors.blueGrey.shade50,
),
SizedBox(height: 4),
Row(
children: [
Expanded(
flex: 2,
child: Text(
"Class",
style: TextStyle(fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
"Planned Trips",
style: TextStyle(fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
"Date",
style: TextStyle(fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
"Time",
style: TextStyle(fontSize: 11, fontFamily: "Archivo"),
)),
],
),
SizedBox(height: 4),
// Trip Rows
if (isDesktop)
if (item["trips"] != null && item["trips"].isNotEmpty)
...item["trips"].map<Widget>((trip) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
children: [
Expanded(
flex: 2,
child: Text(
getRequestForClass(trip["class"].toString()) ??
"N/A",
style: TextStyle(
fontSize: 12, fontWeight: FontWeight.bold),
),
),
Expanded(
flex: 2,
child: Text(
"${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
Expanded(
flex: 2,
child: Text(
formatDate(trip["date"] ?? ""),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
fontFamily: "Archivo"),
),
),
Expanded(
flex: 2,
child: Text(formatTime(trip["time"] ?? ""),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
fontFamily: "Archivo")),
),
],
),
);
}).toList(),
if (!isDesktop &&
item["trips"] != null &&
item["trips"].isNotEmpty)
...item["trips"].map<Widget>((trip) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(6),
color: Colors.grey.shade50,
),
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildKeyValueRow(
"Class",
getRequestForClass(trip["class"].toString()) ??
"N/A"),
_buildKeyValueRow("From - To",
"${trip["from_place"] ?? "N/A"} - ${trip["to_place"] ?? "N/A"}"),
_buildKeyValueRow(
"Date", formatDate(trip["date"] ?? "")),
_buildKeyValueRow(
"Time", formatTime(trip["time"] ?? "")),
],
),
),
);
}).toList(),
],
),
),
);
},
);
}
Widget _buildKeyValueRow(String key, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 80, // adjust width as needed
child: Text(
"$key:",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
),
),
Expanded(
child: Text(
value,
style: TextStyle(fontSize: 12),
),
),
],
),
);
}
List<DataRow> _buildDataRows() {
List<Map<String, dynamic>> filteredList =
flightList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) {
Map<String, dynamic> item = entry.value;
print("Trip Type: ${item["trip_type"]}");
return DataRow(cells: [
// DataCell(Text(item["indx"]?.toString() ?? "N/A")),
DataCell(Text(item["trip_type"]?.toString() ?? "N/A")),
DataCell(Text(item["trips"].isNotEmpty
? item["trips"][0]["from_place"]?.toString() ?? "N/A"
: "N/A")),
DataCell(Text(item["trips"].isNotEmpty
? item["trips"][0]["to_place"]?.toString() ?? "N/A"
: "N/A")),
DataCell(
Row(
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Flight"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteFlight(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
IconButton(
icon: Icon(Icons.keyboard_arrow_down_outlined,
size: 28, color: Color(0xFF475569)),
onPressed: () {
// Expand logic
},
),
],
),
//
// Row(
// children: [
// IconButton(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
// onPressed: () {
// // View action
// },
// ),
// IconButton(
// icon: Icon(Icons.edit, color: Colors.green),
// onPressed: () {
// onOpen(true, item, "Flight");
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteFlight(item);
// },
// ),
// ],
// )
//
),
]);
}).toList();
}
}

View File

@ -1,12 +1,14 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class ForexListWidget extends StatelessWidget {
final List<Map<String, dynamic>> forexList;
final Function(bool, Map<String, dynamic>, String) onOpen;
final Function(Map<String, dynamic>) onDeleteForex;
final List<dynamic>? apiCountryData;
final Map<String, dynamic>? apiData;
final Function(String, bool) onAddNew;
final bool isViewMode;
@ -18,107 +20,100 @@ class ForexListWidget extends StatelessWidget {
required this.onDeleteForex,
required this.apiCountryData,
required this.onAddNew,
required this.apiData,
required this.isViewMode});
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header with title and button
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
? null
: () {
print("New data");
onAddNew("Forex", 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,
),
],
),
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: [
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,
),
],
),
),
),
],
),
const SizedBox(height: 16),
Container(
// color: Colors.blueGrey,
width: double.infinity,
child: Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(
color: Colors.black12), // Only horizontal lines
),
columns: const [
// DataColumn(label: Text('#')),
DataColumn(label: Text('Forex Start Date')),
DataColumn(label: Text('Forex End Date')),
DataColumn(label: Text('Country')),
DataColumn(label: Text('Perdiem Amount')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
//
),
],
),
),
],
const SizedBox(height: 16),
// Scroll behavior based on device
_buildData(context, isDesktop)
],
),
),
);
}
List<DataRow> _buildDataRows() {
List<dynamic> countryList = apiCountryData ?? [];
Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList =
forexList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList");
List<dynamic> visatypeList = apiData?['visa_type_of_visa'] ?? [];
List<dynamic> countryList = apiCountryData ?? [];
String getRequestForVisa(String? specialRequestKey) {
if (specialRequestKey == null) return "N/A";
return visatypeList
.firstWhere(
(element) =>
element["dropdown_key"].toString() == specialRequestKey,
orElse: () => {"dropdown_value": "N/A"},
)["dropdown_value"]
.toString();
}
String getRequestForCountry(String? countryCode) {
if (countryCode == null) return "N/A";
@ -130,64 +125,296 @@ class ForexListWidget extends StatelessWidget {
.toString();
}
return filteredList.asMap().entries.map((entry) {
Map<String, dynamic> item = entry.value;
String formatDate(String dateString) {
try {
DateTime date = DateTime.parse(dateString);
return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25
} catch (e) {
return "Invalid Date";
}
}
return DataRow(cells: [
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
DataCell(Text(item["start_date"] ?? "N/A")),
DataCell(Text(item["end_date"] ?? "N/A")),
DataCell(Text(getRequestForCountry(item["country_code"]!.toString()))),
// DataCell(Text(item["country_code"] ?? "N/A")),
DataCell(Text(item["perdiem_amount"] ?? "N/A")),
DataCell(Row(
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Forex"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteForex(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
IconButton(
icon: Icon(Icons.keyboard_arrow_down_outlined,
size: 28, color: Color(0xFF475569)),
onPressed: () {
// Expand logic
},
),
],
)
// Row(
// children: [
// IconButton(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
// onPressed: () {
// // View action
// },
// ),
// IconButton(
// icon: Icon(Icons.edit, color: Colors.green),
// onPressed: () {
// onOpen(true, item, "Forex");
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteForex(item);
// },
// ),
// ],
// )
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
return Container(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
// color: Colors.orange.shade50,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 3,
offset: Offset(0, 1),
),
],
border: Border(
top: BorderSide(
color: Colors.white70, // Change color to match your theme
width: 2,
),
),
]);
}).toList();
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
getRequestForCountry(item["country_code"]!.toString()),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
Spacer(),
GestureDetector(
onTap: () => onOpen(true, item, "Forex"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteForex(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
],
),
Divider(
color: Colors.blueGrey.shade50,
),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
" Currency",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Duration",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Time Period ",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
// Expanded(
// flex: 2,
// child: Text(
// " Total ",
// style: TextStyle(
// fontSize: 11, fontFamily: "Archivo"),
// )),
Expanded(
flex: 2,
child: Text(
" Comments",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
],
)
: SizedBox.shrink(),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
item["currency"],
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text(
item["duration"],
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text(
" ${formatDate(item["start_date"]!)} ${formatDate(item["end_date"]!)}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
SizedBox(width: 10),
// Expanded(
// flex: 2,
// child: Text(
// " ",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// fontFamily: "Archivo"),
// ),
// ),
// SizedBox(width: 10),
Expanded(
flex: 2,
child: _buildComments(
"Comments:", item["comments"] ?? "N/A"),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildRow(
"Currency:",
item["currency"],
),
// SizedBox(height: 6),
_buildRow(
"Duration:",
item["duration"],
),
// SizedBox(height: 6),
_buildRow("StartDate:", item["start_date"]!),
_buildRow("EndDate:", item["end_date"]!),
// SizedBox(height: 6),
_buildRow("Comments:", item["comments"] ?? "N/A"),
],
),
],
),
),
);
},
);
}
Widget _buildComments(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 30,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
],
);
}
Widget _buildRow(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 10,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
SizedBox(width: 8),
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(fontSize: 12),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(fontSize: 12),
),
),
],
);
}
}

View File

@ -1,6 +1,7 @@
import 'dart:js_interop';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class InsuranceListWidget extends StatelessWidget {
final List<Map<String, dynamic>> insuranceList;
@ -22,12 +23,15 @@ class InsuranceListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header with title and button
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -79,35 +83,290 @@ class InsuranceListWidget extends StatelessWidget {
],
),
const SizedBox(height: 16),
Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(
color: Colors.black12), // Only horizontal lines
),
columns: const [
// DataColumn(label: Text('#')),
DataColumn(label: Text('Insurance Type')),
DataColumn(label: Text('Start Date')),
DataColumn(label: Text('End Date')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
// Scroll behavior based on device
_buildData(context, isDesktop)
],
),
),
);
}
Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList =
insuranceList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList");
List<dynamic> insurancetypeList =
apiData?['insurance_type_of_insurance'] ?? [];
String getRequestForInsuranceType(String? specialRequestKey) {
if (specialRequestKey == null) return "N/A";
return insurancetypeList
.firstWhere(
(element) =>
element["dropdown_key"].toString() == specialRequestKey,
orElse: () => {"dropdown_value": "N/A"},
)["dropdown_value"]
.toString();
}
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
String getRequestValue(String? specialRequestKey) {
if (specialRequestKey == null) return "N/A";
return (apiData?['miscellaneous_special_request'] ?? [])
.firstWhere(
(element) =>
element["dropdown_key"].toString() == specialRequestKey,
orElse: () => {"dropdown_value": "N/A"},
)["dropdown_value"]
.toString();
}
String formatDate(String dateString) {
try {
DateTime date = DateTime.parse(dateString);
return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25
} catch (e) {
return "Invalid Date";
}
}
return Container(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
// color: Colors.orange.shade50,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 3,
offset: Offset(0, 1),
),
],
border: Border(
top: BorderSide(
color: Colors.white70, // Change color to match your theme
width: 2,
),
),
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Insurance"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteInsurance(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
],
),
SizedBox(height: 4),
Divider(
color: Colors.blueGrey.shade50,
),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
" InsuranceType",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Time Period ",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Comments",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
],
)
: SizedBox.shrink(),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
getRequestForInsuranceType(
item["type_of_insurance"]!.toString()),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text(
"${formatDate(item["start_date"]!)} ${formatDate(item["end_date"]!)}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: _buildComments(
"Comments:", item["comments"] ?? "N/A"),
),
],
)
: Column(
children: [
_buildRow(
"InsuranceType:",
getRequestForInsuranceType(
item["type_of_insurance"]!.toString())),
SizedBox(width: 10),
_buildRow(
"StartDate:", formatDate(item["start_date"]!)),
SizedBox(width: 10),
_buildRow("EndDate:", formatDate(item["end_date"])),
SizedBox(width: 10),
_buildRow("Comments:", item["comments"] ?? "N/A"),
],
),
],
),
),
);
},
);
}
Widget _buildComments(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 20,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
],
);
}
Widget _buildRow(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
"Special Request:": 30,
"Comments:": 70,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 12),
),
SizedBox(width: 8),
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle: TextStyle(
color: Colors.black, fontSize: 12), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(
value.substring(0, limit) + "...",
style: TextStyle(fontSize: 12),
overflow: TextOverflow.ellipsis,
),
)
: Text(
value,
style: TextStyle(fontSize: 12),
),
),
],
);
}
List<DataRow> _buildDataRows() {
List<Map<String, dynamic>> filteredList =
insuranceList.where((item) => item["is_active"] == "1").toList();

View File

@ -19,18 +19,21 @@ class MiscellaneousListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header with title and button
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Miscellaneous Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
"Miscellaneous List",
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
MouseRegion(
cursor: isViewMode
@ -52,23 +55,15 @@ class MiscellaneousListWidget extends StatelessWidget {
onPressed: isViewMode
? null
: () {
print("New data");
onAddNew("Miscellaneous", true);
},
child: Row(
mainAxisSize:
MainAxisSize.min, // Ensures content fits nicely
mainAxisSize: MainAxisSize.min,
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,
),
Text("Add New", style: TextStyle(fontSize: 13)),
SizedBox(width: 8),
Icon(Icons.add_circle_outline_rounded,
size: 15, color: Colors.white),
],
),
),
@ -76,118 +71,305 @@ class MiscellaneousListWidget extends StatelessWidget {
],
),
const SizedBox(height: 16),
Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(
color: Colors.black12), // Only horizontal lines
),
columns: const [
// DataColumn(label: Text('#')),
DataColumn(label: Text('Special Request')),
DataColumn(label: Text('Comments')),
// DataColumn(label: Text('Created On')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
// Scroll behavior based on device
_buildData(context, isDesktop)
],
),
),
);
}
List<DataRow> _buildDataRows() {
print("miscellaneousList - $miscellaneousList");
List<dynamic> purposeList = apiData?['miscellaneous_special_request'] ?? [];
print("purposeList - $purposeList");
String getRequestValue(String? specialRequestKey) {
if (specialRequestKey == null) return "N/A";
return purposeList
.firstWhere(
(element) =>
element["dropdown_key"].toString() == specialRequestKey,
orElse: () => {"dropdown_value": "N/A"},
)["dropdown_value"]
.toString();
}
Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList =
miscellaneousList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) {
int index = entry.key + 1; // To start index from 1
Map<String, dynamic> item = entry.value;
print(item);
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
return DataRow(cells: [
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
// DataCell(Text(specialRequestValue)),
DataCell(Text(getRequestValue(item["special_request"]?.toString()))),
DataCell(Text(item["comments"] ?? "N/A")),
// DataCell(Text(item["created_on"] ?? "N/A")),
DataCell(Row(
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Miscellaneous"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onOpen(true, item, "Miscellaneous"),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
IconButton(
icon: Icon(Icons.keyboard_arrow_down_outlined,
size: 28, color: Color(0xFF475569)),
onPressed: () {
// Expand logic
},
),
],
)
// Row(
// children: [
// IconButton(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
// onPressed: () {
// // View action
// },
// ),
// IconButton(
// icon: Icon(Icons.edit, color: Colors.green),
// onPressed: () {
// onOpen(true, item, "Miscellaneous");
// // Edit action
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteMiscellaneous(item);
// // Delete action
// },
// ),
// ],
// )
String getRequestValue(String? specialRequestKey) {
if (specialRequestKey == null) return "N/A";
return (apiData?['miscellaneous_special_request'] ?? [])
.firstWhere(
(element) =>
element["dropdown_key"].toString() == specialRequestKey,
orElse: () => {"dropdown_value": "N/A"},
)["dropdown_value"]
.toString();
}
return Container(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
// color: Colors.yellow.shade50,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 3,
offset: Offset(0, 1),
),
],
border: Border(
top: BorderSide(
color: Colors.white70, // Change color to match your theme
width: 2,
),
),
]);
}).toList();
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Miscellaneous"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onOpen(true, item, "Miscellaneous"),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
],
),
Divider(
color: Colors.blueGrey.shade50,
),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
"Special Request",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 3,
child: Text(
" Comments",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
],
)
: SizedBox.shrink(),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
getRequestValue(
item["special_request"]?.toString()),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
Expanded(
flex: 3,
child: Text(item["comments"],
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
],
)
: Column(
children: [
_buildRow(
"Special Request:",
getRequestValue(
item["special_request"]?.toString())),
SizedBox(width: 10),
_buildRow("Comments:", item["comments"] ?? "N/A"),
],
),
],
),
),
);
},
);
}
Widget _buildComments(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 40,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
],
);
}
Widget _buildRow(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
"Special Request:": 30,
"Comments:": 70,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 12),
),
SizedBox(width: 8),
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle: TextStyle(
color: Colors.black, fontSize: 12), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(
value.substring(0, limit) + "...",
overflow: TextOverflow.ellipsis,
),
)
: Text(value),
),
],
);
}
//
// Widget _buildData(BuildContext context) {
// return Container(
// width: MediaQuery.of(context).size.width,
// child: DataTable(
// border: TableBorder(
// bottom: BorderSide(color: Colors.black12),
// horizontalInside: BorderSide(color: Colors.black12),
// ),
// columns: const [
// DataColumn(label: Text('Special Request')),
// DataColumn(label: Text('Comments')),
// DataColumn(label: Text('Actions')),
// ],
// rows: _buildDataRows(),
// ),
// );
// }
//
// List<DataRow> _buildDataRows() {
// List<dynamic> purposeList = apiData?['miscellaneous_special_request'] ?? [];
//
// String getRequestValue(String? specialRequestKey) {
// if (specialRequestKey == null) return "N/A";
// return purposeList
// .firstWhere(
// (element) =>
// element["dropdown_key"].toString() == specialRequestKey,
// orElse: () => {"dropdown_value": "N/A"},
// )["dropdown_value"]
// .toString();
// }
//
// List<Map<String, dynamic>> filteredList =
// miscellaneousList.where((item) => item["is_active"] == "1").toList();
//
// return filteredList.asMap().entries.map((entry) {
// Map<String, dynamic> item = entry.value;
//
// return DataRow(cells: [
// DataCell(Text(getRequestValue(item["special_request"]?.toString()))),
// DataCell(Text(item["comments"] ?? "N/A")),
// DataCell(Row(
// children: [
// GestureDetector(
// onTap: () => onOpen(true, item, "Miscellaneous"),
// child: Image.asset('assets/images/IconsImg/edit.png',
// width: 20, height: 15),
// ),
// SizedBox(width: 10),
// GestureDetector(
// onTap: () => onOpen(true, item, "Miscellaneous"),
// child: Image.asset('assets/images/IconsImg/delete.png',
// width: 20, height: 15),
// ),
// IconButton(
// icon: Icon(Icons.keyboard_arrow_down_outlined,
// size: 28, color: Color(0xFF475569)),
// onPressed: () {
// // Expand logic (optional)
// },
// ),
// ],
// )),
// ]);
// }).toList();
// }
}

View File

@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class TaxiListWidget extends StatelessWidget {
final List<Map<String, dynamic>> taxiList;
final Function(bool, Map<String, dynamic>, String) onOpen;
final Function(Map<String, dynamic>) onDeleteTaxi;
final Map<String, dynamic>? apiData;
final Function(String, bool) onAddNew;
final bool isViewMode;
@ -11,6 +13,7 @@ class TaxiListWidget extends StatelessWidget {
const TaxiListWidget(
{super.key,
required this.taxiList,
required this.apiData,
required this.onOpen,
required this.onDeleteTaxi,
required this.onAddNew,
@ -18,153 +21,411 @@ class TaxiListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header with title and button
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
? null
: () {
print("New data");
onAddNew("Taxi", 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,
),
],
),
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: [
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,
),
],
),
),
),
],
),
const SizedBox(height: 16),
Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(
color: Colors.black12), // Only horizontal lines
),
columns: const [
// DataColumn(label: Text('#')),
DataColumn(label: Text('Destination')),
DataColumn(label: Text('Location Of Pickup')),
DataColumn(label: Text('Date')),
DataColumn(label: Text('Taxi Required For')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
],
),
),
],
const SizedBox(height: 16),
// Scroll behavior based on device
_buildData(context, isDesktop)
],
),
),
);
}
List<DataRow> _buildDataRows() {
Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList =
taxiList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) {
final Map<String, dynamic> item = entry.value;
List<dynamic> taxiClassList = apiData?['taxt_car_type'] ?? [];
List<dynamic> taxirequiredFor = apiData?['taxi_car_required_for'] ?? [];
return DataRow(cells: [
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
DataCell(Text(item["destination_city"]!)),
DataCell(Text(item["location_of_pickup"]!)),
DataCell(Text(item["date"]!)),
DataCell(Text(item["car_required_for"]!)),
DataCell(Row(
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Taxi"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteTaxi(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
IconButton(
icon: Icon(Icons.keyboard_arrow_down_outlined,
size: 28, color: Color(0xFF475569)),
onPressed: () {
// Expand logic
},
),
],
)
String getRequestForTaxiClass(String? specialRequestKey) {
if (specialRequestKey == null) return "N/A";
// Row(
// children: [
// IconButton(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
// onPressed: () {
// // View action
// },
// ),
// IconButton(
// icon: Icon(Icons.edit, color: Colors.green),
// onPressed: () {
// onOpen(true, item, "Taxi");
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteTaxi(item);
// },
// ),
// ],
// )
//
return taxiClassList
.firstWhere(
(element) =>
element["dropdown_key"].toString() == specialRequestKey,
orElse: () => {"dropdown_value": "N/A"},
)["dropdown_value"]
.toString();
}
String getRequestFortaxirequiredFor(String? specialRequestKey) {
if (specialRequestKey == null) return "N/A";
return taxirequiredFor
.firstWhere(
(element) =>
element["dropdown_key"].toString() == specialRequestKey,
orElse: () => {"dropdown_value": "N/A"},
)["dropdown_value"]
.toString();
}
String formatDate(String dateString) {
try {
DateTime date = DateTime.parse(dateString);
return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25
} catch (e) {
return "Invalid Date";
}
}
String formatTime(String timeString) {
try {
final parts = timeString.split(':');
final now = DateTime.now();
final dateTime = DateTime(
now.year,
now.month,
now.day,
int.parse(parts[0]),
int.parse(parts[1]),
);
return DateFormat.jm().format(dateTime); // e.g., 12:16 PM or 12 AM
} catch (e) {
return "Invalid Time";
}
}
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
return Container(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
// color: Colors.orange.shade50,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 3,
offset: Offset(0, 1),
),
],
border: Border(
top: BorderSide(
color: Colors.white70, // Change color to match your theme
width: 2,
),
),
]);
}).toList();
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
"${getRequestForTaxiClass(item["car_type"]!.toString())} For ${item["no_of_passengers"]!} Passernger",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
fontFamily: "Archivo"),
),
Spacer(),
GestureDetector(
onTap: () => onOpen(true, item, "Taxi"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => (item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
],
),
Divider(
color: Colors.blueGrey.shade50,
),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
"Class",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Planned Trips",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Date",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
"Comments",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
],
)
: SizedBox.shrink(),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text("${item["destination_city"]}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text("${item["location_of_pickup"]}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text(
"${formatDate(item["date"]!)} ${formatTime(item["time"]!)}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: _buildComments(
" Comments:", item["comments"] ?? "N/A"),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildRow("City:", item["destination_city"]),
SizedBox(width: 10),
_buildRow("Pickup:", item["location_of_pickup"]!),
SizedBox(width: 10),
_buildDateTimeRow(
"Date:",
formatDate(item["date"]!),
formatTime(item["time"]!),
),
_buildRow("TaxiFor:", item["car_required_for"]!),
_buildRow("Comments:", item["comments"] ?? "N/A"),
],
)
],
),
),
);
},
);
}
Widget _buildComments(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 10,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
],
);
}
Widget _buildRow(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 10,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
SizedBox(width: 8),
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(fontSize: 12),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(fontSize: 12),
),
),
],
);
}
Widget _buildDateTimeRow(String title, String date, String time) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
SizedBox(width: 8),
Expanded(
child: Text(
"$date, $time",
style: TextStyle(fontSize: 12),
),
),
],
);
}
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class TrainListWidget extends StatelessWidget {
final List<Map<String, dynamic>> trainList;
@ -6,6 +7,7 @@ class TrainListWidget extends StatelessWidget {
final Function(Map<String, dynamic>) onDeleteTrain;
final Function(String, bool) onAddNew;
final bool isViewMode;
final Map<String, dynamic>? apiData;
const TrainListWidget({
super.key,
@ -14,16 +16,20 @@ class TrainListWidget extends StatelessWidget {
required this.onDeleteTrain,
required this.onAddNew,
required this.isViewMode,
required this.apiData,
});
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header with title and button
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -75,100 +81,340 @@ class TrainListWidget extends StatelessWidget {
],
),
const SizedBox(height: 16),
Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(
color: Colors.black12), // Only horizontal lines
),
columns: const [
// DataColumn(label: Text('#')),
DataColumn(label: Text('Train Number')),
// DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
// DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
// Scroll behavior based on device
_buildData(context, isDesktop)
],
),
),
);
}
List<DataRow> _buildDataRows() {
Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList =
trainList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList");
// print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) {
final Map<String, dynamic> item = entry.value;
List<dynamic> trainClassList = apiData?['train_class'] ?? [];
return DataRow(cells: [
// DataCell(Text(item["indx"]?.toString() ?? "N/A")),
String getRequestForTrainClass(String? specialRequestKey) {
if (specialRequestKey == null) return "N/A";
DataCell(Text(item["train_no"]!)),
// DataCell(Text(item["class"]!)),
DataCell(Text(item["from_station"]!)),
// DataCell(Text(item["to_station"]!)),
DataCell(Row(
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Train"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
return trainClassList
.firstWhere(
(element) =>
element["dropdown_key"].toString() == specialRequestKey,
orElse: () => {"dropdown_value": "N/A"},
)["dropdown_value"]
.toString();
}
String formatDate(String dateString) {
try {
DateTime date = DateTime.parse(dateString);
return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25
} catch (e) {
return "Invalid Date";
}
}
String formatTime(String timeString) {
try {
final parts = timeString.split(':');
final now = DateTime.now();
final dateTime = DateTime(
now.year,
now.month,
now.day,
int.parse(parts[0]),
int.parse(parts[1]),
);
return DateFormat.jm().format(dateTime); // e.g., 12:16 PM or 12 AM
} catch (e) {
return "Invalid Time";
}
}
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
return Container(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
// color: Colors.orange.shade50,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 3,
offset: Offset(0, 1),
),
],
border: Border(
top: BorderSide(
color: Colors.white70, // Change color to match your theme
width: 2,
),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteTrain(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
item["train_no"]?.toString() ?? "N/A",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
fontFamily: "Archivo"),
),
Spacer(),
GestureDetector(
onTap: () => onOpen(true, item, "Train"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteTrain(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
],
),
Divider(
color: Colors.blueGrey.shade50,
),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
" Class",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Planned Trips",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Date",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Comments",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
],
)
: SizedBox.shrink(),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
"${getRequestForTrainClass(item["class"]!.toString())} ",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text(
"${item["from_station"]!} ${(item["to_station"])}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"))),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text(
"${formatDate(item["date"]!)} ${formatTime(item["time"]!)}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo")),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: _buildComments(
"Comments:", item["comments"] ?? "N/A"),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildRow(
"Class:",
getRequestForTrainClass(
item["class"]!.toString())),
_buildRow("From:", item["from_station"]!),
_buildRow("To:", item["to_station"]!),
_buildDateTimeRow(
"Date:",
formatDate(item["date"]!),
formatTime(item["time"]!),
),
_buildRow("Comments:", item["comments"] ?? "N/A"),
],
)
],
),
IconButton(
icon: Icon(Icons.keyboard_arrow_down_outlined,
size: 28, color: Color(0xFF475569)),
onPressed: () {
// Expand logic
},
),
],
)
),
);
},
);
}
// Row(
// children: [
// IconButton(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
// onPressed: () {
// // View action
// },
// ),
// IconButton(
// icon: Icon(Icons.edit, color: Colors.green),
// onPressed: () {
// onOpen(true, item, "Train");
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteTrain(item);
// },
// ),
// ],
// )
//
Widget _buildComments(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 30,
// Add more keys and limits if needed
};
),
]);
}).toList();
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
],
);
}
Widget _buildRow(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 10,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
SizedBox(width: 8),
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(fontSize: 12),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(fontSize: 12),
),
),
],
);
}
Widget _buildDateTimeRow(String title, String date, String time) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
SizedBox(width: 8),
Expanded(
child: Text(
"$date, $time",
style: TextStyle(fontSize: 12),
),
),
],
);
}
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class VisaListWidget extends StatelessWidget {
final List<Map<String, dynamic>> visaList;
@ -22,18 +23,21 @@ class VisaListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header with title and button
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Visa Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
MouseRegion(
cursor: isViewMode
@ -59,19 +63,12 @@ class VisaListWidget extends StatelessWidget {
onAddNew("Visa", true);
},
child: Row(
mainAxisSize:
MainAxisSize.min, // Ensures content fits nicely
mainAxisSize: MainAxisSize.min,
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,
),
Text("Add New", style: TextStyle(fontSize: 13)),
SizedBox(width: 8),
Icon(Icons.add_circle_outline_rounded,
size: 15, color: Colors.white),
],
),
),
@ -79,36 +76,102 @@ class VisaListWidget extends StatelessWidget {
],
),
const SizedBox(height: 16),
Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(
color: Colors.black12), // Only horizontal lines
),
columns: const [
// DataColumn(label: Text('#')),
DataColumn(label: Text('Type of Visa')),
DataColumn(label: Text('Country')),
DataColumn(label: Text('Start Date')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
// Scroll behavior based on device
_buildData(context, isDesktop)
],
),
),
);
}
// Widget build(BuildContext context) {
// return Padding(
// padding: const EdgeInsets.all(16.0),
// child: Container(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.stretch,
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// "Visa 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
// ? null
// : () {
// print("New data");
// onAddNew("Visa", 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,
// ),
// ],
// ),
// ),
// ),
// ],
// ),
// const SizedBox(height: 16),
// Center(
// child: SingleChildScrollView(
// scrollDirection: Axis.horizontal,
// child: SizedBox(
// width: MediaQuery.of(context).size.width,
// child: DataTable(
// border: TableBorder(
// bottom: BorderSide(color: Colors.black12),
// horizontalInside: BorderSide(
// color: Colors.black12), // Only horizontal lines
// ),
// columns: const [
// // DataColumn(label: Text('#')),
// DataColumn(label: Text('Type of Visa')),
// DataColumn(label: Text('Country')),
// DataColumn(label: Text('Start Date')),
// DataColumn(label: Text('Actions')),
// ],
// rows: _buildDataRows(),
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// );
// }
List<DataRow> _buildDataRows() {
Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList =
visaList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList");
@ -139,66 +202,362 @@ class VisaListWidget extends StatelessWidget {
.toString();
}
return filteredList.asMap().entries.map((entry) {
int index = entry.key + 1; // To start index from 1
Map<String, dynamic> item = entry.value;
print(item);
return DataRow(cells: [
// DataCell(Text(item["indx"]?.toString() ?? "N/A")),
// DataCell(Text(item["type_of_visa"]!)),
DataCell(Text(getRequestForVisa(item["type_of_visa"]!.toString()))),
DataCell(Text(getRequestForCountry(item["country_code"]!.toString()))),
// DataCell(Text(item["country_code"]!)),
DataCell(Text(item["start_date"]!)),
DataCell(Row(
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Visa"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteMiscellaneous(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
IconButton(
icon: Icon(Icons.keyboard_arrow_down_outlined,
size: 28, color: Color(0xFF475569)),
onPressed: () {
// Expand logic
},
),
],
)
String formatDate(String dateString) {
try {
DateTime date = DateTime.parse(dateString);
return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25
} catch (e) {
return "Invalid Date";
}
}
//
// Row(
// children: [
// IconButton(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
// onPressed: () {
// // View action
// },
// ),
// IconButton(
// icon: Icon(Icons.edit, color: Colors.green),
// onPressed: () {
// onOpen(true, item, "Visa");
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteMiscellaneous(item);
// },
// ),
// ],
// )
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
return Container(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
// color: Colors.orange.shade50,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 3,
offset: Offset(0, 1),
),
],
border: Border(
top: BorderSide(
color: Colors.white70, // Change color to match your theme
width: 2,
),
),
]);
}).toList();
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () => onOpen(true, item, "Visa"),
child: Image.asset('assets/images/IconsImg/edit.png',
width: 20, height: 15),
),
SizedBox(width: 10),
GestureDetector(
onTap: () => onDeleteMiscellaneous(item),
child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15),
),
],
),
Divider(
color: Colors.blueGrey.shade50,
),
SizedBox(height: 4),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
" Visa Type",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Country",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Date",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
Expanded(
flex: 2,
child: Text(
" Comments",
style: TextStyle(
fontSize: 11, fontFamily: "Archivo"),
)),
],
)
: SizedBox.shrink(),
isDesktop
? Row(
children: [
Expanded(
flex: 2,
child: Text(
getRequestForVisa(
item["type_of_visa"]!.toString()),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text(
getRequestForCountry(
item["country_code"]!.toString()),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Text(
formatDate(item["start_date"]!),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
SizedBox(width: 10),
Expanded(
flex: 2,
child: _buildComments(
"Comments:", item["comments"] ?? "N/A"),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildRow(
"VisaType:",
getRequestForVisa(
item["type_of_visa"]!.toString())),
SizedBox(height: 6),
_buildRow(
"Country:",
getRequestForCountry(
item["country_code"]!.toString())),
SizedBox(height: 6),
_buildRow("StartDate:", item["start_date"]!),
SizedBox(height: 6),
_buildRow("Comments:", item["comments"] ?? "N/A"),
],
),
],
),
),
);
},
);
}
Widget _buildComments(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 20,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
),
],
);
}
Widget _buildRow(String title, String value) {
// Define character limits based on title
Map<String, int> limits = {
// "Special Request:": 30,
"Comments:": 40,
// Add more keys and limits if needed
};
int limit = limits[title] ?? 50; // Default limit if title not found
bool exceedsLimit = value.length > limit;
String wrapText(String text, int maxLineLength) {
final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 12),
),
SizedBox(width: 8),
Expanded(
child: exceedsLimit
? Tooltip(
message: wrapText(value, 50),
decoration: BoxDecoration(
color: Colors.grey.shade200, // Black background
borderRadius: BorderRadius.circular(6),
),
textStyle:
TextStyle(color: Colors.black), // Tooltip text color
padding: EdgeInsets.all(8),
preferBelow: false,
child: Text(value.substring(0, limit) + "...",
style: TextStyle(fontSize: 12),
overflow: TextOverflow.ellipsis),
)
: Text(
value,
style: TextStyle(fontSize: 12),
),
),
],
);
}
// List<DataRow> _buildDataRows() {
// List<Map<String, dynamic>> filteredList =
// visaList.where((item) => item["is_active"] == "1").toList();
// print("filteredList- $filteredList");
//
// List<dynamic> visatypeList = apiData?['visa_type_of_visa'] ?? [];
// List<dynamic> countryList = apiCountryData ?? [];
//
// String getRequestForVisa(String? specialRequestKey) {
// if (specialRequestKey == null) return "N/A";
//
// return visatypeList
// .firstWhere(
// (element) =>
// element["dropdown_key"].toString() == specialRequestKey,
// orElse: () => {"dropdown_value": "N/A"},
// )["dropdown_value"]
// .toString();
// }
//
// String getRequestForCountry(String? countryCode) {
// if (countryCode == null) return "N/A";
//
// return countryList
// .firstWhere(
// (element) => element["country_code"].toString() == countryCode,
// orElse: () => {"country_name": "N/A"},
// )["country_name"]
// .toString();
// }
//
// return filteredList.asMap().entries.map((entry) {
// int index = entry.key + 1; // To start index from 1
// Map<String, dynamic> item = entry.value;
// print(item);
// return DataRow(cells: [
// // DataCell(Text(item["indx"]?.toString() ?? "N/A")),
// // DataCell(Text(item["type_of_visa"]!)),
// DataCell(Text(getRequestForVisa(item["type_of_visa"]!.toString()))),
// DataCell(Text(getRequestForCountry(item["country_code"]!.toString()))),
// // DataCell(Text(item["country_code"]!)),
// DataCell(Text(item["start_date"]!)),
// DataCell(Row(
// children: [
// GestureDetector(
// onTap: () => onOpen(true, item, "Visa"),
// child: Image.asset('assets/images/IconsImg/edit.png',
// width: 20, height: 15),
// ),
// SizedBox(width: 10),
// GestureDetector(
// onTap: () => onDeleteMiscellaneous(item),
// child: Image.asset('assets/images/IconsImg/delete.png',
// width: 20, height: 15),
// ),
// IconButton(
// icon: Icon(Icons.keyboard_arrow_down_outlined,
// size: 28, color: Color(0xFF475569)),
// onPressed: () {
// // Expand logic
// },
// ),
// ],
// )
//
// //
// // Row(
// // children: [
// // IconButton(
// // icon: Icon(Icons.remove_red_eye, color: Colors.blue),
// // onPressed: () {
// // // View action
// // },
// // ),
// // IconButton(
// // icon: Icon(Icons.edit, color: Colors.green),
// // onPressed: () {
// // onOpen(true, item, "Visa");
// // },
// // ),
// // IconButton(
// // icon: Icon(Icons.delete, color: Colors.red),
// // onPressed: () {
// // onDeleteMiscellaneous(item);
// // },
// // ),
// // ],
// // )
//
// ),
// ]);
// }).toList();
// }
}

View File

@ -354,6 +354,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
children: [
Expanded(
child: Container(
// color: Colors.redAccent,
color: bodyColor,
child: buildOrgLayout(isDesktop),
),

File diff suppressed because it is too large Load Diff

View File

@ -431,6 +431,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
onOpen: handleEdit,
onAddNew: handlecreateNewPlan,
isViewMode: widget.isViewMode,
apiData: widget.apiData,
onDeleteTrain: (data) => handleItinerarydelete("Train", data),
);
break;
@ -440,6 +441,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
onOpen: handleEdit,
onAddNew: handlecreateNewPlan,
isViewMode: widget.isViewMode,
apiData: widget.apiData,
onDeleteTaxi: (data) => handleItinerarydelete("Taxi", data),
);
break;
@ -480,6 +482,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
onOpen: handleEdit,
onAddNew: handlecreateNewPlan,
isViewMode: widget.isViewMode,
apiData: widget.apiData,
onDeleteForex: (data) => handleItinerarydelete("Forex", data),
);
break;
@ -510,6 +513,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
onOpen: handleEdit,
onAddNew: handlecreateNewPlan,
isViewMode: widget.isViewMode,
apiData: widget.apiData,
onDeleteFlight: (data) => handleItinerarydelete("Flight", data));
break;
}
@ -612,7 +616,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
borderRadius: BorderRadius.circular(1),
// color: Color(0xFFF4F4FB),
),
padding: EdgeInsets.all(10),
// padding: EdgeInsets.all(10),
child: isMobile
? Expanded(
child: SingleChildScrollView(
@ -645,6 +649,24 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
List<Widget> _buildOptions() {
if (ServicesChoosed == null) return [];
if (ServicesChoosed != null &&
ServicesChoosed!.isNotEmpty &&
selectedListOption == "") {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
selectedListOption = ServicesChoosed!.first['name'];
isSelected = false;
});
});
}
// if (ServicesChoosed != null &&
// ServicesChoosed!.isNotEmpty &&
// selectedListOption == "") {
// selectedListOption = ServicesChoosed!.first['name'];
// isSelected = false;
// }
return ServicesChoosed!.map((service) {
return Padding(
padding: const EdgeInsets.only(right: 20.0),
@ -657,6 +679,76 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
Widget _buildOption(
Map<String, dynamic> service,
bool hasData,
) {
String name = service['name'];
String iconUrl = service['icon']; // Can be empty string
IconData fallbackIcon = _getLocalIconForService(name);
bool isOptionSelected = selectedListOption == name;
return GestureDetector(
onTap: () {
setState(() {
selectedListOption = name;
isSelected = false;
});
},
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: isOptionSelected ? Color(0xFF114D8B) : Colors.transparent,
width: 2,
),
),
),
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 10),
child: Row(
children: [
iconUrl.isNotEmpty
? Image.network(
iconUrl,
width: 18,
height: 18,
errorBuilder: (context, error, stackTrace) {
return Icon(
fallbackIcon,
size: 18,
color: isOptionSelected
? Color(0xFF114D8B)
: Color(0xFF475569),
);
},
)
: Icon(
fallbackIcon,
size: 18,
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(width: 4),
if (hasData) Icon(Icons.circle, size: 8, color: Colors.green),
],
),
),
);
}
Widget _buildOption1(
Map<String, dynamic> service,
bool hasData,
) {
String name = service['name'];
String iconUrl = service['icon']; // Can be empty string
@ -701,9 +793,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
: Color(0xFF475569),
),
SizedBox(width: 5),
SizedBox(width: 5),
SizedBox(width: 2),
Text(
name,
style: TextStyle(
@ -719,20 +809,19 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
// fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)),
),
SizedBox(width: 5),
SizedBox(width: 2),
// if (selectedListOption == title && widget.isViewMode == false)
if (hasData)
Container(
height: 13,
width: 13,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.green, width: 1.5),
),
child:
Icon(Icons.notifications_rounded, size: 8, color: Colors.green
// color: Colors.grey,
)),
height: 10,
width: 10,
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// border: Border.all(color: Colors.green, width: 1.5),
// ),
child: Icon(Icons.circle, size: 8, color: Colors.green
// color: Colors.grey,
)),
]),
);
}
@ -744,7 +833,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
case 'train':
return Icons.train_outlined;
case 'bus':
return Icons.bus_alert_outlined;
return Icons.directions_bus_filled_outlined;
case 'taxi':
return Icons.local_taxi_outlined;
case 'accomodation':

View File

@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:frontend/config/apiUrl.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -198,13 +199,23 @@ class _ListPlansState extends State<ListPlans> {
return Container(
decoration: BoxDecoration(
// color: Colors.amber,
color: bodyColor,
// color: bodyColor,
color: Color(0xFFE1F5FE),
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: buildTableLayout(isDesktop),
);
}
Widget buildTableLayout(isDesktop) {
String _formatDate(String rawDate) {
try {
final dateTime = DateTime.parse(rawDate);
return DateFormat('dd MMM yy : hh a').format(dateTime);
} catch (e) {
return rawDate; // fallback if parsing fails
}
}
return Container(
margin: isDesktop
? EdgeInsets.all(10.0)
@ -499,7 +510,8 @@ class _ListPlansState extends State<ListPlans> {
fontSize: 13,
fontFamily: "Archivo",
))),
DataCell(Text(plan.createdOn,
DataCell(Text(_formatDate(plan.createdOn),
// plan.createdOn,
style: TextStyle(
fontSize: 13,
fontFamily: "Archivo",

View File

@ -106,20 +106,21 @@ class _PolicyState extends State<Policy> {
@override
void initState() {
super.initState();
WidgetsFlutterBinding.ensureInitialized();
// WidgetsFlutterBinding.ensureInitialized();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadinitializeData();
loadInitialData();
if (widget.policy != null) {
final details =
List<Map<String, dynamic>>.from(widget.policy!['policy_details']);
policyCriteriaKey.currentState?.loadPolicyDetails(details);
}
updateSelectedServices();
updateData();
loadInitialData();
});
updateSelectedServices();
updateData();
}
void loadInitialData() async {
@ -440,14 +441,15 @@ class _PolicyState extends State<Policy> {
return Container(
// margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0),
decoration: BoxDecoration(
// color: Colors.amber,
color: bodyColor,
color: Color(0xFFE1F5FE),
// color: bodyColor,
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: Column(
children: [
Expanded(
child: Container(
color: bodyColor,
// color: bodyColor,
color: Color(0xFFE1F5FE),
child: buildPolicyLayout(isDesktop),
),
),

View File

@ -156,7 +156,8 @@ class _PolicyListState extends State<PolicyList> {
return Container(
decoration: BoxDecoration(
// color: Colors.amber,
color: bodyColor,
color: Color(0xFFE1F5FE),
// color: bodyColor,
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: buildGroupListLayout(isDesktop),
);

View File

@ -689,7 +689,8 @@ class _CreateUserFormState extends State<CreateUserForm> {
// margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0),
decoration: BoxDecoration(
// color: Colors.amber,
color: bodyColor,
// color: bodyColor,
color: Color(0xFFE1F5FE),
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: Column(
children: [

View File

@ -265,7 +265,8 @@ class _UserListScreenState extends State<UserListScreen> {
return Container(
decoration: BoxDecoration(
// color: Colors.amber,
color: bodyColor,
// color: bodyColor,
color: Color(0xFFE1F5FE),
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: buildUserTable(isDesktop),
);

View File

@ -582,7 +582,7 @@ packages:
source: hosted
version: "14.3.1"
web:
dependency: transitive
dependency: "direct main"
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"

View File

@ -45,6 +45,7 @@ dependencies:
bcrypt: ^1.1.3
http_parser: ^4.1.2
image_picker: ^1.1.2
web: ^1.1.0
dev_dependencies: