import React, { useEffect, useState } from 'react'; import PriceInput from '../ui/PriceInput'; import SearchableSelect from '../ui/SearchableSelect'; import Switch from '../ui/Switch'; import { formatRial, rialToToman, tomanToRial } from '../../lib/utils'; import type { ClinicResource, ResourceServiceOffering, ServiceItemOption } from '../../types'; type Line = { service_uuid: string; service_name: string; duration_minutes: string; /** مقدارِ داخل فیلد به **تومان** است؛ تبدیل به ریال فقط موقع ذخیره انجام می‌شود. */ price_tomans: 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), // ورودی به تومان است تا با placeholder و بقیهٔ پنل یکی باشد؛ پیش از این // برچسب «ریال» بود و placeholder تومان — یعنی ده برابر اختلاف در یک فیلد. price_tomans: o.price_rials === null ? '' : String(rialToToman(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_tomans: v === 0 ? '' : String(v) })} 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, // سرویسِ تازه با پیش‌فرض خودش پر می‌شود تا کاربر عددی جلوی چشمش // باشد و لازم نباشد از placeholder رونویسی کند. پاک‌کردنش همان // «ارث از سرویس» را برمی‌گرداند؛ ردیف‌های موجود دست نمی‌خورند. duration_minutes: picked.duration_minutes == null ? '' : String(picked.duration_minutes), price_tomans: picked.price_rials == null ? '' : String(rialToToman(picked.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 && ( )}
); }