import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../app/error_message.dart'; import '../models/ignis_group.dart'; import '../providers/providers.dart'; import '../widgets/build_info_text.dart'; import '../widgets/group_card.dart'; import 'homes_screen.dart'; import 'group_edit_screen.dart'; import 'schedules_screen.dart'; import 'stats_screen.dart'; import 'event_log_screen.dart'; import 'api_keys_screen.dart'; /// Основной экран пульта управления. /// Показывает группы текущего дома с управлением. class RemoteScreen extends ConsumerStatefulWidget { const RemoteScreen({super.key}); @override ConsumerState createState() => _RemoteScreenState(); } class _RemoteScreenState extends ConsumerState { @override void initState() { super.initState(); Future.microtask(() => ref.read(groupsProvider.notifier).startPolling()); } @override void dispose() { ref.read(groupsProvider.notifier).stopPolling(); super.dispose(); } @override Widget build(BuildContext context) { final groups = ref.watch(groupsProvider); final groupsLoadState = ref.watch(groupsLoadStateProvider); final currentHome = ref.watch(currentHomeProvider); final authInfoState = ref.watch(authInfoProvider); final isAdmin = authInfoState.data?.isAdmin == true; return Scaffold( appBar: AppBar( title: Text(currentHome?.name ?? 'IGNIS'), leading: IconButton( icon: const Icon(Icons.home), tooltip: 'Дома', onPressed: () => Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (_) => const HomesScreen()), ), ), actions: [ // Кнопка добавления группы IconButton( icon: const Icon(Icons.add_circle_outline), tooltip: 'Создать группу', onPressed: () => Navigator.of( context, ).push(MaterialPageRoute(builder: (_) => const GroupEditScreen())), ), // Меню PopupMenuButton( icon: const Icon(Icons.more_vert), onSelected: (value) { switch (value) { case 'schedules': Navigator.of(context).push( MaterialPageRoute(builder: (_) => const SchedulesScreen()), ); break; case 'stats': Navigator.of(context).push( MaterialPageRoute(builder: (_) => const StatsScreen()), ); break; case 'log': Navigator.of(context).push( MaterialPageRoute(builder: (_) => const EventLogScreen()), ); break; case 'api_keys': Navigator.of(context).push( MaterialPageRoute(builder: (_) => const ApiKeysScreen()), ); break; } }, itemBuilder: (context) => [ const PopupMenuItem( value: 'schedules', child: ListTile( leading: Icon(Icons.schedule), title: Text('Расписания'), contentPadding: EdgeInsets.zero, ), ), const PopupMenuItem( value: 'stats', child: ListTile( leading: Icon(Icons.bar_chart), title: Text('Статистика'), contentPadding: EdgeInsets.zero, ), ), const PopupMenuItem( value: 'log', child: ListTile( leading: Icon(Icons.list_alt), title: Text('Лог событий'), contentPadding: EdgeInsets.zero, ), ), if (isAdmin) const PopupMenuItem( value: 'api_keys', child: ListTile( leading: Icon(Icons.vpn_key), title: Text('API-ключи'), contentPadding: EdgeInsets.zero, ), ), const PopupMenuItem( enabled: false, child: Padding( padding: EdgeInsets.symmetric(vertical: 4), child: BuildInfoText(), ), ), ], ), ], ), body: groupsLoadState.isLoading && groups.isEmpty ? const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(color: Colors.deepOrange), SizedBox(height: 20), Text( "Опрос ламп (это долго)...", style: TextStyle(color: Colors.white54), ), ], ), ) : groupsLoadState.hasError && groups.isEmpty ? _GroupsErrorView(message: groupsLoadState.errorMessage) : groups.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.lightbulb_outline, size: 64, color: Colors.white24, ), const SizedBox(height: 16), const Text( 'Нет групп', style: TextStyle(color: Colors.white54, fontSize: 16), ), const SizedBox(height: 8), TextButton.icon( icon: const Icon(Icons.add), label: const Text('Создать группу'), onPressed: () => Navigator.of(context).push( MaterialPageRoute( builder: (_) => const GroupEditScreen(), ), ), ), ], ), ) : RefreshIndicator( color: Colors.deepOrange, onRefresh: () => ref.read(groupsProvider.notifier).refresh(), child: ListView.builder( padding: const EdgeInsets.only(top: 8, bottom: 80), itemCount: groups.length, itemBuilder: (context, index) { final g = groups[index]; return Dismissible( key: Key(g.id), direction: DismissDirection.endToStart, background: Container( alignment: Alignment.centerRight, padding: const EdgeInsets.only(right: 20), margin: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), decoration: BoxDecoration( color: Colors.redAccent.withValues(alpha: 0.3), borderRadius: BorderRadius.circular(16), ), child: const Icon(Icons.delete, color: Colors.redAccent), ), confirmDismiss: (_) => _confirmAndDeleteGroup(context, g), child: GroupCard(group: g), ); }, ), ), ); } Future _confirmAndDeleteGroup( BuildContext context, IgnisGroup g, ) async { final messenger = ScaffoldMessenger.of(context); final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('Удалить группу?'), content: Text('Удалить "${g.name}"?'), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(false), child: const Text('Отмена'), ), TextButton( onPressed: () => Navigator.of(ctx).pop(true), child: const Text( 'Удалить', style: TextStyle(color: Colors.redAccent), ), ), ], ), ) ?? false; if (!confirmed) return false; try { await ref.read(apiProvider).deleteGroup(g.id); await ref.read(groupsProvider.notifier).refresh(); return true; } catch (e) { if (mounted) { messenger.showSnackBar( SnackBar(content: Text('Ошибка удаления: ${describeLoadError(e)}')), ); } return false; } } } class _GroupsErrorView extends ConsumerWidget { final String? message; const _GroupsErrorView({this.message}); @override Widget build(BuildContext context, WidgetRef ref) { return Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.wifi_off_rounded, size: 64, color: Colors.deepOrange, ), const SizedBox(height: 16), const Text( 'Не удалось загрузить группы', textAlign: TextAlign.center, style: TextStyle(color: Colors.white70, fontSize: 16), ), if (message != null) ...[ const SizedBox(height: 8), Text( message!, textAlign: TextAlign.center, style: const TextStyle(color: Colors.white38, fontSize: 12), ), ], const SizedBox(height: 16), FilledButton.icon( onPressed: () => ref.read(groupsProvider.notifier).refresh(), icon: const Icon(Icons.refresh), label: const Text('Повторить'), ), ], ), ), ); } }