From b8bd8952edd2bf3548671b08451835ccd19a32c6 Mon Sep 17 00:00:00 2001 From: SurendarSuri30 Date: Tue, 4 Aug 2026 10:24:05 +0530 Subject: [PATCH] policy list page new design --- lib/presentation/hrPolicyDetails.dart | 1078 +++++++++++++++++++------ 1 file changed, 811 insertions(+), 267 deletions(-) diff --git a/lib/presentation/hrPolicyDetails.dart b/lib/presentation/hrPolicyDetails.dart index a01574f..c78e027 100755 --- a/lib/presentation/hrPolicyDetails.dart +++ b/lib/presentation/hrPolicyDetails.dart @@ -911,23 +911,7 @@ class _HrPolicyDetailsState extends State } void _resetPreStatusFilter() { - setState(() { - _preStatusFilter = null; - _currentPage = 1; - }); - _searchDebounce?.cancel(); - final query = searchController.text.trim(); - if (query.isEmpty) { - setState(() { - _hasSearched = false; - _isSearching = false; - originalData = []; - filteredData = []; - getCDPolicies = []; - }); - } else { - _searchEmployeesViaApi(query); - } + _resetToLanding(); } Future downloadEmployeeListExcel() async { @@ -1745,6 +1729,21 @@ class _HrPolicyDetailsState extends State } @override + void _resetToLanding() { + _searchDebounce?.cancel(); + searchController.clear(); + setState(() { + _preStatusFilter = null; + _hasSearched = false; + _isSearching = false; + isLoading = false; + originalData = []; + filteredData = []; + getCDPolicies = []; + _currentPage = 1; + }); + } + Widget build(BuildContext context) { return BaseLayout( child: _buildContent(context), @@ -1752,89 +1751,601 @@ class _HrPolicyDetailsState extends State } Widget _buildContent(BuildContext context) { + final showResults = _hasSearched || _isSearching; + return Scaffold( - backgroundColor: const Color(0xFFF8FAFC), + backgroundColor: Colors.white, body: SafeArea( - child: Stack( + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 280), + switchInCurve: Curves.easeOutCubic, + switchOutCurve: Curves.easeInCubic, + child: showResults + ? KeyedSubtree( + key: const ValueKey('results'), + child: _buildResultsWorkspace(), + ) + : KeyedSubtree( + key: const ValueKey('landing'), + child: _buildGoogleLandingView(), + ), + ), + ), + ); + } + + String get _policyTitleLine => + '${localCardType ?? ''} - ${localCardPolicyNo ?? ''}'.trim(); + + String get _policySubtitleLine => localTokenType == 'pre' + ? '${localCardPolicyName ?? ''} (${localCardPolicyExpDate ?? ''})' + : '${localCardInsurerName ?? ''} - ${localCardPolicyName ?? ''} (${localCardPolicyExpDate ?? ''})'; + + /// First-entry Google-style home: title → search → action icons → filter icons. + Widget _buildGoogleLandingView() { + return Stack( + children: [ + Positioned( + top: 12, + left: 12, + child: IconButton( + tooltip: 'Previous Page', + onPressed: () async { + await clearPolicyStorage(); + if (!mounted) return; + Navigator.push( + context, + MaterialPageRoute(builder: (context) => policies()), + ); + }, + icon: const Icon( + Icons.arrow_back_ios_new_rounded, + size: 18, + color: Color(0xFF0F172A), + ), + ), + ), + Center( + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 48, 24, 32), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 720), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + _policyTitleLine, + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + color: const Color(0xFF0F172A), + fontSize: 34, + fontWeight: FontWeight.w700, + height: 1.15, + ), + ), + const SizedBox(height: 8), + Text( + _policySubtitleLine, + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + color: const Color(0xFF64748B), + fontSize: 14, + fontWeight: FontWeight.w400, + ), + ), + const SizedBox(height: 36), + _buildLandingSearchPill(), + const SizedBox(height: 28), + _buildLandingShortcutRow( + items: _landingActionShortcuts(), + ), + const SizedBox(height: 22), + if (_isLoadingCards) + const Padding( + padding: EdgeInsets.only(top: 12), + child: CircularProgressIndicator( + color: Color(0xFF009195), + ), + ) + else if (_filterChips.isNotEmpty) + _buildLandingFilterMetricsRow(), + ], + ), + ), + ), + ), + ], + ); + } + + Widget _buildLandingSearchPill() { + return Container( + height: 54, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(999), + border: Border.all(color: const Color(0xFFE2E8F0)), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.06), + blurRadius: 16, + offset: const Offset(0, 6), + ), + ], + ), + child: TextField( + controller: searchController, + onChanged: search, + onSubmitted: (_) { + _searchDebounce?.cancel(); + _searchEmployees(); + }, + textInputAction: TextInputAction.search, + style: GoogleFonts.poppins( + fontSize: 15, + fontWeight: FontWeight.w500, + color: const Color(0xFF0F172A), + ), + decoration: InputDecoration( + hintText: 'Search by name, emp code, mobile, email…', + hintStyle: GoogleFonts.poppins( + fontSize: 14, + color: const Color(0xFF94A3B8), + ), + prefixIcon: const Icon( + Icons.search_rounded, + color: Color(0xFF64748B), + ), + suffixIcon: searchController.text.isNotEmpty + ? IconButton( + tooltip: 'Clear', + onPressed: () { + searchController.clear(); + setState(() {}); + }, + icon: const Icon(Icons.close_rounded, size: 18), + ) + : null, + border: InputBorder.none, + contentPadding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 16), + ), + ), + ); + } + + List<({String label, IconData icon, Color color, VoidCallback? onTap})> + _landingActionShortcuts() { + return [ + if (localTokenType == 'pre') ...[ + ( + label: 'Reminder', + icon: Icons.tune_rounded, + color: const Color(0xFF009195), + onTap: _openReminderConfigDialog, + ), + ( + label: 'Mail Template', + icon: Icons.mail_outline_rounded, + color: const Color(0xFFE26728), + onTap: _isSendingReminder ? null : _openReminderTemplateDialog, + ), + ], + if (localTokenType == 'post') ...[ + ( + label: 'Policy Terms', + icon: Icons.description_outlined, + color: const Color(0xFF009195), + onTap: _isLoadingPolicyTerms ? null : _openPolicyTermsDialog, + ), + ( + label: 'Hospitals', + icon: Icons.local_hospital_outlined, + color: const Color(0xFF009195), + onTap: _isLoadingHospitalList ? null : _openNetworkHospitalListDialog, + ), + ], + ( + label: 'Import', + icon: Icons.upload_file_rounded, + color: const Color(0xFFE26728), + onTap: _openImportFlow, + ), + ( + label: 'Export', + icon: Icons.download_rounded, + color: const Color(0xFFE26728), + onTap: localTokenType == 'pre' + ? null + : (_isExportingExcel ? null : downloadEmployeeListExcel), + ), + ]; + } + + List<({String key, String label, int count, IconData icon, Color color})> + _landingFilterMetrics() { + return _filterChips.map((filter) { + final key = filter['key'].toString(); + return ( + key: key, + label: (filter['label'] ?? _chipLabelForKey(key)).toString(), + count: int.tryParse('${filter['count']}') ?? 0, + icon: _landingFilterIcon(key), + color: _landingFilterColor(key), + ); + }).toList(); + } + + /// Filter metrics with rounded-square icons and stylish dividers between. + Widget _buildLandingFilterMetricsRow() { + final items = _landingFilterMetrics(); + if (items.isEmpty) return const SizedBox.shrink(); + + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12), + decoration: BoxDecoration( + color: const Color(0xFFF8FAFC), + borderRadius: BorderRadius.circular(22), + border: Border.all(color: const Color(0xFFE2E8F0)), + ), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.min, children: [ - // ===================== MAIN CONTENT ===================== - Column( - children: [ - // ---------------- HEADER ---------------- - Padding( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 8), - child: LayoutBuilder( - builder: (context, constraints) { - final isCompact = constraints.maxWidth < 900; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - IconButton( - tooltip: 'Previous Page', - onPressed: () async => { - await clearPolicyStorage(), - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => policies(), - ), - ) - }, - icon: const Icon( - Icons.arrow_back_ios_new_rounded, - size: 18, - color: Color(0xFF0F172A), - ), - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), + for (var i = 0; i < items.length; i++) ...[ + if (i > 0) _buildLandingFilterDivider(), + _buildLandingFilterMetricTile(items[i]), + ], + ], + ), + ), + ); + } + + Widget _buildLandingFilterDivider() { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 6), + width: 1.5, + height: 52, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(999), + gradient: const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0x00CBD5E1), + Color(0xFF94A3B8), + Color(0x00CBD5E1), + ], + ), + ), + ); + } + + Widget _buildLandingFilterMetricTile( + ({String key, String label, int count, IconData icon, Color color}) item, + ) { + return Material( + color: Colors.transparent, + child: InkWell( + onTap: () => _onPreStatusFilterTap(item.key), + borderRadius: BorderRadius.circular(16), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 52, + height: 52, + decoration: BoxDecoration( + color: item.color.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: item.color.withValues(alpha: 0.22), + ), + ), + child: Icon(item.icon, color: item.color, size: 24), + ), + const SizedBox(height: 8), + Text( + '${item.count}', + style: GoogleFonts.poppins( + fontSize: 15, + fontWeight: FontWeight.w700, + height: 1.1, + color: const Color(0xFF0F172A), + ), + ), + const SizedBox(height: 2), + Text( + item.label, + textAlign: TextAlign.center, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w500, + color: const Color(0xFF64748B), + ), + ), + ], + ), + ), + ), + ); + } + + IconData _landingFilterIcon(String key) { + switch (key.toLowerCase()) { + case 'emp_count': + return Icons.groups_rounded; + case 'submitted': + return Icons.verified_rounded; + case 'logged_in': + return Icons.login_rounded; + case 'not_logged_in': + return Icons.person_off_rounded; + case 'draft': + return Icons.edit_note_rounded; + default: + return Icons.filter_alt_rounded; + } + } + + Color _landingFilterColor(String key) { + switch (key.toLowerCase()) { + case 'emp_count': + return const Color(0xFF3B82F6); + case 'submitted': + return const Color(0xFF16A34A); + case 'logged_in': + return const Color(0xFF009195); + case 'not_logged_in': + return const Color(0xFF6366F1); + case 'draft': + return const Color(0xFFE26728); + default: + return const Color(0xFF009195); + } + } + + Widget _buildLandingShortcutRow({ + required List< + ({String label, IconData icon, Color color, VoidCallback? onTap})> + items, + }) { + return Wrap( + alignment: WrapAlignment.center, + spacing: 18, + runSpacing: 16, + children: [ + for (final item in items) + _buildLandingShortcutTile( + label: item.label, + icon: item.icon, + color: item.color, + onTap: item.onTap, + isExportMenu: item.label == 'Export' && localTokenType == 'pre', + ), + ], + ); + } + + Widget _buildLandingShortcutTile({ + required String label, + required IconData icon, + required Color color, + required VoidCallback? onTap, + bool isExportMenu = false, + }) { + final tile = SizedBox( + width: 84, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 56, + height: 56, + decoration: BoxDecoration( + color: color.withValues(alpha: 0.1), + shape: BoxShape.circle, + border: Border.all(color: color.withValues(alpha: 0.18)), + ), + child: Icon(icon, color: color, size: 24), + ), + const SizedBox(height: 8), + Text( + label, + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w500, + height: 1.2, + color: const Color(0xFF334155), + ), + ), + ], + ), + ); + + if (isExportMenu) { + return PopupMenuButton( + tooltip: 'Export', + offset: const Offset(0, 8), + onSelected: (value) { + if (value == 'csv') { + downloadEmployeeListExcel(); + } else if (value == 'inception') { + downloadInceptionExport(); + } + }, + itemBuilder: (context) => [ + PopupMenuItem( + value: 'csv', + child: Text('Export Excel', style: GoogleFonts.poppins(fontSize: 13)), + ), + PopupMenuItem( + value: 'inception', + child: Text( + 'Inception Export', + style: GoogleFonts.poppins(fontSize: 13), + ), + ), + ], + child: tile, + ); + } + + return Material( + color: Colors.transparent, + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(16), + child: tile, + ), + ); + } + + Future _openImportFlow() async { + await tokenService.writeValue( + 'upload_ClientId', widget.ClientId.toString()); + await tokenService.writeValue( + 'upload_policyTypeId', widget.policyTypeId.toString()); + await tokenService.writeValue( + 'upload_ClientPoliyId', widget.ClientPoliyId.toString()); + await tokenService.writeValue( + 'upload_clientBranchId', widget.clientBranchId.toString()); + await tokenService.writeValue('upload_Token', widget.Token.toString()); + await tokenService.writeValue( + 'upload_TokenType', widget.TokenType.toString()); + await tokenService.writeValue( + 'upload_cardType', widget.cardType.toString()); + await tokenService.writeValue( + 'upload_cardPolicyNo', widget.cardPolicyNo.toString()); + await tokenService.writeValue( + 'upload_cardInsurer_name', widget.cardInsurer_name.toString()); + await tokenService.writeValue( + 'upload_cardPolicy_name', widget.cardPolicy_name.toString()); + await tokenService.writeValue( + 'upload_cardPolicy_ExpDate', widget.cardPolicy_ExpDate.toString()); + await tokenService.writeValue( + 'upload_total_premium', widget.total_premium.toString()); + if (!mounted) return; + Navigator.push( + context, + MaterialPageRoute( + settings: localTokenType != "post" + ? const RouteSettings(name: 'preFileUpload') + : const RouteSettings(name: 'postFileUpload'), + builder: (context) => localTokenType != "post" + ? preFileUpload( + ClientId: widget.ClientId, + policyTypeId: widget.policyTypeId, + ClientPoliyId: widget.ClientPoliyId, + clientBranchId: widget.clientBranchId, + Token: widget.Token, + TokenType: widget.TokenType, + cardType: widget.cardType, + cardPolicyNo: widget.cardPolicyNo, + cardInsurer_name: widget.cardInsurer_name, + cardPolicy_name: widget.cardPolicy_name, + cardPolicy_ExpDate: widget.cardPolicy_ExpDate, + total_premium: widget.total_premium, + ) + : postFileUpload( + ClientId: widget.ClientId, + policyTypeId: widget.policyTypeId, + ClientPoliyId: widget.ClientPoliyId, + clientBranchId: widget.clientBranchId, + Token: widget.Token, + TokenType: widget.TokenType, + cardType: widget.cardType, + cardPolicyNo: widget.cardPolicyNo, + cardInsurer_name: widget.cardInsurer_name, + cardPolicy_name: widget.cardPolicy_name, + cardPolicy_ExpDate: widget.cardPolicy_ExpDate, + total_premium: widget.total_premium, + allocgType: 'EB', + ), + ), + ); + } + + /// Current design: header actions + find members + table. + Widget _buildResultsWorkspace() { + return Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 8), + child: LayoutBuilder( + builder: (context, constraints) { + final isCompact = constraints.maxWidth < 900; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconButton( + tooltip: 'Back to home', + onPressed: _resetToLanding, + icon: const Icon( + Icons.arrow_back_ios_new_rounded, + size: 18, + color: Color(0xFF0F172A), + ), + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _policyTitleLine, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: GoogleFonts.poppins( + color: const Color(0xFF0F172A), + fontSize: isCompact ? 15 : 17, + fontWeight: FontWeight.w700, ), - const SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "${localCardType} - ${localCardPolicyNo} " ?? - '', - style: GoogleFonts.poppins( - color: const Color(0xFF0F172A), - fontSize: isCompact ? 15 : 17, - fontWeight: FontWeight.w700, - ), - ), - const SizedBox(height: 2), - Text( - localTokenType == 'pre' - ? "${localCardPolicyName} (${localCardPolicyExpDate})" - : "${localCardInsurerName} - ${localCardPolicyName} (${localCardPolicyExpDate})", - style: GoogleFonts.poppins( - color: const Color(0xFF64748B), - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ], - ), + ), + const SizedBox(height: 2), + Text( + _policySubtitleLine, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: GoogleFonts.poppins( + color: const Color(0xFF64748B), + fontSize: 12, + fontWeight: FontWeight.w400, ), - ], - ), - const SizedBox(height: 14), - Wrap( - spacing: 10, - runSpacing: 10, + ), + ], + ), + ), + const SizedBox(width: 12), + Flexible( + child: Align( + alignment: Alignment.centerRight, + child: Wrap( + spacing: 8, + runSpacing: 8, alignment: WrapAlignment.end, + crossAxisAlignment: WrapCrossAlignment.center, children: [ if (localIsEcardBulkDownload == 1) SizedBox( width: 40, height: 40, child: ElevatedButton( - onPressed: () { - getEcardBulkDownload(); - }, + onPressed: getEcardBulkDownload, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFE26728), elevation: 0, @@ -1855,13 +2366,13 @@ class _HrPolicyDetailsState extends State _buildHeaderActionButton( label: 'Reminder Config', color: const Color(0xFF009195), - width: 150, + width: isCompact ? 130 : 150, onPressed: _openReminderConfigDialog, ), _buildHeaderActionButton( label: 'Reminder Mail Template', color: const Color(0xFFE26728), - width: 210, + width: isCompact ? 170 : 210, onPressed: _isSendingReminder ? null : _openReminderTemplateDialog, @@ -1892,95 +2403,108 @@ class _HrPolicyDetailsState extends State _buildExportButton(), ], ), - ], - ); - }, + ), + ), + ], ), - ), - - const SizedBox(height: 8), - - // ---------------- SEARCH + FILTER CHIPS ---------------- - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: LayoutBuilder( - builder: (context, constraints) { - return _buildSearchAndFiltersSection(constraints); - }, - ), - ), - - const SizedBox(height: 12), - - // ---------------- TABLE (after search only) ---------------- - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 320), - switchInCurve: Curves.easeOutCubic, - switchOutCurve: Curves.easeInCubic, - child: (_isSearching || (isLoading && _hasSearched)) - ? Center( - key: const ValueKey('loader'), - child: Image.asset( - height: 60, - width: 60, - 'assets/nhance-loader.gif', - ), - ) - : !_hasSearched - ? KeyedSubtree( - key: const ValueKey('prompt'), - child: _buildSearchPromptEmptyState(), - ) - : filteredData.isEmpty - ? Center( - key: const ValueKey('empty'), - child: Text( - 'No matching members found', - style: GoogleFonts.poppins( - fontSize: 14, - color: const Color(0xFF64748B), - ), - ), - ) - : DecoratedBox( - key: const ValueKey('table'), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16), - border: Border.all( - color: const Color(0xFFE2E8F0), - ), - boxShadow: [ - BoxShadow( - color: Colors.black - .withValues(alpha: 0.04), - blurRadius: 16, - offset: const Offset(0, 6), - ), - ], - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(16), - child: Column( - children: [ - _buildCDDataTable(context), - ], - ), - ), - ), - ), - ), - ), - - const SizedBox(height: 48), - ], - ), - ], + ], + ); + }, + ), ), - ), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: LayoutBuilder( + builder: (context, constraints) { + return _buildSearchAndFiltersSection(constraints); + }, + ), + ), + const SizedBox(height: 12), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 320), + switchInCurve: Curves.easeOutCubic, + switchOutCurve: Curves.easeInCubic, + child: (_isSearching || (isLoading && _hasSearched)) + ? Center( + key: const ValueKey('loader'), + child: Image.asset( + height: 60, + width: 60, + 'assets/nhance-loader.gif', + ), + ) + : filteredData.isEmpty + ? Center( + key: const ValueKey('empty'), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'No matching members found', + style: GoogleFonts.poppins( + fontSize: 14, + color: const Color(0xFF64748B), + ), + ), + const SizedBox(height: 8), + TextButton( + onPressed: _resetToLanding, + child: Text( + 'Back to home', + style: GoogleFonts.poppins( + fontWeight: FontWeight.w600, + color: const Color(0xFF009195), + ), + ), + ), + ], + ), + ) + : LayoutBuilder( + key: const ValueKey('table'), + builder: (context, constraints) { + return Align( + alignment: Alignment.topCenter, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: constraints.maxHeight, + minWidth: constraints.maxWidth, + ), + child: DecoratedBox( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: const Color(0xFFE2E8F0), + ), + boxShadow: [ + BoxShadow( + color: Colors.black + .withValues(alpha: 0.04), + blurRadius: 16, + offset: const Offset(0, 6), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: _buildCDDataTable(context), + ), + ), + ), + ); + }, + ), + ), + ), + ), + const SizedBox(height: 24), + ], ); } @@ -2669,38 +3193,42 @@ class _HrPolicyDetailsState extends State } Widget _buildCDDataTable(BuildContext context) { - return Expanded( - child: CustomScrollView( - slivers: [ - /// 🔒 Sticky Header - SliverPersistentHeader( - pinned: true, - delegate: _CDHeaderDelegate( - showUHID: localPolicyTypeId != '6' && localPolicyTypeId != '7', - uhidHeaderLabel: localTokenType == 'post' ? 'TPA ID' : 'UHID', - showLoggedIn: localTokenType == "pre", - showAction: - localTokenType != "pre" && (hasAnyEcardLink || hasModule), - ), - ), + final showUHID = localPolicyTypeId != '6' && localPolicyTypeId != '7'; + final showLoggedIn = localTokenType == 'pre'; + final showAction = + localTokenType != 'pre' && (hasAnyEcardLink || hasModule); - /// 📄 Rows - SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - final item = _paginatedData[index]; - return _buildCDRow(item, index); - }, - childCount: _paginatedData.length, - ), + return CustomScrollView( + shrinkWrap: true, + slivers: [ + SliverPersistentHeader( + pinned: true, + delegate: _CDHeaderDelegate( + showUHID: showUHID, + uhidHeaderLabel: localTokenType == 'post' ? 'TPA ID' : 'UHID', + showLoggedIn: showLoggedIn, + showAction: showAction, ), - - /// 📌 Pagination - SliverToBoxAdapter( - child: _buildPagination(context), + ), + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final item = _paginatedData[index]; + return _buildCDRow(item, index); + }, + childCount: _paginatedData.length, ), - ], - ), + ), + SliverToBoxAdapter( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Divider(height: 1, thickness: 1, color: Color(0xFFE2E8F0)), + _buildPagination(context), + ], + ), + ), + ], ); } @@ -3589,85 +4117,90 @@ class _HrPolicyDetailsState extends State List visiblePages = getVisiblePages(); return Padding( - // Match this horizontal padding (16) to your Table Header padding for perfect alignment - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + padding: const EdgeInsets.fromLTRB(12, 6, 12, 6), child: Row( - mainAxisAlignment: MainAxisAlignment - .spaceBetween, // Pushes text to left, buttons to right + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // --- LEFT SIDE: Showing Text --- - Text( - "Showing $startEntry to $endEntry of $totalItems entries", - style: GoogleFonts.poppins( - fontSize: 13, - color: const Color(0xFF585757), - fontWeight: FontWeight.w400, + Flexible( + child: Text( + "Showing $startEntry to $endEntry of $totalItems entries", + style: GoogleFonts.poppins( + fontSize: 12, + color: const Color(0xFF585757), + fontWeight: FontWeight.w400, + ), + overflow: TextOverflow.ellipsis, ), ), - - // --- RIGHT SIDE: Controls --- Row( + mainAxisSize: MainAxisSize.min, children: [ - // Dropdown for rows per page - DropdownButton( - value: _rowsPerPage, - focusColor: Colors - .transparent, // Fix: Removes the grey/blue highlight on change - items: [5, 10, 15, 20, 50].map((int value) { - return DropdownMenuItem( - value: value, - child: Text(' $value ', - style: GoogleFonts.poppins(fontSize: 15)), - ); - }).toList(), - onChanged: (newValue) { - setState(() { - _rowsPerPage = newValue!; - _currentPage = 1; - }); - }, + Text( + 'Rows', + style: GoogleFonts.poppins( + fontSize: 12, + color: const Color(0xFF64748B), + ), + ), + const SizedBox(width: 6), + DropdownButtonHideUnderline( + child: DropdownButton( + value: _rowsPerPage, + isDense: true, + focusColor: Colors.transparent, + items: [5, 10, 15, 20, 50].map((int value) { + return DropdownMenuItem( + value: value, + child: Text( + '$value', + style: GoogleFonts.poppins(fontSize: 13), + ), + ); + }).toList(), + onChanged: (newValue) { + setState(() { + _rowsPerPage = newValue!; + _currentPage = 1; + }); + }, + ), ), - - const SizedBox(width: 8), - - // Previous button IconButton( tooltip: 'Previous Page', + visualDensity: VisualDensity.compact, + constraints: const BoxConstraints(minWidth: 32, minHeight: 32), + padding: EdgeInsets.zero, onPressed: _currentPage > 1 ? () => setState(() => _currentPage--) : null, - icon: const Icon(Icons.chevron_left), + icon: const Icon(Icons.chevron_left, size: 20), ), - - // First page + left ellipsis if (!visiblePages.contains(1)) Row(children: [ _buildPageButton(1), const Padding( - padding: EdgeInsets.symmetric(horizontal: 4), + padding: EdgeInsets.symmetric(horizontal: 2), child: Text("..."), ), ]), - - // Visible page buttons for (int page in visiblePages) _buildPageButton(page), - - // Right ellipsis + last page if (!visiblePages.contains(totalPages) && totalPages > 0) Row(children: [ const Padding( - padding: EdgeInsets.symmetric(horizontal: 4), + padding: EdgeInsets.symmetric(horizontal: 2), child: Text("..."), ), _buildPageButton(totalPages), ]), - - // Next button IconButton( + tooltip: 'Next Page', + visualDensity: VisualDensity.compact, + constraints: const BoxConstraints(minWidth: 32, minHeight: 32), + padding: EdgeInsets.zero, onPressed: _currentPage < totalPages ? () => setState(() => _currentPage++) : null, - icon: const Icon(Icons.chevron_right), + icon: const Icon(Icons.chevron_right, size: 20), ), ], ), @@ -3678,21 +4211,32 @@ class _HrPolicyDetailsState extends State Widget _buildPageButton(int page) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: - _currentPage == page ? const Color(0xFF00A6A6) : Colors.grey[300], - foregroundColor: _currentPage == page ? Colors.white : Colors.black, - minimumSize: const Size(36, 36), - padding: EdgeInsets.zero, + padding: const EdgeInsets.symmetric(horizontal: 2), + child: SizedBox( + width: 30, + height: 30, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: _currentPage == page + ? const Color(0xFF00A6A6) + : Colors.grey[300], + foregroundColor: + _currentPage == page ? Colors.white : Colors.black, + minimumSize: const Size(30, 30), + padding: EdgeInsets.zero, + elevation: 0, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + onPressed: () { + setState(() { + _currentPage = page; + }); + }, + child: Text( + page.toString(), + style: GoogleFonts.poppins(fontSize: 12), + ), ), - onPressed: () { - setState(() { - _currentPage = page; - }); - }, - child: Text(page.toString()), ), ); }