diff --git a/lib/Screens/approvals/approval_dialogs.dart b/lib/Screens/approvals/approval_dialogs.dart index d39a3fc..785267e 100644 --- a/lib/Screens/approvals/approval_dialogs.dart +++ b/lib/Screens/approvals/approval_dialogs.dart @@ -67,14 +67,28 @@ Future showRejectDialog( style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 16), - const Text("Please enter remarks to reject the plan."), + const Text("Please enter reason for rejection."), const SizedBox(height: 10), TextField( maxLines: 3, onChanged: (value) => remarks = value, decoration: const InputDecoration( hintText: "Remarks...", - border: OutlineInputBorder(), + hintStyle: TextStyle( + fontSize: 12, // 👈 Set your desired font size here + color: Colors.grey, + fontFamily: + "Archivo", // optional if you want consistent font + ), + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.blueGrey, width: 0.5), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.blueGrey, width: 0.5), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.grey, width: 1), + ), ), ), ], diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 35e75e5..d6d6221 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:frontend/config/apiUrl.dart'; +import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -275,13 +276,23 @@ class _ApprovalListState extends State { return Container( decoration: BoxDecoration( // color: Colors.amber, - color: bodyColor, + // color: bodyColor, + color: Color(0xFFE1F5FE), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: buildTableLayout(isDesktop), ); } Widget buildTableLayout(isDesktop) { + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat('dd MMM yy : hh a').format(dateTime); + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + return Container( margin: isDesktop ? EdgeInsets.all(10.0) @@ -414,7 +425,9 @@ class _ApprovalListState extends State { double minWidth = isDesktop ? constraints.maxWidth : 1300; return ConstrainedBox( - constraints: BoxConstraints(minWidth: minWidth), + constraints: BoxConstraints( + minWidth: minWidth, + ), child: DataTable( dividerThickness: 0.5, columnSpacing: isDesktop ? 24.0 : 16.0, @@ -494,7 +507,10 @@ class _ApprovalListState extends State { fontSize: 13, fontFamily: "Archivo", ))), - DataCell(Text(plan.createdOn, + DataCell(Text( + + // plan.createdOn, + _formatDate(plan.createdOn), style: TextStyle( fontSize: 13, fontFamily: "Archivo", diff --git a/lib/Screens/authentication/login/login_widget.dart b/lib/Screens/authentication/login/login_widget.dart index 4a381cc..bbed66a 100644 --- a/lib/Screens/authentication/login/login_widget.dart +++ b/lib/Screens/authentication/login/login_widget.dart @@ -191,6 +191,10 @@ class _LoginWidgetState extends State { _buildLabel("Email Address"), TextFormField( controller: _emailController, + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.w600, + fontSize: 11), decoration: _inputDecoration("Enter your email address").copyWith( prefixIcon: Icon( Icons.email_outlined, @@ -207,6 +211,10 @@ class _LoginWidgetState extends State { _buildLabel("Password"), TextFormField( controller: _passwordController, + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.w600, + fontSize: 11), obscureText: _obscureText, decoration: _inputDecoration("Enter your password").copyWith( prefixIcon: Icon( diff --git a/lib/Screens/group/group.dart b/lib/Screens/group/group.dart index 9be348b..b25578f 100644 --- a/lib/Screens/group/group.dart +++ b/lib/Screens/group/group.dart @@ -78,14 +78,13 @@ class _groupState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { loadAllServices(); + + for (var field in dataHeader) { + controllers[field] = TextEditingController(); + } + updateData(); loadInitialData(); }); - - for (var field in dataHeader) { - controllers[field] = TextEditingController(); - } - - updateData(); } void loadInitialData() async { @@ -342,13 +341,15 @@ class _groupState extends State { // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), decoration: BoxDecoration( // color: Colors.amber, - color: bodyColor, + // color: bodyColor, + color: Color(0xFFE1F5FE), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: Column( children: [ Expanded( child: Container( - color: bodyColor, + // color: bodyColor, + color: Color(0xFFE1F5FE), child: buildOrganizationLayout(isDesktop), ), ), @@ -386,6 +387,7 @@ class _groupState extends State { ) : null, color: Colors.white, + // color: Color(0xFFF7F7FB), // color: Colors.amber, diff --git a/lib/Screens/group/groupList.dart b/lib/Screens/group/groupList.dart index 1a24c4e..eafe21a 100644 --- a/lib/Screens/group/groupList.dart +++ b/lib/Screens/group/groupList.dart @@ -148,7 +148,8 @@ class _GroupListState extends State { return Container( decoration: BoxDecoration( // color: Colors.amber, - color: bodyColor, + // color: bodyColor, + color: Color(0xFFE1F5FE), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: buildGroupData(isDesktop), ); diff --git a/lib/Screens/itnerary/accomodations.dart b/lib/Screens/itnerary/accomodations.dart index 460aa88..1449f3c 100644 --- a/lib/Screens/itnerary/accomodations.dart +++ b/lib/Screens/itnerary/accomodations.dart @@ -725,7 +725,7 @@ class _AccomodationScreenState extends State { handleSave(); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, // Primary color for save + backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), diff --git a/lib/Screens/itnerary/bus.dart b/lib/Screens/itnerary/bus.dart index 404a1d8..8149036 100644 --- a/lib/Screens/itnerary/bus.dart +++ b/lib/Screens/itnerary/bus.dart @@ -657,7 +657,7 @@ class _BusScreenState extends State { handleSave(); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, // Primary color for save + backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), diff --git a/lib/Screens/itnerary/flights.dart b/lib/Screens/itnerary/flights.dart index 029623e..d6ddf17 100644 --- a/lib/Screens/itnerary/flights.dart +++ b/lib/Screens/itnerary/flights.dart @@ -628,6 +628,7 @@ class _FlightScreenState extends State { child: SizedBox( height: 40, child: DropdownButtonFormField( + isExpanded: true, // focusNode: _tripTypeFocusNode, // Assign the correct focus node focusNode: focusNodes["_tripType1FocusNode"], value: selectedTripType, @@ -1292,7 +1293,7 @@ class _FlightScreenState extends State { handleSave(); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, // Primary color for save + backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), diff --git a/lib/Screens/itnerary/forex.dart b/lib/Screens/itnerary/forex.dart index bf57266..ca8b920 100644 --- a/lib/Screens/itnerary/forex.dart +++ b/lib/Screens/itnerary/forex.dart @@ -201,7 +201,7 @@ class _ForexScreenState extends State { "country_code", "deposit_on_card", "deposit_on_cash", - "card_number" + // "card_number" ]; // If have_card is "1", then delivery_location is required @@ -932,7 +932,7 @@ class _ForexScreenState extends State { height: 8, ), Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( "Currency *", @@ -946,19 +946,24 @@ class _ForexScreenState extends State { isFocused: focusStates["_currencyFocused"] ?? false, isDesktop: isDesktop, color: Colors.transparent, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.330 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: Padding( padding: const EdgeInsets.all(8.0), - child: Text( - // "cur", - // "${selectedCurrency}", - selectedCurrency ?? "Currency", - // selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency", - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + child: Center( + child: Text( + // "cur", + // "${selectedCurrency}", + selectedCurrency ?? "Currency", + // selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), ), ), ), @@ -1012,46 +1017,13 @@ class _ForexScreenState extends State { ), ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + // if (isDesktop) + // Spacer() + // else + // SizedBox( + // height: 8, + // ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Quoted Amount", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: focusStates["_perdiemAmount"] ?? false, - isDesktop: isDesktop, - color: Colors.transparent, - child: SizedBox( - height: 40, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - // "amo", - selectedQuotedAmount ?? "0", - // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - ), - ), - ), - ], - ), // if (isDesktop)Spacer() else SizedBox(height: 8,), ]; } @@ -1268,7 +1240,7 @@ class _ForexScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldForexWrapper( + CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_cash"] ?? false, isDesktop: isDesktop, child: SizedBox( @@ -1321,7 +1293,7 @@ class _ForexScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldForexWrapper( + CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_card"] ?? false, isDesktop: isDesktop, child: SizedBox( @@ -1355,6 +1327,47 @@ class _ForexScreenState extends State { ], ), + if (isDesktop) + Spacer() + else + SizedBox( + height: 8, + ), + + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Total Amount", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + isFocused: focusStates["_perdiemAmount"] ?? false, + isDesktop: isDesktop, + color: Colors.transparent, + child: SizedBox( + height: 40, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + // "amo", + selectedQuotedAmount ?? "0", + // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), + ), + ), + ), + ], + ), + if (isDesktop) Spacer() else @@ -1406,19 +1419,20 @@ class _ForexScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Card Number*", + "Card Number", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldForexWrapper( + CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_cardNumber"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( + enabled: !isChecked, focusNode: focusNodes["_cardNumber"], controller: textControllers["_cardNumber"], style: const TextStyle(fontSize: 12), @@ -1463,7 +1477,7 @@ class _ForexScreenState extends State { false, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.464 + ? MediaQuery.of(context).size.width * 0.330 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: focusNodes["_deliveryLocation"], @@ -1511,7 +1525,7 @@ class _ForexScreenState extends State { focusStates["_comments"] ?? false, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.464 + ? MediaQuery.of(context).size.width * 0.330 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: focusNodes["_comments"], @@ -1547,6 +1561,10 @@ class _ForexScreenState extends State { onChanged: (bool? value) { setState(() { isChecked = value!; + if (isChecked) { + textControllers["_cardNumber"] + ?.clear(); // Clear the value when isChecked is true + } }); }, ), @@ -1589,7 +1607,7 @@ class _ForexScreenState extends State { handleSave(); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, // Primary color for save + backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), diff --git a/lib/Screens/itnerary/insurance.dart b/lib/Screens/itnerary/insurance.dart index 9d0e7a1..5119c16 100644 --- a/lib/Screens/itnerary/insurance.dart +++ b/lib/Screens/itnerary/insurance.dart @@ -595,7 +595,7 @@ class _InsuranceScreenState extends State { handleSave(); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, // Primary color for save + backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), diff --git a/lib/Screens/itnerary/miscellaneous.dart b/lib/Screens/itnerary/miscellaneous.dart index c41beff..999f6da 100644 --- a/lib/Screens/itnerary/miscellaneous.dart +++ b/lib/Screens/itnerary/miscellaneous.dart @@ -379,7 +379,7 @@ class _MiscellaneousScreenState extends State { handleSave(); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, // Primary color for save + backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), diff --git a/lib/Screens/itnerary/taxi.dart b/lib/Screens/itnerary/taxi.dart index 66d3714..e7c5141 100644 --- a/lib/Screens/itnerary/taxi.dart +++ b/lib/Screens/itnerary/taxi.dart @@ -785,7 +785,7 @@ class _TaxiScreenState extends State { handleSave(); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, // Primary color for save + backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), diff --git a/lib/Screens/itnerary/train.dart b/lib/Screens/itnerary/train.dart index 5602e2d..d867922 100644 --- a/lib/Screens/itnerary/train.dart +++ b/lib/Screens/itnerary/train.dart @@ -748,7 +748,7 @@ class _TrainScreenState extends State { handleSave(); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, // Primary color for save + backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), diff --git a/lib/Screens/itnerary/visa.dart b/lib/Screens/itnerary/visa.dart index 14b8f62..29c9d5d 100644 --- a/lib/Screens/itnerary/visa.dart +++ b/lib/Screens/itnerary/visa.dart @@ -601,7 +601,7 @@ class _VisaScreenState extends State { handleSave(); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, // Primary color for save + backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), diff --git a/lib/Screens/itnerary_list/accomodation_list.dart b/lib/Screens/itnerary_list/accomodation_list.dart index 30abbee..3c856f3 100644 --- a/lib/Screens/itnerary_list/accomodation_list.dart +++ b/lib/Screens/itnerary_list/accomodation_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; class AccomodationListWidget extends StatelessWidget { final List> accommodationList; @@ -18,149 +19,386 @@ class AccomodationListWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final isDesktop = MediaQuery.of(context).size.width > 1024; + return Padding( padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Accomodation Booking List", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF114D8B), - foregroundColor: Colors.white, - disabledBackgroundColor: Color(0xFF114D8B), - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Color(0xFF114D8B), width: 2), + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Header with title and button + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Accomodation Booking List", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + MouseRegion( + cursor: isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: + EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: isViewMode + ? null + : () { + print("New data"); + onAddNew("Accomodation", true); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add New", + style: TextStyle(fontSize: 13), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - ), - onPressed: isViewMode - ? null - : () { - print("New data"); - onAddNew("Accomodation", true); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add New", - style: TextStyle(fontSize: 13), - ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, - ), - ], ), ), - ), - ], - ), - const SizedBox(height: 16), - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: DataTable( - border: TableBorder( - bottom: BorderSide(color: Colors.black12), - horizontalInside: BorderSide( - color: Colors.black12), // Only horizontal lines - ), - columns: const [ - DataColumn(label: Text('#')), - DataColumn(label: Text('Destination City')), - DataColumn(label: Text('Hotel Name')), - DataColumn(label: Text('CheckIn Date')), - DataColumn(label: Text('CheckOut Date')), - DataColumn(label: Text('Actions')), - ], - rows: _buildDataRows(), - ), + ], ), - ), - ], + const SizedBox(height: 16), + // Scroll behavior based on device + + _buildData(context, isDesktop) + ], + ), ), ); } - List _buildDataRows() { + Widget _buildData(BuildContext context, bool isDesktop) { List> filteredList = accommodationList.where((item) => item["is_active"] == "1").toList(); print("filteredList- $filteredList"); - return filteredList.asMap().entries.map((entry) { - final Map item = entry.value; + String formatDate(String dateString) { + try { + DateTime date = DateTime.parse(dateString); + return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25 + } catch (e) { + return "Invalid Date"; + } + } - return DataRow(cells: [ - DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column - DataCell(Text(item["destination_city"]!)), - DataCell(Text(item["hotel_name"]!)), - DataCell(Text(item["checkin_date"]!)), - DataCell(Text(item["checkout_date"]!)), - DataCell(Row( - children: [ - GestureDetector( - onTap: () => onOpen(true, item, "Accomodation"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), - ), - SizedBox(width: 10), - GestureDetector( - onTap: () => onDeleteAccommodation(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), - ), - IconButton( - icon: Icon(Icons.keyboard_arrow_down_outlined, - size: 28, color: Color(0xFF475569)), - onPressed: () { - // Expand logic - }, - ), - ], - )) + String formatTime(String timeString) { + try { + final parts = timeString.split(':'); + final now = DateTime.now(); + final dateTime = DateTime( + now.year, + now.month, + now.day, + int.parse(parts[0]), + int.parse(parts[1]), + ); + return DateFormat.jm().format(dateTime); // e.g., 12:16 PM or 12 AM + } catch (e) { + return "Invalid Time"; + } + } - // Row( - // children: [ - // IconButton( - // icon: Icon(Icons.remove_red_eye, color: Colors.blue), - // onPressed: () { - // // View action - // }, - // ), - // IconButton( - // icon: Icon(Icons.edit, color: Colors.green), - // onPressed: () { - // onOpen(true, item, "Accomodation"); - // }, - // ), - // IconButton( - // icon: Icon(Icons.delete, color: Colors.red), - // onPressed: () { - // onDeleteAccommodation(item); - // }, - // ), - // ], - // )), - // - ]); - }).toList(); + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: filteredList.length, + itemBuilder: (context, index) { + final item = filteredList[index]; + + return Container( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + // color: Colors.orange.shade50, + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 3, + offset: Offset(0, 1), + ), + ], + border: Border( + top: BorderSide( + color: Colors.white70, // Change color to match your theme + width: 2, + ), + ), + ), + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + item["hotel_name"]!, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w800, + fontFamily: "Archivo"), + ), + Spacer(), + GestureDetector( + onTap: () => onOpen(true, item, "Accomodation"), + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox(width: 10), + GestureDetector( + onTap: () => onDeleteAccommodation(item), + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), + ), + ], + ), + Divider( + color: Colors.blueGrey.shade50, + ), + SizedBox(height: 4), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + " DestinationCity", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Check (in) ", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Check (out) ", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Comments", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + ], + ) + : SizedBox.shrink(), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text(item["destination_city"], + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + "${formatDate(item["checkin_date"]!)} ${formatTime(item["checkin_time"]!)}", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + "${formatDate(item["checkout_date"]!)} ${formatTime(item["checkout_time"]!)}", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: _buildComments( + "Comments:", item["comments"] ?? "N/A"), + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildRow("City:", item["destination_city"]), + SizedBox(width: 10), + SizedBox(width: 10), + _buildDateTimeRow( + "Check-in:", + formatDate(item["checkin_date"]!), + item["checkin_time"]!, + ), + SizedBox(width: 10), + _buildRow( + "Date(out):", formatDate(item["checkout_date"]!)), + SizedBox(width: 10), + _buildRow("Comments:", item["comments"] ?? "N/A"), + ], + ) + ], + ), + ), + ); + }, + ); + } + + Widget _buildComments(String title, String value) { + // Define character limits based on title + Map 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 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), + ), + ), + ], + ); } } diff --git a/lib/Screens/itnerary_list/bus_list.dart b/lib/Screens/itnerary_list/bus_list.dart index 38a4da4..ac2063d 100644 --- a/lib/Screens/itnerary_list/bus_list.dart +++ b/lib/Screens/itnerary_list/bus_list.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -25,161 +26,359 @@ class BusListWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final isDesktop = MediaQuery.of(context).size.width > 1024; + return Padding( padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Bus Booking List", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF114D8B), - foregroundColor: Colors.white, - disabledBackgroundColor: Color(0xFF114D8B), - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Color(0xFF114D8B), width: 2), + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Header with title and button + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Bus Booking List", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + MouseRegion( + cursor: isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: + EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: isViewMode + ? null + : () { + print("New data"); + onAddNew("Bus", true); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add New", + style: TextStyle(fontSize: 13), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - ), - onPressed: isViewMode - ? null - : () { - print("New data"); - onAddNew("Bus", true); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add New", - style: TextStyle(fontSize: 13), - ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, - ), - ], ), ), - ), - ], - ), - const SizedBox(height: 16), - Container( - // color: Colors.blueGrey, - width: double.infinity, - child: Center( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - // child: Expanded( - - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: DataTable( - border: TableBorder( - bottom: BorderSide(color: Colors.black12), - horizontalInside: BorderSide( - color: Colors.black12), // Only horizontal lines - ), - columns: const [ - // DataColumn(label: Text('#')), - DataColumn(label: Text('From')), - DataColumn(label: Text('To')), - DataColumn(label: Text('Date')), - DataColumn(label: Text('Time')), - - DataColumn(label: Text('Actions')), - ], - rows: _buildDataRows(), - ), - ), - - // ) - ), + ], ), - ), - ], + const SizedBox(height: 16), + // Scroll behavior based on device + + _buildData(context, isDesktop) + ], + ), ), ); } - List _buildDataRows() { + Widget _buildData(BuildContext context, bool isDesktop) { List> filteredList = busList.where((item) => item["is_active"] == "1").toList(); print("filteredList- $filteredList"); - return filteredList.asMap().entries.map((entry) { - final Map item = entry.value; + String formatDate(String dateString) { + try { + DateTime date = DateTime.parse(dateString); + return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25 + } catch (e) { + return "Invalid Date"; + } + } - return DataRow(cells: [ - // DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column - DataCell(Text(item["from"]!)), - DataCell(Text(item["to"]!)), - DataCell(Text(item["date"]!)), - DataCell(Text(item["time"]!)), - DataCell(Row( - children: [ - GestureDetector( - onTap: () => onOpen(true, item, "Bus"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), - ), - SizedBox(width: 10), - GestureDetector( - onTap: () => onDeleteBus(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), - ), - IconButton( - icon: Icon(Icons.keyboard_arrow_down_outlined, - size: 28, color: Color(0xFF475569)), - onPressed: () { - // Expand logic - }, - ), - ], - ) + String formatTime(String timeString) { + try { + final parts = timeString.split(':'); + final now = DateTime.now(); + final dateTime = DateTime( + now.year, + now.month, + now.day, + int.parse(parts[0]), + int.parse(parts[1]), + ); + return DateFormat.jm().format(dateTime); // e.g., 12:16 PM or 12 AM + } catch (e) { + return "Invalid Time"; + } + } - // Row( - // children: [ - // IconButton( - // icon: Icon(Icons.remove_red_eye, color: Colors.blue), - // onPressed: () { - // // View action - // }, - // ), - // IconButton( - // icon: Icon(Icons.edit, color: Colors.green), - // onPressed: () { - // onOpen(true, item, "Bus"); - // }, - // ), - // IconButton( - // icon: Icon(Icons.delete, color: Colors.red), - // onPressed: () { - // onDeleteBus(item); - // }, - // ), - // ], - // ) + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: filteredList.length, + itemBuilder: (context, index) { + final item = filteredList[index]; + return Container( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + // color: Colors.orange.shade50, + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 3, + offset: Offset(0, 1), + ), + ], + border: Border( + top: BorderSide( + color: Colors.white70, // Change color to match your theme + width: 2, + ), ), - ]); - }).toList(); + ), + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () => onOpen(true, item, "Bus"), + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox(width: 10), + GestureDetector( + onTap: () => onDeleteBus(item), + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), + ), + ], + ), + Divider( + color: Colors.blueGrey.shade50, + ), + SizedBox(height: 4), + isDesktop + ? Row( + children: [ + Expanded( + flex: 3, + child: Text( + " Planned Trips", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 3, + child: Text( + " Date", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 3, + child: Text( + " Comments", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + ], + ) + : SizedBox.shrink(), + SizedBox(height: 4), + isDesktop + ? Row( + children: [ + Expanded( + flex: 3, + child: Text("${item["from"]} - ${item["to"]} ", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + SizedBox(width: 10), + Expanded( + flex: 3, + child: Text( + "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + SizedBox(width: 10), + Expanded( + flex: 3, + child: _buildComments( + "Comments:", item["comments"] ?? "N/A"), + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildRow("From:", item["from"]), + SizedBox(width: 10), + _buildRow("To:", item["to"]!), + SizedBox(width: 10), + _buildDateTimeRow( + "Date:", + formatDate(item["date"]!), + formatTime(item["time"]!), + ), + _buildRow("Comments:", item["comments"] ?? "N/A"), + ], + ) + ], + ), + ), + ); + }, + ); + } + + Widget _buildComments(String title, String value) { + // Define character limits based on title + Map 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 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), + ), + ), + ], + ); } } diff --git a/lib/Screens/itnerary_list/flight_list.dart b/lib/Screens/itnerary_list/flight_list.dart index adeb9f2..7fac9c3 100644 --- a/lib/Screens/itnerary_list/flight_list.dart +++ b/lib/Screens/itnerary_list/flight_list.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; class FlightListWidget extends StatelessWidget { final List> flightList; final Function(bool, Map, String) onOpen; final Function(Map) onDeleteFlight; + final Map? apiData; final Function(String, bool) onAddNew; final bool isViewMode; @@ -14,161 +16,324 @@ class FlightListWidget extends StatelessWidget { required this.onOpen, required this.onDeleteFlight, required this.onAddNew, + required this.apiData, required this.isViewMode}); @override Widget build(BuildContext context) { + final isDesktop = MediaQuery.of(context).size.width > 1024; + return Padding( padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Flight Booking List", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF114D8B), - foregroundColor: Colors.white, - disabledBackgroundColor: Color(0xFF114D8B), - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Color(0xFF114D8B), width: 2), + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Header with title and button + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Flight Booking List", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + MouseRegion( + cursor: isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: + EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: isViewMode + ? null + : () { + print("New data"); + onAddNew("Flight", true); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add New", + style: TextStyle(fontSize: 13), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - ), - onPressed: isViewMode - ? null - : () { - print("New data"); - onAddNew("Flight", true); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add New", - style: TextStyle(fontSize: 13), - ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, - ), - ], ), ), + ], + ), + const SizedBox(height: 16), + // Scroll behavior based on device + + _buildData(context, isDesktop) + ], + ), + ), + ); + } + + Widget _buildData(BuildContext context, bool isDesktop) { + List> filteredList = + flightList.where((item) => item["is_active"] == "1").toList(); + print("filteredList- $filteredList"); + + List visatypeList = apiData?['flight_class'] ?? []; + + String getRequestForClass(String? specialRequestKey) { + if (specialRequestKey == null) return "N/A"; + + return visatypeList + .firstWhere( + (element) => + element["dropdown_key"].toString() == specialRequestKey, + orElse: () => {"dropdown_value": "N/A"}, + )["dropdown_value"] + .toString(); + } + + String formatDate(String dateString) { + try { + DateTime date = DateTime.parse(dateString); + return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25 + } catch (e) { + return "Invalid Date"; + } + } + + String formatTime(String timeString) { + try { + final parts = timeString.split(':'); + final now = DateTime.now(); + final dateTime = DateTime( + now.year, + now.month, + now.day, + int.parse(parts[0]), + int.parse(parts[1]), + ); + return DateFormat.jm().format(dateTime); // e.g., 12:16 PM or 12 AM + } catch (e) { + return "Invalid Time"; + } + } + + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: filteredList.length, + itemBuilder: (context, index) { + final item = filteredList[index]; + + return Container( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + // color: Colors.orange.shade50, + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 3, + offset: Offset(0, 1), ), ], - ), - const SizedBox(height: 16), - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SizedBox( - // width: 1000, - width: MediaQuery.of(context).size.width, - child: DataTable( - border: TableBorder( - bottom: BorderSide(color: Colors.black12), - horizontalInside: BorderSide( - color: Colors.black12), // Only horizontal lines - ), - columns: const [ - // DataColumn(label: Text('#')), - DataColumn(label: Text('Trip Type')), - DataColumn(label: Text('From')), - DataColumn(label: Text('To')), - DataColumn(label: Text('Actions')), - ], - rows: _buildDataRows(), + border: Border( + top: BorderSide( + color: Colors.white70, // Change color to match your theme + width: 2, ), ), ), + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + item["trip_type"]?.toString() ?? "N/A", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w800, + fontFamily: "Archivo"), + ), + Spacer(), + GestureDetector( + onTap: () => onOpen(true, item, "Flight"), + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox(width: 10), + GestureDetector( + onTap: () => onDeleteFlight(item), + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), + ), + ], + ), + Divider( + color: Colors.blueGrey.shade50, + ), + SizedBox(height: 4), + Row( + children: [ + Expanded( + flex: 2, + child: Text( + "Class", + style: TextStyle(fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + "Planned Trips", + style: TextStyle(fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + "Date", + style: TextStyle(fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + "Time", + style: TextStyle(fontSize: 11, fontFamily: "Archivo"), + )), + ], + ), + SizedBox(height: 4), + // Trip Rows + + if (isDesktop) + if (item["trips"] != null && item["trips"].isNotEmpty) + ...item["trips"].map((trip) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + children: [ + Expanded( + flex: 2, + child: Text( + getRequestForClass(trip["class"].toString()) ?? + "N/A", + style: TextStyle( + fontSize: 12, fontWeight: FontWeight.bold), + ), + ), + Expanded( + flex: 2, + child: Text( + "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + ), + ), + Expanded( + flex: 2, + child: Text( + formatDate(trip["date"] ?? ""), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + fontFamily: "Archivo"), + ), + ), + Expanded( + flex: 2, + child: Text(formatTime(trip["time"] ?? ""), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + fontFamily: "Archivo")), + ), + ], + ), + ); + }).toList(), + + if (!isDesktop && + item["trips"] != null && + item["trips"].isNotEmpty) + ...item["trips"].map((trip) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 6.0), + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(6), + color: Colors.grey.shade50, + ), + padding: const EdgeInsets.all(8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildKeyValueRow( + "Class", + getRequestForClass(trip["class"].toString()) ?? + "N/A"), + _buildKeyValueRow("From - To", + "${trip["from_place"] ?? "N/A"} - ${trip["to_place"] ?? "N/A"}"), + _buildKeyValueRow( + "Date", formatDate(trip["date"] ?? "")), + _buildKeyValueRow( + "Time", formatTime(trip["time"] ?? "")), + ], + ), + ), + ); + }).toList(), + ], + ), + ), + ); + }, + ); + } + + Widget _buildKeyValueRow(String key, String value) { + return Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 80, // adjust width as needed + child: Text( + "$key:", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12), + ), + ), + Expanded( + child: Text( + value, + style: TextStyle(fontSize: 12), + ), + ), ], ), ); } - - List _buildDataRows() { - List> filteredList = - flightList.where((item) => item["is_active"] == "1").toList(); - print("filteredList- $filteredList"); - - return filteredList.asMap().entries.map((entry) { - Map item = entry.value; - print("Trip Type: ${item["trip_type"]}"); - - return DataRow(cells: [ - // DataCell(Text(item["indx"]?.toString() ?? "N/A")), - DataCell(Text(item["trip_type"]?.toString() ?? "N/A")), - DataCell(Text(item["trips"].isNotEmpty - ? item["trips"][0]["from_place"]?.toString() ?? "N/A" - : "N/A")), - DataCell(Text(item["trips"].isNotEmpty - ? item["trips"][0]["to_place"]?.toString() ?? "N/A" - : "N/A")), - - DataCell( - Row( - children: [ - GestureDetector( - onTap: () => onOpen(true, item, "Flight"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), - ), - SizedBox(width: 10), - GestureDetector( - onTap: () => onDeleteFlight(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), - ), - IconButton( - icon: Icon(Icons.keyboard_arrow_down_outlined, - size: 28, color: Color(0xFF475569)), - onPressed: () { - // Expand logic - }, - ), - ], - ), - - // - // Row( - // children: [ - // IconButton( - // icon: Icon(Icons.remove_red_eye, color: Colors.blue), - // onPressed: () { - // // View action - // }, - // ), - // IconButton( - // icon: Icon(Icons.edit, color: Colors.green), - // onPressed: () { - // onOpen(true, item, "Flight"); - // }, - // ), - // IconButton( - // icon: Icon(Icons.delete, color: Colors.red), - // onPressed: () { - // onDeleteFlight(item); - // }, - // ), - // ], - // ) - // - ), - ]); - }).toList(); - } } diff --git a/lib/Screens/itnerary_list/forex_list.dart b/lib/Screens/itnerary_list/forex_list.dart index 56fd1df..789889f 100644 --- a/lib/Screens/itnerary_list/forex_list.dart +++ b/lib/Screens/itnerary_list/forex_list.dart @@ -1,12 +1,14 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; class ForexListWidget extends StatelessWidget { final List> forexList; final Function(bool, Map, String) onOpen; final Function(Map) onDeleteForex; final List? apiCountryData; + final Map? apiData; final Function(String, bool) onAddNew; final bool isViewMode; @@ -18,107 +20,100 @@ class ForexListWidget extends StatelessWidget { required this.onDeleteForex, required this.apiCountryData, required this.onAddNew, + required this.apiData, required this.isViewMode}); @override Widget build(BuildContext context) { + final isDesktop = MediaQuery.of(context).size.width > 1024; + return Padding( padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Forex List", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF114D8B), - foregroundColor: Colors.white, - disabledBackgroundColor: Color(0xFF114D8B), - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Color(0xFF114D8B), width: 2), + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Header with title and button + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Forex List", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + MouseRegion( + cursor: isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: + EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: isViewMode + ? null + : () { + print("New data"); + onAddNew("Forex", true); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add New", + style: TextStyle(fontSize: 13), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - ), - onPressed: isViewMode - ? null - : () { - print("New data"); - onAddNew("Forex", true); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add New", - style: TextStyle(fontSize: 13), - ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, - ), - ], ), ), - ), - ], - ), - const SizedBox(height: 16), - Container( - // color: Colors.blueGrey, - width: double.infinity, - child: Center( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: DataTable( - border: TableBorder( - bottom: BorderSide(color: Colors.black12), - horizontalInside: BorderSide( - color: Colors.black12), // Only horizontal lines - ), - columns: const [ - // DataColumn(label: Text('#')), - DataColumn(label: Text('Forex Start Date')), - DataColumn(label: Text('Forex End Date')), - DataColumn(label: Text('Country')), - DataColumn(label: Text('Perdiem Amount')), - DataColumn(label: Text('Actions')), - ], - rows: _buildDataRows(), - ), - ), - // - ), + ], ), - ), - ], + const SizedBox(height: 16), + // Scroll behavior based on device + + _buildData(context, isDesktop) + ], + ), ), ); } - List _buildDataRows() { - List countryList = apiCountryData ?? []; - + Widget _buildData(BuildContext context, bool isDesktop) { List> filteredList = forexList.where((item) => item["is_active"] == "1").toList(); print("filteredList- $filteredList"); + List visatypeList = apiData?['visa_type_of_visa'] ?? []; + List countryList = apiCountryData ?? []; + + String getRequestForVisa(String? specialRequestKey) { + if (specialRequestKey == null) return "N/A"; + + return visatypeList + .firstWhere( + (element) => + element["dropdown_key"].toString() == specialRequestKey, + orElse: () => {"dropdown_value": "N/A"}, + )["dropdown_value"] + .toString(); + } + String getRequestForCountry(String? countryCode) { if (countryCode == null) return "N/A"; @@ -130,64 +125,296 @@ class ForexListWidget extends StatelessWidget { .toString(); } - return filteredList.asMap().entries.map((entry) { - Map item = entry.value; + String formatDate(String dateString) { + try { + DateTime date = DateTime.parse(dateString); + return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25 + } catch (e) { + return "Invalid Date"; + } + } - return DataRow(cells: [ - // DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column - DataCell(Text(item["start_date"] ?? "N/A")), - DataCell(Text(item["end_date"] ?? "N/A")), - DataCell(Text(getRequestForCountry(item["country_code"]!.toString()))), - // DataCell(Text(item["country_code"] ?? "N/A")), - DataCell(Text(item["perdiem_amount"] ?? "N/A")), - DataCell(Row( - children: [ - GestureDetector( - onTap: () => onOpen(true, item, "Forex"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), - ), - SizedBox(width: 10), - GestureDetector( - onTap: () => onDeleteForex(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), - ), - IconButton( - icon: Icon(Icons.keyboard_arrow_down_outlined, - size: 28, color: Color(0xFF475569)), - onPressed: () { - // Expand logic - }, - ), - ], - ) - - // Row( - // children: [ - // IconButton( - // icon: Icon(Icons.remove_red_eye, color: Colors.blue), - // onPressed: () { - // // View action - // }, - // ), - // IconButton( - // icon: Icon(Icons.edit, color: Colors.green), - // onPressed: () { - // onOpen(true, item, "Forex"); - // }, - // ), - // IconButton( - // icon: Icon(Icons.delete, color: Colors.red), - // onPressed: () { - // onDeleteForex(item); - // }, - // ), - // ], - // ) + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: filteredList.length, + itemBuilder: (context, index) { + final item = filteredList[index]; + return Container( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + // color: Colors.orange.shade50, + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 3, + offset: Offset(0, 1), + ), + ], + border: Border( + top: BorderSide( + color: Colors.white70, // Change color to match your theme + width: 2, + ), ), - ]); - }).toList(); + ), + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + getRequestForCountry(item["country_code"]!.toString()), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + ), + Spacer(), + GestureDetector( + onTap: () => onOpen(true, item, "Forex"), + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox(width: 10), + GestureDetector( + onTap: () => onDeleteForex(item), + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), + ), + ], + ), + Divider( + color: Colors.blueGrey.shade50, + ), + SizedBox(height: 4), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + " Currency", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Duration", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Time Period ", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + // Expanded( + // flex: 2, + // child: Text( + // " Total ", + // style: TextStyle( + // fontSize: 11, fontFamily: "Archivo"), + // )), + Expanded( + flex: 2, + child: Text( + " Comments", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + ], + ) + : SizedBox.shrink(), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + item["currency"], + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + ), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + item["duration"], + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + ), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + " ${formatDate(item["start_date"]!)} ${formatDate(item["end_date"]!)}", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + ), + ), + SizedBox(width: 10), + // Expanded( + // flex: 2, + // child: Text( + // " ", + // style: TextStyle( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // fontFamily: "Archivo"), + // ), + // ), + // SizedBox(width: 10), + Expanded( + flex: 2, + child: _buildComments( + "Comments:", item["comments"] ?? "N/A"), + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildRow( + "Currency:", + item["currency"], + ), + // SizedBox(height: 6), + _buildRow( + "Duration:", + item["duration"], + ), + // SizedBox(height: 6), + _buildRow("StartDate:", item["start_date"]!), + _buildRow("EndDate:", item["end_date"]!), + // SizedBox(height: 6), + _buildRow("Comments:", item["comments"] ?? "N/A"), + ], + ), + ], + ), + ), + ); + }, + ); + } + + Widget _buildComments(String title, String value) { + // Define character limits based on title + Map 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 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), + ), + ), + ], + ); } } diff --git a/lib/Screens/itnerary_list/insurance_list.dart b/lib/Screens/itnerary_list/insurance_list.dart index 51a8f7c..23eaa4b 100644 --- a/lib/Screens/itnerary_list/insurance_list.dart +++ b/lib/Screens/itnerary_list/insurance_list.dart @@ -1,6 +1,7 @@ import 'dart:js_interop'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; class InsuranceListWidget extends StatelessWidget { final List> insuranceList; @@ -22,12 +23,15 @@ class InsuranceListWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final isDesktop = MediaQuery.of(context).size.width > 1024; + return Padding( padding: const EdgeInsets.all(16.0), child: Container( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + // Header with title and button Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -79,35 +83,290 @@ class InsuranceListWidget extends StatelessWidget { ], ), const SizedBox(height: 16), - Center( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: DataTable( - border: TableBorder( - bottom: BorderSide(color: Colors.black12), - horizontalInside: BorderSide( - color: Colors.black12), // Only horizontal lines - ), - columns: const [ - // DataColumn(label: Text('#')), - DataColumn(label: Text('Insurance Type')), - DataColumn(label: Text('Start Date')), - DataColumn(label: Text('End Date')), - DataColumn(label: Text('Actions')), - ], - rows: _buildDataRows(), - ), - ), - ), - ), + // Scroll behavior based on device + + _buildData(context, isDesktop) ], ), ), ); } + Widget _buildData(BuildContext context, bool isDesktop) { + List> filteredList = + insuranceList.where((item) => item["is_active"] == "1").toList(); + print("filteredList- $filteredList"); + + List 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 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 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 _buildDataRows() { List> filteredList = insuranceList.where((item) => item["is_active"] == "1").toList(); diff --git a/lib/Screens/itnerary_list/miscellaneous_list.dart b/lib/Screens/itnerary_list/miscellaneous_list.dart index 959b4d3..951f068 100644 --- a/lib/Screens/itnerary_list/miscellaneous_list.dart +++ b/lib/Screens/itnerary_list/miscellaneous_list.dart @@ -19,18 +19,21 @@ class MiscellaneousListWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final isDesktop = MediaQuery.of(context).size.width > 1024; + return Padding( padding: const EdgeInsets.all(16.0), child: Container( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + // Header with title and button Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "Miscellaneous Booking List", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + "Miscellaneous List", + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ), MouseRegion( cursor: isViewMode @@ -52,23 +55,15 @@ class MiscellaneousListWidget extends StatelessWidget { onPressed: isViewMode ? null : () { - print("New data"); onAddNew("Miscellaneous", true); }, child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely + mainAxisSize: MainAxisSize.min, children: [ - Text( - "Add New", - style: TextStyle(fontSize: 13), - ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, - ), + Text("Add New", style: TextStyle(fontSize: 13)), + SizedBox(width: 8), + Icon(Icons.add_circle_outline_rounded, + size: 15, color: Colors.white), ], ), ), @@ -76,118 +71,305 @@ class MiscellaneousListWidget extends StatelessWidget { ], ), const SizedBox(height: 16), - Center( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: DataTable( - border: TableBorder( - bottom: BorderSide(color: Colors.black12), - horizontalInside: BorderSide( - color: Colors.black12), // Only horizontal lines - ), - columns: const [ - // DataColumn(label: Text('#')), - DataColumn(label: Text('Special Request')), - DataColumn(label: Text('Comments')), - // DataColumn(label: Text('Created On')), - DataColumn(label: Text('Actions')), - ], - rows: _buildDataRows(), - ), - ), - ), - ), + // Scroll behavior based on device + _buildData(context, isDesktop) ], ), ), ); } - List _buildDataRows() { - print("miscellaneousList - $miscellaneousList"); - List purposeList = apiData?['miscellaneous_special_request'] ?? []; - - print("purposeList - $purposeList"); - - String getRequestValue(String? specialRequestKey) { - if (specialRequestKey == null) return "N/A"; - - return purposeList - .firstWhere( - (element) => - element["dropdown_key"].toString() == specialRequestKey, - orElse: () => {"dropdown_value": "N/A"}, - )["dropdown_value"] - .toString(); - } - + Widget _buildData(BuildContext context, bool isDesktop) { List> filteredList = miscellaneousList.where((item) => item["is_active"] == "1").toList(); - print("filteredList- $filteredList"); - return filteredList.asMap().entries.map((entry) { - int index = entry.key + 1; // To start index from 1 - Map item = entry.value; - print(item); + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: filteredList.length, + itemBuilder: (context, index) { + final item = filteredList[index]; - return DataRow(cells: [ - // DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column - // DataCell(Text(specialRequestValue)), - DataCell(Text(getRequestValue(item["special_request"]?.toString()))), - DataCell(Text(item["comments"] ?? "N/A")), - // DataCell(Text(item["created_on"] ?? "N/A")), - DataCell(Row( - children: [ - GestureDetector( - onTap: () => onOpen(true, item, "Miscellaneous"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), - ), - SizedBox(width: 10), - GestureDetector( - onTap: () => onOpen(true, item, "Miscellaneous"), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), - ), - IconButton( - icon: Icon(Icons.keyboard_arrow_down_outlined, - size: 28, color: Color(0xFF475569)), - onPressed: () { - // Expand logic - }, - ), - ], - ) - - // Row( - // children: [ - // IconButton( - // icon: Icon(Icons.remove_red_eye, color: Colors.blue), - // onPressed: () { - // // View action - // }, - // ), - // IconButton( - // icon: Icon(Icons.edit, color: Colors.green), - // onPressed: () { - // onOpen(true, item, "Miscellaneous"); - // // Edit action - // }, - // ), - // IconButton( - // icon: Icon(Icons.delete, color: Colors.red), - // onPressed: () { - // onDeleteMiscellaneous(item); - // // Delete action - // }, - // ), - // ], - // ) + String getRequestValue(String? specialRequestKey) { + if (specialRequestKey == null) return "N/A"; + return (apiData?['miscellaneous_special_request'] ?? []) + .firstWhere( + (element) => + element["dropdown_key"].toString() == specialRequestKey, + orElse: () => {"dropdown_value": "N/A"}, + )["dropdown_value"] + .toString(); + } + return Container( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + // color: Colors.yellow.shade50, + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 3, + offset: Offset(0, 1), + ), + ], + border: Border( + top: BorderSide( + color: Colors.white70, // Change color to match your theme + width: 2, + ), ), - ]); - }).toList(); + ), + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () => onOpen(true, item, "Miscellaneous"), + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox(width: 10), + GestureDetector( + onTap: () => onOpen(true, item, "Miscellaneous"), + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), + ), + ], + ), + Divider( + color: Colors.blueGrey.shade50, + ), + SizedBox(height: 4), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + "Special Request", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 3, + child: Text( + " Comments", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + ], + ) + : SizedBox.shrink(), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + getRequestValue( + item["special_request"]?.toString()), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + ), + ), + Expanded( + flex: 3, + child: Text(item["comments"], + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + ], + ) + : Column( + children: [ + _buildRow( + "Special Request:", + getRequestValue( + item["special_request"]?.toString())), + SizedBox(width: 10), + _buildRow("Comments:", item["comments"] ?? "N/A"), + ], + ), + ], + ), + ), + ); + }, + ); } + + Widget _buildComments(String title, String value) { + // Define character limits based on title + Map 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 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 _buildDataRows() { + // List 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> filteredList = + // miscellaneousList.where((item) => item["is_active"] == "1").toList(); + // + // return filteredList.asMap().entries.map((entry) { + // Map item = entry.value; + // + // return DataRow(cells: [ + // DataCell(Text(getRequestValue(item["special_request"]?.toString()))), + // DataCell(Text(item["comments"] ?? "N/A")), + // DataCell(Row( + // children: [ + // GestureDetector( + // onTap: () => onOpen(true, item, "Miscellaneous"), + // child: Image.asset('assets/images/IconsImg/edit.png', + // width: 20, height: 15), + // ), + // SizedBox(width: 10), + // GestureDetector( + // onTap: () => onOpen(true, item, "Miscellaneous"), + // child: Image.asset('assets/images/IconsImg/delete.png', + // width: 20, height: 15), + // ), + // IconButton( + // icon: Icon(Icons.keyboard_arrow_down_outlined, + // size: 28, color: Color(0xFF475569)), + // onPressed: () { + // // Expand logic (optional) + // }, + // ), + // ], + // )), + // ]); + // }).toList(); + // } } diff --git a/lib/Screens/itnerary_list/taxi_list.dart b/lib/Screens/itnerary_list/taxi_list.dart index 3129b9e..3d9c5c4 100644 --- a/lib/Screens/itnerary_list/taxi_list.dart +++ b/lib/Screens/itnerary_list/taxi_list.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; class TaxiListWidget extends StatelessWidget { final List> taxiList; final Function(bool, Map, String) onOpen; final Function(Map) onDeleteTaxi; + final Map? apiData; final Function(String, bool) onAddNew; final bool isViewMode; @@ -11,6 +13,7 @@ class TaxiListWidget extends StatelessWidget { const TaxiListWidget( {super.key, required this.taxiList, + required this.apiData, required this.onOpen, required this.onDeleteTaxi, required this.onAddNew, @@ -18,153 +21,411 @@ class TaxiListWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final isDesktop = MediaQuery.of(context).size.width > 1024; + return Padding( padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Taxi Booking List", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF114D8B), - foregroundColor: Colors.white, - disabledBackgroundColor: Color(0xFF114D8B), - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Color(0xFF114D8B), width: 2), + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Header with title and button + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Taxi Booking List", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + MouseRegion( + cursor: isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), + foregroundColor: Colors.white, + disabledBackgroundColor: Color(0xFF114D8B), + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), + ), + padding: + EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: isViewMode + ? null + : () { + print("New data"); + onAddNew("Taxi", true); + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add New", + style: TextStyle(fontSize: 13), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - ), - onPressed: isViewMode - ? null - : () { - print("New data"); - onAddNew("Taxi", true); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - Text( - "Add New", - style: TextStyle(fontSize: 13), - ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, - ), - ], ), ), - ), - ], - ), - const SizedBox(height: 16), - Center( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: DataTable( - border: TableBorder( - bottom: BorderSide(color: Colors.black12), - horizontalInside: BorderSide( - color: Colors.black12), // Only horizontal lines - ), - columns: const [ - // DataColumn(label: Text('#')), - DataColumn(label: Text('Destination')), - DataColumn(label: Text('Location Of Pickup')), - DataColumn(label: Text('Date')), - DataColumn(label: Text('Taxi Required For')), - DataColumn(label: Text('Actions')), - ], - rows: _buildDataRows(), - ), - ), + ], ), - ), - ], + const SizedBox(height: 16), + // Scroll behavior based on device + + _buildData(context, isDesktop) + ], + ), ), ); } - List _buildDataRows() { + Widget _buildData(BuildContext context, bool isDesktop) { List> filteredList = taxiList.where((item) => item["is_active"] == "1").toList(); print("filteredList- $filteredList"); - return filteredList.asMap().entries.map((entry) { - final Map item = entry.value; + List taxiClassList = apiData?['taxt_car_type'] ?? []; + List taxirequiredFor = apiData?['taxi_car_required_for'] ?? []; - return DataRow(cells: [ - // DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column - DataCell(Text(item["destination_city"]!)), - DataCell(Text(item["location_of_pickup"]!)), - DataCell(Text(item["date"]!)), - DataCell(Text(item["car_required_for"]!)), - DataCell(Row( - children: [ - GestureDetector( - onTap: () => onOpen(true, item, "Taxi"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), - ), - SizedBox(width: 10), - GestureDetector( - onTap: () => onDeleteTaxi(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), - ), - IconButton( - icon: Icon(Icons.keyboard_arrow_down_outlined, - size: 28, color: Color(0xFF475569)), - onPressed: () { - // Expand logic - }, - ), - ], - ) + String getRequestForTaxiClass(String? specialRequestKey) { + if (specialRequestKey == null) return "N/A"; - // Row( - // children: [ - // IconButton( - // icon: Icon(Icons.remove_red_eye, color: Colors.blue), - // onPressed: () { - // // View action - // }, - // ), - // IconButton( - // icon: Icon(Icons.edit, color: Colors.green), - // onPressed: () { - // onOpen(true, item, "Taxi"); - // }, - // ), - // IconButton( - // icon: Icon(Icons.delete, color: Colors.red), - // onPressed: () { - // onDeleteTaxi(item); - // }, - // ), - // ], - // ) - // + return taxiClassList + .firstWhere( + (element) => + element["dropdown_key"].toString() == specialRequestKey, + orElse: () => {"dropdown_value": "N/A"}, + )["dropdown_value"] + .toString(); + } + String getRequestFortaxirequiredFor(String? specialRequestKey) { + if (specialRequestKey == null) return "N/A"; + + return taxirequiredFor + .firstWhere( + (element) => + element["dropdown_key"].toString() == specialRequestKey, + orElse: () => {"dropdown_value": "N/A"}, + )["dropdown_value"] + .toString(); + } + + String formatDate(String dateString) { + try { + DateTime date = DateTime.parse(dateString); + return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25 + } catch (e) { + return "Invalid Date"; + } + } + + String formatTime(String timeString) { + try { + final parts = timeString.split(':'); + final now = DateTime.now(); + final dateTime = DateTime( + now.year, + now.month, + now.day, + int.parse(parts[0]), + int.parse(parts[1]), + ); + return DateFormat.jm().format(dateTime); // e.g., 12:16 PM or 12 AM + } catch (e) { + return "Invalid Time"; + } + } + + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: filteredList.length, + itemBuilder: (context, index) { + final item = filteredList[index]; + + return Container( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + // color: Colors.orange.shade50, + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 3, + offset: Offset(0, 1), + ), + ], + border: Border( + top: BorderSide( + color: Colors.white70, // Change color to match your theme + width: 2, + ), ), - ]); - }).toList(); + ), + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "${getRequestForTaxiClass(item["car_type"]!.toString())} For ${item["no_of_passengers"]!} Passernger", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w800, + fontFamily: "Archivo"), + ), + Spacer(), + GestureDetector( + onTap: () => onOpen(true, item, "Taxi"), + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox(width: 10), + GestureDetector( + onTap: () => (item), + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), + ), + ], + ), + Divider( + color: Colors.blueGrey.shade50, + ), + SizedBox(height: 4), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + "Class", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Planned Trips", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Date", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + "Comments", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + ], + ) + : SizedBox.shrink(), + SizedBox(height: 4), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text("${item["destination_city"]}", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text("${item["location_of_pickup"]}", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: _buildComments( + " Comments:", item["comments"] ?? "N/A"), + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildRow("City:", item["destination_city"]), + SizedBox(width: 10), + _buildRow("Pickup:", item["location_of_pickup"]!), + SizedBox(width: 10), + _buildDateTimeRow( + "Date:", + formatDate(item["date"]!), + formatTime(item["time"]!), + ), + _buildRow("TaxiFor:", item["car_required_for"]!), + _buildRow("Comments:", item["comments"] ?? "N/A"), + ], + ) + ], + ), + ), + ); + }, + ); + } + + Widget _buildComments(String title, String value) { + // Define character limits based on title + Map 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 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), + ), + ), + ], + ); } } diff --git a/lib/Screens/itnerary_list/train_list.dart b/lib/Screens/itnerary_list/train_list.dart index 9da6e3e..289a46b 100644 --- a/lib/Screens/itnerary_list/train_list.dart +++ b/lib/Screens/itnerary_list/train_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; class TrainListWidget extends StatelessWidget { final List> trainList; @@ -6,6 +7,7 @@ class TrainListWidget extends StatelessWidget { final Function(Map) onDeleteTrain; final Function(String, bool) onAddNew; final bool isViewMode; + final Map? apiData; const TrainListWidget({ super.key, @@ -14,16 +16,20 @@ class TrainListWidget extends StatelessWidget { required this.onDeleteTrain, required this.onAddNew, required this.isViewMode, + required this.apiData, }); @override Widget build(BuildContext context) { + final isDesktop = MediaQuery.of(context).size.width > 1024; + return Padding( padding: const EdgeInsets.all(16.0), child: Container( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + // Header with title and button Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -75,100 +81,340 @@ class TrainListWidget extends StatelessWidget { ], ), const SizedBox(height: 16), - Center( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: DataTable( - border: TableBorder( - bottom: BorderSide(color: Colors.black12), - horizontalInside: BorderSide( - color: Colors.black12), // Only horizontal lines - ), - columns: const [ - // DataColumn(label: Text('#')), - DataColumn(label: Text('Train Number')), - // DataColumn(label: Text('Class')), - DataColumn(label: Text('From')), - // DataColumn(label: Text('To')), - DataColumn(label: Text('Actions')), - ], - rows: _buildDataRows(), - ), - ), - ), - ), + // Scroll behavior based on device + + _buildData(context, isDesktop) ], ), ), ); } - List _buildDataRows() { + Widget _buildData(BuildContext context, bool isDesktop) { List> filteredList = trainList.where((item) => item["is_active"] == "1").toList(); - print("filteredList- $filteredList"); + // print("filteredList- $filteredList"); - return filteredList.asMap().entries.map((entry) { - final Map item = entry.value; + List trainClassList = apiData?['train_class'] ?? []; - return DataRow(cells: [ - // DataCell(Text(item["indx"]?.toString() ?? "N/A")), + String getRequestForTrainClass(String? specialRequestKey) { + if (specialRequestKey == null) return "N/A"; - DataCell(Text(item["train_no"]!)), - // DataCell(Text(item["class"]!)), - DataCell(Text(item["from_station"]!)), - // DataCell(Text(item["to_station"]!)), - DataCell(Row( - children: [ - GestureDetector( - onTap: () => onOpen(true, item, "Train"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), + return trainClassList + .firstWhere( + (element) => + element["dropdown_key"].toString() == specialRequestKey, + orElse: () => {"dropdown_value": "N/A"}, + )["dropdown_value"] + .toString(); + } + + String formatDate(String dateString) { + try { + DateTime date = DateTime.parse(dateString); + return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25 + } catch (e) { + return "Invalid Date"; + } + } + + String formatTime(String timeString) { + try { + final parts = timeString.split(':'); + final now = DateTime.now(); + final dateTime = DateTime( + now.year, + now.month, + now.day, + int.parse(parts[0]), + int.parse(parts[1]), + ); + return DateFormat.jm().format(dateTime); // e.g., 12:16 PM or 12 AM + } catch (e) { + return "Invalid Time"; + } + } + + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: filteredList.length, + itemBuilder: (context, index) { + final item = filteredList[index]; + + return Container( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + // color: Colors.orange.shade50, + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 3, + offset: Offset(0, 1), + ), + ], + border: Border( + top: BorderSide( + color: Colors.white70, // Change color to match your theme + width: 2, + ), ), - SizedBox(width: 10), - GestureDetector( - onTap: () => onDeleteTrain(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), + ), + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + item["train_no"]?.toString() ?? "N/A", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w800, + fontFamily: "Archivo"), + ), + Spacer(), + GestureDetector( + onTap: () => onOpen(true, item, "Train"), + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox(width: 10), + GestureDetector( + onTap: () => onDeleteTrain(item), + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), + ), + ], + ), + Divider( + color: Colors.blueGrey.shade50, + ), + SizedBox(height: 4), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + " Class", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Planned Trips", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Date", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Comments", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + ], + ) + : SizedBox.shrink(), + SizedBox(height: 4), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + "${getRequestForTrainClass(item["class"]!.toString())} ", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + "${item["from_station"]!} ${(item["to_station"])}", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"))), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo")), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: _buildComments( + "Comments:", item["comments"] ?? "N/A"), + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildRow( + "Class:", + getRequestForTrainClass( + item["class"]!.toString())), + _buildRow("From:", item["from_station"]!), + _buildRow("To:", item["to_station"]!), + _buildDateTimeRow( + "Date:", + formatDate(item["date"]!), + formatTime(item["time"]!), + ), + _buildRow("Comments:", item["comments"] ?? "N/A"), + ], + ) + ], ), - IconButton( - icon: Icon(Icons.keyboard_arrow_down_outlined, - size: 28, color: Color(0xFF475569)), - onPressed: () { - // Expand logic - }, - ), - ], - ) + ), + ); + }, + ); + } - // Row( - // children: [ - // IconButton( - // icon: Icon(Icons.remove_red_eye, color: Colors.blue), - // onPressed: () { - // // View action - // }, - // ), - // IconButton( - // icon: Icon(Icons.edit, color: Colors.green), - // onPressed: () { - // onOpen(true, item, "Train"); - // }, - // ), - // IconButton( - // icon: Icon(Icons.delete, color: Colors.red), - // onPressed: () { - // onDeleteTrain(item); - // }, - // ), - // ], - // ) - // + Widget _buildComments(String title, String value) { + // Define character limits based on title + Map limits = { + // "Special Request:": 30, + "Comments:": 30, + // Add more keys and limits if needed + }; - ), - ]); - }).toList(); + int limit = limits[title] ?? 50; // Default limit if title not found + bool exceedsLimit = value.length > limit; + + String wrapText(String text, int maxLineLength) { + final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)'); + return pattern.allMatches(text).map((m) => m.group(0)!).join('\n'); + } + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: exceedsLimit + ? Tooltip( + message: wrapText(value, 50), + decoration: BoxDecoration( + color: Colors.grey.shade200, // Black background + borderRadius: BorderRadius.circular(6), + ), + textStyle: + TextStyle(color: Colors.black), // Tooltip text color + padding: EdgeInsets.all(8), + preferBelow: false, + child: Text(value.substring(0, limit) + "...", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + overflow: TextOverflow.ellipsis), + ) + : Text( + value, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + ), + ), + ], + ); + } + + Widget _buildRow(String title, String value) { + // Define character limits based on title + Map 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), + ), + ), + ], + ); } } diff --git a/lib/Screens/itnerary_list/visa_list.dart b/lib/Screens/itnerary_list/visa_list.dart index facccf7..0916118 100644 --- a/lib/Screens/itnerary_list/visa_list.dart +++ b/lib/Screens/itnerary_list/visa_list.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; class VisaListWidget extends StatelessWidget { final List> visaList; @@ -22,18 +23,21 @@ class VisaListWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final isDesktop = MediaQuery.of(context).size.width > 1024; + return Padding( padding: const EdgeInsets.all(16.0), child: Container( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + // Header with title and button Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Visa Booking List", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), MouseRegion( cursor: isViewMode @@ -59,19 +63,12 @@ class VisaListWidget extends StatelessWidget { onAddNew("Visa", true); }, child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely + mainAxisSize: MainAxisSize.min, children: [ - Text( - "Add New", - style: TextStyle(fontSize: 13), - ), - SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_outline_rounded, - size: 15, - color: Colors.white, - ), + Text("Add New", style: TextStyle(fontSize: 13)), + SizedBox(width: 8), + Icon(Icons.add_circle_outline_rounded, + size: 15, color: Colors.white), ], ), ), @@ -79,36 +76,102 @@ class VisaListWidget extends StatelessWidget { ], ), const SizedBox(height: 16), - Center( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: DataTable( - border: TableBorder( - bottom: BorderSide(color: Colors.black12), - horizontalInside: BorderSide( - color: Colors.black12), // Only horizontal lines - ), - columns: const [ - // DataColumn(label: Text('#')), - DataColumn(label: Text('Type of Visa')), - DataColumn(label: Text('Country')), - DataColumn(label: Text('Start Date')), - DataColumn(label: Text('Actions')), - ], - rows: _buildDataRows(), - ), - ), - ), - ), + // Scroll behavior based on device + + _buildData(context, isDesktop) ], ), ), ); } + // Widget build(BuildContext context) { + // return Padding( + // padding: const EdgeInsets.all(16.0), + // child: Container( + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.stretch, + // children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // "Visa Booking List", + // style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + // ), + // MouseRegion( + // cursor: isViewMode + // ? SystemMouseCursors.forbidden + // : SystemMouseCursors.click, + // child: ElevatedButton( + // style: ElevatedButton.styleFrom( + // backgroundColor: Color(0xFF114D8B), + // foregroundColor: Colors.white, + // disabledBackgroundColor: Color(0xFF114D8B), + // disabledForegroundColor: Colors.white, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8), + // side: BorderSide(color: Color(0xFF114D8B), width: 2), + // ), + // padding: + // EdgeInsets.symmetric(horizontal: 20, vertical: 12), + // ), + // onPressed: isViewMode + // ? null + // : () { + // print("New data"); + // onAddNew("Visa", true); + // }, + // child: Row( + // mainAxisSize: + // MainAxisSize.min, // Ensures content fits nicely + // children: [ + // Text( + // "Add New", + // style: TextStyle(fontSize: 13), + // ), + // SizedBox(width: 8), // spacing between icon and text + // Icon( + // Icons.add_circle_outline_rounded, + // size: 15, + // color: Colors.white, + // ), + // ], + // ), + // ), + // ), + // ], + // ), + // const SizedBox(height: 16), + // Center( + // child: SingleChildScrollView( + // scrollDirection: Axis.horizontal, + // child: SizedBox( + // width: MediaQuery.of(context).size.width, + // child: DataTable( + // border: TableBorder( + // bottom: BorderSide(color: Colors.black12), + // horizontalInside: BorderSide( + // color: Colors.black12), // Only horizontal lines + // ), + // columns: const [ + // // DataColumn(label: Text('#')), + // DataColumn(label: Text('Type of Visa')), + // DataColumn(label: Text('Country')), + // DataColumn(label: Text('Start Date')), + // DataColumn(label: Text('Actions')), + // ], + // rows: _buildDataRows(), + // ), + // ), + // ), + // ), + // ], + // ), + // ), + // ); + // } - List _buildDataRows() { + Widget _buildData(BuildContext context, bool isDesktop) { List> filteredList = visaList.where((item) => item["is_active"] == "1").toList(); print("filteredList- $filteredList"); @@ -139,66 +202,362 @@ class VisaListWidget extends StatelessWidget { .toString(); } - return filteredList.asMap().entries.map((entry) { - int index = entry.key + 1; // To start index from 1 - Map item = entry.value; - print(item); - return DataRow(cells: [ - // DataCell(Text(item["indx"]?.toString() ?? "N/A")), - // DataCell(Text(item["type_of_visa"]!)), - DataCell(Text(getRequestForVisa(item["type_of_visa"]!.toString()))), - DataCell(Text(getRequestForCountry(item["country_code"]!.toString()))), - // DataCell(Text(item["country_code"]!)), - DataCell(Text(item["start_date"]!)), - DataCell(Row( - children: [ - GestureDetector( - onTap: () => onOpen(true, item, "Visa"), - child: Image.asset('assets/images/IconsImg/edit.png', - width: 20, height: 15), - ), - SizedBox(width: 10), - GestureDetector( - onTap: () => onDeleteMiscellaneous(item), - child: Image.asset('assets/images/IconsImg/delete.png', - width: 20, height: 15), - ), - IconButton( - icon: Icon(Icons.keyboard_arrow_down_outlined, - size: 28, color: Color(0xFF475569)), - onPressed: () { - // Expand logic - }, - ), - ], - ) + String formatDate(String dateString) { + try { + DateTime date = DateTime.parse(dateString); + return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25 + } catch (e) { + return "Invalid Date"; + } + } - // - // Row( - // children: [ - // IconButton( - // icon: Icon(Icons.remove_red_eye, color: Colors.blue), - // onPressed: () { - // // View action - // }, - // ), - // IconButton( - // icon: Icon(Icons.edit, color: Colors.green), - // onPressed: () { - // onOpen(true, item, "Visa"); - // }, - // ), - // IconButton( - // icon: Icon(Icons.delete, color: Colors.red), - // onPressed: () { - // onDeleteMiscellaneous(item); - // }, - // ), - // ], - // ) + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: filteredList.length, + itemBuilder: (context, index) { + final item = filteredList[index]; + return Container( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + // color: Colors.orange.shade50, + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 3, + offset: Offset(0, 1), + ), + ], + border: Border( + top: BorderSide( + color: Colors.white70, // Change color to match your theme + width: 2, + ), ), - ]); - }).toList(); + ), + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () => onOpen(true, item, "Visa"), + child: Image.asset('assets/images/IconsImg/edit.png', + width: 20, height: 15), + ), + SizedBox(width: 10), + GestureDetector( + onTap: () => onDeleteMiscellaneous(item), + child: Image.asset('assets/images/IconsImg/delete.png', + width: 20, height: 15), + ), + ], + ), + Divider( + color: Colors.blueGrey.shade50, + ), + SizedBox(height: 4), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + " Visa Type", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Country", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Date", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + Expanded( + flex: 2, + child: Text( + " Comments", + style: TextStyle( + fontSize: 11, fontFamily: "Archivo"), + )), + ], + ) + : SizedBox.shrink(), + isDesktop + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + getRequestForVisa( + item["type_of_visa"]!.toString()), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + ), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + getRequestForCountry( + item["country_code"]!.toString()), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + ), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + formatDate(item["start_date"]!), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo"), + ), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: _buildComments( + "Comments:", item["comments"] ?? "N/A"), + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildRow( + "VisaType:", + getRequestForVisa( + item["type_of_visa"]!.toString())), + SizedBox(height: 6), + _buildRow( + "Country:", + getRequestForCountry( + item["country_code"]!.toString())), + SizedBox(height: 6), + _buildRow("StartDate:", item["start_date"]!), + SizedBox(height: 6), + _buildRow("Comments:", item["comments"] ?? "N/A"), + ], + ), + ], + ), + ), + ); + }, + ); } + + Widget _buildComments(String title, String value) { + // Define character limits based on title + Map 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 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 _buildDataRows() { + // List> filteredList = + // visaList.where((item) => item["is_active"] == "1").toList(); + // print("filteredList- $filteredList"); + // + // List visatypeList = apiData?['visa_type_of_visa'] ?? []; + // List 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 item = entry.value; + // print(item); + // return DataRow(cells: [ + // // DataCell(Text(item["indx"]?.toString() ?? "N/A")), + // // DataCell(Text(item["type_of_visa"]!)), + // DataCell(Text(getRequestForVisa(item["type_of_visa"]!.toString()))), + // DataCell(Text(getRequestForCountry(item["country_code"]!.toString()))), + // // DataCell(Text(item["country_code"]!)), + // DataCell(Text(item["start_date"]!)), + // DataCell(Row( + // children: [ + // GestureDetector( + // onTap: () => onOpen(true, item, "Visa"), + // child: Image.asset('assets/images/IconsImg/edit.png', + // width: 20, height: 15), + // ), + // SizedBox(width: 10), + // GestureDetector( + // onTap: () => onDeleteMiscellaneous(item), + // child: Image.asset('assets/images/IconsImg/delete.png', + // width: 20, height: 15), + // ), + // IconButton( + // icon: Icon(Icons.keyboard_arrow_down_outlined, + // size: 28, color: Color(0xFF475569)), + // onPressed: () { + // // Expand logic + // }, + // ), + // ], + // ) + // + // // + // // Row( + // // children: [ + // // IconButton( + // // icon: Icon(Icons.remove_red_eye, color: Colors.blue), + // // onPressed: () { + // // // View action + // // }, + // // ), + // // IconButton( + // // icon: Icon(Icons.edit, color: Colors.green), + // // onPressed: () { + // // onOpen(true, item, "Visa"); + // // }, + // // ), + // // IconButton( + // // icon: Icon(Icons.delete, color: Colors.red), + // // onPressed: () { + // // onDeleteMiscellaneous(item); + // // }, + // // ), + // // ], + // // ) + // + // ), + // ]); + // }).toList(); + // } } diff --git a/lib/Screens/organization/orgSetup.dart b/lib/Screens/organization/orgSetup.dart index 1799778..ae7e142 100644 --- a/lib/Screens/organization/orgSetup.dart +++ b/lib/Screens/organization/orgSetup.dart @@ -354,6 +354,7 @@ class _OrgSetUpState extends State { children: [ Expanded( child: Container( + // color: Colors.redAccent, color: bodyColor, child: buildOrgLayout(isDesktop), ), diff --git a/lib/Screens/plans/create_plans.dart b/lib/Screens/plans/create_plans.dart index 5d18894..b320e20 100644 --- a/lib/Screens/plans/create_plans.dart +++ b/lib/Screens/plans/create_plans.dart @@ -1,4 +1,11 @@ import 'dart:convert'; +import 'dart:ui' as html; + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:web/web.dart' as web; import 'package:flutter/material.dart'; import 'package:frontend/Screens/plans/dynamic_itinerary_stepper.dart'; @@ -35,7 +42,6 @@ class _CreatePlansState extends State { @override void initState() { super.initState(); - loadInitialData(); } @@ -95,7 +101,8 @@ class _CreatePlansState extends State { // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), decoration: BoxDecoration( // color: Colors.amber, - color: bodyColor, + // color: bodyColor, + color: Color(0xFFE1F5FE), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: Column( children: [ @@ -274,6 +281,8 @@ class CreateNewPlansState extends State { late String _selectedOption = "Option 1"; // late String? _selectedIsBillable = "Billable"; + bool isStatusExpanded = false; + bool isShowApprovalAction = false; String? selectedPlanId; String? userDetails; @@ -292,6 +301,13 @@ class CreateNewPlansState extends State { bool showDomestic = false; bool showInternational = false; + late Map costCenterMap; + List costCenterIds = []; + // List apiCostData = []; // if you’re not already using this + + late Map purposeMap; + List purposeKeys = []; + String? orgId; String? planUsrId; String? planTravlrId; @@ -315,6 +331,8 @@ class CreateNewPlansState extends State { List> forexList = []; List> flightList = []; + List> planStatusList = []; + late bool isApproverApproved = false; late bool isApproverRejected = false; @@ -443,6 +461,11 @@ class CreateNewPlansState extends State { widget.selectedPlanData!["cost_center_id"].toString(); } + if (widget.selectedPlanData['plan_status'] != null) { + planStatusList = List>.from( + widget.selectedPlanData['plan_status'] ?? []); + } + // if (widget.selectedPlanData!["purpose_of_travel"] != null) { selectedPurpose = @@ -643,17 +666,30 @@ class CreateNewPlansState extends State { } List plansJson = data['data']; // 'data' is a Map, not a List - setState(() { - apiCostData = plansJson; // Store API response in state - // if(apiCostData!.isNotEmpty){ - // selectedCostCenterId =apiCostData?.first['department_id']; - // } + // setState(() { + // apiCostData = plansJson; // Store API response in state + // // if(apiCostData!.isNotEmpty){ + // // selectedCostCenterId =apiCostData?.first['department_id']; + // // } + // + // if (apiCostData != null && apiCostData!.isNotEmpty) { + // selectedCostCenterId ??= apiCostData!.first['name']?.toString(); + // } + // }); - if (apiCostData != null && apiCostData!.isNotEmpty) { - selectedCostCenterId ??= - apiCostData!.first['department_id']?.toString(); - } + setState(() { + apiCostData = plansJson; + costCenterMap = { + for (var item in apiCostData!) + item['department_id'].toString(): item['name'].toString() + }; + costCenterIds = costCenterMap.keys.toList(); + + // Optionally auto-select the first item if not already selected + selectedCostCenterId ??= + costCenterIds.isNotEmpty ? costCenterIds.first : null; }); + print('plansJSON'); } catch (e) { throw Exception('Error parsing response: $e'); @@ -901,201 +937,71 @@ class CreateNewPlansState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (widget.selectedPlanData != null && - widget.selectedPlanData['status_value'] != null) - Row( - children: [ - Text("Status : "), - if (isApproverApproved == false && - isApproverRejected == false) - Text(statusValue ?? "", - style: TextStyle( - fontFamily: "Archivo", - // fontSize: 13, - fontWeight: FontWeight.bold, - color: widget.layoutColor ?? Colors.grey, - )), - if (isApproverApproved == true) - Text( - "Approved", - style: TextStyle( - fontFamily: "Archivo", - // fontSize: 13, - fontWeight: FontWeight.bold, - color: widget.layoutColor ?? Colors.grey, - ), - ), - if (isApproverRejected == true) - Text("Rejected", - style: TextStyle( - fontFamily: "Archivo", - // fontSize: 13, - fontWeight: FontWeight.bold, - color: widget.layoutColor ?? Colors.grey, - )), - ], - ), - SizedBox( - height: 10, - ), - if (widget.isApprover) - Container( + ResponsiveBuilder(builder: (context, sizingInfo) { + bool isDesktop = sizingInfo.deviceScreenType == + DeviceScreenType.desktop; + + return Container( padding: const EdgeInsets.all(10), - // color: Colors.yellow.shade50, - child: Row( + child: isDesktop + ? Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: _buildApproverControls(), + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildApproverControls(), + ), + ); + }), + if (isStatusExpanded) + Container( + margin: isDesktop + ? const EdgeInsets.only(left: 60, top: 0) + : const EdgeInsets.only(left: 5, top: 2), + padding: const EdgeInsets.all(12), + width: isDesktop + ? MediaQuery.of(context).size.width * 0.2 + : MediaQuery.of(context).size.width, + decoration: BoxDecoration( + color: Color(0xFFF5F5F5), + border: Border.all( + // color: Colors.grey.shade300, + color: Colors.white, + width: 0.2), + borderRadius: BorderRadius.circular(8), + // boxShadow: [ + // BoxShadow( + // // color: Colors.grey.withAlpha(20), + // color: Colors.grey.withAlpha(20), + // spreadRadius: 1.5, + // blurRadius: 7, + // offset: Offset(0, 4), // shadow direction: bottom + // ), + // ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "To Approve or Reject Plan", - style: TextStyle( - fontFamily: "Archivo", - // fontSize: 13, - fontWeight: FontWeight.w600, - color: Color(0xFF212121), - // color: Color(0xFF575A74), // Default color - ), - ), - Spacer(), - MouseRegion( - cursor: widget.isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - // backgroundColor: widget.isViewMode - // ? widget.layoutColor - // : Colors - // .grey.shade100, // Keep original color - // foregroundColor: widget.isViewMode - // ? Colors.white - // : Colors.white, // Keep original color - disabledBackgroundColor: statusValue == - "Approved" - ? Colors.green.shade100 - : null, // Ensure color remains when disabled - disabledForegroundColor: - statusValue == "Approved" - ? Colors.white - : Colors.black, - - backgroundColor: isApproverApproved || - (isApproverApproved == false && - isApproverRejected == false && - statusValue == "Approved") - ? Colors.green - : Colors.grey.shade100, - foregroundColor: isApproverApproved || - (isApproverApproved == false && - isApproverRejected == false && - statusValue == "Approved") - ? Colors.white - : Colors.black, - - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - // side: BorderSide( - // color: Colors.green.shade100, width: 2) , - ), - padding: EdgeInsets.symmetric( - horizontal: 20, vertical: 12), + for (int i = 0; i < planStatusList.length; i++) ...[ + if (planStatusList[i].entries.any((entry) => + entry.key.contains('status') && + entry.value != null && + entry.value.toString().isNotEmpty)) ...[ + _buildApprovalItem( + "Approver ${i + 1}", + planStatusList[i] + .entries + .firstWhere( + (entry) => entry.key.contains('status'), + orElse: () => MapEntry('', ''), + ) + .value + .toString(), ), - onPressed: widget.isViewMode - ? null - : () async { - final confirmed = - await showApproveDialog( - context, - widget.layoutColor ?? - Colors.grey); - if (confirmed == true) { - setState(() { - isApproverApproved = true; - isApproverRejected = false; - statusValue = "Approved"; - }); - // " ${_remarksController.text}, ${planData['user_id']}, ${planData['traveller_id']}, " - // // "${planData['traveller_id']}," - // // " ${selectedPlanId}, " - callApproveAPI( - selectedPlanId!, - planData['user_id'], - ); // Your API call - } - }, - // Disable when in view mode - - child: Text("Approve"), - ), - ), - SizedBox( - width: 10, - ), - MouseRegion( - cursor: widget.isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - // backgroundColor: widget.isViewMode - // ? Colors.redAccent - // : Colors.redAccent - // .shade100, // Keep original color - // foregroundColor: widget.isViewMode - // ? Colors.white - // : Colors.white, // Keep original color - disabledBackgroundColor: statusValue == - "Rejected" - ? Colors.redAccent.shade100 - : null, // Ensure color remains when disabled - disabledForegroundColor: - statusValue == "Rejected" - ? Colors.white - : Colors.black, - - backgroundColor: isApproverRejected || - (isApproverApproved == false && - isApproverRejected == false && - statusValue == "Rejected") - ? Colors.redAccent - : Colors.grey.shade100, - foregroundColor: isApproverRejected || - (isApproverApproved == false && - isApproverRejected == false && - statusValue == "Rejected") - ? Colors.white - : Colors.black, - - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - // side: BorderSide( - // // color: Colors.redAccent.shade100, - // width: 2), - ), - padding: EdgeInsets.symmetric( - horizontal: 20, vertical: 12), - ), - onPressed: widget.isViewMode - ? null - : () async { - final remarks = await showRejectDialog( - context, - widget.layoutColor ?? Colors.grey); - if (remarks != null) { - _remarksController.text = remarks; - setState(() { - isApproverApproved = false; - isApproverRejected = true; - statusValue = "Rejected"; - }); - - callRejectAPI( - selectedPlanId!, - planData['user_id'], - remarks); // Your API call with remarks - } - }, - child: Text("Reject"), - ), - ) + SizedBox(height: 6), + ], + ], ], ), ), @@ -1104,8 +1010,12 @@ class CreateNewPlansState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( - padding: EdgeInsets.only(top: 1.0), - child: Text("Remarks : "), + padding: EdgeInsets.only(top: 4, left: 8), + child: Text("Remarks : ", + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.w600, + fontSize: 11)), ), Expanded( child: Padding( @@ -1113,6 +1023,10 @@ class CreateNewPlansState extends State { top: 0), // tweak if needed child: TextFormField( controller: _remarksController, + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.w600, + fontSize: 11), decoration: const InputDecoration( hintText: "Please enter remarks...", hintStyle: TextStyle( @@ -1126,10 +1040,10 @@ class CreateNewPlansState extends State { ), ], ), - if (widget.isApprover) + if (widget.isApprover || isStatusExpanded) Divider( - thickness: 0.5, - color: Colors.grey, + thickness: 0.1, + color: Colors.blueGrey, ), if (widget.isApprover) SizedBox( @@ -1235,7 +1149,7 @@ class CreateNewPlansState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Trip Type ( $TripPlanAction )", // Your label + "Trip Type *", // Your label style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, @@ -1350,6 +1264,14 @@ class CreateNewPlansState extends State { }); } + String? getSelectedCostCenterName() { + if (selectedCostCenterId == null || apiCostData == null) return null; + return apiCostData!.firstWhere( + (item) => item['department_id'] == selectedCostCenterId, + orElse: () => null, + )?['name']; + } + /// Extracted helper function List _buildCostIsBillable() { List purposeList = apiData?['plan_is_billable'] ?? []; @@ -1370,30 +1292,95 @@ class CreateNewPlansState extends State { isFocused: false, // Dropdown doesn't use focus isDesktop: widget.isDesktop, child: SizedBox( - height: 45, // Set appropriate height - child: DropdownButtonFormField( - value: selectedCostCenterId, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding - ), - onChanged: widget.isViewMode - ? null - : (newValue) { + height: 40, + width: double.infinity, + child: apiCostData == null + ? Center( + child: Transform.scale( + scale: 0.5, + child: CircularProgressIndicator(), + ), + ) + : DropdownSearch( + selectedItem: costCenterMap[selectedCostCenterId], + enabled: !widget.isViewMode, + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search Cost Center...", + hintStyle: + TextStyle(fontSize: 13, color: Colors.grey), + contentPadding: + EdgeInsets.symmetric(horizontal: 10), + ), + style: TextStyle(fontSize: 13)), + menuProps: MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, vertical: 6.0), + child: Text( + item, + style: TextStyle( + fontSize: + 13), // 👈 Set your desired text size here + ), + ), + ), + + items: costCenterMap.values.toList(), // just names + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select", + style: TextStyle(fontSize: 12), + ), + ), + onChanged: (String? newValue) { setState(() { - selectedCostCenterId = newValue; + selectedCostCenterId = costCenterMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; }); }, - items: apiCostData?.map>((item) { - return DropdownMenuItem( - value: item['department_id'], // ID as value - child: Text(item['name'] ?? "Unknown"), - ); - }).toList(), - ), + ), ), + + // child: SizedBox( + // height: 45, // Set appropriate height + // child: DropdownButtonFormField( + // value: selectedCostCenterId, + // style: TextStyle(fontSize: 12), + // decoration: InputDecoration( + // border: InputBorder.none, + // contentPadding: + // EdgeInsets.symmetric(horizontal: 10), // Proper padding + // ), + // onChanged: widget.isViewMode + // ? null + // : (newValue) { + // setState(() { + // selectedCostCenterId = newValue; + // }); + // }, + // items: apiCostData?.map>((item) { + // return DropdownMenuItem( + // value: item['department_id'], // ID as value + // child: Text(item['name'] ?? "Unknown"), + // ); + // }).toList(), + // ), + // ), ), ], ), @@ -1592,7 +1579,6 @@ class CreateNewPlansState extends State { : null, ), ), - GestureDetector( onTap: widget.isViewMode ? null @@ -1626,22 +1612,6 @@ class CreateNewPlansState extends State { : null, // Add checkmark if selected ), ) - - // Radio( - // value: option["value"]!, - // groupValue: _selectedOption, - // activeColor: Colors.white, - // onChanged: widget.isViewMode - // ? null - // : (value) { - // setState(() { - // _selectedOption = value!; - // if (value == 'Option 2' || value == 'Option 3') { - // _showInputDialog(option["title"]!); - // } - // }); - // }, - // ), ], ), ), @@ -1652,53 +1622,37 @@ class CreateNewPlansState extends State { List _buildTripType(bool isMobile) { return [ if (showDomestic == true) - CustomTextFieldWrapper( - color: Color(0xFFF4F4FB), - layoutColor: widget.layoutColor, - borderRadius: BorderRadius.circular(25), - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), - width: 130, - isFocused: _selectedTripType == "1", - isDesktop: widget.isDesktop, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Domestic", - style: TextStyle( - color: - _selectedTripType == "1" ? Colors.white : Colors.black, - fontWeight: - _selectedTripType == "1" ? FontWeight.w500 : null, - fontSize: 13), - ), - - // Radio( - // activeColor: Colors.blueAccent, - // // contentPadding: EdgeInsets.zero, - // visualDensity: VisualDensity.compact, - // // dense: true, - // value: "1", - // groupValue: _selectedTripType, - // onChanged: widget.isViewMode - // ? null - // : (value) { - // setState(() { - // _selectedTripType = value!; - // }); - // }, - // ), - - GestureDetector( - onTap: widget.isViewMode - ? null - : () { - setState(() { - _selectedTripType = "1"; - }); - }, - child: Container( + GestureDetector( + onTap: widget.isViewMode + ? null + : () { + setState(() { + _selectedTripType = "1"; + }); + }, + child: CustomTextFieldWrapper( + color: Color(0xFFF4F4FB), + layoutColor: widget.layoutColor, + borderRadius: BorderRadius.circular(25), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + width: 130, + isFocused: _selectedTripType == "1", + isDesktop: widget.isDesktop, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Domestic", + style: TextStyle( + color: _selectedTripType == "1" + ? Colors.white + : Colors.black, + fontWeight: + _selectedTripType == "1" ? FontWeight.w500 : null, + fontSize: 13), + ), + Container( width: 15, height: 15, @@ -1719,42 +1673,44 @@ class CreateNewPlansState extends State { ? Icon(Icons.rectangle, size: 8, color: Colors.white) : null, // Add checkmark if selected ), - ) - ], + ], + ), ), ), SizedBox(width: 20), if (showInternational) - CustomTextFieldWrapper( - color: Color(0xFFF4F4FB), - layoutColor: widget.layoutColor, - borderRadius: BorderRadius.circular(25), - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - width: 150, - // padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2), - isFocused: _selectedTripType == "2", - isDesktop: widget.isDesktop, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "International", - style: TextStyle( - fontSize: 13, - color: _selectedTripType == "2" ? Colors.white : Colors.black, - fontWeight: _selectedTripType == "2" ? FontWeight.w500 : null, + GestureDetector( + onTap: widget.isViewMode + ? null + : () { + setState(() { + _selectedTripType = "2"; + }); + }, + child: CustomTextFieldWrapper( + color: Color(0xFFF4F4FB), + layoutColor: widget.layoutColor, + borderRadius: BorderRadius.circular(25), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + width: 150, + // padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2), + isFocused: _selectedTripType == "2", + isDesktop: widget.isDesktop, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "International", + style: TextStyle( + fontSize: 13, + color: + _selectedTripType == "2" ? Colors.white : Colors.black, + fontWeight: + _selectedTripType == "2" ? FontWeight.w500 : null, + ), ), - ), - GestureDetector( - onTap: widget.isViewMode - ? null - : () { - setState(() { - _selectedTripType = "2"; - }); - }, - child: Container( + Container( width: 15, height: 15, decoration: BoxDecoration( @@ -1774,25 +1730,25 @@ class CreateNewPlansState extends State { ? Icon(Icons.rectangle, size: 8, color: Colors.white) : null, // Add checkmark if selected ), - ) - ], - ), + ], + ), - // RadioListTile( - // activeColor: Colors.blueAccent, - // contentPadding: EdgeInsets.zero, - // dense: true, - // title: Text("International"), - // value: "2", - // groupValue: _selectedTripType, - // onChanged: widget.isViewMode - // ? null - // : (value) { - // setState(() { - // _selectedTripType = value!; - // }); - // }, - // ), + // RadioListTile( + // activeColor: Colors.blueAccent, + // contentPadding: EdgeInsets.zero, + // dense: true, + // title: Text("International"), + // value: "2", + // groupValue: _selectedTripType, + // onChanged: widget.isViewMode + // ? null + // : (value) { + // setState(() { + // _selectedTripType = value!; + // }); + // }, + // ), + ), ), ]; } @@ -1804,7 +1760,9 @@ class CreateNewPlansState extends State { // 'plan_purpose_of_travel' Starts ------------------------------------------------------ - List purposeList = apiData?['plan_purpose_of_travel'] ?? []; + // List purposeList = apiData?['plan_purpose_of_travel'] ?? []; + List> purposeList = List>.from( + apiData?['plan_purpose_of_travel'] ?? []); List> dropdownItems = purposeList .map((item) => DropdownMenuItem( @@ -1826,10 +1784,6 @@ class CreateNewPlansState extends State { ); } -// Ensure Selected Value Exists in the Dropdown List - List dropdownKeys = - dropdownItems.map((e) => e.value ?? "").toList(); - selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value.toString() : "No options"; @@ -1840,7 +1794,9 @@ class CreateNewPlansState extends State { // 'plan_functional_department' Starts --------------------------------------------- - List funcDeptList = apiData?['plan_functional_department'] ?? []; + // List funcDeptList = apiData?['plan_functional_department'] ?? []; + List> funcDeptList = List>.from( + apiData?['plan_functional_department'] ?? []); List> dropdownFuncDeptItems = funcDeptList .map((item) => DropdownMenuItem( @@ -1889,33 +1845,108 @@ class CreateNewPlansState extends State { isDesktop: widget.isDesktop, child: SizedBox( height: 45, // Set appropriate height + width: double.infinity, child: apiData == null ? Center( child: CircularProgressIndicator()) // Show loading inside dropdown - : DropdownButtonFormField( - value: selectedPurpose, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + : DropdownSearch>( + selectedItem: purposeList.firstWhere( + (item) => + item['dropdown_key'].toString() == + selectedPurpose, + orElse: () => + {}, // ✅ Safe fallback + ), + items: purposeList, + itemAsString: (Map item) => + item['dropdown_value'], + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 200), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search Purpose...", + hintStyle: TextStyle( + fontSize: 13, color: Colors.grey), + contentPadding: + EdgeInsets.symmetric(horizontal: 10), + ), + style: TextStyle(fontSize: 13)), + menuProps: MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, vertical: 6.0), + child: Text( + item['dropdown_value'], + style: TextStyle( + fontSize: + 13), // 👈 Set your desired text size here + ), + ), + ), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + contentPadding: + EdgeInsets.symmetric(horizontal: 1), + border: InputBorder.none, + ), ), onChanged: widget.isViewMode ? null - : purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedPurpose = newValue; - }); - print( - "selectedPurpose - $selectedPurpose"); - } - : null, - items: dropdownItems, + : (Map? newValue) { + setState(() { + selectedPurpose = + newValue?['dropdown_key'].toString(); + }); + print("selectedPurpose - $selectedPurpose"); + }, + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem?['dropdown_value'] ?? '', + style: TextStyle(fontSize: 12), + ), + ), ), ), ), + + // CustomTextFieldWrapper( + // isFocused: false, // Dropdown doesn't use focus + // isDesktop: widget.isDesktop, + // child: SizedBox( + // height: 45, // Set appropriate height + // child: apiData == null + // ? Center( + // child: + // CircularProgressIndicator()) // Show loading inside dropdown + // : DropdownButtonFormField( + // value: selectedPurpose, + // style: TextStyle(fontSize: 12), + // decoration: InputDecoration( + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric( + // horizontal: 10), // Proper padding + // ), + // onChanged: widget.isViewMode + // ? null + // : purposeList.isNotEmpty + // ? (newValue) { + // setState(() { + // selectedPurpose = newValue; + // }); + // print( + // "selectedPurpose - $selectedPurpose"); + // } + // : null, + // items: dropdownItems, + // ), + // ), + // ), ], ), ], @@ -1945,25 +1976,89 @@ class CreateNewPlansState extends State { ? Center( child: CircularProgressIndicator()) // Show loading inside dropdown - : DropdownButtonFormField( - value: selectedFuncDept, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + : DropdownSearch>( + selectedItem: funcDeptList.firstWhere( + (item) => + item['dropdown_key'].toString() == + selectedFuncDept, + orElse: () => + {}, // ✅ Safe fallback + ), + items: funcDeptList, + itemAsString: (Map item) => + item['dropdown_value'], + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 180), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search department...", + hintStyle: TextStyle( + fontSize: 13, color: Colors.grey), + contentPadding: + EdgeInsets.symmetric(horizontal: 10), + ), + style: TextStyle(fontSize: 13)), + menuProps: MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, vertical: 6.0), + child: Text( + item['dropdown_value'], + style: TextStyle( + fontSize: + 13), // 👈 Set your desired text size here + ), + ), + ), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + contentPadding: + EdgeInsets.symmetric(horizontal: 1), + border: InputBorder.none, + ), ), onChanged: widget.isViewMode ? null - : funcDeptList.isNotEmpty - ? (newValue) { - setState(() { - selectedFuncDept = newValue; - }); - } - : null, - items: dropdownFuncDeptItems, + : (Map? newValue) { + setState(() { + selectedFuncDept = + newValue?['dropdown_key'].toString(); + }); + print("selectedFuncDept - $selectedFuncDept"); + }, + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem?['dropdown_value'] ?? '', + style: TextStyle(fontSize: 12), + ), + ), ), + + // DropdownButtonFormField( + // value: selectedFuncDept, + // style: TextStyle(fontSize: 12), + // decoration: InputDecoration( + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric( + // horizontal: 10), // Proper padding + // ), + // onChanged: widget.isViewMode + // ? null + // : funcDeptList.isNotEmpty + // ? (newValue) { + // setState(() { + // selectedFuncDept = newValue; + // }); + // } + // : null, + // items: dropdownFuncDeptItems, + // ), + // ), ), ], @@ -2073,6 +2168,212 @@ class CreateNewPlansState extends State { ]; } + Widget _buildApprovalItem(String title, String name) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "$title : ", + style: TextStyle( + fontFamily: "Archivo", + fontSize: 11, + fontWeight: FontWeight.w500, + color: Colors.black87, + ), + ), + Expanded( + child: Text( + name, + style: TextStyle( + fontFamily: "Archivo", + fontSize: 11, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + ), + ], + ); + } + + List _buildApproverControls() { + return [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text("Status : "), + if (!isApproverApproved && !isApproverRejected) + Text(statusValue ?? "", + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.bold, + color: widget.layoutColor ?? Colors.grey, + )), + if (isApproverApproved) + Text("Approved", + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.bold, + color: widget.layoutColor ?? Colors.grey, + )), + if (isApproverRejected) + Text("Rejected", + style: TextStyle( + fontFamily: "Archivo", + fontWeight: FontWeight.bold, + color: widget.layoutColor ?? Colors.grey, + )), + SizedBox( + width: 5, + ), + MouseRegion( + onEnter: (_) { + setState(() { + isStatusExpanded = true; + }); + }, + onExit: (_) { + setState(() { + isStatusExpanded = false; + }); + }, + child: Icon( + Icons.approval, + size: 18, + color: isStatusExpanded ? Colors.green : Colors.grey, + ), + ), + ], + ), + const SizedBox(height: 10, width: 10), + if (widget.isApprover) + GestureDetector( + onTap: () { + setState(() { + isShowApprovalAction = !isShowApprovalAction; + }); + }, + child: Icon( + Icons.edit, + size: 18, + color: isShowApprovalAction ? Colors.green : Colors.grey, + ), + ), + const SizedBox(height: 10, width: 10), + if (isShowApprovalAction) + Row( + mainAxisSize: MainAxisSize.min, + children: [ + MouseRegion( + cursor: widget.isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + disabledBackgroundColor: + statusValue == "Approved" ? Colors.green.shade100 : null, + disabledForegroundColor: + statusValue == "Approved" ? Colors.white : Colors.black, + backgroundColor: isApproverApproved || + (!isApproverApproved && + !isApproverRejected && + statusValue == "Approved") + ? Colors.green + : Colors.grey.shade100, + foregroundColor: isApproverApproved || + (!isApproverApproved && + !isApproverRejected && + statusValue == "Approved") + ? Colors.white + : Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + padding: EdgeInsets.symmetric(horizontal: 18, vertical: 10), + ), + onPressed: widget.isViewMode + ? null + : () async { + final confirmed = await showApproveDialog( + context, widget.layoutColor ?? Colors.grey); + if (confirmed == true) { + setState(() { + isApproverApproved = true; + isApproverRejected = false; + statusValue = "Approved"; + }); + callApproveAPI( + selectedPlanId!, + planData['user_id'], + ); + } + }, + child: Text( + isApproverApproved || statusValue == "Approved" + ? "Approved" + : "Approve", + style: TextStyle(fontSize: 12), + ), + ), + ), + const SizedBox(width: 10), + MouseRegion( + cursor: widget.isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + disabledBackgroundColor: statusValue == "Rejected" + ? Colors.redAccent.shade100 + : null, + disabledForegroundColor: + statusValue == "Rejected" ? Colors.white : Colors.black, + backgroundColor: isApproverRejected || + (!isApproverApproved && + !isApproverRejected && + statusValue == "Rejected") + ? Colors.redAccent + : Colors.grey.shade100, + foregroundColor: isApproverRejected || + (!isApproverApproved && + !isApproverRejected && + statusValue == "Rejected") + ? Colors.white + : Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + padding: EdgeInsets.symmetric(horizontal: 18, vertical: 10), + ), + onPressed: widget.isViewMode + ? null + : () async { + final remarks = await showRejectDialog( + context, widget.layoutColor ?? Colors.grey); + if (remarks != null) { + _remarksController.text = remarks; + setState(() { + isApproverApproved = false; + isApproverRejected = true; + statusValue = "Rejected"; + }); + callRejectAPI( + selectedPlanId!, planData['user_id'], remarks); + } + }, + child: Text( + isApproverRejected || statusValue == "Rejected" + ? "Rejected" + : "Reject", + style: TextStyle(fontSize: 12), + ), + ), + ), + ], + ), + ]; + } + void _showInputDialog(String title) { showDialog( context: context, diff --git a/lib/Screens/plans/dynamic_itinerary_stepper.dart b/lib/Screens/plans/dynamic_itinerary_stepper.dart index b60ceab..767801f 100644 --- a/lib/Screens/plans/dynamic_itinerary_stepper.dart +++ b/lib/Screens/plans/dynamic_itinerary_stepper.dart @@ -431,6 +431,7 @@ class _DynamicItineraryState extends State { onOpen: handleEdit, onAddNew: handlecreateNewPlan, isViewMode: widget.isViewMode, + apiData: widget.apiData, onDeleteTrain: (data) => handleItinerarydelete("Train", data), ); break; @@ -440,6 +441,7 @@ class _DynamicItineraryState extends State { onOpen: handleEdit, onAddNew: handlecreateNewPlan, isViewMode: widget.isViewMode, + apiData: widget.apiData, onDeleteTaxi: (data) => handleItinerarydelete("Taxi", data), ); break; @@ -480,6 +482,7 @@ class _DynamicItineraryState extends State { onOpen: handleEdit, onAddNew: handlecreateNewPlan, isViewMode: widget.isViewMode, + apiData: widget.apiData, onDeleteForex: (data) => handleItinerarydelete("Forex", data), ); break; @@ -510,6 +513,7 @@ class _DynamicItineraryState extends State { onOpen: handleEdit, onAddNew: handlecreateNewPlan, isViewMode: widget.isViewMode, + apiData: widget.apiData, onDeleteFlight: (data) => handleItinerarydelete("Flight", data)); break; } @@ -612,7 +616,7 @@ class _DynamicItineraryState extends State { borderRadius: BorderRadius.circular(1), // color: Color(0xFFF4F4FB), ), - padding: EdgeInsets.all(10), + // padding: EdgeInsets.all(10), child: isMobile ? Expanded( child: SingleChildScrollView( @@ -645,6 +649,24 @@ class _DynamicItineraryState extends State { List _buildOptions() { if (ServicesChoosed == null) return []; + if (ServicesChoosed != null && + ServicesChoosed!.isNotEmpty && + selectedListOption == "") { + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + selectedListOption = ServicesChoosed!.first['name']; + isSelected = false; + }); + }); + } + + // if (ServicesChoosed != null && + // ServicesChoosed!.isNotEmpty && + // selectedListOption == "") { + // selectedListOption = ServicesChoosed!.first['name']; + // isSelected = false; + // } + return ServicesChoosed!.map((service) { return Padding( padding: const EdgeInsets.only(right: 20.0), @@ -657,6 +679,76 @@ class _DynamicItineraryState extends State { Widget _buildOption( Map service, bool hasData, + ) { + String name = service['name']; + String iconUrl = service['icon']; // Can be empty string + IconData fallbackIcon = _getLocalIconForService(name); + + bool isOptionSelected = selectedListOption == name; + + return GestureDetector( + onTap: () { + setState(() { + selectedListOption = name; + isSelected = false; + }); + }, + child: Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: isOptionSelected ? Color(0xFF114D8B) : Colors.transparent, + width: 2, + ), + ), + ), + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 10), + child: Row( + children: [ + iconUrl.isNotEmpty + ? Image.network( + iconUrl, + width: 18, + height: 18, + errorBuilder: (context, error, stackTrace) { + return Icon( + fallbackIcon, + size: 18, + color: isOptionSelected + ? Color(0xFF114D8B) + : Color(0xFF475569), + ); + }, + ) + : Icon( + fallbackIcon, + size: 18, + color: isOptionSelected + ? Color(0xFF114D8B) + : Color(0xFF475569), + ), + SizedBox(width: 2), + Text( + name, + style: TextStyle( + fontSize: 14, + color: isOptionSelected ? Color(0xFF114D8B) : Color(0xFF475569), + fontFamily: "Archivo", + fontWeight: + isOptionSelected ? FontWeight.bold : FontWeight.w500, + ), + ), + SizedBox(width: 4), + if (hasData) Icon(Icons.circle, size: 8, color: Colors.green), + ], + ), + ), + ); + } + + Widget _buildOption1( + Map service, + bool hasData, ) { String name = service['name']; String iconUrl = service['icon']; // Can be empty string @@ -701,9 +793,7 @@ class _DynamicItineraryState extends State { : Color(0xFF475569), ), - SizedBox(width: 5), - - SizedBox(width: 5), + SizedBox(width: 2), Text( name, style: TextStyle( @@ -719,20 +809,19 @@ class _DynamicItineraryState extends State { // fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)), ), - SizedBox(width: 5), + SizedBox(width: 2), // if (selectedListOption == title && widget.isViewMode == false) if (hasData) Container( - height: 13, - width: 13, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all(color: Colors.green, width: 1.5), - ), - child: - Icon(Icons.notifications_rounded, size: 8, color: Colors.green - // color: Colors.grey, - )), + height: 10, + width: 10, + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // border: Border.all(color: Colors.green, width: 1.5), + // ), + child: Icon(Icons.circle, size: 8, color: Colors.green + // color: Colors.grey, + )), ]), ); } @@ -744,7 +833,7 @@ class _DynamicItineraryState extends State { case 'train': return Icons.train_outlined; case 'bus': - return Icons.bus_alert_outlined; + return Icons.directions_bus_filled_outlined; case 'taxi': return Icons.local_taxi_outlined; case 'accomodation': diff --git a/lib/Screens/plans/list_plans.dart b/lib/Screens/plans/list_plans.dart index f91e616..a9784d3 100644 --- a/lib/Screens/plans/list_plans.dart +++ b/lib/Screens/plans/list_plans.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:frontend/config/apiUrl.dart'; +import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -198,13 +199,23 @@ class _ListPlansState extends State { return Container( decoration: BoxDecoration( // color: Colors.amber, - color: bodyColor, + // color: bodyColor, + color: Color(0xFFE1F5FE), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: buildTableLayout(isDesktop), ); } Widget buildTableLayout(isDesktop) { + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat('dd MMM yy : hh a').format(dateTime); + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + return Container( margin: isDesktop ? EdgeInsets.all(10.0) @@ -499,7 +510,8 @@ class _ListPlansState extends State { fontSize: 13, fontFamily: "Archivo", ))), - DataCell(Text(plan.createdOn, + DataCell(Text(_formatDate(plan.createdOn), + // plan.createdOn, style: TextStyle( fontSize: 13, fontFamily: "Archivo", diff --git a/lib/Screens/policy/policy.dart b/lib/Screens/policy/policy.dart index 68bfb0a..31e6506 100644 --- a/lib/Screens/policy/policy.dart +++ b/lib/Screens/policy/policy.dart @@ -106,20 +106,21 @@ class _PolicyState extends State { @override void initState() { super.initState(); - WidgetsFlutterBinding.ensureInitialized(); + // WidgetsFlutterBinding.ensureInitialized(); WidgetsBinding.instance.addPostFrameCallback((_) { loadinitializeData(); - loadInitialData(); if (widget.policy != null) { final details = List>.from(widget.policy!['policy_details']); policyCriteriaKey.currentState?.loadPolicyDetails(details); } + updateSelectedServices(); + updateData(); + + loadInitialData(); }); - updateSelectedServices(); - updateData(); } void loadInitialData() async { @@ -440,14 +441,15 @@ class _PolicyState extends State { return Container( // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), decoration: BoxDecoration( - // color: Colors.amber, - color: bodyColor, + color: Color(0xFFE1F5FE), + // color: bodyColor, border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: Column( children: [ Expanded( child: Container( - color: bodyColor, + // color: bodyColor, + color: Color(0xFFE1F5FE), child: buildPolicyLayout(isDesktop), ), ), diff --git a/lib/Screens/policy/policy_list.dart b/lib/Screens/policy/policy_list.dart index 5795e90..de615bc 100644 --- a/lib/Screens/policy/policy_list.dart +++ b/lib/Screens/policy/policy_list.dart @@ -156,7 +156,8 @@ class _PolicyListState extends State { return Container( decoration: BoxDecoration( // color: Colors.amber, - color: bodyColor, + color: Color(0xFFE1F5FE), + // color: bodyColor, border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: buildGroupListLayout(isDesktop), ); diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index daa5879..e560a7f 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -689,7 +689,8 @@ class _CreateUserFormState extends State { // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), decoration: BoxDecoration( // color: Colors.amber, - color: bodyColor, + // color: bodyColor, + color: Color(0xFFE1F5FE), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: Column( children: [ diff --git a/lib/Screens/userManagement/user_List.dart b/lib/Screens/userManagement/user_List.dart index 29fc9d9..b799e76 100644 --- a/lib/Screens/userManagement/user_List.dart +++ b/lib/Screens/userManagement/user_List.dart @@ -265,7 +265,8 @@ class _UserListScreenState extends State { return Container( decoration: BoxDecoration( // color: Colors.amber, - color: bodyColor, + // color: bodyColor, + color: Color(0xFFE1F5FE), border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), child: buildUserTable(isDesktop), ); diff --git a/pubspec.lock b/pubspec.lock index 1248b87..8f7bd5f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -582,7 +582,7 @@ packages: source: hosted version: "14.3.1" web: - dependency: transitive + dependency: "direct main" description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" diff --git a/pubspec.yaml b/pubspec.yaml index aed4efa..c9c14fb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: bcrypt: ^1.1.3 http_parser: ^4.1.2 image_picker: ^1.1.2 + web: ^1.1.0 dev_dependencies: