package repository import ( "Yimaru-Backend/internal/domain" "Yimaru-Backend/internal/ports" "context" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" ) func NewProfileFieldOptionStore(s *Store) ports.ProfileFieldOptionStore { return s } func profileFieldOptionToDomain( id int64, fieldKey, code, label string, displayOrder int32, status string, createdAt pgtype.Timestamptz, updatedAt pgtype.Timestamptz, ) domain.ProfileFieldOption { return domain.ProfileFieldOption{ ID: id, FieldKey: fieldKey, Code: code, Label: label, DisplayOrder: displayOrder, Status: status, CreatedAt: createdAt.Time, UpdatedAt: timePtr(updatedAt), } } func (s *Store) CreateProfileFieldOption(ctx context.Context, input domain.CreateProfileFieldOptionInput) (domain.ProfileFieldOption, error) { displayOrder := int32(0) if input.DisplayOrder != nil { displayOrder = *input.DisplayOrder } status := domain.ProfileFieldOptionStatusActive if input.Status != nil { status = *input.Status } row := s.conn.QueryRow(ctx, ` INSERT INTO field_options (field_key, code, label, display_order, status) VALUES ($1, $2, $3, $4, $5) RETURNING id, field_key, code, label, display_order, status, created_at, updated_at `, input.FieldKey, input.Code, input.Label, displayOrder, status) var ( id int64 fieldKey string code string label string orderVal int32 optionStatus string createdAt pgtype.Timestamptz updatedAt pgtype.Timestamptz ) if err := row.Scan(&id, &fieldKey, &code, &label, &orderVal, &optionStatus, &createdAt, &updatedAt); err != nil { return domain.ProfileFieldOption{}, err } return profileFieldOptionToDomain(id, fieldKey, code, label, orderVal, optionStatus, createdAt, updatedAt), nil } func (s *Store) UpdateProfileFieldOption(ctx context.Context, id int64, input domain.UpdateProfileFieldOptionInput) (domain.ProfileFieldOption, error) { row := s.conn.QueryRow(ctx, ` UPDATE field_options SET label = COALESCE($2, label), display_order = COALESCE($3, display_order), status = COALESCE($4, status), updated_at = NOW() WHERE id = $1 RETURNING id, field_key, code, label, display_order, status, created_at, updated_at `, id, input.Label, input.DisplayOrder, input.Status) var ( optionID int64 fieldKey string code string label string orderVal int32 optionStatus string createdAt pgtype.Timestamptz updatedAt pgtype.Timestamptz ) if err := row.Scan(&optionID, &fieldKey, &code, &label, &orderVal, &optionStatus, &createdAt, &updatedAt); err != nil { return domain.ProfileFieldOption{}, err } return profileFieldOptionToDomain(optionID, fieldKey, code, label, orderVal, optionStatus, createdAt, updatedAt), nil } func (s *Store) GetProfileFieldOptionByID(ctx context.Context, id int64, includeInactive bool) (domain.ProfileFieldOption, error) { row := s.conn.QueryRow(ctx, ` SELECT id, field_key, code, label, display_order, status, created_at, updated_at FROM field_options WHERE id = $1 AND ($2::boolean = TRUE OR status = 'ACTIVE') `, id, includeInactive) var ( optionID int64 fieldKey string code string label string orderVal int32 optionStatus string createdAt pgtype.Timestamptz updatedAt pgtype.Timestamptz ) if err := row.Scan(&optionID, &fieldKey, &code, &label, &orderVal, &optionStatus, &createdAt, &updatedAt); err != nil { return domain.ProfileFieldOption{}, err } return profileFieldOptionToDomain(optionID, fieldKey, code, label, orderVal, optionStatus, createdAt, updatedAt), nil } func (s *Store) ListProfileFieldOptions(ctx context.Context, fieldKey *string, status *string, limit, offset int32) ([]domain.ProfileFieldOption, int64, error) { rows, err := s.conn.Query(ctx, ` SELECT id, field_key, code, label, display_order, status, created_at, updated_at FROM field_options WHERE ($1::text IS NULL OR field_key = $1) AND ($2::text IS NULL OR status = $2) ORDER BY field_key ASC, display_order ASC, id ASC LIMIT $3 OFFSET $4 `, toPgText(fieldKey), toPgText(status), limit, offset) if err != nil { return nil, 0, err } defer rows.Close() options := make([]domain.ProfileFieldOption, 0) for rows.Next() { var ( optionID int64 fk string code string label string orderVal int32 optionStatus string createdAt pgtype.Timestamptz updatedAt pgtype.Timestamptz ) if err := rows.Scan(&optionID, &fk, &code, &label, &orderVal, &optionStatus, &createdAt, &updatedAt); err != nil { return nil, 0, err } options = append(options, profileFieldOptionToDomain(optionID, fk, code, label, orderVal, optionStatus, createdAt, updatedAt)) } if err := rows.Err(); err != nil { return nil, 0, err } var totalCount int64 if err := s.conn.QueryRow(ctx, ` SELECT COUNT(*) FROM field_options WHERE ($1::text IS NULL OR field_key = $1) AND ($2::text IS NULL OR status = $2) `, toPgText(fieldKey), toPgText(status)).Scan(&totalCount); err != nil { return nil, 0, err } return options, totalCount, nil } func (s *Store) ListActiveProfileFieldOptions(ctx context.Context, fieldKey *string) ([]domain.ProfileFieldOption, error) { active := domain.ProfileFieldOptionStatusActive options, _, err := s.ListProfileFieldOptions(ctx, fieldKey, &active, 500, 0) return options, err } func (s *Store) IsActiveProfileFieldOption(ctx context.Context, fieldKey, code string) (bool, error) { var exists bool err := s.conn.QueryRow(ctx, ` SELECT EXISTS ( SELECT 1 FROM field_options WHERE field_key = $1 AND code = $2 AND status = 'ACTIVE' ) `, fieldKey, code).Scan(&exists) return exists, err } func (s *Store) ListDistinctFieldKeys(ctx context.Context, activeOnly bool) ([]string, error) { rows, err := s.conn.Query(ctx, ` SELECT DISTINCT field_key FROM field_options WHERE ($1::boolean = FALSE OR status = 'ACTIVE') ORDER BY field_key ASC `, activeOnly) if err != nil { return nil, err } defer rows.Close() keys := make([]string, 0) for rows.Next() { var key string if err := rows.Scan(&key); err != nil { return nil, err } keys = append(keys, key) } return keys, rows.Err() } func (s *Store) DeleteProfileFieldOption(ctx context.Context, id int64) error { cmd, err := s.conn.Exec(ctx, `DELETE FROM field_options WHERE id = $1`, id) if err != nil { return err } if cmd.RowsAffected() == 0 { return pgx.ErrNoRows } return nil }