root 220b7ccbc3 feat: Retry-Funktion + idempotente Pipeline (Transkript bleibt erhalten)
Backend:
- POST /api/jobs/{id}/retry — löscht summary/protocol/docx, queued den Job
  neu und startet Pipeline. Transkript bleibt erhalten (teurer Schritt).
- Pipeline überspringt Steps, deren Output-Datei schon existiert →
  retry rennt direkt in summarize ohne erneute Transkription.

Frontend:
- Kleiner gelber '↻ Erneut versuchen'-Button in jeder Job-Zeile/-Karte
  wenn Status != done
- Confirm-Dialog erklärt was passiert
- Event-Delegation, funktioniert für Tabelle (Desktop) und Cards (Mobile)
2026-05-14 04:11:13 +00:00

563 lines
14 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

:root {
--bg: #0f1115;
--card: #181c24;
--border: #262b36;
--text: #e6e8eb;
--muted: #8b93a3;
--accent: #4f8cff;
--accent-hover: #3a78f0;
--ok: #2ecc71;
--warn: #f1c40f;
--err: #e74c3c;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
line-height: 1.5;
}
header {
padding: 32px 24px 16px;
text-align: center;
border-bottom: 1px solid var(--border);
}
header h1 { margin: 0 0 4px; font-size: 28px; letter-spacing: .3px; }
.sub { color: var(--muted); margin: 0 0 12px; }
.badge {
display: inline-block;
padding: 4px 10px;
border-radius: 999px;
background: var(--card);
border: 1px solid var(--border);
font-size: 12px;
color: var(--muted);
}
.badge.ok { color: var(--ok); border-color: rgba(46,204,113,.4); }
.badge.err { color: var(--err); border-color: rgba(231,76,60,.4); }
main {
max-width: 1000px;
margin: 24px auto;
padding: 0 16px;
display: grid;
gap: 20px;
}
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px 22px;
}
.card h2 { margin-top: 0; font-size: 18px; }
form { display: grid; gap: 12px; }
label { display: grid; gap: 6px; font-size: 14px; color: var(--muted); }
input[type=text], input[type=file] {
background: #0f1219;
border: 1px solid var(--border);
color: var(--text);
padding: 10px 12px;
border-radius: 8px;
font-size: 14px;
}
button {
background: var(--accent);
color: white;
border: 0;
padding: 10px 16px;
border-radius: 8px;
font-size: 14px;
cursor: pointer;
justify-self: start;
}
button:hover { background: var(--accent-hover); }
button:disabled { opacity: .6; cursor: not-allowed; }
.hidden { display: none !important; }
.msg { min-height: 1em; margin: 8px 0 0; font-size: 13px; }
.msg.ok { color: var(--ok); }
.msg.err { color: var(--err); }
.muted { color: var(--muted); }
.bar {
width: 100%;
height: 8px;
background: #0f1219;
border-radius: 4px;
overflow: hidden;
margin-top: 6px;
}
.bar-fill {
height: 100%;
width: 0%;
background: var(--accent);
transition: width .25s ease;
}
table { width: 100%; border-collapse: collapse; font-size: 14px; table-layout: fixed; }
th, td { padding: 10px 8px; text-align: left; border-bottom: 1px solid var(--border); vertical-align: top; }
th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .5px; }
/* Spaltenbreiten — Status bleibt schmal, Downloads bekommen Platz */
.col-id { width: 56px; }
.col-status { width: 110px; }
.col-progress { width: 130px; }
.col-updated { width: 130px; }
.col-dl { width: 280px; }
/* col-title nimmt den Rest */
.col-title { word-break: break-word; }
.status-pill {
display: inline-block;
padding: 2px 10px;
border-radius: 999px;
font-size: 12px;
border: 1px solid var(--border);
white-space: nowrap;
}
.status-queued { color: var(--muted); }
.status-transcribing, .status-summarizing, .status-protocolling, .status-exporting { color: var(--warn); border-color: rgba(241,196,15,.4); }
.status-done { color: var(--ok); border-color: rgba(46,204,113,.4); }
.status-failed { color: var(--err); border-color: rgba(231,76,60,.4); }
/* Fehlertext unter dem Pill — bricht um, max. 3 Zeilen */
.status-error {
margin-top: 6px;
font-size: 11px;
color: var(--err);
line-height: 1.35;
word-break: break-word;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* Download-Buttons — sauberes 2×2-Grid, alle gleich breit */
.dl {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px;
}
.btn-dl {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 5px 10px;
border-radius: 6px;
background: rgba(79,140,255,.12);
border: 1px solid rgba(79,140,255,.4);
color: var(--accent);
text-decoration: none;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
transition: background .15s ease, border-color .15s ease;
}
.btn-dl:hover { background: rgba(79,140,255,.22); border-color: var(--accent); }
.btn-dl.disabled {
color: var(--muted);
background: transparent;
border-color: var(--border);
pointer-events: none;
opacity: .55;
}
footer { text-align: center; padding: 24px 16px 32px; }
/* ── Header-Layout ─────────────────────────────────────────────── */
.header-row {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
justify-content: space-between;
gap: 16px;
max-width: 1000px;
margin: 0 auto;
}
.header-left { text-align: left; }
.header-right { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.user-bar {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
}
.user-bar strong { color: var(--text); }
.btn-link {
background: transparent;
color: var(--accent);
border: 0;
padding: 2px 6px;
font-size: 13px;
cursor: pointer;
text-decoration: underline;
}
.btn-link:hover { color: var(--accent-hover); }
.btn-link.btn-danger { color: var(--err); }
.admin-badge {
background: rgba(79,140,255,.18);
color: var(--accent);
border-color: rgba(79,140,255,.45);
text-transform: uppercase;
font-size: 11px;
letter-spacing: .5px;
padding: 2px 8px;
}
/* ── Auth-Overlay ──────────────────────────────────────────────── */
.overlay {
position: fixed;
inset: 0;
background: rgba(15, 17, 21, .92);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
padding: 20px;
}
.auth-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 28px 28px 22px;
width: 100%;
max-width: 380px;
box-shadow: 0 20px 60px rgba(0,0,0,.4);
}
.auth-card h2 { margin: 0 0 8px; font-size: 20px; }
.auth-card #auth-info { font-size: 13px; margin: 0 0 14px; }
.auth-card form { display: grid; gap: 12px; }
.auth-card button { justify-self: stretch; }
/* ── Admin: Benutzer-Verwaltung ────────────────────────────────── */
.user-form {
display: grid;
grid-template-columns: 1fr 1fr auto auto;
gap: 12px;
align-items: end;
}
.user-form label { font-size: 13px; }
.inline-check {
display: flex !important;
flex-direction: row !important;
align-items: center;
gap: 6px;
color: var(--text);
}
.inline-check input { margin: 0; }
#users-table .user-actions {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
@media (max-width: 700px) {
.user-form { grid-template-columns: 1fr; }
}
/* Select-Felder im selben Stil wie Inputs */
select {
background: #0f1219;
border: 1px solid var(--border);
color: var(--text);
padding: 10px 12px;
border-radius: 8px;
font-size: 14px;
font-family: inherit;
}
/* Kompakter Profil-Selector im Header */
.header-profile-wrap {
display: inline-flex;
align-items: center;
gap: 6px;
}
.header-profile {
padding: 3px 6px;
font-size: 12px;
border-radius: 5px;
background: #0f1219;
border: 1px solid var(--border);
color: var(--text);
max-width: 180px;
}
.header-profile:disabled { opacity: .5; }
/* Aufnahme-Button (groß, rot, touch-tauglich) */
.btn-record {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 14px 18px;
background: rgba(231,76,60,.12);
color: var(--err);
border: 1px solid rgba(231,76,60,.5);
border-radius: 10px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
min-height: 48px;
}
.btn-record:hover { background: rgba(231,76,60,.22); }
.rec-dot {
width: 12px; height: 12px;
background: var(--err);
border-radius: 50%;
}
/* Aufnahme-Bühne */
.record-pane {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
padding: 20px;
border: 1px solid rgba(231,76,60,.4);
background: rgba(231,76,60,.06);
border-radius: 12px;
margin-top: 6px;
}
.record-timer {
font-size: 44px;
font-variant-numeric: tabular-nums;
font-weight: 600;
color: var(--err);
letter-spacing: .04em;
}
.record-status { color: var(--muted); font-size: 13px; }
.record-status::before {
content: "● ";
color: var(--err);
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: .25; } }
.record-controls { display: flex; gap: 10px; margin-top: 6px; }
.btn-record-ctrl {
padding: 12px 22px;
border-radius: 8px;
background: var(--card);
color: var(--text);
border: 1px solid var(--border);
font-size: 15px;
cursor: pointer;
min-height: 44px;
min-width: 100px;
}
.btn-record-ctrl.danger {
background: var(--err);
color: white;
border-color: var(--err);
}
/* Aufnahme-Ergebnis */
.record-result {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
background: rgba(46,204,113,.08);
border: 1px solid rgba(46,204,113,.4);
border-radius: 8px;
font-size: 13px;
}
/* ── Sichtbarkeit-Helfer ───────────────────────────────────────── */
.mobile-only { display: none; }
/* Upload-Form Layout (Desktop) */
.upload-meta {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.audio-file-details summary {
cursor: pointer;
padding: 8px 4px;
color: var(--muted);
font-size: 13px;
user-select: none;
}
.audio-file-details summary:hover { color: var(--text); }
.audio-file-details[open] summary { color: var(--text); margin-bottom: 8px; }
.audio-file-label {
display: grid;
gap: 6px;
}
/* Admin-Toggle (auf Desktop deaktiviert) */
.admin-toggle { cursor: default; }
.admin-toggle-icon { display: none; }
/* ── Mobile-Optimierung (echte Mobile-First-UI) ────────────────── */
@media (max-width: 700px) {
.desktop-only { display: none !important; }
.mobile-only { display: block; }
body { font-size: 15px; }
/* HEADER: nur Logo + User */
header {
padding: 10px 14px;
text-align: left;
border-bottom: 1px solid var(--border);
}
header h1 { font-size: 18px; margin: 0; }
.header-row {
flex-direction: row;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.header-left, .header-right { align-items: center; }
.header-right { flex-direction: row; gap: 8px; flex: 1; justify-content: flex-end; }
.user-bar {
display: flex;
flex-direction: row;
align-items: center;
gap: 6px;
font-size: 13px;
flex-wrap: nowrap;
}
.user-bar strong { font-size: 14px; max-width: 90px; overflow: hidden; text-overflow: ellipsis; }
.header-profile { max-width: 130px; padding: 4px 6px; font-size: 12px; }
.btn-link { font-size: 12px; padding: 4px 6px; }
main { padding: 14px 12px 90px; gap: 14px; }
.card { padding: 16px; border-radius: 12px; }
.card h2 { font-size: 16px; margin-bottom: 10px; }
/* UPLOAD-CARD: Recording-Hero-Style */
.upload-card form { display: flex; flex-direction: column; gap: 14px; }
.btn-record {
/* HERO: groß und auffällig */
padding: 26px;
font-size: 19px;
min-height: 90px;
border-radius: 14px;
box-shadow: 0 4px 20px rgba(231,76,60,.15);
}
.btn-record .rec-dot { width: 16px; height: 16px; animation: pulse 1.5s ease-in-out infinite; }
.upload-meta { grid-template-columns: 1fr; gap: 10px; }
.upload-meta select,
.upload-meta input[type=text] {
padding: 14px 12px;
font-size: 16px; /* iOS: ≥16px verhindert Zoom-on-Focus */
min-height: 48px;
}
.audio-file-details summary { padding: 12px 4px; font-size: 14px; }
button[type="submit"] {
padding: 16px;
font-size: 16px;
min-height: 52px;
border-radius: 10px;
width: 100%;
}
/* Aufnahme-Bühne: noch größer auf Mobile */
.record-pane { padding: 24px 16px; }
.record-timer { font-size: 64px; line-height: 1; }
.record-controls { width: 100%; gap: 12px; }
.btn-record-ctrl {
flex: 1;
padding: 18px;
font-size: 17px;
min-height: 56px;
min-width: 0;
}
/* JOBS-CARDS statt Tabelle */
.jobs-cards { display: flex; flex-direction: column; gap: 10px; }
.jc {
background: #0f1219;
border: 1px solid var(--border);
border-radius: 10px;
padding: 12px 14px;
}
.jc-head {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 8px;
margin-bottom: 6px;
}
.jc-id { color: var(--muted); font-size: 12px; }
.jc-title { font-weight: 600; font-size: 15px; line-height: 1.3; word-break: break-word; }
.jc-sub { color: var(--muted); font-size: 12px; margin-top: 2px; }
.jc-status-row {
display: flex;
align-items: center;
gap: 8px;
margin: 8px 0 6px;
}
.jc-progress { flex: 1; }
.jc-error {
color: var(--err);
font-size: 12px;
margin: 4px 0;
word-break: break-word;
}
.jc-dl {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px;
margin-top: 8px;
}
.jc-dl .btn-dl { padding: 8px; font-size: 12px; min-height: 36px; }
/* Admin-Bereich: collapsible */
.admin-collapsible .admin-toggle {
cursor: pointer;
user-select: none;
display: flex;
justify-content: space-between;
align-items: center;
margin: 0;
}
.admin-toggle-icon { display: inline; transition: transform .2s; }
.admin-collapsible:not(.open) .admin-body { display: none; }
.admin-collapsible.open .admin-toggle-icon { transform: rotate(180deg); }
.user-form { grid-template-columns: 1fr; }
.user-form input { font-size: 16px; }
}
/* iOS PWA: oberer Sicherheitsbereich (Notch) respektieren */
@supports (padding: max(0px)) {
header { padding-top: max(10px, env(safe-area-inset-top)); }
main { padding-bottom: max(20px, env(safe-area-inset-bottom)); }
}
/* Retry-Button in der Status-Spalte */
.btn-retry {
display: inline-block;
margin-top: 6px;
padding: 3px 8px;
font-size: 11px;
font-weight: 500;
background: rgba(241,196,15,.12);
color: var(--warn);
border: 1px solid rgba(241,196,15,.4);
border-radius: 5px;
text-decoration: none;
cursor: pointer;
}
.btn-retry:hover { background: rgba(241,196,15,.22); text-decoration: none; }
.btn-retry:disabled { opacity: .55; cursor: not-allowed; }
/* Profile-Tag in der Job-Zeile */
.tag-profile {
display: inline-block;
padding: 1px 8px;
margin-left: 6px;
border-radius: 4px;
background: rgba(46,204,113,.12);
border: 1px solid rgba(46,204,113,.4);
color: var(--ok);
font-size: 11px;
text-transform: lowercase;
vertical-align: middle;
}