feat(appointments): book onto a resource from its tab, drop the read-only resource timeline
"Add appointment" while a resource tab is active now opens a booking modal for that resource: its own services, then a time, then the responsible doctor. It reuses the booking engine that already existed (appointment-availability → appointment-hold → appointment-confirm) rather than adding a second path. That engine answers service-first and returns a resource assignment per slot, so the modal keeps only the slots where the engine actually offered this resource and pins that role to it on hold. Showing the other slots would let an operator pick a time that can only come back as a 409. The responsible doctor is required because every appointment has a doctor and confirm will not run without one; the resource records which device the work happens on. The read-only "منابع" timeline under the schedule is removed along with its component and hook, which had no other consumers. GET /api/v1/resources/timeline is untouched on the backend and now has no client. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,59 +0,0 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { api, type ApiResponse } from '../lib/api';
|
||||
|
||||
export type ResourceTimelineItem = {
|
||||
uuid: string;
|
||||
starts_at: number;
|
||||
ends_at: number;
|
||||
status: 'booked' | 'hold';
|
||||
segment_name: string | null;
|
||||
appointment_id: number | null;
|
||||
patient_name: string | null;
|
||||
appointment_uuid: string | null;
|
||||
appointment_status: string | null;
|
||||
};
|
||||
|
||||
export type ResourceTimelineLane = {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type_name: string;
|
||||
address_name: string | null;
|
||||
capacity: number;
|
||||
shifts: { start_minute: number; end_minute: number }[];
|
||||
/** دقیقهٔ شیفت، دقیقهٔ پرشده (ظرفیتآگاه) و باقیماندهٔ آزاد. */
|
||||
shift_minutes: number;
|
||||
busy_minutes: number;
|
||||
free_minutes: number;
|
||||
items: ResourceTimelineItem[];
|
||||
};
|
||||
|
||||
export type ResourceTimelineDay = {
|
||||
date: number;
|
||||
day_of_week: number;
|
||||
resources: ResourceTimelineLane[];
|
||||
};
|
||||
|
||||
/**
|
||||
* «کدام منبع در این روز کِی گرفته است» — ورودی نمای منبعمحورِ صفحهٔ نوبتها.
|
||||
*
|
||||
* بازهها از جدول اشغال میآیند نه از خودِ نوبت: آمادهسازی و تمیزکاری دستگاه هم
|
||||
* درونشان است و همان بازهای است که موتور جستجو اشغال میبیند.
|
||||
*/
|
||||
export function useResourceTimeline(date: string, options: { addressUuid?: string; onlyBookable?: boolean } = {}) {
|
||||
const params = new URLSearchParams({ date });
|
||||
if (options.addressUuid) params.set('address_uuid', options.addressUuid);
|
||||
// منبعی که به هیچ سرویسی وصل نیست ردیفِ همیشهخالی است؛ در تایملاین نوبتها نمیآید.
|
||||
if (options.onlyBookable) params.set('only_bookable', '1');
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ['resource-timeline', date, options.addressUuid ?? null, options.onlyBookable ?? false],
|
||||
queryFn: () => api.get<ApiResponse<ResourceTimelineDay>>(`/api/v1/resources/timeline?${params}`),
|
||||
enabled: !!date,
|
||||
});
|
||||
|
||||
return {
|
||||
day: query.data?.data,
|
||||
loading: query.isLoading,
|
||||
error: query.isError ? ((query.error as Error)?.message || 'خطای نامشخص') : null,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user