/** * Admin Layout Context * Provides global state management for the admin dashboard layout and navigation */ "use client"; import React, { createContext, useContext, useCallback, useReducer, useMemo, useEffect, } from "react"; import { AdminLayoutContextValue, AdminState, AdminActions, AdminEvent, AdminEventHandler, DEFAULT_LAYOUT_CONFIG, NavigationGroup, BreadcrumbItem, NotificationItem, LayoutConfig, UserMenuConfig, DashboardConfig, EditorConfig, SearchConfig, ThemeConfig, } from "./types"; // Action Types type AdminActionType = | { type: "SET_LAYOUT"; payload: LayoutConfig } | { type: "UPDATE_LAYOUT"; payload: Partial } | { type: "SET_NAVIGATION"; payload: NavigationGroup[] } | { type: "SET_ACTIVE_NAVIGATION"; payload: string } | { type: "SET_BREADCRUMBS"; payload: BreadcrumbItem[] } | { type: "ADD_BREADCRUMB"; payload: BreadcrumbItem } | { type: "SET_USER_MENU"; payload: UserMenuConfig } | { type: "ADD_NOTIFICATION"; payload: NotificationItem } | { type: "MARK_NOTIFICATION_READ"; payload: string } | { type: "CLEAR_NOTIFICATIONS" } | { type: "SET_DASHBOARD"; payload: DashboardConfig } | { type: "SET_EDITOR"; payload: EditorConfig } | { type: "SET_SEARCH"; payload: SearchConfig } | { type: "SET_THEME"; payload: ThemeConfig } | { type: "TOGGLE_SIDEBAR" } | { type: "SET_LOADING"; payload: boolean } | { type: "SET_ERROR"; payload: string | null }; // Default State const defaultState: AdminState = { layout: DEFAULT_LAYOUT_CONFIG, navigation: [], breadcrumbs: [], userMenu: { items: [], showProfile: true, showSettings: true, showLogout: true, }, notifications: { items: [], unreadCount: 0, showBadge: true, autoMarkAsRead: false, maxItems: 50, }, dashboard: { widgets: [], layout: "grid", autoRefresh: true, refreshInterval: 300000, // 5 minutes }, editor: { tabs: [], activeTab: "", showPreview: true, previewMode: "split", autoSave: true, autoSaveInterval: 30000, // 30 seconds }, search: { placeholder: "검색...", showRecentSearches: true, showSuggestions: true, minQueryLength: 2, maxResults: 10, debounceDelay: 300, }, theme: { name: "default", colors: { primary: "#3b82f6", secondary: "#64748b", background: "#ffffff", surface: "#f8fafc", text: "#1e293b", textSecondary: "#64748b", border: "#e2e8f0", success: "#10b981", warning: "#f59e0b", error: "#ef4444", info: "#06b6d4", }, borderRadius: 8, fontFamily: "Inter, system-ui, sans-serif", shadows: true, animations: true, }, isLoading: false, error: null, }; // Reducer function adminReducer(state: AdminState, action: AdminActionType): AdminState { switch (action.type) { case "SET_LAYOUT": return { ...state, layout: action.payload }; case "UPDATE_LAYOUT": return { ...state, layout: { ...state.layout, ...action.payload }, }; case "SET_NAVIGATION": return { ...state, navigation: action.payload }; case "SET_ACTIVE_NAVIGATION": return { ...state, navigation: state.navigation.map((group) => ({ ...group, items: group.items.map((item) => ({ ...item, isActive: item.id === action.payload, children: item.children?.map((child) => ({ ...child, isActive: child.id === action.payload, })), })), })), }; case "SET_BREADCRUMBS": return { ...state, breadcrumbs: action.payload }; case "ADD_BREADCRUMB": return { ...state, breadcrumbs: [ ...state.breadcrumbs.map((item) => ({ ...item, isActive: false })), action.payload, ], }; case "SET_USER_MENU": return { ...state, userMenu: action.payload }; case "ADD_NOTIFICATION": const newNotifications = [action.payload, ...state.notifications.items]; const trimmedNotifications = newNotifications.slice( 0, state.notifications.maxItems ); return { ...state, notifications: { ...state.notifications, items: trimmedNotifications, unreadCount: state.notifications.unreadCount + 1, }, }; case "MARK_NOTIFICATION_READ": return { ...state, notifications: { ...state.notifications, items: state.notifications.items.map((item) => item.id === action.payload ? { ...item, isRead: true } : item ), unreadCount: Math.max(0, state.notifications.unreadCount - 1), }, }; case "CLEAR_NOTIFICATIONS": return { ...state, notifications: { ...state.notifications, items: [], unreadCount: 0, }, }; case "SET_DASHBOARD": return { ...state, dashboard: action.payload }; case "SET_EDITOR": return { ...state, editor: action.payload }; case "SET_SEARCH": return { ...state, search: action.payload }; case "SET_THEME": return { ...state, theme: action.payload }; case "TOGGLE_SIDEBAR": return { ...state, layout: { ...state.layout, sidebar: { ...state.layout.sidebar, isCollapsed: !state.layout.sidebar.isCollapsed, }, }, }; case "SET_LOADING": return { ...state, isLoading: action.payload }; case "SET_ERROR": return { ...state, error: action.payload }; default: return state; } } // Context const AdminLayoutContext = createContext(null); // Provider Props interface AdminLayoutProviderProps { children: React.ReactNode; initialConfig?: Partial; } // Provider Component export function AdminLayoutProvider({ children, initialConfig, }: AdminLayoutProviderProps) { const [state, dispatch] = useReducer(adminReducer, { ...defaultState, layout: { ...defaultState.layout, ...initialConfig }, }); // Event handling const eventHandlers = useMemo(() => new Set(), []); const emitEvent = useCallback( (event: Omit) => { const fullEvent: AdminEvent = { ...event, timestamp: new Date(), }; eventHandlers.forEach((handler) => { try { handler(fullEvent); } catch (error) { console.error("Error in admin event handler:", error); } }); }, [eventHandlers] ); const addEventListener = useCallback( (handler: AdminEventHandler) => { eventHandlers.add(handler); return () => eventHandlers.delete(handler); }, [eventHandlers] ); // Actions const actions: AdminActions = useMemo( () => ({ setLayout: (config: LayoutConfig) => { dispatch({ type: "SET_LAYOUT", payload: config }); emitEvent({ type: "layout", payload: { config } }); }, updateLayout: (updates: Partial) => { dispatch({ type: "UPDATE_LAYOUT", payload: updates }); emitEvent({ type: "layout", payload: { updates } }); }, setNavigation: (navigation: NavigationGroup[]) => { dispatch({ type: "SET_NAVIGATION", payload: navigation }); emitEvent({ type: "navigation", payload: { navigation } }); }, setActiveNavigation: (itemId: string) => { dispatch({ type: "SET_ACTIVE_NAVIGATION", payload: itemId }); emitEvent({ type: "navigation", payload: { activeItem: itemId } }); }, setBreadcrumbs: (breadcrumbs: BreadcrumbItem[]) => { dispatch({ type: "SET_BREADCRUMBS", payload: breadcrumbs }); }, addBreadcrumb: (item: BreadcrumbItem) => { dispatch({ type: "ADD_BREADCRUMB", payload: item }); }, setUserMenu: (menu: UserMenuConfig) => { dispatch({ type: "SET_USER_MENU", payload: menu }); }, addNotification: ( notification: Omit ) => { const fullNotification: NotificationItem = { ...notification, id: `notification-${Date.now()}-${Math.random() .toString(36) .substr(2, 9)}`, timestamp: new Date(), }; dispatch({ type: "ADD_NOTIFICATION", payload: fullNotification }); emitEvent({ type: "notification", payload: { notification: fullNotification }, }); }, markNotificationAsRead: (id: string) => { dispatch({ type: "MARK_NOTIFICATION_READ", payload: id }); emitEvent({ type: "notification", payload: { markAsRead: id } }); }, clearNotifications: () => { dispatch({ type: "CLEAR_NOTIFICATIONS" }); emitEvent({ type: "notification", payload: { action: "clear" } }); }, setDashboard: (config: DashboardConfig) => { dispatch({ type: "SET_DASHBOARD", payload: config }); }, setEditor: (config: EditorConfig) => { dispatch({ type: "SET_EDITOR", payload: config }); }, setSearch: (config: SearchConfig) => { dispatch({ type: "SET_SEARCH", payload: config }); emitEvent({ type: "search", payload: { config } }); }, setTheme: (theme: ThemeConfig) => { dispatch({ type: "SET_THEME", payload: theme }); emitEvent({ type: "theme", payload: { theme } }); }, toggleSidebar: () => { dispatch({ type: "TOGGLE_SIDEBAR" }); emitEvent({ type: "layout", payload: { action: "toggle_sidebar" } }); }, setLoading: (loading: boolean) => { dispatch({ type: "SET_LOADING", payload: loading }); }, setError: (error: string | null) => { dispatch({ type: "SET_ERROR", payload: error }); }, }), [emitEvent] ); // Context value with additional helper methods const contextValue: AdminLayoutContextValue = useMemo( () => ({ config: state.layout, navigation: state.navigation, breadcrumbs: state.breadcrumbs, userMenu: state.userMenu, notifications: state.notifications, dashboard: state.dashboard, editor: state.editor, search: state.search, theme: state.theme, updateConfig: actions.updateLayout, setNavigation: actions.setNavigation, setBreadcrumbs: actions.setBreadcrumbs, toggleSidebar: actions.toggleSidebar, setActiveNavigation: actions.setActiveNavigation, setTheme: (theme: "light" | "dark" | "auto") => { const updatedTheme = { ...state.theme, name: theme }; actions.setTheme(updatedTheme); }, addNotification: actions.addNotification, markNotificationAsRead: actions.markNotificationAsRead, clearNotifications: actions.clearNotifications, emitEvent, addEventListener, }), [state, actions, emitEvent, addEventListener] ); // Auto-save layout preferences to localStorage useEffect(() => { const layoutKey = "admin-layout-config"; try { localStorage.setItem(layoutKey, JSON.stringify(state.layout)); } catch (error) { console.warn("Failed to save layout config to localStorage:", error); } }, [state.layout]); return ( {children} ); } // Hook export function useAdminLayout(): AdminLayoutContextValue { const context = useContext(AdminLayoutContext); if (!context) { throw new Error("useAdminLayout must be used within AdminLayoutProvider"); } return context; } // Default export export default AdminLayoutProvider;