import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../shared/services/api_client.dart'; import '../../../shared/providers/auth_provider.dart'; final clientsProvider = FutureProvider.autoDispose .family>, String?>((ref, search) async { final apiClient = ref.watch(apiClientProvider); return apiClient.getClients(search: search); }); class ClientsScreen extends ConsumerStatefulWidget { const ClientsScreen({super.key}); @override ConsumerState createState() => _ClientsScreenState(); } class _ClientsScreenState extends ConsumerState { final _searchController = TextEditingController(); String? _searchQuery; @override void dispose() { _searchController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final clientsAsync = ref.watch(clientsProvider(_searchQuery)); return Scaffold( appBar: AppBar( title: const Text('Clients'), actions: [ IconButton( icon: const Icon(Icons.logout), onPressed: () async { await ref.read(authStateProvider.notifier).signOut(); }, ), ], ), body: Column( children: [ // Search bar Padding( padding: const EdgeInsets.all(16), child: TextField( controller: _searchController, decoration: InputDecoration( hintText: 'Search clients...', prefixIcon: const Icon(Icons.search), suffixIcon: _searchQuery != null ? IconButton( icon: const Icon(Icons.clear), onPressed: () { _searchController.clear(); setState(() { _searchQuery = null; }); }, ) : null, ), onSubmitted: (value) { setState(() { _searchQuery = value.isEmpty ? null : value; }); }, ), ), // Client list Expanded( child: clientsAsync.when( data: (clients) { if (clients.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.people_outline, size: 64, color: Colors.grey.shade400, ), const SizedBox(height: 16), Text( _searchQuery != null ? 'No clients found' : 'No clients yet', style: Theme.of(context).textTheme.titleMedium?.copyWith( color: Colors.grey, ), ), const SizedBox(height: 8), if (_searchQuery == null) FilledButton.icon( onPressed: () => context.go('/clients/new'), icon: const Icon(Icons.add), label: const Text('Add Client'), ), ], ), ); } return RefreshIndicator( onRefresh: () async { ref.invalidate(clientsProvider(_searchQuery)); }, child: ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: clients.length, itemBuilder: (context, index) { final client = clients[index]; return _ClientCard(client: client); }, ), ); }, loading: () => const Center( child: CircularProgressIndicator(), ), error: (error, stack) => Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, size: 64, color: Colors.red.shade300, ), const SizedBox(height: 16), Text( 'Failed to load clients', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 8), TextButton( onPressed: () { ref.invalidate(clientsProvider(_searchQuery)); }, child: const Text('Retry'), ), ], ), ), ), ), ], ), floatingActionButton: FloatingActionButton( onPressed: () => context.go('/clients/new'), child: const Icon(Icons.add), ), ); } } class _ClientCard extends StatelessWidget { final Map client; const _ClientCard({required this.client}); @override Widget build(BuildContext context) { final name = '${client['firstName']} ${client['lastName']}'; final company = client['company'] as String?; final tags = (client['tags'] as List?)?.cast() ?? []; return Card( margin: const EdgeInsets.only(bottom: 12), child: InkWell( onTap: () => context.go('/clients/${client['id']}'), borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(16), child: Row( children: [ CircleAvatar( backgroundColor: Theme.of(context).colorScheme.primaryContainer, child: Text( '${client['firstName'][0]}${client['lastName'][0]}', style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryContainer, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( name, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), if (company != null && company.isNotEmpty) ...[ const SizedBox(height: 2), Text( company, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey, ), ), ], if (tags.isNotEmpty) ...[ const SizedBox(height: 8), Wrap( spacing: 4, runSpacing: 4, children: tags.take(3).map((tag) => Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2, ), decoration: BoxDecoration( color: Theme.of(context).colorScheme.secondaryContainer, borderRadius: BorderRadius.circular(12), ), child: Text( tag, style: Theme.of(context).textTheme.labelSmall?.copyWith( color: Theme.of(context).colorScheme.onSecondaryContainer, ), ), )).toList(), ), ], ], ), ), Icon( Icons.chevron_right, color: Colors.grey.shade400, ), ], ), ), ), ); } }