feat: versão 1.5 - CRM Beta com leads, funis, campanhas e portal do cliente
This commit is contained in:
210
backend/internal/api/handlers/export.go
Normal file
210
backend/internal/api/handlers/export.go
Normal file
@@ -0,0 +1,210 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"aggios-app/backend/internal/api/middleware"
|
||||
"aggios-app/backend/internal/domain"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
// ExportLeads handles exporting leads in different formats
|
||||
func (h *CRMHandler) ExportLeads(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
|
||||
if tenantID == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Missing tenant_id"})
|
||||
return
|
||||
}
|
||||
|
||||
format := r.URL.Query().Get("format")
|
||||
if format == "" {
|
||||
format = "csv"
|
||||
}
|
||||
|
||||
customerID := r.URL.Query().Get("customer_id")
|
||||
campaignID := r.URL.Query().Get("campaign_id")
|
||||
|
||||
var leads []domain.CRMLead
|
||||
var err error
|
||||
|
||||
if campaignID != "" {
|
||||
leads, err = h.repo.GetLeadsByListID(campaignID)
|
||||
} else if customerID != "" {
|
||||
leads, err = h.repo.GetLeadsByTenant(tenantID)
|
||||
// Filter by customer manually
|
||||
filtered := []domain.CRMLead{}
|
||||
for _, lead := range leads {
|
||||
if lead.CustomerID != nil && *lead.CustomerID == customerID {
|
||||
filtered = append(filtered, lead)
|
||||
}
|
||||
}
|
||||
leads = filtered
|
||||
} else {
|
||||
leads, err = h.repo.GetLeadsByTenant(tenantID)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("ExportLeads: Error fetching leads: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Failed to fetch leads"})
|
||||
return
|
||||
}
|
||||
|
||||
switch strings.ToLower(format) {
|
||||
case "json":
|
||||
exportJSON(w, leads)
|
||||
case "xlsx", "excel":
|
||||
exportXLSX(w, leads)
|
||||
default:
|
||||
exportCSV(w, leads)
|
||||
}
|
||||
}
|
||||
|
||||
func exportJSON(w http.ResponseWriter, leads []domain.CRMLead) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=leads.json")
|
||||
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"leads": leads,
|
||||
"count": len(leads),
|
||||
})
|
||||
}
|
||||
|
||||
func exportCSV(w http.ResponseWriter, leads []domain.CRMLead) {
|
||||
w.Header().Set("Content-Type", "text/csv")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=leads.csv")
|
||||
|
||||
writer := csv.NewWriter(w)
|
||||
defer writer.Flush()
|
||||
|
||||
// Header
|
||||
header := []string{"ID", "Nome", "Email", "Telefone", "Status", "Origem", "Notas", "Tags", "Criado Em"}
|
||||
writer.Write(header)
|
||||
|
||||
// Data
|
||||
for _, lead := range leads {
|
||||
tags := ""
|
||||
if len(lead.Tags) > 0 {
|
||||
tags = strings.Join(lead.Tags, ", ")
|
||||
}
|
||||
|
||||
phone := ""
|
||||
if lead.Phone != "" {
|
||||
phone = lead.Phone
|
||||
}
|
||||
|
||||
notes := ""
|
||||
if lead.Notes != "" {
|
||||
notes = lead.Notes
|
||||
}
|
||||
|
||||
row := []string{
|
||||
lead.ID,
|
||||
lead.Name,
|
||||
lead.Email,
|
||||
phone,
|
||||
lead.Status,
|
||||
lead.Source,
|
||||
notes,
|
||||
tags,
|
||||
lead.CreatedAt.Format("02/01/2006 15:04"),
|
||||
}
|
||||
writer.Write(row)
|
||||
}
|
||||
}
|
||||
|
||||
func exportXLSX(w http.ResponseWriter, leads []domain.CRMLead) {
|
||||
f := excelize.NewFile()
|
||||
defer f.Close()
|
||||
|
||||
sheetName := "Leads"
|
||||
index, err := f.NewSheet(sheetName)
|
||||
if err != nil {
|
||||
log.Printf("Error creating sheet: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Set active sheet
|
||||
f.SetActiveSheet(index)
|
||||
|
||||
// Header style
|
||||
headerStyle, _ := f.NewStyle(&excelize.Style{
|
||||
Font: &excelize.Font{
|
||||
Bold: true,
|
||||
Size: 12,
|
||||
},
|
||||
Fill: excelize.Fill{
|
||||
Type: "pattern",
|
||||
Color: []string{"#4472C4"},
|
||||
Pattern: 1,
|
||||
},
|
||||
Alignment: &excelize.Alignment{
|
||||
Horizontal: "center",
|
||||
Vertical: "center",
|
||||
},
|
||||
})
|
||||
|
||||
// Headers
|
||||
headers := []string{"ID", "Nome", "Email", "Telefone", "Status", "Origem", "Notas", "Tags", "Criado Em"}
|
||||
for i, header := range headers {
|
||||
cell := fmt.Sprintf("%s1", string(rune('A'+i)))
|
||||
f.SetCellValue(sheetName, cell, header)
|
||||
f.SetCellStyle(sheetName, cell, cell, headerStyle)
|
||||
}
|
||||
|
||||
// Data
|
||||
for i, lead := range leads {
|
||||
row := i + 2
|
||||
|
||||
tags := ""
|
||||
if len(lead.Tags) > 0 {
|
||||
tags = strings.Join(lead.Tags, ", ")
|
||||
}
|
||||
|
||||
phone := ""
|
||||
if lead.Phone != "" {
|
||||
phone = lead.Phone
|
||||
}
|
||||
|
||||
notes := ""
|
||||
if lead.Notes != "" {
|
||||
notes = lead.Notes
|
||||
}
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("A%d", row), lead.ID)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("B%d", row), lead.Name)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("C%d", row), lead.Email)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("D%d", row), phone)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("E%d", row), lead.Status)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("F%d", row), lead.Source)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("G%d", row), notes)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("H%d", row), tags)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("I%d", row), lead.CreatedAt.Format("02/01/2006 15:04"))
|
||||
}
|
||||
|
||||
// Auto-adjust column widths
|
||||
for i := 0; i < len(headers); i++ {
|
||||
col := string(rune('A' + i))
|
||||
f.SetColWidth(sheetName, col, col, 15)
|
||||
}
|
||||
f.SetColWidth(sheetName, "B", "B", 25) // Nome
|
||||
f.SetColWidth(sheetName, "C", "C", 30) // Email
|
||||
f.SetColWidth(sheetName, "G", "G", 40) // Notas
|
||||
|
||||
// Delete default sheet if exists
|
||||
f.DeleteSheet("Sheet1")
|
||||
|
||||
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=leads.xlsx")
|
||||
|
||||
if err := f.Write(w); err != nil {
|
||||
log.Printf("Error writing xlsx: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user