// ─────────────────────────────────────────────────────────────────────────────
// GoHighLevel Intent Quiz — Engine
// ─────────────────────────────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', function () {
// ── Question meta ──────────────────────────────────────────────────────────
var QUESTIONS = [
{ key: 'roof_condition' },
{ key: 'urgency' },
{ key: 'property_ownership' },
{ key: 'other_quotes' },
{ key: 'decision_timeline' },
{ key: 'work_type' },
{ key: 'storm_insurance' },
];
var TOTAL_QUESTIONS = QUESTIONS.length; // 7
// ── State ──────────────────────────────────────────────────────────────────
var answers = new Array(TOTAL_QUESTIONS).fill(null); // { points, label }
var currentStep = 0;
// ── Branding ───────────────────────────────────────────────────────────────
try {
var c = QUIZ_CONFIG;
document.documentElement.style.setProperty('--accent', c.primaryColor);
document.documentElement.style.setProperty('--accent-dk', darken(c.primaryColor, 15));
if (c.logoUrl) {
document.getElementById('logoWrap').innerHTML =
'

';
}
document.getElementById('logoText').textContent = c.businessName;
var phoneEl = document.getElementById('nurtureBizPhone');
if (phoneEl) phoneEl.textContent = c.businessPhone;
} catch (e) {
console.warn('Quiz: config not loaded or incomplete.', e);
}
// ── Bind option buttons ────────────────────────────────────────────────────
for (var s = 0; s < TOTAL_QUESTIONS; s++) {
(function (stepIdx) {
var stepEl = document.getElementById('step-' + stepIdx);
var nextBtn = document.getElementById('next-' + stepIdx);
if (!stepEl) return;
// Option selection
stepEl.querySelectorAll('.option-btn').forEach(function (btn) {
btn.addEventListener('click', function () {
stepEl.querySelectorAll('.option-btn').forEach(function (b) {
b.classList.remove('selected');
});
btn.classList.add('selected');
answers[stepIdx] = {
points: parseInt(btn.getAttribute('data-points'), 10),
label: btn.getAttribute('data-label'),
};
if (nextBtn) nextBtn.disabled = false;
});
});
// Next
if (nextBtn) {
nextBtn.addEventListener('click', function () { goToStep(stepIdx + 1); });
}
// Back
var backBtn = stepEl.querySelector('.btn-back');
if (backBtn) {
backBtn.addEventListener('click', function () { goToStep(stepIdx - 1); });
}
})(s);
}
// Back button on the form step (step 7)
var formStep = document.getElementById('step-7');
var formBackBtn = formStep && formStep.querySelector('.btn-back');
if (formBackBtn) {
formBackBtn.addEventListener('click', function () { goToStep(TOTAL_QUESTIONS - 1); });
}
// ── Navigation ─────────────────────────────────────────────────────────────
function goToStep(idx) {
var from = document.getElementById('step-' + currentStep);
if (from) from.classList.remove('active');
currentStep = idx;
var to = document.getElementById('step-' + currentStep);
if (to) to.classList.add('active');
updateProgress(currentStep);
window.scrollTo({ top: 0, behavior: 'smooth' });
}
function updateProgress(stepIdx) {
var pct = Math.min(100, Math.round((stepIdx / (TOTAL_QUESTIONS + 1)) * 100));
var label = stepIdx < TOTAL_QUESTIONS
? 'Question ' + (stepIdx + 1) + ' of ' + TOTAL_QUESTIONS
: 'Almost done — enter your details';
var fill = document.getElementById('progressFill');
var lbl = document.getElementById('progressLabel');
if (fill) fill.style.width = pct + '%';
if (lbl) lbl.textContent = label;
}
// ── Scoring ────────────────────────────────────────────────────────────────
function calcScore() {
return answers.reduce(function (sum, a) { return sum + (a ? a.points : 0); }, 0);
}
function intentLevel(score) {
var threshold = (typeof QUIZ_CONFIG !== 'undefined') ? QUIZ_CONFIG.intentThreshold : 55;
return score >= threshold ? 'HIGH' : 'LOW';
}
// ── Form submit ────────────────────────────────────────────────────────────
var leadForm = document.getElementById('leadForm');
if (leadForm) {
leadForm.addEventListener('submit', async function (e) {
e.preventDefault();
if (!validateForm()) return;
var btn = document.getElementById('submitBtn');
btn.classList.add('loading');
btn.disabled = true;
var score = calcScore();
var level = intentLevel(score);
var payload = buildPayload(score, level);
try { await submitToGHL(payload); }
catch (err) { console.warn('GHL webhook failed — showing results anyway.', err); }
showResult(score, level);
});
}
// ── Phone formatter ────────────────────────────────────────────────────────
var phoneInput = document.getElementById('phone');
if (phoneInput) {
phoneInput.addEventListener('input', function () {
var digits = this.value.replace(/\D/g, '').slice(0, 10);
if (digits.length <= 3) this.value = digits;
else if (digits.length <= 6) this.value = '(' + digits.slice(0,3) + ') ' + digits.slice(3);
else this.value = '(' + digits.slice(0,3) + ') ' + digits.slice(3,6) + '-' + digits.slice(6);
});
}
// ── Build GHL payload ──────────────────────────────────────────────────────
function buildPayload(score, level) {
var cfg = (typeof QUIZ_CONFIG !== 'undefined') ? QUIZ_CONFIG : {};
var answerMap = {};
QUESTIONS.forEach(function (q, i) {
answerMap[q.key] = answers[i] ? answers[i].label : 'Not answered';
});
return {
first_name: val('firstName'),
last_name: val('lastName'),
email: val('email'),
phone: val('phone'),
address1: val('address'),
customData: Object.assign({
intent_score: score,
intent_level: level,
ghl_tags: level === 'HIGH' ? (cfg.highIntentTags || []) : (cfg.lowIntentTags || []),
pipeline_name: level === 'HIGH' ? (cfg.highIntentPipeline || '') : (cfg.lowIntentPipeline || ''),
pipeline_stage: level === 'HIGH' ? (cfg.highIntentStage || '') : (cfg.lowIntentStage || ''),
source: 'Roofing Intent Quiz',
quiz_submitted_at: new Date().toISOString(),
}, answerMap),
};
}
function val(id) {
var el = document.getElementById(id);
return el ? el.value.trim() : '';
}
async function submitToGHL(payload) {
var url = (typeof QUIZ_CONFIG !== 'undefined') ? QUIZ_CONFIG.webhookUrl : '';
if (!url || url.indexOf('YOUR_WEBHOOK') !== -1) {
console.warn('GHL webhook URL not configured.');
return;
}
var res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error('GHL responded ' + res.status);
}
// ── Show result screen ─────────────────────────────────────────────────────
function showResult(score, level) {
document.querySelectorAll('.step').forEach(function (s) { s.style.display = 'none'; });
document.querySelector('.progress-bar-wrap').style.display = 'none';
document.querySelector('.progress-label').style.display = 'none';
var arcLen = 251.2;
var filled = (score / 100) * arcLen;
var cfg = (typeof QUIZ_CONFIG !== 'undefined') ? QUIZ_CONFIG : {};
if (level === 'HIGH') {
if (cfg.highIntentRedirectUrl) { window.location.href = cfg.highIntentRedirectUrl; return; }
document.getElementById('resultHigh').classList.add('active');
document.getElementById('scoreDisplayHigh').textContent = score;
animateArc('arcFillHigh', arcLen, filled);
renderCalendar(cfg.calendarUrl || '');
} else {
if (cfg.lowIntentRedirectUrl) { window.location.href = cfg.lowIntentRedirectUrl; return; }
document.getElementById('resultLow').classList.add('active');
document.getElementById('scoreDisplayLow').textContent = score;
animateArc('arcFillLow', arcLen, filled);
}
window.scrollTo({ top: 0, behavior: 'smooth' });
}
function animateArc(arcId, total, filled) {
var arc = document.getElementById(arcId);
if (!arc) return;
var current = total;
var target = total - filled;
var step = (current - target) / 40;
var timer = setInterval(function () {
current -= step;
if (current <= target) { current = target; clearInterval(timer); }
arc.setAttribute('stroke-dashoffset', current.toFixed(2));
}, 20);
}
function renderCalendar(url) {
var container = document.getElementById('calendarContainer');
if (!container) return;
if (!url || url.indexOf('YOUR_CALENDAR') !== -1) {
container.innerHTML =
'
' +
'
📅 Calendar not configured yet.
' +
'Set calendarUrl in config.js to show your GHL booking widget.
';
return;
}
container.innerHTML =
'
';
}
// ── Validation ─────────────────────────────────────────────────────────────
function validateForm() {
var ok = true;
function check(id, testFn, errId) {
var input = document.getElementById(id);
var err = document.getElementById(errId);
var pass = input && testFn(input.value.trim());
if (input) input.classList.toggle('error', !pass);
if (err) err.classList.toggle('visible', !pass);
if (!pass) ok = false;
}
check('firstName', function (v) { return v.length >= 2; }, 'err-firstName');
check('lastName', function (v) { return v.length >= 2; }, 'err-lastName');
check('email', function (v) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v); }, 'err-email');
check('phone', function (v) { return v.replace(/\D/g,'').length >= 10; }, 'err-phone');
return ok;
}
// ── Utilities ──────────────────────────────────────────────────────────────
function darken(hex, pct) {
var n = parseInt(hex.replace('#', ''), 16);
var r = Math.max(0, (n >> 16) - pct);
var g = Math.max(0, ((n >> 8) & 0xff) - pct);
var b = Math.max(0, (n & 0xff) - pct);
return '#' + [r, g, b].map(function (v) { return v.toString(16).padStart(2, '0'); }).join('');
}
}); // end DOMContentLoaded