feat: implement MobileInput component for standardized mobile number input and update related forms to use it
This commit is contained in:
@@ -6,9 +6,11 @@ import { XMarkIcon } from '@heroicons/react/24/outline';
|
||||
import { toast } from 'sonner';
|
||||
import { api } from '../../lib/api';
|
||||
import type { ApiResponse } from '../../lib/api';
|
||||
import MobileInput from './MobileInput';
|
||||
import { iranMobileSchema } from '../../lib/utils';
|
||||
|
||||
const inviteSchema = z.object({
|
||||
mobile: z.string().regex(/^09\d{9}$/, 'شماره موبایل ۱۱ رقمی با 09 شروع میشود'),
|
||||
mobile: iranMobileSchema,
|
||||
name: z.string().optional(),
|
||||
specialty: z.string().optional(),
|
||||
});
|
||||
@@ -47,7 +49,7 @@ export default function InviteDoctorModal({ clinicUuid, onClose, onInvited }: Pr
|
||||
<div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
||||
<div>
|
||||
<label style={{ fontSize: 13, fontWeight: 600, display: 'block', marginBottom: 6 }}>شماره موبایل پزشک *</label>
|
||||
<input className="input" dir="ltr" placeholder="09xxxxxxxxx" {...register('mobile')} />
|
||||
<MobileInput className="input" hasError={!!errors.mobile} {...register('mobile')} />
|
||||
{errors.mobile && <div className="err-text">{errors.mobile.message}</div>}
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { sanitizeMobileInput } from '../../lib/utils';
|
||||
|
||||
type Props = React.InputHTMLAttributes<HTMLInputElement> & {
|
||||
hasError?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* فیلد شماره موبایل ایران: ارقام فارسی/عربی را به انگلیسی تبدیل میکند، فقط رقم میپذیرد و حداکثر ۱۱ رقم.
|
||||
* سازگار با react-hook-form `register` (event-based onChange) و حالت controlled.
|
||||
* قبل از فراخوانی onChange، مقدار DOM پاکسازی میشود تا value در state هم تمیز ذخیره شود.
|
||||
*/
|
||||
export default React.forwardRef<HTMLInputElement, Props>(function MobileInput(
|
||||
{ hasError, className, placeholder, style, onChange, ...rest },
|
||||
ref,
|
||||
) {
|
||||
const handle = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
e.target.value = sanitizeMobileInput(e.target.value);
|
||||
onChange?.(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<input
|
||||
{...rest}
|
||||
ref={ref}
|
||||
type="tel"
|
||||
inputMode="numeric"
|
||||
dir="ltr"
|
||||
maxLength={11}
|
||||
placeholder={placeholder ?? '09xxxxxxxxx'}
|
||||
className={className ?? 'cp-input'}
|
||||
style={hasError ? { borderColor: 'var(--danger)', ...(style || {}) } : style}
|
||||
onChange={handle}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -3,6 +3,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { DevicePhoneMobileIcon, CheckCircleIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
||||
import { api } from '../../lib/api';
|
||||
import type { ApiResponse } from '../../lib/api';
|
||||
import { sanitizeMobileInput } from '../../lib/utils';
|
||||
|
||||
interface NotificationMobileData {
|
||||
notification_mobile: string | null;
|
||||
@@ -126,9 +127,11 @@ export default function NotificationMobileCard({ target }: Props) {
|
||||
<input
|
||||
type="tel"
|
||||
value={newMobile}
|
||||
onChange={e => setNewMobile(e.target.value)}
|
||||
placeholder="09XXXXXXXXX"
|
||||
dir="ltr"
|
||||
inputMode="numeric"
|
||||
maxLength={11}
|
||||
onChange={e => setNewMobile(sanitizeMobileInput(e.target.value))}
|
||||
placeholder="09XXXXXXXXX"
|
||||
style={{
|
||||
width: '100%', maxWidth: 220, height: 38, padding: '0 12px',
|
||||
borderRadius: 'var(--r-sm)', border: '1px solid var(--border)',
|
||||
|
||||
Reference in New Issue
Block a user