Files
tatlock-ui/lib/shared/layouts/app_scaffold.dart
T
Jeroen SchweitzerandClaude a5a8e7ba88 feat: add Security room with Users and Groups sections
- Add Security room accessible from main navigation
- Implement Users list page with DataGrid (fetches from /auth/users)
- Implement Groups list page with DataGrid (fetches from /auth/groups)
- Move user/group management from Control Room to dedicated Security room
- Remove Authentik section from Control Room navigation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-01 22:55:37 +01:00

75 lines
2.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:tatlock_ui/features/control_room/router.dart';
import 'package:tatlock_ui/features/security/router.dart';
import 'package:tatlock_ui/routing/app_router.dart';
import 'package:tatlock_ui/shared/layouts/widgets/top_header_bar.dart';
/// Main application scaffold with top header navigation.
///
/// Layout structure per UI_LAYOUT.md:
/// ```
/// ┌─────────────────────────────────────────────────────────────────┐
/// │ HEADER: [Logo] [Room Tabs] [Profile] │
/// ├─────────────────────────────────────────────────────────────────┤
/// │ BODY: Room page content (may include room-specific sidebar) │
/// └─────────────────────────────────────────────────────────────────┘
/// ```
class AppScaffold extends StatelessWidget {
const AppScaffold({super.key, required this.child});
final Widget child;
// Header height must match TopHeaderBar._headerHeight
static const double _headerHeight = 56.0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// Main content area with top padding for header
Positioned.fill(
child: Padding(
padding: const EdgeInsets.only(top: _headerHeight),
child: child,
),
),
// Top header overlays content (bulge extends into content area)
Positioned(
top: 0,
left: 0,
right: 0,
child: TopHeaderBar(
selectedIndex: _selectedIndex(context),
onRoomSelected: (index) => _onNavSelected(context, index),
),
),
],
),
);
}
int _selectedIndex(BuildContext context) {
final location = GoRouterState.of(context).matchedLocation;
if (location.startsWith(ControlRoomRoutes.base)) return 1;
if (location.startsWith(SecurityRoutes.base)) return 2;
if (location.startsWith(AppRoutes.parlor)) return 3;
// Settings is no longer in main nav (accessed via Profile dropdown)
return 0; // Front Hall
}
void _onNavSelected(BuildContext context, int index) {
final route = switch (index) {
0 => AppRoutes.frontHall,
1 => ControlRoomRoutes.containers,
2 => SecurityRoutes.users,
3 => AppRoutes.parlor,
_ => AppRoutes.frontHall,
};
context.go(route);
}
}