Stragglers from the Communication audit that did not fit into C10's
FilterBar scope:
- ChannelHeader carried two hardcoded English strings: the sidebar
reveal button's aria-label ("Show sidebar") and the "Open task"
link that appears on task-discussion channels. Both now go through
i18n — added `comm.showSidebar` ("显示侧边栏" / "Show sidebar") and
reused the existing `comm.openTask` key. Users running in Chinese
now get a fully Chinese header.
- MessageBubble had two 9px uppercase chips (department badge and
priority badge). 9px with tracking is unreadable at standard display
density; bumped both to 10px and loosened the tracking slightly so
the letters don't collide. Same typography decision we made for
the OrgChart connector label in C3.
Covers the remaining Communication audit items. J-C1 (resizable
sidebar) and J-C6 (resizable ThreadPanel) are feature additions and
stay out of scope per the audit's "bug fix only" contract.
81 lines
2.3 KiB
TypeScript
81 lines
2.3 KiB
TypeScript
import type { AgentMessageRecord, CommentRecord, TaskRecord, MessageChannel } from "@shared/types"
|
|
import { CaretDown } from "../ui/icons"
|
|
import { useT } from "../../i18n"
|
|
|
|
export interface ChannelDescriptor {
|
|
key: string
|
|
kind: "message"
|
|
channel: MessageChannel
|
|
label: string
|
|
targetId: string | null
|
|
agentId: string | null
|
|
participantIds: string[]
|
|
}
|
|
|
|
export interface TaskDiscussionDescriptor {
|
|
key: string
|
|
kind: "task"
|
|
taskId: string
|
|
label: string
|
|
}
|
|
|
|
export type AnyChannel = ChannelDescriptor | TaskDiscussionDescriptor
|
|
|
|
interface ChannelHeaderProps {
|
|
activeChannel: AnyChannel | null
|
|
showSidebar: boolean
|
|
onShowSidebar: () => void
|
|
channelMessages: AgentMessageRecord[]
|
|
taskDiscussionComments: CommentRecord[]
|
|
activeTask: TaskRecord | null
|
|
unreadByChannel: Map<string, { unread: number; hasUrgent: boolean }>
|
|
onNavigateToTask: (taskId: string) => void
|
|
filterBar: React.ReactNode
|
|
}
|
|
|
|
export function ChannelHeader({
|
|
activeChannel,
|
|
showSidebar,
|
|
onShowSidebar,
|
|
channelMessages: _channelMessages,
|
|
taskDiscussionComments: _taskDiscussionComments,
|
|
activeTask,
|
|
unreadByChannel: _unreadByChannel,
|
|
onNavigateToTask,
|
|
filterBar,
|
|
}: ChannelHeaderProps) {
|
|
const t = useT()
|
|
|
|
return (
|
|
<div className="flex items-center gap-3 border-b border-border bg-card px-4 py-2.5">
|
|
{!showSidebar ? (
|
|
<button
|
|
type="button"
|
|
onClick={onShowSidebar}
|
|
className="rounded-[4px] p-1 text-muted-foreground transition hover:text-foreground"
|
|
aria-label={t("comm.showSidebar")}
|
|
>
|
|
<CaretDown size={12} className="rotate-90" />
|
|
</button>
|
|
) : null}
|
|
|
|
{activeChannel ? (
|
|
<div className="min-w-0 flex-1">
|
|
<div className="truncate text-[13px] font-semibold text-foreground">{activeChannel.label}</div>
|
|
{activeChannel.kind === "task" && activeTask ? (
|
|
<button type="button" onClick={() => onNavigateToTask(activeTask.id)} className="mt-0.5 text-[11px] font-medium text-primary transition hover:underline">
|
|
{t("comm.openTask")}
|
|
</button>
|
|
) : null}
|
|
</div>
|
|
) : (
|
|
<span className="text-[13px] text-muted-foreground">{t("comm.selectChannel")}</span>
|
|
)}
|
|
|
|
<div className="ml-auto w-full max-w-[320px]">
|
|
{filterBar}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|