Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 51x 77x 77x 18x 13x 13x 81x 81x 13x 68x 7x 61x 54x 54x 11x | // Validation utilities for forms and API requests
import { parseIndianPhone } from "@interioring/utils/validation/phone/core";
import {
type CustomerNameError,
validateCustomerName as sharedValidateCustomerName,
} from "@interioring/utils/validation/customer-name";
// Type for incoming inquiry request body (before validation)
export interface InquiryRequestBody {
proId?: string;
projectId?: string;
customerName?: string;
customerPhone?: string;
customerEmail?: string;
requirement?: string;
budget?: string;
type?: string;
requirementType?: string;
requirementDescription?: string;
sourceType?: string;
sourcePage?: string;
}
// Type for validated inquiry request
export interface ValidatedInquiryRequest {
proId: string;
customerName: string;
customerPhone: string;
projectId?: string;
customerEmail?: string;
requirement?: string;
budget?: string;
}
// Homeowner wording. The shared messages ("Customer name is required") are
// written for pros entering a client in the Portal and for the API, so the
// marketplace keeps the rule but says it to the person whose name it is.
const NAME_ERROR_MESSAGES: Record<CustomerNameError, string> = {
required: "Please enter your name.",
too_short: "Please enter your name (at least 2 letters).",
invalid_chars:
"Your name can only contain letters, spaces, apostrophes, periods and hyphens.",
};
/**
* Validates a customer name. Thin wrapper over `@interioring/utils` that
* preserves the existing string-or-null return contract for marketplace
* form callers that show `result.message` directly in error UI.
*/
export function validateCustomerName(name: string): string | null {
const error = sharedValidateCustomerName(name);
return error ? NAME_ERROR_MESSAGES[error] : null;
}
/**
* Type guard to validate required inquiry fields
*/
export function isValidInquiryRequest(
body: unknown,
): body is InquiryRequestBody & {
proId: string;
customerName: string;
customerPhone: string;
} {
if (typeof body !== "object" || body === null) return false;
const b = body as Record<string, unknown>;
return (
typeof b.proId === "string" &&
b.proId.length > 0 &&
typeof b.customerName === "string" &&
b.customerName.length > 0 &&
typeof b.customerPhone === "string" &&
b.customerPhone.length > 0
);
}
/**
* Validate Indian phone number format. Returns an error message if invalid,
* or null if valid. Backed by libphonenumber-js (via @interioring/phone) so it
* accepts any Indian mobile format the user might type — with or without
* country code, with or without spaces/dashes — and rejects landlines for
* the marketplace inquiry flow (WhatsApp/SMS only deliver to mobiles).
*/
export function validateIndianPhoneNumber(phone: string): string | null {
const parsed = parseIndianPhone(phone);
if (!parsed) {
return "Enter a valid 10-digit Indian mobile number.";
}
if (parsed.type !== "mobile") {
return "Phone number must be a mobile (we use WhatsApp/SMS to confirm inquiries).";
}
return null;
}
/**
* Extract digits from phone number string. Strips +91, spaces, and other
* formatting via the canonical parser so callers always see consistent input.
*/
export function extractPhoneDigits(phone: string): string {
const parsed = parseIndianPhone(phone);
return parsed ? parsed.e164.slice(3) : phone.replace(/\D/g, "");
}
/**
* The value a 10-digit mobile box should hold after an input event, typed or
* pasted. A pasted "90000 10701", "+91 90000 10701" or "090000 10701" becomes
* "9000010701"; a partial number keeps its digits so typing still works.
*
* The box must not cap its raw length at 10 (`maxLength={10}`): the browser
* truncates a paste BEFORE this runs, so "90000 10701" arrived as
* "90000 1070" and lost its last digit (#1085 QA).
*/
export function toMobileInputDigits(value: string): string {
return extractPhoneDigits(value).slice(0, 10);
}
|