/** * BoostMedia AI Content Generator Admin - Onboarding Hook & Context * * Reads initial state from window.bmaiSettings (no API call on load). * Writes changes optimistically and persists via REST. * * @package BoostMedia_AI * @license GPL-2.0-or-later */ import { useState, useCallback, useMemo, createContext, useContext, type ReactNode, } from 'react' import { endpoints } from '../api/client' import type { OnboardingState } from '../types' function getInitialState(): OnboardingState { if (window.bmaiSettings?.onboardingState) { return window.bmaiSettings.onboardingState } return { usageSeen: false, contentTypesSeen: false, linksSeen: false, reportersSeen: false, settingsSeen: false, autoPublishSeen: false, smartLinkingSeen: false, helpSeen: false, setupComplete: false, firstActivation: '', lastStepCompleted: '', } } interface OnboardingContextValue { state: OnboardingState markSeen: (key: keyof OnboardingState) => Promise needsSetup: Record setupStepsRemaining: number isSetupComplete: boolean } const SETUP_KEYS = [ 'usageSeen', 'contentTypesSeen', 'linksSeen', 'reportersSeen', ] as const function useOnboardingInternal(): OnboardingContextValue { const [state, setState] = useState(getInitialState) const markSeen = useCallback(async (key: keyof OnboardingState) => { if (state[key] === true) return setState(prev => { const next = { ...prev, [key]: true } next.setupComplete = SETUP_KEYS.every(k => next[k]) return next }) try { const result = await endpoints.updateOnboardingState({ [key]: true }) if (result?.data) { setState(result.data) } } catch { // Silent — onboarding state is non-critical } }, [state]) const needsSetup = useMemo(() => ({ usage: !state.usageSeen, contentTypes: !state.contentTypesSeen, links: !state.linksSeen, reporters: !state.reportersSeen, settings: !state.settingsSeen, help: !state.helpSeen, }), [state]) const setupStepsRemaining = useMemo(() => { return SETUP_KEYS.filter(k => !state[k]).length }, [state]) return { state, markSeen, needsSetup, setupStepsRemaining, isSetupComplete: state.setupComplete, } } const OnboardingContext = createContext(null) export function OnboardingProvider({ children }: { children: ReactNode }) { const onboarding = useOnboardingInternal() return ( {children} ) } const fallbackValue: OnboardingContextValue = { state: getInitialState(), markSeen: async () => {}, needsSetup: { usage: false, contentTypes: false, links: false, reporters: false, settings: false, help: false, }, setupStepsRemaining: 0, isSetupComplete: true, } export function useOnboarding(): OnboardingContextValue { const ctx = useContext(OnboardingContext) if (!ctx) return fallbackValue return ctx }