Files
paliad/internal/offices/offices.go
m 0cdc644b50 fix: audit medium items — Verlauf pagination, patholo→paliad rename, offices (t-paliad-018)
Three items from docs/improvement-audit.md §2:

I-5 Verlauf pagination
- AkteService.ListEvents now accepts a (before *uuid.UUID, limit int) cursor
- SQL uses a composite (created_at, id) cursor subquery — stable across
  rows written in the same microsecond
- Handler parses ?before=<uuid>&limit=<n>, service clamps to 200
- Frontend fetches first page (50) on init and exposes a "Mehr laden" /
  "Load more" button that keeps paging until the tail returns < page size
- i18n keys akten.detail.verlauf.loadMore / .loadingMore in DE + EN

I-8 patholo → paliad client-side rename with migrations
- i18n.ts: STORAGE_KEY is now paliad-lang; one-shot migration reads the
  old patholo-lang value, writes the new key, deletes the old
- sidebar.ts: same pattern for paliad-sidebar-pinned
- Cookie rename with dual-read grace period: SessionCookieName is
  paliad_session, LegacySessionCookieName keeps patholo_session as
  read-only fallback. Requests using the legacy cookie get upgraded to
  paliad_session in the response; legacy cookie is expired in the same
  response. ClearAuthCookies clears both names to prevent stale-cookie
  resurrection. Remove the legacy fallback after 2026-05-18 (30d cookie
  max age).
- handlers/links.go:extractEmailFromCookie reads either cookie name via
  auth.SessionCookieName / auth.LegacySessionCookieName

P-6 Single source of truth for offices
- New internal/offices package: Office struct + All + IsValid + Keys
- akte_service.go switched from inline isValidOffice to offices.IsValid
- GET /api/offices returns the list with DE + EN labels
- Akte create form (akten-neu.tsx) has an empty <select>; the client TS
  fetches /api/offices and populates options, re-rendering on lang change

Tests:
- internal/offices/offices_test.go covers IsValid + Keys + label coverage
- internal/auth: three new Middleware tests — legacy cookie still
  authenticates + upgrades the browser, new cookie wins when both are
  present (no clobber), missing cookie returns 401 on API paths

Build: go build ./... + go vet ./... + go test ./... + bun run build all clean.

Known out-of-scope: handlers/links.go still POSTs to public.patholo_link_*
via PostgREST; migration 011 created fresh paliad.link_* tables but the
handler refactor (move to direct DB, copy data, drop public tables) is a
separate phase documented in that migration's header.
2026-04-18 18:56:35 +02:00

43 lines
1.2 KiB
Go

// Package offices is the single source of truth for the HLC office list.
//
// The keys here must stay in sync with the CHECK constraint on
// paliad.users.office and paliad.akten.owning_office (migration 001).
package offices
// Office is a single HLC office with its i18n-ready labels.
type Office struct {
Key string `json:"key"`
LabelDE string `json:"label_de"`
LabelEN string `json:"label_en"`
}
// All offices in display order. Keep in sync with the DB CHECK constraint.
var All = []Office{
{Key: "munich", LabelDE: "München", LabelEN: "Munich"},
{Key: "duesseldorf", LabelDE: "Düsseldorf", LabelEN: "Düsseldorf"},
{Key: "hamburg", LabelDE: "Hamburg", LabelEN: "Hamburg"},
{Key: "amsterdam", LabelDE: "Amsterdam", LabelEN: "Amsterdam"},
{Key: "london", LabelDE: "London", LabelEN: "London"},
{Key: "paris", LabelDE: "Paris", LabelEN: "Paris"},
{Key: "milan", LabelDE: "Mailand", LabelEN: "Milan"},
}
// IsValid reports whether the given key names a known office.
func IsValid(key string) bool {
for _, o := range All {
if o.Key == key {
return true
}
}
return false
}
// Keys returns the office keys in display order.
func Keys() []string {
keys := make([]string, len(All))
for i, o := range All {
keys[i] = o.Key
}
return keys
}