diff --git a/resources/js/components/automation-rules/create-automation-rule-dialog.tsx b/resources/js/components/automation-rules/create-automation-rule-dialog.tsx index 59cda97b..400a77d2 100644 --- a/resources/js/components/automation-rules/create-automation-rule-dialog.tsx +++ b/resources/js/components/automation-rules/create-automation-rule-dialog.tsx @@ -1,10 +1,6 @@ import { store } from '@/actions/App/Http/Controllers/Settings/AutomationRuleController'; +import { RuleBuilder } from '@/components/automation-rules/rule-builder'; import { Button } from '@/components/ui/button'; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from '@/components/ui/collapsible'; import { Dialog, DialogContent, @@ -25,10 +21,15 @@ import { import { Textarea } from '@/components/ui/textarea'; import { encrypt, importKey } from '@/lib/crypto'; import { getStoredKey } from '@/lib/key-storage'; +import { + buildJsonLogic, + createEmptyGroup, + isValidRuleStructure, + type RuleStructure, +} from '@/lib/rule-builder-utils'; import { categorySyncService } from '@/services/category-sync'; import type { Category } from '@/types/category'; import { router } from '@inertiajs/react'; -import { ChevronDown } from 'lucide-react'; import { useEffect, useState } from 'react'; interface CreateAutomationRuleDialogProps { @@ -44,12 +45,14 @@ export function CreateAutomationRuleDialog({ const [categories, setCategories] = useState([]); const [title, setTitle] = useState(''); const [priority, setPriority] = useState('0'); - const [rulesJson, setRulesJson] = useState(''); + const [ruleStructure, setRuleStructure] = useState({ + groups: [createEmptyGroup()], + groupOperator: 'and', + }); const [categoryId, setCategoryId] = useState(''); const [note, setNote] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [errors, setErrors] = useState>({}); - const [helpOpen, setHelpOpen] = useState(false); useEffect(() => { const loadCategories = async () => { @@ -59,22 +62,6 @@ export function CreateAutomationRuleDialog({ loadCategories(); }, []); - const validateJsonLogic = (json: string): boolean => { - try { - const parsed = JSON.parse(json); - if ( - typeof parsed !== 'object' || - parsed === null || - Array.isArray(parsed) - ) { - return false; - } - return Object.keys(parsed).length > 0; - } catch { - return false; - } - }; - const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setErrors({}); @@ -84,18 +71,10 @@ export function CreateAutomationRuleDialog({ return; } - if (!rulesJson.trim()) { + if (!isValidRuleStructure(ruleStructure)) { setErrors((prev) => ({ ...prev, - rules_json: 'Rules JSON is required', - })); - return; - } - - if (!validateJsonLogic(rulesJson)) { - setErrors((prev) => ({ - ...prev, - rules_json: 'Invalid JsonLogic format', + rules_json: 'At least one valid condition is required', })); return; } @@ -125,12 +104,14 @@ export function CreateAutomationRuleDialog({ noteIv = encrypted.iv; } + const jsonLogic = buildJsonLogic(ruleStructure); + router.post( store().url, { title: title.trim(), priority: parseInt(priority, 10), - rules_json: rulesJson.trim(), + rules_json: JSON.stringify(jsonLogic), action_category_id: categoryId ? parseInt(categoryId, 10) : null, @@ -142,7 +123,10 @@ export function CreateAutomationRuleDialog({ setOpen(false); setTitle(''); setPriority('0'); - setRulesJson(''); + setRuleStructure({ + groups: [createEmptyGroup()], + groupOperator: 'and', + }); setCategoryId(''); setNote(''); setErrors({}); @@ -213,74 +197,11 @@ export function CreateAutomationRuleDialog({ )} -
- -