"use client"; import type { ToolCallMessagePartProps } from "@assistant-ui/react"; import { useAtomValue, useSetAtom } from "jotai"; import { Dot } from "lucide-react"; import { useParams, usePathname } from "next/navigation"; import { useEffect, useRef, useState } from "react"; import { z } from "zod"; import { openReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel.atom"; import { PlateEditor } from "@/components/editor/plate-editor"; import { TextShimmerLoader } from "@/components/prompt-kit/loader"; import { useMediaQuery } from "@/hooks/use-media-query"; import { baseApiService } from "@/lib/apis/base-api.service"; /** * Zod schemas for runtime validation */ const GenerateReportArgsSchema = z.object({ topic: z.string(), source_content: z.string(), report_style: z.string().nullish(), user_instructions: z.string().nullish(), parent_report_id: z.number().nullish(), }); const GenerateReportResultSchema = z.object({ status: z.enum(["ready", "failed"]), report_id: z.number().nullish(), title: z.string().nullish(), word_count: z.number().nullish(), message: z.string().nullish(), error: z.string().nullish(), }); const ReportContentResponseSchema = z.object({ id: z.number(), title: z.string(), content: z.string().nullish(), report_metadata: z .object({ status: z.enum(["ready", "failed"]).nullish(), error_message: z.string().nullish(), word_count: z.number().nullish(), section_count: z.number().nullish(), }) .nullish(), report_group_id: z.number().nullish(), versions: z .array( z.object({ id: z.number(), created_at: z.string().nullish(), }) ) .nullish(), }); /** * Types derived from Zod schemas */ type GenerateReportArgs = z.infer; type GenerateReportResult = z.infer; function ContentSkeleton() { return (
); } function ReportGeneratingState({ topic }: { topic: string }) { return (

{topic}

); } function ReportErrorState({ title, error }: { title: string; error: string }) { return (

Report Generation Failed

{title}

{error}

); } function ReportCancelledState() { return (

Report Cancelled

Report generation was cancelled

); } function ReportCard({ reportId, title, wordCount, shareToken, autoOpen = false, }: { reportId: number; title: string; wordCount?: number; shareToken?: string | null; autoOpen?: boolean; }) { const openPanel = useSetAtom(openReportPanelAtom); const panelState = useAtomValue(reportPanelAtom); const isDesktop = useMediaQuery("(min-width: 768px)"); const autoOpenedRef = useRef(false); const [metadata, setMetadata] = useState<{ title: string; wordCount: number | null; versionLabel: string | null; content: string | null; }>({ title, wordCount: wordCount ?? null, versionLabel: null, content: null }); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; const fetchData = async () => { setIsLoading(true); setError(null); try { const url = shareToken ? `/api/v1/public/${shareToken}/reports/${reportId}/content` : `/api/v1/reports/${reportId}/content`; const rawData = await baseApiService.get(url); if (cancelled) return; const parsed = ReportContentResponseSchema.safeParse(rawData); if (parsed.success) { if (parsed.data.report_metadata?.status === "failed") { setError(parsed.data.report_metadata?.error_message || "Report generation failed"); } else { let versionLabel: string | null = null; const versions = parsed.data.versions; if (versions && versions.length > 1) { const idx = versions.findIndex((v) => v.id === reportId); if (idx >= 0) { versionLabel = `version ${idx + 1}`; } } const resolvedTitle = parsed.data.title || title; const resolvedWordCount = parsed.data.report_metadata?.word_count ?? wordCount ?? null; setMetadata({ title: resolvedTitle, wordCount: resolvedWordCount, versionLabel, content: parsed.data.content ?? null, }); if (autoOpen && isDesktop && !autoOpenedRef.current) { autoOpenedRef.current = true; openPanel({ reportId, title: resolvedTitle, wordCount: resolvedWordCount ?? undefined, shareToken, }); } } } } catch { if (!cancelled) setError("No report found"); } finally { if (!cancelled) setIsLoading(false); } }; fetchData(); return () => { cancelled = true; }; }, [reportId, title, wordCount, shareToken, autoOpen, isDesktop, openPanel]); if (!isLoading && error) { return ; } const isActive = panelState.isOpen && panelState.reportId === reportId; const handleOpen = () => { openPanel({ reportId, title: metadata.title, wordCount: metadata.wordCount ?? undefined, shareToken, }); }; return (
); } /** * Generate Report Tool UI — renders custom UI inline in chat * when the generate_report tool is called by the agent. */ export const GenerateReportToolUI = ({ args, result, status }: ToolCallMessagePartProps) => { const params = useParams(); const pathname = usePathname(); const isPublicRoute = pathname?.startsWith("/public/"); const shareToken = isPublicRoute && typeof params?.token === "string" ? params.token : null; const topic = args.topic || "Report"; const sawRunningRef = useRef(false); if (status.type === "running" || status.type === "requires-action") { sawRunningRef.current = true; } if (status.type === "running" || status.type === "requires-action") { return ; } if (status.type === "incomplete") { if (status.reason === "cancelled") { return ; } if (status.reason === "error") { return ( ); } } if (!result) { return ; } if (result.status === "failed") { return ( ); } if (result.status === "ready" && result.report_id) { return ( ); } return ; };