import React, { useState } from 'react'; import { useSessionStore } from '../store/session'; import { useNotebookStore } from '../store/notebook'; import { CHART_TYPES, getChartType, isChartAvailable, countColumnTypes } from '../lib/chartTypes'; export function VisualizationPanel() { const { columns } = useSessionStore(); const { addCell, updateCell } = useNotebookStore(); const [selectedType, setSelectedType] = useState(''); const [params, setParams] = useState>({}); const colNames = columns.map((c) => c.name); const numericCols = columns.filter((c) => c.column_type === 'numeric').map((c) => c.name); const categoricalCols = columns.filter((c) => c.column_type === 'categorical').map((c) => c.name); const datetimeCols = columns.filter((c) => c.column_type === 'datetime').map((c) => c.name); const geoCols = columns.filter((c) => c.column_type === 'geo_lat_lon').map((c) => c.name); const typeCounts = countColumnTypes(columns); const chartType = getChartType(selectedType); const handleCreate = () => { if (!selectedType) return; const cellId = addCell(selectedType === 'map' ? 'map' : 'chart', `${selectedType} chart`, { chartType: selectedType, params, }); updateCell(cellId, { config: { chartType: selectedType, params } }); }; const renderColumnSelect = (name: string, relevantCols: string[], required: boolean) => (
); const getRelevantCols = (param: string): string[] => { if (param.includes('x_col') || param.includes('y_col') || param.includes('value') || param === 'column') { return numericCols.length > 0 ? numericCols : colNames; } if (param === 'columns') { return numericCols.length > 0 ? numericCols : colNames; } if (param.includes('category')) { return categoricalCols.length > 0 ? categoricalCols : colNames; } if (param.includes('date')) { return datetimeCols.length > 0 ? datetimeCols : colNames; } if (param.includes('lat') || param.includes('lon')) { return geoCols.length > 0 ? geoCols : numericCols.length > 0 ? numericCols : colNames; } if (param.includes('group')) { return categoricalCols.length > 0 ? categoricalCols : colNames; } return colNames; }; // Render a multi-select for columns (used by radar chart) const renderMultiColumnSelect = (name: string, relevantCols: string[], required: boolean) => (
{relevantCols.map((c) => { const selected = (params[name] || []).includes(c); return ( ); })} {relevantCols.length === 0 && (

No columns available

)}
{(params[name] || []).length > 0 && (

{(params[name] || []).length} selected

)}
); return (

Create Visualization

{/* Chart type grid */}
{CHART_TYPES.map((ct) => { const available = isChartAvailable(ct, columns); return (
{!available && (
{ct.unavailableMessage}
)}
); })}
{/* Available types summary */}
{typeCounts.numeric > 0 && # Numeric: {typeCounts.numeric}} {typeCounts.categorical > 0 && Aa Categorical: {typeCounts.categorical}} {typeCounts.datetime > 0 && DT Datetime: {typeCounts.datetime}} {typeCounts.geo_lat_lon > 0 && G Geo: {typeCounts.geo_lat_lon}}
{/* Parameter form */} {chartType && (
{chartType.requiredParams.map((param) => { if (param === 'columns') { const relevantCols = getRelevantCols(param); return renderMultiColumnSelect(param, relevantCols, true); } const relevantCols = getRelevantCols(param); return renderColumnSelect(param, relevantCols, true); })} {chartType.optionalParams.map((param) => { if (param === 'color_col' || param === 'label_col' || param === 'group_col') { return renderColumnSelect(param, colNames, false); } if (param === 'bins' || param === 'n') { return (
setParams({ ...params, [param]: parseInt(e.target.value) || 0 })} />
); } if (param === 'sort_desc') { return ( ); } if (param === 'agg') { return (
); } return null; })}
)}
); }