mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-10 23:58:06 -05:00
feat(builder, market): build a "better" market page that loads data
This commit is contained in:
71
rnd/autogpt_builder/src/app/marketplace/[id]/page.tsx
Normal file
71
rnd/autogpt_builder/src/app/marketplace/[id]/page.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Suspense, useMemo } from 'react';
|
||||
import { notFound } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import MarketplaceAPI from "@/lib/marketplace-api";
|
||||
import { AgentDetailResponse } from "@/lib/marketplace-api";
|
||||
|
||||
async function getAgentDetails(id: string): Promise<AgentDetailResponse> {
|
||||
const apiUrl = process.env.AGPT_MARKETPLACE_URL;
|
||||
const api = new MarketplaceAPI(apiUrl);
|
||||
try {
|
||||
console.log(`Fetching agent details for id: ${id}`); // Add logging
|
||||
const agent = await api.getAgentDetails(id);
|
||||
console.log(`Agent details fetched:`, agent); // Add logging
|
||||
return agent;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching agent details:`, error); // Add error logging
|
||||
return notFound();
|
||||
}
|
||||
}
|
||||
|
||||
function AgentDetailContent({ agent }: { agent: AgentDetailResponse }) {
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="flex justify-between items-center">
|
||||
<h1 className="text-3xl font-bold text-gray-900">{agent.name}</h1>
|
||||
<Link href="/marketplace" className="px-4 py-2 bg-gray-200 rounded-lg">
|
||||
Back
|
||||
</Link>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<p className="text-gray-700">{agent.description}</p>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<Link
|
||||
href={`/api/marketplace/agent/${agent.id}/download`}
|
||||
className="px-4 py-2 bg-gray-200 rounded-lg"
|
||||
>
|
||||
Download
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default async function AgentDetailPage({ params }: { params: { id: string } }) {
|
||||
console.log(`Rendering AgentDetailPage for id: ${params.id}`); // Add logging
|
||||
|
||||
let agent: AgentDetailResponse | null = null;
|
||||
let error: Error | null = null;
|
||||
|
||||
try {
|
||||
agent = await getAgentDetails(params.id);
|
||||
} catch (e) {
|
||||
error = e as Error;
|
||||
console.error(`Error in AgentDetailPage:`, error);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div>Error: {error.message}</div>;
|
||||
}
|
||||
|
||||
if (!agent) {
|
||||
return notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<AgentDetailContent agent={agent} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,97 @@
|
||||
"use client";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import MarketplaceAPI, { AgentResponse, AgentListResponse } from "@/lib/marketplace-api";
|
||||
|
||||
interface AgentRowProps {
|
||||
agent: AgentResponse;
|
||||
}
|
||||
|
||||
const Marketplace = () => {
|
||||
const AgentRow = ({ agent }: AgentRowProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
const handleClick = () => {
|
||||
router.push(`/marketplace/${agent.id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<li className="flex justify-between gap-x-6 py-5 cursor-pointer hover:bg-gray-50" onClick={handleClick}>
|
||||
<div className="flex min-w-0 gap-x-4">
|
||||
<img className="h-12 w-12 flex-none rounded-full bg-gray-50" src="https://images.unsplash.com/photo-1562408590-e32931084e23?q=80&w=3270&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="" />
|
||||
<div className="min-w-0 flex-auto">
|
||||
<p className="text-sm font-semibold leading-6 text-gray-900">{agent.name}</p>
|
||||
<p className="mt-1 truncate text-xs leading-5 text-gray-500">{agent.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-x-4">
|
||||
<div className="hidden sm:flex sm:flex-col sm:items-end">
|
||||
<p className="text-sm leading-6 text-gray-900">{agent.categories.join(', ')}</p>
|
||||
<p className="mt-1 text-xs leading-5 text-gray-500">
|
||||
Last updated <time dateTime={agent.updatedAt}>{new Date(agent.updatedAt).toLocaleDateString()}</time>
|
||||
</p>
|
||||
</div>
|
||||
<svg className="h-5 w-5 flex-none text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fillRule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
const Marketplace = () => {
|
||||
const apiUrl = process.env.NEXT_PUBLIC_AGPT_MARKETPLACE_URL;
|
||||
const api = useMemo(() => new MarketplaceAPI(apiUrl), [apiUrl]);
|
||||
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [agents, setAgents] = useState<AgentResponse[]>([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [totalPages, setTotalPages] = useState(1);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const fetchAgents = async (searchTerm: string, currentPage: number) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
let response: AgentListResponse;
|
||||
if (searchTerm) {
|
||||
response = await api.listAgents({ page: currentPage, page_size: 10, keyword: searchTerm });
|
||||
} else {
|
||||
response = await api.getTopDownloadedAgents(currentPage, 10);
|
||||
}
|
||||
setAgents(response.agents);
|
||||
setTotalPages(response.total_pages);
|
||||
} catch (error) {
|
||||
console.error("Error fetching agents:", error);
|
||||
} finally {
|
||||
console.log("Finished fetching agents");
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchAgents(searchValue, page);
|
||||
}, [searchValue, page, api]);
|
||||
|
||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchValue(e.target.value);
|
||||
setPage(1);
|
||||
};
|
||||
|
||||
const handleNextPage = () => {
|
||||
if (page < totalPages) {
|
||||
setPage(page + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePrevPage = () => {
|
||||
if (page > 1) {
|
||||
setPage(page - 1);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative overflow-hidden bg-white">
|
||||
|
||||
<section aria-labelledby="sale-heading" className="relative mx-auto flex max-w-7xl flex-col items-center px-4 pt-32 mb-10 text-center sm:px-6 lg:px-8">
|
||||
<div aria-hidden="true" className="absolute inset-0">
|
||||
<div className="absolute inset-0 mx-auto max-w-7xl overflow-hidden xl:px-8">
|
||||
@@ -21,96 +107,35 @@ const Marketplace = () => {
|
||||
</section>
|
||||
|
||||
<section aria-labelledby="testimonial-heading" className="relative justify-center mx-auto max-w-7xl px-4 sm:px-6 lg:py-8">
|
||||
|
||||
<form className="mb-4 flex justify-center">
|
||||
<input
|
||||
<div className="mb-4 flex justify-center">
|
||||
<Input
|
||||
placeholder="Search…"
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
className="w-3/4 max-w-xl px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring focus:ring-indigo-200"
|
||||
className="w-3/4"
|
||||
value={searchValue}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
</form>
|
||||
<ul role="list" className="divide-y divide-gray-100">
|
||||
</div>
|
||||
|
||||
<li className="flex justify-between gap-x-6 py-5">
|
||||
<div className="flex min-w-0 gap-x-4">
|
||||
<img className="h-12 w-12 flex-none rounded-full bg-gray-50" src="https://images.unsplash.com/photo-1562408590-e32931084e23?q=80&w=3270&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="" />
|
||||
<div className="min-w-0 flex-auto">
|
||||
<p className="text-sm font-semibold leading-6 text-gray-900">
|
||||
<a href="#">
|
||||
<span className="absolute inset-x-0 -top-px bottom-0"></span>
|
||||
Agent Name
|
||||
</a>
|
||||
</p>
|
||||
<p className="mt-1 flex text-xs leading-5 text-gray-500">
|
||||
<a href="mailto:leslie.alexander@example.com" className="relative truncate hover:underline">Agent description</a>
|
||||
</p>
|
||||
</div>
|
||||
{isLoading ? (
|
||||
<div className="text-center">Loading...</div>
|
||||
) : (
|
||||
<>
|
||||
<ul role="list" className="divide-y divide-gray-100">
|
||||
{agents.map((agent) => (
|
||||
<AgentRow agent={agent} key={agent.id} />
|
||||
))}
|
||||
</ul>
|
||||
<div className="flex justify-between mt-4">
|
||||
<Button onClick={handlePrevPage} disabled={page === 1}>Previous</Button>
|
||||
<span>Page {page} of {totalPages}</span>
|
||||
<Button onClick={handleNextPage} disabled={page === totalPages}>Next</Button>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-x-4">
|
||||
<div className="hidden sm:flex sm:flex-col sm:items-end">
|
||||
<p className="text-sm leading-6 text-gray-900">Category</p>
|
||||
<p className="mt-1 text-xs leading-5 text-gray-500">Last updated <time dateTime="2023-01-23T13:23Z">3h ago</time></p>
|
||||
</div>
|
||||
<svg className="h-5 w-5 flex-none text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex justify-between gap-x-6 py-5">
|
||||
<div className="flex min-w-0 gap-x-4">
|
||||
<img className="h-12 w-12 flex-none rounded-full bg-gray-50" src="https://images.unsplash.com/photo-1562408590-e32931084e23?q=80&w=3270&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="" />
|
||||
<div className="min-w-0 flex-auto">
|
||||
<p className="text-sm font-semibold leading-6 text-gray-900">
|
||||
<a href="#">
|
||||
<span className="absolute inset-x-0 -top-px bottom-0"></span>
|
||||
Agent Name
|
||||
</a>
|
||||
</p>
|
||||
<p className="mt-1 flex text-xs leading-5 text-gray-500">
|
||||
<a href="mailto:leslie.alexander@example.com" className="relative truncate hover:underline">Agent description</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-x-4">
|
||||
<div className="hidden sm:flex sm:flex-col sm:items-end">
|
||||
<p className="text-sm leading-6 text-gray-900">Category</p>
|
||||
<p className="mt-1 text-xs leading-5 text-gray-500">Last updated <time dateTime="2023-01-23T13:23Z">3h ago</time></p>
|
||||
</div>
|
||||
<svg className="h-5 w-5 flex-none text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex justify-between gap-x-6 py-5">
|
||||
<div className="flex min-w-0 gap-x-4">
|
||||
<img className="h-12 w-12 flex-none rounded-full bg-gray-50" src="https://images.unsplash.com/photo-1562408590-e32931084e23?q=80&w=3270&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="" />
|
||||
<div className="min-w-0 flex-auto">
|
||||
<p className="text-sm font-semibold leading-6 text-gray-900">
|
||||
<a href="#">
|
||||
<span className="absolute inset-x-0 -top-px bottom-0"></span>
|
||||
Agent Name
|
||||
</a>
|
||||
</p>
|
||||
<p className="mt-1 flex text-xs leading-5 text-gray-500">
|
||||
<a href="mailto:leslie.alexander@example.com" className="relative truncate hover:underline">Agent description</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-x-4">
|
||||
<div className="hidden sm:flex sm:flex-col sm:items-end">
|
||||
<p className="text-sm leading-6 text-gray-900">Category</p>
|
||||
<p className="mt-1 text-xs leading-5 text-gray-500">Last updated <time dateTime="2023-01-23T13:23Z">3h ago</time></p>
|
||||
</div>
|
||||
<svg className="h-5 w-5 flex-none text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
export default Marketplace;
|
||||
export default Marketplace;
|
||||
Reference in New Issue
Block a user