diff --git a/lib/Screens/allTrips/list_all_plans.dart b/lib/Screens/allTrips/list_all_plans.dart index 7e25490..f7b5e7d 100644 --- a/lib/Screens/allTrips/list_all_plans.dart +++ b/lib/Screens/allTrips/list_all_plans.dart @@ -112,14 +112,14 @@ class _ListAllPlansState extends State { setState(() { layoutColor = - layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; bodyColor = - bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -331,99 +331,99 @@ class _ListAllPlansState extends State { context: context, builder: (context) => AlertDialog( - title: Text( - label, - style: GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - overflow: TextOverflow.ellipsis, - ), - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (approverData.isNotEmpty) - SizedBox( - height: 250, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: DataTable( - columns: [ - // DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), - // DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w600, + title: Text( + label, + style: GoogleFonts.poppins( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + overflow: TextOverflow.ellipsis, + ), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (approverData.isNotEmpty) + SizedBox( + height: 250, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: DataTable( + columns: [ + // DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), + // DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + approverData.asMap().entries.map((entry) { + final index = entry.key + 1; + final item = entry.value; + + final approverIdKey = item.keys.firstWhere( + (k) => k.endsWith('_id'), + orElse: () => '', + ); + final statusKey = item.keys.firstWhere( + (k) => k.endsWith('_status'), + orElse: () => '', + ); + + final approverId = + item[approverIdKey]?.toString() ?? '-'; + final statusText = + item[statusKey]?.toString() ?? 'Unknown'; + + return DataRow( + cells: [ + // DataCell(Text('$index')), + // DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))), + DataCell( + SizedBox( + child: Text( + statusText, + overflow: TextOverflow.ellipsis, ), ), ), ], - rows: - approverData.asMap().entries.map((entry) { - final index = entry.key + 1; - final item = entry.value; - - final approverIdKey = item.keys.firstWhere( - (k) => k.endsWith('_id'), - orElse: () => '', - ); - final statusKey = item.keys.firstWhere( - (k) => k.endsWith('_status'), - orElse: () => '', - ); - - final approverId = - item[approverIdKey]?.toString() ?? '-'; - final statusText = - item[statusKey]?.toString() ?? 'Unknown'; - - return DataRow( - cells: [ - // DataCell(Text('$index')), - // DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))), - DataCell( - SizedBox( - child: Text( - statusText, - overflow: TextOverflow.ellipsis, - ), - ), - ), - ], - ); - }).toList(), - ), - ), - ), - ) - else - Text( - "-- no data. --", - style: GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w300, + ); + }).toList(), ), ), - ], - ), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - _listDialogShown = false; - }, - child: Text("Close"), - ), + ), + ) + else + Text( + "-- no data. --", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w300, + ), + ), ], ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + _listDialogShown = false; + }, + child: Text("Close"), + ), + ], + ), ); } @@ -443,16 +443,16 @@ class _ListAllPlansState extends State { drawer: CustomDrawer(isDesktop: false), body: Padding( padding: - isDesktop - ? EdgeInsets.symmetric( - horizontal: - MediaQuery.of(context).size.width * - 0.1, // 30% of screen width as horizontal padding - vertical: - MediaQuery.of(context).size.height * - 0, // 5% of screen height as vertical padding - ) - : EdgeInsets.all(0), + isDesktop + ? EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * + 0.1, // 30% of screen width as horizontal padding + vertical: + MediaQuery.of(context).size.height * + 0, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(0), child: Row( children: [ // if (isDesktop) CustomDrawer(isDesktop: true), @@ -504,22 +504,22 @@ class _ListAllPlansState extends State { return Container( margin: isDesktop ? EdgeInsets.all(10.0) : null, padding: - isDesktop - ? const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20) - : EdgeInsets.all(3.0), + isDesktop + ? const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20) + : EdgeInsets.all(3.0), height: - isDesktop - ? MediaQuery.of(context).size.height * 0.98 - : MediaQuery.of(context).size.height, + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, decoration: BoxDecoration( border: - isDesktop - ? Border.all( - width: 2, - color: Colors.white, - // color: Color(0xFFF7F7FB), - ) - : null, + isDesktop + ? Border.all( + width: 2, + color: Colors.white, + // color: Color(0xFFF7F7FB), + ) + : null, color: isDesktop ? Colors.white : Color(0xFFFCFCFC), // color: Color(0xFFF7F7FB), @@ -781,390 +781,391 @@ class _ListAllPlansState extends State { // filteredPlans.isNotEmpty ? filteredPlans : allPlans; List plans = - searchController.text.isEmpty ? allPlans : filteredPlans; + searchController.text.isEmpty ? allPlans : filteredPlans; plans.sort( - (a, b) => + (a, b) => int.parse(b.planId).compareTo(int.parse(a.planId)), ); List paginatedPlans = - plans - .skip(currentPage * itemsPerPage) - .take(itemsPerPage) - .toList(); + plans + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); Widget table = LayoutBuilder( builder: (context, constraints) { double minWidth = isDesktop ? constraints.maxWidth : 1300; - return ConstrainedBox( - constraints: BoxConstraints(minWidth: minWidth), - child: DataTable( - dividerThickness: 0.5, - columnSpacing: isDesktop ? 24.0 : 16.0, - border: TableBorder( - horizontalInside: BorderSide( - width: 0.5, - color: Colors.grey.shade200, - ), - ), - columns: [ - DataColumn( - label: Expanded( - child: Text( - 'S.NO', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: + ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + dividerThickness: 0.5, + columnSpacing: isDesktop ? 24.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, ), ), - ), - ), + columns: [ + DataColumn( + label: Text( + 'S.NO', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), - DataColumn( - label: Text( - 'Trip ID', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Trip Name', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), + DataColumn( + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), - DataColumn( - label: Text( - 'Trip Type', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Emp Code', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Traveller', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), + DataColumn( + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), - DataColumn( - label: Text( - 'Created On', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Actions', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - ], - rows: - // paginatedPlans.map((plan) - paginatedPlans.asMap().entries.map((entry) { - int index = entry.key; - var plan = entry.value; - return DataRow( - cells: [ - DataCell( - Text( - // "${index + 1}", - "${(currentPage * itemsPerPage) + index + 1}", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), + DataColumn( + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, ), ), - DataCell( - Text( - plan.planId, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, ), ), - DataCell( - Text( - plan.tripTitle, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, // Allows wrapping - overflow: - TextOverflow - .visible, // Ensures full display - maxLines: null, + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, ), ), - DataCell( - Text( - plan.tripType, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - DataCell( - Text( - plan.employeeCode ?? " - ", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - DataCell( - Text( - plan.userName.isNotEmpty - ? plan.userName - : plan.travellerName, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - - DataCell( - Text( - _formatDate(plan.createdOn), - // plan.createdOn, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - DataCell( - GestureDetector( - onTap: () => _showDetails(plan.planId), - child: Container( - width: - double - .infinity, // Set your desired fixed size (equal width and height) - height: 25, - alignment: Alignment.center, - decoration: BoxDecoration( - color: getStatusColor( - plan.statusValue, - ), - borderRadius: BorderRadius.circular( - 10, - ), + ), + ], + rows: + // paginatedPlans.map((plan) + paginatedPlans.asMap().entries.map((entry) { + int index = entry.key; + var plan = entry.value; + return DataRow( + cells: [ + DataCell( + Text( + // "${index + 1}", + "${(currentPage * itemsPerPage) + index + 1}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", ), - child: Text( - plan.statusValue, - textAlign: TextAlign.center, - style: TextStyle( - color: getStatusTextColor( + ), + ), + DataCell( + Text( + plan.planId, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.tripTitle, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + plan.tripType, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.employeeCode ?? " - ", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.userName.isNotEmpty + ? plan.userName + : plan.travellerName, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + + DataCell( + Text( + _formatDate(plan.createdOn), + // plan.createdOn, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + GestureDetector( + onTap: () => _showDetails(plan.planId), + child: Container( + width: + double + .infinity, // Set your desired fixed size (equal width and height) + height: 25, + alignment: Alignment.center, + decoration: BoxDecoration( + color: getStatusColor( plan.statusValue, ), - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.w400, + borderRadius: BorderRadius.circular( + 10, + ), + ), + child: Text( + plan.statusValue, + textAlign: TextAlign.center, + style: TextStyle( + color: getStatusTextColor( + plan.statusValue, + ), + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), ), ), ), ), - ), - DataCell( - Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: - (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: - EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, + DataCell( + Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: + (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Row( + mainAxisSize: + MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + IconButton( + icon: Icon( + Icons + .remove_red_eye, + color: Color( + 0xFF475569, + ), + size: 18, ), - child: Row( - mainAxisSize: - MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment - .center, - children: [ + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + true, + ); + }, + ), + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") IconButton( - icon: Icon( - Icons - .remove_red_eye, - color: Color( - 0xFF475569, - ), - size: 18, + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, ), tooltip: - 'View The Trip Details', + 'Edit The Trip Details', onPressed: () { Navigator.pop( context, - ); // Close popup manually + ); ApiService.viewPlan( context, plan.planId, isViewMode: - true, + false, ); }, ), - if (plan.statusValue == - "Pending Approval" || - plan.statusValue == - "Partially Approved") - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15, - ), - tooltip: - 'Edit The Trip Details', - onPressed: () { - Navigator.pop( + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + deletePlan( + plan.planId, + ); + }, + ), + IconButton( + icon: Icon( + Icons.download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The PDF', + onPressed: () { + Navigator.pop( + context, + ); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Trip Comments', + onPressed: () { + showDialog( + context: + context, + builder: + ( context, - ); - ApiService.viewPlan( - context, - plan.planId, - isViewMode: - false, - ); - }, - ), - IconButton( - icon: Icon( - Icons - .cancel_rounded, - size: 18, - ), - tooltip: - 'Cancellation The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); - deletePlan( - plan.planId, - ); - }, - ), - IconButton( - icon: Icon( - Icons.download, - color: Color( - 0xFF114D8B, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId + .toString(), + layoutColorForUser: + layoutColor!, + role: + "Admin", ), - size: 18, - ), - tooltip: - 'Download The PDF', - onPressed: () { - Navigator.pop( - context, - ); - apiService - .getPdfDownload( - plan.planId, - ); - }, - ), - IconButton( - icon: const Icon( - Icons.comment, - color: Color( - 0xFF475569, - ), - size: 11, - ), - tooltip: - 'Trip Comments', - onPressed: () { - showDialog( - context: - context, - builder: - ( - context, - ) => CommentModalList( - // planId: plan.planId, - planId: - plan.planId - .toString(), - layoutColorForUser: - layoutColor!, - role: - "Admin", - ), - ); - }, - ), - ], - ), + ); + }, + ), + ], ), ), - ], - ), - ], + ), + ], + ), + ], + ), ), - ), - ], - ); - }).toList(), - ), - ); + ], + ); + }).toList(), + ), + ), + )); }, ); @@ -1224,9 +1225,9 @@ class _ListAllPlansState extends State { // color: Colors.red.shade50, child: Column( mainAxisAlignment: - MainAxisAlignment.center, + MainAxisAlignment.center, crossAxisAlignment: - CrossAxisAlignment.center, + CrossAxisAlignment.center, children: [ Image.asset( plan.tripType == 'Domestic' @@ -1234,10 +1235,10 @@ class _ListAllPlansState extends State { : 'assets/images/IconsImg/International_new.png', // width: 65, height: - plan.tripType == - 'Domestic' - ? 28 - : 30, + plan.tripType == + 'Domestic' + ? 28 + : 30, ), ], ), @@ -1247,21 +1248,15 @@ class _ListAllPlansState extends State { flex: 1, child: Column( crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: [ Text( '${plan.tripTitle}', style: GoogleFonts.poppins( fontSize: 12, fontWeight: - FontWeight.w500, + FontWeight.w500, ), - softWrap: - true, // Allows wrapping - overflow: - TextOverflow - .visible, // Ensures full display - maxLines: null, ), ], ), @@ -1287,144 +1282,144 @@ class _ListAllPlansState extends State { ), itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 1, - // horizontal: 8, - // vertical: 8, - ), - child: Row( - mainAxisSize: - MainAxisSize - .min, - mainAxisAlignment: - MainAxisAlignment - .center, - children: [ - IconButton( - icon: Icon( - Icons - .remove_red_eye, - color: Color( - 0xFF475569, - ), - size: 18, - ), - tooltip: - 'View The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); // Close popup manually - ApiService.viewPlan( - context, - plan.planId, - isViewMode: - true, - ); - }, - ), - if (plan.statusValue == - "Pending Approval" || - plan.statusValue == - "Partially Approved") - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: - 15, - ), - tooltip: - 'Edit The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); - ApiService.viewPlan( - context, - plan.planId, - isViewMode: - false, - ); - }, - ), - IconButton( - icon: Icon( - Icons - .cancel_rounded, - size: 18, - ), - tooltip: - 'Cancellation The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); - deletePlan( - plan.planId, - ); - }, - ), - IconButton( - icon: Icon( - Icons - .download, - color: Color( - 0xFF114D8B, - ), - size: 18, - ), - tooltip: - 'Download The PDF', - onPressed: () { - Navigator.pop( - context, - ); - apiService - .getPdfDownload( - plan.planId, - ); - }, - ), - IconButton( - icon: const Icon( - Icons - .comment, - color: Color( - 0xFF475569, - ), - size: 11, - ), - tooltip: - 'Trip Comments', - onPressed: () { - showDialog( - context: - context, - builder: - ( - context, - ) => CommentModalList( - // planId: plan.planId, - planId: - plan.planId.toString(), - layoutColorForUser: - layoutColor!, - role: - "Admin", - ), - ); - }, - ), - ], - ), - ), + CustomPopupMenuEntry( + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 1, + // horizontal: 8, + // vertical: 8, ), - ], + child: Row( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + IconButton( + icon: Icon( + Icons + .remove_red_eye, + color: Color( + 0xFF475569, + ), + size: 18, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + true, + ); + }, + ), + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: + 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + false, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + deletePlan( + plan.planId, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The PDF', + onPressed: () { + Navigator.pop( + context, + ); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons + .comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Trip Comments', + onPressed: () { + showDialog( + context: + context, + builder: + ( + context, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId.toString(), + layoutColorForUser: + layoutColor!, + role: + "Admin", + ), + ); + }, + ), + ], + ), + ), + ), + ], ), ], ), @@ -1433,14 +1428,14 @@ class _ListAllPlansState extends State { Row( mainAxisAlignment: - MainAxisAlignment.spaceBetween, + MainAxisAlignment.spaceBetween, crossAxisAlignment: - CrossAxisAlignment.end, + CrossAxisAlignment.end, children: [ - Column( + Flexible( child:Column( crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: [ Text( // plan.userName.isNotEmpty @@ -1451,6 +1446,8 @@ class _ListAllPlansState extends State { fontSize: 10, color: Colors.black87, ), + // overflow: TextOverflow.ellipsis, + // maxLines: 2, ), // Text( // // plan.userName.isNotEmpty @@ -1472,43 +1469,43 @@ class _ListAllPlansState extends State { ), ), ], - ), + ),), Column( mainAxisAlignment: - MainAxisAlignment.end, + MainAxisAlignment.end, crossAxisAlignment: - CrossAxisAlignment.end, + CrossAxisAlignment.end, children: [ GestureDetector( onTap: () => _showDetails( - plan.planId, - ), + plan.planId, + ), child: Container( padding: - EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), + EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), decoration: BoxDecoration( color: getStatusColor( plan.statusValue, ), borderRadius: - BorderRadius.circular( - 8, - ), + BorderRadius.circular( + 8, + ), ), child: Text( plan.statusValue, style: GoogleFonts.poppins( color: - getStatusTextColor( - plan.statusValue, - ), + getStatusTextColor( + plan.statusValue, + ), fontSize: 9, fontWeight: - FontWeight.w600, + FontWeight.w600, ), ), ), @@ -1838,34 +1835,34 @@ class _ListAllPlansState extends State { children: [ Expanded( child: - isDesktop - ? (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No Matches Found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey, - ), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) - : (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No Matches Found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey, - ), - ), - ) - : buildMobileCardView(paginatedPlans)), + isDesktop + ? (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No Matches Found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No Matches Found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView(paginatedPlans)), // child: isDesktop // ? SingleChildScrollView( @@ -1922,4 +1919,4 @@ class _ListAllPlansState extends State { } return Colors.grey; } -} +} \ No newline at end of file diff --git a/lib/Screens/allTrips/list_all_plans_24july2025.dart b/lib/Screens/allTrips/list_all_plans_24july2025.dart new file mode 100644 index 0000000..7e25490 --- /dev/null +++ b/lib/Screens/allTrips/list_all_plans_24july2025.dart @@ -0,0 +1,1925 @@ +import 'dart:convert'; +import 'dart:core'; +import 'package:frontend/Screens/allTrips/remarks_list.dart'; +import 'package:frontend/data/models/plan.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.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'; + +import '../../routes/custom_appBar.dart'; +import '../../routes/custom_drawer.dart'; +import '../../routes/mainLayout.dart'; +import '../../services/apiService.dart'; +import '../../utils/auth_utils.dart'; +import '../../utils/pagination.dart'; +import '../../utils/travelAgent_remarks.dart'; +import '../../widgets/custom_popup.dart'; +import '../../widgets/popup_listPlan_action.dart'; + +class ListAllPlans extends StatefulWidget { + const ListAllPlans({super.key}); + + @override + _ListAllPlansState createState() => _ListAllPlansState(); +} + +class _ListAllPlansState extends State { + final ApiService apiService = ApiService(); + + int currentPage = 0; + int itemsPerPage = 10; + + String? userId; + String? orgId; + String? roleUser; + String? token; + String? TripPlanAction; + + Color? layoutColor; + Color? bodyColor; + late Future> futurePlans; + List allPlans = []; + List filteredPlans = []; + TextEditingController searchController = TextEditingController(); + late bool _listDialogShown = false; + + @override + void initState() { + super.initState(); + _checkAuthAndLoadData(); + + // // If token exists, load the dashboard data + // WidgetsBinding.instance.addPostFrameCallback((_) { + // initializeData(); + // loadInitialData(); + // + // // futurePlans.then((plans) { + // // setState(() { + // // allPlans = plans; + // // filteredPlans = plans; + // // }); + // // }); + // }); + // // futurePlans = fetchPlans(); + } + + void _checkAuthAndLoadData() async { + final String? token = await getToken(); // Your async function to get token + + if (token == null || token.isEmpty) { + // Token doesn't exist → redirect to login + context.go( + "/", + ); // or use: router.go("/") if you're using `GoRouter` directly + return; + } else { + getToken(); + initializeData(); + loadInitialData(); + } + } + + void filterPlans(String query) { + print("allPlans before filtering: $allPlans"); + final lowerQuery = query.toLowerCase(); + setState(() { + filteredPlans = + allPlans.where((plan) { + return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); + }).toList(); + currentPage = 0; + }); + print("filteredPlans: $filteredPlans"); + } + + void loadInitialData() async { + String? layoutString = await getLayoutColor(); + String? bodyStringColor = await getBodyColor(); + + setState(() { + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; + + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; + }); + } + + Future initializeData() async { + token = await getToken(); + userId = await getUserId(); + orgId = await getOrgId(); + roleUser = await getRoleUser(); + TripPlanAction = await getTripPlanAction(); + + if (token == null || userId == null) { + print("Token or USerId missing"); + return; + } else { + setState(() { + futurePlans = fetchPlans(); + }); + + // Wait for futurePlans to be fetched and update allPlans + futurePlans.then((plans) { + setState(() { + allPlans = plans; // Set allPlans after the fetch is complete + filteredPlans = + allPlans; // You can update filteredPlans too if needed + }); + }); + } + } + + void refresh() { + print("Refresj"); + setState(() { + futurePlans = fetchPlans(); + }); + + // Wait for futurePlans to be fetched and update allPlans + futurePlans.then((plans) { + setState(() { + allPlans = plans; // Set allPlans after the fetch is complete + filteredPlans = allPlans; // You can update filteredPlans too if needed + }); + }); + } + + Future getUserId() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); + return userData["user_id"]?.toString(); + } catch (e) { + return null; + } + } + return null; + } + + Future getOrgId() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); + return userData["org_id"]?.toString(); + } catch (e) { + return null; + } + } + return null; + } + + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('auth_token'); + } + + // Fetch API Data + Future> fetchPlans() async { + late String apiUrldata; + + apiUrldata = '$apiUrl/api/plans?org_id=$orgId'; + + // if (roleUser == "Org Admin") { + // apiUrldata = '$apiUrl/api/plans?org_id=$orgId'; + // } else { + // apiUrldata = + // '$apiUrl/api/plans/findTravelAgentPlanList?org_id=$orgId&user_id=$userId'; + // } + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', // Add token here + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + // List plansJson = []; + List plansJson = data['data']; + return plansJson.map((json) => Plan.fromJson(json)).toList(); + } else { + throw Exception('Failed to load plans'); + } + } + + Future postPlanData(planData, planId) async { + final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan'; + + final token = await getToken(); // Fetch token + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + planData['is_active'] = "0"; + + print("POSTPlanTesting------- $planData"); // Add plan_id for update + + try { + final response = await http.post( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + body: jsonEncode(planData), // Convert map to JSON + ); + + if (response.statusCode == 200) { + print("Plan Deleted successfully!"); + print("Response: ${response.body}"); + initializeData(); + } else { + print("Failed to submit plan. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + } + } catch (e) { + print(" Error submitting plan: $e"); + } + } + + void deletePlan(String planId) async { + bool confirmed = await apiService.showCancelConfirmationDialog( + context, + layoutColor, + ); + + if (confirmed) { + try { + Map planData = await ApiService.getViewPlan(planId); + print("ViewAAA - $planData"); + refresh(); + // postPlanData(planData, planId); + } catch (e) { + print("Error fetching plan: $e"); + } + } else { + print("User cancelled"); + } + } + + Future _showDetails(id) async { + _listDialogShown = true; + final String apiUrldata = + '$apiUrl/api/plans/get_plan_approval_status?plan_id=$id'; + final String? token = await getToken(); + String label = ""; + List> approverData = []; + + if (token == null) { + print('Token not found. Please log in.'); + label = "Error - Token not found"; + } else { + try { + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + label = data['data']['model_heading'] ?? ""; + approverData = List>.from( + data['data']['approver_data'] ?? [], + ); + } else { + print('Failed to load'); + label = "Error - Failed to load data"; + } + } catch (e) { + print('Exception occurred: $e'); + label = "Error - Something went wrong"; + } + } + + showDialog( + context: context, + builder: + (context) => AlertDialog( + title: Text( + label, + style: GoogleFonts.poppins( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + overflow: TextOverflow.ellipsis, + ), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (approverData.isNotEmpty) + SizedBox( + height: 250, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: DataTable( + columns: [ + // DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), + // DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + approverData.asMap().entries.map((entry) { + final index = entry.key + 1; + final item = entry.value; + + final approverIdKey = item.keys.firstWhere( + (k) => k.endsWith('_id'), + orElse: () => '', + ); + final statusKey = item.keys.firstWhere( + (k) => k.endsWith('_status'), + orElse: () => '', + ); + + final approverId = + item[approverIdKey]?.toString() ?? '-'; + final statusText = + item[statusKey]?.toString() ?? 'Unknown'; + + return DataRow( + cells: [ + // DataCell(Text('$index')), + // DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))), + DataCell( + SizedBox( + child: Text( + statusText, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }).toList(), + ), + ), + ), + ) + else + Text( + "-- no data. --", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w300, + ), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + _listDialogShown = false; + }, + child: Text("Close"), + ), + ], + ), + ); + } + + Widget build(BuildContext context) { + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; + + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // backgroundColor: Color(0xFFFCFCFC), + + // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), + // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), + appBar: CustomAppBar(isDesktop: isDesktop), + drawer: CustomDrawer(isDesktop: false), + body: Padding( + padding: + isDesktop + ? EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * + 0.1, // 30% of screen width as horizontal padding + vertical: + MediaQuery.of(context).size.height * + 0, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(0), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + Expanded(child: buildGroupListLayout(isDesktop)), + ], + ), + ), + ); + }, + ); + } + + Widget buildGroupListLayout(bool isDesktop) { + return Container( + // decoration: BoxDecoration( + // color: Colors.amber, + // // color: bodyColor, + // // color: Color(0xFFE1F5FE), + // // border: Border.all( + // // // color: Color(0xFFF7F7FB), + // // color: Colors.white, + // // width: 3.5) + // ), + child: buildTableLayout(isDesktop), + ); + } + + Widget buildTableLayout(isDesktop) { + // String _formatDate(String rawDate) { + // try { + // final dateTime = DateTime.parse(rawDate); + // return DateFormat('dd, MMM yyyy hh:m a').format(dateTime); + // } catch (e) { + // return rawDate; // fallback if parsing fails + // } + // } + + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat( + 'dd, MMM yyyy HH:mm', + ).format(dateTime); // 24-hour format + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + + return Container( + margin: isDesktop ? EdgeInsets.all(10.0) : null, + padding: + isDesktop + ? const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20) + : EdgeInsets.all(3.0), + height: + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, + decoration: BoxDecoration( + border: + isDesktop + ? Border.all( + width: 2, + color: Colors.white, + // color: Color(0xFFF7F7FB), + ) + : null, + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + // color: Color(0xFFF7F7FB), + + // color: Colors.amber, + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + // padding: const EdgeInsets.all(10.0), + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Text( + 'All Trips', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + + // style: TextStyle( + // fontFamily: "Inter", + // fontSize: isDesktop ? 16 : 14, + // fontWeight: FontWeight.w600, + // color: const Color(0xFF212121) ) + ), + ], + ), + + SizedBox(width: 1), + Spacer(), + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterPlans, + decoration: InputDecoration( + hintText: "Search...", + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), + prefixIcon: Icon( + Icons.search, + color: Color(0xFF9E9DBD), + size: 18, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade200, + width: 0.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, + ), + ), + ), + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + + // SizedBox(width: 16), + Spacer(), + // ElevatedButton( + // style: ElevatedButton.styleFrom( + // foregroundColor: Colors.white, + // backgroundColor: Colors.blueAccent, + // ), + // onPressed: () { + // context.go('/createPlan', extra: { + // 'orgId': orgId, + // }); + // if (!isDesktop) Navigator.pop(context); + // }, + // child: Row( + // children: [ + // Icon(Icons.add_circle, color: Colors.white), + // SizedBox(width: 5), + // Text('NewPlan'), + // + // ], + // ), + // ), + + // 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: () { + // if (TripPlanAction == "Plan Creation Not Allowed") { + // showDialog( + // context: context, + // builder: (context) => AlertDialog( + // title: Text( + // "Action Not Allowed", + // style: TextStyle( + // fontSize: 18, fontWeight: FontWeight.bold), + // ), + // content: Text( + // "Plan Creation Not Allowed For This User."), + // actions: [ + // TextButton( + // onPressed: () => Navigator.pop(context), + // child: Text( + // "OK", + // style: TextStyle(color: layoutColor), + // ), + // ), + // ], + // ), + // ); + // } else { + // context.go('/createPlan', extra: { + // 'orgId': orgId, + // }); + // if (!isDesktop) Navigator.pop(context); + // } + // }, + // child: Row( + // mainAxisSize: + // MainAxisSize.min, // Ensures content fits nicely + // children: [ + // Text( + // "Add New Plan", + // 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: 10), + + if (!isDesktop) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.88, + height: 35, + child: TextField( + controller: searchController, + onChanged: filterPlans, + decoration: InputDecoration( + hintText: "Search...", + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), + prefixIcon: Icon( + Icons.search, + color: Color(0xFF9E9DBD), + size: 18, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade200, + width: 0.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, + ), + ), + ), + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + ], + ), + FutureBuilder>( + future: futurePlans, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + final adjHgt = MediaQuery.of(context).size.height; + + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // const Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // const SizedBox(height: 1), + // Text( + // "Oops!", + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent), + // + // ), + SizedBox(height: adjHgt / 4), + Text( + " No Data Found", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + ], + ), + ), + ); + } + + // List plans = snapshot.data!; + + // List plans = + // filteredPlans.isNotEmpty ? filteredPlans : allPlans; + + List plans = + searchController.text.isEmpty ? allPlans : filteredPlans; + + plans.sort( + (a, b) => + int.parse(b.planId).compareTo(int.parse(a.planId)), + ); + + List paginatedPlans = + plans + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); + + Widget table = LayoutBuilder( + builder: (context, constraints) { + double minWidth = isDesktop ? constraints.maxWidth : 1300; + + return ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + dividerThickness: 0.5, + columnSpacing: isDesktop ? 24.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, + ), + ), + columns: [ + DataColumn( + label: Expanded( + child: Text( + 'S.NO', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + + DataColumn( + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + + DataColumn( + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + + DataColumn( + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + // paginatedPlans.map((plan) + paginatedPlans.asMap().entries.map((entry) { + int index = entry.key; + var plan = entry.value; + return DataRow( + cells: [ + DataCell( + Text( + // "${index + 1}", + "${(currentPage * itemsPerPage) + index + 1}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.planId, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.tripTitle, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, // Allows wrapping + overflow: + TextOverflow + .visible, // Ensures full display + maxLines: null, + ), + ), + DataCell( + Text( + plan.tripType, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.employeeCode ?? " - ", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.userName.isNotEmpty + ? plan.userName + : plan.travellerName, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + + DataCell( + Text( + _formatDate(plan.createdOn), + // plan.createdOn, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + GestureDetector( + onTap: () => _showDetails(plan.planId), + child: Container( + width: + double + .infinity, // Set your desired fixed size (equal width and height) + height: 25, + alignment: Alignment.center, + decoration: BoxDecoration( + color: getStatusColor( + plan.statusValue, + ), + borderRadius: BorderRadius.circular( + 10, + ), + ), + child: Text( + plan.statusValue, + textAlign: TextAlign.center, + style: TextStyle( + color: getStatusTextColor( + plan.statusValue, + ), + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ), + DataCell( + Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: + (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Row( + mainAxisSize: + MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + IconButton( + icon: Icon( + Icons + .remove_red_eye, + color: Color( + 0xFF475569, + ), + size: 18, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + true, + ); + }, + ), + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + false, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + deletePlan( + plan.planId, + ); + }, + ), + IconButton( + icon: Icon( + Icons.download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The PDF', + onPressed: () { + Navigator.pop( + context, + ); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Trip Comments', + onPressed: () { + showDialog( + context: + context, + builder: + ( + context, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId + .toString(), + layoutColorForUser: + layoutColor!, + role: + "Admin", + ), + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), + ), + ], + ); + }).toList(), + ), + ); + }, + ); + + Widget buildMobileCardView(List paginatedPlans) { + return ListView.builder( + itemCount: paginatedPlans.length, + itemBuilder: (context, index) { + final plan = paginatedPlans[index]; + return Card( + color: Colors.white, + margin: EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, // Control the strength of the shadow + // shadowColor: Colors.black54, // Softer shadow + // clipBehavior: Clip.antiAlias, + child: Padding( + padding: const EdgeInsets.all(10.0), + + child: Row( + children: [ + // Container( + // // color: Colors.red.shade50, + // child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: + // CrossAxisAlignment.center, + // children: [ + // Image.asset( + // plan.tripType == 'Domestic' + // ? 'assets/images/IconsImg/Domestic_new.png' + // : 'assets/images/IconsImg/International_new.png', + // // width: 65, + // height: + // plan.tripType == 'Domestic' + // ? 37 + // : 40, + // ), + // ], + // ), + // ), + // SizedBox(width: 10), + Expanded( + child: Container( + // color: Colors.yellow.shade50, + child: Column( + children: [ + Row( + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + children: [ + Container( + // color: Colors.red.shade50, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Image.asset( + plan.tripType == 'Domestic' + ? 'assets/images/IconsImg/Domestic_new.png' + : 'assets/images/IconsImg/International_new.png', + // width: 65, + height: + plan.tripType == + 'Domestic' + ? 28 + : 30, + ), + ], + ), + ), + SizedBox(width: 10), + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '${plan.tripTitle}', + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: + FontWeight.w500, + ), + softWrap: + true, // Allows wrapping + overflow: + TextOverflow + .visible, // Ensures full display + maxLines: null, + ), + ], + ), + ), + + SizedBox(width: 10), + Column( + children: [ + // PlanPopupMenu( + // plan: plan, + // deletePlan: deletePlan, + // apiService: apiService, + // layoutColor: layoutColor, + // ), + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: + (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 1, + // horizontal: 8, + // vertical: 8, + ), + child: Row( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + IconButton( + icon: Icon( + Icons + .remove_red_eye, + color: Color( + 0xFF475569, + ), + size: 18, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + true, + ); + }, + ), + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: + 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + false, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + deletePlan( + plan.planId, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The PDF', + onPressed: () { + Navigator.pop( + context, + ); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons + .comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Trip Comments', + onPressed: () { + showDialog( + context: + context, + builder: + ( + context, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId.toString(), + layoutColorForUser: + layoutColor!, + role: + "Admin", + ), + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), + ], + ), + + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + crossAxisAlignment: + CrossAxisAlignment.end, + + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + // plan.userName.isNotEmpty + // ? plan.userName + // : '${plan.travellerName}', + '${plan.userName.isNotEmpty ? plan.userName : plan.travellerName} (${plan.employeeCode ?? 'No Data'})', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + // Text( + // // plan.userName.isNotEmpty + // // ? plan.userName + // // : '${plan.travellerName}', + // // '(${plan.employeeCode ?? 'No Data'})', + // plan.employeeCode ?? + // 'No Data', + // style: GoogleFonts.poppins( + // fontSize: 9, + // color: Colors.black87, + // ), + // ), + Text( + '${_formatDate(plan.createdOn)}', + style: GoogleFonts.poppins( + fontSize: 9, + color: Colors.grey, + ), + ), + ], + ), + Column( + mainAxisAlignment: + MainAxisAlignment.end, + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + GestureDetector( + onTap: + () => _showDetails( + plan.planId, + ), + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + color: getStatusColor( + plan.statusValue, + ), + borderRadius: + BorderRadius.circular( + 8, + ), + ), + child: Text( + plan.statusValue, + style: GoogleFonts.poppins( + color: + getStatusTextColor( + plan.statusValue, + ), + fontSize: 9, + fontWeight: + FontWeight.w600, + ), + ), + ), + ), + ], + ), + ], + ), + ], + ), + ), + ), + ], + ), + + // padding: EdgeInsets.symmetric(vertical: 12), + // child: Container( + // // color: Colors.yellow, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // // Status and Employee Code + // Row( + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // GestureDetector( + // onTap: + // () => _showDetails(plan.planId), + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, + // vertical: 4, + // ), + // decoration: BoxDecoration( + // color: getStatusColor( + // plan.statusValue, + // ), + // borderRadius: + // BorderRadius.circular(8), + // ), + // child: Text( + // plan.statusValue, + // style: TextStyle( + // color: getStatusTextColor( + // plan.statusValue, + // ), + // fontSize: 10, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // ), + // ], + // ), + // Column( + // children: [ + // // PlanPopupMenu( + // // plan: plan, + // // deletePlan: deletePlan, + // // apiService: apiService, + // // layoutColor: layoutColor, + // // ), + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon( + // Icons.more_vert, + // color: Color(0xFF475569), + // size: 14, + // ), + // itemBuilder: + // (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: + // EdgeInsets.symmetric( + // horizontal: 1, + // vertical: 1, + // // horizontal: 8, + // // vertical: 8, + // ), + // child: Row( + // mainAxisSize: + // MainAxisSize.min, + // mainAxisAlignment: + // MainAxisAlignment + // .center, + // children: [ + // IconButton( + // icon: Icon( + // Icons + // .remove_red_eye, + // color: Color( + // 0xFF475569, + // ), + // size: 18, + // ), + // tooltip: + // 'View The Trip Details', + // onPressed: () { + // Navigator.pop( + // context, + // ); // Close popup manually + // ApiService.viewPlan( + // context, + // plan.planId, + // isViewMode: + // true, + // ); + // }, + // ), + // if (plan.statusValue == + // "Pending Approval" || + // plan.statusValue == + // "Partially Approved") + // IconButton( + // icon: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15, + // ), + // tooltip: + // 'Edit The Trip Details', + // onPressed: () { + // Navigator.pop( + // context, + // ); + // ApiService.viewPlan( + // context, + // plan.planId, + // isViewMode: + // false, + // ); + // }, + // ), + // IconButton( + // icon: Icon( + // Icons + // .cancel_rounded, + // size: 18, + // ), + // tooltip: + // 'Cancellation The Trip Details', + // onPressed: () { + // Navigator.pop( + // context, + // ); + // deletePlan( + // plan.planId, + // ); + // }, + // ), + // IconButton( + // icon: Icon( + // Icons.download, + // color: Color( + // 0xFF114D8B, + // ), + // size: 18, + // ), + // tooltip: + // 'Download The PDF', + // onPressed: () { + // Navigator.pop( + // context, + // ); + // apiService + // .getPdfDownload( + // plan.planId, + // ); + // }, + // ), + // IconButton( + // icon: const Icon( + // Icons.comment, + // color: Color( + // 0xFF475569, + // ), + // size: 11, + // ), + // tooltip: + // 'Trip Comments', + // onPressed: () { + // showDialog( + // context: + // context, + // builder: + // ( + // context, + // ) => CommentModalList( + // // planId: plan.planId, + // planId: + // plan.planId + // .toString(), + // layoutColorForUser: + // layoutColor!, + // role: + // "Admin", + // ), + // ); + // }, + // ), + // ], + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // ], + // ), + // + // SizedBox(height: 2), + // // Trip Id and Trip Name + // Row( + // children: [ + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text( + // ' ${plan.tripTitle}', + // style: TextStyle( + // fontSize: 12, + // fontFamily: "Inter", + // fontWeight: FontWeight.bold, + // ), + // ), + // ], + // ), + // ], + // ), + // SizedBox(height: 2), + // // Type and Created On + // Row( + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text( + // ' ${plan.tripType}', + // style: TextStyle( + // fontSize: 9, + // color: Colors.black87, + // fontFamily: "Inter", + // ), + // ), + // ], + // ), + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text( + // '${_formatDate(plan.createdOn)}', + // style: TextStyle( + // fontSize: 9, + // color: Colors.black87, + // fontFamily: "Inter", + // ), + // ), + // ], + // ), + // ], + // ), + // SizedBox(height: 3), + // // Name + // Row( + // children: [ + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text( + // plan.userName.isNotEmpty + // ? '${plan.userName}' + // : '${plan.travellerName}', + // style: TextStyle( + // fontSize: 9, + // fontFamily: "Inter", + // color: Colors.black87, + // ), + // ), + // ], + // ), + // Spacer(), + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text( + // plan.employeeCode ?? 'No Data', + // style: TextStyle( + // fontSize: 9, + // fontFamily: "Inter", + // color: Colors.black87, + // ), + // ), + // ], + // ), + // ], + // ), + // // Actions + // ], + // ), + // ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop + ? (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No Matches Found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No Matches Found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView(paginatedPlans)), + + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedPlans), + ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: plans.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), + ); + } + + // Helper functions for colors + Color getStatusColor(String status) { + if (status == "Partially Approved") return Colors.yellow.shade100; + if (status == "Approved") return Colors.green.shade100; + if (status == "Completed") return Colors.green.shade500; + if (status == "Rejected") return Colors.red.shade100; + return Colors.grey.shade100; + } + + Color getStatusTextColor(String status) { + if (status == "Partially Approved" || + status == "Approved" || + status == "Rejected") { + return Colors.black; + } else if (status == "Completed") { + return Colors.white; + } + return Colors.grey; + } +} diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 59e1a8d..9a17d5c 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -931,7 +931,12 @@ class _ApprovalListState extends State { builder: (context, constraints) { double minWidth = isDesktop ? constraints.maxWidth : 1300; - return ConstrainedBox( + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: + ConstrainedBox( constraints: BoxConstraints(minWidth: minWidth), child: DataTable( dividerThickness: 0.5, @@ -1416,7 +1421,7 @@ class _ApprovalListState extends State { ); }).toList(), ), - ); + ))); }, ); @@ -1647,7 +1652,7 @@ class _ApprovalListState extends State { CrossAxisAlignment.end, children: [ - Column( + Flexible( child:Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1666,7 +1671,7 @@ class _ApprovalListState extends State { ), ), ], - ), + ),), Column( mainAxisAlignment: MainAxisAlignment.end, diff --git a/lib/Screens/approvals/approval_list_backup24july2025.dart b/lib/Screens/approvals/approval_list_backup24july2025.dart new file mode 100644 index 0000000..59e1a8d --- /dev/null +++ b/lib/Screens/approvals/approval_list_backup24july2025.dart @@ -0,0 +1,1816 @@ +import 'dart:convert'; +import 'dart:core'; +import 'package:frontend/data/models/plan.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.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'; + +import '../../routes/custom_appBar.dart'; +import '../../routes/custom_drawer.dart'; +import '../../services/apiService.dart'; +import '../../utils/auth_utils.dart'; +import '../../utils/pagination.dart'; +import '../../utils/travelAgent_remarks.dart'; +import '../../widgets/custom_popup.dart'; +import '../../widgets/popup_listPlan_action.dart'; +import '../allTrips/remarks_list.dart'; + +class ApprovalList extends StatefulWidget { + const ApprovalList({super.key}); + + @override + _ApprovalListState createState() => _ApprovalListState(); +} + +class _ApprovalListState extends State { + final ApiService apiService = ApiService(); + late Future> futurePlans; + String? userId; + String? orgId; + String? token; + + Color? layoutColor; + Color? bodyColor; + + int currentPage = 0; + int itemsPerPage = 10; + List allPlans = []; + List filteredPlans = []; + TextEditingController searchController = TextEditingController(); + + late bool _dialogShown = false; + Map successList = {}; + + @override + void initState() { + super.initState(); + _checkAuthAndLoadData(); + // getToken(); + // + // WidgetsBinding.instance.addPostFrameCallback((_) { + // initializeData(); + // loadInitialData(); + // }); + + // futurePlans = fetchPlans(); + } + + void _checkAuthAndLoadData() async { + final String? token = await getToken(); // Your async function to get token + + if (token == null || token.isEmpty) { + // Token doesn't exist → redirect to login + context.go( + "/", + ); // or use: router.go("/") if you're using `GoRouter` directly + return; + } + getToken(); + initializeData(); + loadInitialData(); + } + + void loadInitialData() async { + String? layoutString = await getLayoutColor(); + String? bodyStringColor = await getBodyColor(); + + setState(() { + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; + + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; + }); + } + + // Future loadAllGroups() async { + // try { + // final result = await apiService.fetchUserApprovalList(); + // // setState(() { + // // apiAllGroups = result; + // // }); + // print("Fetched services: $result"); + // } catch (e) { + // print('Error fetching role list: $e'); + // } + // } + + Future initializeData() async { + token = await getToken(); + userId = await getUserId(); + orgId = await getOrgId(); + + if (token == null || userId == null) { + print("Token or USerId missing"); + return; + } else { + setState(() { + futurePlans = fetchPlans(); + }); + print("Token availa"); + // Wait for futurePlans to be fetched and update allPlans + futurePlans.then((plans) { + setState(() { + allPlans = plans; // Set allPlans after the fetch is complete + filteredPlans = + allPlans; // You can update filteredPlans too if needed + }); + }); + } + } + + void refresh() { + print("Refresj"); + setState(() { + futurePlans = fetchPlans(); + }); + + // Wait for futurePlans to be fetched and update allPlans + futurePlans.then((plans) { + setState(() { + allPlans = plans; // Set allPlans after the fetch is complete + filteredPlans = allPlans; // You can update filteredPlans too if needed + }); + }); + } + + Future getUserId() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); + return userData["user_id"]?.toString(); + } catch (e) { + return null; + } + } + return null; + } + + Future getOrgId() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); + return userData["org_id"]?.toString(); + } catch (e) { + return null; + } + } + return null; + } + + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('auth_token'); + } + + // Fetch API Data + Future> fetchPlans() async { + // final String apiUrldata = '$apiUrl/api/plans'; + // final String apiUrldata = '$apiUrl/api/plans?user_id=$userId'; + // final String apiUrldata = '$apiUrl/api/plans?user_id=$userId'; + // final String apiUrldata = '$apiUrl/api/plans?org_id=$orgId&user_id=$userId'; + + final String apiUrldata = + '$apiUrl/api/plans/findApprovalList?user_id=$userId&org_id=$orgId'; + + // api/plans?org_id=1&user_id=1 + // final token = await getToken(); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', // Add token here + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + print('plansJson1'); + List plansJson = data['data']; + print('plansJson $plansJson'); + return plansJson.map((json) => Plan.fromJson(json)).toList(); + } else { + throw Exception('Failed to load plans'); + } + } + + Future postPlanData(planData, planId) async { + final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan'; + + final token = await getToken(); // Fetch token + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + planData['is_active'] = "0"; + + print("POSTPlanTesting------- $planData"); // Add plan_id for update + + try { + final response = await http.post( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + body: jsonEncode(planData), // Convert map to JSON + ); + + if (response.statusCode == 200) { + print("Plan Deleted successfully!"); + print("Response: ${response.body}"); + initializeData(); + } else { + print("Failed to submit plan. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + } + } catch (e) { + print(" Error submitting plan: $e"); + } + } + + void deletePlan(String planId) async { + // try { + // Map planData = await getViewPlan(planId); + // print("ViewAAA - $planData"); + // + // postPlanData(planData, planId); + // } catch (e) { + // print("Error fetching plan: $e"); + // } + + bool confirmed = await apiService.showCancelConfirmationDialog( + context, + layoutColor, + ); + + if (confirmed) { + try { + Map planData = await ApiService.getViewPlan(planId); + print("ViewAAA - $planData"); + refresh(); + // postPlanData(planData, planId); + } catch (e) { + print("Error fetching plan: $e"); + } + } else { + print("User cancelled"); + } + } + + Future> getViewPlan(String planId) async { + final String apiUrldata = '$apiUrl/api/plans/find/$planId'; + print("API URL: $apiUrldata"); + // final token = await getToken(); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.put( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', // Add token here + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + final Map? resData = json.decode(response.body); + + return resData?["data"]; + } else { + throw Exception('Failed to load plans'); + } + } + + void viewPlanforApprover( + String planId, + String? approverId, + String? delegaterId, { + bool isViewMode = false, + bool isApprover = true, + }) async { + try { + Map planData = await getViewPlan(planId); + + print("ViewAAA - $planData"); + + context.replace( + '/approver/plans', + extra: { + 'planData': planData, + 'approverId': approverId, + 'delegaterId': delegaterId, + 'isViewMode': isViewMode, + 'isApprover': isApprover, + }, + ); + + // context.go( + // '/createPlan', + // extra: { + // 'planData': planData, + // 'isViewMode': isViewMode, + // 'isApprover': isApprover, + // }, + // ); + } catch (e) { + print("Error fetching plan: $e"); + } + } + + // void viewPlan(String planId, {bool isViewMode = false}) async { + // try { + // Map planData = await getViewPlan(planId); + // print("ViewAAA - $planData"); + // + // context.go('/createPlan', + // extra: {'planData': planData, 'isViewMode': isViewMode}); + // } catch (e) { + // print("Error fetching plan: $e"); + // } + // } + + void filterPlans(String query) { + print("allPlans before filtering: $allPlans"); + final lowerQuery = query.toLowerCase(); + setState(() { + filteredPlans = + allPlans.where((plan) { + return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.approverName?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); + }).toList(); + currentPage = 0; + }); + print("filteredPlans: $filteredPlans"); + } + + // Future _showDetails(Id) async { + // _dialogShown = true; + // final String apiUrldata = '$apiUrl/api/plans/get_plan_approval_status?plan_id=$Id'; + // final String? token = await getToken(); + // + // if (token == null) { + // print('Token not found. Please log in.'); + // successList = { + // "model_heading": "Error - Token not found", + // "approver_data": [], + // }; + // } else { + // try { + // final response = await http.get( + // Uri.parse(apiUrldata), + // headers: { + // 'Authorization': 'Bearer $token', + // 'Content-Type': 'application/json', + // }, + // ); + // + // if (response.statusCode == 200) { + // final data = json.decode(response.body); + // print(data['data']); + // successList = data['data']; + // } else { + // print('Failed to load'); + // successList = { + // "model_heading": "Error - Failed to load data", + // "approver_data": [], + // }; + // } + // } catch (e) { + // print('Exception occurred: $e'); + // successList = { + // "model_heading": "Error - Something went wrong", + // "approver_data": [], + // }; + // } + // } + // + // showDialog( + // context: context, + // builder: (context) => AlertDialog( + // title: Text( + // successList["model_heading"] ?? '', + // style: GoogleFonts.poppins( + // fontSize: 18, + // fontWeight: FontWeight.w600, + // color: Colors.black, + // ), + // overflow: TextOverflow.ellipsis, + // ), + // content: SingleChildScrollView( + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // // Text( + // // successList["Trip Title"] ?? '', + // // style: TextStyle(fontWeight: FontWeight.bold), + // // ), + // const SizedBox(height: 8), + // (successList["approver_data"] != null && successList["approver_data"].isNotEmpty) + // ? SizedBox( + // height: 250, + // child: SingleChildScrollView( + // scrollDirection: Axis.horizontal, + // child: SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: DataTable( + // columns: [ + // DataColumn(label: Text('S.No', style: GoogleFonts.poppins( + // fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black, + // ))), + // DataColumn(label: Text('ID', style: GoogleFonts.poppins( + // fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black, + // ))), + // DataColumn(label: Text('Reason', style: GoogleFonts.poppins( + // fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black, + // ))), + // ], + // rows: successList["approver_data"].asMap().entries.map((entry) { + // final index = entry.key + 1; + // final rawData = entry.value['a1_id']; + // final data = (rawData == null || rawData.toString().trim().isEmpty) ? '-' : rawData.toString(); + // final reason = entry.value['a1_status'] ?? 'Unknown'; + // + // return DataRow( + // cells: [ + // DataCell(Text('$index', style: GoogleFonts.poppins( + // fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black, + // ))), + // DataCell(SizedBox( + // width: 180, + // child: Text(data, style: GoogleFonts.poppins( + // fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black, + // ), overflow: TextOverflow.ellipsis), + // )), + // DataCell(SizedBox( + // width: 280, + // child: Text(reason, style: GoogleFonts.poppins( + // fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black, + // ), overflow: TextOverflow.ellipsis), + // )), + // ], + // ); + // }).toList(), + // ), + // ), + // ), + // ) + // : Text("-- no data. --", style: GoogleFonts.poppins( + // fontSize: 14, fontWeight: FontWeight.w200, color: Colors.black, + // )), + // ], + // ), + // ), + // actions: [ + // TextButton( + // onPressed: () { + // Navigator.pop(context); + // _dialogShown = false; + // }, + // child: Text("Close"), + // ), + // ], + // ), + // ); + // } + + Future _showDetails(id) async { + _dialogShown = true; + final String apiUrldata = + '$apiUrl/api/plans/get_plan_approval_status?plan_id=$id'; + final String? token = await getToken(); + String label = ""; + List> approverData = []; + + if (token == null) { + print('Token not found. Please log in.'); + label = "Error - Token not found"; + } else { + try { + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + label = data['data']['model_heading'] ?? ""; + approverData = List>.from( + data['data']['approver_data'] ?? [], + ); + } else { + print('Failed to load'); + label = "Error - Failed to load data"; + } + } catch (e) { + print('Exception occurred: $e'); + label = "Error - Something went wrong"; + } + } + + showDialog( + context: context, + builder: + (context) => AlertDialog( + title: Text( + label, + style: GoogleFonts.poppins( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + overflow: TextOverflow.ellipsis, + ), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (approverData.isNotEmpty) + SizedBox( + height: 250, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: DataTable( + columns: [ + // DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), + // DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + approverData.asMap().entries.map((entry) { + final index = entry.key + 1; + final item = entry.value; + + final approverIdKey = item.keys.firstWhere( + (k) => k.endsWith('_id'), + orElse: () => '', + ); + final statusKey = item.keys.firstWhere( + (k) => k.endsWith('_status'), + orElse: () => '', + ); + + final approverId = + item[approverIdKey]?.toString() ?? '-'; + final statusText = + item[statusKey]?.toString() ?? 'Unknown'; + + return DataRow( + cells: [ + // DataCell(Text('$index')), + // DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))), + DataCell( + SizedBox( + child: Text( + statusText, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }).toList(), + ), + ), + ), + ) + else + Text( + "-- no data. --", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w300, + ), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + _dialogShown = false; + }, + child: Text("Close"), + ), + ], + ), + ); + } + + Widget build(BuildContext context) { + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; + + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), + // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), + appBar: CustomAppBar(isDesktop: isDesktop), + drawer: CustomDrawer(isDesktop: false), + body: Padding( + padding: + isDesktop + ? EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * + 0.1, // 30% of screen width as horizontal padding + vertical: + MediaQuery.of(context).size.height * + 0, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(0), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + Expanded(child: buildGroupListLayout(isDesktop)), + ], + ), + ), + ); + }, + ); + } + + Widget buildGroupListLayout(bool isDesktop) { + return Container( + // decoration: BoxDecoration( + // // color: Colors.amber, + // // color: bodyColor, + // color: Color(0xFFE1F5FE), + // border: Border.all( + // // color: Color(0xFFF7F7FB), + // color: Colors.white, + // width: 3.5)), + child: buildTableLayout(isDesktop), + ); + } + + Widget buildTableLayout(isDesktop) { + // String _formatDate(String rawDate) { + // try { + // final dateTime = DateTime.parse(rawDate); + // return DateFormat('dd, MMM yyyy hh:m a').format(dateTime); + // } catch (e) { + // return rawDate; // fallback if parsing fails + // } + // } + + final adjHgt = MediaQuery.of(context).size.height; + + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat( + 'dd, MMM yyyy HH:mm', + ).format(dateTime); // 24-hour format + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + + return Container( + margin: isDesktop ? EdgeInsets.all(10.0) : null, + padding: + isDesktop + ? const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20) + : EdgeInsets.all(3.0), + height: + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, + decoration: BoxDecoration( + border: + isDesktop + ? Border.all( + width: 2, + color: Colors.white, + // color: Color(0xFFF7F7FB), + ) + : null, + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + // color: Color(0xFFF7F7FB), + + // color: Colors.amber, + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + // padding: const EdgeInsets.all(10.0), + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + children: [ + Row( + children: [ + Text( + 'My Approvals', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + // style: TextStyle( + // fontFamily: "Inter", + // fontSize: isDesktop ? 16 : 14, + // fontWeight: FontWeight.w600, + // ) + ), + ], + ), + if (isDesktop) + SizedBox(width: MediaQuery.of(context).size.width * 0.23), + + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterPlans, + decoration: InputDecoration( + hintText: "Search...", + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), + prefixIcon: Icon( + Icons.search, + color: Color(0xFF9E9DBD), + size: 18, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade200, + width: 0.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, + ), + ), + ), + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + // SizedBox(width: 16), + ], + ), + + if (!isDesktop) SizedBox(height: 5), + isDesktop + ? SizedBox.shrink() + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.82, + height: 35, + child: TextField( + controller: searchController, + onChanged: filterPlans, + decoration: InputDecoration( + hintText: "Search...", + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), + prefixIcon: Icon( + Icons.search, + color: Color(0xFF9E9DBD), + size: 18, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade200, + width: 0.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, + ), + ), + ), + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + // SizedBox(width: 16), + ], + ), + + const SizedBox(height: 10), + FutureBuilder>( + future: futurePlans, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // SizedBox(height: 1), + // Text("Oops!", + // style: TextStyle( + // fontSize: 22, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent)), + SizedBox(height: adjHgt / 4), + Text( + "No Data Found", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + ], + ), + ), + ); + } + + // List plans = + // filteredPlans.isNotEmpty ? filteredPlans : allPlans; + + // List plans = + // searchController.text.isEmpty ? allPlans : filteredPlans; + + List plans = + searchController.text.isEmpty ? allPlans : filteredPlans; + + plans.sort( + (a, b) => + int.parse(b.planId).compareTo(int.parse(a.planId)), + ); + + List paginatedPlans = + plans + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); + + Widget table = LayoutBuilder( + builder: (context, constraints) { + double minWidth = isDesktop ? constraints.maxWidth : 1300; + + return ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + dividerThickness: 0.5, + columnSpacing: isDesktop ? 24.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, + ), + ), + columns: [ + DataColumn( + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Approver', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + paginatedPlans.map((plan) { + return DataRow( + cells: [ + DataCell( + Text( + plan.planId, + style: TextStyle( + fontSize: 13, + fontFamily: "Archivo", + ), + ), + ), + + DataCell( + Text( + plan.tripTitle, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + plan.employeeCode ?? " - ", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + DataCell( + Text( + plan.userName.isNotEmpty + ? plan.userName + : plan.travellerName, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + // DataCell( + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // mainAxisAlignment: + // MainAxisAlignment.center, + // + // children: [ + // SizedBox(height: 1), + // Text( + // plan.userName.isNotEmpty + // ? plan.userName + // : plan.travellerName, + // style: TextStyle( + // fontSize: 12, + // fontFamily: "Inter", + // ), + // ), + // SizedBox(height: 3), + // Expanded( + // child: Text( + // "(${plan.employeeCode})" ?? + // " - ", + // style: GoogleFonts.poppins( + // fontSize: 10, + // // fontFamily: "Inter", + // ), + // ), + // ), + // ], + // ), + // ), + DataCell( + Text( + plan.approverName ?? " ", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.tripType, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + + DataCell( + Text( + // plan.createdOn, + _formatDate(plan.createdOn), + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + // DataCell( + // Container( + // width: + // double + // .infinity, // Set your desired fixed size (equal width and height) + // height: 25, + // alignment: Alignment.center, + // decoration: BoxDecoration( + // color: getStatusColor( + // plan.statusValue, + // ), + // borderRadius: BorderRadius.circular( + // 8, + // ), + // ), + // + // child: Text( + // plan.statusValue, + // textAlign: TextAlign.center, + // style: TextStyle( + // color: getStatusTextColor( + // plan.statusValue, + // ), + // fontSize: 12, + // fontFamily: "Inter", + // fontWeight: FontWeight.w400, + // ), + // ), + // ), + // ), + DataCell( + GestureDetector( + onTap: () => _showDetails(plan.planId), + child: Container( + width: double.infinity, + height: 25, + alignment: Alignment.center, + decoration: BoxDecoration( + color: getStatusColor( + plan.statusValue, + ), + borderRadius: BorderRadius.circular( + 8, + ), + ), + child: Text( + plan.statusValue, + textAlign: TextAlign.center, + style: TextStyle( + color: getStatusTextColor( + plan.statusValue, + ), + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ), + DataCell( + Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: + (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Row( + mainAxisSize: + MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + IconButton( + icon: Icon( + Icons + .remove_red_eye, + color: Color( + 0xFF475569, + ), + size: 18, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + viewPlanforApprover( + plan.planId, + plan.approverId, + plan.delegaterId, + isViewMode: + true, + isApprover: + true, + ); + }, + ), + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + print( + "Approver Edit : ${plan.approverId}", + ); + print( + "Approver Edit2 : ${plan.delegaterId}", + ); + print( + "Approver Edit3 : ${plan.approver_status}", + ); + Navigator.pop( + context, + ); + ApiService.viewPlanForApprover( + context, + plan.planId, + plan.approverId, + plan.delegaterId, + plan.approver_status, + isViewMode: + false, + isApprover: + true, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + deletePlan( + plan.planId, + ); + }, + ), + IconButton( + icon: Icon( + Icons.download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The PDF', + onPressed: () { + Navigator.pop( + context, + ); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Approver Comment', + onPressed: () { + showDialog( + context: + context, + builder: + ( + context, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId + .toString(), + layoutColorForUser: + layoutColor!, + role: + "Approver", + ), + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), + ), + // DataCell(Row(children: [ + // IconButton( + // icon: const Icon( + // Icons.remove_red_eye, + // color: Color(0xFF475569), + // size: 18, + // ), + // onPressed: () => viewPlanforApprover( + // plan.planId, + // isViewMode: true, + // isApprover: true)), + // + // GestureDetector( + // onTap: () => ApiService.viewPlanForApprover( + // context, plan.planId, + // isViewMode: false, isApprover: true), + // child: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15), + // ), + // + // // IconButton( + // // icon: const Icon(Icons.edit, + // // color: Colors.green), + // // onPressed: () => viewPlanforApprover(plan.planId, + // // isViewMode: false) ), + // + // SizedBox( + // width: 5, + // ), + // GestureDetector( + // onTap: () => deletePlan(plan.planId), + // child: Image.asset( + // 'assets/images/IconsImg/delete.png', + // width: 20, + // height: 15), + // ), + // + // IconButton( + // icon: const Icon( + // Icons.download, + // color: Color(0xFF475569), + // size: 18, + // ), + // onPressed: () { + // apiService.getPdfDownload(plan.planId); + // }), + // ])), + ], + ); + }).toList(), + ), + ); + }, + ); + + Widget buildMobileCardView(List paginatedPlans) { + return ListView.builder( + itemCount: paginatedPlans.length, + itemBuilder: (context, index) { + final plan = paginatedPlans[index]; + return Card( + color: Colors.white, + margin: EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Container( + // color: Colors.yellow.shade50, + child: Column( + children: [ + Row( + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + children: [ + Container( + // color: Colors.red.shade50, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Image.asset( + plan.tripType == 'Domestic' + ? 'assets/images/IconsImg/Domestic_new.png' + : 'assets/images/IconsImg/International_new.png', + // width: 65, + height: plan.tripType == 'Domestic' ? 28 : 30, + ), + ], + ), + ), + SizedBox(width: 10), + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + ' ${plan.tripTitle}', + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: + FontWeight.w500, + ), + ), + ], + ), + ), + + SizedBox(width: 10), + Column( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: + (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 1, + // horizontal: 8, + // vertical: 8, + ), + child: Row( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + IconButton( + icon: Icon( + Icons + .remove_red_eye, + color: Color( + 0xFF475569, + ), + size: 18, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + viewPlanforApprover( + plan.planId, + plan.approverId, + plan.delegaterId, + isViewMode: true, + isApprover: true, + ); + }, + ), + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: + 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + viewPlanforApprover( + plan.planId, + plan.approverId, + plan.delegaterId, + isViewMode: true, + isApprover: true, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop(context); + deletePlan(plan.planId); + }, + ), + IconButton( + icon: Icon( + Icons + .download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The PDF', + onPressed: () { + Navigator.pop(context); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons + .comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Trip Comments', + onPressed: () { + showDialog( + context: context, + builder: + ( + context, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId + .toString(), + layoutColorForUser: + layoutColor!, + role: + "Approver", + ), + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), + ], + ), + + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + crossAxisAlignment: + CrossAxisAlignment.end, + + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '${plan.userName.isNotEmpty ? plan.userName : plan.travellerName} ${(plan.employeeCode) ?? ''}', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + Text( + '${_formatDate(plan.createdOn)}', + style: GoogleFonts.poppins( + fontSize: 9, + color: Colors.grey, + ), + ), + ], + ), + Column( + mainAxisAlignment: + MainAxisAlignment.end, + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + GestureDetector( + onTap: + () => _showDetails( + plan.planId, + ), + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + color: getStatusColor( + plan.statusValue, + ), + borderRadius: + BorderRadius.circular( + 8, + ), + ), + child: Text( + plan.statusValue, + style: GoogleFonts.poppins( + color: + getStatusTextColor( + plan.statusValue, + ), + fontSize: 9, + fontWeight: + FontWeight.w600, + ), + ), + ), + ), + ], + ), + ], + ), + ], + ), + ), + ), + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop + ? (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No Matches Found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No Matches Found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView(paginatedPlans)), + + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedPlans), + ), + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: plans.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), + ); + } +} + +// Helper functions for colors +Color getStatusColor(String status) { + if (status == "Partially Approved") return Colors.yellow.shade100; + if (status == "Approved") return Colors.green.shade100; + if (status == "Completed") return Colors.green.shade500; + if (status == "Rejected") return Colors.red.shade100; + return Colors.grey.shade100; +} + +Color getStatusTextColor(String status) { + if (status == "Partially Approved" || + status == "Approved" || + status == "Rejected") { + return Colors.black; + } else if (status == "Completed") { + return Colors.white; + } + return Colors.grey; +} diff --git a/lib/Screens/dashboard/status_dashboard.dart b/lib/Screens/dashboard/status_dashboard.dart index 2650814..1693f78 100644 --- a/lib/Screens/dashboard/status_dashboard.dart +++ b/lib/Screens/dashboard/status_dashboard.dart @@ -491,33 +491,40 @@ class StatusDashboardState extends State { } Widget statusCard(String label, int count, bool isDesktop) { - return Container( + return SizedBox( width: isDesktop ? 120 : double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), - decoration: BoxDecoration( - color: const Color(0xFFEAF3FB), - borderRadius: BorderRadius.circular(8), - ), - child: Column( - children: [ - Text( - count.toString(), - style: GoogleFonts.poppins( - fontSize: isDesktop ? 18 : 16, - color: Colors.black87, - fontWeight: FontWeight.bold, + height: isDesktop ? 100 : 100, // fixed height for all cards + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + color: const Color(0xFFEAF3FB), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, // vertically center everything + children: [ + Text( + count.toString(), + style: GoogleFonts.poppins( + fontSize: isDesktop ? 18 : 16, + color: Colors.black87, + fontWeight: FontWeight.bold, + ), ), - ), - const SizedBox(height: 5), - Text( - label, - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: isDesktop ? 12 : 11, - color: Colors.black87, + const SizedBox(height: 6), + Text( + label, + maxLines: 2, + overflow: TextOverflow.ellipsis, + softWrap: true, + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: isDesktop ? 12 : 11, + color: Colors.black87, + ), ), - ), - ], + ], + ), ), ); } diff --git a/lib/Screens/itnerary/accomodations.dart b/lib/Screens/itnerary/accomodations.dart index e4560ca..222ee9b 100644 --- a/lib/Screens/itnerary/accomodations.dart +++ b/lib/Screens/itnerary/accomodations.dart @@ -613,13 +613,13 @@ class _AccomodationScreenState extends State { ), ), SizedBox(height: 5), - CustomTextFieldWrapper( + CustomTextFieldItnerarySubWrapper( isFocused: _destinationFocused, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + : null,//MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( @@ -1164,7 +1164,7 @@ class _AccomodationScreenState extends State { child: GestureDetector( onTap: () { _checkInTimeFocusNode.requestFocus(); - _checkInTimeController.text = ""; + // _checkInTimeController.text = ""; // pavithra told me. here client reported the issues here thats why. _selectCheckInTime(context); }, child: AbsorbPointer( @@ -1326,13 +1326,14 @@ class _AccomodationScreenState extends State { ), ), SizedBox(height: 5), - CustomTextFieldWrapper( + // CustomTextFieldWrapper + CustomTextFieldItnerarySubWrapper( isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + : null, // MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( diff --git a/lib/Screens/itnerary/bus.dart b/lib/Screens/itnerary/bus.dart index 9522e4f..0b62071 100644 --- a/lib/Screens/itnerary/bus.dart +++ b/lib/Screens/itnerary/bus.dart @@ -647,7 +647,7 @@ class _BusScreenState extends State { ), ), SizedBox(height: 5), - CustomTextFieldWrapper( + CustomTextFieldItnerarySubWrapper( isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null, diff --git a/lib/Screens/itnerary/flights.dart b/lib/Screens/itnerary/flights.dart index 0a1fa54..f30cb2c 100644 --- a/lib/Screens/itnerary/flights.dart +++ b/lib/Screens/itnerary/flights.dart @@ -926,6 +926,9 @@ class FlightScreenState extends State { alignment: Alignment.centerRight, child: ElevatedButton( style: ElevatedButton.styleFrom( + minimumSize: !isDesktop + ? const Size.fromHeight(48) + : null, // No min height on desktop // height backgroundColor: Colors.green, foregroundColor: Colors.white, shape: RoundedRectangleBorder( @@ -1529,32 +1532,33 @@ class FlightScreenState extends State { // } } - return [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "From*", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - // isFocused: _fromFocus, - // isFocused: focusStates["_from${index}Focused"] ?? false, - isFocused: false, - padding: const EdgeInsets.symmetric(horizontal: 0), - // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: - isCountryLoading - ? Center(child: CircularProgressIndicator()) - : Focus( + return isDesktop + ? [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "From*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + // isFocused: _fromFocus, + // isFocused: focusStates["_from${index}Focused"] ?? false, + isFocused: false, + padding: const EdgeInsets.symmetric(horizontal: 0), + // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: + isCountryLoading + ? Center(child: CircularProgressIndicator()) + : Focus( focusNode: focusNodes["_from${index}FocusNode"], onFocusChange: (hasFocus) { setState(() { @@ -1570,12 +1574,12 @@ class FlightScreenState extends State { }, child: DropdownSearch( enabled: - !(selectedTripType == "Roundtrip" && - index == 2), + !(selectedTripType == "Roundtrip" && + index == 2), selectedItem: - selectedFrom[index] != null - ? countryMap[selectedFrom[index]] - : null, + selectedFrom[index] != null + ? countryMap[selectedFrom[index]] + : null, items: countryMap.values.toList(), // asyncItems: (String filter) async { // final allItems = countryMap.values.toSet().toList(); @@ -1588,7 +1592,7 @@ class FlightScreenState extends State { fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 220), showSearchBox: - true, // Enables search functionality + true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search...", @@ -1606,7 +1610,7 @@ class FlightScreenState extends State { final parts = item.split('\n'); final cityAndCode = parts[0]; final airport = - parts.length > 1 ? parts[1] : ''; + parts.length > 1 ? parts[1] : ''; // Extract city and code from "City (CODE)" final cityMatch = RegExp( @@ -1622,11 +1626,11 @@ class FlightScreenState extends State { ), child: Column( crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: - MainAxisAlignment.spaceBetween, + MainAxisAlignment.spaceBetween, children: [ Text( city, @@ -1696,20 +1700,20 @@ class FlightScreenState extends State { borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: - (focusStates["_from${index}Focused"] ?? - false) - ? layoutColor! - : Colors.white, + (focusStates["_from${index}Focused"] ?? + false) + ? layoutColor! + : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: - (focusStates["_from${index}Focused"] ?? - false) - ? layoutColor - : Colors.white, + (focusStates["_from${index}Focused"] ?? + false) + ? layoutColor + : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), @@ -1770,7 +1774,7 @@ class FlightScreenState extends State { countryMap.entries .firstWhere( (entry) => entry.value == newValue, - ) + ) .key; if (selectedTripType == "Roundtrip") { print('rounfTo${selectedFrom[index]}'); @@ -1782,58 +1786,58 @@ class FlightScreenState extends State { ), ), ), - ), + ), - // child: SizedBox( - // height: 40, - // child: TextField( - // // focusNode: _fromFocusNode, - // focusNode: focusNodes["_from${index}FocusNode"], - // controller: textControllers["_from${index}Controller"], - // style: const TextStyle(fontSize: 12), - // decoration: const InputDecoration( - // labelText: "From", - // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - // floatingLabelBehavior: FloatingLabelBehavior.never, - // border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric(vertical: 16), - // ), - // ), - // ), - ), - if (errorMessages["from_place_$index"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["from_place_$index"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - if (isDesktop) SizedBox(width: 20) else SizedBox(height: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "To*", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - // isFocused: focusStates["_to${index}Focused"] ?? false, - isFocused: false, - padding: const EdgeInsets.symmetric(horizontal: 0), - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: - isCountryLoading - ? Center(child: CircularProgressIndicator()) - : Focus( + // child: SizedBox( + // height: 40, + // child: TextField( + // // focusNode: _fromFocusNode, + // focusNode: focusNodes["_from${index}FocusNode"], + // controller: textControllers["_from${index}Controller"], + // style: const TextStyle(fontSize: 12), + // decoration: const InputDecoration( + // labelText: "From", + // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + // floatingLabelBehavior: FloatingLabelBehavior.never, + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + // ), + // ), + // ), + ), + if (errorMessages["from_place_$index"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["from_place_$index"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + if (isDesktop) SizedBox(width: 20) else SizedBox(height: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "To*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + // isFocused: focusStates["_to${index}Focused"] ?? false, + isFocused: false, + padding: const EdgeInsets.symmetric(horizontal: 0), + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: + isCountryLoading + ? Center(child: CircularProgressIndicator()) + : Focus( focusNode: focusNodes["_to${index}FocusNode"], onFocusChange: (hasFocus) { setState(() { @@ -1848,17 +1852,17 @@ class FlightScreenState extends State { }, child: DropdownSearch( enabled: - !(selectedTripType == "Roundtrip" && - index == 2), + !(selectedTripType == "Roundtrip" && + index == 2), selectedItem: - selectedTo[index] != null - ? countryMap[selectedTo[index]] - : null, + selectedTo[index] != null + ? countryMap[selectedTo[index]] + : null, popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 220), showSearchBox: - true, // Enables search functionality + true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search...", @@ -1876,7 +1880,7 @@ class FlightScreenState extends State { final parts = item.split('\n'); final cityAndCode = parts[0]; final airport = - parts.length > 1 ? parts[1] : ''; + parts.length > 1 ? parts[1] : ''; // Extract city and code from "City (CODE)" final cityMatch = RegExp( @@ -1892,11 +1896,11 @@ class FlightScreenState extends State { ), child: Column( crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: - MainAxisAlignment.spaceBetween, + MainAxisAlignment.spaceBetween, children: [ Text( city, @@ -1948,20 +1952,20 @@ class FlightScreenState extends State { borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: - (focusStates["_to${index}Focused"] ?? - false) - ? layoutColor! - : Colors.white, + (focusStates["_to${index}Focused"] ?? + false) + ? layoutColor! + : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: - (focusStates["_to${index}Focused"] ?? - false) - ? layoutColor - : Colors.white, + (focusStates["_to${index}Focused"] ?? + false) + ? layoutColor + : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), @@ -2018,7 +2022,7 @@ class FlightScreenState extends State { countryMap.entries .firstWhere( (entry) => entry.value == newValue, - ) + ) .key; print(selectedTo[index]); @@ -2033,203 +2037,203 @@ class FlightScreenState extends State { ), ), ), - ), - ), - if (errorMessages["to_place_$index"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["to_place_$index"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - if (isDesktop) Spacer() else SizedBox(height: 8), + ), + ), + if (errorMessages["to_place_$index"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["to_place_$index"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + if (isDesktop) Spacer() else SizedBox(height: 8), - // Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text( - // "Class *", - // style: GoogleFonts.poppins( - // fontSize: 12, - // fontWeight: FontWeight.w600, - // color: Color(0xFF575A74), - // ), - // ), - // SizedBox(height: 5), - // CustomTextFieldItnerarySubWrapper( - // isFocused: focusStates["_class${index}Focused"] ?? false, - // isDesktop: isDesktop, - // // width: isDesktop - // // ? MediaQuery.of(context).size.width * 0.34 - // // : MediaQuery.of(context).size.width * 0.66, - // child: SizedBox( - // height: 40, - // child: - // isFlightClassLoading - // ? const Center(child: CircularProgressIndicator()) - // : DropdownButtonFormField( - // focusNode: focusNodes["_class${index}FocusNode"], - // // focusNode: _tripTypeFocusNode, // Assign the correct focus node - // // controller: _hotelNameController, - // value: selectedClasses[index], - // - // style: TextStyle(fontSize: 12), - // decoration: InputDecoration( - // border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric( - // horizontal: 10, - // ), // Proper padding - // ), - // onChanged: - // purposeList.isNotEmpty - // ? (newValue) { - // setState(() { - // selectedClasses[index] = newValue; - // }); - // - // print(selectedClasses[index]); - // } - // : null, - // items: dropdownItems, - // ), - // ), - // ), - // ], - // ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Class *", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - // isFocused: focusStates["_class${index}Focused"] ?? false, - isFocused: false, - padding: const EdgeInsets.symmetric(horizontal: 0), - isDesktop: isDesktop, - child: SizedBox( - height: 40, - width: double.infinity, - child: Focus( - focusNode: focusNodes["_class${index}FocusNode"], + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // "Class *", + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74), + // ), + // ), + // SizedBox(height: 5), + // CustomTextFieldItnerarySubWrapper( + // isFocused: focusStates["_class${index}Focused"] ?? false, + // isDesktop: isDesktop, + // // width: isDesktop + // // ? MediaQuery.of(context).size.width * 0.34 + // // : MediaQuery.of(context).size.width * 0.66, + // child: SizedBox( + // height: 40, + // child: + // isFlightClassLoading + // ? const Center(child: CircularProgressIndicator()) + // : DropdownButtonFormField( + // focusNode: focusNodes["_class${index}FocusNode"], + // // focusNode: _tripTypeFocusNode, // Assign the correct focus node + // // controller: _hotelNameController, + // value: selectedClasses[index], + // + // style: TextStyle(fontSize: 12), + // decoration: InputDecoration( + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric( + // horizontal: 10, + // ), // Proper padding + // ), + // onChanged: + // purposeList.isNotEmpty + // ? (newValue) { + // setState(() { + // selectedClasses[index] = newValue; + // }); + // + // print(selectedClasses[index]); + // } + // : null, + // items: dropdownItems, + // ), + // ), + // ), + // ], + // ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Class *", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + // isFocused: focusStates["_class${index}Focused"] ?? false, + isFocused: false, + padding: const EdgeInsets.symmetric(horizontal: 0), + isDesktop: isDesktop, + child: SizedBox( + height: 40, + width: double.infinity, + child: Focus( + focusNode: focusNodes["_class${index}FocusNode"], - onFocusChange: (hasFocus) { - setState(() { - focusStates["_class${index}Focused"] = hasFocus; - }); - }, - child: GestureDetector( - // - onTap: () { - // Request focus when user taps - focusNodes["_class${index}FocusNode"]?.requestFocus(); - }, - child: DropdownSearch>( - items: purposeList.cast>(), - key: - selectedClasses[index] == null - ? UniqueKey() - : ValueKey(selectedClasses[index]), - // selectedItem: purposeList.firstWhere( - // (item) => item['dropdown_key'] == selectedClasses[index], - // orElse: () => {}, - // ), - selectedItem: - selectedClasses[index] != null - ? purposeList.firstWhere( - (item) => - item['dropdown_key'] == + onFocusChange: (hasFocus) { + setState(() { + focusStates["_class${index}Focused"] = hasFocus; + }); + }, + child: GestureDetector( + // + onTap: () { + // Request focus when user taps + focusNodes["_class${index}FocusNode"]?.requestFocus(); + }, + child: DropdownSearch>( + items: purposeList.cast>(), + key: + selectedClasses[index] == null + ? UniqueKey() + : ValueKey(selectedClasses[index]), + // selectedItem: purposeList.firstWhere( + // (item) => item['dropdown_key'] == selectedClasses[index], + // orElse: () => {}, + // ), + selectedItem: + selectedClasses[index] != null + ? purposeList.firstWhere( + (item) => + item['dropdown_key'] == selectedClasses[index], orElse: () => {}, ) - : null, - itemAsString: (item) => item['dropdown_value'] ?? '', - popupProps: PopupProps.menu( - showSearchBox: false, - fit: FlexFit.loose, - menuProps: const MenuProps(backgroundColor: Colors.white), - itemBuilder: (context, item, isSelected) { - final bool isNotAllowed = item['is_allowed'] == 'No'; - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 8, - ), - child: Text( - item['dropdown_value'] ?? '', - style: GoogleFonts.poppins( - fontSize: 12, - color: - isNotAllowed - ? Colors.redAccent - : Colors.black, + : null, + itemAsString: (item) => item['dropdown_value'] ?? '', + popupProps: PopupProps.menu( + showSearchBox: false, + fit: FlexFit.loose, + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: (context, item, isSelected) { + final bool isNotAllowed = item['is_allowed'] == 'No'; + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, + ), + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: + isNotAllowed + ? Colors.redAccent + : Colors.black, + ), + ), + ); + }, ), - ), - ); - }, - ), - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - // border: InputBorder.none, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide( - color: - (focusStates["_class${index}Focused"] ?? false) - ? layoutColor! - : Colors.white, - width: 0.5, - ), - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: - (focusStates["_class${index}Focused"] ?? false) - ? layoutColor - : Colors.white, - // : const Color(0xFFD6D5E6), - width: 0.5, - // const Color(0xFFD6D5E6), - ), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: layoutColor, width: 1), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, - ), - ), - ), - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null || selectedItem.isEmpty) { - return Text( - "Select Class", - style: GoogleFonts.poppins( - color: Colors.grey, - fontSize: 13, - ), - ); - } - return Text( - selectedItem['dropdown_value'] ?? '', - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black, - ), - ); - }, - onChanged: - purposeList.isNotEmpty - ? (Map? newValue) async { + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + // border: InputBorder.none, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: + (focusStates["_class${index}Focused"] ?? false) + ? layoutColor! + : Colors.white, + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + (focusStates["_class${index}Focused"] ?? false) + ? layoutColor + : Colors.white, + // : const Color(0xFFD6D5E6), + width: 0.5, + // const Color(0xFFD6D5E6), + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: layoutColor, width: 1), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + ), + ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || selectedItem.isEmpty) { + return Text( + "Select Class", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 13, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ); + }, + onChanged: + purposeList.isNotEmpty + ? (Map? newValue) async { // if (newValue != null && // newValue['is_allowed'] == 'No') { // print('not allowed'); @@ -2255,14 +2259,14 @@ class FlightScreenState extends State { final confirm = await showNotAllowedDialog( context, message: - "You are not entitled for this service. If you wish to continue with this service, you may require another approval.", + "You are not entitled for this service. If you wish to continue with this service, you may require another approval.", ); if (confirm) { setState(() { exceptionalClass[index] = "1"; selectedClasses[index] = - newValue['dropdown_key']; + newValue['dropdown_key']; }); print( "Selected Purpose: ${selectedClasses[index]}", @@ -2271,7 +2275,7 @@ class FlightScreenState extends State { setState(() { exceptionalClass[index] = "0"; selectedClasses[index] = - null; // ❌ Clear selection if user cancels + null; // ❌ Clear selection if user cancels }); print( "selectedItem resolved to: ${selectedClasses[index]}", @@ -2284,210 +2288,1157 @@ class FlightScreenState extends State { setState(() { exceptionalClass[index] = "0"; selectedClasses[index] = - newValue?['dropdown_key']; + newValue?['dropdown_key']; print( "Selected Purpose: ${selectedClasses[index]}", ); }); } } - : null, - ), - ), - ), - ), - ), - if (errorMessages["class_$index"] != null) - Padding( - padding: const EdgeInsets.only(top: 4), - child: Text( - errorMessages["class_$index"]!, - style: GoogleFonts.poppins(fontSize: 12, color: Colors.red), - ), - ), - ], - ), - if (isDesktop) Spacer() else SizedBox(height: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Date*", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: focusStates["_date${index}Focused"] ?? false, - - isDesktop: isDesktop, - width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, - child: SizedBox( - height: 40, - child: GestureDetector( - onTap: () { - focusNodes["_date${index}FocusNode"]?.requestFocus(); - _selectCheckOutDate(context); - }, - child: AbsorbPointer( - child: TextField( - focusNode: focusNodes["_date${index}FocusNode"], - // controller: _dateController, - controller: textControllers["_date${index}Controller"], - readOnly: true, - style: const TextStyle(fontSize: 12), - decoration: InputDecoration( - labelText: "Select Date", - labelStyle: const TextStyle( - fontSize: 12, - color: Colors.grey, - ), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - // border: OutlineInputBorder( - // borderRadius: BorderRadius.circular(8), - // borderSide: BorderSide( - // color: - // (focusStates["_date${index}Focused"] ?? false) - // ? layoutColor! - // : Colors.white, - // width: 0.5, - // ), - // ), - // enabledBorder: OutlineInputBorder( - // borderSide: BorderSide( - // color: - // (focusStates["_date${index}Focused"] ?? false) - // ? layoutColor - // : Colors.white, - // // : const Color(0xFFD6D5E6), - // width: 0.5, - // // const Color(0xFFD6D5E6), - // ), - // ), - // focusedBorder: OutlineInputBorder( - // borderSide: BorderSide(color: layoutColor, width: 1), - // ), - // contentPadding: const EdgeInsets.symmetric( - // horizontal: 10, - // vertical: 5, - // ), - suffixIcon: const Icon( - Icons.calendar_today, - size: 16, - color: Colors.grey, + : null, + ), + ), ), ), ), - ), + if (errorMessages["class_$index"] != null) + Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + errorMessages["class_$index"]!, + style: GoogleFonts.poppins(fontSize: 12, color: Colors.red), + ), + ), + ], ), - ), - ), - if (errorMessages["date_$index"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["date_$index"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - if (isDesktop) Spacer() else SizedBox(height: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Time*", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: focusStates["_time${index}Focused"] ?? false, - isDesktop: isDesktop, - width: isDesktop ? MediaQuery.of(context).size.width * 0.1 : null, - child: SizedBox( - height: 40, - child: GestureDetector( - onTap: () { - focusNodes["_time${index}FocusNode"]?.requestFocus(); - textControllers["_time${index}Controller"]?.text = ""; - _clearError("time_$index"); - // validateTimeDifference(index); - // _selectCheckOutTime(context); + if (isDesktop) Spacer() else SizedBox(height: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Date*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + isFocused: focusStates["_date${index}Focused"] ?? false, - _selectCheckOutTime(context, index, () { - validateTimeDifference(index); - setState( - () {}, - ); // ✅ Force rebuild to show the error immediately - }); - }, - - child: AbsorbPointer( - child: TextField( - focusNode: focusNodes["_time${index}FocusNode"], - // controller: _timeController, - controller: textControllers["_time${index}Controller"], - readOnly: true, - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "Time", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon( - Icons.access_time, - size: 16, - color: Colors.grey, + isDesktop: isDesktop, + width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: () { + focusNodes["_date${index}FocusNode"]?.requestFocus(); + _selectCheckOutDate(context); + }, + child: AbsorbPointer( + child: TextField( + focusNode: focusNodes["_date${index}FocusNode"], + // controller: _dateController, + controller: textControllers["_date${index}Controller"], + readOnly: true, + style: const TextStyle(fontSize: 12), + decoration: InputDecoration( + labelText: "Select Date", + labelStyle: const TextStyle( + fontSize: 12, + color: Colors.grey, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(8), + // borderSide: BorderSide( + // color: + // (focusStates["_date${index}Focused"] ?? false) + // ? layoutColor! + // : Colors.white, + // width: 0.5, + // ), + // ), + // enabledBorder: OutlineInputBorder( + // borderSide: BorderSide( + // color: + // (focusStates["_date${index}Focused"] ?? false) + // ? layoutColor + // : Colors.white, + // // : const Color(0xFFD6D5E6), + // width: 0.5, + // // const Color(0xFFD6D5E6), + // ), + // ), + // focusedBorder: OutlineInputBorder( + // borderSide: BorderSide(color: layoutColor, width: 1), + // ), + // contentPadding: const EdgeInsets.symmetric( + // horizontal: 10, + // vertical: 5, + // ), + suffixIcon: const Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), + ), + ), + ), ), ), ), + if (errorMessages["date_$index"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["date_$index"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + if (isDesktop) Spacer() else SizedBox(height: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Time*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + isFocused: focusStates["_time${index}Focused"] ?? false, + isDesktop: isDesktop, + width: isDesktop ? MediaQuery.of(context).size.width * 0.1 : null, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: () { + focusNodes["_time${index}FocusNode"]?.requestFocus(); + textControllers["_time${index}Controller"]?.text = ""; + _clearError("time_$index"); + // validateTimeDifference(index); + // _selectCheckOutTime(context); + + _selectCheckOutTime(context, index, () { + validateTimeDifference(index); + setState( + () {}, + ); // ✅ Force rebuild to show the error immediately + }); + }, + + child: AbsorbPointer( + child: TextField( + focusNode: focusNodes["_time${index}FocusNode"], + // controller: _timeController, + controller: textControllers["_time${index}Controller"], + readOnly: true, + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Time", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + suffixIcon: Icon( + Icons.access_time, + size: 16, + color: Colors.grey, + ), + ), + ), + ), + // Focus( + // focusNode: focusNodes["_time${index}FocusNode"], + // onFocusChange: (hasFocus) { + // setState(() { + // focusStates["_time${index}Focused"] = hasFocus; + // }); + // }, + // child: GestureDetector( + // // + // onTap: () { + // // Request focus when user taps + // focusNodes["_time${index}FocusNode"]?.requestFocus(); + // }, + ), + ), + ), + if (errorMessages["time_$index"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["time_$index"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + if (selectedTripType == "Multitrip") + Container( + // color: Colors.blueGrey, + // padding: const EdgeInsets.only(top: 50, bottom: 50), + child: IconButton( + onPressed: () { + removeTrip(index); + }, + icon: Icon(Icons.close, color: Colors.redAccent, size: 20), + ), + ), + ] + : [ + // For mobile, wrap inside a card with padding and grey background + Card( + elevation: 1, + margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), + color: const Color(0xFFF5F5F5), // Light grey + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "From*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + // isFocused: _fromFocus, + // isFocused: focusStates["_from${index}Focused"] ?? false, + isFocused: false, + padding: const EdgeInsets.symmetric(horizontal: 0), + // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: + isCountryLoading + ? Center(child: CircularProgressIndicator()) + : Focus( + focusNode: focusNodes["_from${index}FocusNode"], + onFocusChange: (hasFocus) { + setState(() { + focusStates["_from${index}Focused"] = hasFocus; + }); + }, + child: GestureDetector( + // + onTap: () { + // Request focus when user taps + focusNodes["_from${index}FocusNode"] + ?.requestFocus(); + }, + child: DropdownSearch( + enabled: + !(selectedTripType == "Roundtrip" && + index == 2), + selectedItem: + selectedFrom[index] != null + ? countryMap[selectedFrom[index]] + : null, + items: countryMap.values.toList(), + // asyncItems: (String filter) async { + // final allItems = countryMap.values.toSet().toList(); + // print("Original items: ${allItems.length}"); + // final filtered = filterAndSortCountryList(allItems, filter); + // // print("Filtered items: ${filtered.length} -> $filtered"); + // return filtered; + // }, + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 220), + showSearchBox: + true, // Enables search functionality + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search...", + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 1, + ), + ), + style: TextStyle(fontSize: 12), + ), + menuProps: MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: (context, item, isSelected) { + final parts = item.split('\n'); + final cityAndCode = parts[0]; + final airport = + parts.length > 1 ? parts[1] : ''; + + // Extract city and code from "City (CODE)" + final cityMatch = RegExp( + r'^(.*)\s+\(([^)]+)\)$', + ).firstMatch(cityAndCode); + final city = cityMatch?.group(1) ?? ''; + final code = cityMatch?.group(2) ?? ''; + + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + city, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + Text( + code, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 1), + Text( + airport, + style: const TextStyle( + fontSize: 11, + color: Colors.grey, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + }, + + // 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: countryMap.values.toList(), + // filterFn: (item, filter) { + // final lowerFilter = filter.toLowerCase(); + // + // final parts = item.split('\n'); + // final cityAndCode = parts[0]; + // final airport = parts.length > 1 ? parts[1].toLowerCase() : ''; + // + // // Extract city and code from "City (CODE)" + // final cityMatch = RegExp(r'^(.*)\s+\(([^)]+)\)$').firstMatch(cityAndCode); + // final city = cityMatch?.group(1)?.toLowerCase() ?? ''; + // final code = cityMatch?.group(2)?.toLowerCase() ?? ''; + // + // // Priority: Code > City > Airport + // return code.contains(lowerFilter) || + // city.contains(lowerFilter) || + // airport.contains(lowerFilter); + // }, + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: + (focusStates["_from${index}Focused"] ?? + false) + ? layoutColor! + : Colors.white, + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + (focusStates["_from${index}Focused"] ?? + false) + ? layoutColor + : Colors.white, + // : const Color(0xFFD6D5E6), + width: 0.5, + // const Color(0xFFD6D5E6), + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: layoutColor, + width: 1, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + ), + ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null) { + return Align( + alignment: Alignment.centerLeft, + child: const Text( + "Select", + style: TextStyle(fontSize: 12), + ), + ); + } + + final parts = selectedItem.split('\n'); + final cityAndCode = parts[0]; + final airport = parts.length > 1 ? parts[1] : ''; + + return Align( + alignment: Alignment.centerLeft, + child: Text( + cityAndCode ?? "Select", + style: const TextStyle(fontSize: 12), + ), + ); + }, + + // dropdownBuilder: + // (context, selectedItem) => Align( + // alignment: Alignment.centerLeft, + // child: Text( + // selectedItem ?? "Select", + // style: TextStyle(fontSize: 12), + // ), + // ), + onChanged: (String? newValue) { + setState(() { + errorMessages.remove("from_place_$index"); + // selectedFrom[index] = countryMap.entries + // .firstWhere((entry) => entry.value == newValue) + // .key; + + selectedFrom[index] = + countryMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; + if (selectedTripType == "Roundtrip") { + print('rounfTo${selectedFrom[index]}'); + // textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!; + selectedTo[2] = selectedFrom[index]!; + } + }); + }, + ), + ), + ), + ), + + // child: SizedBox( + // height: 40, + // child: TextField( + // // focusNode: _fromFocusNode, + // focusNode: focusNodes["_from${index}FocusNode"], + // controller: textControllers["_from${index}Controller"], + // style: const TextStyle(fontSize: 12), + // decoration: const InputDecoration( + // labelText: "From", + // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + // floatingLabelBehavior: FloatingLabelBehavior.never, + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + // ), + // ), + // ), + ), + if (errorMessages["from_place_$index"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["from_place_$index"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + if (isDesktop) SizedBox(width: 20) else SizedBox(height: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "To*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + // isFocused: focusStates["_to${index}Focused"] ?? false, + isFocused: false, + padding: const EdgeInsets.symmetric(horizontal: 0), + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: + isCountryLoading + ? Center(child: CircularProgressIndicator()) + : Focus( + focusNode: focusNodes["_to${index}FocusNode"], + onFocusChange: (hasFocus) { + setState(() { + focusStates["_to${index}Focused"] = hasFocus; + }); + }, + child: GestureDetector( + // + onTap: () { + // Request focus when user taps + focusNodes["_to${index}FocusNode"]?.requestFocus(); + }, + child: DropdownSearch( + enabled: + !(selectedTripType == "Roundtrip" && + index == 2), + selectedItem: + selectedTo[index] != null + ? countryMap[selectedTo[index]] + : null, + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 220), + showSearchBox: + true, // Enables search functionality + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search...", + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 1, + ), + ), + style: TextStyle(fontSize: 12), + ), + menuProps: MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: (context, item, isSelected) { + final parts = item.split('\n'); + final cityAndCode = parts[0]; + final airport = + parts.length > 1 ? parts[1] : ''; + + // Extract city and code from "City (CODE)" + final cityMatch = RegExp( + r'^(.*)\s+\(([^)]+)\)$', + ).firstMatch(cityAndCode); + final city = cityMatch?.group(1) ?? ''; + final code = cityMatch?.group(2) ?? ''; + + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + city, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + Text( + code, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 1), + Text( + airport, + style: const TextStyle( + fontSize: 11, + color: Colors.grey, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + }, + // 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: countryMap.values.toList(), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: + (focusStates["_to${index}Focused"] ?? + false) + ? layoutColor! + : Colors.white, + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + (focusStates["_to${index}Focused"] ?? + false) + ? layoutColor + : Colors.white, + // : const Color(0xFFD6D5E6), + width: 0.5, + // const Color(0xFFD6D5E6), + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: layoutColor, + width: 1, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + ), + ), + + // dropdownBuilder: + // (context, selectedItem) => Align( + // alignment: Alignment.centerLeft, + // child: Text( + // selectedItem ?? "Select Country", + // style: TextStyle(fontSize: 12), + // ), + // ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null) { + return Align( + alignment: Alignment.centerLeft, + child: const Text( + "Select", + style: TextStyle(fontSize: 12), + ), + ); + } + + final parts = selectedItem.split('\n'); + final cityAndCode = parts[0]; + final airport = parts.length > 1 ? parts[1] : ''; + + return Align( + alignment: Alignment.centerLeft, + child: Text( + cityAndCode ?? "Select", + style: const TextStyle(fontSize: 12), + ), + ); + }, + onChanged: (String? newValue) { + setState(() { + errorMessages.remove("to_place_$index"); + selectedTo[index] = + countryMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; + + print(selectedTo[index]); + + if (selectedTripType == "Roundtrip") { + print('rounfTo${selectedTo[index]}'); + // textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!; + selectedFrom[2] = selectedTo[index]!; + } + }); + }, + ), + ), + ), + ), + ), + if (errorMessages["to_place_$index"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["to_place_$index"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + if (isDesktop) Spacer() else SizedBox(height: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Class *", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + // isFocused: focusStates["_class${index}Focused"] ?? false, + isFocused: false, + padding: const EdgeInsets.symmetric(horizontal: 0), + isDesktop: isDesktop, + child: SizedBox( + height: 40, + width: double.infinity, + child: Focus( + focusNode: focusNodes["_class${index}FocusNode"], + + onFocusChange: (hasFocus) { + setState(() { + focusStates["_class${index}Focused"] = hasFocus; + }); + }, + child: GestureDetector( + // + onTap: () { + // Request focus when user taps + focusNodes["_class${index}FocusNode"]?.requestFocus(); + }, + child: DropdownSearch>( + items: purposeList.cast>(), + key: + selectedClasses[index] == null + ? UniqueKey() + : ValueKey(selectedClasses[index]), + // selectedItem: purposeList.firstWhere( + // (item) => item['dropdown_key'] == selectedClasses[index], + // orElse: () => {}, + // ), + selectedItem: + selectedClasses[index] != null + ? purposeList.firstWhere( + (item) => + item['dropdown_key'] == + selectedClasses[index], + orElse: () => {}, + ) + : null, + itemAsString: (item) => item['dropdown_value'] ?? '', + popupProps: PopupProps.menu( + showSearchBox: false, + fit: FlexFit.loose, + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: (context, item, isSelected) { + final bool isNotAllowed = item['is_allowed'] == 'No'; + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, + ), + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: + isNotAllowed + ? Colors.redAccent + : Colors.black, + ), + ), + ); + }, + ), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + // border: InputBorder.none, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: + (focusStates["_class${index}Focused"] ?? false) + ? layoutColor! + : Colors.white, + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + (focusStates["_class${index}Focused"] ?? false) + ? layoutColor + : Colors.white, + // : const Color(0xFFD6D5E6), + width: 0.5, + // const Color(0xFFD6D5E6), + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: layoutColor, width: 1), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + ), + ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || selectedItem.isEmpty) { + return Text( + "Select Class", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 13, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ); + }, + onChanged: + purposeList.isNotEmpty + ? (Map? newValue) async { + // if (newValue != null && + // newValue['is_allowed'] == 'No') { + // print('not allowed'); + // + // exceptionalClass[index] = "1"; + // } else if (newValue != null && + // newValue['is_allowed'] == 'yes') { + // print('not allowed'); + // + // exceptionalClass[index] = "0"; + // } + // + // setState(() { + // selectedClasses[index] = + // newValue?['dropdown_key']; + // print( + // "Selected Class: ${selectedClasses[index]}", + // ); + // }); + + if (newValue != null && + newValue['is_allowed'] == 'No') { + final confirm = await showNotAllowedDialog( + context, + message: + "You are not entitled for this service. If you wish to continue with this service, you may require another approval.", + ); + + if (confirm) { + setState(() { + exceptionalClass[index] = "1"; + selectedClasses[index] = + newValue['dropdown_key']; + }); + print( + "Selected Purpose: ${selectedClasses[index]}", + ); + } else { + setState(() { + exceptionalClass[index] = "0"; + selectedClasses[index] = + null; // ❌ Clear selection if user cancels + }); + print( + "selectedItem resolved to: ${selectedClasses[index]}", + ); + } + + return; + } else if (newValue != null && + newValue['is_allowed'] == 'yes') { + setState(() { + exceptionalClass[index] = "0"; + selectedClasses[index] = + newValue?['dropdown_key']; + print( + "Selected Purpose: ${selectedClasses[index]}", + ); + }); + } + } + : null, + ), + ), + ), + ), + ), + if (errorMessages["class_$index"] != null) + Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + errorMessages["class_$index"]!, + style: GoogleFonts.poppins(fontSize: 12, color: Colors.red), + ), + ), + ], + ), + if (isDesktop) Spacer() else SizedBox(height: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Date*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + isFocused: focusStates["_date${index}Focused"] ?? false, + + isDesktop: isDesktop, + width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: () { + focusNodes["_date${index}FocusNode"]?.requestFocus(); + _selectCheckOutDate(context); + }, + child: AbsorbPointer( + child: TextField( + focusNode: focusNodes["_date${index}FocusNode"], + // controller: _dateController, + controller: textControllers["_date${index}Controller"], + readOnly: true, + style: const TextStyle(fontSize: 12), + decoration: InputDecoration( + labelText: "Select Date", + labelStyle: const TextStyle( + fontSize: 12, + color: Colors.grey, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(8), + // borderSide: BorderSide( + // color: + // (focusStates["_date${index}Focused"] ?? false) + // ? layoutColor! + // : Colors.white, + // width: 0.5, + // ), + // ), + // enabledBorder: OutlineInputBorder( + // borderSide: BorderSide( + // color: + // (focusStates["_date${index}Focused"] ?? false) + // ? layoutColor + // : Colors.white, + // // : const Color(0xFFD6D5E6), + // width: 0.5, + // // const Color(0xFFD6D5E6), + // ), + // ), + // focusedBorder: OutlineInputBorder( + // borderSide: BorderSide(color: layoutColor, width: 1), + // ), + // contentPadding: const EdgeInsets.symmetric( + // horizontal: 10, + // vertical: 5, + // ), + suffixIcon: const Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), + ), + ), + ), + ), + ), + ), + if (errorMessages["date_$index"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["date_$index"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + if (isDesktop) Spacer() else SizedBox(height: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Time*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + isFocused: focusStates["_time${index}Focused"] ?? false, + isDesktop: isDesktop, + width: isDesktop ? MediaQuery.of(context).size.width * 0.1 : null, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: () { + focusNodes["_time${index}FocusNode"]?.requestFocus(); + textControllers["_time${index}Controller"]?.text = ""; + _clearError("time_$index"); + // validateTimeDifference(index); + // _selectCheckOutTime(context); + + _selectCheckOutTime(context, index, () { + validateTimeDifference(index); + setState( + () {}, + ); // ✅ Force rebuild to show the error immediately + }); + }, + + child: AbsorbPointer( + child: TextField( + focusNode: focusNodes["_time${index}FocusNode"], + // controller: _timeController, + controller: textControllers["_time${index}Controller"], + readOnly: true, + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Time", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + suffixIcon: Icon( + Icons.access_time, + size: 16, + color: Colors.grey, + ), + ), + ), + ), + // Focus( + // focusNode: focusNodes["_time${index}FocusNode"], + // onFocusChange: (hasFocus) { + // setState(() { + // focusStates["_time${index}Focused"] = hasFocus; + // }); + // }, + // child: GestureDetector( + // // + // onTap: () { + // // Request focus when user taps + // focusNodes["_time${index}FocusNode"]?.requestFocus(); + // }, + ), + ), + ), + if (errorMessages["time_$index"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["time_$index"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), + if (selectedTripType == "Multitrip") + if(multiTripRowCount > 1) + Padding( + padding: const EdgeInsets.only(top: 10), + child: SizedBox( + width: double.infinity, // ensure full width + child: ElevatedButton( + style: ElevatedButton.styleFrom( + minimumSize: const Size.fromHeight(48), // set height + backgroundColor: Colors.red, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + onPressed: () { + print("Pressed: $index"); + removeTrip(index); + }, + child: const Text( + "Remove", + style: TextStyle(fontSize: 14), + ), + ), + ), + ), + // Container( + // // color: Colors.blueGrey, + // // padding: const EdgeInsets.only(top: 50, bottom: 50), + // child: IconButton( + // onPressed: () { + // removeTrip(index); + // }, + // icon: Icon(Icons.close, color: Colors.redAccent, size: 20), + // ), + // ), + ], ), - // Focus( - // focusNode: focusNodes["_time${index}FocusNode"], - // onFocusChange: (hasFocus) { - // setState(() { - // focusStates["_time${index}Focused"] = hasFocus; - // }); - // }, - // child: GestureDetector( - // // - // onTap: () { - // // Request focus when user taps - // focusNodes["_time${index}FocusNode"]?.requestFocus(); - // }, ), ), - ), - if (errorMessages["time_$index"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["time_$index"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ), - if (selectedTripType == "Multitrip") - Container( - // color: Colors.blueGrey, - // padding: const EdgeInsets.only(top: 50, bottom: 50), - child: IconButton( - onPressed: () { - removeTrip(index); - }, - icon: Icon(Icons.close, color: Colors.redAccent, size: 20), - ), - ), ]; } @@ -2520,7 +3471,6 @@ class FlightScreenState extends State { ), ); } - return [ // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ @@ -2576,182 +3526,375 @@ class FlightScreenState extends State { // ], // ), //********// - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Visa Required", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - // use same wrapper as class - // isFocused: focusStates["_visa1Focused"] ?? false, - isFocused: false, - padding: const EdgeInsets.symmetric(horizontal: 0), - isDesktop: isDesktop, - child: SizedBox( - height: 35, - width: double.infinity, - child: Focus( - focusNode: focusNodes["_visa1FocusNode"], - onFocusChange: (hasFocus) { - setState(() { - focusStates["_visa1Focused"] = hasFocus; - }); - }, - child: GestureDetector( - // - onTap: () { - // Request focus when user taps - focusNodes["_visa1FocusNode"]?.requestFocus(); - }, - child: DropdownSearch>( - items: visa_available.cast>(), - selectedItem: visa_available.firstWhere( - (item) => item['dropdown_key'] == selectedvisa_available, - orElse: () => {}, + return isDesktop + ? [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Visa Required", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), - itemAsString: (item) => item['dropdown_value'] ?? '', - popupProps: PopupProps.menu( - showSearchBox: false, - fit: FlexFit.loose, - menuProps: const MenuProps(backgroundColor: Colors.white), - itemBuilder: (context, item, isSelected) { - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 8, - ), - child: Text( - item['dropdown_value'] ?? '', - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black, + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + // use same wrapper as class + // isFocused: focusStates["_visa1Focused"] ?? false, + isFocused: false, + padding: const EdgeInsets.symmetric(horizontal: 0), + isDesktop: isDesktop, + child: SizedBox( + height: 35, + width: double.infinity, + child: Focus( + focusNode: focusNodes["_visa1FocusNode"], + onFocusChange: (hasFocus) { + setState(() { + focusStates["_visa1Focused"] = hasFocus; + }); + }, + child: GestureDetector( + // + onTap: () { + // Request focus when user taps + focusNodes["_visa1FocusNode"]?.requestFocus(); + }, + child: DropdownSearch>( + items: visa_available.cast>(), + selectedItem: visa_available.firstWhere( + (item) => item['dropdown_key'] == selectedvisa_available, + orElse: () => {}, + ), + itemAsString: (item) => item['dropdown_value'] ?? '', + popupProps: PopupProps.menu( + showSearchBox: false, + fit: FlexFit.loose, + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: (context, item, isSelected) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, + ), + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ), + ); + }, + ), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + // border: InputBorder.none, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: + (focusStates["_visa1Focused"] ?? false) + ? layoutColor! + : Colors.white, + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + (focusStates["_visa1Focused"] ?? false) + ? layoutColor + : Colors.white, + // : const Color(0xFFD6D5E6), + width: 0.5, + // const Color(0xFFD6D5E6), + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: layoutColor, width: 1), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + ), + ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || selectedItem.isEmpty) { + return Text( + "Select Option", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 13, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ); + }, + onChanged: + visa_available.isNotEmpty + ? (Map? newValue) { + setState(() { + selectedvisa_available = + newValue?['dropdown_key']; + print("Selected Visa: $selectedvisa_available"); + }); + } + : null, ), ), - ); - }, - ), - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - // border: InputBorder.none, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide( - color: - (focusStates["_visa1Focused"] ?? false) - ? layoutColor! - : Colors.white, - width: 0.5, - ), - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: - (focusStates["_visa1Focused"] ?? false) - ? layoutColor - : Colors.white, - // : const Color(0xFFD6D5E6), - width: 0.5, - // const Color(0xFFD6D5E6), - ), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: layoutColor, width: 1), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, ), ), ), - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null || selectedItem.isEmpty) { - return Text( - "Select Option", - style: GoogleFonts.poppins( - color: Colors.grey, - fontSize: 13, - ), - ); - } - return Text( - selectedItem['dropdown_value'] ?? '', + ], + ), + if (isDesktop) SizedBox(width: 20), + SizedBox(height: 5), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Comments", style: GoogleFonts.poppins( fontSize: 12, - color: Colors.black, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), ), - ); - }, - onChanged: - visa_available.isNotEmpty - ? (Map? newValue) { - setState(() { - selectedvisa_available = - newValue?['dropdown_key']; - print("Selected Visa: $selectedvisa_available"); - }); - } - : null, + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + isFocused: focusStates["_comments1Focused"] ?? false, + isDesktop: isDesktop, + width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null, + child: TextField( + focusNode: focusNodes["_comments1FocusNode"], + // controller: _commentsController, + controller: textControllers["_comments1Controller"], + // maxLines: 6, + // keyboardType: TextInputType.multiline, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + labelText: "Comments", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 1), + ), + ), + ), + ], + ), + if (isDesktop) Spacer(), + SizedBox(height: 5), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: _handleAction(isDesktop), + ), + ], + ), + ] + : [ + Card( + elevation: 1, + margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), + color: const Color(0xFFF5F5F5), // Light grey + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Visa Required", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + // use same wrapper as class + // isFocused: focusStates["_visa1Focused"] ?? false, + isFocused: false, + padding: const EdgeInsets.symmetric(horizontal: 0), + isDesktop: isDesktop, + child: SizedBox( + height: 35, + width: double.infinity, + child: Focus( + focusNode: focusNodes["_visa1FocusNode"], + onFocusChange: (hasFocus) { + setState(() { + focusStates["_visa1Focused"] = hasFocus; + }); + }, + child: GestureDetector( + // + onTap: () { + // Request focus when user taps + focusNodes["_visa1FocusNode"]?.requestFocus(); + }, + child: DropdownSearch>( + items: visa_available.cast>(), + selectedItem: visa_available.firstWhere( + (item) => item['dropdown_key'] == selectedvisa_available, + orElse: () => {}, + ), + itemAsString: (item) => item['dropdown_value'] ?? '', + popupProps: PopupProps.menu( + showSearchBox: false, + fit: FlexFit.loose, + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: (context, item, isSelected) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, + ), + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ), + ); + }, + ), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + // border: InputBorder.none, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: + (focusStates["_visa1Focused"] ?? false) + ? layoutColor! + : Colors.white, + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + (focusStates["_visa1Focused"] ?? false) + ? layoutColor + : Colors.white, + // : const Color(0xFFD6D5E6), + width: 0.5, + // const Color(0xFFD6D5E6), + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: layoutColor, width: 1), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + ), + ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || selectedItem.isEmpty) { + return Text( + "Select Option", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 13, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ); + }, + onChanged: + visa_available.isNotEmpty + ? (Map? newValue) { + setState(() { + selectedvisa_available = + newValue?['dropdown_key']; + print("Selected Visa: $selectedvisa_available"); + }); + } + : null, + ), + ), + ), + ), + ), + ], + ), + if (isDesktop) SizedBox(width: 20), + SizedBox(height: 5), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Comments", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldItnerarySubWrapper( + isFocused: focusStates["_comments1Focused"] ?? false, + isDesktop: isDesktop, + width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null, + child: TextField( + focusNode: focusNodes["_comments1FocusNode"], + // controller: _commentsController, + controller: textControllers["_comments1Controller"], + // maxLines: 6, + // keyboardType: TextInputType.multiline, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + labelText: "Comments", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 1), + ), + ), + ), + ], + ), + ], ), ), ), + if (isDesktop) Spacer(), + SizedBox(height: 10), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: _handleAction(isDesktop), ), - ), - ], - ), - if (isDesktop) SizedBox(width: 20), - SizedBox(height: 5), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Comments", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - SizedBox(height: 5), - CustomTextFieldWrapper( - isFocused: focusStates["_comments1Focused"] ?? false, - isDesktop: isDesktop, - width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null, - child: TextField( - focusNode: focusNodes["_comments1FocusNode"], - // controller: _commentsController, - controller: textControllers["_comments1Controller"], - // maxLines: 6, - // keyboardType: TextInputType.multiline, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - labelText: "Comments", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric(vertical: 1), - ), - ), - ), - ], - ), - if (isDesktop) Spacer(), - SizedBox(height: 5), - Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: _handleAction(isDesktop), - ), - ], - ), - ]; + ], + ), + ]; } List _buildvisa(bool isDesktop) { diff --git a/lib/Screens/itnerary/forex.dart b/lib/Screens/itnerary/forex.dart index b8f619e..dc31c7c 100644 --- a/lib/Screens/itnerary/forex.dart +++ b/lib/Screens/itnerary/forex.dart @@ -2198,7 +2198,7 @@ class _ForexScreenState extends State { ), ), SizedBox(height: 5), - CustomTextFieldWrapper( + CustomTextFieldForexWrapper( isFocused: focusStates["_comments"] ?? false, // Dropdown doesn't use focus isDesktop: isDesktop, @@ -2301,7 +2301,7 @@ class _ForexScreenState extends State { ), ), SizedBox(height: 5), - CustomTextFieldWrapper( + CustomTextFieldForexWrapper( isFocused: focusStates["_deliveryLocation"] ?? false, // Dropdown doesn't use focus @@ -2344,7 +2344,7 @@ class _ForexScreenState extends State { children: [ Row( crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: isDesktop ? MainAxisAlignment.center : MainAxisAlignment.start, children: [ Container( alignment: Alignment.bottomLeft, diff --git a/lib/Screens/itnerary/insurance.dart b/lib/Screens/itnerary/insurance.dart index f2e9a92..6e0a724 100644 --- a/lib/Screens/itnerary/insurance.dart +++ b/lib/Screens/itnerary/insurance.dart @@ -1333,7 +1333,7 @@ class _InsuranceScreenState extends State { width: isDesktop ? MediaQuery.of(context).size.width * 0.31 - : MediaQuery.of(context).size.width * 0.66, + : null, // MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( diff --git a/lib/Screens/itnerary/miscellaneous.dart b/lib/Screens/itnerary/miscellaneous.dart index 745383f..e5e0a06 100644 --- a/lib/Screens/itnerary/miscellaneous.dart +++ b/lib/Screens/itnerary/miscellaneous.dart @@ -309,7 +309,7 @@ class _MiscellaneousScreenState extends State { width: isDesktop ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + : null,//MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, width: double.infinity, @@ -432,13 +432,14 @@ class _MiscellaneousScreenState extends State { ), ), SizedBox(height: 5), - CustomTextFieldWrapper( + // CustomTextFieldWrapper + CustomTextFieldItnerarySubWrapper( isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + : null,//MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( diff --git a/lib/Screens/itnerary/taxi.dart b/lib/Screens/itnerary/taxi.dart index 3a2b12f..c0e5649 100644 --- a/lib/Screens/itnerary/taxi.dart +++ b/lib/Screens/itnerary/taxi.dart @@ -603,7 +603,7 @@ class _TaxiScreenState extends State { width: isDesktop ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + : null,//MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, width: double.infinity, @@ -1019,13 +1019,13 @@ class _TaxiScreenState extends State { ), ), SizedBox(height: 5), - CustomTextFieldWrapper( + CustomTextFieldItnerarySubWrapper( isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + : null,//MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, diff --git a/lib/Screens/itnerary/train.dart b/lib/Screens/itnerary/train.dart index 446eabf..58342e1 100644 --- a/lib/Screens/itnerary/train.dart +++ b/lib/Screens/itnerary/train.dart @@ -456,7 +456,7 @@ class _TrainScreenState extends State { dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ - CustomTextFieldWrapper( + CustomTextFieldItnerarySubWrapper( isFocused: _trainNoFocused, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null, @@ -653,7 +653,8 @@ class _TrainScreenState extends State { // ), // ), // ), - CustomTextFieldWrapper( + // CustomTextFieldWrapper + CustomTextFieldItnerarySubWrapper( isFocused: _typeOfClassFocus, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, @@ -1332,7 +1333,7 @@ class _TrainScreenState extends State { ), ), SizedBox(height: 5), - CustomTextFieldWrapper( + CustomTextFieldItnerarySubWrapper( isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null, diff --git a/lib/Screens/itnerary/visa.dart b/lib/Screens/itnerary/visa.dart index 6ca8052..28682d4 100644 --- a/lib/Screens/itnerary/visa.dart +++ b/lib/Screens/itnerary/visa.dart @@ -542,14 +542,14 @@ class _VisaScreenState extends State { // ), // ), // ), - CustomTextFieldItnerarySubWrapper( + CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, padding: const EdgeInsets.symmetric(horizontal: 0), width: isDesktop ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + : null,//MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: Focus( @@ -586,7 +586,7 @@ class _VisaScreenState extends State { vertical: 6, ), child: Padding( - padding: const EdgeInsets.only(right: 4.0), + padding: const EdgeInsets.only(right: 1.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -927,7 +927,7 @@ class _VisaScreenState extends State { width: isDesktop ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + : null, //MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( diff --git a/lib/Screens/itnerary_list/accomodation_list.dart b/lib/Screens/itnerary_list/accomodation_list.dart index 6599b5c..4eb8b3b 100644 --- a/lib/Screens/itnerary_list/accomodation_list.dart +++ b/lib/Screens/itnerary_list/accomodation_list.dart @@ -34,10 +34,15 @@ class AccomodationListWidget extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Text( - // "Accomodation Booking List", - // style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - // ), + if ((!isDesktop) && accommodationList.isNotEmpty) + Text( + " ", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + MouseRegion( cursor: isViewMode ? SystemMouseCursors.forbidden @@ -64,6 +69,24 @@ class AccomodationListWidget extends StatelessWidget { size: 30, color: Color(0xFF114D8B), ) + // Container( + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // border: Border.all( + // color: Color(0xFF114D8B), // Outline color + // width: 2, // Outline thickness + // ), + // ), + // height: 30, + // width: 30, + // child: Center( + // child: Icon( + // Icons.add, + // size: 20, + // color: Colors.black, + // ), + // ), + // ), ], ), ), @@ -240,7 +263,7 @@ class AccomodationListWidget extends StatelessWidget { style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w800, - color: Color(0xFF575A74)), + color: !isDesktop ? Color(0xFF114D8B) : Color(0xFF575A74),) ), Spacer(), GestureDetector( @@ -251,6 +274,7 @@ class AccomodationListWidget extends StatelessWidget { 'assets/images/IconsImg/edit.png', width: 20, height: 15, + color: Color(0xFF114D8B), ), ), ), @@ -263,6 +287,7 @@ class AccomodationListWidget extends StatelessWidget { 'assets/images/IconsImg/delete.png', width: 20, height: 15, + color: Colors.red, ), ), ), @@ -356,21 +381,82 @@ class AccomodationListWidget extends StatelessWidget { : 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"]!, + Wrap( + spacing: 20, // horizontal space between items + runSpacing: 10, // vertical space between rows + children: [ + _buildInfoColumn("City", item["destination_city"]), + _buildInfoColumn("Check (In)", "${formatDate(item["checkin_date"]!)} ${formatTime(item["checkin_time"]!)}"), + _buildInfoColumn("Check (Out)", "${formatDate(item["checkout_date"]!)} ${formatTime(item["checkout_time"]!)}"), + // City - long name handled with flexible space + // Expanded( + // flex: 3, + // child: _buildInfoColumn("City", item["destination_city"]), + // ), + + // SizedBox(width: 8), + + // Check In and Check Out stacked right side + // Expanded( + // flex: 4, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.end, + // children: [ + // _buildInfoColumn( + // "Check In", + // "${formatDate(item["checkin_date"]!)} ${formatTime(item["checkin_time"]!)}", + // ), + // SizedBox(height: 6), + // _buildInfoColumn( + // "Check Out", + // "${formatDate(item["checkout_date"]!)} ${formatTime(item["checkout_time"]!)}", + // ), + // ], + // ), + // ), + ], ), - SizedBox(width: 10), - _buildRow( - "Date(out):", formatDate(item["checkout_date"]!)), - SizedBox(width: 10), - _buildRow("Comments:", item["comments"] ?? "N/A"), - ], + const SizedBox(height: 10), + const Text( + "Comment", + style: TextStyle(fontWeight: FontWeight.w500), + ), + const SizedBox(height: 5), + if (item["comments"] != null && item["comments"].toString().trim().isNotEmpty) + Container( + width: double.infinity, + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + border: Border.all(color: Color(0xFF7098DD), width: 1), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + item["comments"], + style: const TextStyle(fontSize: 12), + ), + ) + else + const Text( "N/A", style: TextStyle(fontSize: 12), ), + ], ) + // 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"), + // ], + // ) ], ), ), @@ -429,78 +515,103 @@ class AccomodationListWidget extends StatelessWidget { ); } - 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( + // Helper widget + Widget _buildInfoColumn(String label, String value) { + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - title, + label, style: GoogleFonts.poppins( fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.black87, ), ), - 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), - ), + SizedBox(height: 4), + Text( + value, + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + ), ), ], ); } - Widget _buildDateTimeRow(String title, String date, String time) { - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - ), - SizedBox(width: 8), - Expanded( - child: Text( - "$date, $time", - style: GoogleFonts.poppins( - fontSize: 12, - ), - ), - ), - ], - ); - } + // 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: GoogleFonts.poppins( + // 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: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // ), + // ), + // SizedBox(width: 8), + // Expanded( + // child: Text( + // "$date, $time", + // style: GoogleFonts.poppins( + // fontSize: 12, + // ), + // ), + // ), + // ], + // ); + // } } diff --git a/lib/Screens/itnerary_list/bus_list.dart b/lib/Screens/itnerary_list/bus_list.dart index e4e097a..68791dc 100644 --- a/lib/Screens/itnerary_list/bus_list.dart +++ b/lib/Screens/itnerary_list/bus_list.dart @@ -40,6 +40,15 @@ class BusListWidget extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + if ((!isDesktop) && busList.isNotEmpty) + Text( + " ", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + MouseRegion( cursor: isViewMode ? SystemMouseCursors.forbidden @@ -65,6 +74,24 @@ class BusListWidget extends StatelessWidget { size: 30, color: Color(0xFF114D8B), ) + // Container( + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // border: Border.all( + // color: Color(0xFF114D8B), // Outline color + // width: 2, // Outline thickness + // ), + // ), + // height: 30, + // width: 30, + // child: Center( + // child: Icon( + // Icons.add, + // size: 20, + // color: Colors.black, + // ), + // ), + // ), ], ), ), @@ -230,6 +257,8 @@ class BusListWidget extends StatelessWidget { 'assets/images/IconsImg/edit.png', width: 20, height: 15, + // color: Color(0xFF114D8B), + color: !isDesktop ? Color(0xFF114D8B) : Color(0xFF575A74) ), ), ), @@ -242,6 +271,7 @@ class BusListWidget extends StatelessWidget { 'assets/images/IconsImg/delete.png', width: 20, height: 15, + color: Colors.red, ), ), ), @@ -314,21 +344,70 @@ class BusListWidget extends StatelessWidget { ), ], ) - : 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"), - ], + : + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Top Row (Country, Currency, Total) + Wrap( + spacing: 20, // horizontal space between items + runSpacing: 10, // vertical space between rows + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _infoBlock("From", item["from"] ?? "N/A"), + _infoBlock("To", item["to"]! ?? "N/A"), + _infoBlock("Date", + (item["date"] != null && item["time"] != null) + ? "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}" + : "N/A", + ), + + ], + ), + const SizedBox(height: 16), + + // Comment label + Text( + "Comment", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 8), + if (item["comments"] != null && item["comments"].toString().trim().isNotEmpty) + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border.all(color: const Color(0xFF7098DD), width: 1), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + item["comments"] ?? "N/A", + style: GoogleFonts.poppins(fontSize: 12, color: Colors.black87), + ), ) + else + const Text( "N/A", style: TextStyle(fontSize: 12), ), + ], + ), + // 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"), + // ], + // ) ], ), ), @@ -337,6 +416,34 @@ class BusListWidget extends StatelessWidget { ); } + Widget _infoBlock(String label, String value) { + return Expanded( + child: Padding( + padding: const EdgeInsets.only(right: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + const SizedBox(height: 4), + Text( + value, + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + ), + ), + ], + ), + ), + ); + } Widget _buildComments(String title, String value) { // Define character limits based on title Map limits = { diff --git a/lib/Screens/itnerary_list/flight_list.dart b/lib/Screens/itnerary_list/flight_list.dart index 45ed127..79d886e 100644 --- a/lib/Screens/itnerary_list/flight_list.dart +++ b/lib/Screens/itnerary_list/flight_list.dart @@ -108,6 +108,66 @@ class _FlightListWidgetState extends State { } } + // return Padding( + // padding: const EdgeInsets.all(16.0), + // child: Container( + // margin: const EdgeInsets.only(top: 16.0), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.stretch, + // children: [ + // // Header with title and button + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // "Flight", + // style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + // ), + // widget.flightList.isNotEmpty + // ? MouseRegion( + // cursor: + // widget.isViewMode + // ? SystemMouseCursors.forbidden + // : SystemMouseCursors.click, + // child: GestureDetector( + // onTap: + // widget.isViewMode + // ? null + // : () { + // widget.onAddNew("Flight", true); + // // checkClass(); + // print("New data"); + // }, + // child: Row( + // mainAxisSize: + // MainAxisSize.min, // Ensures content fits nicely + // children: [ + // // Text( + // // "Add New", + // // style: TextStyle(fontSize: 13), + // // ), + // // SizedBox(width: 8), // spacing between icon and text + // Icon( + // Icons.add_circle_sharp, + // size: 30, + // color: Color(0xFF114D8B), + // ), + // ], + // ), + // ), + // ) + // : SizedBox.shrink(), + // ], + // ), + // const SizedBox(height: 16), + // + // // Scroll behavior based on device + // _buildData(context, isDesktop), + // ], + // ), + // ), + // ); + return Padding( padding: const EdgeInsets.all(16.0), child: Container( @@ -115,58 +175,67 @@ class _FlightListWidgetState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - // Header with title and button + // Row: Title (mobile only) + Add Button (conditional) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Text( - // "Flight Booking List", - // style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - // ), - widget.flightList.isNotEmpty - ? MouseRegion( - cursor: - widget.isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: GestureDetector( - onTap: - widget.isViewMode - ? null - : () { - widget.onAddNew("Flight", true); - // checkClass(); - print("New data"); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - // Text( - // "Add New", - // style: TextStyle(fontSize: 13), - // ), - // SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_sharp, - size: 30, - color: Color(0xFF114D8B), - ), - ], + // Title (optional on desktop or mobile) + if ((!isDesktop) && widget.flightList.isNotEmpty) + Text( + " ", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + + // Right side: either "Add" icon or nothing + if (widget.flightList.isNotEmpty) + MouseRegion( + cursor: widget.isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: GestureDetector( + onTap: widget.isViewMode + ? null + : () { + widget.onAddNew("Flight", true); + print("New data"); + }, + child: Icon( + Icons.add_circle_sharp, + size: 30, + color: const Color(0xFF114D8B), + ), + ), + ) + else if (!isDesktop) + // Centered "No Data Found" message for mobile and empty list + Expanded( + child: Center( + child: Text( + "No Data Found", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, ), ), - ) - : SizedBox.shrink(), + ), + ) + ], ), const SizedBox(height: 16), - // Scroll behavior based on device + // Actual flight data UI _buildData(context, isDesktop), ], ), ), ); + } Widget _buildData(BuildContext context, bool isDesktop) { @@ -313,6 +382,7 @@ class _FlightListWidgetState extends State { style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w800, + color: !isDesktop ? Color(0xFF114D8B) : Color(0xFF575A74), ), ), Spacer(), @@ -324,6 +394,7 @@ class _FlightListWidgetState extends State { 'assets/images/IconsImg/edit.png', width: 20, height: 15, + color: Color(0xFF114D8B), ), ), ), @@ -336,6 +407,7 @@ class _FlightListWidgetState extends State { 'assets/images/IconsImg/delete.png', width: 20, height: 15, + color: Colors.red, ), ), ), @@ -559,40 +631,175 @@ class _FlightListWidgetState extends State { final tocity = tocityMatch?.group(1) ?? ''; final tocode = tocityMatch?.group(2) ?? ''; + // 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( + // "Sector", + // "$fromcity ($fromcode), $fromairport → $tocity ($tocode), $toairport", + // ), + // + // _buildKeyValueRow( + // "Date", + // formatDate(trip["date"] ?? ""), + // ), + // _buildKeyValueRow( + // "Time", + // formatTime(trip["time"] ?? ""), + // ), + // ], + // ), + // ), + // ); + + + // 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(12), + // child: Row( + // children: [ + // /// LEFT SIDE: Class and Airport Info + // Expanded( + // flex: 2, + // child: Row( + // children: [ + // Text( + // '${getRequestForClass(trip["class"].toString()) ?? "N/A"} (', + // style: const TextStyle( + // fontWeight: FontWeight.bold, + // fontSize: 14, + // color: Colors.black87, + // ), + // ), + // Text( + // // fromairport, + // fromcode, + // style: const TextStyle( + // fontWeight: FontWeight.bold, + // fontSize: 14, + // color: Colors.black87, + // ), + // ), + // const Padding( + // padding: EdgeInsets.symmetric(horizontal: 6), + // child: Image( + // image: AssetImage('assets/images/IconsImg/Frame.png'), + // width: 18, + // height: 18, + // ), + // ), + // Text( + // '$tocode)', + // // '$toairport)', + // style: const TextStyle( + // fontWeight: FontWeight.bold, + // fontSize: 14, + // color: Colors.black87, + // ), + // ), + // ], + // ), + // ), + // + // /// RIGHT SIDE: Date and Time + // Expanded( + // flex: 1, + // child: Text( + // '${formatDate(trip["date"] ?? "")} ${formatTime(trip["time"] ?? "")}', + // textAlign: TextAlign.right, + // style: const TextStyle( + // fontSize: 13, + // color: Colors.black87, + // ), + // ), + // ), + // ], + // ), + // ), + // ); + 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", + Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 4, + runSpacing: 4, + children: [ + Text( + '${getRequestForClass(trip["class"].toString()) ?? "N/A"} (', + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 14, + color: Colors.black87, + ), + ), + Text( + fromcode, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 14, + color: Colors.black87, + ), + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 4), + child: Image( + image: AssetImage('assets/images/IconsImg/Frame.png'), + width: 18, + height: 18, + ), + ), + Text( + '$tocode)', + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 14, + color: Colors.black87, + ), + ), + ], ), - _buildKeyValueRow( - "Sector", - "$fromcity ($fromcode), $fromairport → $tocity ($tocode), $toairport", - ), - - _buildKeyValueRow( - "Date", - formatDate(trip["date"] ?? ""), - ), - _buildKeyValueRow( - "Time", - formatTime(trip["time"] ?? ""), + const SizedBox(height: 6), + Align( + alignment: Alignment.centerRight, + child: Text( + '${formatDate(trip["date"] ?? "")} ${formatTime(trip["time"] ?? "")}', + textAlign: TextAlign.right, + style: const TextStyle( + fontSize: 13, + color: Colors.black87, + ), + ), ), ], ), ), ); + }).toList(), ], ), diff --git a/lib/Screens/itnerary_list/forex_list.dart b/lib/Screens/itnerary_list/forex_list.dart index 386a13b..97014ec 100644 --- a/lib/Screens/itnerary_list/forex_list.dart +++ b/lib/Screens/itnerary_list/forex_list.dart @@ -39,93 +39,101 @@ class ForexListWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header with title and button - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - - child: GestureDetector( - onTap: 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_sharp, - size: 30, - color: Color(0xFF114D8B), - ) - ], - ), - ), - // onPressed: isViewMode - // ? null - // : () { - // print("New data"); - // - // }, - // child: Row( - // mainAxisSize: MainAxisSize.min, - // children: [ - // Icon( - // Icons.add_circle_sharp, - // size: 30, - // color: Color(0xFF114D8B), - // ) - // ], - // ), - ), - // MouseRegion( - // cursor: isViewMode - // ? SystemMouseCursors.forbidden - // : SystemMouseCursors.click, - // child: ElevatedButton( - // style: ElevatedButton.styleFrom( - // backgroundColor: Color(0xFF114D8B), - // foregroundColor: Colors.white, - // disabledBackgroundColor: Color(0xFF114D8B), - // disabledForegroundColor: Colors.white, - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(8), - // side: BorderSide(color: Color(0xFF114D8B), width: 2), - // ), - // padding: - // EdgeInsets.symmetric(horizontal: 20, vertical: 12), - // ), - // onPressed: isViewMode - // ? null - // : () { - // print("New data"); - // onAddNew("Forex", true); - // }, - // child: Row( - // mainAxisSize: - // MainAxisSize.min, // Ensures content fits nicely - // children: [ - // Icon( - // Icons.add_circle_sharp, - // size: 30, - // color: Color(0xFF114D8B), - // ) - // ], - // ), - // ), - // ), - ], - ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // if ((!isDesktop) && forexList.isNotEmpty) + // Text( + // " ", + // style: const TextStyle( + // fontSize: 18, + // fontWeight: FontWeight.bold, + // ), + // ), + // MouseRegion( + // cursor: isViewMode + // ? SystemMouseCursors.forbidden + // : SystemMouseCursors.click, + // + // child: GestureDetector( + // onTap: 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_sharp, + // size: 30, + // color: Color(0xFF114D8B), + // ) + // ], + // ), + // ), + // // onPressed: isViewMode + // // ? null + // // : () { + // // print("New data"); + // // + // // }, + // // child: Row( + // // mainAxisSize: MainAxisSize.min, + // // children: [ + // // Icon( + // // Icons.add_circle_sharp, + // // size: 30, + // // color: Color(0xFF114D8B), + // // ) + // // ], + // // ), + // ), + // // MouseRegion( + // // cursor: isViewMode + // // ? SystemMouseCursors.forbidden + // // : SystemMouseCursors.click, + // // child: ElevatedButton( + // // style: ElevatedButton.styleFrom( + // // backgroundColor: Color(0xFF114D8B), + // // foregroundColor: Colors.white, + // // disabledBackgroundColor: Color(0xFF114D8B), + // // disabledForegroundColor: Colors.white, + // // shape: RoundedRectangleBorder( + // // borderRadius: BorderRadius.circular(8), + // // side: BorderSide(color: Color(0xFF114D8B), width: 2), + // // ), + // // padding: + // // EdgeInsets.symmetric(horizontal: 20, vertical: 12), + // // ), + // // onPressed: isViewMode + // // ? null + // // : () { + // // print("New data"); + // // onAddNew("Forex", true); + // // }, + // // child: Row( + // // mainAxisSize: + // // MainAxisSize.min, // Ensures content fits nicely + // // children: [ + // // Icon( + // // Icons.add_circle_sharp, + // // size: 30, + // // color: Color(0xFF114D8B), + // // ) + // // ], + // // ), + // // ), + // // ), + // ], + // ), const SizedBox(height: 16), // Scroll behavior based on device @@ -275,21 +283,24 @@ class ForexListWidget extends StatelessWidget { 'assets/images/IconsImg/edit.png', width: 20, height: 15, + // color: Color(0xFF114D8B), + color: !isDesktop ? Color(0xFF114D8B) : Color(0xFF575A74) ), ), ), - SizedBox(width: 10), - GestureDetector( - onTap: () => onDeleteForex(item), - child: Tooltip( - message: 'Delete Forex Details', - child: Image.asset( - 'assets/images/IconsImg/delete.png', - width: 20, - height: 15, - ), - ), - ), + // SizedBox(width: 10), + // GestureDetector( + // onTap: () => onDeleteForex(item), + // child: Tooltip( + // message: 'Delete Forex Details', + // child: Image.asset( + // 'assets/images/IconsImg/delete.png', + // width: 20, + // height: 15, + // color: Colors.red, + // ), + // ), + // ), item['forex_id'] != null ? IconButton( icon: Icon(Icons.download, @@ -495,30 +506,84 @@ class ForexListWidget extends StatelessWidget { "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"]!), - _buildRow("Perdiem:", "$perdiemAmount",), - _buildRow("Others:", "$calculatedOtherExpenses"), - _buildRow("Total:", "$finalTotal"), - _buildRow("Cash:", item["deposit_on_cash"]!), - _buildRow("Card:", item["deposit_on_card"]!), - _buildRow("Comments:", item["comments"] ?? "N/A"), - ], + ): Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Top Row (Country, Currency, Total) + Wrap( + spacing: 20, // horizontal space between items + runSpacing: 10, // vertical space between rows + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _infoBlock("Country code", item["country_code"] ?? "N/A"), + _infoBlock("Currency", item["currency"] ?? "N/A"), + _infoBlock("Total Amount", item["total_amount"]?.toString() ?? "0"), + ], + ), + const SizedBox(height: 12), + + // Second Row (Start Date, End Date, Duration) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _infoBlock("Start Date", item["start_date"] ?? "N/A"), + _infoBlock("End Date", item["end_date"] ?? "N/A"), + _infoBlock("Duration", item["duration"]?.toString() ?? "N/A"), + ], + ), + const SizedBox(height: 16), + + // Comment label + Text( + "Comment", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, ), + ), + const SizedBox(height: 8), + if (item["comments"] != null && item["comments"].toString().trim().isNotEmpty) + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border.all(color: const Color(0xFF7098DD), width: 1), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + item["comments"] ?? "N/A", + style: GoogleFonts.poppins(fontSize: 12, color: Colors.black87), + ), + ) + else + const Text( "N/A", style: TextStyle(fontSize: 12), ), + ], + ), + // old code + // : 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"]!), + // _buildRow("Perdiem:", "$perdiemAmount",), + // _buildRow("Others:", "$calculatedOtherExpenses"), + // _buildRow("Total:", "$finalTotal"), + // _buildRow("Cash:", item["deposit_on_cash"]!), + // _buildRow("Card:", item["deposit_on_card"]!), + // _buildRow("Comments:", item["comments"] ?? "N/A"), + // ], + // ), ], ), ), @@ -577,59 +642,89 @@ class ForexListWidget extends StatelessWidget { ); } - 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: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), + Widget _infoBlock(String label, String value) { + return Expanded( + child: Padding( + padding: const EdgeInsets.only(right: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + const SizedBox(height: 4), + Text( + value, + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + ), + ), + ], ), - 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: GoogleFonts.poppins( - fontSize: 12, - ), - overflow: TextOverflow.ellipsis), - ) - : Text( - value, - style: GoogleFonts.poppins( - fontSize: 12, - ), - ), - ), - ], + ), ); } + + // 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: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // ), + // ), + // 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: GoogleFonts.poppins( + // fontSize: 12, + // ), + // overflow: TextOverflow.ellipsis), + // ) + // : Text( + // value, + // style: GoogleFonts.poppins( + // fontSize: 12, + // ), + // ), + // ), + // ], + // ); + // } + } diff --git a/lib/Screens/itnerary_list/insurance_list.dart b/lib/Screens/itnerary_list/insurance_list.dart index 9e0b1de..c4e3d4f 100644 --- a/lib/Screens/itnerary_list/insurance_list.dart +++ b/lib/Screens/itnerary_list/insurance_list.dart @@ -34,71 +34,79 @@ class InsuranceListWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header with title and button - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - MouseRegion( - cursor: isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: GestureDetector( - onTap: isViewMode - ? null - : () { - print("New data"); - onAddNew("Insurance", true); - }, - child: Row( - mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely - children: [ - // Text( - // "Add New", - // style: TextStyle(fontSize: 13), - // ), - // SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_sharp, - size: 30, - color: Color(0xFF114D8B), - ) - ], - ), - ), - // child: ElevatedButton( - // style: ElevatedButton.styleFrom( - // backgroundColor: Color(0xFF114D8B), - // foregroundColor: Colors.white, - // disabledBackgroundColor: Color(0xFF114D8B), - // disabledForegroundColor: Colors.white, - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(8), - // side: BorderSide(color: Color(0xFF114D8B), width: 2), - // ), - // padding: - // EdgeInsets.symmetric(horizontal: 20, vertical: 12), - // ), - // onPressed: isViewMode - // ? null - // : () { - // print("New data"); - // onAddNew("Insurance", true); - // }, - // child: Row( - // mainAxisSize: - // MainAxisSize.min, // Ensures content fits nicely - // children: [ - // Icon( - // Icons.add_circle_sharp, - // size: 30, - // color: Color(0xFF114D8B), - // ) - // ], - // ), - // ), - ), - ], - ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // if ((!isDesktop) && insuranceList.isNotEmpty) + // Text( + // " ", + // style: const TextStyle( + // fontSize: 18, + // fontWeight: FontWeight.bold, + // ), + // ), + // MouseRegion( + // cursor: isViewMode + // ? SystemMouseCursors.forbidden + // : SystemMouseCursors.click, + // child: GestureDetector( + // onTap: isViewMode + // ? null + // : () { + // print("New data"); + // onAddNew("Insurance", true); + // }, + // child: Row( + // mainAxisSize: + // MainAxisSize.min, // Ensures content fits nicely + // children: [ + // // Text( + // // "Add New", + // // style: TextStyle(fontSize: 13), + // // ), + // // SizedBox(width: 8), // spacing between icon and text + // // Icon( + // // Icons.add_circle_sharp, + // // size: 30, + // // color: Color(0xFF114D8B), + // // ) + // ], + // ), + // ), + // // child: ElevatedButton( + // // style: ElevatedButton.styleFrom( + // // backgroundColor: Color(0xFF114D8B), + // // foregroundColor: Colors.white, + // // disabledBackgroundColor: Color(0xFF114D8B), + // // disabledForegroundColor: Colors.white, + // // shape: RoundedRectangleBorder( + // // borderRadius: BorderRadius.circular(8), + // // side: BorderSide(color: Color(0xFF114D8B), width: 2), + // // ), + // // padding: + // // EdgeInsets.symmetric(horizontal: 20, vertical: 12), + // // ), + // // onPressed: isViewMode + // // ? null + // // : () { + // // print("New data"); + // // onAddNew("Insurance", true); + // // }, + // // child: Row( + // // mainAxisSize: + // // MainAxisSize.min, // Ensures content fits nicely + // // children: [ + // // Icon( + // // Icons.add_circle_sharp, + // // size: 30, + // // color: Color(0xFF114D8B), + // // ) + // // ], + // // ), + // // ), + // ), + // ], + // ), const SizedBox(height: 16), // Scroll behavior based on device @@ -197,6 +205,7 @@ class InsuranceListWidget extends StatelessWidget { style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w800, + color: !isDesktop ? Color(0xFF114D8B) : Color(0xFF575A74) ), ), Spacer(), @@ -208,21 +217,23 @@ class InsuranceListWidget extends StatelessWidget { 'assets/images/IconsImg/edit.png', width: 20, height: 15, + color: Color(0xFF114D8B), ), ), ), - SizedBox(width: 10), - GestureDetector( - onTap: () => onDeleteInsurance(item), - child: Tooltip( - message: 'Delete Insurance Details', - child: Image.asset( - 'assets/images/IconsImg/delete.png', - width: 20, - height: 15, - ), - ), - ), + // SizedBox(width: 10), + // GestureDetector( + // onTap: () => onDeleteInsurance(item), + // child: Tooltip( + // message: 'Delete Insurance Details', + // child: Image.asset( + // 'assets/images/IconsImg/delete.png', + // width: 20, + // height: 15, + // color: Colors.red, + // ), + // ), + // ), ], ), SizedBox(height: 4), @@ -293,21 +304,65 @@ class InsuranceListWidget extends StatelessWidget { ), ], ) - : 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"), - ], + : + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Top Row (Country, Currency, Total) + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + Wrap( + spacing: 20, // horizontal space between items + runSpacing: 10, // vertical space between rows + children: [ + _infoBlock("Insurance Type", getRequestForInsuranceType(item["type_of_insurance"]!.toString()) ?? "N/A"), + _infoBlock("Start Date", formatDate(item["start_date"]!) ?? "N/A"), + _infoBlock("End Date", formatDate(item["end_date"]) ?? "N/A"), + ], + ), + const SizedBox(height: 16), + + // Comment label + Text( + "Comment", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, ), + ), + const SizedBox(height: 8), + if (item["comments"] != null && item["comments"].toString().trim().isNotEmpty) + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border.all(color: const Color(0xFF7098DD), width: 1), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + item["comments"] ?? "N/A", + style: GoogleFonts.poppins(fontSize: 12, color: Colors.black87), + ), + ) + else + const Text( "N/A", style: TextStyle(fontSize: 12), ), + ], + ), + // 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"), + // ], + // ), ], ), ), @@ -366,6 +421,34 @@ class InsuranceListWidget extends StatelessWidget { ); } + Widget _infoBlock(String label, String value) { + return Expanded( + child: Padding( + padding: const EdgeInsets.only(right: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + const SizedBox(height: 4), + Text( + value, + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + ), + ), + ], + ), + ), + ); + } Widget _buildRow(String title, String value) { // Define character limits based on title Map limits = { diff --git a/lib/Screens/itnerary_list/miscellaneous_list.dart b/lib/Screens/itnerary_list/miscellaneous_list.dart index 8de06f9..48b79fc 100644 --- a/lib/Screens/itnerary_list/miscellaneous_list.dart +++ b/lib/Screens/itnerary_list/miscellaneous_list.dart @@ -26,56 +26,67 @@ class MiscellaneousListWidget extends StatelessWidget { return Padding( padding: const EdgeInsets.all(16.0), child: Container( - // color: Color(0xFFE8F0FC), - margin: const EdgeInsets.only(top: 28.0), - padding: const EdgeInsets.all(10.0), + margin: const EdgeInsets.only(top: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header with title and button Row( - mainAxisAlignment: MainAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + if ((!isDesktop) && miscellaneousList.isNotEmpty) + Text( + " ", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), MouseRegion( cursor: - isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, + isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, child: GestureDetector( onTap: - isViewMode - ? null - : () { - print("New data"); - onAddNew("Miscellaneous", true); - }, + isViewMode + ? null + : () { + print("New data"); + onAddNew("Miscellaneous", true); + }, child: Row( mainAxisSize: - MainAxisSize.min, // Ensures content fits nicely + MainAxisSize.min, // Ensures content fits nicely children: [ // Text( // "Add New", // style: TextStyle(fontSize: 13), // ), // SizedBox(width: 8), // spacing between icon and text - Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: Color(0xFF114D8B), // Outline color - width: 2, // Outline thickness - ), - ), - height: 30, - width: 30, - child: Center( - child: Icon( - Icons.add, - size: 20, - color: Colors.black, - ), - ), + Icon( + Icons.add_circle_sharp, + size: 30, + color: Color(0xFF114D8B), ), + // Container( + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // border: Border.all( + // color: Color(0xFF114D8B), // Outline color + // width: 2, // Outline thickness + // ), + // ), + // height: 30, + // width: 30, + // child: Center( + // child: Icon( + // Icons.add, + // size: 20, + // color: Colors.black, + // ), + // ), + // ), ], ), ), @@ -122,7 +133,7 @@ class MiscellaneousListWidget extends StatelessWidget { Widget _buildData(BuildContext context, bool isDesktop) { List> filteredList = - miscellaneousList.where((item) => item["is_active"] == "1").toList(); + miscellaneousList.where((item) => item["is_active"] == "1").toList(); return ListView.builder( shrinkWrap: true, @@ -136,9 +147,9 @@ class MiscellaneousListWidget extends StatelessWidget { return (apiData?['miscellaneous_special_request'] ?? []) .firstWhere( (element) => - element["dropdown_key"].toString() == specialRequestKey, - orElse: () => {"dropdown_value": "N/A"}, - )["dropdown_value"] + element["dropdown_key"].toString() == specialRequestKey, + orElse: () => {"dropdown_value": "N/A"}, + )["dropdown_value"] .toString(); } @@ -174,8 +185,7 @@ class MiscellaneousListWidget extends StatelessWidget { getRequestValue(item["special_request"]?.toString()), style: GoogleFonts.poppins( fontSize: 14, - color: Color(0xFF114D8B), - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w800, ), ), Spacer(), @@ -187,6 +197,7 @@ class MiscellaneousListWidget extends StatelessWidget { 'assets/images/IconsImg/edit.png', width: 20, height: 15, + color: Color(0xFF114D8B), ), ), ), @@ -200,83 +211,101 @@ class MiscellaneousListWidget extends StatelessWidget { 'assets/images/IconsImg/delete.png', width: 20, height: 15, + color: Colors.red, ), ), ), ], ), - // Divider(color: Colors.blueGrey.shade50), - SizedBox(height: 10), - isDesktop - ? Row( - children: [ - // Expanded( - // flex: 2, - // child: Text( - // "Special Request", - // style: GoogleFonts.poppins(fontSize: 11), - // ), - // ), - Expanded( - flex: 3, - child: Text( - "Comments", - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black, - fontWeight: FontWeight.w600, - ), - ), - ), - ], - ) - : SizedBox.shrink(), + Divider(color: Colors.blueGrey.shade50), SizedBox(height: 4), isDesktop - ? Container( - // color: Color(0xFFE8F0FC), - color: Colors.white, - child: Expanded( - child: Row( - children: [ - // Expanded( - // flex: 2, - // child: Text( - // getRequestValue( - // item["special_request"]?.toString(), - // ), - // style: GoogleFonts.poppins( - // fontSize: 12, - // fontWeight: FontWeight.w600, - // ), - // ), - // ), - Container( - // width: MediaQuery.of(context).size.width * 0.6, - // padding: EdgeInsets.all(16), - // color: Color(0xFFE8F0FC), - child: Expanded( - // flex: 1, - child: Text( - item["comments"], - style: GoogleFonts.poppins(fontSize: 11), - ), - ), - ), - ], - ), + ? Row( + children: [ + Expanded( + flex: 2, + child: Text( + "Special Request", + style: GoogleFonts.poppins(fontSize: 11), ), - ) - : Column( - children: [ - _buildRow( - "Special Request:", - getRequestValue(item["special_request"]?.toString()), - ), - SizedBox(width: 10), - _buildRow("Comments:", item["comments"] ?? "N/A"), - ], ), + Expanded( + flex: 3, + child: Text( + " Comments", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + ], + ) + // : SizedBox.shrink(), + // isDesktop + // ? Row( + // children: [ + // Expanded( + // flex: 2, + // child: Text( + // getRequestValue( + // item["special_request"]?.toString(), + // ), + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // Expanded( + // flex: 3, + // child: Text( + // item["comments"], + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // ], + // ) + : + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Comment label + Text( + "Comment", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 8), + if (item["comments"] != null && item["comments"].toString().trim().isNotEmpty) + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border.all(color: const Color(0xFF7098DD), width: 1), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + item["comments"] ?? "N/A", + style: GoogleFonts.poppins(fontSize: 12, color: Colors.black87), + ), + ) + else + const Text( "N/A", style: TextStyle(fontSize: 12), ), + ], + ), + // Column( + // children: [ + // _buildRow( + // "Special Request:", + // getRequestValue(item["special_request"]?.toString()), + // ), + // SizedBox(width: 10), + // _buildRow("Comments:", item["comments"] ?? "N/A"), + // ], + // ), ], ), ), @@ -306,34 +335,34 @@ class MiscellaneousListWidget extends StatelessWidget { 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: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - overflow: TextOverflow.ellipsis, - ), - ) - : Text( - value, - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - ), + 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: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + overflow: TextOverflow.ellipsis, + ), + ) + : Text( + value, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), ), ], ); @@ -365,95 +394,95 @@ class MiscellaneousListWidget extends StatelessWidget { 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), + 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(); - // } -} +// +// 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(); +// } +} \ No newline at end of file diff --git a/lib/Screens/itnerary_list/taxi_list.dart b/lib/Screens/itnerary_list/taxi_list.dart index 8059f4d..3c960f3 100644 --- a/lib/Screens/itnerary_list/taxi_list.dart +++ b/lib/Screens/itnerary_list/taxi_list.dart @@ -36,6 +36,14 @@ class TaxiListWidget extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + if ((!isDesktop) && taxiList.isNotEmpty) + Text( + " ", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), MouseRegion( cursor: isViewMode @@ -248,6 +256,7 @@ class TaxiListWidget extends StatelessWidget { style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w800, + color: !isDesktop ? Color(0xFF114D8B) : Color(0xFF575A74) ), ), Spacer(), @@ -259,6 +268,7 @@ class TaxiListWidget extends StatelessWidget { 'assets/images/IconsImg/edit.png', width: 20, height: 15, + color: Color(0xFF114D8B), ), ), ), @@ -271,6 +281,7 @@ class TaxiListWidget extends StatelessWidget { 'assets/images/IconsImg/delete.png', width: 20, height: 15, + color: Colors.red, ), ), ), @@ -358,22 +369,72 @@ class TaxiListWidget extends StatelessWidget { ), ], ) - : 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"), - ], - ), + : + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // First row: City & Location + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _infoColumn("City", item["destination_city"] ?? "N/A"), + _infoColumn("Location", item["location_of_pickup"] ?? "N/A",alignEnd: true), + ], + ), + const SizedBox(height: 16), + + // Second row: Date & Time + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _infoColumn("Date", "${formatDate(item["date"]!)}" ?? "N/A"), + _infoColumn("Time", "${formatTime(item["time"]!)}" ?? "N/A",alignEnd: true), + ], + ), + const SizedBox(height: 16), + + // Comment label + Text( + "Comment", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 8), + + // Comment box + if (item["comments"] != null && item["comments"].toString().trim().isNotEmpty) + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border.all(color: const Color(0xFF7098DD), width: 1), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + item["comments"] ?? "N/A", + style: GoogleFonts.poppins(fontSize: 12, color: Colors.black87), + ), + )else const Text( "N/A", style: TextStyle(fontSize: 12), ), + + ] ) + // 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"), + // ], + // ), ], ), ), @@ -501,4 +562,30 @@ class TaxiListWidget extends StatelessWidget { ], ); } + Widget _infoColumn(String label, String value, {bool alignEnd = false}) { + return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + alignEnd ? CrossAxisAlignment.end : CrossAxisAlignment.start, + children: [ + Text( + label, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + const SizedBox(height: 4), + Text( + value, + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + ), + ), + ], + ); + } + } diff --git a/lib/Screens/itnerary_list/train_list.dart b/lib/Screens/itnerary_list/train_list.dart index 3379e45..d4f039f 100644 --- a/lib/Screens/itnerary_list/train_list.dart +++ b/lib/Screens/itnerary_list/train_list.dart @@ -109,6 +109,14 @@ class _TrainListWidgetState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + if ((!isDesktop)) + Text( + " ", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), MouseRegion( cursor: widget.isViewMode @@ -335,6 +343,7 @@ class _TrainListWidgetState extends State { style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w800, + color: !isDesktop ? Color(0xFF114D8B) : Color(0xFF575A74) ), ), Spacer(), @@ -346,6 +355,7 @@ class _TrainListWidgetState extends State { 'assets/images/IconsImg/edit.png', width: 20, height: 15, + color: Color(0xFF114D8B), ), ), ), @@ -358,6 +368,7 @@ class _TrainListWidgetState extends State { 'assets/images/IconsImg/delete.png', width: 20, height: 15, + color: Colors.red, ), ), ), @@ -528,23 +539,74 @@ class _TrainListWidgetState extends State { ), ], ) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildRow( - "Class:", - getRequestForTrainClass(item["class"]!.toString()), + : + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // First row: City & Location + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _infoColumn("Class", getRequestForTrainClass(item["class"]!.toString()) ?? "N/A"), + _infoColumn("From", item["from_station"]! ?? "N/A"), + _infoColumn("To", item["to_station"]! ?? "N/A",alignEnd: true), + ], + ), + const SizedBox(height: 16), + + // Second row: Date & Time + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _infoColumn("Date", "${formatDate(item["date"]!)}" ?? "N/A"), + _infoColumn("Time", "${formatTime(item["time"]!)}" ?? "N/A",alignEnd: true), + ], + ), + const SizedBox(height: 16), + + // Comment label + Text( + "Comment", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, ), - _buildRow("From:", item["from_station"]!), - _buildRow("To:", item["to_station"]!), - _buildDateTimeRow( - "Date:", - formatDate(item["date"]!), - formatTime(item["time"]!), - ), - _buildRow("Comments:", item["comments"] ?? "N/A"), - ], - ), + ), + const SizedBox(height: 8), + + // Comment box + if (item["comments"] != null && item["comments"].toString().trim().isNotEmpty) + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border.all(color: const Color(0xFF7098DD), width: 1), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + item["comments"] ?? "N/A", + style: GoogleFonts.poppins(fontSize: 12, color: Colors.black87), + ), + )else const Text( "N/A", style: TextStyle(fontSize: 12), ), + + ] ) + // 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"), + // ], + // ), ], ), ), @@ -659,6 +721,31 @@ class _TrainListWidgetState extends State { ); } + Widget _infoColumn(String label, String value, {bool alignEnd = false}) { + return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + alignEnd ? CrossAxisAlignment.end : CrossAxisAlignment.start, + children: [ + Text( + label, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + const SizedBox(height: 4), + Text( + value, + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + ), + ), + ], + ); + } Widget _buildDateTimeRow(String title, String date, String time) { return Row( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/Screens/itnerary_list/visa_list.dart b/lib/Screens/itnerary_list/visa_list.dart index 3f70b4a..93f1a31 100644 --- a/lib/Screens/itnerary_list/visa_list.dart +++ b/lib/Screens/itnerary_list/visa_list.dart @@ -37,6 +37,14 @@ class VisaListWidget extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + if ((!isDesktop) && visaList.isNotEmpty) + Text( + " ", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), MouseRegion( cursor: isViewMode ? SystemMouseCursors.forbidden @@ -58,11 +66,11 @@ class VisaListWidget extends StatelessWidget { // style: TextStyle(fontSize: 13), // ), // SizedBox(width: 8), // spacing between icon and text - Icon( - Icons.add_circle_sharp, - size: 30, - color: Color(0xFF114D8B), - ) + // Icon( + // Icons.add_circle_sharp, + // size: 30, + // color: Color(0xFF114D8B), + // ) ], ), ), @@ -296,6 +304,7 @@ class VisaListWidget extends StatelessWidget { style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w800, + color: !isDesktop ? Color(0xFF114D8B) : Color(0xFF575A74) ), ), Spacer(), @@ -307,6 +316,7 @@ class VisaListWidget extends StatelessWidget { 'assets/images/IconsImg/edit.png', width: 20, height: 15, + color: Color(0xFF114D8B), ), ), ), @@ -319,6 +329,7 @@ class VisaListWidget extends StatelessWidget { 'assets/images/IconsImg/delete.png', width: 20, height: 15, + color: Colors.red, ), ), ), @@ -411,24 +422,68 @@ class VisaListWidget extends StatelessWidget { ), ], ) - : 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"), - ], + : + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Top Row (Country, Currency, Total) + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + Wrap( + spacing: 20, // horizontal space between items + runSpacing: 10, // vertical space between rows + children: [ + _infoBlock("Visa Type", getRequestForCountry(item["country_code"]!.toString()) ?? "N/A"), + _infoBlock("Country", getRequestForCountry(item["country_code"]!.toString()) ?? "N/A"), + _infoBlock("Start Date", formatDate(item["start_date"]!) ?? "N/A"), + ], + ), + const SizedBox(height: 16), + + // Comment label + Text( + "Comment", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, ), + ), + const SizedBox(height: 8), + if (item["comments"] != null && item["comments"].toString().trim().isNotEmpty) + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border.all(color: const Color(0xFF7098DD), width: 1), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + item["comments"] ?? "N/A", + style: GoogleFonts.poppins(fontSize: 12, color: Colors.black87), + ), + ) + else + const Text( "N/A", style: TextStyle(fontSize: 12), ), + ], + ), + // 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"), + // ], + // ), ], ), ), @@ -487,6 +542,34 @@ class VisaListWidget extends StatelessWidget { ); } + Widget _infoBlock(String label, String value) { + return Expanded( + child: Padding( + padding: const EdgeInsets.only(right: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + const SizedBox(height: 4), + Text( + value, + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + ), + ), + ], + ), + ), + ); + } Widget _buildRow(String title, String value) { // Define character limits based on title Map limits = { diff --git a/lib/Screens/plans/list_plans.dart b/lib/Screens/plans/list_plans.dart index f76b84a..34cf40e 100644 --- a/lib/Screens/plans/list_plans.dart +++ b/lib/Screens/plans/list_plans.dart @@ -72,17 +72,22 @@ class _ListPlansState extends State { print(roleUser); if (roleUser == "User") { print("user"); - // Push a dummy state so back button triggers popstate instead of navigating + final prefs = await SharedPreferences.getInstance(); + final String? msUserStr = prefs.getString('is_microsoft_user'); + final bool msUser = msUserStr?.toLowerCase() == 'true'; + print("msUser - $msUser"); html.window.history.pushState(null, '', html.window.location.href); - _popStateListener = html.window.onPopState.listen((event) { - if (!_dialogShown && mounted) { - _showBackConfirmationDialog(); - } + if(!msUser){ + _popStateListener = html.window.onPopState.listen((event) { + if (!_dialogShown && mounted) { + _showBackConfirmationDialog(); + } - // Re-push to prevent leaving - html.window.history.pushState(null, '', html.window.location.href); - }); + // Re-push to prevent leaving + html.window.history.pushState(null, '', html.window.location.href); + }); + } } } @@ -955,7 +960,11 @@ class _ListPlansState extends State { builder: (context, constraints) { double minWidth = isDesktop ? constraints.maxWidth : 1300; - return ConstrainedBox( + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child:ConstrainedBox( constraints: BoxConstraints(minWidth: minWidth), child: DataTable( dividerThickness: 0.5, @@ -1361,7 +1370,7 @@ class _ListPlansState extends State { ); }).toList(), ), - ); + ))); }, ); @@ -1859,7 +1868,7 @@ class _ListPlansState extends State { CrossAxisAlignment.end, children: [ - Column( + Flexible( child:Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1881,7 +1890,7 @@ class _ListPlansState extends State { ), ), ], - ), + ),), Column( mainAxisAlignment: MainAxisAlignment.end, diff --git a/lib/Screens/plans/list_plans_backup24july2025.dart b/lib/Screens/plans/list_plans_backup24july2025.dart new file mode 100644 index 0000000..f76b84a --- /dev/null +++ b/lib/Screens/plans/list_plans_backup24july2025.dart @@ -0,0 +1,2031 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:core'; +import 'dart:html' as html; + +import 'package:frontend/data/models/plan.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.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'; + +import '../../routes/custom_appBar.dart'; +import '../../routes/custom_drawer.dart'; +import '../../routes/custom_router.dart'; +import '../../services/apiService.dart'; +import '../../utils/auth_utils.dart'; +import '../../utils/pagination.dart'; +import '../../utils/travelAgent_remarks.dart'; +import '../../widgets/custom_popup.dart'; +import '../../widgets/popup_listPlan_action.dart'; +import '../allTrips/remarks_list.dart'; + +class ListPlans extends StatefulWidget { + const ListPlans({super.key}); + + @override + _ListPlansState createState() => _ListPlansState(); +} + +class _ListPlansState extends State { + final ApiService apiService = ApiService(); + + int currentPage = 0; + int itemsPerPage = 10; + + String? userId; + String? orgId; + String? roleUser; + String? token; + String? TripPlanAction; + + Color? layoutColor; + Color? bodyColor; + late Future> futurePlans; + List allPlans = []; + List filteredPlans = []; + TextEditingController searchController = TextEditingController(); + String? location; + + late bool _dialogShown = false; + late bool _listDialogShown = false; + late StreamSubscription _popStateListener; + + @override + void initState() { + super.initState(); + + _checkAuthAndLoadData(); + checkbackbutton(); + + // getToken(); + // initializeData(); + // loadInitialData(); + } + + void checkbackbutton() async { + roleUser = await getRoleUser(); + print(roleUser); + if (roleUser == "User") { + print("user"); + // Push a dummy state so back button triggers popstate instead of navigating + html.window.history.pushState(null, '', html.window.location.href); + + _popStateListener = html.window.onPopState.listen((event) { + if (!_dialogShown && mounted) { + _showBackConfirmationDialog(); + } + + // Re-push to prevent leaving + html.window.history.pushState(null, '', html.window.location.href); + }); + } + } + + @override + void dispose() { + _popStateListener.cancel(); // ✅ Remove the browser popstate listener + super.dispose(); + } + + void _showBackConfirmationDialog() { + if (!mounted) return; + + _dialogShown = true; + + showDialog( + context: context, + builder: + (context) => AlertDialog( + title: Text("Confirm"), + content: Text("Do you want to logout?"), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); // Close dialog + _dialogShown = false; + }, + child: Text("Cancel"), + ), + TextButton( + onPressed: () async { + Navigator.pop(context); // Close dialog + _dialogShown = false; + await _logoutAndRedirect(context); + }, + child: Text("Logout"), + ), + ], + ), + ); + } + + Future _logoutAndRedirect(BuildContext context) async { + print("logue 0"); + + // Example: clear session or shared preferences + final prefs = await SharedPreferences.getInstance(); + await prefs.clear(); + + context.go("/"); + + print("logue 1"); + } + + void _checkAuthAndLoadData() async { + final String? token = await getToken(); // Your async function to get token + + if (token == null || token.isEmpty) { + // Token doesn't exist → redirect to login + + context.go( + "/", + ); // or use: router.go("/") if you're using `GoRouter` directly + return; + } else { + if (roleUser == "User") { + print("user"); + + // ✅ After login is successful and navigation completes: + WidgetsBinding.instance.addPostFrameCallback((_) { + checkbackbutton(); + }); + } + getToken(); + initializeData(); + loadInitialData(); + } + } + + void refresh() { + print("Refresj"); + setState(() { + futurePlans = fetchPlans(); + }); + + // Wait for futurePlans to be fetched and update allPlans + futurePlans.then((plans) { + setState(() { + allPlans = plans; // Set allPlans after the fetch is complete + filteredPlans = allPlans; // You can update filteredPlans too if needed + }); + }); + } + + void filterPlans(String query) { + print("allPlans before filtering: $allPlans"); + final lowerQuery = query.toLowerCase(); + setState(() { + filteredPlans = + allPlans.where((plan) { + return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? + false) || + (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); + }).toList(); + currentPage = 0; + }); + print("filteredPlans: $filteredPlans"); + } + + void loadInitialData() async { + String? layoutString = await getLayoutColor(); + String? bodyStringColor = await getBodyColor(); + + setState(() { + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; + + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; + }); + } + + Future initializeData() async { + token = await getToken(); + userId = await getUserId(); + orgId = await getOrgId(); + roleUser = await getRoleUser(); + TripPlanAction = await getTripPlanAction(); + + if (token == null || userId == null) { + print("Token or USerId missing"); + return; + } else { + setState(() { + futurePlans = fetchPlans(); + }); + + // Wait for futurePlans to be fetched and update allPlans + futurePlans.then((plans) { + setState(() { + allPlans = plans; // Set allPlans after the fetch is complete + filteredPlans = + allPlans; // You can update filteredPlans too if needed + }); + }); + } + } + + Future getUserId() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); + return userData["user_id"]?.toString(); + } catch (e) { + return null; + } + } + return null; + } + + Future getOrgId() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); + return userData["org_id"]?.toString(); + } catch (e) { + return null; + } + } + return null; + } + + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('auth_token'); + } + + // Fetch API Data + Future> fetchPlans() async { + // return []; + // final String apiUrldata = '$apiUrl/api/plans'; + // final String apiUrldata = '$apiUrl/api/plans?user_id=$userId'; + // final String apiUrldata = '$apiUrl/api/plans?user_id=$userId'; + final String apiUrldata = '$apiUrl/api/plans?org_id=$orgId&user_id=$userId'; + // api/plans?org_id=1&user_id=1 + // final token = await getToken(); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', // Add token here + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + // List plansJson = []; + List plansJson = data['data']; + return plansJson.map((json) => Plan.fromJson(json)).toList(); + } else { + throw Exception('Failed to load plans'); + } + } + + Future postPlanData(planData, planId) async { + final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan'; + + final token = await getToken(); // Fetch token + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + planData['is_active'] = "0"; + + print("POSTPlanTesting------- $planData"); // Add plan_id for update + + try { + final response = await http.post( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + body: jsonEncode(planData), // Convert map to JSON + ); + + if (response.statusCode == 200) { + print("Plan Deleted successfully!"); + print("Response: ${response.body}"); + initializeData(); + } else { + print("Failed to submit plan. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + } + } catch (e) { + print(" Error submitting plan: $e"); + } + } + + // Future> getViewPlan(String planId) async { + // final String apiUrldata = '$apiUrl/api/plans/find/$planId'; + // print("API URL: $apiUrldata"); + // // final token = await getToken(); + // + // if (token == null) { + // throw Exception('Token not found. Please log in.'); + // } + // + // final response = await http.put( + // Uri.parse(apiUrldata), + // headers: { + // 'Authorization': 'Bearer $token', // Add token here + // 'Content-Type': 'application/json', + // }, + // ); + // + // if (response.statusCode == 200) { + // final Map? resData = json.decode(response.body); + // + // return resData?["data"]; + // } else { + // throw Exception('Failed to load plans'); + // } + // } + // + // void viewPlan(String planId, {bool isViewMode = false}) async { + // try { + // Map planData = await getViewPlan(planId); + // print("ViewAAA - $planData"); + // + // context.go('/createPlan', + // extra: {'planData': planData, 'isViewMode': isViewMode}); + // } catch (e) { + // print("Error fetching plan: $e"); + // } + // } + + void deletePlan(String planId) async { + // try { + // Map planData = await ApiService.getViewPlan(planId); + // print("ViewAAA - $planData"); + // + // postPlanData(planData, planId); + // } catch (e) { + // print("Error fetching plan: $e"); + // } + + bool confirmed = await apiService.showCancelConfirmationDialog( + context, + layoutColor, + ); + + if (confirmed) { + try { + Map planData = await ApiService.getViewPlan(planId); + print("ViewAAA - $planData"); + refresh(); + // postPlanData(planData, planId); + } catch (e) { + print("Error fetching plan: $e"); + } + } else { + print("User cancelled"); + } + } + + void _handleBackButton() { + // final location = GoRouterState.of(context).uri.toString(); + + print("location - $location"); + if (location!.contains('/listPlan')) { + // Do nothing or show "Press again to exit" toast + print("Blocked back on /listPlan"); + } else { + print("/listPlan .."); + } + } + + Future _showDetails(id) async { + _listDialogShown = true; + final String apiUrldata = + '$apiUrl/api/plans/get_plan_approval_status?plan_id=$id'; + final String? token = await getToken(); + String label = ""; + List> approverData = []; + + if (token == null) { + print('Token not found. Please log in.'); + label = "Error - Token not found"; + } else { + try { + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + label = data['data']['model_heading'] ?? ""; + approverData = List>.from( + data['data']['approver_data'] ?? [], + ); + } else { + print('Failed to load'); + label = "Error - Failed to load data"; + } + } catch (e) { + print('Exception occurred: $e'); + label = "Error - Something went wrong"; + } + } + + showDialog( + context: context, + builder: + (context) => AlertDialog( + title: Text( + label, + style: GoogleFonts.poppins( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + overflow: TextOverflow.ellipsis, + ), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (approverData.isNotEmpty) + SizedBox( + height: 250, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: DataTable( + columns: [ + // DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), + // DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + approverData.asMap().entries.map((entry) { + final index = entry.key + 1; + final item = entry.value; + + final approverIdKey = item.keys.firstWhere( + (k) => k.endsWith('_id'), + orElse: () => '', + ); + final statusKey = item.keys.firstWhere( + (k) => k.endsWith('_status'), + orElse: () => '', + ); + + final approverId = + item[approverIdKey]?.toString() ?? '-'; + final statusText = + item[statusKey]?.toString() ?? 'Unknown'; + + return DataRow( + cells: [ + // DataCell(Text('$index')), + // DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))), + DataCell( + SizedBox( + child: Text( + statusText, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }).toList(), + ), + ), + ), + ) + else + Text( + "-- no data. --", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w300, + ), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + _listDialogShown = false; + }, + child: Text("Close"), + ), + ], + ), + ); + } + + Widget build(BuildContext context) { + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; + + return PopScope( + canPop: false, // Allow back navigation only if not login screen + onPopInvokedWithResult: (didPop, result) { + if (didPop) return; + _handleBackButton(); // Show exit confirmation dialog + }, + child: Scaffold( + backgroundColor: Color(0xFFf5f5f5), + // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), + // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), + appBar: CustomAppBar(isDesktop: isDesktop), + drawer: CustomDrawer(isDesktop: false), + body: Padding( + padding: + isDesktop + ? EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * + 0.1, // 30% of screen width as horizontal padding + vertical: + MediaQuery.of(context).size.height * + 0, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(0), + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + Expanded(child: buildGroupListLayout(isDesktop)), + ], + ), + ), + ), + ); + }, + ); + } + + Widget buildGroupListLayout(bool isDesktop) { + return Container( + // decoration: BoxDecoration( + // // color: Colors.amber, + // // color: bodyColor, + // color: Color(0xFFE1F5FE), + // border: Border.all( + // // color: Color(0xFFF7F7FB), + // color: Colors.white, + // width: 3.5)), + child: buildTableLayout(isDesktop), + ); + } + + Widget buildTableLayout(isDesktop) { + // String _formatDate(String rawDate) { + // try { + // final dateTime = DateTime.parse(rawDate); + // return DateFormat('dd, MMM yyyy hh:m a').format(dateTime); + // } catch (e) { + // return rawDate; // fallback if parsing fails + // } + // } + + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat( + 'dd, MMM yyyy HH:mm', + ).format(dateTime); // 24-hour format + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + + return Container( + margin: isDesktop ? EdgeInsets.all(10.0) : null, + padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20), + height: + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, + decoration: BoxDecoration( + border: + isDesktop + ? Border.all( + width: 2, + color: Colors.white, + // color: Color(0xFFF7F7FB), + ) + : null, + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + // color: Color(0xFFF7F7FB), + + // color: Colors.amber, + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + // padding: const EdgeInsets.all(10.0), + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Divider( + // thickness: 0.2, // how "thick" the line is + // color: Colors.grey, // optional + // ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Text( + 'My Trips', + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + // style: TextStyle( + // fontFamily: "Inter", + // fontSize: isDesktop ? 16 : 14, + // fontWeight: FontWeight.w600, + // color: Color(0xFF212121)) + ), + ], + ), + + // Spacer(), + SizedBox(width: MediaQuery.of(context).size.width * 0.23), + if (isDesktop) + Container( + width: MediaQuery.of(context).size.width * 0.2, + height: 40, + child: TextField( + controller: searchController, + onChanged: filterPlans, + decoration: InputDecoration( + hintText: "Search...", + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), + prefixIcon: Icon( + Icons.search, + color: Color(0xFF9E9DBD), + size: 18, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade200, + width: 0.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, + ), + ), + ), + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + + // SizedBox(width: 16), + if (isDesktop) Spacer(), + + // ElevatedButton( + // style: ElevatedButton.styleFrom( + // foregroundColor: Colors.white, + // backgroundColor: Colors.blueAccent, + // ), + // onPressed: () { + // context.go('/createPlan', extra: { + // 'orgId': orgId, + // }); + // if (!isDesktop) Navigator.pop(context); + // }, + // child: Row( + // children: [ + // Icon(Icons.add_circle, color: Colors.white), + // SizedBox(width: 5), + // Text('NewPlan'), + // + // ], + // ), + // ), + 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: + isDesktop + ? EdgeInsets.symmetric( + horizontal: 20, + vertical: 12, + ) + : EdgeInsets.symmetric( + horizontal: 15, + vertical: 10, + ), + ), + onPressed: () { + if (TripPlanAction == "Plan Creation Not Allowed") { + showDialog( + context: context, + builder: + (context) => AlertDialog( + title: Text( + "Action Not Allowed", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + content: Text( + "Plan Creation Not Allowed For This User.", + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text( + "OK", + style: TextStyle(color: layoutColor), + ), + ), + ], + ), + ); + } else { + context.go('/createPlan', extra: {'orgId': orgId}); + if (!isDesktop) Navigator.pop(context); + } + }, + child: Row( + mainAxisSize: + MainAxisSize.min, // Ensures content fits nicely + children: [ + Text( + "Add New Trip", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 13 : 11, + ), + ), + SizedBox(width: 8), // spacing between icon and text + Icon( + Icons.add_circle_outline_rounded, + size: 15, + color: Colors.white, + ), + ], + ), + ), + ], + ), + if (!isDesktop) const SizedBox(height: 10), + if (!isDesktop) + Row( + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.85, + height: 35, + child: TextField( + controller: searchController, + onChanged: filterPlans, + decoration: InputDecoration( + hintText: "Search...", + hintStyle: TextStyle( + fontSize: 12, + color: Color(0xFF9E9DBD), + ), + prefixIcon: Icon( + Icons.search, + color: Color(0xFF9E9DBD), + size: 18, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade200, + width: 0.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: Colors.grey.shade300, + width: 1, + ), + ), + ), + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + ], + ), + + const SizedBox(height: 10), + + FutureBuilder>( + future: futurePlans, + builder: (context, snapshot) { + final adjHgt = MediaQuery.of(context).size.height; + + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Icon(Icons.error_outline, + // color: Colors.redAccent, size: 60), + // SizedBox(height: 1), + // Text("Oops!", + // style: TextStyle( + // fontSize: 22, + // fontWeight: FontWeight.bold, + // color: Colors.redAccent)), + SizedBox(height: adjHgt / 4), + Text( + " No Trips Found", + textAlign: TextAlign.center, + // style: GoogleFonts.poppins( + // fontSize: 20, + // fontWeight: FontWeight.w500, + // color: Colors.black54, + // ), + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + // const SizedBox(height: 10), + // Text( + // "Please Create Trip", + // textAlign: TextAlign.center, + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w500, + // color: Colors.black54, + // ), + // ), + ], + ), + ), + ); + } + + // List plans = snapshot.data!; + + // List plans = + // filteredPlans.isNotEmpty ? filteredPlans : allPlans; + + List plans = + searchController.text.isEmpty ? allPlans : filteredPlans; + plans.sort( + (a, b) => + int.parse(b.planId).compareTo(int.parse(a.planId)), + ); + + List paginatedPlans = + plans + .skip(currentPage * itemsPerPage) + .take(itemsPerPage) + .toList(); + + Widget table = LayoutBuilder( + builder: (context, constraints) { + double minWidth = isDesktop ? constraints.maxWidth : 1300; + + return ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + dividerThickness: 0.5, + columnSpacing: isDesktop ? 24.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, + ), + ), + columns: [ + DataColumn( + label: Text( + 'Trip ID', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Trip Name', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Emp Code', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Traveller', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Created On', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( + label: Text( + 'Actions', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + rows: + paginatedPlans.map((plan) { + return DataRow( + cells: [ + DataCell( + Text( + plan.planId, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + + DataCell( + Text( + plan.tripTitle, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + + DataCell( + Text( + plan.employeeCode ?? " - ", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.userName.isNotEmpty + ? plan.userName + : plan.travellerName, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + plan.tripType, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + Text( + _formatDate(plan.createdOn), + // plan.createdOn, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), + DataCell( + GestureDetector( + onTap: () => _showDetails(plan.planId), + child: Container( + width: + double + .infinity, // Set your desired fixed size (equal width and height) + height: 25, + alignment: Alignment.center, + decoration: BoxDecoration( + color: getStatusColor( + plan.statusValue, + ), + // color: plan.statusValue == + // "Partially Approved" + // ? Colors.yellow.shade100 + // : plan.statusValue == "Approved" + // ? Colors.green.shade100 + // : plan.statusValue == "Completed" + // ? Colors.green.shade500 + // : plan.statusValue == "Rejected" + // ? Colors.red.shade100 + // : Colors.grey.shade100, + borderRadius: BorderRadius.circular( + 10, + ), + ), + child: Text( + plan.statusValue, + textAlign: TextAlign.center, + style: TextStyle( + color: getStatusTextColor( + plan.statusValue, + ), + // color: (plan.statusValue == + // "Partially Approved" || + // plan.statusValue == "Approved" || + // plan.statusValue == "Rejected") + // ? Colors.black + // : plan.statusValue == "Completed" + // ? Colors.white + // : Colors.grey, + fontSize: 12, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ), + DataCell( + Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + size: 14, + color: Color(0xFF475569), + ), + itemBuilder: + (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Row( + mainAxisSize: + MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + IconButton( + icon: Icon( + Icons + .remove_red_eye, + color: Color( + 0xFF475569, + ), + size: 18, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + true, + isMyTrips: true, + ); + }, + ), + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + false, + isMyTrips: + true, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + deletePlan( + plan.planId, + ); + }, + ), + IconButton( + icon: Icon( + Icons.download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The PDF', + onPressed: () { + Navigator.pop( + context, + ); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Trip Comments', + onPressed: () { + showDialog( + context: + context, + builder: + ( + context, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId + .toString(), + layoutColorForUser: + layoutColor!, + role: + "User", + ), + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), + ), + + // DataCell(Row(children: [ + // IconButton( + // icon: const Icon( + // Icons.remove_red_eye, + // color: Color(0xFF475569), + // size: 18, + // ), + // onPressed: () => ApiService.viewPlan( + // context, plan.planId, + // isViewMode: true)), + // + // GestureDetector( + // onTap: () => ApiService.viewPlan( + // context, plan.planId, + // isViewMode: false), + // child: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 15, + // height: 15), + // ), + // + // // IconButton( + // // icon: const Icon(Icons.edit, + // // color: Colors.green), + // // onPressed: () => viewPlan(plan.planId, + // // isViewMode: false) ), + // + // SizedBox( + // width: 5, + // ), + // GestureDetector( + // onTap: () => deletePlan(plan.planId), + // child: Image.asset( + // 'assets/images/IconsImg/delete.png', + // width: 20, + // height: 15), + // ), + // IconButton( + // icon: const Icon( + // Icons.download, + // color: Color(0xFF475569), + // size: 18, + // ), + // onPressed: () { + // apiService.getPdfDownload(plan.planId); + // }), + // ])), + ], + ); + }).toList(), + ), + ); + }, + ); + + Widget buildMobileCardView(List paginatedPlans) { + return ListView.builder( + itemCount: paginatedPlans.length, + itemBuilder: (context, index) { + final plan = paginatedPlans[index]; + return Card( + color: Colors.white, + margin: EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Status and Employee Code + // Row( + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // GestureDetector( + // onTap: () => _showDetails(plan.planId), + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, + // vertical: 4, + // ), + // decoration: BoxDecoration( + // color: getStatusColor( + // plan.statusValue, + // ), + // borderRadius: BorderRadius.circular( + // 8, + // ), + // ), + // child: Text( + // plan.statusValue, + // style: TextStyle( + // color: getStatusTextColor( + // plan.statusValue, + // ), + // fontSize: 10, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // ), + // // PlanPopupMenu( + // // plan: plan, + // // deletePlan: deletePlan, + // // apiService: apiService, + // // layoutColor: layoutColor, + // // ), + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon( + // Icons.more_vert, + // size: 14, + // color: Color(0xFF475569), + // ), + // itemBuilder: + // (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, + // vertical: 8, + // ), + // child: Row( + // mainAxisSize: + // MainAxisSize.min, + // mainAxisAlignment: + // MainAxisAlignment.center, + // children: [ + // IconButton( + // icon: Icon( + // Icons.remove_red_eye, + // color: Color( + // 0xFF475569, + // ), + // size: 18, + // ), + // tooltip: + // 'View The Trip Details', + // onPressed: () { + // Navigator.pop( + // context, + // ); // Close popup manually + // ApiService.viewPlan( + // context, + // plan.planId, + // isViewMode: true, + // isMyTrips: true, + // ); + // }, + // ), + // if (plan.statusValue == + // "Pending Approval" || + // plan.statusValue == + // "Partially Approved") + // IconButton( + // icon: Image.asset( + // 'assets/images/IconsImg/edit.png', + // width: 20, + // height: 15, + // ), + // tooltip: + // 'Edit Trip Details', + // onPressed: () { + // Navigator.pop( + // context, + // ); + // ApiService.viewPlan( + // context, + // plan.planId, + // isViewMode: false, + // isMyTrips: true, + // ); + // }, + // ), + // IconButton( + // icon: Icon( + // Icons.cancel_rounded, + // size: 18, + // ), + // tooltip: + // 'Cancellation The Trip Details', + // onPressed: () { + // Navigator.pop(context); + // deletePlan(plan.planId); + // }, + // ), + // IconButton( + // icon: Icon( + // Icons.download, + // color: Color( + // 0xFF114D8B, + // ), + // size: 18, + // ), + // tooltip: + // 'Download The PDF', + // onPressed: () { + // Navigator.pop(context); + // apiService + // .getPdfDownload( + // plan.planId, + // ); + // }, + // ), + // IconButton( + // icon: const Icon( + // Icons.comment, + // color: Color( + // 0xFF475569, + // ), + // size: 11, + // ), + // tooltip: 'Trip Comments', + // onPressed: () { + // showDialog( + // context: context, + // builder: + // ( + // context, + // ) => CommentModalList( + // // planId: plan.planId, + // planId: + // plan.planId + // .toString(), + // layoutColorForUser: + // layoutColor!, + // role: "User", + // ), + // ); + // }, + // ), + // ], + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // SizedBox(height: 2), + // // Trip Id and Trip Name + // Row( + // children: [ + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text( + // ' ${plan.tripTitle}', + // style: TextStyle( + // fontSize: 12, + // fontFamily: "Inter", + // fontWeight: FontWeight.bold, + // ), + // ), + // ], + // ), + // ], + // ), + // SizedBox(height: 2), + // // Type and Created On + // Row( + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text( + // ' ${plan.tripType}', + // style: TextStyle( + // fontSize: 9, + // color: Colors.black87, + // fontFamily: "Inter", + // ), + // ), + // ], + // ), + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text( + // '${_formatDate(plan.createdOn)}', + // style: TextStyle( + // fontSize: 9, + // color: Colors.black87, + // fontFamily: "Inter", + // ), + // ), + // ], + // ), + // ], + // ), + // SizedBox(height: 3), + // // Name + // Row( + // children: [ + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text( + // plan.userName.isNotEmpty + // ? '${plan.userName}' + // : '${plan.travellerName}', + // style: TextStyle( + // fontSize: 9, + // fontFamily: "Inter", + // color: Colors.black87, + // ), + // ), + // ], + // ), + // Spacer(), + // Column( + // crossAxisAlignment: + // CrossAxisAlignment.start, + // children: [ + // Text( + // plan.employeeCode ?? 'No Data', + // style: TextStyle( + // fontSize: 9, + // fontFamily: "Inter", + // color: Colors.black87, + // ), + // ), + // ], + // ), + // ], + // ), + // Actions + Expanded( + child: Container( + // color: Colors.yellow.shade50, + child: Column( + children: [ + Row( + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + children: [ + Container( + // color: Colors.red.shade50, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Image.asset( + plan.tripType == 'Domestic' + ? 'assets/images/IconsImg/Domestic_new.png' + : 'assets/images/IconsImg/International_new.png', + // width: 65, + height: + plan.tripType == + 'Domestic' + ? 28 + : 30, + ), + ], + ), + ), + SizedBox(width: 10), + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '${plan.tripTitle}', + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: + FontWeight.w500, + ), + ), + ], + ), + ), + + SizedBox(width: 10), + Column( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: + (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 1, + // horizontal: 8, + // vertical: 8, + ), + child: Row( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + IconButton( + icon: Icon( + Icons + .remove_red_eye, + color: Color( + 0xFF475569, + ), + size: 18, + ), + tooltip: + 'View The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); // Close popup manually + ApiService.viewPlan( + context, + plan.planId, + isViewMode: true, + isMyTrips: true, + ); + }, + ), + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: + 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + ApiService.viewPlan( + context, + plan.planId, + isViewMode: false, + isMyTrips: true, + ); + }, + ), + IconButton( + icon: Icon( + Icons + .cancel_rounded, + size: 18, + ), + tooltip: + 'Cancellation The Trip Details', + onPressed: () { + Navigator.pop(context); + deletePlan(plan.planId); + }, + ), + IconButton( + icon: Icon( + Icons + .download, + color: Color( + 0xFF114D8B, + ), + size: 18, + ), + tooltip: + 'Download The PDF', + onPressed: () { + Navigator.pop(context); + apiService + .getPdfDownload( + plan.planId, + ); + }, + ), + IconButton( + icon: const Icon( + Icons + .comment, + color: Color( + 0xFF475569, + ), + size: 11, + ), + tooltip: + 'Trip Comments', + onPressed: () { + showDialog( + context: context, + builder: + ( + context, + ) => CommentModalList( + // planId: plan.planId, + planId: + plan.planId + .toString(), + layoutColorForUser: + layoutColor!, + role: "User", + ), + ); + }, + ), + ], + ), + ), + ), + ], + ), + ], + ), + ], + ), + + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + crossAxisAlignment: + CrossAxisAlignment.end, + + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + // plan.userName.isNotEmpty + // ? plan.userName + // : '${plan.travellerName}', + '${plan.userName.isNotEmpty ? plan.userName : plan.travellerName} (${plan.employeeCode ?? 'No Data'})', + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + ), + ), + Text( + '${_formatDate(plan.createdOn)}', + style: GoogleFonts.poppins( + fontSize: 9, + color: Colors.grey, + ), + ), + ], + ), + Column( + mainAxisAlignment: + MainAxisAlignment.end, + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + GestureDetector( + onTap: + () => _showDetails( + plan.planId, + ), + child: Container( + padding: + EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + color: getStatusColor( + plan.statusValue, + ), + borderRadius: + BorderRadius.circular( + 8, + ), + ), + child: Text( + plan.statusValue, + style: GoogleFonts.poppins( + color: + getStatusTextColor( + plan.statusValue, + ), + fontSize: 9, + fontWeight: + FontWeight.w600, + ), + ), + ), + ), + ], + ), + ], + ), + ], + ), + ), + ), + ], + ), + ), + ); + }, + ); + } + + return Expanded( + child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: + isDesktop + ? (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No Matches Found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredPlans.isEmpty + ? Center( + child: Text( + "No Matches Found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView(paginatedPlans)), + // child: isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedPlans), + ), + + PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + totalItems: plans.length, + activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 0; + }); + }, + ), + ], + ), + ); + }, + ), + ], + ), + ), + ), + ); + } + + // Helper functions for colors + Color getStatusColor(String status) { + if (status == "Partially Approved") return Colors.yellow.shade100; + if (status == "Approved") return Colors.green.shade100; + if (status == "Completed") return Colors.green.shade500; + if (status == "Rejected") return Colors.red.shade100; + return Colors.grey.shade100; + } + + Color getStatusTextColor(String status) { + if (status == "Partially Approved" || + status == "Approved" || + status == "Rejected") { + return Colors.black; + } else if (status == "Completed") { + return Colors.white; + } + return Colors.grey; + } +} diff --git a/lib/Screens/policy/policy.dart b/lib/Screens/policy/policy.dart index b8df39d..bfff630 100644 --- a/lib/Screens/policy/policy.dart +++ b/lib/Screens/policy/policy.dart @@ -40,7 +40,7 @@ class Policy extends StatefulWidget { class _PolicyState extends State { final GlobalKey policyCriteriaKey = - GlobalKey(); + GlobalKey(); final ApiService apiService = ApiService(); @@ -50,10 +50,12 @@ class _PolicyState extends State { Color? layoutColor; Color? bodyColor; late String policyType = "domestic"; - String selectedServiceDBValue = '1'; + // String selectedServiceDBValue = '1'; + List serviceDBValueList = []; ValueNotifier selectedServiceIndex = ValueNotifier("1"); ValueNotifier selectedServicePriorityIndex = ValueNotifier("1"); - String selectedService = "train"; + ValueNotifier selectedServiceDBValue = ValueNotifier("0"); + String? selectedService; // ValueNotifier selectedService = ValueNotifier("Train"); bool isViewMode = false; bool isDisable = false; @@ -84,22 +86,23 @@ class _PolicyState extends State { Map get policyData { List> policyDetails = - policy_details!.where((service) { - // Only check these specific fields for emptiness - final fieldsToCheck = [ - 'cost', - 'class', - 'a1_action', - 'a2_action', - 'a3_action', - ]; + policy_details!.where((service) { + // Only check these specific fields for emptiness + final fieldsToCheck = [ + 'cost', + 'class', + 'a1_action', + 'a2_action', + 'a3_action', + ]; + + // If any of the important fields has a value, keep it + return fieldsToCheck.any((field) { + final value = service[field]; + return value != null && value.toString().trim().isNotEmpty; + }); + }).toList(); - // If any of the important fields has a value, keep it - return fieldsToCheck.any((field) { - final value = service[field]; - return value != null && value.toString().trim().isNotEmpty; - }); - }).toList(); Map data = { "name": _policyController.text, @@ -152,7 +155,6 @@ class _PolicyState extends State { // } // }); } - void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { @@ -160,7 +162,6 @@ class _PolicyState extends State { }); }); } - void _checkAuthAndLoadData() async { final String? token = await getToken(); // Your async function to get token @@ -175,19 +176,33 @@ class _PolicyState extends State { try { loadinitializeData(); - updateSelectedServices(); + await updateSelectedServices(); updateData(); loadInitialData(); - if (widget.policy != null) { - final details = List>.from( - widget.policy!['policy_details'], - ); - policyCriteriaKey.currentState?.loadPolicyDetails(details); - policyCriteriaKey.currentState?.fetchTrainFlightClass(); - } + + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (widget.policy != null) { + final details = List>.from( + widget.policy!['policy_details'], + ); + policyCriteriaKey.currentState?.loadPolicyDetails(details); + } + }); + + + // if (widget.policy != null) { + // print("185"); + // final details = List>.from( + // widget.policy!['policy_details'], + // ); + // policyCriteriaKey.currentState?.loadPolicyDetails(details); + // + // policyCriteriaKey.currentState?.fetchTrainFlightClass(); + // } } catch (e) { print("group : $e"); } @@ -199,14 +214,14 @@ class _PolicyState extends State { setState(() { layoutColor = - layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; bodyColor = - bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; }); } @@ -238,9 +253,9 @@ class _PolicyState extends State { try { List decoded = json.decode(rawServices); List> formatted = - decoded - .map((e) => {"service_id": e['service_id'].toString()}) - .toList(); + decoded + .map((e) => {"service_id": e['service_id'].toString()}) + .toList(); setState(() { selectedOrgServiceIds = formatted; @@ -261,45 +276,70 @@ class _PolicyState extends State { } Future updateSelectedServices() async { + print("started"); await loadAllServices(); await loadOrgSelectedAlServices(); - + print("III-end"); final selectedIds = - selectedOrgServiceIds.map((e) => e['service_id']).toSet(); + selectedOrgServiceIds.map((e) => e['service_id']).toSet(); if (widget.policy != null) { + print("inside if"); + print(widget.policy); final details = List>.from( widget.policy!['policy_details'], ); + print("****details-$details"); print( "UUFiltered Selected Services - ${widget.policy!['services_ids']} ", ); - // pr int("UUFiltered Selected Services - $details"); + + final exisitingService = List>.from( + jsonDecode(widget.policy!['services_ids']), + ); + print("exisitingService ${exisitingService}"); final filtered = - selectedAllServices! - .where( - (service) => - selectedIds.contains(service['service_id'].toString()), - ) - .toList(); + exisitingService! + .where( + (service) => + selectedIds.contains(service['service_id'].toString()), + ) + .toList(); setState(() { ServicesChoosed = filtered; }); + print("298 ServicesChoosed ${ServicesChoosed}"); + - print("Filtered Selected Services ChooesedIpfa1: $ServicesChoosed"); if (ServicesChoosed!.isNotEmpty) { - String firstServiceName = ServicesChoosed?.first['name']; - print("✅ First service name selected for filter: $firstServiceName"); - selectedService = firstServiceName; - // services = ServicesChoosed! - // .map((service) => service['name'].toString()) - // .toList(); - } + print("Filtered Selected Services ChooesedIpfa1: $ServicesChoosed"); + + ServicesChoosed!.sort( + (a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0), + ); + String firstServiceName = ServicesChoosed?.first['name']; + String firstServiceId = ServicesChoosed?.first['service_id']; + + print("✅ 306 First service name selected for filter: $firstServiceName - $firstServiceId"); + selectedService = firstServiceName; + if (selectedServiceDBValue.value == "0") { + setState(() { + selectedServiceDBValue.value = firstServiceId.toString(); + }); + } + List serviceDBvalue = ServicesChoosed! + .map((item) => int.parse(item['service_id'].toString())) + .toList(); + serviceDBValueList = serviceDBvalue; + + } + print(widget.policy); + // updateData(); if (widget.policy != null && widget.policy!['services_ids'] != null) { final decoded = jsonDecode(widget.policy!['services_ids']); @@ -308,26 +348,30 @@ class _PolicyState extends State { List>.from(decoded) .map( (service) => { - 'service_id': service['service_id'].toString(), - 'name': service['name'].toString(), - 'order': service['order'].toString(), - }, - ) + 'service_id': service['service_id'].toString(), + 'name': service['name'].toString(), + 'order': service['order'].toString(), + }, + ) .toList(); }); print("✅ Loaded services from policy (decoded): $services"); + + } print("Filtered Selected Services Added to Policy: $ServicesChoosed"); - } else { + + } + else { final filtered = - selectedAllServices! - .where( - (service) => - selectedIds.contains(service['service_id'].toString()), - ) - .toList(); + selectedAllServices! + .where( + (service) => + selectedIds.contains(service['service_id'].toString()), + ) + .toList(); print("ServicesChoosedYY: $ServicesChoosed"); setState(() { @@ -342,27 +386,45 @@ class _PolicyState extends State { ServicesChoosed! .map( (service) => { - 'service_id': service['service_id'].toString(), - 'name': - service['name'].toString(), // ✅ no space before 'name' - 'order': service['order'].toString(), - }, - ) + 'service_id': service['service_id'].toString(), + 'name': + service['name'].toString(), // ✅ no space before 'name' + 'order': service['order'].toString(), + }, + ) .toList(); }); print("Filtered Selected Services Chooesed1: $ServicesChoosed"); if (ServicesChoosed!.isNotEmpty) { - String firstServiceName = ServicesChoosed?.first['name']; - print("✅ First service name selected for filter: $firstServiceName"); - selectedService = firstServiceName; policyCriteriaKey.currentState?.fetchTrainFlightClass(); + ServicesChoosed!.sort( + (a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0), + ); + String firstServiceName = ServicesChoosed?.first['name']; + String firstServiceId = ServicesChoosed?.first['service_id']; + print("✅ 363 First service name selected for filter: $firstServiceName - $firstServiceId"); + selectedService = firstServiceName; + if (selectedServiceDBValue.value == "0") { + setState(() { + selectedServiceDBValue.value = firstServiceId.toString(); + }); + } + + List serviceDBvalue = ServicesChoosed! + .map((item) => int.parse(item['service_id'].toString())) + .toList(); + serviceDBValueList = serviceDBvalue; + + } } } void updateData() { + print("updateData"); + // if (widget.policy != null && selectedService != null && selectedServiceDBValue.value != "0") { if (widget.policy != null) { setState(() { selectedPolicyId = widget.policy?["policy_id"] ?? ""; @@ -380,6 +442,7 @@ class _PolicyState extends State { policy_details = List>.from( widget.policy?["policy_details"] ?? [], ); + // print("policy_details ${policy_details}"); }); } } @@ -462,6 +525,7 @@ class _PolicyState extends State { }); } + bool isValidData(Map data) { errorMessages.clear(); // Reset previous errors @@ -480,7 +544,7 @@ class _PolicyState extends State { if (domestic != "1" && international != "1") { errorMessages["trip_type"] = - "Required"; // "Please select Domestic or International."; + "Required"; // "Please select Domestic or International."; } // Validate at least one policy_detail with valid content @@ -502,7 +566,7 @@ class _PolicyState extends State { if (!hasAtLeastOneDetail) { errorMessages["policy_details"] = - "Required"; // "At least one valid policy detail is required."; + "Required"; // "At least one valid policy detail is required."; } return errorMessages.isEmpty; @@ -571,16 +635,16 @@ class _PolicyState extends State { drawer: CustomDrawer(isDesktop: false), body: Padding( padding: - isDesktop - ? EdgeInsets.symmetric( - horizontal: - MediaQuery.of(context).size.width * - 0.1, // 30% of screen width as horizontal padding - vertical: - MediaQuery.of(context).size.height * - 0, // 5% of screen height as vertical padding - ) - : EdgeInsets.all(8), + isDesktop + ? EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * + 0.1, // 30% of screen width as horizontal padding + vertical: + MediaQuery.of(context).size.height * + 0, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(8), child: Column( children: [ Expanded( @@ -666,15 +730,15 @@ class _PolicyState extends State { color: Colors.white, padding: const EdgeInsets.all(8.0), child: - isDesktop - ? Row( - mainAxisAlignment: MainAxisAlignment.end, - children: _buildSubmit(isDesktop), - ) - : Row( - mainAxisAlignment: MainAxisAlignment.center, - children: _buildSubmit(isDesktop), - ), + isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.end, + children: _buildSubmit(isDesktop), + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildSubmit(isDesktop), + ), ), ], ), @@ -687,9 +751,9 @@ class _PolicyState extends State { // ? EdgeInsets.all(10.0) // : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), height: - isDesktop - ? MediaQuery.of(context).size.height * 0.98 - : MediaQuery.of(context).size.height, + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, // decoration: BoxDecoration( // border: isDesktop // ? Border.all( @@ -738,23 +802,23 @@ class _PolicyState extends State { Container( padding: isDesktop ? const EdgeInsets.only(left: 35) : null, child: - isDesktop - ? Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded(child: _buildPolicyNameField(isDesktop)), - Spacer(), - Expanded(child: _buildPolicyTypeField(isDesktop)), - ], - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildPolicyNameField(isDesktop), - SizedBox(height: 20), - _buildPolicyTypeField(isDesktop), - ], - ), + isDesktop + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: _buildPolicyNameField(isDesktop)), + Spacer(), + Expanded(child: _buildPolicyTypeField(isDesktop)), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildPolicyNameField(isDesktop), + SizedBox(height: 20), + _buildPolicyTypeField(isDesktop), + ], + ), ), SizedBox(height: 10), Divider(thickness: 0.1, color: Colors.grey), @@ -767,66 +831,66 @@ class _PolicyState extends State { ], isDesktop ? Padding( - padding: const EdgeInsets.only(left: 30.0, right: 98.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Approval Criteria", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - Text( - "Service Priority", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - ], + padding: const EdgeInsets.only(left: 30.0, right: 98.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Approval Criteria", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), - ) + Text( + "Service Priority", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + ], + ), + ) : SizedBox.shrink(), isDesktop ? Container( - // color: Colors.yellow.shade50, - height: MediaQuery.of(context).size.height * 0.54, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + // color: Colors.yellow.shade50, + height: MediaQuery.of(context).size.height * 0.54, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( children: [ - Column( - children: [ - _buildPolicyOrdering(isDesktop), - _buildPolicyCategory(isDesktop), - ], - ), - Column(children: [_buildPolicyCategoryList(isDesktop)]), + _buildPolicyOrdering(isDesktop), + _buildPolicyCategory(isDesktop), ], ), - ), - ) + Column(children: [_buildPolicyCategoryList(isDesktop)]), + ], + ), + ), + ) : Expanded( - child: Container( - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - children: [ - _buildPolicyCategoryList(isDesktop), - _buildPolicyOrdering(isDesktop), - _buildPolicyCategory(isDesktop), - ], - ), - ), + child: Container( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + _buildPolicyCategoryList(isDesktop), + _buildPolicyOrdering(isDesktop), + _buildPolicyCategory(isDesktop), + ], ), ), + ), + ), // isDesktop // ? Expanded( // child: Row( @@ -940,28 +1004,28 @@ class _PolicyState extends State { border: Border.all(color: Colors.blueGrey.shade100, width: 0.35), ), child: - isDesktop - ? Padding( - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [_buildPolicySubCategoryList(isDesktop)], - ), - ) - : Column( - children: [ - Container( - // color: Colors.amber, - child: _buildPolicySubCategoryList(isDesktop), - // child: Text("FAta"), - // child: Row( - // mainAxisAlignment: MainAxisAlignment.start, - // children: [_buildPolicySubCategoryList(isDesktop)], - // ), - ), - ], - ), + isDesktop + ? Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [_buildPolicySubCategoryList(isDesktop)], + ), + ) + : Column( + children: [ + Container( + // color: Colors.amber, + child: _buildPolicySubCategoryList(isDesktop), + // child: Text("FAta"), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.start, + // children: [_buildPolicySubCategoryList(isDesktop)], + // ), + ), + ], + ), ); } @@ -1118,21 +1182,21 @@ class _PolicyState extends State { width: isDesktop ? MediaQuery.of(context).size.width * 0.62 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.75 : null, child: - isDesktop - ? Container( - // color: Colors.amber, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [_buildPolicyServiceOrdering(isDesktop)], - ), - ) - : Container( - // color: Colors.amber, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [_buildPolicyServiceOrdering(isDesktop)], - ), - ), + isDesktop + ? Container( + // color: Colors.amber, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [_buildPolicyServiceOrdering(isDesktop)], + ), + ) + : Container( + // color: Colors.amber, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [_buildPolicyServiceOrdering(isDesktop)], + ), + ), ); } @@ -1143,16 +1207,18 @@ class _PolicyState extends State { // Sort services by 'order' ServicesChoosed!.sort( - (a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0), + (a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0), ); List services = - ServicesChoosed!.map((service) => service['name'].toString()).toList(); + ServicesChoosed!.map((service) => service['name'].toString()).toList(); + + // List serviceDBvalue = + // ServicesChoosed!.map((service) => int.parse(service['service_id'].toString())).toList(); + List serviceDBvalue = ServicesChoosed! + .map((item) => int.parse(item['service_id'].toString())) + .toList(); - List serviceDBvalue = - ServicesChoosed! - .map((service) => service['service_id'].toString()) - .toList(); print("1093"); print(services); @@ -1164,110 +1230,109 @@ class _PolicyState extends State { child: Flex( direction: Axis.horizontal, children: - services.asMap().entries.map((entry) { - int index = entry.key + 1; - String service = entry.value; - String serviceId = index.toString(); - bool isSelected = - selectedServiceIndex.value == index.toString(); + services.asMap().entries.map((entry) { + print("1182 ${entry}"); + int i = entry.key; + int index = i + 1; + String service = entry.value; + int serviceId = serviceDBvalue[i]; - return SizedBox( - // width: isDesktop ? 40 : null, - height: - isDesktop - ? max( - (MediaQuery.of(context).size.height * 0.075), - 10, - ) - : 45, + bool isSelected = + selectedServiceIndex.value == index.toString(); - // max((MediaQuery.of(context).size.height * 0.09), 10) - child: GestureDetector( - onTap: () { - print("Selected Services - $service - $index"); - setState(() { - selectedServiceIndex.value = index.toString(); - selectedServiceDBValue = serviceDBvalue[entry.key]; - selectedService = service; + return SizedBox( + // width: isDesktop ? 40 : null, + height: + isDesktop + ? max( + (MediaQuery.of(context).size.height * 0.075), + 10, + ) + : 45, - print( - " selectedServiceIndex.value - ${selectedServiceIndex.value}", - ); + // max((MediaQuery.of(context).size.height * 0.09), 10) + child: GestureDetector( + onTap: () { + print("1201 Selected Services - $service - Tab Index - $index Service Id- $serviceId"); + setState(() { + selectedServiceIndex.value = index.toString(); + selectedServiceDBValue.value = serviceId.toString(); + selectedService = service; - print( - " selectedServiceDBValue.value - ${selectedServiceDBValue}", - ); + print( + " selectedServiceIndex.value - ${selectedServiceIndex.value}", + ); - // policyCriteriaKey.currentState?.fieldForPolicy(); - // policyCriteriaKey.currentState - // ?.addOrUpdatePolicy(selectedServiceIndex.value); - print("policyselectedService - $selectedService"); - if (selectedService == "Flight" || - selectedService == "Train" || - selectedService == "Accomodation") { - showClass = true; - showCost = false; - int serviceCode = selectedService == "Flight" ? 1 : 2; - print("policyselectedService1 - $selectedService"); - policyCriteriaKey.currentState - ?.fetchTrainFlightClass(); - } - // else if (selectedService == "Accommodation") { - // showClass = true; - // showCost = false; - // } - else { - showClass = false; - showCost = false; - } - }); - }, - child: Container( - margin: const EdgeInsets.all(5), - padding: - isDesktop - ? const EdgeInsets.all(8) - : const EdgeInsets.symmetric( - horizontal: 8, - vertical: 3, - ), - alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - service, - style: GoogleFonts.poppins( - color: - isSelected - ? const Color(0xFF114D8B) - : Colors.black87, - fontSize: 13, - fontWeight: - isSelected - ? FontWeight.bold - : FontWeight.w500, - decoration: - TextDecoration - .none, // remove built-in underline - ), - ), - if (isSelected) - const SizedBox( - height: 1, - ), // spacing between text and underline - if (isSelected) - Container( - height: 2, - width: 30, // or based on text width - color: const Color(0xFF114D8B), - ), - ], - ), - ), + + // policyCriteriaKey.currentState?.fieldForPolicy(); + // policyCriteriaKey.currentState + // ?.addOrUpdatePolicy(selectedServiceIndex.value); + print("policyselectedService - $selectedService"); + if (selectedService == "Flight" || + selectedService == "Train" || selectedService == "Accomodation") { + showClass = true; + showCost = false; + // int serviceCode = selectedService == "Flight" ? 1 : 2; + print("policyselectedService1 - $selectedService"); + policyCriteriaKey.currentState + ?.fetchTrainFlightClass(); + } + // else if (selectedService == "Accommodation") { + // showClass = true; + // showCost = false; + // } + else { + showClass = false; + showCost = false; + } + }); + }, + child: Container( + margin: const EdgeInsets.all(5), + padding: + isDesktop + ? const EdgeInsets.all(8) + : const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, ), - ); - }).toList(), + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + service, + style: GoogleFonts.poppins( + color: + isSelected + ? const Color(0xFF114D8B) + : Colors.black87, + fontSize: 13, + fontWeight: + isSelected + ? FontWeight.bold + : FontWeight.w500, + decoration: + TextDecoration + .none, // remove built-in underline + ), + ), + if (isSelected) + const SizedBox( + height: 1, + ), // spacing between text and underline + if (isSelected) + Container( + height: 2, + width: 30, // or based on text width + color: const Color(0xFF114D8B), + ), + ], + ), + ), + ), + ); + }).toList(), ), ), ); @@ -1291,7 +1356,7 @@ class _PolicyState extends State { } List services = - ServicesChoosed!.map((service) => service['name'].toString()).toList(); + ServicesChoosed!.map((service) => service['name'].toString()).toList(); return Expanded( child: SingleChildScrollView( @@ -1299,107 +1364,112 @@ class _PolicyState extends State { child: Flex( direction: isDesktop ? Axis.vertical : Axis.horizontal, children: - services.asMap().entries.map((entry) { - int index = entry.key + 1; - String service = entry.value; - bool isSelected = - selectedServiceIndex.value == index.toString(); + services.asMap().entries.map((entry) { + int index = entry.key + 1; + String service = entry.value; + bool isSelected = + selectedServiceIndex.value == index.toString(); - return SizedBox( - width: isDesktop ? 180 : null, - height: - isDesktop - ? max( - (MediaQuery.of(context).size.height * 0.075), - 10, - ) - : 45, + return SizedBox( + width: isDesktop ? 180 : null, + height: + isDesktop + ? max( + (MediaQuery.of(context).size.height * 0.075), + 10, + ) + : 45, - // max((MediaQuery.of(context).size.height * 0.09), 10) - child: GestureDetector( - onTap: () { - print("Selected Services - $service - $index"); - setState(() { - // selectedServiceIndex.value = index.toString(); - // selectedService = service; - // - // if (selectedService == "Flight" || - // selectedService == "Train") { - // showClass = true; - // showCost = true; - // int serviceCode = selectedService == "Flight" ? 1 : 2; - // policyCriteriaKey.currentState?.fetchTrainFlightClass(); - // } else if (selectedService == "Accommodation") { - // showClass = true; - // showCost = false; - // } else { - // showClass = false; - // showCost = false; - // } - }); - }, - child: Container( - margin: EdgeInsets.all(5), - padding: - isDesktop - ? EdgeInsets.all(8) - : EdgeInsets.only( - top: 3, - bottom: 3, - left: 8, - right: 8, - ), - decoration: BoxDecoration( - // color: Colors.blue, - color: isSelected ? Color(0xFF114D8B) : Colors.white, - // : Color(0xFFEBEBF7), - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: Colors.white, // Light grey border - width: 1, - ), - boxShadow: [ - BoxShadow( - color: Colors.grey.withAlpha(90), // Shadow color - blurRadius: 1, // Blur radius - spreadRadius: 1, // Spread radius - offset: Offset(0, 1), // Shadow position - ), - ], - ), - alignment: Alignment.center, - child: Text( - service, - style: GoogleFonts.poppins( - color: isSelected ? Colors.white : Colors.black87, - fontSize: 13, - fontWeight: - isSelected ? FontWeight.bold : FontWeight.w100, - ), + // max((MediaQuery.of(context).size.height * 0.09), 10) + child: GestureDetector( + onTap: () { + print("Selected Services - $service - $index"); + setState(() { + // selectedServiceIndex.value = index.toString(); + // selectedService = service; + // + // if (selectedService == "Flight" || + // selectedService == "Train") { + // showClass = true; + // showCost = true; + // int serviceCode = selectedService == "Flight" ? 1 : 2; + // policyCriteriaKey.currentState?.fetchTrainFlightClass(); + // } else if (selectedService == "Accommodation") { + // showClass = true; + // showCost = false; + // } else { + // showClass = false; + // showCost = false; + // } + }); + }, + child: Container( + margin: EdgeInsets.all(5), + padding: + isDesktop + ? EdgeInsets.all(8) + : EdgeInsets.only( + top: 3, + bottom: 3, + left: 8, + right: 8, + ), + decoration: BoxDecoration( + // color: Colors.blue, + color: isSelected ? Color(0xFF114D8B) : Colors.white, + // : Color(0xFFEBEBF7), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: Colors.white, // Light grey border + width: 1, + ), + boxShadow: [ + BoxShadow( + color: Colors.grey.withAlpha(90), // Shadow color + blurRadius: 1, // Blur radius + spreadRadius: 1, // Spread radius + offset: Offset(0, 1), // Shadow position ), + ], + ), + alignment: Alignment.center, + child: Text( + service, + style: GoogleFonts.poppins( + color: isSelected ? Colors.white : Colors.black87, + fontSize: 13, + fontWeight: + isSelected ? FontWeight.bold : FontWeight.w100, ), ), - ); - }).toList(), + ), + ), + ); + }).toList(), ), ), ); } Widget _buildPolicyCategory(bool isDesktop) { + print("1408 $selectedServiceIndex"); + print("1409 $selectedService"); + print("1410 ${selectedServiceDBValue.value}"); return Container( margin: isDesktop ? EdgeInsets.all(5) : null, // color: Colors.brown.shade100, // child: Text("data"), // color: Colors.white60, - child: PolicyCriteria( + child: (selectedService != null && selectedServiceDBValue.value != "0") + ? PolicyCriteria( key: policyCriteriaKey, isDesktop: isDesktop, isClass: showClass, isCost: showCost, selectedTabNotifier: selectedServiceIndex, - selectedService: selectedService, - selectedServiceDBValue: selectedServiceDBValue, + selectedIdNotifier: selectedServiceDBValue, + selectedService: selectedService!, + serviceDBValueList: serviceDBValueList, // 👈 Pass the array here userId: userId, onPolicyDataChanged: (List>? policyData) { print("🟢 policyData received from child: $policyData"); @@ -1411,7 +1481,7 @@ class _PolicyState extends State { }); }); }, - ), + ) : const Center(child: CircularProgressIndicator()), ); } @@ -1458,14 +1528,14 @@ class _PolicyState extends State { borderRadius: BorderRadius.circular(4), // Rounded rectangle border: Border.all( color: - _selectedTripType == "1" ? Colors.green : Colors.black, + _selectedTripType == "1" ? Colors.green : Colors.black, width: _selectedTripType == "1" ? 2 : 1, ), ), child: - _selectedTripType == "1" - ? Icon(Icons.rectangle, size: 8, color: Colors.green) - : null, // Add checkmark if selected + _selectedTripType == "1" + ? Icon(Icons.rectangle, size: 8, color: Colors.green) + : null, // Add checkmark if selected ), ), ], @@ -1514,14 +1584,14 @@ class _PolicyState extends State { borderRadius: BorderRadius.circular(4), // Rounded rectangle border: Border.all( color: - _selectedTripType == "2" ? Colors.green : Colors.black, + _selectedTripType == "2" ? Colors.green : Colors.black, width: _selectedTripType == "2" ? 2 : 1, ), ), child: - _selectedTripType == "2" - ? Icon(Icons.rectangle, size: 8, color: Colors.green) - : null, // Add checkmark if selected + _selectedTripType == "2" + ? Icon(Icons.rectangle, size: 8, color: Colors.green) + : null, // Add checkmark if selected ), ), ], @@ -1566,17 +1636,17 @@ class _PolicyState extends State { SizedBox(width: 20), MouseRegion( cursor: - isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, + isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: - isViewMode ? layoutColor : layoutColor, // Keep original color + isViewMode ? layoutColor : layoutColor, // Keep original color foregroundColor: - isViewMode ? Colors.white : Colors.white, // Keep original color + isViewMode ? Colors.white : Colors.white, // Keep original color disabledBackgroundColor: - layoutColor, // Ensure color remains when disabled + layoutColor, // Ensure color remains when disabled disabledForegroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), @@ -1585,8 +1655,8 @@ class _PolicyState extends State { padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: - // isViewMode ? null : handleSubmit, // Disable when in view mode - isViewMode || isDisable ? null : handleSubmit, + // isViewMode ? null : handleSubmit, // Disable when in view mode + isViewMode || isDisable ? null : handleSubmit, child: Text("Submit", style: GoogleFonts.poppins(fontSize: 10)), ), ), @@ -1658,4 +1728,4 @@ class _PolicyState extends State { ), ]; } -} +} \ No newline at end of file diff --git a/lib/Screens/policy/policyCriteria.dart b/lib/Screens/policy/policyCriteria.dart index 0193d1f..8bec508 100644 --- a/lib/Screens/policy/policyCriteria.dart +++ b/lib/Screens/policy/policyCriteria.dart @@ -14,11 +14,12 @@ import '../../widgets/custom_user_form.dart'; class PolicyCriteria extends StatefulWidget { bool isDesktop; String selectedService; - String selectedServiceDBValue; final ValueNotifier selectedTabNotifier; + final ValueNotifier selectedIdNotifier; String? userId; bool isClass = true; bool isCost = true; + final List serviceDBValueList; final Function(List>?) onPolicyDataChanged; @@ -27,18 +28,19 @@ class PolicyCriteria extends StatefulWidget { required this.isDesktop, required this.selectedService, required this.selectedTabNotifier, - required this.selectedServiceDBValue, + required this.selectedIdNotifier, required this.userId, required this.isClass, required this.isCost, required this.onPolicyDataChanged, + required this.serviceDBValueList, }); @override PolicyCriteriaState createState() => PolicyCriteriaState(); } class PolicyCriteriaState extends State { - String? ServiceId = "1"; + String? ServiceId;// = "1"; String? ServiceIndxId = "1"; String? orgId; @@ -101,19 +103,32 @@ class PolicyCriteriaState extends State { @override void initState() { + super.initState(); + fetchTrainFlightClass(); + + print("whole value here - ${widget.serviceDBValueList}"); + print(widget.serviceDBValueList); + fieldForPolicy(); + widget.selectedIdNotifier.addListener(() { + setState(() { + ServiceId = widget.selectedIdNotifier.value ?? "1"; + print("new :) ${widget.selectedIdNotifier.value}"); + print("ServiceIndxId changed: $ServiceId"); + }); + }); widget.selectedTabNotifier.addListener(() { print("selectedTab changed: ${widget.selectedTabNotifier.value}"); setState(() { ServiceIndxId = widget.selectedTabNotifier.value ?? "1"; - + // ServiceId = widget.selectedIdNotifier.value ?? "1"; print("ServiceIndxId changed: $ServiceIndxId"); }); fieldForPolicy(); - fetchTrainFlightClass(); + // fetchTrainFlightClass(); // old here }); } @@ -141,11 +156,12 @@ class PolicyCriteriaState extends State { // Step 2: Collect existing service_ids from policyData final existingServiceIds = - policyData?.map((e) => e['service_id'].toString()).toSet(); + policyData?.map((e) => e['service_id'].toString()).toSet(); // Step 3: Loop through all service definitions for (var service in services) { String id = service['service_id'].toString(); + print("164 service == $id"); // Skip if already present if (existingServiceIds!.contains(id)) continue; @@ -184,7 +200,7 @@ class PolicyCriteriaState extends State { "a3_exceptional_action": ThirdExceptionalApproverAction[id], "a4_exceptional_action": FourthExceptionalApproverAction[id], "exceptional_parallel_process_from": - SelectedParallelExceptionalProcess[id], + SelectedParallelExceptionalProcess[id], "a1_amendment_action": FirstAmendApproverAction[id], "a2_amendment_action": SecondAmendApproverAction[id], "a3_amendment_action": ThirdAmendApproverAction[id], @@ -200,6 +216,8 @@ class PolicyCriteriaState extends State { // To set the data (update) void loadPolicyDetails(List> details) { + + print("loadPolicyDetails"); for (var item in details) { final serviceId = item['service_id'].toString(); @@ -266,7 +284,7 @@ class PolicyCriteriaState extends State { print("addOrUpdatePolicyserviceId - $serviceId"); // 1. First, find existing item if any final existingIndex = policyData!.indexWhere( - (item) => item["service_id"] == serviceId, + (item) => item["service_id"] == serviceId, ); print(FirstApproverAction); @@ -284,13 +302,13 @@ class PolicyCriteriaState extends State { "a3_exceptional_action": ThirdExceptionalApproverAction[serviceId], "a4_exceptional_action": FourthExceptionalApproverAction[serviceId], "exceptional_parallel_process_from": - SelectedParallelExceptionalProcess[serviceId], + SelectedParallelExceptionalProcess[serviceId], "a1_amendment_action": FirstAmendApproverAction[serviceId], "a2_amendment_action": SecondAmendApproverAction[serviceId], "a3_amendment_action": ThirdAmendApproverAction[serviceId], "a4_amendment_action": FourthAmendApproverAction[serviceId], "amendment_parallel_process_from": - SelectedParallelAmendProcess[serviceId], + SelectedParallelAmendProcess[serviceId], "created_by": widget.userId, }; @@ -319,19 +337,17 @@ class PolicyCriteriaState extends State { bool allActionsNull = a1 == null && a2 == null && a3 == null; bool someActionsMissing = [a1, a2, a3].where((a) => a != null).length > 0 && - [a1, a2, a3].where((a) => a == null).length > 0; + [a1, a2, a3].where((a) => a == null).length > 0; - bool allExceptionalActionsNull = - ae1 == null && ae2 == null && ae3 == null && ae4 == null; + bool allExceptionalActionsNull = ae1 == null && ae2 == null && ae3 == null && ae4 == null; bool someExceptionalActionsMissing = [ae1, ae2, ae3, ae4].where((a) => a != null).length > 0 && - [ae1, ae2, ae3, ae4].where((a) => a == null).length > 0; + [ae1, ae2, ae3, ae4].where((a) => a == null).length > 0; - bool allAmendActionsNull = - aa1 == null && aa2 == null && aa3 == null && aa4 == null; + bool allAmendActionsNull = aa1 == null && aa2 == null && aa3 == null && aa4 == null; bool someAmendActionsMissing = [aa1, aa2, aa3, aa4].where((a) => a != null).length > 0 && - [aa1, aa2, aa3, aa4].where((a) => a == null).length > 0; + [aa1, aa2, aa3, aa4].where((a) => a == null).length > 0; if (someActionsMissing) { validationErrors[serviceId] = "All 3 approver actions must be selected."; @@ -339,12 +355,12 @@ class PolicyCriteriaState extends State { } if (someExceptionalActionsMissing) { validationErrors[serviceId] = - "All 4 exceptional approver actions must be selected."; + "All 4 exceptional approver actions must be selected."; return; } if (someAmendActionsMissing) { validationErrors[serviceId] = - "All 4 amendment approver actions must be selected."; + "All 4 amendment approver actions must be selected."; return; } @@ -439,6 +455,7 @@ class PolicyCriteriaState extends State { // } Future fetchTrainFlightClass() async { + print("ftech api"); try { final data = await apiService.fetchMasterDropdown(); @@ -460,7 +477,7 @@ class PolicyCriteriaState extends State { void fieldForPolicy() { print("fieldForPolicy - ${widget.selectedTabNotifier.value}"); - print("POlicyCheck - $policyData"); + // Save current input to policyData before switching if (ServiceId != null) { print("Calling addOrUpdatePolicy"); @@ -470,12 +487,25 @@ class PolicyCriteriaState extends State { } setState(() { // ServiceId = widget.selectedTabNotifier.value ?? "1"; - ServiceId = widget.selectedServiceDBValue; - print("ServiceId"); - print(ServiceId); - // Initialize controllers and variables if not present (Controller Creation) + final String tabIndexStr = widget.selectedTabNotifier.value; + print(" fns - $ServiceId"); + // Safely convert tab index to int + final int? tabIndex = int.tryParse(tabIndexStr); + print(" tb inx - $tabIndex"); + print(" db - ${widget.serviceDBValueList.length}"); + print(" db list - ${widget.serviceDBValueList}"); + + if (tabIndex != null && tabIndex >= 0 && tabIndex <= widget.serviceDBValueList.length) { + ServiceId = widget.serviceDBValueList[tabIndex-1].toString(); + print('✅ 484 Selected ServiceId: $ServiceId'); + } + else { + print('❌ Invalid tab index or out of range'); + } + print("502 ServiceId $ServiceId"); + // Initialize controllers and variables if not present costController.putIfAbsent(ServiceId!, () => TextEditingController()); - classAction.putIfAbsent((widget.selectedServiceDBValue), () => "1"); + classAction.putIfAbsent((ServiceId!), () => "1"); // classController.putIfAbsent(ServiceId!, () => TextEditingController()); FirstApproverAction.putIfAbsent(ServiceId!, () => "None"); @@ -530,9 +560,9 @@ class PolicyCriteriaState extends State { widget.isDesktop ? SizedBox(height: 1) : SizedBox(height: 1), Padding( padding: - widget.isDesktop - ? const EdgeInsets.only(left: 0.0) - : const EdgeInsets.only(left: 0), + widget.isDesktop + ? const EdgeInsets.only(left: 0.0) + : const EdgeInsets.only(left: 0), child: Row( children: [ Container( @@ -544,27 +574,27 @@ class PolicyCriteriaState extends State { right: 5, ), child: - widget.isDesktop - ? Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (widget.isClass) - buildClassWidget(widget.isDesktop), - SizedBox(width: 45), - if (widget.isCost!) - buildCostWidget(widget.isDesktop), - ], - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (widget.isClass!) - buildClassWidget(widget.isDesktop), - SizedBox(height: 10), - if (widget.isCost!) - buildCostWidget(widget.isDesktop), - ], - ), + widget.isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (widget.isClass) + buildClassWidget(widget.isDesktop), + SizedBox(width: 45), + if (widget.isCost!) + buildCostWidget(widget.isDesktop), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.isClass!) + buildClassWidget(widget.isDesktop), + SizedBox(height: 10), + if (widget.isCost!) + buildCostWidget(widget.isDesktop), + ], + ), ), ], ), @@ -602,13 +632,13 @@ class PolicyCriteriaState extends State { Container( // color: Colors.yellow, width: - widget.isDesktop - ? MediaQuery.of(context).size.width * 0.18 - : 600, + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.18 + : 600, height: - widget.isDesktop - ? MediaQuery.of(context).size.height * 0.56 - : null, + widget.isDesktop + ? MediaQuery.of(context).size.height * 0.56 + : null, child: Column( children: [ Container( @@ -627,9 +657,9 @@ class PolicyCriteriaState extends State { ), child: Row( mainAxisAlignment: - widget.isDesktop - ? MainAxisAlignment.spaceAround - : MainAxisAlignment.spaceBetween, + widget.isDesktop + ? MainAxisAlignment.spaceAround + : MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 3, @@ -677,11 +707,11 @@ class PolicyCriteriaState extends State { border: Border( right: BorderSide( color: - Colors - .blueGrey - .shade50, // Change this to your desired color + Colors + .blueGrey + .shade50, // Change this to your desired color width: - 1.0, // Change this to your desired width + 1.0, // Change this to your desired width ), ), ), @@ -705,9 +735,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -715,38 +745,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - FirstApproverAction[ServiceId], + FirstApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: GoogleFonts.poppins( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -754,29 +784,29 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: ( - context, - selectedItem, + context, + selectedItem, ) => Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -799,7 +829,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelProcess[ServiceId!] = - "1"; + "1"; }); print( @@ -813,10 +843,10 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - (SelectedParallelProcess[ServiceId] == - "1") - ? Colors.green - : Colors.grey, + (SelectedParallelProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -824,10 +854,10 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - (SelectedParallelProcess[ServiceId] == - "1") - ? Colors.green - : Colors.grey, + (SelectedParallelProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -850,9 +880,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -860,38 +890,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - SecondApproverAction[ServiceId], + SecondApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: TextStyle( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: TextStyle( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -899,29 +929,29 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: ( - context, - selectedItem, + context, + selectedItem, ) => Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -944,7 +974,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelProcess[ServiceId!] = - "2"; + "2"; }); print( @@ -958,12 +988,12 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - ((SelectedParallelProcess[ServiceId] == - "1") || - (SelectedParallelProcess[ServiceId] == - "2")) - ? Colors.green - : Colors.grey, + ((SelectedParallelProcess[ServiceId] == + "1") || + (SelectedParallelProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -971,12 +1001,12 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - ((SelectedParallelProcess[ServiceId] == - "1") || - (SelectedParallelProcess[ServiceId] == - "2")) - ? Colors.green - : Colors.grey, + ((SelectedParallelProcess[ServiceId] == + "1") || + (SelectedParallelProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -998,9 +1028,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -1008,38 +1038,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - ThirdApproverAction[ServiceId], + ThirdApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: GoogleFonts.poppins( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -1047,28 +1077,28 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: (context, selectedItem) => - // Center-align selected item - Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -1091,7 +1121,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelProcess[ServiceId!] = - "3"; + "3"; }); print( "SelectedParallelProcess - $SelectedParallelProcess[ServiceId]", @@ -1104,14 +1134,14 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - ((SelectedParallelProcess[ServiceId] == - "1") || - (SelectedParallelProcess[ServiceId] == - "2") || - (SelectedParallelProcess[ServiceId] == - "3")) - ? Colors.green - : Colors.grey, + ((SelectedParallelProcess[ServiceId] == + "1") || + (SelectedParallelProcess[ServiceId] == + "2") || + (SelectedParallelProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -1119,14 +1149,14 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - ((SelectedParallelProcess[ServiceId] == - "1") || - (SelectedParallelProcess[ServiceId] == - "2") || - (SelectedParallelProcess[ServiceId] == - "3")) - ? Colors.green - : Colors.grey, + ((SelectedParallelProcess[ServiceId] == + "1") || + (SelectedParallelProcess[ServiceId] == + "2") || + (SelectedParallelProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -1149,13 +1179,13 @@ class PolicyCriteriaState extends State { children: [ Container( width: - widget.isDesktop - ? MediaQuery.of(context).size.width * 0.18 - : 600, + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.18 + : 600, height: - widget.isDesktop - ? MediaQuery.of(context).size.height * 0.56 - : null, + widget.isDesktop + ? MediaQuery.of(context).size.height * 0.56 + : null, child: Column( children: [ Container( @@ -1174,9 +1204,9 @@ class PolicyCriteriaState extends State { ), child: Row( mainAxisAlignment: - widget.isDesktop - ? MainAxisAlignment.spaceAround - : MainAxisAlignment.spaceBetween, + widget.isDesktop + ? MainAxisAlignment.spaceAround + : MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 3, @@ -1224,11 +1254,11 @@ class PolicyCriteriaState extends State { border: Border( right: BorderSide( color: - Colors - .blueGrey - .shade50, // Change this to your desired color + Colors + .blueGrey + .shade50, // Change this to your desired color width: - 1.0, // Change this to your desired width + 1.0, // Change this to your desired width ), ), ), @@ -1238,10 +1268,7 @@ class PolicyCriteriaState extends State { children: [ Container( // padding: const EdgeInsets.all(10), - padding: const EdgeInsets.symmetric( - horizontal: 4.0, - vertical: 4.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -1255,9 +1282,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -1265,38 +1292,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - FirstExceptionalApproverAction[ServiceId], + FirstExceptionalApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: GoogleFonts.poppins( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -1304,29 +1331,29 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: ( - context, - selectedItem, + context, + selectedItem, ) => Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -1349,7 +1376,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelExceptionalProcess[ServiceId!] = - "1"; + "1"; }); print( @@ -1363,10 +1390,10 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - (SelectedParallelExceptionalProcess[ServiceId] == - "1") - ? Colors.green - : Colors.grey, + (SelectedParallelExceptionalProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -1374,10 +1401,10 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - (SelectedParallelExceptionalProcess[ServiceId] == - "1") - ? Colors.green - : Colors.grey, + (SelectedParallelExceptionalProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -1387,10 +1414,7 @@ class PolicyCriteriaState extends State { ), Container( // padding: const EdgeInsets.all(10), - padding: const EdgeInsets.symmetric( - horizontal: 4.0, - vertical: 4.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -1404,9 +1428,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -1414,38 +1438,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - SecondExceptionalApproverAction[ServiceId], + SecondExceptionalApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: TextStyle( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: TextStyle( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -1453,29 +1477,29 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: ( - context, - selectedItem, + context, + selectedItem, ) => Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -1498,7 +1522,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelExceptionalProcess[ServiceId!] = - "2"; + "2"; }); print( @@ -1512,12 +1536,12 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - ((SelectedParallelExceptionalProcess[ServiceId] == - "1") || - (SelectedParallelExceptionalProcess[ServiceId] == - "2")) - ? Colors.green - : Colors.grey, + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -1525,12 +1549,12 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - ((SelectedParallelExceptionalProcess[ServiceId] == - "1") || - (SelectedParallelExceptionalProcess[ServiceId] == - "2")) - ? Colors.green - : Colors.grey, + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -1540,10 +1564,7 @@ class PolicyCriteriaState extends State { ), Container( // padding: const EdgeInsets.all(10), - padding: const EdgeInsets.symmetric( - horizontal: 4.0, - vertical: 4.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -1556,9 +1577,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -1566,38 +1587,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - ThirdExceptionalApproverAction[ServiceId], + ThirdExceptionalApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: GoogleFonts.poppins( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -1605,28 +1626,28 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: (context, selectedItem) => - // Center-align selected item - Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -1649,7 +1670,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelExceptionalProcess[ServiceId!] = - "3"; + "3"; }); print( "SelectedParallelExceptionalProcess - $SelectedParallelExceptionalProcess[ServiceId]", @@ -1662,14 +1683,14 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - ((SelectedParallelExceptionalProcess[ServiceId] == - "1") || - (SelectedParallelExceptionalProcess[ServiceId] == - "2") || - (SelectedParallelExceptionalProcess[ServiceId] == - "3")) - ? Colors.green - : Colors.grey, + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2") || + (SelectedParallelExceptionalProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -1677,14 +1698,14 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - ((SelectedParallelExceptionalProcess[ServiceId] == - "1") || - (SelectedParallelExceptionalProcess[ServiceId] == - "2") || - (SelectedParallelExceptionalProcess[ServiceId] == - "3")) - ? Colors.green - : Colors.grey, + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2") || + (SelectedParallelExceptionalProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -1694,10 +1715,7 @@ class PolicyCriteriaState extends State { ), Container( // padding: const EdgeInsets.all(10), - padding: const EdgeInsets.symmetric( - horizontal: 4.0, - vertical: 4.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -1710,9 +1728,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -1720,38 +1738,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - FourthExceptionalApproverAction[ServiceId], + FourthExceptionalApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: GoogleFonts.poppins( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -1759,28 +1777,28 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: (context, selectedItem) => - // Center-align selected item - Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -1803,7 +1821,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelExceptionalProcess[ServiceId!] = - "4"; + "4"; }); print( "SelectedParallelExceptionalProcess - $SelectedParallelExceptionalProcess[ServiceId]", @@ -1816,16 +1834,16 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - ((SelectedParallelExceptionalProcess[ServiceId] == - "1") || - (SelectedParallelExceptionalProcess[ServiceId] == - "2") || - (SelectedParallelExceptionalProcess[ServiceId] == - "3") || - (SelectedParallelExceptionalProcess[ServiceId] == - "4")) - ? Colors.green - : Colors.grey, + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2") || + (SelectedParallelExceptionalProcess[ServiceId] == + "3") || + (SelectedParallelExceptionalProcess[ServiceId] == + "4")) + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -1833,16 +1851,16 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - ((SelectedParallelExceptionalProcess[ServiceId] == - "1") || - (SelectedParallelExceptionalProcess[ServiceId] == - "2") || - (SelectedParallelExceptionalProcess[ServiceId] == - "3") || - (SelectedParallelExceptionalProcess[ServiceId] == - "4")) - ? Colors.green - : Colors.grey, + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2") || + (SelectedParallelExceptionalProcess[ServiceId] == + "3") || + (SelectedParallelExceptionalProcess[ServiceId] == + "4")) + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -1865,13 +1883,13 @@ class PolicyCriteriaState extends State { Container( //color: Colors.grey.shade100, width: - widget.isDesktop - ? MediaQuery.of(context).size.width * 0.18 - : 600, + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.18 + : 600, height: - widget.isDesktop - ? MediaQuery.of(context).size.height * 0.56 - : null, + widget.isDesktop + ? MediaQuery.of(context).size.height * 0.56 + : null, child: Column( children: [ Container( @@ -1890,9 +1908,9 @@ class PolicyCriteriaState extends State { ), child: Row( mainAxisAlignment: - widget.isDesktop - ? MainAxisAlignment.spaceAround - : MainAxisAlignment.spaceBetween, + widget.isDesktop + ? MainAxisAlignment.spaceAround + : MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 3, @@ -1942,10 +1960,7 @@ class PolicyCriteriaState extends State { children: [ Container( // padding: const EdgeInsets.all(10), - padding: const EdgeInsets.symmetric( - horizontal: 4.0, - vertical: 4.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -1959,9 +1974,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -1969,38 +1984,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - FirstAmendApproverAction[ServiceId], + FirstAmendApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: GoogleFonts.poppins( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -2008,29 +2023,29 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: ( - context, - selectedItem, + context, + selectedItem, ) => Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -2053,7 +2068,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelAmendProcess[ServiceId!] = - "1"; + "1"; }); print( @@ -2067,10 +2082,10 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - (SelectedParallelAmendProcess[ServiceId] == - "1") - ? Colors.green - : Colors.grey, + (SelectedParallelAmendProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -2078,10 +2093,10 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - (SelectedParallelAmendProcess[ServiceId] == - "1") - ? Colors.green - : Colors.grey, + (SelectedParallelAmendProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -2091,10 +2106,7 @@ class PolicyCriteriaState extends State { ), Container( // padding: const EdgeInsets.all(10), - padding: const EdgeInsets.symmetric( - horizontal: 4.0, - vertical: 4.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -2108,9 +2120,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -2118,38 +2130,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - SecondAmendApproverAction[ServiceId], + SecondAmendApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: TextStyle( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: TextStyle( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -2157,29 +2169,29 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: ( - context, - selectedItem, + context, + selectedItem, ) => Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -2202,7 +2214,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelAmendProcess[ServiceId!] = - "2"; + "2"; }); print( @@ -2216,12 +2228,12 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - ((SelectedParallelAmendProcess[ServiceId] == - "1") || - (SelectedParallelAmendProcess[ServiceId] == - "2")) - ? Colors.green - : Colors.grey, + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -2229,12 +2241,12 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - ((SelectedParallelAmendProcess[ServiceId] == - "1") || - (SelectedParallelAmendProcess[ServiceId] == - "2")) - ? Colors.green - : Colors.grey, + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -2244,10 +2256,7 @@ class PolicyCriteriaState extends State { ), Container( // padding: const EdgeInsets.all(10), - padding: const EdgeInsets.symmetric( - horizontal: 4.0, - vertical: 4.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -2260,9 +2269,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -2270,38 +2279,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - ThirdAmendApproverAction[ServiceId], + ThirdAmendApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: GoogleFonts.poppins( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -2309,28 +2318,28 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: (context, selectedItem) => - // Center-align selected item - Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -2353,7 +2362,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelAmendProcess[ServiceId!] = - "3"; + "3"; }); print( "SelectedParallelAmendProcess - $SelectedParallelAmendProcess[ServiceId]", @@ -2366,14 +2375,14 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - ((SelectedParallelAmendProcess[ServiceId] == - "1") || - (SelectedParallelAmendProcess[ServiceId] == - "2") || - (SelectedParallelAmendProcess[ServiceId] == - "3")) - ? Colors.green - : Colors.grey, + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2") || + (SelectedParallelAmendProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -2381,14 +2390,14 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - ((SelectedParallelAmendProcess[ServiceId] == - "1") || - (SelectedParallelAmendProcess[ServiceId] == - "2") || - (SelectedParallelAmendProcess[ServiceId] == - "3")) - ? Colors.green - : Colors.grey, + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2") || + (SelectedParallelAmendProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -2398,10 +2407,7 @@ class PolicyCriteriaState extends State { ), Container( // padding: const EdgeInsets.all(10), - padding: const EdgeInsets.symmetric( - horizontal: 4.0, - vertical: 4.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -2414,9 +2420,9 @@ class PolicyCriteriaState extends State { Spacer(), CustomTextFieldUserWrapper( width: - MediaQuery.of( - context, - ).size.width * + MediaQuery.of( + context, + ).size.width * 0.12, isFocused: false, isDesktop: widget.isDesktop, @@ -2424,38 +2430,38 @@ class PolicyCriteriaState extends State { height: 35, child: DropdownSearch( selectedItem: - FourthAmendApproverAction[ServiceId], + FourthAmendApproverAction[ServiceId], // enabled: !isViewMode, popupProps: PopupProps.menu( // showSearchBox: true, fit: - FlexFit - .loose, // Allows flexible height + FlexFit + .loose, // Allows flexible height constraints: BoxConstraints( maxHeight: 250, ), itemBuilder: ( - context, - item, - isSelected, + context, + item, + isSelected, ) => Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Text( - item, - style: GoogleFonts.poppins( - fontSize: - 12, // 👈 Smaller text size here - color: - Colors - .black, // You can customize this - ), - ), + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this ), + ), + ), ), items: [ "Approval", @@ -2463,28 +2469,28 @@ class PolicyCriteriaState extends State { "None", ], dropdownDecoratorProps: - DropDownDecoratorProps( - dropdownSearchDecoration: - InputDecoration( - border: - InputBorder.none, - contentPadding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 5, - ), - ), + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, ), + ), + ), dropdownBuilder: (context, selectedItem) => - // Center-align selected item - Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins( - fontSize: 12, - color: Color(0xFF114D8B), - ), - ), + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), onChanged: (String? newValue) { setState(() { @@ -2507,7 +2513,7 @@ class PolicyCriteriaState extends State { onTap: () { setState(() { SelectedParallelAmendProcess[ServiceId!] = - "4"; + "4"; }); print( "SelectedParallelAmendProcess - $SelectedParallelAmendProcess[ServiceId]", @@ -2520,16 +2526,16 @@ class PolicyCriteriaState extends State { shape: BoxShape.circle, border: Border.all( color: - ((SelectedParallelAmendProcess[ServiceId] == - "1") || - (SelectedParallelAmendProcess[ServiceId] == - "2") || - (SelectedParallelAmendProcess[ServiceId] == - "3") || - (SelectedParallelAmendProcess[ServiceId] == - "4")) - ? Colors.green - : Colors.grey, + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2") || + (SelectedParallelAmendProcess[ServiceId] == + "3") || + (SelectedParallelAmendProcess[ServiceId] == + "4")) + ? Colors.green + : Colors.grey, width: 2, ), ), @@ -2537,16 +2543,16 @@ class PolicyCriteriaState extends State { Icons.check_circle, size: 20, color: - ((SelectedParallelAmendProcess[ServiceId] == - "1") || - (SelectedParallelAmendProcess[ServiceId] == - "2") || - (SelectedParallelAmendProcess[ServiceId] == - "3") || - (SelectedParallelAmendProcess[ServiceId] == - "4")) - ? Colors.green - : Colors.grey, + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2") || + (SelectedParallelAmendProcess[ServiceId] == + "3") || + (SelectedParallelAmendProcess[ServiceId] == + "4")) + ? Colors.green + : Colors.grey, // color: Colors.grey, ), ), @@ -2580,19 +2586,21 @@ class PolicyCriteriaState extends State { // : (apiDataForClass?['train_class'] ?? []); // } - if (ServiceIndxId != null) { - switch (ServiceIndxId) { + if (ServiceId != null) { + // ServiceId = '1'; + print("sid $ServiceId"); + switch (ServiceId) { case "1": textLabel = "Class"; purposeList = apiDataForClass?['flight_class'] ?? []; break; - case "2": + case "5": textLabel = "Category"; purposeList = apiDataForClass?['hotel_class'] ?? []; break; - case "9": + case "2": textLabel = "Class"; purposeList = apiDataForClass?['train_class'] ?? []; break; @@ -2606,8 +2614,7 @@ class PolicyCriteriaState extends State { print("CLASS-ServiceId- $ServiceId"); print("CLASS-ServiceIndxId- $ServiceIndxId"); print("CLASS-purposeList - $purposeList"); - print("CLASS-DB Service Id - ${widget.selectedServiceDBValue}"); - print("CLASS-Action - ${classAction[widget.selectedServiceDBValue]}"); + print("CLASS-Action - ${classAction[ServiceId]}"); // ✅ Sort by dropdown_key purposeList.sort((a, b) { @@ -2617,14 +2624,15 @@ class PolicyCriteriaState extends State { }); List> dropdownItems = - purposeList - .map( - (item) => DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - ), - ) - .toList(); + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); + if (dropdownItems.isEmpty) { dropdownItems.add( @@ -2651,14 +2659,13 @@ class PolicyCriteriaState extends State { // } // 1. First, when dropdownItems are ready, assign default value: - if ((widget.selectedServiceDBValue) != null && - classAction[(widget.selectedServiceDBValue)] == null && + if ((ServiceId) != null && + classAction[ServiceId] == null && dropdownItems.isNotEmpty) { // Important to wrap inside setState if async WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { - classAction[(widget.selectedServiceDBValue)] = - dropdownItems.first.value!; + classAction[ServiceId!] = dropdownItems.first.value!; }); }); } @@ -2718,104 +2725,96 @@ class PolicyCriteriaState extends State { height: 40, width: double.infinity, child: - (widget.selectedServiceDBValue) != null && - (isLoading || dropdownItems.isEmpty) - ? const Center(child: CircularProgressIndicator()) - : DropdownSearch>( - key: - classAction[(widget.selectedServiceDBValue)] == - null - ? UniqueKey() - : ValueKey( - classAction[(widget - .selectedServiceDBValue)], - ), - items: purposeList.cast>(), + (ServiceId!) != null && (isLoading || dropdownItems.isEmpty) + ? const Center(child: CircularProgressIndicator()) + : DropdownSearch>( + key: + classAction[(ServiceId!)] == null + ? UniqueKey() + : ValueKey(classAction[(ServiceId!)]), + items: purposeList.cast>(), - // selectedItem: purposeList.firstWhere( - // (item) => item['dropdown_key'] == selectedClass, - // orElse: () => {}, - // ), - selectedItem: - classAction[(widget.selectedServiceDBValue)] != - null - ? purposeList.firstWhere( - (item) => - item['dropdown_key'] == - classAction[(widget - .selectedServiceDBValue)], - orElse: () => {}, - ) - : null, - itemAsString: (item) => item['dropdown_value'] ?? '', - popupProps: PopupProps.menu( - showSearchBox: false, - fit: FlexFit.loose, - menuProps: const MenuProps( - backgroundColor: Colors.white, - ), - itemBuilder: (context, item, isSelected) { - final bool isNotAllowed = - item['is_allowed'] == 'No'; - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, - ), - child: Text( - item['dropdown_value'] ?? '', - style: GoogleFonts.poppins( - fontSize: 12, - color: - isNotAllowed - ? Colors.redAccent - : Colors.black, - ), - ), - ); - }, - ), - dropdownDecoratorProps: const DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10, - vertical: 10, - ), - ), - ), - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null || selectedItem.isEmpty) { - return Text( - "Select ", - style: GoogleFonts.poppins( - color: Colors.grey, - fontSize: 13, - ), - ); - } - return Text( - selectedItem['dropdown_value'] ?? '', - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black, - ), - ); - }, - onChanged: - purposeList.isNotEmpty - ? (Map? newValue) async { - setState(() { - classAction[(widget - .selectedServiceDBValue)] = - newValue?['dropdown_key']; - print( - "Selected Purpose: ${classAction[(widget.selectedServiceDBValue)]}", - ); - }); - } - : null, + // selectedItem: purposeList.firstWhere( + // (item) => item['dropdown_key'] == selectedClass, + // orElse: () => {}, + // ), + selectedItem: + classAction[(ServiceId!)] != null + ? purposeList.firstWhere( + (item) => + item['dropdown_key'] == + classAction[(ServiceId!)], + orElse: () => {}, + ) + : null, + itemAsString: (item) => item['dropdown_value'] ?? '', + popupProps: PopupProps.menu( + showSearchBox: false, + fit: FlexFit.loose, + menuProps: const MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: (context, item, isSelected) { + final bool isNotAllowed = + item['is_allowed'] == 'No'; + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, ), + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: + isNotAllowed + ? Colors.redAccent + : Colors.black, + ), + ), + ); + }, + ), + dropdownDecoratorProps: const DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 10, + ), + ), + ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || selectedItem.isEmpty) { + return Text( + "Select ", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 13, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ); + }, + onChanged: + purposeList.isNotEmpty + ? (Map? newValue) async { + setState(() { + classAction[(ServiceId!)] = + newValue?['dropdown_key']; + print( + "Selected Purpose: ${classAction[(ServiceId!)]}", + ); + }); + } + : null, + ), ), ), ], @@ -2869,4 +2868,4 @@ class PolicyCriteriaState extends State { ], ); } -} +} \ No newline at end of file diff --git a/lib/Screens/policy/policyCriteria_backup24july2025.dart b/lib/Screens/policy/policyCriteria_backup24july2025.dart new file mode 100644 index 0000000..0193d1f --- /dev/null +++ b/lib/Screens/policy/policyCriteria_backup24july2025.dart @@ -0,0 +1,2872 @@ +import 'dart:convert'; + +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; + +import '../../config/apiUrl.dart'; +import '../../services/apiService.dart'; +import '../../utils/auth_utils.dart'; +import '../../widgets/custom_text_field.dart'; +import '../../widgets/custom_user_form.dart'; + +class PolicyCriteria extends StatefulWidget { + bool isDesktop; + String selectedService; + String selectedServiceDBValue; + final ValueNotifier selectedTabNotifier; + String? userId; + bool isClass = true; + bool isCost = true; + + final Function(List>?) onPolicyDataChanged; + + PolicyCriteria({ + super.key, + required this.isDesktop, + required this.selectedService, + required this.selectedTabNotifier, + required this.selectedServiceDBValue, + required this.userId, + required this.isClass, + required this.isCost, + required this.onPolicyDataChanged, + }); + @override + PolicyCriteriaState createState() => PolicyCriteriaState(); +} + +class PolicyCriteriaState extends State { + String? ServiceId = "1"; + String? ServiceIndxId = "1"; + + String? orgId; + String? userId; + + Map? apiDataForClass; + bool isLoading = true; + + // final TextEditingController _costController = TextEditingController(); + // final TextEditingController _classController = TextEditingController(); + // String? FirstApproverAction; + // String? SecondApproverAction; + // String? ThirdApproverAction; + // String? SelectedParallelProcess = "3"; + + final ApiService apiService = ApiService(); + + Map costController = {}; + // Map classController = {}; + Map classAction = {}; + Map FirstApproverAction = {}; + Map SecondApproverAction = {}; + Map ThirdApproverAction = {}; + Map SelectedParallelProcess = {}; + + Map FirstExceptionalApproverAction = {}; + Map SecondExceptionalApproverAction = {}; + Map ThirdExceptionalApproverAction = {}; + Map FourthExceptionalApproverAction = {}; + Map SelectedParallelExceptionalProcess = {}; + + Map FirstAmendApproverAction = {}; + Map SecondAmendApproverAction = {}; + Map ThirdAmendApproverAction = {}; + Map FourthAmendApproverAction = {}; + Map SelectedParallelAmendProcess = {}; + Map policyDetailsIdMap = {}; // new + Map policyIdMap = {}; // new + + Map validationErrors = {}; + + // bool isClass = true; + // bool isCost = true; + + List>? policyData = []; + + // Map get policyServices { + // Map data = { + // "service_id": ServiceId, + // "cost": costController[ServiceId]?.text, + // "class": classController[ServiceId]?.text, + // "a1_action": FirstApproverAction[ServiceId], + // "a2_action": SecondApproverAction[ServiceId], + // "a3_action": ThirdApproverAction[ServiceId], + // "parallel_process_from": SelectedParallelProcess[ServiceId], + // "created_by": widget.userId + // }; + // return data; + // } + + @override + void initState() { + super.initState(); + fieldForPolicy(); + + widget.selectedTabNotifier.addListener(() { + print("selectedTab changed: ${widget.selectedTabNotifier.value}"); + setState(() { + ServiceIndxId = widget.selectedTabNotifier.value ?? "1"; + + print("ServiceIndxId changed: $ServiceIndxId"); + }); + + fieldForPolicy(); + fetchTrainFlightClass(); + }); + } + + void loadinitializeData() async { + orgId = await getOrgId(); + userId = await getUserId(); + } + + // void saveCurrentPolicy(List> services) { + // print("saveCurrentPolicy- $services"); + // if (ServiceId != null) { + // print("Saving curremt Add or Update"); + // addOrUpdatePolicy(ServiceId!); + // } + // } + + void saveCurrentPolicy(List> services) { + print("saveCurrentPolicy- $services"); + + // Step 1: Save currently selected policy first (if not already saved) + if (ServiceId != null) { + print("Saving current Add or Update"); + addOrUpdatePolicy(ServiceId!); + } + + // Step 2: Collect existing service_ids from policyData + final existingServiceIds = + policyData?.map((e) => e['service_id'].toString()).toSet(); + + // Step 3: Loop through all service definitions + for (var service in services) { + String id = service['service_id'].toString(); + + // Skip if already present + if (existingServiceIds!.contains(id)) continue; + + // Step 4: Initialize any missing controllers or data + costController.putIfAbsent(id, () => TextEditingController()); + classAction.putIfAbsent(id, () => "1"); // working fine + FirstApproverAction.putIfAbsent(id, () => "None"); + SecondApproverAction.putIfAbsent(id, () => "None"); + ThirdApproverAction.putIfAbsent(id, () => "None"); + SelectedParallelProcess.putIfAbsent(id, () => "3"); + + FirstExceptionalApproverAction.putIfAbsent(id, () => "None"); + SecondExceptionalApproverAction.putIfAbsent(id, () => "None"); + ThirdExceptionalApproverAction.putIfAbsent(id, () => "None"); + FourthExceptionalApproverAction.putIfAbsent(id, () => "None"); + SelectedParallelExceptionalProcess.putIfAbsent(id, () => "4"); + + FirstAmendApproverAction.putIfAbsent(id, () => "None"); + SecondAmendApproverAction.putIfAbsent(id, () => "None"); + ThirdAmendApproverAction.putIfAbsent(id, () => "None"); + FourthAmendApproverAction.putIfAbsent(id, () => "None"); + SelectedParallelAmendProcess.putIfAbsent(id, () => "4"); + + // Step 5: Add default policy entry + policyData?.add({ + "service_id": int.parse(id), + "cost": "", + "class": classAction[id], // working fine + "a1_action": FirstApproverAction[id], + "a2_action": SecondApproverAction[id], + "a3_action": ThirdApproverAction[id], + "parallel_process_from": SelectedParallelProcess[id], + "a1_exceptional_action": FirstExceptionalApproverAction[id], + "a2_exceptional_action": SecondExceptionalApproverAction[id], + "a3_exceptional_action": ThirdExceptionalApproverAction[id], + "a4_exceptional_action": FourthExceptionalApproverAction[id], + "exceptional_parallel_process_from": + SelectedParallelExceptionalProcess[id], + "a1_amendment_action": FirstAmendApproverAction[id], + "a2_amendment_action": SecondAmendApproverAction[id], + "a3_amendment_action": ThirdAmendApproverAction[id], + "a4_amendment_action": FourthAmendApproverAction[id], + "amendment_parallel_process_from": SelectedParallelAmendProcess[id], + "created_by": widget.userId, + }); + } + + // Step 6: Emit updated policy data + widget.onPolicyDataChanged(policyData); + } + + // To set the data (update) + void loadPolicyDetails(List> details) { + for (var item in details) { + final serviceId = item['service_id'].toString(); + + costController[serviceId] = TextEditingController( + text: item['cost'] ?? '', + ); + + // classAction[serviceId] = classAction[serviceId]?.toString(); + + classAction[serviceId] = item['class']?.toString() ?? ''; // working fine + + FirstApproverAction[serviceId] = item['a1_action']?.toString(); + SecondApproverAction[serviceId] = item['a2_action']?.toString(); + ThirdApproverAction[serviceId] = item['a3_action']?.toString(); + SelectedParallelProcess[serviceId] = + item['parallel_process_from']?.toString() ?? "3"; + + FirstExceptionalApproverAction[serviceId] = + item['a1_exceptional_action']?.toString(); + SecondExceptionalApproverAction[serviceId] = + item['a2_exceptional_action']?.toString(); + ThirdExceptionalApproverAction[serviceId] = + item['a3_exceptional_action']?.toString(); + FourthExceptionalApproverAction[serviceId] = + item['a4_exceptional_action']?.toString(); + SelectedParallelExceptionalProcess[serviceId] = + item['exceptional_parallel_process_from']?.toString() ?? "3"; + + FirstAmendApproverAction[serviceId] = + item['a1_amendment_action']?.toString(); + SecondAmendApproverAction[serviceId] = + item['a2_amendment_action']?.toString(); + ThirdAmendApproverAction[serviceId] = + item['a3_amendment_action']?.toString(); + FourthAmendApproverAction[serviceId] = + item['a4_amendment_action']?.toString(); + SelectedParallelAmendProcess[serviceId] = + item['amendment_parallel_process_from']?.toString() ?? "4"; + + if (item['policy_details_id'] != null) { + policyDetailsIdMap[serviceId] = item['policy_details_id'].toString(); + } + if (item['policy_id'] != null) { + policyIdMap[serviceId] = item['policy_id'].toString(); + } + } + + policyData = details; + widget.onPolicyDataChanged(policyData); + + if (details.isNotEmpty) { + final firstServiceId = details.first['service_id'].toString(); + print('First Service ID is: $firstServiceId'); + // firstServiceId == "1" + // ? fetchTrainFlightClass(1) + // : fetchTrainFlightClass(2); + } + setState(() {}); + + print("detailsdetails- $details"); + } + + void addOrUpdatePolicy(String serviceId) { + print("addOrUpdatePolicyserviceId - $serviceId"); + // 1. First, find existing item if any + final existingIndex = policyData!.indexWhere( + (item) => item["service_id"] == serviceId, + ); + + print(FirstApproverAction); + Map data = { + "service_id": serviceId, + "cost": costController[serviceId]?.text, + // "class": classController[serviceId]?.text, + "class": classAction[serviceId] ?? "", // working fine + "a1_action": FirstApproverAction[serviceId], + "a2_action": SecondApproverAction[serviceId], + "a3_action": ThirdApproverAction[serviceId], + "parallel_process_from": SelectedParallelProcess[serviceId], + "a1_exceptional_action": FirstExceptionalApproverAction[serviceId], + "a2_exceptional_action": SecondExceptionalApproverAction[serviceId], + "a3_exceptional_action": ThirdExceptionalApproverAction[serviceId], + "a4_exceptional_action": FourthExceptionalApproverAction[serviceId], + "exceptional_parallel_process_from": + SelectedParallelExceptionalProcess[serviceId], + "a1_amendment_action": FirstAmendApproverAction[serviceId], + "a2_amendment_action": SecondAmendApproverAction[serviceId], + "a3_amendment_action": ThirdAmendApproverAction[serviceId], + "a4_amendment_action": FourthAmendApproverAction[serviceId], + "amendment_parallel_process_from": + SelectedParallelAmendProcess[serviceId], + "created_by": widget.userId, + }; + + final cost = costController[serviceId]?.text ?? ""; + + // final travelClass = classController[serviceId]?.text ?? ""; + final travelClass = classAction[serviceId]; // working fine + final a1 = FirstApproverAction[serviceId]; + final a2 = SecondApproverAction[serviceId]; + final a3 = ThirdApproverAction[serviceId]; + + final ae1 = FirstExceptionalApproverAction[serviceId]; + final ae2 = SecondExceptionalApproverAction[serviceId]; + final ae3 = ThirdExceptionalApproverAction[serviceId]; + final ae4 = FourthExceptionalApproverAction[serviceId]; + + final aa1 = FirstAmendApproverAction[serviceId]; + final aa2 = SecondAmendApproverAction[serviceId]; + final aa3 = ThirdAmendApproverAction[serviceId]; + final aa4 = FourthAmendApproverAction[serviceId]; + // bool hasValue = cost.trim().isNotEmpty; + // bool hasValue = cost.trim().isNotEmpty || travelClass!.isNotEmpty; + bool hasValue = + cost.trim().isNotEmpty || (travelClass?.isNotEmpty ?? false); + + bool allActionsNull = a1 == null && a2 == null && a3 == null; + bool someActionsMissing = + [a1, a2, a3].where((a) => a != null).length > 0 && + [a1, a2, a3].where((a) => a == null).length > 0; + + bool allExceptionalActionsNull = + ae1 == null && ae2 == null && ae3 == null && ae4 == null; + bool someExceptionalActionsMissing = + [ae1, ae2, ae3, ae4].where((a) => a != null).length > 0 && + [ae1, ae2, ae3, ae4].where((a) => a == null).length > 0; + + bool allAmendActionsNull = + aa1 == null && aa2 == null && aa3 == null && aa4 == null; + bool someAmendActionsMissing = + [aa1, aa2, aa3, aa4].where((a) => a != null).length > 0 && + [aa1, aa2, aa3, aa4].where((a) => a == null).length > 0; + + if (someActionsMissing) { + validationErrors[serviceId] = "All 3 approver actions must be selected."; + return; + } + if (someExceptionalActionsMissing) { + validationErrors[serviceId] = + "All 4 exceptional approver actions must be selected."; + return; + } + if (someAmendActionsMissing) { + validationErrors[serviceId] = + "All 4 amendment approver actions must be selected."; + return; + } + + if (hasValue && + allActionsNull && + allExceptionalActionsNull && + allAmendActionsNull) { + validationErrors[serviceId] = "Please select all actions."; + return; + } else { + print("HAS VAlue"); + } + + validationErrors.remove(serviceId); + + // 2. ⚡ If updating an existing item, also **keep its IDs** + if (existingIndex != -1) { + final existingItem = policyData![existingIndex]; + + if (existingItem.containsKey("policy_details_id")) { + data["policy_details_id"] = existingItem["policy_details_id"]; + } + if (existingItem.containsKey("policy_id")) { + data["policy_id"] = existingItem["policy_id"]; + } + + // 3. Now update it + policyData![existingIndex] = data; + print("🔁 Updated policy for ServiceId: $serviceId"); + } else { + // 4. Else add as new + policyData!.add(data); + print("➕ Added policy for ServiceId: $serviceId"); + } + + // int index = + // policyData!.indexWhere((item) => item["service_id"] == serviceId); + // if (index != -1) { + // policyData![index] = data; // Replace existing entry + // print("🔁 Updated policy for ServiceId: $serviceId"); + // } else { + // policyData!.add(data); // Add new entry + // print("➕ Added policy for ServiceId: $serviceId"); + // } + + print("📋 policyData - $policyData"); + } + + // Future fetchTrainFlightClass(int tripId) async { + // // final String apiUrldata = '$apiUrl/api/getDropdownMaster'; + // + // // final String apiUrldata = + // // '$apiUrl/api/getFlightAndTrainClass?user_id=${widget.userId}&trip_type=tripId'; + // final String apiUrldata = await apiService.fetchMasterDropdown(); + // + // final token = await getToken(); + // + // if (token == null) { + // throw Exception('Token not found. Please log in.'); + // } + // + // final response = await http.get( + // Uri.parse(apiUrldata), + // headers: { + // 'Authorization': 'Bearer $token', + // 'Content-Type': 'application/json', + // }, + // ); + // + // if (response.statusCode == 200) { + // try { + // final data = json.decode(response.body); + // print(data); + // + // if (!data.containsKey('data') || data['data'] is! Map) { + // throw Exception( + // "Invalid response format: 'data' field is missing or not a Map"); + // } + // + // Map plansJson = + // data['data']; // 'data' is a Map, not a List + // setState(() { + // apiDataForClass = plansJson; // Store API response in state + // isLoading = false; + // }); + // } catch (e) { + // throw Exception('Error parsing response: $e'); + // } + // } else { + // throw Exception('Failed to load plans'); + // } + // } + + Future fetchTrainFlightClass() async { + try { + final data = await apiService.fetchMasterDropdown(); + + print("fetchTrainFlightClass - $data"); + + if (data is! Map) { + throw Exception("Invalid response format: expected a Map"); + } + + setState(() { + apiDataForClass = data; // Store API response in state + isLoading = false; + }); + } catch (e) { + print('Error fetching role list: $e'); + } + } + + void fieldForPolicy() { + print("fieldForPolicy - ${widget.selectedTabNotifier.value}"); + + print("POlicyCheck - $policyData"); + // Save current input to policyData before switching + if (ServiceId != null) { + print("Calling addOrUpdatePolicy"); + addOrUpdatePolicy( + ServiceId!, + ); // 👈 Save current values for existing service + } + setState(() { + // ServiceId = widget.selectedTabNotifier.value ?? "1"; + ServiceId = widget.selectedServiceDBValue; + print("ServiceId"); + print(ServiceId); + // Initialize controllers and variables if not present (Controller Creation) + costController.putIfAbsent(ServiceId!, () => TextEditingController()); + classAction.putIfAbsent((widget.selectedServiceDBValue), () => "1"); + // classController.putIfAbsent(ServiceId!, () => TextEditingController()); + + FirstApproverAction.putIfAbsent(ServiceId!, () => "None"); + SecondApproverAction.putIfAbsent(ServiceId!, () => "None"); + ThirdApproverAction.putIfAbsent(ServiceId!, () => "None"); + SelectedParallelProcess.putIfAbsent(ServiceId!, () => "3"); + + FirstExceptionalApproverAction.putIfAbsent(ServiceId!, () => "None"); + SecondExceptionalApproverAction.putIfAbsent(ServiceId!, () => "None"); + ThirdExceptionalApproverAction.putIfAbsent(ServiceId!, () => "None"); + FourthExceptionalApproverAction.putIfAbsent(ServiceId!, () => "None"); + SelectedParallelExceptionalProcess.putIfAbsent(ServiceId!, () => "4"); + + FirstAmendApproverAction.putIfAbsent(ServiceId!, () => "None"); + SecondAmendApproverAction.putIfAbsent(ServiceId!, () => "None"); + ThirdAmendApproverAction.putIfAbsent(ServiceId!, () => "None"); + FourthAmendApproverAction.putIfAbsent(ServiceId!, () => "None"); + SelectedParallelAmendProcess.putIfAbsent(ServiceId!, () => "4"); + }); + + widget.onPolicyDataChanged(policyData); + } + + void printData() { + // print("policyServices - $policyServices"); + } + + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // widget.isDesktop ? SizedBox(height: 10) : SizedBox(height: 5), + // Row( + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Padding( + // padding: widget.isDesktop + // ? const EdgeInsets.only(left: 28.0) + // : const EdgeInsets.only(left: 0), + // child: Text( + // " Policy Criteria For ${widget.selectedService} ", + // style: GoogleFonts.poppins( + // fontSize: 13, + // color: Color(0xFF9E9DBD), + // fontWeight: FontWeight.bold), + // ), + // ) + // ], + // ), + if (widget.isClass!) + widget.isDesktop ? SizedBox(height: 1) : SizedBox(height: 1), + Padding( + padding: + widget.isDesktop + ? const EdgeInsets.only(left: 0.0) + : const EdgeInsets.only(left: 0), + child: Row( + children: [ + Container( + // color: Colors.grey, + padding: const EdgeInsets.only( + top: 2, + bottom: 2, + left: 5, + right: 5, + ), + child: + widget.isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (widget.isClass) + buildClassWidget(widget.isDesktop), + SizedBox(width: 45), + if (widget.isCost!) + buildCostWidget(widget.isDesktop), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.isClass!) + buildClassWidget(widget.isDesktop), + SizedBox(height: 10), + if (widget.isCost!) + buildCostWidget(widget.isDesktop), + ], + ), + ), + ], + ), + ), + if (widget.isCost!) SizedBox(height: 5), + if (validationErrors[ServiceId] != null) + Row( + children: [ + Text( + validationErrors[ServiceId]!, + style: GoogleFonts.poppins( + color: Colors.red, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + if (validationErrors[ServiceId] != null) SizedBox(height: 5), + Container( + decoration: BoxDecoration( + border: null, + // border: Border.all( + // color: Color(0xFFEBEBF7), + // ), + borderRadius: BorderRadius.circular(8), + // color: Colors.brown.shade200, + ), + child: Container( + child: Row( + children: [ + Column( + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + // color: Colors.yellow, + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.18 + : 600, + height: + widget.isDesktop + ? MediaQuery.of(context).size.height * 0.56 + : null, + child: Column( + children: [ + Container( + margin: const EdgeInsets.only(right: 0), + decoration: BoxDecoration( + // color: Colors.grey.shade100, + // border: Border.all( + // color: Colors.grey.shade100, + // ), + ), + padding: const EdgeInsets.only( + top: 10, + bottom: 0, + left: 0, + right: 35, + ), + child: Row( + mainAxisAlignment: + widget.isDesktop + ? MainAxisAlignment.spaceAround + : MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 3, + child: Align( + alignment: Alignment.center, + child: Text( + "Normal Approval", + style: GoogleFonts.poppins( + color: Color(0xFF9E9DBD), + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ), + ), + // Expanded( + // flex: 2, + // child: Text( + // "Actions", + // style: GoogleFonts.poppins( + // color: Color(0xFF9E9DBD), + // fontWeight: FontWeight.bold, + // fontSize: 12), + // ), + // ), + // Text( + // "Parallel Action", + // style: GoogleFonts.poppins( + // color: Color(0xFF9E9DBD), + // fontWeight: FontWeight.bold, + // fontSize: 12), + // ) + ], + ), + ), + Divider(thickness: 0.2, color: Colors.grey), + SizedBox( + // height: 180, + + // scrollDirection: Axis.vertical, + child: Container( + // height: MediaQuery.of(context).size.height * 3, + // color: Colors.grey, + decoration: BoxDecoration( + border: Border( + right: BorderSide( + color: + Colors + .blueGrey + .shade50, // Change this to your desired color + width: + 1.0, // Change this to your desired width + ), + ), + ), + + margin: const EdgeInsets.only(right: 5, left: 0), + // color: Colors.grey.shade100, + child: Column( + children: [ + Container( + padding: const EdgeInsets.all(10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + // "Approver 1", + "A1", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + FirstApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + ( + context, + selectedItem, + ) => Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + FirstApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelProcess[ServiceId!] = + "1"; + }); + + print( + "SelectedParallelProcess - $SelectedParallelProcess", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + (SelectedParallelProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + (SelectedParallelProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + Container( + padding: const EdgeInsets.all(10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + // "Approver 2", + "A2", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + SecondApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: TextStyle( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + ( + context, + selectedItem, + ) => Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + SecondApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelProcess[ServiceId!] = + "2"; + }); + + print( + "SelectedParallelProcess - $SelectedParallelProcess", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + ((SelectedParallelProcess[ServiceId] == + "1") || + (SelectedParallelProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + ((SelectedParallelProcess[ServiceId] == + "1") || + (SelectedParallelProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + Container( + padding: const EdgeInsets.all(10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "A3", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + ThirdApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + (context, selectedItem) => + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + ThirdApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelProcess[ServiceId!] = + "3"; + }); + print( + "SelectedParallelProcess - $SelectedParallelProcess[ServiceId]", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + ((SelectedParallelProcess[ServiceId] == + "1") || + (SelectedParallelProcess[ServiceId] == + "2") || + (SelectedParallelProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + ((SelectedParallelProcess[ServiceId] == + "1") || + (SelectedParallelProcess[ServiceId] == + "2") || + (SelectedParallelProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + + Column( + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.18 + : 600, + height: + widget.isDesktop + ? MediaQuery.of(context).size.height * 0.56 + : null, + child: Column( + children: [ + Container( + margin: const EdgeInsets.only(right: 0), + decoration: BoxDecoration( + // color: Colors.grey.shade100, + // border: Border.all( + // color: Colors.grey.shade100, + // ), + ), + padding: const EdgeInsets.only( + top: 10, + bottom: 0, + left: 0, + right: 35, + ), + child: Row( + mainAxisAlignment: + widget.isDesktop + ? MainAxisAlignment.spaceAround + : MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 3, + child: Align( + alignment: Alignment.center, + child: Text( + "Exceptional Approval", + style: GoogleFonts.poppins( + color: Color(0xFF9E9DBD), + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ), + ), + // Expanded( + // flex: 2, + // child: Text( + // "Actions", + // style: GoogleFonts.poppins( + // color: Color(0xFF9E9DBD), + // fontWeight: FontWeight.bold, + // fontSize: 12), + // ), + // ), + // Text( + // "Parallel Action", + // style: GoogleFonts.poppins( + // color: Color(0xFF9E9DBD), + // fontWeight: FontWeight.bold, + // fontSize: 12), + // ) + ], + ), + ), + Divider(thickness: 0.2, color: Colors.grey), + SizedBox( + // height: 180, + + // scrollDirection: Axis.vertical, + child: Container( + // height: MediaQuery.of(context).size.height * 3, + // color: Colors.grey, + decoration: BoxDecoration( + border: Border( + right: BorderSide( + color: + Colors + .blueGrey + .shade50, // Change this to your desired color + width: + 1.0, // Change this to your desired width + ), + ), + ), + margin: const EdgeInsets.only(right: 5, left: 0), + // color: Colors.grey.shade100, + child: Column( + children: [ + Container( + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + vertical: 4.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + // "Approver 1", + "A1", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + FirstExceptionalApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + ( + context, + selectedItem, + ) => Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + FirstExceptionalApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelExceptionalProcess[ServiceId!] = + "1"; + }); + + print( + "SelectedParallelExceptionalProcess - $SelectedParallelExceptionalProcess", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + (SelectedParallelExceptionalProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + (SelectedParallelExceptionalProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + Container( + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + vertical: 4.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + // "Approver 2", + "A2", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + SecondExceptionalApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: TextStyle( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + ( + context, + selectedItem, + ) => Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + SecondExceptionalApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelExceptionalProcess[ServiceId!] = + "2"; + }); + + print( + "SelectedParallelExceptionalProcess - $SelectedParallelExceptionalProcess", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + Container( + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + vertical: 4.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "A3", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + ThirdExceptionalApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + (context, selectedItem) => + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + ThirdExceptionalApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelExceptionalProcess[ServiceId!] = + "3"; + }); + print( + "SelectedParallelExceptionalProcess - $SelectedParallelExceptionalProcess[ServiceId]", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2") || + (SelectedParallelExceptionalProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2") || + (SelectedParallelExceptionalProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + Container( + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + vertical: 4.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "A4", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + FourthExceptionalApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + (context, selectedItem) => + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + FourthExceptionalApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelExceptionalProcess[ServiceId!] = + "4"; + }); + print( + "SelectedParallelExceptionalProcess - $SelectedParallelExceptionalProcess[ServiceId]", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2") || + (SelectedParallelExceptionalProcess[ServiceId] == + "3") || + (SelectedParallelExceptionalProcess[ServiceId] == + "4")) + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + ((SelectedParallelExceptionalProcess[ServiceId] == + "1") || + (SelectedParallelExceptionalProcess[ServiceId] == + "2") || + (SelectedParallelExceptionalProcess[ServiceId] == + "3") || + (SelectedParallelExceptionalProcess[ServiceId] == + "4")) + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + Column( + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + //color: Colors.grey.shade100, + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.18 + : 600, + height: + widget.isDesktop + ? MediaQuery.of(context).size.height * 0.56 + : null, + child: Column( + children: [ + Container( + margin: const EdgeInsets.only(right: 0), + decoration: BoxDecoration( + // color: Colors.grey.shade100, + // border: Border.all( + // color: Colors.grey.shade100, + // ), + ), + padding: const EdgeInsets.only( + top: 10, + bottom: 0, + left: 0, + right: 35, + ), + child: Row( + mainAxisAlignment: + widget.isDesktop + ? MainAxisAlignment.spaceAround + : MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 3, + child: Align( + alignment: Alignment.center, + child: Text( + "Amendment Approval", + style: GoogleFonts.poppins( + color: Color(0xFF9E9DBD), + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ), + ), + // Expanded( + // flex: 2, + // child: Text( + // "Actions", + // style: GoogleFonts.poppins( + // color: Color(0xFF9E9DBD), + // fontWeight: FontWeight.bold, + // fontSize: 12), + // ), + // ), + // Text( + // "Parallel Action", + // style: GoogleFonts.poppins( + // color: Color(0xFF9E9DBD), + // fontWeight: FontWeight.bold, + // fontSize: 12), + // ) + ], + ), + ), + Divider(thickness: 0.2, color: Colors.grey), + SizedBox( + // height: 180, + + // scrollDirection: Axis.vertical, + child: Container( + // height: MediaQuery.of(context).size.height * 3, + // color: Colors.grey, + margin: const EdgeInsets.only(right: 5, left: 0), + // color: Colors.grey.shade100, + child: Column( + children: [ + Container( + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + vertical: 4.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + // "Approver 1", + "A1", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + FirstAmendApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + ( + context, + selectedItem, + ) => Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + FirstAmendApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelAmendProcess[ServiceId!] = + "1"; + }); + + print( + "SelectedParallelAmendProcess - $SelectedParallelAmendProcess", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + (SelectedParallelAmendProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + (SelectedParallelAmendProcess[ServiceId] == + "1") + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + Container( + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + vertical: 4.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + // "Approver 2", + "A2", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + SecondAmendApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: TextStyle( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + ( + context, + selectedItem, + ) => Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + SecondAmendApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelAmendProcess[ServiceId!] = + "2"; + }); + + print( + "SelectedParallelAmendProcess - $SelectedParallelAmendProcess", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2")) + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + Container( + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + vertical: 4.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "A3", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + ThirdAmendApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + (context, selectedItem) => + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + ThirdAmendApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelAmendProcess[ServiceId!] = + "3"; + }); + print( + "SelectedParallelAmendProcess - $SelectedParallelAmendProcess[ServiceId]", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2") || + (SelectedParallelAmendProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2") || + (SelectedParallelAmendProcess[ServiceId] == + "3")) + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + Container( + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + vertical: 4.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "A4", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + FourthAmendApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + (context, selectedItem) => + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + FourthAmendApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelAmendProcess[ServiceId!] = + "4"; + }); + print( + "SelectedParallelAmendProcess - $SelectedParallelAmendProcess[ServiceId]", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2") || + (SelectedParallelAmendProcess[ServiceId] == + "3") || + (SelectedParallelAmendProcess[ServiceId] == + "4")) + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2") || + (SelectedParallelAmendProcess[ServiceId] == + "3") || + (SelectedParallelAmendProcess[ServiceId] == + "4")) + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ], + ), + ), + ), + ], + ); + } + + Widget buildClassWidget(isDesktop) { + List purposeList = []; + String textLabel = "Class"; + // if (ServiceId != null) { + // purposeList = (ServiceId == "1") + // ? (apiDataForClass?['flight_class'] ?? []) + // : (apiDataForClass?['train_class'] ?? []); + // } + + if (ServiceIndxId != null) { + switch (ServiceIndxId) { + case "1": + textLabel = "Class"; + purposeList = apiDataForClass?['flight_class'] ?? []; + break; + + case "2": + textLabel = "Category"; + purposeList = apiDataForClass?['hotel_class'] ?? []; + break; + + case "9": + textLabel = "Class"; + purposeList = apiDataForClass?['train_class'] ?? []; + break; + + default: + textLabel = "Class"; + purposeList = []; + } + } + + print("CLASS-ServiceId- $ServiceId"); + print("CLASS-ServiceIndxId- $ServiceIndxId"); + print("CLASS-purposeList - $purposeList"); + print("CLASS-DB Service Id - ${widget.selectedServiceDBValue}"); + print("CLASS-Action - ${classAction[widget.selectedServiceDBValue]}"); + + // ✅ Sort by dropdown_key + purposeList.sort((a, b) { + int aKey = int.tryParse(a['dropdown_key'].toString()) ?? 0; + int bKey = int.tryParse(b['dropdown_key'].toString()) ?? 0; + return aKey.compareTo(bKey); + }); + + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + ), + ) + .toList(); + + if (dropdownItems.isEmpty) { + dropdownItems.add( + DropdownMenuItem( + value: null, + child: Text( + "No options available", + style: GoogleFonts.poppins(color: Colors.grey), + ), + ), + ); + } + + // if (ServiceId != null) { + // if (classAction[ServiceId!] == null && dropdownItems.isNotEmpty) { + // classAction[ServiceId!] = dropdownItems.first.value!; + // } + // } + + // if (ServiceId != null && + // classAction[ServiceId!] == null && + // dropdownItems.isNotEmpty) { + // classAction[ServiceId!] = dropdownItems.first.value!; + // } + + // 1. First, when dropdownItems are ready, assign default value: + if ((widget.selectedServiceDBValue) != null && + classAction[(widget.selectedServiceDBValue)] == null && + dropdownItems.isNotEmpty) { + // Important to wrap inside setState if async + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + classAction[(widget.selectedServiceDBValue)] = + dropdownItems.first.value!; + }); + }); + } + + return Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + textLabel, + style: GoogleFonts.poppins( + fontSize: 12, + // fontWeight: FontWeight.w200, + color: Colors.black, + ), + ), + SizedBox(height: 5), + + // CustomTextFieldUserWrapper( + // isFocused: false, + // isDesktop: widget.isDesktop, + // + // child: SizedBox( + // height: 40, + // child: ServiceId != null && (isLoading || dropdownItems.isEmpty) + // ? const Center(child: CircularProgressIndicator()) + // : DropdownButtonFormField( + // // Assign the correct focus node + // // controller: _hotelNameController, + // value: classAction[ServiceId], + // + // style: GoogleFonts.poppins(fontSize: 12), + // decoration: InputDecoration( + // hintText: "Select", + // hintStyle: GoogleFonts.poppins(fontSize: 12), + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric( + // horizontal: 10), // Proper padding + // ), + // onChanged: (newValue) { + // if (ServiceId != null && newValue != null) { + // setState(() { + // classAction[ServiceId!] = newValue; + // }); + // } + // }, + // + // items: dropdownItems, + // ), + // ), + // ), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + width: double.infinity, + child: + (widget.selectedServiceDBValue) != null && + (isLoading || dropdownItems.isEmpty) + ? const Center(child: CircularProgressIndicator()) + : DropdownSearch>( + key: + classAction[(widget.selectedServiceDBValue)] == + null + ? UniqueKey() + : ValueKey( + classAction[(widget + .selectedServiceDBValue)], + ), + items: purposeList.cast>(), + + // selectedItem: purposeList.firstWhere( + // (item) => item['dropdown_key'] == selectedClass, + // orElse: () => {}, + // ), + selectedItem: + classAction[(widget.selectedServiceDBValue)] != + null + ? purposeList.firstWhere( + (item) => + item['dropdown_key'] == + classAction[(widget + .selectedServiceDBValue)], + orElse: () => {}, + ) + : null, + itemAsString: (item) => item['dropdown_value'] ?? '', + popupProps: PopupProps.menu( + showSearchBox: false, + fit: FlexFit.loose, + menuProps: const MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: (context, item, isSelected) { + final bool isNotAllowed = + item['is_allowed'] == 'No'; + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: + isNotAllowed + ? Colors.redAccent + : Colors.black, + ), + ), + ); + }, + ), + dropdownDecoratorProps: const DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 10, + ), + ), + ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || selectedItem.isEmpty) { + return Text( + "Select ", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 13, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ); + }, + onChanged: + purposeList.isNotEmpty + ? (Map? newValue) async { + setState(() { + classAction[(widget + .selectedServiceDBValue)] = + newValue?['dropdown_key']; + print( + "Selected Purpose: ${classAction[(widget.selectedServiceDBValue)]}", + ); + }); + } + : null, + ), + ), + ), + ], + ), + ], + ); + } + + Widget buildCostWidget(isDesktop) { + return Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Cost", + style: GoogleFonts.poppins( + fontSize: 12, + // fontWeight: FontWeight.w200, + color: Colors.black, + ), + ), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: GoogleFonts.poppins(fontSize: 12), + controller: costController[ServiceId], + // enabled: !isViewMode, + onChanged: (value) { + printData(); + }, + decoration: InputDecoration( + labelText: "Cost", + labelStyle: GoogleFonts.poppins( + fontSize: 12, + color: Colors.grey, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + ], + ), + ], + ); + } +} diff --git a/lib/Screens/policy/policy_backup24july2025.dart b/lib/Screens/policy/policy_backup24july2025.dart new file mode 100644 index 0000000..98ca3a8 --- /dev/null +++ b/lib/Screens/policy/policy_backup24july2025.dart @@ -0,0 +1,1663 @@ +import 'dart:convert'; +import 'dart:math'; +import 'dart:ui' as html; + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'dart:ui' as web; + +import 'package:google_fonts/google_fonts.dart'; +import 'package:reorderables/reorderables.dart'; +import 'package:web/web.dart' as web; + +import 'package:flutter/material.dart'; +import 'package:frontend/Screens/policy/policyCriteria.dart'; +import 'package:go_router/go_router.dart'; +import 'package:http/http.dart' as http; +import 'package:responsive_builder/responsive_builder.dart'; + +import '../../config/apiUrl.dart'; +import '../../routes/custom_appBar.dart'; +import '../../routes/custom_drawer.dart'; +import '../../services/apiService.dart'; +import '../../utils/auth_utils.dart'; +import '../../widgets/custom_text_field.dart'; +import '../../widgets/custom_user_form.dart'; + +class Policy extends StatefulWidget { + final Map? policy; + const Policy({super.key, required this.policy}); + + static Policy fromState(GoRouterState state) { + return Policy(policy: state.extra as Map?); + } + + @override + _PolicyState createState() => _PolicyState(); +} + +class _PolicyState extends State { + final GlobalKey policyCriteriaKey = + GlobalKey(); + + final ApiService apiService = ApiService(); + + // List services = []; + List> services = []; + String? servicesJson; + Color? layoutColor; + Color? bodyColor; + late String policyType = "domestic"; + String selectedServiceDBValue = '1'; + ValueNotifier selectedServiceIndex = ValueNotifier("1"); + ValueNotifier selectedServicePriorityIndex = ValueNotifier("1"); + String selectedService = "train"; + // ValueNotifier selectedService = ValueNotifier("Train"); + bool isViewMode = false; + bool isDisable = false; + bool isSubmitting = false; + + Map focusNodes = {}; + Map focusStates = {}; + Map errorMessages = {}; + + String? selectedPolicyId; + String? _selectedTripType; + String? PolicyName; + String? SelectedDomestic = "0"; + String? SelectedInternational = "0"; + String? orgId; + String? userId; + + bool showClass = true; + bool showCost = false; // bool showCost = true; // on 21st june + + List? selectedAllServices; + List> selectedOrgServiceIds = []; + List? ServicesChoosed; + List filledItineraryKeys = []; + + TextEditingController _policyController = TextEditingController(); + List>? policy_details = []; + + Map get policyData { + List> policyDetails = + policy_details!.where((service) { + // Only check these specific fields for emptiness + final fieldsToCheck = [ + 'cost', + 'class', + 'a1_action', + 'a2_action', + 'a3_action', + ]; + + // If any of the important fields has a value, keep it + return fieldsToCheck.any((field) { + final value = service[field]; + return value != null && value.toString().trim().isNotEmpty; + }); + }).toList(); + + Map data = { + "name": _policyController.text, + "domestic": SelectedDomestic, + "international": SelectedInternational, + "services_ids": services, + "is_active": "1", + "org_id": orgId, + "created_by": userId, + "policy_details": policyDetails, + }; + + return data; + } + + @override + void initState() { + super.initState(); + _checkAuthAndLoadData(); + focusNodes["policyFocusNode"] = FocusNode(); + focusStates["policyFocused"] = false; + for (var key in focusNodes.keys) { + _addFocusListener(focusNodes[key]!, (focus) { + setState(() { + focusStates[key.replaceFirst("FocusNode", "Focused")] = focus; + }); + }); + } + // for (var field in dataHeader) { + // focusNodes["${field}FocusNode"] = FocusNode(); + // focusStates["${field}Focused"] = false; + // } + // WidgetsFlutterBinding.ensureInitialized(); + + // WidgetsBinding.instance.addPostFrameCallback((_) { + // loadinitializeData(); + // + // updateSelectedServices(); + // updateData(); + // + // loadInitialData(); + // + // if (widget.policy != null) { + // final details = List>.from( + // widget.policy!['policy_details'], + // ); + // policyCriteriaKey.currentState?.loadPolicyDetails(details); + // + // policyCriteriaKey.currentState?.fetchTrainFlightClass(); + // } + // }); + } + + void _addFocusListener(FocusNode node, Function(bool) updateState) { + node.addListener(() { + setState(() { + updateState(node.hasFocus); + }); + }); + } + + void _checkAuthAndLoadData() async { + final String? token = await getToken(); // Your async function to get token + + if (token == null || token.isEmpty) { + // Token doesn't exist → redirect to login + context.go( + "/", + ); // or use: router.go("/") if you're using `GoRouter` directly + return; + } + if (!mounted) return; + try { + loadinitializeData(); + + updateSelectedServices(); + updateData(); + + loadInitialData(); + + if (widget.policy != null) { + final details = List>.from( + widget.policy!['policy_details'], + ); + policyCriteriaKey.currentState?.loadPolicyDetails(details); + + policyCriteriaKey.currentState?.fetchTrainFlightClass(); + } + } catch (e) { + print("group : $e"); + } + } + + void loadInitialData() async { + String? layoutString = await getLayoutColor(); + String? bodyStringColor = await getBodyColor(); + + setState(() { + layoutColor = + layoutString != null + ? Color(int.parse(layoutString)) + : Colors.redAccent; + + bodyColor = + bodyStringColor != null + ? Color(int.parse(bodyStringColor)) + : Colors.white; + }); + } + + void loadinitializeData() async { + orgId = await getOrgId(); + userId = await getUserId(); + } + + Future loadAllServices() async { + try { + final result = await apiService.fetchAllServices(); + setState(() { + selectedAllServices = result; + }); + print("Fetched services: $selectedAllServices"); + } catch (e) { + print('Error fetching role list: $e'); + } + } + + Future loadOrgSelectedAlServices() async { + try { + final result = await apiService.fetchOrganization(); + + if (result != null && result is Map) { + final rawServices = result['services_ids']; + + if (rawServices != null && rawServices is String) { + try { + List decoded = json.decode(rawServices); + List> formatted = + decoded + .map((e) => {"service_id": e['service_id'].toString()}) + .toList(); + + setState(() { + selectedOrgServiceIds = formatted; + }); + print("selectedOrgServiceIds: ${selectedOrgServiceIds}"); + + for (var service in selectedOrgServiceIds) { + print("service_id: ${service['service_id']}"); + } + } catch (e) { + print("Failed to decode services_ids: $e"); + } + } + } + } catch (e) { + print('Error fetching role list: $e'); + } + } + + Future updateSelectedServices() async { + await loadAllServices(); + await loadOrgSelectedAlServices(); + + final selectedIds = + selectedOrgServiceIds.map((e) => e['service_id']).toSet(); + + if (widget.policy != null) { + final details = List>.from( + widget.policy!['policy_details'], + ); + + print( + "UUFiltered Selected Services - ${widget.policy!['services_ids']} ", + ); + // pr int("UUFiltered Selected Services - $details"); + + final filtered = + selectedAllServices! + .where( + (service) => + selectedIds.contains(service['service_id'].toString()), + ) + .toList(); + + setState(() { + ServicesChoosed = filtered; + }); + + print("Filtered Selected Services ChooesedIpfa1: $ServicesChoosed"); + + if (ServicesChoosed!.isNotEmpty) { + String firstServiceName = ServicesChoosed?.first['name']; + print("✅ First service name selected for filter: $firstServiceName"); + selectedService = firstServiceName; + // services = ServicesChoosed! + // .map((service) => service['name'].toString()) + // .toList(); + } + + if (widget.policy != null && widget.policy!['services_ids'] != null) { + final decoded = jsonDecode(widget.policy!['services_ids']); + + setState(() { + services = + List>.from(decoded) + .map( + (service) => { + 'service_id': service['service_id'].toString(), + 'name': service['name'].toString(), + 'order': service['order'].toString(), + }, + ) + .toList(); + }); + + print("✅ Loaded services from policy (decoded): $services"); + } + + print("Filtered Selected Services Added to Policy: $ServicesChoosed"); + } else { + final filtered = + selectedAllServices! + .where( + (service) => + selectedIds.contains(service['service_id'].toString()), + ) + .toList(); + + print("ServicesChoosedYY: $ServicesChoosed"); + setState(() { + ServicesChoosed = filtered; + // services = ServicesChoosed! + // .map((service) => service['name'].toString()) + // .toList(); + + // ServicesChoosed = filtered; + + services = + ServicesChoosed! + .map( + (service) => { + 'service_id': service['service_id'].toString(), + 'name': + service['name'].toString(), // ✅ no space before 'name' + 'order': service['order'].toString(), + }, + ) + .toList(); + }); + + print("Filtered Selected Services Chooesed1: $ServicesChoosed"); + + if (ServicesChoosed!.isNotEmpty) { + String firstServiceName = ServicesChoosed?.first['name']; + print("✅ First service name selected for filter: $firstServiceName"); + selectedService = firstServiceName; + policyCriteriaKey.currentState?.fetchTrainFlightClass(); + } + } + } + + void updateData() { + if (widget.policy != null) { + setState(() { + selectedPolicyId = widget.policy?["policy_id"] ?? ""; + _policyController.text = widget.policy?["name"] ?? ""; + SelectedDomestic = widget.policy?["domestic"] ?? ""; + SelectedInternational = widget.policy?["international"] ?? ""; + + if (SelectedDomestic == "1") { + _selectedTripType = "1"; + } else if (SelectedInternational == "1") { + _selectedTripType = "2"; + } + + /// ✅ Load policy_details list safely + policy_details = List>.from( + widget.policy?["policy_details"] ?? [], + ); + }); + } + } + + // void handleSubmit() async { + // setState(() { isDisable = true; }); + // print("Services - $services"); + // print("USR Detail Submit - $policyData"); + // + // policyCriteriaKey.currentState?.saveCurrentPolicy(services); + // + // // Now the full data is ready in policyDataFromChild + // print("Submitting full policyData: $policyData"); + // + // Map data = policyData; + // + // if (!isValidData(data)) { + // print("USERDETAILS : $policyData"); + // print("Validation Failed: Required fields are missing."); + // setState(() { isDisable = false; }); + // return; // Stop execution if validation fails + // } else { + // // print("USERDETAILS : $policyData"); + // // orgId = await getOrgId(); + // + // if(isDisable == true){ createPolicyData(data); } + // setState(() { isDisable = false; }); + // } + // } + + void handleSubmit() async { + if (isSubmitting || isDisable) return; + isSubmitting = true; + setState(() { + isDisable = true; + }); + + policyCriteriaKey.currentState?.saveCurrentPolicy(services); + + // Now the full data is ready in policyDataFromChild + print("Submitting full policyData: $policyData"); + + Map data = policyData; + + if (!isValidData(data)) { + print("Validation Failed"); + isSubmitting = false; + setState(() => isDisable = false); + return; + } + + bool result = false; + + try { + result = await createPolicyData(data); + } catch (e) { + print("Unexpected error during policy creation: $e"); + } + + if (result) { + // ✅ Only navigate on success + context.go('/PolicyList'); + } else { + // ❌ Show error, allow retry + Fluttertoast.showToast( + msg: "Failed to submit policy. Please try again.", + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.CENTER, + timeInSecForIosWeb: 2, + backgroundColor: Colors.redAccent, + textColor: Colors.white, + fontSize: 18.0, + webBgColor: "linear-gradient(to right, #28a745, #28a745)", + ); + } + + isSubmitting = false; + setState(() { + isDisable = false; // Allow retry + }); + } + + bool isValidData(Map data) { + errorMessages.clear(); // Reset previous errors + + // Validate required fields + if (data["name"] == null || data["name"].toString().trim().isEmpty) { + errorMessages["name"] = "Required"; // "Policy name is required."; + } + + // Validate that either domestic or international is selected + final domestic = data["domestic"]?.toString() ?? "0"; + final international = data["international"]?.toString() ?? "0"; + + print( + "domestic: ${data["domestic"]}, international: ${data["international"]}", + ); + + if (domestic != "1" && international != "1") { + errorMessages["trip_type"] = + "Required"; // "Please select Domestic or International."; + } + + // Validate at least one policy_detail with valid content + final policyDetails = data["policy_details"] as List>; + + bool hasAtLeastOneDetail = policyDetails.any((service) { + final fieldsToCheck = [ + 'cost', + 'class', + 'a1_action', + 'a2_action', + 'a3_action', + ]; + return fieldsToCheck.any((field) { + final value = service[field]; + return value != null && value.toString().trim().isNotEmpty; + }); + }); + + if (!hasAtLeastOneDetail) { + errorMessages["policy_details"] = + "Required"; // "At least one valid policy detail is required."; + } + + return errorMessages.isEmpty; + } + + void _clearError(String field) { + if (mounted && errorMessages.containsKey(field)) { + setState(() { + errorMessages.remove(field); + }); + } + } + + Future createPolicyData(Map policyData) async { + final String apiUrldata = '$apiUrl/api/policy/createOrUpdate'; + final token = await getToken(); // Fetch token + + if (token == null) { + print('Token not found. Please log in.'); + // throw Exception('Token not found. Please log in.'); + return false; + } + + final int? policyId; + + if (selectedPolicyId != null && selectedPolicyId!.isNotEmpty) { + policyId = int.tryParse(selectedPolicyId!); + policyData['policy_id'] = policyId; // Add only if updating + } + + try { + final response = await http.post( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + body: jsonEncode(policyData), // Convert map to JSON + ); + + if (response.statusCode == 200) { + print("policyData submitted successfully!"); + print("Response: ${response.body}"); + return true; + } else { + print("Failed to submit policyData. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + return false; + } + } catch (e) { + print(" Error submitting policyData: $e"); + return false; + } + } + + @override + Widget build(BuildContext context) { + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; + + return Scaffold( + backgroundColor: Color(0xFFf5f5f5), + appBar: CustomAppBar(isDesktop: isDesktop), + drawer: CustomDrawer(isDesktop: false), + body: Padding( + padding: + isDesktop + ? EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * + 0.1, // 30% of screen width as horizontal padding + vertical: + MediaQuery.of(context).size.height * + 0, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(8), + child: Column( + children: [ + Expanded( + child: Row( + children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + Expanded(child: buildData(isDesktop, context)), + // Expanded( + // child: Container( + // color: bodyColor, + // child: buildPolicyLayout(isDesktop), + // ), + // ), + ], + ), + ), + ], + ), + ), + + // Row( + // children: [ + // if (isDesktop) CustomDrawer(isDesktop: true), + // Expanded( + // child: Column( + // children: [ + // Expanded( + // child: Container( + // // height: MediaQuery.of(context).size.height * 0.8, + // color: bodyColor, + // child: buildPolicyLayout(isDesktop)), + // ), + // ], + // ), + // ), + // Container( + // color: Colors.white, + // child: Padding( + // padding: const EdgeInsets.all(8.0), + // child: isDesktop + // ? Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: _buildSubmit(isDesktop), + // ) + // : Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: _buildSubmit(isDesktop), + // )), + // ) + // ], + // ), + ); + }, + ); + } + + Widget buildData(bool isDesktop, context) { + return Container( + margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: isDesktop ? Colors.white : Color(0xFFFCFCFC), + ), + // margin: const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), + // decoration: BoxDecoration( + // color: Color(0xFFE1F5FE), + // // color: bodyColor, + // border: Border.all( + // color: Colors.white, + // // color: Color(0xFFF7F7FB), + // + // width: 3.5)), + child: Column( + children: [ + Expanded( + child: Container( + // color: bodyColor, + // color: Color(0xFFE1F5FE), + child: buildPolicyLayout(isDesktop), + ), + ), + Container( + color: Colors.white, + padding: const EdgeInsets.all(8.0), + child: + isDesktop + ? Row( + mainAxisAlignment: MainAxisAlignment.end, + children: _buildSubmit(isDesktop), + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildSubmit(isDesktop), + ), + ), + ], + ), + ); + } + + Widget buildPolicyLayout(bool isDesktop) { + return Container( + // margin: isDesktop + // ? EdgeInsets.all(10.0) + // : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), + height: + isDesktop + ? MediaQuery.of(context).size.height * 0.98 + : MediaQuery.of(context).size.height, + // decoration: BoxDecoration( + // border: isDesktop + // ? Border.all( + // width: 2, + // color: Colors.white, + // // color: Color(0xFFF7F7FB), + // ) + // : null, + // color: Colors.white, + // // color: Color(0xFFF7F7FB), + // + // // color: Colors.amber, + // ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + // color: Color(0xFFF7F7FB), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3), + // color: Colors.white, // Background to avoid overlapping + color: Colors.white, + // color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + // children: [ + // Text( + // "Choose Policy Type", + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74), + // ), + // ), + // ], + ), + ), + ], + ), + ), + isDesktop ? SizedBox(height: 0) : SizedBox(height: 5), + Container( + padding: isDesktop ? const EdgeInsets.only(left: 35) : null, + child: + isDesktop + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: _buildPolicyNameField(isDesktop)), + Spacer(), + Expanded(child: _buildPolicyTypeField(isDesktop)), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildPolicyNameField(isDesktop), + SizedBox(height: 20), + _buildPolicyTypeField(isDesktop), + ], + ), + ), + SizedBox(height: 10), + Divider(thickness: 0.1, color: Colors.grey), + if (errorMessages["policy_details"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["policy_details"]!, + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), + ), + ], + isDesktop + ? Padding( + padding: const EdgeInsets.only(left: 30.0, right: 98.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Approval Criteria", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + Text( + "Service Priority", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + ], + ), + ) + : SizedBox.shrink(), + + isDesktop + ? Container( + // color: Colors.yellow.shade50, + height: MediaQuery.of(context).size.height * 0.54, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: [ + _buildPolicyOrdering(isDesktop), + _buildPolicyCategory(isDesktop), + ], + ), + Column(children: [_buildPolicyCategoryList(isDesktop)]), + ], + ), + ), + ) + : Expanded( + child: Container( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + _buildPolicyCategoryList(isDesktop), + _buildPolicyOrdering(isDesktop), + _buildPolicyCategory(isDesktop), + ], + ), + ), + ), + ), + // isDesktop + // ? Expanded( + // child: Row( + // children: [ + // _buildPolicyCategory(isDesktop), + // _buildPolicyCategoryList(isDesktop), + // ], + // ), + // ) + // : Expanded( + // child: Column( + // children: [ + // _buildPolicyCategoryList(isDesktop), + // _buildPolicyCategory(isDesktop), + // ], + // ), + // ), + ], + ), + ); + } + + // Widget _buildServiceSelect(bool isDesktop){ + // return []; + // } + + Widget _buildPolicyNameField(bool isDesktop) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Policy Name *", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: focusStates["policyFocused"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: GoogleFonts.poppins(fontSize: 12), + focusNode: focusNodes["policyFocusNode"], + controller: _policyController, + onChanged: (value) => _clearError("name"), + decoration: InputDecoration( + labelText: "Policy Name", + labelStyle: GoogleFonts.poppins( + fontSize: 12, + color: Colors.grey, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + if (errorMessages["name"] != null) ...[ + SizedBox(height: 5), + Text( + errorMessages["name"]!, + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), + ), + ], + ], + ); + } + + Widget _buildPolicyTypeField(bool isDesktop) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Policy Type *", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: _buildTripType(isDesktop), + ), + if (errorMessages["trip_type"] != null) ...[ + SizedBox(height: 5), + Text( + errorMessages["trip_type"]!, + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), + ), + ], + ], + ); + } + + Widget _buildPolicyCategoryList(bool isDesktop) { + return Container( + margin: const EdgeInsets.only(top: 8.0), + // color: Colors.white, + height: isDesktop ? 350 : null, + // height: isDesktop ? MediaQuery.of(context).size.height * 0.55 : null, + // color: Colors.blueGrey.shade200, + width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, + decoration: BoxDecoration( + border: Border.all(color: Colors.blueGrey.shade100, width: 0.35), + ), + child: + isDesktop + ? Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [_buildPolicySubCategoryList(isDesktop)], + ), + ) + : Column( + children: [ + Container( + // color: Colors.amber, + child: _buildPolicySubCategoryList(isDesktop), + // child: Text("FAta"), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.start, + // children: [_buildPolicySubCategoryList(isDesktop)], + // ), + ), + ], + ), + ); + } + + Widget _buildPolicySubCategoryList(bool isDesktop) { + if (services.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + + if (isDesktop) { + // 🖥 Desktop (vertical ReorderableListView) + return Expanded( + child: ReorderableListView( + onReorder: (oldIndex, newIndex) { + setState(() { + if (newIndex > oldIndex) newIndex -= 1; + final item = services.removeAt(oldIndex); + services.insert(newIndex, item); + }); + }, + children: List.generate(services.length, (i) { + return SizedBox( + key: ValueKey(services[i]['service_id']), + width: 185, + height: 35, + // height: MediaQuery.of(context).size.height * 0.025, + child: _buildServiceTile(services[i], i.toString()), + ); + }), + ), + ); + } else { + // 📱 Mobile (horizontal ReorderableWrap) + return Container( + // color: Colors.red, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Service Ordering"), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: ReorderableWrap( + spacing: 8, + runSpacing: 8, + direction: Axis.horizontal, + needsLongPressDraggable: true, + onReorder: (oldIndex, newIndex) { + setState(() { + final item = services.removeAt(oldIndex); + services.insert(newIndex, item); + }); + }, + children: List.generate(services.length, (i) { + return SizedBox( + key: ValueKey(services[i]), + height: isDesktop ? 10 : 45, + // child: _buildServiceTile(services[i]['service_id'], i.toString()), + child: _buildServiceTile(services[i], i.toString()), + ); + }), + ), + ), + ], + ), + ); + } + } + + Widget _buildServiceTile(Map service, String index) { + // bool isSelected = selectedServiceIndex.value == index; + bool isSelected = selectedServicePriorityIndex.value == index; + String name = service['name']; // or 'service_id', as needed + + return GestureDetector( + onTap: () { + setState(() { + // selectedServiceIndex.value = index; + selectedServicePriorityIndex.value = index; + }); + }, + child: Container( + margin: const EdgeInsets.all(5), + decoration: BoxDecoration( + // color: Colors.yellow.shade50, + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.white), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.3), + blurRadius: 2, + offset: const Offset(0, 1), + ), + ], + ), + alignment: Alignment.center, + child: Text( + name, + style: GoogleFonts.poppins(color: Colors.black, fontSize: 12), + ), + ), + ); + } + + Widget _buildServiceTile1(Map service, String index) { + // bool isSelected = selectedServiceIndex.value == index; + // bool isSelected = selectedServiceIndex.value == index; + // String name = service['service_id'] + bool isSelected = selectedServiceIndex.value == index; + String name = service['name']; + + return GestureDetector( + onTap: () { + setState(() { + selectedServiceIndex.value = index; + }); + }, + child: Container( + height: 5, + // padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 2), + margin: const EdgeInsets.all(1), + decoration: BoxDecoration( + color: Colors.white, + // color: isSelected ? const Color(0xFF114D8B) : Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.white), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.3), + blurRadius: 2, + offset: const Offset(0, 1), + ), + ], + ), + alignment: Alignment.center, + child: Text( + name, + style: GoogleFonts.poppins( + color: Colors.black, + // color: isSelected ? Colors.white : Colors.black87, + fontSize: 12, + // fontWeight: FontWeight.w100, + ), + ), + ), + ); + } + + Widget _buildPolicyOrdering(bool isDesktop) { + return Container( + // height: 50, + // color: Colors.white, + // color: Colors.yellow.shade200, + padding: isDesktop ? const EdgeInsets.only(left: 30, top: 8) : null, + width: isDesktop ? MediaQuery.of(context).size.width * 0.62 : null, + // width: isDesktop ? MediaQuery.of(context).size.width * 0.75 : null, + child: + isDesktop + ? Container( + // color: Colors.amber, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [_buildPolicyServiceOrdering(isDesktop)], + ), + ) + : Container( + // color: Colors.amber, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [_buildPolicyServiceOrdering(isDesktop)], + ), + ), + ); + } + + Widget _buildPolicyServiceOrdering(bool isDesktop) { + if (ServicesChoosed == null) { + return const Center(child: CircularProgressIndicator()); + } + + // Sort services by 'order' + ServicesChoosed!.sort( + (a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0), + ); + + List services = + ServicesChoosed!.map((service) => service['name'].toString()).toList(); + + List serviceDBvalue = + ServicesChoosed! + .map((service) => service['service_id'].toString()) + .toList(); + + print("1093"); + print(services); + print(serviceDBvalue); + + return Container( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Flex( + direction: Axis.horizontal, + children: + services.asMap().entries.map((entry) { + int index = entry.key + 1; + String service = entry.value; + String serviceId = index.toString(); + bool isSelected = + selectedServiceIndex.value == index.toString(); + + return SizedBox( + // width: isDesktop ? 40 : null, + height: + isDesktop + ? max( + (MediaQuery.of(context).size.height * 0.075), + 10, + ) + : 45, + + // max((MediaQuery.of(context).size.height * 0.09), 10) + child: GestureDetector( + onTap: () { + print("Selected Services - $service - $index"); + setState(() { + selectedServiceIndex.value = index.toString(); + selectedServiceDBValue = serviceDBvalue[entry.key]; + selectedService = service; + + print( + " selectedServiceIndex.value - ${selectedServiceIndex.value}", + ); + + print( + " selectedServiceDBValue.value - ${selectedServiceDBValue}", + ); + + // policyCriteriaKey.currentState?.fieldForPolicy(); + // policyCriteriaKey.currentState + // ?.addOrUpdatePolicy(selectedServiceIndex.value); + print("policyselectedService - $selectedService"); + if (selectedService == "Flight" || + selectedService == "Train" || + selectedService == "Accomodation") { + showClass = true; + showCost = false; + int serviceCode = selectedService == "Flight" ? 1 : 2; + print("policyselectedService1 - $selectedService"); + policyCriteriaKey.currentState + ?.fetchTrainFlightClass(); + } + // else if (selectedService == "Accommodation") { + // showClass = true; + // showCost = false; + // } + else { + showClass = false; + showCost = false; + } + }); + }, + child: Container( + margin: const EdgeInsets.all(5), + padding: + isDesktop + ? const EdgeInsets.all(8) + : const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + service, + style: GoogleFonts.poppins( + color: + isSelected + ? const Color(0xFF114D8B) + : Colors.black87, + fontSize: 13, + fontWeight: + isSelected + ? FontWeight.bold + : FontWeight.w500, + decoration: + TextDecoration + .none, // remove built-in underline + ), + ), + if (isSelected) + const SizedBox( + height: 1, + ), // spacing between text and underline + if (isSelected) + Container( + height: 2, + width: 30, // or based on text width + color: const Color(0xFF114D8B), + ), + ], + ), + ), + ), + ); + }).toList(), + ), + ), + ); + } + + Widget _buildPolicySubCategoryList1(bool isDesktop) { + // List services = [ + // "Flight", + // "Train", + // "Bus", + // "Taxi", + // "Forex", + // "Accommodation", + // "Insurance", + // "Visa", + // "Miscellaneous" + // ]; + + if (ServicesChoosed == null) { + return const Center(child: CircularProgressIndicator()); + } + + List services = + ServicesChoosed!.map((service) => service['name'].toString()).toList(); + + return Expanded( + child: SingleChildScrollView( + scrollDirection: isDesktop ? Axis.vertical : Axis.horizontal, + child: Flex( + direction: isDesktop ? Axis.vertical : Axis.horizontal, + children: + services.asMap().entries.map((entry) { + int index = entry.key + 1; + String service = entry.value; + bool isSelected = + selectedServiceIndex.value == index.toString(); + + return SizedBox( + width: isDesktop ? 180 : null, + height: + isDesktop + ? max( + (MediaQuery.of(context).size.height * 0.075), + 10, + ) + : 45, + + // max((MediaQuery.of(context).size.height * 0.09), 10) + child: GestureDetector( + onTap: () { + print("Selected Services - $service - $index"); + setState(() { + // selectedServiceIndex.value = index.toString(); + // selectedService = service; + // + // if (selectedService == "Flight" || + // selectedService == "Train") { + // showClass = true; + // showCost = true; + // int serviceCode = selectedService == "Flight" ? 1 : 2; + // policyCriteriaKey.currentState?.fetchTrainFlightClass(); + // } else if (selectedService == "Accommodation") { + // showClass = true; + // showCost = false; + // } else { + // showClass = false; + // showCost = false; + // } + }); + }, + child: Container( + margin: EdgeInsets.all(5), + padding: + isDesktop + ? EdgeInsets.all(8) + : EdgeInsets.only( + top: 3, + bottom: 3, + left: 8, + right: 8, + ), + decoration: BoxDecoration( + // color: Colors.blue, + color: isSelected ? Color(0xFF114D8B) : Colors.white, + // : Color(0xFFEBEBF7), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: Colors.white, // Light grey border + width: 1, + ), + boxShadow: [ + BoxShadow( + color: Colors.grey.withAlpha(90), // Shadow color + blurRadius: 1, // Blur radius + spreadRadius: 1, // Spread radius + offset: Offset(0, 1), // Shadow position + ), + ], + ), + alignment: Alignment.center, + child: Text( + service, + style: GoogleFonts.poppins( + color: isSelected ? Colors.white : Colors.black87, + fontSize: 13, + fontWeight: + isSelected ? FontWeight.bold : FontWeight.w100, + ), + ), + ), + ), + ); + }).toList(), + ), + ), + ); + } + + Widget _buildPolicyCategory(bool isDesktop) { + return Container( + margin: isDesktop ? EdgeInsets.all(5) : null, + // color: Colors.brown.shade100, + // child: Text("data"), + // color: Colors.white60, + child: PolicyCriteria( + key: policyCriteriaKey, + isDesktop: isDesktop, + isClass: showClass, + isCost: showCost, + selectedTabNotifier: selectedServiceIndex, + selectedService: selectedService, + selectedIdNotifier: selectedServiceIndex, // added in new 24 july 2025 + // selectedServiceDBValue: selectedServiceDBValue, // removed in new 24 july 2025 + serviceDBValueList: [], // added in new 24 july 2025 + userId: userId, + onPolicyDataChanged: (List>? policyData) { + print("🟢 policyData received from child: $policyData"); + + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + policy_details = policyData; + _clearError("policy_details"); + }); + }); + }, + ), + ); + } + + List _buildTripType(bool isDesktop) { + return [ + CustomTextFieldWrapper( + color: Color(0xFFF4F4FB), + layoutColor: layoutColor, + borderRadius: BorderRadius.circular(25), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + width: 130, + isFocused: _selectedTripType == "1", + isDesktop: isDesktop, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Domestic", + style: GoogleFonts.poppins( + color: _selectedTripType == "1" ? Colors.black : Colors.black, + fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null, + fontSize: 13, + ), + ), + GestureDetector( + onTap: () { + setState(() { + _clearError("trip_type"); + _selectedTripType = "1"; + PolicyName = "Domestic Policy"; + SelectedDomestic = "1"; + SelectedInternational = "0"; + }); + }, + child: Container( + width: 15, + height: 15, + decoration: BoxDecoration( + shape: BoxShape.rectangle, + // color: _selectedOption == option["value"] + // ? Colors.blueAccent + // : Colors.transparent, + borderRadius: BorderRadius.circular(4), // Rounded rectangle + border: Border.all( + color: + _selectedTripType == "1" ? Colors.green : Colors.black, + width: _selectedTripType == "1" ? 2 : 1, + ), + ), + child: + _selectedTripType == "1" + ? Icon(Icons.rectangle, size: 8, color: Colors.green) + : null, // Add checkmark if selected + ), + ), + ], + ), + ), + SizedBox(width: 20), + CustomTextFieldWrapper( + color: Color(0xFFF4F4FB), + layoutColor: 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: isDesktop, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "International", + style: GoogleFonts.poppins( + fontSize: 13, + color: _selectedTripType == "2" ? Colors.black : Colors.black, + fontWeight: _selectedTripType == "2" ? FontWeight.w600 : null, + ), + ), + GestureDetector( + onTap: () { + setState(() { + _clearError("trip_type"); + _selectedTripType = "2"; + PolicyName = "International Policy"; + SelectedDomestic = "0"; + SelectedInternational = "1"; + }); + }, + child: Container( + width: 15, + height: 15, + decoration: BoxDecoration( + shape: BoxShape.rectangle, + // color: _selectedOption == option["value"] + // ? Colors.blueAccent + // : Colors.transparent, + borderRadius: BorderRadius.circular(4), // Rounded rectangle + border: Border.all( + color: + _selectedTripType == "2" ? Colors.green : Colors.black, + width: _selectedTripType == "2" ? 2 : 1, + ), + ), + child: + _selectedTripType == "2" + ? Icon(Icons.rectangle, size: 8, color: Colors.green) + : 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!; + // }); + // }, + // ), + ), + ]; + } + + List _buildSubmit(isDesktop) { + return [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: layoutColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: layoutColor ?? Colors.grey, width: 2), + ), + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: () { + context.go('/PolicyList'); + }, + child: Text("Cancel", style: GoogleFonts.poppins(fontSize: 10)), + ), + SizedBox(width: 20), + MouseRegion( + cursor: + isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + isViewMode ? layoutColor : layoutColor, // Keep original color + foregroundColor: + isViewMode ? Colors.white : Colors.white, // Keep original color + disabledBackgroundColor: + layoutColor, // Ensure color remains when disabled + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: layoutColor ?? Colors.grey, width: 2), + ), + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: + // isViewMode ? null : handleSubmit, // Disable when in view mode + isViewMode || isDisable ? null : handleSubmit, + child: Text("Submit", style: GoogleFonts.poppins(fontSize: 10)), + ), + ), + ]; + } + + List _buildTripType1(bool isDesktop) { + return [ + CustomTextFieldWrapper( + color: Color(0xFFF4F4FB), + padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), + width: isDesktop ? 170 : 140, + isFocused: _selectedTripType == "1", + isDesktop: isDesktop, + child: SizedBox( + height: 35, + child: Material( + color: Colors.transparent, + child: RadioListTile( + activeColor: Colors.blueAccent, + contentPadding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + dense: true, + title: Text("Domestic", style: GoogleFonts.poppins(fontSize: 12)), + value: "1", + groupValue: _selectedTripType, + onChanged: (value) { + setState(() { + _selectedTripType = value!; + PolicyName = "Domestic Policy"; + SelectedDomestic = "1"; + SelectedInternational = "0"; + }); + }, + ), + ), + ), + ), + isDesktop ? SizedBox(width: 28) : SizedBox(width: 15), + CustomTextFieldWrapper( + color: Color(0xFFF4F4FB), + padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), + width: isDesktop ? 180 : 180, + isFocused: _selectedTripType == "2", + isDesktop: isDesktop, + child: SizedBox( + height: 35, + child: Material( + color: Colors.transparent, + child: RadioListTile( + activeColor: Colors.blueAccent, + contentPadding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + dense: true, + title: Text("International"), + value: "2", + groupValue: _selectedTripType, + onChanged: (value) { + setState(() { + _selectedTripType = value!; + PolicyName = "International Policy"; + SelectedDomestic = "0"; + SelectedInternational = "1"; + }); + }, + ), + ), + ), + ), + ]; + } +} diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index cb994e8..9d811d5 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -57,6 +57,7 @@ class _CreateUserFormDetialsState extends State { String? userId; String? orgId; + String? roleId; String? userIdApi; bool isCheckingToken = false; @@ -677,6 +678,7 @@ class _CreateUserFormDetialsState extends State { Future initializeData() async { token = await getToken(); userId = await getUserId(); + roleId = await getRoleId(); if (token == null || userId == null) { print("Token or USerId missing"); @@ -706,6 +708,19 @@ class _CreateUserFormDetialsState extends State { return null; } + Future getRoleId() async { + final prefs = await SharedPreferences.getInstance(); + final userData = prefs.getString('user_data'); + + if (userData != null) { + final decodedData = jsonDecode(userData); + + // Return the user_id from the decoded data + return decodedData['role_id']?.toString(); + } + return null; + } + @override void dispose() { // Dispose all controllers to prevent memory leaks @@ -1313,7 +1328,10 @@ class _CreateUserFormDetialsState extends State { print("✅ User submitted successfully!"); print("📨 Response: ${response.body}"); - context.go('/listUser'); + + if(roleId == "4"){ context.go('/listPlan'); } + else{ context.go('/listUser'); } + } else { print("❌ Submission failed. Status: ${response.statusCode}"); print("📨 Body: ${response.body}"); diff --git a/lib/app.dart b/lib/app.dart index 493d383..f7108c6 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; +// import 'package:flutter/rendering.dart'; import 'dart:html' as html; import 'package:frontend/config/apiUrl.dart'; // 1 newly added import 'package:frontend/services/apiService.dart'; @@ -15,6 +15,7 @@ import 'package:flutter_quill/flutter_quill.dart'; import 'package:frontend/routes/custom_router.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'Screens/myTemplates/templateTest.dart'; +import 'package:fluttertoast/fluttertoast.dart'; class MyApp extends StatefulWidget { const MyApp({super.key}); @@ -27,12 +28,14 @@ class _MyAppState extends State { final ApiService apiService = ApiService(); String? _authCode; String? userRole; - bool _isAuthRedirect = false; + bool _isAuthRedirect = false; // Only true for Microsoft redirect + bool _isloading = false; + @override void initState() { super.initState(); - SemanticsBinding.instance - .ensureSemantics(); // Safe here -only for testing uncomment, Otherwise Email Template wont allow to type + // SemanticsBinding.instance + // .ensureSemantics(); // Safe here -only for testing uncomment, Otherwise Email Template wont allow to type if (kIsWeb) { final uri = Uri.parse(html.window.location.href); print("URI - $uri"); @@ -51,38 +54,41 @@ class _MyAppState extends State { print("query: $query"); final queryParams = Uri.splitQueryString(query); - print("query: $query"); + print("queryParams: $queryParams"); final authCode = queryParams['code']; if (fragmentPath == '/authredirection' && authCode != null) { - // _authCode = uri.queryParameters['code']; - - _authCode = queryParams['code']; - // _isAuthRedirect = true; - html.window.console.log("Auth code detected: $_authCode"); - // print(">>> _isAuthRedirect: $_isAuthRedirect"); - print(">>> Auth code found at startup: $_authCode"); - - WidgetsBinding.instance.addPostFrameCallback((_) { - handleTokenUsingMS(_authCode); + setState(() { + _isAuthRedirect = true; // Show loading screen ONLY for MS login }); - } else { + print("🔍 Auth code detected at startup: $authCode"); + // ✅ Immediately begin token handling + // WidgetsBinding.instance.addPostFrameCallback((_) { + // handleTokenUsingMS(authCode); + // }); + + handleTokenUsingMS(authCode).then((_) { + // 🔥 No URL cleanup — leave it as is + setState(() { + _isAuthRedirect = false; + }); + }); + + html.window.console.log("Auth code detected: $authCode"); + }else { html.window.console.log("No auth code found or wrong path."); } } } - Future handleTokenUsingMS(String? authCode) async { + // Note : Doing any change means just refer on login_widget.dart file - _login function. 24july2025 + Future handleTokenUsingMS(String authCode) async { print("handleTokenUsingMS- $authCode"); - if (authCode == null) return; - - // final url = - // 'http://localhost:40617/tstat/auth/verifyMSAuthUser?code=$authCode'; - final url = '$apiUrl/auth/verifyMSAuthUser?code=$authCode'; - - print("url- $url"); try { + // final url = 'http://localhost:43627/tstat/auth/verifyMSAuthUser?code=$authCode'; + final url = '$apiUrl/auth/verifyMSAuthUser?code=$authCode'; + print("Microsoft BE URL - $url"); final response = await http.get( Uri.parse(url), headers: {'Content-Type': 'application/json'}, @@ -90,44 +96,69 @@ class _MyAppState extends State { if (response.statusCode == 200) { final responseBody = json.decode(response.body); - final MS_Token = responseBody['token']; - print("MS_Token - $MS_Token"); + final token = responseBody['token']; + print("Microsoft_Token - $token"); + setState(() { + _isloading = true; + }); - if (MS_Token != null && MS_Token != '') { + if (token != null && token.isNotEmpty) { print('Microsoft - Token Available'); - await storeUserDetails(MS_Token); + final prefs = await SharedPreferences.getInstance(); + await prefs.setString('is_microsoft_user', 'true'); + await storeUserDetails(token); + setState(() { + _isloading = false; + }); + if(_isloading){ const CircularProgressIndicator(); } + final userData = prefs.getString('user_data'); + print("MS Userdata - $userData"); + final orgDataString = prefs.getString('org_data'); + print("MS orgdata - $orgDataString"); - print("userRole - $userRole"); + if (orgDataString != null && userData != null){ + Fluttertoast.showToast( + msg: "You're in!", + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.CENTER, + backgroundColor: Colors.green, + textColor: Colors.white, + fontSize: 16, + webBgColor: "linear-gradient(to right, #28a745, #28a745)", + ); - if (userRole == "Travel Agent") { - router.go('/listTravelAgentPlan'); - } else if (userRole == "Org Admin" || userRole == "Travel Admin") { - router.go('/listAllPlan'); - } else { - router.go('/listPlan'); + print("Microsoft User Logged In Refer the Role - $userRole"); + // Navigate based on role + if (userRole == "Travel Agent") { + router.go('/listTravelAgentPlan'); + } else if (userRole == "Org Admin" || userRole == "Travel Admin") { + router.go('/StatusDashboard'); + } else { + router.go('/listPlan'); + } } } else { - throw Exception('Token not found in response.'); + throw Exception('Token missing in response.'); } } else { - // Handle 400/401/500 etc. - String errorMsg; - try { - final decoded = json.decode(response.body); - errorMsg = decoded['message'] ?? 'Unknown error'; - } catch (_) { - errorMsg = response.body; // fallback to plain text - } - - print("Error response: $errorMsg"); - throw Exception("Authentication failed: $errorMsg"); + final error = json.decode(response.body)['message'] ?? 'Auth failed'; + throw Exception(error); } - } catch (e, stack) { - print("Caught error: $e"); + } catch (e) { + Fluttertoast.showToast( + msg: "Login Failed: $e", + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.CENTER, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + webBgColor: "linear-gradient(to right, #dc1c13, #dc1c13)", + ); router.go('/'); } } + // Note : Doing any change means just refer on login_widget.dart file - storeUserDetails function. 24july2025 Future storeUserDetails(String token) async { try { final parts = token.split('.'); @@ -141,10 +172,7 @@ class _MyAppState extends State { final prefs = await SharedPreferences.getInstance(); await prefs.setString('auth_token', token); - await prefs.setString( - 'user_data', - jsonEncode(userData), - ); // Store full user data + await prefs.setString('user_data', jsonEncode(userData)); // Store full user data if (userData != null) { final pref = await SharedPreferences.getInstance(); @@ -153,19 +181,31 @@ class _MyAppState extends State { userRole = userData['role']; - print("userData - $userData"); - print("userData11 - ${userData['role']}"); - print("userData12 - $userRole"); + print("MicroSoft UserData RAW - $userData"); + print("MicroSoft UserData ROLE RAW - ${userData['role']}"); + print("MicroSoft UserData ROLE FECTHED - $userRole"); } - apiService.getOrganizationData(); + print("MicroSoft Started"); + await apiService.getOrganizationData(); + print("MicroSoft end"); + } catch (e) { - print('Error decoding token: $e'); + print('Error decoding token MS: $e'); router.go('/'); } } @override Widget build(BuildContext context) { + + if (_isAuthRedirect) { + return const MaterialApp( + home: Scaffold( + body: Center(child: CircularProgressIndicator()), + ), + ); + } + return MaterialApp.router( title: 'Trip Approval Tool', debugShowCheckedModeBanner: false, diff --git a/lib/config/apiUrl.dart b/lib/config/apiUrl.dart index a6f413b..3f0712c 100644 --- a/lib/config/apiUrl.dart +++ b/lib/config/apiUrl.dart @@ -1,5 +1,19 @@ //api url // const String apiUrl = 'http://localhost/tstat_be'; -// const String apiUrl = 'https://uat.tripapprovaltool.com/tstat_be'; -const String apiUrl = - 'http://apitest.tripapprovaltool.com/tstat_be'; // look at this +const String apiUrl = 'https://uat.tripapprovaltool.com/tstat_be'; +// const String apiUrl = 'https://apitest.tripapprovaltool.com/tstat_be'; + +/** Note : Adfactor UAT BE URL + * File : web/index.html - change below + * replaced to **/ +// const String apiUrl = 'https://uat.tripapprovaltool.com/adfactor_be'; + +/** Note : Aujas UAT BE URL + * File : web/index.html - change below + * replaced to **/ +// const String apiUrl = 'https://uat.tripapprovaltool.com/aujas_be'; + +/** Note : Aujas Live BE URL + * File : web/index.html - change below + * replaced to **/ +// const String apiUrl = 'https://tripapprovaltool.com/aujas_be' ; diff --git a/lib/services/apiService.dart b/lib/services/apiService.dart index 98317fb..6e20088 100644 --- a/lib/services/apiService.dart +++ b/lib/services/apiService.dart @@ -15,7 +15,7 @@ import 'package:shared_preferences/shared_preferences.dart'; class ApiService { Future getOrganizationData() async { try { - print("getUpdatedServices"); + print("ORG getUpdatedServices"); final result = await fetchOrganization(); print("UUPdatedServices - $result");