better-chatbot / src /components /shareable-card.tsx
Bot
Initial commit for HF Spaces
05c5ed5
Raw
History Blame Contribute Delete
5.45 kB
"use client";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "ui/card";
import { Avatar, AvatarFallback, AvatarImage } from "ui/avatar";
import { useTranslations } from "next-intl";
import { format } from "date-fns";
import { cn } from "lib/utils";
import { ShareableActions, type Visibility } from "./shareable-actions";
import { WorkflowSummary } from "app-types/workflow";
import { AgentSummary } from "app-types/agent";
import { MCPServerInfo } from "app-types/mcp";
import { MCPIcon } from "ui/mcp-icon";
import Link from "next/link";
export interface ShareableIcon {
value?: string;
style?: {
backgroundColor?: string;
};
}
interface ShareableCardProps {
type: "agent" | "workflow" | "mcp";
item: AgentSummary | WorkflowSummary | MCPServerInfo;
isOwner?: boolean;
href: string;
onBookmarkToggle?: (itemId: string, isBookmarked: boolean) => void;
onVisibilityChange?: (itemId: string, visibility: Visibility) => void;
onDelete?: (itemId: string) => void;
isVisibilityChangeLoading?: boolean;
isBookmarkToggleLoading?: boolean;
isDeleteLoading?: boolean;
actionsDisabled?: boolean;
}
export function ShareableCard({
type,
item,
isOwner = true,
href,
onBookmarkToggle,
onVisibilityChange,
onDelete,
isBookmarkToggleLoading,
isVisibilityChangeLoading,
isDeleteLoading,
actionsDisabled,
}: ShareableCardProps) {
const t = useTranslations();
const isPublished = (item as WorkflowSummary).isPublished;
const isBookmarked =
type === "mcp" ? undefined : (item as AgentSummary).isBookmarked;
return (
<Link href={href} title={item.name}>
<Card
className={cn(
"w-full min-h-[196px] @container transition-colors group flex flex-col gap-3 cursor-pointer hover:bg-input",
)}
data-testid={`${type}-card`}
data-item-name={item.name}
data-item-id={item.id}
>
<CardHeader className="shrink gap-y-0">
<CardTitle className="flex gap-3 items-stretch min-w-0">
<div
style={{ backgroundColor: item.icon?.style?.backgroundColor }}
className="p-2 rounded-lg flex items-center justify-center ring ring-background border shrink-0"
>
{type === "mcp" ? (
<MCPIcon className="fill-white size-6" />
) : (
<Avatar className="size-6">
<AvatarImage src={item.icon?.value} />
<AvatarFallback />
</Avatar>
)}
</div>
<div className="flex flex-col justify-around min-w-0 flex-1 overflow-hidden">
<span
className="truncate font-medium"
data-testid={`${type}-card-name`}
>
{item.name}
</span>
<div className="text-xs text-muted-foreground flex items-center gap-1 min-w-0">
<time className="shrink-0">
{format(item.updatedAt || new Date(), "MMM d, yyyy")}
</time>
{type === "workflow" && !isPublished && (
<span className="px-2 rounded-sm bg-secondary text-foreground shrink-0">
{t("Workflow.draft")}
</span>
)}
</div>
</div>
</CardTitle>
</CardHeader>
<CardContent className="min-h-0 grow">
<CardDescription className="text-xs line-clamp-3 break-words overflow-hidden">
{item.description}
</CardDescription>
</CardContent>
<CardFooter className="shrink min-h-0 overflow-visible">
<div className="flex items-center justify-between w-full min-w-0">
<div onClick={(e) => e.stopPropagation()}>
<ShareableActions
type={type}
visibility={item.visibility}
isOwner={isOwner}
isBookmarked={isBookmarked}
editHref={href}
onVisibilityChange={
onVisibilityChange
? (visibility) => onVisibilityChange(item.id, visibility)
: undefined
}
onBookmarkToggle={
onBookmarkToggle
? (isBookmarked) => onBookmarkToggle(item.id, isBookmarked)
: undefined
}
onDelete={onDelete ? () => onDelete(item.id) : undefined}
isBookmarkToggleLoading={isBookmarkToggleLoading}
isVisibilityChangeLoading={isVisibilityChangeLoading}
isDeleteLoading={isDeleteLoading}
disabled={actionsDisabled}
/>
</div>
{!isOwner && item.userName && (
<div className="flex items-center gap-1.5 min-w-0">
<Avatar className="size-4 ring shrink-0 rounded-full">
<AvatarImage src={item.userAvatar || undefined} />
<AvatarFallback>
{item.userName[0]?.toUpperCase()}
</AvatarFallback>
</Avatar>
<span className="text-xs text-muted-foreground font-medium truncate min-w-0">
{item.userName}
</span>
</div>
)}
</div>
</CardFooter>
</Card>
</Link>
);
}