feat(determinator/slice-3a): outgoing-intent chooser (File / Draft / Enter)

m's 2026-05-08 18:09 spec: Step 3a is itself a 3-option fan-out. When
the user picks "Etwas einreichen" on Step 2 we no longer drop straight
into the Pathway A wizard; we ask "what kind of einreichen?" first.

Three cards:

  - **File** (Schriftsatz einreichen) → navigates to Pathway A — the
    existing wizard with proceeding picker, trigger date, flags,
    timeline, save modal. The rule-library entry point.
  - **Draft** (Schriftsatz entwerfen) → v1 placeholder. Disabled
    button with a "kommt bald" pill in the corner. m specced this
    as a link to a future drafting surface; for now we show the
    intent without doing anything so the surface exists in the IA.
  - **Enter** (Frist manuell erfassen) → routes to
    `/projects/{id}/deadlines/new` (or `/deadlines/new` in ad-hoc
    mode where there's no project to anchor against).

Pathway type extends to include "outgoing"; readPathwayFromURL +
showPathway both handle it. The Step 3a panel reuses .fristen-step2-
card visuals so File / Draft / Enter look consistent with the parent
Step 2 cards but distinct from Pathway A's proceeding picker.

Back-button policy:

  - Step 3a back → Step 2 (the new "fork" state).
  - Pathway A back → Step 3a (since that's where the user came from
    in the new flow). Two clicks back to the fork.
  - Pathway B back → fork directly (Step 2 happened-card jumps
    straight to Pathway B; no intermediate chooser).

Out of scope for this slice:

  - Step 3b's project-type-scoped event picker (Slice 3b).
  - Klägerseite/Beklagtenseite role variants (Slice 3c).
  - Real /drafts route — Draft stays a soft placeholder.

Refs t-paliad-157 / m/paliad#15.
This commit is contained in:
m
2026-05-08 19:58:21 +02:00
parent dba8ad3fdd
commit 34e82ead06
5 changed files with 139 additions and 7 deletions

View File

@@ -2296,7 +2296,12 @@ document.addEventListener("DOMContentLoaded", initSearch);
// re-entry. ?legacy=1 keeps the pre-v3 layout (no fork) for parity testing
// during the rollout window.
type Pathway = "fork" | "a" | "b";
// Pathway values:
// fork — Step 1 / Step 2 visible (the new front of the funnel)
// outgoing — Step 3a chooser (File / Draft / Enter) visible
// a — Pathway A wizard (Verfahrensablauf timeline)
// b — Pathway B cascade
type Pathway = "fork" | "outgoing" | "a" | "b";
type BMode = "tree" | "filter";
const PATHWAY_STORAGE_KEY = "paliad.fristen.pathway";
@@ -2304,7 +2309,7 @@ const PATHWAY_STORAGE_KEY = "paliad.fristen.pathway";
function readPathwayFromURL(): Pathway {
const sp = new URLSearchParams(window.location.search);
const p = sp.get("path");
if (p === "a" || p === "b") return p;
if (p === "a" || p === "b" || p === "outgoing") return p;
return "fork";
}
@@ -2339,6 +2344,18 @@ function setPathwayURL(path: Pathway, mode?: BMode, replace = false) {
}
}
// resolveDeadlinesNewURL builds the Step 3a "Enter" destination. For a
// real project: /projects/{id}/deadlines/new. For ad-hoc explore-mode:
// /deadlines/new (the project picker is in the form itself, but the
// user has no Akte to attach it to without picking one anew). m's
// 2026-05-08 spec: this is the manual-entry escape hatch.
function resolveDeadlinesNewURL(ctx: Step1Context): string {
if (ctx.kind === "project" && ctx.projectId) {
return `/projects/${encodeURIComponent(ctx.projectId)}/deadlines/new`;
}
return "/deadlines/new";
}
function showPathway(path: Pathway, mode?: BMode) {
// m's 2026-05-08 18:08 redesign retired the legacy fork; Step 1 and
// Step 2 sit where it used to live. The "fork" Pathway value now
@@ -2348,6 +2365,7 @@ function showPathway(path: Pathway, mode?: BMode) {
const step1 = document.getElementById("fristen-step1");
const step1Summary = document.getElementById("fristen-step1-summary");
const step2 = document.getElementById("fristen-step2");
const step3a = document.getElementById("fristen-step3a");
const a = document.getElementById("fristen-pathway-a");
const b = document.getElementById("fristen-pathway-b");
if (!a || !b) return;
@@ -2365,6 +2383,7 @@ function showPathway(path: Pathway, mode?: BMode) {
step1Summary.style.display = (ctx.kind !== "none" && path !== "fork") ? "" : step1Summary.style.display;
}
if (step2) step2.hidden = path !== "fork";
if (step3a) step3a.hidden = path !== "outgoing";
a.hidden = path !== "a";
b.hidden = path !== "b";
@@ -2617,23 +2636,39 @@ function initPathwayFork() {
}
});
// Step 2 cards — outgoing (Pathway A wizard) vs incoming (Pathway B
// cascade). showPathway() owns the actual A/B transition; we just
// drive it from the action choice.
// Step 2 cards — outgoing (Step 3a chooser) vs incoming (Pathway B
// cascade). showPathway() owns the actual transition; we just drive
// it from the action choice.
document.getElementById("fristen-step2-file")?.addEventListener("click", () => {
navigateToPathway("a");
navigateToPathway("outgoing");
});
document.getElementById("fristen-step2-happened")?.addEventListener("click", () => {
navigateToPathway("b", "tree");
});
// Step 3a cards — File / Draft / Enter. File drops into the existing
// Pathway A wizard; Enter routes to the manual-create form;
// Draft is a v1 placeholder (button disabled in markup, no handler).
document.getElementById("fristen-step3a-file")?.addEventListener("click", () => {
navigateToPathway("a");
});
document.getElementById("fristen-step3a-enter")?.addEventListener("click", () => {
window.location.href = resolveDeadlinesNewURL(currentStep1Context);
});
// Back-from-Pathway buttons return to Step 2 (the new "fork" state).
// Pathway A's back returns to Step 3a since that's where the user
// came from in the new flow; pre-Slice-3 muscle memory of Pathway A
// back going all the way to fork is preserved by clicking back twice.
document.getElementById("fristen-pathway-a-back")?.addEventListener("click", () => {
navigateToPathway("fork");
navigateToPathway("outgoing");
});
document.getElementById("fristen-pathway-b-back")?.addEventListener("click", () => {
navigateToPathway("fork");
});
document.getElementById("fristen-step3a-back")?.addEventListener("click", () => {
navigateToPathway("fork");
});
// B1/B2 mode toggle inside Pathway B.
const bModeRadios = document.querySelectorAll<HTMLInputElement>("input[name='fristen-b-mode']");

View File

@@ -266,6 +266,15 @@ const translations: Record<Lang, Record<string, string>> = {
"deadlines.step2.happened.title": "Etwas ist passiert",
"deadlines.step2.happened.desc": "Incoming — ein Ereignis hat eine Frist ausgelöst.",
"deadlines.save.cta.adhoc.hint": "Ad-hoc — kein Projekt, kein Speichern",
"deadlines.step3a.heading": "Was möchten Sie einreichen?",
"deadlines.step3a.back": "zurück zur Auswahl",
"deadlines.step3a.file.title": "Schriftsatz einreichen",
"deadlines.step3a.file.desc": "Verfahrensablauf laden — Frist berechnen und zur Akte hinzufügen.",
"deadlines.step3a.draft.title": "Schriftsatz entwerfen",
"deadlines.step3a.draft.desc": "Vorbereitung — später mit Drafting-Surface verknüpft.",
"deadlines.step3a.enter.title": "Frist manuell erfassen",
"deadlines.step3a.enter.desc": "Direkt eintragen — bereits bekanntes Datum / bekannter Typ.",
"deadlines.step3a.soon": "kommt bald",
"deadlines.date.edit.hint": "Datum bearbeiten — Folgefristen werden neu berechnet",
"deadlines.view.label": "Ansicht:",
"deadlines.view.timeline": "Zeitstrahl",
@@ -2362,6 +2371,15 @@ const translations: Record<Lang, Record<string, string>> = {
"deadlines.step2.happened.title": "Something happened",
"deadlines.step2.happened.desc": "Incoming — an event triggered a deadline.",
"deadlines.save.cta.adhoc.hint": "Ad-hoc — no matter, no save",
"deadlines.step3a.heading": "What do you want to file?",
"deadlines.step3a.back": "back to selection",
"deadlines.step3a.file.title": "File a submission",
"deadlines.step3a.file.desc": "Open the Verfahrensablauf — compute deadline and add to the matter.",
"deadlines.step3a.draft.title": "Draft a submission",
"deadlines.step3a.draft.desc": "Preparation — later linked to the drafting surface.",
"deadlines.step3a.enter.title": "Enter deadline manually",
"deadlines.step3a.enter.desc": "Direct entry — date and type already known.",
"deadlines.step3a.soon": "coming soon",
"deadlines.date.edit.hint": "Edit date — downstream deadlines will recalculate",
"deadlines.view.label": "View:",
"deadlines.view.timeline": "Timeline",

View File

@@ -316,6 +316,54 @@ export function renderFristenrechner(): string {
</div>
</div>
{/* Step 3a — outgoing-intent chooser. Reached when the user
picks "Etwas einreichen" on Step 2. Three options per
m's 2026-05-08 18:09 spec: File (drives the Pathway A
wizard), Draft (future drafting surface; v1
placeholder), Enter (routes to the existing manual-
create form). */}
<div className="fristen-pathway-shell" id="fristen-step3a" data-path="outgoing" hidden>
<button type="button" className="fristen-pathway-back" id="fristen-step3a-back">
<span aria-hidden="true">&larr;</span>{" "}
<span data-i18n="deadlines.step3a.back">zur&uuml;ck zur Auswahl</span>
</button>
<h2 className="fristen-pathway-heading">
<span aria-hidden="true">&#9999;&#65039;</span>{" "}
<span data-i18n="deadlines.step3a.heading">Was m&ouml;chten Sie einreichen?</span>
</h2>
<div className="fristen-step2-cards">
<button type="button" className="fristen-step2-card" id="fristen-step3a-file" data-action="file">
<span className="fristen-step2-card-icon" aria-hidden="true">&#128221;</span>
<span className="fristen-step2-card-title" data-i18n="deadlines.step3a.file.title">
Schriftsatz einreichen
</span>
<span className="fristen-step2-card-desc" data-i18n="deadlines.step3a.file.desc">
Verfahrensablauf laden &mdash; Frist berechnen und zur Akte hinzuf&uuml;gen.
</span>
</button>
<button type="button" className="fristen-step2-card fristen-step2-card--soon" id="fristen-step3a-draft" data-action="draft" disabled
data-i18n-title="deadlines.step3a.soon">
<span className="fristen-step2-card-icon" aria-hidden="true">&#128393;</span>
<span className="fristen-step2-card-title" data-i18n="deadlines.step3a.draft.title">
Schriftsatz entwerfen
</span>
<span className="fristen-step2-card-desc" data-i18n="deadlines.step3a.draft.desc">
Vorbereitung &mdash; sp&auml;ter mit Drafting-Surface verkn&uuml;pft.
</span>
<span className="fristen-step2-card-soon" data-i18n="deadlines.step3a.soon">kommt bald</span>
</button>
<button type="button" className="fristen-step2-card" id="fristen-step3a-enter" data-action="enter">
<span className="fristen-step2-card-icon" aria-hidden="true">&#128190;</span>
<span className="fristen-step2-card-title" data-i18n="deadlines.step3a.enter.title">
Frist manuell erfassen
</span>
<span className="fristen-step2-card-desc" data-i18n="deadlines.step3a.enter.desc">
Direkt eintragen &mdash; bereits bekanntes Datum / bekannter Typ.
</span>
</button>
</div>
</div>
{/* Pathway A container — wraps the existing wizard.
Hidden until ?path=a. */}
<div className="fristen-pathway-shell" id="fristen-pathway-a" data-path="a" hidden>

View File

@@ -957,6 +957,15 @@ export type I18nKey =
| "deadlines.step2.happened.title"
| "deadlines.step2.heading"
| "deadlines.step3"
| "deadlines.step3a.back"
| "deadlines.step3a.draft.desc"
| "deadlines.step3a.draft.title"
| "deadlines.step3a.enter.desc"
| "deadlines.step3a.enter.title"
| "deadlines.step3a.file.desc"
| "deadlines.step3a.file.title"
| "deadlines.step3a.heading"
| "deadlines.step3a.soon"
| "deadlines.subtitle"
| "deadlines.summary.completed"
| "deadlines.summary.later"

View File

@@ -7217,6 +7217,28 @@ input[type="range"]::-moz-range-thumb {
margin-top: 1rem;
}
/* Step 3a draft card — disabled placeholder for the future drafting
* surface. Greyed out, no hover lift, "kommt bald" pill on the card. */
.fristen-step2-card--soon {
opacity: 0.55;
cursor: not-allowed;
}
.fristen-step2-card--soon:hover {
background: var(--color-bg, #fff);
border-color: var(--color-border, #ddd);
transform: none;
}
.fristen-step2-card-soon {
align-self: flex-end;
padding: 1px 6px;
border-radius: 9999px;
background: var(--color-bg-subtle, #f4f4f4);
color: var(--color-muted, #666);
font-size: 0.7rem;
font-style: italic;
letter-spacing: 0.02em;
}
/* Compact one-line summary that replaces the four proceeding-group
* blocks once the user picks a Verfahren. m's 2026-05-08 18:26 ask. */
.proceeding-summary {