Files
tessera-ctl/apps/web/src/lib/stores/auth-store.ts
T
schalli b03441da59 fix(dashboard): sidebar active-item text unreadable in light mode with custom accent
applyAccentColor() forced --sidebar-accent-foreground to the raw accent
color regardless of theme. Works in dark mode (bright text on dark-tinted
bg) but in light mode the tinted bg is near-white, so full-saturation
yellow text on pale-yellow bg was nearly invisible. Now only overrides
the foreground in dark mode; light mode keeps the theme's default
high-contrast foreground token.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 08:58:32 +02:00

60 lines
1.9 KiB
TypeScript

import { create } from 'zustand';
export interface AuthUser {
id: string;
username: string;
displayName: string | null;
role: 'SUPER_ADMIN' | 'ADMIN' | 'USER';
tenantId: string;
hasAvatar?: boolean;
accentColor?: string | 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;
if (color) {
root.style.setProperty('--primary', color);
root.style.setProperty('--ring', color);
// Sidebar active state: accent color with 15% opacity (#rrggbbaa, 0x26 ≈ 15%)
root.style.setProperty('--sidebar-accent', `${color}26`);
// Full-saturation accent text only reads well against the dark-mode sidebar's
// tinted-dark background; in light mode it's near-white behind it, so keep
// the theme's default (dark, high-contrast) foreground there instead.
if (root.classList.contains('dark')) {
root.style.setProperty('--sidebar-accent-foreground', color);
} else {
root.style.removeProperty('--sidebar-accent-foreground');
}
} else {
root.style.removeProperty('--primary');
root.style.removeProperty('--ring');
root.style.removeProperty('--sidebar-accent');
root.style.removeProperty('--sidebar-accent-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 })),
}));