Files
web/utils/zod/phoneValidator.ts
2025-01-30 13:51:47 +01:00

88 lines
2.7 KiB
TypeScript

import {
isPossiblePhoneNumber,
ParseError,
parsePhoneNumber,
validatePhoneNumberLength,
} from "libphonenumber-js"
import { z } from "zod"
const enum ParseErrorMessage {
INVALID_COUNTRY = "INVALID_COUNTRY",
INVALID_LENGTH = "INVALID_LENGTH",
NOT_A_NUMBER = "NOT_A_NUMBER",
TOO_LONG = "TOO_LONG",
TOO_SHORT = "TOO_SHORT",
}
export function phoneValidator(
msg = "Required field",
invalidMsg = "Invalid type"
) {
return z
.string({ invalid_type_error: invalidMsg, required_error: msg })
.min(1, { message: msg })
.superRefine((value, ctx) => {
if (value) {
try {
const phoneNumber = parsePhoneNumber(value)
if (phoneNumber) {
if (isPossiblePhoneNumber(value, phoneNumber.country)) {
return validatePhoneNumberLength(value, phoneNumber.country)
} else {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Please enter a valid phone number",
})
}
}
} catch (error) {
if (error instanceof ParseError) {
/**
* Only setup for when we need proper validation,
* should probably move to .superRefine to be able
* to return different messages depending on error.
*/
switch (error.message) {
case ParseErrorMessage.INVALID_COUNTRY:
ctx.addIssue({
code: z.ZodIssueCode.custom,
message:
"The country selected and country code doesn't match",
})
break
case ParseErrorMessage.INVALID_LENGTH:
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Please enter a valid phone number",
})
break
case ParseErrorMessage.NOT_A_NUMBER:
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Please enter a number",
})
break
case ParseErrorMessage.TOO_LONG:
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "The number you have entered is too long",
})
break
case ParseErrorMessage.TOO_SHORT:
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "The number you have entered is too short",
})
break
}
} else {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "The number you have entered is not valid",
})
}
}
}
})
}