140 lines
6.4 KiB
Markdown
140 lines
6.4 KiB
Markdown
# The Founder's Break
|
|
|
|
Event site for the weekly UF startup pool social at Silver Q, Gainesville.
|
|
Hosted by OneTouch Studio at **https://foundersbreak.onetouch.studio**.
|
|
|
|
Corben EJS site. There is no build step: `deploy_site` copies this repo verbatim.
|
|
|
|
```
|
|
site.config.js all schedule, venue and copy data
|
|
views/index.ejs the single page, including the next-date logic
|
|
public/css/style.css brand tokens lifted from onetouch.studio
|
|
public/js/main.js nav state, scroll reveal, live countdown, RSVP submit
|
|
public/assets/ the recurring .ics and the print-resolution QR
|
|
public/images/ logo, favicon, RSVP QR
|
|
```
|
|
|
|
## The date is never hard-coded
|
|
|
|
`views/index.ejs` computes the next occurrence at render time from `weekday` +
|
|
`firstEvent` in `site.config.js`. `public/js/main.js` runs the same rule in the
|
|
browser, so the countdown ticks and rolls itself over to the following Thursday
|
|
once the night ends, with no reload and no redeploy.
|
|
|
|
## Cancelling a week
|
|
|
|
Add the ISO date to `cancelled` in `site.config.js` and redeploy:
|
|
|
|
```js
|
|
cancelled: ["2026-11-26"], // Thanksgiving
|
|
```
|
|
|
|
The page then skips that Thursday, shows a notice naming the cancelled date, and
|
|
points at the next one that is actually happening. Two other places do **not**
|
|
read this list and need the same date added by hand:
|
|
|
|
- `public/assets/founders-break.ics` is a plain weekly recurrence.
|
|
- the `CANCELLED` array in the `founders-break-reminder` Corben function, or it
|
|
will email everyone about a Break that is not happening.
|
|
|
|
## Deploying
|
|
|
|
```
|
|
deploy_site id=4ae63819-3f63-4fea-b0de-eb1e5befe607 repo=gator/founders-break mode=ejs
|
|
```
|
|
|
|
Nothing redeploys itself. Every edit needs the deploy step.
|
|
|
|
## What happens when somebody RSVPs
|
|
|
|
The form POSTs to `/forms/rsvp`. The Corben sites renderer stores it in
|
|
`site_form_submissions` and fires a `form.submitted` event. From there it is all
|
|
plain JavaScript, deliberately: there is no LLM anywhere in the send path.
|
|
|
|
| Piece | Id |
|
|
|---|---|
|
|
| Trigger `Founder's Break RSVP` (form.submitted, form_name=rsvp) | `e035fb68-2ecd-452b-bf74-a051c2775423` |
|
|
| Function `founders-break-rsvp` | `3d3c4bed-c073-40a4-9a32-9ff6944737dd` |
|
|
| Function `founders-break-reminder` | `cf2a1e4c-43f9-4cf4-8fc5-652c494940ae` |
|
|
| Cron `Founder's Break weekly reminder` (every 6h) | `84efd815-5426-4952-af88-2f4ed81efc54` |
|
|
| Email list `Founder's Break` | `09429866-0005-4260-bf56-2d520eea4db7` |
|
|
|
|
`founders-break-rsvp` resolves a CRM contact, writes the email address back onto
|
|
it (`create_contact` dedupes server-side and can return a record with no address,
|
|
which would put an unmailable member on the list), adds them to the list, sends
|
|
the HTML confirmation, and sends an internal note to gatordevin@gmail.com.
|
|
|
|
`founders-break-reminder` runs every 6 hours, works out the Eastern weekday
|
|
itself, and only sends on Wednesday between 09:00 and 20:00 ET. A `kv` marker
|
|
keyed to the event date makes any repeat run a no-op, which is what makes it safe
|
|
to run four times a day.
|
|
|
|
Both functions authenticate with a scoped `email + crm` key held by the paused
|
|
**Founders Break** agent record (`666f7ad0-f546-4fc6-a3c4-14f1384e91a5`). That
|
|
agent runs nothing. It exists only because an API key cannot mint another API
|
|
key. Deleting it revokes the key and breaks both functions.
|
|
|
|
## Sharing
|
|
|
|
`/flyer` (`views/flyer.ejs`) is the link to send someone: the poster shown as a
|
|
sheet, a download button, and the share controls. It is what the footer and the
|
|
post-RSVP confirmation both point at, rather than the raw PDF, so a share always
|
|
lands on something with context.
|
|
|
|
There is no general "share protocol". What exists is the **Web Share API**
|
|
(`navigator.share`), which on a phone opens the real OS share sheet and is the
|
|
only way to reach apps a web page cannot link to. Where `navigator.canShare`
|
|
allows files it shares the PDF itself, which is far better in a group chat than
|
|
a link; it falls back to sharing the URL, and the button stays hidden entirely on
|
|
browsers without support. Under that sit plain per-channel URLs (SMS, mailto,
|
|
WhatsApp, X, LinkedIn, Facebook) that work with no JavaScript at all, and a
|
|
copy-to-clipboard with a select-the-text fallback for in-app browsers that block
|
|
the clipboard.
|
|
|
|
`site.config.js -> images.share` is a 1200x630 unfurl card carrying the same four
|
|
facts the flyer leads with. Both `/` and `/flyer` use it for `og:image`, so a
|
|
pasted link never previews as a wordless photo. Regenerate it from
|
|
`design/og-card.html` the same way as the flyer.
|
|
|
|
## The printable flyer
|
|
|
|
`public/assets/founders-break-flyer.pdf` is a one-page US Letter flyer, linked
|
|
from the footer and from the confirmation someone sees right after they RSVP,
|
|
which is the moment they might think about bringing people. The source that
|
|
generates it lives in `design/` with regeneration instructions; edit that, do not
|
|
re-design the PDF.
|
|
|
|
## The QR code
|
|
|
|
`public/images/rsvp-qr.png` and `public/assets/rsvp-qr.svg` both encode
|
|
`https://foundersbreak.onetouch.studio/#rsvp`. Deliberately dark-on-white:
|
|
inverted QR codes are unreliable on older scanners and these go on flyers.
|
|
Regenerate with the `qrcode` npm package if the URL ever changes.
|
|
|
|
## Tracking
|
|
|
|
The Corben tracker is injected into every EJS site automatically, so pageviews
|
|
(with UTM, referrer, device, browser), clicks on any `a`/`button`/`[data-track]`,
|
|
`form_start`, `form_submit`, scroll depth and engagement time all land with no
|
|
work from us. Key elements carry a `data-track` label so the click rows are
|
|
readable: `rsvp-nav`, `rsvp-hero`, `rsvp-submit`, `add-to-calendar`, `venue-map`,
|
|
`details-map`, `venue-instagram`, `hero-what-to-expect`.
|
|
|
|
`public/js/main.js` adds the three steps the tracker cannot see on its own, via
|
|
the same public `/analytics/events` endpoint:
|
|
|
|
| Event | Means |
|
|
|---|---|
|
|
| `rsvp_reached` | scrolled the RSVP block into view |
|
|
| `rsvp_completed` | submission stored, `event_data.via` is `form` or `reminder-email` |
|
|
| `rsvp_failed` | submission rejected, with the reason |
|
|
|
|
So "reached but did not finish" is `rsvp_reached` minus `rsvp_completed`, and
|
|
"started typing then left" is `form_start` minus `form_submit`.
|
|
|
|
Emails carry a 1x1 open pixel at `GET /analytics/open.gif?s=<site>&k=<kind>&e=<email>&d=<date>`,
|
|
landing in `site_events` as `email_open` with kind `confirm-first`,
|
|
`confirm-returning` or `reminder`. Apple Mail Privacy Protection and Gmail's
|
|
image proxy prefetch images, so treat opens as a ceiling rather than a headcount.
|
|
|
|
Read any of it with `get_site_analytics`, `get_site_events` or `get_site_click_map`.
|