Files
clinicpro/assets/admin/lib/forms.ts
T
hamedandClaude Opus 4.8 00cb9aaa1a feat(admin): normalize Persian/Arabic digits in every numeric field
Users typing on a Persian keyboard produced two distinct failures. Fields with
type="number" silently returned an empty string — the browser rejects Persian
digits, so the value was lost and saved as empty or zero. Text fields passed the
Persian characters straight through to the database, where a mobile stored as
۰۹۱۲… never matches 09… again. The secretary form hit the second case with no
validation at all.

Frontend:
- Adds digitsOnly() and the national-code schemas to lib/utils, plus lib/forms
  with numericField()/latinDigitsField() wrappers for React Hook Form fields.
- Converts every type="number" input to type="text" inputMode="numeric" with
  digit normalization; none remain. Fields that legitimately carry non-digits
  (sheba, landline) only get the digits translated, keeping IR and separators.
- Points the patient national-code and mobile schemas at the shared normalizing
  schemas, which accept Persian input instead of rejecting it.
- Drops two duplicate local digit converters in favour of the shared helper.

Backend:
- Adds NumericFieldNormalizerSubscriber, translating digits in whitelisted
  numeric keys of JSON request bodies under /api/v1/ before controllers run, so
  nobat724_front and clinic-pro-tauri are covered too. Translation only — no
  characters are stripped, non-string values and other keys are untouched.

Three component tests asserted on role="spinbutton" and numeric input values;
both are properties of type="number", so they were updated to match the new
text inputs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 10:38:56 +03:30

45 lines
1.6 KiB
TypeScript

import type { UseFormRegisterReturn } from 'react-hook-form';
import { digitsOnly, toEnglishDigits } from './utils';
/**
* فیلدهای عددی React Hook Form: ارقام فارسی/عربی را حین تایپ به لاتین تبدیل می‌کند.
*
* چرا `type="text"`؟ چون `type="number"` روی ارقام فارسی مقدار را نامعتبر می‌داند
* و `e.target.value` رشتهٔ خالی برمی‌گرداند — یعنی داده از دست می‌رود و هیچ
* onChange‌ای نجاتش نمی‌دهد.
*/
type NumericFieldProps = UseFormRegisterReturn & {
type: 'text';
inputMode: 'numeric';
dir: 'ltr';
};
function wrap(
reg: UseFormRegisterReturn,
normalize: (raw: string) => string,
): NumericFieldProps {
return {
...reg,
type: 'text',
inputMode: 'numeric',
dir: 'ltr',
onChange: (event: { target: any; type?: any }) => {
event.target.value = normalize(String(event.target.value ?? ''));
return reg.onChange(event);
},
};
}
/** فقط ارقام لاتین — برای موبایل، کد ملی، مبلغ، درصد، تعداد. */
export function numericField(reg: UseFormRegisterReturn, maxDigits?: number): NumericFieldProps {
return wrap(reg, (raw) => digitsOnly(raw, maxDigits));
}
/**
* فقط ترجمهٔ رقم؛ جداکننده‌ها و حروف حفظ می‌شوند — برای شبا (`IR…`) و
* تلفن ثابت (`021-1234…`).
*/
export function latinDigitsField(reg: UseFormRegisterReturn): NumericFieldProps {
return wrap(reg, toEnglishDigits);
}