Initial commit: potluck signup app

Go + chi + SQLite + HTMX with SSE live updates.
Soft Brutalism design, emoji picker, Docker deploy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-14 22:55:54 -04:00
commit 8b32d98267
19 changed files with 1699 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
package db
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}
+37
View File
@@ -0,0 +1,37 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
package db
import (
"time"
)
type Claim struct {
ID int64
SlotID int64
Name string
Note string
CreatedAt time.Time
}
type Event struct {
ID int64
Slug string
Title string
Date string
Time string
Location string
AdminToken string
CreatedAt time.Time
}
type Slot struct {
ID int64
EventID int64
Name string
Emoji string
MaxClaims int64
SortOrder int64
}
+53
View File
@@ -0,0 +1,53 @@
-- name: GetEventBySlug :one
SELECT * FROM events WHERE slug = ?;
-- name: GetEventByAdminToken :one
SELECT * FROM events WHERE admin_token = ?;
-- name: CreateEvent :one
INSERT INTO events (slug, title, date, time, location, admin_token)
VALUES (?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: UpdateEvent :exec
UPDATE events SET title = ?, date = ?, time = ?, location = ? WHERE id = ?;
-- name: DeleteEvent :exec
DELETE FROM events WHERE id = ?;
-- name: ListSlots :many
SELECT * FROM slots WHERE event_id = ? ORDER BY sort_order, id;
-- name: GetSlot :one
SELECT * FROM slots WHERE id = ?;
-- name: CreateSlot :one
INSERT INTO slots (event_id, name, emoji, max_claims, sort_order)
VALUES (?, ?, ?, ?, ?)
RETURNING *;
-- name: UpdateSlot :exec
UPDATE slots SET name = ?, emoji = ?, max_claims = ? WHERE id = ?;
-- name: DeleteSlot :exec
DELETE FROM slots WHERE id = ?;
-- name: ListClaimsBySlot :many
SELECT * FROM claims WHERE slot_id = ? ORDER BY created_at;
-- name: ListClaimsByEvent :many
SELECT c.* FROM claims c
JOIN slots s ON c.slot_id = s.id
WHERE s.event_id = ?
ORDER BY c.slot_id, c.created_at;
-- name: CreateClaim :one
INSERT INTO claims (slot_id, name, note)
VALUES (?, ?, ?)
RETURNING *;
-- name: DeleteClaim :exec
DELETE FROM claims WHERE id = ?;
-- name: CountClaimsBySlot :one
SELECT COUNT(*) FROM claims WHERE slot_id = ?;
+350
View File
@@ -0,0 +1,350 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: queries.sql
package db
import (
"context"
)
const countClaimsBySlot = `-- name: CountClaimsBySlot :one
SELECT COUNT(*) FROM claims WHERE slot_id = ?
`
func (q *Queries) CountClaimsBySlot(ctx context.Context, slotID int64) (int64, error) {
row := q.db.QueryRowContext(ctx, countClaimsBySlot, slotID)
var count int64
err := row.Scan(&count)
return count, err
}
const createClaim = `-- name: CreateClaim :one
INSERT INTO claims (slot_id, name, note)
VALUES (?, ?, ?)
RETURNING id, slot_id, name, note, created_at
`
type CreateClaimParams struct {
SlotID int64
Name string
Note string
}
func (q *Queries) CreateClaim(ctx context.Context, arg CreateClaimParams) (Claim, error) {
row := q.db.QueryRowContext(ctx, createClaim, arg.SlotID, arg.Name, arg.Note)
var i Claim
err := row.Scan(
&i.ID,
&i.SlotID,
&i.Name,
&i.Note,
&i.CreatedAt,
)
return i, err
}
const createEvent = `-- name: CreateEvent :one
INSERT INTO events (slug, title, date, time, location, admin_token)
VALUES (?, ?, ?, ?, ?, ?)
RETURNING id, slug, title, date, time, location, admin_token, created_at
`
type CreateEventParams struct {
Slug string
Title string
Date string
Time string
Location string
AdminToken string
}
func (q *Queries) CreateEvent(ctx context.Context, arg CreateEventParams) (Event, error) {
row := q.db.QueryRowContext(ctx, createEvent,
arg.Slug,
arg.Title,
arg.Date,
arg.Time,
arg.Location,
arg.AdminToken,
)
var i Event
err := row.Scan(
&i.ID,
&i.Slug,
&i.Title,
&i.Date,
&i.Time,
&i.Location,
&i.AdminToken,
&i.CreatedAt,
)
return i, err
}
const createSlot = `-- name: CreateSlot :one
INSERT INTO slots (event_id, name, emoji, max_claims, sort_order)
VALUES (?, ?, ?, ?, ?)
RETURNING id, event_id, name, emoji, max_claims, sort_order
`
type CreateSlotParams struct {
EventID int64
Name string
Emoji string
MaxClaims int64
SortOrder int64
}
func (q *Queries) CreateSlot(ctx context.Context, arg CreateSlotParams) (Slot, error) {
row := q.db.QueryRowContext(ctx, createSlot,
arg.EventID,
arg.Name,
arg.Emoji,
arg.MaxClaims,
arg.SortOrder,
)
var i Slot
err := row.Scan(
&i.ID,
&i.EventID,
&i.Name,
&i.Emoji,
&i.MaxClaims,
&i.SortOrder,
)
return i, err
}
const deleteClaim = `-- name: DeleteClaim :exec
DELETE FROM claims WHERE id = ?
`
func (q *Queries) DeleteClaim(ctx context.Context, id int64) error {
_, err := q.db.ExecContext(ctx, deleteClaim, id)
return err
}
const deleteEvent = `-- name: DeleteEvent :exec
DELETE FROM events WHERE id = ?
`
func (q *Queries) DeleteEvent(ctx context.Context, id int64) error {
_, err := q.db.ExecContext(ctx, deleteEvent, id)
return err
}
const deleteSlot = `-- name: DeleteSlot :exec
DELETE FROM slots WHERE id = ?
`
func (q *Queries) DeleteSlot(ctx context.Context, id int64) error {
_, err := q.db.ExecContext(ctx, deleteSlot, id)
return err
}
const getEventByAdminToken = `-- name: GetEventByAdminToken :one
SELECT id, slug, title, date, time, location, admin_token, created_at FROM events WHERE admin_token = ?
`
func (q *Queries) GetEventByAdminToken(ctx context.Context, adminToken string) (Event, error) {
row := q.db.QueryRowContext(ctx, getEventByAdminToken, adminToken)
var i Event
err := row.Scan(
&i.ID,
&i.Slug,
&i.Title,
&i.Date,
&i.Time,
&i.Location,
&i.AdminToken,
&i.CreatedAt,
)
return i, err
}
const getEventBySlug = `-- name: GetEventBySlug :one
SELECT id, slug, title, date, time, location, admin_token, created_at FROM events WHERE slug = ?
`
func (q *Queries) GetEventBySlug(ctx context.Context, slug string) (Event, error) {
row := q.db.QueryRowContext(ctx, getEventBySlug, slug)
var i Event
err := row.Scan(
&i.ID,
&i.Slug,
&i.Title,
&i.Date,
&i.Time,
&i.Location,
&i.AdminToken,
&i.CreatedAt,
)
return i, err
}
const getSlot = `-- name: GetSlot :one
SELECT id, event_id, name, emoji, max_claims, sort_order FROM slots WHERE id = ?
`
func (q *Queries) GetSlot(ctx context.Context, id int64) (Slot, error) {
row := q.db.QueryRowContext(ctx, getSlot, id)
var i Slot
err := row.Scan(
&i.ID,
&i.EventID,
&i.Name,
&i.Emoji,
&i.MaxClaims,
&i.SortOrder,
)
return i, err
}
const listClaimsByEvent = `-- name: ListClaimsByEvent :many
SELECT c.id, c.slot_id, c.name, c.note, c.created_at FROM claims c
JOIN slots s ON c.slot_id = s.id
WHERE s.event_id = ?
ORDER BY c.slot_id, c.created_at
`
func (q *Queries) ListClaimsByEvent(ctx context.Context, eventID int64) ([]Claim, error) {
rows, err := q.db.QueryContext(ctx, listClaimsByEvent, eventID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Claim
for rows.Next() {
var i Claim
if err := rows.Scan(
&i.ID,
&i.SlotID,
&i.Name,
&i.Note,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listClaimsBySlot = `-- name: ListClaimsBySlot :many
SELECT id, slot_id, name, note, created_at FROM claims WHERE slot_id = ? ORDER BY created_at
`
func (q *Queries) ListClaimsBySlot(ctx context.Context, slotID int64) ([]Claim, error) {
rows, err := q.db.QueryContext(ctx, listClaimsBySlot, slotID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Claim
for rows.Next() {
var i Claim
if err := rows.Scan(
&i.ID,
&i.SlotID,
&i.Name,
&i.Note,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listSlots = `-- name: ListSlots :many
SELECT id, event_id, name, emoji, max_claims, sort_order FROM slots WHERE event_id = ? ORDER BY sort_order, id
`
func (q *Queries) ListSlots(ctx context.Context, eventID int64) ([]Slot, error) {
rows, err := q.db.QueryContext(ctx, listSlots, eventID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Slot
for rows.Next() {
var i Slot
if err := rows.Scan(
&i.ID,
&i.EventID,
&i.Name,
&i.Emoji,
&i.MaxClaims,
&i.SortOrder,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateEvent = `-- name: UpdateEvent :exec
UPDATE events SET title = ?, date = ?, time = ?, location = ? WHERE id = ?
`
type UpdateEventParams struct {
Title string
Date string
Time string
Location string
ID int64
}
func (q *Queries) UpdateEvent(ctx context.Context, arg UpdateEventParams) error {
_, err := q.db.ExecContext(ctx, updateEvent,
arg.Title,
arg.Date,
arg.Time,
arg.Location,
arg.ID,
)
return err
}
const updateSlot = `-- name: UpdateSlot :exec
UPDATE slots SET name = ?, emoji = ?, max_claims = ? WHERE id = ?
`
type UpdateSlotParams struct {
Name string
Emoji string
MaxClaims int64
ID int64
}
func (q *Queries) UpdateSlot(ctx context.Context, arg UpdateSlotParams) error {
_, err := q.db.ExecContext(ctx, updateSlot,
arg.Name,
arg.Emoji,
arg.MaxClaims,
arg.ID,
)
return err
}