From 104cd987e9cfcbccc38460d84f5c39040d8d17b0 Mon Sep 17 00:00:00 2001 From: Aditya Siregar Date: Sat, 16 Aug 2025 01:51:37 +0700 Subject: [PATCH] Update UI result --- deployment.sh | 8 ++-- internal/contract/vote_event_contract.go | 48 ++++++++++++++------ internal/handler/vote_event_handler.go | 21 +++++++++ internal/repository/vote_event_repository.go | 18 ++++++++ internal/router/health_handler.go | 1 + internal/router/router.go | 1 + internal/service/vote_event_service.go | 28 ++++++++++++ 7 files changed, 109 insertions(+), 16 deletions(-) diff --git a/deployment.sh b/deployment.sh index d3a4037..3a70518 100644 --- a/deployment.sh +++ b/deployment.sh @@ -1,7 +1,7 @@ #!/bin/bash -APP_NAME="meti-frontend" -PORT="3000" +APP_NAME="meti-backend" +PORT="4000" echo "🔄 Pulling latest code..." git pull @@ -15,7 +15,9 @@ docker rm $APP_NAME 2>/dev/null echo "🚀 Running new container..." docker run -d --name $APP_NAME \ - -p 4002:$PORT \ + -p 4001:$PORT \ + -v "$(pwd)/infra":/infra:ro \ + -v "$(pwd)/templates":/templates:ro \ $APP_NAME:latest echo "✅ Deployment complete." diff --git a/internal/contract/vote_event_contract.go b/internal/contract/vote_event_contract.go index b5f3f47..dee106b 100644 --- a/internal/contract/vote_event_contract.go +++ b/internal/contract/vote_event_contract.go @@ -24,17 +24,17 @@ type UpdateVoteEventRequest struct { } type VoteEventResponse struct { - ID uuid.UUID `json:"id"` - Title string `json:"title"` - Description string `json:"description"` - StartDate time.Time `json:"start_date"` - EndDate time.Time `json:"end_date"` - IsActive bool `json:"is_active"` - ResultsOpen bool `json:"results_open"` - IsVotingOpen bool `json:"is_voting_open"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Candidates []CandidateResponse `json:"candidates,omitempty"` + ID uuid.UUID `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + StartDate time.Time `json:"start_date"` + EndDate time.Time `json:"end_date"` + IsActive bool `json:"is_active"` + ResultsOpen bool `json:"results_open"` + IsVotingOpen bool `json:"is_voting_open"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Candidates []CandidateResponse `json:"candidates,omitempty"` } type ListVoteEventsRequest struct { @@ -90,7 +90,29 @@ type CandidateWithVotesResponse struct { } type CheckVoteStatusResponse struct { - HasVoted bool `json:"has_voted"` + HasVoted bool `json:"has_voted"` VotedAt *time.Time `json:"voted_at,omitempty"` CandidateID *uuid.UUID `json:"candidate_id,omitempty"` -} \ No newline at end of file +} + +type VoteEventDetailsResponse struct { + VoteEvent VoteEventResponse `json:"vote_event"` + TotalParticipants int64 `json:"total_participants"` + TotalVoted int64 `json:"total_voted"` + TotalNotVoted int64 `json:"total_not_voted"` +} + +type UserVoteInfo struct { + UserID uuid.UUID `json:"user_id"` + Username string `json:"username"` + FullName string `json:"full_name"` + VotedAt time.Time `json:"voted_at"` + CandidateID uuid.UUID `json:"candidate_id"` + CandidateName string `json:"candidate_name"` +} + +type UserBasicInfo struct { + UserID uuid.UUID `json:"user_id"` + Username string `json:"username"` + FullName string `json:"full_name"` +} diff --git a/internal/handler/vote_event_handler.go b/internal/handler/vote_event_handler.go index 671b404..bd777d2 100644 --- a/internal/handler/vote_event_handler.go +++ b/internal/handler/vote_event_handler.go @@ -26,6 +26,7 @@ type VoteEventService interface { SubmitVote(ctx context.Context, userID uuid.UUID, req *contract.SubmitVoteRequest) (*contract.VoteResponse, error) GetVoteResults(ctx context.Context, eventID uuid.UUID) (*contract.VoteResultsResponse, error) CheckVoteStatus(ctx context.Context, userID, eventID uuid.UUID) (*contract.CheckVoteStatusResponse, error) + GetVoteEventDetails(ctx context.Context, eventID uuid.UUID) (*contract.VoteEventDetailsResponse, error) } type VoteEventHandler struct { @@ -305,6 +306,26 @@ func (h *VoteEventHandler) sendErrorResponse(c *gin.Context, message string, sta c.JSON(statusCode, errorResponse) } +func (h *VoteEventHandler) GetVoteEventDetails(c *gin.Context) { + eventIDStr := c.Param("id") + eventID, err := uuid.Parse(eventIDStr) + if err != nil { + logger.FromContext(c).WithError(err).Error("VoteEventHandler::GetVoteEventDetails -> Invalid event ID") + h.sendValidationErrorResponse(c, "Invalid event ID", constants.MalformedFieldErrorCode) + return + } + + details, err := h.voteEventService.GetVoteEventDetails(c.Request.Context(), eventID) + if err != nil { + logger.FromContext(c).WithError(err).Error("VoteEventHandler::GetVoteEventDetails -> Failed to get vote event details") + h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError) + return + } + + logger.FromContext(c).Infof("VoteEventHandler::GetVoteEventDetails -> Successfully retrieved vote event details") + c.JSON(http.StatusOK, contract.BuildSuccessResponse(details)) +} + func (h *VoteEventHandler) sendValidationErrorResponse(c *gin.Context, message string, errorCode string) { statusCode := constants.HttpErrorMap[errorCode] if statusCode == 0 { diff --git a/internal/repository/vote_event_repository.go b/internal/repository/vote_event_repository.go index f4fd7f0..a58b80e 100644 --- a/internal/repository/vote_event_repository.go +++ b/internal/repository/vote_event_repository.go @@ -119,4 +119,22 @@ func (r *VoteEventRepositoryImpl) GetVoteResults(ctx context.Context, eventID uu } return resultMap, nil +} + +func (r *VoteEventRepositoryImpl) GetVotedCount(ctx context.Context, eventID uuid.UUID) (int64, error) { + var count int64 + err := r.db.WithContext(ctx). + Model(&entities.Vote{}). + Where("vote_event_id = ?", eventID). + Count(&count).Error + return count, err +} + +func (r *VoteEventRepositoryImpl) GetTotalActiveUsersCount(ctx context.Context) (int64, error) { + var count int64 + err := r.db.WithContext(ctx). + Model(&entities.User{}). + Where("is_active = ?", true). + Count(&count).Error + return count, err } \ No newline at end of file diff --git a/internal/router/health_handler.go b/internal/router/health_handler.go index b0d00cc..e47c22e 100644 --- a/internal/router/health_handler.go +++ b/internal/router/health_handler.go @@ -93,4 +93,5 @@ type VoteEventHandler interface { GetVoteResults(c *gin.Context) CheckVoteStatus(c *gin.Context) GetCandidates(c *gin.Context) + GetVoteEventDetails(c *gin.Context) } diff --git a/internal/router/router.go b/internal/router/router.go index d730ca7..abf96d5 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -174,6 +174,7 @@ func (r *Router) addAppRoutes(rg *gin.Engine) { voteEvents.GET("/:id/candidates", r.voteEventHandler.GetCandidates) voteEvents.GET("/:id/results", r.voteEventHandler.GetVoteResults) voteEvents.GET("/:id/vote-status", r.voteEventHandler.CheckVoteStatus) + voteEvents.GET("/:id/details", r.voteEventHandler.GetVoteEventDetails) } candidates := v1.Group("/candidates") diff --git a/internal/service/vote_event_service.go b/internal/service/vote_event_service.go index 53b30e5..3485f44 100644 --- a/internal/service/vote_event_service.go +++ b/internal/service/vote_event_service.go @@ -23,6 +23,8 @@ type VoteEventRepository interface { SubmitVote(ctx context.Context, vote *entities.Vote) error HasUserVoted(ctx context.Context, userID, eventID uuid.UUID) (bool, error) GetVoteResults(ctx context.Context, eventID uuid.UUID) (map[uuid.UUID]int64, error) + GetVotedCount(ctx context.Context, eventID uuid.UUID) (int64, error) + GetTotalActiveUsersCount(ctx context.Context) (int64, error) } type VoteEventServiceImpl struct { @@ -256,3 +258,29 @@ func (s *VoteEventServiceImpl) CheckVoteStatus(ctx context.Context, userID, even return response, nil } + +func (s *VoteEventServiceImpl) GetVoteEventDetails(ctx context.Context, eventID uuid.UUID) (*contract.VoteEventDetailsResponse, error) { + voteEvent, err := s.voteEventRepo.GetByID(ctx, eventID) + if err != nil { + return nil, err + } + + totalParticipants, err := s.voteEventRepo.GetTotalActiveUsersCount(ctx) + if err != nil { + return nil, err + } + + totalVoted, err := s.voteEventRepo.GetVotedCount(ctx, eventID) + if err != nil { + return nil, err + } + + totalNotVoted := totalParticipants - totalVoted + + return &contract.VoteEventDetailsResponse{ + VoteEvent: *transformer.VoteEventToContract(voteEvent), + TotalParticipants: totalParticipants, + TotalVoted: totalVoted, + TotalNotVoted: totalNotVoted, + }, nil +}