refactor: update pricing input handling to use PriceInput component

- Replaced raw input fields for pricing with PriceInput component across various forms and modals to ensure consistent formatting and accessibility.
- Updated tests to reflect changes in pricing input handling, ensuring values are displayed in toman with proper formatting.
- Enhanced accessibility by adding aria-labels and aria-invalid attributes to PriceInput components.
- Adjusted UI elements to improve layout and user experience, particularly in forms related to service items and scheduling.
- Changed labels from "نمایش در نوبت‌دهی" to "نمایش در نوبت‌دهی آنلاین" for clarity.
This commit is contained in:
hamed
2026-08-09 08:38:23 +03:30
parent e48ab9a974
commit b49abee52f
18 changed files with 299 additions and 96 deletions
+6 -1
View File
@@ -12,6 +12,7 @@ import { formatRial, formatNumber, formatDate, formatResourceLimit } from '../li
import Modal from '../components/ui/Modal';
import ConfirmDialog from '../components/ui/ConfirmDialog';
import PageHeader from '../components/ui/PageHeader';
import PriceInput from '../components/ui/PriceInput';
import Pagination from '../components/ui/Pagination';
import { numericField } from '../lib/forms';
@@ -369,7 +370,11 @@ function PlansTab() {
</div>
<div className="field">
<label>قیمت (ریال) *</label>
<input {...numericField(periodForm.register('price_rials'))} />
<PriceInput
value={periodForm.watch('price_rials') ?? ''}
onChange={(v) => periodForm.setValue('price_rials', v)}
ariaLabel="قیمت دوره (ریال)"
/>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
+5 -2
View File
@@ -40,6 +40,7 @@ import Modal from "../components/ui/Modal";
import PageHeader from "../components/ui/PageHeader";
import Pagination from "../components/ui/Pagination";
import SearchableSelect from "../components/ui/SearchableSelect";
import PriceInput from "../components/ui/PriceInput";
import PatientRecordInfoForm from "../components/PatientRecordInfoForm";
import { usePermissions } from "../hooks/usePermissions";
import {
@@ -1470,8 +1471,10 @@ function MyPatientsPageInner() {
>
<div className="field">
<label>قیمت ویزیت (تومان)</label>
<input
{...numericField(form.register("visit_price_rials"))}
<PriceInput
value={watchVisit || ''}
onChange={(v) => form.setValue("visit_price_rials", v)}
ariaLabel="قیمت ویزیت (تومان)"
/>
</div>
<div className="field">
+2 -2
View File
@@ -62,7 +62,7 @@ const FIELD_LABELS: Record<string, string> = {
price_rials: 'قیمت پایه',
active: 'وضعیت',
duration_minutes: 'زمان متوسط',
bookable: 'نمایش در نوبت‌دهی',
bookable: 'نمایش در نوبت‌دهی آنلاین',
insurance_covered: 'پوشش بیمه',
inventory_package: 'پکیج کالا',
};
@@ -112,7 +112,7 @@ function InfoTab({ item }: { item: ServiceItem }) {
? <span className="badge blue" style={{ fontSize: 11 }}>{formatNumber(Number(item.duration_minutes))} دقیقه</span>
: '—'}
</Row>
<Row icon={<CalendarDaysIcon style={{ width: 15 }} />} label="نمایش در نوبت‌دهی">
<Row icon={<CalendarDaysIcon style={{ width: 15 }} />} label="نمایش در نوبت‌دهی آنلاین">
{item.bookable
? <span className="badge green" style={{ fontSize: 11 }}>فعال</span>
: <span className="badge gray" style={{ fontSize: 11 }}>غیرفعال</span>}
+25 -3
View File
@@ -14,6 +14,7 @@ import {
} from '@heroicons/react/24/outline';
import ConfirmDialog from '../components/ui/ConfirmDialog';
import Switch from '../components/ui/Switch';
import PriceInput from '../components/ui/PriceInput';
interface TaxHistoryRow {
tax_percent: number;
@@ -436,13 +437,27 @@ export default function SettingsPage() {
<div className="settings-grid" style={{ marginTop: 18 }}>
<Field label="مبلغ هر نوبت" hint="مبلغی که بیمار هنگام رزرو آنلاین پرداخت می‌کند (تومان).">
<div className="input-suffix">
<input {...numericField(register('appointment_fee_rials'))} className="input" style={{ maxWidth: 200 }} placeholder="15000" />
<PriceInput
className="input"
style={{ maxWidth: 200 }}
ariaLabel="مبلغ هر نوبت (تومان)"
placeholder="۱۵,۰۰۰"
value={watch('appointment_fee_rials') ? Number(watch('appointment_fee_rials')) : ''}
onChange={(v) => setValue('appointment_fee_rials', String(v), { shouldDirty: true })}
/>
<span className="suf">تومان</span>
</div>
</Field>
<Field label="هزینه ثابت پنل پیامک" hint="از مبلغ هر تراکنش کسر می‌شود (تومان).">
<div className="input-suffix">
<input {...numericField(register('sms_panel_fee_rials'))} className="input" style={{ maxWidth: 200 }} placeholder="150000" />
<PriceInput
className="input"
style={{ maxWidth: 200 }}
ariaLabel="هزینه ثابت پنل پیامک (تومان)"
placeholder="۱۵۰,۰۰۰"
value={watch('sms_panel_fee_rials') ? Number(watch('sms_panel_fee_rials')) : ''}
onChange={(v) => setValue('sms_panel_fee_rials', String(v), { shouldDirty: true })}
/>
<span className="suf">تومان</span>
</div>
</Field>
@@ -532,7 +547,14 @@ export default function SettingsPage() {
</div>
</Field>
<Field label="هزینه هر پیامک" hint="مبلغ کسرشده از کیف پول به ازای هر پیامک ارسالی (تومان). مبنای محاسبهٔ تعداد پیامک از موجودی.">
<input {...numericField(register('sms_price_rials'))} className="input" style={{ maxWidth: 200 }} placeholder="50" />
<PriceInput
className="input"
style={{ maxWidth: 200 }}
ariaLabel="هزینه هر پیامک (تومان)"
placeholder="۵۰"
value={watch('sms_price_rials') ? Number(watch('sms_price_rials')) : ''}
onChange={(v) => setValue('sms_price_rials', String(v), { shouldDirty: true })}
/>
</Field>
</div>
)}
+6 -3
View File
@@ -17,6 +17,7 @@ import { usePaymentConfig } from '../hooks/usePaymentConfig';
import { formatRial, formatNumber, formatDateTime, tomanToRial } from '../lib/utils';
import Modal from '../components/ui/Modal';
import Pagination from '../components/ui/Pagination';
import PriceInput from '../components/ui/PriceInput';
import SearchableSelect from '../components/ui/SearchableSelect';
import PageHeader from '../components/ui/PageHeader';
import FeatureGate from '../components/ui/FeatureGate';
@@ -536,9 +537,11 @@ function SmsWalletPageInner() {
<div className="field">
<label>مبلغ (تومان)</label>
<input
{...numericField(chargeForm.register('amount_rials'))}
placeholder="50000"
<PriceInput
value={watchAmount ?? ''}
onChange={(v) => chargeForm.setValue('amount_rials', v, { shouldValidate: true })}
placeholder="۵۰,۰۰۰"
ariaLabel="مبلغ شارژ (تومان)"
/>
{chargeForm.formState.errors.amount_rials && (
<span className="field-error">{chargeForm.formState.errors.amount_rials.message}</span>