feat(23-02): add ChatTaskCreatedBadge, ChatStatusUpdateBadge, useBrainstormerDefault
- ChatTaskCreatedBadge renders loading state and resolved badge with View task link - ChatStatusUpdateBadge renders CheckCircle2 + agent completion text with task link - useBrainstormerDefault returns general role agent ID with React Query deduplication - Fix ChatMessageList synthetic streaming entry to include messageType: null
This commit is contained in:
parent
5671b24210
commit
fe51918ed2
4 changed files with 102 additions and 0 deletions
|
|
@ -44,6 +44,7 @@ export function ChatMessageList({
|
|||
role: "assistant" as const,
|
||||
content: streamingContent,
|
||||
agentId: null,
|
||||
messageType: null,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: null,
|
||||
isStreamingEntry: true,
|
||||
|
|
|
|||
40
ui/src/components/ChatStatusUpdateBadge.tsx
Normal file
40
ui/src/components/ChatStatusUpdateBadge.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { Link } from "@/lib/router";
|
||||
import { CheckCircle2 } from "lucide-react";
|
||||
import { cn } from "../lib/utils";
|
||||
|
||||
interface ChatStatusUpdateBadgeProps {
|
||||
agentName: string;
|
||||
taskId: string;
|
||||
taskTitle?: string;
|
||||
taskUrl?: string;
|
||||
}
|
||||
|
||||
export function ChatStatusUpdateBadge({ agentName, taskId, taskTitle, taskUrl }: ChatStatusUpdateBadgeProps) {
|
||||
const displayTitle =
|
||||
taskTitle && taskTitle.length > 40 ? taskTitle.slice(0, 40) + "..." : taskTitle;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-1",
|
||||
"inline-flex items-center gap-2 rounded-md border border-border bg-card px-3 py-1 text-[13px]"
|
||||
)}
|
||||
role="status"
|
||||
>
|
||||
<CheckCircle2 className="h-3.5 w-3.5 text-green-500 dark:text-green-400" />
|
||||
<span className="text-foreground">
|
||||
{agentName} completed {taskId}
|
||||
{displayTitle ? `: ${displayTitle}` : ""}
|
||||
</span>
|
||||
{taskUrl && (
|
||||
<Link
|
||||
to={taskUrl}
|
||||
className="text-primary underline-offset-2 hover:underline"
|
||||
aria-label={`View task ${taskId}`}
|
||||
>
|
||||
View task
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
40
ui/src/components/ChatTaskCreatedBadge.tsx
Normal file
40
ui/src/components/ChatTaskCreatedBadge.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { Link } from "@/lib/router";
|
||||
import { cn } from "../lib/utils";
|
||||
|
||||
interface ChatTaskCreatedBadgeProps {
|
||||
taskId?: string | null;
|
||||
taskTitle?: string | null;
|
||||
taskUrl?: string | null;
|
||||
}
|
||||
|
||||
export function ChatTaskCreatedBadge({ taskId, taskTitle, taskUrl }: ChatTaskCreatedBadgeProps) {
|
||||
if (!taskId) {
|
||||
return (
|
||||
<div className="motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-1 inline-flex items-center gap-2 rounded-md border border-border bg-card px-3 py-1 text-[13px] text-muted-foreground">
|
||||
Creating task...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-1",
|
||||
"inline-flex items-center gap-2 rounded-md border border-border bg-card px-3 py-1 text-[13px]"
|
||||
)}
|
||||
role="status"
|
||||
>
|
||||
<span className="text-[11px] font-semibold text-muted-foreground">{taskId}</span>
|
||||
<span className="text-foreground">{taskTitle}</span>
|
||||
{taskUrl && (
|
||||
<Link
|
||||
to={taskUrl}
|
||||
className="text-primary underline-offset-2 hover:underline"
|
||||
aria-label={`View task ${taskId}`}
|
||||
>
|
||||
View task
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
21
ui/src/hooks/useBrainstormerDefault.ts
Normal file
21
ui/src/hooks/useBrainstormerDefault.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { Agent } from "@paperclipai/shared";
|
||||
import { agentsApi } from "../api/agents";
|
||||
import { useCompany } from "../context/CompanyContext";
|
||||
|
||||
export function useBrainstormerDefault(): string | null {
|
||||
const { selectedCompanyId } = useCompany();
|
||||
|
||||
const { data: agents = [] } = useQuery<Agent[]>({
|
||||
queryKey: ["agents", selectedCompanyId],
|
||||
queryFn: () => agentsApi.list(selectedCompanyId!),
|
||||
enabled: !!selectedCompanyId,
|
||||
});
|
||||
|
||||
// Reuses same queryKey as ChatPanel's agent list — React Query deduplicates
|
||||
const generalAgent = agents
|
||||
.filter((a) => a.role === "general")
|
||||
.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())[0];
|
||||
|
||||
return generalAgent?.id ?? null;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue