Add edit RSVP modal, plus-one tracking, and unified signup form
Merge RSVP + slot claim into a single form. Add plus_one field to RSVPs. Add clickable RSVP names that open a modal to edit name/note/plus_one. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+129
-63
@@ -34,39 +34,42 @@ func createTestEvent(t *testing.T, q *db.Queries) db.Event {
|
||||
return event
|
||||
}
|
||||
|
||||
func TestAutoRsvpOnClaim(t *testing.T) {
|
||||
// autoRsvp simulates the merged handleRsvp logic: create RSVP (deduped) + optional claim
|
||||
func autoRsvp(ctx context.Context, q *db.Queries, event db.Event, name string, note string, slotID *int64) error {
|
||||
return autoRsvpPlusOne(ctx, q, event, name, note, 0, slotID)
|
||||
}
|
||||
|
||||
func autoRsvpPlusOne(ctx context.Context, q *db.Queries, event db.Event, name string, note string, plusOne int64, slotID *int64) error {
|
||||
if slotID != nil {
|
||||
_, err := q.CreateClaim(ctx, db.CreateClaimParams{
|
||||
SlotID: *slotID, Name: name, Note: note,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err := q.GetRsvpByName(ctx, db.GetRsvpByNameParams{
|
||||
EventID: event.ID, Name: name,
|
||||
})
|
||||
if err == sql.ErrNoRows {
|
||||
_, err = q.CreateRsvp(ctx, db.CreateRsvpParams{
|
||||
EventID: event.ID, Name: name, Note: note, PlusOne: plusOne,
|
||||
})
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRsvpOnly(t *testing.T) {
|
||||
_, q := setupTestDB(t)
|
||||
ctx := context.Background()
|
||||
event := createTestEvent(t, q)
|
||||
|
||||
slot, err := q.CreateSlot(ctx, db.CreateSlotParams{
|
||||
EventID: event.ID, Name: "Drinks", MaxClaims: 5,
|
||||
})
|
||||
if err != nil {
|
||||
if err := autoRsvp(ctx, q, event, "Alice", "", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Claim a slot — should also create an RSVP
|
||||
_, err = q.CreateClaim(ctx, db.CreateClaimParams{
|
||||
SlotID: slot.ID, Name: "Alice",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Simulate the auto-RSVP logic from handleClaim
|
||||
_, err = q.GetRsvpByName(ctx, db.GetRsvpByNameParams{
|
||||
EventID: event.ID, Name: "Alice",
|
||||
})
|
||||
if err == sql.ErrNoRows {
|
||||
_, err = q.CreateRsvp(ctx, db.CreateRsvpParams{
|
||||
EventID: event.ID, Name: "Alice",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
rsvps, _ := q.ListRsvps(ctx, event.ID)
|
||||
if len(rsvps) != 1 {
|
||||
t.Fatalf("expected 1 RSVP, got %d", len(rsvps))
|
||||
@@ -76,7 +79,42 @@ func TestAutoRsvpOnClaim(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoRsvpNoDuplicate(t *testing.T) {
|
||||
func TestRsvpWithClaim(t *testing.T) {
|
||||
_, q := setupTestDB(t)
|
||||
ctx := context.Background()
|
||||
event := createTestEvent(t, q)
|
||||
|
||||
slot, err := q.CreateSlot(ctx, db.CreateSlotParams{
|
||||
EventID: event.ID, Name: "Drinks", MaxClaims: 5,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := autoRsvp(ctx, q, event, "Alice", "sparkling water", &slot.ID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check RSVP created
|
||||
rsvps, _ := q.ListRsvps(ctx, event.ID)
|
||||
if len(rsvps) != 1 {
|
||||
t.Fatalf("expected 1 RSVP, got %d", len(rsvps))
|
||||
}
|
||||
if rsvps[0].Name != "Alice" {
|
||||
t.Fatalf("expected RSVP name Alice, got %s", rsvps[0].Name)
|
||||
}
|
||||
|
||||
// Check claim created
|
||||
claims, _ := q.ListClaimsByEvent(ctx, event.ID)
|
||||
if len(claims) != 1 {
|
||||
t.Fatalf("expected 1 claim, got %d", len(claims))
|
||||
}
|
||||
if claims[0].Name != "Alice" {
|
||||
t.Fatalf("expected claim name Alice, got %s", claims[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRsvpNoDuplicate(t *testing.T) {
|
||||
_, q := setupTestDB(t)
|
||||
ctx := context.Background()
|
||||
event := createTestEvent(t, q)
|
||||
@@ -85,23 +123,11 @@ func TestAutoRsvpNoDuplicate(t *testing.T) {
|
||||
EventID: event.ID, Name: "Drinks", MaxClaims: 5,
|
||||
})
|
||||
|
||||
// RSVP first
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{
|
||||
EventID: event.ID, Name: "Bob",
|
||||
})
|
||||
// RSVP first (no slot)
|
||||
autoRsvp(ctx, q, event, "Bob", "", nil)
|
||||
|
||||
// Then claim — auto-RSVP should skip
|
||||
q.CreateClaim(ctx, db.CreateClaimParams{
|
||||
SlotID: slot.ID, Name: "Bob",
|
||||
})
|
||||
_, err := q.GetRsvpByName(ctx, db.GetRsvpByNameParams{
|
||||
EventID: event.ID, Name: "Bob",
|
||||
})
|
||||
if err == sql.ErrNoRows {
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{
|
||||
EventID: event.ID, Name: "Bob",
|
||||
})
|
||||
}
|
||||
// Then RSVP again with a slot claim — should not duplicate RSVP
|
||||
autoRsvp(ctx, q, event, "Bob", "", &slot.ID)
|
||||
|
||||
rsvps, _ := q.ListRsvps(ctx, event.ID)
|
||||
if len(rsvps) != 1 {
|
||||
@@ -109,7 +135,7 @@ func TestAutoRsvpNoDuplicate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoRsvpCaseInsensitive(t *testing.T) {
|
||||
func TestRsvpCaseInsensitiveDedup(t *testing.T) {
|
||||
_, q := setupTestDB(t)
|
||||
ctx := context.Background()
|
||||
event := createTestEvent(t, q)
|
||||
@@ -119,22 +145,10 @@ func TestAutoRsvpCaseInsensitive(t *testing.T) {
|
||||
})
|
||||
|
||||
// RSVP as "alice"
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{
|
||||
EventID: event.ID, Name: "alice",
|
||||
})
|
||||
autoRsvp(ctx, q, event, "alice", "", nil)
|
||||
|
||||
// Claim as "Alice" — should not create duplicate
|
||||
q.CreateClaim(ctx, db.CreateClaimParams{
|
||||
SlotID: slot.ID, Name: "Alice",
|
||||
})
|
||||
_, err := q.GetRsvpByName(ctx, db.GetRsvpByNameParams{
|
||||
EventID: event.ID, Name: "Alice",
|
||||
})
|
||||
if err == sql.ErrNoRows {
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{
|
||||
EventID: event.ID, Name: "Alice",
|
||||
})
|
||||
}
|
||||
// Claim as "Alice" — should not create duplicate RSVP
|
||||
autoRsvp(ctx, q, event, "Alice", "", &slot.ID)
|
||||
|
||||
rsvps, _ := q.ListRsvps(ctx, event.ID)
|
||||
if len(rsvps) != 1 {
|
||||
@@ -148,10 +162,10 @@ func TestDeduplicateExistingRsvps(t *testing.T) {
|
||||
event := createTestEvent(t, q)
|
||||
|
||||
// Insert duplicate RSVPs directly
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{EventID: event.ID, Name: "Charlie"})
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{EventID: event.ID, Name: "Charlie"})
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{EventID: event.ID, Name: "charlie"})
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{EventID: event.ID, Name: "Dana"})
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{EventID: event.ID, Name: "Charlie", PlusOne: 0})
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{EventID: event.ID, Name: "Charlie", PlusOne: 0})
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{EventID: event.ID, Name: "charlie", PlusOne: 0})
|
||||
q.CreateRsvp(ctx, db.CreateRsvpParams{EventID: event.ID, Name: "Dana", PlusOne: 0})
|
||||
|
||||
rsvps, _ := q.ListRsvps(ctx, event.ID)
|
||||
if len(rsvps) != 4 {
|
||||
@@ -166,3 +180,55 @@ func TestDeduplicateExistingRsvps(t *testing.T) {
|
||||
t.Fatalf("expected 2 RSVPs after dedup (Charlie + Dana), got %d", len(rsvps))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRsvp(t *testing.T) {
|
||||
_, q := setupTestDB(t)
|
||||
ctx := context.Background()
|
||||
event := createTestEvent(t, q)
|
||||
|
||||
rsvp, err := q.CreateRsvp(ctx, db.CreateRsvpParams{
|
||||
EventID: event.ID, Name: "Alice", Note: "hi", PlusOne: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = q.UpdateRsvp(ctx, db.UpdateRsvpParams{
|
||||
Name: "Alicia", Note: "updated", PlusOne: 3, ID: rsvp.ID,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
updated, err := q.GetRsvp(ctx, rsvp.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if updated.Name != "Alicia" {
|
||||
t.Fatalf("expected name Alicia, got %s", updated.Name)
|
||||
}
|
||||
if updated.Note != "updated" {
|
||||
t.Fatalf("expected note 'updated', got %s", updated.Note)
|
||||
}
|
||||
if updated.PlusOne != 3 {
|
||||
t.Fatalf("expected PlusOne=3, got %d", updated.PlusOne)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRsvpPlusOne(t *testing.T) {
|
||||
_, q := setupTestDB(t)
|
||||
ctx := context.Background()
|
||||
event := createTestEvent(t, q)
|
||||
|
||||
if err := autoRsvpPlusOne(ctx, q, event, "Alice", "", 2, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rsvps, _ := q.ListRsvps(ctx, event.ID)
|
||||
if len(rsvps) != 1 {
|
||||
t.Fatalf("expected 1 RSVP, got %d", len(rsvps))
|
||||
}
|
||||
if rsvps[0].PlusOne != 2 {
|
||||
t.Fatalf("expected PlusOne=2, got %d", rsvps[0].PlusOne)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user