import { useMemo } from 'react'; import { Link } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { CubeIcon, UserGroupIcon } from '@heroicons/react/24/outline'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; import { useAuthStore } from '../stores/authStore'; import { usePermissions } from '../hooks/usePermissions'; import { useUrlState } from '../hooks/useUrlState'; import { useResources } from '../hooks/useResources'; import SettingsLayout from '../components/layout/SettingsLayout'; import { ScheduleSection } from '../components/schedule/ScheduleSection'; import ResourceWorkingHoursPanel from '../components/resources/ResourceWorkingHoursPanel'; import ResourceExceptionsPanel from '../components/resources/ResourceExceptionsPanel'; import FreeVisitPrice from '../components/FreeVisitPrice'; import BackButton from '../components/ui/BackButton'; import type { ClinicDoctorItem } from '../components/ClinicDoctorsManager'; const SCOPES = [ { id: 'doctors', label: 'پزشکان' }, { id: 'resources', label: 'منابع' }, ] as const; type Scope = typeof SCOPES[number]['id']; /** * تنظیمات نوبت‌دهی کلینیک — یک تب به ازای هر پزشک، و یک تب به ازای هر منبع. * * منبع در مدل Resource-First واحدِ ظرفیت است و ساعت کاری و تعطیلات خودش را دارد، پس * دقیقاً همان‌جایی مدیریت می‌شود که برنامهٔ پزشک — نه در یک صفحهٔ جدا. تب پزشک همان * `ScheduleSection` پنل پزشک مستقل است و تب منبع همان پنل‌های صفحهٔ منبع؛ هیچ‌کدام * نسخهٔ دومی ندارند. */ function ClinicAppointmentSettingsContent() { const { dbUuid, context, availableContexts } = useAuthStore(); const { can } = usePermissions(); // منشیِ بدون مجوزِ ویرایشِ تنظیمات نوبت‌دهی، فقط مشاهده می‌کند. const apptReadOnly = !can('appointment_settings', 'update'); // انتخاب‌ها در URL می‌نشینند تا «بازگشت» و رفرش همان تب را برگردانند. const [urlState, setUrlState] = useUrlState({ scope: 'doctors', doctor: '', resource: '' }); const scope = (SCOPES.some((s) => s.id === urlState.scope) ? urlState.scope : 'doctors') as Scope; // کاربری که هم پزشک است هم مالک کلینیک، dbUuid‌اش ممکن است uuid پزشک باشد. const clinicUuid = useMemo(() => { if (context?.type === 'clinic') return dbUuid; return availableContexts.find(c => c.type === 'clinic')?.db_uuid ?? null; }, [context, dbUuid, availableContexts]); const doctorsQ = useQuery({ queryKey: ['clinic-doctors', clinicUuid], queryFn: () => api.get>(`/api/v1/clinic/doctor-list/${clinicUuid}`), enabled: !!clinicUuid, }); const doctorList: ClinicDoctorItem[] = useMemo(() => { const raw = doctorsQ.data?.data; return (raw as any)?.data ?? raw ?? []; }, [doctorsQ.data]); const { resources, loading: resourcesLoading } = useResources({ active: '1' }); const selectedDoctor = doctorList.find(d => d.uuid === urlState.doctor) ?? doctorList[0] ?? null; const selectedResource = resources.find(r => r.uuid === urlState.resource) ?? resources[0] ?? null; if (!clinicUuid) { return (

{dbUuid ? 'کلینیکی برای این حساب کاربری یافت نشد' : 'در حال بارگذاری اطلاعات کلینیک...'}

); } return (

مدیریت نوبت دهی

{scope === 'doctors' ? (selectedDoctor ? `تنظیمات نوبت‌دهی ${selectedDoctor.name}` : 'تنظیمات نوبت‌دهی پزشکان کلینیک') : (selectedResource ? `تنظیمات نوبت‌دهی ${selectedResource.name}` : 'تنظیمات نوبت‌دهی منابع کلینیک')}
{SCOPES.map((s) => ( ))}
{scope === 'doctors' ? ( setUrlState({ doctor: uuid })} /> ) : ( setUrlState({ resource: uuid })} /> )}
); } function TabBar({ items, selected, onSelect }: { items: T[]; selected: T | null; onSelect: (uuid: string) => void; }) { return (
{items.map((item) => ( ))}
); } function SelectedHeader({ icon, label }: { icon: React.ReactNode; label: string }) { return (
{icon} {label}
); } function DoctorsScope({ loading, doctors, selected, clinicUuid, readOnly, onSelect }: { loading: boolean; doctors: ClinicDoctorItem[]; selected: ClinicDoctorItem | null; clinicUuid: string; readOnly: boolean; onSelect: (uuid: string) => void; }) { if (loading) return

در حال بارگذاری پزشکان...

; if (doctors.length === 0) { return (

هیچ پزشکی به این کلینیک متصل نیست

مدیریت پزشکان کلینیک
); } return ( <> {/* key اجباری است: بدون آن state ویرایشِ برنامه بین پزشکان نشت می‌کند */} {selected && (
} label={selected.name} />
)} ); } function ResourcesScope({ loading, resources, selected, canUpdate, onSelect }: { loading: boolean; resources: { uuid: string; name: string; type_name: string }[]; selected: { uuid: string; name: string; type_name: string } | null; canUpdate: boolean; onSelect: (uuid: string) => void; }) { if (loading) return

در حال بارگذاری منابع...

; if (resources.length === 0) { return (

هنوز منبعی تعریف نشده است

تنظیمات ← منابع
); } return ( <> {/* همان دلیل تب پزشک: بدون key، شیفتِ نیمه‌ویرایش‌شده به منبع بعدی می‌چسبد. */} {selected && (
} label={`${selected.name} · ${selected.type_name}`} />
)} ); } export default function ClinicAppointmentSettingsPage() { return ( ); }