import React, { useState, useRef, useEffect } from 'react'; import { Upload, FileSpreadsheet, BarChart2, TrendingUp, Download, Settings, RefreshCw, X, AlertCircle, Brain, Sparkles } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Badge } from '@/components/ui/badge'; import { cn } from '@/lib/utils'; import Papa from 'papaparse'; import * as d3 from 'd3'; import * as ss from 'simple-statistics'; import { API_URL } from '@/config/api'; interface DataPoint { [key: string]: string | number; } interface AnalysisResult { type: 'regression' | 'correlation' | 'clustering'; description: string; value?: number; details?: string; clusters?: number[]; } export default function DataAnalysisWidget() { const [data, setData] = useState([]); const [columns, setColumns] = useState([]); const [fileName, setFileName] = useState(''); const [xAxis, setXAxis] = useState(''); const [yAxis, setYAxis] = useState(''); const [chartType, setChartType] = useState<'scatter' | 'bar' | 'line'>('scatter'); const [analysis, setAnalysis] = useState(null); const [isProcessing, setIsProcessing] = useState(false); const [isAiProcessing, setIsAiProcessing] = useState(false); const d3Container = useRef(null); // Handle File Upload const handleFileUpload = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; setFileName(file.name); setIsProcessing(true); Papa.parse(file, { header: true, dynamicTyping: true, skipEmptyLines: true, complete: (results) => { const parsedData = results.data as DataPoint[]; if (parsedData.length > 0) { setData(parsedData); const cols = Object.keys(parsedData[0]); setColumns(cols); // Default selections if (cols.length >= 2) { setXAxis(cols[0]); setYAxis(cols[1]); } } setIsProcessing(false); }, error: (error) => { console.error('CSV Error:', error); setIsProcessing(false); } }); }; // Render D3 Chart useEffect(() => { if (!data.length || !d3Container.current || !xAxis || !yAxis) return; const svg = d3.select(d3Container.current); svg.selectAll('*').remove(); const margin = { top: 20, right: 20, bottom: 40, left: 50 }; const width = d3Container.current.clientWidth - margin.left - margin.right; const height = d3Container.current.clientHeight - margin.top - margin.bottom; const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`); // Scales let x: d3.AxisScale; const xValues = data.map(d => d[xAxis]); if (typeof xValues[0] === 'number') { x = d3.scaleLinear() .domain([d3.min(xValues as number[]) || 0, d3.max(xValues as number[]) || 100]) .range([0, width]); } else { x = d3.scaleBand() .domain(xValues as string[]) .range([0, width]) .padding(0.1); } const y = d3.scaleLinear() .domain([0, d3.max(data, d => Number(d[yAxis])) || 100]) .range([height, 0]); // Axes g.append('g') .attr('transform', `translate(0,${height})`) .call(d3.axisBottom(x).ticks(width / 80)) // Responsive ticks .selectAll("text") .style("text-anchor", "end") .attr("dx", "-.8em") .attr("dy", ".15em") .attr("transform", "rotate(-15)"); g.append('g') .call(d3.axisLeft(y)); // Plots if (chartType === 'scatter') { g.selectAll('.dot') .data(data) .enter().append('circle') .attr('class', 'dot') .attr('cx', d => x(d[xAxis] as any) || 0) .attr('cy', d => y(Number(d[yAxis]))) .attr('r', d => (analysis?.clusters ? 6 : 4)) // Larger dots if clustered .style('fill', (d, i) => { if (analysis?.clusters) { const colors = ['#8b5cf6', '#10b981', '#f59e0b', '#ef4444', '#3b82f6']; return colors[analysis.clusters[i] % colors.length]; } return '#8b5cf6'; }) .style('opacity', 0.7); } else if (chartType === 'bar') { g.selectAll('.bar') .data(data) .enter().append('rect') .attr('class', 'bar') .attr('x', d => (x as d3.ScaleBand)(String(d[xAxis])) || 0) .attr('width', (x as d3.ScaleBand).bandwidth()) .attr('y', d => y(Number(d[yAxis]))) .attr('height', d => height - y(Number(d[yAxis]))) .style('fill', '#8b5cf6'); } else if (chartType === 'line') { const line = d3.line() .x(d => x(d[xAxis] as any) || 0) .y(d => y(Number(d[yAxis]))) .curve(d3.curveMonotoneX); g.append("path") .datum(data) .attr("fill", "none") .attr("stroke", "#8b5cf6") .attr("stroke-width", 2) .attr("d", line); } }, [data, xAxis, yAxis, chartType, analysis]); // Basic Analysis (Client Side) const runBasicAnalysis = () => { if (!data.length || !xAxis || !yAxis) return; const xValues = data.map(d => Number(d[xAxis])); const yValues = data.map(d => Number(d[yAxis])); if (xValues.some(isNaN) || yValues.some(isNaN)) { setAnalysis({ type: 'correlation', description: 'Cannot perform regression on non-numeric data.', value: 0 }); return; } const regression = ss.linearRegression(data.map(d => [Number(d[xAxis]), Number(d[yAxis])])); const correlation = ss.sampleCorrelation(xValues, yValues); setAnalysis({ type: 'regression', description: `Linear Trend: y = ${regression.m.toFixed(2)}x + ${regression.b.toFixed(2)}`, value: correlation, details: `Correlation (r): ${correlation.toFixed(3)}` }); }; // Advanced AI Analysis (Server Side) const runAdvancedAnalysis = async () => { if (!data.length || !xAxis) return; setIsAiProcessing(true); try { // We use the new data.analysis tool via MCP const response = await fetch(`${API_URL}/api/mcp`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tool: 'data.analysis', payload: { type: 'clustering', // Default to clustering for now data: data, params: { features: [xAxis, yAxis], k: 3 } } }) }); const result = await response.json(); if (result.error) { throw new Error(result.error); } // Handle clustering result if (result.clusters) { setAnalysis({ type: 'clustering', description: `AI identified 3 distinct clusters based on ${xAxis} and ${yAxis}.`, clusters: result.clusters, details: 'Visualization updated with cluster colors.' }); } } catch (error: any) { console.error('AI Analysis Failed:', error); setAnalysis({ type: 'correlation', description: 'AI Analysis Failed', details: error.message, value: 0 }); } finally { setIsAiProcessing(false); } }; return (
{/* Header */}

AI Data Analyst

0 ? "bg-green-400" : "bg-gray-400")} /> {data.length > 0 ? `${data.length} ROWS LOADED` : "READY FOR DATA"}
{/* Main Content */}
{/* Controls Sidebar */}
{data.length > 0 ? ( <>
X Axis (Independent)
Y Axis (Dependent)
{analysis && (

{analysis.type === 'clustering' ? 'AI Insight' : 'Basic Insight'}

{analysis.description}

{analysis.details &&

{analysis.details}

}
)} ) : (

Upload a CSV file to begin analysis

)}
{/* Chart Area */}
{data.length > 0 ? ( ) : (
)}
); }