server/docs/features/kalender.md
vegard a5985ef3f8 Dokumentasjon, erfaringslogg, migrasjoner og infra-oppdateringer
- Omorganiser docs/: konsepter, features, infra og proposals i egne mapper
- Ny docs/erfaringer/ med lærdommer fra chat-implementering (Svelte 5, SpacetimeDB, adapter-mønster)
- Oppdater ARCHITECTURE.md: Lag 1 status, ny §10 Erfaringslogg, SpacetimeDB i lokal dev
- Oppdater synkronisering.md med implementeringsstatus og designvalg
- Oppdater lokal.md med SpacetimeDB og AI Gateway
- Utvid PG-skjema med channels, messages, media_files, message_revisions
- Legg til seed_dev.sql, migration_safety.md, .env.example
- Nye feature-specs: chat, kanban, whiteboard, live_ai, lydmeldinger m.fl.
- Nye konsept-specs: studioet, møterommet, redaksjonen, den asynkrone gjesten m.fl.
- SpacetimeDB og AI Gateway i docker-compose.dev.yml
- collect-docs.sh inkluderer erfaringer/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:40:14 +01:00

5.8 KiB

Kalender

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.

Konsept

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)

Typer kalendere

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"

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)

CREATE TABLE events (
    id            UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
    calendar_id   UUID NOT NULL REFERENCES nodes(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
);

calendars-tabell (detalj for calendar-nodes)

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

-- 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:

INSERT INTO relation_types (name, label, description, system)
VALUES ('SUBSCRIBES_TO', 'Abonnerer på', 'Kalender følger en annen kalender', true);

ICS/CalDAV-eksport

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

SvelteKit genererer ICS-feeden server-side. Brukere kan legge denne inn i Google Calendar, Apple Calendar, Thunderbird osv.

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.

UI-komponenter

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

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.