Added theme toggle, fixed navbar

This commit is contained in:
SwiftyOS
2024-11-21 13:19:04 +01:00
parent 18b5f2047c
commit 610a5b9943
22 changed files with 340 additions and 183 deletions

View File

@@ -8,7 +8,7 @@
* - Please do NOT serve this file on production.
*/
const PACKAGE_VERSION = '2.6.4'
const PACKAGE_VERSION = '2.6.5'
const INTEGRITY_CHECKSUM = 'ca7800994cc8bfb5eb961e037c877074'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()

View File

@@ -49,7 +49,6 @@ export default async function RootLayout({
<Navbar
user={user}
isLoggedIn={!!user}
activeLink={"/store"}
links={[
{
name: "Agent Store",

View File

@@ -31,7 +31,8 @@ export const Default: Story = {
onRunAgent: () => console.log("Run agent clicked"),
name: "AI Video Generator",
creator: "Toran Richards",
shortDescription: "Transform ideas into breathtaking images with this AI-powered Image Generator.",
shortDescription:
"Transform ideas into breathtaking images with this AI-powered Image Generator.",
longDescription: `Create Viral-Ready Content in Seconds! Transform trending topics into engaging videos with this cutting-edge AI Video Generator. Perfect for content creators, social media managers, and marketers looking to quickly produce high-quality content.
Key features include:
@@ -54,8 +55,10 @@ export const LowRating: Story = {
...Default.args,
name: "Data Analyzer",
creator: "DataTech",
shortDescription: "Analyze complex datasets with machine learning algorithms",
longDescription: "A comprehensive data analysis tool that leverages machine learning to provide deep insights into your datasets. Currently in beta testing phase.",
shortDescription:
"Analyze complex datasets with machine learning algorithms",
longDescription:
"A comprehensive data analysis tool that leverages machine learning to provide deep insights into your datasets. Currently in beta testing phase.",
rating: 2.7,
runs: 5000,
categories: ["Data Analysis", "Machine Learning"],
@@ -69,8 +72,10 @@ export const HighRuns: Story = {
...Default.args,
name: "Code Assistant",
creator: "DevAI",
shortDescription: "Get AI-powered coding help for various programming languages",
longDescription: "An advanced AI coding assistant that supports multiple programming languages and frameworks. Features include code completion, refactoring suggestions, and bug detection.",
shortDescription:
"Get AI-powered coding help for various programming languages",
longDescription:
"An advanced AI coding assistant that supports multiple programming languages and frameworks. Features include code completion, refactoring suggestions, and bug detection.",
rating: 4.8,
runs: 1000000,
categories: ["Programming", "AI", "Developer Tools"],
@@ -85,7 +90,8 @@ export const WithInteraction: Story = {
name: "Task Planner",
creator: "Productivity AI",
shortDescription: "Plan and organize your tasks efficiently with AI",
longDescription: "An intelligent task management system that helps you organize, prioritize, and complete your tasks more efficiently. Features smart scheduling and AI-powered suggestions.",
longDescription:
"An intelligent task management system that helps you organize, prioritize, and complete your tasks more efficiently. Features smart scheduling and AI-powered suggestions.",
rating: 4.2,
runs: 50000,
categories: ["Productivity", "Task Management", "AI"],
@@ -94,7 +100,7 @@ export const WithInteraction: Story = {
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Test run agent button
const runButton = canvas.getByText("Run agent");
await userEvent.hover(runButton);
@@ -117,8 +123,10 @@ export const LongDescription: Story = {
...Default.args,
name: "AI Writing Assistant",
creator: "WordCraft AI",
shortDescription: "Enhance your writing with our advanced AI-powered assistant.",
longDescription: "It offers real-time suggestions for grammar, style, and tone, helps with research and fact-checking, and can even generate content ideas based on your input.",
shortDescription:
"Enhance your writing with our advanced AI-powered assistant.",
longDescription:
"It offers real-time suggestions for grammar, style, and tone, helps with research and fact-checking, and can even generate content ideas based on your input.",
rating: 4.7,
runs: 75000,
categories: ["Writing", "AI", "Content Creation"],

View File

@@ -29,48 +29,46 @@ export const AgentInfo: React.FC<AgentInfoProps> = ({
version,
}) => {
return (
<div className="w-full max-w-[396px] lg:w-[396px] px-4 sm:px-6 lg:px-0">
<div className="w-full max-w-[396px] px-4 sm:px-6 lg:w-[396px] lg:px-0">
{/* Title */}
<div className="w-full text-neutral-900 text-2xl sm:text-3xl lg:text-[35px] font-medium font-['Poppins'] leading-normal lg:leading-10 mb-3 lg:mb-4">
<div className="mb-3 w-full font-['Poppins'] text-2xl font-medium leading-normal text-neutral-900 sm:text-3xl lg:mb-4 lg:text-[35px] lg:leading-10">
{name}
</div>
{/* Creator */}
<div className="w-full flex items-center gap-1.5 mb-3 lg:mb-4">
<div className="text-neutral-800 text-base sm:text-lg lg:text-xl font-normal font-['Geist']">
<div className="mb-3 flex w-full items-center gap-1.5 lg:mb-4">
<div className="font-['Geist'] text-base font-normal text-neutral-800 sm:text-lg lg:text-xl">
by
</div>
<div className="text-neutral-800 text-base sm:text-lg lg:text-xl font-medium font-['Geist']">
<div className="font-['Geist'] text-base font-medium text-neutral-800 sm:text-lg lg:text-xl">
{creator}
</div>
</div>
{/* Short Description */}
<div className="w-full text-neutral-600 text-base sm:text-lg lg:text-xl font-normal font-['Geist'] leading-normal lg:leading-7 mb-4 lg:mb-6 line-clamp-2">
<div className="mb-4 line-clamp-2 w-full font-['Geist'] text-base font-normal leading-normal text-neutral-600 sm:text-lg lg:mb-6 lg:text-xl lg:leading-7">
{shortDescription}
</div>
{/* Run Agent Button */}
<div className="w-full mb-4 lg:mb-6">
<button className="w-full sm:w-auto px-4 sm:px-5 lg:px-6 py-3 sm:py-3.5 lg:py-4 bg-violet-600 hover:bg-violet-700 transition-colors rounded-[38px] inline-flex items-center justify-center gap-2 sm:gap-2.5">
<IconPlay className="w-5 h-5 sm:w-5 sm:h-5 lg:w-6 lg:h-6 text-white" />
<span className="text-neutral-50 text-base sm:text-lg font-medium font-['Poppins']">
<div className="mb-4 w-full lg:mb-6">
<button className="inline-flex w-full items-center justify-center gap-2 rounded-[38px] bg-violet-600 px-4 py-3 transition-colors hover:bg-violet-700 sm:w-auto sm:gap-2.5 sm:px-5 sm:py-3.5 lg:px-6 lg:py-4">
<IconPlay className="h-5 w-5 text-white sm:h-5 sm:w-5 lg:h-6 lg:w-6" />
<span className="font-['Poppins'] text-base font-medium text-neutral-50 sm:text-lg">
Run agent
</span>
</button>
</div>
{/* Rating and Runs */}
<div className="w-full flex justify-between items-center mb-4 lg:mb-6">
<div className="mb-4 flex w-full items-center justify-between lg:mb-6">
<div className="flex items-center gap-1.5 sm:gap-2">
<span className="text-neutral-800 text-base sm:text-lg font-semibold font-['Geist'] whitespace-nowrap">
<span className="whitespace-nowrap font-['Geist'] text-base font-semibold text-neutral-800 sm:text-lg">
{rating.toFixed(1)}
</span>
<div className="flex gap-0.5">
{StarRatingIcons(rating)}
</div>
<div className="flex gap-0.5">{StarRatingIcons(rating)}</div>
</div>
<div className="text-neutral-800 text-base sm:text-lg font-semibold font-['Geist'] whitespace-nowrap">
<div className="whitespace-nowrap font-['Geist'] text-base font-semibold text-neutral-800 sm:text-lg">
{runs.toLocaleString()} runs
</div>
</div>
@@ -79,25 +77,25 @@ export const AgentInfo: React.FC<AgentInfoProps> = ({
<Separator className="mb-4 lg:mb-6" />
{/* Description Section */}
<div className="w-full mb-4 lg:mb-6">
<div className="text-neutral-800 text-xs sm:text-sm font-medium mb-1.5 sm:mb-2">
<div className="mb-4 w-full lg:mb-6">
<div className="mb-1.5 text-xs font-medium text-neutral-800 sm:mb-2 sm:text-sm">
Description
</div>
<div className="w-full text-neutral-600 text-sm sm:text-base font-normal font-['Geist'] whitespace-pre-line">
<div className="w-full whitespace-pre-line font-['Geist'] text-sm font-normal text-neutral-600 sm:text-base">
{longDescription}
</div>
</div>
{/* Categories */}
<div className="w-full flex flex-col gap-1.5 sm:gap-2 mb-4 lg:mb-6">
<div className="text-neutral-800 text-xs sm:text-sm font-medium">
<div className="mb-4 flex w-full flex-col gap-1.5 sm:gap-2 lg:mb-6">
<div className="text-xs font-medium text-neutral-800 sm:text-sm">
Categories
</div>
<div className="flex flex-wrap gap-1.5 sm:gap-2">
{categories.map((category, index) => (
<div
key={index}
className="px-2 sm:px-3 py-0.5 sm:py-1 bg-white rounded-full border border-neutral-200 text-neutral-800 text-xs sm:text-sm whitespace-nowrap"
className="whitespace-nowrap rounded-full border border-neutral-200 bg-white px-2 py-0.5 text-xs text-neutral-800 sm:px-3 sm:py-1 sm:text-sm"
>
{category}
</div>
@@ -106,29 +104,29 @@ export const AgentInfo: React.FC<AgentInfoProps> = ({
</div>
{/* Rate Agent */}
<div className="w-full flex flex-col gap-1.5 sm:gap-2 mb-4 lg:mb-6">
<div className="text-neutral-800 text-xs sm:text-sm font-medium">
<div className="mb-4 flex w-full flex-col gap-1.5 sm:gap-2 lg:mb-6">
<div className="text-xs font-medium text-neutral-800 sm:text-sm">
Rate agent
</div>
<div className="flex gap-1">
{[1, 2, 3, 4, 5].map((star) => (
<IconStar
key={star}
className="w-4 h-4 sm:w-5 sm:h-5 text-neutral-300 cursor-pointer hover:text-neutral-800"
<IconStar
key={star}
className="h-4 w-4 cursor-pointer text-neutral-300 hover:text-neutral-800 sm:h-5 sm:w-5"
/>
))}
</div>
</div>
{/* Version History */}
<div className="w-full flex flex-col gap-0.5 sm:gap-1">
<div className="text-neutral-800 text-xs sm:text-sm font-medium">
<div className="flex w-full flex-col gap-0.5 sm:gap-1">
<div className="text-xs font-medium text-neutral-800 sm:text-sm">
Version history
</div>
<div className="text-neutral-600 text-xs sm:text-sm">
<div className="text-xs text-neutral-600 sm:text-sm">
Last updated {lastUpdated}
</div>
<div className="text-neutral-600 text-xs sm:text-sm">
<div className="text-xs text-neutral-600 sm:text-sm">
Version {version}
</div>
</div>

View File

@@ -11,31 +11,39 @@ export const AgentTable: React.FC<AgentTableProps> = ({ agents }) => {
return (
<div className="w-full">
{/* Table header - Hide on mobile */}
<div className="hidden md:flex flex-col">
<div className="hidden flex-col md:flex">
<div className="border-t border-neutral-300" />
<div className="flex items-center px-4 py-2">
<div className="flex items-center">
<div className="flex items-center min-w-[120px]">
<input
type="checkbox"
<div className="flex min-w-[120px] items-center">
<input
type="checkbox"
id="selectAllAgents"
aria-label="Select all agents"
className="w-5 h-5 rounded border-2 border-neutral-400 mr-4"
className="mr-4 h-5 w-5 rounded border-2 border-neutral-400"
/>
<label
htmlFor="selectAllAgents"
<label
htmlFor="selectAllAgents"
className="text-sm font-medium text-neutral-800"
>
Select all
</label>
</div>
</div>
<div className="grid grid-cols-[400px,150px,150px,100px,100px,50px] w-full items-center ml-2">
<div className="text-sm font-medium text-neutral-800">Agent info</div>
<div className="text-sm font-medium text-neutral-800">Date submitted</div>
<div className="ml-2 grid w-full grid-cols-[400px,150px,150px,100px,100px,50px] items-center">
<div className="text-sm font-medium text-neutral-800">
Agent info
</div>
<div className="text-sm font-medium text-neutral-800">
Date submitted
</div>
<div className="text-sm font-medium text-neutral-800">Status</div>
<div className="text-sm font-medium text-neutral-800 text-right">Runs</div>
<div className="text-sm font-medium text-neutral-800 text-right">Reviews</div>
<div className="text-right text-sm font-medium text-neutral-800">
Runs
</div>
<div className="text-right text-sm font-medium text-neutral-800">
Reviews
</div>
<div></div>
</div>
</div>

View File

@@ -26,9 +26,9 @@ export const AgentTableCard: React.FC<AgentTableCardProps> = ({
onEdit,
}) => {
return (
<div className="p-4 border-b border-neutral-300">
<div className="border-b border-neutral-300 p-4">
<div className="flex gap-4">
<div className="relative w-[100px] h-[56px] overflow-hidden rounded-lg bg-[#d9d9d9]">
<div className="relative h-[56px] w-[100px] overflow-hidden rounded-lg bg-[#d9d9d9]">
<Image
src={imageSrc}
alt={agentName}
@@ -37,19 +37,22 @@ export const AgentTableCard: React.FC<AgentTableCardProps> = ({
/>
</div>
<div className="flex-1">
<h3 className="text-[15px] font-medium text-neutral-800">{agentName}</h3>
<p className="text-sm text-neutral-600 line-clamp-2">{description}</p>
<h3 className="text-[15px] font-medium text-neutral-800">
{agentName}
</h3>
<p className="line-clamp-2 text-sm text-neutral-600">{description}</p>
</div>
<button onClick={onEdit} className="p-1 hover:bg-neutral-100 rounded-full h-fit">
<button
onClick={onEdit}
className="h-fit rounded-full p-1 hover:bg-neutral-100"
>
<IconMore className="h-5 w-5 text-neutral-800" />
</button>
</div>
<div className="mt-4 flex flex-wrap gap-4">
<Status status={status} />
<div className="text-sm text-neutral-600">
{dateSubmitted}
</div>
<div className="text-sm text-neutral-600">{dateSubmitted}</div>
{runs && (
<div className="text-sm text-neutral-600">
{runs.toLocaleString()} runs

View File

@@ -31,29 +31,26 @@ export const AgentTableRow: React.FC<AgentTableRowProps> = ({
const checkboxId = `agent-${id}-checkbox`;
return (
<div className="hidden md:flex items-center px-4 py-4 border-b border-neutral-300 hover:bg-neutral-50">
<div className="hidden items-center border-b border-neutral-300 px-4 py-4 hover:bg-neutral-50 md:flex">
<div className="flex items-center">
<div className="flex items-center">
<input
type="checkbox"
<input
type="checkbox"
id={checkboxId}
aria-label={`Select ${agentName}`}
className="w-5 h-5 rounded border-2 border-neutral-400 mr-4"
className="mr-4 h-5 w-5 rounded border-2 border-neutral-400"
/>
{/* Single label instead of multiple */}
<label
htmlFor={checkboxId}
className="sr-only"
>
<label htmlFor={checkboxId} className="sr-only">
Select {agentName}
</label>
</div>
</div>
<div className="grid grid-cols-[minmax(400px,1fr),180px,140px,100px,100px,40px] gap-4 w-full items-center">
<div className="grid w-full grid-cols-[minmax(400px,1fr),180px,140px,100px,100px,40px] items-center gap-4">
{/* Agent info column */}
<div className="flex items-center gap-4">
<div className="relative w-[125px] h-[70px] overflow-hidden rounded-[10px] bg-[#d9d9d9]">
<div className="relative h-[70px] w-[125px] overflow-hidden rounded-[10px] bg-[#d9d9d9]">
<Image
src={imageSrc}
alt={agentName}
@@ -65,16 +62,14 @@ export const AgentTableRow: React.FC<AgentTableRowProps> = ({
<h3 className="text-[15px] font-medium text-neutral-800">
{agentName}
</h3>
<p className="text-sm text-neutral-600 line-clamp-2">
<p className="line-clamp-2 text-sm text-neutral-600">
{description}
</p>
</div>
</div>
{/* Date column */}
<div className="text-sm text-neutral-600 pl-14">
{dateSubmitted}
</div>
<div className="pl-14 text-sm text-neutral-600">{dateSubmitted}</div>
{/* Status column */}
<div>
@@ -82,8 +77,8 @@ export const AgentTableRow: React.FC<AgentTableRowProps> = ({
</div>
{/* Runs column */}
<div className="text-sm text-neutral-600 text-right">
{runs?.toLocaleString() ?? '—'}
<div className="text-right text-sm text-neutral-600">
{runs?.toLocaleString() ?? "—"}
</div>
{/* Reviews column */}
@@ -104,7 +99,7 @@ export const AgentTableRow: React.FC<AgentTableRowProps> = ({
<div className="flex justify-end">
<button
onClick={onEdit}
className="p-1 hover:bg-neutral-100 rounded-full"
className="rounded-full p-1 hover:bg-neutral-100"
>
<IconMore className="h-5 w-5 text-neutral-800" />
</button>

View File

@@ -21,35 +21,34 @@ export const BecomeACreator: React.FC<BecomeACreatorProps> = ({
};
return (
<div className="relative h-auto min-h-[300px] md:min-h-[400px] lg:h-[459px] w-full max-w-[1360px] mx-auto px-4 md:px-6 lg:px-8">
<div className="relative mx-auto h-auto min-h-[300px] w-full max-w-[1360px] px-4 md:min-h-[400px] md:px-6 lg:h-[459px] lg:px-8">
{/* Top border */}
<div className="absolute left-0 top-0 h-px w-full bg-gray-200" />
{/* Title */}
<div className="absolute left-4 md:left-6 lg:left-8 top-[26px] text-base md:text-lg font-semibold font-poppins leading-7 text-neutral-800">
<div className="font-poppins absolute left-4 top-[26px] text-base font-semibold leading-7 text-neutral-800 md:left-6 md:text-lg lg:left-8">
{title}
</div>
{/* Content Container - Centered */}
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-[900px] text-center px-4 md:px-6 lg:px-0 pt-[40px]">
<div className="absolute left-1/2 top-1/2 w-full max-w-[900px] -translate-x-1/2 -translate-y-1/2 px-4 pt-[40px] text-center md:px-6 lg:px-0">
{/* Heading with highlighted word */}
<h2 className="text-3xl md:text-4xl lg:text-5xl font-semibold font-poppins leading-tight md:leading-[1.2] lg:leading-[54px] text-neutral-950 mb-6 md:mb-8 lg:mb-12">
Build AI agents and share{' '}
<span className="text-violet-600">your</span>
{' '}vision
<h2 className="font-poppins mb-6 text-3xl font-semibold leading-tight text-neutral-950 md:mb-8 md:text-4xl md:leading-[1.2] lg:mb-12 lg:text-5xl lg:leading-[54px]">
Build AI agents and share{" "}
<span className="text-violet-600">your</span> vision
</h2>
{/* Description */}
<p className="font-geist text-lg md:text-xl lg:text-2xl font-normal leading-relaxed md:leading-loose text-neutral-700 mb-8 md:mb-10 lg:mb-14 max-w-[90%] mx-auto">
<p className="font-geist mx-auto mb-8 max-w-[90%] text-lg font-normal leading-relaxed text-neutral-700 md:mb-10 md:text-xl md:leading-loose lg:mb-14 lg:text-2xl">
{description}
</p>
{/* Button */}
<button
<button
onClick={handleButtonClick}
className="inline-flex h-[48px] md:h-[56px] lg:h-[68px] cursor-pointer items-center justify-center rounded-[38px] bg-neutral-800 px-4 md:px-5 lg:px-6 py-3 md:py-4 lg:py-5 hover:bg-neutral-700 transition-colors"
className="inline-flex h-[48px] cursor-pointer items-center justify-center rounded-[38px] bg-neutral-800 px-4 py-3 transition-colors hover:bg-neutral-700 md:h-[56px] md:px-5 md:py-4 lg:h-[68px] lg:px-6 lg:py-5"
>
<span className="font-poppins text-base md:text-lg lg:text-xl font-medium leading-normal md:leading-relaxed lg:leading-7 text-neutral-50 whitespace-nowrap">
<span className="font-poppins whitespace-nowrap text-base font-medium leading-normal text-neutral-50 md:text-lg md:leading-relaxed lg:text-xl lg:leading-7">
{buttonText}
</span>
</button>

View File

@@ -25,11 +25,11 @@ import {
} from "../ui/icons";
import { AnimatePresence, motion } from "framer-motion";
import { Button } from "@/components/ui/button";
import { usePathname } from "next/navigation";
interface MobileNavBarProps {
userName?: string;
userEmail?: string;
activeLink: string;
avatarSrc?: string;
menuItemGroups: {
groupName?: string;
@@ -116,11 +116,13 @@ const PopoutMenuItem: React.FC<{
export const MobileNavBar: React.FC<MobileNavBarProps> = ({
userName,
userEmail,
activeLink,
avatarSrc,
menuItemGroups,
}) => {
const [isOpen, setIsOpen] = React.useState(false);
const pathname = usePathname();
const parts = pathname.split("/");
const activeLink = parts.length > 1 ? parts[1] : parts[0];
return (
<Popover open={isOpen} onOpenChange={setIsOpen}>

View File

@@ -1,13 +1,21 @@
import * as React from "react";
import Link from "next/link";
import { ProfilePopoutMenu } from "./ProfilePopoutMenu";
import { IconType, IconLogIn } from "@/components/ui/icons";
import {
IconType,
IconLogIn,
IconBuilder,
IconMarketplace,
IconLibrary,
} 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";
import { ThemeToggle } from "./ThemeToggle";
import { NavbarLink } from "./NavbarLink";
interface NavLink {
name: string;
@@ -18,7 +26,6 @@ interface NavbarProps {
user: User | null;
isLoggedIn: boolean;
links: NavLink[];
activeLink: string;
menuItemGroups: {
groupName?: string;
items: {
@@ -46,7 +53,6 @@ export const Navbar = async ({
user,
isLoggedIn,
links,
activeLink,
menuItemGroups,
}: NavbarProps) => {
let profile: ProfileDetails | null = null;
@@ -61,45 +67,37 @@ export const Navbar = async ({
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">
<div className="flex items-center space-x-10">
<div className="inline-flex h-[60px] items-center justify-start gap-6">
{links.map((link) => (
<div key={link.name} className="relative">
<Link href={link.href}>
<div
className={`text-[${activeLink === link.href ? "#272727" : "#474747"}] font-neue text-2xl font-medium leading-9 tracking-tight`}
>
{link.name}
</div>
</Link>
{activeLink === link.href && (
<div className="absolute bottom-[-30px] left-[-10px] h-1.5 w-full bg-[#282828]" />
)}
</div>
<NavbarLink key={link.name} name={link.name} href={link.href} />
))}
</div>
{/* Profile section */}
{isLoggedIn ? (
<div className="flex items-center gap-4">
{profile && <CreditsCard credits={credits.credits} />}
<ProfilePopoutMenu
menuItemGroups={menuItemGroups}
userName={profile?.username}
userEmail={profile?.name}
avatarSrc={profile?.avatar_url}
/>
</div>
) : (
<Link href="/login">
<Button
variant="default"
size="sm"
className="flex items-center justify-end space-x-2"
>
<IconLogIn className="h-5 w-5" />
<span>Log In</span>
</Button>
</Link>
)}
<div className="flex items-center gap-4">
{isLoggedIn ? (
<div className="flex items-center gap-4">
{profile && <CreditsCard credits={credits.credits} />}
<ProfilePopoutMenu
menuItemGroups={menuItemGroups}
userName={profile?.username}
userEmail={profile?.name}
avatarSrc={profile?.avatar_url}
/>
</div>
) : (
<Link href="/login">
<Button
variant="default"
size="sm"
className="flex items-center justify-end space-x-2"
>
<IconLogIn className="h-5 w-5" />
<span>Log In</span>
</Button>
</Link>
)}
<ThemeToggle />
</div>
</nav>
{/* Mobile Navbar - Adjust positioning */}
<>
@@ -107,7 +105,6 @@ export const Navbar = async ({
<div className="fixed right-4 top-4 z-50">
<MobileNavBar
userName={profile?.username}
activeLink={activeLink}
menuItemGroups={[
{
groupName: "Navigation",

View File

@@ -0,0 +1,53 @@
"use client";
import Link from "next/link";
import {
IconType,
IconMarketplace,
IconBuilder,
IconLibrary,
} from "@/components/ui/icons";
import { usePathname } from "next/navigation";
interface NavbarLinkProps {
name: string;
href: string;
}
export const NavbarLink = ({ name, href }: NavbarLinkProps) => {
const pathname = usePathname();
const parts = pathname.split("/");
const activeLink = "/" + (parts.length > 2 ? parts[2] : parts[1]);
return (
<div
className={`px-5 py-4 ${
activeLink === href ? "rounded-2xl bg-neutral-800" : ""
} flex items-center justify-start gap-3`}
>
{href === "/store" && (
<IconMarketplace
className={`h-6 w-6 ${activeLink === href ? "text-white" : ""}`}
/>
)}
{href === "/build" && (
<IconBuilder
className={`h-6 w-6 ${activeLink === href ? "text-white" : ""}`}
/>
)}
{href === "/library" && (
<IconLibrary
className={`h-6 w-6 ${activeLink === href ? "text-white" : ""}`}
/>
)}
<Link href={href}>
<div
className={`font-['Poppins'] text-xl font-medium leading-7 ${
activeLink === href ? "text-neutral-50" : "text-neutral-900"
}`}
>
{name}
</div>
</Link>
</div>
);
};

View File

@@ -14,7 +14,7 @@ export interface ProfileInfoFormProps {
selectedCategories: string[];
}
export const AVAILABLE_CATEGORIES = [
export const AVAILABLE_CATEGORIES = [
"Entertainment",
"Blog",
"Business",
@@ -142,16 +142,16 @@ export const ProfileInfoForm = ({
<hr className="my-8 border-neutral-300" />
<div className="h-[50px] flex justify-end items-center gap-3">
<div className="flex h-[50px] items-center justify-end gap-3">
<Button
variant="secondary"
className="h-[50px] px-6 py-3 rounded-[35px] font-['Geist'] text-base font-medium bg-neutral-200 text-neutral-800 hover:bg-neutral-300 transition-colors"
className="h-[50px] rounded-[35px] bg-neutral-200 px-6 py-3 font-['Geist'] text-base font-medium text-neutral-800 transition-colors hover:bg-neutral-300"
>
Cancel
</Button>
<Button
variant="primary"
className="h-[50px] px-6 py-3 rounded-[35px] font-['Geist'] text-base font-medium bg-neutral-800 text-white hover:bg-neutral-900 transition-colors"
className="h-[50px] rounded-[35px] bg-neutral-800 px-6 py-3 font-['Geist'] text-base font-medium text-white transition-colors hover:bg-neutral-900"
>
Save changes
</Button>

View File

@@ -154,13 +154,13 @@ export const SettingsInputForm = ({
<div className="flex gap-3">
<Button
variant="secondary"
className="h-[50px] px-6 py-3 rounded-[35px] font-['Geist'] text-base font-medium bg-neutral-200 text-neutral-800 hover:bg-neutral-300 transition-colors"
className="h-[50px] rounded-[35px] bg-neutral-200 px-6 py-3 font-['Geist'] text-base font-medium text-neutral-800 transition-colors hover:bg-neutral-300"
>
Cancel
</Button>
<Button
variant="primary"
className="h-[50px] px-6 py-3 rounded-[35px] font-['Geist'] text-base font-medium bg-neutral-800 text-white hover:bg-neutral-900 transition-colors"
className="h-[50px] rounded-[35px] bg-neutral-800 px-6 py-3 font-['Geist'] text-base font-medium text-white transition-colors hover:bg-neutral-900"
>
Save changes
</Button>

View File

@@ -10,10 +10,10 @@ const meta = {
tags: ["autodocs"],
argTypes: {
status: {
control: 'select',
options: ['draft', 'awaiting_review', 'approved', 'rejected'],
}
}
control: "select",
options: ["draft", "awaiting_review", "approved", "rejected"],
},
},
} satisfies Meta<typeof Status>;
export default meta;
@@ -52,4 +52,4 @@ export const AllStatuses: Story = {
<Status status="rejected" />
</div>
),
};
};

View File

@@ -6,31 +6,34 @@ interface StatusProps {
status: StatusType;
}
const statusConfig: Record<StatusType, {
bgColor: string;
dotColor: string;
text: string;
}> = {
const statusConfig: Record<
StatusType,
{
bgColor: string;
dotColor: string;
text: string;
}
> = {
draft: {
bgColor: "bg-blue-50",
dotColor: "bg-blue-500",
text: "Draft"
text: "Draft",
},
awaiting_review: {
bgColor: "bg-amber-50",
dotColor: "bg-amber-500",
text: "Awaiting review"
text: "Awaiting review",
},
approved: {
bgColor: "bg-green-50",
dotColor: "bg-green-500",
text: "Approved"
text: "Approved",
},
rejected: {
bgColor: "bg-red-50",
dotColor: "bg-red-500",
text: "Rejected"
}
text: "Rejected",
},
};
export const Status: React.FC<StatusProps> = ({ status }) => {
@@ -39,13 +42,15 @@ export const Status: React.FC<StatusProps> = ({ status }) => {
}
const config = statusConfig[status];
return (
<div className={`px-2.5 py-1 ${config.bgColor} rounded-[26px] flex items-center gap-1.5`}>
<div className={`w-3 h-3 ${config.dotColor} rounded-full`} />
<div className="text-neutral-600 text-sm font-normal font-['Geist'] leading-tight">
<div
className={`px-2.5 py-1 ${config.bgColor} flex items-center gap-1.5 rounded-[26px]`}
>
<div className={`h-3 w-3 ${config.dotColor} rounded-full`} />
<div className="font-['Geist'] text-sm font-normal leading-tight text-neutral-600">
{config.text}
</div>
</div>
);
};
};

View File

@@ -36,7 +36,7 @@ export const StoreCard: React.FC<StoreCardProps> = ({
tabIndex={0}
aria-label={`${agentName} agent card`}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
if (e.key === "Enter" || e.key === " ") {
handleClick();
}
}}
@@ -53,13 +53,10 @@ export const StoreCard: React.FC<StoreCardProps> = ({
<div className="-mt-8 flex flex-col px-4">
{!hideAvatar ? (
<Avatar className="mb-2 h-16 w-16">
<AvatarImage
src={avatarSrc}
alt={`${agentName} creator avatar`}
/>
<AvatarFallback
<AvatarImage src={avatarSrc} alt={`${agentName} creator avatar`} />
<AvatarFallback
className="h-16 w-16"
role="img"
role="img"
aria-label={`${agentName} creator initial`}
>
{agentName.charAt(0)}
@@ -71,7 +68,7 @@ export const StoreCard: React.FC<StoreCardProps> = ({
<h2 className="mb-1 font-neue text-xl font-bold tracking-tight text-neutral-900">
{agentName}
</h2>
<div className="flex items-center justify-between mb-4">
<div className="mb-4 flex items-center justify-between">
<div className="font-neue text-base font-medium tracking-tight text-neutral-900">
{runs.toLocaleString()}+ runs
</div>
@@ -79,7 +76,7 @@ export const StoreCard: React.FC<StoreCardProps> = ({
<div className="mr-2 font-neue text-base font-medium tracking-tight text-neutral-900">
{rating.toFixed(1)}
</div>
<div
<div
className="inline-flex items-center justify-start gap-px"
role="img"
aria-label={`Rating: ${rating.toFixed(1)} out of 5 stars`}

View File

@@ -0,0 +1,45 @@
"use client";
import * as React from "react";
import { useTheme } from "next-themes";
import { IconMoon, IconSun } from "@/components/ui/icons";
import { Button } from "./Button";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return (
<div className="relative h-6 w-12 rounded-full bg-gray-200 transition-colors dark:bg-gray-600">
<div className="absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white" />
</div>
);
}
return (
<div
className="relative h-6 w-12 cursor-pointer rounded-full bg-gray-200 transition-colors dark:bg-gray-600"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
role="button"
tabIndex={0}
>
<div
className={`absolute left-0.5 top-0.5 flex h-5 w-5 items-center justify-center rounded-full bg-white transition-transform duration-200 ${
theme === "dark" ? "translate-x-6" : ""
}`}
>
{theme === "light" ? (
<IconSun className="h-4 w-4 text-yellow-500" />
) : (
<IconMoon className="h-4 w-4 text-gray-700" />
)}
</div>
<span className="sr-only">Toggle theme</span>
</div>
);
}

View File

@@ -74,7 +74,8 @@ const mockMenuItemGroups = [
const mockAgentInfo = {
name: "AI Video Generator",
creator: "Toran Richards",
shortDescription: "Transform ideas into breathtaking images with this AI-powered Image Generator.",
shortDescription:
"Transform ideas into breathtaking images with this AI-powered Image Generator.",
longDescription: `Create Viral-Ready Content in Seconds! Transform trending topics into engaging videos with this cutting-edge AI Video Generator. Perfect for content creators, social media managers, and marketers looking to quickly produce high-quality content.
Key features include:

View File

@@ -116,11 +116,11 @@ export const AgentPage: React.FC<AgentPageProps> = ({
<AgentImages images={agentImages} />
</div>
</div>
<Separator className="my-6" />
<AgentsSection
agents={otherAgentsByCreator.map((agent) => ({
slug: agent.agentName.toLowerCase().replace(/\s+/g, '-'),
slug: agent.agentName.toLowerCase().replace(/\s+/g, "-"),
agent_name: agent.agentName,
agent_image: agent.agentImage,
creator: agentInfo.creator,
@@ -136,7 +136,7 @@ export const AgentPage: React.FC<AgentPageProps> = ({
<Separator className="my-6" />
<AgentsSection
agents={similarAgents.map((agent) => ({
slug: agent.agentName.toLowerCase().replace(/\s+/g, '-'),
slug: agent.agentName.toLowerCase().replace(/\s+/g, "-"),
agent_name: agent.agentName,
agent_image: agent.agentImage,
creator: agentInfo.creator,

View File

@@ -138,7 +138,9 @@ export const ManyAgents: Story = {
.map((agent, index) => ({
...agent,
agentName: `Agent ${index + 1}`,
status: ["approved", "awaiting_review", "draft", "rejected"][index % 4] as StatusType,
status: ["approved", "awaiting_review", "draft", "rejected"][
index % 4
] as StatusType,
rating: Math.round((4 + Math.random()) * 10) / 10,
runs: Math.floor(Math.random() * 2000) + 500,
onEdit: () => console.log(`Edit Agent ${index + 1}`),

View File

@@ -66,31 +66,34 @@ export const CreatorDashboardPage: React.FC<CreatorDashboardPageProps> = ({
<main className="flex-1 px-4 py-6 md:px-10 md:py-8">
{/* Header Section */}
<div className="mb-6 md:mb-8">
<h1 className="text-2xl md:text-[32px] font-medium leading-tight md:leading-[38px] text-neutral-900">
<h1 className="text-2xl font-medium leading-tight text-neutral-900 md:text-[32px] md:leading-[38px]">
Agent dashboard
</h1>
</div>
{/* Submit Agent Section */}
<div className="mb-6 md:mb-8">
<h2 className="mb-1 text-lg md:text-[20px] font-medium leading-tight md:leading-[24px] text-neutral-900">
<h2 className="mb-1 text-lg font-medium leading-tight text-neutral-900 md:text-[20px] md:leading-[24px]">
Submit an agent
</h2>
<div className="flex flex-col md:flex-row md:justify-between md:items-end gap-4 md:gap-0">
<p className="text-sm md:text-[14px] leading-[20px] text-neutral-600">
Select from the list of agents you currently already have, or upload from your local machine.
<div className="flex flex-col gap-4 md:flex-row md:items-end md:justify-between md:gap-0">
<p className="text-sm leading-[20px] text-neutral-600 md:text-[14px]">
Select from the list of agents you currently already have, or
upload from your local machine.
</p>
<button className="w-full md:w-auto h-12 px-5 py-3 bg-neutral-800 rounded-[38px] inline-flex items-center justify-center md:justify-start gap-2.5 md:ml-4">
<span className="text-white text-base font-medium font-['Geist'] leading-normal">Submit agent</span>
<button className="inline-flex h-12 w-full items-center justify-center gap-2.5 rounded-[38px] bg-neutral-800 px-5 py-3 md:ml-4 md:w-auto md:justify-start">
<span className="font-['Geist'] text-base font-medium leading-normal text-white">
Submit agent
</span>
</button>
</div>
</div>
<Separator className="my-6 md:my-8 bg-neutral-300" />
<Separator className="my-6 bg-neutral-300 md:my-8" />
{/* Agents List Section */}
<div className="mb-4 md:mb-6">
<h2 className="text-lg md:text-[20px] font-medium leading-tight md:leading-[24px] text-neutral-900">
<h2 className="text-lg font-medium leading-tight text-neutral-900 md:text-[20px] md:leading-[24px]">
Your uploaded agents
</h2>
</div>

View File

@@ -1564,6 +1564,48 @@ export const IconExternalLink = createIcon((props) => (
</svg>
));
export const IconSun = createIcon((props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<circle cx="12" cy="12" r="4" />
<path d="M12 2v2" />
<path d="M12 20v2" />
<path d="m4.93 4.93 1.41 1.41" />
<path d="m17.66 17.66 1.41 1.41" />
<path d="M2 12h2" />
<path d="M20 12h2" />
<path d="m6.34 17.66-1.41 1.41" />
<path d="m19.07 4.93-1.41 1.41" />
</svg>
));
export const IconMoon = createIcon((props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
</svg>
));
export const IconBuilder = createIcon((props) => <IconToyBrick {...props} />);
export enum IconType {