feat(payment): add payment detail endpoint and update payment model with order_id and patient_name

feat(appointment): enhance appointment detail page with time formatting and additional info
fix(payment): update payment query to fetch from the correct endpoint and adjust response structure
docs(api): add search parameter to payments API documentation and detail response structure
test(payment): add unit test for MellatGateway to verify null credentials handling
This commit is contained in:
hamed
2026-07-02 15:10:15 +03:30
parent 1e342a695f
commit ca71c49451
8 changed files with 181 additions and 27 deletions
+16 -6
View File
@@ -6,7 +6,7 @@ import { toast } from 'sonner';
import { api } from '../lib/api';
import type { ApiResponse } from '../lib/api';
import type { Appointment, AppointmentStatus } from '../types';
import { formatDate, formatDateTime } from '../lib/utils';
import { formatDate, formatDateTime, toDate } from '../lib/utils';
import PageHeader from '../components/ui/PageHeader';
import StatusBadge from '../components/ui/StatusBadge';
import ConfirmDialog from '../components/ui/ConfirmDialog';
@@ -22,6 +22,13 @@ const ALL_STATUSES: { value: AppointmentStatus; label: string }[] = [
{ value: 'expired', label: 'منقضی' },
];
const timeOf = (ts?: number | null) => {
const d = toDate(ts ?? null);
return d
? new Intl.DateTimeFormat('fa-IR-u-nu-latn', { hour: '2-digit', minute: '2-digit', hour12: false }).format(d)
: '—';
};
function InfoRow({ label, value }: { label: string; value: React.ReactNode }) {
return (
<div className="cp-info-row">
@@ -64,7 +71,8 @@ export default function AppointmentDetailPage() {
onError: (err: Error) => toast.error(err.message),
});
const appt = data?.data;
// پاسخ single تودرتو است: { data: { data: {...} } }
const appt: any = (data?.data as any)?.data ?? data?.data;
return (
<div>
@@ -96,10 +104,12 @@ export default function AppointmentDetailPage() {
<h3 className="font-semibold text-gray-800 mb-4">اطلاعات بیمار</h3>
<InfoRow label="نام بیمار" value={appt.patient_name} />
<InfoRow label="موبایل" value={<span dir="ltr">{appt.patient_mobile}</span>} />
<InfoRow label="پزشک" value={`دکتر ${appt.doctor_name}`} />
<InfoRow label="تاریخ نوبت" value={formatDate(appt.appointment_date)} />
<InfoRow label="ساعت شروع" value={appt.appointment_time} />
<InfoRow label="ساعت پایان" value={appt.end_time} />
<InfoRow label="پزشک" value={appt.doctor?.name ? `دکتر ${appt.doctor.name}` : null} />
<InfoRow label="تاریخ نوبت" value={formatDate(appt.slot_start)} />
<InfoRow label="ساعت شروع" value={timeOf(appt.slot_start)} />
<InfoRow label="ساعت پایان" value={timeOf(appt.slot_end)} />
{appt.patient_reason && <InfoRow label="علت مراجعه" value={appt.patient_reason} />}
{appt.note && <InfoRow label="توضیحات" value={appt.note} />}
<InfoRow label="تاریخ ثبت" value={formatDateTime(appt.created_at)} />
</div>