Files
clinicpro/assets/admin/pages/AppointmentsPage.pagination.test.tsx
T
hamedandClaude Fable 5 165ececab4 feat(appointments): close the three remaining design gaps
1. شارژ کیف پول is now functional end-to-end. New owner-gated
   POST /api/v1/patient/{uuid}/wallet/charge creates a manual credit
   WalletTransaction (computed balance_after); the patient detail's wallet tab
   gains a top-up modal (PriceInput + description) and supports ?tab= deep
   links. The deposit sections of the create drawer, the edit page and the
   replace modal link to it via WalletChargeLink (record resolved by mobile).
2. جایگزینی نوبت now matches appointments-replace.pdf: patient search-or-new,
   بخش/سرویس/پرسنل selects prefilled from the appointment, deposit toggle +
   amount + charge link, read-only original date/time, status pick and notes —
   all through the general PATCH.
3. The confirmed-appointments table is paginated (20/page, client-side so the
   schedule view and doctor-tab derivation keep the whole day), resetting on
   date/doctor/filter changes. The page-local STATUS_META also adopts the
   design labels plus following_up/salon for the schedule cards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 00:26:47 +03:30

52 lines
2.2 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { screen, fireEvent } from '@testing-library/react';
import { renderWithProviders } from '../test/utils';
vi.mock('../lib/api', () => ({
api: { get: vi.fn(), post: vi.fn(), patch: vi.fn(), put: vi.fn(), delete: vi.fn() },
ApiError: class extends Error {},
}));
import { api } from '../lib/api';
import { useAuthStore } from '../stores/authStore';
import AppointmentsPage from './AppointmentsPage';
const get = api.get as ReturnType<typeof vi.fn>;
const mkRow = (i: number) => ({
uuid: `ap${i}`, patient_name: `بیمار ${i}`, patient_mobile: '09120000000',
doctor_uuid: 'doc1', doctor_name: 'دکتر احمدی',
slot_start: 1735639200 + i * 1800, slot_end: 1735641000 + i * 1800,
appointment_date: '2024-12-31', appointment_time: '09:00', end_time: '09:30',
status: 'confirmed', version: 1, created_at: '',
});
beforeEach(() => {
get.mockReset();
useAuthStore.setState({ primaryRole: 'doctor', dbUuid: 'doc1' } as any);
get.mockImplementation((url: string) => {
if (url.includes('/my/appointments/today-stats')) return Promise.resolve({ success: true, data: { total: 25, completed: 0, waiting: 0, cancelled: 0 } });
if (url.includes('/my/appointments')) return Promise.resolve({
success: true,
data: Array.from({ length: 25 }, (_, i) => mkRow(i + 1)),
meta: { totalRecords: 25, totalPages: 1, currentPage: 1 },
});
return Promise.resolve({ success: true, data: [] });
});
});
describe('AppointmentsPage — table pagination', () => {
it('shows 20 rows per page and navigates to the rest', async () => {
renderWithProviders(<AppointmentsPage />);
fireEvent.click(await screen.findByText('نمایش جدولی'));
expect(await screen.findByText('بیمار 1')).toBeInTheDocument();
expect(screen.getByText('بیمار 20')).toBeInTheDocument();
expect(screen.queryByText('بیمار 21')).toBeNull();
// page 2 → remaining 5 rows
fireEvent.click(screen.getByRole('button', { name: '۲' }));
expect(await screen.findByText('بیمار 21')).toBeInTheDocument();
expect(screen.queryByText('بیمار 1')).toBeNull();
});
});