import React, { useState, useEffect } from 'react'; import { FaTable, FaArrowRight, FaTimes, FaPlus, FaFilter, FaCalculator, FaExclamationTriangle } from 'react-icons/fa'; import { CustomDatabaseIcon, CustomDocumentIcon, CustomDimensionIcon, getTableIcon, CustomProcessIcon } from './CustomIcons'; const ProcessForm = ({ isOpen, onClose, onSave, tables, existingProcess = null }) => { const [processName, setProcessName] = useState(''); const [processType, setProcessType] = useState('ETL'); const [description, setDescription] = useState(''); const [sourceTables, setSourceTables] = useState([]); const [destinationTables, setDestinationTables] = useState([]); const [mappings, setMappings] = useState([]); const [filters, setFilters] = useState([]); const [aggregations, setAggregations] = useState([]); const [processStatus, setProcessStatus] = useState('inactive'); const [activeTab, setActiveTab] = useState('basic'); const [validationError, setValidationError] = useState(''); // Initialize form with existing process data if editing useEffect(() => { if (existingProcess) { setProcessName(existingProcess.name || ''); setProcessType(existingProcess.type || 'ETL'); setDescription(existingProcess.description || ''); setSourceTables(existingProcess.source_table || []); setDestinationTables(existingProcess.destination_table || []); setProcessStatus(existingProcess.status || 'inactive'); // Initialize mappings, filters, and aggregations if they exist if (existingProcess.mappings) setMappings(existingProcess.mappings); if (existingProcess.filters) setFilters(existingProcess.filters); if (existingProcess.aggregations) setAggregations(existingProcess.aggregations); } else { // Reset form for new process setProcessName(''); setProcessType('ETL'); setDescription(''); setSourceTables([]); setDestinationTables([]); setMappings([]); setFilters([]); setAggregations([]); setProcessStatus('inactive'); } setValidationError(''); }, [existingProcess, isOpen]); const handleSave = () => { // Validate form if (!processName.trim()) { setValidationError('Please enter a process name'); return; } if (sourceTables.length === 0) { setValidationError('Please select at least one source table'); return; } if (destinationTables.length === 0) { setValidationError('Please select at least one destination table'); return; } // Create process object const processData = { name: processName, type: processType, description, source_table: sourceTables, destination_table: destinationTables, mappings, filters, aggregations, slug: existingProcess ? existingProcess.slug : `process_${Date.now()}`, status: processStatus // Use the selected status }; onSave(processData); onClose(); }; const handleAddMapping = () => { setMappings([...mappings, { source: '', target: '', type: 'direct' }]); }; const handleUpdateMapping = (index, field, value) => { const updatedMappings = [...mappings]; updatedMappings[index][field] = value; setMappings(updatedMappings); }; const handleRemoveMapping = (index) => { setMappings(mappings.filter((_, i) => i !== index)); }; const handleAddFilter = () => { setFilters([...filters, { column: '', operator: '=', value: '' }]); }; const handleUpdateFilter = (index, field, value) => { const updatedFilters = [...filters]; updatedFilters[index][field] = value; setFilters(updatedFilters); }; const handleRemoveFilter = (index) => { setFilters(filters.filter((_, i) => i !== index)); }; const handleAddAggregation = () => { setAggregations([...aggregations, { function: 'SUM', column: '', alias: '' }]); }; const handleUpdateAggregation = (index, field, value) => { const updatedAggregations = [...aggregations]; updatedAggregations[index][field] = value; setAggregations(updatedAggregations); }; const handleRemoveAggregation = (index) => { setAggregations(aggregations.filter((_, i) => i !== index)); }; // Function to check if a table is a stage table const isStageTable = (tableId) => { const table = tables.find(t => t.slug === tableId); return (table && table.type && table.type.toLowerCase() === 'stage') || (table && table.name && table.name.toLowerCase().includes('_stage')); }; // Function to check if a table is a dimension table const isDimensionTable = (tableId) => { const table = tables.find(t => t.slug === tableId); return (table && table.type && table.type.toLowerCase() === 'dimension') || (table && table.name && table.name.toLowerCase().includes('_dim') && !table.name.toLowerCase().includes('_stage') && !table.name.toLowerCase().includes('_fact')); }; // Function to check if a table is a fact table const isFactTable = (tableId) => { const table = tables.find(t => t.slug === tableId); return (table && table.type && table.type.toLowerCase() === 'fact') || (table && table.name && table.name.toLowerCase().includes('_fact')); }; // Function to check if a connection is valid based on table types const isValidConnection = (sourceId, destinationId) => { // Stage tables can connect to either Dimension or Fact tables if (isStageTable(sourceId)) { return isDimensionTable(destinationId) || isFactTable(destinationId); } // Fact tables can connect to either Dimension or Fact tables if (isFactTable(sourceId)) { return isDimensionTable(destinationId) || isFactTable(destinationId); } // Dimension to Fact table connections are not allowed if (isDimensionTable(sourceId) && isFactTable(destinationId)) { return false; } // All other connections are allowed (including Dimension to Dimension) return true; }; // Check if a destination table can be selected based on current source tables const canSelectDestination = (destinationId) => { // If no source tables are selected, any destination can be selected if (sourceTables.length === 0) { return true; } // Check if any of the selected source tables can connect to this destination return sourceTables.some(sourceId => isValidConnection(sourceId, destinationId)); }; // Check if a source table can be selected based on current destination tables const canSelectSource = (sourceId) => { // If no destination tables are selected, any source can be selected if (destinationTables.length === 0) { return true; } // Check if this source can connect to any of the selected destinations return destinationTables.some(destinationId => isValidConnection(sourceId, destinationId)); }; const handleTableSelection = (tableId, type) => { setValidationError(''); if (type === 'source') { if (sourceTables.includes(tableId)) { // Removing a source table setSourceTables(sourceTables.filter(id => id !== tableId)); } else { // Adding a source table - check if it's compatible with current destinations if (canSelectSource(tableId)) { setSourceTables([...sourceTables, tableId]); } else { setValidationError('This source table type cannot connect to the selected destination table(s). Dimension tables cannot connect to Fact tables.'); } } } else { if (destinationTables.includes(tableId)) { // Removing a destination table setDestinationTables(destinationTables.filter(id => id !== tableId)); } else { // Adding a destination table - check if it's compatible with current sources if (canSelectDestination(tableId)) { setDestinationTables([...destinationTables, tableId]); } else { setValidationError('This destination table type cannot be connected from the selected source table(s). Dimension tables cannot connect to Fact tables.'); } } } }; // Get available columns for selected tables const getAvailableColumns = (tableIds) => { let columns = []; tableIds.forEach(tableId => { const table = tables.find(t => t.slug === tableId); if (table && table.columns) { // Ensure table type is properly set based on naming convention if (table.name && table.name.toLowerCase().includes('_stage')) { table.type = 'stage'; } else if (table.name && table.name.toLowerCase().includes('_fact')) { table.type = 'fact'; } else if (table.name && table.name.toLowerCase().includes('_dim')) { table.type = 'dimension'; } else if (table.type) { // Normalize table type to lowercase for consistency table.type = table.type.toLowerCase(); } columns = [...columns, ...table.columns.map(col => ({ id: `${tableId}.${col}`, name: `${table.name}.${col}`, column: col, table: tableId }))]; } }); return columns; }; const sourceColumns = getAvailableColumns(sourceTables); const destinationColumns = getAvailableColumns(destinationTables); // Function to get table type label const getTableTypeLabel = (tableType) => { if (!tableType) return ''; switch (tableType.toLowerCase()) { case 'dimension': return 'DIM'; case 'fact': return 'FACT'; case 'stage': return 'STAGE'; default: return tableType.toUpperCase(); } }; // Function to get table type color const getTableTypeColor = (tableType) => { if (!tableType) return '#666666'; switch (tableType.toLowerCase()) { case 'dimension': return '#52c41a'; // Green case 'fact': return '#1890ff'; // Blue case 'stage': return '#fa8c16'; // Orange default: return '#666666'; } }; // Function to get the appropriate icon based on table type const getProcessTableIcon = (tableType) => { if (!tableType) return ; const type = tableType && typeof tableType === 'string' ? tableType.toLowerCase() : ''; console.log('Table type:', type); switch (type) { case 'stage': return ; case 'fact': return ; case 'dimension': return ; default: return ; } }; // Simple component to render the appropriate icon const TableIcon = ({ type }) => { if (!type) return ; const tableType = type.toLowerCase(); if (tableType === 'stage') return ; if (tableType === 'fact') return ; if (tableType === 'dimension') return ; return ; }; if (!isOpen) return null; return (

{existingProcess ? 'Edit Process' : 'Create New Process'}

{/* Validation Error Message */} {validationError && (
{validationError}
)} {/* Tabs */}
{/* Basic Info Tab */} {activeTab === 'basic' && (
setProcessName(e.target.value)} style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px' }} placeholder="Enter process name" />
{processStatus === 'active' ? 'Active processes will show animated connections in the flow diagram.' : 'Inactive processes will show gray, non-animated connections in the flow diagram.'}