Live site: changeplease-6hw.pages.dev
A static site that displays a map of social impact businesses — places that train and employ people in recovery, reentry, and other under-served communities. Initial pilot covers the Denver area.
- Map view of social impact businesses, pinned by type. The type catalog is sheet-driven (a
Typestab) with labels, colors, and icon slugs — new types appear without a code change. Featured locations get a gold star without jumping the distance-sorted list. - Per-location page at a stable URL, with social-share preview tags baked into the HTML at build time.
- Metadata beyond what a Google Business listing provides: populations served, hiring model, mission.
- Public submissions through an embedded Google Form; admin approves before publishing. (The
/suggestroute is temporarily hidden in the UI while the form flow is rebuilt — route still exists insrc/pages/suggest.astro.) - Admin manages everything inside a single Google Sheet — no custom admin UI. Location types are sheet-driven (a
Typestab) so new categories can be added without a code change. - Near-zero maintenance: no server to patch, no churning dependency tree, no monthly upgrade ritual.
- Free end-to-end at the project's expected scale.
- Geocoded coordinates stored once and reused (license-clean via MapTiler).
- User accounts, login, favorites, or reviews.
- Native mobile apps.
- Real-time data (hours, wait times, live availability) — link out to the business's own site or Google for that.
- Brand-level pages for multi-location operators.
- Analytics beyond Cloudflare's built-in cookieless metrics.
- An embeddable map widget.
- Approval thank-you emails.
| Layer | Choice |
|---|---|
| CMS / admin | Google Sheet (two tabs: Locations + Types) |
| Location taxonomy | Sheet-driven Types tab (key / label / color / icon slug); icon catalog in src/lib/location-type-icons.ts |
| Automation | Google Apps Script bound to the sheet |
| Public submission | Google Form (appends to the sheet) |
| Site generator | Astro (static output) |
| Map tiles | MapLibre GL JS + OpenFreeMap (free, no API key) |
| Geocoding | MapTiler (license permits storing the returned coordinates) |
| Hosting | Cloudflare Pages |
| Rebuild trigger | Apps Script onEdit / onFormSubmit → Cloudflare deploy hook |
Two modes. Pick based on whether you want real data or speed.
Against the live Google Sheet (needs .env with SHEET_ID):
npm install
npm run devAgainst committed JSON fixtures (fast, offline, no secrets):
npm run dev:fastBoth serve on http://localhost:4321. build and build:fast follow the same pattern. Cloudflare production always uses the live sheet; :fast is local-only.
For setup of the data backend, see docs/SETUP.md. For ongoing operations (watching the pipeline, debugging failures, admin workflow), see docs/MAINTENANCE.md.
Single command:
npm run verifyType-check + fixture-backed build + WCAG a11y on the built HTML. No network calls, no secrets. ~30 seconds.
Also useful:
npm run doctor— infra health check: sheet sharing, MapTiler key, Cloudflare project, deploy hook,PUBLIC_SITE_URLdrift. Needs.env.npm run build— same as verify's build step but against the live Google Sheet. Catches "renamed a column header" bugs that fixtures can't.
README.md goals + non-goals (this file)
docs/SETUP.md one-time stand-up: Sheet, MapTiler, Cloudflare, Apps Script
docs/MAINTENANCE.md day-to-day: watching the pipeline, failures, admin workflow
docs/brainstorms/ the WHAT and WHY
docs/plans/ the HOW
src/ the Astro site
scripts/ Apps Script source, setup + doctor CLIs
public/fixtures/ sample data so the site builds without secrets
MIT.