package handlers import ( "fmt" "net/http" "strconv" "time" "go-crm/internal/db" "go-crm/internal/templates" "github.com/go-chi/chi/v5" ) func (a *App) ListClients(w http.ResponseWriter, r *http.Request) { accountID, ok := a.requireAuth(w, r) if !ok { return } limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) offset, _ := strconv.Atoi(r.URL.Query().Get("offset")) if limit == 0 { limit = 20 } clients, err := db.ListClients(a.DB, accountID, limit, offset) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } buf := templates.BufRender() actions := `` fmt.Fprint(buf, templates.PageHeader("Clients", "Manage your business clients", actions)) // Add form fmt.Fprintf(buf, ` `) if len(clients) == 0 { fmt.Fprint(buf, templates.EmptyState(``, "No clients found. Add your first client to get started.")) } else { fmt.Fprintf(buf, `
`) fmt.Fprint(buf, templates.TableStart([]string{"Name", "Phone", "WhatsApp", "Created", "Actions"})) for _, c := range clients { var whatsappCell string if c.WhatsAppNumber == "" { whatsappCell = fmt.Sprintf(`Connect`, c.ClientID) } else { connected := false if a.WAConnector != nil { connected, _ = a.WAConnector.IsConnected(r.Context(), c.ClientID) } if connected { whatsappCell = fmt.Sprintf(`%s`, htmlEscape(c.WhatsAppNumber)) } else if c.WhatsAppConnected == 1 { whatsappCell = fmt.Sprintf(`%sreconnect`, htmlEscape(c.WhatsAppNumber), c.ClientID) } else { whatsappCell = fmt.Sprintf(`Connect`, c.ClientID) } } created := time.Unix(c.CreatedAt, 0).Format("02/01/2006") fmt.Fprintf(buf, ` %s %s %s %s
View
`, htmlEscape(c.Name), htmlEscape(c.Phone), whatsappCell, created, c.ClientID, c.ClientID, c.ClientID) } fmt.Fprint(buf, templates.TableEnd()) fmt.Fprintf(buf, `
`) // Edit forms (rendered below table, toggled via JS) for _, c := range clients { fmt.Fprintf(buf, ` `, c.ClientID, htmlEscape(c.Name), c.ClientID, htmlEscape(c.Name), htmlEscape(c.Phone), htmlEscape(c.Email), htmlEscape(c.Address), htmlEscape(c.Notes), c.ClientID) } } w.Header().Set("Content-Type", "text/html; charset=utf-8") templates.WritePage(w, buf, "Clients", "clients") } func (a *App) CreateClient(w http.ResponseWriter, r *http.Request) { accountID, ok := a.requireAuth(w, r) if !ok { return } r.ParseForm() client := db.Client{ AccountID: accountID, Name: r.FormValue("name"), Phone: r.FormValue("phone"), Email: r.FormValue("email"), Address: r.FormValue("address"), Notes: r.FormValue("notes"), CreatedAt: time.Now().Unix(), } if err := client.Create(a.DB); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("HX-Refresh", "true") } func (a *App) ViewClient(w http.ResponseWriter, r *http.Request) { accountID, ok := a.requireAuth(w, r) if !ok { return } id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) client, err := db.GetClientByID(a.DB, accountID, id) if err != nil { http.Error(w, "Client not found", http.StatusNotFound) return } buf := templates.BufRender() fmt.Fprint(buf, templates.PageHeader(htmlEscape(client.Name), "Client details and WhatsApp connection")) waSection := "" if client.WhatsAppNumber != "" { connected := false if a.WAConnector != nil { connected, _ = a.WAConnector.IsConnected(r.Context(), client.ClientID) } statusPill := `Disconnected` if connected { statusPill = `Connected` } else if client.WhatsAppConnected == 1 { statusPill = `Stale` } waSection = fmt.Sprintf(`

WhatsApp

%s
%s
%s
`, statusPill, htmlEscape(client.WhatsAppNumber), client.ClientID, map[bool]string{true: "Reconnect", false: "Reconnect"}[true]) } else { waSection = fmt.Sprintf(`

WhatsApp

No WhatsApp number configured for this client.

Connect WhatsApp
`, client.ClientID) } fmt.Fprintf(buf, `

Details

Phone %s
Email %s
Address %s
Notes %s
%s
Back to Clients
`, htmlEscape(client.Phone), htmlEscape(client.Email), htmlEscape(client.Address), htmlEscape(client.Notes), waSection) w.Header().Set("Content-Type", "text/html; charset=utf-8") templates.WritePage(w, buf, client.Name, "clients") } func (a *App) UpdateClient(w http.ResponseWriter, r *http.Request) { accountID, ok := a.requireAuth(w, r) if !ok { return } id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) r.ParseForm() client := db.Client{ ClientID: id, AccountID: accountID, Name: r.FormValue("name"), Phone: r.FormValue("phone"), Email: r.FormValue("email"), Address: r.FormValue("address"), Notes: r.FormValue("notes"), } if err := client.Update(a.DB); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("HX-Refresh", "true") } func (a *App) DeleteClient(w http.ResponseWriter, r *http.Request) { accountID, ok := a.requireAuth(w, r) if !ok { return } id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) client := &db.Client{ClientID: id, AccountID: accountID} if err := client.Delete(a.DB); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Write([]byte("")) } // --- package-level shims kept for existing tests --- func ListClients(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).ListClients(w, r) } func CreateClient(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).CreateClient(w, r) } func ViewClient(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).ViewClient(w, r) } func UpdateClient(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).UpdateClient(w, r) } func DeleteClient(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).DeleteClient(w, r) }