refactor: remove ResourceBookingPage and related components; update references in AppointmentsPage and ScheduleSection

This commit is contained in:
hamed
2026-08-08 07:19:48 +03:30
parent b699476305
commit c452150a83
7 changed files with 10 additions and 606 deletions
+6 -126
View File
@@ -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;