feat(appointments): resource tabs follow their supervising doctor
Resource tabs now sit under the selected doctor and list only the resources that doctor supervises, so moving between a doctor's own appointments and the devices under them is one row of tabs rather than a flat list of everything. The booking modal reads the doctor from the resource's supervisor instead of asking again. The doctor↔resource relation is defined once, on the resource, and repeating the question here would have made a second source of truth. A resource whose supervisor was removed is blocked with a message pointing at the fix rather than a silently disabled button. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ const resource = {
|
||||
uuid: 'r-laser',
|
||||
name: 'لیزر CO2',
|
||||
address_uuid: 'addr-1',
|
||||
supervisor: { uuid: 'd-1', name: 'دکتر مرادی' },
|
||||
} as never;
|
||||
|
||||
/** یکی از این دو وقت مالِ همین منبع است و دیگری مالِ دستگاه دیگری. */
|
||||
@@ -87,14 +88,21 @@ describe('ResourceBookingModal', () => {
|
||||
expect(timeButtons).toHaveLength(1);
|
||||
});
|
||||
|
||||
/** پزشک مسئول اجباری است: `confirm` بدون آن اصلاً کار نمیکند. */
|
||||
it('تا پزشک مسئول انتخاب نشود، ثبت غیرفعال است', async () => {
|
||||
/**
|
||||
* پزشک از ناظرِ خودِ منبع میآید، نه از یک انتخابگر دیگر — ارتباط پزشک↔منبع یک جا
|
||||
* تعریف شده و تکرارش اینجا یعنی دو منبعِ حقیقت.
|
||||
*/
|
||||
it('پزشک را از ناظرِ منبع میگیرد و انتخابگر پزشک ندارد', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(
|
||||
<ResourceBookingModal resource={resource} onClose={vi.fn()} onBooked={vi.fn()} />,
|
||||
);
|
||||
|
||||
await screen.findByText('ثبت نوبت — لیزر CO2');
|
||||
expect(screen.getByText(/پزشک ناظر: دکتر مرادی/)).toBeInTheDocument();
|
||||
expect(screen.queryByText('پزشک انجامدهنده')).toBeNull();
|
||||
|
||||
// بدون سرویس و زمان هنوز غیرفعال است.
|
||||
expect(screen.getByRole('button', { name: 'ثبت نوبت' })).toBeDisabled();
|
||||
|
||||
await user.click(await screen.findByText('سرویس را انتخاب کنید'));
|
||||
@@ -104,8 +112,18 @@ describe('ResourceBookingModal', () => {
|
||||
const timeBtn = screen.getAllByRole('button').find((b) => /\d{2}:\d{2}/.test(b.textContent ?? ''));
|
||||
await user.click(timeBtn!);
|
||||
|
||||
// زمان انتخاب شد ولی پزشک نه → هنوز غیرفعال.
|
||||
// سرویس + زمان + ناظر ⇒ حالا قابل ثبت است.
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: 'ثبت نوبت' })).toBeEnabled());
|
||||
});
|
||||
|
||||
/** منبعی که ناظرش حذف شده نباید بیصدا غیرقابلثبت بماند. */
|
||||
it('منبع بدون ناظر را با پیام روشن مسدود میکند', async () => {
|
||||
const orphan = { uuid: 'r-x', name: 'لیزر یتیم', address_uuid: 'addr-1', supervisor: null } as never;
|
||||
renderWithProviders(
|
||||
<ResourceBookingModal resource={orphan} onClose={vi.fn()} onBooked={vi.fn()} />,
|
||||
);
|
||||
|
||||
expect(await screen.findByText(/این منبع پزشک ناظر ندارد/)).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'ثبت نوبت' })).toBeDisabled();
|
||||
expect(await screen.findByText('پزشک انجامدهنده')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user