feat(web): Dashboard-Kacheln im Fluent-Stil und waehlbarer Hintergrund

Einheitliche Kachel-Kopfzeile, ruhigere Notiz-/Kalender-/Favoriten-
Darstellung, 12 px Raster-Abstand, gestrichelte Kontur und Griff im
Bearbeitungsmodus. Neuer Knopf Hintergrund: Keiner, fuenf eingebaute
(Nebel, Salbei, Bluete, Duenen, Mosaik) oder eigenes Bild aus den
Bilderrahmen-Bildern; Prototyp speichert im localStorage je Benutzer.
Mit Hintergrund werden Kacheln Mica-artig durchscheinend (Kontrast
>= 4,5:1 auch im schlechtesten Fall).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-25 14:49:01 +02:00
parent 522cdfd368
commit 219038b3ee
17 changed files with 521 additions and 43 deletions
+10 -2
View File
@@ -5,6 +5,8 @@ import { useTranslations } from 'next-intl';
import { DashboardGrid } from '@/components/dashboard/dashboard-grid';
import { DashboardTabs } from '@/components/dashboard/dashboard-tabs';
import { EditModeToggle } from '@/components/dashboard/edit-mode-toggle';
import { BackgroundPicker, DashboardBackdrop, useDashboardBackground } from '@/components/dashboard/dashboard-background';
import { useAuthStore } from '@/lib/stores/auth-store';
import { WidgetCatalogModal } from '@/components/dashboard/widget-catalog-modal';
import { registerWidget } from '@/components/dashboard/widget-registry';
import { ClockWidget } from '@/components/dashboard/widgets/clock-widget';
@@ -45,6 +47,10 @@ interface ActiveModule {
export default function DashboardPage() {
const t = useTranslations('widgets');
const [catalogOpen, setCatalogOpen] = useState(false);
// Hintergrund je Benutzer (Design „Mosaik“, Prototyp mit localStorage).
const userId = useAuthStore((s) => s.user?.id);
const { background, choose: chooseBackground } = useDashboardBackground(userId);
const hasBackground = background.kind !== 'none';
// quick-260922-m1h: Slugs der Module, die dieser Benutzer nutzen darf —
// der Katalog blendet Kacheln gesperrter Module damit aus. `null` heisst
// "noch unbekannt oder Abruf fehlgeschlagen" und ist fail-closed.
@@ -115,7 +121,8 @@ export default function DashboardPage() {
}
return (
<div className="relative p-2">
<div className={`relative p-2 ${hasBackground ? 'dashboard-has-bg' : ''}`}>
<DashboardBackdrop background={background} />
{/* Reiterleiste (quick-260923-ad9) — seit quick-260924-h7x per Portal
in der Kopfzeile, hier nimmt sie keinen Platz mehr ein. Sie bleibt
waehrend eines Reiterwechsels stehen, nur das Raster zeigt eine
@@ -151,11 +158,12 @@ export default function DashboardPage() {
Grid direkt unter der Kopfzeile beginnt (12 + 8 + 8 = 28 px statt 60 px).
Im Bearbeitungsmodus steht "Widget hinzufuegen" links neben dem Haekchen. */}
<div className="fixed bottom-6 right-6 z-20 flex items-center gap-2">
{isEditMode && <BackgroundPicker value={background} onChange={chooseBackground} />}
{isEditMode && (
<button
type="button"
onClick={() => setCatalogOpen(true)}
className="flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-lg hover:opacity-90 transition-opacity"
className="btn btn-primary shadow-lg"
>
<svg
aria-hidden="true"