import React, { useState, useMemo, useCallback, useEffect } from "react"; import { X, Settings2, AlertCircle, Target, EyeOff, Search, Wand2, Play, Loader2, Sparkles, Calendar, } from "lucide-react"; import { PreprocessingConfig } from "./PreprocessingConfig"; import { ColumnList } from "./ColumnList"; import { ColumnFilters } from "./ColumnFilters"; import { AutosaveIndicator } from "./AutosaveIndicator"; import { SearchableSelect } from "../SearchableSelect"; import { useAutosave } from "../../hooks/useAutosave"; import { Dataset, Column, Feature } from "../../types/dataset"; import type { PreprocessingStep } from "../../types/dataset"; import { FeaturePicker } from "./FeaturePicker"; import { router } from "@inertiajs/react"; interface ColumnConfig { targetColumn?: string; dateColumn?: string; } interface ColumnConfigModalProps { isOpen: boolean; onClose: () => void; initialDataset: Dataset; onSave: (dataset: Dataset) => Promise; constants: any; } export function ColumnConfigModal({ isOpen, onClose, initialDataset, onSave, constants, }: ColumnConfigModalProps) { const [dataset, setDataset] = useState(initialDataset); const [activeTab, setActiveTab] = useState<"columns" | "features">( "columns" ); const [activeColumnSubTab, setActiveColumnSubTab] = useState< "target" | "date" >("target"); const [isApplying, setIsApplying] = useState(false); const [config, setConfig] = useState({ targetColumn: dataset.target, dateColumn: dataset.date_column, }); const [selectedColumn, setSelectedColumn] = useState(null); const [searchQuery, setSearchQuery] = useState(""); const [activeFilters, setActiveFilters] = useState<{ view: "all" | "training" | "hidden" | "preprocessed" | "nulls"; types: string[]; }>({ view: "all", types: [], }); const [needsRefresh, setNeedsRefresh] = useState( initialDataset.needs_refresh || false ); const handleSave = useCallback( async (data: Dataset) => { await onSave(data); }, [onSave] ); const { saving, saved, error } = useAutosave(dataset, handleSave, 2000); const colHasPreprocessingSteps = (col: Column) => { return ( col.preprocessing_steps?.training != null && col.preprocessing_steps?.training?.method !== "none" ); }; const filteredColumns = useMemo(() => { return dataset.columns.filter((column) => { const matchesSearch = column.name .toLowerCase() .includes(searchQuery.toLowerCase()); const matchesType = activeFilters.types.length === 0 || activeFilters.types.includes(column.datatype); const matchesView = (() => { switch (activeFilters.view) { case "training": return !column.hidden && !column.drop_if_null; case "hidden": return column.hidden; case "preprocessed": return colHasPreprocessingSteps(column); case "nulls": return (column.statistics?.processed?.null_count || 0) > 0; default: return true; } })(); return matchesSearch && matchesType && matchesView; }); }, [dataset.columns, searchQuery, activeFilters]); const columnStats = useMemo( () => ({ total: dataset.columns.length, filtered: filteredColumns.length, training: dataset.columns.filter((c) => !c.hidden && !c.drop_if_null) .length, hidden: dataset.columns.filter((c) => c.hidden).length, withPreprocessing: dataset.columns.filter(colHasPreprocessingSteps) .length, withNulls: dataset.columns.filter( (c) => (c.statistics?.processed?.null_count || 0) > 0 ).length, }), [dataset.columns, filteredColumns] ); const columnTypes = useMemo( () => Array.from(new Set(dataset.columns.map((c) => c.datatype))), [dataset.columns] ); const dateColumnOptions = useMemo(() => { return dataset.columns .filter((column) => column.datatype === "datetime") .map((column) => ({ value: column.name, label: column.name, })); }, [dataset.columns]); const handleColumnSelect = (columnName: string) => { setSelectedColumn(columnName); }; const toggleHiddenColumn = (columnName: string) => { const updatedColumns = dataset.columns.map((c) => ({ ...c, hidden: c.name === columnName ? !c.hidden : c.hidden, })); setDataset({ ...dataset, columns: updatedColumns, }); setNeedsRefresh(true); }; const setTargetColumn = (columnName: string) => { const name = String(columnName); setConfig({ ...config, targetColumn: columnName }); const updatedColumns = dataset.columns.map((c) => ({ ...c, is_target: c.name === name, })); setDataset({ ...dataset, columns: updatedColumns, }); setNeedsRefresh(true); }; const setDateColumn = (columnName: string) => { const name = String(columnName); setConfig((prev) => ({ ...prev, dateColumn: columnName })); const updatedColumns = dataset.columns.map((c) => ({ ...c, is_date_column: c.name === name, })); setDataset({ ...dataset, columns: updatedColumns, }); setNeedsRefresh(true); }; const setColumnType = (columnName: string, datatype: string) => { const updatedColumns = dataset.columns.map((c) => ({ ...c, datatype: c.name === columnName ? datatype : c.datatype, })); setDataset({ ...dataset, columns: updatedColumns, }); setNeedsRefresh(true); }; const handlePreprocessingUpdate = ( columnName: string, training: PreprocessingStep, inference: PreprocessingStep | undefined, useDistinctInference: boolean ) => { const column = dataset.columns.find((c) => c.name === columnName); if (!column) return; const updatedColumns = dataset.columns.map((c) => { if (c.name !== columnName) return c; return { ...c, preprocessing_steps: { training, ...(useDistinctInference && inference ? { inference } : {}), }, }; }); setDataset({ ...dataset, columns: updatedColumns, }); setNeedsRefresh(true); }; const handleFeaturesChange = (newFeatures: Feature[]) => { const existingFeatures = dataset.features || []; const removedFeatures = existingFeatures .filter( (existing) => !newFeatures.find((t) => t.name === existing.name) ) .map((feature) => ({ ...feature, _destroy: true })); const featuresWithDatasetId = [ ...newFeatures, ...removedFeatures, ].map((feature, index) => ({ ...feature, dataset_id: dataset.id, feature_position: index, })); setDataset((prevDataset) => ({ ...prevDataset, features: featuresWithDatasetId, })); setNeedsRefresh(true); }; const handleApplyChanges = async () => { setIsApplying(true); try { await onSave(dataset); router.post( `/easy_ml/datasets/${dataset.id}/refresh`, {}, { onSuccess: () => { setIsApplying(false); }, onError: () => { console.error("Error refreshing dataset"); setIsApplying(false); }, } ); } catch (error) { console.error("Error refreshing dataset:", error); setIsApplying(false); } }; if (!isOpen) return null; const selectedColumnData = selectedColumn ? dataset.columns.find((c) => c.name === selectedColumn) : null; return (

Column Configuration

setSearchQuery(e.target.value)} className="pl-9 pr-4 py-2 w-64 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500" />
{needsRefresh && (
)}
{activeTab === "columns" ? (
{activeColumnSubTab === "target" ? (
setTargetColumn(value)} options={dataset.columns.map((column) => ({ value: column.name, label: column.name, }))} placeholder="Select target column..." />
) : (
{dateColumnOptions.length > 0 ? ( ) : (
No date columns available
)}
)}
{selectedColumnData ? ( handlePreprocessingUpdate( selectedColumnData.name, training, inference, useDistinctInference ) } /> ) : (
Select a column to configure preprocessing
)}
{dataset.columns.filter((c) => !c.hidden).length} columns selected for training
) : (
)}
); }