File size: 3,680 Bytes
d125a03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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>
  );
}