Files
dss/admin-ui/src/state/user.ts
DSS d13e1cd76a
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
admin-ui: align with API, add auth, fix integrations
2025-12-12 15:46:08 -03:00

110 lines
3.2 KiB
TypeScript

import { signal, computed } from '@preact/signals';
import { endpoints } from '../api/client';
import type { AuthLoginRequest, AuthLoginResponse, UserProfile } from '../api/types';
// Types
export interface UserPreferences {
theme: 'light' | 'dark' | 'auto';
sidebarCollapsed: boolean;
panelHeight: number;
defaultTeam: string;
keyboardShortcutsEnabled: boolean;
notificationsEnabled: boolean;
}
// Default preferences
const DEFAULT_PREFERENCES: UserPreferences = {
theme: 'auto',
sidebarCollapsed: false,
panelHeight: 280,
defaultTeam: 'ui',
keyboardShortcutsEnabled: true,
notificationsEnabled: true
};
// User State Signals
export const user = signal<UserProfile | null>(null);
export const preferences = signal<UserPreferences>(DEFAULT_PREFERENCES);
// Computed Values
export const isAuthenticated = computed(() => user.value !== null);
export const userId = computed(() => user.value?.id ?? 1);
export const userName = computed(() => user.value?.display_name ?? user.value?.email ?? 'Guest');
export const userInitials = computed(() => {
const name = user.value?.display_name ?? user.value?.email ?? 'Guest';
return name.split(' ').filter(Boolean).map(n => n[0]).join('').toUpperCase().slice(0, 2);
});
// Actions
export function loadUserPreferences(): void {
if (typeof window === 'undefined') return;
const saved = localStorage.getItem('dss-preferences');
if (saved) {
try {
const parsed = JSON.parse(saved);
preferences.value = { ...DEFAULT_PREFERENCES, ...parsed };
} catch {
preferences.value = DEFAULT_PREFERENCES;
}
}
}
export function saveUserPreferences(): void {
if (typeof window === 'undefined') return;
localStorage.setItem('dss-preferences', JSON.stringify(preferences.value));
}
export function updatePreferences(updates: Partial<UserPreferences>): void {
preferences.value = { ...preferences.value, ...updates };
saveUserPreferences();
}
export function setUser(userData: UserProfile | null): void {
user.value = userData;
}
export function logout(): void {
user.value = null;
localStorage.removeItem('dss-token');
}
export async function refreshUser(): Promise<void> {
if (typeof window === 'undefined') return;
const token = localStorage.getItem('dss-token');
if (!token) {
user.value = null;
return;
}
try {
const profile = await endpoints.auth.me();
user.value = profile;
} catch {
// Token is invalid/expired; clear it.
localStorage.removeItem('dss-token');
user.value = null;
}
}
export async function loginWithAtlassian(credentials: AuthLoginRequest): Promise<AuthLoginResponse> {
const response = await endpoints.auth.login(credentials);
localStorage.setItem('dss-token', response.token);
// Prefer server truth; also supports older login response shapes.
user.value = {
id: response.user.id,
email: response.user.email,
display_name: response.user.display_name,
atlassian_url: response.user.atlassian_url,
atlassian_service: response.user.service,
last_login: new Date().toISOString(),
};
return response;
}
// Keyboard shortcuts state
export const keyboardShortcutsEnabled = computed(() =>
preferences.value.keyboardShortcutsEnabled
);