All files / src/lib validation.ts

100% Statements 15/15
100% Branches 18/18
100% Functions 4/4
100% Lines 14/14

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                                                                        29x               32x 32x                         18x 13x 13x                                   37x 37x 9x   28x 7x   21x               25x 25x    
// Validation utilities for forms and API requests
import { parseIndianPhone } from "@interioring/utils/validation/phone";
import {
	validateCustomerName as sharedValidateCustomerName,
	customerNameErrorMessage,
} 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;
}
 
// Re-export the canonical name pattern for any consumer that imports it
// directly (tests, debug tooling). Sourced from the shared validator.
export const CUSTOMER_NAME_ALLOWED_PATTERN = /^[\p{L}][\p{L}\p{M}\s.'-]*$/u;
 
/**
 * 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 ? customerNameErrorMessage(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, "");
}