From e1fb63eb0f269ef6289b6b3f8d5e8a7f15e3a740 Mon Sep 17 00:00:00 2001 From: hamed <15238-genius.ha@users.noreply.drupalcode.org> Date: Thu, 16 Jul 2026 09:45:04 +0330 Subject: [PATCH] feat: add DigitInput component; force English digits for phone & national code New reusable DigitInput normalizes Persian/Arabic digits to English on input, strips non-digits, enforces maxDigits, and is always LTR + numeric keyboard. Use it for the phone and national-code fields on the appointment create page (phone previously kept raw Persian digits; national code lost Persian digits to the \D strip). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../admin/components/ui/DigitInput.test.tsx | 29 +++++++++++++++++ assets/admin/components/ui/DigitInput.tsx | 32 +++++++++++++++++++ assets/admin/pages/AppointmentCreatePage.tsx | 11 ++++--- 3 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 assets/admin/components/ui/DigitInput.test.tsx create mode 100644 assets/admin/components/ui/DigitInput.tsx diff --git a/assets/admin/components/ui/DigitInput.test.tsx b/assets/admin/components/ui/DigitInput.test.tsx new file mode 100644 index 00000000..ee39a918 --- /dev/null +++ b/assets/admin/components/ui/DigitInput.test.tsx @@ -0,0 +1,29 @@ +import { describe, it, expect, vi } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/react'; +import DigitInput from './DigitInput'; + +describe('DigitInput — ورودی عددی با تبدیل رقم فارسی', () => { + it('converts Persian/Arabic digits to English (happy path)', () => { + const onChange = vi.fn(); + render(); + fireEvent.change(screen.getByPlaceholderText('p'), { target: { value: '۰۹۱۲٣٤' } }); + expect(onChange).toHaveBeenCalledWith('091234'); + }); + + it('strips non-digit characters (error/garbage input)', () => { + const onChange = vi.fn(); + render(); + fireEvent.change(screen.getByPlaceholderText('p'), { target: { value: '0a9 1-2b' } }); + expect(onChange).toHaveBeenCalledWith('0912'); + }); + + it('enforces maxDigits and handles empty input (boundary)', () => { + const onChange = vi.fn(); + render(); + const input = screen.getByPlaceholderText('p'); + fireEvent.change(input, { target: { value: '۱۲۳۴۵۶۷۸۹۰۱۲' } }); + expect(onChange).toHaveBeenCalledWith('1234567890'); + expect(input).toHaveAttribute('dir', 'ltr'); + expect(input).toHaveAttribute('inputmode', 'numeric'); + }); +}); diff --git a/assets/admin/components/ui/DigitInput.tsx b/assets/admin/components/ui/DigitInput.tsx new file mode 100644 index 00000000..dc7b10ae --- /dev/null +++ b/assets/admin/components/ui/DigitInput.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { toEnglishDigits } from '../../lib/utils'; + +interface DigitInputProps extends Omit, 'value' | 'onChange' | 'type'> { + value: string; + onChange: (value: string) => void; + /** حداکثر تعداد رقم (مثلاً ۱۱ برای موبایل، ۱۰ برای کد ملی). */ + maxDigits?: number; +} + +/** + * ورودیِ فقط-عددی که ارقام فارسی/عربی را همان لحظه به انگلیسی تبدیل می‌کند و هر + * کاراکتر غیررقمی را حذف می‌کند؛ همیشه LTR و کیبورد عددی. مناسب شماره تماس و کد ملی. + */ +export default function DigitInput({ value, onChange, maxDigits, ...rest }: DigitInputProps) { + const handleChange = (e: React.ChangeEvent) => { + let digits = toEnglishDigits(e.target.value).replace(/\D/g, ''); + if (maxDigits) digits = digits.slice(0, maxDigits); + onChange(digits); + }; + + return ( + + ); +} diff --git a/assets/admin/pages/AppointmentCreatePage.tsx b/assets/admin/pages/AppointmentCreatePage.tsx index 91008fc5..97178a50 100644 --- a/assets/admin/pages/AppointmentCreatePage.tsx +++ b/assets/admin/pages/AppointmentCreatePage.tsx @@ -8,6 +8,7 @@ import type { ApiResponse } from '../lib/api'; import { useAuthStore } from '../stores/authStore'; import PersianDateInput from '../components/ui/PersianDateInput'; import PriceInput from '../components/ui/PriceInput'; +import DigitInput from '../components/ui/DigitInput'; import SearchableSelect from '../components/ui/SearchableSelect'; import { WalletChargeLink } from '../components/AppointmentActions'; import { useDoctorBookingServices } from '../hooks/useDoctorBookingServices'; @@ -218,8 +219,8 @@ export default function AppointmentCreatePage() {
- setNationalCode(e.target.value.replace(/\D/g, '').slice(0, 10))} - placeholder="کد ملی مراجعه کننده" dir="ltr" inputMode="numeric" maxLength={10} /> +
)} @@ -246,14 +247,14 @@ export default function AppointmentCreatePage() {
- setMobile(e.target.value)} placeholder="شماره تماس مراجعه کننده" dir="ltr" /> +
- setNationalCode(e.target.value.replace(/\D/g, '').slice(0, 10))} - placeholder="کد ملی مراجعه کننده" dir="ltr" inputMode="numeric" maxLength={10} /> +