This commit is contained in:
2026-05-26 23:30:22 +12:00
parent 135a5a3b83
commit 91b22c6d60
27 changed files with 2401 additions and 88 deletions
+45
View File
@@ -4,6 +4,7 @@
import Icon from '$lib/components/Icon.svelte';
import { reveal } from '$lib/actions/reveal';
import type { BookingContent } from '$lib/types';
import { promoteJourney, trackEvent } from '$lib/analytics';
import { trackAb, type AbContext } from '$lib/ab';
type SuccessModalComponentType = typeof import('$lib/components/SuccessModal.svelte').default;
@@ -59,6 +60,7 @@
let sendClickedAt = 0;
let stepChanges = 0;
let journey: string[] = [];
let trackedFormStart = false;
let errors: Record<string, string> = {};
let submitting = false;
@@ -164,6 +166,14 @@
function noteInteraction() {
if (!firstInteractionAt) firstInteractionAt = Date.now();
if (!trackedFormStart) {
trackedFormStart = true;
trackEvent('lead_form_start', {
form_name: isCompactContactPage ? 'contact_compact' : 'booking_wizard',
step: step,
page_path: typeof window !== 'undefined' ? window.location.pathname : pagePath || ''
});
}
}
function clearError(field: string) {
@@ -175,6 +185,12 @@
selectedServices = selectedServices.includes(service)
? selectedServices.filter((s) => s !== service)
: [...selectedServices, service];
trackEvent('service_interest_select', {
form_name: isCompactContactPage ? 'contact_compact' : 'booking_wizard',
selected_service: service,
selection_state: selectedServices.includes(service) ? 'removed' : 'added',
step
});
clearError('services');
}
@@ -226,6 +242,11 @@
function goNext() {
noteInteraction();
if (!validateStep(step)) return;
trackEvent('lead_form_step_complete', {
form_name: isCompactContactPage ? 'contact_compact' : 'booking_wizard',
step,
next_step: step + 1
});
step += 1;
stepChanges += 1;
}
@@ -233,6 +254,11 @@
function goBack() {
noteInteraction();
if (step > 1) {
trackEvent('lead_form_step_back', {
form_name: isCompactContactPage ? 'contact_compact' : 'booking_wizard',
step,
previous_step: step - 1
});
step -= 1;
stepChanges += 1;
errors = {};
@@ -249,6 +275,12 @@
sendClickedAt = Date.now();
submitErrorDetail = '';
showErrorModal = false;
trackEvent('lead_form_submit_attempt', {
form_name: isCompactContactPage ? 'contact_compact' : 'booking_wizard',
selected_services: selectedServices.join(' | '),
step,
step_changes: stepChanges
});
try {
const res = await fetch('/api/submit', {
@@ -287,10 +319,23 @@
}
submitted = true;
trackEvent('lead_form_submit_success', {
form_name: isCompactContactPage ? 'contact_compact' : 'booking_wizard',
selected_services: selectedServices.join(' | '),
step_changes: stepChanges
});
// Link the visitor's session_events journey to the submitted email so
// the owner can review it from the CP dashboard. Without this call,
// session_events expire after 24h. See docs/server-side-analytics.md.
promoteJourney(email);
if (ab) trackAb({ ...ab, event_type: 'conversion', meta: { surface: 'booking_submit' } });
} catch (err: unknown) {
submitErrorDetail = err instanceof Error ? err.message : String(err);
showErrorModal = true;
trackEvent('lead_form_submit_error', {
form_name: isCompactContactPage ? 'contact_compact' : 'booking_wizard',
error_detail: submitErrorDetail.slice(0, 120)
});
} finally {
submitting = false;
}