import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import { api, ApiError, type ApiResponse } from '../lib/api'; import type { ServiceItem } from '../types'; /** * کاتالوگ v2 — گروه انتخاب، رابطهٔ آیتم‌ها، و اعتبارسنجی زندهٔ انتخاب. * * `validate` هم مدت و قیمت را می‌دهد هم خطاها؛ همان اندپوینتی که سایت عمومی می‌زند. * پیش‌نمایش فرم از **همان** مسیر می‌آید تا عددی که اپراتور می‌بیند با عددی که بیمار * می‌بیند یکی باشد. */ export interface ItemGroup { uuid: string; name: string; min_select: number; /** `null` یعنی نامحدود — نه صفر */ max_select: number | null; items: { uuid: string; name: string }[]; } export interface ItemRelation { related_item_uuid: string; related_item_name?: string; type: 'incompatible_with' | 'requires'; } export interface SelectionBreakdownRow { item_uuid: string; item_name: string; counted_as: 'solo' | 'additional'; minutes: number; price_rials: number; } export interface SelectionValidation { valid: boolean; errors: { code: string; message: string; group_uuid?: string; items?: string[] }[]; total_duration_minutes: number; total_price_rials: number; breakdown: SelectionBreakdownRow[]; } function fail(e: unknown, fallback: string) { toast.error(e instanceof ApiError ? e.message : fallback); } export function useServiceGroups(serviceUuid: string | undefined) { const qc = useQueryClient(); const key = ['service-groups', serviceUuid]; const query = useQuery({ queryKey: key, queryFn: () => api.get>(`/api/v1/service-item/${serviceUuid}/groups`), enabled: !!serviceUuid, }); const invalidate = () => qc.invalidateQueries({ queryKey: key }); const create = useMutation({ mutationFn: (body: { name: string; min_select: number; max_select: number | null }) => api.post>(`/api/v1/service-item/${serviceUuid}/groups`, body), onSuccess: () => { toast.success('گروه ساخته شد'); invalidate(); }, onError: (e) => fail(e, 'ساخت گروه ناموفق بود'), }); const update = useMutation({ mutationFn: ({ uuid, body }: { uuid: string; body: Record }) => api.patch>(`/api/v1/item-group/${uuid}`, body), onSuccess: () => { toast.success('گروه به‌روزرسانی شد'); invalidate(); }, onError: (e) => fail(e, 'به‌روزرسانی گروه ناموفق بود'), }); const remove = useMutation({ mutationFn: (uuid: string) => api.delete>(`/api/v1/item-group/${uuid}`), onSuccess: () => { toast.success('گروه حذف شد'); invalidate(); }, onError: (e) => fail(e, 'حذف گروه ناموفق بود'), }); /** جایگزینی کامل — قرارداد `PUT` روی زیرمجموعه. */ const setItems = useMutation({ mutationFn: ({ uuid, itemUuids }: { uuid: string; itemUuids: string[] }) => api.put>(`/api/v1/item-group/${uuid}/items`, { item_uuids: itemUuids }), onSuccess: () => { toast.success('آیتم‌های گروه ذخیره شد'); invalidate(); }, onError: (e) => fail(e, 'ذخیرهٔ آیتم‌ها ناموفق بود'), }); return { groups: query.data?.data ?? [], loading: query.isLoading, create, update, remove, setItems }; } export function useServiceRelations(serviceUuid: string | undefined) { const qc = useQueryClient(); const save = useMutation({ mutationFn: (relations: ItemRelation[]) => api.put>( `/api/v1/service-item/${serviceUuid}/relations`, { relations: relations.map((r) => ({ related_item_uuid: r.related_item_uuid, type: r.type })) }, ), onSuccess: () => { toast.success('روابط ذخیره شد'); qc.invalidateQueries({ queryKey: ['service-item', serviceUuid] }); }, // حلقهٔ پیش‌نیاز همین‌جا ۴۲۲ می‌گیرد؛ پیام سرور دقیق‌تر از هر متن عمومی است. onError: (e) => fail(e, 'ذخیرهٔ روابط ناموفق بود'), }); return { save }; } /** فهرست همهٔ آیتم‌های محیط — ورودی انتخابگرهای گروه و رابطه. */ export function useAllServiceItems() { const query = useQuery({ queryKey: ['service-items-all'], queryFn: () => api.get>('/api/v1/service-items'), staleTime: 60_000, }); return { items: query.data?.data ?? [], loading: query.isLoading }; } /** * اعتبارسنجی زندهٔ یک انتخاب. * * `enabled` روی انتخاب خالی خاموش است: فراخوانی بدون آیتم فقط نویز شبکه‌ای است و * خطای «حداقل انتخاب» را هم بی‌جا نشان می‌دهد. */ export function useSelectionPreview(itemUuids: string[], serviceUuid?: string, branchUuid?: string) { const query = useQuery({ queryKey: ['selection-preview', itemUuids, serviceUuid, branchUuid], queryFn: () => api.post>('/api/v1/service-selection/validate', { item_uuids: itemUuids, ...(serviceUuid ? { service_uuid: serviceUuid } : {}), ...(branchUuid ? { branch_uuid: branchUuid } : {}), }), enabled: itemUuids.length > 0, }); return { preview: query.data?.data, loading: query.isFetching }; }