import { memo } from "react" import { motion } from "framer-motion" import { ChatCircle, At, Circle, WarningCircle, ArrowBendUpLeft, UserCircle, } from "../ui/icons" import type { AgentMessageRecord, AgentRecord, CommentRecord, DepartmentId } from "@shared/types" import { getAgentInitials, formatMessageTime } from "../../lib/formatters" import { safeJson } from "../../lib/safe-json" import { DEPARTMENT_COLORS } from "@shared/constants" import { useT, type TranslationFn } from "../../i18n" const HUMAN_SENDER_ID = "__board__"; function priorityConfig(t: TranslationFn): Record { return { urgent: { label: t("msg.priority.urgent"), className: "bg-destructive-muted text-destructive" }, normal: { label: t("msg.priority.normal"), className: "bg-muted text-muted-foreground" }, low: { label: t("msg.priority.low"), className: "bg-muted text-muted-foreground" }, } } function getDepartmentColor(department: DepartmentId | null | undefined): string { if (!department) return "var(--muted-foreground)" return DEPARTMENT_COLORS[department] ?? "var(--muted-foreground)" } export const AgentAvatar = memo(function AgentAvatar({ agent, size = "md", }: { agent: AgentRecord | undefined size?: "sm" | "md" | "lg" }) { const sizeClass = size === "sm" ? "h-7 w-7 text-[10px]" : size === "lg" ? "h-11 w-11 text-[14px]" : "h-9 w-9 text-[11px]" const color = agent ? getDepartmentColor(agent.department) : "var(--muted-foreground)" const initials = agent ? getAgentInitials(agent.name) : "?" return (
{initials}
) }) export const MessageBubble = memo(function MessageBubble({ message, agent, isThread, replyCount, onReply, }: { message: AgentMessageRecord agent: AgentRecord | undefined isThread: boolean replyCount: number onReply: () => void }) { const t = useT() const priorityMap = priorityConfig(t) const isUnread = !message.readAt const isHuman = message.fromAgentId === HUMAN_SENDER_ID const deptColor = agent ? getDepartmentColor(agent.department) : "var(--muted-foreground)" const senderColor = isHuman ? "var(--primary)" : deptColor const senderName = isHuman ? t("msg.you") : (agent?.name ?? t("msg.unknownAgent")) return ( {isHuman ?
: }
{senderName} {agent?.title ? ( {agent.title} ) : null} {formatMessageTime(message.createdAt)} {message.priority !== "normal" ? ( {message.priority === "urgent" ? : null} {priorityMap[message.priority]?.label} ) : null} {isUnread ? ( ) : null}
{message.subject ? (
{message.subject}
) : null}
{message.body}
{message.attachmentsJson && message.attachmentsJson !== "[]" ? ( ) : null}
{replyCount > 0 && !isThread ? ( ) : null}
) }) export function TaskCommentBubble({ comment, agent, }: { comment: CommentRecord agent: AgentRecord | undefined }) { const isBoard = !comment.authorAgentId const deptColor = agent ? getDepartmentColor(agent.department) : "var(--muted-foreground)" return (
{comment.authorName} {agent?.title ? {agent.title} : null} {formatMessageTime(comment.createdAt)}
{comment.body}
) } function AttachmentList({ json }: { json: string }) { const items = safeJson>(json, []) if (!items.length) return null return (
{items.map((item, i) => ( {item.title || `${item.type}:${item.id.slice(0, 8)}`} ))}
) }