Einstellungen hat jeder, Verwaltung nicht
Die Seite hiess „Verwaltung" und war für alle anderen gar nicht erreichbar — „Meine Sprache" lag dadurch hinter einer Tür, die den meisten verschlossen ist. Jetzt heisst sie **Einstellungen**, steht jedem offen und hat zwei beschriftete Gruppen: oben, was einem selbst gehört (Mein Konto, Geschaut), darunter — nur für Administratoren — **Verwaltung**. Die Überschrift ist hier die halbe Aussage: sie sagt ohne weiteren Satz, wem der untere Teil gehört. In der Hauptleiste steht der Eintrag jetzt unten, ohne Gruppenüberschrift. Eine Überschrift über einem einzigen Eintrag ist eine Überschrift über nichts, und am Fuss sucht man Einstellungen ohnehin. `#/profil` und `#/verwaltung/…` leiten weiter; ein Lesezeichen merkt davon nichts. **Ein kurzes Menü am Bild.** Erst meldete der Klick ab — ohne Rückfrage. Dann führte er auf die eigene Seite, und zum Abmelden musste man erst dorthin. Beides sind zwei Punkte; ein Menü mit zwei Punkten ist ehrlicher als eine Tür, hinter der eine zweite liegt. **Und das Profilbild lässt sich zuschneiden.** Ein Profilbild ist rund, was hochgeladen wird selten quadratisch — den Ausschnitt dem Browser zu überlassen hiesse: mittig, und das trifft bei einem Porträt den Hals. Der Dialog zeigt den runden Ausschnitt, man zieht und vergrössert, und hochgeladen wird genau das: ein Quadrat von 512 Pixeln. Gemessen, es kommt auch so an. **Dazu die Spielerleiste.** Beide Knopfgruppen durften schrumpfen, ihre Knöpfe aber nicht — wird es eng, steht der äusserste halb hinter dem Fensterrand. Rechts sind es nur Knöpfe, da gibt es nichts nachzugeben; links schon. Dazu seitlich `env(safe-area-inset-*)`, damit auf einem Gerät mit runden Ecken nichts darunter liegt. Gemessen: vorher riss es ab 420 Pixeln, jetzt bis 320 nicht mehr. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9c2e2b5f47
commit
327e2e9195
@@ -0,0 +1,144 @@
|
||||
import { istAdmin, type Bibliothek } from '../../api';
|
||||
import { icon, type IconName } from '../../icons';
|
||||
import { kontoseite } from './konto';
|
||||
import { geschautseite } from './geschaut';
|
||||
import { dashboard, serverbereich, speicherbereich } from './uebersicht';
|
||||
import { bibliotheksbereich, vermisstenbereich } from './mediatheken';
|
||||
import { pruefbereich } from './pruefung';
|
||||
import { ohneZuordnungBereich, unzugeordnetbereich } from './zuordnung';
|
||||
import { wiedergabebereich } from './wiedergabe';
|
||||
import { sprachbereich } from './metadaten';
|
||||
import { benutzerbereich } from './benutzer';
|
||||
import { protokollbereich } from './protokolle';
|
||||
|
||||
/** Welcher Teil der Einstellungen offen ist. */
|
||||
export type Bereich =
|
||||
| 'konto' | 'geschaut'
|
||||
| 'uebersicht' | 'mediatheken' | 'pruefung' | 'zuordnung'
|
||||
| 'wiedergabe' | 'metadaten' | 'benutzer' | 'protokolle';
|
||||
|
||||
type Eintrag = { wert: Bereich; text: string; bild: IconName };
|
||||
|
||||
/** Was jedem gehört — das eigene Konto und die eigenen Zahlen. */
|
||||
const MEINS: Eintrag[] = [
|
||||
{ wert: 'konto', text: 'Mein Konto', bild: 'person' },
|
||||
{ wert: 'geschaut', text: 'Geschaut', bild: 'hakenKreis' },
|
||||
];
|
||||
|
||||
/** Was dem Server gehört. Nur Administratoren sehen diese Gruppe. */
|
||||
const SERVER: Eintrag[] = [
|
||||
{ wert: 'uebersicht', text: 'Übersicht', bild: 'verwaltung' },
|
||||
{ wert: 'mediatheken', text: 'Mediatheken', bild: 'filme' },
|
||||
{ wert: 'pruefung', text: 'Zu prüfen', bild: 'haken' },
|
||||
{ wert: 'zuordnung', text: 'Zuordnung', bild: 'suche' },
|
||||
{ wert: 'wiedergabe', text: 'Wiedergabe', bild: 'stufen' },
|
||||
{ wert: 'metadaten', text: 'Metadaten', bild: 'stift' },
|
||||
{ wert: 'benutzer', text: 'Benutzer', bild: 'serien' },
|
||||
{ wert: 'protokolle', text: 'Protokolle', bild: 'protokoll' },
|
||||
];
|
||||
|
||||
/** Macht aus einem Adressteil einen Bereich.
|
||||
*
|
||||
* Unbekanntes und Verbotenes werden zum eigenen Konto: das hat jeder, und es
|
||||
* ist die Seite, auf der man landen will, wenn man hier nichts zu suchen hat.
|
||||
*/
|
||||
export function alsBereich(wert: string | undefined, admin: boolean): Bereich {
|
||||
if (MEINS.some(b => b.wert === wert)) return wert as Bereich;
|
||||
if (admin && SERVER.some(b => b.wert === wert)) return wert as Bereich;
|
||||
return 'konto';
|
||||
}
|
||||
|
||||
/** Die Einstellungen.
|
||||
*
|
||||
* **Ein Ort, zwei Gruppen.** Oben steht, was einem selbst gehört — Bild,
|
||||
* Sprache, die eigenen Zahlen. Darunter, nur für Administratoren, was dem
|
||||
* Server gehört. Vorher hiess die ganze Seite „Verwaltung" und war für alle
|
||||
* anderen gar nicht erreichbar; „Meine Sprache" lag dadurch hinter einer Tür,
|
||||
* die den meisten verschlossen ist.
|
||||
*
|
||||
* **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.
|
||||
*
|
||||
* **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 einstellungenseite(
|
||||
welcher: Bereich = 'konto',
|
||||
geaendert: (bekannt?: Bibliothek[]) => void = () => {},
|
||||
signal: AbortSignal = new AbortController().signal,
|
||||
): HTMLElement {
|
||||
const admin = istAdmin();
|
||||
const wurzel = document.createElement('div');
|
||||
wurzel.className = 'verwaltung';
|
||||
|
||||
const leiste = document.createElement('nav');
|
||||
leiste.className = 'verw-leiste';
|
||||
leiste.setAttribute('aria-label', 'Bereiche der Einstellungen');
|
||||
|
||||
gruppe('Einstellungen', MEINS);
|
||||
if (admin) gruppe('Verwaltung', SERVER);
|
||||
|
||||
const buehne = document.createElement('div');
|
||||
buehne.className = 'verw-buehne';
|
||||
inhalt().forEach(a => buehne.appendChild(a));
|
||||
|
||||
wurzel.append(leiste, buehne);
|
||||
return wurzel;
|
||||
|
||||
/** Eine beschriftete Gruppe — die Beschriftung ist hier die halbe Aussage.
|
||||
*
|
||||
* „Verwaltung" über den unteren Einträgen sagt ohne weiteren Satz, dass
|
||||
* diese dem Server gehören und nicht einem selbst. */
|
||||
function gruppe(name: string, eintraege: Eintrag[]): void {
|
||||
const kopf = document.createElement('div');
|
||||
kopf.className = 'verw-gruppe';
|
||||
kopf.textContent = name;
|
||||
leiste.appendChild(kopf);
|
||||
|
||||
eintraege.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 = `#/einstellungen/${b.wert}`; };
|
||||
leiste.appendChild(k);
|
||||
});
|
||||
}
|
||||
|
||||
function inhalt(): HTMLElement[] {
|
||||
switch (welcher) {
|
||||
case 'geschaut':
|
||||
return [geschautseite()];
|
||||
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 'wiedergabe':
|
||||
return [wiedergabebereich()];
|
||||
case 'metadaten':
|
||||
return [sprachbereich()];
|
||||
case 'benutzer':
|
||||
return [benutzerbereich()];
|
||||
case 'protokolle':
|
||||
return [protokollbereich()];
|
||||
case 'uebersicht':
|
||||
return [dashboard(signal), serverbereich(geaendert), speicherbereich()];
|
||||
default:
|
||||
return [kontoseite()];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user