Files
tatlock-ui/lib/features/security/presentation/pages/security_page.dart
T
Jeroen SchweitzerandClaude Opus 4.5 c494ace5d8
Build and Push / release (push) Successful in 3s
Build and Push / build (push) Successful in 3m5s
feat: add URL deep-linking for DataGrids
- Add PageUrlState utility for URL ↔ state serialization
- Add column `id` field for unique column identification in URLs
- Update idSelector to return String for URL compatibility
- All DataGrid pages now support URL params: search, sort, order, id
- Browser URL updates via replaceState (no GoRouter rebuilds)
- Add FilterPanelSemantics for filter panel semantic IDs
- Add TESTING.md documentation

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 13:59:08 +01:00

119 lines
3.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:tatlock_ui/features/security/router.dart';
import 'package:tatlock_ui/features/security/users/users_list_page.dart';
import 'package:tatlock_ui/features/security/groups/groups_list_page.dart';
import 'package:tatlock_ui/shared/layouts/widgets/nav_panel.dart';
/// Main Security room page with nav panel and section content.
class SecurityPage extends ConsumerWidget {
const SecurityPage({
super.key,
this.nav = SecurityNav.users,
this.routerState,
});
/// The current nav item to display.
final SecurityNav nav;
/// Router state for URL deep-linking (query params).
final GoRouterState? routerState;
@override
Widget build(BuildContext context, WidgetRef ref) {
final colorScheme = Theme.of(context).colorScheme;
return Scaffold(
body: Row(
children: [
// Nav Panel - section navigation with grouping
NavPanel(
title: 'Sections',
icon: Icons.security_outlined,
items: SecurityNav.values.map((n) => n.toNavItem()).toList(),
selectedId: nav.id,
onItemSelected: (id) {
final newNav = SecurityNav.values.firstWhere(
(n) => n.id == id,
);
// Navigate to section URL
context.go(pathForSecurityNav(newNav));
},
),
// Divider
VerticalDivider(
width: 1,
thickness: 1,
color: colorScheme.outlineVariant,
),
// Section content
Expanded(
child: _SectionContent(nav: nav, routerState: routerState),
),
],
),
);
}
}
/// Renders content for the selected nav item.
class _SectionContent extends StatelessWidget {
const _SectionContent({required this.nav, this.routerState});
final SecurityNav nav;
final GoRouterState? routerState;
@override
Widget build(BuildContext context) {
switch (nav) {
case SecurityNav.users:
return UsersListPage(routerState: routerState);
case SecurityNav.groups:
return GroupsListPage(routerState: routerState);
default:
return _PlaceholderSection(nav: nav);
}
}
}
/// Placeholder for nav items not yet implemented.
class _PlaceholderSection extends StatelessWidget {
const _PlaceholderSection({required this.nav});
final SecurityNav nav;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
nav.icon,
size: 64,
color: colorScheme.outline,
),
const SizedBox(height: 16),
Text(
nav.label,
style: textTheme.headlineSmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 8),
Text(
'${nav.section} • Coming soon',
style: textTheme.bodyMedium?.copyWith(
color: colorScheme.outline,
),
),
],
),
);
}
}