feat(appointment): add service mode functionality and update API documentation for service-based booking

This commit is contained in:
hamed
2026-07-15 23:22:57 +03:30
parent 5937f7e176
commit ad94c165e8
4 changed files with 95 additions and 6 deletions
@@ -90,6 +90,46 @@ describe('NewAppointmentDrawer (اضافه کردن نوبت جدید)', () => {
expect(screen.getByRole('button', { name: /شارژ کیف پول/ })).toBeInTheDocument();
});
it('service mode: picks a suggested time and posts service_item_uuids', async () => {
get.mockImplementation((url: string) => {
if (url === '/api/v1/service-sections') return Promise.resolve({ success: true, data: [{ uuid: 'sec1', name: 'زیبایی' }] });
if (url.startsWith('/api/v1/service-items/sec1')) return Promise.resolve({ success: true, data: [{ uuid: 'it1', name: 'لیزر توتال' }] });
if (url === '/api/v1/staff') return Promise.resolve({ success: true, data: [] });
if (url.startsWith('/api/v1/patients?search=')) return Promise.resolve({ success: true, data: [] });
// پزشک در حالت نوبت‌دهی سرویسی
if (url.startsWith('/api/v1/appointment-settings/weekly-schedule/'))
return Promise.resolve({ success: true, data: { data: { meta: { booking_mode: 'service', buffer_minutes: 5 } } } });
if (url.startsWith('/api/v1/appointment-service-slots'))
return Promise.resolve({ success: true, data: { total_duration_minutes: 30, buffer_minutes: 5, start_times: [{ start: 1754000000, end: 1754001800, start_time: '15:00' }] } });
return Promise.resolve({ success: true, data: [] });
});
renderDrawer();
fireEvent.change(screen.getByPlaceholderText('نام و نام خانوادگی مراجعه کننده را وارد نمایید'), { target: { value: 'مریم خلیلی' } });
fireEvent.change(screen.getByPlaceholderText('شماره تماس مراجعه کننده را وارد نمایید'), { target: { value: '09136549874' } });
fireEvent.change(screen.getByPlaceholderText('کد ملی مراجعه کننده را وارد نمایید'), { target: { value: '1234567891' } });
// حالت سرویس: منوی زمان‌دهیِ دستی نباید باشد
await waitFor(() => expect(screen.queryByLabelText('ساعت شروع')).toBeNull());
fireEvent.change(screen.getByLabelText('بخش'), { target: { value: 'sec1' } });
await screen.findByRole('option', { name: 'لیزر توتال' });
fireEvent.change(screen.getByLabelText('سرویس'), { target: { value: 'it1' } });
// زمانِ خالیِ پیشنهادی ظاهر می‌شود؛ انتخاب می‌کنیم
const slotBtn = await screen.findByRole('button', { name: '15:00' });
fireEvent.click(slotBtn);
fireEvent.click(screen.getByRole('button', { name: 'ثبت نوبت' }));
await waitFor(() => expect(post).toHaveBeenCalledWith('/api/v1/my/appointment', expect.objectContaining({
doctor_uuid: 'd1',
slot_start: 1754000000,
slot_end: 1754001800,
service_item_uuids: ['it1'],
is_reserve: false,
})));
});
it('reserve mode hides time fields and posts a day-level entry', async () => {
renderWithProviders(
<NewAppointmentDrawer doctorUuid="d1" defaultDate="2026-08-01" queryKey={['r']} onClose={() => {}} isReserve />,