feat(admin): online-booking window UI in doctor weekly schedule
Add a نوبتدهی آنلاین card to the weekly-schedule tab: a toggle for online_booking_enabled and a value+unit (هفته/ماه) booking window. Hydrated from the schedule meta and sent in the weekly-schedule POST/PATCH payload. Uses existing .input/.btn classes, RTL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -89,7 +89,17 @@ interface SessionConfig {
|
|||||||
}
|
}
|
||||||
interface NewDayConfig { sessions: SessionConfig[]; }
|
interface NewDayConfig { sessions: SessionConfig[]; }
|
||||||
type NewScheduleMap = Record<string, NewDayConfig>;
|
type NewScheduleMap = Record<string, NewDayConfig>;
|
||||||
interface WeeklyScheduleData { uuid: string; doctor_uuid: string; schedule: NewScheduleMap; }
|
interface BookingMeta {
|
||||||
|
online_booking_enabled: boolean;
|
||||||
|
booking_window_value: number;
|
||||||
|
booking_window_unit: 'week' | 'month';
|
||||||
|
}
|
||||||
|
const DEFAULT_BOOKING_META: BookingMeta = {
|
||||||
|
online_booking_enabled: true,
|
||||||
|
booking_window_value: 1,
|
||||||
|
booking_window_unit: 'month',
|
||||||
|
};
|
||||||
|
interface WeeklyScheduleData { uuid: string; doctor_uuid: string; schedule: NewScheduleMap; meta?: BookingMeta; }
|
||||||
|
|
||||||
// ── Constants ──────────────────────────────────────────────────────────────
|
// ── Constants ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -1191,6 +1201,7 @@ function WeeklyScheduleTab({ doctorUuid, addresses }: { doctorUuid: string; addr
|
|||||||
const [scheduleMap, setScheduleMap] = useState<NewScheduleMap>(EMPTY_NEW_SCHEDULE);
|
const [scheduleMap, setScheduleMap] = useState<NewScheduleMap>(EMPTY_NEW_SCHEDULE);
|
||||||
const [scheduleUuid, setScheduleUuid] = useState<string | null>(null);
|
const [scheduleUuid, setScheduleUuid] = useState<string | null>(null);
|
||||||
const [expandedDay, setExpandedDay] = useState<string | null>(null);
|
const [expandedDay, setExpandedDay] = useState<string | null>(null);
|
||||||
|
const [meta, setMeta] = useState<BookingMeta>(DEFAULT_BOOKING_META);
|
||||||
|
|
||||||
const scheduleQ = useQuery({
|
const scheduleQ = useQuery({
|
||||||
queryKey: ['doctor-schedule', doctorUuid],
|
queryKey: ['doctor-schedule', doctorUuid],
|
||||||
@@ -1211,6 +1222,7 @@ function WeeklyScheduleTab({ doctorUuid, addresses }: { doctorUuid: string; addr
|
|||||||
setScheduleMap(merged);
|
setScheduleMap(merged);
|
||||||
setScheduleUuid(d.uuid);
|
setScheduleUuid(d.uuid);
|
||||||
}
|
}
|
||||||
|
if (d?.meta) setMeta({ ...DEFAULT_BOOKING_META, ...d.meta });
|
||||||
} else if (scheduleQ.error instanceof ApiError && scheduleQ.error.status === 404) {
|
} else if (scheduleQ.error instanceof ApiError && scheduleQ.error.status === 404) {
|
||||||
setScheduleMap(EMPTY_NEW_SCHEDULE); setScheduleUuid(null);
|
setScheduleMap(EMPTY_NEW_SCHEDULE); setScheduleUuid(null);
|
||||||
}
|
}
|
||||||
@@ -1237,8 +1249,8 @@ function WeeklyScheduleTab({ doctorUuid, addresses }: { doctorUuid: string; addr
|
|||||||
if (hasAnyOverlap) throw new Error('تداخل زمانی در برنامه وجود دارد');
|
if (hasAnyOverlap) throw new Error('تداخل زمانی در برنامه وجود دارد');
|
||||||
if (missingLocation) throw new Error('مکان مطب برای همه بازههای فعال الزامی است');
|
if (missingLocation) throw new Error('مکان مطب برای همه بازههای فعال الزامی است');
|
||||||
return scheduleUuid
|
return scheduleUuid
|
||||||
? api.patch<ApiResponse<any>>(`/api/v1/appointment-settings/weekly-schedule/${doctorUuid}`, { schedule: scheduleMap })
|
? api.patch<ApiResponse<any>>(`/api/v1/appointment-settings/weekly-schedule/${doctorUuid}`, { schedule: scheduleMap, meta })
|
||||||
: api.post<ApiResponse<any>>('/api/v1/appointment-settings/weekly-schedule', { doctor_uuid: doctorUuid, schedule: scheduleMap });
|
: api.post<ApiResponse<any>>('/api/v1/appointment-settings/weekly-schedule', { doctor_uuid: doctorUuid, schedule: scheduleMap, meta });
|
||||||
},
|
},
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
const d: WeeklyScheduleData = res?.data?.data ?? res?.data;
|
const d: WeeklyScheduleData = res?.data?.data ?? res?.data;
|
||||||
@@ -1298,6 +1310,42 @@ function WeeklyScheduleTab({ doctorUuid, addresses }: { doctorUuid: string; addr
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* ─ نوبتدهی آنلاین */}
|
||||||
|
<div className="mb-3 p-3 rounded-xl border border-slate-200 dark:border-gray-700 space-y-3">
|
||||||
|
<label className="flex items-center justify-between gap-3 cursor-pointer">
|
||||||
|
<span className="text-sm font-medium text-slate-700 dark:text-slate-200">نوبتدهی آنلاین</span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={meta.online_booking_enabled}
|
||||||
|
onChange={(e) => setMeta(m => ({ ...m, online_booking_enabled: e.target.checked }))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-sm text-slate-600 dark:text-slate-400">رزرو آنلاین تا</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
value={meta.booking_window_value}
|
||||||
|
disabled={!meta.online_booking_enabled}
|
||||||
|
onChange={(e) => setMeta(m => ({ ...m, booking_window_value: Math.max(1, Number(e.target.value) || 1) }))}
|
||||||
|
className="input w-16 text-center"
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
value={meta.booking_window_unit}
|
||||||
|
disabled={!meta.online_booking_enabled}
|
||||||
|
onChange={(e) => setMeta(m => ({ ...m, booking_window_unit: e.target.value as 'week' | 'month' }))}
|
||||||
|
className="input"
|
||||||
|
>
|
||||||
|
<option value="week">هفته</option>
|
||||||
|
<option value="month">ماه</option>
|
||||||
|
</select>
|
||||||
|
<span className="text-sm text-slate-600 dark:text-slate-400">آینده</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-slate-400 dark:text-slate-500">
|
||||||
|
بیمار فقط تا این بازه میتواند آنلاین نوبت بگیرد؛ روزهای بعد از آن روی تقویم غیرفعالاند.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{SCHEDULE_DAYS.map(day => {
|
{SCHEDULE_DAYS.map(day => {
|
||||||
const sessions = scheduleMap[day.key]?.sessions ?? [];
|
const sessions = scheduleMap[day.key]?.sessions ?? [];
|
||||||
const isOverlap = overlapDays[day.key];
|
const isOverlap = overlapDays[day.key];
|
||||||
|
|||||||
Reference in New Issue
Block a user