diff --git a/assets/admin/components/TreatmentProtocolTab.tsx b/assets/admin/components/TreatmentProtocolTab.tsx
index af6a1847..2d6ba13e 100644
--- a/assets/admin/components/TreatmentProtocolTab.tsx
+++ b/assets/admin/components/TreatmentProtocolTab.tsx
@@ -1,9 +1,11 @@
import { useEffect, useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
-import { PlusIcon, TrashIcon } from '@heroicons/react/24/outline';
+import { PlusIcon, TrashIcon, XMarkIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline';
import Switch from './ui/Switch';
import SearchableSelect from './ui/SearchableSelect';
+import Input from './ui/Input';
+import ConfirmDialog from './ui/ConfirmDialog';
import { api, ApiError, type ApiResponse } from '../lib/api';
interface ProtocolStep {
@@ -44,12 +46,22 @@ const DEFAULT_STEPS: ProtocolStep[] = [
{ step_number: 2, offset_days: 30 },
];
+const MIN_STEPS = 2;
+
+/** خط جداکنندهٔ بخشها — سه بخشِ مستقل نباید بههم چسبیده به نظر برسند. */
+function Divider() {
+ return
;
+}
+
/**
* «طول درمان» یک سرویس.
*
* وجودِ پروتکل خودش سوییچ است — سرویس بدون پروتکل تکجلسهای است — پس روشنکردن یعنی
- * ساختن و خاموشکردن یعنی حذف. فاصلهٔ هر گام از **جلسهٔ قبل** است، نه از شروع دوره،
- * چون فاصلهٔ درمان به آخرین جلسه گره خورده نه به روز باز شدن پرونده.
+ * ساختن و خاموشکردن یعنی حذف. چون خاموشکردن برگشتناپذیر است و گامها و پرسنل را با
+ * خودش میبرد، با تأیید صریح انجام میشود نه با یک کلیک.
+ *
+ * فاصلهٔ هر گام از **جلسهٔ قبل** است، نه از شروع دوره، چون فاصلهٔ درمان به آخرین جلسه
+ * گره خورده نه به روز باز شدن پرونده.
*/
export default function TreatmentProtocolTab({ serviceUuid, canEdit }: {
serviceUuid: string;
@@ -57,7 +69,7 @@ export default function TreatmentProtocolTab({ serviceUuid, canEdit }: {
}) {
const qc = useQueryClient();
- const { data, isLoading } = useQuery({
+ const { data, isLoading, isError, refetch } = useQuery({
queryKey: ['treatment-protocol', serviceUuid],
queryFn: () => api.get>(
`/api/v1/service-item/${serviceUuid}/treatment-protocol`,
@@ -82,6 +94,7 @@ export default function TreatmentProtocolTab({ serviceUuid, canEdit }: {
const [steps, setSteps] = useState(DEFAULT_STEPS);
const [staffUuids, setStaffUuids] = useState([]);
const [supervisor, setSupervisor] = useState(null);
+ const [confirmOff, setConfirmOff] = useState(false);
useEffect(() => {
setEnabled(protocol !== null);
@@ -116,14 +129,28 @@ export default function TreatmentProtocolTab({ serviceUuid, canEdit }: {
mutationFn: () => api.delete>(`/api/v1/service-item/${serviceUuid}/treatment-protocol`),
onSuccess: () => {
toast.success('طول درمان خاموش شد');
+ setConfirmOff(false);
qc.invalidateQueries({ queryKey: ['treatment-protocol', serviceUuid] });
},
onError: (e) => toast.error(e instanceof ApiError ? e.message : 'خاموشکردن ناموفق بود'),
});
+ /**
+ * روشنکردن بیخطر است و فوری اعمال میشود؛ خاموشکردن پروتکل را با همهٔ گامها و
+ * پرسنلش پاک میکند، پس تأیید میخواهد.
+ */
const toggle = (on: boolean) => {
- setEnabled(on);
- if (!on && protocol !== null) remove.mutate();
+ if (on) {
+ setEnabled(true);
+ return;
+ }
+
+ if (protocol === null) {
+ setEnabled(false);
+ return;
+ }
+
+ setConfirmOff(true);
};
const setOffset = (index: number, value: number) => {
@@ -138,7 +165,7 @@ export default function TreatmentProtocolTab({ serviceUuid, canEdit }: {
};
const removeStep = (index: number) => {
- setSteps((prev) => (prev.length <= 2 ? prev : prev.filter((_, i) => i !== index)));
+ setSteps((prev) => (prev.length <= MIN_STEPS ? prev : prev.filter((_, i) => i !== index)));
};
const addStaff = (uuid: string | number | null) => {
@@ -150,151 +177,229 @@ export default function TreatmentProtocolTab({ serviceUuid, canEdit }: {
return
در حال بارگذاری...
;
}
+ /**
+ * بدون این، شکستِ کوئری `protocol = null` میداد و صفحه سوییچِ خاموش نشان میداد —
+ * یعنی به مدیر میگفت این سرویس تکجلسهای است، در حالی که فقط خواندن شکست خورده.
+ */
+ if (isError) {
+ return (
+
+ طول درمان این سرویس خوانده نشد
+
+ تا وقتی خطا برطرف نشده، وضعیت واقعی این سرویس معلوم نیست.
+
+
+
- هیچ دستگاهی به این سرویس وصل نیست
-
- رزرو قفل نمیشود، ولی منشی میتواند این سرویس را روی هر منبعی ثبت کند و اپراتور فرم
- دستگاه درست را نمیبیند. در «منابع» مشخص کنید کدام دستگاهها این سرویس را میدهند.
-
-
+ هیچ دستگاهی به این سرویس وصل نیست
+
+ رزرو قفل نمیشود، ولی منشی میتواند این سرویس را روی هر منبعی ثبت کند و اپراتور فرم
+ دستگاه درست را نمیبیند. در «منابع» مشخص کنید کدام دستگاهها این سرویس را میدهند.
+
+
+
+ )}
- {enabled && (
- <>
-
-
- جلسات دوره ({steps.length} جلسه)
-
-
- فاصلهٔ هر جلسه از جلسهٔ قبل حساب میشود، نه از شروع دوره. اگر بیمار دیر بیاید،
- بقیهٔ دوره هم جابهجا میشود.
-