1/* âââ Email Notifications via Netlify Function + Resend âââââââââ 2 Frontend never touches the API key â calls /.netlify/functions/send-email 3ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 4 5const Emails = { 6 _timer: null, 7 8 _cancel() { 9 if (this._timer) { clearTimeout(this._timer); this._timer = null; } 10 }, 11 12 _send(type, data) { 13 fetch("/.netlify/functions/send-email", { 14 method: "POST", 15 headers: { "Content-Type": "application/json" }, 16 body: JSON.stringify({ type, data }) 17 }).catch(() => {}); // fail silently â never block UX 18 }, 19 20 _payload(state) { 21 return { 22 client_name: state.contact.firstName + " " + state.contact.lastName, 23 client_email: state.contact.email, 24 client_phone: state.contact.phone, 25 referral: state.contact.referral, 26 property_address: state.address.display.split(",").slice(0, 3).join(",") 27 }; 28 }, 29 30 // Called when contact submitted â waits 5 min before sending 31 newLead(state) { 32 this._cancel(); 33 const data = this._payload(state); 34 this._timer = setTimeout(() => { 35 this._send("lead", data); 36 }, 300000); // 5 minutes 37 }, 38 39 // Called when quote is shown â cancels lead timer, waits 2 min 40 quoteViewed(state, quote) { 41 this._cancel(); 42 const freqLabel = { weekly: "Weekly", biweekly: "Bi-Weekly", monthly: "Monthly" }[state.frequency]; 43 const terrainLabel = { 44 flat: "Flat", slightly_inclined: "Slightly Inclined", 45 moderately_inclined: "Moderately Hilly", steep: "Steep / Hilly" 46 }[state.terrain]; 47 48 const data = { 49 ...this._payload(state), 50 lawn_sqft: state.sqFt + " sq ft", 51 frequency: freqLabel, 52 terrain: terrainLabel, 53 restricted_access: state.hasRestrictedAccess ? "Yes" : "No", 54 price_per_visit: "$" + quote.perVisit.toFixed(2), 55 price_per_month: "$" + quote.perMonth.toFixed(2) + " / month (" + quote.visitsPerMonth + " visits)" 56 }; 57 58 this._timer = setTimeout(() => { 59 this._send("viewed", data); 60 }, 120000); // 2 minutes 61 }, 62 63 // Called when client confirms â cancels any timer, sends immediately 64 quoteSecured(state, quote) { 65 this._cancel(); 66 const freqLabel = { weekly: "Weekly", biweekly: "Bi-Weekly", monthly: "Monthly" }[state.frequency]; 67 68 this._send("secured", { 69 ...this._payload(state), 70 lawn_sqft: state.sqFt + " sq ft", 71 frequency: freqLabel, 72 price_per_visit: "$" + quote.perVisit.toFixed(2), 73 price_per_month: "$" + quote.perMonth.toFixed(2) + " / month (" + quote.visitsPerMonth + " visits)" 74 }); 75 }, 76 77 test() { 78 return fetch("/.netlify/functions/send-email", { 79 method: "POST", 80 headers: { "Content-Type": "application/json" }, 81 body: JSON.stringify({ 82 type: "lead", 83 data: { 84 client_name: "Test Client", 85 client_email: "[email protected]", 86 client_phone: "(416) 555-0100", 87 referral: "Test", 88 property_address: "123 Maple Ave, Mississauga, ON" 89 } 90 }) 91 }); 92 } 93};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.