import { lazy, Suspense, useEffect } from 'react';
import { getConfig } from './config/env';
import { AppShell } from './components/AppShell';
import { SikshyaDialogProvider } from './components/shared/SikshyaDialogContext';
import { ShellStateProvider, useShellState } from './context/ShellStateContext';
import { AdminRoutingProvider, parseAdminRoute, useAdminRouting } from './lib/adminRouting';
import { applyAdminBrandThemeToRoot, clearAdminBrandThemeFromRoot } from './lib/adminBrandTokens';
import { term } from './lib/terminology';
import type { NavItem } from './types';
const ActivityLogPage = lazy(() =>
import('./pages/ActivityLogPage').then((m) => ({ default: m.ActivityLogPage }))
);
const AddonsPage = lazy(() => import('./pages/AddonsPage').then((m) => ({ default: m.AddonsPage })));
const BundlesPage = lazy(() => import('./pages/BundlesPage').then((m) => ({ default: m.BundlesPage })));
const CalendarPage = lazy(() => import('./pages/CalendarPage').then((m) => ({ default: m.CalendarPage })));
const ContentDripPage = lazy(() =>
import('./pages/ContentDripPage').then((m) => ({ default: m.ContentDripPage }))
);
const ContentPostEditorPage = lazy(() =>
import('./pages/ContentPostEditorPage').then((m) => ({ default: m.ContentPostEditorPage }))
);
const CouponsPage = lazy(() => import('./pages/CouponsPage').then((m) => ({ default: m.CouponsPage })));
const DiscussionsPage = lazy(() =>
import('./pages/DiscussionsPage').then((m) => ({ default: m.DiscussionsPage }))
);
const CourseBuilderPage = lazy(() =>
import('./pages/CourseBuilderPage').then((m) => ({ default: m.CourseBuilderPage }))
);
const CourseCategoriesPage = lazy(() =>
import('./pages/CourseCategoriesPage').then((m) => ({ default: m.CourseCategoriesPage }))
);
const CourseTeamPage = lazy(() =>
import('./pages/CourseTeamPage').then((m) => ({ default: m.CourseTeamPage }))
);
const CoursesPage = lazy(() => import('./pages/CoursesPage').then((m) => ({ default: m.CoursesPage })));
const DashboardPage = lazy(() =>
import('./pages/DashboardPage').then((m) => ({ default: m.DashboardPage }))
);
const EmailMarketingPage = lazy(() =>
import('./pages/EmailMarketingPage').then((m) => ({ default: m.EmailMarketingPage }))
);
const EmailPage = lazy(() => import('./pages/EmailPage').then((m) => ({ default: m.EmailPage })));
const EmailTemplateEditPage = lazy(() =>
import('./pages/EmailTemplateEditPage').then((m) => ({ default: m.EmailTemplateEditPage }))
);
const EnrollmentsPage = lazy(() =>
import('./pages/EnrollmentsPage').then((m) => ({ default: m.EnrollmentsPage }))
);
const GenericPlaceholderPage = lazy(() =>
import('./pages/GenericPlaceholderPage').then((m) => ({ default: m.GenericPlaceholderPage }))
);
const GradebookPage = lazy(() =>
import('./pages/GradebookPage').then((m) => ({ default: m.GradebookPage }))
);
const GradingPage = lazy(() => import('./pages/GradingPage').then((m) => ({ default: m.GradingPage })));
const InstructorApplicationsPage = lazy(() =>
import('./pages/InstructorApplicationsPage').then((m) => ({ default: m.InstructorApplicationsPage }))
);
const IntegrationsPage = lazy(() =>
import('./pages/IntegrationsPage').then((m) => ({ default: m.IntegrationsPage }))
);
const IssuedCertificatesPage = lazy(() =>
import('./pages/IssuedCertificatesPage').then((m) => ({ default: m.IssuedCertificatesPage }))
);
const LicensePage = lazy(() => import('./pages/LicensePage').then((m) => ({ default: m.LicensePage })));
const MarketplacePage = lazy(() =>
import('./pages/MarketplacePage').then((m) => ({ default: m.MarketplacePage }))
);
const OrdersPage = lazy(() => import('./pages/OrdersPage').then((m) => ({ default: m.OrdersPage })));
const OrderDetailsPage = lazy(() =>
import('./pages/OrderDetailsPage').then((m) => ({ default: m.OrderDetailsPage }))
);
const PaymentsPage = lazy(() => import('./pages/PaymentsPage').then((m) => ({ default: m.PaymentsPage })));
const PaymentDetailsPage = lazy(() =>
import('./pages/PaymentDetailsPage').then((m) => ({ default: m.PaymentDetailsPage }))
);
const PrerequisitesPage = lazy(() =>
import('./pages/PrerequisitesPage').then((m) => ({ default: m.PrerequisitesPage }))
);
const ReviewsPage = lazy(() => import('./pages/ReviewsPage').then((m) => ({ default: m.ReviewsPage })));
const ReviewDetailPage = lazy(() =>
import('./pages/ReviewDetailPage').then((m) => ({ default: m.ReviewDetailPage }))
);
const SettingsPage = lazy(() => import('./pages/SettingsPage').then((m) => ({ default: m.SettingsPage })));
const SocialLoginPage = lazy(() =>
import('./pages/SocialLoginPage').then((m) => ({ default: m.SocialLoginPage }))
);
const SubscriptionsProPage = lazy(() =>
import('./pages/SubscriptionsProPage').then((m) => ({ default: m.SubscriptionsProPage }))
);
const WhiteLabelPage = lazy(() =>
import('./pages/WhiteLabelPage').then((m) => ({ default: m.WhiteLabelPage }))
);
const WpEntityListPage = lazy(() =>
import('./pages/WpEntityListPage').then((m) => ({ default: m.WpEntityListPage }))
);
const WpUserListPage = lazy(() =>
import('./pages/WpUserListPage').then((m) => ({ default: m.WpUserListPage }))
);
const hubPages = () => import('./pages/hubs/HubPages');
const BrandingHubPage = lazy(() => hubPages().then((m) => ({ default: m.BrandingHubPage })));
const CertificatesHubPage = lazy(() => hubPages().then((m) => ({ default: m.CertificatesHubPage })));
const ContentLibraryHubPage = lazy(() => hubPages().then((m) => ({ default: m.ContentLibraryHubPage })));
const EmailHubPage = lazy(() => hubPages().then((m) => ({ default: m.EmailHubPage })));
const IntegrationsHubPage = lazy(() => hubPages().then((m) => ({ default: m.IntegrationsHubPage })));
const LearningRulesHubPage = lazy(() => hubPages().then((m) => ({ default: m.LearningRulesHubPage })));
const PeopleHubPage = lazy(() => hubPages().then((m) => ({ default: m.PeopleHubPage })));
const ReportsHubPage = lazy(() => hubPages().then((m) => ({ default: m.ReportsHubPage })));
const SalesHubPage = lazy(() => hubPages().then((m) => ({ default: m.SalesHubPage })));
const ToolsHubPage = lazy(() => hubPages().then((m) => ({ default: m.ToolsHubPage })));
/** Same splash as PHP `ReactAdminView` boot markup — one loader for pre-React + lazy chunks. */
function AdminRouteFallback() {
return (
Loading Sikshya…
);
}
function prefetchAdminChunks(): void {
// Reduce sidebar "flicker" by preloading common route chunks shortly after first paint.
// This avoids Suspense fallback flashes on each view change in slower environments.
const w = window as unknown as { requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => void };
const schedule = (cb: () => void) => {
if (typeof w.requestIdleCallback === 'function') {
w.requestIdleCallback(cb, { timeout: 1200 });
} else {
setTimeout(cb, 350);
}
};
schedule(() => {
void import('./pages/DashboardPage');
void import('./pages/CoursesPage');
void import('./pages/SettingsPage');
void import('./pages/OrdersPage');
void import('./pages/OrderDetailsPage');
void import('./pages/PaymentsPage');
void import('./pages/PaymentDetailsPage');
void import('./pages/EnrollmentsPage');
void import('./pages/AddonsPage');
void import('./pages/EmailPage');
});
}
function RoutedApp() {
const baseConfig = getConfig();
const { route } = useAdminRouting();
const { navigation } = useShellState();
const pageKey =
typeof route.page === 'string' && route.page.trim() !== '' ? route.page.trim() : 'dashboard';
const config = { ...baseConfig, page: pageKey, query: route.query ?? {}, navigation };
const page = config.page;
const q = (config.query || {}) as Record;
const isCertificateBuilder = page === 'edit-content' && String(q.post_type || '').trim() === 'sikshya_certificate';
const platformName = config.branding?.pluginName?.trim() || 'Sikshya';
useEffect(() => {
const root = document.getElementById('sikshya-admin-root');
if (!root) {
return;
}
const b = config.branding;
if (!b?.topbarBg && !b?.sidebarBg) {
clearAdminBrandThemeFromRoot(root);
return;
}
applyAdminBrandThemeToRoot(root, b);
return () => {
clearAdminBrandThemeFromRoot(root);
};
}, [config.branding?.topbarBg, config.branding?.sidebarBg]);
const T = {
course: term(config, 'course'),
courses: term(config, 'courses'),
lesson: term(config, 'lesson'),
lessons: term(config, 'lessons'),
quiz: term(config, 'quiz'),
quizzes: term(config, 'quizzes'),
assignment: term(config, 'assignment'),
assignments: term(config, 'assignments'),
chapter: term(config, 'chapter'),
chapters: term(config, 'chapters'),
student: term(config, 'student'),
students: term(config, 'students'),
instructor: term(config, 'instructor'),
instructors: term(config, 'instructors'),
enrollment: term(config, 'enrollment'),
enrollments: term(config, 'enrollments'),
};
useEffect(() => {
prefetchAdminChunks();
}, []);
const routes = (() => {
switch (page) {
case 'dashboard':
return ;
case 'courses':
return ;
case 'add-course':
return ;
case 'bundle-builder':
return ;
case 'edit-content':
return ;
case 'lessons':
case 'add-lesson':
return (
);
case 'quizzes':
return (
);
case 'assignments':
return (
);
case 'questions':
return (
);
case 'chapters':
return (
);
case 'certificates':
return (
);
case 'issued-certificates':
return ;
case 'orders':
return ;
case 'order':
return ;
case 'coupons':
return ;
case 'reviews':
return ;
case 'review':
return ;
case 'discussions':
return ;
case 'gradebook':
return ;
case 'assignment-submissions':
return (
);
case 'grading':
return ;
case 'activity-log':
return ;
case 'content-drip':
return ;
case 'subscriptions':
return ;
case 'course-team':
return ;
case 'marketplace':
return ;
case 'bundles':
return ;
case 'prerequisites':
return ;
case 'social-login':
return ;
case 'white-label':
return ;
case 'crm-automation':
return (
);
case 'calendar':
return ;
/* ---- Tabbed hubs (new sidebar entries that fan out to existing pages). ---- */
case 'content-library':
return ;
case 'people':
return ;
case 'certificates-hub':
return ;
case 'sales':
return ;
case 'email-hub':
case 'email-templates':
return ;
case 'branding':
return ;
case 'integrations-hub':
return ;
case 'learning-rules':
return ;
case 'course-categories':
return (
);
case 'students':
return (
);
case 'instructors':
return (
);
case 'instructor-applications':
return (
);
case 'enrollments':
return ;
case 'reports':
return ;
case 'payments':
return ;
case 'payment':
return ;
case 'settings':
return ;
case 'email':
return ;
case 'email-template-edit':
return ;
case 'tools':
return ;
case 'addons':
return ;
case 'integrations':
return ;
case 'email-marketing':
return ;
case 'license':
return ;
default:
return (
);
}
})();
const navTitle = (() => {
const items = config.navigation as NavItem[];
const walk = (rows: NavItem[] | undefined): string | null => {
if (!Array.isArray(rows)) return null;
for (const r of rows) {
if ((r as any)?.id === page && typeof (r as any)?.label === 'string' && (r as any).label.trim() !== '') {
return String((r as any).label).trim();
}
const kids = (r as any)?.children as NavItem[] | undefined;
const hit = walk(kids);
if (hit) return hit;
}
return null;
};
return walk(items);
})();
const shellTitle = (() => {
if (navTitle) return navTitle;
if (page === 'dashboard') return 'Dashboard';
if (page === 'settings') return 'Settings';
if (page === 'courses') return T.courses;
if (page === 'add-course') return `${T.course} builder`;
if (page === 'bundle-builder') return 'Bundle builder';
if (page === 'edit-content') {
const postType = String(q.post_type || '').trim();
const id = Number(q.post_id || q.id || 0) || 0;
const label =
postType === 'sikshya_certificate'
? 'Certificate'
: postType === 'sik_lesson'
? T.lesson
: postType === 'sik_quiz'
? T.quiz
: postType === 'sik_assignment'
? T.assignment
: postType === 'sik_course'
? T.course
: postType
? postType
: 'Content';
return id > 0 ? `Edit ${label}` : `New ${label}`;
}
return platformName;
})();
// Certificate builder is a full-screen workspace with its own header/actions.
// Do not wrap it in the admin shell (no sidebar, no top bar).
// No Suspense fallback: PHP skips the Sikshya boot loader for this route; hosts often show their own loader first.
if (isCertificateBuilder) {
return {routes};
}
return (
}>{routes}
);
}
export default function App() {
const baseConfig = getConfig();
// Ensure the initial render respects the URL (deep links / reload).
// This also protects us if PHP-provided config.page lags behind the URL.
const initial = parseAdminRoute(baseConfig);
const normalizedBase = { ...baseConfig, page: initial.page, query: initial.query };
return (
);
}