fix(dashboard): Rasterversion schuetzen, Kalender-Mindestbreite, Breiten je Breakpoint, Hintergrund

- Layout mit neuerer Rasterversion wird nie gespeichert, Bearbeiten gesperrt mit Hinweis
- Kalender minW 11 (~260 px bei lg), Breiten je Breakpoint auf Spaltenzahl begrenzt
- optimistische Ruecksetzung nur, wenn noch der gesetzte Wert steht
- Titel-Schalter mit fester Beschriftung + aria-pressed
- Hintergrund-Dialog: Fokus rein/zurueck, Tab bleibt im Dialog
- Loeschen eines Bildes setzt eine darauf zeigende Hintergrund-Wahl zurueck
- Uebersetzungen und CHANGELOG

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-30 03:20:16 +02:00
parent 071082983b
commit 12214a948e
21 changed files with 1194 additions and 205 deletions
+16 -3
View File
@@ -34,6 +34,13 @@
* Idempotenz: `migrateGridLayouts(withGridVersion(migrateGridLayouts(alt).layouts))`
* liefert dasselbe Ergebnis wie `migrateGridLayouts(alt)` mit `migrated: false`.
*
* Neuere Marke (quick-260930): Traegt das JSON eine HOEHERE Marke als
* `GRID_VERSION`, hat es ein neuerer Programmstand geschrieben (etwa ein
* frisch geladener Tab neben einem alten). Umgerechnet wird dann nichts, aber
* `newer: true` gemeldet — der Store darf diese Anordnung NIE speichern: er
* wuerde sie mit der alten Marke zurueckschreiben, und der neue Stand wuerde
* sie beim naechsten Laden ein zweites Mal skalieren.
*
* Ort: Frontend, weil die Raster-Einheiten Frontend-Konstanten sind, die API
* das JSON nur durchreicht (`@IsObject()`) und so kein Schreiben auf einem
* GET und keine Aenderung am API-Dienst noetig ist. Reine Funktionen ohne
@@ -72,14 +79,20 @@ function isPlainObject(value: unknown): value is Record<string, unknown> {
* Raster-Einheiten um. Liefert die Anordnung OHNE Marker und die Angabe, ob
* etwas verdoppelt wurde (dann muss der Aufrufer sofort mit Marker speichern).
*/
export function migrateGridLayouts(raw: unknown): { layouts: GridLayouts; migrated: boolean } {
export function migrateGridLayouts(raw: unknown): {
layouts: GridLayouts;
migrated: boolean;
/** Marke hoeher als `GRID_VERSION` — Anordnung stammt von einem neueren Programmstand. */
newer: boolean;
} {
if (!isPlainObject(raw)) {
return { layouts: {}, migrated: false };
return { layouts: {}, migrated: false, newer: false };
}
const markerValue = raw[GRID_VERSION_KEY];
// Nur eine Zahl ist ein Marker; alles andere (fehlend, Zeichenkette) zaehlt als alt.
const version = typeof markerValue === 'number' ? markerValue : 1;
const newer = version > GRID_VERSION;
const steps = MIGRATION_STEPS.filter((step) => version <= step.from);
const needsScaling = steps.length > 0;
@@ -108,7 +121,7 @@ export function migrateGridLayouts(raw: unknown): { layouts: GridLayouts; migrat
});
}
return { layouts, migrated };
return { layouts, migrated, newer };
}
/**