e5623d2f84
- Add Infrastructure Health section with CPU/Memory/Disk panels - Add Service Status section with PM2/Docker panels - Create GrafanaPanel component for iframe embedding - Build passes successfully
215 lines
5.7 KiB
Go
215 lines
5.7 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/lib/pq"
|
|
)
|
|
|
|
// CustomerRepository hanterar alla customer-relaterade databasoperationer
|
|
type CustomerRepository struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
func NewCustomerRepository(db *sql.DB) *CustomerRepository {
|
|
return &CustomerRepository{db: db}
|
|
}
|
|
|
|
// Customer representerar en kund
|
|
type Customer struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenant_id"`
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
Phone string `json:"phone"`
|
|
Company string `json:"company"`
|
|
OrgNumber string `json:"org_number"`
|
|
Status string `json:"status"`
|
|
Source string `json:"source"`
|
|
Tags []string `json:"tags"`
|
|
AssignedTo *string `json:"assigned_to"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// CreateCustomer skapar en ny kund
|
|
func (r *CustomerRepository) CreateCustomer(ctx context.Context, c *Customer) error {
|
|
query := `
|
|
INSERT INTO boc_customers (tenant_id, name, email, phone, company, org_number, status, source, tags, assigned_to)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
RETURNING id, created_at, updated_at
|
|
`
|
|
|
|
err := r.db.QueryRowContext(ctx, query,
|
|
c.TenantID, c.Name, c.Email, c.Phone, c.Company, c.OrgNumber,
|
|
c.Status, c.Source, pq.Array(c.Tags), c.AssignedTo,
|
|
).Scan(&c.ID, &c.CreatedAt, &c.UpdatedAt)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("create customer: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetCustomer hämtar en kund med ID
|
|
func (r *CustomerRepository) GetCustomer(ctx context.Context, id string) (*Customer, error) {
|
|
query := `
|
|
SELECT id, tenant_id, name, email, phone, company, org_number, status, source, tags, assigned_to, created_at, updated_at
|
|
FROM boc_customers
|
|
WHERE id = $1
|
|
`
|
|
|
|
var c Customer
|
|
var tags pq.StringArray
|
|
|
|
err := r.db.QueryRowContext(ctx, query, id).Scan(
|
|
&c.ID, &c.TenantID, &c.Name, &c.Email, &c.Phone, &c.Company,
|
|
&c.OrgNumber, &c.Status, &c.Source, &tags, &c.AssignedTo,
|
|
&c.CreatedAt, &c.UpdatedAt,
|
|
)
|
|
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("customer not found")
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get customer: %w", err)
|
|
}
|
|
|
|
c.Tags = []string(tags)
|
|
return &c, nil
|
|
}
|
|
|
|
// ListCustomers hämtar kunder med pagination
|
|
func (r *CustomerRepository) ListCustomers(ctx context.Context, tenantID, status string, limit, offset int) ([]Customer, int, error) {
|
|
// Hämta total count
|
|
var total int
|
|
countQuery := `SELECT COUNT(*) FROM boc_customers WHERE tenant_id = $1 AND status = $2`
|
|
if err := r.db.QueryRowContext(ctx, countQuery, tenantID, status).Scan(&total); err != nil {
|
|
return nil, 0, fmt.Errorf("count customers: %w", err)
|
|
}
|
|
|
|
// Hämta kunder
|
|
query := `
|
|
SELECT id, tenant_id, name, email, phone, company, org_number, status, source, tags, assigned_to, created_at, updated_at
|
|
FROM boc_customers
|
|
WHERE tenant_id = $1 AND status = $2
|
|
ORDER BY created_at DESC
|
|
LIMIT $3 OFFSET $4
|
|
`
|
|
|
|
rows, err := r.db.QueryContext(ctx, query, tenantID, status, limit, offset)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("list customers: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var customers []Customer
|
|
for rows.Next() {
|
|
var c Customer
|
|
var tags pq.StringArray
|
|
|
|
if err := rows.Scan(
|
|
&c.ID, &c.TenantID, &c.Name, &c.Email, &c.Phone, &c.Company,
|
|
&c.OrgNumber, &c.Status, &c.Source, &tags, &c.AssignedTo,
|
|
&c.CreatedAt, &c.UpdatedAt,
|
|
); err != nil {
|
|
continue
|
|
}
|
|
|
|
c.Tags = []string(tags)
|
|
customers = append(customers, c)
|
|
}
|
|
|
|
return customers, total, rows.Err()
|
|
}
|
|
|
|
// UpdateCustomer uppdaterar en kund
|
|
func (r *CustomerRepository) UpdateCustomer(ctx context.Context, c *Customer) error {
|
|
query := `
|
|
UPDATE boc_customers
|
|
SET name = $1, email = $2, phone = $3, company = $4, org_number = $5,
|
|
status = $6, source = $7, tags = $8, assigned_to = $9, updated_at = NOW()
|
|
WHERE id = $10
|
|
RETURNING updated_at
|
|
`
|
|
|
|
err := r.db.QueryRowContext(ctx, query,
|
|
c.Name, c.Email, c.Phone, c.Company, c.OrgNumber,
|
|
c.Status, c.Source, pq.Array(c.Tags), c.AssignedTo, c.ID,
|
|
).Scan(&c.UpdatedAt)
|
|
|
|
if err == sql.ErrNoRows {
|
|
return fmt.Errorf("customer not found")
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("update customer: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteCustomer markerar en kund som borttagen (soft delete)
|
|
func (r *CustomerRepository) DeleteCustomer(ctx context.Context, id string) error {
|
|
query := `
|
|
UPDATE boc_customers
|
|
SET status = 'deleted', updated_at = NOW()
|
|
WHERE id = $1
|
|
`
|
|
|
|
result, err := r.db.ExecContext(ctx, query, id)
|
|
if err != nil {
|
|
return fmt.Errorf("delete customer: %w", err)
|
|
}
|
|
|
|
rows, _ := result.RowsAffected()
|
|
if rows == 0 {
|
|
return fmt.Errorf("customer not found")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SearchCustomers söker efter kunder
|
|
func (r *CustomerRepository) SearchCustomers(ctx context.Context, tenantID, query string, limit int) ([]Customer, error) {
|
|
sql := `
|
|
SELECT id, tenant_id, name, email, phone, company, org_number, status, source, tags, assigned_to, created_at, updated_at
|
|
FROM boc_customers
|
|
WHERE tenant_id = $1
|
|
AND status != 'deleted'
|
|
AND (name ILIKE $2 OR email ILIKE $2 OR company ILIKE $2)
|
|
ORDER BY name
|
|
LIMIT $3
|
|
`
|
|
|
|
searchTerm := "%" + query + "%"
|
|
|
|
rows, err := r.db.QueryContext(ctx, sql, tenantID, searchTerm, limit)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("search customers: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var customers []Customer
|
|
for rows.Next() {
|
|
var c Customer
|
|
var tags pq.StringArray
|
|
|
|
if err := rows.Scan(
|
|
&c.ID, &c.TenantID, &c.Name, &c.Email, &c.Phone, &c.Company,
|
|
&c.OrgNumber, &c.Status, &c.Source, &tags, &c.AssignedTo,
|
|
&c.CreatedAt, &c.UpdatedAt,
|
|
); err != nil {
|
|
continue
|
|
}
|
|
|
|
c.Tags = []string(tags)
|
|
customers = append(customers, c)
|
|
}
|
|
|
|
return customers, rows.Err()
|
|
}
|