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), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
const Text("Please enter remarks to reject the plan."), const Text("Please enter reason for rejection."),
const SizedBox(height: 10), const SizedBox(height: 10),
TextField( TextField(
maxLines: 3, maxLines: 3,
onChanged: (value) => remarks = value, onChanged: (value) => remarks = value,
decoration: const InputDecoration( decoration: const InputDecoration(
hintText: "Remarks...", 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:http/http.dart' as http;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/config/apiUrl.dart'; import 'package:frontend/config/apiUrl.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -275,13 +276,23 @@ class _ApprovalListState extends State<ApprovalList> {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
// color: Colors.amber, // color: Colors.amber,
color: bodyColor, // color: bodyColor,
color: Color(0xFFE1F5FE),
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: buildTableLayout(isDesktop), child: buildTableLayout(isDesktop),
); );
} }
Widget 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( return Container(
margin: isDesktop margin: isDesktop
? EdgeInsets.all(10.0) ? EdgeInsets.all(10.0)
@ -414,7 +425,9 @@ class _ApprovalListState extends State<ApprovalList> {
double minWidth = isDesktop ? constraints.maxWidth : 1300; double minWidth = isDesktop ? constraints.maxWidth : 1300;
return ConstrainedBox( return ConstrainedBox(
constraints: BoxConstraints(minWidth: minWidth), constraints: BoxConstraints(
minWidth: minWidth,
),
child: DataTable( child: DataTable(
dividerThickness: 0.5, dividerThickness: 0.5,
columnSpacing: isDesktop ? 24.0 : 16.0, columnSpacing: isDesktop ? 24.0 : 16.0,
@ -494,7 +507,10 @@ class _ApprovalListState extends State<ApprovalList> {
fontSize: 13, fontSize: 13,
fontFamily: "Archivo", fontFamily: "Archivo",
))), ))),
DataCell(Text(plan.createdOn, DataCell(Text(
// plan.createdOn,
_formatDate(plan.createdOn),
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Archivo", fontFamily: "Archivo",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class AccomodationListWidget extends StatelessWidget { class AccomodationListWidget extends StatelessWidget {
final List<Map<String, dynamic>> accommodationList; final List<Map<String, dynamic>> accommodationList;
@ -18,11 +19,15 @@ class AccomodationListWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Container(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// Header with title and button
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -44,7 +49,8 @@ class AccomodationListWidget extends StatelessWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2), side: BorderSide(color: Color(0xFF114D8B), width: 2),
), ),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding:
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: isViewMode onPressed: isViewMode
? null ? null
@ -73,49 +79,89 @@ class AccomodationListWidget extends StatelessWidget {
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
SingleChildScrollView( // Scroll behavior based on device
scrollDirection: Axis.horizontal,
child: SizedBox( _buildData(context, isDesktop)
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(),
), ),
), ),
),
],
),
); );
} }
List<DataRow> _buildDataRows() { Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList = List<Map<String, dynamic>> filteredList =
accommodationList.where((item) => item["is_active"] == "1").toList(); accommodationList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList"); print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) { String formatDate(String dateString) {
final Map<String, dynamic> item = entry.value; 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: [ String formatTime(String timeString) {
DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column try {
DataCell(Text(item["destination_city"]!)), final parts = timeString.split(':');
DataCell(Text(item["hotel_name"]!)), final now = DateTime.now();
DataCell(Text(item["checkin_date"]!)), final dateTime = DateTime(
DataCell(Text(item["checkout_date"]!)), now.year,
DataCell(Row( 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,
),
),
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
item["hotel_name"]!,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
fontFamily: "Archivo"),
),
Spacer(),
GestureDetector( GestureDetector(
onTap: () => onOpen(true, item, "Accomodation"), onTap: () => onOpen(true, item, "Accomodation"),
child: Image.asset('assets/images/IconsImg/edit.png', child: Image.asset('assets/images/IconsImg/edit.png',
@ -127,40 +173,232 @@ class AccomodationListWidget extends StatelessWidget {
child: Image.asset('assets/images/IconsImg/delete.png', child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15), width: 20, height: 15),
), ),
IconButton( ],
icon: Icon(Icons.keyboard_arrow_down_outlined, ),
size: 28, color: Color(0xFF475569)), Divider(
onPressed: () { color: Colors.blueGrey.shade50,
// Expand logic ),
}, 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"),
],
)
],
),
),
);
},
);
}
// Row( Widget _buildComments(String title, String value) {
// children: [ // Define character limits based on title
// IconButton( Map<String, int> limits = {
// icon: Icon(Icons.remove_red_eye, color: Colors.blue), // "Special Request:": 30,
// onPressed: () { "Comments:": 20,
// // View action // Add more keys and limits if needed
// }, };
// ),
// IconButton( int limit = limits[title] ?? 50; // Default limit if title not found
// icon: Icon(Icons.edit, color: Colors.green), bool exceedsLimit = value.length > limit;
// onPressed: () {
// onOpen(true, item, "Accomodation"); String wrapText(String text, int maxLineLength) {
// }, final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)');
// ), return pattern.allMatches(text).map((m) => m.group(0)!).join('\n');
// IconButton( }
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () { return Row(
// onDeleteAccommodation(item); crossAxisAlignment: CrossAxisAlignment.start,
// }, children: [
// ), Expanded(
// ], child: exceedsLimit
// )), ? Tooltip(
// message: wrapText(value, 50),
]); decoration: BoxDecoration(
}).toList(); 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 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -25,11 +26,15 @@ class BusListWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Container(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// Header with title and button
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -51,7 +56,8 @@ class BusListWidget extends StatelessWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2), side: BorderSide(color: Color(0xFF114D8B), width: 2),
), ),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding:
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: isViewMode onPressed: isViewMode
? null ? null
@ -80,59 +86,80 @@ class BusListWidget extends StatelessWidget {
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Container( // Scroll behavior based on device
// color: Colors.blueGrey,
width: double.infinity,
child: Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
// child: Expanded(
child: SizedBox( _buildData(context, isDesktop)
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(),
), ),
), ),
// )
),
),
),
],
),
); );
} }
List<DataRow> _buildDataRows() { Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList = List<Map<String, dynamic>> filteredList =
busList.where((item) => item["is_active"] == "1").toList(); busList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList"); print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) { String formatDate(String dateString) {
final Map<String, dynamic> item = entry.value; 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: [ String formatTime(String timeString) {
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column try {
DataCell(Text(item["from"]!)), final parts = timeString.split(':');
DataCell(Text(item["to"]!)), final now = DateTime.now();
DataCell(Text(item["date"]!)), final dateTime = DateTime(
DataCell(Text(item["time"]!)), now.year,
DataCell(Row( 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,
),
),
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
GestureDetector( GestureDetector(
onTap: () => onOpen(true, item, "Bus"), onTap: () => onOpen(true, item, "Bus"),
@ -145,41 +172,213 @@ class BusListWidget extends StatelessWidget {
child: Image.asset('assets/images/IconsImg/delete.png', child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15), width: 20, height: 15),
), ),
IconButton( ],
icon: Icon(Icons.keyboard_arrow_down_outlined, ),
size: 28, color: Color(0xFF475569)), Divider(
onPressed: () { color: Colors.blueGrey.shade50,
// Expand logic ),
}, 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(
// Row( crossAxisAlignment: CrossAxisAlignment.start,
// children: [ children: [
// IconButton( _buildRow("From:", item["from"]),
// icon: Icon(Icons.remove_red_eye, color: Colors.blue), SizedBox(width: 10),
// onPressed: () { _buildRow("To:", item["to"]!),
// // View action SizedBox(width: 10),
// }, _buildDateTimeRow(
// ), "Date:",
// IconButton( formatDate(item["date"]!),
// icon: Icon(Icons.edit, color: Colors.green), formatTime(item["time"]!),
// onPressed: () {
// onOpen(true, item, "Bus");
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteBus(item);
// },
// ),
// ],
// )
), ),
]); _buildRow("Comments:", item["comments"] ?? "N/A"),
}).toList(); ],
)
],
),
),
);
},
);
}
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:flutter/material.dart';
import 'package:intl/intl.dart';
class FlightListWidget extends StatelessWidget { class FlightListWidget extends StatelessWidget {
final List<Map<String, dynamic>> flightList; final List<Map<String, dynamic>> flightList;
final Function(bool, Map<String, dynamic>, String) onOpen; final Function(bool, Map<String, dynamic>, String) onOpen;
final Function(Map<String, dynamic>) onDeleteFlight; final Function(Map<String, dynamic>) onDeleteFlight;
final Map<String, dynamic>? apiData;
final Function(String, bool) onAddNew; final Function(String, bool) onAddNew;
final bool isViewMode; final bool isViewMode;
@ -14,15 +16,20 @@ class FlightListWidget extends StatelessWidget {
required this.onOpen, required this.onOpen,
required this.onDeleteFlight, required this.onDeleteFlight,
required this.onAddNew, required this.onAddNew,
required this.apiData,
required this.isViewMode}); required this.isViewMode});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Container(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// Header with title and button
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -44,7 +51,8 @@ class FlightListWidget extends StatelessWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2), side: BorderSide(color: Color(0xFF114D8B), width: 2),
), ),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding:
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: isViewMode onPressed: isViewMode
? null ? null
@ -73,55 +81,102 @@ class FlightListWidget extends StatelessWidget {
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
SingleChildScrollView( // Scroll behavior based on device
scrollDirection: Axis.horizontal,
child: SizedBox( _buildData(context, isDesktop)
// 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(),
), ),
), ),
),
],
),
); );
} }
List<DataRow> _buildDataRows() { Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList = List<Map<String, dynamic>> filteredList =
flightList.where((item) => item["is_active"] == "1").toList(); flightList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList"); print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) { List<dynamic> visatypeList = apiData?['flight_class'] ?? [];
Map<String, dynamic> item = entry.value;
print("Trip Type: ${item["trip_type"]}");
return DataRow(cells: [ String getRequestForClass(String? specialRequestKey) {
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), if (specialRequestKey == null) return "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( 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),
),
],
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( Row(
children: [ children: [
Text(
item["trip_type"]?.toString() ?? "N/A",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
fontFamily: "Archivo"),
),
Spacer(),
GestureDetector( GestureDetector(
onTap: () => onOpen(true, item, "Flight"), onTap: () => onOpen(true, item, "Flight"),
child: Image.asset('assets/images/IconsImg/edit.png', child: Image.asset('assets/images/IconsImg/edit.png',
@ -133,42 +188,152 @@ class FlightListWidget extends StatelessWidget {
child: Image.asset('assets/images/IconsImg/delete.png', child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15), width: 20, height: 15),
), ),
IconButton( ],
icon: Icon(Icons.keyboard_arrow_down_outlined, ),
size: 28, color: Color(0xFF475569)), Divider(
onPressed: () { color: Colors.blueGrey.shade50,
// Expand logic ),
}, 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 &&
// Row( item["trips"] != null &&
// children: [ item["trips"].isNotEmpty)
// IconButton( ...item["trips"].map<Widget>((trip) {
// icon: Icon(Icons.remove_red_eye, color: Colors.blue), return Padding(
// onPressed: () { padding: const EdgeInsets.symmetric(vertical: 6.0),
// // View action child: Container(
// }, decoration: BoxDecoration(
// ), border: Border.all(color: Colors.grey.shade300),
// IconButton( borderRadius: BorderRadius.circular(6),
// icon: Icon(Icons.edit, color: Colors.green), color: Colors.grey.shade50,
// onPressed: () {
// onOpen(true, item, "Flight");
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteFlight(item);
// },
// ),
// ],
// )
//
), ),
]); padding: const EdgeInsets.all(8),
}).toList(); 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),
),
),
],
),
);
} }
} }

View File

@ -1,12 +1,14 @@
import 'dart:convert'; import 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class ForexListWidget extends StatelessWidget { class ForexListWidget extends StatelessWidget {
final List<Map<String, dynamic>> forexList; final List<Map<String, dynamic>> forexList;
final Function(bool, Map<String, dynamic>, String) onOpen; final Function(bool, Map<String, dynamic>, String) onOpen;
final Function(Map<String, dynamic>) onDeleteForex; final Function(Map<String, dynamic>) onDeleteForex;
final List<dynamic>? apiCountryData; final List<dynamic>? apiCountryData;
final Map<String, dynamic>? apiData;
final Function(String, bool) onAddNew; final Function(String, bool) onAddNew;
final bool isViewMode; final bool isViewMode;
@ -18,15 +20,20 @@ class ForexListWidget extends StatelessWidget {
required this.onDeleteForex, required this.onDeleteForex,
required this.apiCountryData, required this.apiCountryData,
required this.onAddNew, required this.onAddNew,
required this.apiData,
required this.isViewMode}); required this.isViewMode});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Container(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// Header with title and button
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -48,7 +55,8 @@ class ForexListWidget extends StatelessWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2), side: BorderSide(color: Color(0xFF114D8B), width: 2),
), ),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding:
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: isViewMode onPressed: isViewMode
? null ? null
@ -77,48 +85,35 @@ class ForexListWidget extends StatelessWidget {
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Container( // Scroll behavior based on device
// color: Colors.blueGrey,
width: double.infinity,
child: Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox( _buildData(context, isDesktop)
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(),
), ),
), ),
//
),
),
),
],
),
); );
} }
List<DataRow> _buildDataRows() { Widget _buildData(BuildContext context, bool isDesktop) {
List<dynamic> countryList = apiCountryData ?? [];
List<Map<String, dynamic>> filteredList = List<Map<String, dynamic>> filteredList =
forexList.where((item) => item["is_active"] == "1").toList(); forexList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList"); 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) { String getRequestForCountry(String? countryCode) {
if (countryCode == null) return "N/A"; if (countryCode == null) return "N/A";
@ -130,18 +125,58 @@ class ForexListWidget extends StatelessWidget {
.toString(); .toString();
} }
return filteredList.asMap().entries.map((entry) { String formatDate(String dateString) {
Map<String, dynamic> item = entry.value; 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: [ return ListView.builder(
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column shrinkWrap: true,
DataCell(Text(item["start_date"] ?? "N/A")), physics: NeverScrollableScrollPhysics(),
DataCell(Text(item["end_date"] ?? "N/A")), itemCount: filteredList.length,
DataCell(Text(getRequestForCountry(item["country_code"]!.toString()))), itemBuilder: (context, index) {
// DataCell(Text(item["country_code"] ?? "N/A")), final item = filteredList[index];
DataCell(Text(item["perdiem_amount"] ?? "N/A")),
DataCell(Row( 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: [ children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
getRequestForCountry(item["country_code"]!.toString()),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: "Archivo"),
),
Spacer(),
GestureDetector( GestureDetector(
onTap: () => onOpen(true, item, "Forex"), onTap: () => onOpen(true, item, "Forex"),
child: Image.asset('assets/images/IconsImg/edit.png', child: Image.asset('assets/images/IconsImg/edit.png',
@ -153,41 +188,233 @@ class ForexListWidget extends StatelessWidget {
child: Image.asset('assets/images/IconsImg/delete.png', child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15), width: 20, height: 15),
), ),
IconButton( ],
icon: Icon(Icons.keyboard_arrow_down_outlined, ),
size: 28, color: Color(0xFF475569)), Divider(
onPressed: () { color: Colors.blueGrey.shade50,
// Expand logic ),
}, 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(
// Row( crossAxisAlignment: CrossAxisAlignment.start,
// children: [ children: [
// IconButton( _buildRow(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue), "Currency:",
// onPressed: () { item["currency"],
// // 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);
// },
// ),
// ],
// )
), ),
]); // SizedBox(height: 6),
}).toList(); _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 'dart:js_interop';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class InsuranceListWidget extends StatelessWidget { class InsuranceListWidget extends StatelessWidget {
final List<Map<String, dynamic>> insuranceList; final List<Map<String, dynamic>> insuranceList;
@ -22,12 +23,15 @@ class InsuranceListWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Container( child: Container(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// Header with title and button
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -79,33 +83,288 @@ class InsuranceListWidget extends StatelessWidget {
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Center( // Scroll behavior based on device
child: SingleChildScrollView(
scrollDirection: Axis.horizontal, _buildData(context, isDesktop)
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(), ),
),
);
}
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<DataRow> _buildDataRows() {

View File

@ -19,18 +19,21 @@ class MiscellaneousListWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Container( child: Container(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// Header with title and button
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
"Miscellaneous Booking List", "Miscellaneous List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
), ),
MouseRegion( MouseRegion(
cursor: isViewMode cursor: isViewMode
@ -52,23 +55,15 @@ class MiscellaneousListWidget extends StatelessWidget {
onPressed: isViewMode onPressed: isViewMode
? null ? null
: () { : () {
print("New data");
onAddNew("Miscellaneous", true); onAddNew("Miscellaneous", true);
}, },
child: Row( child: Row(
mainAxisSize: mainAxisSize: MainAxisSize.min,
MainAxisSize.min, // Ensures content fits nicely
children: [ children: [
Text( Text("Add New", style: TextStyle(fontSize: 13)),
"Add New", SizedBox(width: 8),
style: TextStyle(fontSize: 13), Icon(Icons.add_circle_outline_rounded,
), size: 15, color: Colors.white),
SizedBox(width: 8), // spacing between icon and text
Icon(
Icons.add_circle_outline_rounded,
size: 15,
color: Colors.white,
),
], ],
), ),
), ),
@ -76,45 +71,28 @@ class MiscellaneousListWidget extends StatelessWidget {
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Center( // Scroll behavior based on device
child: SingleChildScrollView( _buildData(context, isDesktop)
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(),
),
),
),
),
], ],
), ),
), ),
); );
} }
List<DataRow> _buildDataRows() { Widget _buildData(BuildContext context, bool isDesktop) {
print("miscellaneousList - $miscellaneousList"); List<Map<String, dynamic>> filteredList =
List<dynamic> purposeList = apiData?['miscellaneous_special_request'] ?? []; miscellaneousList.where((item) => item["is_active"] == "1").toList();
print("purposeList - $purposeList"); return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
String getRequestValue(String? specialRequestKey) { String getRequestValue(String? specialRequestKey) {
if (specialRequestKey == null) return "N/A"; if (specialRequestKey == null) return "N/A";
return (apiData?['miscellaneous_special_request'] ?? [])
return purposeList
.firstWhere( .firstWhere(
(element) => (element) =>
element["dropdown_key"].toString() == specialRequestKey, element["dropdown_key"].toString() == specialRequestKey,
@ -123,22 +101,33 @@ class MiscellaneousListWidget extends StatelessWidget {
.toString(); .toString();
} }
List<Map<String, dynamic>> filteredList = return Container(
miscellaneousList.where((item) => item["is_active"] == "1").toList(); margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
print("filteredList- $filteredList"); decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
return filteredList.asMap().entries.map((entry) { // color: Colors.yellow.shade50,
int index = entry.key + 1; // To start index from 1 color: Colors.white,
Map<String, dynamic> item = entry.value; boxShadow: [
print(item); BoxShadow(
color: Colors.black12,
return DataRow(cells: [ blurRadius: 3,
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column offset: Offset(0, 1),
// DataCell(Text(specialRequestValue)), ),
DataCell(Text(getRequestValue(item["special_request"]?.toString()))), ],
DataCell(Text(item["comments"] ?? "N/A")), border: Border(
// DataCell(Text(item["created_on"] ?? "N/A")), top: BorderSide(
DataCell(Row( 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: [ children: [
GestureDetector( GestureDetector(
onTap: () => onOpen(true, item, "Miscellaneous"), onTap: () => onOpen(true, item, "Miscellaneous"),
@ -151,43 +140,236 @@ class MiscellaneousListWidget extends StatelessWidget {
child: Image.asset('assets/images/IconsImg/delete.png', child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15), width: 20, height: 15),
), ),
IconButton( ],
icon: Icon(Icons.keyboard_arrow_down_outlined, ),
size: 28, color: Color(0xFF475569)), Divider(
onPressed: () { color: Colors.blueGrey.shade50,
// Expand logic ),
}, 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"),
],
),
],
),
),
);
},
);
}
// Row( 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: [ // children: [
// IconButton( // GestureDetector(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue), // onTap: () => onOpen(true, item, "Miscellaneous"),
// onPressed: () { // child: Image.asset('assets/images/IconsImg/edit.png',
// // View action // 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( // IconButton(
// icon: Icon(Icons.edit, color: Colors.green), // icon: Icon(Icons.keyboard_arrow_down_outlined,
// size: 28, color: Color(0xFF475569)),
// onPressed: () { // onPressed: () {
// onOpen(true, item, "Miscellaneous"); // // Expand logic (optional)
// // Edit action
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteMiscellaneous(item);
// // Delete action
// }, // },
// ), // ),
// ], // ],
// ) // )),
// ]);
), // }).toList();
]); // }
}).toList();
}
} }

View File

@ -1,9 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class TaxiListWidget extends StatelessWidget { class TaxiListWidget extends StatelessWidget {
final List<Map<String, dynamic>> taxiList; final List<Map<String, dynamic>> taxiList;
final Function(bool, Map<String, dynamic>, String) onOpen; final Function(bool, Map<String, dynamic>, String) onOpen;
final Function(Map<String, dynamic>) onDeleteTaxi; final Function(Map<String, dynamic>) onDeleteTaxi;
final Map<String, dynamic>? apiData;
final Function(String, bool) onAddNew; final Function(String, bool) onAddNew;
final bool isViewMode; final bool isViewMode;
@ -11,6 +13,7 @@ class TaxiListWidget extends StatelessWidget {
const TaxiListWidget( const TaxiListWidget(
{super.key, {super.key,
required this.taxiList, required this.taxiList,
required this.apiData,
required this.onOpen, required this.onOpen,
required this.onDeleteTaxi, required this.onDeleteTaxi,
required this.onAddNew, required this.onAddNew,
@ -18,11 +21,15 @@ class TaxiListWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Container(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// Header with title and button
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -44,7 +51,8 @@ class TaxiListWidget extends StatelessWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2), side: BorderSide(color: Color(0xFF114D8B), width: 2),
), ),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding:
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: isViewMode onPressed: isViewMode
? null ? null
@ -73,51 +81,116 @@ class TaxiListWidget extends StatelessWidget {
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Center( // Scroll behavior based on device
child: SingleChildScrollView(
scrollDirection: Axis.horizontal, _buildData(context, isDesktop)
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(),
), ),
), ),
),
),
],
),
); );
} }
List<DataRow> _buildDataRows() { Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList = List<Map<String, dynamic>> filteredList =
taxiList.where((item) => item["is_active"] == "1").toList(); taxiList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList"); print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) { List<dynamic> taxiClassList = apiData?['taxt_car_type'] ?? [];
final Map<String, dynamic> item = entry.value; List<dynamic> taxirequiredFor = apiData?['taxi_car_required_for'] ?? [];
return DataRow(cells: [ String getRequestForTaxiClass(String? specialRequestKey) {
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column if (specialRequestKey == null) return "N/A";
DataCell(Text(item["destination_city"]!)),
DataCell(Text(item["location_of_pickup"]!)), return taxiClassList
DataCell(Text(item["date"]!)), .firstWhere(
DataCell(Text(item["car_required_for"]!)), (element) =>
DataCell(Row( 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,
),
),
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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( GestureDetector(
onTap: () => onOpen(true, item, "Taxi"), onTap: () => onOpen(true, item, "Taxi"),
child: Image.asset('assets/images/IconsImg/edit.png', child: Image.asset('assets/images/IconsImg/edit.png',
@ -125,46 +198,234 @@ class TaxiListWidget extends StatelessWidget {
), ),
SizedBox(width: 10), SizedBox(width: 10),
GestureDetector( GestureDetector(
onTap: () => onDeleteTaxi(item), onTap: () => (item),
child: Image.asset('assets/images/IconsImg/delete.png', child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15), width: 20, height: 15),
), ),
IconButton( ],
icon: Icon(Icons.keyboard_arrow_down_outlined, ),
size: 28, color: Color(0xFF475569)), Divider(
onPressed: () { color: Colors.blueGrey.shade50,
// Expand logic ),
}, 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(
// Row( crossAxisAlignment: CrossAxisAlignment.start,
// children: [ children: [
// IconButton( _buildRow("City:", item["destination_city"]),
// icon: Icon(Icons.remove_red_eye, color: Colors.blue), SizedBox(width: 10),
// onPressed: () { _buildRow("Pickup:", item["location_of_pickup"]!),
// // View action SizedBox(width: 10),
// }, _buildDateTimeRow(
// ), "Date:",
// IconButton( formatDate(item["date"]!),
// icon: Icon(Icons.edit, color: Colors.green), formatTime(item["time"]!),
// onPressed: () {
// onOpen(true, item, "Taxi");
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteTaxi(item);
// },
// ),
// ],
// )
//
), ),
]); _buildRow("TaxiFor:", item["car_required_for"]!),
}).toList(); _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:flutter/material.dart';
import 'package:intl/intl.dart';
class TrainListWidget extends StatelessWidget { class TrainListWidget extends StatelessWidget {
final List<Map<String, dynamic>> trainList; final List<Map<String, dynamic>> trainList;
@ -6,6 +7,7 @@ class TrainListWidget extends StatelessWidget {
final Function(Map<String, dynamic>) onDeleteTrain; final Function(Map<String, dynamic>) onDeleteTrain;
final Function(String, bool) onAddNew; final Function(String, bool) onAddNew;
final bool isViewMode; final bool isViewMode;
final Map<String, dynamic>? apiData;
const TrainListWidget({ const TrainListWidget({
super.key, super.key,
@ -14,16 +16,20 @@ class TrainListWidget extends StatelessWidget {
required this.onDeleteTrain, required this.onDeleteTrain,
required this.onAddNew, required this.onAddNew,
required this.isViewMode, required this.isViewMode,
required this.apiData,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Container( child: Container(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// Header with title and button
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -75,53 +81,103 @@ class TrainListWidget extends StatelessWidget {
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Center( // Scroll behavior based on device
child: SingleChildScrollView(
scrollDirection: Axis.horizontal, _buildData(context, isDesktop)
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(),
),
),
),
),
], ],
), ),
), ),
); );
} }
List<DataRow> _buildDataRows() { Widget _buildData(BuildContext context, bool isDesktop) {
List<Map<String, dynamic>> filteredList = List<Map<String, dynamic>> filteredList =
trainList.where((item) => item["is_active"] == "1").toList(); trainList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList"); // print("filteredList- $filteredList");
return filteredList.asMap().entries.map((entry) { List<dynamic> trainClassList = apiData?['train_class'] ?? [];
final Map<String, dynamic> item = entry.value;
return DataRow(cells: [ String getRequestForTrainClass(String? specialRequestKey) {
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), if (specialRequestKey == null) return "N/A";
DataCell(Text(item["train_no"]!)), return trainClassList
// DataCell(Text(item["class"]!)), .firstWhere(
DataCell(Text(item["from_station"]!)), (element) =>
// DataCell(Text(item["to_station"]!)), element["dropdown_key"].toString() == specialRequestKey,
DataCell(Row( 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,
),
),
),
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
item["train_no"]?.toString() ?? "N/A",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
fontFamily: "Archivo"),
),
Spacer(),
GestureDetector( GestureDetector(
onTap: () => onOpen(true, item, "Train"), onTap: () => onOpen(true, item, "Train"),
child: Image.asset('assets/images/IconsImg/edit.png', child: Image.asset('assets/images/IconsImg/edit.png',
@ -133,42 +189,232 @@ class TrainListWidget extends StatelessWidget {
child: Image.asset('assets/images/IconsImg/delete.png', child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15), width: 20, height: 15),
), ),
IconButton( ],
icon: Icon(Icons.keyboard_arrow_down_outlined, ),
size: 28, color: Color(0xFF475569)), Divider(
onPressed: () { color: Colors.blueGrey.shade50,
// Expand logic ),
}, 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(
// Row( crossAxisAlignment: CrossAxisAlignment.start,
// children: [ children: [
// IconButton( _buildRow(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue), "Class:",
// onPressed: () { getRequestForTrainClass(
// // View action item["class"]!.toString())),
// }, _buildRow("From:", item["from_station"]!),
// ), _buildRow("To:", item["to_station"]!),
// IconButton( _buildDateTimeRow(
// icon: Icon(Icons.edit, color: Colors.green), "Date:",
// onPressed: () { formatDate(item["date"]!),
// onOpen(true, item, "Train"); formatTime(item["time"]!),
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteTrain(item);
// },
// ),
// ],
// )
//
), ),
]); _buildRow("Comments:", item["comments"] ?? "N/A"),
}).toList(); ],
)
],
),
),
);
},
);
}
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,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class VisaListWidget extends StatelessWidget { class VisaListWidget extends StatelessWidget {
final List<Map<String, dynamic>> visaList; final List<Map<String, dynamic>> visaList;
@ -22,18 +23,21 @@ class VisaListWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 1024;
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Container( child: Container(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// Header with title and button
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
"Visa Booking List", "Visa Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
), ),
MouseRegion( MouseRegion(
cursor: isViewMode cursor: isViewMode
@ -59,19 +63,12 @@ class VisaListWidget extends StatelessWidget {
onAddNew("Visa", true); onAddNew("Visa", true);
}, },
child: Row( child: Row(
mainAxisSize: mainAxisSize: MainAxisSize.min,
MainAxisSize.min, // Ensures content fits nicely
children: [ children: [
Text( Text("Add New", style: TextStyle(fontSize: 13)),
"Add New", SizedBox(width: 8),
style: TextStyle(fontSize: 13), Icon(Icons.add_circle_outline_rounded,
), size: 15, color: Colors.white),
SizedBox(width: 8), // spacing between icon and text
Icon(
Icons.add_circle_outline_rounded,
size: 15,
color: Colors.white,
),
], ],
), ),
), ),
@ -79,36 +76,102 @@ class VisaListWidget extends StatelessWidget {
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Center( // Scroll behavior based on device
child: SingleChildScrollView(
scrollDirection: Axis.horizontal, _buildData(context, isDesktop)
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(),
),
),
),
),
], ],
), ),
), ),
); );
} }
// 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 = List<Map<String, dynamic>> filteredList =
visaList.where((item) => item["is_active"] == "1").toList(); visaList.where((item) => item["is_active"] == "1").toList();
print("filteredList- $filteredList"); print("filteredList- $filteredList");
@ -139,18 +202,49 @@ class VisaListWidget extends StatelessWidget {
.toString(); .toString();
} }
return filteredList.asMap().entries.map((entry) { String formatDate(String dateString) {
int index = entry.key + 1; // To start index from 1 try {
Map<String, dynamic> item = entry.value; DateTime date = DateTime.parse(dateString);
print(item); return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25
return DataRow(cells: [ } catch (e) {
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), return "Invalid Date";
// 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"]!)), return ListView.builder(
DataCell(Text(item["start_date"]!)), shrinkWrap: true,
DataCell(Row( 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: [ children: [
GestureDetector( GestureDetector(
onTap: () => onOpen(true, item, "Visa"), onTap: () => onOpen(true, item, "Visa"),
@ -163,42 +257,307 @@ class VisaListWidget extends StatelessWidget {
child: Image.asset('assets/images/IconsImg/delete.png', child: Image.asset('assets/images/IconsImg/delete.png',
width: 20, height: 15), width: 20, height: 15),
), ),
IconButton( ],
icon: Icon(Icons.keyboard_arrow_down_outlined, ),
size: 28, color: Color(0xFF475569)), Divider(
onPressed: () { color: Colors.blueGrey.shade50,
// Expand logic ),
}, 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");
// //
// Row( // 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: [ // children: [
// IconButton( // GestureDetector(
// icon: Icon(Icons.remove_red_eye, color: Colors.blue), // onTap: () => onOpen(true, item, "Visa"),
// onPressed: () { // child: Image.asset('assets/images/IconsImg/edit.png',
// // View action // width: 20, height: 15),
// }, // ),
// SizedBox(width: 10),
// GestureDetector(
// onTap: () => onDeleteMiscellaneous(item),
// child: Image.asset('assets/images/IconsImg/delete.png',
// width: 20, height: 15),
// ), // ),
// IconButton( // IconButton(
// icon: Icon(Icons.edit, color: Colors.green), // icon: Icon(Icons.keyboard_arrow_down_outlined,
// size: 28, color: Color(0xFF475569)),
// onPressed: () { // onPressed: () {
// onOpen(true, item, "Visa"); // // Expand logic
// },
// ),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// onDeleteMiscellaneous(item);
// }, // },
// ), // ),
// ], // ],
// ) // )
//
), // //
]); // // Row(
}).toList(); // // 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: [ children: [
Expanded( Expanded(
child: Container( child: Container(
// color: Colors.redAccent,
color: bodyColor, color: bodyColor,
child: buildOrgLayout(isDesktop), 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, onOpen: handleEdit,
onAddNew: handlecreateNewPlan, onAddNew: handlecreateNewPlan,
isViewMode: widget.isViewMode, isViewMode: widget.isViewMode,
apiData: widget.apiData,
onDeleteTrain: (data) => handleItinerarydelete("Train", data), onDeleteTrain: (data) => handleItinerarydelete("Train", data),
); );
break; break;
@ -440,6 +441,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
onOpen: handleEdit, onOpen: handleEdit,
onAddNew: handlecreateNewPlan, onAddNew: handlecreateNewPlan,
isViewMode: widget.isViewMode, isViewMode: widget.isViewMode,
apiData: widget.apiData,
onDeleteTaxi: (data) => handleItinerarydelete("Taxi", data), onDeleteTaxi: (data) => handleItinerarydelete("Taxi", data),
); );
break; break;
@ -480,6 +482,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
onOpen: handleEdit, onOpen: handleEdit,
onAddNew: handlecreateNewPlan, onAddNew: handlecreateNewPlan,
isViewMode: widget.isViewMode, isViewMode: widget.isViewMode,
apiData: widget.apiData,
onDeleteForex: (data) => handleItinerarydelete("Forex", data), onDeleteForex: (data) => handleItinerarydelete("Forex", data),
); );
break; break;
@ -510,6 +513,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
onOpen: handleEdit, onOpen: handleEdit,
onAddNew: handlecreateNewPlan, onAddNew: handlecreateNewPlan,
isViewMode: widget.isViewMode, isViewMode: widget.isViewMode,
apiData: widget.apiData,
onDeleteFlight: (data) => handleItinerarydelete("Flight", data)); onDeleteFlight: (data) => handleItinerarydelete("Flight", data));
break; break;
} }
@ -612,7 +616,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
borderRadius: BorderRadius.circular(1), borderRadius: BorderRadius.circular(1),
// color: Color(0xFFF4F4FB), // color: Color(0xFFF4F4FB),
), ),
padding: EdgeInsets.all(10), // padding: EdgeInsets.all(10),
child: isMobile child: isMobile
? Expanded( ? Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
@ -645,6 +649,24 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
List<Widget> _buildOptions() { List<Widget> _buildOptions() {
if (ServicesChoosed == null) return []; 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 ServicesChoosed!.map((service) {
return Padding( return Padding(
padding: const EdgeInsets.only(right: 20.0), padding: const EdgeInsets.only(right: 20.0),
@ -657,6 +679,76 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
Widget _buildOption( Widget _buildOption(
Map<String, dynamic> service, Map<String, dynamic> service,
bool hasData, 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 name = service['name'];
String iconUrl = service['icon']; // Can be empty string String iconUrl = service['icon']; // Can be empty string
@ -701,9 +793,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
: Color(0xFF475569), : Color(0xFF475569),
), ),
SizedBox(width: 5), SizedBox(width: 2),
SizedBox(width: 5),
Text( Text(
name, name,
style: TextStyle( style: TextStyle(
@ -719,18 +809,17 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
// fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)), // fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)),
), ),
SizedBox(width: 5), SizedBox(width: 2),
// if (selectedListOption == title && widget.isViewMode == false) // if (selectedListOption == title && widget.isViewMode == false)
if (hasData) if (hasData)
Container( Container(
height: 13, height: 10,
width: 13, width: 10,
decoration: BoxDecoration( // decoration: BoxDecoration(
shape: BoxShape.circle, // shape: BoxShape.circle,
border: Border.all(color: Colors.green, width: 1.5), // border: Border.all(color: Colors.green, width: 1.5),
), // ),
child: child: Icon(Icons.circle, size: 8, color: Colors.green
Icon(Icons.notifications_rounded, size: 8, color: Colors.green
// color: Colors.grey, // color: Colors.grey,
)), )),
]), ]),
@ -744,7 +833,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
case 'train': case 'train':
return Icons.train_outlined; return Icons.train_outlined;
case 'bus': case 'bus':
return Icons.bus_alert_outlined; return Icons.directions_bus_filled_outlined;
case 'taxi': case 'taxi':
return Icons.local_taxi_outlined; return Icons.local_taxi_outlined;
case 'accomodation': case 'accomodation':

View File

@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/config/apiUrl.dart'; import 'package:frontend/config/apiUrl.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -198,13 +199,23 @@ class _ListPlansState extends State<ListPlans> {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
// color: Colors.amber, // color: Colors.amber,
color: bodyColor, // color: bodyColor,
color: Color(0xFFE1F5FE),
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: buildTableLayout(isDesktop), child: buildTableLayout(isDesktop),
); );
} }
Widget 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( return Container(
margin: isDesktop margin: isDesktop
? EdgeInsets.all(10.0) ? EdgeInsets.all(10.0)
@ -499,7 +510,8 @@ class _ListPlansState extends State<ListPlans> {
fontSize: 13, fontSize: 13,
fontFamily: "Archivo", fontFamily: "Archivo",
))), ))),
DataCell(Text(plan.createdOn, DataCell(Text(_formatDate(plan.createdOn),
// plan.createdOn,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Archivo", fontFamily: "Archivo",

View File

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

View File

@ -156,7 +156,8 @@ class _PolicyListState extends State<PolicyList> {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
// color: Colors.amber, // color: Colors.amber,
color: bodyColor, color: Color(0xFFE1F5FE),
// color: bodyColor,
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: buildGroupListLayout(isDesktop), 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), // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0),
decoration: BoxDecoration( decoration: BoxDecoration(
// color: Colors.amber, // color: Colors.amber,
color: bodyColor, // color: bodyColor,
color: Color(0xFFE1F5FE),
border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)),
child: Column( child: Column(
children: [ children: [

View File

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

View File

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

View File

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