fix: enforce 32-digit numeric EID validation with inline user feedback
EID input now strips non-numeric characters, shows a digit counter warning while typing, and enforces exactly 32 digits via Zod schemas across domain, order configurations, and order selections layers. Also installs missing input-otp dependency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
18b4c515a4
commit
9ae3d5e9c7
@ -138,6 +138,12 @@ function PhysicalSimOption({
|
||||
);
|
||||
}
|
||||
|
||||
function getEidWarning(eid: string): string | undefined {
|
||||
if (!eid) return undefined;
|
||||
if (eid.length < 32) return `EID must be 32 digits (${eid.length}/32)`;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function EidInput({
|
||||
simType,
|
||||
eid,
|
||||
@ -150,6 +156,8 @@ function EidInput({
|
||||
errors: Record<string, string | undefined>;
|
||||
}) {
|
||||
const [showEidInfo, setShowEidInfo] = useState(false);
|
||||
const warning = getEidWarning(eid);
|
||||
const hasError = Boolean(errors["eid"]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -176,16 +184,18 @@ function EidInput({
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
id="eid"
|
||||
value={eid}
|
||||
onChange={e => onEidChange(e.target.value)}
|
||||
className={`w-full px-4 py-3 bg-card border rounded-lg text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors ${
|
||||
errors["eid"] ? "border-destructive" : "border-border"
|
||||
onChange={e => onEidChange(e.target.value.replace(/\D/g, "").slice(0, 32))}
|
||||
className={`w-full px-4 py-3 bg-card border rounded-lg text-foreground font-mono tracking-wider placeholder:text-muted-foreground placeholder:font-sans placeholder:tracking-normal focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-colors ${
|
||||
hasError ? "border-destructive" : warning ? "border-warning" : "border-border"
|
||||
}`}
|
||||
placeholder="32-digit EID number"
|
||||
placeholder="32-digit EID number (numbers only)"
|
||||
maxLength={32}
|
||||
/>
|
||||
{errors["eid"] && <p className="text-destructive text-sm mt-2">{errors["eid"]}</p>}
|
||||
{hasError && <p className="text-destructive text-sm mt-2">{errors["eid"]}</p>}
|
||||
{!hasError && warning && <p className="text-warning text-sm mt-2">{warning}</p>}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@ -131,7 +131,10 @@ export const orderConfigurationsSchema = z.object({
|
||||
scheduledAt: z.string().optional(),
|
||||
accessMode: z.enum(ACCESS_MODE_VALUES).optional(),
|
||||
simType: z.enum(SIM_TYPE_VALUES).optional(),
|
||||
eid: z.string().optional(),
|
||||
eid: z
|
||||
.string()
|
||||
.regex(/^\d{32}$/, "EID must be exactly 32 digits")
|
||||
.optional(),
|
||||
isMnp: z.string().optional(),
|
||||
mnpNumber: z.string().optional(),
|
||||
mnpExpiry: z.string().optional(),
|
||||
@ -164,7 +167,10 @@ export const orderSelectionsSchema = z
|
||||
activationType: z.enum(ACTIVATION_TYPE_VALUES).optional(),
|
||||
scheduledAt: z.string().optional(),
|
||||
simType: z.enum(SIM_TYPE_VALUES).optional(),
|
||||
eid: z.string().optional(),
|
||||
eid: z
|
||||
.string()
|
||||
.regex(/^\d{32}$/, "EID must be exactly 32 digits")
|
||||
.optional(),
|
||||
isMnp: z.string().optional(),
|
||||
mnpNumber: z.string().optional(),
|
||||
mnpExpiry: z.string().optional(),
|
||||
|
||||
@ -426,8 +426,7 @@ export const simConfigureFormSchema = z
|
||||
simType: simCardTypeSchema,
|
||||
eid: z
|
||||
.string()
|
||||
.min(15, "EID must be at least 15 characters")
|
||||
.max(32, "EID must be at most 32 characters")
|
||||
.regex(/^\d{32}$/, "EID must be exactly 32 digits")
|
||||
.optional(),
|
||||
selectedAddons: z.array(z.string()).default([]),
|
||||
activationType: simActivationTypeSchema,
|
||||
@ -436,11 +435,11 @@ export const simConfigureFormSchema = z
|
||||
mnpData: simMnpFormSchema.optional(),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.simType === "eSIM" && (!data.eid || data.eid.trim().length < 15)) {
|
||||
if (data.simType === "eSIM" && (!data.eid || !/^\d{32}$/.test(data.eid.trim()))) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ["eid"],
|
||||
message: "EID is required for eSIM configuration",
|
||||
message: "EID must be exactly 32 digits",
|
||||
});
|
||||
}
|
||||
|
||||
@ -515,7 +514,10 @@ export const simOrderActivationRequestSchema = z
|
||||
.object({
|
||||
planSku: z.string().min(1, "Plan SKU is required"),
|
||||
simType: simCardTypeSchema,
|
||||
eid: z.string().min(15, "EID must be at least 15 characters").optional(),
|
||||
eid: z
|
||||
.string()
|
||||
.regex(/^\d{32}$/, "EID must be exactly 32 digits")
|
||||
.optional(),
|
||||
activationType: simActivationTypeSchema,
|
||||
scheduledAt: z.string().regex(YYYYMMDD_REGEX, MSG_SCHEDULED_DATE_FORMAT).optional(),
|
||||
addons: simOrderActivationAddonsSchema.optional(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user