import React from 'react'; import { ArrowRightIcon, CircleCheckIcon, LoaderCircleIcon, } from 'lucide-react'; import { generationCard, type GenerationCardState, } from '../../../lib/copilot/thread-turns'; import type { ICopilotMessage } from '../../../service/copilot/copilot.interface'; import { Button } from '../../ui/button'; import { StatusCard } from './StatusCard'; /** What a generation card can do about an article it is watching. */ export interface ThreadArticleCard { /** Open the finished article in the reader. */ onOpen: (articleId: string) => void; /** Publish it to the blog. */ onPublish: (articleId: string) => void; /** The article being published right now, if any. */ publishing: string | null; /** Articles already published from a card. */ published: ReadonlySet; /** Articles whose last publish attempt failed. */ failed: ReadonlySet; } /** What a Company ID card says, by what it watches. */ const COMPANY_ID_COPY: Record< 'push' | 'site' | 'engines', { working: string; ready: string; readyDetail: string } > = { push: { working: 'Pushing to AI Visibility', ready: 'Sent to AI Visibility', readyDetail: 'Your brand now reads from your Company ID.', }, site: { working: 'Reading your website', ready: 'Website read complete', readyDetail: 'Your Company ID has new answers to review.', }, engines: { working: 'Asking ChatGPT and Gemini', ready: 'Engines read complete', readyDetail: 'Your Company ID has new answers to review.', }, }; /** * The status card under a turn that started something long-running: an * article, an emailed product-content job, or a Company ID read. A finished * article carries its own row of buttons, since publishing it to the blog is * something only this plugin can offer. * * @param props - Component props. * @param props.message - The assistant turn. * @param props.article - What can be done with a finished article. * @param props.onNavigate - Open a page of the plugin. * @return The card, or nothing when this turn started nothing worth tracking. */ export function GenerationCard({ message, article, onNavigate, }: { message: ICopilotMessage; article: ThreadArticleCard; onNavigate: (path: string) => void; }): React.ReactElement | null { const card: GenerationCardState | null = generationCard(message); if (!card) return null; if (card.kind === 'content') { return card.status === 'ready' ? ( ) : ( ); } if (card.kind === 'company_id') { const copy = COMPANY_ID_COPY[card.watch ?? 'engines']; const openCompanyId = { label: 'Open Company ID', onClick: () => onNavigate('/company-id'), }; if (card.status === 'stalled') { return ( ); } if (card.status === 'ready') { return ( onNavigate('/ai-visibility'), } : openCompanyId } /> ); } return ( ); } if (card.status !== 'ready') { return ( onNavigate('/ai-visibility'), }} /> ); } return ; } /** * The finished article: read it, or put it on the blog. Publishing happens * here rather than on a page, because the merchant is standing in the * conversation that wrote it. * * @param props - Component props. * @param props.articleId - The article the turn produced, when known. * @param props.article - What can be done with it. * @return The card. */ function ArticleReady({ articleId, article, }: { articleId: string | undefined; article: ThreadArticleCard; }): React.ReactElement { const id: string = articleId ?? ''; const isPublished: boolean = article.published.has(id); return (

Ready

Your content is ready to review.

{isPublished ? ( Published to blog ) : ( )}
); }