6a70135a5d
Generates a branded 1200x630 PNG per event with title, date/time/location using Go's image package and a custom bitmap font. Supports BBQ_BASE_URL env var for absolute URLs required by link preview crawlers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
4.5 KiB
HTML
115 lines
4.5 KiB
HTML
{{template "layout" .}}
|
|
|
|
{{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:image" content="{{.BaseURL}}/e/{{.Event.Slug}}/og.png">
|
|
<meta property="og:image:width" content="1200">
|
|
<meta property="og:image:height" content="630">
|
|
<meta property="og:type" content="website">
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<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">
|
|
<meta name="twitter:image" content="{{.BaseURL}}/e/{{.Event.Slug}}/og.png">
|
|
{{end}}
|
|
|
|
{{define "admin-bar"}}{{if .IsAdmin}}<div class="admin-bar">ADMIN VIEW — share the guest link: /e/{{.Event.Slug}}</div>{{end}}{{end}}
|
|
|
|
{{define "content"}}
|
|
<div class="event-header">
|
|
<div class="event-tag">Open · {{.TotalGoing}} going</div>
|
|
<h1 class="event-title">{{.Event.Title}}</h1>
|
|
<div class="event-meta">
|
|
{{if .Event.Date}}<span>📅 {{.Event.Date}}</span>{{end}}
|
|
{{if .Event.Time}}<span>🕒 {{.Event.Time}}</span>{{end}}
|
|
{{if .Event.Location}}<span>📍 {{.Event.Location}}</span>{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<div id="slots-container"
|
|
hx-ext="sse"
|
|
sse-connect="/e/{{.Event.Slug}}/sse"
|
|
sse-swap="message"
|
|
hx-swap="innerHTML settle:0.1s">
|
|
{{template "slots-inner" .}}
|
|
</div>
|
|
|
|
<div class="section-label" style="margin-top:40px">Sign up</div>
|
|
|
|
<div class="claim-form-wrapper">
|
|
<div class="form-title">I'm coming →</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 ↗</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 →</div>
|
|
<form hx-post="/e/{{.Event.Slug}}/claim"
|
|
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>Slot</label>
|
|
<select name="slot_id">
|
|
{{range .Slots}}{{if not .IsFull}}
|
|
<option value="{{.Slot.ID}}">{{.Slot.Emoji}} {{.Slot.Name}} ({{$left := sub .Slot.MaxClaims .ClaimCount}}{{$left}} spot{{if ne $left 1}}s{{end}} left)</option>
|
|
{{end}}{{end}}
|
|
</select>
|
|
</div>
|
|
<div class="form-row">
|
|
<label>Note (optional)</label>
|
|
<input type="text" name="note" placeholder="e.g. bringing sparkling water + lemonade">
|
|
</div>
|
|
<button class="btn-submit" type="submit">Claim ↗</button>
|
|
</form>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .IsAdmin}}
|
|
<div class="section-label" style="margin-top:40px">Admin: Add slot</div>
|
|
<div class="claim-form-wrapper">
|
|
<form hx-post="/e/{{.Event.Slug}}/admin/{{.Event.AdminToken}}/slot"
|
|
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>Emoji</label>
|
|
<div class="emoji-pick" style="width:100%">
|
|
<input type="text" name="emoji" placeholder="🍕" readonly style="border:var(--border-w) solid var(--border);background:var(--cream);padding:10px 14px;font-size:0.95rem;width:100%;">
|
|
</div>
|
|
</div>
|
|
<div class="form-row">
|
|
<label>Slot name</label>
|
|
<input type="text" name="name" placeholder="e.g. Dessert" required>
|
|
</div>
|
|
<div class="form-row">
|
|
<label>Max claims</label>
|
|
<input type="number" name="max_claims" value="2" min="1">
|
|
</div>
|
|
<button class="btn-submit" type="submit">Add slot</button>
|
|
</form>
|
|
</div>
|
|
{{end}}
|
|
{{end}}
|