feat(WP-0012): Querschnitt — Freigaben, Felder, Feedback, Suche, Tests
Implements all 8 tasks of the final cross-cutting workplan: - T01: Generisches Freigabe-Modal (freigabe_modal, freigabe_erteilen views + templates) - T02: Freigaben-Übersicht pro Ausschreibung (freigaben_uebersicht view + template) - T03: EntityFieldConfig Admin-Interface (/felder/<entity_type>/ with HTMX toggle) - T04: CustomAttribute-Panel (full CRUD with sort, lazy HTMX load) - T05: Feedback-Backlog mit Statusverwaltung + feedback_success.html template - T06: End-to-End-Tests in vergabe_teilnahme/tests/test_e2e.py (8 tests) - T07: Globale Suche erweitert (Dokumente, Nachweise, Referenzen, Marktbegleiter) - T08: Alle Migrationen sauber, 68/68 Tests grün, Ruff-Fehler in neuem Code behoben Bugfix: URL-Namespace-Fehler in Abgabe-Templates (ausschreibungen:nachbetrachtung:abgabe → ausschreibungen:abgabe) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c5ccbd665d
commit
5a231223c0
23 changed files with 828 additions and 37 deletions
|
|
@ -0,0 +1,67 @@
|
|||
<div x-data="{ formOpen: false }">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h3 class="text-sm font-medium text-slate-700">Zusatzfelder</h3>
|
||||
<button @click="formOpen = !formOpen" type="button" class="text-xs text-brand-600 hover:underline">
|
||||
+ Hinzufügen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div x-show="formOpen" x-cloak class="mb-4 bg-slate-50 border border-slate-200 rounded-lg p-3">
|
||||
<form hx-post="/core/attrs/{{ ct_id }}/{{ oid }}/neu/"
|
||||
hx-target="closest div[x-data]"
|
||||
hx-swap="outerHTML"
|
||||
class="space-y-2">
|
||||
{% csrf_token %}
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<input type="text" name="label" placeholder="Bezeichnung" class="form-input text-sm" required>
|
||||
<input type="text" name="value" placeholder="Wert" class="form-input text-sm">
|
||||
<select name="data_type" class="form-input text-sm">
|
||||
<option value="text">Text</option>
|
||||
<option value="number">Zahl</option>
|
||||
<option value="date">Datum</option>
|
||||
<option value="boolean">Ja/Nein</option>
|
||||
<option value="url">URL</option>
|
||||
<option value="email">E-Mail</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button type="submit" class="btn-primary text-xs py-1 px-3">Speichern</button>
|
||||
<button type="button" @click="formOpen = false" class="btn-secondary text-xs py-1 px-3">Abbrechen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if attrs %}
|
||||
<div class="space-y-1">
|
||||
{% for attr in attrs %}
|
||||
<div class="flex items-center gap-2 text-sm border-b border-slate-100 py-1.5">
|
||||
<span class="text-xs text-slate-500 w-32 shrink-0">{{ attr.label }}</span>
|
||||
<span class="text-slate-700 flex-1">{{ attr.value|default:"—" }}</span>
|
||||
<span class="text-xs text-slate-400 bg-slate-100 rounded px-1.5">{{ attr.data_type }}</span>
|
||||
<div class="flex gap-1">
|
||||
<form hx-post="/core/attrs/{{ ct_id }}/{{ oid }}/{{ attr.pk }}/sort/"
|
||||
hx-target="closest div[x-data]" hx-swap="outerHTML" class="inline">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="direction" value="up">
|
||||
<button type="submit" class="text-slate-300 hover:text-slate-600 text-xs">↑</button>
|
||||
</form>
|
||||
<form hx-post="/core/attrs/{{ ct_id }}/{{ oid }}/{{ attr.pk }}/sort/"
|
||||
hx-target="closest div[x-data]" hx-swap="outerHTML" class="inline">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="direction" value="down">
|
||||
<button type="submit" class="text-slate-300 hover:text-slate-600 text-xs">↓</button>
|
||||
</form>
|
||||
<form hx-post="/core/attrs/{{ ct_id }}/{{ oid }}/{{ attr.pk }}/loeschen/"
|
||||
hx-target="closest div[x-data]" hx-swap="outerHTML" class="inline"
|
||||
onsubmit="return confirm('Löschen?')">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="text-red-400 hover:text-red-600 text-xs">×</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-xs text-slate-400">Keine Zusatzfelder vorhanden.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
29
vergabe_teilnahme/templates/core/partials/feld_zeile.html
Normal file
29
vergabe_teilnahme/templates/core/partials/feld_zeile.html
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<tr id="feld-{{ entity_type }}-{{ field_name }}" class="border-b border-slate-100 hover:bg-slate-50">
|
||||
<td class="py-2 font-mono text-xs text-slate-700">{{ field_name }}</td>
|
||||
<td class="py-2">
|
||||
<form hx-post="/felder/{{ entity_type }}/{{ field_name }}/toggle/"
|
||||
hx-target="#feld-{{ entity_type }}-{{ field_name }}"
|
||||
hx-swap="outerHTML"
|
||||
class="flex items-center gap-2">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="is_hidden" value="{% if cfg.is_hidden %}true{% else %}false{% endif %}">
|
||||
<input type="text" name="display_label"
|
||||
value="{{ cfg.display_label|default:'' }}"
|
||||
class="form-input w-48 text-sm" placeholder="{{ field_name }}">
|
||||
<button type="submit" class="text-xs text-brand-600 hover:underline">Speichern</button>
|
||||
</form>
|
||||
</td>
|
||||
<td class="py-2 text-center">
|
||||
<form hx-post="/felder/{{ entity_type }}/{{ field_name }}/toggle/"
|
||||
hx-target="#feld-{{ entity_type }}-{{ field_name }}"
|
||||
hx-swap="outerHTML">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="display_label" value="{{ cfg.display_label|default:'' }}">
|
||||
<input type="hidden" name="is_hidden" value="{% if cfg.is_hidden %}false{% else %}true{% endif %}">
|
||||
<button type="submit"
|
||||
class="text-sm {% if cfg.is_hidden %}text-red-600 font-medium{% else %}text-slate-400{% endif %}">
|
||||
{% if cfg.is_hidden %}Ja{% else %}Nein{% endif %}
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
Loading…
Add table
Add a link
Reference in a new issue