Files
whatsapp-bot/handlers/dashboard.go
SekiDesu0 e47b12b0d9 modified: bot.db
modified:   handlers/dashboard.go
	modified:   main.go
	modified:   services/openrouter.go
	modified:   templates/dashboard.html
2026-03-01 07:39:40 -03:00

208 lines
5.2 KiB
Go

package handlers
import (
"log"
"net/http"
"strconv"
"whatsapp-bot/db"
"whatsapp-bot/services"
"github.com/gin-gonic/gin"
)
func ShowDashboard(c *gin.Context) {
// 1. Fetch Appointments for the Table
type Appt struct {
ID int
CustomerPhone string
Date string
Status string
}
var appts []Appt
approws, err := db.Conn.Query("SELECT id, customer_phone, appointment_date, status FROM appointments ORDER BY id DESC")
if err != nil {
log.Println("Appt Query Error:", err)
} else {
defer approws.Close()
for approws.Next() {
var a Appt
approws.Scan(&a.ID, &a.CustomerPhone, &a.Date, &a.Status)
appts = append(appts, a)
}
}
// 2. Fetch Chats for the Sidebar
type Chat struct {
ID int
Title string
}
var chats []Chat
chatrows, err := db.Conn.Query("SELECT id FROM chats ORDER BY id DESC")
if err != nil {
log.Println("Chat Query Error:", err)
} else {
defer chatrows.Close()
for chatrows.Next() {
var ch Chat
chatrows.Scan(&ch.ID)
// Give it a default title so the sidebar isn't empty
ch.Title = "Chat #" + strconv.Itoa(ch.ID)
chats = append(chats, ch)
}
}
// 3. Render the Template with BOTH data sets
c.HTML(http.StatusOK, "dashboard.html", gin.H{
"Appointments": appts,
"Chats": chats,
})
}
// Add this to handlers/dashboard.go
func CreateAppointmentHandler(c *gin.Context) {
var body struct {
Phone string `json:"phone"`
Date string `json:"date"`
}
if err := c.BindJSON(&body); err != nil {
c.Status(400)
return
}
// Use the helper function instead of raw SQL here
if err := db.SaveAppointment(body.Phone, body.Date); err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.Status(200)
}
// Manage/Delete Appointment
func DeleteAppointmentHandler(c *gin.Context) {
id := c.Param("id")
_, err := db.Conn.Exec("DELETE FROM appointments WHERE id = ?", id)
if err != nil {
c.Status(http.StatusInternalServerError)
return
}
c.Status(http.StatusOK)
}
// PUT /admin/appointment/:id
func UpdateAppointmentHandler(c *gin.Context) {
id := c.Param("id")
var body struct {
Phone string `json:"phone"`
Date string `json:"date"`
}
if err := c.BindJSON(&body); err != nil {
c.Status(400)
return
}
_, err := db.Conn.Exec(
"UPDATE appointments SET customer_phone = ?, appointment_date = ? WHERE id = ?",
body.Phone, body.Date, id,
)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.Status(200)
}
// POST /admin/chat
func NewChatHandler(c *gin.Context) {
res, err := db.Conn.Exec("INSERT INTO chats (title) VALUES ('New Chat')")
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
id, _ := res.LastInsertId()
// Return the ID so the frontend can select it immediately
c.JSON(200, gin.H{"id": id, "title": "New Chat"})
}
// DELETE /admin/chat/:id
func DeleteChatHandler(c *gin.Context) {
id := c.Param("id")
// Delete messages first (foreign key cleanup usually, but we'll do manual for SQLite safety)
db.Conn.Exec("DELETE FROM messages WHERE chat_id = ?", id)
db.Conn.Exec("DELETE FROM chats WHERE id = ?", id)
c.Status(200)
}
// PUT /admin/chat/:id/rename
func RenameChatHandler(c *gin.Context) {
id := c.Param("id")
var body struct {
Title string `json:"title"`
}
if err := c.BindJSON(&body); err != nil {
c.Status(400)
return
}
db.Conn.Exec("UPDATE chats SET title = ? WHERE id = ?", body.Title, id)
c.Status(200)
}
// GET /admin/chat/:id/messages
func GetMessagesHandler(c *gin.Context) {
id := c.Param("id")
rows, _ := db.Conn.Query("SELECT role, content FROM messages WHERE chat_id = ? ORDER BY created_at ASC", id)
defer rows.Close()
var msgs []services.Message
for rows.Next() {
var m services.Message
rows.Scan(&m.Role, &m.Content)
msgs = append(msgs, m)
}
c.JSON(200, msgs)
}
// POST /admin/chat/:id/message
func PostMessageHandler(c *gin.Context) {
chatId := c.Param("id")
var body struct {
Content string `json:"content"`
}
if err := c.BindJSON(&body); err != nil {
c.Status(400)
return
}
// 1. Save User Message to DB first
db.Conn.Exec("INSERT INTO messages (chat_id, role, content) VALUES (?, 'user', ?)", chatId, body.Content)
// 2. Fetch history
rows, _ := db.Conn.Query("SELECT role, content FROM messages WHERE chat_id = ? ORDER BY created_at ASC", chatId)
var history []services.Message
defer rows.Close()
for rows.Next() {
var m services.Message
rows.Scan(&m.Role, &m.Content)
history = append(history, m)
}
// 3. Set Headers for Streaming
c.Writer.Header().Set("Content-Type", "text/event-stream")
c.Writer.Header().Set("Cache-Control", "no-cache")
c.Writer.Header().Set("Connection", "keep-alive")
c.Writer.Header().Set("Transfer-Encoding", "chunked")
// 4. Call the Stream Service
// We pass a function that writes chunks directly to the HTTP response
fullResponse, _ := services.StreamAIResponse(history, func(chunk string) {
c.Writer.Write([]byte(chunk))
c.Writer.Flush() // Important: Send it NOW, don't buffer
})
// 5. Save the FULL Assistant Message to DB for history
// We do this AFTER the stream finishes so the next load has the full text
db.Conn.Exec("INSERT INTO messages (chat_id, role, content) VALUES (?, 'assistant', ?)", chatId, fullResponse)
}