import React, { useEffect, useState } from 'react'; import SearchableSelect from '../ui/SearchableSelect'; import Switch from '../ui/Switch'; import { formatRial } from '../../lib/utils'; import type { ClinicResource, ResourceServiceOffering, ServiceItemOption } from '../../types'; type Line = { service_uuid: string; service_name: string; duration_minutes: string; price_rials: string; active: boolean; effective_duration_minutes: number | null; effective_price_rials: number; duration_source: string | null; price_source: string; }; export type ServiceOfferingLine = { service_uuid: string; duration_minutes: string; price_rials: string; active: boolean; }; interface Props { resource: ClinicResource | null; offerings: ResourceServiceOffering[]; services: ServiceItemOption[]; saving: boolean; /** وقتی داده نشود دکمهٔ «انصراف» نمایش داده نمی‌شود — حالت تب. */ onCancel?: () => void; onSave: (lines: ServiceOfferingLine[]) => void; } /** برچسب فارسیِ سطحی که مقدار مؤثر از آن آمده. */ const SOURCE_LABELS: Record = { resource_option: 'همین منبع', resource_service: 'منبع، روی سرویس والد', branch: 'تنظیم محل نوبت‌دهی', service_default: 'پیش‌فرض سرویس', }; /** * سرویس‌هایی که یک منبع ارائه می‌دهد، با مدت و قیمت اختصاصی. * * خالی‌گذاشتن مدت یا قیمت یعنی «ارث از سطح بالاتر»، نه صفر — به همین دلیل مقدار مؤثر * به‌صورت placeholder با برچسب منبعش نشان داده می‌شود، وگرنه کاربر نمی‌فهمد خانهٔ خالی * یعنی «تنظیم نشده» یا «رایگان». * * ذخیره یک PUT است و **جایگزینی کامل**، همان قرارداد پنل مهارت‌ها. */ export default function ResourceServicesPanel({ resource, offerings, services, saving, onCancel, onSave, }: Props) { const [lines, setLines] = useState([]); useEffect(() => { if (!resource) return; setLines( offerings.map((o) => ({ service_uuid: o.service_uuid, service_name: o.service_name, duration_minutes: o.duration_minutes === null ? '' : String(o.duration_minutes), price_rials: o.price_rials === null ? '' : String(o.price_rials), active: o.active, effective_duration_minutes: o.effective_duration_minutes, effective_price_rials: o.effective_price_rials, duration_source: o.duration_source, price_source: o.price_source, })), ); }, [resource, offerings]); const chosen = new Set(lines.map((l) => l.service_uuid)); const available = services.filter((s) => !chosen.has(s.uuid)); const patch = (index: number, changes: Partial) => setLines((l) => l.map((x, i) => (i === index ? { ...x, ...changes } : x))); return (
{services.length === 0 && (

هنوز هیچ سرویسی تعریف نشده است. اول از صفحهٔ «سرویس‌ها» یکی بسازید.

)} {lines.length === 0 ? (

این منبع هیچ سرویسی ارائه نمی‌دهد.

) : (
{lines.map((line, index) => (
{line.service_name} patch(index, { active: v })} label="فعال" ariaLabel={`فعال بودن سرویس ${line.service_name}`} />
patch(index, { duration_minutes: e.target.value })} placeholder={ line.effective_duration_minutes === null ? 'تعیین نشده' : `${line.effective_duration_minutes} — ${SOURCE_LABELS[line.duration_source ?? ''] ?? '—'}` } />
patch(index, { price_rials: e.target.value })} placeholder={`${formatRial(line.effective_price_rials)} — ${SOURCE_LABELS[line.price_source] ?? '—'}`} />
))}
)} {available.length > 0 && (
({ value: s.uuid, label: s.name }))} value={null} onChange={(v) => { const picked = services.find((s) => s.uuid === String(v)); if (!picked) return; setLines((l) => [ ...l, { service_uuid: picked.uuid, service_name: picked.name, duration_minutes: '', price_rials: '', active: true, effective_duration_minutes: picked.duration_minutes ?? null, effective_price_rials: picked.price_rials ?? 0, duration_source: 'service_default', price_source: 'service_default', }, ]); }} placeholder="یک سرویس انتخاب کنید" height={38} />
)}

خالی گذاشتن مدت یا قیمت یعنی همان مقدارِ سطح بالاتر استفاده شود. ذخیره کل فهرست را جایگزین می‌کند؛ سرویسی که اینجا نباشد از این منبع برداشته می‌شود.

{onCancel && ( )}
); }