package repository import ( "context" "encoding/json" "errors" "time" dbgen "Yimaru-Backend/gen/db" "Yimaru-Backend/internal/domain" "Yimaru-Backend/internal/ports" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" ) func NewTeamStore(s *Store) ports.TeamStore { return s } func (s *Store) CreateTeamMember(ctx context.Context, member domain.TeamMember) (domain.TeamMember, error) { var permissionsJSON []byte if len(member.Permissions) > 0 { var err error permissionsJSON, err = json.Marshal(member.Permissions) if err != nil { return domain.TeamMember{}, err } } var hireDate pgtype.Date if member.HireDate != nil { hireDate = pgtype.Date{Time: *member.HireDate, Valid: true} } var createdBy pgtype.Int8 if member.CreatedBy != nil { createdBy = pgtype.Int8{Int64: *member.CreatedBy, Valid: true} } res, err := s.queries.CreateTeamMember(ctx, dbgen.CreateTeamMemberParams{ FirstName: member.FirstName, LastName: member.LastName, Email: member.Email, PhoneNumber: pgtype.Text{String: member.PhoneNumber, Valid: member.PhoneNumber != ""}, Password: member.Password, TeamRole: string(member.TeamRole), Department: pgtype.Text{String: member.Department, Valid: member.Department != ""}, JobTitle: pgtype.Text{String: member.JobTitle, Valid: member.JobTitle != ""}, EmploymentType: pgtype.Text{String: string(member.EmploymentType), Valid: member.EmploymentType != ""}, HireDate: hireDate, ProfilePictureUrl: pgtype.Text{String: member.ProfilePictureURL, Valid: member.ProfilePictureURL != ""}, Bio: pgtype.Text{String: member.Bio, Valid: member.Bio != ""}, WorkPhone: pgtype.Text{String: member.WorkPhone, Valid: member.WorkPhone != ""}, EmergencyContact: pgtype.Text{String: member.EmergencyContact, Valid: member.EmergencyContact != ""}, Status: string(member.Status), EmailVerified: member.EmailVerified, Permissions: permissionsJSON, CreatedBy: createdBy, }) if err != nil { return domain.TeamMember{}, err } return mapDBTeamMember(res), nil } func (s *Store) GetTeamMemberByID(ctx context.Context, id int64) (domain.TeamMember, error) { res, err := s.queries.GetTeamMemberByID(ctx, id) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return domain.TeamMember{}, domain.ErrTeamMemberNotFound } return domain.TeamMember{}, err } return mapDBTeamMember(res), nil } func (s *Store) GetTeamMemberByEmail(ctx context.Context, email string) (domain.TeamMember, error) { res, err := s.queries.GetTeamMemberByEmail(ctx, email) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return domain.TeamMember{}, domain.ErrTeamMemberNotFound } return domain.TeamMember{}, err } return mapDBTeamMember(res), nil } func (s *Store) GetAllTeamMembers( ctx context.Context, teamRole, department, status *string, limit, offset int32, ) ([]domain.TeamMember, int64, error) { var teamRoleParam, departmentParam, statusParam pgtype.Text if teamRole != nil { teamRoleParam = pgtype.Text{String: *teamRole, Valid: true} } if department != nil { departmentParam = pgtype.Text{String: *department, Valid: true} } if status != nil { statusParam = pgtype.Text{String: *status, Valid: true} } rows, err := s.queries.GetAllTeamMembers(ctx, dbgen.GetAllTeamMembersParams{ TeamRole: teamRoleParam, Department: departmentParam, Status: statusParam, Limit: pgtype.Int4{Int32: limit, Valid: true}, Offset: pgtype.Int4{Int32: offset, Valid: true}, }) if err != nil { return nil, 0, err } if len(rows) == 0 { return []domain.TeamMember{}, 0, nil } var totalCount int64 members := make([]domain.TeamMember, len(rows)) for i, row := range rows { if i == 0 { totalCount = row.TotalCount } members[i] = mapGetAllTeamMembersRow(row) } return members, totalCount, nil } func (s *Store) SearchTeamMembers( ctx context.Context, search string, teamRole, status *string, ) ([]domain.TeamMember, error) { var teamRoleParam, statusParam pgtype.Text if teamRole != nil { teamRoleParam = pgtype.Text{String: *teamRole, Valid: true} } if status != nil { statusParam = pgtype.Text{String: *status, Valid: true} } rows, err := s.queries.SearchTeamMembers(ctx, dbgen.SearchTeamMembersParams{ Column1: pgtype.Text{String: search, Valid: true}, TeamRole: teamRoleParam, Status: statusParam, }) if err != nil { return nil, err } members := make([]domain.TeamMember, len(rows)) for i, row := range rows { members[i] = mapSearchTeamMembersRow(row) } return members, nil } func (s *Store) UpdateTeamMember(ctx context.Context, req domain.UpdateTeamMemberReq) error { var permissionsJSON []byte if len(req.Permissions) > 0 { var err error permissionsJSON, err = json.Marshal(req.Permissions) if err != nil { return err } } var hireDate pgtype.Date if req.HireDate != "" { t, err := parseDate(req.HireDate) if err != nil { return err } hireDate = pgtype.Date{Time: t, Valid: true} } return s.queries.UpdateTeamMember(ctx, dbgen.UpdateTeamMemberParams{ FirstName: req.FirstName, LastName: req.LastName, PhoneNumber: pgtype.Text{String: req.PhoneNumber, Valid: req.PhoneNumber != ""}, TeamRole: req.TeamRole, Department: pgtype.Text{String: req.Department, Valid: req.Department != ""}, JobTitle: pgtype.Text{String: req.JobTitle, Valid: req.JobTitle != ""}, EmploymentType: pgtype.Text{String: req.EmploymentType, Valid: req.EmploymentType != ""}, HireDate: hireDate, ProfilePictureUrl: pgtype.Text{String: req.ProfilePictureURL, Valid: req.ProfilePictureURL != ""}, Bio: pgtype.Text{String: req.Bio, Valid: req.Bio != ""}, WorkPhone: pgtype.Text{String: req.WorkPhone, Valid: req.WorkPhone != ""}, EmergencyContact: pgtype.Text{String: req.EmergencyContact, Valid: req.EmergencyContact != ""}, Permissions: permissionsJSON, UpdatedBy: pgtype.Int8{Int64: req.UpdatedBy, Valid: req.UpdatedBy > 0}, ID: req.TeamMemberID, }) } func (s *Store) UpdateTeamMemberStatus(ctx context.Context, req domain.UpdateTeamMemberStatusReq) error { return s.queries.UpdateTeamMemberStatus(ctx, dbgen.UpdateTeamMemberStatusParams{ Status: req.Status, UpdatedBy: pgtype.Int8{Int64: req.UpdatedBy, Valid: req.UpdatedBy > 0}, ID: req.TeamMemberID, }) } func (s *Store) UpdateTeamMemberPassword(ctx context.Context, memberID int64, password string) error { return s.queries.UpdateTeamMemberPassword(ctx, dbgen.UpdateTeamMemberPasswordParams{ Password: []byte(password), ID: memberID, }) } func (s *Store) UpdateTeamMemberLastLogin(ctx context.Context, memberID int64) error { return s.queries.UpdateTeamMemberLastLogin(ctx, memberID) } func (s *Store) DeleteTeamMember(ctx context.Context, memberID int64) error { return s.queries.DeleteTeamMember(ctx, memberID) } func (s *Store) CheckTeamMemberEmailExists(ctx context.Context, email string) (bool, error) { return s.queries.CheckTeamMemberEmailExists(ctx, email) } func (s *Store) GetTeamMembersByDepartment(ctx context.Context, department string) ([]domain.TeamMember, error) { rows, err := s.queries.GetTeamMembersByDepartment(ctx, pgtype.Text{String: department, Valid: true}) if err != nil { return nil, err } members := make([]domain.TeamMember, len(rows)) for i, row := range rows { members[i] = mapGetTeamMembersByDepartmentRow(row) } return members, nil } func (s *Store) GetTeamMembersByRole(ctx context.Context, role string) ([]domain.TeamMember, error) { rows, err := s.queries.GetTeamMembersByRole(ctx, role) if err != nil { return nil, err } members := make([]domain.TeamMember, len(rows)) for i, row := range rows { members[i] = mapGetTeamMembersByRoleRow(row) } return members, nil } func (s *Store) CountTeamMembersByStatus(ctx context.Context) (domain.TeamMemberStats, error) { res, err := s.queries.CountTeamMembersByStatus(ctx) if err != nil { return domain.TeamMemberStats{}, err } return domain.TeamMemberStats{ ActiveCount: res.ActiveCount, InactiveCount: res.InactiveCount, SuspendedCount: res.SuspendedCount, TerminatedCount: res.TerminatedCount, TotalCount: res.TotalCount, }, nil } func (s *Store) UpdateTeamMemberEmailVerified(ctx context.Context, memberID int64, verified bool) error { return s.queries.UpdateTeamMemberEmailVerified(ctx, dbgen.UpdateTeamMemberEmailVerifiedParams{ EmailVerified: verified, ID: memberID, }) } func mapDBTeamMember(m dbgen.TeamMember) domain.TeamMember { var permissions []string if len(m.Permissions) > 0 { _ = json.Unmarshal(m.Permissions, &permissions) } var hireDate *time.Time if m.HireDate.Valid { hireDate = &m.HireDate.Time } var lastLogin *time.Time if m.LastLogin.Valid { lastLogin = &m.LastLogin.Time } var createdBy *int64 if m.CreatedBy.Valid { createdBy = &m.CreatedBy.Int64 } var updatedBy *int64 if m.UpdatedBy.Valid { updatedBy = &m.UpdatedBy.Int64 } var updatedAt *time.Time if m.UpdatedAt.Valid { updatedAt = &m.UpdatedAt.Time } return domain.TeamMember{ ID: m.ID, FirstName: m.FirstName, LastName: m.LastName, Email: m.Email, PhoneNumber: m.PhoneNumber.String, Password: m.Password, TeamRole: domain.TeamRole(m.TeamRole), Department: m.Department.String, JobTitle: m.JobTitle.String, EmploymentType: domain.EmploymentType(m.EmploymentType.String), HireDate: hireDate, ProfilePictureURL: m.ProfilePictureUrl.String, Bio: m.Bio.String, WorkPhone: m.WorkPhone.String, EmergencyContact: m.EmergencyContact.String, Status: domain.TeamMemberStatus(m.Status), EmailVerified: m.EmailVerified, Permissions: permissions, LastLogin: lastLogin, CreatedBy: createdBy, UpdatedBy: updatedBy, CreatedAt: m.CreatedAt.Time, UpdatedAt: updatedAt, } } func mapGetAllTeamMembersRow(row dbgen.GetAllTeamMembersRow) domain.TeamMember { var permissions []string if len(row.Permissions) > 0 { _ = json.Unmarshal(row.Permissions, &permissions) } var hireDate *time.Time if row.HireDate.Valid { hireDate = &row.HireDate.Time } var lastLogin *time.Time if row.LastLogin.Valid { lastLogin = &row.LastLogin.Time } var updatedAt *time.Time if row.UpdatedAt.Valid { updatedAt = &row.UpdatedAt.Time } return domain.TeamMember{ ID: row.ID, FirstName: row.FirstName, LastName: row.LastName, Email: row.Email, PhoneNumber: row.PhoneNumber.String, TeamRole: domain.TeamRole(row.TeamRole), Department: row.Department.String, JobTitle: row.JobTitle.String, EmploymentType: domain.EmploymentType(row.EmploymentType.String), HireDate: hireDate, ProfilePictureURL: row.ProfilePictureUrl.String, Bio: row.Bio.String, WorkPhone: row.WorkPhone.String, Status: domain.TeamMemberStatus(row.Status), EmailVerified: row.EmailVerified, Permissions: permissions, LastLogin: lastLogin, CreatedAt: row.CreatedAt.Time, UpdatedAt: updatedAt, } } func mapSearchTeamMembersRow(row dbgen.SearchTeamMembersRow) domain.TeamMember { var permissions []string if len(row.Permissions) > 0 { _ = json.Unmarshal(row.Permissions, &permissions) } var hireDate *time.Time if row.HireDate.Valid { hireDate = &row.HireDate.Time } var lastLogin *time.Time if row.LastLogin.Valid { lastLogin = &row.LastLogin.Time } var updatedAt *time.Time if row.UpdatedAt.Valid { updatedAt = &row.UpdatedAt.Time } return domain.TeamMember{ ID: row.ID, FirstName: row.FirstName, LastName: row.LastName, Email: row.Email, PhoneNumber: row.PhoneNumber.String, TeamRole: domain.TeamRole(row.TeamRole), Department: row.Department.String, JobTitle: row.JobTitle.String, EmploymentType: domain.EmploymentType(row.EmploymentType.String), HireDate: hireDate, ProfilePictureURL: row.ProfilePictureUrl.String, Bio: row.Bio.String, Status: domain.TeamMemberStatus(row.Status), EmailVerified: row.EmailVerified, Permissions: permissions, LastLogin: lastLogin, CreatedAt: row.CreatedAt.Time, UpdatedAt: updatedAt, } } func mapGetTeamMembersByDepartmentRow(row dbgen.GetTeamMembersByDepartmentRow) domain.TeamMember { return domain.TeamMember{ ID: row.ID, FirstName: row.FirstName, LastName: row.LastName, Email: row.Email, PhoneNumber: row.PhoneNumber.String, TeamRole: domain.TeamRole(row.TeamRole), Department: row.Department.String, JobTitle: row.JobTitle.String, EmploymentType: domain.EmploymentType(row.EmploymentType.String), ProfilePictureURL: row.ProfilePictureUrl.String, Status: domain.TeamMemberStatus(row.Status), CreatedAt: row.CreatedAt.Time, } } func mapGetTeamMembersByRoleRow(row dbgen.GetTeamMembersByRoleRow) domain.TeamMember { return domain.TeamMember{ ID: row.ID, FirstName: row.FirstName, LastName: row.LastName, Email: row.Email, PhoneNumber: row.PhoneNumber.String, TeamRole: domain.TeamRole(row.TeamRole), Department: row.Department.String, JobTitle: row.JobTitle.String, EmploymentType: domain.EmploymentType(row.EmploymentType.String), ProfilePictureURL: row.ProfilePictureUrl.String, Status: domain.TeamMemberStatus(row.Status), CreatedAt: row.CreatedAt.Time, } } func parseDate(dateStr string) (time.Time, error) { return time.Parse("2006-01-02", dateStr) }