mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-10-28 07:41:16 +00:00
feat(bookings): customer self-service rebooking with real slot picker
CustomerBookingView now carries tenantSlug/serviceId/staffId/ classSessionId so the manage page can query live availability; class bookings resolve their session title as the display label. Reschedule rejects waitlisted/completed bookings and session-bound class bookings (400), collapsing a dead branch in the appointment path. The manage page reschedule dialog replaces free-form date/time inputs with a date → availability-slot picker filtered to appointment mode, so customers can only move to a genuinely free slot with the correct duration; 409 surfaces "slot just taken". Actions are status-gated: reschedule for confirmed/pending appointments, cancel for live bookings, explanatory note for class bookings. 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
1cd5b1ba91
commit
796c06ae66
7 files changed
+311
-87
No files matched your search
@@ -111,3 +111,41 @@ func TestRescheduleRejectsConflictingSlot(t *testing.T) {
|
||||
t.Fatalf("expected ErrBookingConflict, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRescheduleRejectsClassWaitlistedAndCompleted(t *testing.T) {
|
||||
repo := db.NewMemoryRepository()
|
||||
svc := NewCustomerService(repo, nil, "http://localhost:3000")
|
||||
sessionID := "4bf74c12-44dd-45ca-86bb-b104f16f2435"
|
||||
future := time.Now().Add(48 * time.Hour)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
status string
|
||||
sessionID *string
|
||||
}{
|
||||
{"class booking", "confirmed", &sessionID},
|
||||
{"waitlisted booking", "waitlisted", &sessionID},
|
||||
{"completed booking", "completed", nil},
|
||||
}
|
||||
for i, tc := range cases {
|
||||
_, err := repo.CreateBooking(context.Background(), db.CreateBookingParams{
|
||||
TenantID: "5d6b3551-0a3e-4b86-bdf0-e9df20a47148",
|
||||
ClassSessionID: tc.sessionID,
|
||||
StartsAt: future,
|
||||
EndsAt: future.Add(time.Hour),
|
||||
Status: tc.status,
|
||||
Reference: "BOK-RS-" + string(rune('A'+i)),
|
||||
ManageToken: "tok",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("%s: create booking: %v", tc.name, err)
|
||||
}
|
||||
err = svc.RescheduleBooking(context.Background(), "BOK-RS-"+string(rune('A'+i)), domain.RescheduleBookingRequest{
|
||||
NewStartsAt: future.Add(2 * time.Hour).Format(time.RFC3339),
|
||||
NewEndsAt: future.Add(3 * time.Hour).Format(time.RFC3339),
|
||||
}, "tok")
|
||||
if err != ErrInvalidReschedule {
|
||||
t.Fatalf("%s: expected ErrInvalidReschedule, got %v", tc.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user