import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'bridge/location_bridge.dart'; class StatusScreen extends StatefulWidget { const StatusScreen({super.key}); @override State createState() => _StatusScreenState(); } class _StatusScreenState extends State { List> _logs = []; final ScrollController _scrollController = ScrollController(); String _filter = 'ALL'; @override void initState() { super.initState(); SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarColor: Color(0xFF0d0d0d), statusBarIconBrightness: Brightness.light, ), ); _loadLogs(); } @override void dispose() { _scrollController.dispose(); super.dispose(); } Future _loadLogs() async { final logs = await LocationBridge.getLogs(); if (mounted) { setState(() { _logs = logs.reversed.toList(); }); WidgetsBinding.instance.addPostFrameCallback((_) { if (_scrollController.hasClients) { _scrollController.jumpTo(_scrollController.position.maxScrollExtent); } }); } } Future _clearLogs() async { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: const Color(0xFF1e1e1e), title: const Text( 'Clear Event Logs', style: TextStyle(fontFamily: 'monospace', color: Color(0xFFe0e0e0)), ), content: const Text( 'This will permanently delete all logged events. Continue?', style: TextStyle(fontFamily: 'monospace', color: Color(0xFF9e9e9e)), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text( 'CANCEL', style: TextStyle( fontFamily: 'monospace', color: Color(0xFF616161), ), ), ), TextButton( onPressed: () => Navigator.pop(ctx, true), child: const Text( 'CLEAR', style: TextStyle( fontFamily: 'monospace', color: Color(0xFFff5252), ), ), ), ], ), ); if (confirmed != true) return; final result = await LocationBridge.clearLogs(); if (mounted) { if (result >= 0) { await _loadLogs(); } } } String _formatTimestamp(int timestamp) { final dt = DateTime.fromMillisecondsSinceEpoch(timestamp); final ms = (dt.millisecond / 1000).toStringAsFixed(3).substring(2); return '${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}:${dt.second.toString().padLeft(2, '0')}.$ms'; } Color _getLogColor(String eventType) { switch (eventType) { case 'ERROR': return const Color(0xFFff5252); case 'WARNING': return const Color(0xFFffb74d); case 'LOCATION': return const Color(0xFF69f0ae); case 'SYNC': return const Color(0xFF40c4ff); case 'HEARTBEAT': return const Color(0xFFffe082); case 'FILTERED': return const Color(0xFF546e7a); default: return const Color(0xFF78909c); } } IconData _getLogIcon(String eventType) { switch (eventType) { case 'ERROR': return Icons.close; case 'WARNING': return Icons.warning; case 'LOCATION': return Icons.gps_fixed; case 'SYNC': return Icons.cloud_upload; case 'HEARTBEAT': return Icons.favorite; case 'FILTERED': return Icons.filter_alt; default: return Icons.info; } } List> get _filteredLogs { if (_filter == 'ALL') return _logs; return _logs.where((l) => l['eventType'] == _filter).toList(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFF0d0d0d), appBar: AppBar( backgroundColor: const Color(0xFF161616), elevation: 0, title: Row( children: [ const Icon(Icons.terminal, color: Color(0xFF00bcd4), size: 18), const SizedBox(width: 10), const Text( 'EVENT LOG', style: TextStyle( fontFamily: 'monospace', fontSize: 14, fontWeight: FontWeight.w700, letterSpacing: 2, color: Color(0xFFe0e0e0), ), ), ], ), leading: IconButton( icon: const Icon( Icons.arrow_back, color: Color(0xFF9e9e9e), size: 20, ), onPressed: () => Navigator.pop(context), ), actions: [ IconButton( icon: const Icon( Icons.delete_outline, color: Color(0xFF616161), size: 20, ), onPressed: _clearLogs, ), IconButton( icon: const Icon(Icons.refresh, color: Color(0xFF616161), size: 20), onPressed: _loadLogs, ), ], bottom: PreferredSize( preferredSize: const Size.fromHeight(1), child: Container(height: 1, color: const Color(0xFF2a2a2a)), ), ), body: Column( children: [ // ── Filter bar ────────────────────────────────────────────────────── Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: const BoxDecoration( color: Color(0xFF161616), border: Border( bottom: BorderSide(color: Color(0xFF2a2a2a), width: 1), ), ), child: Row( children: [ _buildFilterChip('ALL', null), const SizedBox(width: 8), _buildFilterChip('LOG', 'LOCATION'), const SizedBox(width: 8), _buildFilterChip('SYNC', 'SYNC'), const SizedBox(width: 8), _buildFilterChip('ERR', 'ERROR'), ], ), ), // ── Log list ───────────────────────────────────────────────────────── Expanded( child: _filteredLogs.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.inbox_outlined, size: 48, color: Color(0xFF2a2a2a), ), const SizedBox(height: 16), Text( _filter == 'ALL' ? 'No events recorded' : 'No $_filter events', style: const TextStyle( fontFamily: 'monospace', fontSize: 13, color: Color(0xFF424242), ), ), const SizedBox(height: 8), Text( 'Start tracking to capture events', style: TextStyle( fontFamily: 'monospace', fontSize: 11, color: const Color( 0xFF424242, ).withValues(alpha: 0.6), ), ), ], ), ) : ListView.builder( controller: _scrollController, padding: const EdgeInsets.symmetric(vertical: 8), itemCount: _filteredLogs.length, itemBuilder: (context, index) { return _buildLogEntry(_filteredLogs[index], index); }, ), ), ], ), ); } Widget _buildFilterChip(String label, String? eventType) { final isSelected = _filter == (eventType ?? 'ALL'); return GestureDetector( onTap: () => setState(() => _filter = eventType ?? 'ALL'), child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: isSelected ? const Color(0xFF00bcd4).withValues(alpha: 0.15) : Colors.transparent, borderRadius: BorderRadius.circular(3), border: Border.all( color: isSelected ? const Color(0xFF00bcd4).withValues(alpha: 0.5) : const Color(0xFF2a2a2a), width: 1, ), ), child: Text( label, style: TextStyle( fontFamily: 'monospace', fontSize: 10, fontWeight: FontWeight.w700, letterSpacing: 1, color: isSelected ? const Color(0xFF00bcd4) : const Color(0xFF424242), ), ), ), ); } Widget _buildLogEntry(Map log, int index) { final time = _formatTimestamp(log['timestamp'] as int? ?? 0); final type = log['eventType'] as String? ?? 'UNKNOWN'; final msg = log['message'] as String? ?? ''; final color = _getLogColor(type); final icon = _getLogIcon(type); return Padding( padding: EdgeInsets.only( left: 14, right: 14, top: index == 0 ? 8 : 10, bottom: 8, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Line 1: timestamp | icon | type Row( children: [ Text( time, style: const TextStyle( fontFamily: 'monospace', fontSize: 10, color: Color(0xFF546e7a), ), ), const SizedBox(width: 8), Container( width: 16, height: 16, decoration: BoxDecoration( color: color.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(3), ), child: Icon(icon, size: 10, color: color), ), const SizedBox(width: 6), Container( padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1), decoration: BoxDecoration( color: color.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(2), ), child: Text( type, style: TextStyle( fontFamily: 'monospace', fontSize: 9, fontWeight: FontWeight.w700, letterSpacing: 1, color: color, ), ), ), ], ), const SizedBox(height: 5), // Line 2: message Text( msg, style: const TextStyle( fontFamily: 'monospace', fontSize: 11, color: Color(0xFF9e9e9e), ), ), ], ), ); } }