import type { CalculatedField } from '@smallwebco/tinypivot-core' import { validateSimpleFormula } from '@smallwebco/tinypivot-core' /** * Calculated Field Modal for React * UI for creating custom calculated fields with formulas */ import React, { useCallback, useEffect, useMemo, useState } from 'react' import { createPortal } from 'react-dom' interface CalculatedFieldModalProps { show: boolean availableFields: string[] existingField?: CalculatedField | null onClose: () => void onSave: (field: CalculatedField) => void theme?: string } export function CalculatedFieldModal({ show, availableFields, existingField, onClose, onSave, theme, }: CalculatedFieldModalProps) { // Form state const [name, setName] = useState('') const [formula, setFormula] = useState('') const [formatAs, setFormatAs] = useState<'number' | 'percent' | 'currency'>('number') const [decimals, setDecimals] = useState(2) const [error, setError] = useState(null) // Reset form when modal opens useEffect(() => { if (show) { if (existingField) { setName(existingField.name) setFormula(existingField.formula) setFormatAs(existingField.formatAs || 'number') setDecimals(existingField.decimals ?? 2) } else { setName('') setFormula('') setFormatAs('number') setDecimals(2) } setError(null) } }, [show, existingField]) // Validate formula on change const validationError = useMemo(() => { if (!formula.trim()) return null return validateSimpleFormula(formula, availableFields) }, [formula, availableFields]) // Insert field into formula const insertField = useCallback((field: string) => { setFormula((prev) => { if (prev.trim() && !prev.endsWith(' ')) { return `${prev} ${field}` } return prev + field }) }, []) // Insert operator into formula const insertOperator = useCallback((op: string) => { setFormula((prev) => { if (prev.trim() && !prev.endsWith(' ')) { return `${prev} ${op} ` } return `${prev + op} ` }) }, []) // Save calculated field const handleSave = useCallback(() => { if (!name.trim()) { setError('Name is required') return } const validationResult = validateSimpleFormula(formula, availableFields) if (validationResult) { setError(validationResult) return } const field: CalculatedField = { id: existingField?.id || `calc_${Date.now()}`, name: name.trim(), formula: formula.trim(), formatAs, decimals, } onSave(field) onClose() }, [name, formula, formatAs, decimals, existingField, availableFields, onSave, onClose]) // Handle overlay click const handleOverlayClick = useCallback((e: React.MouseEvent) => { if (e.target === e.currentTarget) { onClose() } }, [onClose]) if (!show) return null const modalContent = (

{existingField ? 'Edit' : 'Create'} {' '} Calculated Field

{/* Name */}
setName(e.target.value)} />
{/* Formula */}