import { useCallback, useMemo, useState } 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 { ListEmptyState } from '../components/shared/list/ListEmptyState'; import { DataTable, type Column } from '../components/shared/DataTable'; import { StatusBadge } from '../components/shared/list/StatusBadge'; import { DataTableSkeleton } from '../components/shared/Skeleton'; import { ButtonPrimary, ButtonSecondary } from '../components/shared/buttons'; import { FieldHint } from '../components/shared/FieldHint'; import { useAsyncData } from '../hooks/useAsyncData'; import { useAddonEnabled } from '../hooks/useAddons'; import { isFeatureEnabled, resolveGatedWorkspaceMode } from '../lib/licensing'; import { appViewHref } from '../lib/appUrl'; import { AddonSettingsPage } from './AddonSettingsPage'; import type { SikshyaReactConfig } from '../types'; import { __ } from '../lib/i18n'; type TabId = 'staff' | 'settings'; type Event = { type: string; id?: number; title: string; subtitle?: string; date?: string; datetime?: string; unix?: number; course_id?: number; deep_link?: string; }; type Resp = { ok?: boolean; scope?: string; description?: string; events?: Event[]; meta?: { count?: number; from?: string | null; to?: string | null; limit?: number; generated_at?: string }; }; function typeLabel(type: string): string { switch (type) { case 'course_published': return 'Course published'; case 'lesson_unlock': return 'Lesson opens'; case 'quiz_unlock': return 'Quiz opens'; case 'assignment_unlock': return 'Assignment opens'; case 'assignment_due': return 'Assignment due'; case 'live_class': return 'Live session'; case 'enrollment': return 'Enrollment'; default: return type.replace(/_/g, ' '); } } export function CalendarPage(props: { config: SikshyaReactConfig; title: string; embedded?: boolean }) { const { config, title, embedded } = props; const featureOk = isFeatureEnabled(config, 'calendar'); const addon = useAddonEnabled('calendar'); const mode = resolveGatedWorkspaceMode(featureOk, addon.enabled, addon.loading); const enabled = mode === 'full'; const [tab, setTab] = useState('staff'); const [from, setFrom] = useState(''); const [to, setTo] = useState(''); const staffUrl = useMemo(() => { const q = new URLSearchParams(); const f = from.trim(); const t = to.trim(); if (f) q.set('from', f); if (t) q.set('to', t); const s = q.toString(); return s ? `${SIKSHYA_ENDPOINTS.pro.calendarFeed}?${s}` : SIKSHYA_ENDPOINTS.pro.calendarFeed; }, [from, to]); const loader = useCallback(async () => { if (!enabled || tab !== 'staff') return { ok: true, events: [] as Event[] } as Resp; return getSikshyaApi().get(staffUrl); }, [enabled, tab, staffUrl]); const { loading, data, error, refetch } = useAsyncData(loader, [enabled, tab, staffUrl]); const rows = data?.events ?? []; const description = data?.description; const meta = data?.meta; const columns: Column[] = useMemo( () => [ { id: 'date', header: 'Date', render: (r) => ( {r.date || '—'} ), }, { id: 'type', header: 'Type', render: (r) => , }, { id: 'title', header: 'Title', render: (r) => (
{r.title || '—'}
{r.subtitle ?
{r.subtitle}
: null}
), }, { id: 'links', header: 'Open', render: (r) => { const cid = r.course_id ?? r.id ?? 0; const site = r.deep_link?.trim(); return (
{site ? ( View on site ) : null} {cid ? ( In admin ) : null}
); }, }, ], [config] ); return ( refetch()}> {loading ? __('Refreshing…', 'sikshya') : __('Refresh', 'sikshya')} ) : null } > addon.enable()} addonError={addon.error} > {enabled ? (
setTab('staff')}> Staff catalog setTab('settings')}> Add-on defaults
) : null} {tab === 'settings' && enabled ? ( ) : null} {tab === 'staff' ? ( <> {enabled ? (
setFrom(e.target.value)} className="w-full rounded-md border border-slate-200 bg-white px-2 py-1.5 text-sm dark:border-slate-600 dark:bg-slate-950" />
setTo(e.target.value)} className="w-full rounded-md border border-slate-200 bg-white px-2 py-1.5 text-sm dark:border-slate-600 dark:bg-slate-950" />
void refetch()}> Apply filters { setFrom(''); setTo(''); }} > Clear
Use ISO dates (YYYY-MM-DD) in the site timezone context; invalid values are ignored server-side.
) : null} {error ? refetch()} /> : null} {description ? (

{description}

) : null} {meta?.count !== undefined ? (

Showing {meta.count} row{meta.count === 1 ? '' : 's'} {meta.limit ? ` (limit ${meta.limit})` : ''} {meta.generated_at ? ` · generated ${meta.generated_at}` : ''}

) : null} {loading && tab === 'staff' ? ( ) : ( columns={columns} rows={rows} rowKey={(r) => `${r.type}-${r.id ?? r.course_id ?? ''}-${r.unix ?? r.date ?? r.title}`} emptyContent={ rows.length === 0 ? ( ) : undefined } wrapInCard={false} /> )} ) : null}
); }