diff --git a/web/app/src/style.css b/web/app/src/style.css index df430e5..707ed07 100644 --- a/web/app/src/style.css +++ b/web/app/src/style.css @@ -1618,3 +1618,11 @@ body:has(.auswahlleiste:not(.hidden)) .hinweise { bottom: 104px; } } .fassungsdatei .oz-dazu { flex: 1 1 auto; } .btn.klein { padding: 4px 10px; font-size: 12px; flex: 0 0 auto; } + +/* Kurz hervorheben, wohin gesprungen wurde — sonst sucht man oben weiter. */ +.hervor { animation: hervor 1.6s ease-out; } +@keyframes hervor { + 0%, 60% { background: var(--accent-soft); border-radius: var(--r-karte); } + 100% { background: transparent; } +} +@media (prefers-reduced-motion: reduce) { .hervor { animation: none; } } diff --git a/web/app/src/views/admin.ts b/web/app/src/views/admin.ts index 250e3d7..cbe4578 100644 --- a/web/app/src/views/admin.ts +++ b/web/app/src/views/admin.ts @@ -4,7 +4,7 @@ import { vermisste, vermisstesEntfernen, type VermissteDatei, protokolltage, protokollAdresse, sprachen, spracheSetzen, uebersetzungenHolen, meineSprache, zuPruefen, veroeffentlichen, zurueckziehen, pruefungSetzen, - mediathekFreigeben, mediathekZuruecksetzen, + mediathekZuruecksetzen, type Pruefeintrag, benutzer, benutzerAnlegen, metadatenEinstellung, metadatenSetzen, servernameSetzen, systemauskunft, @@ -37,9 +37,19 @@ export function verwaltung( signal: AbortSignal = new AbortController().signal, ): HTMLElement { const wurzel = document.createElement('div'); - wurzel.append(dashboard(signal), sprachbereich(), pruefbereich(geaendert), - vermisstenbereich(geaendert), bibliotheksbereich(geaendert, signal), benutzerbereich(), - serverbereich(geaendert), speicherbereich(), ohneZuordnungBereich(), + + // **Die Prüfliste hört mit.** Ändert sich der Bestand — ein Scan ist durch, + // etwas wurde freigegeben —, holt sie sich neu. Ohne das stand die Zahl in + // der Mediathekenzeile und die Liste darüber war leer. + const pruefung = pruefbereich(() => { geaendert(); pruefung.auffrischen(); }); + const mitPruefung = (bekannt?: Bibliothek[]): void => { + geaendert(bekannt); + pruefung.auffrischen(); + }; + + wurzel.append(dashboard(signal), sprachbereich(), pruefung.wurzel, + vermisstenbereich(mitPruefung), bibliotheksbereich(mitPruefung, signal), benutzerbereich(), + serverbereich(mitPruefung), speicherbereich(), ohneZuordnungBereich(), unzugeordnetbereich(), protokollbereich()); return wurzel; @@ -182,9 +192,12 @@ function laufzeit(sekunden: number): string { * Geplante bleiben in der Liste. Ein Titel mit Termin ist nicht erledigt, er * hat nur schon einen — ihn verschwinden zu lassen hiesse, dass man ihn nur * wiederfindet, wenn man sich den Termin gemerkt hat. */ -function pruefbereich(geaendert: () => void): HTMLElement { +function pruefbereich(geaendert: () => void): { + wurzel: HTMLElement; auffrischen: () => void; +} { const bereich = document.createElement('section'); bereich.className = 'hidden'; + bereich.id = 'zu-pruefen'; const liste = document.createElement('div'); liste.className = 'einstellungen'; @@ -387,7 +400,12 @@ function pruefbereich(geaendert: () => void): HTMLElement { + `T${z(d.getHours())}:${z(d.getMinutes())}`; } - return bereich; + // **Nachladbar, und das fehlte.** Die Liste wurde einmal beim Öffnen + // geholt. Fand ein laufender Scan danach etwas Neues, zählte die Zeile der + // Mediathek es mit — „4 warten auf Freigabe" —, die Liste darüber blieb + // aber leer und sogar versteckt. Da stand eine Zahl ohne das, worauf sie + // sich bezog. + return { wurzel: bereich, auffrischen: () => void laden() }; } /* ---------- Protokolle ---------- */ @@ -1202,18 +1220,23 @@ function bibliotheksbereich( // anzulegen und wieder wegzuwerfen wäre genau das Flackern, das hier // gerade abgestellt wird. const freigeben = knopf('', 'btn btn-primary'); - freigeben.onclick = async () => { - freigeben.disabled = true; - try { - const bericht = await mediathekFreigeben(aktuell.id); - stand.textContent = `${bericht.published} freigegeben.`; - await laden(); - geaendert(); - } catch (fehler) { - d.appendChild(fehlerzeile(meldung(fehler))); - } finally { - freigeben.disabled = false; - } + // **Zeigen statt blind freigeben.** + // + // Der Knopf veröffentlichte bisher auf einen Klick alles, was wartete — + // ohne dass man gesehen hätte, was. Auf die Frage „wo sehe ich die vier + // Filme?" gab es damit keine Antwort: die Liste stand weiter oben, aber + // niemand wusste das, und sie war obendrein nicht nachgeladen worden. + // + // Jetzt führt er dorthin. Veröffentlicht wird dort, wo die Titel mit + // Plakat und Dateiname stehen und sich einzeln anhaken lassen. + freigeben.onclick = () => { + const liste = document.getElementById('zu-pruefen'); + if (!liste) return; + + liste.scrollIntoView({ behavior: 'smooth', block: 'start' }); + // Kurz hervorheben — sonst sucht man oben wieder, was man sucht. + liste.classList.add('hervor'); + window.setTimeout(() => liste.classList.remove('hervor'), 1600); }; knoepfe.appendChild(freigeben); @@ -1224,7 +1247,9 @@ function bibliotheksbereich( // „0 Titel freigeben" da, obwohl nichts wartet. `.hidden` trägt ein // `!important` und setzt sich durch. freigeben.classList.toggle('hidden', aktuell.pendingCount === 0); - freigeben.textContent = `${aktuell.pendingCount} Titel freigeben`; + freigeben.textContent = aktuell.pendingCount === 1 + ? 'Ein Titel wartet — ansehen' + : `${aktuell.pendingCount} Titel warten — ansehen`; } freigabeSchreiben();