integrated navbar

This commit is contained in:
SwiftyOS
2024-11-13 14:28:15 +01:00
parent 760e2ff592
commit 9aec1f51ed
20 changed files with 380 additions and 159 deletions

View File

@@ -131,6 +131,40 @@ async def get_store_agent_details(
) from e
async def get_user_profile(
user_id: str,
) -> backend.server.v2.store.model.ProfileDetails:
logger.debug(f"Getting user profile for {user_id}")
try:
profile = await prisma.models.Profile.prisma().find_unique(
where={"userId": user_id} # type: ignore
)
if not profile:
logger.warning(f"Profile not found for user {user_id}")
raise backend.server.v2.store.exceptions.ProfileNotFoundError(
f"Profile not found for user {user_id}"
)
return backend.server.v2.store.model.ProfileDetails(
name=profile.name,
username=profile.username,
description=profile.description,
links=profile.links,
avatar_url=profile.avatarUrl,
)
except Exception as e:
logger.error(f"Error getting user profile: {str(e)}")
return backend.server.v2.store.model.ProfileDetails(
name="No Profile Data",
username="No Profile Data",
description="No Profile Data",
links=[],
avatar_url="",
)
async def get_store_creators(
featured: bool = False,
search_query: str | None = None,

View File

@@ -228,3 +228,55 @@ async def test_update_profile(mocker):
# Verify mocks called correctly
mock_profile_db.return_value.find_first.assert_called_once()
mock_profile_db.return_value.update.assert_called_once()
@pytest.mark.asyncio
async def test_get_user_profile(mocker):
# Mock data
mock_profile = prisma.models.Profile(
id="profile-id",
name="Test User",
username="testuser",
description="Test description",
links=["link1", "link2"],
avatarUrl="avatar.jpg",
createdAt=datetime.now(),
updatedAt=datetime.now(),
)
# Mock prisma calls
mock_profile_db = mocker.patch("prisma.models.Profile.prisma")
mock_profile_db.return_value.find_unique = mocker.AsyncMock(
return_value=mock_profile
)
# Call function
result = await db.get_user_profile("user-id")
# Verify results
assert result.name == "Test User"
assert result.username == "testuser"
assert result.description == "Test description"
assert result.links == ["link1", "link2"]
assert result.avatar_url == "avatar.jpg"
# Verify mock called correctly
mock_profile_db.return_value.find_unique.assert_called_once_with(
where={"userId": "user-id"}
)
@pytest.mark.asyncio
async def test_get_user_profile_not_found(mocker):
# Mock prisma calls to return None
mock_profile_db = mocker.patch("prisma.models.Profile.prisma")
mock_profile_db.return_value.find_unique = mocker.AsyncMock(return_value=None)
# Verify exception raised
with pytest.raises(backend.server.v2.store.exceptions.ProfileNotFoundError):
await db.get_user_profile("user-id")
# Verify mock called correctly
mock_profile_db.return_value.find_unique.assert_called_once_with(
where={"userId": "user-id"}
)

View File

@@ -62,3 +62,9 @@ class DatabaseError(StoreError):
"""Raised when there is an error interacting with the database"""
pass
class ProfileNotFoundError(StoreError):
"""Raised when a profile is not found"""
pass

View File

@@ -102,3 +102,11 @@ class StoreSubmissionRequest(pydantic.BaseModel):
image_urls: list[str] = []
description: str = ""
categories: list[str] = []
class ProfileDetails(pydantic.BaseModel):
name: str
username: str
description: str
links: list[str]
avatar_url: str | None = None

View File

@@ -14,8 +14,31 @@ logger = logging.getLogger(__name__)
router = fastapi.APIRouter()
##############################################
############### Agent Endpoints #############
############### Profile Endpoints ############
##############################################
@router.get("/profile", tags=["store", "private"])
async def get_profile(
user_id: typing.Annotated[
str, fastapi.Depends(autogpt_libs.auth.depends.get_user_id)
]
) -> backend.server.v2.store.model.ProfileDetails:
"""
Get the profile details for the authenticated user.
"""
try:
profile = await backend.server.v2.store.db.get_user_profile(user_id)
return profile
except Exception:
logger.exception("Exception occurred whilst getting user profile")
raise
##############################################
############### Agent Endpoints ##############
##############################################

View File

@@ -1,12 +1,19 @@
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
import { CreatorDetails as Creator, StoreAgent } from "@/lib/autogpt-server-api";
import {
CreatorDetails as Creator,
StoreAgent,
} from "@/lib/autogpt-server-api";
import { AgentsSection } from "@/components/agptui/composite/AgentsSection";
import { BreadCrumbs } from "@/components/agptui/BreadCrumbs";
import { Metadata } from "next";
import { CreatorInfoCard } from "@/components/agptui/CreatorInfoCard";
import { CreatorLinks } from "@/components/agptui/CreatorLinks";
export async function generateMetadata({ params }: { params: { creator: string } }): Promise<Metadata> {
export async function generateMetadata({
params,
}: {
params: { creator: string };
}): Promise<Metadata> {
const api = new AutoGPTServerAPI();
const creator = await api.getStoreCreator(params.creator);
@@ -21,7 +28,7 @@ export async function generateStaticParams() {
const creators = await api.getStoreCreators({ featured: true });
return creators.creators.map((creator) => ({
creator: creator.username,
lang: 'en'
lang: "en",
}));
}
@@ -31,18 +38,22 @@ export default async function Page({
params: { lang: string; creator: string };
}) {
const api = new AutoGPTServerAPI();
try {
const creator = await api.getStoreCreator(params.creator);
const creatorAgents = await api.getStoreAgents({ creator: params.creator });
return (
<>
<div className="w-full px-4 sm:px-6 md:px-10 py-4 sm:py-6 md:py-8">
<div className="w-full px-4 py-4 sm:px-6 sm:py-6 md:px-10 md:py-8">
<BreadCrumbs
items={[
{ name: "Store", link: "/store" },
{ name: creator.name, link: "#" },
]}
/>
<BreadCrumbs items={[{ name: "Store", link: "/store" }, { name: creator.name, link: "#" }]}/>
<div className="mt-4 sm:mt-6 md:mt-8 flex flex-col md:flex-row items-start gap-4 sm:gap-6 md:gap-8">
<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">
<CreatorInfoCard
username={creator.name}
@@ -54,7 +65,7 @@ export default async function Page({
/>
</div>
<div className="flex min-w-0 flex-1 flex-col gap-4 sm:gap-6 md:gap-8">
<div className="font-neue text-2xl sm:text-3xl md:text-[35px] font-normal leading-normal md:leading-[45px] text-neutral-900">
<div className="font-neue text-2xl font-normal leading-normal text-neutral-900 sm:text-3xl md:text-[35px] md:leading-[45px]">
{creator.description}
</div>
<CreatorLinks links={creator.links} />
@@ -68,17 +79,16 @@ export default async function Page({
sectionTitle={`Agents by ${creator.name}`}
/>
</div>
</div>
</>
);
} catch (error) {
return (
<div className="w-full h-screen flex items-center justify-center">
<div className="flex h-screen w-full items-center justify-center">
<div className="font-neue text-2xl text-neutral-900">
Creator not found
</div>
</div>
);
}
}
}

View File

@@ -2,14 +2,14 @@ import React from "react";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { Providers } from "@/app/providers";
import { NavBar } from "@/components/NavBar";
import { cn } from "@/lib/utils";
import { Navbar } from "@/components/agptui/Navbar";
import "./globals.css";
import TallyPopupSimple from "@/components/TallyPopup";
import { GoogleAnalytics } from "@next/third-parties/google";
import { Toaster } from "@/components/ui/toaster";
import { IconType } from "@/components/ui/icons";
import { createServerClient } from "@/lib/supabase/server";
// Import Fonts
@@ -45,8 +45,68 @@ export default async function RootLayout({
// enableSystem
disableTransitionOnChange
>
<div className="flex min-h-screen flex-col">
<NavBar />
<div className="flex min-h-screen flex-col items-center justify-center">
<Navbar
user={user}
isLoggedIn={!!user}
activeLink={"/store"}
links={[
{
name: "Agent Store",
href: "/store",
},
{
name: "Library",
href: "/library",
},
{
name: "Build",
href: "/build",
},
]}
menuItemGroups={[
{
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",
},
],
},
]}
/>
<main className="flex-1 p-4">{children}</main>
<TallyPopupSimple />
</div>

View File

@@ -38,4 +38,4 @@ export const BreadCrumbs: React.FC<BreadCrumbsProps> = ({ items }) => {
</div>
</div>
);
};
};

View File

@@ -52,4 +52,4 @@ export const ExperiencedCreator: Story = {
averageRating: 4.9,
totalRuns: 50000,
},
};
};

View File

@@ -20,7 +20,7 @@ export const CreatorInfoCard: React.FC<CreatorInfoCardProps> = ({
totalRuns,
}) => {
return (
<div
<div
className="inline-flex h-auto min-h-[500px] w-full max-w-[440px] flex-col items-start justify-between rounded-[26px] bg-violet-100 p-4 sm:h-[632px] sm:w-[440px] sm:p-6"
role="article"
aria-label={`Creator profile for ${username}`}
@@ -33,23 +33,23 @@ export const CreatorInfoCard: React.FC<CreatorInfoCardProps> = ({
</AvatarFallback>
</Avatar>
<div className="flex w-full flex-col items-start justify-start gap-1.5">
<div className="w-full font-poppins text-2xl sm:text-[35px] font-medium leading-8 sm:leading-10 text-neutral-900">
<div className="font-poppins w-full text-2xl font-medium leading-8 text-neutral-900 sm:text-[35px] sm:leading-10">
{username}
</div>
<div className="w-full font-neue text-lg sm:text-xl font-normal leading-6 sm:leading-7 text-neutral-800">
<div className="w-full font-neue text-lg font-normal leading-6 text-neutral-800 sm:text-xl sm:leading-7">
@{handle}
</div>
</div>
</div>
<div className="flex w-full flex-col items-start justify-start gap-6 sm:gap-[50px] my-4">
<div className="my-4 flex w-full flex-col items-start justify-start gap-6 sm:gap-[50px]">
<div className="flex w-full flex-col items-start justify-start gap-3">
<div className="h-px w-full bg-neutral-700" />
<div className="flex flex-col items-start justify-start gap-2.5">
<div className="w-full font-neue text-base font-medium leading-normal text-neutral-800">
Top categories
</div>
<div
<div
className="flex flex-wrap items-center gap-2.5"
role="list"
aria-label="Categories"
@@ -71,8 +71,8 @@ export const CreatorInfoCard: React.FC<CreatorInfoCardProps> = ({
<div className="flex w-full flex-col items-start justify-start gap-3">
<div className="h-px w-full bg-neutral-700" />
<div className="flex w-full flex-col sm:flex-row justify-between items-start gap-4 sm:gap-0">
<div className="w-full sm:w-[164px] flex flex-col items-start justify-start gap-2.5">
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row sm:gap-0">
<div className="flex w-full flex-col items-start justify-start gap-2.5 sm:w-[164px]">
<div className="w-full font-neue text-base font-medium leading-normal text-neutral-800">
Average rating
</div>
@@ -80,16 +80,16 @@ export const CreatorInfoCard: React.FC<CreatorInfoCardProps> = ({
<div className="font-neue text-lg font-semibold leading-7 text-neutral-800">
{averageRating.toFixed(1)}
</div>
<div
className="flex items-center gap-px"
role="img"
<div
className="flex items-center gap-px"
role="img"
aria-label={`Rating: ${averageRating} out of 5 stars`}
>
{StarRatingIcons(averageRating)}
</div>
</div>
</div>
<div className="w-full sm:w-[164px] flex flex-col items-start justify-start gap-2.5">
<div className="flex w-full flex-col items-start justify-start gap-2.5 sm:w-[164px]">
<div className="w-full font-neue text-base font-medium leading-normal text-neutral-800">
Number of runs
</div>
@@ -102,4 +102,4 @@ export const CreatorInfoCard: React.FC<CreatorInfoCardProps> = ({
</div>
</div>
);
};
};

View File

@@ -25,10 +25,7 @@ export const Default: Story = {
website: "https://example.com",
linkedin: "https://linkedin.com/in/johndoe",
github: "https://github.com/johndoe",
other: [
"https://twitter.com/johndoe",
"https://medium.com/@johndoe",
],
other: ["https://twitter.com/johndoe", "https://medium.com/@johndoe"],
},
},
};
@@ -71,4 +68,4 @@ export const MultipleOtherLinks: Story = {
],
},
},
};
};

View File

@@ -33,11 +33,9 @@ export const CreatorLinks: React.FC<CreatorLinksProps> = ({ links }) => {
</div>
<div className="flex w-full flex-wrap gap-3">
{links.map((link, index) => (
<React.Fragment key={index}>
{renderLinkButton(link)}
</React.Fragment>
<React.Fragment key={index}>{renderLinkButton(link)}</React.Fragment>
))}
</div>
</div>
);
};
};

View File

@@ -1,16 +1,27 @@
"use client";
import { IconRefresh } from "@/components/ui/icons";
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
import { useState } from "react";
interface CreditsCardProps {
credits: number;
onRefresh?: () => void;
}
const CreditsCard = ({ credits, onRefresh }: CreditsCardProps) => {
const CreditsCard = ({ credits }: CreditsCardProps) => {
const [currentCredits, setCurrentCredits] = useState(credits);
const api = new AutoGPTServerAPI();
const onRefresh = async () => {
const { credits } = await api.getUserCredit();
setCurrentCredits(credits);
};
return (
<div className="inline-flex h-[60px] items-center gap-2.5 rounded-2xl bg-neutral-200 p-4">
<div className="flex items-center gap-0.5">
<span className="font-['Geist'] text-base font-semibold leading-7 text-neutral-900">
{credits.toLocaleString()}
{currentCredits.toLocaleString()}
</span>
<span className="font-['Geist'] text-base font-normal leading-7 text-neutral-900">
credits

View File

@@ -1,7 +1,32 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Navbar } from "./Navbar";
import Navbar from "./Navbar";
import { userEvent, within } from "@storybook/test";
import { IconType } from "../ui/icons";
import { ProfileDetails } from "@/lib/autogpt-server-api/types";
import { jest } from "@jest/globals";
// Mock the API responses
const mockProfileData: ProfileDetails = {
name: "John Doe",
username: "johndoe",
description: "",
links: [],
avatar_url: "https://avatars.githubusercontent.com/u/123456789?v=4",
};
const mockCreditData = {
credits: 1500,
};
// Mock the API module
jest.mock("@/lib/autogpt-server-api", () => {
return function () {
return {
getStoreProfile: () => Promise.resolve(mockProfileData),
getUserCredit: () => Promise.resolve(mockCreditData),
};
};
});
const meta = {
title: "AGPT UI/Navbar",
@@ -12,12 +37,11 @@ const meta = {
tags: ["autodocs"],
argTypes: {
isLoggedIn: { control: "boolean" },
userName: { control: "text" },
avatarSrc: { control: "text" },
links: { control: "object" },
activeLink: { control: "text" },
avatarSrc: { control: "text" },
userEmail: { control: "text" },
menuItemGroups: { control: "object" },
params: { control: { type: "object", defaultValue: { lang: "en" } } },
},
} satisfies Meta<typeof Navbar>;
@@ -66,15 +90,12 @@ const defaultLinks = [
export const Default: Story = {
args: {
params: { lang: "en" },
isLoggedIn: true,
userName: "John Doe",
links: defaultLinks,
activeLink: "/marketplace",
avatarSrc: "https://avatars.githubusercontent.com/u/123456789?v=4",
userEmail: "john.doe@example.com",
avatarSrc: mockProfileData.avatar_url,
menuItemGroups: defaultMenuItemGroups,
credits: 1500,
onRefreshCredits: () => console.log("Refreshing credits"),
},
};
@@ -88,8 +109,6 @@ export const WithActiveLink: Story = {
export const LongUserName: Story = {
args: {
...Default.args,
userName: "Alexander Bartholomew Christopherson III",
userEmail: "alexander@example.com",
avatarSrc: "https://avatars.githubusercontent.com/u/987654321?v=4",
},
};
@@ -120,30 +139,24 @@ export const NotLoggedIn: Story = {
args: {
...Default.args,
isLoggedIn: false,
userName: undefined,
userEmail: undefined,
avatarSrc: undefined,
credits: undefined,
},
};
export const WithCredits: Story = {
args: {
...Default.args,
credits: 9999,
},
};
export const WithLargeCredits: Story = {
args: {
...Default.args,
credits: 1000000,
},
};
export const WithZeroCredits: Story = {
args: {
...Default.args,
credits: 0,
},
};

View File

@@ -5,6 +5,9 @@ import { IconType, IconLogIn } from "@/components/ui/icons";
import { MobileNavBar } from "./MobileNavBar";
import { Button } from "./Button";
import CreditsCard from "./CreditsCard";
import { ProfileDetails } from "@/lib/autogpt-server-api/types";
import { User } from "@supabase/supabase-js";
import AutoGPTServerAPIServerSide from "@/lib/autogpt-server-api/clientServer";
interface NavLink {
name: string;
@@ -12,14 +15,10 @@ interface NavLink {
}
interface NavbarProps {
user: User | null;
isLoggedIn: boolean;
userName?: string;
links: NavLink[];
activeLink: string;
avatarSrc?: string;
userEmail?: string;
credits?: number;
onRefreshCredits?: () => void;
menuItemGroups: {
groupName?: string;
items: {
@@ -31,49 +30,37 @@ interface NavbarProps {
}[];
}
{
/* <div className="w-[1408px] h-20 pl-6 pr-3 py-3 bg-white/5 rounded-bl-2xl rounded-br-2xl border border-white/50 backdrop-blur-[26px] justify-between items-center inline-flex">
<div className="justify-start items-center gap-11 flex">
<div className="w-[88.87px] h-10 relative" />
<div className="justify-start items-center gap-6 flex">
<div className="px-5 py-4 bg-neutral-800 rounded-2xl justify-start items-center gap-3 flex">
<div className="w-6 h-6 relative" />
<div className="text-neutral-50 text-xl font-medium font-['Poppins'] leading-7">Marketplace</div>
</div>
<div className="px-5 justify-start items-center gap-3 flex">
<div className="w-6 h-6 relative" />
<div className="text-neutral-900 text-xl font-medium font-['Poppins'] leading-7">Monitor</div>
</div>
<div className="px-5 py-4 rounded-2xl justify-start items-center gap-3 flex">
<div className="w-6 h-6 relative" />
<div className="text-neutral-900 text-xl font-medium font-['Poppins'] leading-7">Build</div>
</div>
</div>
</div>
<div className="justify-start items-center gap-4 flex">
<div className="p-4 bg-neutral-200 rounded-2xl justify-start items-center gap-2.5 flex">
<div className="justify-start items-center gap-0.5 flex">
<div className="text-neutral-900 text-base font-semibold font-['Geist'] leading-7">1500</div>
<div className="text-neutral-900 text-base font-normal font-['Geist'] leading-7">credits</div>
</div>
<div className="w-6 h-6 relative" />
</div>
<img className="w-[60px] h-[60px] rounded-full" src="https://via.placeholder.com/60x60" />
</div>
</div> */
}
async function getProfileData(user: User | null) {
console.log(user);
const api = new AutoGPTServerAPIServerSide();
const [profile, credits] = await Promise.all([
api.getStoreProfile(),
api.getUserCredit(),
]);
export const Navbar: React.FC<NavbarProps> = ({
return {
profile,
credits,
};
}
export const Navbar = async ({
user,
isLoggedIn,
userName,
links,
activeLink,
avatarSrc,
userEmail,
credits = 0,
onRefreshCredits,
menuItemGroups,
}) => {
}: NavbarProps) => {
let profile: ProfileDetails | null = null;
let credits: { credits: number } = { credits: 0 };
if (isLoggedIn) {
console.log("Fetching profile data");
console.log(user);
const { profile: t_profile, credits: t_credits } =
await getProfileData(user);
profile = t_profile;
credits = t_credits;
}
return (
<>
<nav className="sticky top-0 hidden h-20 w-[1408px] items-center justify-between rounded-bl-2xl rounded-br-2xl border border-white/50 bg-white/5 py-3 pl-6 pr-3 backdrop-blur-[26px] md:inline-flex">
@@ -96,12 +83,12 @@ export const Navbar: React.FC<NavbarProps> = ({
{/* Profile section */}
{isLoggedIn ? (
<div className="flex items-center gap-4">
<CreditsCard credits={credits} onRefresh={onRefreshCredits} />
{profile && <CreditsCard credits={credits.credits} />}
<ProfilePopoutMenu
menuItemGroups={menuItemGroups}
userName={userName}
userEmail={userEmail}
avatarSrc={avatarSrc}
userName={profile?.username}
userEmail={profile?.name}
avatarSrc={profile?.avatar_url}
/>
</div>
) : (
@@ -120,31 +107,32 @@ export const Navbar: React.FC<NavbarProps> = ({
{/* Mobile Navbar - Adjust positioning */}
<>
{isLoggedIn ? (
<MobileNavBar
userName={userName}
activeLink={activeLink}
menuItemGroups={[
{
groupName: "Navigation",
items: links.map((link) => ({
icon:
link.name === "Marketplace"
? IconType.Marketplace
: link.name === "Library"
? IconType.Library
: link.name === "Build"
? IconType.Builder
: IconType.LayoutDashboard,
text: link.name,
href: link.href,
})),
},
...menuItemGroups,
]}
userEmail={userEmail}
avatarSrc={avatarSrc}
className="fixed right-4 top-4 z-50"
/>
<div className="fixed right-4 top-4 z-50">
<MobileNavBar
userName={profile?.username}
activeLink={activeLink}
menuItemGroups={[
{
groupName: "Navigation",
items: links.map((link) => ({
icon:
link.name === "Agent Store"
? IconType.Marketplace
: link.name === "Library"
? IconType.Library
: link.name === "Build"
? IconType.Builder
: IconType.LayoutDashboard,
text: link.name,
href: link.href,
})),
},
...menuItemGroups,
]}
userEmail={profile?.name}
avatarSrc={profile?.avatar_url}
/>
</div>
) : (
<Link
href="/login"

View File

@@ -138,48 +138,46 @@ export const ProfilePopoutMenu: React.FC<ProfilePopoutMenuProps> = ({
</Avatar>
</button>
</PopoverTrigger>
<PopoverContent
id={popupId}
className="w-[300px] h-[380px] p-6 bg-zinc-400/70 rounded-[26px] shadow backdrop-blur-2xl flex flex-col justify-start items-start gap-4"
className="flex h-[380px] w-[300px] flex-col items-start justify-start gap-4 rounded-[26px] bg-zinc-400/70 p-6 shadow backdrop-blur-2xl"
>
{/* Header with avatar and user info */}
<div className="self-stretch inline-flex justify-start items-center gap-4">
<div className="inline-flex items-center justify-start gap-4 self-stretch">
<Avatar className="h-[60px] w-[60px]">
<AvatarImage src={avatarSrc} alt="" aria-hidden="true" />
<AvatarFallback aria-hidden="true">
{userName?.charAt(0) || "U"}
</AvatarFallback>
</Avatar>
<div className="w-[173px] h-[47px] relative">
<div className="left-0 top-0 absolute text-white text-base font-semibold font-['Geist'] leading-7">
<div className="relative h-[47px] w-[173px]">
<div className="absolute left-0 top-0 font-['Geist'] text-base font-semibold leading-7 text-white">
{userName}
</div>
<div className="left-0 top-[23px] absolute text-white text-base font-normal font-['Geist'] leading-normal">
<div className="absolute left-0 top-[23px] font-['Geist'] text-base font-normal leading-normal text-white">
{userEmail}
</div>
</div>
</div>
{/* Menu items */}
<div className="w-full rounded-[23px] flex flex-col justify-start items-start gap-1.5">
<div className="flex w-full flex-col items-start justify-start gap-1.5 rounded-[23px]">
{menuItemGroups.map((group, groupIndex) => (
<div
key={groupIndex}
className="w-full p-3.5 bg-white rounded-[18px] flex flex-col justify-start items-start gap-5"
className="flex w-full flex-col items-start justify-start gap-5 rounded-[18px] bg-white p-3.5"
>
{group.items.map((item, itemIndex) => (
<div
key={itemIndex}
className="w-full inline-flex justify-start items-center gap-2.5"
className="inline-flex w-full items-center justify-start gap-2.5"
onClick={item.onClick}
role="button"
tabIndex={0}
>
<div className="w-6 h-6 relative">
{getIcon(item.icon)}
</div>
<div className="text-neutral-800 text-base font-medium font-['Geist'] leading-normal">
<div className="relative h-6 w-6">{getIcon(item.icon)}</div>
<div className="font-['Geist'] text-base font-medium leading-normal text-neutral-800">
{item.text}
</div>
</div>
@@ -210,4 +208,3 @@ const getIcon = (icon: IconType) => {
return null;
}
};

View File

@@ -82,18 +82,17 @@ const mockCreatorInfo = {
website: "https://ailabs.com",
github: "https://github.com/ailabs",
linkedin: "https://linkedin.com/company/ailabs",
other: [
"https://twitter.com/ailabs",
"https://medium.com/@ailabs",
],
other: ["https://twitter.com/ailabs", "https://medium.com/@ailabs"],
},
};
const mockCreatorAgents = [
{
agentName: "Super SEO Optimizer",
agentImage: "https://ddz4ak4pa3d19.cloudfront.net/cache/cc/11/cc1172271dcf723a34f488a3344e82b2.jpg",
description: "Boost your website's search engine rankings with our advanced AI-powered SEO optimization tool.",
agentImage:
"https://ddz4ak4pa3d19.cloudfront.net/cache/cc/11/cc1172271dcf723a34f488a3344e82b2.jpg",
description:
"Boost your website's search engine rankings with our advanced AI-powered SEO optimization tool.",
runs: 100000,
rating: 4.9,
avatarSrc: "https://example.com/avatar1.jpg",
@@ -208,7 +207,8 @@ export const LongDescription: Story = {
...Default.args,
creatorInfo: {
...mockCreatorInfo,
description: "We are a team of passionate developers and researchers dedicated to pushing the boundaries of artificial intelligence. Our focus spans across multiple domains including natural language processing, computer vision, and reinforcement learning. With years of experience in both academia and industry, we strive to create AI agents that are not only powerful but also ethical and user-friendly. Our mission is to make AI accessible to everyone while maintaining the highest standards of quality and reliability.",
description:
"We are a team of passionate developers and researchers dedicated to pushing the boundaries of artificial intelligence. Our focus spans across multiple domains including natural language processing, computer vision, and reinforcement learning. With years of experience in both academia and industry, we strive to create AI agents that are not only powerful but also ethical and user-friendly. Our mission is to make AI accessible to everyone while maintaining the highest standards of quality and reliability.",
},
},
};

View File

@@ -75,10 +75,10 @@ export const CreatorPage: React.FC<CreatorPageProps> = ({
activeLink={activeLink}
menuItemGroups={menuItemGroups}
/>
<main className="w-full px-4 sm:px-6 md:px-10 py-4 sm:py-6 md:py-8">
<main className="w-full px-4 py-4 sm:px-6 sm:py-6 md:px-10 md:py-8">
<BreadCrumbs items={breadcrumbs} />
<div className="mt-4 sm:mt-6 md:mt-8 flex flex-col md:flex-row items-start gap-4 sm:gap-6 md:gap-8">
<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">
<CreatorInfoCard
username={creatorInfo.name}
@@ -90,7 +90,7 @@ export const CreatorPage: React.FC<CreatorPageProps> = ({
/>
</div>
<div className="flex min-w-0 flex-1 flex-col gap-4 sm:gap-6 md:gap-8">
<div className="font-neue text-2xl sm:text-3xl md:text-[35px] font-normal leading-normal md:leading-[45px] text-neutral-900">
<div className="font-neue text-2xl font-normal leading-normal text-neutral-900 sm:text-3xl md:text-[35px] md:leading-[45px]">
{creatorInfo.description}
</div>
<CreatorLinks links={creatorInfo.otherLinks} />

View File

@@ -15,6 +15,7 @@ import {
ExecutionMeta,
NodeExecutionResult,
OAuth2Credentials,
ProfileDetails,
User,
StoreAgentsResponse,
StoreAgentDetails,
@@ -58,7 +59,11 @@ export default class BaseAutoGPTServerAPI {
}
getUserCredit(): Promise<{ credits: number }> {
return this._get(`/credits`);
try {
return this._get(`/credits`);
} catch (error) {
return Promise.resolve({ credits: 0 });
}
}
getBlocks(): Promise<Block[]> {
@@ -245,6 +250,15 @@ export default class BaseAutoGPTServerAPI {
/////////// V2 STORE API /////////////////
/////////////////////////////////////////
getStoreProfile(): Promise<ProfileDetails | null> {
try {
return this._get("/store/profile");
} catch (error) {
console.error("Error fetching store profile:", error);
return Promise.resolve(null);
}
}
getStoreAgents(params?: {
featured?: boolean;
creator?: string;
@@ -320,7 +334,9 @@ export default class BaseAutoGPTServerAPI {
const token =
(await this.supabaseClient?.auth.getSession())?.data.session
?.access_token || "";
?.access_token || "no-token-found";
console.log("Token for request type: ", method, path, token);
let url = this.baseUrl + path;
if (method === "GET" && payload) {

View File

@@ -419,3 +419,11 @@ export type StoreSubmissionRequest = {
description: string;
categories: string[];
};
export type ProfileDetails = {
name: string;
username: string;
description: string;
links: string[];
avatar_url: string;
};