import InputError from '@/components/input-error'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { type Condition, type ConditionGroup, createEmptyCondition, createEmptyGroup, FIELD_CONFIG, OPERATOR_LABELS, type RuleStructure, } from '@/lib/rule-builder-utils'; import { __ } from '@/utils/i18n'; import { ChevronDown, Plus, Trash2, X } from 'lucide-react'; import { useEffect, useState } from 'react'; interface RuleBuilderProps { value: RuleStructure; onChange: (value: RuleStructure) => void; error?: string; } export function RuleBuilder({ value, onChange, error }: RuleBuilderProps) { const [structure, setStructure] = useState(value); useEffect(() => { setStructure(value); }, [value]); const handleStructureChange = (newStructure: RuleStructure) => { setStructure(newStructure); onChange(newStructure); }; const addGroup = () => { handleStructureChange({ ...structure, groups: [...structure.groups, createEmptyGroup()], }); }; const removeGroup = (groupId: string) => { if (structure.groups.length === 1) { return; } handleStructureChange({ ...structure, groups: structure.groups.filter((g) => g.id !== groupId), }); }; const updateGroup = (groupId: string, updatedGroup: ConditionGroup) => { handleStructureChange({ ...structure, groups: structure.groups.map((g) => g.id === groupId ? updatedGroup : g, ), }); }; const toggleGroupOperator = () => { handleStructureChange({ ...structure, groupOperator: structure.groupOperator === 'and' ? 'or' : 'and', }); }; return (
{structure.groups.length > 1 && ( )}
{structure.groups.map((group) => (
{group.conditions.length > 1 && ( <> )}
{structure.groups.length > 1 && ( )}
{group.conditions.map((condition) => (
{ updateGroup(group.id, { ...group, conditions: group.conditions.map( (c) => c.id === condition.id ? updatedCondition : c, ), }); }} onRemove={() => { if ( group.conditions.length === 1 ) { return; } updateGroup(group.id, { ...group, conditions: group.conditions.filter( (c) => c.id !== condition.id, ), }); }} canRemove={ group.conditions.length > 1 } />
))}
))}
); } interface ConditionRowProps { condition: Condition; onChange: (condition: Condition) => void; onRemove: () => void; canRemove: boolean; } function ConditionRow({ condition, onChange, onRemove, canRemove, }: ConditionRowProps) { const fieldConfig = FIELD_CONFIG[condition.field]; const availableOperators = fieldConfig?.operators || []; const handleFieldChange = (field: string) => { const newFieldConfig = FIELD_CONFIG[field]; const newOperator = newFieldConfig.operators[0]; onChange({ ...condition, field, operator: newOperator, value: '', }); }; const handleOperatorChange = (operator: string) => { onChange({ ...condition, operator: operator as Condition['operator'], value: operator === 'is_empty' || operator === 'is_not_empty' ? '' : condition.value, }); }; const showValueInput = condition.operator !== 'is_empty' && condition.operator !== 'is_not_empty'; const inputType = fieldConfig?.type === 'number' ? 'number' : 'text'; const showAmountHint = showValueInput && inputType === 'number'; return (
{showValueInput && ( onChange({ ...condition, value: e.target.value }) } placeholder={__('Value')} className="w-full sm:flex-1" step={inputType === 'number' ? 'any' : undefined} /> )} {!showValueInput && (
)}
{showAmountHint && (

{__( 'Use a negative value for expenses (e.g. -21.99) and a positive value for income.', )}

)}
); }