From 91b7a948fd6d5a3a4cad4d60c1468f82cd99f42b Mon Sep 17 00:00:00 2001 From: King Omar Date: Fri, 10 Jul 2026 13:41:22 +1000 Subject: [PATCH] Add Google Sheets webhook, email to Cgabijendra@gmail.com, WhatsApp button - Every inquiry fires 3 simultaneous destinations: Google Sheet, email, Kondesk - Email to Cgabijendra@gmail.com with HTML template + reply-to set to the lead - GOOGLE_SHEET_WEBHOOK env var (set once Google Apps Script is deployed) - WhatsApp floating button bottom-right, links to +61 405 580 047 Co-Authored-By: Claude Sonnet 4.6 --- google-apps-script.js | 89 +++++++++++++++++++++++++ src/kondesk.js | 151 +++++++++++++++++++++++++----------------- src/templates.js | 10 +++ wrangler.toml | 3 +- 4 files changed, 193 insertions(+), 60 deletions(-) create mode 100644 google-apps-script.js diff --git a/google-apps-script.js b/google-apps-script.js new file mode 100644 index 0000000..2772f42 --- /dev/null +++ b/google-apps-script.js @@ -0,0 +1,89 @@ +// ============================================================ +// Careers Gateway — Google Apps Script Webhook +// Paste this into Extensions → Apps Script in your Google Sheet +// Deploy as: Web App → Execute as: Me → Access: Anyone +// ============================================================ + +const NOTIFY_EMAIL = 'Cgabijendra@gmail.com'; +const SHEET_NAME = 'Inquiries'; + +function doPost(e) { + try { + var data = JSON.parse(e.postData.contents); + appendToSheet(data); + sendNotificationEmail(data); + return ok(); + } catch (err) { + return ContentService + .createTextOutput(JSON.stringify({ success: false, error: err.message })) + .setMimeType(ContentService.MimeType.JSON); + } +} + +// Allow CORS preflight +function doGet(e) { + return ok(); +} + +function appendToSheet(data) { + var ss = SpreadsheetApp.getActiveSpreadsheet(); + var sheet = ss.getSheetByName(SHEET_NAME); + if (!sheet) { + sheet = ss.insertSheet(SHEET_NAME); + sheet.appendRow([ + 'Date (AEST)', 'Full Name', 'Email', 'Phone', + 'Service', 'Course Name', 'CRICOS Code', 'Institution', 'Message', 'Source' + ]); + // Bold the header row + sheet.getRange(1, 1, 1, 10).setFontWeight('bold').setBackground('#1a2744').setFontColor('#ffffff'); + sheet.setFrozenRows(1); + } + + sheet.appendRow([ + data.submittedAt || new Date().toLocaleString('en-AU', { timeZone: 'Australia/Sydney' }), + data.fullName || '', + data.email || '', + data.phone || '', + data.service || '', + data.courseName || '', + data.cricosCode || '', + data.provider || '', + data.message || '', + data.source || 'careersgateway.com.au', + ]); +} + +function sendNotificationEmail(data) { + var subject = '🔔 New Inquiry: ' + (data.fullName || 'Unknown') + ' — ' + (data.service || 'General'); + var body = + '
' + + '
' + + '

New Inquiry — Careers Gateway Portal

' + + '
' + + '' + + row('Name', data.fullName) + + row('Email', '' + data.email + '') + + row('Phone', data.phone || '—') + + row('Service', data.service || '—') + + row('Course', data.courseName ? data.courseName + (data.cricosCode ? ' (CRICOS: ' + data.cricosCode + ')' : '') : '—') + + row('Institution', data.provider || '—') + + row('Message', data.message || '—') + + row('Submitted', data.submittedAt || new Date().toLocaleString('en-AU')) + + '
' + + '
' + + 'Reply to ' + (data.fullName||'').split(' ')[0] + '' + + '
'; + + GmailApp.sendEmail(NOTIFY_EMAIL, subject, '', { htmlBody: body, replyTo: data.email }); +} + +function row(label, value) { + return '' + + label + '' + (value || '') + ''; +} + +function ok() { + return ContentService + .createTextOutput(JSON.stringify({ success: true })) + .setMimeType(ContentService.MimeType.JSON); +} diff --git a/src/kondesk.js b/src/kondesk.js index 79cd8b7..fec5c7f 100644 --- a/src/kondesk.js +++ b/src/kondesk.js @@ -1,76 +1,109 @@ -// Konpare/Kondesk lead push -// The Konpare API key is used for their widget embed (OSHC comparison) -// and for posting lead data when a course inquiry is submitted. +const NOTIFY_EMAIL = 'Cgabijendra@gmail.com'; + async function pushLeadToKondesk(env, lead) { - const payload = { - api_key: env.KONPARE_KEY, - lead: { - full_name: lead.fullName, - email: lead.email, - phone: lead.phone || '', - service: lead.service || 'Course Inquiry', - course_code: lead.cricosCode || '', - course_name: lead.courseName || '', - provider: lead.provider || '', - message: lead.message || '', - source: 'careers.bored.investments', - } - }; - - // Try Konpare lead submission endpoint - const endpoints = [ - 'https://app.konpare.online/api/leads', - 'https://app.konpare.online/api/v1/leads', - 'https://app.kondesk.com/api/leads', - ]; - - for (const url of endpoints) { - try { - const res = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${env.KONPARE_KEY}`, - 'x-api-key': env.KONPARE_KEY, - }, - body: JSON.stringify(payload), - }); - if (res.ok) return { success: true, endpoint: url }; - } catch (_) {} - } - - // Fallback: email notification - await sendEmailFallback(env, lead); - return { success: false, fallback: 'email' }; + // Fire all three in parallel — Google Sheet, email, Kondesk + await Promise.allSettled([ + pushToGoogleSheet(env, lead), + sendEmail(env, lead), + pushToKondesk(env, lead), + ]); + return { success: true }; } -async function sendEmailFallback(env, lead) { - // Cloudflare Email Routing send — uses MailChannels via Workers - const emailBody = ` -New Lead from Careers Gateway Portal +async function pushToGoogleSheet(env, lead) { + const url = env.GOOGLE_SHEET_WEBHOOK; + if (!url) return; + await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + fullName: lead.fullName, + email: lead.email, + phone: lead.phone || '', + service: lead.service || '', + courseName: lead.courseName || '', + cricosCode: lead.cricosCode || '', + provider: lead.provider || '', + message: lead.message || '', + source: 'careersgateway.com.au', + submittedAt: new Date().toLocaleString('en-AU', { timeZone: 'Australia/Sydney' }), + }), + }); +} -Name: ${lead.fullName} -Email: ${lead.email} -Phone: ${lead.phone || 'N/A'} -Service: ${lead.service || 'N/A'} -Course: ${lead.courseName || 'N/A'} (${lead.cricosCode || 'N/A'}) -Provider: ${lead.provider || 'N/A'} -Message: ${lead.message || 'N/A'} -Submitted: ${new Date().toLocaleString('en-AU', { timeZone: 'Australia/Sydney' })} - `.trim(); +async function sendEmail(env, lead) { + const dt = new Date().toLocaleString('en-AU', { timeZone: 'Australia/Sydney', dateStyle: 'full', timeStyle: 'short' }); + const html = ` + +
+ +

New Inquiry — Careers Gateway Portal

+
+
+ + + + + + ${lead.courseName ? `` : ''} + ${lead.provider ? `` : ''} + ${lead.message ? `` : ''} + +
Name${esc(lead.fullName)}
Email${esc(lead.email)}
Phone${esc(lead.phone||'—')}
Service${esc(lead.service||'—')}
Course${esc(lead.courseName)}${lead.cricosCode ? ` (CRICOS: ${esc(lead.cricosCode)})` : ''}
Institution${esc(lead.provider)}
Message${esc(lead.message)}
Submitted${dt}
+
+ Reply to ${esc(lead.fullName.split(' ')[0])} +
+
+`; + // MailChannels (works on Cloudflare Workers for domains with SPF set) try { await fetch('https://api.mailchannels.net/tx/v1/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - personalizations: [{ to: [{ email: env.CONTACT_EMAIL, name: 'Careers Gateway' }] }], + personalizations: [{ to: [{ email: NOTIFY_EMAIL, name: 'Bijendra — Careers Gateway' }] }], from: { email: 'noreply@careersgateway.com.au', name: 'Careers Gateway Portal' }, - subject: `New Lead: ${lead.fullName} — ${lead.service || 'Course Inquiry'}`, - content: [{ type: 'text/plain', value: emailBody }], + reply_to: { email: lead.email, name: lead.fullName }, + subject: `New Inquiry: ${lead.fullName} — ${lead.service || 'General'}`, + content: [ + { type: 'text/plain', value: `New inquiry from ${lead.fullName} (${lead.email} / ${lead.phone})\n\nService: ${lead.service}\nCourse: ${lead.courseName||'N/A'}\nMessage: ${lead.message||'N/A'}` }, + { type: 'text/html', value: html }, + ], }), }); } catch (_) {} } +function esc(str) { + return String(str || '').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); +} + +async function pushToKondesk(env, lead) { + const endpoints = [ + 'https://app.konpare.online/api/leads', + 'https://app.konpare.online/api/v1/leads', + 'https://app.kondesk.com/api/leads', + ]; + const payload = { + api_key: env.KONPARE_KEY, + lead: { + full_name: lead.fullName, email: lead.email, phone: lead.phone||'', + service: lead.service||'Course Inquiry', course_code: lead.cricosCode||'', + course_name: lead.courseName||'', provider: lead.provider||'', + message: lead.message||'', source: 'careersgateway.com.au', + } + }; + for (const url of endpoints) { + try { + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${env.KONPARE_KEY}`, 'x-api-key': env.KONPARE_KEY }, + body: JSON.stringify(payload), + }); + if (res.ok) return; + } catch (_) {} + } +} + export { pushLeadToKondesk }; diff --git a/src/templates.js b/src/templates.js index 341c1d8..5c03a23 100644 --- a/src/templates.js +++ b/src/templates.js @@ -203,6 +203,16 @@ ${extraHead} ${body} + + + + + +