mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-10-28 15:51:15 +00:00
feat(bookings): dashboard cancellation and waitlist promotion
- POST /v1/catalog/bookings/{id}/cancel lets businesses cancel
tenant-owned bookings; two-step confirm in the detail modal
- Shared PromoteWaitlist helper promotes earliest waitlisted bookings
when a class seat frees, on both dashboard and customer cancel paths;
promoted customers get a confirmation email, waitlist ledger marked
resolved
- waitlisted status surfaced in bookings UI: stats card, filter, warning
badge, demo seed, CS+EN strings
- BookingRecord gains created_at for fair ordering; GetBookingByID and
ResolveWaitlistEntry added to the repository surface
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
0405106587
commit
668a54ae1d
15 files changed
+563
-5
No files matched your search
@@ -0,0 +1,64 @@
|
||||
package bookings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
|
||||
"bookra/apps/backend/internal/db"
|
||||
)
|
||||
|
||||
// PromoteWaitlist confirms the earliest waitlisted bookings on a class session
|
||||
// while free capacity remains. It returns the promoted records so callers can
|
||||
// notify customers. A missing session is treated as nothing to promote.
|
||||
func PromoteWaitlist(ctx context.Context, repo db.Repository, tenantID, sessionID string) ([]db.BookingRecord, error) {
|
||||
session, err := repo.GetClassSessionByID(ctx, sessionID)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bookings, err := repo.ListBookingsByTenantBetween(ctx, tenantID, session.StartsAt.Add(-time.Second), session.EndsAt.Add(time.Second))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
confirmed := 0
|
||||
var waitlisted []db.BookingRecord
|
||||
for _, booking := range bookings {
|
||||
if booking.ClassSessionID == nil || *booking.ClassSessionID != sessionID {
|
||||
continue
|
||||
}
|
||||
switch booking.Status {
|
||||
case "confirmed":
|
||||
confirmed++
|
||||
case "waitlisted":
|
||||
waitlisted = append(waitlisted, booking)
|
||||
}
|
||||
}
|
||||
if len(waitlisted) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
sort.SliceStable(waitlisted, func(i, j int) bool {
|
||||
return waitlisted[i].CreatedAt.Before(waitlisted[j].CreatedAt)
|
||||
})
|
||||
|
||||
var promoted []db.BookingRecord
|
||||
for _, booking := range waitlisted {
|
||||
if session.Capacity > 0 && confirmed >= int(session.Capacity) {
|
||||
break
|
||||
}
|
||||
if err := repo.UpdateBookingStatus(ctx, booking.ID, "confirmed"); err != nil {
|
||||
return promoted, err
|
||||
}
|
||||
_ = repo.ResolveWaitlistEntry(ctx, tenantID, sessionID, booking.CustomerEmail)
|
||||
confirmed++
|
||||
promoted = append(promoted, booking)
|
||||
}
|
||||
return promoted, nil
|
||||
}
|
||||
Reference in new issue
Block a user