import React, { useId, useMemo, useState } from 'react'; import { XMarkIcon } from '@heroicons/react/24/outline'; import ConfirmDialog from '../ui/ConfirmDialog'; import SearchableSelect from '../ui/SearchableSelect'; import PersianDateInput from '../ui/PersianDateInput'; import { useResourceExceptions } from '../../hooks/useResourceCalendar'; import { formatDate } from '../../lib/utils'; import type { ResourceException } from '../../types'; const EXCEPTION_TYPES = [ { value: 'leave', label: 'مرخصی' }, { value: 'absence', label: 'غیبت' }, { value: 'maintenance', label: 'سرویس دوره‌ای' }, { value: 'closure', label: 'تعطیلی موردی' }, ]; /** * مرخصی و سرویسِ یک منبع — فهرست استثناهای ثبت‌شده و فرم ثبت استثنای تازه. * * کارتِ مستقل است چون دو ترکیب متفاوت دارد: در صفحهٔ جزئیات منبع کنار تعطیلات رسمی * و پیش‌نمایش می‌نشیند (`ResourceExceptionsPanel`)، و در تنظیمات نوبت‌دهی یکی از دو * تبِ زیر شیفت هفتگی است. حذفِ دوباره‌نویسی، نه انتزاعِ زودرس. */ export default function ResourceExceptionsCard({ resourceUuid, canUpdate }: { resourceUuid?: string; canUpdate: boolean; }) { const { exceptions, create, remove } = useResourceExceptions(resourceUuid); const [toDelete, setToDelete] = useState(null); const [type, setType] = useState('leave'); const [startDate, setStartDate] = useState(''); const [endDate, setEndDate] = useState(''); const [reason, setReason] = useState(''); // id یکتا لازم است: این کارت در صفحهٔ منبع و صفحهٔ تنظیمات نوبت‌دهی هر دو رندر می‌شود. const uid = useId(); const toTimestamp = (value: string): number | null => { if (value === '') return null; const ms = new Date(`${value}T00:00:00`).getTime(); return Number.isNaN(ms) ? null : Math.floor(ms / 1000); }; const start = toTimestamp(startDate); const end = toTimestamp(endDate); // پایان روزِ انتخاب‌شده، نه آغازش: مرخصیِ «تا سه‌شنبه» شامل خودِ سه‌شنبه است. const endExclusive = end === null ? null : end + 86400; const invalid = start === null || endExclusive === null || endExclusive <= start; const typeLabel = useMemo( () => EXCEPTION_TYPES.find((t) => t.value === type)?.label ?? '', [type], ); return (

مرخصی و سرویس

{exceptions.length === 0 ? (

استثنایی ثبت نشده است.

) : (
{exceptions.map((e) => (
{e.type_label} {formatDate(e.starts_at)} تا {formatDate(e.ends_at)} {e.reason ? ` · ${e.reason}` : ''} {canUpdate && ( )}
))}
)} {canUpdate ? (
setType(v ? String(v) : 'leave')} placeholder="نوع استثنا" height={40} />
{/* تقویم شمسی، نه `input type=date` میلادی: اپراتور تاریخ را شمسی می‌گوید و ترجمهٔ ذهنی همان‌جایی است که استثنا یک روز جابه‌جا ثبت می‌شود. */}
) : (

برای ثبت یا حذف استثنا مجوز ویرایش تنظیمات نوبت‌دهی لازم است.

)} toDelete && remove.mutate(toDelete.uuid, { onSuccess: () => setToDelete(null) })} onCancel={() => setToDelete(null)} />
); }