package handlers import ( "log" "net/http" "strconv" "whatsapp-bot/db" "whatsapp-bot/services" "github.com/gin-gonic/gin" ) // ShowDashboard renders the Chat Interface (Discord View) // NOTE: The main SaaS Dashboard is now at /dashboard (UserDashboard in saas.go). // You might want to rename this route to /chat-interface or keep it as a sub-view. func ShowDashboard(c *gin.Context) { userID := c.MustGet("userID").(int) // 1. Fetch Appointments (Only for this user) type Appt struct { ID int CustomerPhone string Date string Status string } var appts []Appt approws, err := db.Conn.Query("SELECT id, customer_phone, appointment_time, status FROM appointments WHERE user_id = ? ORDER BY id DESC", userID) 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 (Only for this user) // We need to add user_id to chats table to make this strict, // but for now, we'll assume all chats are visible to the admin/user // or filter if you add user_id to the chats table later. type Chat struct { ID int Title string } var chats []Chat chatrows, err := db.Conn.Query("SELECT id, title 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, &ch.Title) if ch.Title == "" { ch.Title = "Chat #" + strconv.Itoa(ch.ID) } chats = append(chats, ch) } } // 3. Render the Chat Template // Note: We are using "dashboard.html" for the SaaS view now. // You might want to rename your old chat template to "chat_view.html" // if you want to keep both views separate. // For now, I'll point this to the new SaaS dashboard template to avoid errors, // but realistically you should merge them or have two separate HTML files. c.HTML(http.StatusOK, "dashboard.html", gin.H{ "Appointments": appts, "Chats": chats, // Pass minimal context so the template doesn't crash if it expects user info "UserEmail": "Chat Mode", "Tier": "Pro", "BotConfig": map[string]string{"PhoneID": "N/A"}, }) } // POST /admin/appointment func CreateAppointmentHandler(c *gin.Context) { userID := c.MustGet("userID").(int) var body struct { Phone string `json:"phone"` Date string `json:"date"` } if err := c.BindJSON(&body); err != nil { c.Status(400) return } if err := db.SaveAppointment(userID, body.Phone, body.Date); err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.Status(200) } // DELETE /admin/appointment/:id func DeleteAppointmentHandler(c *gin.Context) { id := c.Param("id") userID := c.MustGet("userID").(int) // Ensure user only deletes their own appointments _, err := db.Conn.Exec("DELETE FROM appointments WHERE id = ? AND user_id = ?", id, userID) 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") userID := c.MustGet("userID").(int) 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_time = ? WHERE id = ? AND user_id = ?", body.Phone, body.Date, id, userID, ) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.Status(200) } // POST /admin/chat func NewChatHandler(c *gin.Context) { // You might want to associate chats with users too: INSERT INTO chats (user_id, title)... 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() c.JSON(200, gin.H{"id": id, "title": "New Chat"}) } // DELETE /admin/chat/:id func DeleteChatHandler(c *gin.Context) { id := c.Param("id") 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") userID := c.MustGet("userID").(int) var body struct { Content string `json:"content"` } if err := c.BindJSON(&body); err != nil { c.Status(400) return } // 1. Save User Message 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. Load User's Custom System Prompt for the AI // We need the AI to behave like the User's bot, even in the chat interface var systemPrompt string err := db.Conn.QueryRow("SELECT system_prompt FROM bot_configs WHERE user_id = ?", userID).Scan(&systemPrompt) if err != nil { systemPrompt = "You are a helpful assistant." // Fallback } // 4. Stream Response 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") // We use the new StreamAIResponse signature that takes (userID, message, prompt, callback) fullResponse, _ := services.StreamAIResponse(userID, body.Content, systemPrompt, func(chunk string) { c.Writer.Write([]byte(chunk)) c.Writer.Flush() }) // 5. Save Assistant Message db.Conn.Exec("INSERT INTO messages (chat_id, role, content) VALUES (?, 'assistant', ?)", chatId, fullResponse) }