feat(projects): et projekts kalender kan vises som en uge

Ugen fandtes på Kommende og på Kalender, men ikke dér hvor et projekt bor — og det
er dér, "hvornår sker de her fire ting" oftest bliver spurgt.

Inde i kalendervisningen frem for som en fjerde `view_mode`. `view_mode` er
projektets og delt med alle, der kan se det, mens uge eller måned er et spørgsmål
om hvor bred skærmen er, foran hvilken der sidder én person — samme grund som
sidebjælkens bredde ligger i localStorage af. Og en fjerde værdi ville kræve en
tabelombygning: `CHECK (view_mode IN ('list','board','calendar'))` kan SQLite ikke
ændre på plads.

**Og så fandt den en fejl, der har ligget og ventet.** `cursor` blev sat med
`span === 'week' ? … : …`, hvilket læser `span` i det øjeblik komponenten bliver
til og fanger den værdi for altid. Byggeriet har advaret om det lige så længe. Så
længe hver visning havde sin egen komponent, gjorde det ingen forskel — og det
gjorde det i samme sekund, en vælger begyndte at skifte `span` på den samme.
Markøren stod på den 1. i måneden, og et skift til uge landede på ugen omkring den
1.: "27. jul.–2. aug." for en person, der kiggede på august.

Rettet, med den ene undtagelse skrevet ned: en månedsvisnings markør *er* den
første i måneden, så et skift til uge, mens man står i den indeværende måned,
skal lande på den uge, i dag ligger i — ikke på ugen omkring den 1.

Og en anden fælde, jeg selv gik i undervejs: en underklasses felter sættes *efter*
basens konstruktør er kørt. Nøglen til localStorage som et felt på en underklasse
betød, at valget blev gemt ét sted og læst et andet — en uge, man havde valgt, var
en måned igen efter en genindlæsning. Nøglen er et argument nu; et argument har
ingen rækkefølge at tage fejl af.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kristian Wind
2026-08-20 21:45:38 +02:00
co-authored by Claude Opus 5
parent dcb71f4c14
commit 868588de10
4 changed files with 146 additions and 8 deletions
+47 -2
View File
@@ -3,7 +3,7 @@
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import { api, humanMessage } from '$lib/api.js';
import { app, completedView, projectName } from '$lib/stores.svelte.js';
import { app, completedView, projectName, projectCalendarView } from '$lib/stores.svelte.js';
import { COLORS, colorVar } from '$lib/colors.js';
import { eventName, eventDetail } from '$lib/events.js';
import { TASK, SECTION, startDrag, carries, dragged, accept } from '$lib/dnd.js';
@@ -786,7 +786,26 @@
onsectionadded={(section) => (sections = [...sections, section])}
/>
{:else if mode === 'calendar'}
<CalendarView projectId={project.id} />
<!-- Uge eller måned inde i kalendervisningen frem for som en fjerde
`view_mode`.
`view_mode` er projektets og delt med alle, der kan se det; uge eller
måned er et spørgsmål om, hvor bred skærmen er, foran hvilken der
sidder én person. Og en fjerde værdi ville kræve en tabelombygning:
`CHECK (view_mode IN ('list','board','calendar'))` kan SQLite ikke
ændre på plads. -->
<div class="span" role="group" aria-label={t('view.mode')}>
{#each [['week', t('view.week')], ['month', t('view.month')]] as [value, label]}
<button
class:active={projectCalendarView.mode === value}
onclick={() => projectCalendarView.set(value)}
aria-pressed={projectCalendarView.mode === value}>{label}</button
>
{/each}
</div>
<CalendarView
projectId={project.id}
span={projectCalendarView.mode === 'week' ? 'week' : 'month'}
/>
{:else}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<section
@@ -1263,6 +1282,32 @@
0 0 0 3px var(--ink);
}
/* Den samme lille vælger som visningerne ovenfor, men ude til højre: den hører
til kalenderen, ikke til siden, og at stille den ved siden af Liste/Board/
Kalender ville lade som om der er fem visninger. */
.span {
display: flex;
justify-content: flex-end;
gap: var(--s1);
margin-bottom: var(--s2);
}
.span button {
padding: 2px var(--s2);
border-radius: var(--radius-sm);
font-size: var(--text-xs);
color: var(--ink-faint);
}
.span button:hover {
color: var(--ink);
}
.span button.active {
background: var(--surface-raised);
color: var(--ink);
}
.views {
display: flex;
gap: 1px;