Add RSVP support and Open Graph meta tags

People can now RSVP without claiming a slot. OG tags show
event title, date/time/location, and headcount in link previews.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-15 10:18:36 -04:00
parent 2b08f81c8d
commit d51e7fe867
9 changed files with 238 additions and 5 deletions
+34 -5
View File
@@ -2,6 +2,15 @@
{{define "title"}}{{.Event.Title}} — bbq{{end}}
{{define "meta"}}
<meta property="og:title" content="{{.Event.Title}}">
<meta property="og:description" content="{{if .Event.Date}}{{.Event.Date}}{{end}}{{if .Event.Time}} at {{.Event.Time}}{{end}}{{if .Event.Location}} — {{.Event.Location}}{{end}} · {{.TotalGoing}} going">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="{{.Event.Title}}">
<meta name="twitter:description" content="{{if .Event.Date}}{{.Event.Date}}{{end}}{{if .Event.Time}} at {{.Event.Time}}{{end}}{{if .Event.Location}} — {{.Event.Location}}{{end}} · {{.TotalGoing}} going">
{{end}}
{{define "admin-bar"}}{{if .IsAdmin}}<div class="admin-bar">ADMIN VIEW — share the guest link: /e/{{.Event.Slug}}</div>{{end}}{{end}}
{{define "content"}}
@@ -15,8 +24,6 @@
</div>
</div>
<div class="section-label">What's needed</div>
<div id="slots-container"
hx-ext="sse"
sse-connect="/e/{{.Event.Slug}}/sse"
@@ -25,10 +32,31 @@
{{template "slots-inner" .}}
</div>
<div class="section-label">Add your name</div>
<div class="section-label" style="margin-top:40px">Sign up</div>
<div class="claim-form-wrapper">
<div class="form-title">I'll bring something &#8594;</div>
<div class="form-title">I'm coming &#8594;</div>
<form hx-post="/e/{{.Event.Slug}}/rsvp"
hx-target="#slots-container"
hx-swap="innerHTML settle:0.1s"
hx-on::after-request="if(event.detail.successful) this.reset()">
<div class="form-row">
<label>Your name</label>
<input type="text" name="name" placeholder="e.g. Sam" required>
</div>
<div class="form-row">
<label>Note (optional)</label>
<input type="text" name="note" placeholder="e.g. +1, arriving late, etc.">
</div>
<button class="btn-submit" type="submit">Count me in &#8599;</button>
</form>
</div>
{{if .Slots}}
<div class="section-label" style="margin-top:40px">I'll bring something</div>
<div class="claim-form-wrapper">
<div class="form-title">Claim a slot &#8594;</div>
<form hx-post="/e/{{.Event.Slug}}/claim"
hx-target="#slots-container"
hx-swap="innerHTML settle:0.1s"
@@ -49,9 +77,10 @@
<label>Note (optional)</label>
<input type="text" name="note" placeholder="e.g. bringing sparkling water + lemonade">
</div>
<button class="btn-submit" type="submit">Count me in &#8599;</button>
<button class="btn-submit" type="submit">Claim &#8599;</button>
</form>
</div>
{{end}}
{{if .IsAdmin}}
<div class="section-label" style="margin-top:40px">Admin: Add slot</div>
+7
View File
@@ -4,6 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{block "title" .}}picnic{{end}}</title>
{{block "meta" .}}{{end}}
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,700;12..96,800&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<script src="https://unpkg.com/htmx-ext-sse@2.2.2/sse.js"></script>
@@ -319,6 +320,12 @@
background: var(--yellow);
border-color: var(--border);
}
.rsvp-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 40px;
}
.btn-danger {
background: none;
border: none;
+21
View File
@@ -1,4 +1,6 @@
{{define "slots-inner"}}
<div class="section-label">What's needed</div>
<div class="slots-grid">
{{range .Slots}}
<div class="slot-card{{if .IsFull}} full{{end}}">
<div class="slot-info">
@@ -28,6 +30,25 @@
</div>
</div>
{{end}}
</div>
<div class="section-label">Going ({{len .Rsvps}})</div>
<div class="rsvp-list">
{{if .Rsvps}}
{{range .Rsvps}}
<span class="claim-chip">
{{.Name}}{{if .Note}} <small style="color:#888">({{.Note}})</small>{{end}}
<button hx-delete="/e/{{$.Event.Slug}}/rsvp/{{.ID}}"
hx-target="#slots-container"
hx-swap="innerHTML settle:0.1s"
hx-confirm="Remove {{.Name}}?"
title="Remove">&#215;</button>
</span>
{{end}}
{{else}}
<span class="nobody">no one yet</span>
{{end}}
</div>
{{end}}
{{define "slots.html"}}{{template "slots-inner" .}}{{end}}