feat(web): improve web build configuration and accessibility
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 14m6s
Test Suite / flutter-quality (push) Failing after 4m44s

- 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:
Nils-Johan Gynther
2026-05-23 18:04:27 +02:00
parent 30d27d6b8a
commit 69bcc3e342
16 changed files with 1847 additions and 301 deletions
+65 -28
View File
@@ -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';
},
+12 -4
View File
@@ -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':