package handlers import ( "context" "encoding/json" "log" "net/http" "strconv" "time" "go-crm/internal/db" "go-crm/internal/whatsapp" "github.com/go-chi/chi/v5" ) func ListLeads(w http.ResponseWriter, r *http.Request) { accountID, ok := requireAuth(w, r) if !ok { return } search := r.URL.Query().Get("search") limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) offset, _ := strconv.Atoi(r.URL.Query().Get("offset")) if limit == 0 { limit = 20 } query := "SELECT customer_id, client_id, name, phone, birth_date, instagram, created_at FROM customers WHERE client_id IN (SELECT client_id FROM clients WHERE account_id = ?)" args := []interface{}{accountID} if search != "" { query += " AND (name LIKE ? OR phone LIKE ?)" searchPat := "%" + search + "%" args = append(args, searchPat, searchPat) } query += " ORDER BY created_at DESC LIMIT ? OFFSET ?" args = append(args, limit, offset) rows, err := DB.Query(query, args...) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer rows.Close() var customers []db.Customer for rows.Next() { var c db.Customer if err := rows.Scan(&c.CustomerID, &c.ClientID, &c.Name, &c.Phone, &c.BirthDate, &c.Instagram, &c.CreatedAt); err != nil { continue } customers = append(customers, c) } w.Header().Set("Content-Type", "text/html") w.Write([]byte(`
| Name | Phone | Birth Date | Actions | |
|---|---|---|---|---|
| ` + c.Name + ` | ` + c.Phone + ` | ` + c.BirthDate + ` | ` + c.Instagram + ` | |
Scan the QR code below with your WhatsApp app to connect
Loading...
`)) } func jsonEscape(s string) string { b, _ := json.Marshal(s) return string(b) } func LeadsQR(w http.ResponseWriter, r *http.Request) { accountID, ok := requireAuth(w, r) if !ok { return } clientID, _ := strconv.ParseInt(r.URL.Query().Get("client_id"), 10, 64) client, err := db.GetClientByID(DB, accountID, clientID) if err != nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) w.Write([]byte(`{"status":"error","error":"client not found"}`)) return } if WAConnector == nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusServiceUnavailable) w.Write([]byte(`{"status":"error","error":"WhatsApp not configured - contact admin"}`)) return } connected, err := WAConnector.IsConnected(r.Context(), clientID) if err != nil { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"client_id":` + strconv.FormatInt(client.ClientID, 10) + `,"status":"error","error":` + jsonEscape(err.Error()) + `}`)) return } if connected { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"client_id":` + strconv.FormatInt(client.ClientID, 10) + `,"status":"ready"}`)) return } w.Header().Set("Content-Type", "application/json") log.Printf("QR: Starting Connect for client %d", clientID) // Use context.Background() so the WA session goroutine outlives this HTTP request. // The handler returns after the first QR frame; subsequent polls reuse the same session. qrChan, err := WAConnector.Connect(context.Background(), clientID) if err != nil { log.Printf("QR: Connect returned error for client %d: %v", clientID, err) w.Write([]byte(`{"client_id":` + strconv.FormatInt(client.ClientID, 10) + `,"status":"error","error":` + jsonEscape(err.Error()) + `}`)) return } timeout := time.After(30 * time.Second) for { select { case <-timeout: log.Printf("QR: Timeout waiting for client %d", clientID) w.Write([]byte(`{"client_id":` + strconv.FormatInt(client.ClientID, 10) + `,"status":"error","error":` + jsonEscape("timeout waiting for QR code") + `}`)) return case frame, ok := <-qrChan: if !ok { w.Write([]byte(`{"client_id":` + strconv.FormatInt(client.ClientID, 10) + `,"status":"error","error":"connection closed"}`)) return } if frame.QR != "" { w.Write([]byte(`{"client_id":` + strconv.FormatInt(client.ClientID, 10) + `,"qr":` + jsonEscape(frame.QR) + `,"status":"waiting"}`)) return } if frame.State == whatsapp.StateConnected { w.Write([]byte(`{"client_id":` + strconv.FormatInt(client.ClientID, 10) + `,"status":"ready"}`)) return } if frame.State == whatsapp.StateFailed { w.Write([]byte(`{"client_id":` + strconv.FormatInt(client.ClientID, 10) + `,"status":"error","error":` + jsonEscape(frame.Error) + `}`)) return } } } }