Files
clinicpro/assets/admin/components/paymentMethods/PosFormModal.tsx
T
hamed 42d9ad26c5 Add tests and implementation for ServiceDetailPage and PriceInput components
- Implement PriceInput component tests to validate Persian and Arabic numeral handling, input formatting, and controlled behavior.
- Create ServiceDetailPage component with detailed service information, including pricing, insurance coverage, and editing capabilities.
- Add API tests for service item detail retrieval and coverage synchronization with insurance contracts.
- Ensure proper error handling and user feedback for service item retrieval and coverage management.
2026-07-18 12:10:49 +03:30

99 lines
3.3 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { toast } from 'sonner';
import Modal from '../ui/Modal';
import SearchableSelect from '../ui/SearchableSelect';
import DigitInput from '../ui/DigitInput';
import { BANK_OPTIONS } from './banks';
import {
useCreatePos,
useUpdatePos,
type Pos,
} from '../../hooks/usePaymentMethods';
/**
* فرم افزودن/ویرایش کارت‌خوان — پورت مبدأ ModalAddPose.jsx.
* اگر `pos` داده شود حالت ویرایش، وگرنه افزودن.
*/
export default function PosFormModal({
open,
pos,
onClose,
}: {
open: boolean;
pos: Pos | null;
onClose: () => void;
}) {
const isEdit = !!pos;
const [bankName, setBankName] = useState('');
const [terminalNumber, setTerminalNumber] = useState('');
const [accountNumber, setAccountNumber] = useState('');
const createMut = useCreatePos();
const updateMut = useUpdatePos();
const pending = createMut.isPending || updateMut.isPending;
useEffect(() => {
if (!open) return;
setBankName(pos?.bank_name ?? '');
setTerminalNumber(pos?.terminal_number ?? '');
setAccountNumber(pos?.account_number ?? '');
}, [open, pos]);
const submit = () => {
if (!bankName.trim()) { toast.error('نام بانک الزامی است'); return; }
if (!terminalNumber.trim()) { toast.error('شماره ترمینال الزامی است'); return; }
const body = {
bank_name: bankName.trim(),
terminal_number: terminalNumber.trim(),
account_number: accountNumber.trim(),
};
const onSuccess = () => {
toast.success(isEdit ? 'کارت خوان ویرایش شد' : 'کارت خوان اضافه شد');
onClose();
};
const onError = (e: unknown) => toast.error(e instanceof Error ? e.message : 'خطا در ذخیره');
if (isEdit && pos) {
updateMut.mutate({ uuid: pos.uuid, body }, { onSuccess, onError });
} else {
createMut.mutate(body, { onSuccess, onError });
}
};
return (
<Modal
open={open}
onClose={onClose}
title={isEdit ? 'ویرایش کارت خوان' : 'افزودن کارت خوان'}
size="sm"
footer={
<button className="btn primary" style={{ width: '100%' }} disabled={pending} onClick={submit}>
{pending ? '...' : isEdit ? 'ذخیره تغییرات' : 'اضافه کردن کارت خوان'}
</button>
}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div>
<label className="field-label">نام بانک</label>
<SearchableSelect
options={BANK_OPTIONS}
value={bankName || null}
onChange={(v) => setBankName(v != null ? String(v) : '')}
placeholder="انتخاب بانک"
/>
</div>
<div>
<label className="field-label">شماره ترمینال</label>
<DigitInput className="input" value={terminalNumber} onChange={setTerminalNumber} placeholder="شماره ترمینال" />
</div>
<div>
<label className="field-label">شماره حساب</label>
<DigitInput className="input" value={accountNumber} onChange={setAccountNumber} placeholder="شماره حساب" />
</div>
</div>
</Modal>
);
}