modified: handlers/dashboard.go modified: main.go modified: services/openrouter.go modified: templates/dashboard.html
187 lines
4.6 KiB
Go
187 lines
4.6 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) {
|
|
// Insert a new chat record. In SQLite, this is enough to generate an ID.
|
|
res, err := db.Conn.Exec("INSERT INTO chats (title) VALUES ('New Chat')")
|
|
if err != nil {
|
|
log.Println("Database Error in NewChat:", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create chat"})
|
|
return
|
|
}
|
|
|
|
id, _ := res.LastInsertId()
|
|
c.JSON(http.StatusOK, gin.H{"id": id})
|
|
}
|
|
|
|
// 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)
|
|
}
|