Files
ignis_app/lib/screens/event_log_screen.dart
2026-04-22 21:08:02 +07:00

166 lines
5.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/providers.dart';
/// Экран просмотра лога событий.
class EventLogScreen extends ConsumerStatefulWidget {
const EventLogScreen({super.key});
@override
ConsumerState<EventLogScreen> createState() => _EventLogScreenState();
}
class _EventLogScreenState extends ConsumerState<EventLogScreen> {
bool _loading = true;
int _limit = 100;
@override
void initState() {
super.initState();
_load();
}
Future<void> _load() async {
setState(() => _loading = true);
await ref.read(eventLogProvider.notifier).load(limit: _limit);
if (mounted) setState(() => _loading = false);
}
@override
Widget build(BuildContext context) {
final events = ref.watch(eventLogProvider);
return Scaffold(
appBar: AppBar(
title: const Text('ЛОГ СОБЫТИЙ'),
actions: [
PopupMenuButton<int>(
icon: const Icon(Icons.filter_list),
tooltip: 'Количество записей',
onSelected: (v) {
_limit = v;
_load();
},
itemBuilder: (_) => [50, 100, 200, 500]
.map((n) => PopupMenuItem(value: n, child: Text('$n записей')))
.toList(),
),
],
),
body: _loading
? const Center(
child: CircularProgressIndicator(color: Colors.deepOrange),
)
: events.isEmpty
? const Center(
child: Text(
'Нет событий',
style: TextStyle(color: Colors.white54),
),
)
: RefreshIndicator(
color: Colors.deepOrange,
onRefresh: _load,
child: ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: events.length,
itemBuilder: (context, index) {
final event = events[index];
return _EventRow(
event: event is Map
? Map<String, dynamic>.from(event)
: {},
);
},
),
),
);
}
}
class _EventRow extends StatelessWidget {
final Map<String, dynamic> event;
const _EventRow({required this.event});
@override
Widget build(BuildContext context) {
final timestamp = event['timestamp'] ?? event['time'] ?? event['created_at'] ?? '';
final action = event['action'] ?? event['command'] ?? event['type'] ?? '';
final targetId = event['target_id'] ?? event['target'] ?? event['group_id'] ?? '';
final params = event['params'] ?? event['details'] ?? '';
final actor = event['actor'] ?? event['user'] ?? event['key_name'] ?? '';
final formattedTime = _formatTime(timestamp.toString());
return Card(
margin: const EdgeInsets.only(bottom: 4),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Время
SizedBox(
width: 80,
child: Text(
formattedTime,
style: const TextStyle(
fontSize: 11,
color: Colors.white38,
fontFamily: 'monospace',
),
),
),
const SizedBox(width: 8),
// Контент
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'$action - $targetId',
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
),
),
if (params.toString().isNotEmpty)
Text(
params.toString(),
style: const TextStyle(
fontSize: 11,
color: Colors.white38,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
if (actor.toString().isNotEmpty)
Text(
actor.toString(),
style: const TextStyle(
fontSize: 10,
color: Colors.white24,
),
),
],
),
),
],
),
),
);
}
String _formatTime(String iso) {
if (iso.isEmpty) return '';
try {
final d = DateTime.parse(iso);
String pad(int n) => n.toString().padLeft(2, '0');
return '${pad(d.day)}.${pad(d.month)} ${pad(d.hour)}:${pad(d.minute)}:${pad(d.second)}';
} catch (_) {
return iso;
}
}
}