import { useCallback, useEffect, useMemo, useState } from 'react'; import { getSikshyaApi, SIKSHYA_ENDPOINTS } from '../api'; import { EmbeddableShell } from '../components/shared/EmbeddableShell'; import { GatedFeatureWorkspace } from '../components/GatedFeatureWorkspace'; import { ListPanel } from '../components/shared/list/ListPanel'; import { ButtonPrimary } from '../components/shared/buttons'; import { RowActionsMenu, type RowActionItem } from '../components/shared/list/RowActionsMenu'; import { StatusBadge } from '../components/shared/list/StatusBadge'; import { FormInput, FormSelect, FormLabel } from '../components/shared/form'; import { useSikshyaDialog } from '../components/shared/SikshyaDialogContext'; import { useAddonEnabled } from '../hooks/useAddons'; import { isFeatureEnabled, resolveGatedWorkspaceMode } from '../lib/licensing'; import type { SikshyaReactConfig } from '../types'; import { __ } from '../lib/i18n'; type ChartPayload = { labels?: string[]; counts?: number[] }; type StatsPayload = { published_courses?: number; total_enrollments?: number; distinct_learners?: number; completed_enrollments?: number; completion_rate?: number; revenue_html?: string; has_enrollment_table?: boolean; has_payments_table?: boolean; }; type Snapshot = { chart: ChartPayload; stats: StatsPayload; }; type QuizAttemptRow = { id: number; user_id: number; user_name: string; user_email: string; quiz_id: number; quiz_title: string; course_id: number; course_title: string; attempt_number: number; score: number; status: string; started_at: string; completed_at: string; attempts_used: number; attempts_limit: number; attempts_remaining: number | null; is_locked: boolean; }; type QuizAttemptsResponse = { success?: boolean; attempts?: QuizAttemptRow[]; total?: number; pages?: number; page?: number; per_page?: number; table_missing?: boolean; }; type ReportsAdvancedExportResp = { ok?: boolean; csv?: string; filename?: string; truncated?: boolean; row_count?: number; notice?: string; }; type EnterpriseStatusResp = { ok?: boolean; enabled?: boolean; recipient?: string; day_of_week?: number; hour?: number; next_run_unix?: number; next_run_iso?: string; last_run_unix?: number; last_run_iso?: string; last_status?: string; }; type EnterpriseScheduleRow = { id: number; status: 'active' | 'paused' | string; label?: string; report_type?: string; frequency?: 'daily' | 'weekly' | 'monthly' | string; day_of_week?: number; day_of_month?: number; hour?: number; recipients?: string; last_status?: string; last_run_at?: string | null; }; type EnterpriseRunRow = { id: number; schedule_id?: number | null; trigger_source?: string; status?: string; report_type?: string; row_count?: number; truncated?: number; error_message?: string | null; created_at?: string; started_at?: string | null; finished_at?: string | null; }; type EnterpriseDashboardV2Resp = { ok?: boolean; schedules?: EnterpriseScheduleRow[]; runs?: EnterpriseRunRow[]; }; function StatCard(props: { label: string; value: string; hint?: string }) { const { label, value, hint } = props; return (
{label}
{value}
{hint ?{hint}
: null}{__('Last twelve months (UTC month buckets).', 'sikshya')}
{labels.length ? (No chart data yet. When the enrollments table is available and students enroll, bars will appear here.
)}{__('Completed payments total (if payments table exists).', 'sikshya')}
{stats.revenue_html ? String(stats.revenue_html) : '—'}
{!stats.has_payments_table ? (Payments table not detected; revenue may show as zero until payments are recorded.
) : null}{attemptsTotal} total • Page {attemptsPage} {attemptsPages > 0 ? ` of ${attemptsPages}` : ''}
) : null}Quiz attempts table not detected yet. Run plugin migrations and record at least one attempt to see rows here.
) : attemptRows.length ? (| {__('Learner', 'sikshya')} | {__('Quiz', 'sikshya')} | {__('Course', 'sikshya')} | {__('Attempt #', 'sikshya')} | {__('Used / Limit', 'sikshya')} | {__('Remaining', 'sikshya')} | {__('Score', 'sikshya')} | {__('Status', 'sikshya')} | {__('Completed', 'sikshya')} | {__('Actions', 'sikshya')} |
|---|---|---|---|---|---|---|---|---|---|
|
{r.user_name || `User #${r.user_id}`}
{r.user_email}
|
{r.quiz_title || `Quiz #${r.quiz_id}`} | {r.course_title || (r.course_id ? `Course #${r.course_id}` : '—')} | {r.attempt_number || '—'} | {r.attempts_limit > 0 ? `${r.attempts_used} / ${r.attempts_limit}` : `${r.attempts_used} / ∞`} | {typeof r.attempts_remaining === 'number' ? r.attempts_remaining : '—'} | {Number.isFinite(r.score) ? String(r.score) : '—'} |
|
{r.completed_at || '—'} |
{(() => {
const items: RowActionItem[] = [
{
key: 'reset-timer',
label: 'Reset timer',
onClick: () => void resetAttemptTimer(Number(r.id) || 0),
disabled: attemptsBusy,
},
];
return |
{__('No quiz attempts recorded yet.', 'sikshya')}
)}Pick a report type, optionally narrow by course or dates, then download. Privacy and row limits live under{' '} Add-ons → Advanced analytics & exports settings.
{exportError}
) : null} {exportHint ?{exportHint}
: null} > ) : null}Sends to{' '} {enterpriseStatus?.recipient || 'site admin email'} {enterpriseStatus?.next_run_iso ? ( <> . Next scheduled run:{' '} {enterpriseStatus.next_run_iso} > ) : ( '.' )}
{enterpriseStatus?.last_run_iso ? (Last run: {enterpriseStatus.last_run_iso} {enterpriseStatus.last_status ? ( <> {' '} · status: {enterpriseStatus.last_status} > ) : null}
) : null} {enterpriseMsg ? ({enterpriseMsg}
) : null}Create multiple schedules and queue runs without touching server cron settings. This is admin-only.
| {__('ID', 'sikshya')} | {__('Label', 'sikshya')} | {__('Type', 'sikshya')} | {__('Cadence', 'sikshya')} | {__('Recipients', 'sikshya')} | {__('Last status', 'sikshya')} | {__('Actions', 'sikshya')} |
|---|---|---|---|---|---|---|
| {s.id} | {s.label || '—'} | {s.report_type || '—'} | {s.frequency || '—'} {typeof s.hour === 'number' ? `@ ${String(s.hour).padStart(2, '0')}:00` : ''} | {s.recipients?.trim() ? s.recipients : 'Default admin email'} | {s.last_status || '—'} |
{__('No schedules yet. Create one above.', 'sikshya')}
)} {enterpriseDash?.runs?.length ? (| {__('Run', 'sikshya')} | {__('Schedule', 'sikshya')} | {__('Status', 'sikshya')} | {__('Type', 'sikshya')} | {__('Created', 'sikshya')} | {__('Error', 'sikshya')} |
|---|---|---|---|---|---|
| {r.id} | {r.schedule_id || '—'} | {r.status || '—'} | {r.report_type || '—'} | {r.created_at || '—'} | {r.error_message || ''} |