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
@@ -39,8 +39,9 @@ double _stepForUnit(String unit) {
/// Formats a step value for display: whole numbers without decimal,
/// fractions with one decimal.
String _fmtStep(double step) =>
step == step.roundToDouble() ? step.toStringAsFixed(0) : step.toStringAsFixed(1);
String _fmtStep(double step) => step == step.roundToDouble()
? step.toStringAsFixed(0)
: step.toStringAsFixed(1);
/// A [ListTile] wrapped in a swipe-to-adjust widget.
///
@@ -122,7 +123,8 @@ class _SwipeableInventoryTileState
// Decrease: use consume endpoint so history is preserved.
// Guard against going below zero.
if (widget.item.quantity <= 0) return;
final consume = step > widget.item.quantity ? widget.item.quantity : step;
final consume =
step > widget.item.quantity ? widget.item.quantity : step;
await repo.consumeInventoryItem(
widget.item.id,
amountUsed: consume,
@@ -402,32 +404,44 @@ class _TrailingActions extends ConsumerWidget {
Tooltip(
message: 'Konsumera',
child: IconButton(
icon: const Icon(Icons.remove_circle_outline),
icon: const Icon(
Icons.remove_circle_outline,
semanticLabel: 'Konsumera',
),
onPressed: () => context.push('/inventory/${item.id}/consume'),
),
),
Tooltip(
message: 'Redigera',
child: IconButton(
icon: const Icon(Icons.edit_outlined),
icon: const Icon(
Icons.edit_outlined,
semanticLabel: 'Redigera',
),
onPressed: () => context.push('/inventory/${item.id}/edit'),
),
),
Tooltip(
message: 'Flytta till baslager',
child: IconButton(
icon: const Icon(Icons.storefront_outlined),
icon: const Icon(
Icons.storefront_outlined,
semanticLabel: 'Flytta till baslager',
),
onPressed: () async {
try {
final token = await ref.read(authStateProvider.future);
await ref.read(inventoryRepositoryProvider).moveInventoryItemToPantry(
await ref
.read(inventoryRepositoryProvider)
.moveInventoryItemToPantry(
item.id,
token: token,
);
ref.invalidate(inventoryProvider);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
buildCopyableErrorSnackBar(context, mapErrorToUserMessage(e, context)),
buildCopyableErrorSnackBar(
context, mapErrorToUserMessage(e, context)),
);
}
},
@@ -449,7 +463,11 @@ class _DeleteButton extends ConsumerWidget {
return Tooltip(
message: 'Ta bort',
child: IconButton(
icon: const Icon(Icons.delete_outline, color: Colors.red),
icon: const Icon(
Icons.delete_outline,
color: Colors.red,
semanticLabel: 'Ta bort',
),
onPressed: () async {
final confirmed = await showDialog<bool>(
context: context,
@@ -477,7 +495,8 @@ class _DeleteButton extends ConsumerWidget {
ref.invalidate(inventoryProvider);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
buildCopyableErrorSnackBar(context, mapErrorToUserMessage(e, context)),
buildCopyableErrorSnackBar(
context, mapErrorToUserMessage(e, context)),
);
}
}
@@ -486,4 +505,3 @@ class _DeleteButton extends ConsumerWidget {
);
}
}