# 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) ```sql 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) ```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 ```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: ```sql 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.