Skip to content

Commit b2fbbb5

Browse files
feat: add cancel booking features
1 parent 4b1e37c commit b2fbbb5

File tree

9 files changed

+380
-0
lines changed

9 files changed

+380
-0
lines changed

cmd/api/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ func (app *application) mount() http.Handler {
143143
r.Post("/pending-bookings/{bookingID}/reject", app.rejectBookingHandler)
144144
r.Post("/pricing", app.createVenuePricingHandler)
145145
r.Put("/pricing/{pricingID}", app.updateVenuePricingHandler)
146+
r.Delete("/pricing/{pricingID}", app.deleteVenuePricingHandler)
146147
r.Patch("/", app.updateVenueInfo)
147148
r.Get("/photos", app.getVenueAllPhotosHandler)
148149
r.Delete("/photos", app.deleteVenuePhotoHandler)
@@ -186,6 +187,7 @@ func (app *application) mount() http.Handler {
186187
r.With(app.CheckAdmin).Post("/assign-assistant/{playerID}", app.AssignAssistantHandler)
187188
r.Get("/players", app.getGamePlayersHandler)
188189
r.Post("/request", app.CreateJoinRequest)
190+
r.Delete("/request", app.DeleteJoinRequest)
189191
r.With(app.RequireGameAdminAssistant).Post("/accept", app.AcceptJoinRequest)
190192
r.With(app.RequireGameAdminAssistant).Get("/requests", app.getAllGameJoinRequestsHandler)
191193
r.With(app.RequireGameAdminAssistant).Post("/reject", app.RejectJoinRequest)

cmd/api/booking.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,50 @@ func (app *application) updateVenuePricingHandler(w http.ResponseWriter, r *http
531531
json.NewEncoder(w).Encode(pricing)
532532
}
533533

534+
// DeleteVenuePricing godoc
535+
//
536+
// @Summary Delete a pricing slot for a venue
537+
// @Description Allows venue owners to delete a specific pricing slot.
538+
// @Tags Venue-Owner
539+
// @Accept json
540+
// @Produce json
541+
// @Param venueID path int true "Venue ID"
542+
// @Param pricingID path int true "Pricing Slot ID"
543+
// @Success 204 "No Content"
544+
// @Failure 400 {object} error "Bad Request: Invalid input"
545+
// @Failure 404 {object} error "Not Found: No such pricing slot"
546+
// @Failure 500 {object} error "Internal Server Error"
547+
// @Security ApiKeyAuth
548+
// @Router /venues/{venueID}/pricing/{pricingID} [delete]
549+
func (app *application) deleteVenuePricingHandler(w http.ResponseWriter, r *http.Request) {
550+
venueIDStr := chi.URLParam(r, "venueID")
551+
pricingIDStr := chi.URLParam(r, "pricingID")
552+
553+
venueID, err := strconv.ParseInt(venueIDStr, 10, 64)
554+
if err != nil {
555+
app.badRequestResponse(w, r, err)
556+
return
557+
}
558+
559+
pricingID, err := strconv.ParseInt(pricingIDStr, 10, 64)
560+
if err != nil {
561+
app.badRequestResponse(w, r, err)
562+
return
563+
}
564+
565+
err = app.store.Bookings.DeletePricingSlot(r.Context(), venueID, pricingID)
566+
if err != nil {
567+
if strings.Contains(err.Error(), "no pricing slot found") {
568+
http.Error(w, err.Error(), http.StatusNotFound)
569+
} else {
570+
app.internalServerError(w, r, err)
571+
}
572+
return
573+
}
574+
575+
w.WriteHeader(http.StatusNoContent)
576+
}
577+
534578
type BulkCreatePricingPayload struct {
535579
//we use dive, required to ensure each item inside the array is individually validated.
536580
Slots []CreatePricingPayload `json:"slots" validate:"required,dive,required"`

cmd/api/games.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"log"
99
"net/http"
1010
"strconv"
11+
"strings"
1112
"time"
1213

1314
"github.com/go-chi/chi/v5"
@@ -227,6 +228,46 @@ func (app *application) AcceptJoinRequest(w http.ResponseWriter, r *http.Request
227228
})
228229
}
229230

231+
// DeleteJoinRequest godoc
232+
//
233+
// @Summary Delete a join request for a game
234+
// @Description Allows a user to delete their previously sent join request for a specific game.
235+
// @Tags Games
236+
// @Param gameID path int true "Game ID"
237+
// @Success 200 {object} map[string]string "Join request deleted"
238+
// @Failure 400 {object} error "Invalid game ID"
239+
// @Failure 404 {object} error "Join request not found"
240+
// @Failure 500 {object} error "Internal server error"
241+
// @Security ApiKeyAuth
242+
// @Router /games/{gameID}/request [delete]
243+
func (app *application) DeleteJoinRequest(w http.ResponseWriter, r *http.Request) {
244+
user := getUserFromContext(r)
245+
246+
// Parse gameID from URL
247+
gameIDStr := chi.URLParam(r, "gameID")
248+
gameID, err := strconv.ParseInt(gameIDStr, 10, 64)
249+
if err != nil {
250+
http.Error(w, "Invalid game ID", http.StatusBadRequest)
251+
return
252+
}
253+
254+
// Attempt to delete join request
255+
err = app.store.Games.DeleteJoinRequest(r.Context(), gameID, user.ID)
256+
if err != nil {
257+
if strings.Contains(err.Error(), "no join request found") {
258+
app.notFoundResponse(w, r, fmt.Errorf("no join request found for game_id=%d", gameID))
259+
return
260+
}
261+
app.logger.Errorf("Error deleting join request: %v", err)
262+
writeJSONError(w, http.StatusInternalServerError, "Internal server error")
263+
return
264+
}
265+
266+
writeJSON(w, http.StatusOK, map[string]string{
267+
"message": "Join request deleted",
268+
})
269+
}
270+
230271
// Prepare response
231272
type PlayerResponse struct {
232273
ID int64 `json:"id"`

docs/docs.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,50 @@ const docTemplate = `{
12561256
"schema": {}
12571257
}
12581258
}
1259+
},
1260+
"delete": {
1261+
"security": [
1262+
{
1263+
"ApiKeyAuth": []
1264+
}
1265+
],
1266+
"description": "Allows a user to delete their previously sent join request for a specific game.",
1267+
"tags": [
1268+
"Games"
1269+
],
1270+
"summary": "Delete a join request for a game",
1271+
"parameters": [
1272+
{
1273+
"type": "integer",
1274+
"description": "Game ID",
1275+
"name": "gameID",
1276+
"in": "path",
1277+
"required": true
1278+
}
1279+
],
1280+
"responses": {
1281+
"200": {
1282+
"description": "Join request deleted",
1283+
"schema": {
1284+
"type": "object",
1285+
"additionalProperties": {
1286+
"type": "string"
1287+
}
1288+
}
1289+
},
1290+
"400": {
1291+
"description": "Invalid game ID",
1292+
"schema": {}
1293+
},
1294+
"404": {
1295+
"description": "Join request not found",
1296+
"schema": {}
1297+
},
1298+
"500": {
1299+
"description": "Internal server error",
1300+
"schema": {}
1301+
}
1302+
}
12591303
}
12601304
},
12611305
"/games/{gameID}/requests": {
@@ -3225,6 +3269,57 @@ const docTemplate = `{
32253269
"schema": {}
32263270
}
32273271
}
3272+
},
3273+
"delete": {
3274+
"security": [
3275+
{
3276+
"ApiKeyAuth": []
3277+
}
3278+
],
3279+
"description": "Allows venue owners to delete a specific pricing slot.",
3280+
"consumes": [
3281+
"application/json"
3282+
],
3283+
"produces": [
3284+
"application/json"
3285+
],
3286+
"tags": [
3287+
"Venue-Owner"
3288+
],
3289+
"summary": "Delete a pricing slot for a venue",
3290+
"parameters": [
3291+
{
3292+
"type": "integer",
3293+
"description": "Venue ID",
3294+
"name": "venueID",
3295+
"in": "path",
3296+
"required": true
3297+
},
3298+
{
3299+
"type": "integer",
3300+
"description": "Pricing Slot ID",
3301+
"name": "pricingID",
3302+
"in": "path",
3303+
"required": true
3304+
}
3305+
],
3306+
"responses": {
3307+
"204": {
3308+
"description": "No Content"
3309+
},
3310+
"400": {
3311+
"description": "Bad Request: Invalid input",
3312+
"schema": {}
3313+
},
3314+
"404": {
3315+
"description": "Not Found: No such pricing slot",
3316+
"schema": {}
3317+
},
3318+
"500": {
3319+
"description": "Internal Server Error",
3320+
"schema": {}
3321+
}
3322+
}
32283323
}
32293324
},
32303325
"/venues/{venueID}/reviews": {

docs/swagger.json

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,50 @@
12481248
"schema": {}
12491249
}
12501250
}
1251+
},
1252+
"delete": {
1253+
"security": [
1254+
{
1255+
"ApiKeyAuth": []
1256+
}
1257+
],
1258+
"description": "Allows a user to delete their previously sent join request for a specific game.",
1259+
"tags": [
1260+
"Games"
1261+
],
1262+
"summary": "Delete a join request for a game",
1263+
"parameters": [
1264+
{
1265+
"type": "integer",
1266+
"description": "Game ID",
1267+
"name": "gameID",
1268+
"in": "path",
1269+
"required": true
1270+
}
1271+
],
1272+
"responses": {
1273+
"200": {
1274+
"description": "Join request deleted",
1275+
"schema": {
1276+
"type": "object",
1277+
"additionalProperties": {
1278+
"type": "string"
1279+
}
1280+
}
1281+
},
1282+
"400": {
1283+
"description": "Invalid game ID",
1284+
"schema": {}
1285+
},
1286+
"404": {
1287+
"description": "Join request not found",
1288+
"schema": {}
1289+
},
1290+
"500": {
1291+
"description": "Internal server error",
1292+
"schema": {}
1293+
}
1294+
}
12511295
}
12521296
},
12531297
"/games/{gameID}/requests": {
@@ -3217,6 +3261,57 @@
32173261
"schema": {}
32183262
}
32193263
}
3264+
},
3265+
"delete": {
3266+
"security": [
3267+
{
3268+
"ApiKeyAuth": []
3269+
}
3270+
],
3271+
"description": "Allows venue owners to delete a specific pricing slot.",
3272+
"consumes": [
3273+
"application/json"
3274+
],
3275+
"produces": [
3276+
"application/json"
3277+
],
3278+
"tags": [
3279+
"Venue-Owner"
3280+
],
3281+
"summary": "Delete a pricing slot for a venue",
3282+
"parameters": [
3283+
{
3284+
"type": "integer",
3285+
"description": "Venue ID",
3286+
"name": "venueID",
3287+
"in": "path",
3288+
"required": true
3289+
},
3290+
{
3291+
"type": "integer",
3292+
"description": "Pricing Slot ID",
3293+
"name": "pricingID",
3294+
"in": "path",
3295+
"required": true
3296+
}
3297+
],
3298+
"responses": {
3299+
"204": {
3300+
"description": "No Content"
3301+
},
3302+
"400": {
3303+
"description": "Bad Request: Invalid input",
3304+
"schema": {}
3305+
},
3306+
"404": {
3307+
"description": "Not Found: No such pricing slot",
3308+
"schema": {}
3309+
},
3310+
"500": {
3311+
"description": "Internal Server Error",
3312+
"schema": {}
3313+
}
3314+
}
32203315
}
32213316
},
32223317
"/venues/{venueID}/reviews": {

0 commit comments

Comments
 (0)