import { useQuery } from '@tanstack/react-query' import { Link } from 'react-router-dom' import { api } from '@/lib/api' import { hasWebsiteScope, scopedQueryKey } from '@/lib/queryKeys' import { useAppStore } from '@/store/useAppStore' import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card' import { Skeleton } from '@/components/ui/skeleton' import { Badge } from '@/components/ui/badge' import { FileText, Users, CheckCircle2, Target, AlertCircle, Activity, Bot } from 'lucide-react' // Adjust type for dashboard stats interface DashboardStats { metrics: { tryouts: number items: number sessions: number completed_sessions: number completion_rate: number calibration_percentage: number } recent_sessions: Array<{ id: number wp_user_id: string tryout_id: string end_time: string NM: number | null NN: number | null }> recent_ai_runs: Array<{ id: number requested_count: number basis_item_id: number created_at: string status: string pending_review_count?: number }> } export default function Dashboard() { const { websiteId } = useAppStore() const { data, isLoading, isError } = useQuery({ queryKey: scopedQueryKey(websiteId, 'dashboard-stats'), queryFn: async () => { const res = await api.get('/admin/dashboard/stats') return res.data }, enabled: hasWebsiteScope(websiteId), }) if (!hasWebsiteScope(websiteId)) { return (
Select a website to load dashboard statistics.
) } const hasPendingAIReview = data?.recent_ai_runs.some((run) => run.status === 'pending_review' || (run.pending_review_count || 0) > 0) ?? false return (

Good Morning, Admin

Here is your system overview for today.

Getting Started & Workflow Follow these steps to generate and calibrate IRT questions.
1
Create Tryout

Import Tryout snapshots from Sejoli to initialize the question tree.

2
Generate Variants

Use AI to generate parallel question variants (Mudah/Sedang/Sulit).

3
Gather Data

Students complete tryouts to gather participant answer data.

4
Calibrate & Normalize

System calibrates IRT parameters (p-value, IRT b) and calculates NN score.

{isLoading ? (
{[1, 2, 3, 4].map((i) => ( ))}
) : isError || !data ? (
Failed to load dashboard statistics.
) : ( <> {/* System Overview KPIs */}
Active Exams
{data.metrics.tryouts}
Total Questions
{data.metrics.items}

{data.metrics.calibration_percentage}% Calibrated

Student Attempts
{data.metrics.sessions}

{data.metrics.completed_sessions} completed

Completion Rate
{data.metrics.completion_rate}%
{/* Attention Needed */} Attention Needed {data.metrics.calibration_percentage < 100 && (

Questions need calibration

Some questions have enough data but haven't been calibrated yet.

)} {hasPendingAIReview && (

AI generated questions pending review

You have new AI-generated questions waiting for your approval.

Review now
)} {data.metrics.calibration_percentage === 100 && !hasPendingAIReview && (

You're all caught up! No urgent tasks.

)}
{/* Recent Activity */} Recent Activity
{data.recent_sessions.length === 0 && data.recent_ai_runs.length === 0 ? (

No recent activity.

) : (
{data.recent_sessions.map((session) => (

Student {session.wp_user_id}

Completed Tryout: {session.tryout_id}

Score: {session.NN ?? session.NM ?? 0}

{new Date(session.end_time).toLocaleTimeString()}

))} {data.recent_ai_runs.map((run) => (

AI Generation Run #{run.id}

Target: {run.requested_count} questions

{run.status}

{new Date(run.created_at).toLocaleTimeString()}

))}
)}
)}
) }