Plan Your Visit landing page for visit.waterschurch.org

Next.js 16 static export + PHP 8 API for cPanel/Apache hosting.

- Location-aware campus finder (IP estimate, optional precise location, ZIP search)
- Three-step Plan Your Visit form with separate email/SMS consent
- PHP API: validation, anti-spam, rate limiting, SQLite lead store,
  Mailchimp and Sinch Engage adapters, idempotent retries via cron
- Attribution capture (utm_*, gclid/gbraid/wbraid, fbclid, ttclid, twclid) and
  consent-aware analytics hooks
- Vitest and PHP test suites, cPanel deployment script and docs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-09-22 13:22:26 -04:00
co-authored by Claude Fable 5.1
commit 78cd0cc550
109 changed files with 16861 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
<?php
/** GET /api/geocode.php?zip=02763 — US ZIP centroid lookup for the manual campus search. */
declare(strict_types=1);
require_once __DIR__ . '/lib/bootstrap.php';
require_once __DIR__ . '/lib/Validator.php';
require_once __DIR__ . '/lib/RateLimit.php';
require_once __DIR__ . '/lib/Geocode.php';
Api::requireMethod('GET');
$ip = Api::clientIp();
$rl = RateLimit::hit(RateLimit::ipBucket('geocode', $ip), 40, 600);
if (!$rl['allowed']) {
Api::json(['ok' => false, 'error' => 'Too many searches. Please wait a moment.'], 429, ['Retry-After' => (string)$rl['retryAfter']]);
}
$zip = isset($_GET['zip']) ? preg_replace('/\D/', '', (string)$_GET['zip']) : '';
$zip = substr((string)$zip, 0, 5);
if (strlen($zip) !== 5) {
Api::json(['ok' => false, 'error' => 'Enter a 5-digit ZIP code.'], 400);
}
$loc = Geocode::zip($zip);
if (!$loc) {
Api::json(['ok' => false, 'error' => "We couldn't find that ZIP. Try another or pick a campus below."], 200);
}
Api::json(['ok' => true, 'location' => $loc], 200, ['Cache-Control' => 'public, max-age=86400']);