package controllers import ( "strconv" "abdanhafidz.com/go-boilerplate/models/dto" entity "abdanhafidz.com/go-boilerplate/models/entity" http_error "abdanhafidz.com/go-boilerplate/models/error" "abdanhafidz.com/go-boilerplate/services" "github.com/gin-gonic/gin" "github.com/google/uuid" ) type AdminEventController interface { ListEvents(ctx *gin.Context) CreateEvent(ctx *gin.Context) UpdateEvent(ctx *gin.Context) DeleteEvent(ctx *gin.Context) ListParticipants(ctx *gin.Context) ListCandidates(ctx *gin.Context) ListExamCandidates(ctx *gin.Context) AddParticipant(ctx *gin.Context) RemoveParticipant(ctx *gin.Context) ListEventExams(ctx *gin.Context) RemoveExam(ctx *gin.Context) ListResults(ctx *gin.Context) GetAttemptReview(ctx *gin.Context) UpdateResult(ctx *gin.Context) DeleteResult(ctx *gin.Context) } type adminEventController struct { adminEventService services.AdminEventService } func NewAdminEventController(adminEventService services.AdminEventService) AdminEventController { return &adminEventController{adminEventService: adminEventService} } // ListEvents godoc // @Summary Admin: List Events // @Description Admin view of all events with participant count, exam count and optional filters/pagination // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param limit query int false "Items per page" default(10) // @Param page query int false "Page number" default(1) // @Param search query string false "Search by title / slug / event code" // @Param sortBy query string false "Sort field (title, start_event, end_event, created_at, participant_count, exam_count)" // @Param order query string false "Sort direction (asc / desc)" // @Param status query string false "Filter by status (UPCOMING, ONGOING, ENDED)" // @Success 200 {object} dto.SuccessResponse[[]dto.AdminEventResponse] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events [get] func (c *adminEventController) ListEvents(ctx *gin.Context) { limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10")) page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) search := ctx.DefaultQuery("search", "") sortBy := ctx.DefaultQuery("sortBy", "") order := ctx.DefaultQuery("order", "") if limit < 1 { limit = 10 } else if limit > 100 { limit = 100 } if page < 1 { page = 1 } var status *string if val := ctx.Query("status"); val != "" { if val == entity.EventStatusUpcoming || val == entity.EventStatusOngoing || val == entity.EventStatusEnded { status = &val } } offset := (page - 1) * limit p := entity.Pagination{ Limit: limit, Offset: offset, Search: search, SortBy: sortBy, Order: order, Status: status, } list, total, err := c.adminEventService.ListEvents(ctx.Request.Context(), p) if err != nil { ResponseJSON[any, any](ctx, nil, nil, err) return } var totalPages int if total == 0 { totalPages = 1 } else { totalPages = int((total + int64(limit) - 1) / int64(limit)) } if page > totalPages { page = totalPages } meta := gin.H{ "totalItems": total, "totalPages": totalPages, "currentPage": page, "limit": limit, } ResponseJSON(ctx, meta, list, nil) } // GetAttemptReview godoc // @Summary Admin: Review Exam Attempt // @Description Review a specific participant attempt for an event exam // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param attempt_id path string true "Attempt ID" // @Param user_id path string true "User ID" // @Success 200 {object} dto.SuccessResponse[dto.AdminExamReviewResponse] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/attempts/{attempt_id}/{user_id}/review [get] func (c *adminEventController) GetAttemptReview(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } attemptId, err := uuid.Parse(ctx.Param("attempt_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"attempt_id": ctx.Param("attempt_id")}, nil, http_error.INVALID_TOKEN) return } userId, err := uuid.Parse(ctx.Param("user_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"user_id": ctx.Param("user_id")}, nil, http_error.INVALID_TOKEN) return } review, reviewErr := c.adminEventService.GetAttemptReview(ctx.Request.Context(), eventId, attemptId, userId) ResponseJSON(ctx, gin.H{"event_id": eventId, "attempt_id": attemptId, "user_id": userId}, review, reviewErr) } // CreateEvent godoc // @Summary Admin: Create Event // @Description Create a new event // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param request body dto.CreateEventRequest true "Create Event Request" // @Success 200 {object} dto.SuccessResponse[entity.Events] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events [post] func (c *adminEventController) CreateEvent(ctx *gin.Context) { req := RequestJSON[dto.CreateEventRequest](ctx) res, err := c.adminEventService.CreateEvent(ctx.Request.Context(), req) ResponseJSON(ctx, req, res, err) } // UpdateEvent godoc // @Summary Admin: Update Event // @Description Update an existing event by ID // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param request body dto.UpdateEventRequest true "Update Event Request" // @Success 200 {object} dto.SuccessResponse[entity.Events] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id} [put] func (c *adminEventController) UpdateEvent(ctx *gin.Context) { id, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } req := RequestJSON[dto.UpdateEventRequest](ctx) res, err := c.adminEventService.UpdateEvent(ctx.Request.Context(), id, req) ResponseJSON(ctx, req, res, err) } // DeleteEvent godoc // @Summary Admin: Delete Event // @Description Delete an event by ID // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Success 200 {object} dto.SuccessResponse[map[string]bool] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id} [delete] func (c *adminEventController) DeleteEvent(ctx *gin.Context) { id, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } delErr := c.adminEventService.DeleteEvent(ctx.Request.Context(), id) ResponseJSON(ctx, gin.H{"id": id}, gin.H{"deleted": delErr == nil}, delErr) } // ListParticipants godoc // @Summary Admin: List Event Participants // @Description List all participants assigned to an event // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param limit query int false "Items per page" default(10) // @Param page query int false "Page number" default(1) // @Param search query string false "Search by username / full name" // @Param sortBy query string false "Sort field (assigned_at, username, email, full_name)" // @Param order query string false "Sort direction (asc / desc)" // @Success 200 {object} dto.SuccessResponse[[]entity.EventAssign] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/participants [get] func (c *adminEventController) ListParticipants(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10")) page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) search := ctx.DefaultQuery("search", "") sortBy := ctx.DefaultQuery("sortBy", "") order := ctx.DefaultQuery("order", "") if limit < 1 { limit = 10 } else if limit > 100 { limit = 100 } if page < 1 { page = 1 } offset := (page - 1) * limit p := entity.Pagination{Limit: limit, Offset: offset, Search: search, SortBy: sortBy, Order: order} list, total, listErr := c.adminEventService.ListParticipants(ctx.Request.Context(), eventId, p) if listErr != nil { ResponseJSON[any, any](ctx, nil, nil, listErr) return } totalPages := int((total + int64(limit) - 1) / int64(limit)) if total == 0 { totalPages = 1 } if page > totalPages { page = totalPages } meta := gin.H{ "event_id": eventId, "totalItems": total, "totalPages": totalPages, "currentPage": page, "limit": limit, } ResponseJSON(ctx, meta, list, nil) } // ListCandidates godoc // @Summary Admin: List of Event Candidates // @Description List accounts that are not yet assigned to the event // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param limit query int false "Items per page" default(10) // @Param page query int false "Page number" default(1) // @Param search query string false "Search by username / full name" // @Param sortBy query string false "Sort field (username, full_name, created_at)" // @Param orderBy query string false "Sort direction (asc / desc)" // @Success 200 {object} dto.SuccessResponse[[]entity.Account] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/candidate [get] func (c *adminEventController) ListCandidates(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10")) page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) search := ctx.DefaultQuery("search", "") sortBy := ctx.DefaultQuery("sortBy", "") order := ctx.DefaultQuery("orderBy", "") if order == "" { order = ctx.DefaultQuery("order", "") } if limit < 1 { limit = 10 } else if limit > 100 { limit = 100 } if page < 1 { page = 1 } offset := (page - 1) * limit p := entity.Pagination{ Limit: limit, Offset: offset, Search: search, SortBy: sortBy, Order: order, } list, total, listErr := c.adminEventService.ListCandidates(ctx.Request.Context(), eventId, p) if listErr != nil { ResponseJSON[any, any](ctx, nil, nil, listErr) return } totalPages := int((total + int64(limit) - 1) / int64(limit)) if total == 0 { totalPages = 1 } if page > totalPages { page = totalPages } meta := gin.H{ "event_id": eventId, "totalItems": total, "totalPages": totalPages, "currentPage": page, "limit": limit, } ResponseJSON(ctx, meta, list, nil) } // ListExamCandidates godoc // @Summary Admin: List Exam Candidates for Event // @Description List exams that are not yet assigned to the event // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param limit query int false "Items per page" default(10) // @Param page query int false "Page number" default(1) // @Param search query string false "Search by exam title / slug" // @Param sortBy query string false "Sort field (title, slug, created_at, duration)" // @Param orderBy query string false "Sort direction (asc / desc)" // @Success 200 {object} dto.SuccessResponse[[]entity.Exam] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/exams/candidate [get] func (c *adminEventController) ListExamCandidates(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10")) page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) search := ctx.DefaultQuery("search", "") sortBy := ctx.DefaultQuery("sortBy", "") if sortBy == "" { sortBy = ctx.DefaultQuery("sortby", "") } order := ctx.DefaultQuery("orderBy", "") if order == "" { order = ctx.DefaultQuery("orderby", "") } if order == "" { order = ctx.DefaultQuery("order", "") } if limit < 1 { limit = 10 } else if limit > 100 { limit = 100 } if page < 1 { page = 1 } offset := (page - 1) * limit p := entity.Pagination{ Limit: limit, Offset: offset, Search: search, SortBy: sortBy, Order: order, } list, total, listErr := c.adminEventService.ListExamCandidates(ctx.Request.Context(), eventId, p) if listErr != nil { ResponseJSON[any, any](ctx, nil, nil, listErr) return } totalPages := int((total + int64(limit) - 1) / int64(limit)) if total == 0 { totalPages = 1 } if page > totalPages { page = totalPages } meta := gin.H{ "event_id": eventId, "totalItems": total, "totalPages": totalPages, "currentPage": page, "limit": limit, } ResponseJSON(ctx, meta, list, nil) } // AddParticipant godoc // @Summary Admin: Add Participant to Event // @Description Manually assign a user to an event (bypasses payment) // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param request body dto.AddParticipantRequest true "Add Participant Request" // @Success 200 {object} dto.SuccessResponse[entity.EventAssign] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/participants [post] func (c *adminEventController) AddParticipant(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } req := RequestJSON[dto.AddParticipantRequest](ctx) userId, parseErr := uuid.Parse(req.UserId) if parseErr != nil { ResponseJSON[any](ctx, req, nil, http_error.INVALID_TOKEN) return } assign, err := c.adminEventService.AddParticipant(ctx.Request.Context(), eventId, userId) ResponseJSON(ctx, req, assign, err) } // RemoveParticipant godoc // @Summary Admin: Remove Participant from Event // @Description Unassign a user from an event // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param user_id path string true "User ID" // @Success 200 {object} dto.SuccessResponse[map[string]bool] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/participants/{user_id} [delete] func (c *adminEventController) RemoveParticipant(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } userId, err := uuid.Parse(ctx.Param("user_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"user_id": ctx.Param("user_id")}, nil, http_error.INVALID_TOKEN) return } delErr := c.adminEventService.RemoveParticipant(ctx.Request.Context(), eventId, userId) ResponseJSON(ctx, gin.H{"event_id": eventId, "user_id": userId}, gin.H{"removed": delErr == nil}, delErr) } // ListEventExams godoc // @Summary Admin: List Event Exams // @Description List all exams assigned to an event // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param limit query int false "Items per page" default(10) // @Param page query int false "Page number" default(1) // @Param search query string false "Search by exam title / slug" // @Param sortBy query string false "Sort field (title, slug, created_at, duration)" // @Param order query string false "Sort direction (asc / desc)" // @Success 200 {object} dto.SuccessResponse[[]entity.EventExamAssign] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/exams [get] func (c *adminEventController) ListEventExams(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10")) page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) search := ctx.DefaultQuery("search", "") sortBy := ctx.DefaultQuery("sortBy", "") order := ctx.DefaultQuery("order", "") if limit < 1 { limit = 10 } else if limit > 100 { limit = 100 } if page < 1 { page = 1 } offset := (page - 1) * limit p := entity.Pagination{Limit: limit, Offset: offset, Search: search, SortBy: sortBy, Order: order} list, total, listErr := c.adminEventService.ListEventExams(ctx.Request.Context(), eventId, p) if listErr != nil { ResponseJSON[any, any](ctx, nil, nil, listErr) return } totalPages := int((total + int64(limit) - 1) / int64(limit)) if total == 0 { totalPages = 1 } if page > totalPages { page = totalPages } meta := gin.H{ "event_id": eventId, "totalItems": total, "totalPages": totalPages, "currentPage": page, "limit": limit, } ResponseJSON(ctx, meta, list, nil) } // RemoveExam godoc // @Summary Admin: Remove Exam from Event // @Description Unassign an exam from an event // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param exam_id path string true "Exam ID" // @Success 200 {object} dto.SuccessResponse[map[string]bool] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/exams/{exam_id} [delete] func (c *adminEventController) RemoveExam(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } examId, err := uuid.Parse(ctx.Param("exam_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"exam_id": ctx.Param("exam_id")}, nil, http_error.INVALID_TOKEN) return } delErr := c.adminEventService.RemoveExam(ctx.Request.Context(), eventId, examId) ResponseJSON(ctx, gin.H{"event_id": eventId, "exam_id": examId}, gin.H{"removed": delErr == nil}, delErr) } // ListResults godoc // @Summary Admin: List Exam Results for an Event // @Description Retrieve all participant results for a specific exam within an event // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param exam_id path string true "Exam ID" // @Param limit query int false "Items per page" default(10) // @Param page query int false "Page number" default(1) // @Param search query string false "Search by username / full name" // @Param sortBy query string false "Sort field (final_score, created_at, username, full_name)" // @Param order query string false "Sort direction (asc / desc)" // @Success 200 {object} dto.SuccessResponse[[]entity.Result] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/exams/{exam_id}/results [get] func (c *adminEventController) ListResults(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } examId, err := uuid.Parse(ctx.Param("exam_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"exam_id": ctx.Param("exam_id")}, nil, http_error.INVALID_TOKEN) return } limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10")) page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1")) search := ctx.DefaultQuery("search", "") sortBy := ctx.DefaultQuery("sortBy", "") if sortBy == "" { sortBy = ctx.DefaultQuery("sortby", "") } order := ctx.DefaultQuery("order", "") if order == "" { order = ctx.DefaultQuery("orderBy", "") } if order == "" { order = ctx.DefaultQuery("orderby", "") } if limit < 1 { limit = 10 } else if limit > 100 { limit = 100 } if page < 1 { page = 1 } offset := (page - 1) * limit p := entity.Pagination{Limit: limit, Offset: offset, Search: search, SortBy: sortBy, Order: order} list, total, listErr := c.adminEventService.ListResults(ctx.Request.Context(), eventId, examId, p) if listErr != nil { ResponseJSON[any, any](ctx, nil, nil, listErr) return } totalPages := int((total + int64(limit) - 1) / int64(limit)) if total == 0 { totalPages = 1 } if page > totalPages { page = totalPages } meta := gin.H{ "event_id": eventId, "exam_id": examId, "totalItems": total, "totalPages": totalPages, "currentPage": page, "limit": limit, } ResponseJSON(ctx, meta, list, nil) } // UpdateResult godoc // @Summary Admin: Update Exam Result // @Description Edit the final score of a participant's exam result // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param exam_id path string true "Exam ID" // @Param result_id path string true "Result ID" // @Param request body dto.UpdateResultRequest true "Update Result Request" // @Success 200 {object} dto.SuccessResponse[entity.Result] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/exams/{exam_id}/results/{result_id} [put] func (c *adminEventController) UpdateResult(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } examId, err := uuid.Parse(ctx.Param("exam_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"exam_id": ctx.Param("exam_id")}, nil, http_error.INVALID_TOKEN) return } resultId, err := uuid.Parse(ctx.Param("result_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"result_id": ctx.Param("result_id")}, nil, http_error.INVALID_TOKEN) return } req := RequestJSON[dto.UpdateResultRequest](ctx) res, err := c.adminEventService.UpdateResult(ctx.Request.Context(), eventId, examId, resultId, req) ResponseJSON(ctx, req, res, err) } // DeleteResult godoc // @Summary Admin: Delete Exam Result // @Description Delete a participant's exam result // @Tags AdminEvent // @Accept json // @Produce json // @Security BearerAuth // @Param event_id path string true "Event ID" // @Param exam_id path string true "Exam ID" // @Param result_id path string true "Result ID" // @Success 200 {object} dto.SuccessResponse[map[string]bool] // @Failure 400 {object} dto.ErrorResponse // @Failure 401 {object} dto.ErrorResponse // @Failure 403 {object} dto.ErrorResponse // @Router /api/v1/admin/events/{event_id}/exams/{exam_id}/results/{result_id} [delete] func (c *adminEventController) DeleteResult(ctx *gin.Context) { eventId, err := uuid.Parse(ctx.Param("event_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"event_id": ctx.Param("event_id")}, nil, http_error.INVALID_TOKEN) return } examId, err := uuid.Parse(ctx.Param("exam_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"exam_id": ctx.Param("exam_id")}, nil, http_error.INVALID_TOKEN) return } resultId, err := uuid.Parse(ctx.Param("result_id")) if err != nil { ResponseJSON[any](ctx, gin.H{"result_id": ctx.Param("result_id")}, nil, http_error.INVALID_TOKEN) return } delErr := c.adminEventService.DeleteResult(ctx.Request.Context(), eventId, examId, resultId) ResponseJSON(ctx, gin.H{"result_id": resultId}, gin.H{"deleted": delErr == nil}, delErr) }