/* Hoard Haulers website forms — v1 (13 Sep 2026). Shared by the Contact page, the Commercial walk-through request and the hoarding estimate form. Uploads photos/videos straight to the private Supabase bucket 'lead-uploads' (anon insert only, nobody can read them back without an Office login), then posts the form to the web-lead edge function, which files the customer, note, lead, files, bell alert and the getquote@ email. Patch via asset-put. */ (function(){ var SB = 'https://jwqnuofcawgqrvsjujxr.supabase.co'; var ANON = 'sb_publishable_L1RqXdd1oWEvno3sqckxsQ_CIzGcuL_'; var LEAD = SB + '/functions/v1/web-lead?k=hhlead-2026-R7tQ2mV9xL4pN8sK'; var MAX_FILES = 12, MAX_MB = 200; function rnd(){ return Math.random().toString(36).slice(2, 10); } function safeName(n){ return String(n || 'file').replace(/[^A-Za-z0-9._-]+/g, '-').slice(-80); } window.hhUploadFiles = async function(fileList, onProgress){ var files = [].slice.call(fileList || []).slice(0, MAX_FILES), out = [], i; var day = new Date().toISOString().slice(0, 10); for (i = 0; i < files.length; i++){ var f = files[i]; if (f.size > MAX_MB * 1048576) throw new Error(f.name + ' is over ' + MAX_MB + ' MB — please trim the video or send it by text to (843) 861-8000.'); if (!/^(image|video)\//.test(f.type)) throw new Error(f.name + ' is not a photo or video.'); var path = 'web/' + day + '/' + rnd() + '-' + safeName(f.name); if (onProgress) onProgress(i + 1, files.length, f.name); var r = await fetch(SB + '/storage/v1/object/lead-uploads/' + path, { method: 'POST', headers: { apikey: ANON, Authorization: 'Bearer ' + ANON, 'Content-Type': f.type || 'application/octet-stream', 'x-upsert': 'false' }, body: f }); if (!r.ok){ var t = ''; try { t = (await r.json()).message || ''; } catch(e){} throw new Error('Upload failed for ' + f.name + (t ? ' (' + t + ')' : '')); } out.push({ path: path, name: f.name, type: f.type, size: f.size }); } return out; }; window.hhLeadSend = async function(payload){ payload.page_url = location.href; var r = await fetch(LEAD, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); var j = {}; try { j = await r.json(); } catch(e){} if (!r.ok || !j.ok) throw new Error((j && j.error) || 'send failed'); return j; }; /* Wires any
on the page: reads inputs by name, uploads files from input[type=file], posts, shows the message. */ function wire(form){ if (form.dataset.wired) return; form.dataset.wired = '1'; var out = form.querySelector('[data-hh-msg]'), btn = form.querySelector('button[type=submit]'), fileIn = form.querySelector('input[type=file]'), pick = form.querySelector('[data-hh-picked]'); if (fileIn && pick) fileIn.addEventListener('change', function(){ var n = fileIn.files.length; pick.textContent = n ? (n + ' file' + (n > 1 ? 's' : '') + ' selected: ' + [].slice.call(fileIn.files).map(function(f){ return f.name; }).join(', ')) : ''; }); form.addEventListener('submit', async function(e){ e.preventDefault(); var data = {}, els = form.querySelectorAll('input[name],select[name],textarea[name]'); els.forEach(function(el){ if (el.type === 'file') return; if (el.type === 'checkbox'){ if (el.checked) data[el.name] = (data[el.name] ? data[el.name] + ', ' : '') + (el.value || 'yes'); return; } if (el.type === 'radio'){ if (el.checked) data[el.name] = el.value; return; } data[el.name] = el.value.trim(); }); var digits = String(data.phone || '').replace(/\D/g, ''); if (digits.length < 10){ show(out, 'bad', 'Please enter a 10-digit phone number so we can reach you.'); return; } var label = btn ? btn.textContent : ''; if (btn){ btn.disabled = true; btn.textContent = 'Sending…'; } show(out, '', ''); try { var files = []; if (fileIn && fileIn.files.length){ files = await window.hhUploadFiles(fileIn.files, function(i, n, name){ if (btn) btn.textContent = 'Uploading ' + i + ' of ' + n + '…'; }); } data.files = files; var j = await window.hhLeadSend(data); var ok = form.dataset.hhSuccess || 'Got it — someone from our office will reach out shortly. Thank you!'; show(out, 'ok', ok); form.reset(); if (pick) pick.textContent = ''; if (btn){ btn.textContent = 'Sent ✓'; } // v2: forms marked data-hh-fee="estimate" collect the on-site estimate fee ($ from Office › Price Sheet › Rules) through Stripe right after the request is filed if (form.dataset.hhFee === 'estimate' && j && j.customerId){ try { if (btn) btn.textContent = 'Opening secure payment…'; var fr = await fetch(LEAD.replace('/web-lead', '/estimate-fee-checkout'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ customerId: j.customerId, leadId: j.leadId, returnUrl: location.href.split('?')[0] }) }); var fj = await fr.json(); if (fj && fj.url){ show(out, 'ok', 'Request received. Taking you to secure payment for the estimate visit — it is credited in full when you book.'); location.href = fj.url; return; } } catch(feeErr){ /* the request is already filed; the office can send a payment link */ } } try { if (window.gtag) gtag('event', 'generate_lead', { lead_type: data.lead_type || 'website' }); } catch(x){} } catch(err){ show(out, 'bad', (err && err.message ? err.message : 'That didn’t go through.') + ' You can also call or text (843) 861-8000.'); if (btn){ btn.disabled = false; btn.textContent = label; } } }); } function show(el, cls, text){ if (!el) return; el.className = 'hh-form-msg' + (cls ? ' ' + cls : ''); el.textContent = text; } function init(){ document.querySelectorAll('form[data-hh-lead]').forEach(wire); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init(); })();