import 'package:flutter/material.dart'; import 'package:worldhopper/models/opds_server.dart'; /// Card widget displaying an OPDS server class ServerCard extends StatelessWidget { final OPDSServer server; final VoidCallback onTap; final VoidCallback onEdit; final VoidCallback onDelete; const ServerCard({ super.key, required this.server, required this.onTap, required this.onEdit, required this.onDelete, }); @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.only(bottom: 12), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ // Server icon Container( width: 48, height: 48, decoration: BoxDecoration( color: Theme.of(context).colorScheme.primaryContainer, borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.cloud, color: Theme.of(context).colorScheme.primary, ), ), const SizedBox(width: 16), // Server info Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( server.name, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( server.url, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.6), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), // Actions menu PopupMenuButton( onSelected: (value) { if (value == 'edit') { onEdit(); } else if (value == 'delete') { onDelete(); } }, itemBuilder: (context) => [ const PopupMenuItem( value: 'edit', child: Row( children: [ Icon(Icons.edit), SizedBox(width: 8), Text('Edit'), ], ), ), const PopupMenuItem( value: 'delete', child: Row( children: [ Icon(Icons.delete, color: Colors.red), SizedBox(width: 8), Text('Delete', style: TextStyle(color: Colors.red)), ], ), ), ], ), ], ), // Server metadata if (server.requiresAuth || server.lastSyncedAt != null) ...[ const SizedBox(height: 12), const Divider(), const SizedBox(height: 8), Row( children: [ if (server.requiresAuth) ...[ Icon( Icons.lock, size: 16, color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.6), ), const SizedBox(width: 4), Text( 'Authenticated', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.6), ), ), ], if (server.requiresAuth && server.lastSyncedAt != null) const SizedBox(width: 16), if (server.lastSyncedAt != null) ...[ Icon( Icons.sync, size: 16, color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.6), ), const SizedBox(width: 4), Text( 'Last synced: ${_formatDate(server.lastSyncedAt!)}', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.6), ), ), ], ], ), ], ], ), ), ), ); } String _formatDate(DateTime date) { final now = DateTime.now(); final difference = now.difference(date); if (difference.inDays > 7) { return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}'; } else if (difference.inDays > 0) { return '${difference.inDays}d ago'; } else if (difference.inHours > 0) { return '${difference.inHours}h ago'; } else if (difference.inMinutes > 0) { return '${difference.inMinutes}m ago'; } else { return 'just now'; } } }