Update frontend to use generated API types and React Query

This commit is contained in:
abhi1992002
2025-06-29 12:26:01 +05:30
parent 6bbcd44a92
commit c2caac6b5b
12 changed files with 158 additions and 110 deletions

View File

@@ -29,6 +29,7 @@ export default defineConfig({
query: {
useQuery: true,
useMutation: true,
usePrefetch: true,
// Will add more as their use cases arise
},
},

View File

@@ -1,56 +0,0 @@
"use server";
import BackendAPI, {
CreatorsResponse,
StoreAgentsResponse,
} from "@/lib/autogpt-server-api";
const EMPTY_AGENTS_RESPONSE: StoreAgentsResponse = {
agents: [],
pagination: {
total_items: 0,
total_pages: 0,
current_page: 0,
page_size: 0,
},
};
const EMPTY_CREATORS_RESPONSE: CreatorsResponse = {
creators: [],
pagination: {
total_items: 0,
total_pages: 0,
current_page: 0,
page_size: 0,
},
};
export async function getMarketplaceData(): Promise<{
featuredAgents: StoreAgentsResponse;
topAgents: StoreAgentsResponse;
featuredCreators: CreatorsResponse;
}> {
const api = new BackendAPI();
const [featuredAgents, topAgents, featuredCreators] = await Promise.all([
api.getStoreAgents({ featured: true }).catch((error) => {
console.error("Error fetching featured marketplace agents:", error);
return EMPTY_AGENTS_RESPONSE;
}),
api.getStoreAgents({ sorted_by: "runs" }).catch((error) => {
console.error("Error fetching top marketplace agents:", error);
return EMPTY_AGENTS_RESPONSE;
}),
api
.getStoreCreators({ featured: true, sorted_by: "num_agents" })
.catch((error) => {
console.error("Error fetching featured marketplace creators:", error);
return EMPTY_CREATORS_RESPONSE;
}),
]);
return {
featuredAgents,
topAgents,
featuredCreators,
};
}

View File

@@ -7,6 +7,7 @@ import {
CarouselItem,
} from "@/components/ui/carousel";
import { useAgentsSection } from "./useAgentsSection";
import { StoreAgent } from "@/app/api/__generated__/models/storeAgent";
export interface Agent {
slug: string;
@@ -22,7 +23,7 @@ export interface Agent {
interface AgentsSectionProps {
sectionTitle: string;
agents: Agent[];
agents: StoreAgent[];
hideAvatars?: boolean;
margin?: string;
}

View File

@@ -9,7 +9,7 @@ import {
CardTitle,
} from "@/components/ui/card";
import { useState } from "react";
import { StoreAgent } from "@/lib/autogpt-server-api";
import { StoreAgent } from "@/app/api/__generated__/models/storeAgent";
interface FeaturedStoreCardProps {
agent: StoreAgent;

View File

@@ -2,18 +2,11 @@
import { CreatorCard } from "@/app/(platform)/marketplace/components/CreatorCard/CreatorCard";
import { useFeaturedCreators } from "./useFeaturedCreators";
export interface FeaturedCreator {
name: string;
username: string;
description: string;
avatar_url: string;
num_agents: number;
}
import { Creator } from "@/app/api/__generated__/models/creator";
interface FeaturedCreatorsProps {
title?: string;
featuredCreators: FeaturedCreator[];
featuredCreators: Creator[];
}
export const FeaturedCreators = ({

View File

@@ -1,8 +1,8 @@
import { Creator } from "@/app/api/__generated__/models/creator";
import { useRouter } from "next/navigation";
import { FeaturedCreator } from "./FeaturedCreators";
interface useFeaturedCreatorsProps {
featuredCreators: FeaturedCreator[];
featuredCreators: Creator[];
}
export const useFeaturedCreators = ({

View File

@@ -9,10 +9,10 @@ import {
CarouselNext,
CarouselIndicator,
} from "@/components/ui/carousel";
import { StoreAgent } from "@/lib/autogpt-server-api";
import Link from "next/link";
import { getBackgroundColor } from "./helper";
import { useFeaturedSection } from "./useFeaturedSection";
import { StoreAgent } from "@/app/api/__generated__/models/storeAgent";
interface FeaturedSectionProps {
featuredAgents: StoreAgent[];
@@ -22,6 +22,7 @@ export const FeaturedSection = ({ featuredAgents }: FeaturedSectionProps) => {
const { handleNextSlide, handlePrevSlide } = useFeaturedSection({
featuredAgents,
});
return (
<section className="w-full">
<h2 className="mb-8 font-poppins text-2xl font-semibold leading-7 text-neutral-800 dark:text-neutral-200">

View File

@@ -1,5 +1,5 @@
import { useState } from "react";
import { StoreAgent } from "@/lib/autogpt-server-api";
import { StoreAgent } from "@/app/api/__generated__/models/storeAgent";
interface useFeaturedSectionProps {
featuredAgents: StoreAgent[];

View File

@@ -0,0 +1,123 @@
"use client";
import { Separator } from "@/components/ui/separator";
import { FeaturedSection } from "../FeaturedSection/FeaturedSection";
import { HeroSection } from "../HeroSection/HeroSection";
import { AgentsSection } from "../AgentsSection/AgentsSection";
import { FeaturedCreators } from "../FeaturedCreators/FeaturedCreators";
import { BecomeACreator } from "../BecomeACreator/BecomeACreator";
import {
useGetV2ListStoreAgents,
useGetV2ListStoreCreators,
} from "@/app/api/__generated__/endpoints/store/store";
import { StoreAgentsResponse } from "@/app/api/__generated__/models/storeAgentsResponse";
import { CreatorsResponse } from "@/app/api/__generated__/models/creatorsResponse";
export const MainMarkeplacePage = () => {
// Below queries are already fetched on server and hydrated properly in cache, hence these requests are fast
const {
data: featuredAgents,
isLoading: isFeaturedAgentsLoading,
isError: isFeaturedAgentsError,
} = useGetV2ListStoreAgents(
{ featured: true },
{
query: {
select: (x) => {
return x.data as StoreAgentsResponse;
},
},
},
);
const {
data: topAgents,
isLoading: isTopAgentsLoading,
isError: isTopAgentsError,
} = useGetV2ListStoreAgents(
{
sorted_by: "runs",
},
{
query: {
select: (x) => {
return x.data as StoreAgentsResponse;
},
},
},
);
const {
data: featuredCreators,
isLoading: isFeaturedCreatorsLoading,
isError: isFeaturedCreatorsError,
} = useGetV2ListStoreCreators(
{ featured: true, sorted_by: "num_agents" },
{
query: {
select: (x) => {
return x.data as CreatorsResponse;
},
},
},
);
const isLoading =
isFeaturedAgentsLoading || isTopAgentsLoading || isFeaturedCreatorsLoading;
const hasError =
isFeaturedAgentsError || isTopAgentsError || isFeaturedCreatorsError;
// TODO : Add better Loading Skeletons
if (isLoading) {
return (
<div className="mx-auto w-screen max-w-[1360px]">
<main className="px-4">
<div className="flex min-h-[400px] items-center justify-center">
<div className="text-lg">Loading...</div>
</div>
</main>
</div>
);
}
// TODO : Add better Error UI
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">
<div className="text-lg text-red-500">
Error loading marketplace data. Please try again later.
</div>
</div>
</main>
</div>
);
}
return (
<div className="mx-auto w-screen max-w-[1360px]">
<main className="px-4">
<HeroSection />
{featuredAgents && (
<FeaturedSection featuredAgents={featuredAgents.agents} />
)}
{/* 100px margin because our featured sections button are placed 40px below the container */}
<Separator className="mb-6 mt-24" />
{topAgents && (
<AgentsSection sectionTitle="Top Agents" agents={topAgents.agents} />
)}
<Separator className="mb-[25px] mt-[60px]" />
{featuredCreators && (
<FeaturedCreators featuredCreators={featuredCreators.creators} />
)}
<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

@@ -1,17 +1,11 @@
import React from "react";
import { Separator } from "@/components/ui/separator";
import { Metadata } from "next";
import { getMarketplaceData } from "./actions";
import { HeroSection } from "./components/HeroSection/HeroSection";
import { Agent, AgentsSection } from "./components/AgentsSection/AgentsSection";
import { MainMarkeplacePage } from "./components/MainMarkeplacePage/MainMarkeplacePage";
import {
FeaturedCreator,
FeaturedCreators,
} from "./components/FeaturedCreators/FeaturedCreators";
import { BecomeACreator } from "./components/BecomeACreator/BecomeACreator";
import { FeaturedSection } from "./components/FeaturedSection/FeaturedSection";
prefetchGetV2ListStoreAgentsQuery,
prefetchGetV2ListStoreCreatorsQuery,
} from "@/app/api/__generated__/endpoints/store/store";
import { getQueryClient } from "@/lib/react-query/queryClient";
import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
// Force dynamic rendering to avoid static generation issues with cookies
export const dynamic = "force-dynamic";
@@ -61,31 +55,24 @@ export const metadata: Metadata = {
};
export default async function MarketplacePage(): Promise<React.ReactElement> {
const { featuredAgents, topAgents, featuredCreators } =
await getMarketplaceData();
const queryClient = getQueryClient();
await Promise.all([
prefetchGetV2ListStoreAgentsQuery(queryClient, {
featured: true,
}),
prefetchGetV2ListStoreAgentsQuery(queryClient, {
sorted_by: "runs",
}),
prefetchGetV2ListStoreCreatorsQuery(queryClient, {
featured: true,
sorted_by: "num_agents",
}),
]);
return (
<div className="mx-auto w-screen max-w-[1360px]">
<main className="px-4">
<HeroSection />
<FeaturedSection featuredAgents={featuredAgents.agents} />
{/* 100px margin because our featured sections button are placed 40px below the container */}
<Separator className="mb-6 mt-24" />
<AgentsSection
sectionTitle="Top Agents"
agents={topAgents.agents as Agent[]}
/>
<Separator className="mb-[25px] mt-[60px]" />
<FeaturedCreators
featuredCreators={featuredCreators.creators as FeaturedCreator[]}
/>
<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)}>
<MainMarkeplacePage />
</HydrationBoundary>
);
}

View File

@@ -19,7 +19,7 @@ import {
} from "../ui/icons";
import Link from "next/link";
import { ProfilePopoutMenuLogoutButton } from "./ProfilePopoutMenuLogoutButton";
import { PublishAgentPopout } from "./composite/PublishAgentPopout";
import { PublishAgentPopout } from "@/app/(platform)/marketplace/components/PublishAgentPopout/PublishAgentPopout";
interface ProfilePopoutMenuProps {
userName?: string;

View File

@@ -1,5 +1,3 @@
"use client";
import { isServer, QueryClient } from "@tanstack/react-query";
function makeQueryClient() {