import { createContext, useCallback, useContext, useMemo, useState, type ReactNode } from 'react'; import { getSikshyaApi, SIKSHYA_ENDPOINTS } from '../api'; import { getConfig, normalizeLicensing, normalizeNavigation, normalizeShellAlerts } from '../config/env'; import { useAdminRouting } from '../lib/adminRouting'; import type { NavItem, ShellAlert, SikshyaLicensing } from '../types'; type ShellMetaResponse = { shellAlerts?: unknown; licensing?: unknown; navigation?: unknown; proVersion?: unknown; proPluginVersion?: unknown; }; type ShellStateValue = { shellAlerts: ShellAlert[]; licensing: SikshyaLicensing | undefined; /** Sidebar tree (badges reflect add-on + tier state; refresh after license / add-on changes). */ navigation: NavItem[]; /** Pro add-on semver whenever the Pro plugin is loaded. */ proPluginVersion: string; refreshShell: () => Promise; }; const ShellStateContext = createContext(null); export function useShellState(): ShellStateValue { const ctx = useContext(ShellStateContext); if (!ctx) { // Defensive fallback: some WP admin pages can mount subsets of the React shell // (or mount order can be disrupted by 3rd-party scripts). Returning a safe // default prevents a full white-screen crash; callers still get a usable UI. const c = getConfig(); return { shellAlerts: normalizeShellAlerts(c.shellAlerts ?? []), licensing: normalizeLicensing(c.licensing), navigation: normalizeNavigation(c.navigation), proPluginVersion: typeof c.proPluginVersion === 'string' && c.proPluginVersion.trim() !== '' ? c.proPluginVersion.trim() : '', refreshShell: async () => { // no-op (provider not mounted) }, }; } return ctx; } export function ShellStateProvider({ children }: { children: ReactNode }) { const { route } = useAdminRouting(); const initial = useMemo(() => { const c = getConfig(); return { shellAlerts: c.shellAlerts ?? [], licensing: c.licensing, navigation: normalizeNavigation(c.navigation), proPluginVersion: typeof c.proPluginVersion === 'string' && c.proPluginVersion.trim() !== '' ? c.proPluginVersion.trim() : '', }; }, []); const [shellAlerts, setShellAlerts] = useState(initial.shellAlerts); const [licensing, setLicensing] = useState(initial.licensing); const [navigation, setNavigation] = useState(initial.navigation); const [proPluginVersion, setProPluginVersion] = useState(initial.proPluginVersion); const refreshShell = useCallback(async () => { const view = typeof route.page === 'string' && route.page.trim() !== '' ? route.page.trim() : 'dashboard'; try { const res = await getSikshyaApi().get(SIKSHYA_ENDPOINTS.admin.shellMeta(view)); const nextAlerts = normalizeShellAlerts(res.shellAlerts); const nextLic = normalizeLicensing(res.licensing); const nextPv = typeof res.proVersion === 'string' && res.proVersion.trim() !== '' ? res.proVersion.trim() : ''; const nextPluginPv = typeof res.proPluginVersion === 'string' && res.proPluginVersion.trim() !== '' ? res.proPluginVersion.trim() : ''; const nextNav = normalizeNavigation(res.navigation); setShellAlerts(nextAlerts); setLicensing(nextLic); if (nextNav.length) { setNavigation(nextNav); } setProPluginVersion(nextPluginPv); if (typeof window !== 'undefined' && window.sikshyaReact) { Object.assign(window.sikshyaReact, { shellAlerts: res.shellAlerts ?? [], licensing: res.licensing ?? window.sikshyaReact.licensing, proVersion: nextPv, proPluginVersion: nextPluginPv, ...(nextNav.length ? { navigation: nextNav } : {}), }); } } catch { /* Non-fatal: shell strip may fail if REST is blocked; UI keeps last known state. */ } }, [route.page]); const value = useMemo( () => ({ shellAlerts, licensing, navigation, proPluginVersion, refreshShell, }), [shellAlerts, licensing, navigation, proPluginVersion, refreshShell] ); return {children}; }