Files
clinicpro/assets/admin/components/holidays/NationalHolidaysCard.tsx
T
hamed 6876135a53 feat: add BlogBodySanitizer for HTML sanitization on article save
- Implemented BlogBodySanitizer to clean HTML content before saving articles, ensuring security against XSS attacks.
- Added tests for BlogBodySanitizer to verify that unsafe tags and attributes are stripped from the content.
- Introduced ApiLeastPrivilegeTest to ensure that unauthorized users cannot access sensitive API routes, maintaining strict access control.
2026-08-07 21:13:38 +03:30

85 lines
3.8 KiB
TypeScript

import React, { useMemo } from 'react';
import { Link } from 'react-router';
import { useHolidays } from '../../hooks/useResourceCalendar';
import { formatDate, currentJalaliYear } from '../../lib/utils';
/**
* تعطیلات رسمیِ سال، همان‌جایی که تعطیلی اختصاصی تعریف می‌شود.
*
* تقویم رسمی یک بار مرکزی ثبت می‌شود و هر پزشک و هر منبع از آن ارث می‌برد؛ اینجا فقط
* دیده می‌شود و — اگر این محیط آن روز باز باشد — با یک سوییچ خنثی می‌شود. ساخت و حذفِ
* خودِ تعطیلی کارِ مدیر سیستم است، نه کلینیک.
*
* یک کامپوننت برای دو مصرف‌کننده: تب تعطیلات پزشک و تب تعطیلات منبع. دو نسخه یعنی دو
* رفتار که با هم واگرا می‌شوند.
*/
export default function NationalHolidaysCard({ canUpdate, year = currentJalaliYear() }: {
canUpdate: boolean;
year?: number;
}) {
const { holidays, overrides, loading, setOverride, removeOverride } = useHolidays(year);
const overrideByDate = useMemo(
() => new Map(overrides.map((o) => [o.date, o])),
[overrides],
);
return (
<div className="card" style={{ padding: 14 }}>
<h2 className="section-title" style={{ margin: '0 0 4px' }}>تعطیلات رسمی {year.toLocaleString('fa-IR', { useGrouping: false })}</h2>
<p style={{ fontSize: 12, color: 'var(--text-3)', margin: '0 0 10px', lineHeight: 1.9 }}>
این‌ها برای همهٔ پزشکان و منابع اعمال می‌شوند. اگر این محیط روزی را باز است،
همین‌جا استثنا بزنید مدیریت کاملشان در{' '}
<Link to="/admin/holidays" style={{ color: 'var(--primary)' }}>تنظیمات تعطیلات رسمی</Link>.
</p>
{loading ? (
<span style={{ fontSize: 13, color: 'var(--text-3)' }}>در حال بارگذاری...</span>
) : holidays.length === 0 ? (
<p style={{ fontSize: 13, color: 'var(--text-3)', margin: 0 }}>
برای سال {year.toLocaleString('fa-IR', { useGrouping: false })} تعطیلی رسمی ثبت نشده است.
</p>
) : (
<div style={{ display: 'grid', gap: 8 }}>
{holidays.map((h) => {
const override = overrideByDate.get(h.date);
const open = override?.is_working === true;
return (
<div key={h.uuid} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 }}>
<span className={`badge ${open ? 'gray' : 'red'}`} style={{ fontSize: 11 }}>
{open ? 'باز' : 'تعطیل'}
</span>
<span style={{ flex: 1, color: 'var(--text-2)' }}>
{formatDate(h.date)} · {h.title}
</span>
{canUpdate && (
open ? (
<button
type="button"
className="btn secondary sm"
disabled={removeOverride.isPending}
onClick={() => override && removeOverride.mutate(override.uuid)}
>
تعطیل کن
</button>
) : (
<button
type="button"
className="btn secondary sm"
disabled={setOverride.isPending}
onClick={() => setOverride.mutate({ date: h.date, is_working: true })}
>
این روز باز است
</button>
)
)}
</div>
);
})}
</div>
)}
</div>
);
}