0aaa15240d
- Spalte User.dashboardBackground (JSONB) samt Migration - PATCH /users/me/dashboard-background, geprueft mit parseDashboardBackground aus @tessera/shared (Allowlist, UUID-Bildkennung) - getMe liefert dashboardBackground normalisiert neben accentColor - Web liest die Wahl aus dem Auth-Store, speichert ueber die Server-Aktion, alte localStorage-Wahl wird einmalig uebernommen - Hinweistext: gilt auf jedem Geraet Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
61 lines
2.1 KiB
TypeScript
61 lines
2.1 KiB
TypeScript
import type { DashboardBackground } from '@tessera/shared';
|
|
import { create } from 'zustand';
|
|
import { readableOnAccent } from '@/lib/color';
|
|
|
|
export interface AuthUser {
|
|
id: string;
|
|
username: string;
|
|
displayName: string | null;
|
|
role: 'SUPER_ADMIN' | 'ADMIN' | 'USER';
|
|
tenantId: string;
|
|
hasAvatar?: boolean;
|
|
accentColor?: string | null;
|
|
/** quick-260928-ujj: Dashboard-Hintergrund aus der Sitzungsantwort; null = nie gewaehlt. */
|
|
dashboardBackground?: DashboardBackground | null;
|
|
}
|
|
|
|
interface AuthState {
|
|
user: AuthUser | null;
|
|
avatarVersion: number;
|
|
setUser: (user: AuthUser) => void;
|
|
clearUser: () => void;
|
|
bumpAvatarVersion: () => void;
|
|
}
|
|
|
|
function applyAccentColor(color: string | null | undefined) {
|
|
if (typeof document === 'undefined') return;
|
|
const root = document.documentElement;
|
|
// Design „Mosaik“: die persoenliche Akzentfarbe setzt nur noch `--primary`
|
|
// (Hauptknoepfe, Auswahlpille der Seitenleiste, Reiter-Unterstrich, Logo).
|
|
// Fokusring und Auswahlflaeche der Seitenleiste bleiben neutral — eine
|
|
// helle Akzentfarbe (Standard Gelb) waere als Fokusring auf Weiss kaum
|
|
// sichtbar (1,3:1). Alte Werte frueherer Fassungen werden entfernt.
|
|
root.style.removeProperty('--ring');
|
|
root.style.removeProperty('--sidebar-accent');
|
|
root.style.removeProperty('--sidebar-accent-foreground');
|
|
if (color) {
|
|
root.style.setProperty('--primary', color);
|
|
// Schrift auf der Akzentflaeche: hell oder dunkel, je nach Kontrast.
|
|
root.style.setProperty('--primary-foreground', readableOnAccent(color));
|
|
} else {
|
|
root.style.removeProperty('--primary');
|
|
root.style.removeProperty('--primary-foreground');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zustand store for client-side auth state.
|
|
* No persist middleware -- user state comes from API, not localStorage.
|
|
* Populated on portal load via fetchCurrentUser().
|
|
*/
|
|
export const useAuthStore = create<AuthState>()((set) => ({
|
|
user: null,
|
|
avatarVersion: 0,
|
|
setUser: (user) => {
|
|
applyAccentColor(user.accentColor);
|
|
set({ user });
|
|
},
|
|
clearUser: () => set({ user: null }),
|
|
bumpAvatarVersion: () => set((s) => ({ avatarVersion: s.avatarVersion + 1 })),
|
|
}));
|