Oppdater dokumentasjon: kalender implementert, Lag 2 status

- ARCHITECTURE.md: Kalender markert [~] i Lag 2
- docs/features/kalender.md: Oppdatert med implementert status, API-docs, tidssone-håndtering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
vegard 2026-03-15 02:59:09 +01:00
parent d924645dd3
commit 7397c8ad93
2 changed files with 74 additions and 109 deletions

View file

@ -184,6 +184,7 @@ Chat (channels), Kanban, Whiteboard, Live transkripsjon, Live AI (faktoid + refe
- [ ] Kunnskapsgraf CRUD (SvelteKit server-side) - [ ] Kunnskapsgraf CRUD (SvelteKit server-side)
- [~] Chat med channels (PG-adapter + SpacetimeDB hybrid-adapter ferdig, sync-worker gjenstår) - [~] Chat med channels (PG-adapter + SpacetimeDB hybrid-adapter ferdig, sync-worker gjenstår)
- [~] Kanban (PG-adapter ferdig med drag & drop, redigeringsmodal, CRUD API. SpacetimeDB-sync gjenstår) - [~] Kanban (PG-adapter ferdig med drag & drop, redigeringsmodal, CRUD API. SpacetimeDB-sync gjenstår)
- [~] Kalender (PG-adapter ferdig med månedsvisning, fargekoder, heldags/tidshendelser. SpacetimeDB-sync gjenstår)
- [ ] Lydmeldinger & Diktering (opptak + Whisper + AI-opprydding) - [ ] Lydmeldinger & Diktering (opptak + Whisper + AI-opprydding)
- [ ] Prompt-Laboratorium (prompt-testing mot egne data) - [ ] Prompt-Laboratorium (prompt-testing mot egne data)
- [ ] Promptfoo testsett for første jobbtyper (norsk testdata) - [ ] Promptfoo testsett for første jobbtyper (norsk testdata)

View file

@ -1,134 +1,98 @@
# Kalender # Feature: Kalender
**Filsti:** `docs/features/kalender.md`
## Oversikt ## 1. Konsept
Kalender er en workspace-blokk for redaksjonell planlegging og tidslinjevisning. Hendelser er nodes i kunnskapsgrafen og kan kobles til episoder, temaer, aktører og channels. Kalendere kan fritt abonnere på hverandre — på tvers av brukere og workspaces. Månedsbasert kalendervisning for redaksjonell planlegging. Hendelser er nodes i kunnskapsgrafen og kan kobles til episoder, temaer, aktører og kanban-kort. Komplementerer Kanban ("hva" vs "når").
## Konsept ## 2. Status
**PG-adapter ferdig og deployet (mars 2025).** Abonnement, ICS-eksport og SpacetimeDB-sync gjenstår.
### Kalendere som grafnoder ### Implementert
Hver kalender er en node (`node_type: 'calendar'`). Hendelser er også nodes (`node_type: 'event'`) som tilhører en kalender via en `PART_OF`-relasjon. Dette gir: - Migrering `0003_calendar.sql`: `calendars` + `calendar_events` (begge FK→nodes)
- Full workspace-isolasjon via eksisterende RLS - Hendelser er nodes — arver workspace-isolasjon automatisk
- Relasjoner mellom hendelser og resten av kunnskapsgrafen (episoder, aktører, temaer) - Heldagshendelser (`T12:00:00` for tidssone-trygghet) vs. tidshendelser med klokkeslett
- Channels knyttet til hendelser (diskusjonstråd per hendelse) - Fargekoder per hendelse (7 forhåndsdefinerte) + standard kalenderfarge
- REST API: GET med tidsvindu-filtrering, POST/PATCH/DELETE hendelser
- PG polling-adapter med 5 sek intervall
- CalendarBlock.svelte: månedsrutenett, navigering, opprett/rediger-modal, Escape-lukking
- `linked_node`-kolonne for fremtidig kobling til kanban-kort, episoder etc.
### Typer kalendere ### Gjenstår — Fase 2
- Kobling til kanban-kort (vis deadline på kalender)
- Uke- og dagsvisning
- Gjentakende hendelser (RRULE)
- Dra-og-slipp for å flytte hendelser mellom datoer
- Flerdag-hendelser (vises over flere celler)
- Abonnementsmodell (kalender → kalender via graph_edges)
- Personlige vs. workspace-kalendere
- ICS/CalDAV-eksport
- SpacetimeDB-modul + hybrid-adapter
- Varsler/påminnelser via jobbkøen
| Type | Eier | Synlighet | Eksempel | ## 3. Datamodell (implementert)
|------|------|-----------|----------|
| **Workspace** | Workspace (admin) | Alle medlemmer | "Sidelinja Produksjonskalender" |
| **Personlig** | Bruker | Kun eier (+ eksplisitt deling) | "Vegards arbeidskalender" |
| **Offentlig** | Workspace (admin) | Alle som abonnerer | "Sidelinja Publiseringsplan" |
### Abonnementsmodell
Kalendere kan abonnere på andre kalendere via en `SUBSCRIBES_TO`-edge i grafen. Abonnenten ser hendelser read-only — bare kildekalenderen kan redigere.
```
Vegards kalender ──SUBSCRIBES_TO──▶ Sidelinja Produksjon
Vegards kalender ──SUBSCRIBES_TO──▶ Sidelinja Publisering
Ekstern partner ──SUBSCRIBES_TO──▶ Sidelinja Publisering (offentlig)
```
**Prinsipp:** Enhver kalender kan abonnere på enhver annen kalender den har tilgang til. Tilgang styres av:
- Samme workspace → alltid tilgang til workspace-kalendere
- Cross-workspace → kun til kalendere merket "offentlig"
- Personlige → kun eier + eksplisitt inviterte
### Samlet visning
UI-et viser én samlet kalendervisning per bruker: alle hendelser fra egne + abonnerte kalendere, fargekodet per kilde. Brukeren kan toggle synlighet per kalender.
## Datamodell
### Nye node_types
Krever at `node_type` enum utvides med `'calendar'` og `'event'`.
### events-tabell (detalj for event-nodes)
```sql ```sql
CREATE TABLE events ( CREATE TABLE calendars (
id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
parent_id UUID NOT NULL REFERENCES nodes(id),
name TEXT NOT NULL,
color TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE calendar_events (
id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE, id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
calendar_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE, calendar_id UUID NOT NULL REFERENCES calendars(id) ON DELETE CASCADE,
title TEXT NOT NULL, title TEXT NOT NULL,
description TEXT, description TEXT,
starts_at TIMESTAMPTZ NOT NULL, starts_at TIMESTAMPTZ NOT NULL,
ends_at TIMESTAMPTZ, ends_at TIMESTAMPTZ,
all_day BOOLEAN NOT NULL DEFAULT false, all_day BOOLEAN NOT NULL DEFAULT false,
recurrence JSONB, -- iCal RRULE som JSON (valgfritt) color TEXT,
location TEXT, linked_node UUID REFERENCES nodes(id) ON DELETE SET NULL,
color TEXT, -- hex fargekode for UI created_by TEXT REFERENCES users(authentik_id),
created_by TEXT REFERENCES users(authentik_id) ON DELETE SET NULL created_at TIMESTAMPTZ NOT NULL DEFAULT now()
); );
``` ```
### calendars-tabell (detalj for calendar-nodes) Indekser: `(calendar_id)` og `(calendar_id, starts_at)` for effektiv tidsvindu-filtrering.
```sql
CREATE TABLE calendars (
id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
name TEXT NOT NULL,
color TEXT NOT NULL DEFAULT '#3b82f6',
visibility TEXT NOT NULL DEFAULT 'workspace'
CHECK (visibility IN ('workspace', 'personal', 'public')),
owner_user TEXT REFERENCES users(authentik_id) ON DELETE SET NULL
-- owner_user er NULL for workspace-kalendere (eid av workspace via nodes.workspace_id)
);
```
### Abonnementer via graph_edges ## 4. API-endepunkter
```sql
-- Kalender A abonnerer på Kalender B
INSERT INTO graph_edges (workspace_id, source_id, target_id, relation_type)
VALUES (:ws, :kalender_a, :kalender_b, 'SUBSCRIBES_TO');
```
Krever ny relasjonstype: | Metode | Sti | Beskrivelse |
```sql |---|---|---|
INSERT INTO relation_types (name, label, description, system) | GET | `/api/calendar/[calendarId]?from=...&to=...` | Hent kalender med hendelser i tidsvindu |
VALUES ('SUBSCRIBES_TO', 'Abonnerer på', 'Kalender følger en annen kalender', true); | POST | `/api/calendar/[calendarId]/events` | Opprett hendelse |
``` | PATCH | `/api/calendar/[calendarId]/events/[eventId]` | Oppdater hendelse |
| DELETE | `/api/calendar/[calendarId]/events/[eventId]` | Slett hendelse |
## ICS/CalDAV-eksport ## 5. Brukes av
### Read-only ICS-feed | Konsept | Bruk |
Hver kalender med `visibility = 'public'` (eller workspace-kalendere for innloggede) får en ICS-URL: |---|---|
``` | Redaksjonen | Innspillingsdatoer, publiseringsplan, deadlines |
https://sidelinja.org/cal/{workspace_slug}/{calendar_id}.ics | Foreningen Liberalistene | Styremøter, arrangementer |
``` | Møterommet (fremtidig) | Møteplanlegging |
SvelteKit genererer ICS-feeden server-side. Brukere kan legge denne inn i Google Calendar, Apple Calendar, Thunderbird osv. ## 6. Tidssone-håndtering
- Heldagshendelser lagres som `T12:00:00` (middag) — unngår datoforskyvning ved UTC-konvertering
- Tidshendelser konverteres til ISO via `new Date().toISOString()` (lokal → UTC)
- Visning bruker `new Date()` som konverterer tilbake til lokal tid
### Ingen import ## 7. Fremtidig: Abonnementsmodell
Sidelinja importerer ikke fra eksterne kalendere i Fase 1. Sidelinja er kilden til sannhet for sine egne hendelser. Eventuell CalDAV-synk (toveis) er en fremtidig utvidelse. Kalendere kan abonnere på andre kalendere via `SUBSCRIBES_TO`-edge i grafen. Abonnenten ser hendelser read-only.
## UI-komponenter | Type | Eier | Synlighet |
|------|------|-----------|
| Workspace | Workspace (admin) | Alle medlemmer |
| Personlig | Bruker | Kun eier + eksplisitt deling |
| Offentlig | Workspace | Alle som abonnerer |
### CalendarBlock (workspace-blokk) ## 8. Fremtidig: ICS-eksport
Integreres i det komponerbare side-systemet som blokk-type `calendar`: Offentlige kalendere får en ICS-URL (`/cal/{workspace_slug}/{calendar_id}.ics`) for Google Calendar, Apple Calendar etc.
- Månedsvisning (default), ukevisning, listevisning
- Fargekodet per kalender-kilde
- Klikk hendelse → åpne detalj med grafkoblinger og channel
- Dra for å opprette ny hendelse (desktop)
- Toggle synlighet per abonnert kalender i sidebar-filter
### Hendelsesdetalj ## 9. Instruks for Claude Code
- Tittel, tid, beskrivelse, lokasjon * Bruk `eventsForDate()` med lokal dato-konvertering, ikke UTC-substring
- Grafkoblinger: koble til episode, tema, aktør * Heldagshendelser: `T12:00:00`, aldri `T00:00:00` (tidssone-felle)
- Channel: diskusjonstråd (opprettes on-demand) * Alt er workspace-scopet via node-modellen
- Deltakere: workspace-medlemmer * Sjekk `docs/erfaringer/adapter_moenster.md` for hybrid-strategi
## Redaksjonell verdi
Kalenderen er ikke generisk møtebooking — den er **redaksjonell planlegging**:
- **Publiseringsdatoer**: Når skal Episode 43 ut? Synlig for hele redaksjonen
- **Innspillingsslots**: Bookede tider i Studioet, koblet til episode-node
- **Gjeste-intervjuer**: Hendelse koblet til aktør-node, med forberedelseskanal
- **Research-deadlines**: Koblet til tema-node
- **Sesongoversikt**: Alle episoder i en sesong som hendelser på tidslinje
## Bygger på
- Kunnskapsgraf (nodes, graph_edges)
- Workspace-modell og RLS
- Komponerbare sider (blokk-type `calendar`)
- Channels (diskusjon per hendelse)
## Åpne spørsmål
- **Recurring events**: RRULE er komplekst. Fase 1 kan starte uten recurrence og legge det til som utvidelse.
- **Tidssoner**: Alle tider lagres som TIMESTAMPTZ (UTC i PG). Frontend viser i brukerens lokale tidssone. Eksplisitt tidssone per hendelse trengs bare ved reise/eksterne gjester.
- **Varsler/påminnelser**: Kan kobles til jobbkøen — en `reminder`-jobb som poster i hendelsens channel X minutter før start. Utsettes til Fase 2.
- **Drag-and-drop i kalender**: Flytte/resize hendelser. Nyttig men ikke kritisk for Fase 1 — enkel klikk-for-å-redigere holder.