feat(19-02): create MessageCard component for agent inbox

MessageCard displays agent name with status, message preview (truncated
to 80 chars), relative timestamp, and response indicator (filled/empty
circle). Uses shadcn Card base with selected state highlighting.
This commit is contained in:
Lukas May
2026-02-04 21:51:24 +01:00
parent d9b6ce4748
commit 6450e4072a

View File

@@ -0,0 +1,83 @@
import { Card } from "@/components/ui/card";
import { cn } from "@/lib/utils";
function formatRelativeTime(isoDate: string): string {
const now = Date.now();
const then = new Date(isoDate).getTime();
const diffMs = now - then;
const diffSec = Math.floor(diffMs / 1000);
const diffMin = Math.floor(diffSec / 60);
const diffHr = Math.floor(diffMin / 60);
const diffDay = Math.floor(diffHr / 24);
if (diffSec < 60) return "just now";
if (diffMin < 60) return `${diffMin} min ago`;
if (diffHr < 24) return `${diffHr}h ago`;
return `${diffDay}d ago`;
}
interface MessageCardProps {
agentName: string;
agentStatus: string;
preview: string;
timestamp: string;
requiresResponse: boolean;
isSelected: boolean;
onClick: () => void;
}
function formatStatusLabel(status: string): string {
return status.replace(/_/g, " ");
}
function truncatePreview(text: string, maxLength = 80): string {
if (text.length <= maxLength) return text;
return text.slice(0, maxLength) + "...";
}
export function MessageCard({
agentName,
agentStatus,
preview,
timestamp,
requiresResponse,
isSelected,
onClick,
}: MessageCardProps) {
return (
<Card
className={cn(
"cursor-pointer p-4 transition-colors hover:bg-accent/50",
isSelected && "bg-accent",
)}
onClick={onClick}
>
<div className="flex items-start justify-between gap-4">
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span
className={cn(
"text-sm",
requiresResponse ? "text-orange-500" : "text-muted-foreground",
)}
>
{requiresResponse ? "\u25CF" : "\u25CB"}
</span>
<span className="text-sm font-bold">
{agentName}{" "}
<span className="font-normal text-muted-foreground">
({formatStatusLabel(agentStatus)})
</span>
</span>
</div>
<p className="mt-1 pl-5 text-sm text-muted-foreground">
&ldquo;{truncatePreview(preview)}&rdquo;
</p>
</div>
<span className="shrink-0 text-xs text-muted-foreground">
{formatRelativeTime(timestamp)}
</span>
</div>
</Card>
);
}