"use client"; import { useState, useCallback, useEffect } from "react"; import { createClient } from "@/lib/supabase/client"; import type { Conversation, Message, Contact, ConversationStatus } from "@/types"; import { useRealtime } from "@/hooks/use-realtime"; import { ConversationList } from "@/components/inbox/conversation-list"; import { MessageThread } from "@/components/inbox/message-thread"; import { ContactSidebar } from "@/components/inbox/contact-sidebar"; import { toast } from "sonner"; import { WifiOff } from "lucide-react"; export default function InboxPage() { const [conversations, setConversations] = useState([]); const [activeConversation, setActiveConversation] = useState(null); const [activeContact, setActiveContact] = useState(null); const [messages, setMessages] = useState([]); const [whatsappConnected, setWhatsappConnected] = useState( null ); // Check WhatsApp connection status on mount useEffect(() => { const checkConnection = async () => { const supabase = createClient(); const { data: { session }, } = await supabase.auth.getSession(); const user = session?.user; if (!user) return; // Table is `whatsapp_config` (singular) — the previous "whatsapp_configs" // query always returned no rows, so the banner always showed "not connected". const { data } = await supabase .from("whatsapp_config") .select("status") .eq("user_id", user.id) .maybeSingle(); setWhatsappConnected(data?.status === "connected"); }; checkConnection(); }, []); // Handle realtime message events const handleMessageEvent = useCallback( (event: { eventType: string; new: Message; old: Partial }) => { const newMsg = event.new; if (event.eventType === "INSERT") { // Add to messages if it belongs to active conversation if ( activeConversation && newMsg.conversation_id === activeConversation.id ) { setMessages((prev) => { // Avoid duplicates if (prev.some((m) => m.id === newMsg.id)) return prev; // Replace optimistic message if it exists const withoutOptimistic = prev.filter( (m) => !m.id.startsWith("temp-") ); return [...withoutOptimistic, newMsg]; }); } // Update conversation list preview setConversations((prev) => prev.map((c) => c.id === newMsg.conversation_id ? { ...c, last_message_text: newMsg.content_text ?? "", last_message_at: newMsg.created_at, unread_count: activeConversation?.id === newMsg.conversation_id ? 0 : c.unread_count + 1, } : c ) ); } if (event.eventType === "UPDATE") { // Update message status setMessages((prev) => prev.map((m) => (m.id === newMsg.id ? { ...m, ...newMsg } : m)) ); } }, [activeConversation] ); // Handle realtime conversation events const handleConversationEvent = useCallback( (event: { eventType: string; new: Conversation; old: Partial; }) => { const conv = event.new; if (event.eventType === "INSERT") { setConversations((prev) => [conv, ...prev]); } if (event.eventType === "UPDATE") { setConversations((prev) => prev.map((c) => (c.id === conv.id ? { ...c, ...conv } : c)) ); // Update active conversation if it changed if (activeConversation && conv.id === activeConversation.id) { setActiveConversation((prev) => prev ? { ...prev, ...conv } : prev ); } } }, [activeConversation] ); // Subscribe to realtime useRealtime({ channelName: "inbox-realtime", onMessageEvent: handleMessageEvent, onConversationEvent: handleConversationEvent, enabled: true, }); const handleConversationsLoaded = useCallback( (loaded: Conversation[]) => { setConversations(loaded); }, [] ); const handleSelectConversation = useCallback((conv: Conversation) => { setActiveConversation(conv); setActiveContact(conv.contact ?? null); setMessages([]); }, []); const handleMessagesLoaded = useCallback((loaded: Message[]) => { setMessages(loaded); }, []); const handleNewMessage = useCallback((msg: Message) => { setMessages((prev) => { if (prev.some((m) => m.id === msg.id)) return prev; return [...prev, msg]; }); }, []); const handleUpdateMessage = useCallback( (id: string, updates: Partial) => { setMessages((prev) => prev.map((m) => (m.id === id ? { ...m, ...updates } : m)) ); }, [] ); const handleStatusChange = useCallback( (conversationId: string, status: ConversationStatus) => { setConversations((prev) => prev.map((c) => (c.id === conversationId ? { ...c, status } : c)) ); if (activeConversation?.id === conversationId) { setActiveConversation((prev) => (prev ? { ...prev, status } : prev)); } }, [activeConversation] ); return (
{/* WhatsApp connection banner — in the flex column, not absolute, so it pushes the panels down instead of overlapping them. */} {whatsappConnected === false && (

WhatsApp is not connected. Go to Settings to connect your account.

)}
{/* Left panel: Conversation list */} {/* Center panel: Message thread */} {/* Right panel: Contact sidebar (hidden on small screens) */}
); }