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
+60
View File
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/**
* Reads the shared campus configuration (config/campuses.json). The build
* copies it to api/config/campuses.json; in local development we fall back to
* the repo copy.
*/
final class Campuses
{
/** @var array<string,array<string,mixed>>|null */
private static ?array $byId = null;
/** @return array<string,array<string,mixed>> */
public static function all(): array
{
if (self::$byId !== null) {
return self::$byId;
}
$candidates = [
dirname(__DIR__) . '/config/campuses.json',
dirname(__DIR__, 3) . '/config/campuses.json',
];
foreach ($candidates as $file) {
if (is_readable($file)) {
$data = json_decode((string)file_get_contents($file), true);
if (is_array($data) && isset($data['campuses'])) {
$out = [];
foreach ($data['campuses'] as $c) {
$out[$c['id']] = $c;
}
return self::$byId = $out;
}
}
}
throw new RuntimeException('campuses.json not found');
}
/** @return array<string,mixed>|null */
public static function get(string $id): ?array
{
return self::all()[$id] ?? null;
}
/** @return string[] */
public static function openIds(): array
{
return array_keys(array_filter(self::all(), static fn($c) => ($c['status'] ?? 'open') === 'open'));
}
/** "Sunday 9:30 AM" style summary of the first service day. */
public static function timesSummary(array $campus): string
{
$parts = [];
foreach ($campus['serviceTimes'] ?? [] as $st) {
$parts[] = $st['day'] . ' ' . implode(', ', $st['times']);
}
return implode(' · ', $parts);
}
}