mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-07-01 03:04:52 +02:00
feat: show housekeeper task details
This commit is contained in:
@@ -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.",
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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.")
|
||||||
|
|||||||
Reference in New Issue
Block a user