Spaces:
Sleeping
Sleeping
| import { useEffect, useRef } from "react"; | |
| import { ScrollArea } from "@/components/ui/scroll-area"; | |
| import { Bot, User } from "lucide-react"; | |
| import { cn } from "@/lib/utils"; | |
| import type { TranscriptMessage } from "@shared/schema"; | |
| interface LiveTranscriptProps { | |
| messages: TranscriptMessage[]; | |
| className?: string; | |
| } | |
| export function LiveTranscript({ messages, className }: LiveTranscriptProps) { | |
| const scrollRef = useRef<HTMLDivElement>(null); | |
| useEffect(() => { | |
| if (scrollRef.current) { | |
| scrollRef.current.scrollTop = scrollRef.current.scrollHeight; | |
| } | |
| }, [messages]); | |
| const formatTime = (date: Date | string): string => { | |
| const d = new Date(date); | |
| return d.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit" }); | |
| }; | |
| return ( | |
| <div className={cn("flex flex-col h-full", className)}> | |
| <div className="flex items-center justify-between px-4 py-3 border-b border-border"> | |
| <h3 className="text-sm font-semibold">Live Transcript</h3> | |
| <span className="text-xs text-muted-foreground">{messages.length} messages</span> | |
| </div> | |
| <ScrollArea className="flex-1 px-4" ref={scrollRef}> | |
| <div className="space-y-3 py-4" role="log" aria-live="polite" aria-label="Live call transcript"> | |
| {messages.length === 0 && ( | |
| <div className="text-center py-8 text-sm text-muted-foreground"> | |
| Waiting for conversation to start... | |
| </div> | |
| )} | |
| {messages.map((message) => ( | |
| <div | |
| key={message.id} | |
| className={cn( | |
| "flex gap-3", | |
| message.speaker === "ai" ? "flex-row" : "flex-row-reverse" | |
| )} | |
| data-testid={`transcript-message-${message.id}`} | |
| > | |
| <div | |
| className={cn( | |
| "flex h-8 w-8 shrink-0 items-center justify-center rounded-full", | |
| message.speaker === "ai" | |
| ? "bg-primary/10" | |
| : "bg-emerald-500/10" | |
| )} | |
| > | |
| {message.speaker === "ai" ? ( | |
| <Bot className="h-4 w-4 text-primary" aria-hidden="true" /> | |
| ) : ( | |
| <User className="h-4 w-4 text-emerald-600 dark:text-emerald-400" aria-hidden="true" /> | |
| )} | |
| </div> | |
| <div | |
| className={cn( | |
| "flex-1 max-w-[80%]", | |
| message.speaker === "ai" ? "text-left" : "text-right" | |
| )} | |
| > | |
| <div className="flex items-center gap-2 mb-1"> | |
| <span className={cn( | |
| "text-xs font-medium", | |
| message.speaker === "ai" ? "text-primary" : "text-emerald-600 dark:text-emerald-400" | |
| )}> | |
| {message.speaker === "ai" ? "AI Agent" : "Customer"} | |
| </span> | |
| <span className="text-xs text-muted-foreground font-mono"> | |
| {formatTime(message.timestamp)} | |
| </span> | |
| </div> | |
| <div | |
| className={cn( | |
| "p-3 rounded-lg text-sm leading-relaxed", | |
| message.speaker === "ai" | |
| ? "bg-primary/5 border border-primary/10" | |
| : "bg-emerald-500/5 border border-emerald-500/10" | |
| )} | |
| > | |
| {message.content} | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </ScrollArea> | |
| </div> | |
| ); | |
| } | |