Add skeleton components and loading states

This commit is contained in:
Abhimanyu Yadav
2025-05-18 17:16:08 +05:30
parent 82e3a485f0
commit d22464a75e
20 changed files with 986 additions and 371 deletions

View File

@@ -1,4 +1,5 @@
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import { Plus } from "lucide-react";
import React, { ButtonHTMLAttributes } from "react";
@@ -8,7 +9,11 @@ interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
description?: string;
}
const Block: React.FC<Props> = ({ title, description, className, ...rest }) => {
interface BlockComponent extends React.FC<Props> {
Skeleton: React.FC<{ className?: string }>;
}
const Block: BlockComponent = ({ title, description, className, ...rest }) => {
return (
<Button
className={cn(
@@ -43,4 +48,18 @@ const Block: React.FC<Props> = ({ title, description, className, ...rest }) => {
);
};
const BlockSkeleton = () => {
return (
<Skeleton className="flex h-16 w-full min-w-[7.5rem] animate-pulse items-center justify-start space-x-3 rounded-[0.75rem] bg-zinc-100 px-[0.875rem] py-[0.625rem]">
<div className="flex flex-1 flex-col items-start gap-0.5">
<Skeleton className="h-[1.375rem] w-24 rounded bg-zinc-200" />
<Skeleton className="h-5 w-32 rounded bg-zinc-200" />
</div>
<Skeleton className="h-7 w-7 rounded-[0.5rem] bg-zinc-200" />
</Skeleton>
);
};
Block.Skeleton = BlockSkeleton;
export default Block;

View File

@@ -1,6 +1,6 @@
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import { Plus } from "lucide-react";
import Image from "next/image";
import React, { ButtonHTMLAttributes } from "react";
@@ -11,7 +11,11 @@ interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
number_of_blocks?: number;
}
const Integration: React.FC<Props> = ({
interface IntegrationComponent extends React.FC<Props> {
Skeleton: React.FC<{ className?: string }>;
}
const Integration: IntegrationComponent = ({
title,
icon_url,
description,
@@ -23,6 +27,7 @@ const Integration: React.FC<Props> = ({
<Button
className={cn(
"group flex h-16 w-full min-w-[7.5rem] items-center justify-start space-x-3 whitespace-normal rounded-[0.75rem] bg-zinc-50 px-[0.875rem] py-[0.625rem] text-start shadow-none hover:bg-zinc-100 focus:ring-0 active:border active:border-zinc-300 active:bg-zinc-50 disabled:pointer-events-none",
className,
)}
{...rest}
>
@@ -54,4 +59,28 @@ const Integration: React.FC<Props> = ({
);
};
const IntegrationSkeleton: React.FC<{ className?: string }> = ({
className,
}) => {
return (
<Skeleton
className={cn(
"flex h-16 w-full min-w-[7.5rem] animate-pulse items-center justify-start space-x-3 rounded-[0.75rem] bg-zinc-100 px-[0.875rem] py-[0.625rem]",
className,
)}
>
<Skeleton className="h-[2.625rem] w-[2.625rem] rounded-[0.5rem] bg-zinc-200" />
<div className="flex flex-1 flex-col items-start gap-0.5">
<div className="flex w-full items-center justify-between">
<Skeleton className="h-[1.375rem] w-24 rounded bg-zinc-200" />
<Skeleton className="h-[1.375rem] w-[1.6875rem] rounded-[1.25rem] bg-zinc-200" />
</div>
<Skeleton className="h-5 w-[80%] rounded bg-zinc-200" />
</div>
</Skeleton>
);
};
Integration.Skeleton = IntegrationSkeleton;
export default Integration;

View File

@@ -1,4 +1,5 @@
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import Image from "next/image";
import React, { ButtonHTMLAttributes } from "react";
@@ -8,7 +9,12 @@ interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
icon_url?: string;
}
const IntegrationChip: React.FC<Props> = ({
// Define the component type with Skeleton property
interface IntegrationChipComponent extends React.FC<Props> {
Skeleton: React.FC;
}
const IntegrationChip: IntegrationChipComponent = ({
icon_url,
name,
className,
@@ -39,4 +45,16 @@ const IntegrationChip: React.FC<Props> = ({
);
};
// Skeleton subcomponent
const IntegrationChipSkeleton: React.FC = () => {
return (
<Skeleton className="flex h-[3.25rem] w-full min-w-[7.5rem] gap-2 rounded-[0.5rem] bg-zinc-100 p-2 pr-3">
<Skeleton className="h-9 w-12 rounded-[0.5rem] bg-zinc-200" />
<Skeleton className="h-5 w-24 self-center rounded-sm bg-zinc-200" />
</Skeleton>
);
};
IntegrationChip.Skeleton = IntegrationChipSkeleton;
export default IntegrationChip;

View File

@@ -1,4 +1,5 @@
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import { Plus } from "lucide-react";
import Image from "next/image";
@@ -11,7 +12,11 @@ interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
image_url?: string;
}
const MarketplaceAgentBlock: React.FC<Props> = ({
interface MarketplaceAgentBlockComponent extends React.FC<Props> {
Skeleton: React.FC<{ className?: string }>;
}
const MarketplaceAgentBlock: MarketplaceAgentBlockComponent = ({
title,
image_url,
creator_name,
@@ -23,6 +28,7 @@ const MarketplaceAgentBlock: React.FC<Props> = ({
<Button
className={cn(
"group flex h-[4.375rem] w-full min-w-[7.5rem] items-center justify-start gap-3 whitespace-normal rounded-[0.75rem] bg-zinc-50 p-[0.625rem] pr-[0.875rem] text-start shadow-none hover:bg-zinc-100 focus:ring-0 active:border active:border-zinc-300 active:bg-zinc-100 disabled:pointer-events-none",
className,
)}
{...rest}
>
@@ -75,4 +81,30 @@ const MarketplaceAgentBlock: React.FC<Props> = ({
);
};
const MarketplaceAgentBlockSkeleton: React.FC<{ className?: string }> = ({
className,
}) => {
return (
<Skeleton
className={cn(
"flex h-[4.375rem] w-full min-w-[7.5rem] animate-pulse items-center justify-start gap-3 rounded-[0.75rem] bg-zinc-100 p-[0.625rem] pr-[0.875rem]",
className,
)}
>
<Skeleton className="h-[3.125rem] w-[5.625rem] rounded-[0.375rem] bg-zinc-200" />
<div className="flex flex-1 flex-col items-start gap-0.5">
<Skeleton className="h-[1.375rem] w-24 rounded bg-zinc-200" />
<div className="flex items-center gap-1">
<Skeleton className="h-5 w-16 rounded bg-zinc-200" />
<Skeleton className="h-5 w-16 rounded bg-zinc-200" />
</div>
</div>
<Skeleton className="h-7 w-7 rounded-[0.5rem] bg-zinc-200" />
</Skeleton>
);
};
MarketplaceAgentBlock.Skeleton = MarketplaceAgentBlockSkeleton;
export default MarketplaceAgentBlock;

View File

@@ -1,4 +1,5 @@
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import { ArrowUpRight } from "lucide-react";
import React, { ButtonHTMLAttributes } from "react";
@@ -7,7 +8,11 @@ interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
content?: string;
}
const SearchHistoryChip: React.FC<Props> = ({
interface SearchHistoryChipComponent extends React.FC<Props> {
Skeleton: React.FC<{ className?: string }>;
}
const SearchHistoryChip: SearchHistoryChipComponent = ({
content,
className,
...rest
@@ -28,4 +33,16 @@ const SearchHistoryChip: React.FC<Props> = ({
);
};
const SearchHistoryChipSkeleton: React.FC<{ className?: string }> = ({
className,
}) => {
return (
<Skeleton
className={cn("h-[2.25rem] w-32 rounded-[1.5rem] bg-zinc-100", className)}
/>
);
};
SearchHistoryChip.Skeleton = SearchHistoryChipSkeleton;
export default SearchHistoryChip;

View File

@@ -1,4 +1,5 @@
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import { Plus } from "lucide-react";
import Image from "next/image";
@@ -11,7 +12,11 @@ interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
image_url?: string;
}
const UGCAgentBlock: React.FC<Props> = ({
interface UGCAgentBlockComponent extends React.FC<Props> {
Skeleton: React.FC<{ className?: string }>;
}
const UGCAgentBlock: UGCAgentBlockComponent = ({
title,
image_url,
edited_time,
@@ -23,6 +28,7 @@ const UGCAgentBlock: React.FC<Props> = ({
<Button
className={cn(
"group flex h-[4.375rem] w-full min-w-[7.5rem] items-center justify-start gap-3 whitespace-normal rounded-[0.75rem] bg-zinc-50 p-[0.625rem] pr-[0.875rem] text-start shadow-none hover:bg-zinc-100 focus:ring-0 active:border active:border-zinc-300 active:bg-zinc-100 disabled:pointer-events-none",
className,
)}
{...rest}
>
@@ -76,4 +82,29 @@ const UGCAgentBlock: React.FC<Props> = ({
);
};
const UGCAgentBlockSkeleton: React.FC<{ className?: string }> = ({
className,
}) => {
return (
<Skeleton
className={cn(
"flex h-[4.375rem] w-full min-w-[7.5rem] animate-pulse items-center justify-start gap-3 rounded-[0.75rem] bg-zinc-100 p-[0.625rem] pr-[0.875rem]",
className,
)}
>
<Skeleton className="h-[3.125rem] w-[5.625rem] rounded-[0.375rem] bg-zinc-200" />
<div className="flex flex-1 flex-col items-start gap-0.5">
<Skeleton className="h-[1.375rem] w-24 rounded bg-zinc-200" />
<div className="flex items-center gap-1">
<Skeleton className="h-5 w-16 rounded bg-zinc-200" />
<Skeleton className="h-5 w-16 rounded bg-zinc-200" />
</div>
</div>
<Skeleton className="h-7 w-7 rounded-[0.5rem] bg-zinc-200" />
</Skeleton>
);
};
UGCAgentBlock.Skeleton = UGCAgentBlockSkeleton;
export default UGCAgentBlock;

View File

@@ -1,23 +1,29 @@
import React from "react";
import Block from "../Block";
import React, { useEffect, useState } from "react";
import { actionBlocksListData } from "../../testing_data";
import { BlockListType } from "./BlockMenuDefaultContent";
import BlocksList from "./BlocksList";
const ActionBlocksContent: React.FC = () => {
return (
<div className="scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-zinc-200 scrollbar-track-transparent h-full overflow-y-scroll pt-4">
<div className="w-full space-y-3 px-4 pb-4">
<Block title="Date Input" description="Input a date into your agent." />
<Block
title="Dropdown input"
description="Give your users the ability to select from a dropdown menu"
/>
<Block title="File upload" description="Upload a file to your agent" />
<Block
title="Text input"
description="Allow users to select multiple options using checkboxes"
/>
</div>
</div>
);
const [blocks, setBlocks] = useState<BlockListType[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchBlocks = async () => {
setLoading(true);
try {
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000));
setBlocks(actionBlocksListData);
} catch (error) {
console.error("Error fetching blocks:", error);
} finally {
setLoading(false);
}
};
fetchBlocks();
}, []);
return <BlocksList blocks={blocks} loading={loading} />;
};
export default ActionBlocksContent;

View File

@@ -1,119 +1,98 @@
// BLOCK MENU TODO: Currently I have hide the scrollbar, but need to add better designed custom scroller
import React from "react";
import React, { useState, useEffect, Fragment } from "react";
import Block from "../Block";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { allBlocksDataWithCategories } from "../../testing_data";
import { Skeleton } from "@/components/ui/skeleton";
// These are Temporary type, need to change it in future
type BlockItem = {
title: string;
description: string;
};
export type BlockCategory = {
name: string;
count: number;
items: BlockItem[];
};
const AllBlocksContent: React.FC = () => {
const [categories, setCategories] = useState<BlockCategory[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchBlocks = async () => {
setLoading(true);
setTimeout(() => {
setCategories(allBlocksDataWithCategories);
setLoading(false);
}, 800);
};
fetchBlocks();
}, []);
if (loading) {
return (
<div className="w-full space-y-3 p-4">
{[0, 1, 3].map((categoryIndex) => (
<Fragment key={categoryIndex}>
{categoryIndex > 0 && (
<Skeleton className="h-[1px] w-full text-zinc-100" />
)}
{[0, 1, 2].map((blockIndex) => (
<Block.Skeleton key={`${categoryIndex}-${blockIndex}`} />
))}
</Fragment>
))}
</div>
);
}
return (
<div className="scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-zinc-200 scrollbar-track-transparent h-full overflow-y-scroll pt-4">
<div className="w-full space-y-3 px-4 pb-4">
{/* AI Category */}
<div className="space-y-2.5">
<div className="flex items-center justify-between">
<p className="font-sans text-sm font-medium leading-[1.375rem] text-zinc-800">
AI
</p>
<span className="rounded-full bg-zinc-100 px-[0.375rem] font-sans text-sm leading-[1.375rem] text-zinc-600">
10
</span>
</div>
{categories.map((category, index) => (
<Fragment key={category.name}>
{index > 0 && (
<Separator className="h-[1px] w-full text-zinc-300" />
)}
<div className="space-y-2">
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
{/* Category Section */}
<div className="space-y-2.5">
<div className="flex items-center justify-between">
<p className="font-sans text-sm font-medium leading-[1.375rem] text-zinc-800">
{category.name}
</p>
<span className="rounded-full bg-zinc-100 px-[0.375rem] font-sans text-sm leading-[1.375rem] text-zinc-600">
{category.count}
</span>
</div>
<Button
variant={"link"}
className="px-0 font-sans text-sm leading-[1.375rem] text-zinc-600 underline hover:text-zinc-800"
>
see all
</Button>
</div>
</div>
<div className="space-y-2">
{category.items.slice(0, 3).map((item, idx) => (
<Block
key={`${category.name}-${idx}`}
title={item.title}
description={item.description}
/>
))}
<Separator className="h-[1px] w-full text-zinc-300" />
{/* Basic Category */}
<div className="space-y-2.5">
<div className="flex items-center justify-between">
<p className="font-sans text-sm font-medium leading-[1.375rem] text-zinc-800">
Basic
</p>
<span className="rounded-full bg-zinc-100 px-[0.375rem] font-sans text-sm leading-[1.375rem] text-zinc-600">
6
</span>
</div>
<div className="space-y-2">
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Button
variant={"link"}
className="px-0 font-sans text-sm leading-[1.375rem] text-zinc-600 underline hover:text-zinc-800"
>
see all
</Button>
</div>
</div>
<Separator className="h-[1px] w-full text-zinc-300" />
{/* Communincation Category */}
<div className="space-y-2.5">
<div className="flex items-center justify-between">
<p className="font-sans text-sm font-medium leading-[1.375rem] text-zinc-800">
Communincation
</p>
<span className="rounded-full bg-zinc-100 px-[0.375rem] font-sans text-sm leading-[1.375rem] text-zinc-600">
6
</span>
</div>
<div className="space-y-2">
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Button
variant={"link"}
className="px-0 font-sans text-sm leading-[1.375rem] text-zinc-600 underline hover:text-zinc-800"
>
see all
</Button>
</div>
</div>
{category.items.length > 3 && (
<Button
variant={"link"}
className="px-0 font-sans text-sm leading-[1.375rem] text-zinc-600 underline hover:text-zinc-800"
>
see all
</Button>
)}
</div>
</div>
</Fragment>
))}
</div>
</div>
);

View File

@@ -0,0 +1,18 @@
import React from "react";
import Block from "../Block";
interface BlockListSkeletonProps {
count?: number;
}
const BlockListSkeleton: React.FC<BlockListSkeletonProps> = ({ count = 3 }) => {
return (
<>
{Array.from({ length: count }).map((_, index) => (
<Block.Skeleton key={index} />
))}
</>
);
};
export default BlockListSkeleton;

View File

@@ -27,7 +27,6 @@ const BlockMenuDefault: React.FC = () => {
defaultState={defaultState}
setDefaultState={setDefaultState}
setIntegration={setIntegration}
integration={integration}
/>
<Separator className="h-full w-[1px] text-zinc-300" />

View File

@@ -1,13 +1,13 @@
import React, { useState } from "react";
import React from "react";
import { DefaultStateType } from "./BlockMenuDefault";
import SuggestionContent from "./SuggestionContent";
import AllBlocksContent from "./AllBlocksContent";
import InputBlocksContent from "./InputBlocksContent";
import ActionBlocksContent from "./ActionBlocksContent";
import OutputBlocksContent from "./OutputBlocksContent";
import IntegrationsContent from "./IntegrationsContent";
import MarketplaceAgentsContent from "./MarketplaceAgentsContent";
import MyAgentsContent from "./MyAgentsContent";
import ActionBlocksContent from "./ActionBlocksContent";
import InputBlocksContent from "./InputBlocksContent";
import OutputBlocksContent from "./OutputBlocksContent";
interface BlockMenuDefaultContentProps {
defaultState: DefaultStateType;
@@ -16,6 +16,18 @@ interface BlockMenuDefaultContentProps {
setIntegration: React.Dispatch<React.SetStateAction<string>>;
}
export interface ActionBlock {
id: number;
title: string;
description: string;
}
export interface BlockListType {
id: number;
title: string;
description: string;
}
const BlockMenuDefaultContent: React.FC<BlockMenuDefaultContentProps> = ({
defaultState,
setDefaultState,
@@ -26,7 +38,6 @@ const BlockMenuDefaultContent: React.FC<BlockMenuDefaultContentProps> = ({
<div className="h-full flex-1 overflow-hidden">
{defaultState == "suggestion" && (
<SuggestionContent
integration={integration}
setIntegration={setIntegration}
setDefaultState={setDefaultState}
/>

View File

@@ -5,14 +5,12 @@ import { DefaultStateType } from "./BlockMenuDefault";
interface BlockMenuSidebarProps {
defaultState: DefaultStateType;
setDefaultState: React.Dispatch<React.SetStateAction<DefaultStateType>>;
integration: string;
setIntegration: React.Dispatch<React.SetStateAction<string>>;
}
const BlockMenuSidebar: React.FC<BlockMenuSidebarProps> = ({
defaultState,
setDefaultState,
integration,
setIntegration,
}) => {
// BLOCK MENU TODO: We need to fetch the number of Blocks/Integrations/Agents when opening the menu.

View File

@@ -0,0 +1,30 @@
import React from "react";
import Block from "../Block";
import { BlockListType } from "./BlockMenuDefaultContent";
interface BlocksListProps {
blocks: BlockListType[];
loading?: boolean;
}
const BlocksList: React.FC<BlocksListProps> = ({ blocks, loading = false }) => {
return (
<div className="scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-zinc-200 scrollbar-track-transparent h-full overflow-y-scroll pt-4">
<div className="w-full space-y-3 px-4 pb-4">
{loading
? Array.from({ length: 7 }).map((_, index) => (
<Block.Skeleton key={index} />
))
: blocks.map((block) => (
<Block
key={block.id}
title={block.title}
description={block.description}
/>
))}
</div>
</div>
);
};
export default BlocksList;

View File

@@ -1,43 +1,29 @@
import React from "react";
import Block from "../Block";
import React, { useEffect, useState } from "react";
import BlocksList from "./BlocksList";
import { BlockListType } from "./BlockMenuDefaultContent";
import { inputBlocksListData } from "../../testing_data";
const InputBlocksContent: React.FC = () => {
return (
<div className="scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-zinc-200 scrollbar-track-transparent h-full overflow-y-scroll pt-4">
<div className="w-full space-y-3 px-4 pb-4">
<Block title="Date Input" description="Input a date into your agent." />
<Block
title="Dropdown input"
description="Give your users the ability to select from a dropdown menu"
/>
<Block title="File upload" description="Upload a file to your agent" />
<Block
title="Text input"
description="Allow users to select multiple options using checkboxes"
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Add to list"
description="Enables your agent to chat with users in natural language."
/>
</div>
</div>
);
const [blocks, setBlocks] = useState<BlockListType[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchBlocks = async () => {
setLoading(true);
try {
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000));
setBlocks(inputBlocksListData);
} catch (error) {
console.error("Error fetching blocks:", error);
} finally {
setLoading(false);
}
};
fetchBlocks();
}, []);
return <BlocksList blocks={blocks} loading={loading} />;
};
export default InputBlocksContent;

View File

@@ -1,43 +1,67 @@
import React from "react";
import IntegrationBlock from "../IntegrationBlock";
import React, { useState, useEffect } from "react";
import Integration from "../Integration";
import { integrationsListData } from "../../testing_data";
interface IntegrationListProps {
setIntegration: React.Dispatch<React.SetStateAction<string>>;
}
export interface IntegrationData {
title: string;
icon_url: string;
description: string;
number_of_blocks: number;
}
const IntegrationList: React.FC<IntegrationListProps> = ({
setIntegration,
}) => {
const [integrations, setIntegrations] = useState<IntegrationData[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(true);
useEffect(() => {
// Mock API call to fetch integrations
const fetchIntegrations = async () => {
setIsLoading(true);
try {
// Simulate network delay
await new Promise((resolve) => setTimeout(resolve, 1000));
setIntegrations(integrationsListData);
} catch (error) {
console.error("Failed to fetch integrations:", error);
} finally {
setIsLoading(false);
}
};
fetchIntegrations();
}, []);
if (isLoading) {
return (
<div className="space-y-3">
{Array(5)
.fill(null)
.map((_, index) => (
<Integration.Skeleton key={index} />
))}
</div>
);
}
return (
<div className="space-y-3">
<Integration
title="Twitter Blocks"
icon_url="/integrations/x.png"
description="All twitter blocks, It has everthing to interact with twitter"
number_of_blocks={10}
onClick={() => setIntegration("Twitter Blocks")}
/>
<Integration
title="Discord Blocks"
icon_url="/integrations/discord.png"
description="All Discord blocks, It has everthing to interact with discord"
number_of_blocks={14}
onClick={() => setIntegration("Discord Blocks")}
/>
<Integration
title="Github Blocks"
icon_url="/integrations/github.png"
description="All Github blocks, It has everthing to interact with github"
number_of_blocks={4}
onClick={() => setIntegration("Github Blocks")}
/>
<Integration
title="Hubspot Blocks"
icon_url="/integrations/hubspot.png"
description="All Hubspot blocks, It has everthing to interact with Hubspot"
number_of_blocks={2}
onClick={() => setIntegration("Hubspot Blocks")}
/>
{integrations.map((integration, index) => (
<Integration
key={index}
title={integration.title}
icon_url={integration.icon_url}
description={integration.description}
number_of_blocks={integration.number_of_blocks}
onClick={() => setIntegration(integration.title)}
/>
))}
</div>
);
};

View File

@@ -1,64 +1,57 @@
import React from "react";
import React, { useState, useEffect } from "react";
import MarketplaceAgentBlock from "../MarketplaceAgentBlock";
import { marketplaceAgentData } from "../../testing_data";
export interface MarketplaceAgent {
id: number;
title: string;
image_url: string;
creator_name: string;
number_of_runs: number;
}
const MarketplaceAgentsContent: React.FC = () => {
const [agents, setAgents] = useState<MarketplaceAgent[]>([]);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
const fetchAgents = async () => {
try {
await new Promise((resolve) => setTimeout(resolve, 1500));
setAgents(marketplaceAgentData);
setLoading(false);
} catch (err) {
setLoading(false);
}
};
fetchAgents();
}, []);
if (loading) {
return (
<div className="w-full space-y-3 p-4">
{Array(5)
.fill(null)
.map((_, index) => (
<MarketplaceAgentBlock.Skeleton key={index} />
))}
</div>
);
}
return (
<div className="scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-zinc-200 scrollbar-track-transparent h-full overflow-y-scroll pt-4">
<div className="w-full space-y-3 px-4 pb-4">
<MarketplaceAgentBlock
title="turtle test"
image_url="/placeholder.png"
creator_name="Autogpt"
number_of_runs={1000}
/>
<MarketplaceAgentBlock
title="turtle test 1"
image_url="/placeholder.png"
creator_name="Autogpt"
number_of_runs={1324}
/>
<MarketplaceAgentBlock
title="turtle test 2"
image_url="/placeholder.png"
creator_name="Autogpt"
number_of_runs={10030}
/>
<MarketplaceAgentBlock
title="turtle test 3"
image_url="/placeholder.png"
creator_name="Autogpt"
number_of_runs={324}
/>
<MarketplaceAgentBlock
title="turtle test"
image_url="/placeholder.png"
creator_name="Autogpt"
number_of_runs={4345}
/>
<MarketplaceAgentBlock
title="turtle test"
image_url="/placeholder.png"
creator_name="Autogpt"
number_of_runs={324}
/>
<MarketplaceAgentBlock
title="turtle test 3"
image_url="/placeholder.png"
creator_name="Autogpt"
number_of_runs={324}
/>
<MarketplaceAgentBlock
title="turtle test"
image_url="/placeholder.png"
creator_name="Autogpt"
number_of_runs={4345}
/>
<MarketplaceAgentBlock
title="turtle test"
image_url="/placeholder.png"
creator_name="Autogpt"
number_of_runs={324}
/>
{agents.map((agent) => (
<MarketplaceAgentBlock
key={agent.id}
title={agent.title}
image_url={agent.image_url}
creator_name={agent.creator_name}
number_of_runs={agent.number_of_runs}
/>
))}
</div>
</div>
);

View File

@@ -1,46 +1,57 @@
import React from "react";
import React, { useState, useEffect } from "react";
import UGCAgentBlock from "../UGCAgentBlock";
import { myAgentData } from "../../testing_data";
export interface UserAgent {
id: number;
title: string;
edited_time: string;
version: number;
image_url: string;
}
const MyAgentsContent: React.FC = () => {
const [agents, setAgents] = useState<UserAgent[]>([]);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
const fetchAgents = async () => {
try {
await new Promise((resolve) => setTimeout(resolve, 1500));
setAgents(myAgentData);
setLoading(false);
} catch (err) {
setLoading(false);
}
};
fetchAgents();
}, []);
if (loading) {
return (
<div className="w-full space-y-3 p-4">
{Array(5)
.fill(null)
.map((_, index) => (
<UGCAgentBlock.Skeleton key={index} />
))}
</div>
);
}
return (
<div className="scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-zinc-200 scrollbar-track-transparent h-full overflow-y-scroll pt-4">
<div className="w-full space-y-3 px-4 pb-4">
<UGCAgentBlock
title="My Agent 1"
edited_time="23rd April"
version={3}
image_url="/placeholder.png"
/>
<UGCAgentBlock
title="My Agent 2"
edited_time="21st April"
version={4}
image_url="/placeholder.png"
/>
<UGCAgentBlock
title="My Agent 3"
edited_time="23rd May"
version={7}
image_url="/placeholder.png"
/>
<UGCAgentBlock
title="My Agent 4"
edited_time="23rd April"
version={3}
image_url="/placeholder.png"
/>
<UGCAgentBlock
title="My Agent 5"
edited_time="23rd April"
version={3}
image_url="/placeholder.png"
/>
<UGCAgentBlock
title="My Agent 6"
edited_time="23rd April"
version={3}
image_url="/placeholder.png"
/>
{agents.map((agent) => (
<UGCAgentBlock
key={agent.id}
title={agent.title}
edited_time={agent.edited_time}
version={agent.version}
image_url={agent.image_url}
/>
))}
</div>
</div>
);

View File

@@ -1,19 +1,29 @@
import React from "react";
import Block from "../Block";
import React, { useEffect, useState } from "react";
import BlocksList from "./BlocksList";
import { BlockListType } from "./BlockMenuDefaultContent";
import { outputBlocksListData } from "../../testing_data";
const OutputBlocksContent: React.FC = () => {
return (
<div className="scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-zinc-200 scrollbar-track-transparent h-full overflow-y-scroll pt-4">
<div className="w-full space-y-3 px-4 pb-4">
<Block title="Date Input" description="Input a date into your agent." />
<Block
title="Dropdown input"
description="Give your users the ability to select from a dropdown menu"
/>
<Block title="File upload" description="Upload a file to your agent" />
</div>
</div>
);
const [blocks, setBlocks] = useState<BlockListType[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchBlocks = async () => {
setLoading(true);
try {
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000));
setBlocks(outputBlocksListData);
} catch (error) {
console.error("Error fetching blocks:", error);
} finally {
setLoading(false);
}
};
fetchBlocks();
}, []);
return <BlocksList blocks={blocks} loading={loading} />;
};
export default OutputBlocksContent;

View File

@@ -1,20 +1,76 @@
import React from "react";
import React, { useEffect, useState } from "react";
import SearchHistoryChip from "../SearchHistoryChip";
import IntegrationChip from "../IntegrationChip";
import Block from "../Block";
import { DefaultStateType } from "./BlockMenuDefault";
import {
integrationsData,
topBlocksData,
recentSearchesData,
} from "../../testing_data";
interface SuggestionContentProps {
integration: string;
setIntegration: React.Dispatch<React.SetStateAction<string>>;
setDefaultState: React.Dispatch<React.SetStateAction<DefaultStateType>>;
}
const SuggestionContent: React.FC<SuggestionContentProps> = ({
integration,
setIntegration,
setDefaultState,
}) => {
const [recentSearches, setRecentSearches] = useState<string[] | null>(null);
const [integrations, setIntegrations] = useState<
{ icon_url: string; name: string }[] | null
>(null);
const [topBlocks, setTopBlocks] = useState<
{ title: string; description: string }[] | null
>(null);
useEffect(() => {
const fetchData = async () => {
try {
// Create fetch functions that return their respective data
const fetchRecentSearches = async (): Promise<string[]> => {
await new Promise((resolve) => setTimeout(resolve, 300));
return recentSearchesData;
};
const fetchIntegrations = async (): Promise<
{ icon_url: string; name: string }[]
> => {
await new Promise((resolve) => setTimeout(resolve, 400));
return integrationsData;
};
const fetchTopBlocks = async (): Promise<
{ title: string; description: string }[]
> => {
await new Promise((resolve) => setTimeout(resolve, 600));
return topBlocksData;
};
// Fetch all data simultaneously using Promise.all
const [
recentSearchesDataFetched,
integrationsDataFetched,
topBlocksDataFetched,
] = await Promise.all([
fetchRecentSearches(),
fetchIntegrations(),
fetchTopBlocks(),
]);
setRecentSearches(recentSearchesDataFetched);
setIntegrations(integrationsDataFetched);
setTopBlocks(topBlocksDataFetched);
} catch (error) {
console.error("Error fetching data:", error);
}
};
fetchData();
}, []);
return (
<div className="scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-zinc-200 scrollbar-track-transparent h-full overflow-y-scroll pt-4">
<div className="w-full space-y-6 pb-4">
@@ -24,15 +80,22 @@ const SuggestionContent: React.FC<SuggestionContentProps> = ({
Recent searches
</p>
<div className="scrollbar-hide flex flex-nowrap gap-2 overflow-x-auto">
<SearchHistoryChip content="image generator" className="ml-4" />
<SearchHistoryChip content="deepfake" />
<SearchHistoryChip content="competitor analysis" />
<SearchHistoryChip content="image generator" />
<SearchHistoryChip content="deepfake" />
<SearchHistoryChip content="competitor analysis" />
<SearchHistoryChip content="image generator" />
<SearchHistoryChip content="deepfake" />
<SearchHistoryChip content="competitor analysis" />
{recentSearches
? recentSearches.map((search, index) => (
<SearchHistoryChip
key={`search-${index}`}
content={search}
className={index === 0 ? "ml-4" : ""}
/>
))
: Array(3)
.fill(0)
.map((_, index) => (
<SearchHistoryChip.Skeleton
key={`search-${index}`}
className={index === 0 ? "ml-4" : ""}
/>
))}
</div>
</div>
@@ -42,34 +105,25 @@ const SuggestionContent: React.FC<SuggestionContentProps> = ({
Integrations
</p>
<div className="grid grid-cols-3 grid-rows-2 gap-2">
<IntegrationChip
icon_url="/integrations/x.png"
name="Twitter"
onClick={() => {
setDefaultState("integrations");
setIntegration("Twitter Blocks");
}}
/>
<IntegrationChip
icon_url="/integrations/github.png"
name="Github"
/>
<IntegrationChip
icon_url="/integrations/hubspot.png"
name="Hubspot"
/>
<IntegrationChip
icon_url="/integrations/discord.png"
name="Discord"
/>
<IntegrationChip
icon_url="/integrations/medium.png"
name="Medium"
/>
<IntegrationChip
icon_url="/integrations/todoist.png"
name="Todoist"
/>
{integrations
? integrations.map((integration, index) => (
<IntegrationChip
key={`integration-${index}`}
icon_url={integration.icon_url}
name={integration.name}
onClick={() => {
setDefaultState("integrations");
setIntegration(integration.name);
}}
/>
))
: Array(6)
.fill(0)
.map((_, index) => (
<IntegrationChip.Skeleton
key={`integration-skeleton-${index}`}
/>
))}
</div>
</div>
@@ -79,26 +133,19 @@ const SuggestionContent: React.FC<SuggestionContentProps> = ({
Top blocks
</p>
<div className="space-y-2">
<Block
title="Find in Dictionary"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Find in Dictionary"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Find in Dictionary"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Find in Dictionary"
description="Enables your agent to chat with users in natural language."
/>
<Block
title="Find in Dictionary"
description="Enables your agent to chat with users in natural language."
/>
{topBlocks
? topBlocks.map((block, index) => (
<Block
key={`block-${index}`}
title={block.title}
description={block.description}
/>
))
: Array(3)
.fill(0)
.map((_, index) => (
<Block.Skeleton key={`block-skeleton-${index}`} />
))}
</div>
</div>
</div>

View File

@@ -1,3 +1,360 @@
// Default state data
// All blocks
import { BlockCategory } from "./block-menu/default/AllBlocksContent";
import { BlockListType } from "./block-menu/default/BlockMenuDefaultContent";
import { IntegrationData } from "./block-menu/default/IntegrationList";
import { MarketplaceAgent } from "./block-menu/default/MarketplaceAgentsContent";
import { UserAgent } from "./block-menu/default/MyAgentsContent";
// Suggestion
export const recentSearchesData = [
"image generator",
"deepfake",
"competitor analysis",
"market research",
"AI tools",
"content creation",
"data visualization",
"automation workflow",
"analytics dashboard",
];
// Define data for integrations
export const integrationsData = [
{
icon_url: "/integrations/x.png",
name: "Twitter",
},
{ icon_url: "/integrations/github.png", name: "Github" },
{ icon_url: "/integrations/hubspot.png", name: "Hubspot" },
{ icon_url: "/integrations/discord.png", name: "Discord" },
{ icon_url: "/integrations/medium.png", name: "Medium" },
{ icon_url: "/integrations/todoist.png", name: "Todoist" },
];
// Define data for top blocks
export const topBlocksData = [
{
title: "Find in Dictionary",
description: "Enables your agent to chat with users in natural language.",
},
{
title: "Web Search",
description: "Allows your agent to search the web for information.",
},
{
title: "Code Interpreter",
description: "Helps your agent understand and execute code snippets.",
},
{
title: "Data Analysis",
description:
"Enables your agent to analyze data and create visualizations.",
},
{
title: "File Manager",
description: "Gives your agent the ability to manage files and documents.",
},
];
// All Blocks
export const allBlocksDataWithCategories: BlockCategory[] = [
{
name: "AI",
count: 10,
items: [
{
title: "Natural Language Processing",
description:
"Enables your agent to chat with users in natural language.",
},
{
title: "Sentiment Analysis",
description:
"Analyzes the sentiment of user messages to respond appropriately.",
},
{
title: "Text Generation",
description:
"Creates human-like text based on the context and inputs provided.",
},
{
title: "Entity Recognition",
description: "Identifies and extracts entities from user messages.",
},
],
},
{
name: "Basic",
count: 6,
items: [
{
title: "Condition",
description: "Creates branching logic based on specific conditions.",
},
{
title: "Loop",
description: "Repeats actions until a specific condition is met.",
},
{
title: "Variable",
description:
"Stores and manages data for use throughout your workflow.",
},
],
},
{
name: "Communication",
count: 6,
items: [
{
title: "Email Sender",
description: "Sends emails to users based on triggers or conditions.",
},
{
title: "SMS Notification",
description:
"Sends text message notifications to users' mobile devices.",
},
{
title: "Webhook",
description:
"Integrates with external services through HTTP callbacks.",
},
],
},
];
export const actionBlocksListData: BlockListType[] = [
{
id: 1,
title: "Date Input Block",
description: "Input a date into your agent.",
},
{
id: 2,
title: "Dropdown input",
description: "Give your users the ability to select from a dropdown menu",
},
{
id: 3,
title: "File upload",
description: "Upload a file to your agent",
},
{
id: 4,
title: "Text input",
description: "Allow users to select multiple options using checkboxes",
},
];
export const inputBlocksListData: BlockListType[] = [
{
id: 1,
title: "Text Field",
description: "Collect single line text input from users.",
},
{
id: 2,
title: "Checkbox",
description: "Allow users to select multiple options using checkboxes.",
},
{
id: 3,
title: "Radio Button",
description: "Let users choose one option from a list of alternatives.",
},
{
id: 4,
title: "Textarea",
description: "Collect multi-line text input from users.",
},
{
id: 5,
title: "Number Input",
description: "Collect numerical values with optional min/max constraints.",
},
];
export const outputBlocksListData: BlockListType[] = [
{
id: 1,
title: "Display Text",
description: "Show formatted text content to users.",
},
{
id: 2,
title: "Image Output",
description: "Display images, charts, or visual content.",
},
{
id: 3,
title: "Table Display",
description: "Present data in an organized tabular format.",
},
{
id: 4,
title: "PDF Generation",
description: "Create and export data as PDF documents.",
},
{
id: 5,
title: "Status Alert",
description: "Show success, error, or informational alerts to users.",
},
];
export const integrationsListData: IntegrationData[] = [
{
title: "Twitter Blocks",
icon_url: "/integrations/x.png",
description:
"All twitter blocks, It has everthing to interact with twitter",
number_of_blocks: 10,
},
{
title: "Discord Blocks",
icon_url: "/integrations/discord.png",
description:
"All Discord blocks, It has everthing to interact with discord",
number_of_blocks: 14,
},
{
title: "Github Blocks",
icon_url: "/integrations/github.png",
description: "All Github blocks, It has everthing to interact with github",
number_of_blocks: 4,
},
{
title: "Hubspot Blocks",
icon_url: "/integrations/hubspot.png",
description:
"All Hubspot blocks, It has everthing to interact with Hubspot",
number_of_blocks: 2,
},
{
title: "Medium Blocks",
icon_url: "/integrations/medium.png",
description: "All Medium blocks, It has everything to interact with Medium",
number_of_blocks: 6,
},
{
title: "Todoist Blocks",
icon_url: "/integrations/todoist.png",
description:
"All Todoist blocks, It has everything to interact with Todoist",
number_of_blocks: 8,
},
];
export const marketplaceAgentData: MarketplaceAgent[] = [
{
id: 1,
title: "turtle test",
image_url: "/placeholder.png",
creator_name: "Autogpt",
number_of_runs: 1000,
},
{
id: 2,
title: "turtle test 1",
image_url: "/placeholder.png",
creator_name: "Autogpt",
number_of_runs: 1324,
},
{
id: 3,
title: "turtle test 2",
image_url: "/placeholder.png",
creator_name: "Autogpt",
number_of_runs: 10030,
},
{
id: 4,
title: "turtle test 3",
image_url: "/placeholder.png",
creator_name: "Autogpt",
number_of_runs: 324,
},
{
id: 5,
title: "turtle test",
image_url: "/placeholder.png",
creator_name: "Autogpt",
number_of_runs: 4345,
},
{
id: 6,
title: "turtle test",
image_url: "/placeholder.png",
creator_name: "Autogpt",
number_of_runs: 324,
},
{
id: 7,
title: "turtle test 3",
image_url: "/placeholder.png",
creator_name: "Autogpt",
number_of_runs: 324,
},
{
id: 8,
title: "turtle test",
image_url: "/placeholder.png",
creator_name: "Autogpt",
number_of_runs: 4345,
},
{
id: 9,
title: "turtle test",
image_url: "/placeholder.png",
creator_name: "Autogpt",
number_of_runs: 324,
},
];
export const myAgentData: UserAgent[] = [
{
id: 1,
title: "My Agent 1",
edited_time: "23rd April",
version: 3,
image_url: "/placeholder.png",
},
{
id: 2,
title: "My Agent 2",
edited_time: "21st April",
version: 4,
image_url: "/placeholder.png",
},
{
id: 3,
title: "My Agent 3",
edited_time: "23rd May",
version: 7,
image_url: "/placeholder.png",
},
{
id: 4,
title: "My Agent 4",
edited_time: "23rd April",
version: 3,
image_url: "/placeholder.png",
},
{
id: 5,
title: "My Agent 5",
edited_time: "23rd April",
version: 3,
image_url: "/placeholder.png",
},
{
id: 6,
title: "My Agent 6",
edited_time: "23rd April",
version: 3,
image_url: "/placeholder.png",
},
];