All files / src/scripts consultation-booking.ts

95.91% Statements 47/49
87.8% Branches 36/41
83.33% Functions 5/6
100% Lines 39/39

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                    8x 8x 8x 8x       9x     9x   8x 8x 8x     8x     3x 3x 3x 3x       8x 7x 7x     8x 8x 8x   8x 8x 8x     8x 1x 1x     7x   7x 7x               6x           6x 5x 5x 5x 5x         1x 1x     1x 1x        
// Client script for the standalone Free Consultation Booking form.
// Lightweight, framework-free (matches the marketplace's minimal-JS pattern).
// Client checks are for fast feedback only — 'api/book-consultation.ts' is the
// authoritative validator (shared 'lib/consultation.ts' rules).
//
// Exposed as an init function (not a module side effect) so it is unit-testable
// with a jsdom fixture; the .astro component calls it on load.
 
// Basic Indian-mobile shape check (server does the authoritative parse).
function looksLikeIndianMobile(raw: string): boolean {
	let digits = raw.replace(/\D/g, "");
	if (digits.length === 12 && digits.startsWith("91")) digits = digits.slice(2);
	if (digits.length === 11 && digits.startsWith("0")) digits = digits.slice(1);
	return /^[6-9]\d{9}$/.test(digits);
}
 
export function initConsultationBooking(): void {
	const form = document.getElementById(
		"consultation-form",
	) as HTMLFormElement | null;
	if (!form) return;
 
	const confirmation = document.getElementById("consultation-confirmation");
	const errorEl = document.getElementById("cb-error");
	const submitBtn = document.getElementById(
		"cb-submit",
	) as HTMLButtonElement | null;
	const refEl = document.getElementById("cb-ref");
 
	function showError(message: string): void {
		Iif (!errorEl) return;
		errorEl.textContent = message;
		errorEl.classList.remove("hidden");
		errorEl.focus?.();
	}
 
	function clearError(): void {
		if (!errorEl) return;
		errorEl.textContent = "";
		errorEl.classList.add("hidden");
	}
 
	form.addEventListener("submit", async (e) => {
		e.preventDefault();
		clearError();
 
		const fd = new FormData(form);
		const phone = String(fd.get("phone") ?? "").trim();
		const details = String(fd.get("details") ?? "").trim();
 
		// Client-side quick check (mirrors server messages).
		if (!phone || !looksLikeIndianMobile(phone)) {
			showError("Enter a valid 10-digit Indian mobile number.");
			return;
		}
 
		if (submitBtn) submitBtn.disabled = true;
 
		try {
			const res = await fetch("/api/book-consultation", {
				method: "POST",
				headers: { "Content-Type": "application/json" },
				body: JSON.stringify({
					phone,
					details: details || undefined,
				}),
			});
			const json = (await res.json().catch(() => ({}))) as {
				success?: boolean;
				error?: string;
				data?: { bookingRef?: string };
			};
 
			if (res.ok && json.success && json.data) {
				if (refEl) refEl.textContent = json.data.bookingRef ?? "—";
				form.classList.add("hidden");
				confirmation?.classList.remove("hidden");
				confirmation?.scrollIntoView?.({
					behavior: "smooth",
					block: "center",
				});
			} else {
				showError(json.error ?? "Something went wrong. Please try again.");
				Eif (submitBtn) submitBtn.disabled = false;
			}
		} catch {
			showError("Could not submit right now. Please try again.");
			Eif (submitBtn) submitBtn.disabled = false;
		}
	});
}