feat(ui): Provision-Wizard neu gestaltet

This commit is contained in:
Alwin 2026-03-25 07:44:48 +00:00
parent 10a495c13c
commit 41e12d1235

View file

@ -61,73 +61,68 @@ const loginTmpl = `<!DOCTYPE html>
</html>` </html>`
const provisionTmpl = `<!DOCTYPE html> const provisionTmpl = `<!DOCTYPE html>
<html lang="de" data-theme="light"> <html lang="de">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light">
<title>Einrichten {{.Screen.Name}}</title> <title>Einrichten {{.Screen.Name}}</title>
<link rel="stylesheet" href="/static/bulma.min.css"> <link rel="stylesheet" href="/static/bulma.min.css">
<style> <style>
body { background: #f5f5f5; } :root { --morz-red:#E30613; --morz-red-dark:#B8000F; --nav-bg:#1A1A2E; --bg:#F7F8FA; --surface:#FFFFFF; --shadow-sm:0 1px 4px rgba(0,0,0,.08); --radius:8px; --radius-btn:6px; }
pre { background: #1e1e1e; color: #d4d4d4; padding: 1rem; border-radius: 4px; body { background:var(--bg); font-family:system-ui,-apple-system,"Segoe UI",sans-serif; }
overflow-x: auto; font-size: 0.9em; line-height: 1.5; } .navbar { background:var(--nav-bg) !important; }
.step-number { background: var(--bulma-primary, hsl(229, 53%, 53%)); color: #fff; border-radius: 50%; .navbar-item { color:rgba(255,255,255,.85) !important; }
width: 2rem; height: 2rem; display: inline-flex; .navbar-item:hover { background:rgba(255,255,255,.08) !important; color:#fff !important; }
align-items: center; justify-content: center; .morz-brand .accent { color:var(--morz-red); font-weight:800; }
font-weight: bold; margin-right: 0.5rem; flex-shrink: 0; } .box { border-radius:var(--radius); box-shadow:var(--shadow-sm); }
.step { display: flex; align-items: flex-start; gap: 1rem; margin-bottom: 2rem; } .step-num { width:2.25rem; height:2.25rem; border-radius:50%; background:var(--morz-red); color:#fff;
.step-body { flex: 1; } display:inline-flex; align-items:center; justify-content:center; font-weight:800;
.copy-btn { cursor: pointer; font-size: 0.75em; } font-size:.95rem; flex-shrink:0; }
.step-row { display:flex; gap:1.25rem; align-items:flex-start; }
.step-body { flex:1; }
pre { background:#0f172a; color:#e2e8f0; padding:1rem 1.25rem; border-radius:6px; font-size:.85rem;
line-height:1.6; overflow-x:auto; margin:.75rem 0; }
code { background:#f1f5f9; color:#1e293b; padding:.1em .35em; border-radius:4px; font-size:.875em; }
.copy-btn { font-size:.75rem; cursor:pointer; border-radius:4px; }
.button.is-primary { background:var(--morz-red) !important; border-color:var(--morz-red) !important; border-radius:var(--radius-btn); }
.button.is-primary:hover { background:var(--morz-red-dark) !important; border-color:var(--morz-red-dark) !important; }
.success-banner { background:#f0fdf4; border:1px solid #bbf7d0; color:#166534; border-radius:var(--radius); padding:1rem 1.25rem; margin-bottom:1.5rem; }
</style> </style>
</head> </head>
<body> <body>
<nav class="navbar is-dark"> <nav class="navbar" role="navigation">
<div class="navbar-brand"> <div class="navbar-brand">
<a class="navbar-item" href="/admin"> Admin</a> <a class="navbar-item" href="/admin"> Admin</a>
<span class="navbar-item"><strong>Bildschirm einrichten: {{.Screen.Name}}</strong></span> <span class="navbar-item morz-brand"><span class="accent">MORZ</span> Infoboard</span>
</div> </div>
</nav> </nav>
<section class="section pb-0 pt-3"> <section class="section">
<div class="container" style="max-width:860px"> <div class="container" style="max-width:820px">
<nav class="breadcrumb" aria-label="breadcrumb">
<ul>
<li><a href="/admin">Admin</a></li>
<li class="is-active"><a href="#" aria-current="page">Neuer Bildschirm</a></li>
</ul>
</nav>
</div>
</section>
<section class="section pt-2"> <div class="success-banner">
<div class="container" style="max-width:860px"> <strong> Screen «{{.Screen.Name}}» ({{.Screen.Slug}}) wurde angelegt.</strong><br>
Führe die folgenden Schritte aus, um den Bildschirm zu provisionieren.
<div class="notification is-success is-light">
<strong> Screen «{{.Screen.Name}}» ({{.Screen.Slug}}) wurde im Backend angelegt.</strong><br>
Führe die folgenden Schritte auf deinem Ansible-Host aus, um den Bildschirm zu provisionieren.
</div> </div>
<!-- Schritt 1 --> <div class="box mb-4">
<div class="box"> <div class="step-row">
<div class="step"> <span class="step-num">1</span>
<span class="step-number">1</span>
<div class="step-body"> <div class="step-body">
<p class="title is-6">Host zur Ansible-Inventardatei hinzufügen</p> <p class="has-text-weight-semibold mb-2">Host zur Ansible-Inventardatei hinzufügen</p>
<p class="mb-3">Öffne <code>ansible/inventory.yml</code> und füge den Host unter <code>signage_players hosts</code> ein:</p> <p class="mb-2 has-text-grey is-size-7">Öffne <code>ansible/inventory.yml</code> und füge ein:</p>
<pre id="inv"> {{.Screen.Slug}}:</pre> <pre id="inv"> {{.Screen.Slug}}:</pre>
<button class="button is-small is-light copy-btn mt-2" onclick="copy('inv')">📋 Kopieren</button> <button class="button is-small is-light copy-btn" onclick="copyEl('inv',this)">📋 Kopieren</button>
</div> </div>
</div> </div>
</div> </div>
<!-- Schritt 2 --> <div class="box mb-4">
<div class="box"> <div class="step-row">
<div class="step"> <span class="step-num">2</span>
<span class="step-number">2</span>
<div class="step-body"> <div class="step-body">
<p class="title is-6">Host-Variablen anlegen</p> <p class="has-text-weight-semibold mb-2">Host-Variablen anlegen</p>
<p class="mb-3">Erstelle die Datei <code>ansible/host_vars/{{.Screen.Slug}}/vars.yml</code> mit folgendem Inhalt:</p> <p class="mb-2 has-text-grey is-size-7">Erstelle <code>ansible/host_vars/{{.Screen.Slug}}/vars.yml</code>:</p>
<pre id="hostvars">--- <pre id="hostvars">---
ansible_host: {{.IP}} ansible_host: {{.IP}}
ansible_user: {{.SSHUser}} ansible_user: {{.SSHUser}}
@ -135,53 +130,45 @@ screen_id: {{.Screen.Slug}}
screen_name: "{{.Screen.Name}}" screen_name: "{{.Screen.Name}}"
screen_orientation: {{.Orientation}}</pre> screen_orientation: {{.Orientation}}</pre>
<div class="buttons mt-2"> <div class="buttons mt-2">
<button id="copy-btn-hostvars" class="button is-small is-light copy-btn" onclick="copy('hostvars', 'copy-btn-hostvars')">📋 Kopieren</button> <button class="button is-small is-light copy-btn" id="btn-hostvars" onclick="copyEl('hostvars','btn-hostvars')">📋 Kopieren</button>
<button class="button is-small is-light" onclick="downloadFile(document.getElementById('hostvars').innerText, 'vars.yml')"> Als Datei herunterladen</button> <button class="button is-small is-light" onclick="dlFile(document.getElementById('hostvars').innerText,'vars.yml')"> Herunterladen</button>
</div> </div>
<p class="help mt-2">Tipp: <code>mkdir -p ansible/host_vars/{{.Screen.Slug}}</code></p> <p class="help mt-1">Tipp: <code>mkdir -p ansible/host_vars/{{.Screen.Slug}}</code></p>
</div> </div>
</div> </div>
</div> </div>
<!-- Schritt 3 --> <div class="box mb-4">
<div class="box"> <div class="step-row">
<div class="step"> <span class="step-num">3</span>
<span class="step-number">3</span>
<div class="step-body"> <div class="step-body">
<p class="title is-6">SSH-Zugang sicherstellen</p> <p class="has-text-weight-semibold mb-2">SSH-Zugang sicherstellen</p>
<p>Stelle sicher, dass dein SSH-Key auf dem Zielgerät hinterlegt ist:</p>
<pre id="sshcopy">ssh-copy-id {{.SSHUser}}@{{.IP}}</pre> <pre id="sshcopy">ssh-copy-id {{.SSHUser}}@{{.IP}}</pre>
<button class="button is-small is-light copy-btn mt-2" onclick="copy('sshcopy')">📋 Kopieren</button> <button class="button is-small is-light copy-btn" onclick="copyEl('sshcopy',this)">📋 Kopieren</button>
</div> </div>
</div> </div>
</div> </div>
<!-- Schritt 4 --> <div class="box mb-4">
<div class="box"> <div class="step-row">
<div class="step"> <span class="step-num">4</span>
<span class="step-number">4</span>
<div class="step-body"> <div class="step-body">
<p class="title is-6">Ansible-Playbook ausführen</p> <p class="has-text-weight-semibold mb-2">Ansible-Playbook ausführen</p>
<p class="mb-3">Führe das Playbook vom Projektverzeichnis aus aus. Das installiert den Agent, konfiguriert Chromium und startet den Kiosk-Modus:</p> <pre id="playcmd">cd /path/to/morz-infoboard
<pre id="playbookcmd">cd /path/to/morz-infoboard
ansible-playbook -i ansible/inventory.yml ansible/site.yml --limit {{.Screen.Slug}}</pre> ansible-playbook -i ansible/inventory.yml ansible/site.yml --limit {{.Screen.Slug}}</pre>
<button class="button is-small is-light copy-btn mt-2" onclick="copy('playbookcmd')">📋 Kopieren</button> <button class="button is-small is-light copy-btn" onclick="copyEl('playcmd',this)">📋 Kopieren</button>
<p class="help mt-2"> <p class="help mt-1">Mit Vault: <code>--vault-password-file ansible/.vault_pass</code></p>
Falls du einen Vault-Pass verwendest:
<code>--vault-password-file ansible/.vault_pass</code>
</p>
</div> </div>
</div> </div>
</div> </div>
<!-- Schritt 5 --> <div class="box" style="border-left:4px solid #22c55e">
<div class="box"> <div class="step-row">
<div class="step"> <span class="step-num" style="background:#22c55e">5</span>
<span class="step-number">5</span>
<div class="step-body"> <div class="step-body">
<p class="title is-6">Fertig Playlist befüllen</p> <p class="has-text-weight-semibold mb-2">Fertig Playlist befüllen</p>
<p>Nach erfolgreichem Ansible-Lauf meldet sich der Bildschirm automatisch im Backend an und lädt seine Playlist. Jetzt kannst du Inhalte zuweisen:</p> <p class="mb-3 has-text-grey">Nach dem Ansible-Lauf meldet sich der Bildschirm automatisch an.</p>
<div class="buttons mt-3"> <div class="buttons">
<a class="button is-primary" href="/manage/{{.Screen.Slug}}">Playlist für «{{.Screen.Name}}» verwalten </a> <a class="button is-primary" href="/manage/{{.Screen.Slug}}">Playlist für «{{.Screen.Name}}» verwalten </a>
<a class="button" href="/admin"> Zurück zu Admin</a> <a class="button" href="/admin"> Zurück zu Admin</a>
</div> </div>
@ -191,27 +178,19 @@ ansible-playbook -i ansible/inventory.yml ansible/site.yml --limit {{.Screen.Slu
</div> </div>
</section> </section>
<script> <script>
function copy(id, btnId) { function copyEl(id, btn) {
var el = document.getElementById(id); navigator.clipboard.writeText(document.getElementById(id).innerText).then(function() {
if (!el) return; var b = typeof btn === 'string' ? document.getElementById(btn) : btn;
navigator.clipboard.writeText(el.innerText).then(function() { if (!b) return;
var btn = btnId var orig = b.textContent; b.textContent = ' Kopiert!';
? document.getElementById(btnId) setTimeout(function() { b.textContent = orig; }, 1500);
: el.nextElementSibling;
if (!btn) return;
var orig = btn.textContent;
btn.textContent = ' Kopiert!';
setTimeout(function() { btn.textContent = orig; }, 1500);
}); });
} }
function dlFile(content, name) {
function downloadFile(content, filename) {
var a = document.createElement('a'); var a = document.createElement('a');
a.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(content); a.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(content);
a.download = filename; a.download = name; a.click();
a.click();
} }
</script> </script>
</body> </body>