feat: initialize CCMA member administration

This commit is contained in:
Marcel Peterkau
2026-06-21 16:46:15 +02:00
parent 4c6a1191ee
commit dfd5b1192b
184 changed files with 5051 additions and 0 deletions
+90
View File
@@ -0,0 +1,90 @@
import tkinter as tk
from collections.abc import Callable
from tkinter import messagebox, ttk
from ccma.domain.dates import age_label, date_input_hint
from ccma.domain.models import Member
from ccma.storage.repository import MemberRepository, RepositoryError
class NewMemberDialog(tk.Toplevel):
def __init__(self, master: tk.Misc, repository: MemberRepository, on_created: Callable[[Member], None]):
super().__init__(master)
self.repository = repository
self.on_created = on_created
self.title("Neue Mitgliederakte")
self.transient(master.winfo_toplevel())
self.grab_set()
self.resizable(False, False)
self.number_policy = repository.get_member_number_policy()
self.variables = {
name: tk.StringVar()
for name in ("first_name", "last_name", "email", "birth_date", "member_number")
}
self._build_ui()
self.bind("<Escape>", lambda _event: self.destroy())
self.bind("<Return>", lambda _event: self._create())
self.after_idle(self._focus_first)
def _build_ui(self) -> None:
frame = ttk.Frame(self, padding=18)
frame.pack(fill="both", expand=True)
fields = [
("Vorname *", "first_name"),
("Nachname *", "last_name"),
("E-Mail-Adresse", "email"),
(f"Geburtsdatum ({date_input_hint()})", "birth_date"),
]
if self.number_policy["mode"] == "manual":
fields.append(("Mitgliedsnummer *", "member_number"))
self.entries: dict[str, ttk.Entry] = {}
for row, (label, key) in enumerate(fields):
ttk.Label(frame, text=label).grid(row=row, column=0, sticky="w", pady=5, padx=(0, 12))
if key == "birth_date":
birth_row = ttk.Frame(frame)
birth_row.grid(row=row, column=1, sticky="ew", pady=5)
birth_row.columnconfigure(0, weight=1)
entry = ttk.Entry(birth_row, textvariable=self.variables[key], width=24)
entry.grid(row=0, column=0, sticky="ew")
self.birth_age_var = tk.StringVar(value="Alter: —")
ttk.Label(birth_row, textvariable=self.birth_age_var, style="Mono.TLabel").grid(
row=0, column=1, sticky="w", padx=(10, 0)
)
self.variables[key].trace_add(
"write",
lambda *_args: self.birth_age_var.set(age_label(self.variables["birth_date"].get())),
)
else:
entry = ttk.Entry(frame, textvariable=self.variables[key], width=38)
entry.grid(row=row, column=1, sticky="ew", pady=5)
self.entries[key] = entry
button_row = len(fields)
if self.number_policy["mode"] == "automatic":
preview = self.repository.preview_member_number(self.number_policy["pattern"])
ttk.Label(frame, text="Mitgliedsnummer").grid(
row=button_row, column=0, sticky="w", pady=5, padx=(0, 12)
)
ttk.Label(frame, text=f"Automatisch: {preview}", style="TimelineHeader.TLabel").grid(
row=button_row, column=1, sticky="w", pady=5
)
button_row += 1
buttons = ttk.Frame(frame)
buttons.grid(row=button_row, column=0, columnspan=2, sticky="e", pady=(16, 0))
ttk.Button(buttons, text="Abbrechen", command=self.destroy).pack(side="left", padx=(0, 8))
ttk.Button(buttons, text="Akte anlegen", style="Accent.TButton", command=self._create).pack(
side="left"
)
def _focus_first(self) -> None:
self.entries["first_name"].focus_set()
def _create(self) -> None:
try:
member = self.repository.create_member(
**{key: variable.get() for key, variable in self.variables.items()}
)
except RepositoryError as exc:
messagebox.showerror("Akte konnte nicht angelegt werden", str(exc), parent=self)
return
self.destroy()
self.on_created(member)