Add calculator UX enhancements and submission tracking

- Button loading spinner + disable on submit to prevent duplicate AI calls
- Email field and opt-in consent checkbox on calculator form (checked by default)
- Privacy assurance blurb below opt-in
- Refinement blurb on results screen explaining live cash-flow optimization
- calc_submissions SQLite table stores form inputs, computed results, AI text, email, opt-in
- GET /api/calc-submissions endpoint (x-admin-key protected) to retrieve submissions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 10:58:16 -04:00
parent f195c6082d
commit 8a369f6a57
4 changed files with 156 additions and 3 deletions

20
app.js
View File

@@ -52,6 +52,16 @@
close();
});
const calcBtnText = submitBtn?.querySelector('.calc-btn-text');
const calcBtnLoading = submitBtn?.querySelector('.calc-btn-loading');
function setCalcLoading(on) {
if (!submitBtn) return;
submitBtn.disabled = on;
calcBtnText?.classList.toggle('hidden', on);
calcBtnLoading?.classList.toggle('hidden', !on);
}
submitBtn?.addEventListener('click', async () => {
const homesites = parseFloat(document.getElementById('calcHomesites').value) || 0;
const propertyType = document.getElementById('calcPropertyType').value;
@@ -59,12 +69,15 @@
const paymentFreq = document.getElementById('calcPaymentFreq').value;
const reserveFunds = parseFloat(document.getElementById('calcReserveFunds').value) || 0;
const interest2025 = parseFloat(document.getElementById('calcInterest2025').value) || 0;
const calcEmail = document.getElementById('calcEmail')?.value.trim() || '';
const calcOptIn = document.getElementById('calcOptIn')?.checked ?? true;
if (!homesites || !annualIncome) {
calcErr.classList.remove('hidden');
return;
}
calcErr.classList.add('hidden');
setCalcLoading(true);
// ── Conservative investment assumptions ──
// Operating cash: depending on payment frequency, portion investable in high-yield savings
@@ -121,7 +134,11 @@
const aiRes = await fetch('/api/calculate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ homesites, propertyType, annualIncome, paymentFreq, reserveFunds, interest2025 }),
body: JSON.stringify({
homesites, propertyType, annualIncome, paymentFreq, reserveFunds, interest2025,
email: calcEmail, optIn: calcOptIn,
totalPotential, opInterest, resInterest,
}),
});
if (aiRes.ok) {
const { recommendation } = await aiRes.json();
@@ -136,6 +153,7 @@
// ── Animate the main number ──
animateValue(document.getElementById('resultAmount'), 0, totalPotential);
setCalcLoading(false);
calcForm.classList.add('hidden');
calcRes.classList.remove('hidden');
});