mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-10-28 15:51:15 +00:00
feat(bookings): session-to-session move for class bookings
Class customers can move a booking to another future session of the same template from the manage page. Full target sessions place the booking on the waitlist; freeing a confirmed seat drains the old session's queue via the existing promotion path. Reminders re-arm for the new time. 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
6c3b2bf6bf
commit
e5cb9bd543
10 files changed
+927
-10
No files matched your search
@@ -1176,6 +1176,54 @@ func NewServer(cfg config.Config, pools *db.Pools) (*Server, error) {
|
||||
c.JSON(http.StatusOK, gin.H{"status": "cancelled"})
|
||||
})
|
||||
|
||||
// Class bookings move between sessions of the same template rather than
|
||||
// rescheduling into arbitrary windows.
|
||||
server.router.GET("/v1/public/bookings/:reference/sessions", func(c *gin.Context) {
|
||||
token := c.Query("token")
|
||||
sessions, err := customerBookingService.ListSessionMoveTargets(c.Request.Context(), c.Param("reference"), token)
|
||||
if err != nil {
|
||||
status := http.StatusInternalServerError
|
||||
switch {
|
||||
case errors.Is(err, bookings.ErrBookingNotFound):
|
||||
status = http.StatusNotFound
|
||||
case errors.Is(err, bookings.ErrUnauthorized):
|
||||
status = http.StatusForbidden
|
||||
case errors.Is(err, bookings.ErrBookingCancelled):
|
||||
status = http.StatusConflict
|
||||
case errors.Is(err, bookings.ErrInvalidReschedule):
|
||||
status = http.StatusBadRequest
|
||||
}
|
||||
c.JSON(status, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"sessions": sessions})
|
||||
})
|
||||
server.router.POST("/v1/public/bookings/:reference/move", publicRateLimiter.Middleware(), func(c *gin.Context) {
|
||||
token := c.Query("token")
|
||||
var request domain.MoveBookingRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid_request"})
|
||||
return
|
||||
}
|
||||
response, err := customerBookingService.MoveBookingToSession(c.Request.Context(), c.Param("reference"), token, request.ClassSessionID)
|
||||
if err != nil {
|
||||
status := http.StatusInternalServerError
|
||||
switch {
|
||||
case errors.Is(err, bookings.ErrBookingNotFound):
|
||||
status = http.StatusNotFound
|
||||
case errors.Is(err, bookings.ErrUnauthorized):
|
||||
status = http.StatusForbidden
|
||||
case errors.Is(err, bookings.ErrBookingCancelled):
|
||||
status = http.StatusConflict
|
||||
case errors.Is(err, bookings.ErrInvalidReschedule):
|
||||
status = http.StatusBadRequest
|
||||
}
|
||||
c.JSON(status, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response)
|
||||
})
|
||||
|
||||
// Read-only ICS feed. The path token is the only credential — same trust
|
||||
// model as private calendar URLs from Google/Outlook.
|
||||
server.router.GET("/v1/public/calendar/:token", publicRateLimiter.Middleware(), func(c *gin.Context) {
|
||||
|
||||
Reference in new issue
Block a user