import { useCallback, useEffect, useMemo, useState } from 'react' import { gradeTask, resetTask, submitQuery } from '../api/client' import { TASK_CATALOG } from '../data/taskCatalog' import type { ApiInfo, GraderResponse, StepResponse, TaskInfo, TaskObservation } from '../types' import { SectionCard } from './SectionCard' import { SqlPreviewTable } from './SqlPreviewTable' interface QueryWorkbenchTabProps { selectedTask: string taskInfo?: TaskInfo apiInfo: ApiInfo } function scoreTone(score: number | null) { if (score === null) return 'text-zinc-400' if (score >= 0.9) return 'text-emerald-300' if (score >= 0.3) return 'text-amber-300' return 'text-rose-300' } export function QueryWorkbenchTab({ selectedTask, taskInfo, apiInfo, }: QueryWorkbenchTabProps) { const [task, setTask] = useState(null) const [query, setQuery] = useState('') const [grader, setGrader] = useState(null) const [stepResult, setStepResult] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const catalogEntry = TASK_CATALOG[selectedTask] const loadTask = useCallback(async () => { setLoading(true) setError(null) try { const nextTask = await resetTask(selectedTask) setTask(nextTask) setQuery(nextTask.broken_query) setStepResult(null) setGrader(null) } catch (loadError) { setError(loadError instanceof Error ? loadError.message : String(loadError)) } finally { setLoading(false) } }, [selectedTask]) useEffect(() => { void loadTask() }, [loadTask]) const mergedObservation = useMemo(() => { if (!task) return null if (!stepResult) return task return { ...task, ...stepResult.observation } }, [stepResult, task]) const runQuery = async (nextQuery: string) => { setLoading(true) setError(null) try { const response = await submitQuery(nextQuery) setStepResult(response) setTask((current) => (current ? { ...current, ...response.observation } : response.observation)) const nextScore = await gradeTask(selectedTask) setGrader(nextScore) } catch (runError) { setError(runError instanceof Error ? runError.message : String(runError)) } finally { setLoading(false) } } return (
void loadTask()} className="px-3 py-2 rounded-lg text-sm border border-zinc-800 bg-zinc-950/60 hover:bg-zinc-900/60" > Reset Task } > {mergedObservation ? (

Difficulty

{mergedObservation.difficulty}

Expected Shape

{mergedObservation.expected_row_count} × {mergedObservation.expected_column_count}

Current Score

{grader ? grader.score.toFixed(4) : '—'}

Hint

{mergedObservation.hint}

Broken Query

                  {mergedObservation.broken_query}
                

Schema

                  {mergedObservation.schema_sql}
                
{mergedObservation.broken_query_error && (
Baseline error: {mergedObservation.broken_query_error}
)}
) : (
Loading task snapshot…
)}
} >