hooking up pages, added missing update at

This commit is contained in:
SwiftyOS
2024-11-12 11:15:14 +01:00
parent 38b20e6158
commit a71b2a1de6
17 changed files with 244 additions and 91 deletions

View File

@@ -120,6 +120,7 @@ async def get_store_agent_details(
runs=agent.runs,
rating=agent.rating,
versions=agent.versions,
last_updated=agent.updated_at,
)
except backend.server.v2.store.exceptions.AgentNotFoundError:
raise

View File

@@ -38,6 +38,7 @@ async def test_get_store_agents(mocker):
runs=10,
rating=4.5,
versions=["1.0"],
updated_at=datetime.now(),
)
]
@@ -77,6 +78,7 @@ async def test_get_store_agent_details(mocker):
runs=10,
rating=4.5,
versions=["1.0"],
updated_at=datetime.now(),
)
# Mock prisma call

View File

@@ -50,6 +50,7 @@ class StoreAgentDetails(pydantic.BaseModel):
runs: int
rating: float
versions: list[str]
last_updated: datetime.datetime
class Creator(pydantic.BaseModel):

View File

@@ -70,6 +70,7 @@ def test_store_agent_details():
runs=50,
rating=4.5,
versions=["1.0", "2.0"],
last_updated=datetime.datetime.now(),
)
assert details.slug == "test-agent"
assert len(details.agent_image) == 2

View File

@@ -348,6 +348,7 @@ def test_get_agent_details(mocker: pytest_mock.MockFixture):
runs=100,
rating=4.5,
versions=["1.0.0", "1.1.0"],
last_updated=datetime.datetime.now(),
)
mock_db_call = mocker.patch("backend.server.v2.store.db.get_store_agent_details")
mock_db_call.return_value = mocked_value

View File

@@ -19,6 +19,7 @@ AgentRuns AS (
)
SELECT
sl.id AS listing_id,
slv."updatedAt" AS updated_at,
slv.slug,
a.name AS agent_name,
slv."videoUrl" AS agent_video,

View File

@@ -423,7 +423,8 @@ view Creator {
}
view StoreAgent {
listing_id String @id
listing_id String @id
updated_at DateTime
slug String
agent_name String

View File

@@ -1,8 +1,93 @@
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
import { Navbar } from "@/components/agptui/Navbar";
import { BreadCrumbs } from "@/components/agptui/BreadCrumbs";
import { AgentInfo } from "@/components/agptui/AgentInfo";
import { AgentImages } from "@/components/agptui/AgentImages";
import { AgentsSection } from "@/components/agptui/composite/AgentsSection";
import { BecomeACreator } from "@/components/agptui/BecomeACreator";
import { Separator } from "@/components/ui/separator";
import { Metadata } from "next";
export async function generateMetadata({
params,
}: {
params: { creator: string; slug: string };
}): Promise<Metadata> {
const api = new AutoGPTServerAPI();
const agent = await api.getStoreAgent(params.creator, params.slug);
return {
title: `${agent.agent_name} - AutoGPT Store`,
description: agent.description,
};
}
export async function generateStaticParams() {
const api = new AutoGPTServerAPI();
const agents = await api.getStoreAgents({ featured: true });
return agents.agents.map((agent) => ({
creator: agent.creator,
slug: agent.slug,
lang: "en",
}));
}
export default async function Page({
params,
}: {
params: Promise<{ lang: string, creator: string, slug: string }>
}) {
const { lang, creator, slug } = await params
return <div>My Post: {slug}</div>
}
params,
}: {
params: { lang: string; creator: string; slug: string };
}) {
const api = new AutoGPTServerAPI();
const agent = await api.getStoreAgent(params.creator, params.slug);
const otherAgents = await api.getStoreAgents({ creator: params.creator });
const similarAgents = await api.getStoreAgents({
search_query: agent.categories[0],
});
const breadcrumbs = [
{ name: "Store", link: "/store" },
{ name: agent.creator, link: `/store/creator/${agent.creator}` },
{ name: agent.agent_name, link: "#" },
];
return (
<div className="mx-auto w-screen max-w-[1360px]">
<main className="px-4 md:mt-4 lg:mt-8">
<BreadCrumbs items={breadcrumbs} />
<div className="flex flex-col gap-5 lg:flex-row">
<div>
<AgentInfo
name={agent.agent_name}
creator={agent.creator}
description={agent.description}
rating={agent.rating}
runs={agent.runs}
categories={agent.categories}
lastUpdated={agent.updated_at}
version={agent.versions[agent.versions.length - 1]}
/>
</div>
<AgentImages images={agent.agent_image} />
</div>
<Separator className="my-6" />
<AgentsSection
agents={otherAgents.agents}
sectionTitle={`Other agents by ${agent.creator}`}
/>
<Separator className="my-6" />
<AgentsSection
agents={similarAgents.agents}
sectionTitle="Similar agents"
/>
<Separator className="my-6" />
<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"
/>
</main>
</div>
);
}

View File

@@ -1,13 +1,33 @@
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
import {
CreatorDetails as Creator,
StoreAgent,
} from "@/lib/autogpt-server-api";
import { CreatorDetails } from "@/components/agptui/composite/CreatorDetails";
import { AgentsSection } from "@/components/agptui/composite/AgentsSection";
import { BreadCrumbs } from "@/components/agptui/BreadCrumbs";
import { Metadata } from "next";
export async function generateMetadata({
params,
}: {
params: { creator: string };
}): Promise<Metadata> {
const api = new AutoGPTServerAPI();
const creator = await api.getStoreCreator(params.creator);
return {
title: `${creator.name} - AutoGPT Store`,
description: creator.description,
};
}
export async function generateStaticParams() {
const api = new AutoGPTServerAPI();
const creators = await api.getStoreCreators({ featured: true });
return creators.creators.map((creator) => ({
creator: creator.username,
lang: "en",
}));
}
@@ -16,22 +36,40 @@ export default async function Page({
}: {
params: { lang: string; creator: string };
}) {
const { creator } = params;
const api = new AutoGPTServerAPI();
const creatorDetails = await api.getStoreCreator(creator);
const creator = await api.getStoreCreator(params.creator);
const creatorAgents = await api.getStoreAgents({ creator: params.creator });
const agents = creatorAgents.agents;
return (
<div className="flex w-full flex-col items-center justify-center px-4">
<CreatorDetails
name={creatorDetails.name}
username={creatorDetails.username}
description={creatorDetails.description}
avgRating={creatorDetails.agent_rating}
agentCount={creatorDetails.agent_runs}
topCategories={creatorDetails.top_categories}
otherLinks={creatorDetails.links}
avatarSrc={creatorDetails.avatar_url}
/>
</div>
<>
<div className="flex w-full flex-col items-center justify-center px-4">
<div className="mt-8">
<BreadCrumbs
items={[
{ name: "Store", link: "/store" },
{ name: creator.name, link: "#" },
]}
/>
<CreatorDetails
avatarSrc={creator.avatar_url}
name={creator.name}
username={creator.username}
description={creator.description}
avgRating={creator.agent_rating}
agentCount={creator.agent_runs}
topCategories={creator.top_categories}
otherLinks={creator.links}
/>
</div>
<div className="mt-16">
<AgentsSection
agents={agents}
hideAvatars={true}
sectionTitle={`Agents by ${creator.name}`}
/>
</div>
</div>
</>
);
}
}

View File

@@ -22,7 +22,7 @@ async function getStoreData() {
const [featuredAgents, topAgents, featuredCreators] = await Promise.all([
api.getStoreAgents({ featured: true }),
api.getStoreAgents({ sorted_by: "runs" }),
api.getStoreCreators({ featured: true }),
api.getStoreCreators({ featured: true, sorted_by: "num_agents" }),
]);
return {
@@ -38,13 +38,19 @@ export const metadata: Metadata = {
description: "Find and use AI Agents created by our community",
applicationName: "NextGen AutoGPT Store",
authors: [{ name: "AutoGPT Team" }],
keywords: ["AI agents", "automation", "artificial intelligence", "AutoGPT", "marketplace"],
keywords: [
"AI agents",
"automation",
"artificial intelligence",
"AutoGPT",
"marketplace",
],
robots: {
index: true,
follow: true,
},
openGraph: {
title: "Agent Store - NextGen AutoGPT",
title: "Agent Store - NextGen AutoGPT",
description: "Find and use AI Agents created by our community",
type: "website",
siteName: "NextGen AutoGPT Store",
@@ -53,24 +59,23 @@ export const metadata: Metadata = {
url: "/images/store-og.png",
width: 1200,
height: 630,
alt: "NextGen AutoGPT Store"
}
]
alt: "NextGen AutoGPT Store",
},
],
},
twitter: {
card: "summary_large_image",
title: "Agent Store - NextGen AutoGPT",
description: "Find and use AI Agents created by our community",
images: ["/images/store-twitter.png"]
images: ["/images/store-twitter.png"],
},
icons: {
icon: "/favicon.ico",
shortcut: "/favicon-16x16.png",
apple: "/apple-touch-icon.png"
}
apple: "/apple-touch-icon.png",
},
};
export default async function Page({
params: { lang },
}: {

View File

@@ -1,10 +1,10 @@
export default async function Page({
params,
searchParams,
}: {
params: { lang: string },
searchParams: { term?: string }
}) {
const searchTerm = searchParams.term || ''
return <div>Search Results for: {searchTerm}</div>
}
params,
searchParams,
}: {
params: { lang: string };
searchParams: { term?: string };
}) {
const searchTerm = searchParams.term || "";
return <div>Search Results for: {searchTerm}</div>;
}

View File

@@ -1,9 +1,10 @@
"use client";
import * as React from "react";
import { Button } from "./Button";
import Link from "next/link";
import { StarRatingIcons } from "@/components/ui/icons";
interface AgentInfoProps {
onRunAgent: () => void;
name: string;
creator: string;
description: string;
@@ -15,7 +16,6 @@ interface AgentInfoProps {
}
export const AgentInfo: React.FC<AgentInfoProps> = ({
onRunAgent,
name,
creator,
description,
@@ -25,6 +25,11 @@ export const AgentInfo: React.FC<AgentInfoProps> = ({
lastUpdated,
version,
}) => {
const onRunAgent = () => {
// TODO: Implement run agent functionality
console.log("Running agent:", name);
};
return (
<div className="flow-root w-full lg:w-[27.5rem]">
<div className="mb-2 font-neue text-3xl font-medium tracking-wide text-[#272727] md:mb-4 md:text-4xl lg:text-5xl">
@@ -39,7 +44,7 @@ export const AgentInfo: React.FC<AgentInfoProps> = ({
{creator}
</Link>
</div>
<Button onClick={onRunAgent} className="mb-8" variant="outline">
<Button onClick={onRunAgent} className="mb-8" variants="outline">
Run agent
</Button>
<div className="font-['PP Neue Montreal TT'] mb-6 text-[1.1875rem] font-normal leading-relaxed tracking-tight text-[#282828]">

View File

@@ -41,6 +41,14 @@ export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
variant?:
| "default"
| "destructive"
| "outline"
| "secondary"
| "ghost"
| "link";
size?: "default" | "sm" | "lg" | "primary" | "icon";
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(

View File

@@ -43,16 +43,37 @@ export const AgentsSection: React.FC<AgentsSectionProps> = ({
<div className="mb-6 font-neue text-[23px] font-bold leading-9 tracking-tight text-[#282828]">
{sectionTitle}
</div>
<Carousel
className="md:hidden"
opts={{
loop: true,
}}
>
<CarouselContent>
{topAgents.map((agent, index) => (
<CarouselItem key={index} className="min-w-64 max-w-68">
{!topAgents || topAgents.length === 0 ? (
<div className="text-center text-gray-500">No agents found</div>
) : (
<>
<Carousel
className="md:hidden"
opts={{
loop: true,
}}
>
<CarouselContent>
{topAgents.map((agent, index) => (
<CarouselItem key={index} className="min-w-64 max-w-68">
<StoreCard
agentName={agent.agent_name}
agentImage={agent.agent_image}
description={agent.description}
runs={agent.runs}
rating={agent.rating}
avatarSrc={agent.creator_avatar}
hideAvatar={hideAvatars}
onClick={() => handleCardClick(agent.creator, agent.slug)}
/>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
<div className="hidden grid-cols-1 place-items-center gap-3 md:grid md:grid-cols-2 lg:grid-cols-3">
{topAgents.map((agent, index) => (
<StoreCard
key={index}
agentName={agent.agent_name}
agentImage={agent.agent_image}
description={agent.description}
@@ -62,25 +83,10 @@ export const AgentsSection: React.FC<AgentsSectionProps> = ({
hideAvatar={hideAvatars}
onClick={() => handleCardClick(agent.creator, agent.slug)}
/>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
<div className="hidden grid-cols-1 place-items-center gap-3 md:grid md:grid-cols-2 lg:grid-cols-3">
{topAgents.map((agent, index) => (
<StoreCard
key={index}
agentName={agent.agent_name}
agentImage={agent.agent_image}
description={agent.description}
runs={agent.runs}
rating={agent.rating}
avatarSrc={agent.creator_avatar}
hideAvatar={hideAvatars}
onClick={() => handleCardClick(agent.creator, agent.slug)}
/>
))}
</div>
))}
</div>
</>
)}
</div>
</div>
);

View File

@@ -9,7 +9,7 @@ interface CreatorDetailsProps {
avgRating: number;
agentCount: number;
topCategories: string[];
otherLinks: Record<string, string>;
otherLinks: string[];
avatarSrc: string;
}
@@ -53,22 +53,20 @@ export const CreatorDetails: React.FC<CreatorDetailsProps> = React.memo(
Other links
</h2>
<div className="flex flex-wrap gap-4">
{Object.entries(otherLinks)
.slice(0, 5)
.map(([key, url]) => (
<a
key={key}
href={url}
className="flex items-center gap-2"
target="_blank"
rel="noopener noreferrer"
>
{getIconForSocial(url, { className: "h-6 w-6" })}
<span className="font-neue text-lg font-normal tracking-tight text-[#282828]">
{key}
</span>
</a>
))}
{otherLinks.map((url, index) => (
<a
key={index}
href={url}
className="flex items-center gap-2"
target="_blank"
rel="noopener noreferrer"
>
{getIconForSocial(url, { className: "h-6 w-6" })}
<span className="font-neue text-lg font-normal tracking-tight text-[#282828]">
{new URL(url).hostname.replace("www.", "")}
</span>
</a>
))}
</div>
</div>
)}

View File

@@ -100,7 +100,6 @@ export const AgentPage: React.FC<AgentPageProps> = ({
<div className="flex flex-col gap-5 lg:flex-row">
<div>
<AgentInfo
onRunAgent={handleRunAgent}
name={agentInfo.name}
creator={agentInfo.creator}
description={agentInfo.description}

View File

@@ -339,6 +339,7 @@ export type StoreAgentsResponse = {
export type StoreAgentDetails = {
slug: string;
updated_at: string;
agent_name: string;
agent_video: string;
agent_image: string[];