Elf Abschnitte in einer Spalte waren zehn zu viel
Die Verwaltung war eine Seite mit 1786 Zeilen. Wer das Protokoll sehen wollte, scrollte an Mediatheken, Prüfliste, Benutzern und Speicher vorbei — und jede neue Einstellung hätte sie länger gemacht. Jetzt hat sie eine eigene Leiste mit sieben Bereichen, jeder mit seiner Adresse: `#/verwaltung/protokolle` lässt sich ablegen, und ein Neuladen bleibt dort stehen. Die Leiste sieht aus wie die Hauptleiste daneben, damit sie als Navigation gelesen wird und nicht als Knopfreihe; am Telefon wird aus der Spalte ein Streifen zum Schieben. **Gebaut wird nur, was offen ist.** Das ist nicht nur sparsamer: das Abspielfenster fragt im Fünf-Sekunden-Takt nach, die Mediathekenliste beobachtet laufende Scans. Gemessen: nach dem Wechsel in einen anderen Bereich kommt zwölf Sekunden lang keine Anfrage mehr. `admin.ts` ist in acht Dateien zerlegt — eine je Bereich, dazu `teile.ts` für die gemeinsame Überschrift und `index.ts` für Leiste und Weiche. Zwei Dinge mussten dabei nachziehen: **Die Markierung in der Hauptleiste.** Sie verglich stur zwei Segmente; bei `#/verwaltung/metadaten` traf das auf keinen Eintrag, und „Verwaltung" verlor seinen Balken. Jetzt gilt auch ein Segment. **„5 Titel warten — ansehen".** Der Knopf sprang mit `scrollIntoView` zur Prüfliste. Die steht nicht mehr im selben Dokument — er wechselt jetzt den Bereich. Die Hervorhebung danach entfällt damit, und mit ihr ihr CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
94c6604e8b
commit
1e4a231d1b
@@ -0,0 +1,102 @@
|
||||
import { type Bibliothek } from '../../api';
|
||||
import { icon, type IconName } from '../../icons';
|
||||
import { dashboard, serverbereich, speicherbereich } from './uebersicht';
|
||||
import { bibliotheksbereich, vermisstenbereich } from './mediatheken';
|
||||
import { pruefbereich } from './pruefung';
|
||||
import { ohneZuordnungBereich, unzugeordnetbereich } from './zuordnung';
|
||||
import { sprachbereich } from './metadaten';
|
||||
import { benutzerbereich } from './benutzer';
|
||||
import { protokollbereich } from './protokolle';
|
||||
|
||||
/** Welcher Teil der Verwaltung offen ist. */
|
||||
export type Bereich =
|
||||
| 'uebersicht' | 'mediatheken' | 'pruefung' | 'zuordnung'
|
||||
| 'metadaten' | 'benutzer' | 'protokolle';
|
||||
|
||||
const BEREICHE: { wert: Bereich; text: string; bild: IconName }[] = [
|
||||
{ wert: 'uebersicht', text: 'Übersicht', bild: 'verwaltung' },
|
||||
{ wert: 'mediatheken', text: 'Mediatheken', bild: 'filme' },
|
||||
{ wert: 'pruefung', text: 'Zu prüfen', bild: 'hakenKreis' },
|
||||
{ wert: 'zuordnung', text: 'Zuordnung', bild: 'suche' },
|
||||
{ wert: 'metadaten', text: 'Metadaten', bild: 'stift' },
|
||||
{ wert: 'benutzer', text: 'Benutzer', bild: 'person' },
|
||||
{ wert: 'protokolle', text: 'Protokolle', bild: 'protokoll' },
|
||||
];
|
||||
|
||||
/** Macht aus einem Adressteil einen Bereich — Unbekanntes wird zur Übersicht. */
|
||||
export function alsBereich(wert: string | undefined): Bereich {
|
||||
return BEREICHE.some(b => b.wert === wert) ? wert as Bereich : 'uebersicht';
|
||||
}
|
||||
|
||||
/** Die Verwaltung.
|
||||
*
|
||||
* **Was etwas anlegt oder ändert, steht in einem Dialog**, nicht als Block
|
||||
* in der Seite. Ein Formular, das immer dasteht, nimmt Platz für etwas weg,
|
||||
* das man einmal im Monat braucht — und sieht aus wie eine Eingabe, die
|
||||
* noch aussteht.
|
||||
*
|
||||
* **Warum eine zweite Leiste.** Vorher hingen elf Abschnitte untereinander in
|
||||
* einer Seite — 1786 Zeilen, und jede neue Einstellung machte sie länger. Wer
|
||||
* das Protokoll sehen wollte, scrollte an den Mediatheken, der Prüfliste und
|
||||
* dem Speicher vorbei. Jetzt hat jeder Bereich seine eigene Adresse: man kann
|
||||
* ihn als Lesezeichen ablegen, und ein Neuladen bleibt dort stehen.
|
||||
*
|
||||
* **Nur der offene Bereich wird gebaut.** Das ist nicht nur sparsamer — das
|
||||
* Abspielfenster der Übersicht fragt im Takt von fünf Sekunden nach, und die
|
||||
* Mediathekenliste beobachtet laufende Scans. Was nicht dasteht, fragt auch
|
||||
* nicht.
|
||||
*
|
||||
* `geaendert` wird gerufen, wenn sich die Mediathekenliste geändert hat; die
|
||||
* Seitenleiste steht auf ihr und muss dann neu gezeichnet werden. */
|
||||
export function verwaltung(
|
||||
welcher: Bereich = 'uebersicht',
|
||||
geaendert: (bekannt?: Bibliothek[]) => void = () => {},
|
||||
signal: AbortSignal = new AbortController().signal,
|
||||
): HTMLElement {
|
||||
const wurzel = document.createElement('div');
|
||||
wurzel.className = 'verwaltung';
|
||||
|
||||
const leiste = document.createElement('nav');
|
||||
leiste.className = 'verw-leiste';
|
||||
leiste.setAttribute('aria-label', 'Bereiche der Verwaltung');
|
||||
BEREICHE.forEach(b => {
|
||||
const k = document.createElement('button');
|
||||
k.type = 'button';
|
||||
k.className = 'verw-eintrag' + (b.wert === welcher ? ' check' : '');
|
||||
k.appendChild(icon(b.bild));
|
||||
k.append(document.createTextNode(b.text));
|
||||
if (b.wert === welcher) k.setAttribute('aria-current', 'page');
|
||||
k.onclick = () => { location.hash = `#/verwaltung/${b.wert}`; };
|
||||
leiste.appendChild(k);
|
||||
});
|
||||
|
||||
const buehne = document.createElement('div');
|
||||
buehne.className = 'verw-buehne';
|
||||
inhalt().forEach(a => buehne.appendChild(a));
|
||||
|
||||
wurzel.append(leiste, buehne);
|
||||
return wurzel;
|
||||
|
||||
function inhalt(): HTMLElement[] {
|
||||
switch (welcher) {
|
||||
case 'mediatheken':
|
||||
return [bibliotheksbereich(geaendert, signal), vermisstenbereich(geaendert)];
|
||||
case 'pruefung': {
|
||||
// **Die Prüfliste hört auf sich selbst.** Wird etwas freigegeben,
|
||||
// ändert sich der Bestand — und die Liste darüber muss nachziehen.
|
||||
const p = pruefbereich(() => { geaendert(); p.auffrischen(); });
|
||||
return [p.wurzel];
|
||||
}
|
||||
case 'zuordnung':
|
||||
return [ohneZuordnungBereich(), unzugeordnetbereich()];
|
||||
case 'metadaten':
|
||||
return [sprachbereich()];
|
||||
case 'benutzer':
|
||||
return [benutzerbereich()];
|
||||
case 'protokolle':
|
||||
return [protokollbereich()];
|
||||
default:
|
||||
return [dashboard(signal), serverbereich(geaendert), speicherbereich()];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user