import React, { useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import PageHeader from '../components/ui/PageHeader'; import SearchableSelect from '../components/ui/SearchableSelect'; import PolicyConditionBuilder from '../components/PolicyConditionBuilder'; import { usePolicyMutations, usePolicySchema, usePolicyTemplates, } from '../hooks/usePolicies'; import type { PolicyCategory, PolicyClause, PolicyEffect } from '../types'; type Mode = 'template' | 'advanced'; /** * ساخت قانون — با الگو (راه ۹۰٪ کاربران) یا دستی. * * قانون تازه پیش‌نویس ذخیره می‌شود و کاربر مستقیم به صفحهٔ آزمایش می‌رود: فعال‌سازی * بدون دیدن نتیجه ممکن نیست، پس بردنش به همان‌جا کوتاه‌ترین مسیر درست است. */ export default function PolicyFormPage() { const navigate = useNavigate(); const { schema } = usePolicySchema(); const { templates } = usePolicyTemplates(); const { create } = usePolicyMutations(); const [mode, setMode] = useState('template'); const [name, setName] = useState(''); const [priority, setPriority] = useState(0); const [templateKey, setTemplateKey] = useState(''); const [values, setValues] = useState>({}); const [category, setCategory] = useState('timing'); const [match, setMatch] = useState<'all' | 'any'>('all'); const [clauses, setClauses] = useState([]); const [effects, setEffects] = useState([]); const template = templates.find((t) => t.key === templateKey); const categorySchema = schema?.[category]; const categoryOptions = useMemo( () => Object.entries(schema ?? {}).map(([key, v]) => ({ value: key, label: v.label })), [schema], ); const submit = async () => { const body = mode === 'template' ? { name, priority, template: templateKey, values } : { name, priority, category, condition: clauses.length ? { match, conditions: clauses } : {}, effects, }; const created = await create.mutateAsync(body); navigate(`/admin/policies/${created.data.uuid}/simulate`); }; const canSubmit = name.trim() !== '' && (mode === 'template' ? templateKey !== '' : effects.length > 0); return (
setName(e.target.value)} placeholder="مثلاً: حداقل ۲۱ روز فاصله بین جلسات لیزر" /> این نام در پیام خطا به بیمار نشان داده می‌شود؛ قابل فهم بنویسید.
setPriority(Number(e.target.value))} /> در تناقض، عدد بزرگ‌تر برنده است.
{mode === 'template' ? ( <>
{ setTemplateKey(String(v ?? '')); setValues({}); }} options={templates.map((t) => ({ value: t.key, label: t.title }))} placeholder="یک الگو انتخاب کنید" /> {template && ( {template.description} )}
{template?.inputs.map((input) => (
setValues({ ...values, [input.key]: e.target.value })} />
))} ) : ( <>
{ setCategory((v as PolicyCategory) ?? 'timing'); // فیلدها و اثرهای مجاز per دسته فرق دارند؛ نگه‌داشتنشان یعنی ۴۲۲. setClauses([]); setEffects([]); }} options={categoryOptions} />
{categorySchema && ( <>

شرط

{ setMatch(m); setClauses(c); }} />

اثر

{categorySchema.effects.map((meta) => { const current = effects.find((e) => e.type === meta.type); return (
{current && meta.value_type !== 'none' && ( setEffects( effects.map((x) => x.type === meta.type ? { ...x, value: meta.value_type === 'int' ? Number(e.target.value) : e.target.value, } : x, ), ) } /> )} {current && meta.type === 'forbid' && ( setEffects( effects.map((x) => x.type === 'forbid' ? { ...x, reason: e.target.value } : x, ), ) } /> )} ترکیب: {meta.combination}
); })}
)} )}
); }