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), tooltip: 'Edit Client', onPressed: () => context.go('/clients/$clientId/edit'), ), ], ), body: clientAsync.when( data: (client) => _ClientDetailContent( client: client, clientId: clientId, onMarkContacted: () async { await ref.read(apiClientProvider).markClientContacted(clientId); ref.invalidate(clientDetailProvider(clientId)); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Marked as contacted')), ); } }, onDelete: () async { 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('/'); } } }, ), 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; final String clientId; final VoidCallback onMarkContacted; final VoidCallback onDelete; const _ClientDetailContent({ required this.client, required this.clientId, required this.onMarkContacted, required this.onDelete, }); @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: 24), // Quick Actions Card( child: Padding( padding: const EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _ActionButton( icon: Icons.email, label: 'Email', onTap: () => context.go('/emails/compose?clientId=$clientId'), ), _ActionButton( icon: Icons.check_circle, label: 'Contacted', onTap: onMarkContacted, ), _ActionButton( icon: Icons.delete_outline, label: 'Delete', color: Colors.red, onTap: onDelete, ), ], ), ), ), const SizedBox(height: 24), // 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), ), ], ), ); } } class _ActionButton extends StatelessWidget { final IconData icon; final String label; final VoidCallback onTap; final Color? color; const _ActionButton({ required this.icon, required this.label, required this.onTap, this.color, }); @override Widget build(BuildContext context) { final effectiveColor = color ?? Theme.of(context).colorScheme.primary; return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, color: effectiveColor, size: 28), const SizedBox(height: 4), Text( label, style: TextStyle( color: effectiveColor, fontWeight: FontWeight.w500, fontSize: 12, ), ), ], ), ), ); } }