Email-code sign-in for admins only; Overview with a needs-attention list; Songs with search, playback, editing, approve/hide and bulk edit; Albums and Singers with merge; creator applications queue. Adds the admin views, functions and creator_applications table, a deploy-admin script and a how-to guide. The admin nginx config no longer caches unhashed JS and sends its security headers again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
2.0 KiB
Dart
55 lines
2.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
|
|
import 'api.dart';
|
|
import 'screens/albums_screen.dart';
|
|
import 'screens/applications_screen.dart';
|
|
import 'screens/overview_screen.dart';
|
|
import 'screens/sign_in_screen.dart';
|
|
import 'screens/singers_screen.dart';
|
|
import 'screens/songs_screen.dart';
|
|
import 'session.dart';
|
|
import 'shell.dart';
|
|
|
|
final adminRouter = GoRouter(
|
|
refreshListenable: AdminSession.instance,
|
|
redirect: (context, state) {
|
|
final session = AdminSession.instance.state;
|
|
final atSignIn = state.matchedLocation == '/sign-in';
|
|
if (session == SessionState.checking) return null;
|
|
if (session == SessionState.signedOut) {
|
|
return atSignIn ? null : '/sign-in?from=${Uri.encodeComponent(state.uri.toString())}';
|
|
}
|
|
if (atSignIn) return state.uri.queryParameters['from'] ?? '/';
|
|
return null;
|
|
},
|
|
routes: [
|
|
GoRoute(path: '/sign-in', builder: (context, state) => const SignInScreen()),
|
|
ShellRoute(
|
|
builder: (context, state, child) => AdminShell(location: state.matchedLocation, child: child),
|
|
routes: [
|
|
GoRoute(path: '/', pageBuilder: (context, state) => _page(const OverviewScreen())),
|
|
GoRoute(
|
|
path: '/songs',
|
|
pageBuilder: (context, state) {
|
|
final p = state.uri.queryParameters;
|
|
return _page(SongsScreen(
|
|
key: ValueKey(state.uri.toString()),
|
|
filter: SongFilter.fromId(p['show']),
|
|
query: p['q'] ?? '',
|
|
albumId: p['album'],
|
|
artistId: p['singer'],
|
|
filterName: p['name'],
|
|
));
|
|
},
|
|
),
|
|
GoRoute(path: '/albums', pageBuilder: (context, state) => _page(const AlbumsScreen())),
|
|
GoRoute(path: '/singers', pageBuilder: (context, state) => _page(const SingersScreen())),
|
|
GoRoute(path: '/applications', pageBuilder: (context, state) => _page(const ApplicationsScreen())),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
|
|
Page<void> _page(Widget child) => NoTransitionPage(child: child);
|