Merge: t-paliad-157 Determinator Slice 1 — project picker + do/happened bifurcation
df04e50 — feat(fristenrechner/determinator): the legacy "Was möchten
Sie tun?" landing fork is replaced by:
Step 1: filtered Akte picker + "Neue Akte anlegen" link (bare; the
bounce-back to the wizard after creation is Slice 2 scope) +
4 ad-hoc chips driving ?ad_hoc=upc|de|epa|dpma.
Step 2: "Etwas einreichen" / "Etwas ist passiert" cards driving
showPathway('a' | 'b'). Quick-pick chips moved here from the old
fork. Pathway A/B back buttons return to Step 2.
Save CTA on Pathway A's wizard disables in ad-hoc mode with hint
"Ad-hoc — kein Projekt, kein Speichern" (DE+EN). The locked context
collapses to a one-line summary; Reselect re-expands.
URL contract:
?project=<uuid> | ?ad_hoc=upc|de|epa|dpma — Step 1 result
?path=a|b — Step 2 result (back-compat)
?mode=tree|filter — Pathway B sub-mode
Pathway A/B sub-routing primitives (showPathway, showBMode) unchanged
— Step 2 cards just drive the same hooks.
Still open:
Slice 2 — /projects/new return-bounce on save.
Slice 3+ — scoping the picker / cascade by project's proceeding-type
+ role; replacing the wizard with the Step 3a File/Draft/Enter
chooser.
This commit is contained in:
@@ -494,7 +494,22 @@ function renderProcedureResults(data: DeadlineResponse) {
|
||||
|
||||
container.innerHTML = headerHtml + bodyHtml;
|
||||
printBtn.style.display = "block";
|
||||
if (saveBtn) saveBtn.style.display = "block";
|
||||
if (saveBtn) {
|
||||
// Ad-hoc explore-mode has no project to save against — show the
|
||||
// CTA disabled with a hint so the user understands why the action
|
||||
// is blocked (m's 2026-05-08 Slice 1 lock #2). Hiding it would
|
||||
// leave the user wondering where the save went.
|
||||
saveBtn.style.display = "block";
|
||||
if (isAdhocMode()) {
|
||||
saveBtn.disabled = true;
|
||||
saveBtn.title = t("deadlines.save.cta.adhoc.hint");
|
||||
saveBtn.dataset.adhocDisabled = "true";
|
||||
} else {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.removeAttribute("title");
|
||||
delete saveBtn.dataset.adhocDisabled;
|
||||
}
|
||||
}
|
||||
if (toggle) toggle.style.display = "";
|
||||
|
||||
applyPendingFocus();
|
||||
@@ -2325,12 +2340,31 @@ function setPathwayURL(path: Pathway, mode?: BMode, replace = false) {
|
||||
}
|
||||
|
||||
function showPathway(path: Pathway, mode?: BMode) {
|
||||
const fork = document.getElementById("fristen-pathway-fork");
|
||||
// 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
|
||||
// means "show Step 1 + Step 2 (Step 2 visibility gated by whether a
|
||||
// project / ad-hoc context is selected — managed by initStep1Step2)";
|
||||
// "a" / "b" still drive the existing wizard / cascade shells.
|
||||
const step1 = document.getElementById("fristen-step1");
|
||||
const step1Summary = document.getElementById("fristen-step1-summary");
|
||||
const step2 = document.getElementById("fristen-step2");
|
||||
const a = document.getElementById("fristen-pathway-a");
|
||||
const b = document.getElementById("fristen-pathway-b");
|
||||
if (!fork || !a || !b) return;
|
||||
if (!a || !b) return;
|
||||
|
||||
fork.hidden = path !== "fork";
|
||||
// Step 1 + 2 stay mounted under "fork". Step 2 visibility is also
|
||||
// gated by the Step 1 context state — initStep1Step2 owns the
|
||||
// toggle between Step 1 expanded vs collapsed-with-summary, and
|
||||
// the "show Step 2" gate. We just hide them wholesale when not on
|
||||
// the fork.
|
||||
if (step1) step1.style.display = path === "fork" ? "" : "none";
|
||||
if (step1Summary) {
|
||||
// Summary stays visible from Step 2 onward so the user always
|
||||
// sees their selected Akte. Hidden only when no context is set.
|
||||
const ctx = readStep1ContextFromURL();
|
||||
step1Summary.style.display = (ctx.kind !== "none" && path !== "fork") ? "" : step1Summary.style.display;
|
||||
}
|
||||
if (step2) step2.hidden = path !== "fork";
|
||||
a.hidden = path !== "a";
|
||||
b.hidden = path !== "b";
|
||||
|
||||
@@ -2374,29 +2408,226 @@ function navigateToPathway(path: Pathway, mode?: BMode) {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// m's 2026-05-08 18:08 Determinator redesign — Step 1 + Step 2 state
|
||||
// ============================================================================
|
||||
// Step 1: pick the project (Akte) that scopes everything downstream, OR
|
||||
// pick an ad-hoc explore-mode chip (4 jurisdictions). Step 2: choose
|
||||
// between outgoing intent (Pathway A / Verfahrensablauf) and incoming
|
||||
// event (Pathway B / cascade). Step 3+ stay as today (Pathway A wizard,
|
||||
// B1 cascade, B2 search). The legacy "Was möchten Sie tun?" fork is
|
||||
// retired; back-buttons inside Pathway A/B return to Step 2.
|
||||
|
||||
type Step1ContextKind = "project" | "adhoc" | "none";
|
||||
type AdhocForum = "upc" | "de" | "epa" | "dpma";
|
||||
|
||||
interface Step1Context {
|
||||
kind: Step1ContextKind;
|
||||
projectId?: string;
|
||||
project?: ProjectOption;
|
||||
adhocForum?: AdhocForum;
|
||||
}
|
||||
|
||||
let currentStep1Context: Step1Context = { kind: "none" };
|
||||
let cachedAkten: ProjectOption[] = [];
|
||||
|
||||
function readStep1ContextFromURL(): Step1Context {
|
||||
const sp = new URLSearchParams(window.location.search);
|
||||
const project = sp.get("project");
|
||||
const adhoc = sp.get("ad_hoc");
|
||||
if (project) return { kind: "project", projectId: project };
|
||||
if (adhoc === "upc" || adhoc === "de" || adhoc === "epa" || adhoc === "dpma") {
|
||||
return { kind: "adhoc", adhocForum: adhoc };
|
||||
}
|
||||
return { kind: "none" };
|
||||
}
|
||||
|
||||
function writeStep1ContextToURL(ctx: Step1Context, replace = false) {
|
||||
const url = new URL(window.location.href);
|
||||
if (ctx.kind === "project" && ctx.projectId) {
|
||||
url.searchParams.set("project", ctx.projectId);
|
||||
url.searchParams.delete("ad_hoc");
|
||||
} else if (ctx.kind === "adhoc" && ctx.adhocForum) {
|
||||
url.searchParams.set("ad_hoc", ctx.adhocForum);
|
||||
url.searchParams.delete("project");
|
||||
} else {
|
||||
url.searchParams.delete("project");
|
||||
url.searchParams.delete("ad_hoc");
|
||||
}
|
||||
if (replace) window.history.replaceState({}, "", url.toString());
|
||||
else window.history.pushState({}, "", url.toString());
|
||||
}
|
||||
|
||||
// isAdhocMode is read by the save-to-project CTA — ad-hoc has no
|
||||
// project to save against, so the CTA disables and renders a hint.
|
||||
function isAdhocMode(): boolean {
|
||||
return currentStep1Context.kind === "adhoc";
|
||||
}
|
||||
|
||||
function adhocSummaryLabel(forum: AdhocForum): string {
|
||||
switch (forum) {
|
||||
case "upc": return "Ad-hoc UPC";
|
||||
case "de": return "Ad-hoc DE";
|
||||
case "epa": return "Ad-hoc EPA";
|
||||
case "dpma": return "Ad-hoc DPMA";
|
||||
}
|
||||
}
|
||||
|
||||
function renderAkteList(query: string) {
|
||||
const list = document.getElementById("fristen-akte-list");
|
||||
if (!list) return;
|
||||
const q = query.trim().toLowerCase();
|
||||
const filtered = q === ""
|
||||
? cachedAkten.slice(0, 12)
|
||||
: cachedAkten.filter((p) =>
|
||||
(p.title || "").toLowerCase().includes(q) ||
|
||||
(p.reference || "").toLowerCase().includes(q));
|
||||
if (filtered.length === 0) {
|
||||
list.innerHTML = `<li class="fristen-akte-list-empty">${escHtml(t("deadlines.step1.search.empty"))}</li>`;
|
||||
return;
|
||||
}
|
||||
list.innerHTML = filtered.map((p) => {
|
||||
const ref = p.reference ? `<span class="fristen-akte-list-ref">${escHtml(p.reference)}</span> · ` : "";
|
||||
return `<li><button type="button" class="fristen-akte-list-item" data-project-id="${escAttr(p.id)}">
|
||||
${ref}<span class="fristen-akte-list-title">${escHtml(p.title)}</span>
|
||||
</button></li>`;
|
||||
}).join("");
|
||||
list.querySelectorAll<HTMLButtonElement>(".fristen-akte-list-item").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const id = btn.dataset.projectId!;
|
||||
const project = cachedAkten.find((p) => p.id === id);
|
||||
if (project) selectProject(project);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function selectProject(project: ProjectOption) {
|
||||
currentStep1Context = { kind: "project", projectId: project.id, project };
|
||||
writeStep1ContextToURL(currentStep1Context);
|
||||
renderStep1Summary();
|
||||
showStep2Card();
|
||||
}
|
||||
|
||||
function selectAdhoc(forum: AdhocForum) {
|
||||
currentStep1Context = { kind: "adhoc", adhocForum: forum };
|
||||
writeStep1ContextToURL(currentStep1Context);
|
||||
renderStep1Summary();
|
||||
showStep2Card();
|
||||
}
|
||||
|
||||
function clearStep1Context() {
|
||||
currentStep1Context = { kind: "none" };
|
||||
writeStep1ContextToURL(currentStep1Context);
|
||||
renderStep1Summary();
|
||||
hideStep2Card();
|
||||
}
|
||||
|
||||
function renderStep1Summary() {
|
||||
const summary = document.getElementById("fristen-step1-summary") as HTMLElement | null;
|
||||
const name = document.getElementById("fristen-step1-summary-name");
|
||||
const meta = document.getElementById("fristen-step1-summary-meta");
|
||||
const step1 = document.getElementById("fristen-step1") as HTMLElement | null;
|
||||
if (!summary || !name || !step1) return;
|
||||
|
||||
if (currentStep1Context.kind === "none") {
|
||||
summary.style.display = "none";
|
||||
step1.style.display = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentStep1Context.kind === "project" && currentStep1Context.project) {
|
||||
const p = currentStep1Context.project;
|
||||
const ref = p.reference ? p.reference + " · " : "";
|
||||
name.textContent = ref + p.title;
|
||||
if (meta) meta.textContent = "";
|
||||
} else if (currentStep1Context.kind === "adhoc" && currentStep1Context.adhocForum) {
|
||||
name.textContent = adhocSummaryLabel(currentStep1Context.adhocForum);
|
||||
if (meta) meta.textContent = " · " + t("deadlines.step1.summary.adhoc.suffix");
|
||||
}
|
||||
summary.style.display = "";
|
||||
step1.style.display = "none";
|
||||
}
|
||||
|
||||
function showStep2Card() {
|
||||
const step2 = document.getElementById("fristen-step2");
|
||||
if (step2) step2.hidden = false;
|
||||
}
|
||||
|
||||
function hideStep2Card() {
|
||||
const step2 = document.getElementById("fristen-step2");
|
||||
if (step2) step2.hidden = true;
|
||||
}
|
||||
|
||||
function initPathwayFork() {
|
||||
// Set chip labels to active language before user sees them.
|
||||
relabelChips();
|
||||
// Initial render from URL (or saved preference if URL is bare).
|
||||
|
||||
// Hydrate Step 1 context from URL first — Step 2 visibility depends
|
||||
// on whether a project / ad-hoc chip is already locked in.
|
||||
currentStep1Context = readStep1ContextFromURL();
|
||||
|
||||
// Initial Pathway render. Inherits the URL ?path= semantic — Step 2
|
||||
// having been satisfied is implied if path = a / b.
|
||||
const initial = readPathwayFromURL();
|
||||
const initialMode = readBModeFromURL();
|
||||
showPathway(initial, initialMode);
|
||||
|
||||
// Persist initial choice from URL.
|
||||
// Step 1 summary visibility flows from the context kind.
|
||||
renderStep1Summary();
|
||||
if (currentStep1Context.kind !== "none") {
|
||||
showStep2Card();
|
||||
}
|
||||
|
||||
if (initial !== "fork") {
|
||||
try { localStorage.setItem(PATHWAY_STORAGE_KEY, initial); } catch { /* */ }
|
||||
}
|
||||
|
||||
// Click handlers on the two fork cards.
|
||||
document.getElementById("fristen-pathway-a-cta")?.addEventListener("click", () => {
|
||||
// Step 1 — fetch projects + render filtered list. Search filters the
|
||||
// list in-place; click on a row drops the user into Step 2.
|
||||
void (async () => {
|
||||
cachedAkten = await fetchProjects();
|
||||
if (currentStep1Context.kind === "project" && currentStep1Context.projectId) {
|
||||
currentStep1Context.project = cachedAkten.find((p) => p.id === currentStep1Context.projectId);
|
||||
renderStep1Summary();
|
||||
}
|
||||
renderAkteList("");
|
||||
})();
|
||||
|
||||
const akteSearch = document.getElementById("fristen-akte-search") as HTMLInputElement | null;
|
||||
if (akteSearch) {
|
||||
akteSearch.addEventListener("input", () => renderAkteList(akteSearch.value));
|
||||
}
|
||||
|
||||
// Ad-hoc chips — explore-mode escape hatch. No DB write; the
|
||||
// save-modal CTA disables itself in this state.
|
||||
document.querySelectorAll<HTMLButtonElement>(".fristen-adhoc-chip").forEach((chip) => {
|
||||
chip.addEventListener("click", () => {
|
||||
const forum = chip.dataset.adHoc as AdhocForum | undefined;
|
||||
if (forum) selectAdhoc(forum);
|
||||
});
|
||||
});
|
||||
|
||||
// Reselect: drop the locked context, return to Step 1.
|
||||
document.getElementById("fristen-step1-summary-reselect")?.addEventListener("click", () => {
|
||||
clearStep1Context();
|
||||
// Bounce back to fork (Step 1 + 2) so the user sees the picker,
|
||||
// even if they were currently in Pathway A or B.
|
||||
if (initial !== "fork") {
|
||||
navigateToPathway("fork");
|
||||
}
|
||||
});
|
||||
|
||||
// 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.
|
||||
document.getElementById("fristen-step2-file")?.addEventListener("click", () => {
|
||||
navigateToPathway("a");
|
||||
});
|
||||
document.getElementById("fristen-pathway-b-cta")?.addEventListener("click", () => {
|
||||
// Default to tree mode on first entry to Pathway B.
|
||||
document.getElementById("fristen-step2-happened")?.addEventListener("click", () => {
|
||||
navigateToPathway("b", "tree");
|
||||
});
|
||||
|
||||
// Back-to-fork buttons inside each pathway shell.
|
||||
// Back-from-Pathway buttons return to Step 2 (the new "fork" state).
|
||||
document.getElementById("fristen-pathway-a-back")?.addEventListener("click", () => {
|
||||
navigateToPathway("fork");
|
||||
});
|
||||
@@ -2415,7 +2646,9 @@ function initPathwayFork() {
|
||||
});
|
||||
});
|
||||
|
||||
// Quick-pick chips on the fork shortcut row → jump straight to Pathway B + filter mode + prefilled query.
|
||||
// Quick-pick chips on the Step 2 shortcut row → jump straight to
|
||||
// Pathway B + filter mode + prefilled query. Same behaviour as the
|
||||
// legacy fork; only the ID's mounting point changed.
|
||||
document.querySelectorAll<HTMLButtonElement>("#fristen-fork-chips .fristen-search-chip").forEach((chip) => {
|
||||
chip.addEventListener("click", () => {
|
||||
const q = chipQueryFor(chip);
|
||||
@@ -2425,8 +2658,6 @@ function initPathwayFork() {
|
||||
if (q) url.searchParams.set("q", q);
|
||||
window.history.pushState({}, "", url.toString());
|
||||
showPathway("b", "filter");
|
||||
// initSearch listens for popstate, but we used pushState; sync the
|
||||
// search input directly.
|
||||
const input = document.getElementById("fristen-search-input") as HTMLInputElement | null;
|
||||
if (input && q) {
|
||||
input.value = q;
|
||||
@@ -2435,8 +2666,14 @@ function initPathwayFork() {
|
||||
});
|
||||
});
|
||||
|
||||
// Browser back/forward should restore pathway state.
|
||||
// Browser back/forward restores pathway + Step 1 context.
|
||||
window.addEventListener("popstate", () => {
|
||||
currentStep1Context = readStep1ContextFromURL();
|
||||
if (currentStep1Context.kind === "project" && currentStep1Context.projectId) {
|
||||
currentStep1Context.project = cachedAkten.find((p) => p.id === currentStep1Context.projectId);
|
||||
}
|
||||
renderStep1Summary();
|
||||
if (currentStep1Context.kind !== "none") showStep2Card(); else hideStep2Card();
|
||||
const path = readPathwayFromURL();
|
||||
const mode = readBModeFromURL();
|
||||
showPathway(path, mode);
|
||||
|
||||
@@ -247,6 +247,25 @@ const translations: Record<Lang, Record<string, string>> = {
|
||||
"deadlines.optional.badge": "auf Antrag",
|
||||
"deadlines.proceeding.selected": "Verfahren:",
|
||||
"deadlines.proceeding.reselect": "Anderes Verfahren wählen",
|
||||
"deadlines.step1.heading": "Schritt 1 — Welche Akte?",
|
||||
"deadlines.step1.search.placeholder": "Akte suchen…",
|
||||
"deadlines.step1.search.empty": "Keine passende Akte gefunden.",
|
||||
"deadlines.step1.divider.new": "oder eine neue Akte",
|
||||
"deadlines.step1.divider.adhoc": "oder ad-hoc, ohne Akte",
|
||||
"deadlines.step1.new.cta": "+ Neue Akte anlegen",
|
||||
"deadlines.step1.adhoc.upc": "Custom UPC-Verfahren",
|
||||
"deadlines.step1.adhoc.de": "Custom DE-Verfahren",
|
||||
"deadlines.step1.adhoc.epa": "Custom EPA-Verfahren",
|
||||
"deadlines.step1.adhoc.dpma": "Custom DPMA-Verfahren",
|
||||
"deadlines.step1.selected": "Akte:",
|
||||
"deadlines.step1.reselect": "Andere Akte",
|
||||
"deadlines.step1.summary.adhoc.suffix": "ohne Akte (Erkundung)",
|
||||
"deadlines.step2.heading": "Schritt 2 — Was möchten Sie tun?",
|
||||
"deadlines.step2.file.title": "Etwas einreichen",
|
||||
"deadlines.step2.file.desc": "Outgoing — eine Frist tritt aus eigener Handlung ein.",
|
||||
"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.date.edit.hint": "Datum bearbeiten — Folgefristen werden neu berechnet",
|
||||
"deadlines.view.label": "Ansicht:",
|
||||
"deadlines.view.timeline": "Zeitstrahl",
|
||||
@@ -2324,6 +2343,25 @@ const translations: Record<Lang, Record<string, string>> = {
|
||||
"deadlines.optional.badge": "on request",
|
||||
"deadlines.proceeding.selected": "Proceeding:",
|
||||
"deadlines.proceeding.reselect": "Choose another proceeding",
|
||||
"deadlines.step1.heading": "Step 1 — Which matter?",
|
||||
"deadlines.step1.search.placeholder": "Search matters…",
|
||||
"deadlines.step1.search.empty": "No matching matter.",
|
||||
"deadlines.step1.divider.new": "or a new matter",
|
||||
"deadlines.step1.divider.adhoc": "or ad-hoc, without a matter",
|
||||
"deadlines.step1.new.cta": "+ Create new matter",
|
||||
"deadlines.step1.adhoc.upc": "Custom UPC proceeding",
|
||||
"deadlines.step1.adhoc.de": "Custom DE proceeding",
|
||||
"deadlines.step1.adhoc.epa": "Custom EPA proceeding",
|
||||
"deadlines.step1.adhoc.dpma": "Custom DPMA proceeding",
|
||||
"deadlines.step1.selected": "Matter:",
|
||||
"deadlines.step1.reselect": "Other matter",
|
||||
"deadlines.step1.summary.adhoc.suffix": "no matter (exploration)",
|
||||
"deadlines.step2.heading": "Step 2 — What do you want to do?",
|
||||
"deadlines.step2.file.title": "File something",
|
||||
"deadlines.step2.file.desc": "Outgoing — your action triggers a deadline.",
|
||||
"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.date.edit.hint": "Edit date — downstream deadlines will recalculate",
|
||||
"deadlines.view.label": "View:",
|
||||
"deadlines.view.timeline": "Timeline",
|
||||
|
||||
@@ -112,27 +112,99 @@ export function renderFristenrechner(): string {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* v3 landing fork (t-paliad-133) — visible by default, hidden once
|
||||
the user picks a pathway. URL ?path= drives visibility. */}
|
||||
<div className="fristen-pathway-fork" id="fristen-pathway-fork" role="group" aria-label="Pathway selector">
|
||||
<h2 className="fristen-pathway-fork-heading" data-i18n="deadlines.pathway.fork.heading">Was möchten Sie tun?</h2>
|
||||
<div className="fristen-pathway-fork-cards">
|
||||
<button type="button" className="fristen-pathway-card" data-path="a" id="fristen-pathway-a-cta">
|
||||
<span className="fristen-pathway-card-icon" aria-hidden="true">📖</span>
|
||||
<span className="fristen-pathway-card-title" data-i18n="deadlines.pathway.a.title">Verfahrensablauf informieren</span>
|
||||
<span className="fristen-pathway-card-desc" data-i18n="deadlines.pathway.a.desc">
|
||||
Verfahrenstyp wählen und alle dazugehörigen Fristen auf einer Zeitleiste sehen.
|
||||
{/* m's 2026-05-08 18:08 Determinator redesign — Step 1: pick the
|
||||
Akte (project) that scopes the rest of the flow. Filtered
|
||||
list of visible projects + "Neue Akte anlegen" link +
|
||||
four ad-hoc explore-mode chips for users who just want to
|
||||
look up a rule without saving anywhere. */}
|
||||
<div className="fristen-step1" id="fristen-step1" role="group" aria-label="Akte picker">
|
||||
<h2 className="fristen-step-heading" data-i18n="deadlines.step1.heading">
|
||||
Schritt 1 — Welche Akte?
|
||||
</h2>
|
||||
<div className="fristen-step1-search-row">
|
||||
<svg className="fristen-search-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<circle cx="11" cy="11" r="7"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<input type="search" id="fristen-akte-search"
|
||||
className="fristen-akte-search" autocomplete="off"
|
||||
data-i18n-placeholder="deadlines.step1.search.placeholder"
|
||||
placeholder="Akte suchen…" />
|
||||
</div>
|
||||
<ul className="fristen-akte-list" id="fristen-akte-list" role="listbox" aria-label="Akten"></ul>
|
||||
|
||||
<div className="fristen-step1-divider">
|
||||
<span data-i18n="deadlines.step1.divider.new">oder eine neue Akte</span>
|
||||
</div>
|
||||
<a href="/projects/new" className="fristen-step1-new" id="fristen-step1-new"
|
||||
data-i18n="deadlines.step1.new.cta">
|
||||
+ Neue Akte anlegen
|
||||
</a>
|
||||
|
||||
<div className="fristen-step1-divider">
|
||||
<span data-i18n="deadlines.step1.divider.adhoc">oder ad-hoc, ohne Akte</span>
|
||||
</div>
|
||||
<div className="fristen-adhoc-chips" role="group" aria-label="Ad-hoc proceeding">
|
||||
<button type="button" className="fristen-adhoc-chip" data-ad-hoc="upc"
|
||||
data-i18n="deadlines.step1.adhoc.upc">
|
||||
Custom UPC proceeding
|
||||
</button>
|
||||
<button type="button" className="fristen-adhoc-chip" data-ad-hoc="de"
|
||||
data-i18n="deadlines.step1.adhoc.de">
|
||||
Custom DE proceeding
|
||||
</button>
|
||||
<button type="button" className="fristen-adhoc-chip" data-ad-hoc="epa"
|
||||
data-i18n="deadlines.step1.adhoc.epa">
|
||||
Custom EPA proceeding
|
||||
</button>
|
||||
<button type="button" className="fristen-adhoc-chip" data-ad-hoc="dpma"
|
||||
data-i18n="deadlines.step1.adhoc.dpma">
|
||||
Custom DPMA proceeding
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step 1 collapsed summary, shown after a pick. Mirrors the
|
||||
proceeding-summary collapse pattern from 097e21c. */}
|
||||
<div className="fristen-step1-summary" id="fristen-step1-summary" style="display:none" role="group">
|
||||
<span className="fristen-step1-summary-label" data-i18n="deadlines.step1.selected">Akte:</span>
|
||||
<strong className="fristen-step1-summary-name" id="fristen-step1-summary-name">—</strong>
|
||||
<span className="fristen-step1-summary-meta" id="fristen-step1-summary-meta"></span>
|
||||
<button type="button" className="fristen-step1-summary-reselect" id="fristen-step1-summary-reselect"
|
||||
data-i18n="deadlines.step1.reselect">
|
||||
Andere Akte
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Step 2 — Do / Happened bifurcation. Hidden until Step 1 is
|
||||
satisfied. Click on a card routes to the existing Pathway A
|
||||
(Verfahrensablauf wizard) or Pathway B (cascade) shells —
|
||||
we keep the routing primitive in showPathway()/showBMode(). */}
|
||||
<div className="fristen-step2" id="fristen-step2" hidden>
|
||||
<h2 className="fristen-step-heading" data-i18n="deadlines.step2.heading">
|
||||
Schritt 2 — Was möchten Sie tun?
|
||||
</h2>
|
||||
<div className="fristen-step2-cards">
|
||||
<button type="button" className="fristen-step2-card" data-action="file" id="fristen-step2-file">
|
||||
<span className="fristen-step2-card-icon" aria-hidden="true">✏️</span>
|
||||
<span className="fristen-step2-card-title" data-i18n="deadlines.step2.file.title">
|
||||
Etwas einreichen
|
||||
</span>
|
||||
<span className="fristen-step2-card-desc" data-i18n="deadlines.step2.file.desc">
|
||||
Outgoing — eine Frist tritt aus eigener Handlung ein.
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" className="fristen-pathway-card" data-path="b" id="fristen-pathway-b-cta">
|
||||
<span className="fristen-pathway-card-icon" aria-hidden="true">📅</span>
|
||||
<span className="fristen-pathway-card-title" data-i18n="deadlines.pathway.b.title">Frist eintragen aufgrund Ereignis</span>
|
||||
<span className="fristen-pathway-card-desc" data-i18n="deadlines.pathway.b.desc">
|
||||
Ein Ereignis ist eingetreten — ich brauche die richtige Frist für meine Akte.
|
||||
<button type="button" className="fristen-step2-card" data-action="happened" id="fristen-step2-happened">
|
||||
<span className="fristen-step2-card-icon" aria-hidden="true">📥</span>
|
||||
<span className="fristen-step2-card-title" data-i18n="deadlines.step2.happened.title">
|
||||
Etwas ist passiert
|
||||
</span>
|
||||
<span className="fristen-step2-card-desc" data-i18n="deadlines.step2.happened.desc">
|
||||
Incoming — ein Ereignis hat eine Frist ausgelöst.
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="fristen-pathway-fork-shortcut">
|
||||
<div className="fristen-step2-shortcut">
|
||||
<div className="fristen-pathway-fork-shortcut-label" data-i18n="deadlines.pathway.shortcut.label">
|
||||
oder direkt zu einer Frist springen:
|
||||
</div>
|
||||
|
||||
@@ -898,6 +898,7 @@ export type I18nKey =
|
||||
| "deadlines.proceeding.selected"
|
||||
| "deadlines.reset"
|
||||
| "deadlines.save.cta"
|
||||
| "deadlines.save.cta.adhoc.hint"
|
||||
| "deadlines.save.error"
|
||||
| "deadlines.save.modal.akte"
|
||||
| "deadlines.save.modal.akte.choose"
|
||||
@@ -936,7 +937,25 @@ export type I18nKey =
|
||||
| "deadlines.status.pending"
|
||||
| "deadlines.status.waived"
|
||||
| "deadlines.step1"
|
||||
| "deadlines.step1.adhoc.de"
|
||||
| "deadlines.step1.adhoc.dpma"
|
||||
| "deadlines.step1.adhoc.epa"
|
||||
| "deadlines.step1.adhoc.upc"
|
||||
| "deadlines.step1.divider.adhoc"
|
||||
| "deadlines.step1.divider.new"
|
||||
| "deadlines.step1.heading"
|
||||
| "deadlines.step1.new.cta"
|
||||
| "deadlines.step1.reselect"
|
||||
| "deadlines.step1.search.empty"
|
||||
| "deadlines.step1.search.placeholder"
|
||||
| "deadlines.step1.selected"
|
||||
| "deadlines.step1.summary.adhoc.suffix"
|
||||
| "deadlines.step2"
|
||||
| "deadlines.step2.file.desc"
|
||||
| "deadlines.step2.file.title"
|
||||
| "deadlines.step2.happened.desc"
|
||||
| "deadlines.step2.happened.title"
|
||||
| "deadlines.step2.heading"
|
||||
| "deadlines.step3"
|
||||
| "deadlines.subtitle"
|
||||
| "deadlines.summary.completed"
|
||||
|
||||
@@ -6949,6 +6949,274 @@ input[type="range"]::-moz-range-thumb {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* m's 2026-05-08 Determinator redesign — Step 1 (Akte picker) + Step 2
|
||||
* (Do/Happened bifurcation) sit where the legacy "Was möchten Sie tun?"
|
||||
* fork used to be. */
|
||||
|
||||
.fristen-step-heading {
|
||||
font-size: 1.05rem;
|
||||
margin: 0 0 0.75rem;
|
||||
color: var(--color-text, #222);
|
||||
}
|
||||
|
||||
.fristen-step1 {
|
||||
margin: 0.5rem 0 1.25rem;
|
||||
}
|
||||
|
||||
.fristen-step1-search-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.4rem 0.75rem;
|
||||
border: 1px solid var(--color-border, #ddd);
|
||||
border-radius: 0.5rem;
|
||||
background: var(--color-bg, #fff);
|
||||
}
|
||||
|
||||
.fristen-step1-search-row .fristen-search-icon {
|
||||
color: var(--color-muted, #666);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.fristen-akte-search {
|
||||
flex: 1;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
background: transparent;
|
||||
color: var(--color-text, #222);
|
||||
font-size: 0.95rem;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.fristen-akte-list {
|
||||
list-style: none;
|
||||
margin: 0.5rem 0 0;
|
||||
padding: 0;
|
||||
max-height: 280px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--color-border, #e5e5e5);
|
||||
border-radius: 0.5rem;
|
||||
background: var(--color-bg, #fff);
|
||||
}
|
||||
|
||||
.fristen-akte-list li {
|
||||
border-bottom: 1px solid var(--color-border, #f0f0f0);
|
||||
}
|
||||
|
||||
.fristen-akte-list li:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.fristen-akte-list-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding: 0.55rem 0.85rem;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--color-text, #222);
|
||||
font-size: 0.92rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fristen-akte-list-item:hover {
|
||||
background: var(--color-bg-subtle, #f7f7f7);
|
||||
}
|
||||
|
||||
.fristen-akte-list-item:focus-visible {
|
||||
outline: 2px solid var(--color-accent, #c6f41c);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.fristen-akte-list-ref {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-muted, #666);
|
||||
}
|
||||
|
||||
.fristen-akte-list-title {
|
||||
color: var(--color-text, #222);
|
||||
}
|
||||
|
||||
.fristen-akte-list-empty {
|
||||
padding: 0.7rem 0.85rem;
|
||||
color: var(--color-muted, #666);
|
||||
font-size: 0.9rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.fristen-step1-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
margin: 1rem 0 0.5rem;
|
||||
color: var(--color-muted, #666);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.fristen-step1-divider::before,
|
||||
.fristen-step1-divider::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: var(--color-border, #e5e5e5);
|
||||
}
|
||||
|
||||
.fristen-step1-new {
|
||||
display: inline-block;
|
||||
padding: 0.4rem 0.85rem;
|
||||
border: 1px solid var(--color-border, #ddd);
|
||||
border-radius: 0.5rem;
|
||||
background: var(--color-bg, #fff);
|
||||
color: var(--color-text, #222);
|
||||
font-size: 0.9rem;
|
||||
text-decoration: none;
|
||||
transition: background 120ms, border-color 120ms;
|
||||
}
|
||||
|
||||
.fristen-step1-new:hover {
|
||||
background: var(--color-bg-subtle, #f4f4f4);
|
||||
border-color: var(--color-text-muted, #aaa);
|
||||
}
|
||||
|
||||
.fristen-adhoc-chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.fristen-adhoc-chip {
|
||||
padding: 0.35rem 0.85rem;
|
||||
border: 1px dashed var(--color-border, #c5c5c5);
|
||||
border-radius: 9999px;
|
||||
background: var(--color-bg, #fff);
|
||||
color: var(--color-text-muted, #555);
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
transition: background 120ms, border-color 120ms, color 120ms;
|
||||
}
|
||||
|
||||
.fristen-adhoc-chip:hover {
|
||||
background: var(--color-bg-subtle, #f4f4f4);
|
||||
border-style: solid;
|
||||
color: var(--color-text, #222);
|
||||
}
|
||||
|
||||
.fristen-adhoc-chip:focus-visible {
|
||||
outline: 2px solid var(--color-accent, #c6f41c);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.fristen-step1-summary {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
margin: 0.5rem 0 1rem;
|
||||
padding: 0.5rem 0.85rem;
|
||||
border: 1px solid var(--color-border, #e5e5e5);
|
||||
border-radius: 0.5rem;
|
||||
background: var(--color-bg-subtle, #fafafa);
|
||||
font-size: 0.95rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.fristen-step1-summary-label {
|
||||
color: var(--color-muted, #666);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.fristen-step1-summary-name {
|
||||
color: var(--color-text, #222);
|
||||
}
|
||||
|
||||
.fristen-step1-summary-meta {
|
||||
color: var(--color-muted, #666);
|
||||
font-size: 0.85rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.fristen-step1-summary-reselect {
|
||||
margin-left: 0.4rem;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border: 1px solid var(--color-border, #ddd);
|
||||
border-radius: 9999px;
|
||||
background: var(--color-bg, #fff);
|
||||
color: var(--color-text-muted, #555);
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
transition: background 120ms, border-color 120ms;
|
||||
}
|
||||
|
||||
.fristen-step1-summary-reselect:hover {
|
||||
background: var(--color-bg-subtle, #f4f4f4);
|
||||
border-color: var(--color-text-muted, #aaa);
|
||||
}
|
||||
|
||||
.fristen-step1-summary-reselect:focus-visible {
|
||||
outline: 2px solid var(--color-accent, #c6f41c);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.fristen-step2 {
|
||||
margin: 0.75rem 0 1.25rem;
|
||||
}
|
||||
|
||||
.fristen-step2-cards {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.fristen-step2-cards { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.fristen-step2-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.4rem;
|
||||
padding: 1rem 1.1rem;
|
||||
border: 1px solid var(--color-border, #ddd);
|
||||
border-radius: 0.6rem;
|
||||
background: var(--color-bg, #fff);
|
||||
color: var(--color-text, #222);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: background 120ms, border-color 120ms, transform 120ms;
|
||||
}
|
||||
|
||||
.fristen-step2-card:hover {
|
||||
background: var(--color-bg-subtle, #fafafa);
|
||||
border-color: var(--color-text-muted, #aaa);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.fristen-step2-card:focus-visible {
|
||||
outline: 2px solid var(--color-accent, #c6f41c);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.fristen-step2-card-icon {
|
||||
font-size: 1.4rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.fristen-step2-card-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.fristen-step2-card-desc {
|
||||
color: var(--color-muted, #666);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.fristen-step2-shortcut {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
|
||||
Reference in New Issue
Block a user