From 7397c8ad935ddf3e0097d58b4873b0cc453793bb Mon Sep 17 00:00:00 2001 From: vegard Date: Sun, 15 Mar 2026 02:59:09 +0100 Subject: [PATCH] Oppdater dokumentasjon: kalender implementert, Lag 2 status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- ARCHITECTURE.md | 1 + docs/features/kalender.md | 182 +++++++++++++++----------------------- 2 files changed, 74 insertions(+), 109 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 29fdc85..1f4737f 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -184,6 +184,7 @@ Chat (channels), Kanban, Whiteboard, Live transkripsjon, Live AI (faktoid + refe - [ ] Kunnskapsgraf CRUD (SvelteKit server-side) - [~] 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) +- [~] Kalender (PG-adapter ferdig med månedsvisning, fargekoder, heldags/tidshendelser. SpacetimeDB-sync gjenstår) - [ ] Lydmeldinger & Diktering (opptak + Whisper + AI-opprydding) - [ ] Prompt-Laboratorium (prompt-testing mot egne data) - [ ] Promptfoo testsett for første jobbtyper (norsk testdata) diff --git a/docs/features/kalender.md b/docs/features/kalender.md index 937fc46..34df49d 100644 --- a/docs/features/kalender.md +++ b/docs/features/kalender.md @@ -1,134 +1,98 @@ -# Kalender +# Feature: Kalender +**Filsti:** `docs/features/kalender.md` -## Oversikt -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. +## 1. Konsept +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 -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: -- Full workspace-isolasjon via eksisterende RLS -- Relasjoner mellom hendelser og resten av kunnskapsgrafen (episoder, aktører, temaer) -- Channels knyttet til hendelser (diskusjonstråd per hendelse) +### Implementert +- Migrering `0003_calendar.sql`: `calendars` + `calendar_events` (begge FK→nodes) +- Hendelser er nodes — arver workspace-isolasjon automatisk +- Heldagshendelser (`T12:00:00` for tidssone-trygghet) vs. tidshendelser med klokkeslett +- 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 | -|------|------|-----------|----------| -| **Workspace** | Workspace (admin) | Alle medlemmer | "Sidelinja Produksjonskalender" | -| **Personlig** | Bruker | Kun eier (+ eksplisitt deling) | "Vegards arbeidskalender" | -| **Offentlig** | Workspace (admin) | Alle som abonnerer | "Sidelinja Publiseringsplan" | +## 3. Datamodell (implementert) -### 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 -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, - 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, description TEXT, starts_at TIMESTAMPTZ NOT NULL, ends_at TIMESTAMPTZ, all_day BOOLEAN NOT NULL DEFAULT false, - recurrence JSONB, -- iCal RRULE som JSON (valgfritt) - location TEXT, - color TEXT, -- hex fargekode for UI - created_by TEXT REFERENCES users(authentik_id) ON DELETE SET NULL + color TEXT, + linked_node UUID REFERENCES nodes(id) ON DELETE SET NULL, + created_by TEXT REFERENCES users(authentik_id), + created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ``` -### calendars-tabell (detalj for calendar-nodes) -```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) -); -``` +Indekser: `(calendar_id)` og `(calendar_id, starts_at)` for effektiv tidsvindu-filtrering. -### Abonnementer via graph_edges -```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'); -``` +## 4. API-endepunkter -Krever ny relasjonstype: -```sql -INSERT INTO relation_types (name, label, description, system) -VALUES ('SUBSCRIBES_TO', 'Abonnerer på', 'Kalender følger en annen kalender', true); -``` +| Metode | Sti | Beskrivelse | +|---|---|---| +| GET | `/api/calendar/[calendarId]?from=...&to=...` | Hent kalender med hendelser i tidsvindu | +| 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 -Hver kalender med `visibility = 'public'` (eller workspace-kalendere for innloggede) får en ICS-URL: -``` -https://sidelinja.org/cal/{workspace_slug}/{calendar_id}.ics -``` +| Konsept | Bruk | +|---|---| +| Redaksjonen | Innspillingsdatoer, publiseringsplan, deadlines | +| 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 -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. +## 7. Fremtidig: Abonnementsmodell +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) -Integreres i det komponerbare side-systemet som blokk-type `calendar`: -- 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 +## 8. Fremtidig: ICS-eksport +Offentlige kalendere får en ICS-URL (`/cal/{workspace_slug}/{calendar_id}.ics`) for Google Calendar, Apple Calendar etc. -### Hendelsesdetalj -- Tittel, tid, beskrivelse, lokasjon -- Grafkoblinger: koble til episode, tema, aktør -- Channel: diskusjonstråd (opprettes on-demand) -- Deltakere: workspace-medlemmer - -## 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. +## 9. Instruks for Claude Code +* Bruk `eventsForDate()` med lokal dato-konvertering, ikke UTC-substring +* Heldagshendelser: `T12:00:00`, aldri `T00:00:00` (tidssone-felle) +* Alt er workspace-scopet via node-modellen +* Sjekk `docs/erfaringer/adapter_moenster.md` for hybrid-strategi