feat(web): improve web build configuration and accessibility
- Add source maps and web renderer build arguments with defaults - Configure Caddy with CSP headers, cache policies, and service worker handling - Defer loading of import screen for performance optimization - Add semantic labels to icons for accessibility - Update web index.html with Swedish language, meta tags, and description - Add robots.txt and lighthouse configuration - Add new planning documents and archive entries
This commit is contained in:
@@ -21,22 +21,55 @@ import '../../features/inventory/presentation/consume_inventory_screen.dart';
|
||||
import '../../features/inventory/presentation/consumption_history_screen.dart';
|
||||
import '../../features/meal_plan/presentation/meal_plan_screen.dart';
|
||||
import '../../features/pantry/presentation/pantry_screen.dart';
|
||||
import '../../features/import/presentation/import_screen.dart';
|
||||
import '../../features/shopping_list/presentation/shopping_list_screen.dart';
|
||||
import '../../features/admin/presentation/admin_screen.dart';
|
||||
import '../../features/import/presentation/import_screen.dart'
|
||||
deferred as import_ui;
|
||||
import '../../features/shopping_list/presentation/shopping_list_screen.dart';
|
||||
import '../../features/admin/presentation/admin_screen.dart';
|
||||
|
||||
int? _shellBranchIndexForPath(String path) {
|
||||
if (path.startsWith('/recipes')) return 0;
|
||||
if (path.startsWith('/inventory')) return 1;
|
||||
if (path.startsWith('/matsedel')) return 2;
|
||||
if (path.startsWith('/baslager')) return 3;
|
||||
if (path.startsWith('/import')) return 4;
|
||||
if (path.startsWith('/inkopslista')) return 5;
|
||||
if (path.startsWith('/profile')) return 6;
|
||||
if (path.startsWith('/admin')) return 7;
|
||||
if (path.startsWith('/import')) return 4;
|
||||
if (path.startsWith('/inkopslista')) return 5;
|
||||
if (path.startsWith('/profile')) return 6;
|
||||
if (path.startsWith('/admin')) return 7;
|
||||
return null;
|
||||
}
|
||||
|
||||
class _DeferredRouteLoader extends StatelessWidget {
|
||||
const _DeferredRouteLoader({
|
||||
required this.loadLibrary,
|
||||
required this.builder,
|
||||
});
|
||||
|
||||
final Future<void> Function() loadLibrary;
|
||||
final WidgetBuilder builder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<void>(
|
||||
future: loadLibrary(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return const Scaffold(
|
||||
body: LoadingStateView(label: 'Laddar vy...'),
|
||||
);
|
||||
}
|
||||
if (snapshot.hasError) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Text('Kunde inte ladda sidan: ${snapshot.error}'),
|
||||
),
|
||||
);
|
||||
}
|
||||
return builder(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final appRouterProvider = Provider<GoRouter>((ref) {
|
||||
final authState = ref.watch(authStateProvider);
|
||||
|
||||
@@ -244,26 +277,29 @@ final appRouterProvider = Provider<GoRouter>((ref) {
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/import',
|
||||
builder: (context, state) => const ImportScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/inkopslista',
|
||||
builder: (context, state) => const ShoppingListScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/profile',
|
||||
StatefulShellBranch(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/import',
|
||||
builder: (context, state) => _DeferredRouteLoader(
|
||||
loadLibrary: import_ui.loadLibrary,
|
||||
builder: (_) => import_ui.ImportScreen(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/inkopslista',
|
||||
builder: (context, state) => const ShoppingListScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/profile',
|
||||
builder: (context, state) => const ProfileScreen(),
|
||||
),
|
||||
],
|
||||
@@ -273,7 +309,8 @@ final appRouterProvider = Provider<GoRouter>((ref) {
|
||||
GoRoute(
|
||||
path: '/admin',
|
||||
redirect: (context, state) {
|
||||
final token = ref.read(authStateProvider)
|
||||
final token = ref
|
||||
.read(authStateProvider)
|
||||
.maybeWhen(data: (t) => t, orElse: () => null);
|
||||
return jwtIsAdmin(token) ? null : '/recipes';
|
||||
},
|
||||
|
||||
@@ -181,9 +181,14 @@ class AppShell extends ConsumerWidget {
|
||||
tooltip: view.mode == RecipesViewMode.grid
|
||||
? 'Visa som lista'
|
||||
: 'Visa som grid',
|
||||
icon: Icon(view.mode == RecipesViewMode.grid
|
||||
? Icons.view_list
|
||||
: Icons.grid_view),
|
||||
icon: Icon(
|
||||
view.mode == RecipesViewMode.grid
|
||||
? Icons.view_list
|
||||
: Icons.grid_view,
|
||||
semanticLabel: view.mode == RecipesViewMode.grid
|
||||
? 'Visa som lista'
|
||||
: 'Visa som grid',
|
||||
),
|
||||
onPressed: () =>
|
||||
ref.read(recipesViewProvider.notifier).toggleMode(),
|
||||
),
|
||||
@@ -207,7 +212,10 @@ class AppShell extends ConsumerWidget {
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
tooltip: 'Profil och konto',
|
||||
icon: const Icon(Icons.account_circle_outlined),
|
||||
icon: const Icon(
|
||||
Icons.account_circle_outlined,
|
||||
semanticLabel: 'Profil och konto',
|
||||
),
|
||||
onSelected: (value) async {
|
||||
switch (value) {
|
||||
case 'profile':
|
||||
|
||||
Reference in New Issue
Block a user