refactor: remove ResourceBookingPage and related components; update references in AppointmentsPage and ScheduleSection
This commit is contained in:
@@ -1,133 +1,13 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
import { api, ApiError, type ApiResponse } from '../lib/api';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { api, type ApiResponse } from '../lib/api';
|
||||
|
||||
/**
|
||||
* جستجوی وقت، رزرو موقت و ثبت نهایی در حالت منبعمحور.
|
||||
* بخشهای زمانیِ یک نوبت.
|
||||
*
|
||||
* سه عمل جدا هستند و باید جدا بمانند: بین «دیدن وقت» و «گرفتنش» صندلی هنوز آزاد است،
|
||||
* و بین «گرفتن» و «ثبت» یک مهلت وجود دارد که اگر نگذرد، ظرفیت برای همیشه قفل میماند.
|
||||
* تا ۲۰۲۶-۰۸-۰۸ این فایل موتورِ صفحهٔ «نوبت منبعمحور» هم بود — جستجوی وقت، رزرو
|
||||
* موقت و ثبت نهایی. آن صفحه حذف شد و آن سه هوک با خودش رفتند. فقط خواندنِ
|
||||
* بخشهای نوبت ماند، چون کارت جزئیات نوبت و AppointmentSegmentsCard به آن وابستهاند.
|
||||
*/
|
||||
export interface ResourceRef {
|
||||
uuid: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type SlotAssignment = Record<string, ResourceRef[]>;
|
||||
|
||||
export interface AvailableSlot {
|
||||
start: number;
|
||||
end: number;
|
||||
assignment: SlotAssignment;
|
||||
}
|
||||
|
||||
export interface PlanSegment {
|
||||
sequence: number;
|
||||
name: string;
|
||||
offset_minutes: number;
|
||||
duration_minutes: number;
|
||||
patient_present: boolean;
|
||||
requirements: { role: string; role_name: string; count: number }[];
|
||||
}
|
||||
|
||||
export interface AvailabilityResult {
|
||||
plan: { total_minutes: number; segments: PlanSegment[] };
|
||||
slots: AvailableSlot[];
|
||||
/** خالی بودن فهرست خطا نیست؛ این میگوید چرا خالی است. */
|
||||
reason: string | null;
|
||||
}
|
||||
|
||||
export interface HoldResult {
|
||||
hold_uuid: string;
|
||||
starts_at: number;
|
||||
ends_at: number;
|
||||
expires_at: number;
|
||||
confirmed: boolean;
|
||||
assignment: SlotAssignment;
|
||||
}
|
||||
|
||||
export const REASON_LABELS: Record<string, string> = {
|
||||
no_capacity_in_range: 'در این بازه هیچ ظرفیتی نیست — بازه را بزرگتر کنید یا سرویس دیگری را امتحان کنید.',
|
||||
no_working_hours: 'در این بازه ساعت کاری تعریف نشده است.',
|
||||
no_eligible_resource: 'هیچ منبعی شرایط بخشهای این خدمت را ندارد.',
|
||||
};
|
||||
|
||||
function fail(e: unknown, fallback: string) {
|
||||
toast.error(e instanceof ApiError ? e.message : fallback);
|
||||
}
|
||||
|
||||
export function useAvailabilitySearch(
|
||||
params: { serviceUuid: string; branchUuid: string; from: number; to: number; stepMinutes?: number },
|
||||
enabled: boolean,
|
||||
) {
|
||||
const query = useQuery({
|
||||
queryKey: ['resource-availability', params],
|
||||
queryFn: () =>
|
||||
api.post<ApiResponse<AvailabilityResult>>('/api/v1/appointment-availability', {
|
||||
service_uuid: params.serviceUuid,
|
||||
branch_uuid: params.branchUuid,
|
||||
from: params.from,
|
||||
to: params.to,
|
||||
...(params.stepMinutes ? { step_minutes: params.stepMinutes } : {}),
|
||||
}),
|
||||
enabled: enabled && !!params.serviceUuid && !!params.branchUuid,
|
||||
retry: false,
|
||||
});
|
||||
|
||||
return {
|
||||
result: query.data?.data,
|
||||
loading: query.isFetching,
|
||||
error: query.error,
|
||||
refetch: query.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
export function useHold() {
|
||||
const create = useMutation({
|
||||
mutationFn: (body: {
|
||||
service_uuid: string;
|
||||
branch_uuid: string;
|
||||
start: number;
|
||||
assignment: Record<string, string[]>;
|
||||
item_uuids?: string[];
|
||||
patient_gender?: string;
|
||||
}) => api.post<ApiResponse<HoldResult>>('/api/v1/appointment-hold', body),
|
||||
// ۴۰۹ یعنی همین لحظه کس دیگری گرفت — پیام سرور دقیقاً همین را میگوید.
|
||||
onError: (e) => fail(e, 'گرفتن این زمان ناموفق بود'),
|
||||
});
|
||||
|
||||
const release = useMutation({
|
||||
mutationFn: (uuid: string) => api.delete<ApiResponse<null>>(`/api/v1/appointment-hold/${uuid}`),
|
||||
onSuccess: () => toast.success('رزرو موقت آزاد شد'),
|
||||
onError: (e) => fail(e, 'آزادسازی ناموفق بود'),
|
||||
});
|
||||
|
||||
const confirm = useMutation({
|
||||
mutationFn: (body: { hold_uuid: string; doctor_uuid: string; patient_uuid?: string }) =>
|
||||
api.post<ApiResponse<{ uuid: string }>>('/api/v1/appointment-confirm', body),
|
||||
onSuccess: () => toast.success('نوبت ثبت شد'),
|
||||
onError: (e) => fail(e, 'ثبت نهایی ناموفق بود'),
|
||||
});
|
||||
|
||||
/**
|
||||
* جابهجایی: رزرو موقتِ زمان تازه از قبل گرفته شده و اینجا فقط تأیید میشود.
|
||||
*
|
||||
* عمداً از `confirm` جداست — سرور در همین یک درخواست زمان قدیم را هم آزاد میکند، پس
|
||||
* لغو دستی پیش از رزرو یعنی پنجرهای که در آن بیمار اصلاً نوبت ندارد.
|
||||
*/
|
||||
const rebook = useMutation({
|
||||
mutationFn: ({ appointmentUuid, holdUuid }: { appointmentUuid: string; holdUuid: string }) =>
|
||||
api.post<ApiResponse<{ appointment_uuid: string; starts_at: number }>>(
|
||||
`/api/v1/appointment/${appointmentUuid}/rebook`,
|
||||
{ hold_uuid: holdUuid },
|
||||
),
|
||||
onSuccess: () => toast.success('نوبت جابهجا شد'),
|
||||
onError: (e) => fail(e, 'جابهجایی نوبت ناموفق بود'),
|
||||
});
|
||||
|
||||
return { create, release, confirm, rebook };
|
||||
}
|
||||
|
||||
export interface AppointmentSegmentRow {
|
||||
sequence: number;
|
||||
name: string;
|
||||
|
||||
Reference in New Issue
Block a user