import 'package:flutter/material.dart'; import 'package:kooltab2/views/widgets/closed_tab_card.dart'; import 'package:kooltab2/views/widgets/open_tab_history_card.dart'; import 'package:provider/provider.dart'; import '../app/router.dart'; import '../models/bar_tab.dart'; import '../models/closed_tab.dart'; import '../utils/navigation.dart'; import '../viewmodels/history_view_model.dart'; import '../l10n/app_localizations.dart'; import '../l10n/app_localizations_helpers.dart'; class HistoryScreenView extends StatefulWidget { const HistoryScreenView({super.key}); @override State createState() => _HistoryScreenViewState(); } class _HistoryScreenViewState extends State with RouteAware { final _scrollController = ScrollController(); @override void initState() { super.initState(); _scrollController.addListener(_onScroll); WidgetsBinding.instance.addPostFrameCallback((_) { context.read().load(); }); } @override void didChangeDependencies() { super.didChangeDependencies(); routeObserver.subscribe(this, ModalRoute.of(context)! as PageRoute); } @override void didPopNext() { context.read().load(); } @override void dispose() { routeObserver.unsubscribe(this); _scrollController.removeListener(_onScroll); _scrollController.dispose(); super.dispose(); } void _onScroll() { if (_scrollController.position.extentAfter < 300) { context.read().loadMore(); } } @override Widget build(BuildContext context) { final viewModel = context.watch(); final l10n = AppLocalizations.of(context); return Scaffold( appBar: AppBar( leading: IconButton( onPressed: () => context.popOrGo('/bar'), icon: const Icon(Icons.arrow_back), ), title: Text(l10n.tabHistory), actionsPadding: const EdgeInsets.symmetric(horizontal: 8), actions: [ IconButton( tooltip: l10n.refresh, onPressed: viewModel.load, icon: const Icon(Icons.refresh_rounded), ), const SizedBox(width: 8), ], ), body: Builder( builder: (context) { if (viewModel.isLoading && !viewModel.hasLoaded) { return const Center( child: CircularProgressIndicator(strokeWidth: 2.5), ); } if (viewModel.errorMessage != null) { return Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.error_outline_rounded, size: 40, color: Theme.of(context).colorScheme.error, ), const SizedBox(height: 12), Text( l10n.localizedError(viewModel.errorMessage), textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyLarge, ), ], ), ), ); } return Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 8), child: Row( children: [ Expanded( child: TextField( onChanged: viewModel.search, decoration: InputDecoration( hintText: l10n.searchByName, prefixIcon: Icon(Icons.search_rounded, size: 20), isDense: true, ), ), ), const SizedBox(width: 12), _CustomerDropdown( customerNames: viewModel.customerNames, selectedCustomer: viewModel.selectedCustomer, onSelected: viewModel.filterByCustomer, ), ], ), ), Expanded( child: viewModel.openTabs.isEmpty && viewModel.closedTabs.isEmpty ? _EmptyState() : _HistoryList( openTabs: viewModel.openTabs, closedTabs: viewModel.closedTabs, hasMore: viewModel.hasMore, isLoadingMore: viewModel.isLoadingMore, scrollController: _scrollController, ), ), ], ); }, ), ); } } class _HistoryList extends StatelessWidget { final List openTabs; final List closedTabs; final bool hasMore; final bool isLoadingMore; final ScrollController scrollController; const _HistoryList({ required this.openTabs, required this.closedTabs, required this.hasMore, required this.isLoadingMore, required this.scrollController, }); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final openSectionCount = openTabs.isEmpty ? 0 : openTabs.length + 1; final closedSectionCount = closedTabs.isEmpty ? 0 : closedTabs.length + 1; final loadingCount = hasMore || isLoadingMore ? 1 : 0; return ListView.separated( controller: scrollController, padding: const EdgeInsets.fromLTRB(20, 8, 20, 20), itemCount: openSectionCount + closedSectionCount + loadingCount, separatorBuilder: (_, _) => const SizedBox(height: 10), itemBuilder: (context, index) { if (openTabs.isNotEmpty) { if (index == 0) { return _HistorySectionHeader(title: l10n.openTabs); } if (index <= openTabs.length) { return OpenTabHistoryCard(tab: openTabs[index - 1]); } } final closedIndex = index - openSectionCount; if (closedTabs.isNotEmpty) { if (closedIndex == 0) { return _HistorySectionHeader(title: l10n.closedTabs); } if (closedIndex <= closedTabs.length) { return ClosedTabCard(closedTab: closedTabs[closedIndex - 1]); } } return const Padding( padding: EdgeInsets.symmetric(vertical: 24), child: Center(child: CircularProgressIndicator(strokeWidth: 2.5)), ); }, ); } } class _HistorySectionHeader extends StatelessWidget { final String title; const _HistorySectionHeader({required this.title}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(top: 8, bottom: 2), child: Text( title.toUpperCase(), style: Theme.of(context).textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w700, letterSpacing: 0.7, color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5), ), ), ); } } class _CustomerDropdown extends StatelessWidget { static const _allSentinel = r'$__all__$'; final List customerNames; final String? selectedCustomer; final ValueChanged onSelected; const _CustomerDropdown({ required this.customerNames, required this.selectedCustomer, required this.onSelected, }); @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; final l10n = AppLocalizations.of(context); final isFiltered = selectedCustomer != null; return PopupMenuButton( onSelected: (value) { onSelected(value == _allSentinel ? null : value); }, tooltip: l10n.customer, padding: EdgeInsets.zero, offset: const Offset(0, 8), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), color: Theme.of(context).cardTheme.color ?? scheme.surface, itemBuilder: (context) => [ PopupMenuItem( value: _allSentinel, height: 50, child: _CustomerMenuRow( icon: Icons.people_outline_rounded, label: l10n.allCustomers, selected: !isFiltered, ), ), if (customerNames.isNotEmpty) const PopupMenuDivider(height: 1), ...customerNames.map( (name) => PopupMenuItem( value: name, height: 50, child: _CustomerMenuRow( icon: name == selectedCustomer ? Icons.person_rounded : Icons.person_outline_rounded, label: name, selected: name == selectedCustomer, ), ), ), ], child: ConstrainedBox( constraints: const BoxConstraints(minWidth: 144, maxWidth: 220), child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( color: isFiltered ? scheme.primary.withValues(alpha: 0.35) : scheme.onSurface.withValues(alpha: 0.12), ), color: isFiltered ? scheme.primary.withValues(alpha: 0.08) : null, ), child: Row( children: [ Icon( isFiltered ? Icons.filter_alt_rounded : Icons.people_outline_rounded, size: 18, color: isFiltered ? scheme.primary : scheme.onSurface.withValues(alpha: 0.5), ), const SizedBox(width: 8), Expanded( child: Text( selectedCustomer ?? l10n.allCustomers, overflow: TextOverflow.ellipsis, style: TextStyle( fontWeight: FontWeight.w600, fontSize: 13, color: isFiltered ? scheme.primary : scheme.onSurface.withValues(alpha: 0.65), ), ), ), const SizedBox(width: 4), Icon( Icons.keyboard_arrow_down_rounded, size: 18, color: isFiltered ? scheme.primary : scheme.onSurface.withValues(alpha: 0.5), ), ], ), ), ), ); } } class _CustomerMenuRow extends StatelessWidget { final IconData icon; final String label; final bool selected; const _CustomerMenuRow({ required this.icon, required this.label, required this.selected, }); @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; return Row( children: [ Icon( icon, size: 18, color: selected ? scheme.primary : scheme.onSurface.withValues(alpha: 0.65), ), const SizedBox(width: 10), Expanded( child: Text( label, overflow: TextOverflow.ellipsis, style: TextStyle( fontWeight: selected ? FontWeight.w700 : FontWeight.w400, color: selected ? scheme.primary : scheme.onSurface, ), ), ), if (selected) ...[ const SizedBox(width: 10), Icon(Icons.check_rounded, size: 18, color: scheme.primary), ], ], ); } } class _EmptyState extends StatelessWidget { @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; final l10n = AppLocalizations.of(context); return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( shape: BoxShape.circle, color: scheme.onSurface.withValues(alpha: 0.05), ), child: Icon( Icons.history_rounded, size: 40, color: scheme.onSurface.withValues(alpha: 0.3), ), ), const SizedBox(height: 16), Text( l10n.noClosedTabs, style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 4), Text( l10n.tabsYouCloseAppearHere, style: Theme.of(context).textTheme.bodyMedium, ), ], ), ); } }