import 'package:app/core/app/router.dart'; import 'package:app/core/features/feature_controller.dart'; import 'package:app/core/i18n/translations.g.dart'; import 'package:app/core/ui/glas_bottom_bar.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; class AppShell extends StatefulWidget { final Widget child; const AppShell({super.key, required this.child}); @override State createState() => _AppShellState(); } class _AppShellState extends State { static const double _breakpoint = 800; final FocusNode _contentFocus = FocusNode(debugLabel: 'contentFocus'); @override void dispose() { _contentFocus.dispose(); super.dispose(); } // --- NAV ITEMS ------------------------------------------------------------- // Desktop/Tablet drawer items List<({IconData icon, IconData? selectedIcon, String label, String route})> _getDesktopItems(BuildContext context) { final t = Translations.of(context); return [ ( icon: Icons.dashboard_outlined, selectedIcon: Icons.dashboard, label: t.app.navigationDashboard, route: AppRoute.home.path, ), ( icon: Icons.account_balance_wallet_outlined, selectedIcon: Icons.account_balance_wallet, label: t.app.navigationBudgets, // Haushaltsbereich inkl. Budget route: AppRoute.budget.path, ), ( icon: Icons.inventory_2_outlined, selectedIcon: Icons.inventory_2, label: t.app.navigationInventory, route: AppRoute.inventory.path, ), ( icon: Icons.bar_chart_outlined, selectedIcon: Icons.bar_chart, label: t.app.navigationReports, route: AppRoute.reports.path, ), ( icon: Icons.settings_outlined, selectedIcon: Icons.settings, label: t.app.navigationSettings, route: AppRoute.settings.path, ), ]; } // Mobile bottom bar items (4 Tabs) List<({IconData icon, String label, String route})> _getMobileTabs( BuildContext context, ) { final t = Translations.of(context); return [ ( icon: Icons.dashboard, label: t.app.navigationDashboard, route: AppRoute.home.path, ), ( icon: Icons.home, label: (t.app.navigationHousehold), route: AppRoute.budget.path, ), ( icon: Icons.inventory_2, label: t.app.navigationInventory, route: AppRoute.inventory.path, ), ( icon: Icons.directions_car, label: (t.app.navigationCar), route: AppRoute.car.path, ), ]; } int _indexForPath(String path, List items, String Function(T) routeOf) { for (var i = 0; i < items.length; i++) { if (path.startsWith(routeOf(items[i]))) return i; } return 0; } // Route→Feature-Guard bool _routeEnabled(String route, FeatureController fc) { if (route.startsWith(AppRoute.home.path)) return true; if (route.startsWith(AppRoute.settings.path)) return true; if (route.startsWith(AppRoute.budget.path)) return fc.hasHousehold; if (route.startsWith(AppRoute.inventory.path)) return fc.hasInventory; if (route.startsWith(AppRoute.car.path)) return fc.hasCar; if (route.startsWith(AppRoute.reports.path)) return fc.hasReports; // Default: sichtbar return true; } @override Widget build(BuildContext context) { final t = Translations.of(context); final width = MediaQuery.of(context).size.width; final isDesktop = width >= _breakpoint; final currentPath = GoRouterState.of(context).matchedLocation; // keep focus on right/content pane on wide layouts if (isDesktop) { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted && !_contentFocus.hasFocus) _contentFocus.requestFocus(); }); } final appBar = AppBar( // title: const _LogoHeader(), // Kein Burger-Button, da mobil ohne Drawer (BottomNav) und Desktop mit persistentem Drawer leading: null, actions: [ IconButton( tooltip: t.app.tooltipNotifications, onPressed: () {}, icon: const Icon(Icons.notifications_none), ), IconButton( tooltip: t.app.tooltipUserSettings, onPressed: () => context.push(AppRoute.settings.path), icon: const Icon(Icons.account_circle_outlined), ), ], ); if (!isDesktop) { // ------------------- MOBILE: Bottom Navigation ------------------- final fc = context.read(); // Wenn aktuelle Route deaktiviert ist, sanft nach Home umleiten WidgetsBinding.instance.addPostFrameCallback((_) { final p = GoRouterState.of(context).matchedLocation; if (!_routeEnabled(p, fc)) { if (mounted) context.go(AppRoute.home.path); } }); return Scaffold( appBar: appBar, // Let content flow under the floating bar for the glass effect extendBody: true, body: SafeArea(child: widget.child), bottomNavigationBar: AnimatedBuilder( animation: fc, builder: (context, _) { final baseTabs = _getMobileTabs(context); // Filter allowed tabs (your existing guard) var tabs = <({IconData icon, String label, String route})>[ for (final it in baseTabs) if (_routeEnabled(it.route, fc)) it, ]; // Fallback to ensure min. 2 items if (tabs.length < 2) { tabs = [ ( icon: Icons.dashboard, label: Translations.of(context).app.navigationDashboard, route: AppRoute.home.path, ), ( icon: Icons.settings, label: Translations.of(context).app.navigationSettings, route: AppRoute.settings.path, ), ]; } final currentPath = GoRouterState.of(context).matchedLocation; return GlassBottomBar( currentPath: currentPath, onSelect: (route) => context.go(route), items: [ for (final it in tabs) GlassBottomBarItem( icon: it.icon, label: it.label, route: it.route, ), ], ); }, ), ); } // ------------------- TABLET/DESKTOP: Persistent Drawer ------------------- final items = _getDesktopItems(context); final selected = _indexForPath(currentPath, items, (it) => it.route); return Scaffold( appBar: appBar, body: Row( children: [ // Persistent drawer area SizedBox( width: 300, child: _DesktopDrawer(items: items, selectedIndex: selected), ), const VerticalDivider(width: 1), // Content Expanded( child: SafeArea( child: Focus( focusNode: _contentFocus, autofocus: true, child: widget.child, ), ), ), ], ), ); } } class _DesktopDrawer extends StatelessWidget { final List< ({IconData icon, IconData? selectedIcon, String label, String route}) > items; final int selectedIndex; const _DesktopDrawer({required this.items, required this.selectedIndex}); @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; final fc = context.read(); bool routeEnabled(String route) { if (route.startsWith(AppRoute.home.path)) return true; if (route.startsWith(AppRoute.settings.path)) return true; if (route.startsWith(AppRoute.budget.path)) return fc.hasHousehold; if (route.startsWith(AppRoute.inventory.path)) return fc.hasInventory; if (route.startsWith(AppRoute.car.path)) return fc.hasCar; if (route.startsWith(AppRoute.reports.path)) return fc.hasReports; return true; } return AnimatedBuilder( animation: fc, builder: (context, _) { final visibleItems = [ for (final it in items) if (routeEnabled(it.route)) it, ]; final currentPath = GoRouterState.of(context).matchedLocation; int safeSelected = 0; for (var i = 0; i < visibleItems.length; i++) { if (currentPath.startsWith(visibleItems[i].route)) { safeSelected = i; break; } } return Material( elevation: 0, child: SafeArea( child: ListTileTheme( selectedColor: scheme.onSecondaryContainer, selectedTileColor: scheme.secondaryContainer, child: ListView( padding: const EdgeInsets.symmetric(vertical: 8), children: [ // const Divider(), for (var i = 0; i < visibleItems.length; i++) ListTile( selected: i == safeSelected, leading: Icon( i == safeSelected ? (visibleItems[i].selectedIcon ?? visibleItems[i].icon) : visibleItems[i].icon, ), title: Text(visibleItems[i].label), onTap: () => context.go(visibleItems[i].route), ), // Optional: Settings als fixer Eintrag, // falls du ihn unabhängig von items immer unten haben willst: // const Divider(), // ListTile( // leading: const Icon(Icons.settings_outlined), // title: Text(t.app.navigationSettings), // onTap: () => context.go(AppRoute.settings.path), // ), ], ), ), ), ); }, ); } } class _LogoHeader extends StatelessWidget { const _LogoHeader(); @override Widget build(BuildContext context) { return const Padding( padding: EdgeInsets.all(16), child: Row( children: [ Icon(Icons.account_balance_wallet, size: 28), SizedBox(width: 8), ], ), ); } }