78b57273e2
- Add password hashing with bcrypt - Add AuthService with proper login - Add password strength validation - Add RBAC middleware (AdminOnly, ManagerOrAdmin) - Add tenant isolation middleware - Update CRM handler with tenant filtering - Add JWT fallback for development mode - Add user context helpers - Build successful
172 lines
4.0 KiB
Go
172 lines
4.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"database/sql"
|
|
"net/http"
|
|
)
|
|
|
|
// UnifiedHandler hanterar allt i ett enda API
|
|
type UnifiedHandler struct {
|
|
DB *sql.DB
|
|
}
|
|
|
|
func NewUnifiedHandler(db *sql.DB) *UnifiedHandler {
|
|
return &UnifiedHandler{DB: db}
|
|
}
|
|
|
|
// GetUnifiedDashboard returnerar allt på ett ställe
|
|
func (h *UnifiedHandler) GetUnifiedDashboard(w http.ResponseWriter, r *http.Request) {
|
|
// 1. Hämta CRM-data från BOC-databasen
|
|
customers := []map[string]interface{}{}
|
|
customerRows, err := h.DB.Query(`
|
|
SELECT id, name, email, status, created_at
|
|
FROM boc_customers
|
|
WHERE status = 'active'
|
|
ORDER BY created_at DESC
|
|
`)
|
|
if err == nil {
|
|
defer customerRows.Close()
|
|
for customerRows.Next() {
|
|
var c map[string]interface{}
|
|
var id, name, email, status string
|
|
var createdAt sql.NullTime
|
|
if err := customerRows.Scan(&id, &name, &email, &status, &createdAt); err != nil {
|
|
continue
|
|
}
|
|
c = map[string]interface{}{
|
|
"id": id,
|
|
"name": name,
|
|
"email": email,
|
|
"status": status,
|
|
}
|
|
if createdAt.Valid {
|
|
c["created_at"] = createdAt.Time.Format("2006-01-02")
|
|
}
|
|
customers = append(customers, c)
|
|
}
|
|
}
|
|
|
|
// 2. Hämta deals från BOC-databasen
|
|
deals := []map[string]interface{}{}
|
|
dealRows, err := h.DB.Query(`
|
|
SELECT id, name, customer_id, value, currency, stage, status
|
|
FROM boc_deals
|
|
ORDER BY
|
|
CASE stage
|
|
WHEN 'negotiation' THEN 1
|
|
WHEN 'proposal' THEN 2
|
|
WHEN 'closed' THEN 3
|
|
ELSE 4
|
|
END,
|
|
value DESC
|
|
`)
|
|
if err == nil {
|
|
defer dealRows.Close()
|
|
for dealRows.Next() {
|
|
var d map[string]interface{}
|
|
var id, name, customerID, currency, stage, status string
|
|
var value float64
|
|
if err := dealRows.Scan(&id, &name, &customerID, &value, ¤cy, &stage, &status); err != nil {
|
|
continue
|
|
}
|
|
d = map[string]interface{}{
|
|
"id": id,
|
|
"name": name,
|
|
"customer_id": customerID,
|
|
"value": value,
|
|
"currency": currency,
|
|
"stage": stage,
|
|
"status": status,
|
|
}
|
|
deals = append(deals, d)
|
|
}
|
|
}
|
|
|
|
// 3. Hämta mail-statistik
|
|
mailCount := 0
|
|
mailRows, err := h.DB.Query(`
|
|
SELECT COUNT(*) FROM boc_mail_messages
|
|
`)
|
|
if err == nil && mailRows.Next() {
|
|
mailRows.Scan(&mailCount)
|
|
mailRows.Close()
|
|
}
|
|
|
|
// 4. Hämta analytics
|
|
var revenue, moms float64
|
|
analyticsRows, err := h.DB.Query(`
|
|
SELECT kpi_key, value FROM boc_analytics_kpis
|
|
WHERE kpi_key IN ('revenue_h1', 'moms_att_betala')
|
|
`)
|
|
if err == nil {
|
|
defer analyticsRows.Close()
|
|
for analyticsRows.Next() {
|
|
var key string
|
|
var value float64
|
|
if err := analyticsRows.Scan(&key, &value); err != nil {
|
|
continue
|
|
}
|
|
if key == "revenue_h1" {
|
|
revenue = value
|
|
} else if key == "moms_att_betala" {
|
|
moms = value
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. Hämta team
|
|
team := []map[string]interface{}{}
|
|
teamRows, err := h.DB.Query(`
|
|
SELECT first_name, last_name, position, department, status
|
|
FROM boc_employees
|
|
WHERE status = 'active'
|
|
ORDER BY department, first_name
|
|
`)
|
|
if err == nil {
|
|
defer teamRows.Close()
|
|
for teamRows.Next() {
|
|
var firstName, lastName, position, department, status string
|
|
if err := teamRows.Scan(&firstName, &lastName, &position, &department, &status); err != nil {
|
|
continue
|
|
}
|
|
team = append(team, map[string]interface{}{
|
|
"name": firstName + " " + lastName,
|
|
"position": position,
|
|
"department": department,
|
|
"status": status,
|
|
})
|
|
}
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
|
"ok": true,
|
|
"dashboard": map[string]interface{}{
|
|
"crm": map[string]interface{}{
|
|
"customers": customers,
|
|
"deals": deals,
|
|
"total_pipeline": calculatePipeline(deals),
|
|
},
|
|
"finance": map[string]interface{}{
|
|
"revenue": revenue,
|
|
"moms": moms,
|
|
},
|
|
"mail": map[string]interface{}{
|
|
"total_messages": mailCount,
|
|
},
|
|
"team": team,
|
|
},
|
|
})
|
|
}
|
|
|
|
func calculatePipeline(deals []map[string]interface{}) float64 {
|
|
var total float64
|
|
for _, deal := range deals {
|
|
if status, ok := deal["status"].(string); ok && status == "open" {
|
|
if value, ok := deal["value"].(float64); ok {
|
|
total += value
|
|
}
|
|
}
|
|
}
|
|
return total
|
|
}
|