diff --git a/assets/admin/pages/PatientsListPage.test.tsx b/assets/admin/pages/PatientsListPage.test.tsx
index ca947786..d039cecd 100644
--- a/assets/admin/pages/PatientsListPage.test.tsx
+++ b/assets/admin/pages/PatientsListPage.test.tsx
@@ -31,21 +31,31 @@ beforeEach(() => {
});
describe('PatientsListPage (پروندهها)', () => {
- it('renders the records table with the create button', async () => {
+ it('renders records as cards by default', async () => {
renderWithProviders(, { route: '/admin/patients' });
expect(await screen.findByText('دنیا خلیلی')).toBeInTheDocument();
expect(screen.getByText('علی بدیعی زاده')).toBeInTheDocument();
+ // card-only labels (with colon) — proves the default view is the card grid
+ expect(screen.getAllByText('شماره پرونده:').length).toBe(2);
expect(screen.getByRole('button', { name: /تشکیل پرونده/ })).toBeInTheDocument();
- // record with no tags shows the inline "add" trigger
- expect(screen.getByText('اضافه کردن')).toBeInTheDocument();
+ // default card view: requests 16 per page
+ expect(get.mock.calls.some(([u]) => String(u).includes('limit=16'))).toBe(true);
});
- it('switches to the card view', async () => {
+ it('opens the card ⋮ menu with view/edit actions', async () => {
renderWithProviders(, { route: '/admin/patients' });
await screen.findByText('دنیا خلیلی');
- fireEvent.click(screen.getByLabelText('نمایش کارتی'));
- expect(screen.getByText('دنیا خلیلی')).toBeInTheDocument();
- expect(screen.getAllByText('مشاهده').length).toBeGreaterThan(0);
+ fireEvent.click(screen.getAllByLabelText('عملیات')[0]);
+ expect(await screen.findByText('مشاهده')).toBeInTheDocument();
+ expect(screen.getByText('ویرایش')).toBeInTheDocument();
+ });
+
+ it('switches to the table view', async () => {
+ renderWithProviders(, { route: '/admin/patients' });
+ await screen.findByText('دنیا خلیلی');
+ fireEvent.click(screen.getByLabelText('نمایش جدولی'));
+ expect(await screen.findByText('مراجعه کننده')).toBeInTheDocument(); // table header
+ expect(get.mock.calls.some(([u]) => String(u).includes('limit=12'))).toBe(true);
});
it('assigns a tag inline through the برچسبها popover', async () => {
diff --git a/assets/admin/pages/PatientsListPage.tsx b/assets/admin/pages/PatientsListPage.tsx
index 2c6ed934..72fb4f0d 100644
--- a/assets/admin/pages/PatientsListPage.tsx
+++ b/assets/admin/pages/PatientsListPage.tsx
@@ -4,6 +4,7 @@ import { Link, useNavigate } from 'react-router-dom';
import {
PlusIcon, PencilIcon, EyeIcon, MagnifyingGlassIcon, AdjustmentsHorizontalIcon,
Squares2X2Icon, TableCellsIcon, ExclamationCircleIcon, IdentificationIcon,
+ UserIcon, EllipsisHorizontalIcon,
} from '@heroicons/react/24/outline';
import { api } from '../lib/api';
import type { ApiResponse } from '../lib/api';
@@ -13,7 +14,9 @@ import Pagination from '../components/ui/Pagination';
import PatientTagsCell from '../components/PatientTagsCell';
import PatientsFilterModal, { type PatientFilters } from '../components/PatientsFilterModal';
-const LIMIT = 20;
+// tauri /files paginates card view by 16 and table view by 12.
+const CARD_PAGE_SIZE = 16;
+const TABLE_PAGE_SIZE = 12;
const EMPTY: PatientRecord[] = [];
/** unix start-of-day for `from`, end-of-day for `to`, from a gregorian Y-m-d. */
@@ -31,16 +34,83 @@ function countFilters(f: PatientFilters): number {
.filter(Boolean).length;
}
-/** پروندهها — patient records list (table + card views) matching the Figma design. */
+/**
+ * A single patient card — mirrors tauri `files/list/CardView` pixel-for-pixel
+ * (avatar + name + ⋮ menu header, file-number/mobile rows, tags footer).
+ * The whole card navigates to the detail; the ⋮ menu offers view/edit.
+ */
+function PatientCard({ r, onView, onEdit }: { r: PatientRecord; onView: () => void; onEdit: () => void }) {
+ const [menu, setMenu] = useState(false);
+ return (
+
{ if ((e.target as HTMLElement).closest('.more-square-icon')) return; onView(); }}
+ >
+ {/* نام بیمار */}
+
+
+
+
+
+
{r.user_name || '—'}
+
+
+
+ {menu && (
+ <>
+
{ e.stopPropagation(); setMenu(false); }} />
+
+
+
+
+ >
+ )}
+
+
+
+
+ {/* شماره پرونده */}
+
+ شماره پرونده:
+ {r.record_number || '—'}
+
+ {/* شماره موبایل */}
+
+ موبایل:
+ {r.user_mobile || '—'}
+
+
+
+ {/* فوتر: برچسبها */}
+
e.stopPropagation()}
+ >
+
برچسبها:
+
+
+
+ );
+}
+
+/** پروندهها — patient records list. Ported from tauri /files (default card view). */
export default function PatientsListPage() {
const navigate = useNavigate();
const [page, setPage] = useState(1);
const [search, setSearch] = useState('');
- const [view, setView] = useState<'table' | 'card'>('table');
+ const [view, setView] = useState<'table' | 'card'>('card');
const [filterOpen, setFilterOpen] = useState(false);
const [filters, setFilters] = useState
({});
- const qs = new URLSearchParams({ page: String(page), limit: String(LIMIT) });
+ const pageSize = view === 'card' ? CARD_PAGE_SIZE : TABLE_PAGE_SIZE;
+ const switchView = (v: 'table' | 'card') => { setView(v); setPage(1); };
+
+ const qs = new URLSearchParams({ page: String(page), limit: String(pageSize) });
if (search) qs.set('search', search);
if (filters.gender) qs.set('gender', filters.gender);
if (filters.insurance_id) qs.set('insurance_id', filters.insurance_id);
@@ -58,7 +128,6 @@ export default function PatientsListPage() {
});
const activeFilters = countFilters(filters);
-
const records = data?.data ?? EMPTY;
const total = data?.meta?.totalRecords ?? 0;
@@ -81,10 +150,10 @@ export default function PatientsListPage() {
{/* view toggle */}
-
@@ -105,7 +174,7 @@ export default function PatientsListPage() {
) : records.length === 0 ? (
-
پروندهای یافت نشد.
+
بیماری یافت نشد
) : view === 'table' ? (
@@ -120,7 +189,7 @@ export default function PatientsListPage() {
{records.map((r, i) => (
- | {formatNumber((page - 1) * LIMIT + i + 1)} |
+ {formatNumber((page - 1) * pageSize + i + 1)} |
{r.user_name || '—'}
@@ -145,29 +214,15 @@ export default function PatientsListPage() {
) : (
-
+
{records.map((r) => (
-
-
- {r.user_name || '—'}
-
-
-
- شماره پرونده: {r.record_number || '—'}
- تماس: {r.user_mobile || '—'}
- کد ملی: {r.user_national_code || '—'}
-
-
-
+ navigate(viewHref(r))} onEdit={() => navigate(editHref(r))} />
))}
)}
|