Implementierung des Veranstaltungsmoduls inkl. Serienbrief-PDF-Generator mit dynamischen, editierbaren Feldern für Betreff und Unterschriften. ### Veranstaltungsmodul (STI-35) - Neues Veranstaltungs-Modell: Titel, Datum, Uhrzeit, Ort, Gasthaus-Adresse, Briefvorlage, Gästeliste (VerstaltungsGast mit freien/Destinatär-Feldern) - Views: Veranstaltungsliste, -detail, Serienbrief-PDF-Generator - Templates: list.html, detail.html, serienbrief_pdf.html (A4, einseitig) - API: Serializer + Endpunkte für Veranstaltungen - Admin: Inline-Bearbeitung der Gästeliste - Migration: 0044_veranstaltungsmodul ### Serienbrief editierbare Felder + PDF-Fix (STI-39) - Neue Felder an Veranstaltung: betreff, unterschrift_1_name/titel, unterschrift_2_name/titel (mit Defaults: Katrin Kleinpaß / Jan Remmer Siebels) - PDF-CSS: Margins, Font-Sizes und Line-Heights reduziert für einseitigen Druck - Migration: 0045_add_serienbrief_editable_fields ### Infrastruktur - scripts/init-paperless-db.sh: Erstellt separate Paperless-DB beim DB-Init - compose.yml: init-paperless-db.sh eingebunden, PAPERLESS_DBNAME-Fix - .gitignore: .claude/ ausgeschlossen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
200 lines
9.6 KiB
HTML
200 lines
9.6 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}{{ veranstaltung.titel }} – Stiftungsverwaltung{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container-fluid py-4">
|
||
<!-- Header -->
|
||
<div class="d-flex justify-content-between align-items-start mb-4">
|
||
<div>
|
||
<nav aria-label="breadcrumb">
|
||
<ol class="breadcrumb">
|
||
<li class="breadcrumb-item"><a href="{% url 'stiftung:veranstaltung_list' %}">Veranstaltungen</a></li>
|
||
<li class="breadcrumb-item active">{{ veranstaltung.titel }}</li>
|
||
</ol>
|
||
</nav>
|
||
<h1 class="h3 mb-1">{{ veranstaltung.titel }}</h1>
|
||
<p class="text-muted mb-0">
|
||
{{ veranstaltung.datum|date:"l, d. F Y" }}{% if veranstaltung.uhrzeit %}, {{ veranstaltung.uhrzeit|time:"H:i" }} Uhr{% endif %}
|
||
· {{ veranstaltung.ort }}
|
||
</p>
|
||
</div>
|
||
<div class="d-flex gap-2">
|
||
<a href="{% url 'admin:stiftung_veranstaltung_change' veranstaltung.pk %}" class="btn btn-outline-secondary">
|
||
<i class="fas fa-edit me-1"></i>Bearbeiten
|
||
</a>
|
||
<a href="{% url 'stiftung:veranstaltung_serienbrief_pdf' veranstaltung.pk %}" class="btn btn-success">
|
||
<i class="fas fa-file-pdf me-1"></i>Serienbrief-PDF
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row g-4">
|
||
<!-- Veranstaltungsdetails -->
|
||
<div class="col-lg-4">
|
||
<div class="card shadow-sm h-100">
|
||
<div class="card-header bg-dark text-white">
|
||
<i class="fas fa-info-circle me-2"></i>Details
|
||
</div>
|
||
<div class="card-body">
|
||
<dl class="row mb-0">
|
||
<dt class="col-sm-5">Status</dt>
|
||
<dd class="col-sm-7">
|
||
{% if veranstaltung.status == "geplant" %}
|
||
<span class="badge bg-secondary">Geplant</span>
|
||
{% elif veranstaltung.status == "einladungen_versendet" %}
|
||
<span class="badge bg-primary">Einladungen versendet</span>
|
||
{% elif veranstaltung.status == "abgeschlossen" %}
|
||
<span class="badge bg-success">Abgeschlossen</span>
|
||
{% elif veranstaltung.status == "abgesagt" %}
|
||
<span class="badge bg-danger">Abgesagt</span>
|
||
{% endif %}
|
||
</dd>
|
||
<dt class="col-sm-5">Gasthaus</dt>
|
||
<dd class="col-sm-7">{{ veranstaltung.ort }}</dd>
|
||
{% if veranstaltung.adresse %}
|
||
<dt class="col-sm-5">Adresse</dt>
|
||
<dd class="col-sm-7">{{ veranstaltung.adresse }}</dd>
|
||
{% endif %}
|
||
{% if veranstaltung.budget_pro_person %}
|
||
<dt class="col-sm-5">Budget/Person</dt>
|
||
<dd class="col-sm-7">{{ veranstaltung.budget_pro_person }} €</dd>
|
||
{% endif %}
|
||
{% if veranstaltung.beschreibung %}
|
||
<dt class="col-sm-5">Beschreibung</dt>
|
||
<dd class="col-sm-7">{{ veranstaltung.beschreibung }}</dd>
|
||
{% endif %}
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- RSVP-Statistik -->
|
||
<div class="col-lg-4">
|
||
<div class="card shadow-sm h-100">
|
||
<div class="card-header bg-dark text-white">
|
||
<i class="fas fa-chart-pie me-2"></i>RSVP-Übersicht
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row text-center g-3">
|
||
<div class="col-6">
|
||
<div class="border rounded p-3">
|
||
<div class="fs-2 fw-bold text-primary">{{ teilnehmer.count }}</div>
|
||
<div class="small text-muted">Eingeladen gesamt</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-6">
|
||
<div class="border rounded p-3">
|
||
<div class="fs-2 fw-bold text-success">{{ zugesagte.count }}</div>
|
||
<div class="small text-muted">Zugesagt</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-6">
|
||
<div class="border rounded p-3">
|
||
<div class="fs-2 fw-bold text-danger">{{ abgesagte.count }}</div>
|
||
<div class="small text-muted">Abgesagt</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-6">
|
||
<div class="border rounded p-3">
|
||
<div class="fs-2 fw-bold text-warning">{{ keine_rueckmeldung.count }}</div>
|
||
<div class="small text-muted">Keine Rückmeldung</div>
|
||
</div>
|
||
</div>
|
||
{% if eingeladen.count %}
|
||
<div class="col-12">
|
||
<div class="border rounded p-3">
|
||
<div class="fs-2 fw-bold text-secondary">{{ eingeladen.count }}</div>
|
||
<div class="small text-muted">Nur eingeladen (noch kein RSVP)</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Schnellaktionen -->
|
||
<div class="col-lg-4">
|
||
<div class="card shadow-sm h-100">
|
||
<div class="card-header bg-dark text-white">
|
||
<i class="fas fa-tools me-2"></i>Aktionen
|
||
</div>
|
||
<div class="card-body d-flex flex-column gap-2">
|
||
<a href="{% url 'stiftung:veranstaltung_serienbrief_pdf' veranstaltung.pk %}"
|
||
class="btn btn-success w-100">
|
||
<i class="fas fa-file-pdf me-2"></i>Serienbrief-PDF (alle Teilnehmer)
|
||
</a>
|
||
<a href="{% url 'admin:stiftung_veranstaltungsteilnehmer_add' %}?veranstaltung={{ veranstaltung.pk }}"
|
||
class="btn btn-outline-primary w-100">
|
||
<i class="fas fa-user-plus me-2"></i>Teilnehmer hinzufügen
|
||
</a>
|
||
<a href="{% url 'admin:stiftung_veranstaltung_change' veranstaltung.pk %}"
|
||
class="btn btn-outline-secondary w-100">
|
||
<i class="fas fa-edit me-2"></i>Veranstaltung bearbeiten
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Teilnehmerliste -->
|
||
<div class="card shadow-sm mt-4">
|
||
<div class="card-header bg-dark text-white d-flex justify-content-between align-items-center">
|
||
<span><i class="fas fa-users me-2"></i>Teilnehmerliste ({{ teilnehmer.count }})</span>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
{% if teilnehmer %}
|
||
<table class="table table-hover mb-0">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Adresse</th>
|
||
<th>E-Mail</th>
|
||
<th>RSVP</th>
|
||
<th>Bemerkungen</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for t in teilnehmer %}
|
||
<tr>
|
||
<td>
|
||
{{ t.anrede }} {{ t.vorname }} {{ t.nachname }}
|
||
</td>
|
||
<td>
|
||
{% if t.strasse %}{{ t.strasse }},{% endif %}
|
||
{% if t.plz %}{{ t.plz }}{% endif %}
|
||
{% if t.ort %}{{ t.ort }}{% endif %}
|
||
</td>
|
||
<td>{% if t.email %}<a href="mailto:{{ t.email }}">{{ t.email }}</a>{% else %}–{% endif %}</td>
|
||
<td>
|
||
{% if t.rsvp_status == "eingeladen" %}
|
||
<span class="badge bg-secondary">Eingeladen</span>
|
||
{% elif t.rsvp_status == "zugesagt" %}
|
||
<span class="badge bg-success">Zugesagt</span>
|
||
{% elif t.rsvp_status == "abgesagt" %}
|
||
<span class="badge bg-danger">Abgesagt</span>
|
||
{% elif t.rsvp_status == "keine_rueckmeldung" %}
|
||
<span class="badge bg-warning text-dark">Keine Rückmeldung</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>{{ t.bemerkungen|default:"–" }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
{% else %}
|
||
<div class="p-4 text-center text-muted">
|
||
<i class="fas fa-users fa-2x mb-2"></i>
|
||
<p>Noch keine Teilnehmer eingetragen.</p>
|
||
<a href="{% url 'admin:stiftung_veranstaltungsteilnehmer_add' %}?veranstaltung={{ veranstaltung.pk }}"
|
||
class="btn btn-primary">
|
||
<i class="fas fa-user-plus me-1"></i>Ersten Teilnehmer hinzufügen
|
||
</a>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|