Header: Profil- und Modell-Dropdowns rausgenommen, dafür ein
"⚙ Einstellungen"-Link. Klick öffnet eine eigene Settings-View
(toggled die main-View aus) mit drei Sektionen:
- Standard-Profil (sofort speichernd)
- Standard-LLM-Modell (sofort speichernd, leer = Worker-Default)
- Passwort ändern (aktuelles + neues + Bestätigung)
Neuer Endpoint POST /api/auth/me/password — verlangt zwingend das
aktuelle Passwort (verhindert Übernahme via offene Browser-Session).
Admin-Reset über /api/admin/users/<id>/password bleibt unberührt.
Layout: Settings-View ist eine Card mit max-width 640px, jede
Sektion durch Trennlinie abgegrenzt. Inline-Status-Meldungen
("Standard-Profil ist jetzt: …") direkt unter den Controls.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
226 lines
8.8 KiB
HTML
226 lines
8.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
<meta name="theme-color" content="#0f1115" />
|
|
<title>Voice-Agent — Transkription</title>
|
|
<link rel="stylesheet" href="/static/style.css" />
|
|
<link rel="manifest" href="/static/manifest.webmanifest" />
|
|
<link rel="icon" href="/static/icon.svg" type="image/svg+xml" />
|
|
<link rel="apple-touch-icon" href="/static/icon-192.png" />
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
<meta name="apple-mobile-web-app-title" content="Voice-Agent" />
|
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<div class="header-row">
|
|
<div class="header-left">
|
|
<h1>Voice-Agent</h1>
|
|
<p class="sub desktop-only">Sitzungsaufnahmen automatisch transkribieren und protokollieren</p>
|
|
</div>
|
|
<div class="header-right">
|
|
<div id="mac-status" class="badge desktop-only">Mac-Backend: prüfe …</div>
|
|
<div id="user-bar" class="user-bar hidden">
|
|
<span class="muted desktop-only">Angemeldet als</span>
|
|
<strong id="user-name"></strong>
|
|
<span id="user-admin-badge" class="badge admin-badge hidden">Admin</span>
|
|
<button type="button" id="settings-btn" class="btn-link" title="Eigene Einstellungen — Profil, Modell, Passwort">⚙ Einstellungen</button>
|
|
<button type="button" id="logout-btn" class="btn-link">Abmelden</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Auth-Overlay (Login oder Erst-Setup) -->
|
|
<div id="auth-overlay" class="overlay hidden">
|
|
<div class="auth-card">
|
|
<h2 id="auth-title">Anmelden</h2>
|
|
<p id="auth-info" class="muted hidden"></p>
|
|
<form id="auth-form">
|
|
<label>
|
|
Benutzername
|
|
<input type="text" id="auth-username" autocomplete="username" required />
|
|
</label>
|
|
<label>
|
|
Passwort
|
|
<input type="password" id="auth-password" autocomplete="current-password" required />
|
|
</label>
|
|
<button type="submit" id="auth-submit">Anmelden</button>
|
|
</form>
|
|
<p id="auth-msg" class="msg"></p>
|
|
</div>
|
|
</div>
|
|
|
|
<main id="app" class="hidden">
|
|
<!-- Settings-View — eigene "Unterseite" für Profil/Modell/Passwort -->
|
|
<section id="settings-view" class="card hidden settings-view">
|
|
<div class="settings-head">
|
|
<h2>Einstellungen für <span id="settings-username">…</span></h2>
|
|
<button type="button" id="settings-back" class="btn-link">← zurück</button>
|
|
</div>
|
|
|
|
<div class="settings-section">
|
|
<h3>Standard-Profil</h3>
|
|
<p class="muted">Wird bei jedem neuen Upload vorausgewählt. Pro Upload überschreibbar.</p>
|
|
<select id="settings-profile" class="settings-input"></select>
|
|
<p id="settings-profile-msg" class="msg"></p>
|
|
</div>
|
|
|
|
<div class="settings-section" id="settings-model-section">
|
|
<h3>Standard-LLM-Modell</h3>
|
|
<p class="muted">Leer = der Mac-Worker entscheidet (sein .env-Default). Wechsel braucht keinen Worker-Restart — Ollama lädt das gewählte Modell beim ersten Job nach.</p>
|
|
<select id="settings-model" class="settings-input"></select>
|
|
<p id="settings-model-msg" class="msg"></p>
|
|
</div>
|
|
|
|
<div class="settings-section">
|
|
<h3>Passwort ändern</h3>
|
|
<form id="settings-pw-form" autocomplete="off">
|
|
<label>
|
|
Aktuelles Passwort
|
|
<input type="password" id="pw-current" autocomplete="current-password" required minlength="1" />
|
|
</label>
|
|
<label>
|
|
Neues Passwort <span class="muted">(min. 8 Zeichen)</span>
|
|
<input type="password" id="pw-new" autocomplete="new-password" required minlength="8" />
|
|
</label>
|
|
<label>
|
|
Neues Passwort wiederholen
|
|
<input type="password" id="pw-confirm" autocomplete="new-password" required minlength="8" />
|
|
</label>
|
|
<button type="submit" id="pw-save">Passwort speichern</button>
|
|
</form>
|
|
<p id="settings-pw-msg" class="msg"></p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Haupt-View (Upload + Jobs + Admin) -->
|
|
<div id="main-view">
|
|
<section class="card upload-card">
|
|
<h2 class="desktop-only">Neue Aufnahme hochladen</h2>
|
|
<form id="upload-form">
|
|
<!-- HERO: Aufnahme-Button (auf Mobile groß und zentriert) -->
|
|
<button type="button" id="record-toggle" class="btn-record">
|
|
<span class="rec-dot"></span>
|
|
<span id="record-toggle-label">Aufnahme starten</span>
|
|
</button>
|
|
|
|
<!-- Aufnahme-Bühne -->
|
|
<div id="record-pane" class="record-pane hidden">
|
|
<div class="record-timer" id="record-timer">00:00</div>
|
|
<div class="record-status" id="record-status">Aufnahme läuft</div>
|
|
<div class="record-controls">
|
|
<button type="button" id="rec-pause" class="btn-record-ctrl">Pause</button>
|
|
<button type="button" id="rec-stop" class="btn-record-ctrl danger">Stoppen</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Aufnahme bereit -->
|
|
<div id="record-result" class="record-result hidden">
|
|
<span class="muted">Aufnahme bereit:</span>
|
|
<span id="record-filename"></span>
|
|
<button type="button" id="record-discard" class="btn-link btn-danger">Verwerfen</button>
|
|
</div>
|
|
|
|
<!-- Profil + Titel -->
|
|
<div class="upload-meta">
|
|
<label id="profile-label" class="hidden">
|
|
Verarbeitungs-Profil
|
|
<select id="profile-select"></select>
|
|
</label>
|
|
<label>
|
|
Titel
|
|
<input type="text" name="title" id="title" placeholder="optional, z. B. Monatsbesprechung" />
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Datei-Upload als Klappe (auf Mobile collapsed, auf Desktop offen) -->
|
|
<details class="audio-file-details" id="audio-file-details">
|
|
<summary>Lieber eine vorhandene Audio-Datei hochladen?</summary>
|
|
<label class="audio-file-label">
|
|
<span class="muted">MP3, WAV, M4A, MP4, OGG, FLAC, WebM</span>
|
|
<input type="file" name="audio" id="audio" accept="audio/*,.mp3,.wav,.m4a,.mp4,.ogg,.flac,.webm" />
|
|
</label>
|
|
</details>
|
|
|
|
<button type="submit" id="submit-btn">Hochladen & verarbeiten</button>
|
|
</form>
|
|
<div id="upload-progress" class="hidden">
|
|
<div class="bar"><div class="bar-fill"></div></div>
|
|
<small class="muted">Upload läuft …</small>
|
|
</div>
|
|
<p id="upload-msg" class="msg"></p>
|
|
</section>
|
|
|
|
<section class="card jobs-card">
|
|
<h2>Jobs</h2>
|
|
<!-- Desktop: Tabelle -->
|
|
<table id="jobs-table" class="desktop-only">
|
|
<thead>
|
|
<tr>
|
|
<th class="col-id">#</th>
|
|
<th class="col-title">Titel / Datei</th>
|
|
<th class="col-status">Status</th>
|
|
<th class="col-progress">Fortschritt</th>
|
|
<th class="col-updated">Aktualisiert</th>
|
|
<th class="col-dl">Downloads</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="jobs-body">
|
|
<tr><td colspan="6" class="muted">Noch keine Jobs.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
<!-- Mobile: Card-Liste -->
|
|
<div id="jobs-cards" class="jobs-cards mobile-only">
|
|
<div class="muted">Noch keine Jobs.</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Nur sichtbar für Admins -->
|
|
<section id="admin-card" class="card hidden admin-collapsible">
|
|
<h2 class="admin-toggle" id="admin-toggle">Benutzer verwalten <span class="admin-toggle-icon">▾</span></h2>
|
|
<div class="admin-body">
|
|
<form id="user-form" class="user-form">
|
|
<label>
|
|
Benutzername
|
|
<input type="text" id="new-username" autocomplete="off" required pattern="[A-Za-z0-9._@\-]+" />
|
|
</label>
|
|
<label>
|
|
Passwort (min. 8 Zeichen)
|
|
<input type="password" id="new-password" autocomplete="new-password" required minlength="8" />
|
|
</label>
|
|
<label class="inline-check">
|
|
<input type="checkbox" id="new-is-admin" />
|
|
Admin-Rechte
|
|
</label>
|
|
<button type="submit">Anlegen</button>
|
|
</form>
|
|
<p id="user-msg" class="msg"></p>
|
|
<table id="users-table">
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Benutzer</th>
|
|
<th>Rolle</th>
|
|
<th>Angelegt</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="users-body"></tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
</div><!-- /main-view -->
|
|
</main>
|
|
|
|
<footer>
|
|
<small class="muted">DSGVO-konform, alles lokal verarbeitet.</small>
|
|
</footer>
|
|
|
|
<script src="/static/app.js"></script>
|
|
</body>
|
|
</html>
|