synops/docs/features/kalender.md
vegard 5b3367e7e5 CalDAV-abonnement: periodisk polling av eksterne kalendere (oppgave 29.12)
Utvider synops-calendar CLI med --url for å hente ICS fra eksterne URLer
(Google Calendar, Outlook, etc). Ny calendar_poller i maskinrommet poller
samlingers calendar_subscriptions[] med konfigurerbart intervall, etter
samme mønster som feed_poller for RSS-feeds.

Endringer:
- synops-calendar: ny --url parameter + reqwest for HTTP-henting
- calendar_poller.rs: bakgrunnsloop som finner forfalne abonnementer
- calendar_poll jobbtype i dispatcher med CLI-dispatch til synops-calendar
- API: configure_calendar_subscription + remove_calendar_subscription
- Migrasjon 031: indeks + prioritetsregel for calendar_poll-jobber
2026-03-18 23:04:29 +00:00

5 KiB

Feature: Kalender

Filsti: docs/features/kalender.md

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").

2. Status

Kalendervisning implementert (mars 2026). Bruker scheduled-edges i stedet for separat calendar_events-tabell. CalDAV/ICS-abonnement implementert. ICS-eksport gjenstår.

Implementert

  • Fase 1 (v1, mars 2025): PG-adapter med calendars + calendar_events (legacy)
  • Fase 2 (v2, mars 2026): Edge-basert kalender med scheduled-edges
    • Rute: /calendar — månedsbasert rutenett
    • Hendelser er noder med scheduled-edge (metadata.at = ISO 8601 tidspunkt)
    • Heldagshendelser bruker T12:00:00-konvensjon (tidssone-trygg)
    • Tidsbaserte hendelser viser klokkeslett i rutenett
    • Drag-and-drop for å flytte hendelser mellom datoer
    • Inline-oppretting: klikk + på en dato, angi tittel og valgfritt klokkeslett
    • Fargekoding basert på node_kind (innhold, kommunikasjon, media, samling)
    • Månedsnavigering med «I dag»-knapp
    • Hendelsesliste under rutenett for gjeldende måned
    • Lenke fra mottak-siden med hendelsesteller
    • Tilgang via nodeVisibility (respekterer node_access-matrise)
    • Sanntidsoppdatering via WebSocket (PG LISTEN/NOTIFY)
  • ICS-import (oppgave 29.11): synops-calendar CLI som parser ICS-filer
    • Input: --file <ics> eller --url <url> + --collection-id <uuid>
    • Duplikatdeteksjon via metadata.ics_uid
    • Oppdatering ved re-import
  • CalDAV-abonnement (oppgave 29.12): Periodisk polling av eksterne kalendere
    • Abonnement lagres i metadata.calendar_subscriptions[] på samlingsnoder
    • calendar_poller i maskinrommet sjekker hvert 60. sekund
    • Enqueuer calendar_poll-jobber som spawner synops-calendar --url
    • API: POST /intentions/configure_calendar_subscription og remove_calendar_subscription
    • Støtter Google Calendar, Outlook, og andre ICS/CalDAV-URLer

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. delte kalendere (via samlings-noder)
  • ICS-eksport (offentlige kalendere → /cal/{id}.ics)
  • Varsler/påminnelser via jobbkøen

3. Datamodell (implementert)

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 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,
    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()
);

Indekser: (calendar_id) og (calendar_id, starts_at) for effektiv tidsvindu-filtrering.

4. API-endepunkter

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

5. Brukes av

Konsept Bruk
Redaksjonen Innspillingsdatoer, publiseringsplan, deadlines
Foreningen Liberalistene Styremøter, arrangementer
Møterommet (fremtidig) Møteplanlegging

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

7. Fremtidig: Abonnementsmodell

Kalendere kan abonnere på andre kalendere via SUBSCRIBES_TO-edge i grafen. Abonnenten ser hendelser read-only.

Type Eier Synlighet
Samlings-node Admin for samlings-noden Alle med tilgang via node_access
Personlig Bruker Kun eier + eksplisitt deling
Offentlig Samlings-node Alle som abonnerer

8. Fremtidig: ICS-eksport

Offentlige kalendere får en ICS-URL (/cal/{calendar_id}.ics) for Google Calendar, Apple Calendar etc.

9. Instruks for Claude Code

  • Bruk eventsForDate() med lokal dato-konvertering, ikke UTC-substring
  • Heldagshendelser: T12:00:00, aldri T00:00:00 (tidssone-felle)
  • Tilgang styres via node_access-matrisen
  • Sjekk docs/erfaringer/adapter_moenster.md for hybrid-strategi