- Create CreatePlanModal component with Headless UI Dialog - Implement dark mode support throughout plans UI - Update plans/page.tsx with professional card layout - Update plans/[id]/page.tsx with consistent styling - Add proper spacing, typography, and color consistency - Implement smooth animations and transitions - Add success/error message feedback - Improve form UX with better input styling
284 lines
5.9 KiB
Go
284 lines
5.9 KiB
Go
package repository
|
|
|
|
import (
|
|
"database/sql"
|
|
"time"
|
|
|
|
"aggios-app/backend/internal/domain"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/lib/pq"
|
|
)
|
|
|
|
// PlanRepository handles database operations for plans
|
|
type PlanRepository struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// NewPlanRepository creates a new plan repository
|
|
func NewPlanRepository(db *sql.DB) *PlanRepository {
|
|
return &PlanRepository{db: db}
|
|
}
|
|
|
|
// Create creates a new plan
|
|
func (r *PlanRepository) Create(plan *domain.Plan) error {
|
|
query := `
|
|
INSERT INTO plans (id, name, slug, description, min_users, max_users, monthly_price, annual_price, features, differentiators, storage_gb, is_active, created_at, updated_at)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
|
RETURNING id, created_at, updated_at
|
|
`
|
|
|
|
now := time.Now()
|
|
plan.ID = uuid.New()
|
|
plan.CreatedAt = now
|
|
plan.UpdatedAt = now
|
|
|
|
features := pq.Array(plan.Features)
|
|
differentiators := pq.Array(plan.Differentiators)
|
|
|
|
return r.db.QueryRow(
|
|
query,
|
|
plan.ID,
|
|
plan.Name,
|
|
plan.Slug,
|
|
plan.Description,
|
|
plan.MinUsers,
|
|
plan.MaxUsers,
|
|
plan.MonthlyPrice,
|
|
plan.AnnualPrice,
|
|
features,
|
|
differentiators,
|
|
plan.StorageGB,
|
|
plan.IsActive,
|
|
plan.CreatedAt,
|
|
plan.UpdatedAt,
|
|
).Scan(&plan.ID, &plan.CreatedAt, &plan.UpdatedAt)
|
|
}
|
|
|
|
// GetByID retrieves a plan by ID
|
|
func (r *PlanRepository) GetByID(id uuid.UUID) (*domain.Plan, error) {
|
|
query := `
|
|
SELECT id, name, slug, description, min_users, max_users, monthly_price, annual_price, features, differentiators, storage_gb, is_active, created_at, updated_at
|
|
FROM plans
|
|
WHERE id = $1
|
|
`
|
|
|
|
plan := &domain.Plan{}
|
|
var features, differentiators pq.StringArray
|
|
|
|
err := r.db.QueryRow(query, id).Scan(
|
|
&plan.ID,
|
|
&plan.Name,
|
|
&plan.Slug,
|
|
&plan.Description,
|
|
&plan.MinUsers,
|
|
&plan.MaxUsers,
|
|
&plan.MonthlyPrice,
|
|
&plan.AnnualPrice,
|
|
&features,
|
|
&differentiators,
|
|
&plan.StorageGB,
|
|
&plan.IsActive,
|
|
&plan.CreatedAt,
|
|
&plan.UpdatedAt,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
plan.Features = []string(features)
|
|
plan.Differentiators = []string(differentiators)
|
|
|
|
return plan, nil
|
|
}
|
|
|
|
// GetBySlug retrieves a plan by slug
|
|
func (r *PlanRepository) GetBySlug(slug string) (*domain.Plan, error) {
|
|
query := `
|
|
SELECT id, name, slug, description, min_users, max_users, monthly_price, annual_price, features, differentiators, storage_gb, is_active, created_at, updated_at
|
|
FROM plans
|
|
WHERE slug = $1
|
|
`
|
|
|
|
plan := &domain.Plan{}
|
|
var features, differentiators pq.StringArray
|
|
|
|
err := r.db.QueryRow(query, slug).Scan(
|
|
&plan.ID,
|
|
&plan.Name,
|
|
&plan.Slug,
|
|
&plan.Description,
|
|
&plan.MinUsers,
|
|
&plan.MaxUsers,
|
|
&plan.MonthlyPrice,
|
|
&plan.AnnualPrice,
|
|
&features,
|
|
&differentiators,
|
|
&plan.StorageGB,
|
|
&plan.IsActive,
|
|
&plan.CreatedAt,
|
|
&plan.UpdatedAt,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
plan.Features = []string(features)
|
|
plan.Differentiators = []string(differentiators)
|
|
|
|
return plan, nil
|
|
}
|
|
|
|
// ListAll retrieves all plans
|
|
func (r *PlanRepository) ListAll() ([]*domain.Plan, error) {
|
|
query := `
|
|
SELECT id, name, slug, description, min_users, max_users, monthly_price, annual_price, features, differentiators, storage_gb, is_active, created_at, updated_at
|
|
FROM plans
|
|
ORDER BY min_users ASC
|
|
`
|
|
|
|
rows, err := r.db.Query(query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var plans []*domain.Plan
|
|
|
|
for rows.Next() {
|
|
plan := &domain.Plan{}
|
|
var features, differentiators pq.StringArray
|
|
|
|
err := rows.Scan(
|
|
&plan.ID,
|
|
&plan.Name,
|
|
&plan.Slug,
|
|
&plan.Description,
|
|
&plan.MinUsers,
|
|
&plan.MaxUsers,
|
|
&plan.MonthlyPrice,
|
|
&plan.AnnualPrice,
|
|
&features,
|
|
&differentiators,
|
|
&plan.StorageGB,
|
|
&plan.IsActive,
|
|
&plan.CreatedAt,
|
|
&plan.UpdatedAt,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
plan.Features = []string(features)
|
|
plan.Differentiators = []string(differentiators)
|
|
plans = append(plans, plan)
|
|
}
|
|
|
|
return plans, rows.Err()
|
|
}
|
|
|
|
// ListActive retrieves all active plans
|
|
func (r *PlanRepository) ListActive() ([]*domain.Plan, error) {
|
|
query := `
|
|
SELECT id, name, slug, description, min_users, max_users, monthly_price, annual_price, features, differentiators, storage_gb, is_active, created_at, updated_at
|
|
FROM plans
|
|
WHERE is_active = true
|
|
ORDER BY min_users ASC
|
|
`
|
|
|
|
rows, err := r.db.Query(query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var plans []*domain.Plan
|
|
|
|
for rows.Next() {
|
|
plan := &domain.Plan{}
|
|
var features, differentiators pq.StringArray
|
|
|
|
err := rows.Scan(
|
|
&plan.ID,
|
|
&plan.Name,
|
|
&plan.Slug,
|
|
&plan.Description,
|
|
&plan.MinUsers,
|
|
&plan.MaxUsers,
|
|
&plan.MonthlyPrice,
|
|
&plan.AnnualPrice,
|
|
&features,
|
|
&differentiators,
|
|
&plan.StorageGB,
|
|
&plan.IsActive,
|
|
&plan.CreatedAt,
|
|
&plan.UpdatedAt,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
plan.Features = []string(features)
|
|
plan.Differentiators = []string(differentiators)
|
|
plans = append(plans, plan)
|
|
}
|
|
|
|
return plans, rows.Err()
|
|
}
|
|
|
|
// Update updates a plan
|
|
func (r *PlanRepository) Update(plan *domain.Plan) error {
|
|
query := `
|
|
UPDATE plans
|
|
SET name = $2, slug = $3, description = $4, min_users = $5, max_users = $6, monthly_price = $7, annual_price = $8, features = $9, differentiators = $10, storage_gb = $11, is_active = $12, updated_at = $13
|
|
WHERE id = $1
|
|
RETURNING updated_at
|
|
`
|
|
|
|
plan.UpdatedAt = time.Now()
|
|
|
|
features := pq.Array(plan.Features)
|
|
differentiators := pq.Array(plan.Differentiators)
|
|
|
|
return r.db.QueryRow(
|
|
query,
|
|
plan.ID,
|
|
plan.Name,
|
|
plan.Slug,
|
|
plan.Description,
|
|
plan.MinUsers,
|
|
plan.MaxUsers,
|
|
plan.MonthlyPrice,
|
|
plan.AnnualPrice,
|
|
features,
|
|
differentiators,
|
|
plan.StorageGB,
|
|
plan.IsActive,
|
|
plan.UpdatedAt,
|
|
).Scan(&plan.UpdatedAt)
|
|
}
|
|
|
|
// Delete deletes a plan
|
|
func (r *PlanRepository) Delete(id uuid.UUID) error {
|
|
query := `DELETE FROM plans WHERE id = $1`
|
|
result, err := r.db.Exec(query, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return sql.ErrNoRows
|
|
}
|
|
|
|
return nil
|
|
}
|