import 'package:flutter/material.dart'; import 'package:worldhopper/l10n/app_localizations.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) { final l10n = AppLocalizations.of(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) => [ PopupMenuItem( value: 'edit', child: Row( children: [ const Icon(Icons.edit), const SizedBox(width: 8), Text(l10n.serverCardEdit), ], ), ), PopupMenuItem( value: 'delete', child: Row( children: [ const Icon(Icons.delete, color: Colors.red), const SizedBox(width: 8), Text(l10n.serverCardDelete, style: const 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( l10n.serverCardAuthenticated, 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( l10n.serverCardLastSynced( _formatDate(context, server.lastSyncedAt!)), style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.6), ), ), ], ], ), ], ], ), ), ), ); } String _formatDate(BuildContext context, DateTime date) { final l10n = AppLocalizations.of(context); 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 l10n.timeDaysAgo(difference.inDays); } else if (difference.inHours > 0) { return l10n.timeHoursAgo(difference.inHours); } else if (difference.inMinutes > 0) { return l10n.timeMinutesAgo(difference.inMinutes); } else { return l10n.timeJustNow; } } }