refactor(frontend): Revamp marketplace agent page data fetching and structure (#10756)

- Updated the agent page to utilize React Query for data fetching,
improving performance and reliability.
- Removed legacy API calls and integrated prefetching for creator
details and agents.
- Introduced a new MainAgentPage component for better separation of
concerns.
- Added a hydration boundary for managing server state.

> It’s important to note that I haven’t changed any UI in this, as it’s
out of scope for this PR.

### Checklist 📋

- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
- [x] I have manually tested both `Add to Library` and `Download`
functions, and they are working correctly.
  - [x] All fetching functions are working perfectly.
  - [x] All end-to-end tests are also working correctly.
This commit is contained in:
Abhimanyu Yadav
2025-09-01 11:10:01 +05:30
committed by GitHub
parent 7544028b94
commit ae4c9897b4
12 changed files with 751 additions and 97 deletions

View File

@@ -1,124 +1,75 @@
import BackendAPI from "@/lib/autogpt-server-api";
import { Breadcrumbs } from "@/components/molecules/Breadcrumbs/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";
import { getServerUser } from "@/lib/supabase/server/getServerUser";
import {
getV2GetSpecificAgent,
prefetchGetV2GetSpecificAgentQuery,
prefetchGetV2ListStoreAgentsQuery,
} from "@/app/api/__generated__/endpoints/store/store";
import { StoreAgentDetails } from "@/app/api/__generated__/models/storeAgentDetails";
import { MainAgentPage } from "../../../components/MainAgentPage/MainAgentPage";
import { getQueryClient } from "@/lib/react-query/queryClient";
import { prefetchGetV2GetAgentByStoreIdQuery } from "@/app/api/__generated__/endpoints/library/library";
import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
// Force dynamic rendering to avoid static generation issues with cookies
export const dynamic = "force-dynamic";
type MarketplaceAgentPageParams = { creator: string; slug: string };
export type MarketplaceAgentPageParams = { creator: string; slug: string };
export async function generateMetadata({
params: _params,
}: {
params: Promise<MarketplaceAgentPageParams>;
}): Promise<Metadata> {
const api = new BackendAPI();
const params = await _params;
const agent = await api.getStoreAgent(params.creator, params.slug);
const { data: creator_agent } = await getV2GetSpecificAgent(
params.creator,
params.slug,
);
return {
title: `${agent.agent_name} - AutoGPT Marketplace`,
description: agent.description,
title: `${(creator_agent as StoreAgentDetails).agent_name} - AutoGPT Marketplace`,
description: (creator_agent as StoreAgentDetails).description,
};
}
// export async function generateStaticParams() {
// const api = new BackendAPI();
// const agents = await api.getStoreAgents({ featured: true });
// return agents.agents.map((agent) => ({
// creator: agent.creator,
// slug: agent.slug,
// }));
// }
export default async function MarketplaceAgentPage({
params: _params,
}: {
params: Promise<MarketplaceAgentPageParams>;
}) {
const queryClient = getQueryClient();
const params = await _params;
const creator_lower = params.creator.toLowerCase();
const { user } = await getServerUser();
const api = new BackendAPI();
const agent = await api.getStoreAgent(creator_lower, params.slug);
const otherAgents = await api.getStoreAgents({ creator: creator_lower });
const similarAgents = await api.getStoreAgents({
// We are using slug as we know its has been sanitized and is not null
search_query: agent.slug.replace(/-/g, " "),
});
const libraryAgent = user
? await api
.getLibraryAgentByStoreListingVersionID(agent.active_version_id || "")
.catch((error) => {
console.error("Failed to fetch library agent:", error);
return null;
})
: null;
await Promise.all([
prefetchGetV2GetSpecificAgentQuery(queryClient, creator_lower, params.slug),
prefetchGetV2ListStoreAgentsQuery(queryClient, {
creator: creator_lower,
}),
prefetchGetV2ListStoreAgentsQuery(queryClient, {
search_query: params.slug.replace(/-/g, " "),
}),
]);
const breadcrumbs = [
{ name: "Marketplace", link: "/marketplace" },
{
name: agent.creator,
link: `/marketplace/creator/${encodeURIComponent(agent.creator)}`,
},
{ name: agent.agent_name, link: "#" },
];
const { user } = await getServerUser();
const { data: creator_agent, status } = await getV2GetSpecificAgent(
creator_lower,
params.slug,
); // Already cached in above prefetch
if (status === 200) {
await prefetchGetV2GetAgentByStoreIdQuery(
queryClient,
creator_agent.active_version_id ?? "",
{
query: {
enabled: !!user && !!creator_agent.active_version_id,
},
},
);
}
return (
<div className="mx-auto w-screen max-w-[1360px]">
<main className="mt-5 px-4">
<Breadcrumbs items={breadcrumbs} />
<div className="mt-4 flex flex-col items-start gap-4 sm:mt-6 sm:gap-6 md:mt-8 md:flex-row md:gap-8">
<div className="w-full md:w-auto md:shrink-0">
<AgentInfo
user={user}
name={agent.agent_name}
creator={agent.creator}
shortDescription={agent.sub_heading}
longDescription={agent.description}
rating={agent.rating}
runs={agent.runs}
categories={agent.categories}
lastUpdated={agent.updated_at}
version={agent.versions[agent.versions.length - 1]}
storeListingVersionId={agent.store_listing_version_id}
libraryAgent={libraryAgent}
/>
</div>
<AgentImages
images={
agent.agent_video
? [agent.agent_video, ...agent.agent_image]
: agent.agent_image
}
/>
</div>
<Separator className="mb-[25px] mt-[60px]" />
<AgentsSection
margin="32px"
agents={otherAgents.agents}
sectionTitle={`Other agents by ${agent.creator}`}
/>
<Separator className="mb-[25px] mt-[60px]" />
<AgentsSection
margin="32px"
agents={similarAgents.agents}
sectionTitle="Similar agents"
/>
<Separator className="mb-[25px] mt-[60px]" />
<BecomeACreator
title="Become a Creator"
description="Join our ever-growing community of hackers and tinkerers"
buttonText="Become a Creator"
/>
</main>
</div>
<HydrationBoundary state={dehydrate(queryClient)}>
<MainAgentPage params={params} />
</HydrationBoundary>
);
}

View File

@@ -0,0 +1,92 @@
import Image from "next/image";
import { PlayIcon } from "@radix-ui/react-icons";
import { Button } from "@/components/ui/button";
import {
getYouTubeVideoId,
isValidVideoFile,
isValidVideoUrl,
} from "./helpers";
import { useAgentImageItem } from "./useAgentImageItem";
interface AgentImageItemProps {
image: string;
index: number;
playingVideoIndex: number | null;
handlePlay: (index: number) => void;
handlePause: (index: number) => void;
}
export const AgentImageItem: React.FC<AgentImageItemProps> = ({
image,
index,
playingVideoIndex,
handlePlay,
handlePause,
}) => {
const { videoRef } = useAgentImageItem({ playingVideoIndex, index });
const isVideoFile = isValidVideoFile(image);
return (
<div className="relative">
<div className="h-[15rem] overflow-hidden rounded-[26px] bg-[#a8a8a8] dark:bg-neutral-700 sm:h-[20rem] sm:w-full md:h-[25rem] lg:h-[30rem]">
{isValidVideoUrl(image) ? (
getYouTubeVideoId(image) ? (
<iframe
width="100%"
height="100%"
src={`https://www.youtube.com/embed/${getYouTubeVideoId(image)}`}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
title="YouTube video player"
></iframe>
) : (
<div className="relative h-full w-full overflow-hidden">
<video
ref={videoRef}
className="absolute inset-0 h-full w-full object-cover"
controls
preload="metadata"
poster={`${image}#t=0.1`}
style={{ objectPosition: "center 25%" }}
onPlay={() => handlePlay(index)}
onPause={() => handlePause(index)}
autoPlay={false}
title="Video"
>
<source src={image} type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>
)
) : (
<div className="relative h-full w-full">
<Image
src={image}
alt="Image"
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
className="rounded-xl object-cover"
/>
</div>
)}
</div>
{isVideoFile && playingVideoIndex !== index && (
<div className="absolute bottom-2 left-2 sm:bottom-3 sm:left-3 md:bottom-4 md:left-4 lg:bottom-[1.25rem] lg:left-[1.25rem]">
<Button
size="default"
onClick={() => {
if (videoRef.current) {
videoRef.current.play();
}
}}
>
<span className="pr-1 text-sm font-medium leading-6 tracking-tight text-[#272727] dark:text-neutral-200 sm:pr-2 sm:text-base sm:leading-7 md:text-lg md:leading-8 lg:text-xl lg:leading-9">
Play demo
</span>
<PlayIcon className="h-5 w-5 text-black dark:text-neutral-200 sm:h-6 sm:w-6 md:h-7 md:w-7" />
</Button>
</div>
)}
</div>
);
};

View File

@@ -0,0 +1,17 @@
export const isValidVideoFile = (url: string): boolean => {
const videoExtensions = /\.(mp4|webm|ogg)$/i;
return videoExtensions.test(url);
};
export const isValidVideoUrl = (url: string): boolean => {
const videoExtensions = /\.(mp4|webm|ogg)$/i;
const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/;
return videoExtensions.test(url) || youtubeRegex.test(url);
};
export const getYouTubeVideoId = (url: string) => {
const regExp =
/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
const match = url.match(regExp);
return match && match[7].length === 11 ? match[7] : null;
};

View File

@@ -0,0 +1,27 @@
import { useEffect, useRef } from "react";
interface UseAgentImageItemProps {
playingVideoIndex: number | null;
index: number;
}
export const useAgentImageItem = ({
playingVideoIndex,
index,
}: UseAgentImageItemProps) => {
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
if (
playingVideoIndex !== index &&
videoRef.current &&
!videoRef.current.paused
) {
videoRef.current.pause();
}
}, [playingVideoIndex, index]);
return {
videoRef,
};
};

View File

@@ -0,0 +1,27 @@
"use client";
import { AgentImageItem } from "../AgentImageItem/AgentImageItem";
import { useAgentImage } from "./useAgentImage";
interface AgentImagesProps {
images: string[];
}
export const AgentImages: React.FC<AgentImagesProps> = ({ images }) => {
const { playingVideoIndex, handlePlay, handlePause } = useAgentImage();
return (
<div className="w-full overflow-y-auto bg-white px-2 dark:bg-transparent lg:w-[56.25rem]">
<div className="space-y-4 sm:space-y-6 md:space-y-[1.875rem]">
{images.map((image, index) => (
<AgentImageItem
key={index}
image={image}
index={index}
playingVideoIndex={playingVideoIndex}
handlePlay={handlePlay}
handlePause={handlePause}
/>
))}
</div>
</div>
);
};

View File

@@ -0,0 +1,26 @@
import { useCallback, useState } from "react";
export const useAgentImage = () => {
const [playingVideoIndex, setPlayingVideoIndex] = useState<number | null>(
null,
);
const handlePlay = useCallback((index: number) => {
setPlayingVideoIndex(index);
}, []);
const handlePause = useCallback(
(index: number) => {
if (playingVideoIndex === index) {
setPlayingVideoIndex(null);
}
},
[playingVideoIndex],
);
return {
playingVideoIndex,
handlePlay,
handlePause,
};
};

View File

@@ -0,0 +1,168 @@
"use client";
import { StarRatingIcons } from "@/components/ui/icons";
import { Separator } from "@/components/ui/separator";
import Link from "next/link";
import { User } from "@supabase/supabase-js";
import { cn } from "@/lib/utils";
import { useAgentInfo } from "./useAgentInfo";
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
interface AgentInfoProps {
user: User | null;
name: string;
creator: string;
shortDescription: string;
longDescription: string;
rating: number;
runs: number;
categories: string[];
lastUpdated: string;
version: string;
storeListingVersionId: string;
libraryAgent: LibraryAgent | undefined;
}
export const AgentInfo = ({
user,
name,
creator,
shortDescription,
longDescription,
rating,
runs,
categories,
lastUpdated,
version,
storeListingVersionId,
libraryAgent,
}: AgentInfoProps) => {
const {
handleDownload,
isDownloadingAgent,
handleLibraryAction,
isAddingAgentToLibrary,
} = useAgentInfo({ storeListingVersionId });
return (
<div className="w-full max-w-[396px] px-4 sm:px-6 lg:w-[396px] lg:px-0">
{/* Title */}
<div
data-testid="agent-title"
className="mb-3 w-full font-poppins text-2xl font-medium leading-normal text-neutral-900 dark:text-neutral-100 sm:text-3xl lg:mb-4 lg:text-[35px] lg:leading-10"
>
{name}
</div>
{/* Creator */}
<div className="mb-3 flex w-full items-center gap-1.5 lg:mb-4">
<div className="text-base font-normal text-neutral-800 dark:text-neutral-200 sm:text-lg lg:text-xl">
by
</div>
<Link
data-testid={"agent-creator"}
href={`/marketplace/creator/${encodeURIComponent(creator)}`}
className="text-base font-medium text-neutral-800 hover:underline dark:text-neutral-200 sm:text-lg lg:text-xl"
>
{creator}
</Link>
</div>
{/* Short Description */}
<div className="mb-4 line-clamp-2 w-full text-base font-normal leading-normal text-neutral-600 dark:text-neutral-300 sm:text-lg lg:mb-6 lg:text-xl lg:leading-7">
{shortDescription}
</div>
{/* Buttons */}
<div className="mb-4 flex w-full gap-3 lg:mb-[60px]">
{user && (
<button
className={cn(
"inline-flex min-w-24 items-center justify-center rounded-full bg-violet-600 px-4 py-3",
"transition-colors duration-200 hover:bg-violet-500 disabled:bg-zinc-400",
)}
data-testid={"agent-add-library-button"}
onClick={handleLibraryAction}
disabled={isAddingAgentToLibrary}
>
<span className="justify-start font-sans text-sm font-medium leading-snug text-primary-foreground">
{libraryAgent ? "See runs" : "Add to library"}
</span>
</button>
)}
<button
className={cn(
"inline-flex min-w-24 items-center justify-center rounded-full bg-zinc-200 px-4 py-3",
"transition-colors duration-200 hover:bg-zinc-200/70 disabled:bg-zinc-200/40",
)}
data-testid={"agent-download-button"}
onClick={handleDownload}
disabled={isDownloadingAgent}
>
<div className="justify-start text-center font-sans text-sm font-medium leading-snug text-zinc-800">
Download agent
</div>
</button>
</div>
{/* Rating and Runs */}
<div className="mb-4 flex w-full items-center justify-between lg:mb-[44px]">
<div className="flex items-center gap-1.5 sm:gap-2">
<span className="whitespace-nowrap text-base font-semibold text-neutral-800 dark:text-neutral-200 sm:text-lg">
{rating.toFixed(1)}
</span>
<div className="flex gap-0.5">{StarRatingIcons(rating)}</div>
</div>
<div className="whitespace-nowrap text-base font-semibold text-neutral-800 dark:text-neutral-200 sm:text-lg">
{runs.toLocaleString()} runs
</div>
</div>
{/* Separator */}
<Separator className="mb-4 lg:mb-[44px]" />
{/* Description Section */}
<div className="mb-4 w-full lg:mb-[36px]">
<div className="decoration-skip-ink-none mb-1.5 text-base font-medium leading-6 text-neutral-800 dark:text-neutral-200 sm:mb-2">
Description
</div>
<div
data-testid={"agent-description"}
className="whitespace-pre-line text-base font-normal leading-6 text-neutral-600 dark:text-neutral-400"
>
{longDescription}
</div>
</div>
{/* Categories */}
<div className="mb-4 flex w-full flex-col gap-1.5 sm:gap-2 lg:mb-[36px]">
<div className="decoration-skip-ink-none mb-1.5 text-base font-medium leading-6 text-neutral-800 dark:text-neutral-200 sm:mb-2">
Categories
</div>
<div className="flex flex-wrap gap-1.5 sm:gap-2">
{categories.map((category, index) => (
<div
key={index}
className="decoration-skip-ink-none whitespace-nowrap rounded-full border border-neutral-600 bg-white px-2 py-0.5 text-base font-normal leading-6 text-neutral-800 underline-offset-[from-font] dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-200 sm:px-[16px] sm:py-[10px]"
>
{category}
</div>
))}
</div>
</div>
{/* Version History */}
<div className="flex w-full flex-col gap-0.5 sm:gap-1">
<div className="decoration-skip-ink-none mb-1.5 text-base font-medium leading-6 text-neutral-800 dark:text-neutral-200 sm:mb-2">
Version history
</div>
<div className="decoration-skip-ink-none text-base font-normal leading-6 text-neutral-600 underline-offset-[from-font] dark:text-neutral-400">
Last updated {lastUpdated}
</div>
<div className="text-xs text-neutral-600 dark:text-neutral-400 sm:text-sm">
Version {version}
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,97 @@
import { usePostV2AddMarketplaceAgent } from "@/app/api/__generated__/endpoints/library/library";
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
import { useToast } from "@/components/molecules/Toast/use-toast";
import { useRouter } from "next/navigation";
import * as Sentry from "@sentry/nextjs";
import { useOnboarding } from "@/components/onboarding/onboarding-provider";
import { useGetV2DownloadAgentFile } from "@/app/api/__generated__/endpoints/store/store";
interface UseAgentInfoProps {
storeListingVersionId: string;
}
export const useAgentInfo = ({ storeListingVersionId }: UseAgentInfoProps) => {
const { toast } = useToast();
const router = useRouter();
const { completeStep } = useOnboarding();
const {
mutate: addMarketplaceAgentToLibrary,
isPending: isAddingAgentToLibrary,
} = usePostV2AddMarketplaceAgent({
mutation: {
onSuccess: ({ data }) => {
completeStep("MARKETPLACE_ADD_AGENT");
router.push(`/library/agents/${(data as LibraryAgent).id}`);
toast({
title: "Agent Added",
description: "Redirecting to your library...",
duration: 2000,
});
},
onError: (error) => {
Sentry.captureException(error);
toast({
title: "Error",
description: "Failed to add agent to library. Please try again.",
variant: "destructive",
});
},
},
});
const { refetch: downloadAgent, isFetching: isDownloadingAgent } =
useGetV2DownloadAgentFile(storeListingVersionId, {
query: {
enabled: false,
select: (data) => {
return data.data;
},
},
});
const handleLibraryAction = async () => {
addMarketplaceAgentToLibrary({
data: { store_listing_version_id: storeListingVersionId },
});
};
const handleDownload = async () => {
try {
const { data: file } = await downloadAgent();
const jsonData = JSON.stringify(file, null, 2);
const blob = new Blob([jsonData], { type: "application/json" });
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `agent_${storeListingVersionId}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
toast({
title: "Download Complete",
description: "Your agent has been successfully downloaded.",
});
} catch (error) {
Sentry.captureException(error);
toast({
title: "Error",
description: "Failed to download agent. Please try again.",
variant: "destructive",
});
}
};
return {
isAddingAgentToLibrary,
handleLibraryAction,
handleDownload,
isDownloadingAgent,
};
};

View File

@@ -0,0 +1,26 @@
import { Skeleton } from "@/components/ui/skeleton";
export const AgentPageLoading = () => {
return (
<div className="mx-auto w-screen max-w-[1360px]">
<main className="mt-5 px-4">
<div className="flex items-center space-x-2">
<Skeleton className="h-4 w-24" />
<span>/</span>
<Skeleton className="h-4 w-32" />
<span>/</span>
<Skeleton className="h-4 w-40" />
</div>
<div className="mt-8 flex flex-col gap-8 md:flex-row">
<div className="w-full max-w-sm">
<Skeleton className="h-64 w-full rounded-lg" />
</div>
<div className="flex-1">
<Skeleton className="aspect-video w-full rounded-lg" />
</div>
</div>
</main>
</div>
);
};

View File

@@ -0,0 +1,131 @@
"use client";
import { Breadcrumbs } from "@/components/molecules/Breadcrumbs/Breadcrumbs";
import { useMainAgentPage } from "./useMainAgentPage";
import { MarketplaceAgentPageParams } from "../../agent/[creator]/[slug]/page";
import { Separator } from "@/components/ui/separator";
import { AgentsSection } from "../AgentsSection/AgentsSection";
import { BecomeACreator } from "../BecomeACreator/BecomeACreator";
import { AgentPageLoading } from "../AgentPageLoading";
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
import { AgentInfo } from "../AgentInfo/AgentInfo";
import { AgentImages } from "../AgentImages/AgentImage";
type MainAgentPageProps = {
params: MarketplaceAgentPageParams;
};
export const MainAgentPage = ({ params }: MainAgentPageProps) => {
const {
agent,
otherAgents,
similarAgents,
libraryAgent,
isLoading,
hasError,
user,
} = useMainAgentPage({ params });
if (isLoading) {
return <AgentPageLoading />;
}
if (hasError) {
return (
<div className="mx-auto w-screen max-w-[1360px]">
<main className="px-4">
<div className="flex min-h-[400px] items-center justify-center">
<ErrorCard
isSuccess={false}
responseError={{ message: "Failed to load agent data" }}
context="agent page"
onRetry={() => window.location.reload()}
className="w-full max-w-md"
/>
</div>
</main>
</div>
);
}
if (!agent) {
return (
<div className="mx-auto w-screen max-w-[1360px]">
<main className="px-4">
<div className="flex min-h-[400px] items-center justify-center">
<ErrorCard
isSuccess={false}
responseError={{ message: "Agent not found" }}
context="agent page"
onRetry={() => window.location.reload()}
className="w-full max-w-md"
/>
</div>
</main>
</div>
);
}
const breadcrumbs = [
{ name: "Markertplace", link: "/marketplace" },
{
name: agent.creator,
link: `/marketplace/creator/${encodeURIComponent(agent.creator)}`,
},
{ name: agent.agent_name, link: "#" },
];
return (
<div className="mx-auto w-screen max-w-[1360px]">
<main className="mt-5 px-4">
<Breadcrumbs items={breadcrumbs} />
<div className="mt-4 flex flex-col items-start gap-4 sm:mt-6 sm:gap-6 md:mt-8 md:flex-row md:gap-8">
<div className="w-full md:w-auto md:shrink-0">
<AgentInfo
user={user}
name={agent.agent_name}
creator={agent.creator}
shortDescription={agent.sub_heading}
longDescription={agent.description}
rating={agent.rating}
runs={agent.runs}
categories={agent.categories}
lastUpdated={agent.last_updated.toISOString()}
version={agent.versions[agent.versions.length - 1]}
storeListingVersionId={agent.store_listing_version_id}
libraryAgent={libraryAgent}
/>
</div>
<AgentImages
images={
agent.agent_video
? [agent.agent_video, ...agent.agent_image]
: agent.agent_image
}
/>
</div>
<Separator className="mb-[25px] mt-[60px]" />
{otherAgents && (
<AgentsSection
margin="32px"
agents={otherAgents.agents}
sectionTitle={`Other agents by ${agent.creator}`}
/>
)}
<Separator className="mb-[25px] mt-[60px]" />
{similarAgents && (
<AgentsSection
margin="32px"
agents={similarAgents.agents}
sectionTitle="Similar agents"
/>
)}
<Separator className="mb-[25px] mt-[60px]" />
<BecomeACreator
title="Become a Creator"
description="Join our ever-growing community of hackers and tinkerers"
buttonText="Become a Creator"
/>
</main>
</div>
);
};

View File

@@ -0,0 +1,92 @@
import {
useGetV2GetSpecificAgent,
useGetV2ListStoreAgents,
} from "@/app/api/__generated__/endpoints/store/store";
import { MarketplaceAgentPageParams } from "../../agent/[creator]/[slug]/page";
import { useGetV2GetAgentByStoreId } from "@/app/api/__generated__/endpoints/library/library";
import { StoreAgentsResponse } from "@/app/api/__generated__/models/storeAgentsResponse";
import { StoreAgentDetails } from "@/app/api/__generated__/models/storeAgentDetails";
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
export const useMainAgentPage = ({
params,
}: {
params: MarketplaceAgentPageParams;
}) => {
const creator_lower = params.creator.toLowerCase();
const { user } = useSupabase();
const {
data: agent,
isLoading: isAgentLoading,
isError: isAgentError,
} = useGetV2GetSpecificAgent(creator_lower, params.slug, {
query: {
select: (x) => {
return x.data as StoreAgentDetails;
},
},
});
const {
data: otherAgents,
isLoading: isOtherAgentsLoading,
isError: isOtherAgentsError,
} = useGetV2ListStoreAgents(
{ creator: creator_lower },
{
query: {
select: (x) => {
return x.data as StoreAgentsResponse;
},
},
},
);
const {
data: similarAgents,
isLoading: isSimilarAgentsLoading,
isError: isSimilarAgentsError,
} = useGetV2ListStoreAgents(
{ search_query: params.slug.replace(/-/g, " ") },
{
query: {
select: (x) => {
return x.data as StoreAgentsResponse;
},
},
},
);
const {
data: libraryAgent,
isLoading: isLibraryAgentLoading,
isError: isLibraryAgentError,
} = useGetV2GetAgentByStoreId(agent?.active_version_id ?? "", {
query: {
select: (x) => {
return x.data as LibraryAgent;
},
enabled: !!user && !!agent?.active_version_id,
},
});
const isLoading =
isAgentLoading ||
isOtherAgentsLoading ||
isSimilarAgentsLoading ||
isLibraryAgentLoading;
const hasError =
isAgentError ||
isOtherAgentsError ||
isSimilarAgentsError ||
isLibraryAgentError;
return {
agent,
otherAgents,
similarAgents,
libraryAgent,
isLoading,
hasError,
user,
};
};

View File

@@ -43,7 +43,7 @@ export const MainCreatorPage = ({ params }: MainCreatorPageProps) => {
<main className="mt-5 px-4">
<Breadcrumbs
items={[
{ name: "Store", link: "/marketplace" },
{ name: "Marketplace", link: "/marketplace" },
{ name: creator.name, link: "#" },
]}
/>