ifixkart-storefront/components/PhoneInput.tsx

141 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
import { validatePhone } from '@/lib/validation';
interface PhoneInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
value?: string;
onChange: (val: string) => void;
label?: string;
error?: string;
required?: boolean;
className?: string;
placeholder?: string;
}
export const PhoneInput: React.FC<PhoneInputProps> = ({
value,
onChange,
label,
error: customError,
required = false,
className = '',
placeholder = '98765 43210',
disabled,
...props
}) => {
const [touched, setTouched] = useState(false);
// Normalize current value to bare 10 digits for input display
let displayValue = value || '';
if (displayValue.startsWith('+91')) displayValue = displayValue.slice(3);
else if (displayValue.startsWith('91') && displayValue.length === 12) displayValue = displayValue.slice(2);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
let raw = e.target.value.replace(/\D/g, ''); // keep only numbers
if (raw.length > 10) raw = raw.slice(0, 10);
onChange(raw);
};
const validationError = touched ? validatePhone(displayValue, required) : null;
const displayError = customError || validationError;
return (
<div className={`space-y-1 ${className}`}>
{label && (
<label className="block text-[13px] font-medium text-gray-700">
{label} {required && <span className="text-red-500">*</span>}
</label>
)}
<div className="relative flex rounded-lg shadow-sm">
{/* Fixed +91 Prefix Badge */}
<span className="inline-flex items-center px-3 rounded-l-lg border border-r-0 border-gray-300 bg-gray-50 text-gray-700 font-semibold text-[13px] select-none shrink-0">
🇮🇳 +91
</span>
<input
{...props}
type="tel"
inputMode="numeric"
pattern="[0-9]*"
maxLength={10}
value={displayValue}
onChange={handleChange}
onBlur={() => setTouched(true)}
disabled={disabled}
placeholder={placeholder}
className={`block w-full min-w-0 flex-1 rounded-none rounded-r-lg border text-[13px] px-3 py-2 transition-colors focus:outline-none focus:ring-2 focus:ring-primary/20 ${
displayError
? 'border-red-500 text-red-900 focus:border-red-500 focus:ring-red-200'
: 'border-gray-300 focus:border-primary text-gray-900'
} ${disabled ? 'bg-gray-100 cursor-not-allowed text-gray-500' : 'bg-white'}`}
/>
</div>
{displayError && (
<p className="text-[11px] font-medium text-red-500 mt-1 flex items-center gap-1">
<span></span> {displayError}
</p>
)}
</div>
);
};
interface PincodeInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
value?: string;
onChange: (val: string) => void;
label?: string;
error?: string;
required?: boolean;
className?: string;
}
export const PincodeInput: React.FC<PincodeInputProps> = ({
value,
onChange,
label,
error: customError,
required = false,
className = '',
placeholder = '6-digit PIN code (e.g. 560001)',
disabled,
...props
}) => {
const [touched, setTouched] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
let raw = e.target.value.replace(/\D/g, '');
if (raw.length > 6) raw = raw.slice(0, 6);
onChange(raw);
};
const validationError = touched && required && !value ? 'PIN code is required' : (touched && value && value.length !== 6 ? 'PIN code must be exactly 6 digits' : null);
const displayError = customError || validationError;
return (
<div className={`space-y-1 ${className}`}>
{label && (
<label className="block text-[13px] font-medium text-gray-700">
{label} {required && <span className="text-red-500">*</span>}
</label>
)}
<input
{...props}
type="text"
inputMode="numeric"
maxLength={6}
value={value || ''}
onChange={handleChange}
onBlur={() => setTouched(true)}
disabled={disabled}
placeholder={placeholder}
className={`block w-full rounded-lg border text-[13px] px-3 py-2 transition-colors focus:outline-none focus:ring-2 focus:ring-primary/20 ${
displayError
? 'border-red-500 text-red-900 focus:border-red-500 focus:ring-red-200'
: 'border-gray-300 focus:border-primary text-gray-900'
} ${disabled ? 'bg-gray-100 cursor-not-allowed text-gray-500' : 'bg-white'}`}
/>
{displayError && (
<p className="text-[11px] font-medium text-red-500 mt-1 flex items-center gap-1">
<span></span> {displayError}
</p>
)}
</div>
);
};