feat(web): board and calendar views, saved filters and label pages
The board is one column per section with native drag-and-drop, which works with a trackpad, a mouse and a screen reader without any of them being reimplemented. Dropping is optimistic: a card that snaps back for 200ms reads as a failed drop. Weeks in the calendar start on Monday. That is ISO 8601 and what every Danish calendar does; a Sunday-first grid makes a Dane misread the month at a glance. On a phone the chips give way to coloured bars — at that width the month's shape is the useful part, not the titles. Label pages run the filter language rather than a bespoke endpoint, so a label page and a saved filter can never disagree about what a label selects. fix(recurrence): "hver mandag", not "hver uge mandag" A weekly rule that names its days does not also need the word for the interval. That is how the phrase is said, and it is what the parser accepts back — the round trip through Describe now holds for weekday rules too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7e4c730049
commit
0ee72d399c
@@ -24,6 +24,14 @@
|
||||
location.href = '/';
|
||||
}
|
||||
|
||||
let filters = $state([]);
|
||||
let labels = $state([]);
|
||||
|
||||
$effect(() => {
|
||||
api.listFilters().then((r) => (filters = r.filters)).catch(() => {});
|
||||
api.listLabels().then((r) => (labels = r.labels)).catch(() => {});
|
||||
});
|
||||
|
||||
let shared = $derived(app.projects.filter((p) => !p.is_inbox && p.shared));
|
||||
let own = $derived(app.projects.filter((p) => !p.is_inbox && !p.shared));
|
||||
let current = $derived($page.url.pathname);
|
||||
@@ -112,6 +120,39 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if filters.length}
|
||||
<div class="group">
|
||||
<div class="group-head"><h2>Filtre</h2></div>
|
||||
{#each filters as filter (filter.id)}
|
||||
<a
|
||||
href="/filter/{filter.id}"
|
||||
class:active={current === `/filter/${filter.id}`}
|
||||
onclick={onnavigate}
|
||||
>
|
||||
<span class="dot" aria-hidden="true"></span>
|
||||
{filter.name}
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if labels.length}
|
||||
<div class="group">
|
||||
<div class="group-head"><h2>Etiketter</h2></div>
|
||||
{#each labels.filter((l) => l.task_count > 0) as label (label.id)}
|
||||
<a
|
||||
href="/etiket/{encodeURIComponent(label.name)}"
|
||||
class:active={current === `/etiket/${encodeURIComponent(label.name)}`}
|
||||
onclick={onnavigate}
|
||||
>
|
||||
<span class="dot" aria-hidden="true"></span>
|
||||
{label.name}
|
||||
<span class="count">{label.task_count}</span>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="foot">
|
||||
<!-- A quiet indicator, not an alarm: losing the socket for a moment is
|
||||
normal, and the app keeps working without it. -->
|
||||
|
||||
Reference in New Issue
Block a user