tasq/lib/routing/app_router.dart

183 lines
5.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../providers/auth_provider.dart';
import '../providers/profile_provider.dart';
import '../providers/supabase_provider.dart';
import '../utils/lock_enforcer.dart';
import '../screens/auth/login_screen.dart';
import '../screens/auth/signup_screen.dart';
import '../screens/admin/offices_screen.dart';
import '../screens/admin/user_management_screen.dart';
import '../screens/dashboard/dashboard_screen.dart';
import '../screens/notifications/notifications_screen.dart';
import '../screens/profile/profile_screen.dart';
import '../screens/shared/under_development_screen.dart';
import '../screens/tasks/task_detail_screen.dart';
import '../screens/tasks/tasks_list_screen.dart';
import '../screens/tickets/ticket_detail_screen.dart';
import '../screens/tickets/tickets_list_screen.dart';
import '../screens/workforce/workforce_screen.dart';
import '../widgets/app_shell.dart';
import '../screens/teams/teams_screen.dart';
final appRouterProvider = Provider<GoRouter>((ref) {
final notifier = RouterNotifier(ref);
ref.onDispose(notifier.dispose);
return GoRouter(
initialLocation: '/dashboard',
refreshListenable: notifier,
redirect: (context, state) {
final authState = ref.read(authStateChangesProvider);
final session = authState.when(
data: (state) => state.session,
loading: () => ref.read(sessionProvider),
error: (_, __) => ref.read(sessionProvider),
);
final isAuthRoute =
state.fullPath == '/login' || state.fullPath == '/signup';
final isSignedIn = session != null;
final profileAsync = ref.read(currentProfileProvider);
final isAdminRoute = state.matchedLocation.startsWith('/settings');
final isAdmin = profileAsync.maybeWhen(
data: (profile) => profile?.role == 'admin',
orElse: () => false,
);
if (!isSignedIn && !isAuthRoute) {
return '/login';
}
if (isSignedIn && isAuthRoute) {
return '/dashboard';
}
if (isAdminRoute && !isAdmin) {
return '/tickets';
}
return null;
},
routes: [
GoRoute(path: '/login', builder: (context, state) => const LoginScreen()),
GoRoute(
path: '/signup',
builder: (context, state) => const SignUpScreen(),
),
ShellRoute(
builder: (context, state, child) => AppScaffold(child: child),
routes: [
GoRoute(
path: '/settings/teams',
builder: (context, state) => const TeamsScreen(),
),
GoRoute(
path: '/dashboard',
builder: (context, state) => const DashboardScreen(),
),
GoRoute(
path: '/tickets',
builder: (context, state) => const TicketsListScreen(),
routes: [
GoRoute(
path: ':id',
builder: (context, state) => TicketDetailScreen(
ticketId: state.pathParameters['id'] ?? '',
),
),
],
),
GoRoute(
path: '/tasks',
builder: (context, state) => const TasksListScreen(),
routes: [
GoRoute(
path: ':id',
builder: (context, state) =>
TaskDetailScreen(taskId: state.pathParameters['id'] ?? ''),
),
],
),
GoRoute(
path: '/events',
builder: (context, state) => const UnderDevelopmentScreen(
title: 'Events',
subtitle: 'Event monitoring is under development.',
icon: Icons.event,
),
),
GoRoute(
path: '/announcements',
builder: (context, state) => const UnderDevelopmentScreen(
title: 'Announcement',
subtitle: 'Operational broadcasts are coming soon.',
icon: Icons.campaign,
),
),
GoRoute(
path: '/workforce',
builder: (context, state) => const WorkforceScreen(),
),
GoRoute(
path: '/reports',
builder: (context, state) => const UnderDevelopmentScreen(
title: 'Reports',
subtitle: 'Reporting automation is under development.',
icon: Icons.analytics,
),
),
GoRoute(
path: '/settings/users',
builder: (context, state) => const UserManagementScreen(),
),
GoRoute(
path: '/settings/offices',
builder: (context, state) => const OfficesScreen(),
),
GoRoute(
path: '/notifications',
builder: (context, state) => const NotificationsScreen(),
),
GoRoute(
path: '/profile',
builder: (context, state) => const ProfileScreen(),
),
],
),
],
);
});
class RouterNotifier extends ChangeNotifier {
RouterNotifier(this.ref) {
_authSub = ref.listen(authStateChangesProvider, (previous, next) {
// Enforce auth-level ban when a session becomes available.
next.when(
data: (authState) {
final session = authState.session;
if (session != null) {
// Fire-and-forget enforcement (best-effort client-side sign-out)
enforceLockForCurrentUser(ref.read(supabaseClientProvider));
}
},
loading: () {},
error: (_, __) {},
);
notifyListeners();
});
_profileSub = ref.listen(currentProfileProvider, (previous, next) {
notifyListeners();
});
}
final Ref ref;
late final ProviderSubscription _authSub;
late final ProviderSubscription _profileSub;
@override
void dispose() {
_authSub.close();
_profileSub.close();
super.dispose();
}
}