package handlers import ( "Yimaru-Backend/internal/domain" jwtutil "Yimaru-Backend/internal/web_server/jwt" "errors" "fmt" "strconv" "time" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) // teamMemberLoginRes represents the response body for team member login type teamMemberLoginRes struct { AccessToken string `json:"access_token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."` RefreshToken string `json:"refresh_token" example:""` MemberID int64 `json:"member_id" example:"1"` TeamRole string `json:"team_role" example:"admin"` } // changePasswordReq represents the request body for changing password type changePasswordReq struct { CurrentPassword string `json:"current_password" validate:"required" example:"oldpassword123"` NewPassword string `json:"new_password" validate:"required,min=8" example:"newpassword123"` } func mapTeamRoleToRole(teamRole domain.TeamRole) domain.Role { switch teamRole { case domain.TeamRoleSuperAdmin: return domain.RoleSuperAdmin case domain.TeamRoleAdmin: return domain.RoleAdmin case domain.TeamRoleInstructor: return domain.RoleInstructor case domain.TeamRoleSupportAgent: return domain.RoleSupport default: return domain.RoleAdmin } } // TeamMemberLogin godoc // @Summary Login team member // @Description Authenticate a team member (internal staff) with email and password // @Tags team // @Accept json // @Produce json // @Param body body domain.TeamMemberLoginReq true "Team member login credentials" // @Success 200 {object} domain.Response{data=teamMemberLoginRes} // @Failure 400 {object} domain.ErrorResponse // @Failure 401 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/team/login [post] func (h *Handler) TeamMemberLogin(c *fiber.Ctx) error { var req domain.TeamMemberLoginReq if err := c.BodyParser(&req); err != nil { h.mongoLoggerSvc.Info("Failed to parse TeamMemberLogin request", zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to login", Error: "Invalid request body: " + err.Error(), }) } if valErrs, ok := h.validator.Validate(c, req); !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to login", Error: errMsg, }) } loginRes, err := h.teamSvc.Login(c.Context(), req) if err != nil { switch { case errors.Is(err, domain.ErrTeamMemberNotFound): h.mongoLoggerSvc.Info("Team member login failed: member not found", zap.Int("status_code", fiber.StatusUnauthorized), zap.String("email", req.Email), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ Message: "Failed to login", Error: "Invalid credentials", }) case errors.Is(err, domain.ErrInvalidTeamMemberStatus): h.mongoLoggerSvc.Info("Team member login failed: account suspended or inactive", zap.Int("status_code", fiber.StatusUnauthorized), zap.String("email", req.Email), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ Message: "Failed to login", Error: "Account is not active", }) default: h.mongoLoggerSvc.Error("Team member login failed", zap.Int("status_code", fiber.StatusInternalServerError), zap.String("email", req.Email), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to login", Error: err.Error(), }) } } role := mapTeamRoleToRole(loginRes.TeamRole) accessToken, err := jwtutil.CreateJwt( loginRes.ID, role, h.jwtConfig.JwtAccessKey, h.jwtConfig.JwtAccessExpiry, ) if err != nil { h.mongoLoggerSvc.Error("Failed to create access token for team member", zap.Int("status_code", fiber.StatusInternalServerError), zap.Int64("member_id", loginRes.ID), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to login", Error: "Failed to generate access token", }) } h.mongoLoggerSvc.Info("Team member login successful", zap.Int("status_code", fiber.StatusOK), zap.Int64("member_id", loginRes.ID), zap.String("team_role", string(loginRes.TeamRole)), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Login successful", Data: teamMemberLoginRes{ AccessToken: accessToken, RefreshToken: "", MemberID: loginRes.ID, TeamRole: string(loginRes.TeamRole), }, Success: true, StatusCode: fiber.StatusOK, }) } // CreateTeamMember godoc // @Summary Create a new team member // @Description Create a new internal team member (admin only) // @Tags team // @Accept json // @Produce json // @Security Bearer // @Param body body domain.CreateTeamMemberReq true "Team member creation payload" // @Success 201 {object} domain.Response{data=domain.TeamMemberResponse} // @Failure 400 {object} domain.ErrorResponse // @Failure 401 {object} domain.ErrorResponse // @Failure 403 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/team/members [post] func (h *Handler) CreateTeamMember(c *fiber.Ctx) error { var req domain.CreateTeamMemberReq if err := c.BodyParser(&req); err != nil { h.mongoLoggerSvc.Info("Failed to parse CreateTeamMember request", zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to create team member", Error: "Invalid request body: " + err.Error(), }) } if valErrs, ok := h.validator.Validate(c, req); !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to create team member", Error: errMsg, }) } creatorID, ok := c.Locals("user_id").(int64) if !ok { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to create team member", Error: "User ID not found in request context", }) } member, err := h.teamSvc.CreateTeamMember(c.Context(), req, &creatorID) if err != nil { switch { case errors.Is(err, domain.ErrTeamMemberEmailExists): return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to create team member", Error: "Email already exists", }) case errors.Is(err, domain.ErrInvalidTeamRole): return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to create team member", Error: "Invalid team role", }) default: h.mongoLoggerSvc.Error("Failed to create team member", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to create team member", Error: err.Error(), }) } } h.mongoLoggerSvc.Info("Team member created successfully", zap.Int("status_code", fiber.StatusCreated), zap.Int64("member_id", member.ID), zap.Int64("created_by", creatorID), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusCreated).JSON(domain.Response{ Message: "Team member created successfully", Data: toTeamMemberResponse(&member), Success: true, StatusCode: fiber.StatusCreated, }) } // GetTeamMember godoc // @Summary Get team member by ID // @Description Retrieve a team member's details by their ID // @Tags team // @Accept json // @Produce json // @Security Bearer // @Param id path int true "Team Member ID" // @Success 200 {object} domain.Response{data=domain.TeamMemberResponse} // @Failure 400 {object} domain.ErrorResponse // @Failure 401 {object} domain.ErrorResponse // @Failure 404 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/team/members/{id} [get] func (h *Handler) GetTeamMember(c *fiber.Ctx) error { memberIDStr := c.Params("id") memberID, err := strconv.ParseInt(memberIDStr, 10, 64) if err != nil || memberID <= 0 { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid member ID", Error: "Member ID must be a valid positive integer", }) } member, err := h.teamSvc.GetTeamMemberByID(c.Context(), memberID) if err != nil { if errors.Is(err, domain.ErrTeamMemberNotFound) { return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Team member not found", Error: err.Error(), }) } h.mongoLoggerSvc.Error("Failed to get team member", zap.Int64("member_id", memberID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to get team member", Error: err.Error(), }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Team member retrieved successfully", Data: toTeamMemberResponse(&member), Success: true, StatusCode: fiber.StatusOK, }) } // GetAllTeamMembers godoc // @Summary List all team members // @Description Get a paginated list of team members with optional filtering // @Tags team // @Accept json // @Produce json // @Security Bearer // @Param team_role query string false "Filter by team role (super_admin, admin, content_manager, support_agent, instructor, finance, hr, analyst)" // @Param department query string false "Filter by department" // @Param status query string false "Filter by status (active, inactive, suspended, terminated)" // @Param search query string false "Search by name, email, or phone number" // @Param page query int false "Page number (default: 1)" // @Param page_size query int false "Items per page (default: 10, max: 100)" // @Success 200 {object} domain.Response{data=[]domain.TeamMemberResponse,metadata=domain.Pagination} // @Failure 401 {object} domain.ErrorResponse // @Failure 403 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/team/members [get] func (h *Handler) GetAllTeamMembers(c *fiber.Ctx) error { filter := domain.TeamMemberFilter{} if teamRole := c.Query("team_role"); teamRole != "" { filter.TeamRole = &teamRole } if department := c.Query("department"); department != "" { filter.Department = &department } if status := c.Query("status"); status != "" { filter.Status = &status } filter.Search = c.Query("search") page, _ := strconv.ParseInt(c.Query("page", "1"), 10, 64) if page < 1 { page = 1 } filter.Page = page pageSize, _ := strconv.ParseInt(c.Query("page_size", "10"), 10, 64) if pageSize < 1 || pageSize > 100 { pageSize = 10 } filter.PageSize = pageSize var members []domain.TeamMember var total int64 var err error if filter.Search != "" { members, err = h.teamSvc.SearchTeamMembers(c.Context(), filter.Search, filter.TeamRole, filter.Status) if err == nil { total = int64(len(members)) } } else { members, total, err = h.teamSvc.GetAllTeamMembers(c.Context(), filter) } if err != nil { h.mongoLoggerSvc.Error("Failed to get team members", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to get team members", Error: err.Error(), }) } memberResponses := make([]domain.TeamMemberResponse, 0, len(members)) for i := range members { memberResponses = append(memberResponses, toTeamMemberResponse(&members[i])) } totalPages := int((total + filter.PageSize - 1) / filter.PageSize) return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Team members retrieved successfully", Data: memberResponses, Success: true, MetaData: domain.Pagination{ Total: int(total), TotalPages: totalPages, CurrentPage: int(filter.Page), Limit: int(filter.PageSize), }, StatusCode: fiber.StatusOK, }) } // UpdateTeamMember godoc // @Summary Update team member // @Description Update an existing team member's details (admin only) // @Tags team // @Accept json // @Produce json // @Security Bearer // @Param id path int true "Team Member ID" // @Param body body domain.UpdateTeamMemberReq true "Team member update payload" // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 401 {object} domain.ErrorResponse // @Failure 403 {object} domain.ErrorResponse // @Failure 404 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/team/members/{id} [put] func (h *Handler) UpdateTeamMember(c *fiber.Ctx) error { memberIDStr := c.Params("id") memberID, err := strconv.ParseInt(memberIDStr, 10, 64) if err != nil || memberID <= 0 { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid member ID", Error: "Member ID must be a valid positive integer", }) } var req domain.UpdateTeamMemberReq if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to update team member", Error: "Invalid request body: " + err.Error(), }) } updaterID, ok := c.Locals("user_id").(int64) if !ok { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to update team member", Error: "User ID not found in request context", }) } req.TeamMemberID = memberID req.UpdatedBy = updaterID if err := h.teamSvc.UpdateTeamMember(c.Context(), req); err != nil { switch { case errors.Is(err, domain.ErrTeamMemberNotFound): return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Team member not found", Error: err.Error(), }) case errors.Is(err, domain.ErrInvalidTeamRole): return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to update team member", Error: "Invalid team role", }) default: h.mongoLoggerSvc.Error("Failed to update team member", zap.Int64("member_id", memberID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to update team member", Error: err.Error(), }) } } h.mongoLoggerSvc.Info("Team member updated successfully", zap.Int64("member_id", memberID), zap.Int64("updated_by", updaterID), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Team member updated successfully", Success: true, StatusCode: fiber.StatusOK, }) } // UpdateTeamMemberStatus godoc // @Summary Update team member status // @Description Update a team member's status (active, inactive, suspended, terminated) // @Tags team // @Accept json // @Produce json // @Security Bearer // @Param id path int true "Team Member ID" // @Param body body domain.UpdateTeamMemberStatusReq true "Status update payload" // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 401 {object} domain.ErrorResponse // @Failure 403 {object} domain.ErrorResponse // @Failure 404 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/team/members/{id}/status [patch] func (h *Handler) UpdateTeamMemberStatus(c *fiber.Ctx) error { memberIDStr := c.Params("id") memberID, err := strconv.ParseInt(memberIDStr, 10, 64) if err != nil || memberID <= 0 { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid member ID", Error: "Member ID must be a valid positive integer", }) } var req domain.UpdateTeamMemberStatusReq if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to update team member status", Error: "Invalid request body: " + err.Error(), }) } if valErrs, ok := h.validator.Validate(c, req); !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to update team member status", Error: errMsg, }) } updaterID, ok := c.Locals("user_id").(int64) if !ok { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to update team member status", Error: "User ID not found in request context", }) } req.TeamMemberID = memberID req.UpdatedBy = updaterID if err := h.teamSvc.UpdateTeamMemberStatus(c.Context(), req); err != nil { switch { case errors.Is(err, domain.ErrTeamMemberNotFound): return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Team member not found", Error: err.Error(), }) case errors.Is(err, domain.ErrInvalidTeamMemberStatus): return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to update team member status", Error: "Invalid status value", }) default: h.mongoLoggerSvc.Error("Failed to update team member status", zap.Int64("member_id", memberID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to update team member status", Error: err.Error(), }) } } h.mongoLoggerSvc.Info("Team member status updated successfully", zap.Int64("member_id", memberID), zap.String("new_status", req.Status), zap.Int64("updated_by", updaterID), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Team member status updated successfully", Success: true, StatusCode: fiber.StatusOK, }) } // DeleteTeamMember godoc // @Summary Delete team member // @Description Delete a team member (super admin only) // @Tags team // @Accept json // @Produce json // @Security Bearer // @Param id path int true "Team Member ID" // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 401 {object} domain.ErrorResponse // @Failure 403 {object} domain.ErrorResponse // @Failure 404 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/team/members/{id} [delete] func (h *Handler) DeleteTeamMember(c *fiber.Ctx) error { memberIDStr := c.Params("id") memberID, err := strconv.ParseInt(memberIDStr, 10, 64) if err != nil || memberID <= 0 { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid member ID", Error: "Member ID must be a valid positive integer", }) } if err := h.teamSvc.DeleteTeamMember(c.Context(), memberID); err != nil { if errors.Is(err, domain.ErrTeamMemberNotFound) { return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Team member not found", Error: err.Error(), }) } h.mongoLoggerSvc.Error("Failed to delete team member", zap.Int64("member_id", memberID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to delete team member", Error: err.Error(), }) } h.mongoLoggerSvc.Info("Team member deleted successfully", zap.Int64("member_id", memberID), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Team member deleted successfully", Success: true, StatusCode: fiber.StatusOK, }) } // GetTeamMemberStats godoc // @Summary Get team member statistics // @Description Get statistics about team members by status // @Tags team // @Accept json // @Produce json // @Security Bearer // @Success 200 {object} domain.Response{data=domain.TeamMemberStats} // @Failure 401 {object} domain.ErrorResponse // @Failure 403 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/team/stats [get] func (h *Handler) GetTeamMemberStats(c *fiber.Ctx) error { stats, err := h.teamSvc.GetTeamMemberStats(c.Context()) if err != nil { h.mongoLoggerSvc.Error("Failed to get team member stats", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to get team member stats", Error: err.Error(), }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Team member stats retrieved successfully", Data: stats, Success: true, StatusCode: fiber.StatusOK, }) } // ChangeTeamMemberPassword godoc // @Summary Change team member password // @Description Change a team member's password (requires current password) // @Tags team // @Accept json // @Produce json // @Security Bearer // @Param id path int true "Team Member ID" // @Param body body changePasswordReq true "Password change payload" // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 401 {object} domain.ErrorResponse // @Failure 404 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/team/members/{id}/change-password [post] func (h *Handler) ChangeTeamMemberPassword(c *fiber.Ctx) error { memberIDStr := c.Params("id") memberID, err := strconv.ParseInt(memberIDStr, 10, 64) if err != nil || memberID <= 0 { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid member ID", Error: "Member ID must be a valid positive integer", }) } var req changePasswordReq if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to change password", Error: "Invalid request body: " + err.Error(), }) } if valErrs, ok := h.validator.Validate(c, req); !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to change password", Error: errMsg, }) } if err := h.teamSvc.ChangePassword(c.Context(), memberID, req.CurrentPassword, req.NewPassword); err != nil { switch { case errors.Is(err, domain.ErrTeamMemberNotFound): return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Team member not found", Error: err.Error(), }) default: h.mongoLoggerSvc.Error("Failed to change team member password", zap.Int64("member_id", memberID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to change password", Error: err.Error(), }) } } h.mongoLoggerSvc.Info("Team member password changed successfully", zap.Int64("member_id", memberID), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Password changed successfully", Success: true, StatusCode: fiber.StatusOK, }) } // GetMyTeamProfile godoc // @Summary Get my team profile // @Description Get the authenticated team member's own profile // @Tags team // @Accept json // @Produce json // @Security Bearer // @Success 200 {object} domain.Response{data=domain.TeamMemberResponse} // @Failure 400 {object} domain.ErrorResponse // @Failure 401 {object} domain.ErrorResponse // @Failure 404 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/team/me [get] func (h *Handler) GetMyTeamProfile(c *fiber.Ctx) error { memberID, ok := c.Locals("user_id").(int64) if !ok { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid user context", Error: "User ID not found in request context", }) } member, err := h.teamSvc.GetTeamMemberByID(c.Context(), memberID) if err != nil { if errors.Is(err, domain.ErrTeamMemberNotFound) { return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{ Message: "Team member not found", Error: err.Error(), }) } h.mongoLoggerSvc.Error("Failed to get team member profile", zap.Int64("member_id", memberID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to get profile", Error: err.Error(), }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Profile retrieved successfully", Data: toTeamMemberResponse(&member), Success: true, StatusCode: fiber.StatusOK, }) } func toTeamMemberResponse(m *domain.TeamMember) domain.TeamMemberResponse { resp := domain.TeamMemberResponse{ ID: m.ID, FirstName: m.FirstName, LastName: m.LastName, Email: m.Email, PhoneNumber: m.PhoneNumber, TeamRole: m.TeamRole, Department: m.Department, JobTitle: m.JobTitle, EmploymentType: m.EmploymentType, ProfilePictureURL: m.ProfilePictureURL, Bio: m.Bio, WorkPhone: m.WorkPhone, Status: m.Status, EmailVerified: m.EmailVerified, Permissions: m.Permissions, LastLogin: m.LastLogin, CreatedAt: m.CreatedAt, UpdatedAt: m.UpdatedAt, } if m.HireDate != nil { resp.HireDate = m.HireDate.Format("2006-01-02") } return resp }