/* Avvale AI Tools — Shared JavaScript */ const API_BASE = window.location.origin; /* --- Multi-Step Form Engine --- */ class AvvaleForm { constructor(formEl, config = {}) { this.form = formEl; this.steps = Array.from(formEl.querySelectorAll('.form-step')); this.currentStep = 0; this.totalSteps = this.steps.length; this.toolType = config.toolType || 'business-plan'; this.progressFill = document.querySelector('.progress-fill'); this.stepIndicator = document.querySelector('.step-indicator'); this.init(); } init() { this.showStep(0); this.setupOptionCards(); } showStep(index) { this.steps.forEach((s, i) => { s.classList.toggle('active', i === index); }); this.currentStep = index; const pct = ((index + 1) / this.totalSteps) * 100; if (this.progressFill) this.progressFill.style.width = pct + '%'; if (this.stepIndicator) { this.stepIndicator.textContent = `Step ${index + 1} of ${this.totalSteps}`; } window.scrollTo({ top: 0, behavior: 'smooth' }); } next() { if (!this.validateStep(this.currentStep)) return; if (this.currentStep < this.totalSteps - 1) { this.showStep(this.currentStep + 1); } } prev() { if (this.currentStep > 0) { this.showStep(this.currentStep - 1); } } validateStep(index) { const step = this.steps[index]; const required = step.querySelectorAll('[required]'); let valid = true; required.forEach(el => { if (!el.value.trim()) { el.style.borderColor = '#E53935'; valid = false; el.addEventListener('input', () => { el.style.borderColor = ''; }, { once: true }); } }); return valid; } collectData() { const data = {}; const inputs = this.form.querySelectorAll('input, select, textarea'); inputs.forEach(el => { if (!el.name) return; if (el.type === 'radio') { if (el.checked) data[el.name] = el.value; } else { data[el.name] = el.value; } }); // Collect dynamic table rows this.form.querySelectorAll('.dynamic-table').forEach(table => { const tableName = table.dataset.name; const rows = []; table.querySelectorAll('.table-row:not(.table-header)').forEach(row => { const rowData = {}; row.querySelectorAll('input').forEach(inp => { rowData[inp.dataset.field] = inp.value; }); if (Object.values(rowData).some(v => v.trim())) { rows.push(rowData); } }); data[tableName] = rows; }); return data; } setupOptionCards() { this.form.querySelectorAll('.option-card').forEach(card => { card.addEventListener('click', () => { const input = card.querySelector('input'); const group = card.closest('.option-group'); if (input.type === 'radio') { group.querySelectorAll('.option-card').forEach(c => c.classList.remove('selected')); card.classList.add('selected'); input.checked = true; } }); }); } async submit() { if (!this.validateStep(this.currentStep)) return; const data = this.collectData(); try { const res = await fetch(`${API_BASE}/api/questionnaire/save`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tool_type: this.toolType, data }), }); const result = await res.json(); return result.session_id; } catch (err) { console.error('Save failed:', err); alert('Something went wrong. Please try again.'); return null; } } } /* --- Dynamic Table (Expenses / Revenues) --- */ function addTableRow(tableEl) { const rows = tableEl.querySelectorAll('.table-row:not(.table-header)'); const maxRows = parseInt(tableEl.dataset.maxRows) || 99; if (rows.length >= maxRows) return; const template = rows[rows.length - 1]; const clone = template.cloneNode(true); clone.querySelectorAll('input').forEach(inp => { inp.value = ''; }); const removeBtn = clone.querySelector('.btn-remove-row'); if (removeBtn) { removeBtn.addEventListener('click', () => { if (tableEl.querySelectorAll('.table-row:not(.table-header)').length > 1) { clone.remove(); } }); } tableEl.querySelector('.btn-add-row') ? tableEl.insertBefore(clone, tableEl.querySelector('.btn-add-row')) : tableEl.appendChild(clone); } /* --- Generation Status Polling --- */ async function pollStatus(sessionId, onComplete, onError) { const poll = async () => { try { const res = await fetch(`${API_BASE}/api/status/${sessionId}`); const data = await res.json(); if (data.status === 'complete') { onComplete(data); } else if (data.status === 'error') { onError(data); } else { setTimeout(poll, 3000); } } catch (err) { setTimeout(poll, 5000); } }; poll(); } /* --- Direct Generate (for testing without Shopify) --- */ async function triggerGenerate(sessionId) { try { const res = await fetch(`${API_BASE}/api/generate/${sessionId}`, { method: 'POST' }); return await res.json(); } catch (err) { console.error('Generate trigger failed:', err); return null; } } /* --- Investor Readiness Score --- */ async function submitReadinessScore(answers, email = null) { try { const payload = { answers }; if (email) payload.email = email; const res = await fetch(`${API_BASE}/api/score/investor-readiness`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); return await res.json(); } catch (err) { console.error('Score failed:', err); return null; } }