2 Commits
Author SHA1 Message Date
bryan.hoffmann 455f513f6e Auftragsübersicht anpassen
Build und Push Docker Image / build-and-push (push) Successful in 11s
2026-08-27 11:52:24 +02:00
bryan.hoffmann d6c066e18f Versionsnummer
Build und Push Docker Image / build-and-push (push) Successful in 9s
2026-08-26 11:32:52 +02:00
6 changed files with 129 additions and 36 deletions
+30 -7
View File
@@ -26,22 +26,45 @@ jobs:
- name: Bei der Registry anmelden - name: Bei der Registry anmelden
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin
- name: Image-Namen und Tag bestimmen # Die Versionsnummer kommt aus der Datei VERSION im Repo-Root (z.B.
# "1.4.0") - vor einem Release dort einfach den Wert erhoehen.
#
# Tags je nach Push:
# - Branch main -> :latest und :v<VERSION>
# - Branch Dev -> :dev und :v<VERSION>-dev
# - Branch beta -> :beta und :v<VERSION>-beta
# - Git-Tag "v1.4.0" -> genau :v1.4.0 (expliziter Versions-Release)
# - immer zusaetzlich -> :<commit-sha> (exakte Nachverfolgung)
- name: Image-Namen und Tags bestimmen
id: vars id: vars
run: | run: |
IMAGE=${{ env.REGISTRY }}/${{ github.repository }} IMAGE=${{ env.REGISTRY }}/${{ github.repository }}
if [ "${{ github.ref_name }}" = "main" ]; then VERSION=$(tr -d '[:space:]' < VERSION)
TAG=latest
if [ "${{ github.ref_type }}" = "tag" ]; then
TAGS="${{ github.ref_name }}"
elif [ "${{ github.ref_name }}" = "main" ]; then
TAGS="latest v${VERSION}"
else else
TAG=$(echo "${{ github.ref_name }}" | tr '[:upper:]' '[:lower:]') BRANCH=$(echo "${{ github.ref_name }}" | tr '[:upper:]' '[:lower:]')
TAGS="${BRANCH} v${VERSION}-${BRANCH}"
fi fi
echo "image=$IMAGE" >> "$GITHUB_OUTPUT" echo "image=$IMAGE" >> "$GITHUB_OUTPUT"
echo "tag=$TAG" >> "$GITHUB_OUTPUT" echo "tags=$TAGS" >> "$GITHUB_OUTPUT"
echo "Ermittelte Tags: $TAGS"
- name: Image bauen - name: Image bauen
run: docker build -t ${{ steps.vars.outputs.image }}:${{ steps.vars.outputs.tag }} -t ${{ steps.vars.outputs.image }}:${{ github.sha }} . run: |
BUILD_ARGS=""
for TAG in ${{ steps.vars.outputs.tags }}; do
BUILD_ARGS="$BUILD_ARGS -t ${{ steps.vars.outputs.image }}:${TAG}"
done
docker build $BUILD_ARGS -t ${{ steps.vars.outputs.image }}:${{ github.sha }} .
- name: Image pushen - name: Image pushen
run: | run: |
docker push ${{ steps.vars.outputs.image }}:${{ steps.vars.outputs.tag }} for TAG in ${{ steps.vars.outputs.tags }}; do
docker push ${{ steps.vars.outputs.image }}:${TAG}
done
docker push ${{ steps.vars.outputs.image }}:${{ github.sha }} docker push ${{ steps.vars.outputs.image }}:${{ github.sha }}
+39 -16
View File
@@ -43,15 +43,33 @@ bestehende Postgres-Datenbank mit Daten, dann:
## Bauen und Verteilen als Container ## Bauen und Verteilen als Container
Bei jedem Push nach `main` bzw. auf einen `v*`-Tag baut die Gitea-Actions- Bei jedem Push auf `main`, `Dev`, `beta` oder einen `v*`-Tag baut die
Pipeline (`.gitea/workflows/build.yml`) automatisch ein Image und pusht es in Gitea-Actions-Pipeline (`.gitea/workflows/build.yml`) automatisch ein Image
die Gitea-Registry (getaggt als `:latest` und mit dem Commit-Hash). und pusht es in die Gitea-Registry. Welche Tags dabei entstehen, hängt vom
Branch ab:
| Push auf … | Image-Tags |
|--------------------|--------------------------------------|
| `main` | `:latest` und `:v<VERSION>` |
| `Dev` | `:dev` und `:v<VERSION>-dev` |
| `beta` | `:beta` und `:v<VERSION>-beta` |
| Git-Tag `v1.4.0` | genau `:v1.4.0` |
Zusätzlich wird bei jedem Build immer auch `:<commit-sha>` gepusht, für die
exakte Nachverfolgung einzelner Builds.
`<VERSION>` kommt aus der Datei `VERSION` im Repo-Root (aktuell `1.0.0`).
Vor einem Release dort einfach die Zahl erhöhen und committen die Pipeline
liest den Wert automatisch bei jedem Build neu ein, es ist keine weitere
Konfiguration nötig.
Für einen **anderen Server**, der nur das fertige Image ziehen und selbst Für einen **anderen Server**, der nur das fertige Image ziehen und selbst
nichts bauen soll, gibt es `docker-compose.deploy.yml`: sie referenziert das nichts bauen soll, gibt es `docker-compose.deploy.yml`: sie referenziert das
Image direkt aus der Registry (`image: git.thiede-brauer.de/bryan.hoffmann/wdm:latest`, Image direkt aus der Registry (`image: git.thiede-brauer.de/bryan.hoffmann/wdm:latest`,
kein `build:`). Auf diesem Server reicht allein diese eine Datei der Rest kein `build:`). Auf diesem Server reicht allein diese eine Datei der Rest
des Repos wird dort nicht benötigt: des Repos wird dort nicht benötigt. Statt `:latest` kann dort auch gezielt
ein Versions-Tag (z.B. `:v1.4.0`) oder `:dev`/`:beta` eingetragen werden, um
eine bestimmte Version bzw. einen bestimmten Entwicklungsstand zu deployen:
```bash ```bash
docker login git.thiede-brauer.de -u <benutzername> # falls die Registry nicht öffentlich lesbar ist docker login git.thiede-brauer.de -u <benutzername> # falls die Registry nicht öffentlich lesbar ist
@@ -133,16 +151,20 @@ location /app/wdm-performance/ {
(.xlsx) oder CSV-Datei per Drag & Drop oder Dateiauswahl bulk-anlegen, (.xlsx) oder CSV-Datei per Drag & Drop oder Dateiauswahl bulk-anlegen,
siehe eigener Abschnitt unten siehe eigener Abschnitt unten
- **Auftragsübersicht**: alle Aufträge (inkl. Artikel, Maschinenname), - **Auftragsübersicht**: alle Aufträge (inkl. Artikel, Maschinenname),
standardmäßig nur offen/pausiert (umschaltbar auf "Alle"), optional nach standardmäßig offen/pausiert **und aktiv** (umschaltbar auf "Alle", zeigt
Maschine gefiltert, löschbar und (mit Einschränkungen) bearbeitbar: dann zusätzlich auch abgeschlossene Aufträge), optional nach Maschine
- **Status und Priorität sind hier nicht editierbar** (nur informativ gefiltert, löschbar und (mit Einschränkungen) bearbeitbar:
angezeigt) Status ändert sich ausschließlich über die eigentlichen - **Status ist hier manuell änderbar** (z.B. um einen versehentlich
Maschinen-Aktionen (Start/Pause/Beenden), Priorität ausschließlich über abgeschlossenen Auftrag wieder auf "Offen" zu setzen) **"Aktiv" ist als
die Drag & Drop-Seite "Auftragspriorität". Zielwert bewusst ausgeschlossen**, das passiert ausschließlich über die
- **Aktive Aufträge werden angezeigt, können aber nicht bearbeitet eigentlichen Maschinen-Aktionen (Start/Pause/Beenden). **Priorität bleibt
werden** ein Bearbeitungsversuch (auch ein direkter POST) wird mit weiterhin nicht editierbar**, dafür gibt es die Drag & Drop-Seite
einer Fehlermeldung abgelehnt, solange der Auftrag gerade an der "Auftragspriorität".
Maschine läuft. - **Aktive Aufträge werden angezeigt, können aber weder bearbeitet noch
gelöscht werden** Bearbeiten- und Löschen-Button werden für sie in der
Liste durch einen Hinweistext ersetzt; ein direkter POST-Versuch (am UI
vorbei) wird serverseitig ebenfalls abgelehnt, solange der Auftrag gerade
an der Maschine läuft.
- Editierbar bleiben Maschine (inkl. automatischem Umhängen der kompletten - Editierbar bleiben Maschine (inkl. automatischem Umhängen der kompletten
MDE-Buchungshistorie auf die neue Maschine), Auftragsnummer, Artikel, MDE-Buchungshistorie auf die neue Maschine), Auftragsnummer, Artikel,
Produktionsmenge und Stück/Stunde. Produktionsmenge und Stück/Stunde.
@@ -238,7 +260,8 @@ wdm/
├── Dockerfile Build-Anweisungen (Kontext = Projektwurzel) ├── Dockerfile Build-Anweisungen (Kontext = Projektwurzel)
├── docker-compose.yml lokal bauen/testen (build: .) ├── docker-compose.yml lokal bauen/testen (build: .)
├── docker-compose.deploy.yml nur Image ziehen (für andere Server) ├── docker-compose.deploy.yml nur Image ziehen (für andere Server)
├── .gitea/workflows/build.yml CI: baut Image und pusht in die Gitea-Registry ├── .gitea/workflows/build.yml CI: baut Image (branch-/versionsabhängig getaggt) und pusht in die Gitea-Registry
├── VERSION aktuelle Versionsnummer (z.B. "1.0.0"), von der CI-Pipeline gelesen
├── README.md ├── README.md
└── build/ └── build/
├── app.py Flask-Anwendung (alle Routen, Server-Side-Rendering) ├── app.py Flask-Anwendung (alle Routen, Server-Side-Rendering)
@@ -251,4 +274,4 @@ wdm/
``` ```
Wichtig: `docker compose` muss aus dem Projektwurzel-Verzeichnis heraus Wichtig: `docker compose` muss aus dem Projektwurzel-Verzeichnis heraus
aufgerufen werden (dort, wo `docker-compose.yml` liegt), nicht aus `build/`. aufgerufen werden (dort, wo `docker-compose.yml` liegt), nicht aus `build/`.
+1
View File
@@ -0,0 +1 @@
1.0.1
+42 -11
View File
@@ -58,6 +58,11 @@ STATUS_LABEL = {
"abgeschlossen": "Abgeschlossen", "abgeschlossen": "Abgeschlossen",
} }
STATUS_OPTIONEN = [("offen", "Offen"), ("aktiv", "Aktiv"), ("pause", "Pausiert"), ("abgeschlossen", "Abgeschlossen")] STATUS_OPTIONEN = [("offen", "Offen"), ("aktiv", "Aktiv"), ("pause", "Pausiert"), ("abgeschlossen", "Abgeschlossen")]
# In der Auftragsübersicht darf der Status manuell gesetzt werden (z.B. um einen
# Auftrag versehentlich abgeschlossenen wieder zu öffnen) - "aktiv" ist hier
# aber bewusst ausgeschlossen, das passiert ausschließlich über die
# Start-Aktion an der Maschine selbst.
STATUS_OPTIONEN_BEARBEITBAR = [(wert, label) for wert, label in STATUS_OPTIONEN if wert != "aktiv"]
# Anzahl Zeilen, auf die MDE-/Auftragslisten ausgelegt sind (kein hartes Limit, # Anzahl Zeilen, auf die MDE-/Auftragslisten ausgelegt sind (kein hartes Limit,
# aber Referenzgröße für Layout/Performance-Überlegungen). # aber Referenzgröße für Layout/Performance-Überlegungen).
@@ -705,7 +710,11 @@ def leitstand_uebersicht():
bedingungen.append('maschine_id = %s') bedingungen.append('maschine_id = %s')
params.append(filter_maschine_id) params.append(filter_maschine_id)
if filter_status == "offen_pause": if filter_status == "offen_pause":
bedingungen.append("status IN ('offen', 'pause')") # "aktiv" wird hier bewusst mit angezeigt (nur informativ, nicht
# bearbeitbar/löschbar - siehe leitstand_uebersicht_bearbeiten
# bzw. leitstand_uebersicht_loeschen), damit laufende Aufträge in
# der Übersicht nicht verschwinden.
bedingungen.append("status IN ('offen', 'pause', 'aktiv')")
where_sql = (" WHERE " + " AND ".join(bedingungen)) if bedingungen else "" where_sql = (" WHERE " + " AND ".join(bedingungen)) if bedingungen else ""
cur.execute( cur.execute(
@@ -735,12 +744,14 @@ def leitstand_uebersicht():
@app.route("/start_uebersicht/<int:auftrag_id>/bearbeiten", methods=["GET", "POST"]) @app.route("/start_uebersicht/<int:auftrag_id>/bearbeiten", methods=["GET", "POST"])
def leitstand_uebersicht_bearbeiten(auftrag_id): def leitstand_uebersicht_bearbeiten(auftrag_id):
"""Auftrag bearbeiten. Status und Priorität (sid) sind hier bewusst NICHT """Auftrag bearbeiten. Der Status ist hier manuell änderbar (z.B. um einen
editierbar - Status ändert sich nur über die eigentlichen Maschinen- versehentlich abgeschlossenen Auftrag wieder auf "offen" zu setzen) -
Aktionen (Start/Pause/Beenden), Priorität nur über die Drag&Drop-Seite "aktiv" ist als Zielwert bewusst ausgeschlossen, das passiert
"Auftragspriorität". Aktive Aufträge werden nur angezeigt, eine ausschließlich über die eigentlichen Maschinen-Aktionen (Start/Pause/
Bearbeitung wird komplett verweigert (Fehlermeldung), da sie gerade an Beenden). Priorität (sid) ist weiterhin NICHT editierbar, dafür gibt es
der Maschine läuft.""" die Drag&Drop-Seite "Auftragspriorität". Aktive Aufträge werden nur
angezeigt, eine Bearbeitung wird komplett verweigert (Fehlermeldung), da
sie gerade an der Maschine läuft."""
filter_maschine_id = request.args.get("maschine_id") or request.form.get("maschine_id_filter") or "" filter_maschine_id = request.args.get("maschine_id") or request.form.get("maschine_id_filter") or ""
filter_status = request.args.get("status") or request.form.get("status_filter") or "offen_pause" filter_status = request.args.get("status") or request.form.get("status_filter") or "offen_pause"
with db.get_cursor() as cur: with db.get_cursor() as cur:
@@ -759,6 +770,7 @@ def leitstand_uebersicht_bearbeiten(auftrag_id):
"leitstand_uebersicht_bearbeiten.html", title="Auftrag bearbeiten", "leitstand_uebersicht_bearbeiten.html", title="Auftrag bearbeiten",
auftrag=alt, maschinen=maschinen, gesperrt=True, auftrag=alt, maschinen=maschinen, gesperrt=True,
status_anzeige=STATUS_LABEL.get(alt["status"], alt["status"]), status_anzeige=STATUS_LABEL.get(alt["status"], alt["status"]),
status_optionen=STATUS_OPTIONEN_BEARBEITBAR,
filter_maschine_id=filter_maschine_id, filter_status=filter_status, filter_maschine_id=filter_maschine_id, filter_status=filter_status,
error="Dieser Auftrag ist aktuell aktiv und kann deshalb nicht bearbeitet werden. " error="Dieser Auftrag ist aktuell aktiv und kann deshalb nicht bearbeitet werden. "
"Bitte an der Maschine erst pausieren oder beenden.", "Bitte an der Maschine erst pausieren oder beenden.",
@@ -770,6 +782,7 @@ def leitstand_uebersicht_bearbeiten(auftrag_id):
soll_h = request.form.get("soll_h") soll_h = request.form.get("soll_h")
artikel = (request.form.get("artikel") or "").strip() or None artikel = (request.form.get("artikel") or "").strip() or None
neue_maschine_id = request.form.get("maschine_id") neue_maschine_id = request.form.get("maschine_id")
neuer_status = request.form.get("status")
error = None error = None
einheiten_f = soll_h_f = None einheiten_f = soll_h_f = None
@@ -782,6 +795,10 @@ def leitstand_uebersicht_bearbeiten(auftrag_id):
if not error and not auftragsnummer: if not error and not auftragsnummer:
error = "Auftragsnummer darf nicht leer sein." error = "Auftragsnummer darf nicht leer sein."
gueltige_status = {wert for wert, _ in STATUS_OPTIONEN_BEARBEITBAR}
if not error and neuer_status not in gueltige_status:
error = "Ungültiger Status."
if not error: if not error:
if not neue_maschine_id: if not neue_maschine_id:
error = "Maschine muss ausgewählt werden." error = "Maschine muss ausgewählt werden."
@@ -806,17 +823,27 @@ def leitstand_uebersicht_bearbeiten(auftrag_id):
auftrag["sid_fmt"] = fmt_num(alt["sid"]) auftrag["sid_fmt"] = fmt_num(alt["sid"])
auftrag["artikel"] = artikel auftrag["artikel"] = artikel
auftrag["maschine_id"] = neue_maschine_id or alt["maschine_id"] auftrag["maschine_id"] = neue_maschine_id or alt["maschine_id"]
auftrag["status"] = neuer_status if neuer_status in gueltige_status else alt["status"]
return render_template( return render_template(
"leitstand_uebersicht_bearbeiten.html", title="Auftrag bearbeiten", "leitstand_uebersicht_bearbeiten.html", title="Auftrag bearbeiten",
auftrag=auftrag, maschinen=maschinen, auftrag=auftrag, maschinen=maschinen,
status_anzeige=STATUS_LABEL.get(alt["status"], alt["status"]), status_anzeige=STATUS_LABEL.get(alt["status"], alt["status"]),
status_optionen=STATUS_OPTIONEN_BEARBEITBAR,
filter_maschine_id=filter_maschine_id, filter_status=filter_status, error=error, filter_maschine_id=filter_maschine_id, filter_status=filter_status, error=error,
) )
# Wurde der Status auf "abgeschlossen" geändert (oder war er das
# schon und bleibt es), Endzeitpunkt entsprechend setzen bzw.
# beibehalten - beim (Wieder-)Öffnen wird er zurückgesetzt.
if neuer_status == "abgeschlossen":
ende_wert = alt["ende"] if alt["status"] == "abgeschlossen" else now_ts()
else:
ende_wert = None
cur.execute( cur.execute(
'UPDATE "Auftraege" SET auftragsnummer = %s, einheiten = %s, soll_h = %s, ' 'UPDATE "Auftraege" SET auftragsnummer = %s, einheiten = %s, soll_h = %s, '
'artikel = %s, maschine_id = %s WHERE id = %s', 'artikel = %s, maschine_id = %s, status = %s, aktiv = false, ende = %s WHERE id = %s',
(auftragsnummer, einheiten_f, soll_h_f, artikel, neue_maschine_id, auftrag_id), (auftragsnummer, einheiten_f, soll_h_f, artikel, neue_maschine_id, neuer_status, ende_wert, auftrag_id),
) )
# Maschine und/oder Auftragsnummer geändert? Dann muss die MDE-Historie # Maschine und/oder Auftragsnummer geändert? Dann muss die MDE-Historie
# (dort per maschine_id + Auftragsnummer verknüpft) mitgezogen werden. # (dort per maschine_id + Auftragsnummer verknüpft) mitgezogen werden.
@@ -835,6 +862,7 @@ def leitstand_uebersicht_bearbeiten(auftrag_id):
"leitstand_uebersicht_bearbeiten.html", title="Auftrag bearbeiten", "leitstand_uebersicht_bearbeiten.html", title="Auftrag bearbeiten",
auftrag=alt, maschinen=maschinen, auftrag=alt, maschinen=maschinen,
status_anzeige=STATUS_LABEL.get(alt["status"], alt["status"]), status_anzeige=STATUS_LABEL.get(alt["status"], alt["status"]),
status_optionen=STATUS_OPTIONEN_BEARBEITBAR,
filter_maschine_id=filter_maschine_id, filter_status=filter_status, filter_maschine_id=filter_maschine_id, filter_status=filter_status,
) )
@@ -844,7 +872,10 @@ def leitstand_uebersicht_loeschen(auftrag_id):
filter_maschine_id = request.form.get("maschine_id") or "" filter_maschine_id = request.form.get("maschine_id") or ""
filter_status = request.form.get("status") or "offen_pause" filter_status = request.form.get("status") or "offen_pause"
with db.get_cursor() as cur: with db.get_cursor() as cur:
cur.execute('DELETE FROM "Auftraege" WHERE id = %s', (auftrag_id,)) # Aktive Aufträge (gerade an der Maschine laufend) dürfen hier nicht
# gelöscht werden können, unabhängig davon, was im UI angeboten wird
# (analog zur Bearbeiten-Sperre für aktive Aufträge).
cur.execute('DELETE FROM "Auftraege" WHERE id = %s AND status <> \'aktiv\'', (auftrag_id,))
return redirect(url_for("leitstand_uebersicht", maschine_id=filter_maschine_id, status=filter_status)) return redirect(url_for("leitstand_uebersicht", maschine_id=filter_maschine_id, status=filter_status))
@@ -1123,4 +1154,4 @@ def health():
if __name__ == "__main__": if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=False) app.run(host="0.0.0.0", port=5000, debug=False)
+6 -2
View File
@@ -14,7 +14,7 @@
</select> </select>
<span>Status:</span> <span>Status:</span>
<select name="status" onchange="this.form.submit()"> <select name="status" onchange="this.form.submit()">
<option value="offen_pause" {% if filter_status == "offen_pause" %}selected{% endif %}>Offen/Pausiert</option> <option value="offen_pause" {% if filter_status == "offen_pause" %}selected{% endif %}>Offen/Pausiert/Aktiv</option>
<option value="alle" {% if filter_status == "alle" %}selected{% endif %}>Alle</option> <option value="alle" {% if filter_status == "alle" %}selected{% endif %}>Alle</option>
</select> </select>
<button type="submit">Filtern</button> <button type="submit">Filtern</button>
@@ -42,6 +42,9 @@
<td>{{ "Ja" if a.aktiv else "Nein" }}</td> <td>{{ "Ja" if a.aktiv else "Nein" }}</td>
<td>{{ a.sid_fmt }}</td> <td>{{ a.sid_fmt }}</td>
<td class="col-actions"> <td class="col-actions">
{% if a.status == "aktiv" %}
<span style="color:var(--text-dim); font-size:0.85em;">gesperrt (aktiv an Maschine)</span>
{% else %}
<div class="row-actions"> <div class="row-actions">
<a class="icon-btn" href="{{ url_for('leitstand_uebersicht_bearbeiten', auftrag_id=a.id, maschine_id=filter_maschine_id, status=filter_status) }}">Bearbeiten</a> <a class="icon-btn" href="{{ url_for('leitstand_uebersicht_bearbeiten', auftrag_id=a.id, maschine_id=filter_maschine_id, status=filter_status) }}">Bearbeiten</a>
<form method="post" action="{{ url_for('leitstand_uebersicht_loeschen', auftrag_id=a.id) }}" class="order-form"> <form method="post" action="{{ url_for('leitstand_uebersicht_loeschen', auftrag_id=a.id) }}" class="order-form">
@@ -50,6 +53,7 @@
<button type="submit" class="icon-btn danger">L&ouml;schen</button> <button type="submit" class="icon-btn danger">L&ouml;schen</button>
</form> </form>
</div> </div>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@@ -57,4 +61,4 @@
</table> </table>
</div> </div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
@@ -31,7 +31,18 @@
<label for="soll_h">St&uuml;ck pro Stunde</label> <label for="soll_h">St&uuml;ck pro Stunde</label>
<input type="number" step="any" id="soll_h" name="soll_h" value="{{ auftrag.soll_h_fmt }}" {{ 'readonly' if gesperrt }} required> <input type="number" step="any" id="soll_h" name="soll_h" value="{{ auftrag.soll_h_fmt }}" {{ 'readonly' if gesperrt }} required>
</div> </div>
{% if gesperrt %}
<div class="status-line"><span class="label">Status</span><span>{{ status_anzeige }}</span></div> <div class="status-line"><span class="label">Status</span><span>{{ status_anzeige }}</span></div>
{% else %}
<div class="form-group">
<label for="status">Status</label>
<select id="status" name="status" required>
{% for wert, label in status_optionen %}
<option value="{{ wert }}" {% if auftrag.status == wert %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
{% endif %}
<div class="status-line"> <div class="status-line">
<span class="label">Priorit&auml;t</span> <span class="label">Priorit&auml;t</span>
<span>{{ auftrag.sid_fmt }} <span style="color:var(--text-dim); font-size:0.85em;">(&auml;nderbar unter "Auftragspriorit&auml;t")</span></span> <span>{{ auftrag.sid_fmt }} <span style="color:var(--text-dim); font-size:0.85em;">(&auml;nderbar unter "Auftragspriorit&auml;t")</span></span>