import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:qpay/providers/uptime_provider.dart'; import 'package:qpay/models/uptime_item_model.dart'; class UptimeStatusScreen extends StatefulWidget { const UptimeStatusScreen({super.key}); @override State createState() => _UptimeStatusScreenState(); } class _UptimeStatusScreenState extends State { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { context.read().fetchUptimes(); }); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( appBar: AppBar(title: const Text('Uptime Status'), centerTitle: false), body: Center( child: SizedBox( width: kIsWeb ? 600 : double.infinity, child: Consumer( builder: (context, provider, _) { if (provider.isLoading) { return const Center(child: CircularProgressIndicator()); } if (provider.hasError) { return _ErrorView( message: provider.error ?? 'An unexpected error occurred.', onRetry: () => provider.fetchUptimes(), ); } if (provider.status == UptimeStatus.idle) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.monitor_heart_outlined, size: 64, color: theme.colorScheme.primary.withAlpha(100), ), const SizedBox(height: 16), Text( 'Uptime Status', style: theme.textTheme.headlineSmall, ), const SizedBox(height: 8), Text( 'Pull to load service status.', style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withAlpha(150), ), ), ], ), ); } final items = provider.items; if (items.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.cloud_off, size: 64, color: theme.colorScheme.onSurface.withAlpha(80), ), const SizedBox(height: 16), Text( 'No services found.', style: theme.textTheme.titleMedium, ), ], ), ); } return RefreshIndicator( onRefresh: () => provider.fetchUptimes(), child: Column( children: [ _SummaryBar( upCount: provider.upCount, downCount: provider.downCount, ), Expanded( child: ListView.builder( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), itemCount: items.length, itemBuilder: (context, index) { return _UptimeCard(item: items[index]); }, ), ), ], ), ); }, ), ), ), ); } } class _SummaryBar extends StatelessWidget { const _SummaryBar({required this.upCount, required this.downCount}); final int upCount; final int downCount; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Container( margin: const EdgeInsets.fromLTRB(16, 16, 16, 4), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), decoration: BoxDecoration( gradient: LinearGradient( colors: [ theme.colorScheme.primary, theme.colorScheme.primary.withAlpha(220), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: theme.colorScheme.primary.withAlpha(60), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: Row( children: [ _StatBadge( label: 'Up', count: upCount, color: const Color(0xFF10B981), icon: Icons.check_circle, ), Container( height: 36, width: 1, color: Colors.white.withAlpha(60), margin: const EdgeInsets.symmetric(horizontal: 16), ), _StatBadge( label: 'Down', count: downCount, color: const Color(0xFFEF4444), icon: Icons.cancel, ), const Spacer(), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( '${upCount + downCount}', style: theme.textTheme.headlineMedium?.copyWith( color: Colors.white, fontWeight: FontWeight.w700, ), ), Text( 'Total', style: theme.textTheme.bodySmall?.copyWith( color: Colors.white.withAlpha(200), ), ), ], ), ], ), ); } } class _StatBadge extends StatelessWidget { const _StatBadge({ required this.label, required this.count, required this.color, required this.icon, }); final String label; final int count; final Color color; final IconData icon; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Row( children: [ Container( width: 32, height: 32, decoration: BoxDecoration( color: color.withAlpha(40), borderRadius: BorderRadius.circular(8), ), child: Icon(icon, size: 18, color: color), ), const SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '$count', style: theme.textTheme.titleLarge?.copyWith( color: Colors.white, fontWeight: FontWeight.w700, ), ), Text( label, style: theme.textTheme.bodySmall?.copyWith( color: Colors.white.withAlpha(200), ), ), ], ), ], ); } } class _UptimeCard extends StatelessWidget { const _UptimeCard({required this.item}); final UptimeItemModel item; IconData _iconForService(String name) { final lower = name.toLowerCase(); if (lower.contains('visa') || lower.contains('mastercard')) { return Icons.credit_card; } if (lower.contains('ecocash')) { return Icons.account_balance_wallet; } if (lower.contains('econet')) { return Icons.signal_cellular_alt; } if (lower.contains('netone')) { return Icons.cell_tower; } if (lower.contains('zesa')) { return Icons.electric_bolt; } return Icons.dns; } String _formatTimestamp(String iso) { try { final dt = DateTime.parse(iso); final now = DateTime.now(); final diff = now.difference(dt); if (diff.inMinutes < 1) return 'Just now'; if (diff.inMinutes < 60) return '${diff.inMinutes}m ago'; if (diff.inHours < 24) return '${diff.inHours}h ago'; if (diff.inDays < 7) return '${diff.inDays}d ago'; return '${dt.day}/${dt.month}/${dt.year}'; } catch (_) { return iso; } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isUp = item.status; return Card( margin: const EdgeInsets.only(bottom: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: isUp ? const Color(0xFFD1FAE5) : const Color(0xFFFEE2E2), borderRadius: BorderRadius.circular(12), ), child: Icon( _iconForService(item.name), size: 22, color: isUp ? const Color(0xFF065F46) : const Color(0xFF991B1B), ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.name, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), const SizedBox(height: 4), Text( 'Last checked ${_formatTimestamp(item.createdAt)}', style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withAlpha(120), ), ), ], ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: isUp ? const Color(0xFFD1FAE5) : const Color(0xFFFEE2E2), borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 8, height: 8, decoration: BoxDecoration( color: isUp ? const Color(0xFF10B981) : const Color(0xFFEF4444), shape: BoxShape.circle, ), ), const SizedBox(width: 6), Text( isUp ? 'Up' : 'Down', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: isUp ? const Color(0xFF065F46) : const Color(0xFF991B1B), ), ), ], ), ), ], ), ), ); } } class _ErrorView extends StatelessWidget { const _ErrorView({required this.message, required this.onRetry}); final String message; final VoidCallback onRetry; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, size: 56, color: theme.colorScheme.error.withAlpha(150), ), const SizedBox(height: 16), Text('Failed to Load', style: theme.textTheme.titleLarge), const SizedBox(height: 8), Text( message, textAlign: TextAlign.center, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withAlpha(150), ), ), const SizedBox(height: 20), ElevatedButton.icon( onPressed: onRetry, icon: const Icon(Icons.refresh, size: 18), label: const Text('Retry'), ), ], ), ), ); } }