feat: port نوبتها (appointments) tab from tauri to patient detail page
Replace the placeholder row list on the patient detail «نوبتها» tab with the
card-grid design ported pixel-for-pixel from clinic-pro-tauri TurnsSection:
- New AppointmentTurnCard mirrors tauri TurnsCard (success icon, title, date/time
chips, personnel, status). Status uses the live AppointmentStatusDropdown
instead of the tauri mock.
- New AppointmentsTab in PatientDetailPage: sort/filter toolbar (client-side) +
reserve/new buttons linking to existing /admin/appointments pages + card grid.
- Add CalendarD/ClockP/UserD/StatusGlobe icons (verbatim from tauri).
- Backend: expose version on GET /patient/{uuid}/appointments so the status
dropdown can optimistic-lock. No new endpoint.
- Tests: PatientAppointmentsTest (shape/version/order/empty/ownership) +
AppointmentTurnCard + tab data/empty cases. docs/api/patient.md updated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -169,4 +169,34 @@ describe('PatientDetailPage (پرونده تبدار)', () => {
|
||||
expect(await screen.findByPlaceholderText('متن پیام...')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'ارسال' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders appointment turn cards + toolbar on the نوبتها tab', async () => {
|
||||
get.mockImplementation((url: string) => {
|
||||
if (url === '/api/v1/patient/r1') return Promise.resolve({ success: true, data: {
|
||||
uuid: 'r1', user_name: 'ساغر صابری', record_number: 'P-1001', created_at: 1700000000, profile: null,
|
||||
} });
|
||||
if (url === '/api/v1/patient/r1/appointments') return Promise.resolve({ success: true, data: [
|
||||
{ uuid: 'a1', starts_at: 1754000000, ends_at: 1754001800, status: 'confirmed', version: 1, doctor_name: 'دکتر راد', service_name: null },
|
||||
] });
|
||||
return Promise.resolve({ success: true, data: [] });
|
||||
});
|
||||
renderDetail();
|
||||
await loaded();
|
||||
fireEvent.click(screen.getByText('نوبتها'));
|
||||
// card: generic title + doctor + live status label (confirmed → قطعی شده)
|
||||
expect(await screen.findByText('دکتر راد')).toBeInTheDocument();
|
||||
expect(screen.getByText('قطعی شده')).toBeInTheDocument();
|
||||
expect(screen.getByText('تاریخ:')).toBeInTheDocument();
|
||||
expect(screen.getByText('ساعت:')).toBeInTheDocument();
|
||||
// toolbar buttons link to the existing appointment pages
|
||||
expect(screen.getByRole('link', { name: /نوبت رزرو/ })).toHaveAttribute('href', '/admin/appointments/reserve');
|
||||
expect(screen.getByRole('link', { name: /نوبت جدید/ })).toHaveAttribute('href', '/admin/appointments/new');
|
||||
});
|
||||
|
||||
it('shows the empty state on the نوبتها tab when there are no appointments', async () => {
|
||||
renderDetail();
|
||||
await loaded();
|
||||
fireEvent.click(screen.getByText('نوبتها'));
|
||||
expect(await screen.findByText('نوبتی ثبت نشده است')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user