fix(web): the label route was written outside the routes tree, and the PWA promised icons that do not exist
Release / Build and publish image (push) Failing after 13m28s

Every label link in the sidebar was dead. The route file landed in
web/web/src/routes/ because the shell that created it was already inside
web/ — SvelteKit never saw it, so /etiket/<name> fell through to the
app shell and rendered nothing.

The manifest also listed icon-192.png and icon-512.png, and app.html an
apple-touch-icon, none of which were ever generated. A manifest pointing
at missing icons is worse than one listing only what exists, so the dead
references are gone and the SVG — which Chrome and Firefox install from
happily — is what remains.

docs: hand over the project

HANDOFF.md covers what cannot be read out of the code: why the name is
not skuld, the decisions that look strange until you know the reason,
what is tested against a real service and what is only tested against
its specification, the gaps in the order I would take them, and the
traps that cost the most time to find.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kristian Wind
2026-08-17 08:47:29 +02:00
co-authored by Claude Opus 5
parent a8e523b377
commit 2a3367bfe0
3 changed files with 6 additions and 4 deletions
+74
View File
@@ -0,0 +1,74 @@
<script>
/**
* Everything carrying one label.
*
* This runs the filter language rather than a bespoke endpoint: "@indkøb" is
* already exactly this query, and having one code path means a label page and
* a saved filter can never disagree about what a label selects.
*/
import { page } from '$app/stores';
import { api } from '$lib/api.js';
import { app } from '$lib/stores.svelte.js';
import TaskRow from '$lib/components/TaskRow.svelte';
let loaded = $state(false);
let name = $derived(decodeURIComponent($page.params.navn ?? ''));
$effect(() => {
const label = name;
if (!label) return;
loaded = false;
api
.previewFilter(`@${label}`)
.then((result) => {
app.tasks = result?.tasks ?? [];
})
.catch(() => {
app.tasks = [];
})
.finally(() => (loaded = true));
});
let open = $derived(app.tasks.filter((t) => !t.completed));
</script>
<div class="view">
<header><h1>@{name}</h1></header>
{#each open as task (task.id)}
<TaskRow {task} />
{/each}
{#if loaded && !open.length}
<p class="clear">
<span class="rune" aria-hidden="true">ᚹ</span>
Ingen åbne opgaver med denne etiket.
</p>
{/if}
</div>
<style>
.view {
max-width: var(--content-max);
margin: 0 auto;
padding: var(--s6) var(--s4) var(--s8);
}
h1 {
font-size: var(--text-2xl);
margin-bottom: var(--s5);
}
.clear {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--s3);
padding: var(--s8) var(--s4);
color: var(--ink-faint);
font-size: var(--text-sm);
}
.rune {
font-size: var(--text-2xl);
color: var(--accent);
opacity: 0.5;
}
</style>