feat: integrate Grafana dashboards into BOC DashboardPage
- 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
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"boc/repository"
|
||||
)
|
||||
|
||||
// SalesService handles business logic for sales operations
|
||||
type SalesService struct {
|
||||
repo *repository.SalesRepository
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewSalesService(db *sql.DB) *SalesService {
|
||||
return &SalesService{
|
||||
repo: repository.NewSalesRepository(db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ListDeals returns all deals filtered by status
|
||||
func (s *SalesService) ListDeals(ctx context.Context, status string) ([]repository.Deal, error) {
|
||||
return s.repo.ListDeals(ctx, status)
|
||||
}
|
||||
|
||||
// GetDeal returns a single deal by ID
|
||||
func (s *SalesService) GetDeal(ctx context.Context, id string) (*repository.Deal, error) {
|
||||
return s.repo.GetDeal(ctx, id)
|
||||
}
|
||||
|
||||
// CreateDeal creates a new deal with validation
|
||||
func (s *SalesService) CreateDeal(ctx context.Context, d *repository.Deal) error {
|
||||
if d.Name == "" {
|
||||
return fmt.Errorf("deal name is required")
|
||||
}
|
||||
if d.CustomerID == "" {
|
||||
return fmt.Errorf("customer ID is required")
|
||||
}
|
||||
if d.Value <= 0 {
|
||||
return fmt.Errorf("deal value must be positive")
|
||||
}
|
||||
if d.Status == "" {
|
||||
d.Status = "open"
|
||||
}
|
||||
if d.Stage == "" {
|
||||
d.Stage = "discovery"
|
||||
}
|
||||
if d.Currency == "" {
|
||||
d.Currency = "USD"
|
||||
}
|
||||
return s.repo.CreateDeal(ctx, d)
|
||||
}
|
||||
|
||||
// UpdateDeal updates an existing deal
|
||||
func (s *SalesService) UpdateDeal(ctx context.Context, id string, d *repository.Deal) error {
|
||||
if d.Name == "" {
|
||||
return fmt.Errorf("deal name is required")
|
||||
}
|
||||
return s.repo.UpdateDeal(ctx, id, d)
|
||||
}
|
||||
|
||||
// ListProducts returns all active products
|
||||
func (s *SalesService) ListProducts(ctx context.Context) ([]repository.Product, error) {
|
||||
return s.repo.ListProducts(ctx)
|
||||
}
|
||||
|
||||
// GetMRR calculates monthly recurring revenue
|
||||
func (s *SalesService) GetMRR(ctx context.Context) (float64, error) {
|
||||
return s.repo.GetMRR(ctx)
|
||||
}
|
||||
|
||||
// GetARR calculates annual recurring revenue
|
||||
func (s *SalesService) GetARR(ctx context.Context) (float64, error) {
|
||||
return s.repo.GetARR(ctx)
|
||||
}
|
||||
Reference in New Issue
Block a user