Commit Graph

9 Commits

Author SHA1 Message Date
mAi
e56cb3b210 feat(checklists): t-paliad-225 Slice C frontend — Geteilte Vorlagen tab + outdated-template badge
m/paliad#61 Slice C frontend pass.

Discovery (Geteilte Vorlagen):
- New 4th tab on /checklists between "Meine Vorlagen" and "Vorhandene
  Instanzen". Filters the merged catalog response to authored entries
  not owned by the caller (firm-visible OR globally-promoted OR
  share-recipient). Tab state round-trips via ?tab=gallery.
- Regime filter pills (UPC / DE / EPA / OTHER) operate independently
  from the main Vorlagen tab.
- Cards show regime badge, item count, author line, visibility chip.
- Self-filter relies on /api/me email match — loadMe() fires once on
  page boot and is idempotent.

Versioning UI on /checklists/instances/{id}:
- "Vorlage aktualisiert" badge appears when the instance's
  template_version is known AND lags the live template version (only
  for authored templates; static templates never bump). Shows "v{from}
  → v{to}" delta.
- "Änderungen anzeigen" button opens a diff modal that compares the
  instance's template_snapshot against the live template body.
  Item-level grouping by (section title, item label). Surfaces added /
  removed / changed items with localised section labels. Empty state
  when only metadata changed.

i18n: 13 new keys per language (DE + EN) under
checklisten.tab.gallery, checklisten.gallery.*, checklisten.filter.other,
and checklisten.instance.{outdated,diff}.*. Total 2666 keys.

Build hygiene: bun run build clean; i18n scan clean. Go build/vet/test
+ TestBootSmoke ./cmd/server/ all green.
2026-05-20 15:50:38 +02:00
mAi
7a1fd81d23 feat(checklists): t-paliad-225 Slice A frontend — Meine Vorlagen + authoring wizard
m/paliad#61 Slice A frontend pass.

Pages:
- /checklists gets a third tab "Meine Vorlagen" between Vorlagen and
  Vorhandene Instanzen — lists owned authored templates with regime
  badge, visibility chip, Bearbeiten / Löschen actions, "Neue Vorlage"
  CTA. Tab state round-trips via ?tab=mine.
- /checklists/new and /checklists/{slug}/edit serve a shared bundle
  (checklists-author.html). Client reads location.pathname to decide
  create vs edit mode; edit mode prefills from /api/checklists/templates/mine.

Wizard:
- Metadata form (title, description, regime UPC/DE/EPA/OTHER, court,
  reference, deadline, language de/en, visibility private/firm).
- Repeating section + item editor — add/remove sections, add/remove
  items per section, label + optional note + optional rule per item.
- Single-language authoring (lang column on paliad.checklists). The
  catalog read layer mirrors the title/description onto both DE and EN
  sides so the existing bilingual frontend renders without a special
  case for authored entries.
- Save POSTs (create) or PATCHes (edit) the template; visibility flip
  on edit goes through its own endpoint so the audit row captures the
  transition.

Merged catalog:
- /api/checklists now returns the merged list (static + DB visible);
  the Summary shape gained origin / visibility / owner_email /
  owner_display_name fields.

i18n: 55 new keys per language (110 total) under
checklisten.tab.mine.*, checklisten.mine.*, checklisten.author.*,
checklisten.detail.* (Bearbeiten/Löschen labels for Slice B). i18n
codegen total: 2621 keys.

Build hygiene: bun run build clean, go build clean, go vet clean,
go test ./internal/... + ./cmd/server/ all green.
2026-05-20 15:24:07 +02:00
m
ba2408eb51 feat(paliadin/inline-widget): t-paliad-161 Slice C — floating button + slide-out drawer
The inline Paliadin chat surface — reachable from every authenticated
page, replacing the standalone /paliadin route as the primary entry
point. The standalone page survives as the dedicated full-screen mode
(the drawer's "↗ fullscreen" action links to it).

Components:

- frontend/src/components/PaliadinWidget.tsx — emits the floating
  trigger button (bottom-right, lime , owner-revealed by JS), a
  scrim, and the right-edge slide-out drawer with header (reset /
  fullscreen / close), context chip, message stream, empty-state
  starter list, and textarea+send form. Loads /assets/paliadin-widget.js.

- frontend/src/client/paliadin-widget.ts — runtime. /api/me probe
  reveals the trigger when caller matches PaliadinOwnerEmail (with
  optional is_paliadin_owner flag fast-path); Cmd+J / Ctrl+J shortcut
  toggles open/close (Cmd+K stays reserved for global search per
  client/search.ts). Uses computePaliadinContext() (Slice B) per send
  so route + entity + selection flow into every turn. SSE consumer
  writes assistant bubbles; localStorage persists per-session history.

- frontend/src/client/paliadin-starters.ts — per-route starter prompt
  registry. 14 routes covered (dashboard, projects.*, deadlines.*,
  appointments.*, agenda, events, inbox, tools.*, glossary, courts) +
  a _default fallback. Bilingual (DE/EN); prompts ending in `: ` seed
  the textarea for the user to finish; fully-formed prompts auto-send.

- 39 authenticated TSX pages get a `<PaliadinWidget />` element after
  `<Footer />` via a mechanical pass. paliadin.tsx (the standalone)
  is intentionally excluded — its dedicated UI is the widget's
  fullscreen escape hatch, not a place to overlay another widget.

- frontend/build.ts registers the new bundle.
- frontend/src/styles/global.css gains ~280 lines of widget CSS
  (trigger / scrim / drawer / header / context-chip / messages /
   bubbles / starters / form / send-btn) using only existing tokens.
   Mobile (≤640px): drawer goes full-screen; trigger lifts above
   bottom-nav slots.
- 11 new i18n keys × 2 langs = 22 entries under paliadin.widget.*.

Visibility predicate (paliadin-context.shouldSendContext) hides the
widget on /paliadin, /login, /onboarding. Owner-only gate stays on
PaliadinOwnerEmail.

Build clean: i18n 1955 → 1966 keys, IIFE-wrapped 218KB bundle, go test
green.

Refs: docs/design-paliadin-inline-2026-05-08.md §3, §5.
2026-05-08 19:54:18 +02:00
m
df321acb63 feat(t-paliad-097): clickable checklist references + Vorhandene Instanzen tab
Two related checklist UX gaps:

1. Checklist events in a project's Verlauf tab were unclickable — and
   nothing in the project_events row carried the originating instance ID.
   Add an `insertProjectEventWithMeta` helper, write
   {"checklist_instance_id": <uuid>} as project_events.metadata for
   checklist_created / _renamed / _linked / _unlinked / _reset (skipped
   for _deleted — instance is gone). Surface metadata on
   /api/projects/{id}/events and on dashboard recent_activity. The
   Verlauf renderer wraps the title in <a href="/checklists/instances/{id}">
   when metadata.checklist_instance_id is present, and the dashboard's
   activity feed deep-links the project ref to the instance directly for
   checklist_* events. Existing rows (metadata `{}`) stay non-clickable —
   no migration backfill needed.

2. /checklists previously demanded a template pick before any existing
   instance was reachable. Add a tab nav (Vorlagen / Vorhandene Instanzen)
   using the existing entity-tab pattern. New endpoint
   GET /api/checklist-instances and ChecklistInstanceService.ListAllVisible
   return every visible instance across templates + projects, joined with
   project ref/title and sorted by created_at DESC. Rows show template,
   instance name (linked), project link (or "Persönlich"), progress bar,
   and created date. URL state (?tab=instances) keeps the active tab
   shareable. EN + DE i18n covered for tab labels and column headers.

Also adds event.title.checklist_* localizations for the Verlauf header
that translateEvent looks up.
2026-05-01 09:48:25 +02:00
m
b21dacf15c feat(t-paliad-063): adopt HLC brand palette across paliad
Replace ad-hoc lime/forest-green system with the official 4-color HLC
palette. Lime + midnight are the primary pair; cyan + cream supporting.

Tokens
- :root now exposes --hlc-lime, --hlc-midnight, --hlc-cyan, --hlc-cream
  plus channel-token siblings (--hlc-*-rgb) so tints can be expressed as
  rgb(var(--hlc-*-rgb) / a) without hex literals.
- --color-bg → cream, --color-text/--color-hero-bg → midnight,
  --color-accent → lime, --color-accent-dark → midnight (foreground on
  lime; passes WCAG AA where #fff failed).
- New --sidebar-* tokens for the dark sidebar surface.

Sweep (frontend/src/styles/global.css)
- Replaced every hard-coded #c6f41c / #65a30d / #84cc16 / #b8e616 /
  #4d7c0f / #1a2e1a / #1a1a2e / #1a2e05 with the matching var(...).
- rgba(101,163,13,a) and rgba(198,244,28,a) collapsed to
  rgb(var(--hlc-lime-rgb) / a).
- text-on-lime now uses var(--color-accent-dark) instead of #fff;
  btn-danger keeps white on red.

Sidebar reskin (cronus's audit, F-30)
- Background: midnight; text: cream (muted via cream-channel alpha);
  active/hover: lime. Border + hover use cream-channel alphas so no
  rgba hex creep on the dark surface.

Brand assets
- manifest.json theme_color → lime, background_color → cream.
- icon.svg / icon-maskable.svg base recoloured to lime + midnight glyph.
- 32× <meta name="theme-color"> across pages updated to #BFF355.
- Email templates (base.html, invitation.html) lime accent updated;
  mail_service_test.go expectation tracks the new hex.

Deferred / out of scope
- PNG icons under public/icons/ are baked artefacts; regen left to the
  next deploy.
- Categorical chip colours (office tints, traffic-light red/amber/green,
  termin-type hues) are functional, not brand, and deliberately
  untouched.
- Dark mode is not in scope.

Verified
- bun run build clean.
- go build ./... clean; mail render tests pass.
- Visual sweep at 1280×900 against frontend/dist via Playwright on
  /, /login, /dashboard, /projects, /agenda, /team, /fristenrechner,
  /glossary — sidebar midnight + lime active, cream page bg, white
  cards, midnight text on lime CTAs.

Supersedes audit findings F-14, F-30, F-31.
2026-04-27 20:15:17 +02:00
m
8921830f43 feat(pwa): app-shell phase 2 — manifest + icons + service worker + install prompt (t-paliad-042)
Ship the installability bits that t-paliad-041 deferred so iOS / Android
users can add Paliad to their home screen.

What landed:
- frontend/public/manifest.json — name=Paliad, theme_color #65a30d (lime),
  display=standalone, scope=/, start_url=/dashboard, four icon entries
  (192/512 × any/maskable). Served from /manifest.json with the
  spec-mandated application/manifest+json content type (servePWAManifest
  in internal/handlers/pwa.go).
- frontend/public/icons/ — lime "p" logo rendered to 192/512 PNGs in both
  "any" and maskable variants (maskable variant has extra safe-zone
  padding), 180×180 apple-touch-icon, 32×32 favicon. SVG sources kept
  under frontend/icons-src/ for regeneration via rsvg-convert.
- frontend/public/sw.js — minimal cache-first for /assets/* and /icons/*,
  network-first for /api/*, network passthrough for everything else.
  CACHE_VERSION + activate-clean lets us bump and purge cleanly. Served
  from /sw.js so its scope can claim /; Service-Worker-Allowed: / header
  set, no-cache on the SW file itself so updates take effect on next load.
- frontend/src/components/PWAHead.tsx — head fragment (manifest link,
  apple-touch-icon, favicon, app-name metas, <script src="/assets/app.js"
  defer>). Added to all 30 page TSX files via mechanical insertion.
- frontend/src/client/app.ts — universal client bundle loaded on every
  page. Three jobs: register the service worker, init the BottomNav
  (icarus flagged that bottom-nav.ts was written but never wired into
  the build — m reproduced the broken [+] Anlegen and Menü buttons in
  prod), and surface the install banner.
- frontend/src/client/pwa-install.ts — install banner UI. Two flows:
  beforeinstallprompt for Chromium/Android (deferred → CTA → prompt),
  one-time iOS Safari hint pointing at the share sheet. Both dismissals
  persist in localStorage (paliad-install-dismissed / -ios-shown).
- frontend/src/styles/global.css — banner styles, sits above BottomNav on
  mobile and pinned bottom-right on desktop, lime-on-white card with the
  brand "p" mark.
- frontend/build.ts — copies frontend/public → dist verbatim so the
  manifest, icons, and SW land at the application root.

Verification before merge:
- bun run build clean, go build/vet/test clean.
- Local server smoke: curl -sI confirmed manifest.json (200,
  application/manifest+json), all icon files (200, image/png), sw.js
  (200, Service-Worker-Allowed: /), app.js (200, text/javascript).
- Playwright at 390×844: Chrome fired beforeinstallprompt, the banner
  rendered with "Paliad installieren" + "Installieren" CTA in German,
  dismiss persisted across reload via localStorage. Manifest validated
  in-browser (name/short_name/start_url/display/scope all correct, all
  four icon URLs returned 200).
- The InvalidStateError on serviceWorker.register() seen in the MCP
  Playwright profile is a known headless flag; SW registration works in
  real Chrome / Safari on localhost and HTTPS production.

Out of scope: push notifications, runtime offline mode (SW intentionally
stays minimal — cache shell + assets, network passthrough for everything
else).
2026-04-26 10:48:27 +02:00
m
3f0c26fd3a feat(frontend): PWA mobile BottomNav + Quick-Add sheet (t-paliad-041)
Phone-first bottom navigation per pwa-baseline.md. Renders only at
<768px; tablets and desktop are unchanged.

Slots: Start / Projekte / [+] Anlegen / Agenda / Menü.

- Center [+] opens a slide-up <dialog> sheet with three rows: Frist,
  Termin, Projekt. Native showModal() + ::backdrop, ESC and backdrop-tap
  dismiss, transform-based slide-up transition.
- Right Menü slot reuses the existing Sidebar mobile drawer via a new
  exported toggleMobileSidebar() (DRY with the legacy hamburger handler).
- Agenda slot carries a red-dot badge: count = today + overdue pending
  deadlines (live via /api/deadlines/summary, refreshed every 60s). Pulse
  animation when overdue > 0 — m: "Due is the latest we can do, OVERDUE
  is a catastrophy."
- visualViewport resize watcher hides the bar when the on-screen keyboard
  opens (>100px height shrink) so it doesn't cover form fields.
- safe-area-inset-bottom padding on the bar; main padding-bottom adjusts
  on phones so the last row stays above the bar.

PWA shell groundwork (defers manifest/SW/install-prompt to follow-ups):
- viewport-fit=cover on every page (required for safe-area to register)
- theme-color #65a30d (lime), apple-mobile-web-app-capable, status-bar
  style — all 30 page heads updated in one sweep.

Backend: deadline_service.SummaryCounts gains a `today` bucket so the
Agenda badge can distinguish "due today" from "this week" without a new
endpoint.

Files added:
  frontend/src/components/BottomNav.tsx
  frontend/src/client/bottom-nav.ts

Verified visually via headless chromium at 375x812, 800x600, 1280x800:
phone shows BottomNav (5 slots, lime [+] elevated), tablet shows the
existing hamburger only, desktop sidebar untouched. go build/vet/test
and bun run build all clean.
2026-04-26 10:32:00 +02:00
m
01de3f736b fix: update all script src references from German to English filenames
knuth's rename changed TS/TSX filenames but left <script src> tags
pointing at old German JS names (akten-neu.js, fristen.js, termine.js,
glossar.js, einstellungen.js, gerichte.js, checklisten.js). These 404'd
in production.
2026-04-23 00:32:22 +02:00
m
caf319e7ee refactor(rename): frontend TSX + client TS files, fetch URLs, nav hrefs
t-paliad-025 Phase 3 — frontend rename pass:

File renames (git mv, preserving history):
  frontend/src/
    akten.tsx               → projects.tsx
    akten-neu.tsx           → projects-new.tsx
    akten-detail.tsx        → projects-detail.tsx
    fristen.tsx             → deadlines.tsx
    fristen-neu.tsx         → deadlines-new.tsx
    fristen-detail.tsx      → deadlines-detail.tsx
    fristen-kalender.tsx    → deadlines-calendar.tsx
    termine.tsx             → appointments.tsx
    termine-neu.tsx         → appointments-new.tsx
    termine-detail.tsx      → appointments-detail.tsx
    termine-kalender.tsx    → appointments-calendar.tsx
    einstellungen.tsx       → settings.tsx
    checklisten*.tsx        → checklists*.tsx
    gerichte.tsx            → courts.tsx
    glossar.tsx             → glossary.tsx

  frontend/src/client/ — same renames, plus notizen.ts → notes.ts.

Render exports renamed (renderAkten → renderProjects, renderFristen →
renderDeadlines, …). build.ts rewired to new names.

Client-side changes:
* fetch() API paths: /api/projekte → /api/projects, /api/fristen →
  /api/deadlines, /api/termine → /api/appointments, /api/notizen →
  /api/notes, /api/gerichte → /api/courts, /api/glossar → /api/glossary,
  /api/dezernate → /api/departments, /api/parteien → /api/parties,
  /api/checklisten → /api/checklists. Legacy /api/akten aliases removed.
* Navigation href/template strings: /akten → /projects, /fristen →
  /deadlines, /termine → /appointments, /einstellungen → /settings,
  /notizen → /notes, /checklisten → /checklists, /gerichte → /courts,
  /glossar → /glossary. Nested paths /neu → /new, /verlauf → /events,
  /kinder → /children, /kalender → /calendar, /dokumente → /documents.
* Interface names in client TS: Frist → Deadline, Termin → Appointment,
  Notiz → Note, Partei → Party, Akte → Project, ProjektMini → ProjectMini,
  Dezernat → Department, DezernatMitglied → DepartmentMember.
* JSON wire-format keys follow backend: projekt_id → project_id, akte_id
  → project_id, frist_id → deadline_id, termin_id → appointment_id,
  akten_event_id → project_event_id, dezernat_id → department_id,
  termin_type → appointment_type.

Go handlers (projects_pages.go, deadlines_pages.go, appointments_pages.go,
checklists.go, courts.go, glossary.go) serve the correctly-named HTML
files from dist/.

Kept German (user-facing i18n + product names):
* i18n keys/strings (src/client/i18n.ts) — DE labels and their keys
* Product names: fristenrechner, kostenrechner, gebuehrentabellen

Build verified: go build / vet / test clean; bun run build clean;
dist/ contains all 26 English-named HTML pages.
2026-04-20 17:44:45 +02:00