Add UI settings API and improve context initialization

- Add /settings/ui GET and PUT endpoints for UI settings
- Improve ThemeContext with better initialization and auto accent handling
- Update SidebarContext with expanded state persistence
- Fix context initialization in ModulesContext and ViewModeContext
- Update components to use improved theme/sidebar contexts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-14 19:50:30 +01:00
parent 0608217702
commit 41c41adb98
10 changed files with 163 additions and 52 deletions

View File

@@ -1,5 +1,6 @@
import { createContext, useContext, useState, useEffect, useCallback } from 'react';
import type { ReactNode } from 'react';
import { useAuth } from './AuthContext';
export type SidebarMode = 'collapsed' | 'expanded' | 'toggle' | 'dynamic';
@@ -21,6 +22,7 @@ interface SidebarContextType {
const SidebarContext = createContext<SidebarContextType | undefined>(undefined);
export function SidebarProvider({ children }: { children: ReactNode }) {
const { token } = useAuth();
const [userCollapsed, setUserCollapsed] = useState(true);
const [isMobileOpen, setIsMobileOpen] = useState(false);
const [sidebarMode, setSidebarModeState] = useState<SidebarMode>('toggle');
@@ -31,19 +33,29 @@ export function SidebarProvider({ children }: { children: ReactNode }) {
useEffect(() => {
const loadSidebarMode = async () => {
try {
const token = localStorage.getItem('token');
if (!token) return;
if (!token) {
setSidebarModeState('toggle');
setShowLogo(false);
return;
}
const response = await fetch('/api/v1/settings', {
headers: { 'Authorization': `Bearer ${token} ` }
const response = await fetch('/api/v1/settings/ui', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (response.ok) {
const data = await response.json();
const parseBool = (val: any, defaultVal: boolean): boolean => {
if (val === undefined || val === null) return defaultVal;
if (val === true || val === 'true' || val === 'True' || val === 1 || val === '1') return true;
if (val === false || val === 'false' || val === 'False' || val === 0 || val === '0') return false;
return defaultVal;
};
if (data.sidebar_mode && ['collapsed', 'expanded', 'toggle', 'dynamic'].includes(data.sidebar_mode)) {
setSidebarModeState(data.sidebar_mode as SidebarMode);
}
if (data.show_logo !== undefined) {
setShowLogo(data.show_logo === true);
setShowLogo(parseBool(data.show_logo, false));
}
}
} catch (error) {
@@ -51,7 +63,7 @@ export function SidebarProvider({ children }: { children: ReactNode }) {
}
};
loadSidebarMode();
}, []);
}, [token]);
// Compute isCollapsed based on mode
const isCollapsed = sidebarMode === 'collapsed' ? true :
@@ -78,11 +90,11 @@ export function SidebarProvider({ children }: { children: ReactNode }) {
const setSidebarMode = useCallback(async (mode: SidebarMode) => {
try {
const token = localStorage.getItem('token');
if (!token) return;
const response = await fetch('/api/v1/settings/sidebar_mode', {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token} `,
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ value: mode }),
@@ -94,7 +106,7 @@ export function SidebarProvider({ children }: { children: ReactNode }) {
} catch (error) {
console.error('Failed to save sidebar mode:', error);
}
}, []);
}, [token]);
return (
<SidebarContext.Provider