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) ListServices(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")) clientID, _ := strconv.ParseInt(r.URL.Query().Get("client_id"), 10, 64) if limit == 0 { limit = 20 } services, err := db.ListServices(a.DB, accountID, clientID, limit, offset) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } clients, err := db.ListClients(a.DB, accountID, limit, offset) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } var clientOptions string for _, c := range clients { clientOptions += fmt.Sprintf(``, c.ClientID, htmlEscape(c.Name)) } buf := templates.BufRender() actions := `` fmt.Fprint(buf, templates.PageHeader("Services", "Your service catalog and pricing", actions)) fmt.Fprintf(buf, ` `, clientOptions) if len(services) == 0 { fmt.Fprint(buf, templates.EmptyState(``, "No services yet. Add your first service to the catalog.")) } else { fmt.Fprintf(buf, `
`) fmt.Fprint(buf, templates.TableStart([]string{"Name", "Price", "Duration", "Actions"})) for _, s := range services { fmt.Fprintf(buf, ` %s R$ %.2f %s
View
`, htmlEscape(s.Name), s.Price, htmlEscape(s.Duration), s.ServiceID, s.ServiceID, s.ServiceID) } fmt.Fprint(buf, templates.TableEnd()) fmt.Fprintf(buf, `
`) for _, s := range services { fmt.Fprintf(buf, ` `, s.ServiceID, htmlEscape(s.Name), s.ServiceID, htmlEscape(s.Name), s.Price, htmlEscape(s.Duration), s.ClientID, htmlEscape(s.Name), clientOptions, htmlEscape(s.Description), s.ServiceID) } } w.Header().Set("Content-Type", "text/html; charset=utf-8") templates.WritePage(w, buf, "Services", "services") } func (a *App) CreateService(w http.ResponseWriter, r *http.Request) { accountID, ok := a.requireAuth(w, r) if !ok { return } r.ParseForm() clientID, _ := strconv.ParseInt(r.FormValue("client_id"), 10, 64) var checkID int64 err := a.DB.QueryRow("SELECT client_id FROM clients WHERE client_id = ? AND account_id = ?", clientID, accountID).Scan(&checkID) if err != nil { http.Error(w, "Invalid client", http.StatusBadRequest) return } price, _ := strconv.ParseFloat(r.FormValue("price"), 64) service := db.Service{ ClientID: clientID, Name: r.FormValue("name"), Price: price, Description: r.FormValue("description"), Duration: r.FormValue("duration"), CreatedAt: time.Now().Unix(), } if err := service.Create(a.DB); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("HX-Refresh", "true") } func (a *App) ViewService(w http.ResponseWriter, r *http.Request) { _, ok := a.requireAuth(w, r) if !ok { return } id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) var s db.Service err := a.DB.QueryRow("SELECT service_id, client_id, name, price, description, duration, created_at FROM services WHERE service_id = ?", id).Scan(&s.ServiceID, &s.ClientID, &s.Name, &s.Price, &s.Description, &s.Duration, &s.CreatedAt) if err != nil { http.Error(w, "Service not found", http.StatusNotFound) return } buf := templates.BufRender() fmt.Fprint(buf, templates.PageHeader(htmlEscape(s.Name), "Service details")) fmt.Fprintf(buf, `
Price R$ %.2f
Duration %s
Description %s
`, s.Price, htmlEscape(s.Duration), htmlEscape(s.Description)) w.Header().Set("Content-Type", "text/html; charset=utf-8") templates.WritePage(w, buf, s.Name, "services") } func (a *App) UpdateService(w http.ResponseWriter, r *http.Request) { _, ok := a.requireAuth(w, r) if !ok { return } id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) r.ParseForm() clientID, _ := strconv.ParseInt(r.FormValue("client_id"), 10, 64) price, _ := strconv.ParseFloat(r.FormValue("price"), 64) _, err := a.DB.Exec("UPDATE services SET client_id=?, name=?, price=?, description=?, duration=? WHERE service_id=?", clientID, r.FormValue("name"), price, r.FormValue("description"), r.FormValue("duration"), id) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("HX-Refresh", "true") } func (a *App) DeleteService(w http.ResponseWriter, r *http.Request) { _, ok := a.requireAuth(w, r) if !ok { return } id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) a.DB.Exec("DELETE FROM services WHERE service_id = ?", id) } // --- package-level shims --- func ListServices(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).ListServices(w, r) } func CreateService(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).CreateService(w, r) } func ViewService(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).ViewService(w, r) } func UpdateService(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).UpdateService(w, r) } func DeleteService(w http.ResponseWriter, r *http.Request) { (&App{DB: DB, WAConnector: WAConnector}).DeleteService(w, r) }