`;
}
-function renderItem(it: AgendaItem): string {
+// F-32: an item's urgency tag duplicates the day-bucket heading in the
+// common case (a "Heute" item under HEUTE, a "Diese Woche" item under "in 3
+// Tagen"). The tag stays only when it disagrees with the bucket — e.g. an
+// "Überfällig" deadline that lands in today's bucket because of a filter
+// quirk. expectedUrgency mirrors the server's bucketing rule against the
+// bucket's day.
+function expectedUrgency(day: Date): Urgency {
+ const today = startOfToday();
+ const diff = Math.round((day.getTime() - today.getTime()) / 86400000);
+ if (diff < 0) return "overdue";
+ if (diff === 0) return "today";
+ if (diff === 1) return "tomorrow";
+ if (diff <= 6) return "this_week";
+ return "later";
+}
+
+function renderItem(it: AgendaItem, bucketUrgency: Urgency): string {
const urgencyClass = `agenda-item-${it.urgency}`;
const typeClass = `agenda-item-type-${it.type}`;
const iconHTML = it.type === "deadline" ? deadlineIcon() : appointmentIcon();
@@ -230,7 +247,9 @@ function renderItem(it: AgendaItem): string {
const timePart = it.type === "appointment"
? `${esc(formatAppointmentTime(it))}`
: "";
- const urgencyTag = `${esc(t(`agenda.urgency.${it.urgency}`))}`;
+ const urgencyTag = it.urgency !== bucketUrgency
+ ? `${esc(t(`agenda.urgency.${it.urgency}`))}`
+ : "";
const locationPart = it.type === "appointment" && it.location
? `${esc(it.location)}`
: "";
diff --git a/frontend/src/client/bottom-nav.ts b/frontend/src/client/bottom-nav.ts
index 5545792..7e874af 100644
--- a/frontend/src/client/bottom-nav.ts
+++ b/frontend/src/client/bottom-nav.ts
@@ -1,4 +1,5 @@
import { toggleMobileSidebar } from "./sidebar";
+import { t } from "./i18n";
const KEYBOARD_THRESHOLD_PX = 100;
const BADGE_REFRESH_MS = 60_000;
@@ -99,11 +100,22 @@ function initAgendaBadge(): void {
if (total <= 0) {
badge!.style.display = "none";
badge!.classList.remove("bottom-nav-badge-overdue");
+ badge!.removeAttribute("title");
+ badge!.removeAttribute("aria-label");
return;
}
badge!.textContent = total > 9 ? "9+" : String(total);
badge!.style.display = "";
badge!.classList.toggle("bottom-nav-badge-overdue", overdue > 0);
+ // F-38: the badge counts "actionable" items only — overdue + due
+ // today. The accessible label spells that out so the "2" never
+ // reads as ambiguous (e.g. "2 things this week").
+ const label = t("bottomnav.badge.deadlines")
+ .replace("{overdue}", String(overdue))
+ .replace("{today}", String(today));
+ badge!.setAttribute("title", label);
+ badge!.setAttribute("aria-label", label);
+ badge!.setAttribute("aria-hidden", "false");
})
.catch(() => {
// Badge is decorative; never break the page.
diff --git a/frontend/src/client/deadlines.ts b/frontend/src/client/deadlines.ts
index 36a3948..2c62b3a 100644
--- a/frontend/src/client/deadlines.ts
+++ b/frontend/src/client/deadlines.ts
@@ -209,11 +209,18 @@ function render() {
${esc(f.project_title)}
${ruleLabel}
-
${esc(statusLabel)}
+
${esc(statusLabel)}
`;
})
.join("");
+ // F-23: when every visible row carries the same status, hide the column.
+ // The class is dropped as soon as the user widens the filter and variety
+ // reappears, so the header is never permanently removed from the DOM.
+ const statusUnique = new Set(allDeadlines.map((f) => f.status)).size;
+ const table = document.getElementById("deadlines-table");
+ table?.classList.toggle("akten-table--hide-status", statusUnique <= 1);
+
tbody.querySelectorAll(".frist-row").forEach((row) => {
const id = row.dataset.id!;
row.addEventListener("click", (e) => {
diff --git a/frontend/src/client/i18n.ts b/frontend/src/client/i18n.ts
index 7c7cc05..ce00dc1 100644
--- a/frontend/src/client/i18n.ts
+++ b/frontend/src/client/i18n.ts
@@ -50,6 +50,7 @@ const translations: Record> = {
"bottomnav.add.project": "Projekt anlegen",
"bottomnav.add.project.sub": "Neues Mandat / Verfahren / Patent",
"bottomnav.add.cancel": "Abbrechen",
+ "bottomnav.badge.deadlines": "{overdue} überfällig + {today} heute fällig",
// Changelog (What's New) — t-paliad-027
"changelog.title": "Neuigkeiten — Paliad",
@@ -252,8 +253,8 @@ const translations: Record> = {
"glossar.subtitle": "Zweisprachiges Glossar der wichtigsten Begriffe im Patentrecht.",
"glossar.search.placeholder": "Suchen...",
"glossar.filter.all": "Alle",
- "glossar.filter.litigation": "Litigation",
- "glossar.filter.prosecution": "Prosecution",
+ "glossar.filter.litigation": "Streitsachen",
+ "glossar.filter.prosecution": "Erteilungsverfahren",
"glossar.filter.general": "Allgemein",
"glossar.col.de": "Deutsch",
"glossar.col.en": "English",
@@ -1431,6 +1432,7 @@ const translations: Record> = {
"bottomnav.add.project": "New project",
"bottomnav.add.project.sub": "New matter / case / patent",
"bottomnav.add.cancel": "Cancel",
+ "bottomnav.badge.deadlines": "{overdue} overdue + {today} due today",
// Changelog (What's New) — t-paliad-027
"changelog.title": "What's New — Paliad",
diff --git a/frontend/src/client/projects.ts b/frontend/src/client/projects.ts
index 5cd588b..8a565eb 100644
--- a/frontend/src/client/projects.ts
+++ b/frontend/src/client/projects.ts
@@ -148,12 +148,19 @@ function render() {
${esc(typeLabel)}
${refCell}
${clientMatterCell}
-
${esc(statusLabel)}
+
${esc(statusLabel)}
${fmtDate(p.updated_at)}
`;
})
.join("");
+ // F-23: when every visible row shares the same status, hide the column to
+ // cut redundant noise. The toggle re-runs on every filter change, so the
+ // column comes back as soon as the rows mix again.
+ const statusUnique = new Set(filtered.map((p) => p.status)).size;
+ const table = document.getElementById("akten-table");
+ table?.classList.toggle("akten-table--hide-status", statusUnique <= 1);
+
tbody.querySelectorAll(".akten-row").forEach((row) => {
row.addEventListener("click", () => {
const id = row.dataset.id!;
diff --git a/frontend/src/deadlines.tsx b/frontend/src/deadlines.tsx
index afe567b..57f7328 100644
--- a/frontend/src/deadlines.tsx
+++ b/frontend/src/deadlines.tsx
@@ -100,7 +100,7 @@ export function renderDeadlines(): string {
Titel
Projekt
Regel
-
Status
+
Status
diff --git a/frontend/src/glossary.tsx b/frontend/src/glossary.tsx
index ba25d2b..e529e4d 100644
--- a/frontend/src/glossary.tsx
+++ b/frontend/src/glossary.tsx
@@ -57,8 +57,8 @@ export function renderGlossary(): string {