import Link from "next/link" import { fetchQuery } from "convex/nextjs" import { MessageSquare, Search } from "lucide-react" import { api } from "@/convex/_generated/api" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card" import { Input } from "@/components/ui/input" type PageProps = { searchParams: Promise<{ search?: string channel?: "call" | "sms" | "chat" | "unknown" status?: "open" | "closed" | "archived" page?: string }> } function formatTimestamp(value?: number) { if (!value) { return "—" } return new Date(value).toLocaleString("en-US", { month: "short", day: "numeric", year: "numeric", hour: "2-digit", minute: "2-digit", }) } function getSyncMessage(sync: any) { if (!sync.ghlConfigured) { return "Connect GHL to load contacts and conversations." } if (sync.stages.conversations.status === "running") { return "Conversations are syncing now." } if (sync.stages.conversations.error) { return "Conversations could not be loaded from GHL yet." } if (!sync.latestSyncAt) { return "No conversations yet." } return "Calls and messages appear here as they are synced." } export default async function AdminConversationsPage({ searchParams, }: PageProps) { const params = await searchParams const page = Math.max(1, Number.parseInt(params.page || "1", 10) || 1) const search = params.search?.trim() || undefined const data = await fetchQuery(api.crm.listAdminConversations, { search, page, limit: 25, channel: params.channel, status: params.status, }) return (

Conversations

Customer conversations in one inbox.

Sync Status {getSyncMessage(data.sync)} {data.sync.overallStatus} Last sync: {formatTimestamp(data.sync.latestSyncAt || undefined)} {!data.sync.ghlConfigured ? ( GHL is not connected. ) : null} {data.sync.stages.conversations.error ? ( {data.sync.stages.conversations.error} ) : null} Conversation Inbox Search by contact, phone, email, or recent message.
{data.items.length === 0 ? ( ) : ( data.items.map((conversation: any) => ( )) )}
Conversation Contact Channel Status Messages Recordings Last Activity Open
{search || params.channel || params.status ? "No conversations matched this search." : getSyncMessage(data.sync)}
{conversation.title || "Untitled conversation"}
{conversation.lastMessagePreview || "No preview yet"}
{conversation.contact ? (
{conversation.contact.name}
{conversation.contact.phone || conversation.contact.email || "—"}
) : ( "—" )}
{conversation.channel} {conversation.status} {conversation.messageCount} {conversation.recordingCount} {formatTimestamp(conversation.lastMessageAt)}
) } export const metadata = { title: "Conversations | Admin", description: "View Rocky customer conversations", }