Commit Graph

12 Commits

Author SHA1 Message Date
m
aeeded7e21 feat(t-paliad-149) PR2 step 2/2: frontend — Cards view + drag-rearrange named layouts
Adds the Cards view-mode to /projects (third option in the segment-control
between Tree and Liste).

frontend/src/projects.tsx:
- View-mode segment gains "Karten" button
- Two new toolbars (initially display:none, surfaced by Cards mode):
  - .projects-cards-toolbar: layout dropdown + [Bearbeiten] + [Neue Ansicht]
    + "Alle Ebenen anzeigen" toggle
  - .projects-cards-edit-toolbar: density radio + grid select + rename /
    delete / set-default / discard / save buttons
- New container: .projects-cards-wrap > #projects-cards-grid

frontend/src/client/projects-cards.ts (NEW, ~640 LoC):
- Layout management: GET /api/user-card-layouts on first mount; auto-seeds
  Standard layout if empty (POST). Layout dropdown switches active layout
  in-place; show_all_levels toggle persists immediately.
- Edit mode: clones the active layout into editDraft; renders per-card
  fact list with drag handles + visibility checkboxes + count steppers
  (1..5) for next-events / recent-verlauf. HTML5 drag-and-drop reorders
  facts; title-row is forced to the first position so the server-side
  validator's invariant holds.
- New layout: prompts for a name, seeds with the current draft (or active
  layout's facts), POSTs, enters edit mode.
- Set-default / rename / delete: each maps to PATCH or DELETE; default
  cannot be deleted (server returns 409 + UI alerts).
- Card render: title row (icon + link + pin star), type/status chips,
  client-matter, parent-path-as-reference (parent breadcrumb deferred —
  needs an extra fetch per card), deadline-counts (subtree-aggregated
  when available), next-events from /api/projects/cards-preview, recent-
  verlauf, team-chips initials with overflow count.
- Pin click on a card star does optimistic toggle + POST/DELETE pin
  endpoint and updates treeCache in place.
- Cards sort: pinned first, then last_activity_at DESC, then title ASC.
- "Alle Ebenen anzeigen" toggle decides whether Mandanten + Litigations
  appear as their own cards (off by default — leaf-ish projects only:
  Cases, Patents, Verfahren, Projekte).

frontend/src/client/projects.ts (orchestrator):
- ViewMode type expands to "tree" | "cards" | "flat"
- View segment-control wires through to Cards mode
- render() dispatches to renderCardsView / teardownCardsView based on
  active mode

frontend/src/client/i18n.ts: 53 new keys DE+EN under projects.cards.* —
section titles, empty-states, layout picker labels (label/new/edit/save/
discard/set_default/delete/rename/is_default/new.prompt/delete.confirm/
delete.default_blocked), per-fact labels (title-row/type-chip/status-chip/
client-matter/parent-path/deadline-counts/next-events/recent-verlauf/
team-chips/reference/last-activity-at), density values (compact/roomy),
grid values (auto/2/3/4), event-kind labels (deadline/appointment/
project_event), edit toggles (toggle.hide/show/move_up/move_down/count).

frontend/src/styles/global.css: ~290 LoC appended for cards toolbar +
grid + card layout (title row / row / section / event row / team chips)
+ edit-mode chrome (drag handles, drop targets, count steppers) + dark-
themed dashed border on edit cards. Mobile media query forces single-
column grid.

i18n codegen: 1830 → 1882 keys (+52). bun run build clean. tsc on new
files clean (pre-existing JSX-IntrinsicElements noise unrelated).
go build/vet/test still clean.
2026-05-07 22:46:26 +02:00
m
a5f7b5009b feat(t-paliad-149) PR1 step 2/3: frontend rewrite — chips + pin star + last-view restore
frontend/src/projects.tsx — strip the legacy 3-select toolbar; replace with
search input + view-mode segment-control (Tree | Liste) + chip filter row
(Alle / Nur meine / Angepinnt / Status / Typ / Mit aktiven Fristen). Tree
container is the default visible mount; flat-table hidden until view mode
toggles.

frontend/src/client/projects.ts — orchestrator. Owns chip + search + view-
mode state. Last-viewed restore from sessionStorage (Q1 lock-in), URL params
override on load, syncURL on every state change. Debounced search (250ms).
Multi-select panels via <details> for status/type. Delegates rendering to
project-tree.ts (tree mode) or projects-flat.ts (flat mode).

frontend/src/client/projects-flat.ts (NEW) — extracted table render from the
old projects.ts so the orchestrator can mount/unmount cleanly.

frontend/src/client/project-tree.ts — extends ProjectTreeNode shape with
pinned, inherited_visibility, match_kind, *_subtree fields. Renders pin
star button (always-visible per design §4.6 — touch-friendly), greyed-
ancestor opacity for InheritedVisibility=true, lime backdrop on
match_kind=self. Pin click does optimistic toggle + POST/DELETE
/api/projects/{id}/pin then invalidates the tree cache.

frontend/src/styles/global.css — toolbar + chips + pin star + greyed-
ancestor + match highlighting. ~200 LoC appended.

frontend/src/client/i18n.ts — 29 new keys DE+EN under projects.toolbar.*,
projects.chip.*, projects.tree.deadlines.*, projects.tree.pin/unpin,
projects.search.match.*, projects.empty.filtered.action.

internal/services/pin_service_test.go (NEW) — live-DB tests for PinService
(pin/unpin/idempotent/owner-scope/visibility-gate) + 2 BuildTreeWithOptions
cases (PinnedSet surfaces, ScopeMine greys ancestors). Skips without
TEST_DATABASE_URL; pure-Go path runs clean.

Frontend bun build clean. go build / vet / test (short) clean.
2026-05-07 22:29:39 +02:00
m
c6872f94b0 refactor(t-paliad-093): rename .akten-* CSS classes to English equivalents
F-7 of the t-paliad-074 architecture audit. Sweeps the last German-named
CSS leftovers — purely a class-name change, no behaviour or styling
delta. 466 references across global.css and ~30 TSX/TS files.

Naming rules applied:
- Generic table/tabs/form/empty/controls/detail/events/status/type/
  suggestion/chip/col/ref/search-wrap/select/soon/loading/muted/
  unavailable/row/header-row/title-input -> .entity-*
- Truly generic widgets dropped the prefix: .multi-* (multi-select
  panel), .filter-*, .collab-* (collaborator picker; bare class is
  now .collab-picker), .firmwide-*, .office-*, .back-link
- Project-specific names kept specific: .party-form/-controls/-table
  (parties on a project), .checklists-hint, .netdocs-link
- Page-scoped IDs in projects.tsx -> projects-search/-count/-body;
  projects-new.tsx -> project-new-form/-msg
- German content "akten-bezogen" tightened to "aktenbezogen" (one-word
  form is also valid German) so the strict grep stays clean
2026-04-30 16:52:10 +02:00
m
5c11fe5e6d feat(t-paliad-079): bulk-rename German-prefix i18n keys to English
F-9 from t-paliad-074. Aligns the i18n key namespace with the codebase's
English-language convention; no UI text changes.

Renames in frontend/src/client/i18n.ts (the source-of-truth file):
  akten.*    -> projects.*  (merged with projekte.*; projekte wins on collision,
                              the more-recently-edited value, per brief)
  fristen.*  -> deadlines.*
  termine.*  -> appointments.*
  projekte.* -> projects.*
  notizen.*  -> notes.*

Scope of changes:
  - 760 key lines renamed in i18n.ts (380 unique keys × 2 langs)
  - 70 akten/projekte suffix collisions resolved by dropping akten.* lines
    (140 lines dropped total — projekte values preserved)
  - 19 inner-segment fixes (e.g. projects.detail.fristen.add ->
    projects.detail.deadlines.add, and template-literal sites like
    `tDyn(`fristen.${x}`)` whose suffix begins with ${...})
  - 476 caller-side replacements across 27 *.ts/*.tsx files
    (literal t() / tDyn() args, template-literal prefixes,
     "prefix." string concatenations, data-i18n attributes)
  - i18n-keys.ts (generated) regenerated by build.ts: 1218 keys total

t-paliad-078's typed registry + build-time data-i18n scanner caught this
rename was complete: `bun run build` reports "i18n scan: data-i18n
attributes clean", meaning every literal data-i18n attribute in TSX/TS
sources references a key that exists in i18n.ts post-rename.

Out of scope (per brief): backend Go service rename (t-paliad-080 F-4),
URL paths (/akten, /projekte routes still server-side), CSS class names
(akten-table, akten-form, etc.), and German sub-tokens like .akte (label
"Akte:") or .no_akten (the modal hint when no project is linked).
2026-04-30 04:38:06 +02:00
m
800668a483 feat(t-paliad-078): type i18n key registry + build-time data-i18n scan
F-8 from the t-paliad-074 audit. Replaces silent `?? key` fallback with a
typed key surface so drift caught at compile/build time, not in prod.

- New `frontend/src/i18n-keys.ts` (generated): `I18nKey` literal union of
  all 1288 keys in `i18n.ts`. Regenerated by `frontend/build.ts` on every
  build; written only when content changes (no spurious diffs).
- `t(key: I18nKey)` is now strict — `t("fristn.detail.title")` fails
  `tsc --noEmit`. New `tDyn(key: string)` is the explicit escape hatch
  for runtime-composed keys (`tDyn(\`fristen.status.${x}\`)`); 27 dynamic
  call sites migrated.
- Build-time scan in `build.ts` walks `src/**/*.{ts,tsx}` for literal
  `data-i18n` / `data-i18n-placeholder` / `data-i18n-title` attributes
  and aborts the build on any value not in the key set. Skips `${...}`
  interpolations (can't resolve statically). Applied before bundling so
  no artefact ships when an unknown literal is present.

Surfaced and fixed during migration:

- `data-i18n="fristen.save.modal.project"` (fristenrechner.ts:145) →
  `fristen.save.modal.akte` — F-04-class bug, would render the raw key.
- `t("termine.field.project.none")` (appointments-new.ts:30) →
  `termine.field.akte.none` — same class.
- `t("checklisten.instance.project.open")` (checklists-instance.ts:155)
  → `checklisten.instance.akte.open` — same class.
- 4 duplicate-key entries in `i18n.ts` removed (TS1117): `nav.termine`
  and `akten.detail.tab.termine` each appeared twice in DE and twice in
  EN with identical values.

Out of scope (per brief): the German-vs-English i18n-key namespace split
flagged as F-9, JSX intrinsic typing, and the `akten` → `projects`
half-rename in checklists-detail.ts. Those stay tsc-noisy until separate
tasks land.
2026-04-30 03:56:32 +02:00
m
aef40bb425 feat(t-paliad-073): audit polish-2 DEFER list cleanup
Six findings from docs/audit-polish-2-2026-04-29.md DEFER list:

- F-23: hide STATUS column on /deadlines + /projects when every visible
  row shares the same status. Toggled at render time via a CSS class on
  the table; the column re-appears the moment filters re-introduce
  variety.
- F-32: agenda urgency pill now renders only when it disagrees with
  the day-bucket heading (e.g. an Überfällig deadline that lands in
  HEUTE through a filter quirk). Common case drops the redundant tag.
- F-38: bottom-nav agenda badge already counted overdue+today (the
  brief's option (b)); added a localized title + aria-label so the
  count's semantics ("X überfällig + Y heute fällig") is no longer
  ambiguous.
- F-40: glossary filter chips no longer mix EN+DE — DE shows
  "Streitsachen / Erteilungsverfahren / Allgemein", EN keeps
  "Litigation / Prosecution / General". Same i18n keys cover the
  Suggest-modal category dropdown.
- F-48: /projects/{id}/sub-projects now 301-redirects to the canonical
  /children URL via the existing redirects.go mechanism. Added a small
  redirects_test.go to lock the alias in.
- F-49: dropped the meta-circular 2026-04-22 "Neuigkeiten / What's New"
  changelog entry that referenced "this changelog" itself.

go build/vet/test clean, bun run build clean.

F-25 (mobile tables → card layout) is redesign-class and is scoped at
the bottom of the PR description as t-paliad-074, not implemented here.
2026-04-30 02:29:09 +02:00
m
7d45626d57 fix(t-paliad-067): PR-2 visual residue + per-page polish (F-13, F-15, F-24, F-27, F-28, F-33, F-36, F-39, F-42, F-43, F-47, F-50)
Per docs/audit-polish-2-2026-04-29.md PR-2. Local visual cleanups across
deadlines, appointments, projects, project detail, dashboard, settings,
onboarding.

- F-13 + F-42 the .frist-akte-title CSS rule was renamed to
  .frist-project-title (matching the markup that the rename sweep already
  produced) plus text-overflow:ellipsis and a max-width gutter, and the
  client renderers now stamp title= on the project-title span so the full
  ref+title is reachable on hover. Fixes the
  "L-2026-001Siemens AG ./." collision and trims the deadline rows that
  were ballooning to 2 lines.
- F-15 "Projekt archivieren" demoted from .btn-danger to .btn-secondary
  (neutral outline). Confirm-modal action stays red.
- F-24 the /projects filter row groups label+select pairs into
  .akten-filter-group divs and stacks each as a full-width labelled block
  at <480px instead of wrapping each label/select onto its own line.
- F-27 single-element breadcrumbs hide on root projects — the lone crumb
  used to echo the H1 below it.
- F-28 empty REFERENZ + CLIENTMATTER cells on /projects and ORT on
  /appointments render an em-dash so the placeholder convention matches
  /admin/team and /projects/{id}/deadlines.
- F-33 truncated project refs on the dashboard upcoming-deadlines and
  upcoming-appointments lists carry a title= attribute with the full
  "REF · Title" string, so hover reveals the truncated tail.
- F-36 /projects/new no longer defaults to Mandant — a "Bitte wählen…"
  placeholder is the initial selection (required attr blocks submit).
  New projekte.field.type.choose i18n key in DE+EN.
- F-39 the /projects search counter renders "X / Y" in tree view too
  (was bare "X"), matching the flat-view format.
- F-43 /projects/{id}/parties empty state is now an .akten-empty-card
  with a "Partei hinzufügen" CTA underneath the message, wired to the
  same form-open handler as the toolbar button.
- F-47 onboarding + settings job_title placeholder swaps the EN-DE-EN
  mix "z.B. Associate, Partner, PA" for "z.B. Associate, Partner,
  Patentanwalt" / "e.g. Associate, Partner, Patent Attorney". Three
  named titles, no abbrev, EN-jargon convention kept consistent.
- F-50 mobile bottom-nav clearance bumped from 1rem to 1.75rem on
  body.has-sidebar main so the centre FAB (margin-top: -10px above the
  56px bottom-nav) clears the last list item with a real gutter.

Verified
- go build/vet/test ./... all green.
- bun run build clean.
- Live smoke pending deploy.
2026-04-29 14:32:53 +02:00
m
5423a4e1f1 Merge: PR-E bug batch (t-paliad-062)
# Conflicts:
#	frontend/src/client/projects.ts
2026-04-27 19:36:21 +02:00
m
c9ca08fcbb fix(t-paliad-062): PR-E bug batch — F-02, F-03, F-08, F-09
Four standalone bugs from the 2026-04-27 polish audit (PR-E batch).

F-02 — /admin/team search input: long placeholder ("Nach Name oder
E-Mail suchen…") visually overlapped the absolutely-positioned count
badge ("31 / 31") because .glossar-search reserved only 0.75rem of
right padding. Bumped padding-right to 4.5rem so the badge sits in its
own gutter — same fix protects every other use of the .glossar-search
shell (admin team, glossary, etc.) without touching individual pages.

F-03 — /api/departments?include=members 500 regression. Migration 020
renamed paliad.dezernat_mitglieder → department_members but missed the
dezernat_id column on prod youpc. Application code (DepartmentService.
ListWithMembers / ListMembers / AddMember / RemoveMember) selects
department_id, which doesn't exist there → "column does not exist"
500. New migration 024 renames the column idempotently, plus the
indexes/constraints/policies that postgres did not auto-rename when
their table was renamed (departments_pkey, departments_office_idx,
departments_lead_idx, departments_lead_user_id_fkey,
departments_office_check, department_members_pkey,
department_members_user_idx, department_members_department_id_fkey,
department_members_user_id_fkey, departments_select / _write,
department_members_select / _write). Every rename uses a DO block that
swallows undefined_object / undefined_column so the migration is a
no-op on dev DBs that already had English names from migration 018.
Down step puts the German names back symmetrically.

F-08 — Project detail tabs (/projects/{id}/Verlauf|Team|…) used
href="#", so middle-click and "open in new tab" were broken even
though the SPA already mirrored the canonical path via
history.replaceState. initTabs() now sets each tab anchor's href to
/projects/{id}/{tab} (id resolved from the URL when the project hasn't
loaded yet) and only intercepts plain left-clicks — middle/ctrl/meta/
shift/alt fall through to the browser. Backend gains the previously-
missing /projects/{id}/history and /projects/{id}/children server
routes (both bound to handleProjectsDetailPage like every other tab),
so opening the URL in a fresh tab no longer 404s.

F-09 — /projects?view=tree was silently ignored: viewMode was hard-
coded to "flat" and the URL was never read. parseInitialView() now
seeds viewMode from ?view=, initFilters() syncs the dropdown to the
parsed value before binding the change handler, and changing the
dropdown rewrites the query string via history.replaceState (default
"flat" stays implicit to keep the canonical path clean). Bookmarks,
dashboard links, and copy-shared URLs round-trip correctly.

Verification:
- /api/departments?include=members live-tested after applying 024 to
  youpc: returns 200 with members enriched.
- go build ./... + go vet ./... + go test ./... clean.
- bun run build clean.
2026-04-27 19:34:56 +02:00
m
c9054ed753 fix(t-paliad-061): rename residue + small i18n cleanups (PR-D)
Per docs/audit-polish-2026-04-27.md PR-D batch:
- F-11 office labels on /projects/{id}/team — use t("office."+key) so
  "duesseldorf"/"munich" render as "Düsseldorf"/"München"
- F-17 "Lead" → "Leitung" in DE on the Rolle column and /projects/new
  subtitle (EN keeps "Lead")
- F-18 admin.team.permission.global_admin → "Globaler Admin" (DE) plus
  matching "globaler Admin" in last_admin error
- F-19 rename DOM IDs: projekt-type → project-type, projekt-view →
  project-view, akten-status → project-status (markup + all
  getElementById/$ callsites in client modules)
- F-26 Akte filter dropdown on /deadlines + /appointments → "Projekt"
  / "Alle Projekte" in DE (column headers stay for PR-A/F-12)
- F-44 admin card "Departments / Dezernate" → "Dezernate"
- F-45 "Dezernat / Partner" → "Dezernat oder Partner" on settings +
  onboarding profile fields

go build/vet/test clean; frontend bun run build clean.
2026-04-27 19:28:05 +02:00
m
aafbfbbf12 feat(projects): interactive tree view of project hierarchy (t-paliad-028)
- Backend: GET /api/projects/tree returns the full visible project tree as
  nested JSON with embedded children, open/overdue deadline counts per
  node — visibility-scoped via the existing predicate, single round-trip.
- Frontend: new project-tree.ts module renders a collapsible, indented tree
  with type icons (client/litigation/patent/case/project), status badges,
  deadline-count chips and chevron toggles. Top two levels expand by
  default; deeper nodes start collapsed. Expansion state persists in
  sessionStorage so toggling list/tree keeps user choices.
- Wired to /projects via the existing Ansicht select (Liste/Baum/Wurzeln);
  dedicated tree container coexists with the flat-list table.
- New i18n keys (de/en) + tree styles in global.css (lime accent on hover).
2026-04-25 13:22:16 +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