feat: port payment management tab from tauri to admin dashboard
Add per-clinic payment methods (bank accounts + POS/card-reader devices)
under the "مدیریت پرداخت" settings tab at /admin/my-financial, ported from
clinic-pro-tauri's mock-only PaymentManagement tab into a real persisted
feature. These records are referenceable (by uuid) from patient invoices to
record which method a service payment was made with.
Backend (new src/PaymentMethod domain):
- BankAccount + Pos entities, repositories, PaymentMethodService (validation,
ownership scoping, create/update/toggle logic).
- Thin PaymentMethodController exposing /api/v1/my/payment-methods/{bank-accounts,pos}
(GET/POST/PUT + PATCH .../status), guarded to clinic/doctor/secretary/admin.
- Migration for bank_accounts + pos_devices tables.
- Functional tests (success + validation/404/403 + empty boundaries).
- docs/api/payment-method.md.
Frontend:
- Replace MyFinancialPage content with the payment-management UI (two tabs,
tables, add/edit modals, status toggle) using the admin design system.
- usePaymentMethods hook (TanStack Query) + presentational components.
- Update page test to cover tabs, data, empty state and the add modal.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { PencilIcon } from '@heroicons/react/24/outline';
|
||||
import DataTable, { type Column } from '../ui/DataTable';
|
||||
import StatusToggle from './StatusToggle';
|
||||
import type { Pos } from '../../hooks/usePaymentMethods';
|
||||
|
||||
/**
|
||||
* جدول کارتخوانها — پورت مبدأ PoseList.jsx.
|
||||
* ستونها: نام بانک، شماره سریال، شماره ترمینال، وضعیت، عملیات.
|
||||
*/
|
||||
export default function PosTable({
|
||||
data,
|
||||
loading,
|
||||
togglingUuid,
|
||||
onToggle,
|
||||
onEdit,
|
||||
onAdd,
|
||||
}: {
|
||||
data: Pos[];
|
||||
loading?: boolean;
|
||||
togglingUuid: string | null;
|
||||
onToggle: (uuid: string) => void;
|
||||
onEdit: (pos: Pos) => void;
|
||||
onAdd: () => void;
|
||||
}) {
|
||||
const columns: Column<Pos>[] = [
|
||||
{ key: 'bank_name', header: 'نام بانک' },
|
||||
{ key: 'serial_number', header: 'شماره سریال', render: (r) => r.serial_number || '—' },
|
||||
{ key: 'terminal_number', header: 'شماره ترمینال', render: (r) => r.terminal_number || '—' },
|
||||
{
|
||||
key: 'is_active',
|
||||
header: 'وضعیت',
|
||||
render: (r) => (
|
||||
<StatusToggle
|
||||
active={r.is_active}
|
||||
disabled={togglingUuid === r.uuid}
|
||||
onToggle={() => onToggle(r.uuid)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<p style={{ fontSize: 13.5, color: 'var(--text-2)', margin: '0 0 14px' }}>
|
||||
مدیریت و اضافه کردن دستگاه های کارت خوان موجود
|
||||
</p>
|
||||
<DataTable<Pos>
|
||||
columns={columns}
|
||||
data={data}
|
||||
loading={loading}
|
||||
emptyMessage="هنوز کارت خوانی ثبت نشده است"
|
||||
emptyAction={<button className="btn primary sm" onClick={onAdd}>افزودن کارت خوان</button>}
|
||||
actions={(r) => (
|
||||
<button
|
||||
className="btn sm ghost"
|
||||
style={{ color: 'var(--accent)' }}
|
||||
onClick={() => onEdit(r)}
|
||||
>
|
||||
<PencilIcon style={{ width: 14 }} />
|
||||
ویرایش
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user