import { useCallback, useEffect, useState, type FormEvent } from 'react'; import { getSikshyaApi, SIKSHYA_ENDPOINTS } from '../api'; import { EmbeddableShell } from '../components/shared/EmbeddableShell'; import { GatedFeatureWorkspace } from '../components/GatedFeatureWorkspace'; import { ApiErrorPanel } from '../components/shared/ApiErrorPanel'; import { ListPanel } from '../components/shared/list/ListPanel'; import { ButtonPrimary } from '../components/shared/buttons'; import { TopRightToast, useTopRightToast } from '../components/shared/TopRightToast'; import { useAsyncData } from '../hooks/useAsyncData'; import { useAddonEnabled } from '../hooks/useAddons'; import { isFeatureEnabled, resolveGatedWorkspaceMode } from '../lib/licensing'; import { WPMediaPickerField } from '../components/shared/WPMediaPickerField'; import type { SikshyaReactConfig } from '../types'; import { __ } from '../lib/i18n'; type Options = { brand_name?: string; brand_short_name?: string; plugin_name?: string; logo_url?: string; admin_menu_icon_url?: string; topbar_bg?: string; topbar_text?: string; sidebar_bg?: string; sidebar_text?: string; frontend_accent?: string; hide_sikshya_footer?: boolean; admin_footer_html?: string; login_accent_color?: string; login_logo_url?: string; admin_enabled?: boolean; login_enabled?: boolean; frontend_enabled?: boolean; email_enabled?: boolean; documentation_url?: string; support_url?: string; upgrade_url?: string; terminology?: Partial< Record< | 'course' | 'courses' | 'lesson' | 'lessons' | 'quiz' | 'quizzes' | 'assignment' | 'assignments' | 'chapter' | 'chapters' | 'student' | 'students' | 'instructor' | 'instructors' | 'enrollment' | 'enrollments', string > >; }; type Resp = { ok?: boolean; options?: Options }; export function WhiteLabelPage(props: { config: SikshyaReactConfig; title: string; embedded?: boolean }) { const { config, title, embedded } = props; const featureOk = isFeatureEnabled(config, 'white_label'); const addon = useAddonEnabled('white_label'); const mode = resolveGatedWorkspaceMode(featureOk, addon.enabled, addon.loading); const enabled = mode === 'full'; const platformName = config.branding?.pluginName?.trim() || 'Sikshya'; const [opts, setOpts] = useState({}); const [courseId, setCourseId] = useState(0); const [courseOverrides, setCourseOverrides] = useState>({}); const [courseLoading, setCourseLoading] = useState(false); const [saving, setSaving] = useState(false); const toast = useTopRightToast(); const loader = useCallback(async () => { if (!enabled) return { ok: true, options: {} as Options }; return getSikshyaApi().get(SIKSHYA_ENDPOINTS.pro.whiteLabel); }, [enabled]); const { loading, data, error, refetch } = useAsyncData(loader, [enabled]); useEffect(() => { if (data?.options) setOpts({ ...data.options }); }, [data]); const onSave = async (e: FormEvent) => { e.preventDefault(); setSaving(true); try { await getSikshyaApi().post(SIKSHYA_ENDPOINTS.pro.whiteLabel, opts); toast.success(__('Saved', 'sikshya'), embedded ? __('Changes saved.', 'sikshya') : __('Saved. Applying…', 'sikshya')); refetch(); // The shell branding is injected server-side into the bootstrap config. // Reload so all pages pick up updated colors/menu branding immediately. if (!embedded) { window.setTimeout(() => window.location.reload(), 350); } } catch (err) { toast.error(__('Save failed', 'sikshya'), err instanceof Error ? err.message : 'Save failed'); } finally { setSaving(false); } }; const loadCourseOverrides = useCallback( async (id: number) => { if (!enabled || !id) return; setCourseLoading(true); try { const r = await getSikshyaApi().get<{ ok?: boolean; overrides?: Record }>( SIKSHYA_ENDPOINTS.pro.whiteLabelCourse(id), ); setCourseOverrides(r.overrides || {}); } catch (err) { toast.error(__('Could not load', 'sikshya'), err instanceof Error ? err.message : 'Could not load course overrides'); } finally { setCourseLoading(false); } }, [enabled], ); const saveCourseOverrides = async () => { if (!enabled || !courseId) return; setSaving(true); try { await getSikshyaApi().post(SIKSHYA_ENDPOINTS.pro.whiteLabelCourse(courseId), courseOverrides); toast.success(__('Saved', 'sikshya'), 'Course overrides saved.'); } catch (err) { toast.error(__('Save failed', 'sikshya'), err instanceof Error ? err.message : 'Course override save failed'); } finally { setSaving(false); } }; return ( addon.enable()} addonError={addon.error} > {error ? refetch()} /> : null} {loading ? (

{__('Loading…', 'sikshya')}

) : (
Where should branding apply?
{__('Logo (React sidebar)', 'sikshya')}
Shown in the Sikshya admin sidebar header.
{__('Admin menu icon (WordPress sidebar)', 'sikshya')}
Replaces the icon next to the top-level menu item in wp-admin. Use a square image (20×20 works best).
setOpts((p) => ({ ...p, admin_menu_icon_url: url }))} />
{__('Login logo (optional)', 'sikshya')}
Replaces the WordPress login logo when login branding is enabled.