import { useCallback, useMemo, useState } from 'react'; import { getSikshyaApi, SIKSHYA_ENDPOINTS } from '../api'; import { EmbeddableShell } from '../components/shared/EmbeddableShell'; import { ApiErrorPanel } from '../components/shared/ApiErrorPanel'; import { ListPanel } from '../components/shared/list/ListPanel'; import { ListEmptyState } from '../components/shared/list/ListEmptyState'; import { RowActionsMenu, type RowActionItem } from '../components/shared/list/RowActionsMenu'; import { useDebouncedValue } from '../hooks/useDebouncedValue'; import { useAsyncData } from '../hooks/useAsyncData'; import { formatPostDate } from '../lib/formatPostDate'; import type { SikshyaReactConfig } from '../types'; import { TopRightToast, useTopRightToast } from '../components/shared/TopRightToast'; import { __ } from '../lib/i18n'; type Row = { user_id: number; email: string; display_name: string; registered: string; status: string; applied_at: string; headline: string; }; type ListResponse = { ok?: boolean; rows?: Row[]; total?: number; pages?: number; page?: number; per_page?: number; }; export function InstructorApplicationsPage(props: { config: SikshyaReactConfig; title: string; subtitle?: string; embedded?: boolean; }) { const { config, title, subtitle, embedded } = props; const adminBase = config.adminUrl.replace(/\/?$/, '/'); const [page, setPage] = useState(1); const [status, setStatus] = useState('pending'); const [search, setSearch] = useState(''); const debouncedSearch = useDebouncedValue(search, 320); const [busyId, setBusyId] = useState(null); const toast = useTopRightToast(2600); const queryKey = useMemo(() => `${page}|${status}|${debouncedSearch}`, [page, status, debouncedSearch]); const loader = useCallback(async () => { return getSikshyaApi().get( SIKSHYA_ENDPOINTS.admin.instructorApplications({ page, per_page: 20, status: status || undefined, search: debouncedSearch.trim() || undefined, }) ); }, [page, status, debouncedSearch]); const { loading, data, error, refetch } = useAsyncData(loader, [queryKey]); const rows = data?.rows ?? []; const total = data?.total ?? 0; const pages = data?.pages ?? 0; const act = async (userId: number, action: 'approve' | 'reject') => { setBusyId(userId); toast.clear(); try { const path = action === 'approve' ? SIKSHYA_ENDPOINTS.admin.instructorApplicationApprove(userId) : SIKSHYA_ENDPOINTS.admin.instructorApplicationReject(userId); await getSikshyaApi().post(path, {}); toast.success(__('Saved', 'sikshya'), action === 'approve' ? __('Approved.', 'sikshya') : __('Rejected.', 'sikshya')); void refetch(); } catch (e) { toast.error(__('Request failed', 'sikshya'), e instanceof Error ? e.message : 'Request failed'); } finally { setBusyId(null); } }; return (

{total ? `${total} total` : null}

{error ? (
void refetch()} />
) : loading ? (

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

) : rows.length === 0 ? (
) : (
{rows.map((r) => { const editUrl = `${adminBase}user-edit.php?user_id=${r.user_id}`; const busy = busyId === r.user_id; return ( ); })}
{__('User', 'sikshya')} {__('Headline', 'sikshya')} {__('Status', 'sikshya')} {__('Submitted', 'sikshya')} {__('Actions', 'sikshya')}
{r.display_name || `User #${r.user_id}`}
{r.email}
{r.headline ? {r.headline} : '—'} {r.status || '—'} {r.applied_at ? formatPostDate(r.applied_at) : '—'} {(() => { const items: RowActionItem[] = [ { key: 'profile', label: __('Open WP user', 'sikshya'), href: editUrl, external: true }, ]; if (r.status === 'pending') { items.unshift( { key: 'approve', label: busy ? __('Working…', 'sikshya') : __('Approve', 'sikshya'), onClick: () => void act(r.user_id, 'approve'), disabled: busy, }, { key: 'reject', label: __('Reject', 'sikshya'), onClick: () => void act(r.user_id, 'reject'), danger: true, disabled: busy, } ); } return ; })()}
)} {pages > 1 ? (
Page {page} of {pages}
) : null}
); }