Files
verdande/web/src/routes/+page.svelte
T
Kristian WindandClaude Opus 5 ab5e3e35a6 fix(i18n): three Danish strings that had no æ, ø or å in them
Spotted on the running instance: the version line read "Running v0.11.0. Der
bliver ikke set efter opdateringer." — half translated, on one line.

The sweep that was supposed to catch these grepped for Danish letters, and none of
the three survivors contain one: "Ingen nyere version fundet.", "Ikke mere i dag."
and a token hint split across two source lines so neither half looked like prose.

So there is a test for it now. It reads the templates and looks for the words that
give Danish away whatever letters they are spelled with — a word list rather than a
language detector, because this ships as one static binary and thirty common words
catch the sentences a person actually writes. It fails on the exact three that got
through.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 10:50:25 +02:00

140 lines
3.0 KiB
Svelte

<script>
/** I dag: what is overdue, and what is due today. */
import { api } from '$lib/api.js';
import { app } from '$lib/stores.svelte.js';
import TaskRow from '$lib/components/TaskRow.svelte';
import QuickAdd from '$lib/components/QuickAdd.svelte';
import { t, tag } from '$lib/i18n.svelte.js';
let loaded = $state(false);
// The local date, not the server's: "today" is a question about where the
// person is standing.
const isoToday = (() => {
const now = new Date();
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(
now.getDate()
).padStart(2, '0')}`;
})();
async function load() {
const data = await api.today();
app.tasks = [...data.overdue, ...data.today];
loaded = true;
}
$effect(() => {
load();
});
// Derived from the store by *date*, not from the ids that came back with the
// first request. Filtering against a captured list would mean a task added
// through quick add — which is pushed into the store, not into that list —
// never appeared until a reload, which is exactly the moment the interface has
// to feel immediate.
let liveOverdue = $derived(
app.tasks.filter((t) => !t.completed && t.due_date && t.due_date < isoToday)
);
let liveToday = $derived(app.tasks.filter((t) => !t.completed && t.due_date === isoToday));
let date = $derived(
new Date().toLocaleDateString(tag(), {
weekday: 'long',
day: 'numeric',
month: 'long'
})
);
</script>
<div class="view">
<header>
<h1>{t('nav.today')}</h1>
<p>{date}</p>
</header>
<QuickAdd />
{#if liveOverdue.length}
<section>
<h2 class="overdue">Forsinket</h2>
{#each liveOverdue as task (task.id)}
<TaskRow {task} />
{/each}
</section>
{/if}
<section>
{#if liveOverdue.length}
<h2>I dag</h2>
{/if}
{#each liveToday as task (task.id)}
<TaskRow {task} />
{/each}
{#if loaded && !liveToday.length && !liveOverdue.length}
<!-- An empty Today is the goal, not a failure state. It should feel
like finishing, not like something is missing. -->
<p class="clear">
<span class="rune" aria-hidden="true">ᚹ</span>
{t('view.nothingMoreToday')}
</p>
{/if}
</section>
</div>
<style>
.view {
max-width: var(--content-max);
margin: 0 auto;
padding: var(--s6) var(--s4) var(--s8);
}
header {
margin-bottom: var(--s5);
}
h1 {
font-size: var(--text-2xl);
}
header p {
margin: var(--s1) 0 0;
color: var(--ink-faint);
font-size: var(--text-sm);
text-transform: capitalize;
}
section {
margin-top: var(--s5);
}
h2 {
font-size: var(--text-xs);
font-weight: 560;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--ink-faint);
padding: 0 var(--s2) var(--s2);
}
h2.overdue {
color: var(--p1);
}
.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>