This commit is contained in:
SwiftyOS
2024-10-17 16:14:40 +02:00
parent 82f553ec0d
commit 26bef8b918
8 changed files with 353 additions and 9 deletions

View File

@@ -24,7 +24,7 @@ export const AgentImages: React.FC<AgentImagesProps> = ({ images }) => {
);
return (
<div className="h-full w-screen overflow-y-auto px-2 md:w-full lg:h-[91.25rem] lg:w-[56.25rem]">
<div className="w-screen overflow-y-auto px-2 md:w-full lg:w-[56.25rem]">
<div className="space-y-4 sm:space-y-6 md:space-y-[1.875rem]">
{images.map((image, index) => (
<AgentImageItem

View File

@@ -58,15 +58,15 @@ export const StoreCard: React.FC<StoreCardProps> = ({
<Image
src={agentImage}
alt={`${agentName} preview`}
layout="fill"
objectFit="cover"
className="rounded-xl"
fill
sizes="192px"
className="rounded-xl object-cover"
/>
</div>
<div className="-mt-8 flex flex-col px-4">
<Avatar className="mb-2 h-16 w-16">
<AvatarImage src={avatarSrc} alt={agentName} />
<AvatarFallback>{agentName.charAt(0)}</AvatarFallback>
<AvatarFallback className='h-16 w-16'>{agentName.charAt(0)}</AvatarFallback>
</Avatar>
<div className="mb-1 font-neue text-xl font-bold tracking-tight text-[#272727]">
{agentName}

View File

@@ -0,0 +1,184 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Page } from "./Page";
import { userEvent, within } from "@storybook/test";
import { IconType } from "../../../ui/icons";
const meta = {
title: "AGPTUI/Marketplace/Agent/Page",
component: Page,
parameters: {
layout: {
center: true,
fullscreen: true,
padding: 0,
},
},
tags: ["autodocs"],
argTypes: {
userName: { control: "text" },
userEmail: { control: "text" },
navLinks: { control: "object" },
activeLink: { control: "text" },
menuItemGroups: { control: "object" },
agentInfo: { control: "object" },
agentImages: { control: "object" },
otherAgentsByCreator: { control: "object" },
similarAgents: { control: "object" },
},
} satisfies Meta<typeof Page>;
export default meta;
type Story = StoryObj<typeof meta>;
const mockNavLinks = [
{ name: "Marketplace", href: "/" },
{ name: "Library", href: "/library" },
{ name: "Build", href: "/build" },
];
const mockMenuItemGroups = [
{
items: [
{ icon: IconType.Edit, text: "Edit profile", href: "/profile/edit" },
],
},
{
items: [
{
icon: IconType.LayoutDashboard,
text: "Creator Dashboard",
href: "/dashboard",
},
{
icon: IconType.UploadCloud,
text: "Publish an agent",
href: "/publish",
},
],
},
{
items: [{ icon: IconType.Settings, text: "Settings", href: "/settings" }],
},
{
items: [
{
icon: IconType.LogOut,
text: "Log out",
onClick: () => console.log("Logged out"),
},
],
},
];
const mockAgentInfo = {
name: "Super SEO Optimizer",
creator: "AI Labs",
description: "Boost your website's search engine rankings with our advanced AI-powered SEO optimization tool.",
rating: 4.9,
runs: 100000,
categories: ["SEO", "Marketing", "Content"],
lastUpdated: "2023-05-15",
version: "2.1.0",
};
const mockAgentImages = [
"https://ddz4ak4pa3d19.cloudfront.net/cache/cc/11/cc1172271dcf723a34f488a3344e82b2.jpg",
"https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg",
];
const mockOtherAgentsByCreator = [
{
agentName: "Content Wizard",
agentImage: "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg",
description: "Generate high-quality, engaging content for your blog, social media, or marketing campaigns.",
runs: 75000,
rating: 4.7,
avatarSrc: "https://example.com/avatar1.jpg",
},
{
agentName: "Data Analyzer Pro",
agentImage: "https://ddz4ak4pa3d19.cloudfront.net/cache/07/78/0778415062f8dff56a046a7eca44567c.jpg",
description: "Powerful tool for analyzing large datasets and generating insights.",
runs: 50000,
rating: 5,
avatarSrc: "https://github.com/shadcn.png",
},
{
agentName: "AI Copywriter",
agentImage: "https://ddz4ak4pa3d19.cloudfront.net/cache/33/bb/33bb7b3c8252b35a0086d893f7a5790c.jpg",
description: "AI-powered copywriting assistant for creating compelling marketing copy.",
runs: 62000,
rating: 4.8,
avatarSrc: "https://example.com/avatar4.jpg",
},
];
const mockSimilarAgents = [
{
agentName: "SEO Master",
agentImage: "https://ddz4ak4pa3d19.cloudfront.net/cache/59/b9/59b9415d4044f48f9b9e318c4c5a7984.jpg",
description: "Comprehensive SEO tool for website optimization and ranking improvement.",
runs: 80000,
rating: 4.8,
avatarSrc: "https://example.com/avatar2.jpg",
},
{
agentName: "Keyword Genius",
agentImage: "https://ddz4ak4pa3d19.cloudfront.net/cache/11/47/114784105a9b180e08e117cbf2612e5b.jpg",
description: "Advanced keyword research and analysis tool for SEO professionals.",
runs: 60000,
rating: 4.6,
avatarSrc: "https://example.com/avatar3.jpg",
},
{
agentName: "Backlink Builder",
agentImage: "https://ddz4ak4pa3d19.cloudfront.net/cache/22/cc/22cc7136d7eac435657b316bb16b7e89.jpg",
description: "Automated tool for building high-quality backlinks to improve SEO performance.",
runs: 55000,
rating: 4.7,
avatarSrc: "https://example.com/avatar5.jpg",
},
];
export const Default: Story = {
args: {
userName: "John Doe",
userEmail: "john.doe@example.com",
navLinks: mockNavLinks,
activeLink: "/marketplace",
menuItemGroups: mockMenuItemGroups,
agentInfo: mockAgentInfo,
agentImages: mockAgentImages,
otherAgentsByCreator: mockOtherAgentsByCreator,
similarAgents: mockSimilarAgents,
},
};
export const WithInteraction: Story = {
args: {
...Default.args,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Click on the "Run Agent" button
const runAgentButton = canvas.getByText("Run Agent");
await userEvent.click(runAgentButton);
// Click on an "Other agents by creator" card
const otherAgentCard = canvas.getByText("Content Wizard");
await userEvent.click(otherAgentCard);
// Click on the "Become a Creator" button
const becomeCreatorButton = canvas.getByText("Become a Creator");
await userEvent.click(becomeCreatorButton);
},
};
export const LongLists: Story = {
args: {
...Default.args,
otherAgentsByCreator: Array(10).fill(mockOtherAgentsByCreator[0]),
similarAgents: Array(10).fill(mockSimilarAgents[0]),
},
};

View File

@@ -0,0 +1,134 @@
import * as React from "react";
import { Navbar } from "../../Navbar";
import { AgentInfo } from "../../AgentInfo";
import { AgentImages } from "../../AgentImages";
import { BecomeACreator } from "../../BecomeACreator";
import { TopAgentsSection } from "../home/TopAgentsSection";
import { Separator } from "../../../ui/separator";
import { IconType } from "../../../ui/icons";
import Link from "next/link";
interface PageProps {
userName: string;
userEmail: string;
navLinks: { name: string; href: string }[];
activeLink: string;
menuItemGroups: {
groupName?: string;
items: {
icon: IconType;
text: string;
href?: string;
onClick?: () => void;
}[];
}[];
agentInfo: {
name: string;
creator: string;
description: string;
rating: number;
runs: number;
categories: string[];
lastUpdated: string;
version: string;
};
agentImages: string[];
otherAgentsByCreator: {
agentName: string;
agentImage: string;
description: string;
runs: number;
rating: number;
avatarSrc: string;
}[];
similarAgents: {
agentName: string;
agentImage: string;
description: string;
runs: number;
rating: number;
avatarSrc: string;
}[];
}
export const Page: React.FC<PageProps> = ({
userName,
userEmail,
navLinks,
activeLink,
menuItemGroups,
agentInfo,
agentImages,
otherAgentsByCreator,
similarAgents,
}) => {
const handleRunAgent = () => {
console.log("Run agent clicked");
// Implement run agent functionality
};
const handleCardClick = (agentName: string) => {
console.log("Clicked on agent:", agentName);
// Implement card click functionality
};
const handleBecomeCreator = () => {
console.log("Become a Creator clicked");
// Implement become a creator functionality
};
return (
<div className="mx-auto w-screen max-w-[1440px]">
<Navbar
userName={userName}
userEmail={userEmail}
links={navLinks}
activeLink={activeLink}
menuItemGroups={menuItemGroups}
/>
<main className="px-10">
<div className="mt-10 mb-6">
<Link href="/marketplace" className="text-2xl font-medium text-[#272727]">
Marketplace
</Link>
<span className="mx-2 text-2xl font-medium text-[#272727]">/</span>
<span className="text-2xl font-medium text-[#272727]">{agentInfo.name}</span>
</div>
<div className="flex flex-col lg:flex-row gap-5">
<AgentInfo
onRunAgent={handleRunAgent}
name={agentInfo.name}
creator={agentInfo.creator}
description={agentInfo.description}
rating={agentInfo.rating}
runs={agentInfo.runs}
categories={agentInfo.categories}
lastUpdated={agentInfo.lastUpdated}
version={agentInfo.version}
/>
<AgentImages images={agentImages} />
</div>
<Separator className="my-12" />
<TopAgentsSection
topAgents={otherAgentsByCreator}
onCardClick={handleCardClick}
sectionTitle={`Other agents by ${agentInfo.creator}`}
/>
<Separator className="my-12" />
<TopAgentsSection
topAgents={similarAgents}
onCardClick={handleCardClick}
sectionTitle="Similar agents"
/>
<Separator className="my-12" />
<BecomeACreator
title="Want to contribute?"
heading="We're always looking for more Creators!"
description="Join our ever-growing community of hackers and tinkerers"
buttonText="Become a Creator"
onButtonClick={handleBecomeCreator}
/>
</main>
</div>
);
};

View File

@@ -100,7 +100,11 @@ export const Page: React.FC<PageProps> = ({
onCardClick={handleCardClick}
/>
<Separator />
<TopAgentsSection topAgents={topAgents} onCardClick={handleCardClick} />
<TopAgentsSection
sectionTitle="Top Agents"
topAgents={topAgents}
onCardClick={handleCardClick}
/>
<Separator />
<FeaturedCreators
featuredCreators={featuredCreators}

View File

@@ -16,11 +16,13 @@ interface TopAgent {
}
interface TopAgentsSectionProps {
sectionTitle: string;
topAgents: TopAgent[];
onCardClick: (agentName: string) => void;
}
export const TopAgentsSection: React.FC<TopAgentsSectionProps> = ({
sectionTitle,
topAgents,
onCardClick,
}) => {
@@ -28,7 +30,7 @@ export const TopAgentsSection: React.FC<TopAgentsSectionProps> = ({
<div className="flex flex-col items-center justify-center py-8">
<div className="w-full">
<div className="mb-6 font-neue text-[23px] font-bold leading-9 tracking-tight text-[#282828]">
Top agents
{sectionTitle}
</div>
<Carousel
className="md:hidden"

View File

@@ -3,6 +3,7 @@
import * as React from "react";
import * as AvatarPrimitive from "@radix-ui/react-avatar";
import BoringAvatar from "./BoringAvatarWrapper";
import tailwindConfig from "../../../tailwind.config";
import { cn } from "@/lib/utils";
@@ -33,6 +34,25 @@ const AvatarImage = React.forwardRef<
));
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
/**
* Hack to match the avatar size based on Tailwind classes.
* This function attempts to extract the size from a 'h-' class in the className string,
* and maps it to the corresponding size in the Tailwind config.
* If no matching class is found, it defaults to 40.
* @param className - The className string to parse
* @returns The size of the avatar in pixels
*/
const getAvatarSize = (className: string | undefined): number => {
if (className?.includes('h-')) {
const match = parseInt(className.match(/h-(\d+)/)?.[1] || '16');
if (match) {
const size = tailwindConfig.theme.extend.spacing[match as keyof typeof tailwindConfig.theme.extend.spacing];
return size ? parseInt(size.replace('rem', '')) * 16 : 40;
}
}
return 40;
};
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
@@ -46,7 +66,7 @@ const AvatarFallback = React.forwardRef<
{...props}
>
<BoringAvatar
size={40}
size={getAvatarSize(className)}
name={props.children?.toString() || "User"}
variant="marble"
colors={["#92A1C6", "#146A7C", "#F0AB3D", "#C271B4", "#C20D90"]}

View File

@@ -66,7 +66,7 @@ const config = {
},
spacing: {
// Tailwind spacing + custom sizes
0: "0px",
0: "0rem",
0.5: "0.125rem",
1: "0.25rem",
1.5: "0.375rem",