import { useMemo } from 'react'; import { HorizontalEditorTabs } from './HorizontalEditorTabs'; import { useAdminRouting } from '../../lib/adminRouting'; import { EmbeddableShell } from './EmbeddableShell'; import type { SikshyaReactConfig } from '../../types'; export type HubTab = { /** URL slug for the tab (`?tab=…`). */ id: string; label: string; icon?: string; /** * Renders the tab body. Receives the same `config` so child pages keep * working with their existing prop contract; child pages MUST honour * `embedded` so they skip rendering their own `AppShell`. */ render: (config: SikshyaReactConfig) => React.ReactNode; /** Hide the tab entirely (e.g. unlicensed Pro feature). */ hidden?: boolean; }; type Props = { embedded?: boolean; config: SikshyaReactConfig; /** Hub title shown in the AppShell header. */ title: string; subtitle?: string; badge?: string; /** Sidebar nav id this hub maps to (for active highlight). */ sidebarActivePage?: string; tabs: HubTab[]; /** Fallback tab when the URL has no `?tab=` (defaults to first visible tab). */ defaultTabId?: string; }; /** * Generic shell for a tabbed admin hub. Owns the `AppShell`, the tab strip, * and reads/writes `?tab=` so each tab has a real URL (deep links + browser * back/forward both work). Child pages render in `embedded` mode. */ export function TabbedHubPage(props: Props) { const { config, title, subtitle, badge, sidebarActivePage, tabs, defaultTabId } = props; const { route, navigateView } = useAdminRouting(); const visibleTabs = useMemo(() => tabs.filter((t) => !t.hidden), [tabs]); const fallbackId = defaultTabId && visibleTabs.find((t) => t.id === defaultTabId) ? defaultTabId : visibleTabs[0]?.id || ''; const activeId = (() => { const fromUrl = (route.query?.tab || '').trim(); if (fromUrl && visibleTabs.some((t) => t.id === fromUrl)) { return fromUrl; } return fallbackId; })(); const onTabChange = (id: string) => { const view = typeof route.page === 'string' && route.page.trim() !== '' ? route.page.trim() : 'dashboard'; navigateView(view, { tab: id }); }; const activeTab = visibleTabs.find((t) => t.id === activeId); return ( {visibleTabs.length > 1 ? (
({ id: t.id, label: t.label, icon: t.icon }))} value={activeId} onChange={onTabChange} />
) : null} {activeTab ? activeTab.render(config) : null}
); }