join form mirrors scouting73-form (children textarea, interested_in, source w/ schools) + forwards to shared sheet pipeline; firefox joinbtn fix
This commit is contained in:
+54
-12
@@ -1,4 +1,4 @@
|
|||||||
import json, os, shutil, datetime, urllib.request
|
import json, os, shutil, datetime, urllib.request, urllib.parse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi import FastAPI, Form
|
from fastapi import FastAPI, Form
|
||||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
@@ -16,6 +16,9 @@ LEADS = DATA / "leads.jsonl"
|
|||||||
EVENTS_LIVE = DATA / "events.json"
|
EVENTS_LIVE = DATA / "events.json"
|
||||||
EVENTS_SEED = Path(__file__).parent / "events.json"
|
EVENTS_SEED = Path(__file__).parent / "events.json"
|
||||||
NTFY_URL = os.environ.get("NTFY_URL", "")
|
NTFY_URL = os.environ.get("NTFY_URL", "")
|
||||||
|
# Same lead pipeline as the flyer form (scouting73.thewichersfamily.com):
|
||||||
|
# forward /join posts to that container's /submit (Google Sheet + ntfy push).
|
||||||
|
FORM_UPSTREAM = os.environ.get("FORM_UPSTREAM", "http://scouting73-form:8000/submit")
|
||||||
|
|
||||||
app = FastAPI(title="Pack & Troop 73")
|
app = FastAPI(title="Pack & Troop 73")
|
||||||
app.mount("/static", StaticFiles(directory="/app/static"), name="static")
|
app.mount("/static", StaticFiles(directory="/app/static"), name="static")
|
||||||
@@ -90,7 +93,7 @@ nav.links{display:flex;align-items:center;gap:26px;flex-wrap:wrap}
|
|||||||
nav.links a{text-decoration:none;font-weight:600;font-size:15px;color:#333D52;padding:6px 0}
|
nav.links a{text-decoration:none;font-weight:600;font-size:15px;color:#333D52;padding:6px 0}
|
||||||
nav.links a:hover{color:#1E2F52}
|
nav.links a:hover{color:#1E2F52}
|
||||||
nav.links a.on{font-weight:700;color:#1E2F52;border-bottom:2px solid #F0AD2D}
|
nav.links a.on{font-weight:700;color:#1E2F52;border-bottom:2px solid #F0AD2D}
|
||||||
a.joinbtn{font-family:'Archivo';font-weight:700;font-size:15px;background:#F0AD2D;color:#1E2F52 !important;padding:11px 22px;border-radius:999px;border-bottom:none !important}
|
a.joinbtn{display:inline-block;line-height:1.2;font-family:'Archivo';font-weight:700;font-size:15px;background:#F0AD2D;color:#1E2F52 !important;padding:11px 22px;border-radius:999px;border-bottom:none !important}
|
||||||
a.joinbtn:hover{background:#DB9714}
|
a.joinbtn:hover{background:#DB9714}
|
||||||
.hero{position:relative;display:flex;align-items:flex-end;min-height:520px}
|
.hero{position:relative;display:flex;align-items:flex-end;min-height:520px}
|
||||||
.hero .wrap{padding-top:110px;padding-bottom:58px;width:100%}
|
.hero .wrap{padding-top:110px;padding-bottom:58px;width:100%}
|
||||||
@@ -499,14 +502,26 @@ def join_body(sent=False):
|
|||||||
<div><label>Email <span class="req">*</span></label><input type="email" name="email" required></div>
|
<div><label>Email <span class="req">*</span></label><input type="email" name="email" required></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="frow">
|
<div class="frow">
|
||||||
<div><label>Phone</label><input name="phone" inputmode="tel"></div>
|
<div><label>Phone</label><input name="phone" inputmode="tel" placeholder="(610) 555-0100"></div>
|
||||||
<div><label>Which program?</label><select name="program"><option value="">Not sure yet</option><option>Cub Scouts (Pack 73, K-5)</option><option>Troop 73 (ages 11-17)</option><option>Volunteering / helping out</option></select></div>
|
<div><label>Interested in <span class="req">*</span></label><select name="interested_in" required><option value="" disabled selected>Select one...</option><option value="Pack 73 (Cub Scouts, K-5)">Pack 73: Cub Scouts (K-5)</option><option value="Troop 73 (Scouts BSA, 11-17)">Troop 73: ages 11-17</option><option value="Both / kids spanning ages">Both: kids spanning ages</option></select></div>
|
||||||
</div>
|
</div>
|
||||||
<div><label>Kids' names and ages</label><input name="children" placeholder="e.g. Maya (7) and Sam (10)"></div>
|
<div><label>Child(ren): names and grades this fall <span class="req">*</span></label><textarea name="children" rows="2" required placeholder="Emma (Kindergarten), Jack (2nd grade)"></textarea><div style="font-size:12.5px;color:#8B93A3;margin-top:4px">List all kids you're considering. Any grade K-12 welcome.</div></div>
|
||||||
<div><label>Questions, or anything you'd like us to know</label><textarea name="message" rows="4"></textarea></div>
|
<div><label>How did you hear about us? <span class="req">*</span></label><select name="source" id="source" required><option value="" disabled selected>Select one...</option>
|
||||||
|
<optgroup label="Daycares & Preschools"><option>New Hanover Child Care</option><option>Jerusalem Lutheran Nursery</option><option>Goddard School Schwenksville</option><option>St. Teresa Early Learning</option></optgroup>
|
||||||
|
<optgroup label="Elementary Schools"><option>Schwenksville Elementary</option><option>Marlborough Elementary (Green Lane)</option><option>New Hanover Elementary</option><option>Upper Perkiomen 4/5 Center</option></optgroup>
|
||||||
|
<optgroup label="Middle & High Schools"><option>Perkiomen Valley Middle School</option><option>Upper Perkiomen Middle School</option><option>Perkiomen Valley High School</option><option>Upper Perkiomen High School</option></optgroup>
|
||||||
|
<optgroup label="Pack / Troop Events"><option>Back-to-School Night table</option><option>Pack Open House</option><option>Sign-Up Night</option></optgroup>
|
||||||
|
<optgroup label="Other"><option>Pack 73 / Troop 73 family referral</option><option>BeAScout / searched online</option><option value="Other school or daycare">Other school or daycare</option><option value="Other">Other</option></optgroup>
|
||||||
|
</select></div>
|
||||||
|
<div id="source-place" style="display:none"><label>Which school or daycare?</label><input name="source_place" placeholder="e.g. Oak Ridge Elementary"></div>
|
||||||
|
<div id="source-other" style="display:none"><label>Please tell us how you heard about us</label><input name="source_other" placeholder="e.g. a friend, Facebook, yard sign"></div>
|
||||||
|
<div><label>Questions, or anything you'd like us to know</label><textarea name="message" rows="3"></textarea></div>
|
||||||
<button class="cta" type="submit" style="justify-self:start;font-size:16px;padding:14px 30px">Send interest form</button>
|
<button class="cta" type="submit" style="justify-self:start;font-size:16px;padding:14px 30px">Send interest form</button>
|
||||||
<div style="font-size:13px;color:#8B93A3">We only use this to reply about Scouting. No newsletters, no sharing.</div>
|
<div style="font-size:13px;color:#8B93A3">We only use this to reply about Scouting. No newsletters, no sharing.</div>
|
||||||
</form></div>
|
</form>
|
||||||
|
<script>
|
||||||
|
(function(){var s=document.getElementById('source');if(!s)return;s.addEventListener('change',function(){var v=s.value;document.getElementById('source-place').style.display=v==='Other school or daycare'?'block':'none';document.getElementById('source-other').style.display=v==='Other'?'block':'none';});})();
|
||||||
|
</script></div>
|
||||||
</div></section>
|
</div></section>
|
||||||
<section style="padding:64px 0 84px"><div class="wrap">
|
<section style="padding:64px 0 84px"><div class="wrap">
|
||||||
<div class="eyebrow dark">New-family FAQ</div><h2 class="sec" style="margin-bottom:30px">The questions every new family asks.</h2>
|
<div class="eyebrow dark">New-family FAQ</div><h2 class="sec" style="margin-bottom:30px">The questions every new family asks.</h2>
|
||||||
@@ -526,19 +541,46 @@ def contact_redirect():
|
|||||||
|
|
||||||
@app.post("/join")
|
@app.post("/join")
|
||||||
def join_post(parent_name: str = Form(...), email: str = Form(...), phone: str = Form(""),
|
def join_post(parent_name: str = Form(...), email: str = Form(...), phone: str = Form(""),
|
||||||
children: str = Form(""), program: str = Form(""), message: str = Form("")):
|
children: str = Form(""), interested_in: str = Form(""), source: str = Form(""),
|
||||||
|
source_place: str = Form(""), source_other: str = Form(""), message: str = Form("")):
|
||||||
|
if source == "Other school or daycare" and source_place.strip():
|
||||||
|
source = "School/Daycare: " + source_place.strip()
|
||||||
|
elif source == "Other" and source_other.strip():
|
||||||
|
source = "Other: " + source_other.strip()
|
||||||
rec = {"ts": datetime.datetime.now().isoformat(timespec="seconds"),
|
rec = {"ts": datetime.datetime.now().isoformat(timespec="seconds"),
|
||||||
"parent_name": parent_name.strip(), "email": email.strip(), "phone": phone.strip(),
|
"parent_name": parent_name.strip(), "email": email.strip(), "phone": phone.strip(),
|
||||||
"children": children.strip(), "program": program.strip(), "message": message.strip()}
|
"children": children.strip(), "interested_in": interested_in.strip(),
|
||||||
|
"source": source.strip(), "message": message.strip()}
|
||||||
try:
|
try:
|
||||||
with open(LEADS, "a", encoding="utf-8") as f:
|
with open(LEADS, "a", encoding="utf-8") as f:
|
||||||
f.write(json.dumps(rec) + "\n")
|
f.write(json.dumps(rec) + "\n")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
if NTFY_URL:
|
# Forward into the shared lead pipeline (Google Sheet + ntfy on the 'scouting'
|
||||||
|
# topic), same as scouting73.thewichersfamily.com. The comment rides along in
|
||||||
|
# the source column so it reaches the sheet and the phone push.
|
||||||
|
src = rec["source"] or "greenlanescouts73.org"
|
||||||
|
if rec["message"]:
|
||||||
|
src = src + " | Note: " + rec["message"][:300]
|
||||||
|
forwarded = False
|
||||||
|
try:
|
||||||
|
data = urllib.parse.urlencode({
|
||||||
|
"parent_name": rec["parent_name"], "email": rec["email"], "phone": rec["phone"],
|
||||||
|
"children": rec["children"] or "(not given)",
|
||||||
|
"interested_in": rec["interested_in"] or "(not given)", "source": src,
|
||||||
|
}).encode()
|
||||||
|
rq = urllib.request.Request(FORM_UPSTREAM, data=data)
|
||||||
|
with urllib.request.urlopen(rq, timeout=6) as resp:
|
||||||
|
forwarded = resp.status < 400
|
||||||
|
except Exception:
|
||||||
|
forwarded = False
|
||||||
|
if not forwarded and NTFY_URL:
|
||||||
|
# fallback push so a lead is never silently lost
|
||||||
try:
|
try:
|
||||||
b = ("New Scout interest: %s (%s) - %s" % (rec["parent_name"], rec["email"], rec["program"] or "program TBD")).encode()
|
b = ("New Scout interest: %s (%s) - %s | Kids: %s | %s" % (
|
||||||
rq = urllib.request.Request(NTFY_URL, data=b, headers={"Title": "greenlanescouts73.org lead", "Tags": "tent"})
|
rec["parent_name"], rec["email"], rec["interested_in"] or "program TBD",
|
||||||
|
rec["children"] or "?", src)).encode()
|
||||||
|
rq = urllib.request.Request(NTFY_URL, data=b, headers={"Title": "greenlanescouts73.org lead (sheet forward FAILED)", "Tags": "tent,warning"})
|
||||||
urllib.request.urlopen(rq, timeout=3)
|
urllib.request.urlopen(rq, timeout=3)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|||||||
Reference in New Issue
Block a user