import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import '../../../shared/services/api_client.dart'; final clientDetailProvider = FutureProvider.autoDispose .family, String>((ref, id) async { final apiClient = ref.watch(apiClientProvider); return apiClient.getClient(id); }); class ClientDetailScreen extends ConsumerWidget { final String clientId; const ClientDetailScreen({super.key, required this.clientId}); @override Widget build(BuildContext context, WidgetRef ref) { final clientAsync = ref.watch(clientDetailProvider(clientId)); return Scaffold( appBar: AppBar( actions: [ IconButton( icon: const Icon(Icons.edit), onPressed: () => context.go('/clients/$clientId/edit'), ), PopupMenuButton( itemBuilder: (context) => [ const PopupMenuItem( value: 'email', child: ListTile( leading: Icon(Icons.email), title: Text('Generate Email'), contentPadding: EdgeInsets.zero, ), ), const PopupMenuItem( value: 'contacted', child: ListTile( leading: Icon(Icons.check_circle), title: Text('Mark Contacted'), contentPadding: EdgeInsets.zero, ), ), const PopupMenuItem( value: 'delete', child: ListTile( leading: Icon(Icons.delete, color: Colors.red), title: Text('Delete', style: TextStyle(color: Colors.red)), contentPadding: EdgeInsets.zero, ), ), ], onSelected: (value) async { switch (value) { case 'email': context.go('/emails/compose?clientId=$clientId'); break; case 'contacted': await ref.read(apiClientProvider).markClientContacted(clientId); ref.invalidate(clientDetailProvider(clientId)); break; case 'delete': final confirm = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Delete Client'), content: const Text('Are you sure you want to delete this client?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Cancel'), ), TextButton( onPressed: () => Navigator.pop(context, true), style: TextButton.styleFrom(foregroundColor: Colors.red), child: const Text('Delete'), ), ], ), ); if (confirm == true) { await ref.read(apiClientProvider).deleteClient(clientId); if (context.mounted) { context.go('/'); } } break; } }, ), ], ), body: clientAsync.when( data: (client) => _ClientDetailContent(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), const Text('Failed to load client'), TextButton( onPressed: () => ref.invalidate(clientDetailProvider(clientId)), child: const Text('Retry'), ), ], ), ), ), ); } } class _ClientDetailContent extends StatelessWidget { final Map client; const _ClientDetailContent({required this.client}); @override Widget build(BuildContext context) { final name = '${client['firstName']} ${client['lastName']}'; final dateFormat = DateFormat.yMMMd(); return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Center( child: Column( children: [ CircleAvatar( radius: 48, backgroundColor: Theme.of(context).colorScheme.primaryContainer, child: Text( '${client['firstName'][0]}${client['lastName'][0]}', style: TextStyle( fontSize: 32, color: Theme.of(context).colorScheme.onPrimaryContainer, fontWeight: FontWeight.bold, ), ), ), const SizedBox(height: 16), Text( name, style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, ), ), if (client['company'] != null) ...[ const SizedBox(height: 4), Text( '${client['role'] ?? ''} ${client['role'] != null && client['company'] != null ? 'at ' : ''}${client['company'] ?? ''}'.trim(), style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: Colors.grey, ), ), ], ], ), ), const SizedBox(height: 32), // Contact info _Section( title: 'Contact', children: [ if (client['email'] != null) _InfoRow(icon: Icons.email, label: 'Email', value: client['email']), if (client['phone'] != null) _InfoRow(icon: Icons.phone, label: 'Phone', value: client['phone']), if (client['city'] != null || client['state'] != null) _InfoRow( icon: Icons.location_on, label: 'Location', value: [client['city'], client['state']].where((e) => e != null).join(', '), ), ], ), // Personal info if (client['birthday'] != null || client['anniversary'] != null || (client['interests'] as List?)?.isNotEmpty == true) _Section( title: 'Personal', children: [ if (client['birthday'] != null) _InfoRow( icon: Icons.cake, label: 'Birthday', value: dateFormat.format(DateTime.parse(client['birthday'])), ), if (client['anniversary'] != null) _InfoRow( icon: Icons.favorite, label: 'Anniversary', value: dateFormat.format(DateTime.parse(client['anniversary'])), ), if ((client['interests'] as List?)?.isNotEmpty == true) ...[ const SizedBox(height: 8), Text( 'Interests', style: Theme.of(context).textTheme.labelLarge?.copyWith( color: Colors.grey, ), ), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: (client['interests'] as List).cast().map((interest) => Chip(label: Text(interest)), ).toList(), ), ], ], ), // Family if (client['family'] != null && (client['family']['spouse'] != null || (client['family']['children'] as List?)?.isNotEmpty == true)) _Section( title: 'Family', children: [ if (client['family']['spouse'] != null) _InfoRow( icon: Icons.person, label: 'Spouse', value: client['family']['spouse'], ), if ((client['family']['children'] as List?)?.isNotEmpty == true) _InfoRow( icon: Icons.child_care, label: 'Children', value: (client['family']['children'] as List).join(', '), ), ], ), // Notes if (client['notes'] != null && client['notes'].toString().isNotEmpty) _Section( title: 'Notes', children: [ Text(client['notes']), ], ), // Tags if ((client['tags'] as List?)?.isNotEmpty == true) _Section( title: 'Tags', children: [ Wrap( spacing: 8, runSpacing: 8, children: (client['tags'] as List).cast().map((tag) => Chip( label: Text(tag), backgroundColor: Theme.of(context).colorScheme.secondaryContainer, ), ).toList(), ), ], ), // Last contacted if (client['lastContactedAt'] != null) Padding( padding: const EdgeInsets.only(top: 16), child: Text( 'Last contacted: ${dateFormat.format(DateTime.parse(client['lastContactedAt']))}', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Colors.grey, ), ), ), ], ), ); } } class _Section extends StatelessWidget { final String title; final List children; const _Section({required this.title, required this.children}); @override Widget build(BuildContext context) { if (children.isEmpty) return const SizedBox.shrink(); return Padding( padding: const EdgeInsets.only(bottom: 24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), ...children, ], ), ); } } class _InfoRow extends StatelessWidget { final IconData icon; final String label; final String value; const _InfoRow({ required this.icon, required this.label, required this.value, }); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ Icon(icon, size: 20, color: Colors.grey), const SizedBox(width: 12), Expanded( child: Text(value), ), ], ), ); } }