feat(t-paliad-131): Phase B1 — UPC counterclaim cross-flows
Closes m's primary complaint: today's `with_ccr` flag on UPC_INF only
swaps the Replik / Duplik durations. Per UPC RoP R.29 the with-CCR flow
ALSO adds 5–7 new submissions across the claimant / defendant exchange.
Same gap on UPC_REV: Application to amend (R.49.2.a → R.55 = R.32 m.m.)
and Counterclaim for infringement (R.49.2.b → R.50, R.56 cycle) were
entirely missing.
UPC_INF gets a nested `with_amend` flag under `with_ccr` (R.30 amend
is only available with a CCR). UPC_REV gets two parallel independent
flags `with_amend` + `with_cci`; both can be on. Citations verified
against data.laws_contents (youpcdb, UPCRoP).
Migration 041 (waved INSERTs because each subsequent rule references
the prior wave's parent_id):
- Wave 0: 11 new concept rows (counterclaim-for-revocation,
defence-to-counterclaim-for-revocation, defence-to-application-to-amend,
reply-to-defence-to-counterclaim-for-revocation,
reply-to-defence-to-application-to-amend,
rejoinder-on-reply-to-defence-to-ccr, rejoinder-on-reply-to-amend,
counterclaim-for-infringement, defence-to-counterclaim-for-infringement,
reply-to-defence-to-counterclaim-for-infringement,
rejoinder-on-counterclaim-for-infringement). counterclaim-for-revocation
also seeded for the search bar even though its rule lives implicitly
in inf.sod (the with_ccr flag captures it).
- UPC_INF + UPC_REV sequence_orders renumbered to leave gaps (10/20/30…)
so new cross-flow rows interleave chronologically with the backbone.
- 7 new UPC_INF rules: inf.def_to_ccr (R.29.a), inf.app_to_amend (R.30.1),
inf.def_to_amend (R.32.1), inf.reply_def_ccr (R.29.d),
inf.reply_def_amd (R.32.3), inf.rejoin_reply_ccr (R.29.e),
inf.rejoin_amd (R.32.3).
- 8 new UPC_REV rules: rev.app_to_amend (R.49.2.a), rev.def_to_amend
(R.43.3), rev.reply_def_amd (R.32.3 m.m.), rev.rejoin_amd (R.32.3 m.m.),
rev.cc_inf (R.49.2.b), rev.def_cci (R.56.1), rev.reply_def_cci (R.56.3),
rev.rejoin_cci (R.56.4).
Calculator (services/fristenrechner.go):
- Zero-duration rules now split into 4 buckets, not 2:
1. parent=nil + non-court → IsRootEvent (existing)
2. parent=nil + court → IsCourtSet (existing, e.g. inf.oral when stand-alone)
3. parent set + court → IsCourtSet (existing, waypoints)
4. parent set + non-court → "filed-with-parent" — inherit parent's
date. NEW. Used by rev.app_to_amend / rev.cc_inf which per
R.49(2) are filed AS PART OF the Defence to revocation.
- AnchorOverrides on a zero-duration rule short-circuits to the user's
date, propagating downstream as before.
Frontend:
- New checkboxes inf-amend-flag (UPC_INF, nested under ccr-flag),
rev-amend-flag, rev-cci-flag (UPC_REV). Visibility per proceeding
type; inf-amend disabled until ccr is on (R.30 dependency).
- Three new i18n keys (DE+EN). Small CSS for nested-checkbox indent
and disabled-state colour.
Live-verified via curl on paliad.de against tester@hlc.de:
UPC_INF + with_ccr+with_amend, trigger 2026-05-04 → all 7 new rules
render at correct dates (R.29.a 2mo, R.30.1 2mo, R.32.1 2mo from
app_to_amend, R.29.d 2mo from def_to_ccr, R.32.3 1mo, R.29.e 1mo,
R.32.3 1mo).
UPC_REV + with_amend+with_cci → rev.app_to_amend / rev.cc_inf show
rev.defence's date (filed-with-parent), R.43.3 2mo / R.56.1 2mo /
R.32.3 + R.56.3 1mo / R.32.3 + R.56.4 1mo all line up.
This commit is contained in:
@@ -202,10 +202,24 @@ async function calculate() {
|
||||
const priorityInput = document.getElementById("priority-date") as HTMLInputElement | null;
|
||||
const priorityDate = selectedType === "EP_GRANT" && priorityInput?.value ? priorityInput.value : "";
|
||||
|
||||
// Flags — UPC_INF surfaces "Mit Widerklage auf Nichtigkeit" toggle.
|
||||
const ccrFlag = document.getElementById("ccr-flag") as HTMLInputElement | null;
|
||||
// Flags — three proceeding-specific checkboxes:
|
||||
// UPC_INF: with_ccr (always available); with_amend (nested under
|
||||
// with_ccr — R.30 application is only available with a CCR).
|
||||
// UPC_REV: with_amend (R.49.2.a) and with_cci (R.49.2.b) as two
|
||||
// independent gates; both can be on simultaneously.
|
||||
const ccrFlag = document.getElementById("ccr-flag") as HTMLInputElement | null;
|
||||
const infAmendFlag = document.getElementById("inf-amend-flag") as HTMLInputElement | null;
|
||||
const revAmendFlag = document.getElementById("rev-amend-flag") as HTMLInputElement | null;
|
||||
const revCciFlag = document.getElementById("rev-cci-flag") as HTMLInputElement | null;
|
||||
const flags: string[] = [];
|
||||
if (selectedType === "UPC_INF" && ccrFlag?.checked) flags.push("with_ccr");
|
||||
if (selectedType === "UPC_INF") {
|
||||
if (ccrFlag?.checked) flags.push("with_ccr");
|
||||
if (ccrFlag?.checked && infAmendFlag?.checked) flags.push("with_amend");
|
||||
}
|
||||
if (selectedType === "UPC_REV") {
|
||||
if (revAmendFlag?.checked) flags.push("with_amend");
|
||||
if (revCciFlag?.checked) flags.push("with_cci");
|
||||
}
|
||||
|
||||
// Forward any user-set per-rule date overrides so downstream rules
|
||||
// re-anchor off them. Empty map → omitted from the payload.
|
||||
@@ -686,16 +700,41 @@ function selectProceeding(btn: HTMLButtonElement) {
|
||||
const name = btn.querySelector("strong")?.textContent || "";
|
||||
document.getElementById("trigger-event")!.textContent = name;
|
||||
|
||||
// Conditional inputs: priority date for EP_GRANT, CCR toggle for UPC_INF.
|
||||
// Conditional inputs:
|
||||
// priority-date → EP_GRANT
|
||||
// ccr-flag → UPC_INF only
|
||||
// inf-amend-flag → UPC_INF only, but disabled until ccr-flag is on
|
||||
// (R.30 amend only available with a CCR)
|
||||
// rev-amend-flag → UPC_REV only
|
||||
// rev-cci-flag → UPC_REV only
|
||||
const priorityRow = document.getElementById("priority-date-row");
|
||||
if (priorityRow) priorityRow.style.display = selectedType === "EP_GRANT" ? "" : "none";
|
||||
const ccrRow = document.getElementById("ccr-flag-row");
|
||||
if (ccrRow) ccrRow.style.display = selectedType === "UPC_INF" ? "" : "none";
|
||||
const infAmendRow = document.getElementById("inf-amend-flag-row");
|
||||
if (infAmendRow) infAmendRow.style.display = selectedType === "UPC_INF" ? "" : "none";
|
||||
const revAmendRow = document.getElementById("rev-amend-flag-row");
|
||||
if (revAmendRow) revAmendRow.style.display = selectedType === "UPC_REV" ? "" : "none";
|
||||
const revCciRow = document.getElementById("rev-cci-flag-row");
|
||||
if (revCciRow) revCciRow.style.display = selectedType === "UPC_REV" ? "" : "none";
|
||||
|
||||
syncInfAmendEnabled();
|
||||
|
||||
showStep(2);
|
||||
scheduleProcCalc(0);
|
||||
}
|
||||
|
||||
// inf-amend-flag is only meaningful when ccr-flag is on (R.30 application
|
||||
// is filed within the Defence to CCR). When ccr-flag flips off, also
|
||||
// untick inf-amend-flag so the calc payload stays coherent.
|
||||
function syncInfAmendEnabled() {
|
||||
const ccr = document.getElementById("ccr-flag") as HTMLInputElement | null;
|
||||
const infAmend = document.getElementById("inf-amend-flag") as HTMLInputElement | null;
|
||||
if (!ccr || !infAmend) return;
|
||||
infAmend.disabled = !ccr.checked;
|
||||
if (!ccr.checked) infAmend.checked = false;
|
||||
}
|
||||
|
||||
// View toggle wiring. Persist the choice in `?view=…` so reload / share-link
|
||||
// restores the same layout.
|
||||
function initViewToggle() {
|
||||
@@ -755,7 +794,17 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
}
|
||||
|
||||
const ccrFlag = document.getElementById("ccr-flag") as HTMLInputElement | null;
|
||||
if (ccrFlag) ccrFlag.addEventListener("change", () => scheduleProcCalc(0));
|
||||
if (ccrFlag) ccrFlag.addEventListener("change", () => {
|
||||
syncInfAmendEnabled();
|
||||
scheduleProcCalc(0);
|
||||
});
|
||||
|
||||
const infAmendFlag = document.getElementById("inf-amend-flag") as HTMLInputElement | null;
|
||||
if (infAmendFlag) infAmendFlag.addEventListener("change", () => scheduleProcCalc(0));
|
||||
const revAmendFlag = document.getElementById("rev-amend-flag") as HTMLInputElement | null;
|
||||
if (revAmendFlag) revAmendFlag.addEventListener("change", () => scheduleProcCalc(0));
|
||||
const revCciFlag = document.getElementById("rev-cci-flag") as HTMLInputElement | null;
|
||||
if (revCciFlag) revCciFlag.addEventListener("change", () => scheduleProcCalc(0));
|
||||
|
||||
// Click-to-edit on timeline / column dates: open an inline date input
|
||||
// and persist the user's choice as an anchor override so downstream
|
||||
|
||||
@@ -208,6 +208,9 @@ const translations: Record<Lang, Record<string, string>> = {
|
||||
"deadlines.trigger.label": "Ausgangsdatum",
|
||||
"deadlines.priority.date": "Priorit\u00e4tstag (optional):",
|
||||
"deadlines.flag.ccr": "Mit Widerklage auf Nichtigkeit",
|
||||
"deadlines.flag.inf_amend": "Mit Antrag auf Patentänderung (R.30)",
|
||||
"deadlines.flag.rev_amend": "Mit Antrag auf Patentänderung (R.49.2.a)",
|
||||
"deadlines.flag.rev_cci": "Mit Verletzungswiderklage (R.49.2.b)",
|
||||
"deadlines.calculate": "Fristen berechnen",
|
||||
"deadlines.print": "Drucken",
|
||||
"deadlines.reset": "\u2190 Neu berechnen",
|
||||
@@ -1728,6 +1731,9 @@ const translations: Record<Lang, Record<string, string>> = {
|
||||
"deadlines.trigger.label": "Trigger date",
|
||||
"deadlines.priority.date": "Priority date (optional):",
|
||||
"deadlines.flag.ccr": "Counterclaim for revocation filed",
|
||||
"deadlines.flag.inf_amend": "Application to amend the patent filed (R.30)",
|
||||
"deadlines.flag.rev_amend": "Application to amend the patent filed (R.49.2.a)",
|
||||
"deadlines.flag.rev_cci": "Counterclaim for infringement filed (R.49.2.b)",
|
||||
"deadlines.calculate": "Calculate Deadlines",
|
||||
"deadlines.print": "Print",
|
||||
"deadlines.reset": "\u2190 Start Over",
|
||||
|
||||
@@ -128,6 +128,24 @@ export function renderFristenrechner(): string {
|
||||
<span data-i18n="deadlines.flag.ccr">Mit Widerklage auf Nichtigkeit</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="date-field-row date-field-row--nested" id="inf-amend-flag-row" style="display:none">
|
||||
<label className="date-label">
|
||||
<input type="checkbox" id="inf-amend-flag" />
|
||||
<span data-i18n="deadlines.flag.inf_amend">Mit Antrag auf Patentänderung (R.30)</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="date-field-row" id="rev-amend-flag-row" style="display:none">
|
||||
<label className="date-label">
|
||||
<input type="checkbox" id="rev-amend-flag" />
|
||||
<span data-i18n="deadlines.flag.rev_amend">Mit Antrag auf Patentänderung (R.49.2.a)</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="date-field-row" id="rev-cci-flag-row" style="display:none">
|
||||
<label className="date-label">
|
||||
<input type="checkbox" id="rev-cci-flag" />
|
||||
<span data-i18n="deadlines.flag.rev_cci">Mit Verletzungswiderklage (R.49.2.b)</span>
|
||||
</label>
|
||||
</div>
|
||||
<button type="button" id="calculate-btn" className="calculate-btn" data-i18n="deadlines.calculate">
|
||||
Fristen berechnen
|
||||
</button>
|
||||
|
||||
@@ -638,6 +638,9 @@ export type I18nKey =
|
||||
| "deadlines.filter.thisweek"
|
||||
| "deadlines.filter.today"
|
||||
| "deadlines.flag.ccr"
|
||||
| "deadlines.flag.inf_amend"
|
||||
| "deadlines.flag.rev_amend"
|
||||
| "deadlines.flag.rev_cci"
|
||||
| "deadlines.heading"
|
||||
| "deadlines.kalender.empty"
|
||||
| "deadlines.kalender.heading"
|
||||
|
||||
@@ -1826,6 +1826,16 @@ input[type="range"]::-moz-range-thumb {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* Nested checkbox under a parent flag (e.g. UPC_INF inf-amend-flag is
|
||||
only meaningful with ccr-flag on — indent so the dependency is
|
||||
visible). */
|
||||
.date-field-row--nested {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
.date-field-row--nested input[type="checkbox"]:disabled + span {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.date-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
|
||||
Reference in New Issue
Block a user