UI: Replace hidden menu with visible quick action buttons on client profile

This commit is contained in:
2026-01-27 22:35:51 +00:00
parent ef5bb58b5d
commit ef6a032791

View File

@@ -24,77 +24,51 @@ class ClientDetailScreen extends ConsumerWidget {
actions: [
IconButton(
icon: const Icon(Icons.edit),
tooltip: 'Edit Client',
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<bool>(
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),
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<bool>(
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(
@@ -117,8 +91,16 @@ class ClientDetailScreen extends ConsumerWidget {
class _ClientDetailContent extends StatelessWidget {
final Map<String, dynamic> client;
final String clientId;
final VoidCallback onMarkContacted;
final VoidCallback onDelete;
const _ClientDetailContent({required this.client});
const _ClientDetailContent({
required this.client,
required this.clientId,
required this.onMarkContacted,
required this.onDelete,
});
@override
Widget build(BuildContext context) {
@@ -165,7 +147,36 @@ class _ClientDetailContent extends StatelessWidget {
],
),
),
const SizedBox(height: 32),
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(
@@ -344,3 +355,45 @@ class _InfoRow extends StatelessWidget {
);
}
}
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,
),
),
],
),
),
);
}
}