Files
clinicpro/assets/admin/pages/PaymentDetailPage.tsx
T
hamed ca71c49451 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
2026-07-02 15:10:15 +03:30

98 lines
4.0 KiB
TypeScript

import React from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import { api } from '../lib/api';
import type { ApiResponse } from '../lib/api';
import type { Payment } from '../types';
import { formatDate, formatDateTime, formatRial } from '../lib/utils';
import PageHeader from '../components/ui/PageHeader';
import StatusBadge from '../components/ui/StatusBadge';
const PAYMENT_TYPE_LABELS: Record<string, string> = {
appointment: 'نوبت',
subscription: 'اشتراک',
sms_wallet: 'کیف پول پیامک',
};
function InfoRow({ label, value }: { label: string; value: React.ReactNode }) {
return (
<div className="cp-info-row">
<span className="cp-info-label text-sm">{label}</span>
<span className="cp-info-value">{value ?? '—'}</span>
</div>
);
}
export default function PaymentDetailPage() {
const { uuid } = useParams<{ uuid: string }>();
const navigate = useNavigate();
const { data, isLoading } = useQuery({
queryKey: ['payment', uuid],
queryFn: () => api.get<ApiResponse<Payment>>(`/api/v1/admin/payments/${uuid}`),
enabled: !!uuid,
});
const payment = data?.data;
return (
<div>
<PageHeader
title="جزئیات پرداخت"
breadcrumbs={[
{ label: 'داشبورد', to: '/admin/dashboard' },
{ label: 'پرداخت‌ها', to: '/admin/payments' },
{ label: 'جزئیات' },
]}
action={
<button onClick={() => navigate('/admin/payments')}
className="flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-100 transition-colors">
<ArrowRightIcon className="w-4 h-4" />
بازگشت
</button>
}
/>
{isLoading ? (
<div className="cp-card p-6 space-y-3">
{Array.from({ length: 6 }).map((_, i) => (
<div key={i} className="h-8 rounded-lg skeleton" />
))}
</div>
) : payment ? (
<div className="max-w-lg">
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-6">
<InfoRow label="شناسه" value={<span dir="ltr" className="font-mono text-xs">{payment.uuid}</span>} />
<InfoRow label="شماره سفارش" value={payment.order_id ? <span dir="ltr" className="font-mono text-xs">{payment.order_id}</span> : null} />
<InfoRow label="بیمار" value={payment.patient_name} />
<InfoRow label="موبایل" value={<span dir="ltr">{payment.patient_mobile}</span>} />
<InfoRow label="نوع" value={PAYMENT_TYPE_LABELS[payment.type ?? ''] ?? payment.type} />
<InfoRow label="مبلغ" value={formatRial(payment.amount)} />
<InfoRow label="وضعیت" value={<StatusBadge type="payment" value={payment.status} />} />
<InfoRow label="درگاه" value={<span className="uppercase">{payment.gateway}</span>} />
<InfoRow label="شماره مرجع" value={payment.ref_id ? <span dir="ltr" className="font-mono text-xs">{payment.ref_id}</span> : null} />
<InfoRow label="تاریخ پرداخت" value={formatDateTime(payment.paid_at)} />
<InfoRow label="تاریخ ثبت" value={formatDateTime(payment.created_at)} />
{payment.appointment_uuid && (
<InfoRow
label="نوبت مرتبط"
value={
<a href={`/admin/appointments/${payment.appointment_uuid}`}
className="text-primary-600 hover:underline text-xs font-mono">
مشاهده نوبت
</a>
}
/>
)}
</div>
</div>
) : (
<div className="bg-white rounded-2xl border border-gray-100 p-16 text-center text-gray-400">
پرداختی یافت نشد
</div>
)}
</div>
);
}