Bereich Datenbank geht in Workspaces auf (Reiter Workspaces und Vorlagen)

Workspace-Liste unter /admin/workspaces/liste, Merkmale, Kategorien,
Webapp-Vorlagen und Distro-Abdeckung (frühere Flottenmatrix) unter
/admin/workspaces/vorlagen. Gemeinsame Unternavigation, Menüpunkt
Datenbank entfällt, alte /admin/technik-Adressen leiten weiter
(POST mit 307). Workspace-Creator neu, Schritt B.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-07 11:45:37 +02:00
1 parent bf1470a3ff
commit 3c7a7e49f3
13 files changed
+125 -112

No files matched your search

+1
View File
@@ -62,6 +62,7 @@ app.include_router(installationsmedium.router)
app.include_router(webapps.router)
app.include_router(admin_technik.router)
app.include_router(admin_workspaces.router)
app.include_router(admin_workspaces.technik_weiterleitung)
app.include_router(admin_geraete.router)
app.include_router(admin_flows.router)
app.include_router(admin_kunden.router)
+27 -32
View File
@@ -6,17 +6,12 @@ from auth import require_admin
from templating import templates
router = APIRouter(prefix="/admin/technik")
@router.get("")
def index(user: dict = Depends(require_admin)):
return RedirectResponse("/admin/technik/merkmale", status_code=303)
router = APIRouter(prefix="/admin/workspaces")
# --- Merkmale ---
@router.get("/merkmale")
@router.get("/vorlagen/merkmale")
def merkmale_liste(request: Request, user: dict = Depends(require_admin)):
merkmale_result = anode_request("GET", "/api/v1/merkmale")
merkmale = merkmale_result.get("merkmale", []) if merkmale_result.get("success") else []
@@ -31,7 +26,7 @@ def merkmale_liste(request: Request, user: dict = Depends(require_admin)):
)
@router.post("/merkmale")
@router.post("/vorlagen/merkmale")
def merkmal_anlegen(
key: str = Form(...), name: str = Form(...), description: str = Form(default=""),
user: dict = Depends(require_admin),
@@ -41,10 +36,10 @@ def merkmal_anlegen(
json={"key": key, "name": name, "description": description or None},
)
return RedirectResponse("/admin/technik/merkmale", status_code=303)
return RedirectResponse("/admin/workspaces/vorlagen/merkmale", status_code=303)
@router.post("/merkmale/{merkmal_id}")
@router.post("/vorlagen/merkmale/{merkmal_id}")
def merkmal_aktualisieren(
merkmal_id: str,
name: str = Form(...),
@@ -63,12 +58,12 @@ def merkmal_aktualisieren(
},
)
return RedirectResponse("/admin/technik/merkmale", status_code=303)
return RedirectResponse("/admin/workspaces/vorlagen/merkmale", status_code=303)
# --- Kategorien ---
@router.get("/kategorien")
@router.get("/vorlagen/kategorien")
def kategorien_liste(request: Request, user: dict = Depends(require_admin)):
result = anode_request("GET", "/api/v1/kategorien")
kategorien = result.get("kategorien", []) if result.get("success") else []
@@ -78,7 +73,7 @@ def kategorien_liste(request: Request, user: dict = Depends(require_admin)):
)
@router.post("/kategorien")
@router.post("/vorlagen/kategorien")
def kategorie_anlegen(
key: str = Form(...), name: str = Form(...), description: str = Form(default=""),
sort_order: int = Form(default=0), art: str = Form(default="app"), user: dict = Depends(require_admin),
@@ -88,12 +83,12 @@ def kategorie_anlegen(
json={"key": key, "name": name, "description": description or None, "sort_order": sort_order, "art": art},
)
return RedirectResponse("/admin/technik/kategorien", status_code=303)
return RedirectResponse("/admin/workspaces/vorlagen/kategorien", status_code=303)
# --- Workspaces ---
@router.get("/workspaces")
@router.get("/liste")
def workspaces_liste(request: Request, user: dict = Depends(require_admin)):
result = anode_request("GET", "/api/v1/workspaces")
workspaces = result.get("workspaces", []) if result.get("success") else []
@@ -108,7 +103,7 @@ def workspaces_liste(request: Request, user: dict = Depends(require_admin)):
)
@router.post("/workspaces")
@router.post("/liste")
def workspace_anlegen(
key: str = Form(...), name: str = Form(...), description: str = Form(default=""),
organization_id: str = Form(default=""), user: dict = Depends(require_admin),
@@ -121,7 +116,7 @@ def workspace_anlegen(
},
)
return RedirectResponse("/admin/technik/workspaces", status_code=303)
return RedirectResponse("/admin/workspaces/liste", status_code=303)
def gruppiere_merkmale_nach_kategorie(alle_merkmale: list[dict], komponierte_ids: set[str]) -> dict[str, list[dict]]:
@@ -143,7 +138,7 @@ def gruppiere_merkmale_nach_kategorie(alle_merkmale: list[dict], komponierte_ids
return kategorien
@router.get("/workspaces/{workspace_id}")
@router.get("/liste/{workspace_id}")
def workspace_detail(request: Request, workspace_id: str, user: dict = Depends(require_admin)):
result = anode_request("GET", f"/api/v1/workspaces/{workspace_id}")
workspace = result.get("workspace") if result.get("success") else None
@@ -170,7 +165,7 @@ def workspace_detail(request: Request, workspace_id: str, user: dict = Depends(r
)
@router.post("/workspaces/{workspace_id}/merkmale")
@router.post("/liste/{workspace_id}/merkmale")
async def workspace_merkmale_speichern(request: Request, workspace_id: str, user: dict = Depends(require_admin)):
"""
Ersetzt die alte "ein Merkmal pro Formular"-Zuordnung: die
@@ -194,7 +189,7 @@ async def workspace_merkmale_speichern(request: Request, workspace_id: str, user
for merkmal_id in aktuell - ausgewaehlt:
anode_request("DELETE", f"/api/v1/workspaces/{workspace_id}/merkmale/{merkmal_id}")
return RedirectResponse(f"/admin/technik/workspaces/{workspace_id}", status_code=303)
return RedirectResponse(f"/admin/workspaces/liste/{workspace_id}", status_code=303)
# --- Webapps (Phase 2, 09.09.2026) ---
@@ -207,7 +202,7 @@ async def workspace_merkmale_speichern(request: Request, workspace_id: str, user
# OE-/Gruppen-Ebene umziehen (analog zu oe_merkmale) statt Workspace-Ebene -
# bewusst nicht unilateral entschieden.
@router.post("/workspaces/{workspace_id}/webapps")
@router.post("/liste/{workspace_id}/webapps")
def workspace_webapp_hinzufuegen(
workspace_id: str, name: str = Form(...), url: str = Form(...), user: dict = Depends(require_admin)
):
@@ -216,16 +211,16 @@ def workspace_webapp_hinzufuegen(
json={"name": name, "url": url},
)
return RedirectResponse(f"/admin/technik/workspaces/{workspace_id}", status_code=303)
return RedirectResponse(f"/admin/workspaces/liste/{workspace_id}", status_code=303)
@router.post("/workspaces/{workspace_id}/webapps/{webapp_id}/entfernen")
@router.post("/liste/{workspace_id}/webapps/{webapp_id}/entfernen")
def workspace_webapp_entfernen(
workspace_id: str, webapp_id: str, user: dict = Depends(require_admin)
):
anode_request("DELETE", f"/api/v1/workspaces/{workspace_id}/webapps/{webapp_id}")
return RedirectResponse(f"/admin/technik/workspaces/{workspace_id}", status_code=303)
return RedirectResponse(f"/admin/workspaces/liste/{workspace_id}", status_code=303)
@router.get("/webapp-icons/{filename}")
@@ -284,7 +279,7 @@ def _blueprint_matrix(backends: list[dict], merkmale: list[dict], blueprints: li
]
@router.get("/backends")
@router.get("/vorlagen/distros")
def backends_liste(request: Request, user: dict = Depends(require_admin)):
backends_result = anode_request("GET", "/api/v1/backends")
backends = backends_result.get("backends", []) if backends_result.get("success") else []
@@ -308,7 +303,7 @@ def backends_liste(request: Request, user: dict = Depends(require_admin)):
# --- Webapp-Vorlagen (Phase 2, 09.09.2026) ---
@router.get("/webapp-templates")
@router.get("/vorlagen/webapp-vorlagen")
def webapp_templates_liste(request: Request, user: dict = Depends(require_admin)):
result = anode_request("GET", "/api/v1/webapp-templates")
webapp_templates = result.get("webapp_templates", []) if result.get("success") else []
@@ -327,7 +322,7 @@ def webapp_templates_liste(request: Request, user: dict = Depends(require_admin)
)
@router.post("/webapp-templates")
@router.post("/vorlagen/webapp-vorlagen")
def webapp_template_anlegen(
name: str = Form(...), url: str = Form(...), description: str = Form(default=""),
kategorie_id: str = Form(default=""), user: dict = Depends(require_admin),
@@ -337,10 +332,10 @@ def webapp_template_anlegen(
json={"name": name, "url": url, "description": description or None, "kategorie_id": kategorie_id or None},
)
return RedirectResponse("/admin/technik/webapp-templates", status_code=303)
return RedirectResponse("/admin/workspaces/vorlagen/webapp-vorlagen", status_code=303)
@router.post("/webapp-templates/{template_id}")
@router.post("/vorlagen/webapp-vorlagen/{template_id}")
def webapp_template_bearbeiten(
template_id: str, name: str = Form(...), url: str = Form(...), description: str = Form(default=""),
kategorie_id: str = Form(default=""), user: dict = Depends(require_admin),
@@ -350,11 +345,11 @@ def webapp_template_bearbeiten(
json={"name": name, "url": url, "description": description or None, "kategorie_id": kategorie_id or None},
)
return RedirectResponse("/admin/technik/webapp-templates", status_code=303)
return RedirectResponse("/admin/workspaces/vorlagen/webapp-vorlagen", status_code=303)
@router.post("/webapp-templates/{template_id}/loeschen")
@router.post("/vorlagen/webapp-vorlagen/{template_id}/loeschen")
def webapp_template_loeschen(template_id: str, user: dict = Depends(require_admin)):
anode_request("DELETE", f"/api/v1/webapp-templates/{template_id}")
return RedirectResponse("/admin/technik/webapp-templates", status_code=303)
return RedirectResponse("/admin/workspaces/vorlagen/webapp-vorlagen", status_code=303)
+40 -2
View File
@@ -74,7 +74,7 @@ def katalog_aufbauen(
for m in merkmale:
eintrag = _eintrag(
m["name"], m.get("description"),
f"/admin/technik{m['icon_path']}" if m.get("icon_path") else None,
f"/admin/workspaces{m['icon_path']}" if m.get("icon_path") else None,
distro_liste(distros_je_merkmal.get(m["key"], set())),
)
ziel = richtlinien if art_je_kategorie.get(m.get("kategorie_id")) == "richtlinie" else apps
@@ -86,7 +86,7 @@ def katalog_aufbauen(
t.get("kategorie_id"),
_eintrag(
t["name"], t.get("description"),
f"/admin/technik/webapp-icons/{t['icon_path'].split('/')[-1]}" if t.get("icon_path") else None,
f"/admin/workspaces/webapp-icons/{t['icon_path'].split('/')[-1]}" if t.get("icon_path") else None,
distro_liste(alle_distros),
),
)
@@ -113,6 +113,44 @@ def index(user: dict = Depends(require_admin)):
return RedirectResponse("/admin/workspaces/katalog", status_code=303)
@router.get("/vorlagen")
def vorlagen(user: dict = Depends(require_admin)):
return RedirectResponse("/admin/workspaces/vorlagen/merkmale", status_code=303)
# Bereich "Datenbank" (/admin/technik) ist in "Workspaces" aufgegangen
# (Schritt B, 07.10.2026) - alte Lesezeichen und Links leiten weiter.
# Bewusst 307/308 statt 303 für POST, damit ein noch offenes altes Formular
# nicht stillschweigend zu einem GET wird.
ALTE_TECHNIK_PFADE = [
("webapp-templates", "vorlagen/webapp-vorlagen"),
("webapp-icons", "webapp-icons"),
("merkmal-icons", "merkmal-icons"),
("merkmale", "vorlagen/merkmale"),
("kategorien", "vorlagen/kategorien"),
("backends", "vorlagen/distros"),
("workspaces", "liste"),
]
def neuer_pfad_fuer_technik(rest: str) -> str:
rest = rest.strip("/")
for alt, neu in ALTE_TECHNIK_PFADE:
if rest == alt or rest.startswith(alt + "/"):
return "/admin/workspaces/" + neu + rest[len(alt):]
return "/admin/workspaces"
technik_weiterleitung = APIRouter(prefix="/admin/technik")
@technik_weiterleitung.api_route("", methods=["GET", "POST"])
@technik_weiterleitung.api_route("/{rest:path}", methods=["GET", "POST"])
def alte_technik_adresse(request: Request, rest: str = ""):
status = 303 if request.method == "GET" else 307
return RedirectResponse(neuer_pfad_fuer_technik(rest), status_code=status)
@router.get("/katalog")
def katalog(request: Request, user: dict = Depends(require_admin)):
kategorien = _liste(anode_request("GET", "/api/v1/kategorien"), "kategorien")
+1 -1
View File
@@ -13,7 +13,7 @@ router = APIRouter()
# Workspace-Creator kundenseitig gesperrt (07.10.2026, Nutzer-Entscheidung):
# Der Creator ist noch nicht verständlich genug bedienbar, Workspaces von
# Kunden-Organisationen richtet bis auf Weiteres nur der root-admin über
# Admin → Datenbank → Workspaces-Vorlagen ein (Organisation im
# Admin → Workspaces → Workspaces ein (Organisation im
# Anlege-Formular wählen). Die Zuweisung eines fertigen Workspace an eine
# Gerätegruppe (routers/gruppen.py) bleibt für Kunden offen - das ist reine
# Auswahl, kein Zusammenstellen. Freischalten: True setzen.
+11
View File
@@ -0,0 +1,11 @@
{# Unterseiten von Workspaces → Vorlagen: Pflege dessen, woraus Workspaces
zusammengestellt werden. #}
{% set vl_pfad = request.url.path %}
<nav>
<ul>
<li><a href="/admin/workspaces/vorlagen/merkmale">{% if vl_pfad.startswith("/admin/workspaces/vorlagen/merkmale") %}<strong>Merkmale</strong>{% else %}Merkmale{% endif %}</a></li>
<li><a href="/admin/workspaces/vorlagen/kategorien">{% if vl_pfad.startswith("/admin/workspaces/vorlagen/kategorien") %}<strong>Kategorien</strong>{% else %}Kategorien{% endif %}</a></li>
<li><a href="/admin/workspaces/vorlagen/webapp-vorlagen">{% if vl_pfad.startswith("/admin/workspaces/vorlagen/webapp-vorlagen") %}<strong>Webapp-Vorlagen</strong>{% else %}Webapp-Vorlagen{% endif %}</a></li>
<li><a href="/admin/workspaces/vorlagen/distros">{% if vl_pfad.startswith("/admin/workspaces/vorlagen/distros") %}<strong>Distro-Abdeckung</strong>{% else %}Distro-Abdeckung{% endif %}</a></li>
</ul>
</nav>
+11 -7
View File
@@ -1,11 +1,15 @@
{# Unterreiter des Bereichs "Workspaces" (Workspace-Creator neu, 07.10.2026).
"Workspaces" und "Vorlagen" zeigen bis Schritt B auf die bisherigen
Seiten unter /admin/technik. #}
{# Unterreiter des Bereichs "Workspaces" (Workspace-Creator neu, 07.10.2026):
Katalog (Übersicht), Workspaces (Liste/Verwaltung), Vorlagen (Pflege des
Katalogs, nur root). Ersetzt den früheren Bereich "Datenbank". #}
{% set ws_pfad = request.url.path %}
<nav>
{% set ws_reiter = "katalog" if ws_pfad.startswith("/admin/workspaces/katalog")
else "liste" if ws_pfad.startswith("/admin/workspaces/liste")
else "vorlagen" if ws_pfad.startswith("/admin/workspaces/vorlagen")
else "" %}
<nav class="sekundaer-nav" aria-label="Workspaces-Unterseiten">
<ul>
<li><a href="/admin/workspaces/katalog">{% if ws_pfad.startswith("/admin/workspaces/katalog") %}<strong>Katalog</strong>{% else %}Katalog{% endif %}</a></li>
<li><a href="/admin/technik/workspaces">Workspaces</a></li>
<li><a href="/admin/technik/merkmale">Vorlagen</a></li>
<li><a href="/admin/workspaces/katalog" {% if ws_reiter == "katalog" %}class="active"{% endif %}>Katalog</a></li>
<li><a href="/admin/workspaces/liste" {% if ws_reiter == "liste" %}class="active"{% endif %}>Workspaces</a></li>
<li><a href="/admin/workspaces/vorlagen" {% if ws_reiter == "vorlagen" %}class="active"{% endif %}>Vorlagen</a></li>
</ul>
</nav>
+6 -13
View File
@@ -1,17 +1,10 @@
{% extends "admin/layout.html" %}
{% block title %}Flottenmatrix — Tuxflotte Admin{% endblock %}
{% block title %}Distro-Abdeckung — Tuxflotte Admin{% endblock %}
{% block admin_content %}
<nav>
<ul>
<li><a href="/admin/technik/merkmale">Merkmale</a></li>
<li><a href="/admin/technik/kategorien">App-Kategorien</a></li>
<li><a href="/admin/technik/workspaces">Workspaces-Vorlagen</a></li>
<li><a href="/admin/technik/backends"><strong>Flottenmatrix</strong></a></li>
<li><a href="/admin/technik/webapp-templates">Webapp-Vorlagen</a></li>
</ul>
</nav>
<h2>Flottenmatrix</h2>
<p><small>Nur Anzeige — Änderungen brauchen echten Ansible-Rollen-Code, nicht nur einen Datenbankeintrag. Ein Haken zeigt, dass für dieses Merkmal auf diesem Backend eine Ansible-Rolle hinterlegt ist (Mauszeiger drüber zeigt den Rollennamen) — eine leere Zeile heißt, das Merkmal ist auf keinem Backend umgesetzt.</small></p>
{% include "admin/_workspaces_nav.html" %}
{% include "admin/_vorlagen_nav.html" %}
<h2>Distro-Abdeckung</h2>
<p><small>Nur Anzeige — Änderungen brauchen echten Ansible-Rollen-Code, nicht nur einen Datenbankeintrag. Ein Haken zeigt, dass für dieses Merkmal auf dieser Distro eine Ansible-Rolle hinterlegt ist (Mauszeiger drüber zeigt den Rollennamen) — eine leere Zeile heißt, das Merkmal ist auf keiner Distro umgesetzt.</small></p>
<input type="search" placeholder="Suche nach Merkmal …" data-table-search="#blueprint-matrix" style="max-width: 24rem">
@@ -29,7 +22,7 @@
{% for zeile in matrix %}
<tr>
<td>
{% if zeile.merkmal.icon_path %}<img src="/admin/technik{{ zeile.merkmal.icon_path }}" alt="" width="18" height="18" style="vertical-align:middle;margin-right:0.35rem">{% endif %}
{% if zeile.merkmal.icon_path %}<img src="/admin/workspaces{{ zeile.merkmal.icon_path }}" alt="" width="18" height="18" style="vertical-align:middle;margin-right:0.35rem">{% endif %}
<code>{{ zeile.merkmal.key }}</code> {{ zeile.merkmal.name }}
</td>
{% for ansible_role in zeile.zellen %}
+6 -12
View File
@@ -1,16 +1,10 @@
{% extends "admin/layout.html" %}
{% block title %}App-Kategorien — Tuxflotte Admin{% endblock %}
{% block title %}Kategorien — Tuxflotte Admin{% endblock %}
{% block admin_content %}
<nav>
<ul>
<li><a href="/admin/technik/merkmale">Merkmale</a></li>
<li><a href="/admin/technik/kategorien"><strong>App-Kategorien</strong></a></li>
<li><a href="/admin/technik/workspaces">Workspaces-Vorlagen</a></li>
<li><a href="/admin/technik/backends">Flottenmatrix</a></li>
<li><a href="/admin/technik/webapp-templates">Webapp-Vorlagen</a></li>
</ul>
</nav>
<h2>App-Kategorien</h2>
{% include "admin/_workspaces_nav.html" %}
{% include "admin/_vorlagen_nav.html" %}
<h2>Kategorien</h2>
<p><small>Kategorien ordnen Apps und Webapps (Art „App“) bzw. Konfigurationsrichtlinien (Art „Richtlinie“) im Katalog und im Assistenten.</small></p>
<figure>
<table>
@@ -30,7 +24,7 @@
<details>
<summary role="button" class="secondary outline">Neue Kategorie anlegen</summary>
<form method="post" action="/admin/technik/kategorien">
<form method="post" action="/admin/workspaces/vorlagen/kategorien">
<label>Key<input type="text" name="key" required></label>
<label>Name<input type="text" name="name" required></label>
<label>Beschreibung<input type="text" name="description"></label>
+5 -12
View File
@@ -1,15 +1,8 @@
{% extends "admin/layout.html" %}
{% block title %}Merkmale — Tuxflotte Admin{% endblock %}
{% block admin_content %}
<nav>
<ul>
<li><a href="/admin/technik/merkmale"><strong>Merkmale</strong></a></li>
<li><a href="/admin/technik/kategorien">App-Kategorien</a></li>
<li><a href="/admin/technik/workspaces">Workspaces-Vorlagen</a></li>
<li><a href="/admin/technik/backends">Flottenmatrix</a></li>
<li><a href="/admin/technik/webapp-templates">Webapp-Vorlagen</a></li>
</ul>
</nav>
{% include "admin/_workspaces_nav.html" %}
{% include "admin/_vorlagen_nav.html" %}
<h2>Merkmale</h2>
<input type="search" placeholder="Suche nach Key, Name, Kategorie …" data-table-search="#merkmale-tabelle" style="max-width: 24rem">
@@ -28,7 +21,7 @@
<tr>
<td><code>{{ m.key }}</code></td>
<td>
{% if m.icon_path %}<img src="/admin/technik{{ m.icon_path }}" alt="" width="20" height="20" style="vertical-align:middle;margin-right:0.35rem">{% endif %}
{% if m.icon_path %}<img src="/admin/workspaces{{ m.icon_path }}" alt="" width="20" height="20" style="vertical-align:middle;margin-right:0.35rem">{% endif %}
{{ m.name }}
{% if m.description %}<br><small>{{ m.description }}</small>{% endif %}
</td>
@@ -37,7 +30,7 @@
<td>
<details>
<summary role="button" class="outline">Bearbeiten</summary>
<form method="post" action="/admin/technik/merkmale/{{ m.id }}">
<form method="post" action="/admin/workspaces/vorlagen/merkmale/{{ m.id }}">
<label>Name<input type="text" name="name" value="{{ m.name }}" required></label>
<label>Beschreibung<input type="text" name="description" value="{{ m.description or '' }}"></label>
<label>Kategorie
@@ -64,7 +57,7 @@
<details>
<summary role="button" class="secondary outline">Neues Merkmal anlegen</summary>
<form method="post" action="/admin/technik/merkmale">
<form method="post" action="/admin/workspaces/vorlagen/merkmale">
<label>Key<input type="text" name="key" required></label>
<label>Name<input type="text" name="name" required></label>
<label>Beschreibung<input type="text" name="description"></label>
+6 -13
View File
@@ -1,15 +1,8 @@
{% extends "admin/layout.html" %}
{% block title %}Webapp-Vorlagen — Tuxflotte Admin{% endblock %}
{% block admin_content %}
<nav>
<ul>
<li><a href="/admin/technik/merkmale">Merkmale</a></li>
<li><a href="/admin/technik/kategorien">App-Kategorien</a></li>
<li><a href="/admin/technik/workspaces">Workspaces-Vorlagen</a></li>
<li><a href="/admin/technik/backends">Flottenmatrix</a></li>
<li><a href="/admin/technik/webapp-templates"><strong>Webapp-Vorlagen</strong></a></li>
</ul>
</nav>
{% include "admin/_workspaces_nav.html" %}
{% include "admin/_vorlagen_nav.html" %}
<h2>Webapp-Vorlagen</h2>
<p><small>Global wiederverwendbare Webapp-Vorschläge, die Kunden für ihre eigenen Workspaces aktivieren können (zusätzlich zu ihren eigenen freien Einträgen).</small></p>
@@ -21,7 +14,7 @@
<tr>
<td style="width:2rem">
{% if t.icon_path %}
<img src="/admin/technik/webapp-icons/{{ t.icon_path.split('/')[-1] }}" alt="" width="24" height="24">
<img src="/admin/workspaces/webapp-icons/{{ t.icon_path.split('/')[-1] }}" alt="" width="24" height="24">
{% endif %}
</td>
<td>{{ t.name }}{% if t.description %}<br><small>{{ t.description }}</small>{% endif %}</td>
@@ -30,7 +23,7 @@
<td>
<details>
<summary role="button" class="outline">Bearbeiten</summary>
<form method="post" action="/admin/technik/webapp-templates/{{ t.id }}">
<form method="post" action="/admin/workspaces/vorlagen/webapp-vorlagen/{{ t.id }}">
<label>Name<input type="text" name="name" value="{{ t.name }}" required></label>
<label>URL<input type="url" name="url" value="{{ t.url }}" required></label>
<label>Beschreibung<input type="text" name="description" value="{{ t.description or '' }}"></label>
@@ -45,7 +38,7 @@
<button type="submit">Speichern</button>
</form>
</details>
<form method="post" action="/admin/technik/webapp-templates/{{ t.id }}/loeschen">
<form method="post" action="/admin/workspaces/vorlagen/webapp-vorlagen/{{ t.id }}/loeschen">
<button type="submit" class="secondary outline">Löschen</button>
</form>
</td>
@@ -57,7 +50,7 @@
<details>
<summary role="button" class="secondary outline">Neue Vorlage anlegen</summary>
<form method="post" action="/admin/technik/webapp-templates">
<form method="post" action="/admin/workspaces/vorlagen/webapp-vorlagen">
<label>Name<input type="text" name="name" required placeholder="z.B. Wikipedia"></label>
<label>URL<input type="url" name="url" required placeholder="https://..."></label>
<label>Beschreibung<input type="text" name="description"></label>
@@ -1,7 +1,8 @@
{% extends "admin/layout.html" %}
{% block title %}{{ workspace.name if workspace else workspace_id }} — Tuxflotte Admin{% endblock %}
{% block admin_content %}
<p><a href="/admin/technik/workspaces">← Workspaces-Vorlagen</a></p>
{% include "admin/_workspaces_nav.html" %}
<p><a href="/admin/workspaces/liste">← Workspaces</a></p>
{% if not workspace %}
<p><mark>Workspace wurde nicht gefunden.</mark></p>
{% else %}
@@ -20,7 +21,7 @@
{% if not kategorien %}
<p>Keine katalogfähigen Merkmale vorhanden.</p>
{% else %}
<form method="post" action="/admin/technik/workspaces/{{ workspace_id }}/merkmale">
<form method="post" action="/admin/workspaces/liste/{{ workspace_id }}/merkmale">
{% for kategorie_name, eintraege in kategorien.items() %}
<fieldset>
<legend><strong>{{ kategorie_name }}</strong></legend>
@@ -49,12 +50,12 @@
<tr>
<td style="width:2rem">
{% if w.icon_path %}
<img src="/admin/technik/webapp-icons/{{ w.icon_path.split('/')[-1] }}" alt="" width="24" height="24">
<img src="/admin/workspaces/webapp-icons/{{ w.icon_path.split('/')[-1] }}" alt="" width="24" height="24">
{% endif %}
</td>
<td>{{ w.name }}<br><small><a href="{{ w.url }}" target="_blank" rel="noopener">{{ w.url }}</a></small></td>
<td>
<form method="post" action="/admin/technik/workspaces/{{ workspace_id }}/webapps/{{ w.id }}/entfernen">
<form method="post" action="/admin/workspaces/liste/{{ workspace_id }}/webapps/{{ w.id }}/entfernen">
<button type="submit" class="secondary outline">Entfernen</button>
</form>
</td>
@@ -65,7 +66,7 @@
</figure>
{% endif %}
<form method="post" action="/admin/technik/workspaces/{{ workspace_id }}/webapps">
<form method="post" action="/admin/workspaces/liste/{{ workspace_id }}/webapps">
<label>Name
<input type="text" name="name" required placeholder="z.B. Schulportal">
</label>
+5 -13
View File
@@ -1,16 +1,8 @@
{% extends "admin/layout.html" %}
{% block title %}Workspaces-Vorlagen — Tuxflotte Admin{% endblock %}
{% block title %}Workspaces — Tuxflotte Admin{% endblock %}
{% block admin_content %}
<nav>
<ul>
<li><a href="/admin/technik/merkmale">Merkmale</a></li>
<li><a href="/admin/technik/kategorien">App-Kategorien</a></li>
<li><a href="/admin/technik/workspaces"><strong>Workspaces-Vorlagen</strong></a></li>
<li><a href="/admin/technik/backends">Flottenmatrix</a></li>
<li><a href="/admin/technik/webapp-templates">Webapp-Vorlagen</a></li>
</ul>
</nav>
<h2>Workspaces-Vorlagen</h2>
{% include "admin/_workspaces_nav.html" %}
<h2>Workspaces</h2>
<figure>
<table>
@@ -21,7 +13,7 @@
<td><code>{{ w.key }}</code></td>
<td>{{ w.name }}</td>
<td>{{ w.organization_name or "Global" }}</td>
<td><a href="/admin/technik/workspaces/{{ w.id }}" role="button" class="outline">Zusammensetzung</a></td>
<td><a href="/admin/workspaces/liste/{{ w.id }}" role="button" class="outline">Zusammensetzung</a></td>
</tr>
{% endfor %}
</tbody>
@@ -30,7 +22,7 @@
<details>
<summary role="button" class="secondary outline">Neuen Workspace anlegen</summary>
<form method="post" action="/admin/technik/workspaces">
<form method="post" action="/admin/workspaces/liste">
<label>Key<input type="text" name="key" required></label>
<label>Name<input type="text" name="name" required></label>
<label>Beschreibung<input type="text" name="description"></label>
-2
View File
@@ -130,7 +130,6 @@
else "installationsmedium" if pfad.startswith("/installationsmedium")
else "admin_dashboard" if pfad.startswith("/admin/dashboard")
else "admin_kunden" if pfad.startswith("/admin/kunden")
else "admin_technik" if pfad.startswith("/admin/technik")
else "admin_workspaces" if pfad.startswith("/admin/workspaces")
else "admin_geraete" if pfad.startswith("/admin/geraete")
else "admin_flows" if pfad.startswith("/admin/flows")
@@ -165,7 +164,6 @@
<li><a href="/admin/dashboard" {% if aktiver_bereich == "admin_dashboard" %}class="active"{% endif %}>📊 Dashboard</a></li>
<li><a href="/admin/kunden" {% if aktiver_bereich == "admin_kunden" %}class="active"{% endif %}>🏢 Organisationen</a></li>
<li><a href="/admin/workspaces" {% if aktiver_bereich == "admin_workspaces" %}class="active"{% endif %}>🧩 Workspaces</a></li>
<li><a href="/admin/technik" {% if aktiver_bereich == "admin_technik" %}class="active"{% endif %}>🗄️ Datenbank</a></li>
<li><a href="/admin/geraete" {% if aktiver_bereich == "admin_geraete" %}class="active"{% endif %}>🖥️ Flotte</a></li>
<li><a href="/admin/accounts" {% if aktiver_bereich == "admin_accounts" %}class="active"{% endif %}>🔑 Admin-Accounts</a></li>
{% else %}