feat(appointments): resource tabs follow their supervising doctor
Resource tabs now sit under the selected doctor and list only the resources that doctor supervises, so moving between a doctor's own appointments and the devices under them is one row of tabs rather than a flat list of everything. The booking modal reads the doctor from the resource's supervisor instead of asking again. The doctor↔resource relation is defined once, on the resource, and repeating the question here would have made a second source of truth. A resource whose supervisor was removed is blocked with a message pointing at the fix rather than a silently disabled button. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -47,9 +47,12 @@ export default function ResourceBookingModal({ resource, onClose, onBooked }: {
|
||||
const [serviceUuid, setServiceUuid] = useState('');
|
||||
const [days, setDays] = useState('7');
|
||||
const [pickedSlot, setPickedSlot] = useState<AvailableSlot | null>(null);
|
||||
const [doctorUuid, setDoctorUuid] = useState('');
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
// پزشکِ نوبت از ناظرِ خودِ منبع میآید، نه از یک انتخابگرِ دیگر: ارتباط پزشک و منبع
|
||||
// یک جا تعریف شده (فرم منبع) و تکرارش اینجا یعنی دو منبعِ حقیقت.
|
||||
const doctorUuid = resource.supervisor?.uuid ?? '';
|
||||
|
||||
// سرویسهای همین منبع، نه کل کاتالوگ: منبعی که سرویسی را ارائه نمیدهد نباید
|
||||
// در فهرست بیاید — سرور هم همان را با ۴۲۲ رد میکند.
|
||||
const offeringsQuery = useQuery<ApiResponse<ResourceServiceOffering[]>>({
|
||||
@@ -58,13 +61,6 @@ export default function ResourceBookingModal({ resource, onClose, onBooked }: {
|
||||
});
|
||||
const offerings = (offeringsQuery.data?.data ?? []).filter((o) => o.active);
|
||||
|
||||
const doctorsQuery = useQuery<ApiResponse<{ data: { uuid: string; name: string }[] }>>({
|
||||
queryKey: ['booking-doctors'],
|
||||
queryFn: () => api.get('/api/v1/my/clinic-doctors'),
|
||||
staleTime: 60_000,
|
||||
});
|
||||
const doctors = doctorsQuery.data?.data?.data ?? [];
|
||||
|
||||
const range = useMemo(() => {
|
||||
const from = Math.floor(Date.now() / 1000);
|
||||
return { from, to: from + Number(days) * DAY };
|
||||
@@ -82,6 +78,8 @@ export default function ResourceBookingModal({ resource, onClose, onBooked }: {
|
||||
|
||||
const reasonText = result?.reason ? REASON_LABELS[result.reason] ?? result.reason : null;
|
||||
const canSubmit = !!serviceUuid && !!pickedSlot && !!doctorUuid && !submitting;
|
||||
// ناظر در API الزامی است؛ ردیفهای قدیمیای که پزشکشان حذف شده `supervisor: null` دارند.
|
||||
const missingSupervisor = !resource.supervisor;
|
||||
|
||||
const submit = async () => {
|
||||
if (!pickedSlot) return;
|
||||
@@ -128,6 +126,12 @@ export default function ResourceBookingModal({ resource, onClose, onBooked }: {
|
||||
</>
|
||||
}
|
||||
>
|
||||
{missingSupervisor && (
|
||||
<p className="field-err" role="alert" style={{ marginTop: 0 }}>
|
||||
این منبع پزشک ناظر ندارد و نوبتی برایش ثبت نمیشود — از «منابع» ناظرش را تعیین کنید.
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="field-block" style={{ marginBottom: 14 }}>
|
||||
<label htmlFor="rb-service">سرویسهای این منبع <span className="req">*</span></label>
|
||||
{offeringsQuery.isLoading ? (
|
||||
@@ -206,22 +210,14 @@ export default function ResourceBookingModal({ resource, onClose, onBooked }: {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pickedSlot && (
|
||||
<div className="field-block">
|
||||
<label htmlFor="rb-doctor">پزشک مسئول <span className="req">*</span></label>
|
||||
<SearchableSelect
|
||||
inputId="rb-doctor"
|
||||
options={doctors.map((d) => ({ value: d.uuid, label: d.name }))}
|
||||
value={doctorUuid || null}
|
||||
onChange={(v) => setDoctorUuid(v ? String(v) : '')}
|
||||
placeholder="پزشک انجامدهنده"
|
||||
height={40}
|
||||
/>
|
||||
<p className="field-hint">
|
||||
هر نوبت پزشک مسئول دارد؛ منبع میگوید کار روی چه دستگاهی انجام میشود.
|
||||
</p>
|
||||
<div className="toggle-row">
|
||||
<div>
|
||||
<div className="tr-title">پزشک ناظر: {resource.supervisor?.name ?? '—'}</div>
|
||||
<div className="tr-desc">
|
||||
نوبت به نام همین پزشک ثبت میشود. برای تغییرش، ناظرِ منبع را در «منابع» ویرایش کنید.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user