Add attendee cap with required login for capped events
Hosts can optionally cap total attendance (RSVPs + plus-ones) when creating an event. Capped events require guests to log in via the existing email code flow; RSVPs are tied to accounts and deduped per user. The event page shows spots left and replaces the form with a full notice when the cap is reached; the server rejects over-cap RSVPs and edits with 409 as a race backstop. Also threads a safeNext-validated ?next= param through the login and name-setup flow so guests land back on the event after signing in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+59
-12
@@ -22,6 +22,17 @@ func (q *Queries) CountClaimsBySlot(ctx context.Context, slotID int64) (int64, e
|
||||
return count, err
|
||||
}
|
||||
|
||||
const countGoing = `-- name: CountGoing :one
|
||||
SELECT CAST(COALESCE(SUM(1 + plus_one), 0) AS INTEGER) FROM rsvps WHERE event_id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) CountGoing(ctx context.Context, eventID int64) (int64, error) {
|
||||
row := q.db.QueryRowContext(ctx, countGoing, eventID)
|
||||
var column_1 int64
|
||||
err := row.Scan(&column_1)
|
||||
return column_1, err
|
||||
}
|
||||
|
||||
const countRsvps = `-- name: CountRsvps :one
|
||||
SELECT COUNT(*) FROM rsvps WHERE event_id = ?
|
||||
`
|
||||
@@ -59,9 +70,9 @@ func (q *Queries) CreateClaim(ctx context.Context, arg CreateClaimParams) (Claim
|
||||
}
|
||||
|
||||
const createEvent = `-- name: CreateEvent :one
|
||||
INSERT INTO events (slug, title, date, time, location, admin_token, description)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
RETURNING id, slug, title, date, time, location, admin_token, description, user_id, created_at
|
||||
INSERT INTO events (slug, title, date, time, location, admin_token, description, attendee_cap)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
RETURNING id, slug, title, date, time, location, admin_token, description, attendee_cap, user_id, created_at
|
||||
`
|
||||
|
||||
type CreateEventParams struct {
|
||||
@@ -72,6 +83,7 @@ type CreateEventParams struct {
|
||||
Location string
|
||||
AdminToken string
|
||||
Description string
|
||||
AttendeeCap int64
|
||||
}
|
||||
|
||||
func (q *Queries) CreateEvent(ctx context.Context, arg CreateEventParams) (Event, error) {
|
||||
@@ -83,6 +95,7 @@ func (q *Queries) CreateEvent(ctx context.Context, arg CreateEventParams) (Event
|
||||
arg.Location,
|
||||
arg.AdminToken,
|
||||
arg.Description,
|
||||
arg.AttendeeCap,
|
||||
)
|
||||
var i Event
|
||||
err := row.Scan(
|
||||
@@ -94,6 +107,7 @@ func (q *Queries) CreateEvent(ctx context.Context, arg CreateEventParams) (Event
|
||||
&i.Location,
|
||||
&i.AdminToken,
|
||||
&i.Description,
|
||||
&i.AttendeeCap,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
@@ -101,9 +115,9 @@ func (q *Queries) CreateEvent(ctx context.Context, arg CreateEventParams) (Event
|
||||
}
|
||||
|
||||
const createRsvp = `-- name: CreateRsvp :one
|
||||
INSERT INTO rsvps (event_id, name, note, plus_one)
|
||||
VALUES (?, ?, ?, ?)
|
||||
RETURNING id, event_id, name, note, plus_one, created_at
|
||||
INSERT INTO rsvps (event_id, name, note, plus_one, user_id)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
RETURNING id, event_id, name, note, plus_one, user_id, created_at
|
||||
`
|
||||
|
||||
type CreateRsvpParams struct {
|
||||
@@ -111,6 +125,7 @@ type CreateRsvpParams struct {
|
||||
Name string
|
||||
Note string
|
||||
PlusOne int64
|
||||
UserID sql.NullInt64
|
||||
}
|
||||
|
||||
func (q *Queries) CreateRsvp(ctx context.Context, arg CreateRsvpParams) (Rsvp, error) {
|
||||
@@ -119,6 +134,7 @@ func (q *Queries) CreateRsvp(ctx context.Context, arg CreateRsvpParams) (Rsvp, e
|
||||
arg.Name,
|
||||
arg.Note,
|
||||
arg.PlusOne,
|
||||
arg.UserID,
|
||||
)
|
||||
var i Rsvp
|
||||
err := row.Scan(
|
||||
@@ -127,6 +143,7 @@ func (q *Queries) CreateRsvp(ctx context.Context, arg CreateRsvpParams) (Rsvp, e
|
||||
&i.Name,
|
||||
&i.Note,
|
||||
&i.PlusOne,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
@@ -328,7 +345,7 @@ func (q *Queries) GetClaimByID(ctx context.Context, id int64) (Claim, error) {
|
||||
}
|
||||
|
||||
const getEventByAdminToken = `-- name: GetEventByAdminToken :one
|
||||
SELECT id, slug, title, date, time, location, admin_token, description, user_id, created_at FROM events WHERE admin_token = ?
|
||||
SELECT id, slug, title, date, time, location, admin_token, description, attendee_cap, user_id, created_at FROM events WHERE admin_token = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetEventByAdminToken(ctx context.Context, adminToken string) (Event, error) {
|
||||
@@ -343,6 +360,7 @@ func (q *Queries) GetEventByAdminToken(ctx context.Context, adminToken string) (
|
||||
&i.Location,
|
||||
&i.AdminToken,
|
||||
&i.Description,
|
||||
&i.AttendeeCap,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
@@ -350,7 +368,7 @@ func (q *Queries) GetEventByAdminToken(ctx context.Context, adminToken string) (
|
||||
}
|
||||
|
||||
const getEventBySlug = `-- name: GetEventBySlug :one
|
||||
SELECT id, slug, title, date, time, location, admin_token, description, user_id, created_at FROM events WHERE slug = ?
|
||||
SELECT id, slug, title, date, time, location, admin_token, description, attendee_cap, user_id, created_at FROM events WHERE slug = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetEventBySlug(ctx context.Context, slug string) (Event, error) {
|
||||
@@ -365,6 +383,7 @@ func (q *Queries) GetEventBySlug(ctx context.Context, slug string) (Event, error
|
||||
&i.Location,
|
||||
&i.AdminToken,
|
||||
&i.Description,
|
||||
&i.AttendeeCap,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
@@ -372,7 +391,7 @@ func (q *Queries) GetEventBySlug(ctx context.Context, slug string) (Event, error
|
||||
}
|
||||
|
||||
const getRsvp = `-- name: GetRsvp :one
|
||||
SELECT id, event_id, name, note, plus_one, created_at FROM rsvps WHERE id = ?
|
||||
SELECT id, event_id, name, note, plus_one, user_id, created_at FROM rsvps WHERE id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetRsvp(ctx context.Context, id int64) (Rsvp, error) {
|
||||
@@ -384,13 +403,14 @@ func (q *Queries) GetRsvp(ctx context.Context, id int64) (Rsvp, error) {
|
||||
&i.Name,
|
||||
&i.Note,
|
||||
&i.PlusOne,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getRsvpByName = `-- name: GetRsvpByName :one
|
||||
SELECT id, event_id, name, note, plus_one, created_at FROM rsvps WHERE event_id = ? AND name = ? COLLATE NOCASE LIMIT 1
|
||||
SELECT id, event_id, name, note, plus_one, user_id, created_at FROM rsvps WHERE event_id = ? AND name = ? COLLATE NOCASE LIMIT 1
|
||||
`
|
||||
|
||||
type GetRsvpByNameParams struct {
|
||||
@@ -407,6 +427,31 @@ func (q *Queries) GetRsvpByName(ctx context.Context, arg GetRsvpByNameParams) (R
|
||||
&i.Name,
|
||||
&i.Note,
|
||||
&i.PlusOne,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getRsvpByUser = `-- name: GetRsvpByUser :one
|
||||
SELECT id, event_id, name, note, plus_one, user_id, created_at FROM rsvps WHERE event_id = ? AND user_id = ? LIMIT 1
|
||||
`
|
||||
|
||||
type GetRsvpByUserParams struct {
|
||||
EventID int64
|
||||
UserID sql.NullInt64
|
||||
}
|
||||
|
||||
func (q *Queries) GetRsvpByUser(ctx context.Context, arg GetRsvpByUserParams) (Rsvp, error) {
|
||||
row := q.db.QueryRowContext(ctx, getRsvpByUser, arg.EventID, arg.UserID)
|
||||
var i Rsvp
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.EventID,
|
||||
&i.Name,
|
||||
&i.Note,
|
||||
&i.PlusOne,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
@@ -569,7 +614,7 @@ func (q *Queries) ListClaimsBySlot(ctx context.Context, slotID int64) ([]Claim,
|
||||
}
|
||||
|
||||
const listEventsByUser = `-- name: ListEventsByUser :many
|
||||
SELECT id, slug, title, date, time, location, admin_token, description, user_id, created_at FROM events WHERE user_id = ? ORDER BY created_at DESC
|
||||
SELECT id, slug, title, date, time, location, admin_token, description, attendee_cap, user_id, created_at FROM events WHERE user_id = ? ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListEventsByUser(ctx context.Context, userID sql.NullInt64) ([]Event, error) {
|
||||
@@ -590,6 +635,7 @@ func (q *Queries) ListEventsByUser(ctx context.Context, userID sql.NullInt64) ([
|
||||
&i.Location,
|
||||
&i.AdminToken,
|
||||
&i.Description,
|
||||
&i.AttendeeCap,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
@@ -607,7 +653,7 @@ func (q *Queries) ListEventsByUser(ctx context.Context, userID sql.NullInt64) ([
|
||||
}
|
||||
|
||||
const listRsvps = `-- name: ListRsvps :many
|
||||
SELECT id, event_id, name, note, plus_one, created_at FROM rsvps WHERE event_id = ? ORDER BY created_at
|
||||
SELECT id, event_id, name, note, plus_one, user_id, created_at FROM rsvps WHERE event_id = ? ORDER BY created_at
|
||||
`
|
||||
|
||||
func (q *Queries) ListRsvps(ctx context.Context, eventID int64) ([]Rsvp, error) {
|
||||
@@ -625,6 +671,7 @@ func (q *Queries) ListRsvps(ctx context.Context, eventID int64) ([]Rsvp, error)
|
||||
&i.Name,
|
||||
&i.Note,
|
||||
&i.PlusOne,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user