refactor: remove ResourceBookingPage and related components; update references in AppointmentsPage and ScheduleSection

This commit is contained in:
hamed
2026-08-08 07:19:48 +03:30
parent b699476305
commit c452150a83
7 changed files with 10 additions and 606 deletions
@@ -1,39 +0,0 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, act } from '@testing-library/react';
import HoldCountdown from './HoldCountdown';
describe('HoldCountdown', () => {
beforeEach(() => vi.useFakeTimers());
afterEach(() => vi.useRealTimers());
const nowSeconds = () => Math.floor(Date.now() / 1000);
it('counts down from the server expiry', () => {
render(<HoldCountdown expiresAt={nowSeconds() + 125} onExpired={() => {}} />);
expect(screen.getByText(/2:05/)).toBeInTheDocument();
act(() => { vi.advanceTimersByTime(5000); });
expect(screen.getByText(/2:00/)).toBeInTheDocument();
});
/** ⭐ رزروی که در سکوت منقضی شود، اپراتور را با یک ۴۰۹ بی‌توضیح تنها می‌گذارد. */
it('reports expiry to the parent exactly once it lapses', () => {
const onExpired = vi.fn();
render(<HoldCountdown expiresAt={nowSeconds() + 2} onExpired={onExpired} />);
expect(onExpired).not.toHaveBeenCalled();
act(() => { vi.advanceTimersByTime(3000); });
expect(onExpired).toHaveBeenCalled();
expect(screen.getByText('مهلت تمام شد')).toBeInTheDocument();
});
it('switches to the urgent style under a minute', () => {
const { container } = render(<HoldCountdown expiresAt={nowSeconds() + 30} onExpired={() => {}} />);
expect(container.querySelector('.badge.red')).not.toBeNull();
});
});
-56
View File
@@ -1,56 +0,0 @@
import React, { useEffect, useState } from 'react';
interface Props {
expiresAt: number;
onExpired: () => void;
}
/**
* شمارش معکوس مهلت رزرو موقت.
*
* بدون این، اپراتور نمی‌داند چقدر وقت دارد و رزرو در سکوت منقضی می‌شود — بعد کلیک
* «ثبت» یک ۴۰۹ می‌گیرد که هیچ‌جا توضیحش را ندیده.
*
* مبنا `expires_at` سرور است، نه شمارنده‌ای که از لحظهٔ رندر شروع شود: ساعت مرورگر و
* تأخیر شبکه هر دو می‌توانند چند ثانیه اختلاف بسازند و آن چند ثانیه دقیقاً همان‌جایی
* است که رزرو از دست می‌رود.
*/
export default function HoldCountdown({ expiresAt, onExpired }: Props) {
const [remaining, setRemaining] = useState(() => expiresAt - Math.floor(Date.now() / 1000));
useEffect(() => {
const tick = () => {
const left = expiresAt - Math.floor(Date.now() / 1000);
setRemaining(left);
if (left <= 0) onExpired();
};
tick();
const timer = setInterval(tick, 1000);
return () => clearInterval(timer);
}, [expiresAt, onExpired]);
if (remaining <= 0) {
return (
<span className="badge red">
<span className="bdot" />
مهلت تمام شد
</span>
);
}
const minutes = Math.floor(remaining / 60);
const seconds = remaining % 60;
// زیر یک دقیقه هشدار می‌گیرد؛ همان لحظه‌ای که اپراتور باید تصمیمش را بگیرد.
const urgent = remaining < 60;
return (
<span className={urgent ? 'badge red' : 'badge amber'}>
<span className="bdot" />
مهلت ثبت: {minutes}:{String(seconds).padStart(2, '0')}
</span>
);
}
@@ -86,9 +86,9 @@ const MODE_LABELS: Record<BookingMeta['booking_mode'], string> = {
};
/**
* حالت‌های قابل انتخاب برای پزشک. `resource` عمداً اینجا نیست: نوبت‌دهی منبع‌محور از
* تنظیمات پزشک برداشته شده و مسیر خودش (صفحهٔ «نوبت منبع‌محور» و تقویم هر منبع) را
* دارد. انتخابش برگشت‌ناپذیر بود و محیط را قفل می‌کرد.
* حالت‌های قابل انتخاب برای پزشک. `resource` عمداً اینجا نیست: انتخابش برگشت‌ناپذیر
* بود و محیط را قفل می‌کرد، پس از تنظیمات پزشک برداشته شد. صفحهٔ «نوبت منبع‌محور» هم
* در ۲۰۲۶-۰۸-۰۸ حذف شد؛ رکوردهای قدیمی فقط خواندنی اعلام می‌شوند (پایین همین فایل).
*/
const BOOKING_MODE_CARDS: ReadonlyArray<readonly [BookingMeta['booking_mode'], string, string]> = [
['slot', 'نوبت‌دهی اسلاتی', 'شما بازه‌های کاری و «مدت هر نوبت» را مشخص می‌کنید؛ سیستم بازه را به نوبت‌های هم‌اندازه تقسیم می‌کند. مناسب ویزیت‌های با زمان یکسان.'],