Files
clinicpro/assets/admin/hooks/useNationalHolidays.ts
T
hamedandClaude Opus 5 bd4347f9c5 feat(admin): a page for the official holiday calendar
The three admin endpoints shipped without anywhere to call them from, so the
person who is supposed to maintain the national calendar could only do it
with curl or a console command. That is not "the director can register the
year's holidays".

/admin/national-holidays is ROLE_ADMIN only and sits under the System group
in the admin nav. The year lives in the query string, so back and refresh
return to the year being edited.

Editing takes only the title: `date` is the unique key, so moving a holiday
is really deleting one and creating another, and the form says so rather than
silently creating a duplicate. The date field is the shared Jalali picker,
which speaks Gregorian, so the page converts before POSTing the jalali_date
the API expects — and shows the converted value under the field so the user
can see what will be stored.

Deleting warns that the day leaves every clinic's calendar, because it does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 17:14:23 +03:30

62 lines
2.5 KiB
TypeScript

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
import { api, ApiError, type ApiResponse } from '../lib/api';
import type { NationalHoliday } from '../types';
/**
* تقویم تعطیلات رسمی از دید **مدیر سیستم** — تنها جایی که ساخته و حذف می‌شود.
*
* ⚠️ با `useHolidays` اشتباه نشود: آن مالِ یک محیط است و فقط «این روز برای ما باز
* است» را ثبت می‌کند (`holiday-overrides`). خودِ تعطیلی سراسری است و محیط ندارد.
*/
export function useNationalHolidays(year: number) {
const qc = useQueryClient();
const key = ['national-holidays', year];
const invalidate = () => {
qc.invalidateQueries({ queryKey: key });
// کارت تعطیلات در تب پزشک و منبع هم از همین سال می‌خواند.
qc.invalidateQueries({ queryKey: ['national-holidays'] });
};
const fail = (e: unknown, fallback: string) =>
toast.error(e instanceof ApiError ? e.message : fallback);
const query = useQuery({
queryKey: key,
queryFn: () =>
api.get<ApiResponse<{ year: number; holidays: NationalHoliday[] }>>(
`/api/v1/national-holidays?year=${year}`,
),
});
const create = useMutation({
mutationFn: (d: { jalali_date: string; title: string }) =>
api.post<ApiResponse<NationalHoliday>>('/api/v1/admin/national-holidays', d),
// سرور upsert می‌کند: همان روز دوباره یعنی تغییر عنوان، نه ردیف دوم.
onSuccess: () => { toast.success('تعطیلی ثبت شد'); invalidate(); },
onError: (e) => fail(e, 'ثبت تعطیلی ناموفق بود'),
});
const update = useMutation({
mutationFn: ({ uuid, title }: { uuid: string; title: string }) =>
api.patch<ApiResponse<NationalHoliday>>(`/api/v1/admin/national-holiday/${uuid}`, { title }),
onSuccess: () => { toast.success('عنوان به‌روزرسانی شد'); invalidate(); },
onError: (e) => fail(e, 'به‌روزرسانی ناموفق بود'),
});
const remove = useMutation({
mutationFn: (uuid: string) => api.delete<ApiResponse<null>>(`/api/v1/admin/national-holiday/${uuid}`),
onSuccess: () => { toast.success('تعطیلی حذف شد'); invalidate(); },
onError: (e) => fail(e, 'حذف تعطیلی ناموفق بود'),
});
return {
holidays: query.data?.data?.holidays ?? [],
loading: query.isLoading,
create,
update,
remove,
};
}