import { useState, useRef, useEffect } from 'react' import './App.css' const QUICK_PROMPTS = [ 'What were total sales by category?', 'Compare revenue by region last quarter', 'Show monthly sales trend for 2023', 'Top products by revenue' ] const DEFAULT_AGENT_MESSAGE = { type: 'agent', content: 'Hi! Ask me anything about your business data and I will generate SQL, visuals, and insights for you.' } function App() { const sessionIdRef = useRef(null) if (!sessionIdRef.current) { sessionIdRef.current = crypto?.randomUUID ? crypto.randomUUID() : `session-${Date.now()}` } const sessionId = sessionIdRef.current const [messages, setMessages] = useState([DEFAULT_AGENT_MESSAGE]) const [input, setInput] = useState('') const [loading, setLoading] = useState(false) const [activeResult, setActiveResult] = useState(null) const [error, setError] = useState(null) const [datasetCatalog, setDatasetCatalog] = useState([]) const [catalogLoading, setCatalogLoading] = useState(false) const [tableName, setTableName] = useState('sales') const [uploading, setUploading] = useState(false) const [uploadError, setUploadError] = useState(null) const [clearing, setClearing] = useState(false) const messagesEndRef = useRef(null) const inputRef = useRef(null) const [datasetPreview, setDatasetPreview] = useState(null) const dataPreview = activeResult?.data ?? [] const previewColumns = dataPreview.length ? Object.keys(dataPreview[0]) : [] const previewRows = dataPreview.slice(0, 5) const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) } useEffect(() => { scrollToBottom() }, [messages]) useEffect(() => { if (!inputRef.current) return const el = inputRef.current el.style.height = 'auto' const nextHeight = Math.min(el.scrollHeight, 160) el.style.height = `${nextHeight}px` }, [input]) const fetchDatasetCatalog = async () => { setCatalogLoading(true) try { const res = await fetch('http://localhost:8000/api/datasets') const payload = await res.json() setDatasetCatalog(payload.tables || []) } catch (err) { console.error('Failed to fetch dataset catalog', err) } finally { setCatalogLoading(false) } } useEffect(() => { fetchDatasetCatalog() }, []) const sendQuery = async (question) => { const trimmed = question.trim() if (!trimmed) return const userMessage = { type: 'user', content: trimmed } setMessages((prev) => [...prev, userMessage]) setInput('') setLoading(true) setError(null) try { const response = await fetch('http://localhost:8000/api/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: trimmed, session_id: sessionId }) }) const data = await response.json() if (!response.ok || data.error) { const message = data.error || 'Something went wrong.' setError(message) setMessages((prev) => [...prev, { type: 'agent', content: `⚠️ ${message}` }]) return } const agentPayload = { type: 'agent', content: data.insights || 'Analysis complete.', sql: data.sql_query, visualization: data.visualization_url, chartSummary: data.visualization_summary, trendSummary: data.trend_analysis?.summary, anomalySummary: data.anomaly_analysis?.summary, report: data.report_url } setMessages((prev) => [...prev, agentPayload]) setActiveResult(data) } catch (err) { const fallback = `Network error: ${err.message}` setError(fallback) setMessages((prev) => [...prev, { type: 'agent', content: fallback }]) } finally { setLoading(false) } } const handleSubmit = (e) => { e.preventDefault() if (loading) return sendQuery(input) } const handlePromptClick = (prompt) => { if (loading) return setInput(prompt) sendQuery(prompt) } const handleDatasetUpload = async (event) => { const file = event.target.files?.[0] if (!file || uploading) return const formData = new FormData() formData.append('file', file) formData.append('table_name', tableName || 'sales') setUploading(true) setUploadError(null) try { const response = await fetch('http://localhost:8000/api/upload-csv', { method: 'POST', body: formData }) const data = await response.json() if (!response.ok || data.status !== 'success') { throw new Error(data.detail || 'Upload failed') } if (data.preview) { setDatasetPreview({ table: data.table, columns: data.columns, rows: data.preview }) setActiveResult(null) } fetchDatasetCatalog() } catch (err) { setUploadError(err.message) } finally { setUploading(false) event.target.value = '' } } const handleClearChat = async () => { if (loading || clearing) return setClearing(true) setMessages([DEFAULT_AGENT_MESSAGE]) setActiveResult(null) setError(null) try { await fetch('http://localhost:8000/api/session/reset', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: sessionId }) }) } catch (err) { console.error('Failed to reset session', err) } finally { setClearing(false) } } return (
{loading ? 'Agent is thinking...' : 'Agent is ready'}
{messages.length === 1 ? (

Autonomous Data Analyst

Ask natural-language questions about your warehouse. The agent will plan the query, validate results, draw charts, and package everything in a PDF.

{QUICK_PROMPTS.map((prompt) => ( ))}
) : ( messages.map((msg, index) => (
{msg.type === 'agent' ? '🤖' : '👤'}

{msg.content}

{msg.sql && (
{msg.sql}
)} {(msg.trendSummary || msg.anomalySummary) && (
{msg.trendSummary && Trend: {msg.trendSummary}} {msg.anomalySummary && Anomaly: {msg.anomalySummary}}
)}
)) )}