/** * Rule editor — condition builder + action selector for mail rules. */ import React, { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { Select } from '@/components/ui/Select'; import { Card } from '@/components/ui/Card'; import { EmptyState } from '@/components/ui/EmptyState'; import { useToast } from '@/components/ui/Toast'; import { ConfirmDialog } from '@/components/ui/ConfirmDialog'; import { fetchRules, createRule, deleteRule, type MailRule, type RuleCondition, type RuleAction, type RuleConditionType, type RuleActionType, } from '@/api/mail'; const conditionTypeOptions = (t: (s: string) => string) => [ { value: 'from_contains', label: t('mail.ruleCondFromContains') }, { value: 'to_contains', label: t('mail.ruleCondToContains') }, { value: 'subject_contains', label: t('mail.ruleCondSubjectContains') }, { value: 'body_contains', label: t('mail.ruleCondBodyContains') }, { value: 'has_attachment', label: t('mail.ruleCondHasAttachment') }, ]; const actionTypeOptions = (t: (s: string) => string) => [ { value: 'move_to_folder', label: t('mail.ruleActionMoveToFolder') }, { value: 'mark_as_read', label: t('mail.ruleActionMarkAsRead') }, { value: 'mark_as_flagged', label: t('mail.ruleActionMarkAsFlagged') }, { value: 'forward_to', label: t('mail.ruleActionForwardTo') }, { value: 'delete', label: t('mail.ruleActionDelete') }, ]; export function RuleEditor({ accountId }: { accountId: string }) { const { t } = useTranslation(); const toast = useToast(); const [rules, setRules] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showForm, setShowForm] = useState(false); const [name, setName] = useState(''); const [priority, setPriority] = useState(1); const [conditions, setConditions] = useState([{ type: 'from_contains', value: '' }]); const [actions, setActions] = useState([{ type: 'mark_as_read', value: '' }]); const [saving, setSaving] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const load = useCallback(() => { setLoading(true); fetchRules() .then((data) => { setRules(data); setError(null); }) .catch((err) => { setError(err instanceof Error ? err.message : String(err)); }) .finally(() => setLoading(false)); }, []); useEffect(() => { load(); }, [load]); const addCondition = () => { setConditions((prev) => [...prev, { type: 'from_contains', value: '' }]); }; const updateCondition = (index: number, field: 'type' | 'value', val: string) => { setConditions((prev) => prev.map((c, i) => (i === index ? { ...c, [field]: val } : c))); }; const removeCondition = (index: number) => { setConditions((prev) => prev.filter((_, i) => i !== index)); }; const addAction = () => { setActions((prev) => [...prev, { type: 'mark_as_read', value: '' }]); }; const updateAction = (index: number, field: 'type' | 'value', val: string) => { setActions((prev) => prev.map((a, i) => (i === index ? { ...a, [field]: val } : a))); }; const removeAction = (index: number) => { setActions((prev) => prev.filter((_, i) => i !== index)); }; const handleSave = useCallback(async () => { if (!name.trim()) return; setSaving(true); try { const rule = await createRule({ name, account_id: accountId, priority, is_active: true, conditions, actions, }); setRules((prev) => [...prev, rule].sort((a, b) => a.priority - b.priority)); toast.success(t('mail.ruleCreated')); setShowForm(false); setName(''); setPriority(1); setConditions([{ type: 'from_contains', value: '' }]); setActions([{ type: 'mark_as_read', value: '' }]); } catch (err) { toast.error(err instanceof Error ? err.message : String(err)); } finally { setSaving(false); } }, [name, accountId, priority, conditions, actions, toast, t]); const handleDelete = useCallback(async () => { if (!deleteTarget) return; try { await deleteRule(deleteTarget.id); setRules((prev) => prev.filter((r) => r.id !== deleteTarget.id)); toast.success(t('mail.ruleDeleted')); setDeleteTarget(null); } catch (err) { toast.error(err instanceof Error ? err.message : String(err)); } }, [deleteTarget, toast, t]); if (loading) { return (
); } if (error) { return (

{error}

); } return (

{t('mail.rules')}

{showForm && (
setName(e.target.value)} placeholder={t('mail.ruleName')} required /> setPriority(Number(e.target.value))} /> {/* Conditions */}

{t('mail.ruleConditions')}

{conditions.map((cond, idx) => (
updateCondition(idx, 'value', e.target.value)} placeholder={t('mail.ruleCondValue')} className="flex-1" /> )}
))}
{/* Actions */}

{t('mail.ruleActions')}

{actions.map((act, idx) => (
updateAction(idx, 'value', e.target.value)} placeholder={t('mail.ruleActionValue')} className="flex-1" /> )}
))}
)} {rules.length === 0 && !showForm ? ( ) : (
{rules.map((rule) => (
#{rule.priority}

{rule.name}

{rule.conditions.length} {t('mail.ruleConditions')} → {rule.actions.length} {t('mail.ruleActions')}

))}
)} setDeleteTarget(null)} />
); }