Commit Graph

5 Commits

Author SHA1 Message Date
mAi
3533d79a25 feat(deadline-system): S1+S1a — cross-party display + spawn-only picker (m/paliad#149)
Phase 2 S1 + S1a (pre-ratified from t-paliad-327, folded into the
Phase 2 train).

S1 — Cross-party display:
- FristenrechnerService.LookupFollowUps stops filtering by party
  server-side; queryFollowUpRows drops the perspective WHERE clause
  and returns every published+active child.
- Server now computes is_cross_party per row (true only when
  perspective ∈ {claimant,defendant} AND primary_party is the
  opposite side; NULL/both/court is never cross-party).
- FollowUpRule wire shape gains the boolean.
- Frontend renderRule adds a "Gegenseitig" badge + is-cross-party
  row class (muted styling, disabled checkbox affordance).
- defaultChecked returns false for cross-party rows.
- countSelected + submitWriteBack skip cross-party rows
  unconditionally — even if a user manually checks the box, they
  describe opposing-side filings and don't belong in our Akte set
  (design §2.4 write-back exclusion).
- i18n: deadlines.overhaul.crossparty.badge / .tooltip (DE+EN).
- CSS: .fristen-overhaul-rule-crossparty + .is-cross-party row
  modifier.

S1a — Spawn-only picker filter:
- SearchEvents WHERE now adds `sr.is_spawn = false` so spawn rules
  (e.g. appeal_spawn, the inf.cfi → upc.apl.merits hop) no longer
  surface as picker hits. Spawn rules are consequences, not
  triggers — a lawyer searching "Berufung" wants the appeal-tree
  root, not the inf.cfi spawn link.
- Terminal leaves (Duplik etc.) stay pickable per design §2.2's
  carve-out: their own anchor is non-spawn, so they surface and
  render an honest empty follow-up list.

Honest UX: hiding cross-party follow-ups lied about what the
workflow does next (cf. RoP.029.d falling off when perspective=
claimant on def_to_ccr — the workflow continues, just on the
defendant's docket). The fix makes the data legible without
contaminating the write-back path.

Verified: go vet clean, bun build clean, bun test 256/256,
go test ./internal/services/... -run LookupFollowUps... clean.

Design: docs/design-deadline-system-revision-2026-05-27.md §2.4
(cross-party) + §2.2 (spawn-only picker). t-paliad-331.
2026-05-27 15:07:01 +02:00
mAi
4571bd4980 feat(fristenrechner): Slice S5 — flip overhaul default; legacy under ?legacy=1 (m/paliad#146)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
`isOverhaulMode()` now returns true unless the URL carries
`?legacy=1`. The overhaul UI from S2-S4 (mode tabs + Mode A
search + Mode B wizard + shared result view) becomes the default
landing for /tools/fristenrechner; the legacy three-step wizard +
Pathway A/B + cascade is reachable only via the explicit
`?legacy=1` opt-out for the two-week deprecation window before
S6 drops the legacy code paths entirely.

The pre-existing `?overhaul=1` deep links from S2-S4 still
resolve — the detector treats *absence* of `?legacy=1` as
overhaul, so bookmarks stay valid. No sidebar / header / outbound
link change needed: those all point at the bare
`/tools/fristenrechner` URL, which now boots overhaul.

Verified — bun build clean (2971 i18n keys unchanged), 256
frontend tests pass, go build + vet clean.
2026-05-27 10:16:07 +02:00
mAi
70985d88b0 feat(fristenrechner): Slice S4 — Mode B wizard (m/paliad#146)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
Mode B "🧭 Geführt" — the guided 3-5 row wizard defined in
docs/design-fristenrechner-overhaul-2026-05-26.md §3.2. Lands the
user on a single procedural_event (the trigger), then transitions
to the shared §4 result view.

Frontend:
  * `fristenrechner-wizard.ts` — row stack with R1..R5:
      R1 Was ist passiert?           (event_kind, always asked)
      R2 Vor welchem Gericht?        (jurisdiction, skip if R1 narrows)
      R3 In welchem Verfahren?       (proceeding_type, auto-skip when
                                      narrowed pool has 1 option)
      R4 Welches Schriftstück?       (procedural_event, landing)
      R5 Welche Seite vertreten Sie? (party, only when follow-ups
                                      differ by primary_party)
    Row badges per §11.Q3: R1+R2 = Filter, R3+R4+R5 = Qualifier.
    R5 has NO "Beide" option per §11.Q8 — Mode B is the file-mode
    where perspective is a qualifier.
  * Project prefill — derives R3 + R2 jurisdiction from
    project.proceeding_type, R5 from project.our_side. Annotates
    pre-filled rows with "aus Akte" tag and implicit rows with
    "implizit" tag per §11.Q10 ("erhalten" annotation when a pick is
    carried across an upstream change).
  * R4-to-result transition — after R4 the wizard fetches /follow-
    ups (no dates) to inspect primary_party variance. If both
    claimant and defendant rules exist AND R5 isn't already set,
    swaps the loading row for the R5 chip picker. Otherwise jumps
    straight to mountResultView.
  * URL state — `?mode=wizard&kind=…&forum=…&pt=…&r4=…&party=…`
    keeps deep-link / back-nav consistent (the launchResult step
    sets `event=` so the result view picks up).
  * `fristenrechner-result.ts` mountModeShell now dispatches the
    "wizard" tab to the wizard module (was a coming-soon
    placeholder).
  * 18 i18n keys added (DE + EN parity), 145-line CSS block for the
    wizard row stack with Filter / Qualifier badge styling and
    "aus Akte" annotation chip.

Backend:
  * `ProceedingListOptions.EventKind` adds an EXISTS subquery
    filter on `paliad.sequencing_rules` ⨯ `paliad.procedural_events`
    so Mode B R3 chips only show proceedings whose event roster
    contains at least one event of the requested kind (design
    §6.3). Endpoint param: `event_kind=` on
    /api/tools/proceeding-types.

Test updates:
  * `TestListProceedings` switched from SKIP-when-column-missing to
    asserting the live filter — mig 153 has landed, `kind` column
    is in place. New subtests: kind=proceeding includes
    upc.inf.cfi and excludes the phase row upc.cfi.interim;
    event_kind=filing narrows to proceedings with filing events.
  * `fristenrechner-wizard.test.ts` covers
    `followUpsDifferByParty` — the R5 trigger predicate. 7 cases:
    asymmetric → true; uniform / both / court / empty → false.

Verified — bun build clean (2971 i18n keys), 256 frontend tests
pass (incl. 7 new), go build + vet clean, live-DB
TestListProceedings passes all 6 subtests against mig 153 data.
2026-05-27 10:14:37 +02:00
mAi
2a2c5b8033 feat(fristenrechner): Slice S3 — Mode A direct search (m/paliad#146)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
Mode A " Direkt suchen" — the power-user entry path defined in
docs/design-fristenrechner-overhaul-2026-05-26.md §3.1. Renders
above the §4 result view; clicking a result row locks the trigger
event and transitions to the shared result surface from S2.

Frontend:
  * `fristenrechner-mode-a.ts` — filter strip (Forum / Verfahren /
    Was passierte / Partei) + free-text search input + result list.
    Section-split visual hierarchy per m §11.Q3: filter chips in a
    bordered "Filter (eingrenzen)" strip on top, result list below.
    Inbox channel chip lives behind an "Erweitert" details summary
    per §3.3; picking CMS / beA auto-nudges the Forum chip. Party
    chip retains a "Beide" option (Mode A is filter mode per §11.Q8;
    Mode B drops it in S4).
  * `fristenrechner-result.ts` — new `mountModeShell(activeTab)`
    renders the two mode tabs per §11.Q2 and lazy-imports Mode A.
    Mode B tab is a placeholder until S4 lands.
  * `fristenrechner.ts` boot — when `?overhaul=1` is set and `?event`
    is empty, mountModeShell takes over (default tab = search; `?mode=
    wizard` opens the wizard tab when S4 ships). With `?event=` the
    flow still jumps straight to the result view. URL state syncs
    forum / pt / kind / party / q on every chip click.
  * 28 i18n keys added (DE + EN parity), 310-line CSS block for the
    mode tabs + Mode A surface.

Backend:
  * New `ProceedingListOptions { Jurisdiction, Kind }` + service
    method `ListProceedings(ctx, opts)`. Legacy
    `ListFristenrechnerTypes` keeps the no-filter signature for
    existing callers. Handler `/api/tools/proceeding-types` accepts
    `?jurisdiction=` and `?kind=` query params.
  * `kind=proceeding` filter targets the taxonomy column landed in
    mig 153 (parallel branch t-paliad-325, m/paliad#147). Sequenced
    per the taxonomy doc §7 option (c): mig 153 merges before S3
    ships to main, so the filter is never false-positive (no phase
    / side_action / meta rows leak into the chip strip).

Verified — bun build clean (2955 i18n keys, data-i18n attributes
clean), 249 frontend tests pass, go build + vet clean. New
TestListProceedings — 4 PASS (no-filter, jurisdiction=UPC,
jurisdiction=DE, ListFristenrechnerTypes alias) + 1 SKIP for the
kind=proceeding case that probes the column and skips when mig 153
hasn't landed yet. S1 + S2 live tests still green.
2026-05-27 10:07:27 +02:00
mAi
9ab8dd8e0f feat(fristenrechner): Slice S2 — result view under ?overhaul=1 (m/paliad#146)
Some checks failed
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
New `frontend/src/client/fristenrechner-result.ts` module renders the
shared result surface defined in
docs/design-fristenrechner-overhaul-2026-05-26.md §4:

  * Sticky trigger card — event icon + name, proceeding/jurisdiction
    chips, inline trigger-date input that re-fetches on change.
  * Four follow-up groups — Mandatory / Recommended / Optional /
    Conditional. SPAWNED rules fold into their priority bucket with
    a `⇲ neues Verfahren` badge (§11.Q5). Conditional bucket holds
    every rule with sr.condition_expr IS NOT NULL.
  * Per-rule rows — title, duration phrase, party chip, legal-source
    citation (with youpc.org link when available), pre-checked
    checkbox driven by `defaultChecked(r)` (mandatory + recommended
    on; conditional + court-set + optional off), inline ✏ Datum
    override that re-renders.
  * Write-back footer — conditional on `?project=<uuid>` per §11.Q7;
    in kontextfrei mode the footer is hidden and an inline nudge
    invites the user to pick an Akte. CTA submits to the existing
    POST /api/projects/{id}/deadlines/bulk endpoint, stamping each
    row with `audit_reason: "Aus Fristenrechner — Trigger: {name}
    ({date})"` per §11.Q12.

Mount + URL contract — when `?overhaul=1` is set in the URL,
`fristenrechner.ts` hides every legacy panel (`fristen-step1`,
`fristen-step2`, `fristen-pathway-a`, `fristen-pathway-b`,
`fristen-step3a`, the step-1 summary) and shows the overhaul root
instead. With `?overhaul=1&event=<code>&trigger_date=…` the surface
is deep-linkable end-to-end. Without `?event=` the empty-shell
nudge renders — S3+S4 will mount the entry-mode UIs onto this same
root.

Verified — bun build clean, 249 frontend tests pass (incl. 9 new
helper tests for groupFollowUps + defaultChecked), go build + vet
clean, S1 live-DB tests still green.
2026-05-26 22:09:27 +02:00