```
Die `phases`-Liste wird von einer View-Hilfsfunktion `build_phase_nav(ausschreibung, current_url)` befüllt.
Implementiere diese Funktion in `core/services.py`.
```
```task
id: WP-0003-T05
title: Breadcrumb-Partial
status: todo
`vergabe_teilnahme/templates/partials/breadcrumb.html`:
Breadcrumb rendert eine Liste von Links aus dem Template-Context-Variable `breadcrumbs`.
```html
{% if breadcrumbs %}
{% endif %}
```
`breadcrumbs` wird in jeder View-Funktion als Liste von Dicts übergeben:
`[{'label': 'Ausschreibungen', 'url': '/ausschreibungen/'}, {'label': 'Titel', 'url': None}]`
Erstelle eine Hilfsfunktion `core.views_helpers.make_breadcrumbs(*args)` die diese Liste baut.
```
```task
id: WP-0003-T06
title: Template-Tags: status_badge und phase_badge
status: todo
Erstelle `vergabe_teilnahme/apps/core/templatetags/__init__.py` (leer).
Erstelle `vergabe_teilnahme/apps/core/templatetags/vergabe_tags.py`.
```python
from django import template
register = template.Library()
STATUS_COLORS = {
# Ausschreibung / Aufgabe / Dokument Status
'offen': 'bg-slate-100 text-slate-700',
'in_bearbeitung': 'bg-blue-100 text-blue-700',
'erledigt': 'bg-green-100 text-green-700',
'freigegeben': 'bg-green-100 text-green-700',
'erteilt': 'bg-green-100 text-green-700',
'gewonnen': 'bg-green-100 text-green-700',
'ueberfaellig': 'bg-red-100 text-red-700',
'nicht_erfuellbar': 'bg-red-100 text-red-700',
'verloren': 'bg-red-100 text-red-700',
'abgelehnt': 'bg-red-100 text-red-700',
'ausstehend': 'bg-amber-100 text-amber-700',
'in_pruefung': 'bg-amber-100 text-amber-700',
'wartend_intern': 'bg-amber-100 text-amber-700',
'wartend_sub': 'bg-amber-100 text-amber-700',
'wartend_ausschreiber': 'bg-amber-100 text-amber-700',
'archiviert': 'bg-gray-100 text-gray-500',
'ersetzt': 'bg-gray-100 text-gray-500',
'verworfen': 'bg-gray-100 text-gray-500',
}
@register.inclusion_tag('partials/status_badge.html')
def status_badge(value, display_label=None):
css = STATUS_COLORS.get(value, 'bg-slate-100 text-slate-700')
return {'css': css, 'label': display_label or value.replace('_', ' ').capitalize()}
@register.simple_tag
def phase_badge(nummer, zustand='todo'):
css_map = {'todo': 'phase-todo', 'active': 'phase-active',
'done': 'phase-done', 'warn': 'phase-warn'}
return f'{nummer}'
```
Erstelle `partials/status_badge.html`:
```html
{{ label }}
```
```
```task
id: WP-0003-T07
title: Template-Tag: render_field (EntityFieldConfig-aware)
status: todo
Ergänze `vergabe_tags.py`:
```python
from vergabe_teilnahme.apps.core.models import EntityFieldConfig
_HIDDEN_FIELDS_CACHE = {}
def _is_field_hidden(entity_type, field_name):
key = (entity_type, field_name)
if key not in _HIDDEN_FIELDS_CACHE:
_HIDDEN_FIELDS_CACHE[key] = EntityFieldConfig.objects.filter(
entity_type=entity_type, field_name=field_name, is_hidden=True
).exists()
return _HIDDEN_FIELDS_CACHE[key]
def _get_field_label(entity_type, field_name, default_label):
try:
cfg = EntityFieldConfig.objects.get(entity_type=entity_type, field_name=field_name)
return cfg.display_label or default_label
except EntityFieldConfig.DoesNotExist:
return default_label
@register.inclusion_tag('partials/field_row.html')
def render_field(obj, field_name, label=None, force_show=False):
entity_type = obj._meta.model_name
if not force_show and _is_field_hidden(entity_type, field_name):
return {'hidden': True}
value = getattr(obj, field_name, None)
display_label = _get_field_label(entity_type, field_name, label or field_name)
return {'hidden': False, 'label': display_label, 'value': value, 'field_name': field_name}
```
Erstelle `partials/field_row.html`:
```html
{% if not hidden %}
{{ label }}
{% if value %}{{ value }}{% else %}—{% endif %}
{% endif %}
```
Wichtig: `_HIDDEN_FIELDS_CACHE` per Request invalidieren (oder einfach kein Cache in v1,
da Admin-Änderungen sofort wirken sollen — entscheide dich für kein Caching in v1).
```
```task
id: WP-0003-T08
title: Feedback-Button und Feedback-Modal-Partial
status: todo
`partials/feedback_button.html`:
```html
```
`partials/feedback_modal.html` (wird vom HTMX-Endpunkt zurückgegeben):
```html
Feedback
```
Erstelle in `feedback/views.py`:
- `GET /feedback/modal/` → rendert `partials/feedback_modal.html`
- `POST /feedback/` → speichert Feedbackeintrag, gibt Danke-Fragment zurück
Verkable URLs in `feedback/urls.py` und include in Haupt-URLs.
```
```task
id: WP-0003-T09
title: Error-Templates und Django-URL-Konfiguration
status: todo
`vergabe_teilnahme/templates/errors/404.html`:
```html
{% extends "base.html" %}
{% block title %}Seite nicht gefunden{% endblock %}
{% block content %}
404
Seite nicht gefunden
Die angeforderte Seite existiert nicht oder wurde verschoben.