import React from 'react'; import { TrashIcon, PlusIcon } from '@heroicons/react/24/outline'; import SearchableSelect from './ui/SearchableSelect'; import type { PolicyCategorySchema, PolicyClause } from '../types'; const OPERATOR_LABELS: Record = { equals: 'برابر است با', not_equals: 'برابر نیست با', greater_than: 'بیشتر از', less_than: 'کمتر از', in: 'یکی از', contains: 'شامل', }; interface Props { schema: PolicyCategorySchema; match: 'all' | 'any'; clauses: PolicyClause[]; onChange: (match: 'all' | 'any', clauses: PolicyClause[]) => void; } /** * شرط‌ساز — کاملاً از `policy-schema` ساخته می‌شود. * * فیلدها، عملگرهای **مجاز برای همان فیلد**، و نوع ورودی مقدار، همه از سرور می‌آیند. * اگر اینجا فهرست دستی می‌نوشتیم، هر فیلد تازه در بک‌اند نیاز به تغییر فرانت داشت و * بعد از دو ماه دو فهرست ناهمگام می‌داشتیم. */ export default function PolicyConditionBuilder({ schema, match, clauses, onChange }: Props) { const metaOf = (field: string) => schema.field_meta.find((m) => m.key === field); const update = (index: number, patch: Partial) => { onChange( match, clauses.map((c, i) => (i === index ? { ...c, ...patch } : c)), ); }; const addClause = () => { const first = schema.field_meta[0]; if (!first) return; onChange(match, [...clauses, { field: first.key, operator: first.operators[0], value: '' }]); }; return (
شرط‌ها با هم:
onChange((v as 'all' | 'any') ?? 'all', clauses)} options={[ { value: 'all', label: 'همه برقرار باشند' }, { value: 'any', label: 'یکی برقرار باشد' }, ]} />
{clauses.length === 0 && (

بدون شرط، این قانون روی همهٔ نوبت‌های دامنه‌اش اعمال می‌شود.

)} {clauses.map((clause, index) => { const meta = metaOf(clause.field); return (
{ const next = metaOf(String(v ?? '')); update(index, { field: String(v ?? ''), // عملگر قبلی ممکن است برای فیلد تازه بی‌معنا باشد؛ به اولین // عملگرِ مجاز برمی‌گردد تا کاربر ۴۲۲ نگیرد. operator: next?.operators[0] ?? 'equals', value: '', }); }} options={schema.field_meta.map((m) => ({ value: m.key, label: m.label }))} />
update(index, { operator: String(v ?? 'equals') })} options={(meta?.operators ?? []).map((op) => ({ value: op, label: OPERATOR_LABELS[op] ?? op, }))} />
{meta?.type === 'enum' ? ( update(index, { value: String(v ?? '') })} options={(meta.values ?? []).map((val) => ({ value: val, label: val === 'male' ? 'آقا' : val === 'female' ? 'خانم' : val, }))} /> ) : meta?.type === 'bool' ? ( update(index, { value: v === 'true' })} options={[ { value: 'true', label: 'بله' }, { value: 'false', label: 'خیر' }, ]} /> ) : ( update(index, { value: meta?.type === 'int' ? Number(e.target.value) : e.target.value, }) } placeholder="مقدار" /> )}
); })}
); }