feat: section-then-service picker with cross-section accumulation
Rework slot-mode service selection: pick a section, its services show as a checkbox list, and chosen services collect into a removable 'selected' chip list that persists when switching to another section (services from multiple sections accumulate). Changing the section no longer clears the selection. Add a test covering multi-section accumulation and chip removal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -108,6 +108,53 @@ describe('AppointmentCreatePage — افزودن نوبت', () => {
|
||||
expect(body).toMatchObject({ patient_name: 'حامد حسینی', patient_mobile: '09210671745', patient_national_code: '0012345675' });
|
||||
});
|
||||
|
||||
it('slot mode: accumulates services across multiple sections and removes them (happy path)', async () => {
|
||||
get.mockImplementation((url: string) => {
|
||||
if (url === '/api/v1/service-sections')
|
||||
return Promise.resolve({ success: true, data: [{ uuid: 'sec1', name: 'زیبایی' }, { uuid: 'sec2', name: 'پوست' }] });
|
||||
if (url === '/api/v1/service-items/sec1')
|
||||
return Promise.resolve({ success: true, data: [{ uuid: 'i1', name: 'لیزر' }, { uuid: 'i2', name: 'ماساژ' }] });
|
||||
if (url === '/api/v1/service-items/sec2')
|
||||
return Promise.resolve({ success: true, data: [{ uuid: 'i3', name: 'پاکسازی' }] });
|
||||
return Promise.resolve({ success: true, data: [] });
|
||||
});
|
||||
|
||||
const pickSection = async (optionLabel: string) => {
|
||||
const input = document.getElementById('appt-section-select') as HTMLInputElement;
|
||||
fireEvent.focus(input);
|
||||
fireEvent.keyDown(input, { key: 'ArrowDown' });
|
||||
fireEvent.click(await screen.findByText(optionLabel));
|
||||
};
|
||||
|
||||
renderWithProviders(<AppointmentCreatePage />, { route: '/admin/appointments/new' });
|
||||
|
||||
fireEvent.click(screen.getByText('مراجعه کننده جدید'));
|
||||
fireEvent.change(screen.getByPlaceholderText('نام و نام خانوادگی مراجعه کننده'), { target: { value: 'علی محمدی' } });
|
||||
fireEvent.change(screen.getByPlaceholderText('شماره تماس مراجعه کننده'), { target: { value: '09121234567' } });
|
||||
fireEvent.change(screen.getByPlaceholderText('کد ملی مراجعه کننده'), { target: { value: '1234567891' } });
|
||||
|
||||
// بخش اول → دو سرویس انتخاب
|
||||
await pickSection('زیبایی');
|
||||
fireEvent.click(await screen.findByText('لیزر'));
|
||||
fireEvent.click(await screen.findByText('ماساژ'));
|
||||
|
||||
// بخش دوم → یک سرویس؛ لیستِ انباشته باید سرویسهای بخش قبل را نگه دارد
|
||||
await pickSection('پوست');
|
||||
fireEvent.click(await screen.findByText('پاکسازی'));
|
||||
|
||||
expect(screen.getByText('سرویسهای انتخابشده (3)')).toBeInTheDocument();
|
||||
|
||||
// حذف یک سرویس از لیست
|
||||
fireEvent.click(screen.getByLabelText('حذف ماساژ'));
|
||||
expect(screen.getByText('سرویسهای انتخابشده (2)')).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByText('ثبت اطلاعات'));
|
||||
await waitFor(() => expect(post).toHaveBeenCalled());
|
||||
const [, body] = post.mock.calls[0];
|
||||
expect(body.service_item_uuids).toEqual(['i1', 'i3']);
|
||||
expect(body.duration_from_services).toBeUndefined(); // حالت اسلاتی: ساعت پایانِ دستی حفظ
|
||||
});
|
||||
|
||||
it('picking an existing patient WITHOUT a national code keeps submit disabled until one is entered (boundary)', async () => {
|
||||
get.mockImplementation((url: string) => {
|
||||
if (url.startsWith('/api/v1/patients?'))
|
||||
|
||||
Reference in New Issue
Block a user