/*** CONFIG ***/ const SHEET_ID = "PASTE_YOUR_SHEET_ID"; const ADMIN_EMAIL = "[email protected]"; /*** Schema: Sheet name: "Signups" Header row A1:L1 with: timestamp, event, day, code, label, time, name1, email1, phone1, name2, email2, phone2 ***/ function doGet(e) { try { const action = (e.parameter.action || "").toLowerCase(); if (action === "availability") { const eventName = e.parameter.event || ""; const takenMap = getTakenMap_(eventName); return json_({ status: "ok", availability: takenMap }); } return json_({ status: "ok", message: "Ignite Church Sign-Up API" }); } catch (err) { return json_( { status: "error", message: String(err) }, 500 ); } } function doPost(e) { try { const body = JSON.parse(e.postData.contents || "{}"); if (body.action === "signup") { return handleSignup_(body.payload); } return json_( { status: "error", message: "Unknown action." }, 400 ); } catch (err) { return json_( { status: "error", message: String(err) }, 500 ); } } function handleSignup_(p) { // Honeypot if (p.website) return json_({ status: "ok" }); const required = ["event","day","code","label","time","name1","email1","phone1"]; for (const k of required) { if (!p[k]) return json_({ status: "error", message: `Missing field: ${k}` }, 400); } // If any part of volunteer 2 is present, require all three const v2Any = p.name2 || p.email2 || p.phone2; if (v2Any && !(p.name2 && p.email2 && p.phone2)) { return json_({ status: "error", message: "Volunteer 2 must include name, email, and phone, or be left entirely blank." }, 400); } const takenMap = getTakenMap_(p.event); const taken = takenMap[p.code]?.taken || 0; const requested = 1 + (p.name2 ? 1 : 0); if (taken + requested > 2) { const remaining = Math.max(0, 2 - taken); return json_({ status: "error", message: `That shift is nearly full. Only ${remaining} spot${remaining===1?"":"s"} remain.` }, 409); } const ss = SpreadsheetApp.openById(SHEET_ID); const sh = ss.getSheetByName("Signups") || ss.insertSheet("Signups"); if (sh.getLastRow() === 0) { sh.appendRow(["timestamp","event","day","code","label","time","name1","email1","phone1","name2","email2","phone2"]); } const now = new Date(); sh.appendRow([ now, p.event, p.day, p.code, p.label, p.time, p.name1, p.email1, p.phone1, p.name2 || "", p.email2 || "", p.phone2 || "" ]); // Send notifications const subject = `Eureka Days - ${p.day} ${p.label} (${p.time}) sign-up`; const adminBody = `New sign-up recorded.nnShift: ${p.day} ${p.label} (${p.time})nEvent: ${p.event}nnVolunteer 1:n${p.name1}n${p.email1}n${p.phone1}nn` + (p.name2 ? `Volunteer 2:n${p.name2}n${p.email2}n${p.phone2}nn` : "") + `Manage sheet: https://docs.google.com/spreadsheets/d/${SHEET_ID}/edit`; MailApp.sendEmail(ADMIN_EMAIL, subject, adminBody); const conf = `Thank you for signing up to serve at Eureka Days with Ignite Church.nn` + `Shift: ${p.day} ${p.label} (${p.time})nEvent: ${p.event}nn` + `We will see you at your shift.`; MailApp.sendEmail(p.email1, subject, conf); if (p.email2) MailApp.sendEmail(p.email2, subject, conf); return json_({ status: "ok" }); } function getTakenMap_(eventName) { const ss = SpreadsheetApp.openById(SHEET_ID); const sh = ss.getSheetByName("Signups"); const map = {}; if (!sh) return map; const values = sh.getDataRange().getValues(); const header = values[0] || []; const idx = { event: header.indexOf("event"), code: header.indexOf("code"), name1: header.indexOf("name1"), email1: header.indexOf("email1"), phone1: header.indexOf("phone1"), name2: header.indexOf("name2"), email2: header.indexOf("email2"), phone2: header.indexOf("phone2") }; for (let r = 1; r < values.length; r++) { const row = values[r]; if (eventName && row[idx.event] !== eventName) continue; const code = row[idx.code]; if (!code) continue; map[code] ||= { taken: 0 }; if (row[idx.name1] && row[idx.email1] && row[idx.phone1]) map[code].taken++; if (row[idx.name2] && row[idx.email2] && row[idx.phone2]) map[code].taken++; } return map; } function json_(obj, status=200) { return ContentService .createTextOutput(JSON.stringify(obj)) .setMimeType(ContentService.MimeType.JSON); }