@dnd-kit (nur Reihenfolge tauschen) durch react-grid-layout ersetzt - Kacheln lassen sich jetzt frei auf einem 12-Spalten-Raster positionieren (nicht nur umsortieren) UND per Ecke ziehen dynamisch in der Größe ändern (nicht nur zwischen 4 festen Stufen wechseln). Layout-Modell in layout.ts auf x/y/w/h pro Kachel umgestellt (vorher Reihenfolge-Array + Größen-Enum). TileFrame vereinfacht (kein Drag-Griff/Größen-Button mehr nötig, das übernimmt die Grid-Lib direkt an der Kachel). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
export type DashboardTileId =
|
|
| "aufgaben"
|
|
| "einsatzbereitschaft"
|
|
| "prueftermine"
|
|
| "ablaufdaten"
|
|
| "kennzahlen"
|
|
| "maengel"
|
|
| "qualifikation"
|
|
| "aktivitaet";
|
|
|
|
export const ALLE_TILE_IDS: DashboardTileId[] = [
|
|
"aufgaben",
|
|
"einsatzbereitschaft",
|
|
"prueftermine",
|
|
"ablaufdaten",
|
|
"kennzahlen",
|
|
"maengel",
|
|
"qualifikation",
|
|
"aktivitaet",
|
|
];
|
|
|
|
export const GRID_SPALTEN = 12;
|
|
export const GRID_ZEILENHOEHE = 56;
|
|
|
|
export interface RasterPosition {
|
|
x: number;
|
|
y: number;
|
|
w: number;
|
|
h: number;
|
|
}
|
|
|
|
// Freie Grid-Positionen als Startzustand - grob nach Wichtigkeit sortiert,
|
|
// Aufgaben-Kachel volle Breite oben.
|
|
export const STANDARD_RASTER: Record<DashboardTileId, RasterPosition> = {
|
|
aufgaben: { x: 0, y: 0, w: 12, h: 3 },
|
|
einsatzbereitschaft: { x: 0, y: 3, w: 4, h: 5 },
|
|
prueftermine: { x: 4, y: 3, w: 4, h: 5 },
|
|
ablaufdaten: { x: 8, y: 3, w: 4, h: 5 },
|
|
kennzahlen: { x: 0, y: 8, w: 4, h: 5 },
|
|
maengel: { x: 4, y: 8, w: 4, h: 5 },
|
|
qualifikation: { x: 8, y: 8, w: 4, h: 4 },
|
|
aktivitaet: { x: 0, y: 13, w: 8, h: 5 },
|
|
};
|
|
|
|
export interface DashboardLayout {
|
|
raster: Record<DashboardTileId, RasterPosition>;
|
|
ausgeblendet: DashboardTileId[];
|
|
}
|
|
|
|
const SPEICHER_KEY = "mabea-dashboard-layout-v2";
|
|
|
|
function istGueltigeId(wert: unknown): wert is DashboardTileId {
|
|
return typeof wert === "string" && (ALLE_TILE_IDS as string[]).includes(wert);
|
|
}
|
|
|
|
function istGueltigePosition(wert: unknown): wert is RasterPosition {
|
|
if (!wert || typeof wert !== "object") return false;
|
|
const p = wert as Record<string, unknown>;
|
|
return typeof p.x === "number" && typeof p.y === "number" && typeof p.w === "number" && typeof p.h === "number";
|
|
}
|
|
|
|
export function layoutLaden(): DashboardLayout {
|
|
try {
|
|
const roh = localStorage.getItem(SPEICHER_KEY);
|
|
if (!roh) return { raster: { ...STANDARD_RASTER }, ausgeblendet: [] };
|
|
const geparst = JSON.parse(roh);
|
|
const raster: Record<DashboardTileId, RasterPosition> = { ...STANDARD_RASTER };
|
|
if (geparst.raster && typeof geparst.raster === "object") {
|
|
for (const [id, position] of Object.entries(geparst.raster)) {
|
|
if (istGueltigeId(id) && istGueltigePosition(position)) raster[id] = position;
|
|
}
|
|
}
|
|
const ausgeblendet = Array.isArray(geparst.ausgeblendet) ? geparst.ausgeblendet.filter(istGueltigeId) : [];
|
|
return { raster, ausgeblendet };
|
|
} catch {
|
|
return { raster: { ...STANDARD_RASTER }, ausgeblendet: [] };
|
|
}
|
|
}
|
|
|
|
export function layoutSpeichern(layout: DashboardLayout): void {
|
|
try {
|
|
localStorage.setItem(SPEICHER_KEY, JSON.stringify(layout));
|
|
} catch {
|
|
// Speichern optional - Layout fällt auf Standard zurück
|
|
}
|
|
}
|