package repository import ( "Yimaru-Backend/internal/domain" "Yimaru-Backend/internal/ports" "context" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" ) func NewFAQStore(s *Store) ports.FAQStore { return s } func faqToDomain( id int64, question string, answer string, category pgtype.Text, displayOrder int32, status string, createdAt pgtype.Timestamptz, updatedAt pgtype.Timestamptz, ) domain.FAQ { return domain.FAQ{ ID: id, Question: question, Answer: answer, Category: fromPgText(category), DisplayOrder: displayOrder, Status: status, CreatedAt: createdAt.Time, UpdatedAt: timePtr(updatedAt), } } func (s *Store) CreateFAQ(ctx context.Context, input domain.CreateFAQInput) (domain.FAQ, error) { displayOrder := int32(0) if input.DisplayOrder != nil { displayOrder = *input.DisplayOrder } status := domain.FAQStatusActive if input.Status != nil { status = *input.Status } row := s.conn.QueryRow(ctx, ` INSERT INTO faqs (question, answer, category, display_order, status) VALUES ($1, $2, $3, $4, $5) RETURNING id, question, answer, category, display_order, status, created_at, updated_at `, input.Question, input.Answer, toPgText(input.Category), displayOrder, status) var ( id int64 question string answer string category pgtype.Text orderVal int32 faqStatus string createdAt pgtype.Timestamptz updatedAt pgtype.Timestamptz ) if err := row.Scan(&id, &question, &answer, &category, &orderVal, &faqStatus, &createdAt, &updatedAt); err != nil { return domain.FAQ{}, err } return faqToDomain(id, question, answer, category, orderVal, faqStatus, createdAt, updatedAt), nil } func (s *Store) UpdateFAQ(ctx context.Context, id int64, input domain.UpdateFAQInput) (domain.FAQ, error) { categorySet := input.Category != nil var categoryValue pgtype.Text if categorySet { if *input.Category == "" { categoryValue = pgtype.Text{Valid: false} } else { categoryValue = pgtype.Text{String: *input.Category, Valid: true} } } row := s.conn.QueryRow(ctx, ` UPDATE faqs SET question = COALESCE($2, question), answer = COALESCE($3, answer), category = CASE WHEN $4::boolean THEN $5::text ELSE category END, display_order = COALESCE($6, display_order), status = COALESCE($7, status), updated_at = NOW() WHERE id = $1 RETURNING id, question, answer, category, display_order, status, created_at, updated_at `, id, input.Question, input.Answer, categorySet, categoryValue, input.DisplayOrder, input.Status, ) var ( faqID int64 question string answer string rowCategory pgtype.Text orderVal int32 faqStatus string createdAt pgtype.Timestamptz updatedAt pgtype.Timestamptz ) if err := row.Scan(&faqID, &question, &answer, &rowCategory, &orderVal, &faqStatus, &createdAt, &updatedAt); err != nil { return domain.FAQ{}, err } return faqToDomain(faqID, question, answer, rowCategory, orderVal, faqStatus, createdAt, updatedAt), nil } func (s *Store) GetFAQByID(ctx context.Context, id int64, includeInactive bool) (domain.FAQ, error) { row := s.conn.QueryRow(ctx, ` SELECT id, question, answer, category, display_order, status, created_at, updated_at FROM faqs WHERE id = $1 AND ($2::boolean = TRUE OR status = 'ACTIVE') `, id, includeInactive) var ( faqID int64 question string answer string category pgtype.Text orderVal int32 faqStatus string createdAt pgtype.Timestamptz updatedAt pgtype.Timestamptz ) if err := row.Scan(&faqID, &question, &answer, &category, &orderVal, &faqStatus, &createdAt, &updatedAt); err != nil { return domain.FAQ{}, err } return faqToDomain(faqID, question, answer, category, orderVal, faqStatus, createdAt, updatedAt), nil } func (s *Store) ListFAQs(ctx context.Context, status *string, category *string, limit int32, offset int32) ([]domain.FAQ, int64, error) { rows, err := s.conn.Query(ctx, ` SELECT id, question, answer, category, display_order, status, created_at, updated_at FROM faqs WHERE ($1::text IS NULL OR status = $1) AND ($2::text IS NULL OR category = $2) ORDER BY display_order ASC, id ASC LIMIT $3 OFFSET $4 `, toPgText(status), toPgText(category), limit, offset) if err != nil { return nil, 0, err } defer rows.Close() faqs := make([]domain.FAQ, 0) for rows.Next() { var ( faqID int64 question string answer string rowCategory pgtype.Text orderVal int32 faqStatus string createdAt pgtype.Timestamptz updatedAt pgtype.Timestamptz ) if err := rows.Scan(&faqID, &question, &answer, &rowCategory, &orderVal, &faqStatus, &createdAt, &updatedAt); err != nil { return nil, 0, err } faqs = append(faqs, faqToDomain(faqID, question, answer, rowCategory, orderVal, faqStatus, createdAt, updatedAt)) } if err := rows.Err(); err != nil { return nil, 0, err } var totalCount int64 if err := s.conn.QueryRow(ctx, ` SELECT COUNT(*) FROM faqs WHERE ($1::text IS NULL OR status = $1) AND ($2::text IS NULL OR category = $2) `, toPgText(status), toPgText(category)).Scan(&totalCount); err != nil { return nil, 0, err } return faqs, totalCount, nil } func (s *Store) DeleteFAQ(ctx context.Context, id int64) error { cmd, err := s.conn.Exec(ctx, `DELETE FROM faqs WHERE id = $1`, id) if err != nil { return err } if cmd.RowsAffected() == 0 { return pgx.ErrNoRows } return nil }