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) }