feat: show housekeeper task details

This commit is contained in:
Marcel Peterkau
2026-06-21 17:59:49 +02:00
parent 7596e47981
commit 3e9f347435
3 changed files with 60 additions and 0 deletions
+1
View File
@@ -21,6 +21,7 @@
"Regelskripte im Mitglieder-Store können eingebaute Regeln anhand ihres Dateinamens gezielt ersetzen.", "Regelskripte im Mitglieder-Store können eingebaute Regeln anhand ihres Dateinamens gezielt ersetzen.",
"Beschädigte Beitragsdateien blockieren die Mitgliederansicht nicht mehr und werden vom Hausmeister ohne automatisches Überschreiben gemeldet.", "Beschädigte Beitragsdateien blockieren die Mitgliederansicht nicht mehr und werden vom Hausmeister ohne automatisches Überschreiben gemeldet.",
"Ein Akten-Preflight sperrt bei beschädigten Mitglieder-, Beitrags- oder Eventdateien alle Regeln für die betroffene Akte.", "Ein Akten-Preflight sperrt bei beschädigten Mitglieder-, Beitrags- oder Eventdateien alle Regeln für die betroffene Akte.",
"Der Hausmeister-Tab zeigt den vollständigen Inhalt eines markierten Vorgangs in einem mehrzeiligen Detailbereich.",
"Hausmeister um konfigurierbare Geburtstags- und Mitgliedsjubiläumsmeldungen erweitert.", "Hausmeister um konfigurierbare Geburtstags- und Mitgliedsjubiläumsmeldungen erweitert.",
"Statusänderungen werden mit altem und neuem Klartextwert in der Mitgliederchronik protokolliert.", "Statusänderungen werden mit altem und neuem Klartextwert in der Mitgliederchronik protokolliert.",
"Fensterposition, normaler Fensterzustand und Maximierung werden gespeichert; der Splash startet auf dem zuletzt verwendeten Monitor.", "Fensterposition, normaler Fensterzustand und Maximierung werden gespeichert; der Splash startet auf dem zuletzt verwendeten Monitor.",
+38
View File
@@ -227,6 +227,24 @@ class HousekeeperTab(ttk.Frame):
self.tree.column(key, width=width, anchor="w") self.tree.column(key, width=width, anchor="w")
self.tree.grid(row=1, column=0, sticky="nsew") self.tree.grid(row=1, column=0, sticky="nsew")
self.tree.bind("<Double-1>", lambda _event: self._open_selected()) self.tree.bind("<Double-1>", lambda _event: self._open_selected())
self.tree.bind("<<TreeviewSelect>>", lambda _event: self._show_selected_details())
details = ttk.LabelFrame(self, text="Details", padding=12)
details.grid(row=2, column=0, sticky="ew", pady=(10, 0))
details.columnconfigure(0, weight=1)
self.detail_var = tk.StringVar(value="Eintrag auswählen, um Details anzuzeigen.")
self.detail_label = ttk.Label(
details,
textvariable=self.detail_var,
anchor="nw",
justify="left",
wraplength=800,
style="Status.TLabel",
)
self.detail_label.grid(row=0, column=0, sticky="ew")
details.bind(
"<Configure>",
lambda event: self.detail_label.configure(wraplength=max(300, event.width - 32)),
)
self._render() self._render()
def refresh(self) -> None: def refresh(self) -> None:
@@ -235,6 +253,7 @@ class HousekeeperTab(ttk.Frame):
def _render(self) -> None: def _render(self) -> None:
self.tree.delete(*self.tree.get_children()) self.tree.delete(*self.tree.get_children())
self.detail_var.set("Eintrag auswählen, um Details anzuzeigen.")
self.title_var.set(f"HAUSMEISTER · {len(self.findings)} Vorgänge") self.title_var.set(f"HAUSMEISTER · {len(self.findings)} Vorgänge")
for index, finding in enumerate(self.findings): for index, finding in enumerate(self.findings):
self.tree.insert( self.tree.insert(
@@ -244,7 +263,26 @@ class HousekeeperTab(ttk.Frame):
values=(finding.severity.upper(), finding.title, finding.detail, finding.due_date or ""), values=(finding.severity.upper(), finding.title, finding.detail, finding.due_date or ""),
) )
def _show_selected_details(self) -> None:
selected = self.tree.selection()
if not selected:
self.detail_var.set("Eintrag auswählen, um Details anzuzeigen.")
return
index = int(selected[0])
if index >= len(self.findings):
return
self.detail_var.set(_finding_details(self.findings[index]))
def _open_selected(self) -> None: def _open_selected(self) -> None:
selected = self.tree.selection() selected = self.tree.selection()
if selected: if selected:
self.on_open_member(self.findings[int(selected[0])].member_id) self.on_open_member(self.findings[int(selected[0])].member_id)
def _finding_details(finding: HousekeeperFinding) -> str:
lines = [f"{finding.severity.upper()} · {finding.code}", finding.title]
if finding.due_date:
lines.append(f"Fällig: {format_date_for_display(finding.due_date.isoformat())}")
if finding.detail:
lines.extend(("", finding.detail))
return "\n".join(lines)
+21
View File
@@ -38,3 +38,24 @@ def test_event_labels_hide_board_actor_but_keep_automatic_marker() -> None:
system_event = Event("2", "2026-01-01T00:00:00+01:00", "automatic", "Automatisch") system_event = Event("2", "2026-01-01T00:00:00+01:00", "automatic", "Automatisch")
assert _event_label(user_event) == "Kommentar" assert _event_label(user_event) == "Kommentar"
assert _event_label(system_event) == "[AUTO] Automatisch" assert _event_label(system_event) == "[AUTO] Automatisch"
def test_housekeeper_details_are_multiline() -> None:
from datetime import date
from ccma.domain.models import HousekeeperFinding
from ccma.ui.work_tabs import _finding_details
finding = HousekeeperFinding(
severity="error",
member_id="member-1",
code="invalid_member_record",
title="Mitgliederakte beschädigt",
detail="Die JSON-Datei ist leer und wird nicht automatisch überschrieben.",
due_date=date(2026, 7, 31),
)
rendered = _finding_details(finding)
assert rendered.splitlines()[0] == "ERROR · invalid_member_record"
assert "Mitgliederakte beschädigt\nFällig:" in rendered
assert rendered.endswith("nicht automatisch überschrieben.")