Added AuthProvider

This commit is contained in:
SwiftyOS
2024-11-06 13:48:30 +01:00
parent 7044782689
commit 2340e9b3f5
26 changed files with 487 additions and 219 deletions

View File

@@ -103,7 +103,7 @@ def execute_graph_block(block_id: str, data: BlockInput) -> CompletedBlockOutput
@v1_router.get(path="/credits", dependencies=[Depends(auth_middleware)])
async def get_user_credits(
user_id: Annotated[str, Depends(get_user_id)]
user_id: Annotated[str, Depends(get_user_id)],
) -> dict[str, int]:
return {"credits": await _user_credit_model.get_or_refill_credit(user_id)}
@@ -373,7 +373,7 @@ async def get_graph_run_status(
dependencies=[Depends(auth_middleware)],
)
async def get_templates(
user_id: Annotated[str, Depends(get_user_id)]
user_id: Annotated[str, Depends(get_user_id)],
) -> list[graph_db.GraphMeta]:
return await graph_db.get_graphs_meta(filter_by="template", user_id=user_id)

View File

@@ -45,3 +45,6 @@ node_modules/
*storybook.log
storybook-static
*.ignore.*
*.ign.*
.cursorrules

View File

@@ -1,11 +1,10 @@
import type { Preview } from "@storybook/react";
import { initialize, mswLoader } from 'msw-storybook-addon';
import { initialize, mswLoader } from "msw-storybook-addon";
import "../src/app/globals.css";
// Initialize MSW
initialize();
const preview: Preview = {
parameters: {
nextjs: {

View File

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

View File

@@ -18,7 +18,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { PasswordInput } from "@/components/PasswordInput";
import { FaGoogle, FaGithub, FaDiscord, FaSpinner } from "react-icons/fa";
import { useState } from "react";
import { useSupabase } from "@/components/SupabaseProvider";
import { useSupabase } from "@/components/providers/SupabaseProvider";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { Checkbox } from "@/components/ui/checkbox";

View File

@@ -1,6 +1,6 @@
"use client";
import { useSupabase } from "@/components/SupabaseProvider";
import { useSupabase } from "@/components/providers/SupabaseProvider";
import { Button } from "@/components/ui/button";
import useUser from "@/hooks/useUser";
import { useRouter } from "next/navigation";

View File

@@ -11,8 +11,8 @@ import { GoogleAnalytics } from "@next/third-parties/google";
import { Toaster } from "@/components/ui/toaster";
// Import Fonts
import { GeistSans } from 'geist/font/sans';
import { GeistMono } from 'geist/font/mono';
import { GeistSans } from "geist/font/sans";
import { GeistMono } from "geist/font/mono";
const inter = Inter({ subsets: ["latin"] });

View File

@@ -2,18 +2,21 @@
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { ThemeProviderProps } from "next-themes/dist/types";
import { ThemeProviderProps } from "next-themes";
import { TooltipProvider } from "@/components/ui/tooltip";
import SupabaseProvider from "@/components/SupabaseProvider";
import { AuthProvider } from "@/components/providers/AuthContext";
import SupabaseProvider from "@/components/providers/SupabaseProvider";
import CredentialsProvider from "@/components/integrations/credentials-provider";
export function Providers({ children, ...props }: ThemeProviderProps) {
return (
<NextThemesProvider {...props}>
<SupabaseProvider>
<CredentialsProvider>
<TooltipProvider>{children}</TooltipProvider>
</CredentialsProvider>
<AuthProvider>
<CredentialsProvider>
<TooltipProvider>{children}</TooltipProvider>
</CredentialsProvider>
</AuthProvider>
</SupabaseProvider>
</NextThemesProvider>
);

View File

@@ -18,7 +18,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { PasswordInput } from "@/components/PasswordInput";
import { FaGoogle, FaGithub, FaDiscord, FaSpinner } from "react-icons/fa";
import { useState } from "react";
import { useSupabase } from "@/components/SupabaseProvider";
import { useSupabase } from "@/components/providers/SupabaseProvider";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { Checkbox } from "@/components/ui/checkbox";

View File

@@ -7,7 +7,7 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "./ui/button";
import { useSupabase } from "./SupabaseProvider";
import { useSupabase } from "./providers/SupabaseProvider";
import { useRouter } from "next/navigation";
import useUser from "@/hooks/useUser";

View File

@@ -16,6 +16,7 @@ const meta = {
agentName: { control: "text" },
subHeading: { control: "text" },
agentImage: { control: "text" },
creatorImage: { control: "text" },
creatorName: { control: "text" },
description: { control: "text" },
runs: { control: "number" },
@@ -29,13 +30,16 @@ type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
agentName: "SEO Optimizer Pro",
subHeading: "Optimize your website's SEO",
agentName: "Personalized Morning Coffee Newsletter example of three lines",
subHeading:
"Transform ideas into breathtaking images with this AI-powered Image Generator.",
description:
"Elevate your web content with this powerful AI Webpage Copy Improver. Designed for marketers, SEO specialists, and web developers, this tool analyses and enhances website copy for maximum impact. Using advanced language models, it optimizes text for better clarity, SEO performance, and increased conversion rates. The AI examines your existing content, identifies areas for improvement, and generates refined copy that maintains your brand voice while boosting engagement. From homepage headlines to product descriptions, transform your web presence with AI-driven insights. Improve readability, incorporate targeted keywords, and craft compelling calls-to-action - all with the click of a button. Take your digital marketing to the next level with the AI Webpage Copy Improver.",
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorName: "AI Solutions Inc.",
description:
"Boost your website's search engine rankings with our advanced AI-powered SEO optimization tool.",
runs: 50000,
rating: 4.7,
onClick: () => console.log("Card clicked"),
@@ -46,10 +50,13 @@ export const LowRating: Story = {
args: {
agentName: "Data Analyzer Lite",
subHeading: "Basic data analysis tool",
description:
"A lightweight data analysis tool for basic data processing needs.",
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorName: "DataTech",
description: "A basic tool for analyzing small to medium-sized datasets.",
runs: 10000,
rating: 2.8,
onClick: () => console.log("Card clicked"),
@@ -60,11 +67,13 @@ export const HighRuns: Story = {
args: {
agentName: "CodeAssist AI",
subHeading: "Your AI coding companion",
description:
"An intelligent coding assistant that helps developers write better code faster.",
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorName: "DevTools Co.",
description:
"Get instant coding help and suggestions for multiple programming languages.",
runs: 1000000,
rating: 4.9,
onClick: () => console.log("Card clicked"),
@@ -75,11 +84,13 @@ export const LongDescription: Story = {
args: {
agentName: "MultiTasker",
subHeading: "All-in-one productivity suite",
description:
"A comprehensive productivity suite that combines task management, note-taking, and project planning into one seamless interface. Features include smart task prioritization, automated scheduling, and AI-powered insights to help you work more efficiently.",
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorName: "Productivity Plus",
description:
"An all-in-one productivity suite that helps you manage tasks, schedule meetings, track time, and collaborate with team members. Powered by advanced AI to optimize your workflow and boost efficiency.",
runs: 75000,
rating: 4.5,
onClick: () => console.log("Card clicked"),
@@ -90,10 +101,12 @@ export const ShortDescription: Story = {
args: {
agentName: "QuickTask",
subHeading: "Fast task automation",
description: "Simple and efficient task automation tool.",
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorName: "EfficientWorks",
description: "Streamline your workflow.",
runs: 50000,
rating: 4.2,
onClick: () => console.log("Card clicked"),
@@ -104,11 +117,13 @@ export const TwoLineName: Story = {
args: {
agentName: "Agent name goes here example of agent with two lines of text",
subHeading: "Multi-line agent example",
description:
"An example agent showcasing how the card handles multi-line agent names.",
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorName: "InnovativeTech Solutions",
description:
"Boost your productivity with our cutting-edge AI assistant. Manages tasks, schedules, and more.",
runs: 100000,
rating: 4.8,
onClick: () => console.log("Card clicked"),
@@ -119,11 +134,13 @@ export const TwoLineNameLongDescription: Story = {
args: {
agentName: "Advanced Natural Language Processing and Machine Learning",
subHeading: "State-of-the-art NLP & ML",
description:
"Cutting-edge natural language processing and machine learning capabilities combined in one powerful tool. Perfect for researchers and developers working on advanced AI applications.",
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorName: "AI Research Labs",
description:
"Our cutting-edge AI assistant combines state-of-the-art natural language processing and machine learning algorithms to provide unparalleled support in various domains. From text analysis and sentiment detection to predictive modeling and data visualization, this powerful tool empowers researchers, data scientists, and businesses to unlock valuable insights from complex datasets and textual information. With continuous learning capabilities and adaptable interfaces, it's the perfect companion for pushing the boundaries of AI-driven discovery and innovation.",
runs: 150000,
rating: 4.9,
onClick: () => console.log("Card clicked"),
@@ -134,11 +151,13 @@ export const WithInteraction: Story = {
args: {
agentName: "AI Writing Assistant",
subHeading: "Enhance your writing",
description:
"An AI-powered writing assistant that helps improve your writing style and clarity.",
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorName: "WordCraft AI",
description:
"Enhance your writing with AI-powered suggestions, grammar checks, and style improvements.",
runs: 200000,
rating: 4.6,
onClick: () => console.log("Card clicked"),

View File

@@ -1,12 +1,14 @@
import * as React from "react";
import Image from "next/image";
import { StarRatingIcons } from "@/components/ui/icons";
interface FeaturedStoreCardProps {
agentName: string;
subHeading: string;
agentImage: string;
creatorImage?: string;
creatorName: string;
description: string;
description: string; // Added description prop
runs: number;
rating: number;
onClick: () => void;
@@ -16,6 +18,7 @@ export const FeaturedStoreCard: React.FC<FeaturedStoreCardProps> = ({
agentName,
subHeading,
agentImage,
creatorImage,
creatorName,
description,
runs,
@@ -24,54 +27,86 @@ export const FeaturedStoreCard: React.FC<FeaturedStoreCardProps> = ({
}) => {
return (
<div
className="group flex h-[90%] w-[90%] cursor-pointer flex-col items-start justify-start gap-3 rounded-[26px] bg-neutral-200 px-[22px] pb-5 pt-[30px] font-neue text-sm tracking-tight transition-shadow duration-300 hover:shadow-lg md:max-h-[705px] lg:w-[440px] md:gap-5"
className={`group flex inline-flex h-[783px] w-[390px] cursor-pointer flex-col items-start justify-start gap-7 rounded-[26px] bg-violet-100 px-[22px] pb-5 pt-[30px] font-neue text-sm tracking-tight transition-shadow duration-300 hover:shadow-lg md:h-[755px] md:w-[440px] lg:h-[755px] lg:w-[500px]`}
onClick={onClick}
data-testid="featured-store-card"
>
<div className="flex w-full flex-col items-start justify-start gap-1 md:gap-3">
<h2 className="font-['Poppins'] text-2xl font-medium leading-tight text-neutral-900 md:text-[35px] md:leading-10">
<div
className={`flex h-[216px] flex-col items-start justify-start gap-3 self-stretch md:h-[188px]`}
>
<div
className={`self-stretch font-['Poppins'] text-[35px] font-medium leading-10 text-neutral-900`}
>
{agentName}
</h2>
<p className="font-['Geist'] text-lg font-normal leading-7 text-neutral-800 md:text-xl">
</div>
<div
className={`self-stretch font-['Geist'] text-xl font-normal leading-7 text-neutral-800`}
>
{subHeading}
</p>
<p className="font-['Geist'] text-lg font-normal leading-7 text-neutral-800 md:text-xl">
by {creatorName}
</p>
</div>
</div>
<div className="w-full flex-1">
<div className="flex flex-col gap-[18px] transition-opacity duration-300 group-hover:hidden">
<div className="relative w-full">
<Image
src={agentImage}
alt={`${agentName} preview`}
width={396}
height={396}
className="aspect-square w-full rounded-xl object-cover"
/>
</div>
<div
className={`flex h-[489px] flex-col items-start justify-start gap-[18px] self-stretch`}
>
<div
className={`self-stretch font-['Geist'] text-xl font-normal leading-7 text-neutral-800`}
>
by {creatorName}
</div>
<div className="hidden aspect-square w-full flex-col gap-[18px] transition-opacity duration-300 group-hover:flex">
<div className="flex-1 overflow-y-auto rounded-xl py-4">
<p className="font-['Geist'] text-lg font-normal leading-7 text-neutral-800 md:text-xl">
<div className="relative h-[397px] w-full">
{/* Image Container */}
<div className={` ${"group-hover:hidden"} relative h-full w-full`}>
<div
className={`relative h-[397px] w-[346px] md:h-[397px] md:w-[396px] lg:h-[397px] lg:w-[456px]`}
>
<Image
src={agentImage}
alt={`${agentName} preview`}
layout="fill"
objectFit="cover"
className="rounded-xl"
/>
{creatorImage && (
<Image
src={creatorImage}
alt={`${creatorName} image`}
width={74}
height={74}
className={`absolute left-[8.74px] top-[313px] h-[74px] w-[74px] rounded-full md:left-[10px] lg:left-[11.52px]`}
/>
)}
</div>
</div>
{/* Description Container */}
<div
className={` ${"hidden group-hover:flex"} absolute inset-0 flex flex-col overflow-y-auto rounded-xl bg-white bg-opacity-90 p-4`}
>
<p
className={`font-['Geist'] text-lg font-normal leading-7 text-neutral-800`}
>
{description}
</p>
</div>
</div>
</div>
<div className="flex w-full items-center justify-between">
<div className="font-['Inter'] text-base font-semibold leading-7 text-neutral-800 md:text-lg">
{runs.toLocaleString()} runs
</div>
<div className="flex items-center gap-[5px]">
<div className="font-['Inter'] text-base font-semibold leading-7 text-neutral-800 md:text-lg">
{rating.toFixed(1)}
<div className={`flex items-center justify-between self-stretch`}>
<div
className={`font-['Inter'] text-lg font-semibold leading-7 text-neutral-800`}
>
{runs.toLocaleString()} runs
</div>
<div className="flex w-[84px] items-center justify-start gap-px">
{StarRatingIcons(rating)}
<div className={`flex items-center gap-[5px]`}>
<div
className={`font-['Inter'] text-lg font-semibold leading-7 text-neutral-800`}
>
{rating.toFixed(1)}
</div>
<div className={`relative h-4 w-[84px]`}>
{StarRatingIcons(rating)}
</div>
</div>
</div>
</div>

View File

@@ -17,7 +17,8 @@ export const Filled: Story = {
args: {
agentName: "AI Video Generator",
subheader: "Create Viral-Ready Content in Seconds",
description: "AI Shortform Video Generator: Create Viral-Ready Content in Seconds Transform trending topics into engaging shortform videos with this cutting-edge AI Video Generator. Perfect for content creators, social media managers, and marketers looking to capitalize on the latest news and viral trends. Simply input your desired video count and source website, and watch as the AI scours the internet for the hottest stories, crafting them into attention-grabbing scripts optimized for platforms like TikTok, Instagram Reels, and YouTube Shorts. Key features include: - Customizable video count (1-5 per generation) - Flexible source selection for trending topics - AI-driven script writing following best practices for shortform content - Hooks that capture attention in the first 3 seconds - Dual narrative storytelling for maximum engagement - SEO-optimized content to boost discoverability - Integration with video generation tools for seamless production From hook to conclusion, each script is meticulously crafted to maintain viewer interest, incorporating proven techniques like 'but so' storytelling, visual metaphors, and strategically placed calls-to-action. The AI Shortform Video Generator streamlines your content creation process, allowing you to stay ahead of trends and consistently produce viral-worthy videos that resonate with your audience.",
description:
"AI Shortform Video Generator: Create Viral-Ready Content in Seconds Transform trending topics into engaging shortform videos with this cutting-edge AI Video Generator. Perfect for content creators, social media managers, and marketers looking to capitalize on the latest news and viral trends. Simply input your desired video count and source website, and watch as the AI scours the internet for the hottest stories, crafting them into attention-grabbing scripts optimized for platforms like TikTok, Instagram Reels, and YouTube Shorts. Key features include: - Customizable video count (1-5 per generation) - Flexible source selection for trending topics - AI-driven script writing following best practices for shortform content - Hooks that capture attention in the first 3 seconds - Dual narrative storytelling for maximum engagement - SEO-optimized content to boost discoverability - Integration with video generation tools for seamless production From hook to conclusion, each script is meticulously crafted to maintain viewer interest, incorporating proven techniques like 'but so' storytelling, visual metaphors, and strategically placed calls-to-action. The AI Shortform Video Generator streamlines your content creation process, allowing you to stay ahead of trends and consistently produce viral-worthy videos that resonate with your audience.",
thumbnailSrc: "https://picsum.photos/seed/video/500/350",
onClose: () => console.log("Close clicked"),
onDone: () => console.log("Done clicked"),

View File

@@ -13,7 +13,9 @@ interface PublishAgentAwaitingReviewProps {
onViewProgress: () => void;
}
export const PublishAgentAwaitingReview: React.FC<PublishAgentAwaitingReviewProps> = ({
export const PublishAgentAwaitingReview: React.FC<
PublishAgentAwaitingReviewProps
> = ({
agentName,
subheader,
description,
@@ -23,47 +25,50 @@ export const PublishAgentAwaitingReview: React.FC<PublishAgentAwaitingReviewProp
onViewProgress,
}) => {
return (
<div
className="inline-flex min-h-screen sm:h-auto sm:min-h-[824px] w-full sm:max-w-[670px] flex-col rounded-none sm:rounded-3xl border border-slate-200 bg-white shadow"
<div
className="inline-flex min-h-screen w-full flex-col rounded-none border border-slate-200 bg-white shadow sm:h-auto sm:min-h-[824px] sm:max-w-[670px] sm:rounded-3xl"
role="dialog"
aria-labelledby="modal-title"
>
<div className="w-full relative h-[180px] sm:h-[140px] rounded-none sm:rounded-t-3xl border-b border-slate-200">
<div className="w-full absolute left-0 top-[40px] sm:top-[40px] flex flex-col items-center justify-start px-6">
<div
<div className="relative h-[180px] w-full rounded-none border-b border-slate-200 sm:h-[140px] sm:rounded-t-3xl">
<div className="absolute left-0 top-[40px] flex w-full flex-col items-center justify-start px-6 sm:top-[40px]">
<div
id="modal-title"
className="text-neutral-900 text-xl sm:text-2xl font-semibold font-['Poppins'] leading-relaxed mb-4 sm:mb-2 text-center"
className="mb-4 text-center font-['Poppins'] text-xl font-semibold leading-relaxed text-neutral-900 sm:mb-2 sm:text-2xl"
>
Agent is awaiting review
</div>
<div className="text-center text-slate-500 text-sm font-normal font-['Inter'] leading-relaxed max-w-[280px] sm:max-w-none">
In the meantime you can check your progress on your Creator Dashboard page
<div className="max-w-[280px] text-center font-['Inter'] text-sm font-normal leading-relaxed text-slate-500 sm:max-w-none">
In the meantime you can check your progress on your Creator
Dashboard page
</div>
</div>
<button
<button
onClick={onClose}
className="absolute right-4 top-4 w-[38px] h-[38px] rounded-full bg-gray-100 flex items-center justify-center hover:bg-gray-200 transition-colors"
className="absolute right-4 top-4 flex h-[38px] w-[38px] items-center justify-center rounded-full bg-gray-100 transition-colors hover:bg-gray-200"
aria-label="Close dialog"
>
<IconClose size="default" className="text-neutral-600" />
</button>
</div>
<div className="flex flex-1 flex-col items-center px-6 py-6 gap-8 sm:gap-6">
<div className="flex w-full flex-col items-center gap-6 sm:gap-4 mt-4 sm:mt-0">
<div className="flex flex-1 flex-col items-center gap-8 px-6 py-6 sm:gap-6">
<div className="mt-4 flex w-full flex-col items-center gap-6 sm:mt-0 sm:gap-4">
<div className="flex flex-col items-center gap-3 sm:gap-2">
<div className="font-['Geist'] text-lg font-semibold leading-7 text-neutral-800 text-center">
<div className="text-center font-['Geist'] text-lg font-semibold leading-7 text-neutral-800">
{agentName}
</div>
<div className="font-['Geist'] text-base font-normal leading-normal text-neutral-600 text-center max-w-[280px] sm:max-w-none">
<div className="max-w-[280px] text-center font-['Geist'] text-base font-normal leading-normal text-neutral-600 sm:max-w-none">
{subheader}
</div>
</div>
<div
className="w-full h-[280px] sm:h-[350px] bg-neutral-200 rounded-xl"
<div
className="h-[280px] w-full rounded-xl bg-neutral-200 sm:h-[350px]"
role="img"
aria-label={thumbnailSrc ? "Agent thumbnail" : "Thumbnail placeholder"}
aria-label={
thumbnailSrc ? "Agent thumbnail" : "Thumbnail placeholder"
}
>
{thumbnailSrc && (
<Image
@@ -76,8 +81,8 @@ export const PublishAgentAwaitingReview: React.FC<PublishAgentAwaitingReviewProp
)}
</div>
<div
className="w-full h-[150px] sm:h-[180px] overflow-y-auto font-['Geist'] text-base font-normal leading-normal text-neutral-600"
<div
className="h-[150px] w-full overflow-y-auto font-['Geist'] text-base font-normal leading-normal text-neutral-600 sm:h-[180px]"
tabIndex={0}
role="region"
aria-label="Agent description"
@@ -87,18 +92,18 @@ export const PublishAgentAwaitingReview: React.FC<PublishAgentAwaitingReviewProp
</div>
</div>
<div className="w-full p-6 flex flex-col sm:flex-row items-center justify-center gap-4 border-t border-slate-200">
<div className="flex w-full flex-col items-center justify-center gap-4 border-t border-slate-200 p-6 sm:flex-row">
<Button
onClick={onDone}
variant="outline"
className="w-full sm:flex-1 h-12 rounded-[59px]"
className="h-12 w-full rounded-[59px] sm:flex-1"
>
Done
</Button>
<Button
onClick={onViewProgress}
variant="default"
className="w-full sm:flex-1 h-12 rounded-[59px] bg-neutral-800 hover:bg-neutral-900 text-white"
className="h-12 w-full rounded-[59px] bg-neutral-800 text-white hover:bg-neutral-900 sm:flex-1"
>
View progress
</Button>

View File

@@ -11,15 +11,51 @@ export default meta;
type Story = StoryObj<typeof PublishAgentSelect>;
const mockAgents = [
{ name: "SEO Optimizer", lastEdited: "2 days ago", imageSrc: "https://picsum.photos/seed/seo/300/200" },
{ name: "Content Writer", lastEdited: "5 days ago", imageSrc: "https://picsum.photos/seed/writer/300/200" },
{ name: "Data Analyzer", lastEdited: "1 week ago", imageSrc: "https://picsum.photos/seed/data/300/200" },
{ name: "Image Recognition", lastEdited: "2 weeks ago", imageSrc: "https://picsum.photos/seed/image/300/200" },
{ name: "Chatbot Assistant", lastEdited: "3 weeks ago", imageSrc: "https://picsum.photos/seed/chat/300/200" },
{ name: "Code Generator", lastEdited: "1 month ago", imageSrc: "https://picsum.photos/seed/code/300/200" },
{ name: "AI Translator", lastEdited: "6 weeks ago", imageSrc: "https://picsum.photos/seed/translate/300/200" },
{ name: "Voice Assistant", lastEdited: "2 months ago", imageSrc: "https://picsum.photos/seed/voice/300/200" },
{ name: "Data Visualizer", lastEdited: "3 months ago", imageSrc: "https://picsum.photos/seed/visualize/300/200" },
{
name: "SEO Optimizer",
lastEdited: "2 days ago",
imageSrc: "https://picsum.photos/seed/seo/300/200",
},
{
name: "Content Writer",
lastEdited: "5 days ago",
imageSrc: "https://picsum.photos/seed/writer/300/200",
},
{
name: "Data Analyzer",
lastEdited: "1 week ago",
imageSrc: "https://picsum.photos/seed/data/300/200",
},
{
name: "Image Recognition",
lastEdited: "2 weeks ago",
imageSrc: "https://picsum.photos/seed/image/300/200",
},
{
name: "Chatbot Assistant",
lastEdited: "3 weeks ago",
imageSrc: "https://picsum.photos/seed/chat/300/200",
},
{
name: "Code Generator",
lastEdited: "1 month ago",
imageSrc: "https://picsum.photos/seed/code/300/200",
},
{
name: "AI Translator",
lastEdited: "6 weeks ago",
imageSrc: "https://picsum.photos/seed/translate/300/200",
},
{
name: "Voice Assistant",
lastEdited: "2 months ago",
imageSrc: "https://picsum.photos/seed/voice/300/200",
},
{
name: "Data Visualizer",
lastEdited: "3 months ago",
imageSrc: "https://picsum.photos/seed/visualize/300/200",
},
];
const defaultArgs = {

View File

@@ -34,24 +34,28 @@ export const PublishAgentSelect: React.FC<PublishAgentSelectProps> = ({
};
return (
<div className="w-full max-w-[900px] bg-white rounded-3xl shadow-lg flex flex-col mx-auto">
<div className="p-4 sm:p-6 border-b border-slate-200 relative">
<div className="absolute top-4 right-4">
<div className="mx-auto flex w-full max-w-[900px] flex-col rounded-3xl bg-white shadow-lg">
<div className="relative border-b border-slate-200 p-4 sm:p-6">
<div className="absolute right-4 top-4">
<button
onClick={onClose}
className="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center hover:bg-gray-200 transition-colors"
className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-100 transition-colors hover:bg-gray-200"
aria-label="Close"
>
<IconClose size="default" className="text-neutral-600" />
</button>
</div>
<h2 className="text-neutral-900 text-xl sm:text-2xl font-semibold font-['Poppins'] leading-loose text-center mb-2">Publish Agent</h2>
<p className="text-neutral-600 text-sm sm:text-base font-normal font-['Geist'] leading-7 text-center">Select your project that you'd like to publish</p>
<h2 className="mb-2 text-center font-['Poppins'] text-xl font-semibold leading-loose text-neutral-900 sm:text-2xl">
Publish Agent
</h2>
<p className="text-center font-['Geist'] text-sm font-normal leading-7 text-neutral-600 sm:text-base">
Select your project that you'd like to publish
</p>
</div>
{agents.length === 0 ? (
<div className="h-[370px] px-4 sm:px-6 py-5 flex-col justify-center items-center gap-[29px] inline-flex">
<div className="w-full sm:w-[573px] text-center text-neutral-600 text-lg sm:text-xl font-normal font-['Geist'] leading-7">
<div className="inline-flex h-[370px] flex-col items-center justify-center gap-[29px] px-4 py-5 sm:px-6">
<div className="w-full text-center font-['Geist'] text-lg font-normal leading-7 text-neutral-600 sm:w-[573px] sm:text-xl">
Uh-oh.. It seems like you don't have any agents in your library.
<br />
We'd suggest you to create an agent in our builder first
@@ -60,34 +64,36 @@ export const PublishAgentSelect: React.FC<PublishAgentSelectProps> = ({
onClick={onOpenBuilder}
variant="default"
size="lg"
className="text-white bg-neutral-800 hover:bg-neutral-900"
className="bg-neutral-800 text-white hover:bg-neutral-900"
>
Open builder
</Button>
</div>
) : (
<>
<div className="flex-grow p-4 sm:p-6 overflow-hidden">
<div className="flex-grow overflow-hidden p-4 sm:p-6">
<h3 className="sr-only">List of agents</h3>
<div
className="h-[300px] sm:h-[400px] md:h-[500px] overflow-y-auto pr-2"
<div
className="h-[300px] overflow-y-auto pr-2 sm:h-[400px] md:h-[500px]"
role="region"
aria-labelledby="agentListHeading"
>
<div id="agentListHeading" className="sr-only">Scrollable list of agents</div>
<div id="agentListHeading" className="sr-only">
Scrollable list of agents
</div>
<div className="p-2">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
{agents.map((agent) => (
<div
key={agent.name}
className={`rounded-2xl overflow-hidden cursor-pointer transition-all ${
selectedAgent === agent.name
? "ring-4 ring-violet-600 shadow-lg"
className={`cursor-pointer overflow-hidden rounded-2xl transition-all ${
selectedAgent === agent.name
? "shadow-lg ring-4 ring-violet-600"
: "hover:shadow-md"
}`}
onClick={() => handleAgentClick(agent.name)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleAgentClick(agent.name);
}
@@ -96,7 +102,7 @@ export const PublishAgentSelect: React.FC<PublishAgentSelectProps> = ({
role="button"
aria-pressed={selectedAgent === agent.name}
>
<div className="relative h-32 sm:h-40 bg-gray-100">
<div className="relative h-32 bg-gray-100 sm:h-40">
<Image
src={agent.imageSrc}
alt={agent.name}
@@ -105,8 +111,12 @@ export const PublishAgentSelect: React.FC<PublishAgentSelectProps> = ({
/>
</div>
<div className="p-3">
<h3 className="text-neutral-800 text-sm sm:text-base font-medium font-['Geist'] leading-normal">{agent.name}</h3>
<p className="text-neutral-500 text-xs sm:text-sm font-normal font-['Geist'] leading-[14px]">Edited {agent.lastEdited}</p>
<h3 className="font-['Geist'] text-sm font-medium leading-normal text-neutral-800 sm:text-base">
{agent.name}
</h3>
<p className="font-['Geist'] text-xs font-normal leading-[14px] text-neutral-500 sm:text-sm">
Edited {agent.lastEdited}
</p>
</div>
</div>
))}
@@ -114,8 +124,8 @@ export const PublishAgentSelect: React.FC<PublishAgentSelectProps> = ({
</div>
</div>
</div>
<div className="p-4 sm:p-6 border-t border-slate-200 flex justify-between gap-4">
<div className="flex justify-between gap-4 border-t border-slate-200 p-4 sm:p-6">
<Button
onClick={onCancel}
variant="outline"
@@ -129,7 +139,7 @@ export const PublishAgentSelect: React.FC<PublishAgentSelectProps> = ({
disabled={!selectedAgent}
variant="default"
size="default"
className="w-full sm:flex-1 text-white bg-neutral-800 hover:bg-neutral-900"
className="w-full bg-neutral-800 text-white hover:bg-neutral-900 sm:flex-1"
>
Next
</Button>

View File

@@ -5,7 +5,13 @@ const meta: Meta<typeof PublishAgentInfo> = {
title: "AGPT UI/Publish Agent Info",
component: PublishAgentInfo,
tags: ["autodocs"],
decorators: [(Story) => <div style={{ maxWidth: "670px", margin: "0 auto" }}><Story /></div>],
decorators: [
(Story) => (
<div style={{ maxWidth: "670px", margin: "0 auto" }}>
<Story />
</div>
),
],
};
export default meta;
@@ -28,7 +34,8 @@ export const Filled: Story = {
thumbnailSrc: "https://picsum.photos/seed/seo/500/350",
youtubeLink: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
category: "SEO",
description: "This AI agent specializes in analyzing websites and providing actionable recommendations to improve search engine optimization. It can perform keyword research, analyze backlinks, and suggest content improvements.",
description:
"This AI agent specializes in analyzing websites and providing actionable recommendations to improve search engine optimization. It can perform keyword research, analyze backlinks, and suggest content improvements.",
},
},
};
@@ -42,7 +49,8 @@ export const ThreeImages: Story = {
thumbnailSrc: "https://picsum.photos/seed/initial/500/350",
youtubeLink: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
category: "SEO",
description: "This agent allows you to upload and manage multiple images.",
description:
"This agent allows you to upload and manage multiple images.",
additionalImages: [
"https://picsum.photos/seed/second/500/350",
"https://picsum.photos/seed/third/500/350",

View File

@@ -28,10 +28,12 @@ export const PublishAgentInfo: React.FC<PublishAgentInfoProps> = ({
initialData?.additionalImages
? [initialData.thumbnailSrc, ...initialData.additionalImages]
: initialData?.thumbnailSrc
? [initialData.thumbnailSrc]
: []
? [initialData.thumbnailSrc]
: [],
);
const [selectedImage, setSelectedImage] = React.useState<string | null>(
initialData?.thumbnailSrc || null,
);
const [selectedImage, setSelectedImage] = React.useState<string | null>(initialData?.thumbnailSrc || null);
const thumbnailsContainerRef = React.useRef<HTMLDivElement | null>(null);
const handleRemoveImage = (indexToRemove: number) => {
@@ -45,47 +47,63 @@ export const PublishAgentInfo: React.FC<PublishAgentInfoProps> = ({
};
return (
<div className="w-full max-w-[670px] bg-white rounded-3xl shadow-lg border border-slate-200 flex flex-col mx-auto">
<div className="p-6 border-b border-slate-200 relative">
<div className="absolute top-2 right-4">
<div className="mx-auto flex w-full max-w-[670px] flex-col rounded-3xl border border-slate-200 bg-white shadow-lg">
<div className="relative border-b border-slate-200 p-6">
<div className="absolute right-4 top-2">
<button
onClick={onClose}
className="w-[38px] h-[38px] rounded-full bg-gray-100 flex items-center justify-center hover:bg-gray-200 transition-colors"
className="flex h-[38px] w-[38px] items-center justify-center rounded-full bg-gray-100 transition-colors hover:bg-gray-200"
aria-label="Close"
>
<IconClose size="default" className="text-neutral-600" />
</button>
</div>
<h2 className="text-neutral-900 text-2xl font-semibold font-['Poppins'] leading-loose text-center">Publish Agent</h2>
<p className="text-neutral-600 text-base font-normal font-['Geist'] leading-7 text-center">Write a bit of details about your agent</p>
<h2 className="text-center font-['Poppins'] text-2xl font-semibold leading-loose text-neutral-900">
Publish Agent
</h2>
<p className="text-center font-['Geist'] text-base font-normal leading-7 text-neutral-600">
Write a bit of details about your agent
</p>
</div>
<div className="flex-grow p-6 space-y-5 overflow-y-auto">
<div className="flex-grow space-y-5 overflow-y-auto p-6">
<div className="space-y-1.5">
<label htmlFor="title" className="text-slate-950 text-sm font-medium font-['Geist'] leading-tight">Title</label>
<label
htmlFor="title"
className="font-['Geist'] text-sm font-medium leading-tight text-slate-950"
>
Title
</label>
<input
id="title"
type="text"
placeholder="Agent name"
defaultValue={initialData?.title}
className="w-full pl-4 pr-14 py-2.5 rounded-[55px] border border-slate-200 text-slate-500 text-base font-normal font-['Geist'] leading-normal"
className="w-full rounded-[55px] border border-slate-200 py-2.5 pl-4 pr-14 font-['Geist'] text-base font-normal leading-normal text-slate-500"
/>
</div>
<div className="space-y-1.5">
<label htmlFor="subheader" className="text-slate-950 text-sm font-medium font-['Geist'] leading-tight">Subheader</label>
<label
htmlFor="subheader"
className="font-['Geist'] text-sm font-medium leading-tight text-slate-950"
>
Subheader
</label>
<input
id="subheader"
type="text"
placeholder="A tagline for your agent"
defaultValue={initialData?.subheader}
className="w-full pl-4 pr-14 py-2.5 rounded-[55px] border border-slate-200 text-slate-500 text-base font-normal font-['Geist'] leading-normal"
className="w-full rounded-[55px] border border-slate-200 py-2.5 pl-4 pr-14 font-['Geist'] text-base font-normal leading-normal text-slate-500"
/>
</div>
<div className="space-y-2.5">
<label className="text-slate-950 text-sm font-medium font-['Geist'] leading-tight">Thumbnail images</label>
<div className="h-[350px] p-2.5 rounded-[20px] border border-neutral-300 flex items-center justify-center overflow-hidden">
<label className="font-['Geist'] text-sm font-medium leading-tight text-slate-950">
Thumbnail images
</label>
<div className="flex h-[350px] items-center justify-center overflow-hidden rounded-[20px] border border-neutral-300 p-2.5">
{selectedImage ? (
<Image
src={selectedImage}
@@ -96,19 +114,26 @@ export const PublishAgentInfo: React.FC<PublishAgentInfoProps> = ({
className="rounded-md"
/>
) : (
<p className="text-neutral-600 text-sm font-normal font-['Geist']">No images yet</p>
<p className="font-['Geist'] text-sm font-normal text-neutral-600">
No images yet
</p>
)}
</div>
<div ref={thumbnailsContainerRef} className="flex items-center space-x-2 overflow-x-auto">
<div
ref={thumbnailsContainerRef}
className="flex items-center space-x-2 overflow-x-auto"
>
{images.length === 0 ? (
<div className="w-full flex justify-center">
<div className="flex w-full justify-center">
<Button
onClick={handleAddImage}
variant="ghost"
className="w-[100px] h-[70px] bg-neutral-200 rounded-md flex flex-col items-center justify-center hover:bg-neutral-300"
className="flex h-[70px] w-[100px] flex-col items-center justify-center rounded-md bg-neutral-200 hover:bg-neutral-300"
>
<IconPlus size="lg" className="text-neutral-600" />
<span className="text-neutral-600 text-xs font-normal font-['Geist'] mt-1">Add image</span>
<span className="mt-1 font-['Geist'] text-xs font-normal text-neutral-600">
Add image
</span>
</Button>
</div>
) : (
@@ -121,12 +146,12 @@ export const PublishAgentInfo: React.FC<PublishAgentInfoProps> = ({
width={100}
height={70}
objectFit="cover"
className="rounded-md cursor-pointer"
className="cursor-pointer rounded-md"
onClick={() => setSelectedImage(src)}
/>
<button
onClick={() => handleRemoveImage(index)}
className="absolute top-1 right-1 w-5 h-5 bg-white bg-opacity-70 rounded-full flex items-center justify-center hover:bg-opacity-100 transition-opacity"
className="absolute right-1 top-1 flex h-5 w-5 items-center justify-center rounded-full bg-white bg-opacity-70 transition-opacity hover:bg-opacity-100"
aria-label="Remove image"
>
<IconClose size="sm" className="text-neutral-600" />
@@ -136,10 +161,12 @@ export const PublishAgentInfo: React.FC<PublishAgentInfoProps> = ({
<Button
onClick={handleAddImage}
variant="ghost"
className="w-[100px] h-[70px] bg-neutral-200 rounded-md flex flex-col items-center justify-center hover:bg-neutral-300"
className="flex h-[70px] w-[100px] flex-col items-center justify-center rounded-md bg-neutral-200 hover:bg-neutral-300"
>
<IconPlus size="lg" className="text-neutral-600" />
<span className="text-neutral-600 text-xs font-normal font-['Geist'] mt-1">Add image</span>
<span className="mt-1 font-['Geist'] text-xs font-normal text-neutral-600">
Add image
</span>
</Button>
</>
)}
@@ -147,13 +174,17 @@ export const PublishAgentInfo: React.FC<PublishAgentInfoProps> = ({
</div>
<div className="space-y-1.5">
<label className="text-slate-950 text-sm font-medium font-['Geist'] leading-tight">AI image generator</label>
<label className="font-['Geist'] text-sm font-medium leading-tight text-slate-950">
AI image generator
</label>
<div className="flex items-center justify-between">
<p className="text-slate-700 text-base font-normal font-['Geist'] leading-normal">You can use AI to generate a cover image for you</p>
<p className="font-['Geist'] text-base font-normal leading-normal text-slate-700">
You can use AI to generate a cover image for you
</p>
<Button
variant="default"
size="sm"
className="text-white bg-neutral-800 hover:bg-neutral-900"
className="bg-neutral-800 text-white hover:bg-neutral-900"
>
Generate
</Button>
@@ -161,22 +192,32 @@ export const PublishAgentInfo: React.FC<PublishAgentInfoProps> = ({
</div>
<div className="space-y-1.5">
<label htmlFor="youtube" className="text-slate-950 text-sm font-medium font-['Geist'] leading-tight">YouTube video link</label>
<label
htmlFor="youtube"
className="font-['Geist'] text-sm font-medium leading-tight text-slate-950"
>
YouTube video link
</label>
<input
id="youtube"
type="text"
placeholder="Paste a video link here"
defaultValue={initialData?.youtubeLink}
className="w-full pl-4 pr-14 py-2.5 rounded-[55px] border border-slate-200 text-slate-500 text-base font-normal font-['Geist'] leading-normal"
className="w-full rounded-[55px] border border-slate-200 py-2.5 pl-4 pr-14 font-['Geist'] text-base font-normal leading-normal text-slate-500"
/>
</div>
<div className="space-y-1.5">
<label htmlFor="category" className="text-slate-950 text-sm font-medium font-['Geist'] leading-tight">Category</label>
<label
htmlFor="category"
className="font-['Geist'] text-sm font-medium leading-tight text-slate-950"
>
Category
</label>
<select
id="category"
defaultValue={initialData?.category}
className="w-full pl-4 pr-5 py-2.5 rounded-[55px] border border-slate-200 text-slate-500 text-base font-normal font-['Geist'] leading-normal appearance-none"
className="w-full appearance-none rounded-[55px] border border-slate-200 py-2.5 pl-4 pr-5 font-['Geist'] text-base font-normal leading-normal text-slate-500"
>
<option value="">Select a category for your agent</option>
<option value="SEO">SEO</option>
@@ -185,17 +226,22 @@ export const PublishAgentInfo: React.FC<PublishAgentInfoProps> = ({
</div>
<div className="space-y-1.5">
<label htmlFor="description" className="text-slate-950 text-sm font-medium font-['Geist'] leading-tight">Description</label>
<label
htmlFor="description"
className="font-['Geist'] text-sm font-medium leading-tight text-slate-950"
>
Description
</label>
<textarea
id="description"
placeholder="Describe your agent and what it does"
defaultValue={initialData?.description}
className="w-full h-[100px] pl-4 pr-14 py-2.5 rounded-2xl border border-slate-200 text-slate-900 text-base font-normal font-['Geist'] leading-normal resize-none bg-white"
className="h-[100px] w-full resize-none rounded-2xl border border-slate-200 bg-white py-2.5 pl-4 pr-14 font-['Geist'] text-base font-normal leading-normal text-slate-900"
></textarea>
</div>
</div>
<div className="p-6 border-t border-slate-200 flex justify-between gap-4">
<div className="flex justify-between gap-4 border-t border-slate-200 p-6">
<Button
onClick={onBack}
variant="outline"
@@ -208,7 +254,7 @@ export const PublishAgentInfo: React.FC<PublishAgentInfoProps> = ({
onClick={onSubmit}
variant="default"
size="default"
className="w-full sm:flex-1 text-white bg-neutral-800 hover:bg-neutral-900"
className="w-full bg-neutral-800 text-white hover:bg-neutral-900 sm:flex-1"
>
Submit for review
</Button>

View File

@@ -13,12 +13,12 @@ const meta = {
nextjs: {
appDirectory: true,
navigation: {
pathname: '/search',
pathname: "/search",
query: {
searchTerm: ''
}
}
}
searchTerm: "",
},
},
},
},
tags: ["autodocs"],
argTypes: {
@@ -30,7 +30,7 @@ const meta = {
},
decorators: [
(Story) => (
<div className="w-full max-w-screen-lg mx-auto p-4">
<div className="mx-auto w-full max-w-screen-lg p-4">
<Story />
</div>
),
@@ -50,7 +50,7 @@ export const CustomStyles: Story = {
args: {
placeholder: "Enter your search query",
backgroundColor: "bg-blue-100",
iconColor: "text-blue-500",
iconColor: "text-blue-500",
textColor: "text-blue-700",
placeholderColor: "text-blue-400",
},

View File

@@ -24,57 +24,68 @@ type Story = StoryObj<typeof meta>;
const mockFeaturedAgents = [
{
agentName: "SEO Optimizer Pro",
subHeading: "Optimize your website's SEO",
agentName: "Personalized Morning Coffee Newsletter example of three lines",
subHeading:
"Transform ideas into breathtaking images with this AI-powered Image Generator.",
creatorName: "AI Solutions Inc.",
description:
"Boost your website's search engine rankings with our advanced AI-powered SEO optimization tool.",
"Elevate your web content with this powerful AI Webpage Copy Improver. Designed for marketers, SEO specialists, and web developers, this tool analyses and enhances website copy for maximum impact. Using advanced language models, it optimizes text for better clarity, SEO performance, and increased conversion rates.",
runs: 50000,
rating: 4.7,
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
},
{
agentName: "Content Writer AI",
subHeading: "Create engaging content",
creatorName: "WordCraft AI",
description:
"Generate high-quality, engaging content for your blog, social media, or marketing campaigns.",
runs: 75000,
rating: 4.5,
agentImage:
"https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
},
{
agentName: "Data Analyzer Lite",
subHeading: "Analyze data with ease",
subHeading: "Basic data analysis tool",
creatorName: "DataTech",
description: "A basic tool for analyzing small to medium-sized datasets.",
description:
"A lightweight data analysis tool for basic data processing needs.",
runs: 10000,
rating: 3.8,
rating: 2.8,
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
},
{
agentName: "Social Media Manager",
subHeading: "Automate your social presence",
creatorName: "SocialBot Inc",
agentName: "CodeAssist AI",
subHeading: "Your AI coding companion",
creatorName: "DevTools Co.",
description:
"Schedule posts, analyze engagement, and grow your social media following.",
runs: 25000,
"An intelligent coding assistant that helps developers write better code faster.",
runs: 1000000,
rating: 4.9,
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
},
{
agentName: "MultiTasker",
subHeading: "All-in-one productivity suite",
creatorName: "Productivity Plus",
description:
"A comprehensive productivity suite that combines task management, note-taking, and project planning into one seamless interface. Features include smart task prioritization, automated scheduling, and AI-powered insights to help you work more efficiently.",
runs: 75000,
rating: 4.5,
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
},
{
agentName: "QuickTask",
subHeading: "Fast task automation",
creatorName: "EfficientWorks",
description: "Simple and efficient task automation tool.",
runs: 50000,
rating: 4.2,
agentImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
},
{
agentName: "Email Marketing Assistant",
subHeading: "Optimize email campaigns",
creatorName: "EmailPro AI",
description:
"Create and optimize email campaigns with AI-powered suggestions.",
runs: 30000,
rating: 4.4,
agentImage:
creatorImage:
"https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg",
},
];
@@ -107,9 +118,11 @@ export const WithInteraction: Story = {
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const firstCard = canvas.getAllByRole("featured-store-card")[0];
await userEvent.click(firstCard);
await userEvent.hover(firstCard);
await expect(firstCard).toHaveClass("hover:shadow-lg");
const featuredCard = canvas.getByText(
"Personalized Morning Coffee Newsletter example of three lines",
);
await userEvent.hover(featuredCard);
await userEvent.click(featuredCard);
},
};

View File

@@ -59,7 +59,10 @@ export const FeaturedSection: React.FC<FeaturedSectionProps> = ({
<CarouselContent className="ml-0 transition-transform duration-500">
{/* Add transition */}
{featuredAgents.map((agent, index) => (
<CarouselItem key={index} className="ml-0 basis-auto basis-2/3 min-w-64 max-w-68">
<CarouselItem
key={index}
className="ml-0 min-w-64 max-w-68 basis-2/3 basis-auto"
>
<FeaturedStoreCard
agentName={agent.agentName}
subHeading={agent.subHeading}
@@ -75,7 +78,7 @@ export const FeaturedSection: React.FC<FeaturedSectionProps> = ({
</CarouselContent>
</Carousel>
<div className="md:mt-4 flex w-full items-center justify-between">
<div className="flex w-full items-center justify-between md:mt-4">
<div className="flex h-3 items-center gap-2">
{featuredAgents.map((_, index) => (
<div
@@ -92,13 +95,13 @@ export const FeaturedSection: React.FC<FeaturedSectionProps> = ({
{/* We can't get the exact styling of the button using the button from the component library */}
<button
onClick={handlePrevSlide}
className="flex h-10 w-10 mb:h-12 mb:w-12 items-center justify-center rounded-full border border-neutral-400 bg-white"
className="mb:h-12 mb:w-12 flex h-10 w-10 items-center justify-center rounded-full border border-neutral-400 bg-white"
>
<IconLeftArrow className="h-8 w-8" />
</button>
<button
onClick={handleNextSlide}
className="flex h-10 w-10 mb:h-12 mb:w-12 items-center justify-center rounded-full border border-neutral-900 bg-white"
className="mb:h-12 mb:w-12 flex h-10 w-10 items-center justify-center rounded-full border border-neutral-900 bg-white"
>
<IconRightArrow className="h-8 w-8" />
</button>

View File

@@ -1,4 +1,4 @@
"use client"
"use client";
import * as React from "react";
import { SearchBar } from "@/components/agptui/SearchBar";
@@ -9,7 +9,6 @@ export const HeroSection: React.FC = () => {
const router = useRouter();
function onFilterChange(selectedFilters: string[]) {
const encodedTerm = encodeURIComponent(selectedFilters.join(", "));
router.push(`/search?searchTerm=${encodedTerm}`);
}

View File

@@ -58,7 +58,6 @@ export const Page: React.FC<PageProps> = ({
featuredCreators,
menuItemGroups,
}) => {
const handleFilterChange = (selectedFilters: string[]) => {
console.log("Selected filters:", selectedFilters);
// Implement filter functionality
@@ -90,9 +89,7 @@ export const Page: React.FC<PageProps> = ({
menuItemGroups={menuItemGroups}
/>
<main className="px-4">
<HeroSection
onFilterChange={handleFilterChange}
/>
<HeroSection onFilterChange={handleFilterChange} />
<FeaturedSection
featuredAgents={featuredAgents}
onCardClick={handleCardClick}

View File

@@ -0,0 +1,91 @@
/**
* Authentication Context and Provider for Supabase Auth
*
* This module provides authentication state management using Supabase.
*
* Usage:
* 1. Wrap your app with AuthProvider:
* ```tsx
* <AuthProvider>
* <App />
* </AuthProvider>
* ```
*
* 2. Access auth state in child components using useAuth hook:
* ```tsx
* const MyComponent = () => {
* const { user, session, role } = useAuth();
*
* if (!user) return <div>Please log in</div>;
*
* return <div>Welcome {user.email}</div>;
* };
* ```
*
* The context provides:
* - user: Current authenticated User object or null
* - session: Current Session object or null
* - role: User's role string or null
*/
import { User, Session } from "@supabase/supabase-js";
import { createContext, useContext } from "react";
import { createServerClient } from "@/lib/supabase/server";
interface AuthContextType {
user: User | null;
session: Session | null;
role: string | null;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export async function AuthProvider({
children,
}: {
children: React.ReactNode;
}) {
const supabase = createServerClient();
if (!supabase) {
console.error("Could not create Supabase client");
return null;
}
const {
data: { user },
error: userError,
} = await supabase.auth.getUser();
const {
data: { session },
error: sessionError,
} = await supabase.auth.getSession();
if (userError) {
console.error("Error fetching user:", userError);
}
if (sessionError) {
console.error("Error fetching session:", sessionError);
}
return (
<AuthContext.Provider
value={{
user: user || null,
session: session || null,
role: user?.role || null,
}}
>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error("useAuth must be used inside AuthProvider");
}
return context;
};

View File

@@ -2,7 +2,7 @@
import { useEffect, useState } from "react";
import { User, Session } from "@supabase/supabase-js";
import { useSupabase } from "@/components/SupabaseProvider";
import { useSupabase } from "@/components/providers/SupabaseProvider";
const useUser = () => {
const { supabase, isLoading: isSupabaseLoading } = useSupabase();