import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import '../l10n/app_localizations.dart'; import '../models/remote_command.dart'; import '../utils/navigation.dart'; import '../viewmodels/admin_pin_view_model.dart'; import '../viewmodels/remote_sync_view_model.dart'; import 'dialogs/new_tab_dialog.dart'; class AdminScreenView extends StatelessWidget { const AdminScreenView({super.key}); void _leave(BuildContext context) { final adminPinViewModel = context.read(); context.popOrGo('/bar'); adminPinViewModel.lock(); } Future _openNewTab(BuildContext context) async { final l10n = AppLocalizations.of(context); final name = await showNewTabDialog(context); if (name == null || !context.mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(l10n.tabOpened(name)))); } Future _confirmUnlink(BuildContext context) async { final l10n = AppLocalizations.of(context); final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('Disconnect from server'), content: const Text( 'This will unlink the device from the remote server. ' 'Open tabs will no longer be synced. Continue?', ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: Text(l10n.cancel), ), FilledButton( onPressed: () => Navigator.pop(ctx, true), style: FilledButton.styleFrom(backgroundColor: Colors.red), child: const Text('Disconnect'), ), ], ), ); if (confirmed == true && context.mounted) { await context.read().unlink(); } } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final remoteSync = context.watch(); return PopScope( onPopInvokedWithResult: (didPop, result) { if (didPop) context.read().lock(); }, child: Scaffold( appBar: AppBar( leading: IconButton( onPressed: () => _leave(context), icon: const Icon(Icons.arrow_back), ), title: Text(l10n.admin), actions: [ IconButton( tooltip: l10n.logout, onPressed: () { final adminPinViewModel = context.read(); context.go('/bar'); adminPinViewModel.lock(); }, icon: const Icon(Icons.logout), ), ], ), body: ListView( padding: const EdgeInsets.symmetric(vertical: 12), children: [ _AdminSection( title: l10n.adminTools, children: [ _AdminTile( icon: Icons.person_add_alt_1_outlined, title: l10n.openNewTab, subtitle: l10n.openNewTabDescription, onTap: () => _openNewTab(context), ), _AdminTile( icon: Icons.inventory_2_outlined, title: l10n.products, subtitle: l10n.manageProducts, onTap: () => context.push('/products'), ), _AdminTile( icon: Icons.history_rounded, title: l10n.tabHistory, subtitle: l10n.closedTabs, onTap: () => context.push('/history'), ), _AdminTile( icon: Icons.file_upload_outlined, title: l10n.importData, subtitle: l10n.importNamesDescription, onTap: () => context.push('/admin/import'), ), ], ), const SizedBox(height: 8), _AdminSection( title: 'Remote Server', children: [ if (remoteSync.isConfigured) ...[ _AdminTile( icon: Icons.cloud_done_outlined, title: 'Connected', subtitle: 'Device linked to remote server', trailing: TextButton( onPressed: () => _confirmUnlink(context), style: TextButton.styleFrom(foregroundColor: Colors.red), child: const Text('Disconnect'), ), onTap: null, ), ] else _AdminTile( icon: Icons.cloud_outlined, title: 'Not connected', subtitle: 'Link to remote dashboard', onTap: () => context.push('/register'), ), ], ), if (remoteSync.isConfigured) ...[ const SizedBox(height: 8), _AdminSection( title: l10n.remoteActivity, children: [ _AdminTile( icon: Icons.sync, title: l10n.syncNow, subtitle: remoteSync.isSyncing ? l10n.syncing : remoteSync.lastSyncAt != null ? l10n.lastSync( remoteSync.lastSyncAt! .toLocal() .toString() .substring(0, 19), ) : l10n.notSyncedYet, trailing: remoteSync.isSyncing ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : null, onTap: () => remoteSync.syncNow(), ), ], ), if (remoteSync.activity.isNotEmpty) _AdminSection( title: l10n.remoteActivityHistory, children: remoteSync.activity.map((entry) { return ListTile( dense: true, leading: Icon(switch (entry.result) { CommandResult.completed => Icons.check_circle_outline, CommandResult.conflict => Icons.warning_amber_outlined, CommandResult.failed || CommandResult.expired => Icons.error_outline, }, size: 20), title: Text( '${entry.commandType} (${entry.commandId.substring(0, 8)}...)', style: const TextStyle(fontSize: 13), ), subtitle: Text( '${entry.processedAt.toLocal()} - ${entry.result.name}', style: const TextStyle(fontSize: 11), ), ); }).toList(), ), ], ], ), ), ); } } class _AdminSection extends StatelessWidget { final String title; final List children; const _AdminSection({required this.title, required this.children}); @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; return Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title.toUpperCase(), style: TextStyle( fontSize: 12, fontWeight: FontWeight.w700, letterSpacing: 0.5, color: scheme.onSurface.withValues(alpha: 0.5), ), ), const SizedBox(height: 8), Container( decoration: BoxDecoration( color: scheme.onSurface.withValues(alpha: 0.04), borderRadius: BorderRadius.circular(14), border: Border.all( color: scheme.onSurface.withValues(alpha: 0.06), ), ), clipBehavior: Clip.antiAlias, child: Column(children: children), ), ], ), ); } } class _AdminTile extends StatelessWidget { final IconData icon; final String title; final String? subtitle; final Widget? trailing; final VoidCallback? onTap; const _AdminTile({ required this.icon, required this.title, this.subtitle, this.trailing, required this.onTap, }); @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; return Material( color: Colors.transparent, child: InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( children: [ Icon( icon, size: 22, color: scheme.onSurface.withValues(alpha: 0.7), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontWeight: FontWeight.w600, color: scheme.onSurface, ), ), if (subtitle != null) Text( subtitle!, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: scheme.onSurface.withValues(alpha: 0.5), ), ), ], ), ), if (trailing != null) ...[const SizedBox(width: 8), trailing!], if (onTap != null) Icon( Icons.chevron_right_rounded, color: scheme.onSurface.withValues(alpha: 0.3), ), ], ), ), ), ); } }