netops/netops-todo-node/CLAUDE.md
Jon Vanvik be166034de Legg til dagsplan + kommentar ved statusbytte
Dagsplan: tredje visning ved siden av tavle/tabell. To-kolonne planlegger
der man plukker oppgaver fra backlog og bygger en ordnet plan for dagen
(drag-sortering, kapasitet i timer, datovelger). Per bruker, per dato,
lagret i plans.json; synkes mellom egne faner via plan-WS-melding.

Kommentar ved statusbytte: når en oppgave markeres ferdig/blokkert (knapp,
kanban-drag eller modal) tilbys en valgfri kommentar som festes til oppgaven
(kind:status) og legges i aktivitetsloggen — atomisk i ett update-kall.

20 integrasjonstester (8 nye), alle grønne. README + CLAUDE.md oppdatert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-15 13:18:33 +02:00

163 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# NetOps To-Do (Node) — Handoff / Project context
> Egnet som `CLAUDE.md` for fremtidige Claude-sesjoner. Dette er
> videreutviklingen av PHP-versjonen i `../netops-todo-server/` — samme formål
> og datamodell, men ombygd til Node/npm med sanntidssynk og flere
> samarbeidsfunksjoner. PHP-versjonen ligger urørt ved siden av.
## Hva er dette
To-do-applikasjon for en network engineer / et lite driftsteam. Kategorier
L1L4, BGP, peering, security, NMS, automatisering, dokumentasjon, incident,
prosjekt. Hver oppgave har prioritet (P1P4), status, estimat, lokasjon, frist,
tags, eier, kommentarer og audit-felter.
Nytt mot PHP-versjonen:
- **WebSocket-sanntidssynk** (erstatter 60s-polling) + presence + "redigerer nå"
- **Kanban-tavle** med drag & drop, i tillegg til tabellvisning
- **Dagsplan** (tredje visning) — per bruker, per dato; ordnet liste man bygger
ved å plukke fra backlog. Drag-sortering, kapasitet i timer, synk mellom egne faner.
- **Kommentar ved statusbytte** — valgfri `comment``PUT /api/tasks/:id`
festes som kommentar med `kind:'status'` og legges i aktivitets-detaljen.
- **Kommentarer** per oppgave
- **Aktivitetslogg** (hvem gjorde hva, server-side, capped til 400 entries)
- **Optimistisk låsing** — `v`-felt per oppgave, 409 ved konflikt
- **Hurtig-registrering** med token-parsing (`#kat @eier p1 !frist ~timer loc:sted`)
- **Angre sletting**, tastatursnarveier, persistente filtre (localStorage)
## Filer
```
netops-todo-node/
├── server.js Express + ws. REST-API, WebSocket-hub, auth-oppslag.
├── lib/store.js Datalager: normalisering, validering, aktivitetslogg,
│ atomisk JSON-persistens. Kilden til sannhet for skjema.
├── public/
│ ├── index.html Markup. Ingen templating — app.js fyller alt.
│ ├── app.js All frontend-logikk (vanilla JS, ingen rammeverk).
│ └── style.css Mørkt tema, samme visuelle identitet som PHP-versjonen.
├── seed.json 16 eksempel-oppgaver.
├── setup-debian13.sh Engangs-oppsett på Debian 13 LXC (apt + npm ci + systemd).
├── test/api.test.js Integrasjonstester (node:test) — REST + WS ende-til-ende.
├── data/ Opprettes ved kjøring: tasks.json, activity.json,
│ users.json, plans.json.
└── README.md Installasjon, proxy-oppsett, API-referanse.
```
## Arkitektur
- **Alle mutasjoner via REST** (lett å curl-e/feilsøke). WebSocket (`/ws`)
brukes kun til push: endringer, presence, editing-signaler.
- Én node-prosess eier datakatalogen. Lagring i minnet, skriv til disk er
atomisk (tmp + rename) og serialisert gjennom en promise-kjede i `store.js`.
- Auth ligger utenfor appen: `X-Remote-User`-header > Basic Auth > cookie
(`netops_user`, kan skrus av med `DISABLE_COOKIE_AUTH=1`) > `anon`.
Samme oppslag for HTTP og WS-upgrade (`userFromRequest` i server.js).
### WebSocket-protokoll (server → klient)
| type | Innhold | Når |
|---|---|---|
| `hello` | `user`, `online[]`, `editing{}` | Ved tilkobling |
| `sync` | `tasks[]`, `activity[]`, evt. `by` | Ved tilkobling + etter replace/seed |
| `task` | `op: create/update/delete`, `task`/`id`, `by`, `activity` | Etter hver mutasjon |
| `presence` | `online[]` | Når noen kobler til/fra |
| `editing` | `editing{}` = `{taskId: [brukere]}` | Når noen åpner/lukker modal |
| `plan` | `date`, `items[]` | Etter `PUT /api/plan` — sendes KUN til samme brukers faner (`sendToUser`) |
Klient → server: kun `{ type: 'editing', id, active }`.
### Task-skjema (kanonisk form — `normalizeTask()` i lib/store.js)
Som PHP-versjonen, pluss:
```json
{
"v": 3,
"comments": [{ "id": "c_…", "by": "jon", "at": "ISO", "text": "≤1000",
"kind": "status", "status": "done" }]
}
```
`v` bumpes ved hver felt-oppdatering (også når en `comment` følger med et
statusbytte via `update()`, siden det ER en oppdatering — men IKKE via det
separate `POST .../comments`-endepunktet, så en ren kommentar ikke gir falsk
konflikt for noen som redigerer felter samtidig). `kind`/`status` settes kun på
kommentarer som kommer fra et statusbytte; vanlige kommentarer mangler dem.
Klienten sender `v` den redigerte fra; mismatch → 409 med serverens task i
svaret. Frontend håndterer det i `handleSaveError()`: oppdaterer visningen,
viser varsel, og lar brukeren lagre på nytt (som da overskriver bevisst).
### Dagsplan-skjema (`plans.json`)
```json
{ "jon": { "2026-06-15": ["t_abc", "t_def"] } }
```
Per bruker, per dato, ordnet liste av task-ids. `getPlan()` filtrerer bort
slettede oppgaver ved lesing; `setPlan()` deduperer og validerer mot
eksisterende oppgaver; `_scrubFromPlans()` rydder ved sletting; `_prunePlans()`
dropper datoer eldre enn 30 dager ved oppstart. Planen er bevisst privat (kun
`owner`-attribusjon på selve oppgavene er delt), men synkes mellom samme
brukers faner via `plan`-WS-meldingen.
## Designvalg verdt å vite
- **Ingen database, ingen frontend-rammeverk, to npm-avhengigheter**
(express, ws). Samme "lett å patche for neste vakthavende"-filosofi som før.
- **Broadcast går til alle, inkludert avsender.** Klienten upserter idempotent
på id, så dobbel oppdatering er harmløs. REST-svaret brukes også, slik at
UI-et fungerer selv om WS er nede (da poller frontend hvert 30s).
- **Delvis update er lov:** `PUT /api/tasks/:id` med bare `{v, status}`
normalisering merger mot eksisterende. Quick-actions bruker dette.
- **Statuskommentar går gjennom samme update-kall** (transient `comment`-felt),
ikke et eget endepunkt — så statusbytte + kommentar + aktivitetslinje blir
atomisk og ett enkelt WS-broadcast. `changeStatusWithComment()` i app.js viser
dialogen for ferdig/blokkert; tavle-drag og tabell-✓ ruter gjennom den.
- **Dagsplanen er optimistisk i frontend:** `addToPlan`/`removeFromPlan`/drag
muterer `state.plan.items` og kaller `savePlan()` (PUT) som reconciler mot
serverens validerte svar. Container-nivå drag-lyttere bindes én gang
(`bindPlanContainer`); per-element bindes ved hver render (`bindPlanDnD`).
- **Kommentar-sletting er begrenset til egen bruker** (eneste
autorisasjonsregel i appen — alt annet er åpent for alle innloggede).
- Chart.js fra CDN; frontend degraderer pent (skjuler grafer) uten nett.
## Sikkerhetsstatus
Som PHP-versjonen (XSS-escaping i alle render-paths, whitelist-enums,
strenglengde-klamping, brukernavn-regex `[A-Za-z0-9._@-]{1,64}`), pluss:
- `/api/login` avviser ugyldige navn i stedet for å sanitere stille.
- Cookie settes med `SameSite=Lax` — gir grunnleggende CSRF-vern for
cookie-auth-modus. Bak proxy-auth gjelder samme vurdering som før
(intern bruk OK; offentlig nett → legg på token).
- `express.json({ limit: '2mb' })` begrenser payload (import av store lister).
## Vanlige utvidelser
- **Nytt felt per oppgave:** input i modal (index.html) → `saveForm()` og
`parseQuickAdd()` om ønskelig (app.js) → `normalizeTask()` (store.js) →
`diffDetail()` for aktivitetsloggen → `renderCard()`/`renderTable()`.
- **Ny kategori:** `CATEGORIES`/`CAT_COLORS` i app.js + `VALID_CATEGORIES`
i store.js.
- **SQLite:** bytt persistens i `Store` (load/_persist) mot `node:sqlite`
resten av appen er uberørt siden alt går gjennom Store-metodene.
- **Varsler ved frist:** server har allerede deadline per task; en
`setInterval` i server.js som broadcaster `{type:'due', …}` + Notification
API i frontend er den naturlige veien.
## Testing-status
- `npm test`: 20 integrasjonstester (node:test), booter ekte server på
tilfeldig port med temp-datakatalog. Dekker normalisering, versjonskonflikt,
kommentar-autorisasjon, statuskommentar-på-update, dagsplan (CRUD, per-bruker,
validering, scrub ved sletting, dato-fallback), aktivitetslogg, WS-broadcast +
per-bruker plan-push, persistens på disk, cookie-auth. Alle grønne per
2026-06-15 (Node 25).
- Manuell røyk-test utført: `/healthz`, statiske filer, seed, state, plan
set/get med rekkefølge, ferdig-med-kommentar, og per-bruker-isolasjon av plan
verifisert ende-til-ende over HTTP.
- Ikke testet i nettleser med flere samtidige brukere — første ekte test bør
være to nettleservinduer mot `npm start`: flytt kort på tavla, bygg en
dagsplan og dra for å sortere, og marker ferdig for å se kommentar-dialogen.