import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { Link, useNavigate } from 'react-router-dom'; import { PencilIcon, EyeIcon, ExclamationCircleIcon, IdentificationIcon, UserIcon, EllipsisHorizontalIcon, } from '@heroicons/react/24/outline'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; import type { PatientRecord } from '../types'; import { formatNumber, toDate } from '../lib/utils'; import Pagination from '../components/ui/Pagination'; import PatientTagsCell from '../components/PatientTagsCell'; import PatientsFilterModal, { type PatientFilters } from '../components/PatientsFilterModal'; import { SearchHeaderP, TurnsFilter, PatientsGridView, PatientsCategoryView, AddTurn, } from '../components/icons/FilesToolbarIcons'; // 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. */ function dayBound(value: string | undefined, end: boolean): number | undefined { if (!value) return undefined; const d = toDate(value); if (!d) return undefined; const secs = Math.floor(d.setHours(0, 0, 0, 0) / 1000); return end ? secs + 86399 : secs; } /** Number of filters currently applied (for the button badge). */ function countFilters(f: PatientFilters): number { return [f.gender, f.insurance_id, f.admitted_from, f.admitted_to, f.service_status, f.has_debt || undefined, f.tags?.length ? '1' : undefined] .filter(Boolean).length; } /** * A single patient card — mirrors tauri `files/list/CardView` pixel-for-pixel * (avatar + name + ⋮ menu header, file-number/mobile rows, tags footer). */ 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'>('card'); const [filterOpen, setFilterOpen] = useState(false); const [filters, setFilters] = useState({}); 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); if (filters.service_status) qs.set('service_status', filters.service_status); if (filters.has_debt) qs.set('has_debt', '1'); if (filters.tags?.length) qs.set('tags', filters.tags.join(',')); const af = dayBound(filters.admitted_from, false); const at = dayBound(filters.admitted_to, true); if (af) qs.set('admitted_from', String(af)); if (at) qs.set('admitted_to', String(at)); const { data, isLoading, error } = useQuery & { meta?: { totalRecords: number } }>({ queryKey: ['patients', qs.toString()], queryFn: () => api.get(`/api/v1/patients?${qs.toString()}`), }); const activeFilters = countFilters(filters); const records = data?.data ?? EMPTY; const total = data?.meta?.totalRecords ?? 0; const editHref = (r: PatientRecord) => `/admin/patients/${r.uuid}/edit`; const viewHref = (r: PatientRecord) => `/admin/patients/${r.uuid}`; // نمایش جدولی/کارتی — دو دکمه با آیکون SVG سفارشی (عین tauri ViewModeToggle). const viewToggle = (
); return (
{/* Head — تیتر تنها (tauri files/head) */}

پرونده‌ها

{/* Inputs — ردیف کنترل‌ها (tauri files/inputs) */}
{/* جستجو + سوییچ نما */}
{ setSearch(e.target.value); setPage(1); }} placeholder="کد ملی، شماره پرونده یا نام مراجعه کننده را وارد کنید..." className="text-[#525252] dark:text-[#D7D8ED]" style={{ flex: 1, minWidth: 0, background: 'transparent', border: 'none', outline: 'none', fontSize: 13, textAlign: 'left' }} />
{viewToggle}
{/* فیلتر + تشکیل پرونده */}
{isLoading ? (
در حال بارگذاری...
) : error ? ( /* «دسترسی ندارید» با «بیماری یافت نشد» یکی نیست — پیام سرور را نشان بده. */
{(error as any)?.message || 'دسترسی به پرونده‌ها امکان‌پذیر نیست'}
اگر به‌تازگی محیط کاری‌تان تغییر کرده، از منوی بالا محیط درست را انتخاب کنید.
) : records.length === 0 ? (
بیماری یافت نشد
) : view === 'table' ? (
{['ردیف', 'مراجعه کننده', 'برچسب ها', 'شماره پرونده', 'شماره تماس', 'کد ملی', 'عملیات'].map((h) => ( ))} {records.map((r, i) => ( ))}
{h}
{formatNumber((page - 1) * pageSize + i + 1)} {r.user_name || '—'} {!r.user_national_code && } {r.record_number || '—'} {r.user_mobile || '—'} {r.user_national_code || '—'}
) : (
{records.map((r) => ( navigate(viewHref(r))} onEdit={() => navigate(editHref(r))} /> ))}
)}
setFilterOpen(false)} onApply={(f) => { setFilters(f); setPage(1); setFilterOpen(false); }} />
); }