Sync with Langflow Store
This commit is contained in:
parent
d009476a8e
commit
a81abb4c2d
4 changed files with 353 additions and 250 deletions
|
|
@ -3,6 +3,7 @@ import { alertContext } from "../../contexts/alertContext";
|
||||||
import { FlowsContext } from "../../contexts/flowsContext";
|
import { FlowsContext } from "../../contexts/flowsContext";
|
||||||
import { StoreContext } from "../../contexts/storeContext";
|
import { StoreContext } from "../../contexts/storeContext";
|
||||||
import { getComponent, postLikeComponent } from "../../controllers/API";
|
import { getComponent, postLikeComponent } from "../../controllers/API";
|
||||||
|
import DeleteConfirmationModal from "../../modals/DeleteConfirmationModal";
|
||||||
import { storeComponent } from "../../types/store";
|
import { storeComponent } from "../../types/store";
|
||||||
import cloneFLowWithParent from "../../utils/storeUtils";
|
import cloneFLowWithParent from "../../utils/storeUtils";
|
||||||
import { cn } from "../../utils/utils";
|
import { cn } from "../../utils/utils";
|
||||||
|
|
@ -166,13 +167,17 @@ export default function CollectionCardComponent({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{onDelete && (
|
{onDelete && !data.metadata && (
|
||||||
<button onClick={onDelete}>
|
<DeleteConfirmationModal
|
||||||
|
onConfirm={() => {
|
||||||
|
onDelete();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Trash2"
|
name="Trash2"
|
||||||
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
|
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
|
||||||
/>
|
/>
|
||||||
</button>
|
</DeleteConfirmationModal>
|
||||||
)}
|
)}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -207,36 +212,69 @@ export default function CollectionCardComponent({
|
||||||
</div>
|
</div>
|
||||||
{data.liked_by_count != undefined && (
|
{data.liked_by_count != undefined && (
|
||||||
<div className="flex gap-0.5">
|
<div className="flex gap-0.5">
|
||||||
<ShadTooltip
|
{onDelete && data.metadata ? (
|
||||||
content={authorized ? "Like" : "Please review your API key."}
|
<ShadTooltip
|
||||||
>
|
content={
|
||||||
<Button
|
authorized ? "Delete" : "Please review your API key."
|
||||||
disabled={loadingLike}
|
|
||||||
variant="ghost"
|
|
||||||
size="xs"
|
|
||||||
className={
|
|
||||||
"whitespace-nowrap" +
|
|
||||||
(!authorized ? " cursor-not-allowed" : "")
|
|
||||||
}
|
}
|
||||||
onClick={() => {
|
|
||||||
if (!authorized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleLike();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<IconComponent
|
<DeleteConfirmationModal
|
||||||
name="Heart"
|
onConfirm={() => {
|
||||||
className={cn(
|
onDelete();
|
||||||
"h-5 w-5",
|
}}
|
||||||
liked_by_user
|
>
|
||||||
? "fill-destructive stroke-destructive"
|
<Button
|
||||||
: "",
|
variant="ghost"
|
||||||
!authorized ? " text-ring" : ""
|
size="xs"
|
||||||
)}
|
className={
|
||||||
/>
|
"whitespace-nowrap" +
|
||||||
</Button>
|
(!authorized ? " cursor-not-allowed" : "")
|
||||||
</ShadTooltip>
|
}
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
name="Trash2"
|
||||||
|
className={cn(
|
||||||
|
"h-5 w-5",
|
||||||
|
!authorized ? " text-ring" : ""
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</DeleteConfirmationModal>
|
||||||
|
</ShadTooltip>
|
||||||
|
) : (
|
||||||
|
<ShadTooltip
|
||||||
|
content={
|
||||||
|
authorized ? "Like" : "Please review your API key."
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
disabled={loadingLike}
|
||||||
|
variant="ghost"
|
||||||
|
size="xs"
|
||||||
|
className={
|
||||||
|
"whitespace-nowrap" +
|
||||||
|
(!authorized ? " cursor-not-allowed" : "")
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
if (!authorized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleLike();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
name="Heart"
|
||||||
|
className={cn(
|
||||||
|
"h-5 w-5",
|
||||||
|
liked_by_user
|
||||||
|
? "fill-destructive stroke-destructive"
|
||||||
|
: "",
|
||||||
|
!authorized ? " text-ring" : ""
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</ShadTooltip>
|
||||||
|
)}
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
content={
|
content={
|
||||||
authorized
|
authorized
|
||||||
|
|
|
||||||
26
src/frontend/src/components/pageLayout/index.tsx
Normal file
26
src/frontend/src/components/pageLayout/index.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import Header from "../headerComponent";
|
||||||
|
import { Separator } from "../ui/separator";
|
||||||
|
|
||||||
|
export default function PageLayout({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen w-full flex-col">
|
||||||
|
<Header />
|
||||||
|
<div className="flex h-full w-full flex-col justify-between overflow-auto bg-background px-16">
|
||||||
|
<div className="flex w-full flex-col justify-between space-y-0.5 py-8 pb-2">
|
||||||
|
<h2 className="text-2xl font-bold tracking-tight">{title}</h2>
|
||||||
|
<p className="text-muted-foreground">{description}</p>
|
||||||
|
</div>
|
||||||
|
<Separator className="my-6 flex" />
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
56
src/frontend/src/modals/DeleteConfirmationModal/index.tsx
Normal file
56
src/frontend/src/modals/DeleteConfirmationModal/index.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { DialogClose } from "@radix-ui/react-dialog";
|
||||||
|
import { Trash2 } from "lucide-react";
|
||||||
|
import { Button } from "../../components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "../../components/ui/dialog";
|
||||||
|
|
||||||
|
export default function DeleteConfirmationModal({
|
||||||
|
children,
|
||||||
|
onConfirm,
|
||||||
|
description,
|
||||||
|
}: {
|
||||||
|
children: JSX.Element;
|
||||||
|
onConfirm: () => void;
|
||||||
|
description?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger>{children}</DialogTrigger>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>
|
||||||
|
<div className="flex">
|
||||||
|
Delete <Trash2 className="ml-3 h-4 w-4" strokeWidth={1.5} />
|
||||||
|
</div>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Are you sure you want to delete this {description ?? "component"}?
|
||||||
|
<br></br>
|
||||||
|
This action cannot be undone.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<DialogClose>
|
||||||
|
<Button className="mr-3">Cancel</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
onConfirm();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -4,10 +4,9 @@ import PaginatorComponent from "../../components/PaginatorComponent";
|
||||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||||
import CollectionCardComponent from "../../components/cardComponent";
|
import CollectionCardComponent from "../../components/cardComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import Header from "../../components/headerComponent";
|
import PageLayout from "../../components/pageLayout";
|
||||||
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
||||||
import { Badge } from "../../components/ui/badge";
|
import { Badge } from "../../components/ui/badge";
|
||||||
import { Button } from "../../components/ui/button";
|
|
||||||
import { Input } from "../../components/ui/input";
|
import { Input } from "../../components/ui/input";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
|
|
@ -21,7 +20,6 @@ import { alertContext } from "../../contexts/alertContext";
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
import { StoreContext } from "../../contexts/storeContext";
|
import { StoreContext } from "../../contexts/storeContext";
|
||||||
import { getStoreComponents, getStoreTags } from "../../controllers/API";
|
import { getStoreComponents, getStoreTags } from "../../controllers/API";
|
||||||
import StoreApiKeyModal from "../../modals/StoreApiKeyModal";
|
|
||||||
import { storeComponent } from "../../types/store";
|
import { storeComponent } from "../../types/store";
|
||||||
import { cn } from "../../utils/utils";
|
import { cn } from "../../utils/utils";
|
||||||
export default function StorePage(): JSX.Element {
|
export default function StorePage(): JSX.Element {
|
||||||
|
|
@ -197,237 +195,222 @@ export default function StorePage(): JSX.Element {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<PageLayout
|
||||||
<Header />
|
title="Langflow Store"
|
||||||
|
description="Search flows and components from the community."
|
||||||
<div className="flex h-full w-full flex-col justify-between overflow-auto bg-background px-16">
|
/* button={{StoreApiKeyModal && (
|
||||||
<div>
|
<StoreApiKeyModal disabled={loading}>
|
||||||
<div className="flex w-full justify-between py-8 pb-2">
|
<Button
|
||||||
<span className="flex items-center justify-center gap-2 text-2xl font-semibold">
|
disabled={loading}
|
||||||
<IconComponent name="Store" className="w-6" />
|
className={cn(
|
||||||
Langflow Store
|
`${!validApiKey ? "animate-pulse border-error" : ""}`,
|
||||||
</span>
|
loading ? "cursor-not-allowed" : ""
|
||||||
<div className="flex gap-2">
|
)}
|
||||||
{StoreApiKeyModal && (
|
variant="primary"
|
||||||
<StoreApiKeyModal disabled={loading}>
|
>
|
||||||
<Button
|
<IconComponent name="Key" className="mr-2 w-4" />
|
||||||
disabled={loading}
|
API Key
|
||||||
className={cn(
|
</Button>
|
||||||
`${!validApiKey ? "animate-pulse border-error" : ""}`,
|
</StoreApiKeyModal>
|
||||||
loading ? "cursor-not-allowed" : ""
|
)}} */
|
||||||
)}
|
>
|
||||||
variant="primary"
|
<div className="flex h-full w-full flex-col justify-between">
|
||||||
>
|
<div className="flex w-full flex-col gap-4 p-0">
|
||||||
<IconComponent name="Key" className="mr-2 w-4" />
|
<div className="flex items-end gap-4">
|
||||||
API Key
|
<div className="relative h-12 w-[40%]">
|
||||||
</Button>
|
<Input
|
||||||
</StoreApiKeyModal>
|
disabled={loading}
|
||||||
)}
|
placeholder="Search Flows and Components"
|
||||||
</div>
|
className="absolute h-12 pl-5 pr-12"
|
||||||
</div>
|
onChange={(e) => {
|
||||||
<span className="flex w-[70%] pb-8 text-muted-foreground">
|
setInputText(e.target.value);
|
||||||
Search flows and components from the community.
|
}}
|
||||||
</span>
|
onKeyDown={(e) => {
|
||||||
<div className="flex w-full flex-col gap-4 p-0">
|
if (e.key === "Enter") {
|
||||||
<div className="flex items-end gap-4">
|
|
||||||
<div className="relative h-12 w-[40%]">
|
|
||||||
<Input
|
|
||||||
disabled={loading}
|
|
||||||
placeholder="Search Flows and Components"
|
|
||||||
className="absolute h-12 pl-5 pr-12"
|
|
||||||
onChange={(e) => {
|
|
||||||
setInputText(e.target.value);
|
|
||||||
}}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
setSearchNow(uniqueId());
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={inputText}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
disabled={loading}
|
|
||||||
className="absolute bottom-0 right-4 top-0 my-auto h-6 cursor-pointer stroke-1 text-muted-foreground"
|
|
||||||
onClick={() => {
|
|
||||||
setSearchNow(uniqueId());
|
setSearchNow(uniqueId());
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconComponent
|
|
||||||
name={loading ? "Loader2" : "Search"}
|
|
||||||
className={
|
|
||||||
loading ? " animate-spin cursor-not-allowed" : ""
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="ml-4 flex w-full gap-2 border-b border-border">
|
|
||||||
<button
|
|
||||||
disabled={loading}
|
|
||||||
onClick={() => {
|
|
||||||
setTabActive("All");
|
|
||||||
}}
|
|
||||||
className={
|
|
||||||
(tabActive === "All"
|
|
||||||
? "border-b-2 border-primary p-3"
|
|
||||||
: " border-b-2 border-transparent p-3 text-muted-foreground hover:text-primary") +
|
|
||||||
(loading ? " cursor-not-allowed " : "")
|
|
||||||
}
|
}
|
||||||
>
|
}}
|
||||||
All
|
value={inputText}
|
||||||
</button>
|
/>
|
||||||
<button
|
<button
|
||||||
disabled={loading}
|
|
||||||
onClick={() => {
|
|
||||||
setTabActive("Flows");
|
|
||||||
}}
|
|
||||||
className={
|
|
||||||
(tabActive === "Flows"
|
|
||||||
? "border-b-2 border-primary p-3"
|
|
||||||
: " border-b-2 border-transparent p-3 text-muted-foreground hover:text-primary") +
|
|
||||||
(loading ? " cursor-not-allowed " : "")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Flows
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
disabled={loading}
|
|
||||||
onClick={() => {
|
|
||||||
setTabActive("Components");
|
|
||||||
}}
|
|
||||||
className={
|
|
||||||
(tabActive === "Components"
|
|
||||||
? "border-b-2 border-primary p-3"
|
|
||||||
: " border-b-2 border-transparent p-3 text-muted-foreground hover:text-primary") +
|
|
||||||
(loading ? " cursor-not-allowed " : "")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Components
|
|
||||||
</button>
|
|
||||||
<ShadTooltip content="Coming Soon">
|
|
||||||
<button className="cursor-not-allowed p-3 text-muted-foreground">
|
|
||||||
Bundles
|
|
||||||
</button>
|
|
||||||
</ShadTooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Select
|
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
onValueChange={setSelectFilter}
|
className="absolute bottom-0 right-4 top-0 my-auto h-6 cursor-pointer stroke-1 text-muted-foreground"
|
||||||
value={selectFilter}
|
onClick={() => {
|
||||||
>
|
setSearchNow(uniqueId());
|
||||||
<SelectTrigger className="mr-4 w-[160px] flex-shrink-0">
|
|
||||||
<SelectValue placeholder="Filter Values" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectGroup>
|
|
||||||
<SelectItem value="all">All</SelectItem>
|
|
||||||
<SelectItem value="createdbyme">Created By Me</SelectItem>
|
|
||||||
<SelectItem value="likedbyme">Liked By Me</SelectItem>
|
|
||||||
</SelectGroup>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<div ref={fadeContainerRef} className="fade-container">
|
|
||||||
<div
|
|
||||||
ref={scrollContainerRef}
|
|
||||||
className="scroll-container flex gap-2"
|
|
||||||
>
|
|
||||||
{!loadingTags &&
|
|
||||||
tags.map((tag, idx) => (
|
|
||||||
<button
|
|
||||||
disabled={loading}
|
|
||||||
className={loading ? "cursor-not-allowed" : ""}
|
|
||||||
onClick={() => {
|
|
||||||
updateTags(tag.name);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Badge
|
|
||||||
key={idx}
|
|
||||||
variant="outline"
|
|
||||||
size="sq"
|
|
||||||
className={cn(
|
|
||||||
filteredCategories.some(
|
|
||||||
(category) => category === tag.name
|
|
||||||
)
|
|
||||||
? "bg-beta-foreground text-background hover:bg-beta-foreground"
|
|
||||||
: ""
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{tag.name}
|
|
||||||
</Badge>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-end justify-between">
|
|
||||||
<span className="px-0.5 text-sm text-muted-foreground">
|
|
||||||
{(!loading || searchData.length !== 0) && (
|
|
||||||
<>
|
|
||||||
{totalRowsCount}{" "}
|
|
||||||
{totalRowsCount !== 1 ? "results" : "result"}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<Select
|
|
||||||
disabled={loading}
|
|
||||||
onValueChange={(e) => {
|
|
||||||
setPageOrder(e);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<IconComponent
|
||||||
<SelectValue placeholder="Popular" />
|
name={loading ? "Loader2" : "Search"}
|
||||||
</SelectTrigger>
|
className={loading ? " animate-spin cursor-not-allowed" : ""}
|
||||||
<SelectContent>
|
/>
|
||||||
<SelectItem value="Popular">Popular</SelectItem>
|
</button>
|
||||||
{/* <SelectItem value="Recent">Most Recent</SelectItem> */}
|
|
||||||
<SelectItem value="Alphabetical">Alphabetical</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="ml-4 flex w-full gap-2 border-b border-border">
|
||||||
|
<button
|
||||||
|
disabled={loading}
|
||||||
|
onClick={() => {
|
||||||
|
setTabActive("All");
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
(tabActive === "All"
|
||||||
|
? "border-b-2 border-primary p-3"
|
||||||
|
: " border-b-2 border-transparent p-3 text-muted-foreground hover:text-primary") +
|
||||||
|
(loading ? " cursor-not-allowed " : "")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
All
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={loading}
|
||||||
|
onClick={() => {
|
||||||
|
setTabActive("Flows");
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
(tabActive === "Flows"
|
||||||
|
? "border-b-2 border-primary p-3"
|
||||||
|
: " border-b-2 border-transparent p-3 text-muted-foreground hover:text-primary") +
|
||||||
|
(loading ? " cursor-not-allowed " : "")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Flows
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={loading}
|
||||||
|
onClick={() => {
|
||||||
|
setTabActive("Components");
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
(tabActive === "Components"
|
||||||
|
? "border-b-2 border-primary p-3"
|
||||||
|
: " border-b-2 border-transparent p-3 text-muted-foreground hover:text-primary") +
|
||||||
|
(loading ? " cursor-not-allowed " : "")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Components
|
||||||
|
</button>
|
||||||
|
<ShadTooltip content="Coming Soon">
|
||||||
|
<button className="cursor-not-allowed p-3 text-muted-foreground">
|
||||||
|
Bundles
|
||||||
|
</button>
|
||||||
|
</ShadTooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid w-full gap-4 md:grid-cols-2 lg:grid-cols-3">
|
<div className="flex items-center gap-2">
|
||||||
{!loading || searchData.length !== 0 ? (
|
<Select
|
||||||
searchData.map((item) => {
|
disabled={loading}
|
||||||
return (
|
onValueChange={setSelectFilter}
|
||||||
<>
|
value={selectFilter}
|
||||||
<CollectionCardComponent
|
>
|
||||||
key={item.id}
|
<SelectTrigger className="mr-4 w-[160px] flex-shrink-0">
|
||||||
data={item}
|
<SelectValue placeholder="Filter Values" />
|
||||||
authorized={validApiKey}
|
</SelectTrigger>
|
||||||
disabled={loading}
|
<SelectContent>
|
||||||
/>
|
<SelectGroup>
|
||||||
</>
|
<SelectItem value="all">All</SelectItem>
|
||||||
);
|
<SelectItem value="createdbyme">Created By Me</SelectItem>
|
||||||
})
|
<SelectItem value="likedbyme">Liked By Me</SelectItem>
|
||||||
) : (
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<div ref={fadeContainerRef} className="fade-container">
|
||||||
|
<div
|
||||||
|
ref={scrollContainerRef}
|
||||||
|
className="scroll-container flex gap-2"
|
||||||
|
>
|
||||||
|
{!loadingTags &&
|
||||||
|
tags.map((tag, idx) => (
|
||||||
|
<button
|
||||||
|
disabled={loading}
|
||||||
|
className={loading ? "cursor-not-allowed" : ""}
|
||||||
|
onClick={() => {
|
||||||
|
updateTags(tag.name);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Badge
|
||||||
|
key={idx}
|
||||||
|
variant="outline"
|
||||||
|
size="sq"
|
||||||
|
className={cn(
|
||||||
|
filteredCategories.some(
|
||||||
|
(category) => category === tag.name
|
||||||
|
)
|
||||||
|
? "bg-beta-foreground text-background hover:bg-beta-foreground"
|
||||||
|
: ""
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{tag.name}
|
||||||
|
</Badge>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-end justify-between">
|
||||||
|
<span className="px-0.5 text-sm text-muted-foreground">
|
||||||
|
{(!loading || searchData.length !== 0) && (
|
||||||
<>
|
<>
|
||||||
<SkeletonCardComponent />
|
{totalRowsCount} {totalRowsCount !== 1 ? "results" : "result"}
|
||||||
<SkeletonCardComponent />
|
|
||||||
<SkeletonCardComponent />
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</span>
|
||||||
|
|
||||||
{!loading && searchData?.length === 0 && (
|
<Select
|
||||||
<div className="mt-6 flex w-full items-center justify-center text-center">
|
disabled={loading}
|
||||||
<div className="flex h-full w-full flex-col">
|
onValueChange={(e) => {
|
||||||
<div className="flex w-full flex-col gap-4">
|
setPageOrder(e);
|
||||||
<div className="grid w-full gap-4">
|
}}
|
||||||
You haven't{" "}
|
>
|
||||||
{selectFilter === "createdbyme" ? "created" : "liked"}{" "}
|
<SelectTrigger>
|
||||||
anything yet.
|
<SelectValue placeholder="Popular" />
|
||||||
</div>
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="Popular">Popular</SelectItem>
|
||||||
|
{/* <SelectItem value="Recent">Most Recent</SelectItem> */}
|
||||||
|
<SelectItem value="Alphabetical">Alphabetical</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid w-full gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{!loading || searchData.length !== 0 ? (
|
||||||
|
searchData.map((item) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CollectionCardComponent
|
||||||
|
key={item.id}
|
||||||
|
data={item}
|
||||||
|
authorized={validApiKey}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<SkeletonCardComponent />
|
||||||
|
<SkeletonCardComponent />
|
||||||
|
<SkeletonCardComponent />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!loading && searchData?.length === 0 && (
|
||||||
|
<div className="mt-6 flex w-full items-center justify-center text-center">
|
||||||
|
<div className="flex h-full w-full flex-col">
|
||||||
|
<div className="flex w-full flex-col gap-4">
|
||||||
|
<div className="grid w-full gap-4">
|
||||||
|
You haven't{" "}
|
||||||
|
{selectFilter === "createdbyme" ? "created" : "liked"}{" "}
|
||||||
|
anything yet.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!loading && searchData.length > 0 && (
|
{!loading && searchData.length > 0 && (
|
||||||
<div className="relative my-6">
|
<div className="relative py-6">
|
||||||
<PaginatorComponent
|
<PaginatorComponent
|
||||||
storeComponent={true}
|
storeComponent={true}
|
||||||
pageIndex={pageIndex}
|
pageIndex={pageIndex}
|
||||||
|
|
@ -441,6 +424,6 @@ export default function StorePage(): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue