Files
clinicpro/assets/admin/pages/MyPatientsPage.tsx
T
hamed 8ad983310c feat: add clinic management and financial reporting features
- Implemented ClinicFormPage for adding new clinics with validation.
- Created MyFinancialPage to display financial summaries and charts.
- Developed MyPatientsPage for managing patient data with search and pagination.
- Added PreRegistrationsPage for handling pre-registration requests with approval and rejection functionalities.
- Introduced database migration for pre_registrations table.
- Built PreRegistrationController for managing pre-registration logic, including submission, approval, and rejection.
- Created PreRegistration entity and repository for handling pre-registration data.
2026-06-12 12:31:27 +03:30

87 lines
2.7 KiB
TypeScript

import React, { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { PhoneIcon } from '@heroicons/react/24/outline';
import { api } from '../lib/api';
import type { PaginatedResponse } from '../lib/api';
import { formatDate, formatNumber } from '../lib/utils';
import DataTable, { type Column } from '../components/ui/DataTable';
import Pagination from '../components/ui/Pagination';
import PageHeader from '../components/ui/PageHeader';
interface Patient {
uuid: string;
name: string;
mobile: string;
total_appointments: number;
last_appointment: number | null;
}
const EMPTY: Patient[] = [];
export default function MyPatientsPage() {
const [page, setPage] = useState(1);
const [search, setSearch] = useState('');
const limit = 20;
const { data, isLoading } = useQuery<PaginatedResponse<Patient>>({
queryKey: ['my-patients', page, search],
queryFn: () => api.get(`/api/v1/my/patients?page=${page}&limit=${limit}&search=${encodeURIComponent(search)}`),
});
const patients = data?.data ?? EMPTY;
const total = data?.meta?.totalRecords ?? 0;
const columns: Column<Patient>[] = [
{
key: 'name',
header: 'نام بیمار',
render: (p) => (
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<div className="avatar sm" style={{ background: 'linear-gradient(145deg, oklch(0.62 0.15 256), oklch(0.48 0.16 256))', flexShrink: 0 }}>
{(p.name || '؟').charAt(0)}
</div>
<span>{p.name || '—'}</span>
</div>
),
},
{
key: 'mobile',
header: 'موبایل',
render: (p) => (
<div style={{ display: 'flex', alignItems: 'center', gap: 6, direction: 'ltr' }}>
<PhoneIcon style={{ width: 14, height: 14, color: 'var(--text-3)' }} />
<span>{p.mobile}</span>
</div>
),
},
{
key: 'total_appointments',
header: 'تعداد نوبت',
render: (p) => <span className="badge blue">{formatNumber(p.total_appointments)}</span>,
},
{
key: 'last_appointment',
header: 'آخرین نوبت',
render: (p) => p.last_appointment ? formatDate(String(p.last_appointment)) : '—',
},
];
return (
<div className="page">
<PageHeader title="بیماران من" description={`${formatNumber(total)} بیمار`} />
<DataTable
columns={columns}
data={patients}
loading={isLoading}
searchValue={search}
onSearchChange={(v) => { setSearch(v); setPage(1); }}
searchPlaceholder="جستجوی نام یا موبایل..."
emptyMessage="بیماری یافت نشد"
/>
<Pagination page={page} total={total} limit={limit} onPageChange={setPage} />
</div>
);
}