Initial commit

This commit is contained in:
2025-12-04 22:24:47 +01:00
commit 453ce10494
106 changed files with 17145 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
import { createContext, useContext, useState, useEffect, useCallback } from 'react';
import type { ReactNode } from 'react';
export type SidebarMode = 'collapsed' | 'expanded' | 'toggle' | 'dynamic';
interface SidebarContextType {
isCollapsed: boolean;
isMobileOpen: boolean;
sidebarMode: SidebarMode;
canToggle: boolean;
toggleCollapse: () => void;
toggleMobileMenu: () => void;
closeMobileMenu: () => void;
setSidebarMode: (mode: SidebarMode) => Promise<void>;
isHovered: boolean;
setIsHovered: (isHovered: boolean) => void;
showLogo: boolean;
setShowLogo: (show: boolean) => void;
}
const SidebarContext = createContext<SidebarContextType | undefined>(undefined);
export function SidebarProvider({ children }: { children: ReactNode }) {
const [userCollapsed, setUserCollapsed] = useState(true);
const [isMobileOpen, setIsMobileOpen] = useState(false);
const [sidebarMode, setSidebarModeState] = useState<SidebarMode>('toggle');
const [isHovered, setIsHovered] = useState(false);
const [showLogo, setShowLogo] = useState(false);
// Load sidebar mode from backend
useEffect(() => {
const loadSidebarMode = async () => {
try {
const token = localStorage.getItem('token');
if (!token) return;
const response = await fetch('/api/v1/settings', {
headers: { 'Authorization': `Bearer ${token} ` }
});
if (response.ok) {
const data = await response.json();
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);
}
}
} catch (error) {
console.error('Failed to load sidebar mode:', error);
}
};
loadSidebarMode();
}, []);
// Compute isCollapsed based on mode
const isCollapsed = sidebarMode === 'collapsed' ? true :
sidebarMode === 'dynamic' ? true :
sidebarMode === 'expanded' ? false :
userCollapsed;
// Can only toggle if mode is 'toggle'
const canToggle = sidebarMode === 'toggle';
const toggleCollapse = () => {
if (canToggle) {
setUserCollapsed((prev) => !prev);
}
};
const toggleMobileMenu = () => {
setIsMobileOpen((prev) => !prev);
};
const closeMobileMenu = () => {
setIsMobileOpen(false);
};
const setSidebarMode = useCallback(async (mode: SidebarMode) => {
try {
const token = localStorage.getItem('token');
const response = await fetch('/api/v1/settings/sidebar_mode', {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token} `,
'Content-Type': 'application/json',
},
body: JSON.stringify({ value: mode }),
});
if (response.ok) {
setSidebarModeState(mode);
}
} catch (error) {
console.error('Failed to save sidebar mode:', error);
}
}, []);
return (
<SidebarContext.Provider
value={{
isCollapsed,
isMobileOpen,
sidebarMode,
canToggle,
toggleCollapse,
toggleMobileMenu,
closeMobileMenu,
setSidebarMode,
isHovered,
setIsHovered: (value: boolean) => setIsHovered(value),
showLogo,
setShowLogo,
}}
>
{children}
</SidebarContext.Provider>
);
}
export function useSidebar() {
const context = useContext(SidebarContext);
if (context === undefined) {
throw new Error('useSidebar must be used within a SidebarProvider');
}
return context;
}