File size: 8,223 Bytes
9f6a4af
 
 
 
 
 
 
 
 
 
 
b87136f
 
 
 
 
174fabb
9f6a4af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4a66f20
 
 
 
9f6a4af
4a66f20
 
 
9f6a4af
 
 
 
 
 
 
 
 
 
 
 
 
b87136f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a1d832a
b87136f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174fabb
 
 
 
 
 
 
 
656b323
174fabb
 
 
 
 
 
 
 
 
 
656b323
174fabb
 
 
 
 
656b323
174fabb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
package controllers

import (
	"abdanhafidz.com/go-boilerplate/models/dto"
	"abdanhafidz.com/go-boilerplate/services"
	"github.com/gin-gonic/gin"
)

type ClassBookingController interface {
	GetBookingOptions(ctx *gin.Context)
	BookClass(ctx *gin.Context)
	GetClassBookingDetail(ctx *gin.Context)
	GetClassBookingsByFilter(ctx *gin.Context)
	CreateClassBooking(ctx *gin.Context)
	UpdateClassBooking(ctx *gin.Context)
	DeleteClassBooking(ctx *gin.Context)
	ReduceCapacity(ctx *gin.Context)
}

type classBookingController struct {
	classService services.ClassService
}

func NewClassBookingController(classService services.ClassService) ClassBookingController {
	return &classBookingController{
		classService: classService,
	}
}

// GetBookingOptions godoc
// @Summary      Get Class Booking Options
// @Description  Get available slots and class types for a specific class to be booked.
// @Tags         Class Booking
// @Accept       json
// @Produce      json
// @Param        classId  path      string  true  "Class ID"
// @Success      200      {object}  dto.ClassBookingOptionsResponse
// @Failure      400      {object}  dto.ErrorResponse
// @Failure      404      {object}  dto.ErrorResponse
// @Security     BearerAuth
// @Router       /api/v1/bookings/classes/{classId}/options [get]
func (c *classBookingController) GetBookingOptions(ctx *gin.Context) {
	classId := ctx.Param("classId")
	res, err := c.classService.GetClassBookingOptions(ctx.Request.Context(), classId)
	ResponseJSON(ctx, "", res, err)
}

// BookClass godoc
// @Summary      Book a Class
// @Description  Book a specific slot and type of a class. Returns Xendit checkout URL if payment is required.
// @Tags         Class Booking
// @Accept       json
// @Produce      json
// @Param        request  body      dto.BookClassRequest  true  "Class Booking Data"
// @Success      201      {object}  dto.BookClassResponse
// @Failure      400      {object}  dto.ErrorResponse
// @Failure      401      {object}  dto.ErrorResponse
// @Security     BearerAuth
// @Router       /api/v1/bookings/classes [post]
func (c *classBookingController) BookClass(ctx *gin.Context) {
	req, err := RequestJSON[dto.BookClassRequest](ctx)
	if err != nil {
		return
	}
	userId := ParseUserId(ctx)
	if userId == "" {
		return
	}

	res, err := c.classService.BookClass(ctx.Request.Context(), userId, req)
	if err != nil {
		ResponseJSON(ctx, req, res, err)
		return
	}

	ctx.JSON(201, gin.H{
		"success": true,
		"message": "Class booked successfully",
		"data":    res,
	})
}

// GetClassBookingDetail godoc
// @Summary      Get Class Booking Detail
// @Description  Get class booking details by booking ID
// @Tags         Class Booking
// @Accept       json
// @Produce      json
// @Param        booking_id  query     string  true  "Booking ID"
// @Success      200         {object}  dto.CreateClassBookingResponse
// @Failure      400         {object}  dto.ErrorResponse
// @Security     BearerAuth
// @Router       /api/v1/bookings/classes [get]
func (c *classBookingController) GetClassBookingDetail(ctx *gin.Context) {
	bookingID := ctx.Query("booking_id")
	if bookingID == "" {
		ctx.JSON(400, gin.H{"error": "booking_id is required"})
		return
	}

	res, err := c.classService.GetClassBookingDetail(ctx.Request.Context(), bookingID)
	ResponseJSON(ctx, "", res, err)
}

// GetClassBookingsByFilter godoc
// @Summary      Get Class Bookings By Filter
// @Description  Get a list of class bookings filtered by user ID
// @Tags         Class Booking
// @Accept       json
// @Produce      json
// @Param        user_id  query     string  true  "User ID"
// @Success      200      {array}   dto.CreateClassBookingResponse
// @Failure      400      {object}  dto.ErrorResponse
// @Security     BearerAuth
// @Router       /api/v1/bookings/classes/filters [get]
func (c *classBookingController) GetClassBookingsByFilter(ctx *gin.Context) {
	userID := ctx.Query("user_id")
	if userID == "" {
		ctx.JSON(400, gin.H{"error": "user_id is required"})
		return
	}

	res, err := c.classService.GetClassBookingsByFilter(ctx.Request.Context(), userID)
	ResponseJSON(ctx, "", res, err)
}

// CreateClassBooking godoc
// @Summary      Create a new Class Booking (Direct)
// @Description  Create a class booking with a specific payload (direct booking)
// @Tags         Class Booking
// @Accept       json
// @Produce      json
// @Param        request  body      dto.CreateClassBookingRequest  true  "Class Booking Data"
// @Success      201      {object}  dto.CreateClassBookingResponse
// @Failure      400      {object}  dto.ErrorResponse
// @Security     BearerAuth
// @Router       /api/v1/bookings/classes/create [post]
func (c *classBookingController) CreateClassBooking(ctx *gin.Context) {
	req, err := RequestJSON[dto.CreateClassBookingRequest](ctx)
	if err != nil {
		return
	}

	userID := ParseUserId(ctx)
	if userID == "" {
		return
	}

	res, err := c.classService.CreateClassBooking(ctx.Request.Context(), userID, req)
	if err != nil {
		ResponseJSON(ctx, req, res, err)
		return
	}

	ctx.JSON(201, res)
}

// UpdateClassBooking godoc
// @Summary      Partially Update a Class Booking
// @Description  Partially update a class booking. JSON fields (payment_details, available_slots, available_types) are deep-merged — only provided keys are overwritten.
// @Tags         Class Booking
// @Accept       json
// @Produce      json
// @Param        booking_id  query     string                       true   "Booking ID"
// @Param        request     body      dto.UpdateClassBookingRequest  true   "Fields to update"
// @Success      200         {object}  dto.UpdateClassBookingResponse
// @Failure      400         {object}  dto.ErrorResponse
// @Failure      404         {object}  dto.ErrorResponse
// @Security     BearerAuth
// @Router       /api/v1/bookings/classes/update [put]
func (c *classBookingController) UpdateClassBooking(ctx *gin.Context) {
	bookingID := ctx.Query("booking_id")
	if bookingID == "" {
		ctx.JSON(400, gin.H{"error": "booking_id is required"})
		return
	}

	req, err := RequestJSON[dto.UpdateClassBookingRequest](ctx)
	if err != nil {
		return
	}

	res, err := c.classService.UpdateClassBooking(ctx.Request.Context(), bookingID, req)
	if err != nil {
		ResponseJSON(ctx, req, res, err)
		return
	}

	ctx.JSON(200, res)
}

// DeleteClassBooking godoc
// @Summary      Delete a Class Booking
// @Description  Delete a class booking record by booking ID.
// @Tags         Class Booking
// @Accept       json
// @Produce      json
// @Param        booking_id  query     string  true  "Booking ID"
// @Success      200         {object}  map[string]interface{}
// @Failure      400         {object}  dto.ErrorResponse
// @Failure      404         {object}  dto.ErrorResponse
// @Security     BearerAuth
// @Router       /api/v1/bookings/classes/delete [post]
func (c *classBookingController) DeleteClassBooking(ctx *gin.Context) {
	bookingID := ctx.Query("booking_id")
	if bookingID == "" {
		ctx.JSON(400, gin.H{"error": "booking_id is required"})
		return
	}

	err := c.classService.DeleteClassBooking(ctx.Request.Context(), bookingID)
	if err != nil {
		ctx.JSON(400, gin.H{"success": false, "message": err.Error()})
		return
	}


	ctx.JSON(200, gin.H{"success": true, "message": "Booking deleted successfully"})
}

// ReduceCapacity godoc
// @Summary      Reduce Capacity
// @Description  Reduce available capacity of a specific ticket type in a slot for a class
// @Tags         Class Booking
// @Accept       json
// @Produce      json
// @Param        request  body      dto.ReduceCapacityRequest  true  "Reduce Capacity Data"
// @Success      200      {object}  dto.ClassBookingOptionsResponse
// @Failure      400      {object}  dto.ErrorResponse
// @Failure      500      {object}  dto.ErrorResponse
// @Security     BearerAuth
// @Router       /api/v1/bookings/classes/reduce-capacity [post]
func (c *classBookingController) ReduceCapacity(ctx *gin.Context) {
	req, err := RequestJSON[dto.ReduceCapacityRequest](ctx)
	if err != nil {
		return
	}

	res, err := c.classService.ReduceCapacity(ctx.Request.Context(), req)
	if err != nil {
		ctx.JSON(500, gin.H{"success": false, "message": err.Error()})
		return
	}

	ctx.JSON(200, res)
}