fix: refactor loading of main page, use new Sidebar in main page, fix small UI bugs (#4451)
* Tighten space between main page sidebar buttons * Fixed skeleton size * Added playground button back * Updated no components and flows state * Update default icon to Workflow * Fixed size of list cards * Removed browse store * Removed playground button * Removed black background from empty folder state * Update empty state color * Fix color of empty state * fix text not selectable * updated border color * added shadow only on hover * Remove JSON from Download JSON * Fixed colors and weight of tabs on home page * Fixed padding on list and grid components * Update icons that take long to load * Fixed icon and bg color for home tiles * Removed unused code * removed placeholder data for skeleton to not appear on first load * Make onSuccess refetch the queries so that the loading waits for it * Removed unused divs on foldersidebarnav * Refactor sidebar buttons to use new shadcn sidebar * Created skeletons for folder, grid and list * Added new sidebar size * Use new sidebar button on header and implemented animation * Changed icon to getIcon * Added sidebar provider and fixed loading states of the main page * Removed folder buttons on emptyPage * Fixed foldername to appear immediatly, and fixed loading states for the folders * Removed unused state from folders store * Removed unused states from folders store type * Added new icon * fixed modals component to not show a trash icon * Changed icons to load immediatly * Added empty folder condition to not display header info * Added conditions to show loading state until everything loads * Created empty folder state * Changed empty page to correct colors * Added skeletons while flows of the folder are loading * Removed shadow from text * Fixed font chivo taking long time to load * Fix adding new folder not redirecting * [autofix.ci] apply automated fixes * Fixed colors and paddings on list and grid components * Re added tooltips to upload and create folders * fix input for name editing * Fix tests --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
6761df6110
commit
a09652ca5f
30 changed files with 697 additions and 631 deletions
|
|
@ -9,11 +9,7 @@
|
|||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Chivo:ital,wght@0,100..900;1,100..900&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap"
|
||||
href="https://fonts.googleapis.com/css2?family=Chivo:ital,wght@0,100..900;1,100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script
|
||||
|
|
|
|||
|
|
@ -5,6 +5,17 @@ import {
|
|||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from "@/components/ui/select-custom";
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarHeader,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarTrigger,
|
||||
} from "@/components/ui/sidebar";
|
||||
import {
|
||||
usePatchFolders,
|
||||
usePostFolders,
|
||||
|
|
@ -18,7 +29,7 @@ import { getObjectsFromFilelist } from "@/helpers/get-objects-from-filelist";
|
|||
import useUploadFlow from "@/hooks/flows/use-upload-flow";
|
||||
import { useIsFetching, useIsMutating } from "@tanstack/react-query";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useLocation, useParams } from "react-router-dom";
|
||||
import { FolderType } from "../../../../pages/MainPage/entities";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
|
|
@ -26,25 +37,28 @@ import { useFolderStore } from "../../../../stores/foldersStore";
|
|||
import { handleKeyDown } from "../../../../utils/reactflowUtils";
|
||||
import { cn } from "../../../../utils/utils";
|
||||
import IconComponent from "../../../genericIconComponent";
|
||||
import { Button, buttonVariants } from "../../../ui/button";
|
||||
import { Button } from "../../../ui/button";
|
||||
import { Input } from "../../../ui/input";
|
||||
import useFileDrop from "../../hooks/use-on-file-drop";
|
||||
import { SidebarFolderSkeleton } from "../sidebarFolderSkeleton";
|
||||
|
||||
type SideBarFoldersButtonsComponentProps = {
|
||||
pathname: string;
|
||||
handleChangeFolder?: (id: string) => void;
|
||||
handleDeleteFolder?: (item: FolderType) => void;
|
||||
folders: FolderType[] | undefined;
|
||||
loading?: boolean;
|
||||
};
|
||||
const SideBarFoldersButtonsComponent = ({
|
||||
pathname,
|
||||
handleChangeFolder,
|
||||
handleDeleteFolder,
|
||||
folders = [],
|
||||
loading,
|
||||
}: SideBarFoldersButtonsComponentProps) => {
|
||||
const location = useLocation();
|
||||
const pathname = location.pathname;
|
||||
const folders = useFolderStore((state) => state.folders);
|
||||
|
||||
const isFetchingFolders = !!useIsFetching({
|
||||
queryKey: ["useGetFolders"],
|
||||
exact: false,
|
||||
});
|
||||
const loading = !folders;
|
||||
const refInput = useRef<HTMLInputElement>(null);
|
||||
const [foldersNames, setFoldersNames] = useState({});
|
||||
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
||||
|
|
@ -56,10 +70,6 @@ const SideBarFoldersButtonsComponent = ({
|
|||
pathname.split("/").length < (ENABLE_CUSTOM_PARAM ? 5 : 4);
|
||||
const myCollectionId = useFolderStore((state) => state.myCollectionId);
|
||||
const folderIdDragging = useFolderStore((state) => state.folderIdDragging);
|
||||
const showFolderModal = useFolderStore((state) => state.showFolderModal);
|
||||
const setShowFolderModal = useFolderStore(
|
||||
(state) => state.setShowFolderModal,
|
||||
);
|
||||
|
||||
const checkPathName = (itemId: string) => {
|
||||
if (urlWithoutPath && itemId === myCollectionId) {
|
||||
|
|
@ -152,14 +162,21 @@ const SideBarFoldersButtonsComponent = ({
|
|||
const { mutate: mutateUpdateFolder } = usePatchFolders();
|
||||
|
||||
function addNewFolder() {
|
||||
mutateAddFolder({
|
||||
data: {
|
||||
name: "New Folder",
|
||||
parent_id: null,
|
||||
description: "",
|
||||
mutateAddFolder(
|
||||
{
|
||||
data: {
|
||||
name: "New Folder",
|
||||
parent_id: null,
|
||||
description: "",
|
||||
},
|
||||
},
|
||||
});
|
||||
track("Create New Folder");
|
||||
{
|
||||
onSuccess: (folder) => {
|
||||
track("Create New Folder");
|
||||
handleChangeFolder!(folder.id);
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function handleEditFolderName(e, name): void {
|
||||
|
|
@ -232,11 +249,6 @@ const SideBarFoldersButtonsComponent = ({
|
|||
}
|
||||
};
|
||||
|
||||
const isFetchingFolders = !!useIsFetching({
|
||||
queryKey: ["useGetFolders"],
|
||||
exact: false,
|
||||
});
|
||||
|
||||
const isFetchingFolder = !!useIsFetching({
|
||||
queryKey: ["useGetFolder"],
|
||||
exact: false,
|
||||
|
|
@ -254,27 +266,28 @@ const SideBarFoldersButtonsComponent = ({
|
|||
isDeletingFolder;
|
||||
|
||||
const HeaderButtons = () => (
|
||||
<div className="my-4 flex shrink-0 items-center justify-between gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-7 w-7 border-0 text-zinc-500 hover:bg-zinc-200 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-white lg:hidden"
|
||||
size="icon"
|
||||
onClick={() => setShowFolderModal(!showFolderModal)}
|
||||
data-testid="upload-folder-button"
|
||||
>
|
||||
<IconComponent name="panel-right-open" className="h-4 w-4" />
|
||||
</Button>
|
||||
<div className="flex shrink-0 items-center justify-between gap-2">
|
||||
<SidebarTrigger className="lg:hidden">
|
||||
<IconComponent name="PanelLeftClose" className="h-4 w-4" />
|
||||
</SidebarTrigger>
|
||||
|
||||
<div className="flex-1 text-sm font-semibold">Folders</div>
|
||||
<UploadFolderButton
|
||||
onClick={handleUploadFlowsToFolder}
|
||||
disabled={isUpdatingFolder}
|
||||
/>
|
||||
<AddFolderButton onClick={addNewFolder} disabled={isUpdatingFolder} />
|
||||
<div className="flex items-center gap-1">
|
||||
<UploadFolderButton
|
||||
onClick={handleUploadFlowsToFolder}
|
||||
disabled={isUpdatingFolder}
|
||||
/>
|
||||
<AddFolderButton
|
||||
onClick={addNewFolder}
|
||||
disabled={isUpdatingFolder}
|
||||
loading={isPending}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const AddFolderButton = ({ onClick, disabled }) => (
|
||||
<ShadTooltip content="Create new folder" styleClasses="z-10">
|
||||
const AddFolderButton = ({ onClick, disabled, loading }) => (
|
||||
<ShadTooltip content="Create new folder" styleClasses="z-50">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
|
|
@ -282,6 +295,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
onClick={onClick}
|
||||
data-testid="add-folder-button"
|
||||
disabled={disabled}
|
||||
loading={loading}
|
||||
>
|
||||
<IconComponent name="Plus" className="h-4 w-4" />
|
||||
</Button>
|
||||
|
|
@ -289,12 +303,11 @@ const SideBarFoldersButtonsComponent = ({
|
|||
);
|
||||
|
||||
const UploadFolderButton = ({ onClick, disabled }) => (
|
||||
/* Todo: change this back to being a folder upload */
|
||||
<ShadTooltip content="Upload a flow" styleClasses="z-10">
|
||||
<ShadTooltip content="Upload a flow" styleClasses="z-50">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-7 w-7 border-0 text-zinc-500 hover:bg-zinc-200 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-white"
|
||||
size="icon"
|
||||
className="h-7 w-7 border-0 text-zinc-500 hover:bg-zinc-200 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-white"
|
||||
onClick={onClick}
|
||||
data-testid="upload-folder-button"
|
||||
disabled={disabled}
|
||||
|
|
@ -389,153 +402,158 @@ const SideBarFoldersButtonsComponent = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderButtons />
|
||||
<Sidebar collapsible="offcanvas" data-testid="folder-sidebar">
|
||||
<SidebarHeader className="p-4">
|
||||
<HeaderButtons />
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<SidebarGroup className="p-4 py-2">
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{!loading ? (
|
||||
folders.map((item, index) => {
|
||||
const editFolderName = editFolders?.filter(
|
||||
(folder) => folder.name === item.name,
|
||||
)[0];
|
||||
return (
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
size="md"
|
||||
onDragOver={(e) => dragOver(e, item.id!)}
|
||||
onDragEnter={(e) => dragEnter(e, item.id!)}
|
||||
onDragLeave={dragLeave}
|
||||
onDrop={(e) => onDrop(e, item.id!)}
|
||||
key={item.id}
|
||||
data-testid={`sidebar-nav-${item.name}`}
|
||||
isActive={checkPathName(item.id!)}
|
||||
onClick={() => handleChangeFolder!(item.id!)}
|
||||
className="group/menu-button"
|
||||
>
|
||||
<div
|
||||
onDoubleClick={(event) => {
|
||||
handleDoubleClick(event, item);
|
||||
}}
|
||||
className="flex w-full items-center justify-between gap-2"
|
||||
>
|
||||
<div className="flex flex-1 items-center gap-2">
|
||||
{editFolderName?.edit && !isUpdatingFolder ? (
|
||||
<Input
|
||||
className="h-6 flex-1 focus:border-0"
|
||||
onChange={(e) => {
|
||||
handleEditFolderName(e, item.name);
|
||||
}}
|
||||
ref={refInput}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDownFn(e, item);
|
||||
handleKeyDown(e, e.key, "");
|
||||
}}
|
||||
autoFocus={true}
|
||||
onBlur={(e) => {
|
||||
// fixes autofocus problem where cursor isn't present
|
||||
if (
|
||||
e.relatedTarget?.id ===
|
||||
`options-trigger-${item.name}`
|
||||
) {
|
||||
refInput.current?.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
<div className="flex h-[70vh] flex-col gap-2 overflow-auto">
|
||||
<>
|
||||
{!loading ? (
|
||||
folders.map((item, index) => {
|
||||
const editFolderName = editFolders?.filter(
|
||||
(folder) => folder.name === item.name,
|
||||
)[0];
|
||||
return (
|
||||
<div
|
||||
onDragOver={(e) => dragOver(e, item.id!)}
|
||||
onDragEnter={(e) => dragEnter(e, item.id!)}
|
||||
onDragLeave={dragLeave}
|
||||
onDrop={(e) => onDrop(e, item.id!)}
|
||||
key={item.id}
|
||||
data-testid={`sidebar-nav-${item.name}`}
|
||||
className={cn(
|
||||
buttonVariants({ variant: "ghost" }),
|
||||
checkPathName(item.id!)
|
||||
? "bg-zinc-200 hover:bg-zinc-200 dark:bg-zinc-800"
|
||||
: "hover:bg-transparent hover:bg-zinc-200 dark:hover:bg-zinc-800 lg:border-transparent",
|
||||
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
|
||||
folderIdDragging === item.id! ? "bg-border" : "",
|
||||
)}
|
||||
onClick={() => handleChangeFolder!(item.id!)}
|
||||
>
|
||||
<div
|
||||
onDoubleClick={(event) => {
|
||||
handleDoubleClick(event, item);
|
||||
}}
|
||||
className="flex w-full items-center justify-between"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{editFolderName?.edit && !isUpdatingFolder ? (
|
||||
<div>
|
||||
<Input
|
||||
className="w-36"
|
||||
onChange={(e) => {
|
||||
handleEditFolderName(e, item.name);
|
||||
}}
|
||||
ref={refInput}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDownFn(e, item);
|
||||
handleKeyDown(e, e.key, "");
|
||||
}}
|
||||
autoFocus={true}
|
||||
onBlur={(e) => {
|
||||
// fixes autofocus problem where cursor isn't present
|
||||
if (
|
||||
e.relatedTarget?.id ===
|
||||
`options-trigger-${item.name}`
|
||||
) {
|
||||
refInput.current?.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (refInput.current?.value !== item.name) {
|
||||
handleEditNameFolder(item);
|
||||
} else {
|
||||
editFolderName.edit = false;
|
||||
}
|
||||
refInput.current?.blur();
|
||||
}}
|
||||
value={foldersNames[item.name]}
|
||||
id={`input-folder-${item.name}`}
|
||||
data-testid={`input-folder`}
|
||||
/>
|
||||
if (refInput.current?.value !== item.name) {
|
||||
handleEditNameFolder(item);
|
||||
} else {
|
||||
editFolderName.edit = false;
|
||||
}
|
||||
refInput.current?.blur();
|
||||
}}
|
||||
value={foldersNames[item.name]}
|
||||
id={`input-folder-${item.name}`}
|
||||
data-testid={`input-folder`}
|
||||
/>
|
||||
) : (
|
||||
<span className="block w-full grow truncate text-[13px] opacity-100">
|
||||
{item.name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Select
|
||||
onValueChange={(value) =>
|
||||
handleSelectChange(value, item)
|
||||
}
|
||||
value=""
|
||||
>
|
||||
<ShadTooltip
|
||||
content="Options"
|
||||
side="right"
|
||||
styleClasses="z-50"
|
||||
>
|
||||
<SelectTrigger
|
||||
className="w-fit"
|
||||
id={`options-trigger-${item.name}`}
|
||||
data-testid="more-options-button"
|
||||
>
|
||||
<IconComponent
|
||||
name={"MoreHorizontal"}
|
||||
className={`w-4 stroke-[1.5] px-0 text-muted-foreground group-hover/menu-button:block group-hover/menu-button:text-foreground ${
|
||||
checkPathName(item.id!) ? "block" : "hidden"
|
||||
}`}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
</ShadTooltip>
|
||||
<SelectContent
|
||||
align="end"
|
||||
alignOffset={-16}
|
||||
position="popper"
|
||||
>
|
||||
{item.name !== "My Projects" && (
|
||||
<SelectItem
|
||||
id="rename-button"
|
||||
value="rename"
|
||||
data-testid="btn-rename-folder"
|
||||
>
|
||||
<FolderSelectItem
|
||||
name="Rename"
|
||||
iconName="SquarePen"
|
||||
/>
|
||||
</SelectItem>
|
||||
)}
|
||||
<SelectItem
|
||||
value="download"
|
||||
data-testid="btn-download-folder"
|
||||
>
|
||||
<FolderSelectItem
|
||||
name="Download Content"
|
||||
iconName="Download"
|
||||
/>
|
||||
</SelectItem>
|
||||
{index > 0 && (
|
||||
<SelectItem
|
||||
value="delete"
|
||||
data-testid="btn-delete-folder"
|
||||
>
|
||||
<FolderSelectItem
|
||||
name="Delete"
|
||||
iconName="Trash2"
|
||||
/>
|
||||
</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
) : (
|
||||
<span className="block w-full grow truncate text-[13px] opacity-100">
|
||||
{item.name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Select
|
||||
onValueChange={(value) => handleSelectChange(value, item)}
|
||||
value=""
|
||||
>
|
||||
<ShadTooltip
|
||||
content="Options"
|
||||
side="right"
|
||||
styleClasses="z-10"
|
||||
>
|
||||
<SelectTrigger
|
||||
className="w-fit"
|
||||
id={`options-trigger-${item.name}`}
|
||||
data-testid="more-options-button"
|
||||
>
|
||||
<IconComponent
|
||||
name={"MoreHorizontal"}
|
||||
className={`w-4 stroke-[1.5] px-0 text-zinc-500 group-hover:block group-hover:text-black dark:text-zinc-400 dark:group-hover:text-white ${
|
||||
checkPathName(item.id!) ? "block" : "hidden"
|
||||
}`}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
</ShadTooltip>
|
||||
<SelectContent
|
||||
align="end"
|
||||
alignOffset={-16}
|
||||
position="popper"
|
||||
>
|
||||
{item.name !== "My Projects" && (
|
||||
<SelectItem
|
||||
id="rename-button"
|
||||
value="rename"
|
||||
data-testid="btn-rename-folder"
|
||||
>
|
||||
<FolderSelectItem
|
||||
name="Rename"
|
||||
iconName="square-pen"
|
||||
/>
|
||||
</SelectItem>
|
||||
)}
|
||||
<SelectItem
|
||||
value="download"
|
||||
data-testid="btn-download-folder"
|
||||
>
|
||||
<FolderSelectItem
|
||||
name="Download Content"
|
||||
iconName="download"
|
||||
/>
|
||||
</SelectItem>
|
||||
{index > 0 && (
|
||||
<SelectItem
|
||||
value="delete"
|
||||
data-testid="btn-delete-folder"
|
||||
>
|
||||
<FolderSelectItem name="Delete" iconName="trash" />
|
||||
</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<>
|
||||
<SidebarFolderSkeleton />
|
||||
<SidebarFolderSkeleton />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<>
|
||||
<SidebarFolderSkeleton />
|
||||
<SidebarFolderSkeleton />
|
||||
</>
|
||||
)}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
</SidebarContent>
|
||||
</Sidebar>
|
||||
);
|
||||
};
|
||||
export default SideBarFoldersButtonsComponent;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import { Skeleton } from "@/components/ui/skeleton";
|
|||
|
||||
export function SidebarFolderSkeleton() {
|
||||
return (
|
||||
<div className="flex h-10 w-full shrink-0 animate-pulse cursor-pointer items-center gap-4 rounded-md border bg-background px-4 opacity-100 lg:min-w-full">
|
||||
<Skeleton className="h-3 w-4 rounded-full" />
|
||||
<div className="flex h-9 w-full shrink-0 animate-pulse cursor-pointer items-center gap-4 rounded-md border bg-background px-2 opacity-100 lg:min-w-full">
|
||||
<Skeleton className="h-3 w-[40%]" />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { useGetFoldersQuery } from "@/controllers/API/queries/folders/use-get-folders";
|
||||
import { useFolderStore } from "@/stores/foldersStore";
|
||||
import { useIsFetching } from "@tanstack/react-query";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { FolderType } from "../../pages/MainPage/entities";
|
||||
import { cn } from "../../utils/utils";
|
||||
import HorizontalScrollFadeComponent from "../horizontalScrollFadeComponent";
|
||||
import SideBarFoldersButtonsComponent from "./components/sideBarFolderButtons";
|
||||
|
||||
type SidebarNavProps = {
|
||||
|
|
@ -29,16 +26,9 @@ export default function FolderSidebarNav({
|
|||
});
|
||||
|
||||
return (
|
||||
<nav className={cn(className)} {...props}>
|
||||
<HorizontalScrollFadeComponent>
|
||||
<SideBarFoldersButtonsComponent
|
||||
loading={isPending || !folders}
|
||||
pathname={pathname}
|
||||
handleChangeFolder={handleChangeFolder}
|
||||
handleDeleteFolder={handleDeleteFolder}
|
||||
folders={folders}
|
||||
/>
|
||||
</HorizontalScrollFadeComponent>
|
||||
</nav>
|
||||
<SideBarFoldersButtonsComponent
|
||||
handleChangeFolder={handleChangeFolder}
|
||||
handleDeleteFolder={handleDeleteFolder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -518,6 +518,7 @@ const sidebarMenuButtonVariants = cva(
|
|||
},
|
||||
size: {
|
||||
default: "h-8 text-sm",
|
||||
md: "h-9 text-sm",
|
||||
sm: "h-7 text-xs",
|
||||
lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ interface IPostAddFlow {
|
|||
is_component: boolean;
|
||||
folder_id: string;
|
||||
endpoint_name: string | undefined;
|
||||
icon: string | undefined;
|
||||
}
|
||||
|
||||
export const usePostAddFlow: useMutationFunctionType<
|
||||
|
|
@ -29,6 +30,7 @@ export const usePostAddFlow: useMutationFunctionType<
|
|||
description: payload.description,
|
||||
is_component: payload.is_component,
|
||||
folder_id: payload.folder_id || null,
|
||||
icon: payload.icon || null,
|
||||
endpoint_name: payload.endpoint_name || null,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ export const useGetFolderQuery: useQueryFunctionType<
|
|||
() => getFolderFn(params),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
placeholderData: true,
|
||||
...options,
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ export const usePostFolders: useMutationFunctionType<
|
|||
|
||||
const mutation = mutate(["usePostFolders"], addFoldersFn, {
|
||||
...options,
|
||||
onSettled: () => {
|
||||
queryClient.refetchQueries({ queryKey: ["useGetFolders"] });
|
||||
onSuccess: () => {
|
||||
return queryClient.refetchQueries({ queryKey: ["useGetFolders"] });
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -35,23 +35,10 @@ export default function TemplateCardComponent({
|
|||
onKeyDown={handleKeyDown}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div
|
||||
className="relative h-20 w-20 shrink-0 overflow-hidden rounded-md p-4 outline-none ring-ring"
|
||||
style={{
|
||||
backgroundImage: bgGradient,
|
||||
transform: "scale(1)",
|
||||
transition: "transform 0.3s ease-in-out",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="absolute inset-0 transition-transform duration-300 group-hover:scale-125 group-focus-visible:scale-125"
|
||||
style={{
|
||||
backgroundImage: bgGradient,
|
||||
}}
|
||||
/>
|
||||
<div className="relative h-20 w-20 shrink-0 overflow-hidden rounded-md bg-muted p-4 outline-none ring-ring group-hover:bg-border group-focus-visible:bg-border">
|
||||
<IconComponent
|
||||
name={example.icon || "FileText"}
|
||||
className="absolute left-1/2 top-1/2 h-10 w-10 -translate-x-1/2 -translate-y-1/2 text-white duration-300 group-hover:scale-105 group-focus-visible:scale-105"
|
||||
className="absolute left-1/2 top-1/2 h-10 w-10 -translate-x-1/2 -translate-y-1/2 text-muted-foreground duration-300 group-hover:scale-105 group-hover:text-foreground group-focus-visible:scale-105 group-focus-visible:text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col justify-between">
|
||||
|
|
|
|||
|
|
@ -78,11 +78,11 @@ const DropdownComponent = ({
|
|||
data-testid="btn-download-json"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="download"
|
||||
name="Download"
|
||||
aria-hidden="true"
|
||||
className="mr-2 h-4 w-4"
|
||||
/>
|
||||
Download JSON
|
||||
Download
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
|
|
@ -93,7 +93,7 @@ const DropdownComponent = ({
|
|||
data-testid="btn-duplicate-flow"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="copy-plus"
|
||||
name="CopyPlus"
|
||||
aria-hidden="true"
|
||||
className="mr-2 h-4 w-4"
|
||||
/>
|
||||
|
|
@ -104,10 +104,10 @@ const DropdownComponent = ({
|
|||
e.stopPropagation();
|
||||
setOpenDelete(true);
|
||||
}}
|
||||
className="cursor-pointer text-red-500 focus:text-red-500 dark:text-red-500 dark:focus:text-red-500"
|
||||
className="cursor-pointer text-destructive"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="trash"
|
||||
name="Trash2"
|
||||
aria-hidden="true"
|
||||
className="mr-2 h-4 w-4"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -8,10 +8,8 @@ import {
|
|||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||
import { track } from "@/customization/utils/analytics";
|
||||
import useDeleteFlow from "@/hooks/flows/use-delete-flow";
|
||||
import DeleteConfirmationModal from "@/modals/deleteConfirmationModal";
|
||||
import IOModal from "@/modals/IOModal";
|
||||
import useAlertStore from "@/stores/alertStore";
|
||||
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
||||
import { FlowType } from "@/types/flow";
|
||||
|
|
@ -19,27 +17,27 @@ import { getInputsAndOutputs } from "@/utils/storeUtils";
|
|||
import { useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import useDescriptionModal from "../../oldComponents/componentsComponent/hooks/use-description-modal";
|
||||
import { getTemplateStyle } from "../../utils/get-template-style";
|
||||
import { useGetTemplateStyle } from "../../utils/get-template-style";
|
||||
import { timeElapsed } from "../../utils/time-elapse";
|
||||
import DropdownComponent from "../dropdown";
|
||||
|
||||
const GridComponent = ({ flowData }: { flowData: FlowType }) => {
|
||||
const navigate = useCustomNavigate();
|
||||
// const [openPlayground, setOpenPlayground] = useState(false);
|
||||
const [loadingPlayground, setLoadingPlayground] = useState(false);
|
||||
/* const [openPlayground, setOpenPlayground] = useState(false);
|
||||
const [loadingPlayground, setLoadingPlayground] = useState(false); */
|
||||
const [openDelete, setOpenDelete] = useState(false);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const { deleteFlow } = useDeleteFlow();
|
||||
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||
/* const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); */
|
||||
const { folderId } = useParams();
|
||||
const isComponent = flowData.is_component ?? false;
|
||||
const setFlowToCanvas = useFlowsManagerStore(
|
||||
(state) => state.setFlowToCanvas,
|
||||
);
|
||||
|
||||
const { icon, icon_bg_color } = getTemplateStyle(flowData);
|
||||
const { getIcon } = useGetTemplateStyle(flowData);
|
||||
|
||||
const editFlowLink = `/flow/${flowData.id}${folderId ? `/folder/${folderId}` : ""}`;
|
||||
|
||||
|
|
@ -51,7 +49,7 @@ const GridComponent = ({ flowData }: { flowData: FlowType }) => {
|
|||
return inputs.length > 0 || outputs.length > 0;
|
||||
}
|
||||
|
||||
const handlePlaygroundClick = () => {
|
||||
/* const handlePlaygroundClick = () => {
|
||||
track("Playground Button Clicked", { flowId: flowData.id });
|
||||
setLoadingPlayground(true);
|
||||
|
||||
|
|
@ -73,7 +71,7 @@ const GridComponent = ({ flowData }: { flowData: FlowType }) => {
|
|||
list: ["Error getting flow data."],
|
||||
});
|
||||
}
|
||||
};
|
||||
}; */
|
||||
|
||||
const handleClick = async () => {
|
||||
if (!isComponent) {
|
||||
|
|
@ -108,18 +106,16 @@ const GridComponent = ({ flowData }: { flowData: FlowType }) => {
|
|||
draggable
|
||||
onDragStart={onDragStart}
|
||||
onClick={handleClick}
|
||||
className={`my-1 flex flex-col rounded-lg border border-zinc-100 bg-background p-5 shadow-sm hover:border-border dark:border-zinc-800 dark:hover:border-muted-foreground ${
|
||||
className={`my-1 flex flex-col rounded-lg border border-border bg-background p-4 hover:border-placeholder-foreground hover:shadow-sm ${
|
||||
isComponent ? "cursor-default" : "cursor-pointer"
|
||||
}`}
|
||||
>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<div
|
||||
className={`mr-3 flex rounded-lg border ${flowData?.icon_bg_color || icon_bg_color} p-3`}
|
||||
>
|
||||
<div className="flex w-full items-center gap-4">
|
||||
<div className={`flex rounded-lg bg-muted p-3`}>
|
||||
<ForwardedIconComponent
|
||||
name={flowData?.icon || icon}
|
||||
name={getIcon()}
|
||||
aria-hidden="true"
|
||||
className="h-5 w-5 dark:text-black"
|
||||
className="h-5 w-5 text-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-full min-w-0 items-center justify-between">
|
||||
|
|
@ -127,27 +123,27 @@ const GridComponent = ({ flowData }: { flowData: FlowType }) => {
|
|||
<div className="text-md truncate font-semibold">
|
||||
{flowData.name}
|
||||
</div>
|
||||
<div className="truncate text-xs text-zinc-500 dark:text-zinc-400">
|
||||
<div className="truncate text-xs text-muted-foreground">
|
||||
Edited {timeElapsed(flowData.updated_at)} ago
|
||||
</div>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
variant="ghost"
|
||||
data-testid="home-dropdown-menu"
|
||||
size="icon"
|
||||
className="group ml-2 h-10 w-10 border-none dark:hover:bg-zinc-700"
|
||||
size="iconMd"
|
||||
className="group"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="ellipsis"
|
||||
name="Ellipsis"
|
||||
aria-hidden="true"
|
||||
className="h-5 w-5 dark:text-zinc-400 dark:group-hover:text-white"
|
||||
className="h-5 w-5 text-muted-foreground group-hover:text-foreground"
|
||||
/>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="mr-[30px] w-[185px] bg-white dark:bg-black"
|
||||
className="w-[185px]"
|
||||
sideOffset={5}
|
||||
side="bottom"
|
||||
>
|
||||
|
|
@ -160,7 +156,7 @@ const GridComponent = ({ flowData }: { flowData: FlowType }) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="line-clamp-2 h-full pt-5 text-sm text-zinc-800 dark:text-white">
|
||||
<div className="line-clamp-2 h-full pt-5 text-sm text-primary">
|
||||
{flowData.description}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
import { Card } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
const GridSkeleton = () => {
|
||||
return (
|
||||
<Card className="my-1 flex flex-col rounded-lg border border-border bg-background p-4">
|
||||
<div className="flex w-full items-center gap-4">
|
||||
{/* Icon skeleton */}
|
||||
<div className="flex rounded-lg">
|
||||
<Skeleton className="h-[44px] w-[44px] rounded-lg" />
|
||||
</div>
|
||||
|
||||
<div className="flex w-full min-w-0 items-center justify-between">
|
||||
<div className="flex min-w-0 flex-col gap-2">
|
||||
{/* Title skeleton */}
|
||||
<Skeleton className="h-5 w-[120px]" />
|
||||
{/* Time skeleton */}
|
||||
<Skeleton className="h-4 w-[150px]" />
|
||||
</div>
|
||||
{/* Dropdown button skeleton */}
|
||||
<Skeleton className="ml-2 h-10 w-10 rounded-md" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description skeleton */}
|
||||
<div className="pt-5">
|
||||
<Skeleton className="h-4 w-full" />
|
||||
<Skeleton className="mt-2 h-4 w-3/4" />
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default GridSkeleton;
|
||||
|
|
@ -2,8 +2,8 @@ import ForwardedIconComponent from "@/components/genericIconComponent";
|
|||
import ShadTooltip from "@/components/shadTooltipComponent";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||
import { useFolderStore } from "@/stores/foldersStore";
|
||||
import { SidebarTrigger, useSidebar } from "@/components/ui/sidebar";
|
||||
import { cn } from "@/utils/utils";
|
||||
import { debounce } from "lodash";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
|
|
@ -15,6 +15,7 @@ interface HeaderComponentProps {
|
|||
setNewProjectModal: (newProjectModal: boolean) => void;
|
||||
folderName?: string;
|
||||
setSearch: (search: string) => void;
|
||||
isEmptyFolder: boolean;
|
||||
}
|
||||
|
||||
const HeaderComponent = ({
|
||||
|
|
@ -25,10 +26,10 @@ const HeaderComponent = ({
|
|||
setView,
|
||||
setNewProjectModal,
|
||||
setSearch,
|
||||
isEmptyFolder,
|
||||
}: HeaderComponentProps) => {
|
||||
const navigate = useCustomNavigate();
|
||||
const [debouncedSearch, setDebouncedSearch] = useState("");
|
||||
const { showFolderModal, setShowFolderModal } = useFolderStore();
|
||||
const { open } = useSidebar();
|
||||
|
||||
// Debounce the setSearch function from the parent
|
||||
const debouncedSetSearch = useCallback(
|
||||
|
|
@ -56,75 +57,83 @@ const HeaderComponent = ({
|
|||
className="flex items-center pb-8 text-xl font-semibold"
|
||||
data-testid="mainpage_title"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="mr-2 lg:hidden"
|
||||
size="icon"
|
||||
onClick={() => setShowFolderModal(!showFolderModal)}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name={showFolderModal ? "panel-right-open" : "panel-right-close"}
|
||||
aria-hidden="true"
|
||||
className="h-5 w-5 text-zinc-500 dark:text-zinc-400"
|
||||
/>
|
||||
</Button>
|
||||
<div className={cn("w-10 transition-all lg:hidden", open && "md:w-0")}>
|
||||
<div
|
||||
className={cn(
|
||||
"relative left-0 opacity-100 transition-all",
|
||||
open ? "md:opacity-0" : "",
|
||||
)}
|
||||
>
|
||||
<SidebarTrigger>
|
||||
<ForwardedIconComponent
|
||||
name="PanelLeftOpen"
|
||||
aria-hidden="true"
|
||||
className="text-zinc-500 dark:text-zinc-400"
|
||||
/>
|
||||
</SidebarTrigger>
|
||||
</div>
|
||||
</div>
|
||||
{folderName}
|
||||
</div>
|
||||
<div className="flex flex-row-reverse pb-8">
|
||||
<div className="w-full border-b dark:border-border" />
|
||||
{["components", "flows"].map((type) => (
|
||||
<Button
|
||||
key={type}
|
||||
unstyled
|
||||
id={`${type}-btn`}
|
||||
data-testid={`${type}-btn`}
|
||||
onClick={() => setFlowType(type as "flows" | "components")}
|
||||
className={`border-b ${
|
||||
flowType === type
|
||||
? "border-b-2 border-black font-semibold dark:border-white dark:text-white"
|
||||
: "border-border text-zinc-400 hover:text-black dark:hover:text-white"
|
||||
} px-3 pb-2`}
|
||||
>
|
||||
{type.charAt(0).toUpperCase() + type.slice(1)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
{/* Search and filters */}
|
||||
<div className="flex justify-between">
|
||||
<div className="flex w-full xl:w-5/12">
|
||||
<Input
|
||||
icon="search"
|
||||
data-testid="search-store-input"
|
||||
type="text"
|
||||
placeholder={`Search ${flowType}...`}
|
||||
className="mr-2"
|
||||
value={debouncedSearch}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
<div className="px-py mr-2 flex rounded-lg border border-zinc-100 bg-zinc-100 dark:border-zinc-800 dark:bg-zinc-800">
|
||||
{["list", "grid"].map((viewType) => (
|
||||
{!isEmptyFolder && (
|
||||
<>
|
||||
<div className="flex flex-row-reverse pb-8">
|
||||
<div className="w-full border-b dark:border-border" />
|
||||
{["components", "flows"].map((type) => (
|
||||
<Button
|
||||
key={viewType}
|
||||
key={type}
|
||||
unstyled
|
||||
size="icon"
|
||||
className={`group mx-[2px] my-[2px] rounded-lg p-2 ${
|
||||
view === viewType
|
||||
? "bg-white text-black shadow-md dark:bg-black dark:text-white"
|
||||
: "bg-zinc-100 text-zinc-500 dark:bg-zinc-800 dark:hover:bg-zinc-800"
|
||||
}`}
|
||||
onClick={() => setView(viewType as "list" | "grid")}
|
||||
id={`${type}-btn`}
|
||||
data-testid={`${type}-btn`}
|
||||
onClick={() => setFlowType(type as "flows" | "components")}
|
||||
className={`border-b ${
|
||||
flowType === type
|
||||
? "border-b-2 border-foreground text-foreground"
|
||||
: "border-border text-muted-foreground hover:text-foreground"
|
||||
} px-3 pb-2 text-sm`}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name={viewType === "list" ? "menu" : "layout-grid"}
|
||||
aria-hidden="true"
|
||||
className="h-4 w-4 group-hover:text-black dark:group-hover:text-white"
|
||||
/>
|
||||
<div className={flowType === type ? "-mb-px" : ""}>
|
||||
{type.charAt(0).toUpperCase() + type.slice(1)}
|
||||
</div>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<ShadTooltip content="Store" side="bottom">
|
||||
{/* Search and filters */}
|
||||
<div className="flex justify-between">
|
||||
<div className="flex w-full xl:w-5/12">
|
||||
<Input
|
||||
icon="Search"
|
||||
data-testid="search-store-input"
|
||||
type="text"
|
||||
placeholder={`Search ${flowType}...`}
|
||||
className="mr-2"
|
||||
value={debouncedSearch}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
<div className="px-py mr-2 flex rounded-lg border border-zinc-100 bg-zinc-100 dark:border-zinc-800 dark:bg-zinc-800">
|
||||
{["list", "grid"].map((viewType) => (
|
||||
<Button
|
||||
key={viewType}
|
||||
unstyled
|
||||
size="icon"
|
||||
className={`group mx-[2px] my-[2px] rounded-lg p-2 ${
|
||||
view === viewType
|
||||
? "bg-white text-black shadow-md dark:bg-black dark:text-white"
|
||||
: "bg-zinc-100 text-zinc-500 dark:bg-zinc-800 dark:hover:bg-zinc-800"
|
||||
}`}
|
||||
onClick={() => setView(viewType as "list" | "grid")}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name={viewType === "list" ? "Menu" : "LayoutGrid"}
|
||||
aria-hidden="true"
|
||||
className="h-4 w-4 group-hover:text-black dark:group-hover:text-white"
|
||||
/>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{/* <ShadTooltip content="Store" side="bottom">
|
||||
<Button variant="outline" onClick={() => navigate("/store")}>
|
||||
<ForwardedIconComponent
|
||||
name="store"
|
||||
|
|
@ -135,25 +144,27 @@ const HeaderComponent = ({
|
|||
Browse Store
|
||||
</span>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
<ShadTooltip content="New Flow" side="bottom">
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={() => setNewProjectModal(true)}
|
||||
id="new-project-btn"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="plus"
|
||||
aria-hidden="true"
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<span className="hidden whitespace-nowrap font-semibold md:inline">
|
||||
New Flow
|
||||
</span>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</ShadTooltip> */}
|
||||
<ShadTooltip content="New Flow" side="bottom">
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={() => setNewProjectModal(true)}
|
||||
id="new-project-btn"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="Plus"
|
||||
aria-hidden="true"
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<span className="hidden whitespace-nowrap font-semibold md:inline">
|
||||
New Flow
|
||||
</span>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,74 +5,70 @@ import { Card } from "@/components/ui/card";
|
|||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||
import { track } from "@/customization/utils/analytics";
|
||||
import useDeleteFlow from "@/hooks/flows/use-delete-flow";
|
||||
import DeleteConfirmationModal from "@/modals/deleteConfirmationModal";
|
||||
import IOModal from "@/modals/IOModal";
|
||||
import useAlertStore from "@/stores/alertStore";
|
||||
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
||||
import { FlowType } from "@/types/flow";
|
||||
import { getInputsAndOutputs } from "@/utils/storeUtils";
|
||||
import { useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import useDescriptionModal from "../../oldComponents/componentsComponent/hooks/use-description-modal";
|
||||
import { getTemplateStyle } from "../../utils/get-template-style";
|
||||
import { useGetTemplateStyle } from "../../utils/get-template-style";
|
||||
import { timeElapsed } from "../../utils/time-elapse";
|
||||
import DropdownComponent from "../dropdown";
|
||||
|
||||
const ListComponent = ({ flowData }: { flowData: FlowType }) => {
|
||||
const navigate = useCustomNavigate();
|
||||
// const [openPlayground, setOpenPlayground] = useState(false);
|
||||
// const [loadingPlayground, setLoadingPlayground] = useState(false);
|
||||
/* const [openPlayground, setOpenPlayground] = useState(false);
|
||||
const [loadingPlayground, setLoadingPlayground] = useState(false); */
|
||||
const [openDelete, setOpenDelete] = useState(false);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const { deleteFlow } = useDeleteFlow();
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||
/* const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); */
|
||||
const { folderId } = useParams();
|
||||
const isComponent = flowData.is_component ?? false;
|
||||
const setFlowToCanvas = useFlowsManagerStore(
|
||||
(state) => state.setFlowToCanvas,
|
||||
);
|
||||
const { icon, icon_bg_color } = getTemplateStyle(flowData);
|
||||
const { getIcon } = useGetTemplateStyle(flowData);
|
||||
|
||||
const editFlowLink = `/flow/${flowData.id}${folderId ? `/folder/${folderId}` : ""}`;
|
||||
|
||||
function hasPlayground(flow?: FlowType) {
|
||||
/* function hasPlayground(flow?: FlowType) {
|
||||
if (!flow) {
|
||||
return false;
|
||||
}
|
||||
const { inputs, outputs } = getInputsAndOutputs(flow?.data?.nodes ?? []);
|
||||
return inputs.length > 0 || outputs.length > 0;
|
||||
}
|
||||
} */
|
||||
|
||||
// const handlePlaygroundClick = () => {
|
||||
// track("Playground Button Clicked", { flowId: flowData.id });
|
||||
// setLoadingPlayground(true);
|
||||
/* const handlePlaygroundClick = () => {
|
||||
track("Playground Button Clicked", { flowId: flowData.id });
|
||||
setLoadingPlayground(true);
|
||||
|
||||
// if (flowData) {
|
||||
// if (!hasPlayground(flowData)) {
|
||||
// setErrorData({
|
||||
// title: "Error",
|
||||
// list: ["This flow doesn't have a playground."],
|
||||
// });
|
||||
// setLoadingPlayground(false);
|
||||
// return;
|
||||
// }
|
||||
// setCurrentFlow(flowData);
|
||||
// setOpenPlayground(true);
|
||||
// setLoadingPlayground(false);
|
||||
// } else {
|
||||
// setErrorData({
|
||||
// title: "Error",
|
||||
// list: ["Error getting flow data."],
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
if (flowData) {
|
||||
if (!hasPlayground(flowData)) {
|
||||
setErrorData({
|
||||
title: "Error",
|
||||
list: ["This flow doesn't have a playground."],
|
||||
});
|
||||
setLoadingPlayground(false);
|
||||
return;
|
||||
}
|
||||
setCurrentFlow(flowData);
|
||||
setOpenPlayground(true);
|
||||
setLoadingPlayground(false);
|
||||
} else {
|
||||
setErrorData({
|
||||
title: "Error",
|
||||
list: ["Error getting flow data."],
|
||||
});
|
||||
}
|
||||
}; */
|
||||
|
||||
const handleClick = async () => {
|
||||
if (!isComponent) {
|
||||
|
|
@ -107,24 +103,24 @@ const ListComponent = ({ flowData }: { flowData: FlowType }) => {
|
|||
draggable
|
||||
onDragStart={onDragStart}
|
||||
onClick={handleClick}
|
||||
className={`my-2 flex h-[110px] flex-row bg-background ${
|
||||
className={`my-2 flex flex-row bg-background ${
|
||||
isComponent ? "cursor-default" : "cursor-pointer"
|
||||
} justify-between rounded-lg border border-zinc-100 p-5 shadow-sm hover:border-border dark:border-zinc-800 dark:hover:border-muted-foreground`}
|
||||
} group justify-between rounded-lg border border-border p-4 hover:border-placeholder-foreground hover:shadow-sm`}
|
||||
>
|
||||
{/* left side */}
|
||||
<div
|
||||
className={`flex min-w-0 ${
|
||||
isComponent ? "cursor-default" : "cursor-pointer"
|
||||
} items-center gap-2`}
|
||||
} items-center gap-4`}
|
||||
>
|
||||
{/* Icon */}
|
||||
<div
|
||||
className={`item-center mr-3 flex justify-center rounded-lg border ${flowData?.icon_bg_color || icon_bg_color} p-3`}
|
||||
className={`item-center flex justify-center rounded-lg bg-muted p-3`}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name={flowData?.icon || icon}
|
||||
name={flowData?.icon || getIcon()}
|
||||
aria-hidden="true"
|
||||
className="flex h-5 w-5 items-center justify-center dark:text-black"
|
||||
className="flex h-5 w-5 items-center justify-center text-foreground"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -133,11 +129,11 @@ const ListComponent = ({ flowData }: { flowData: FlowType }) => {
|
|||
<div className="text-md flex truncate pr-2 font-semibold max-md:w-full">
|
||||
<span className="truncate">{flowData.name}</span>
|
||||
</div>
|
||||
<div className="item-baseline flex text-xs text-zinc-500 dark:text-zinc-400">
|
||||
<div className="item-baseline flex text-xs text-muted-foreground">
|
||||
Edited {timeElapsed(flowData.updated_at)} ago
|
||||
</div>
|
||||
</div>
|
||||
<div className="line-clamp-2 flex text-sm text-zinc-800 truncate-doubleline dark:text-white">
|
||||
<div className="overflow-hidden truncate text-sm text-primary">
|
||||
{flowData.description}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -164,20 +160,20 @@ const ListComponent = ({ flowData }: { flowData: FlowType }) => {
|
|||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
size="iconMd"
|
||||
data-testid="home-dropdown-menu"
|
||||
className="group h-10 w-10 border-none dark:hover:bg-zinc-700"
|
||||
className="group"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="ellipsis"
|
||||
name="Ellipsis"
|
||||
aria-hidden="true"
|
||||
className="h-5 w-5 dark:text-zinc-400 dark:group-hover:text-white"
|
||||
className="h-5 w-5 text-muted-foreground group-hover:text-foreground"
|
||||
/>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="mr-[30px] w-[185px] bg-white dark:bg-black"
|
||||
className="w-[185px]"
|
||||
sideOffset={5}
|
||||
side="bottom"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import { Card } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
const ListSkeleton = () => {
|
||||
return (
|
||||
<Card className="my-2 flex flex-row justify-between rounded-lg border border-border bg-background p-4">
|
||||
{/* left side */}
|
||||
<div className="flex min-w-0 items-center gap-4">
|
||||
{/* Icon skeleton */}
|
||||
<div className="flex h-[52px] w-[52px] items-center justify-center rounded-lg">
|
||||
<Skeleton className="h-full w-full rounded-lg" />
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-0 flex-col justify-start gap-2">
|
||||
{/* Title and time skeleton */}
|
||||
<div className="flex min-w-0 items-baseline max-md:flex-col">
|
||||
<Skeleton className="h-5 w-[150px]" />
|
||||
<Skeleton className="ml-2 h-4 w-[180px]" />
|
||||
</div>
|
||||
{/* Description skeleton */}
|
||||
<Skeleton className="h-4 w-[250px]" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* right side */}
|
||||
<div className="ml-5 flex items-center gap-2">
|
||||
<Skeleton className="h-10 w-10 rounded-md" />
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListSkeleton;
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Modals.tsx
|
||||
import TemplatesModal from "@/modals/templatesModal";
|
||||
import DeleteConfirmationModal from "../../../../modals/deleteConfirmationModal";
|
||||
|
||||
interface ModalsProps {
|
||||
openModal: boolean;
|
||||
setOpenModal: (value: boolean) => void;
|
||||
openDeleteFolderModal: boolean;
|
||||
setOpenDeleteFolderModal: (value: boolean) => void;
|
||||
handleDeleteFolder: () => void;
|
||||
}
|
||||
|
||||
const ModalsComponent = ({
|
||||
openModal = false,
|
||||
setOpenModal = () => {},
|
||||
openDeleteFolderModal = false,
|
||||
setOpenDeleteFolderModal = () => {},
|
||||
handleDeleteFolder = () => {},
|
||||
}: ModalsProps) => (
|
||||
<>
|
||||
{openModal && <TemplatesModal open={openModal} setOpen={setOpenModal} />}
|
||||
{openDeleteFolderModal && (
|
||||
<DeleteConfirmationModal
|
||||
open={openDeleteFolderModal}
|
||||
setOpen={setOpenDeleteFolderModal}
|
||||
onConfirm={() => {
|
||||
handleDeleteFolder();
|
||||
setOpenDeleteFolderModal(false);
|
||||
}}
|
||||
description="folder"
|
||||
note={
|
||||
"Deleting the selected folder will remove all associated flows and components."
|
||||
}
|
||||
>
|
||||
<></>
|
||||
</DeleteConfirmationModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
export default ModalsComponent;
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
// Modals.tsx
|
||||
import TemplatesModal from "@/modals/templatesModal";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
import DeleteConfirmationModal from "../../../../modals/deleteConfirmationModal";
|
||||
import { cn } from "../../../../utils/utils";
|
||||
|
||||
interface ModalsProps {
|
||||
openModal: boolean;
|
||||
|
|
@ -35,13 +32,7 @@ const ModalsComponent = ({
|
|||
"Deleting the selected folder will remove all associated flows and components."
|
||||
}
|
||||
>
|
||||
<Button variant="ghost" size="icon" className={"whitespace-nowrap"}>
|
||||
<IconComponent
|
||||
data-testid={`delete-folder`}
|
||||
name="Trash2"
|
||||
className={cn("h-5 w-5")}
|
||||
/>
|
||||
</Button>
|
||||
<></>
|
||||
</DeleteConfirmationModal>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
45
src/frontend/src/pages/MainPage/pages/emptyFolder/index.tsx
Normal file
45
src/frontend/src/pages/MainPage/pages/emptyFolder/index.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import ForwardedIconComponent from "@/components/genericIconComponent";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useFolderStore } from "@/stores/foldersStore";
|
||||
|
||||
type EmptyFolderProps = {
|
||||
setOpenModal: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export const EmptyFolder = ({ setOpenModal }: EmptyFolderProps) => {
|
||||
const folders = useFolderStore((state) => state.folders);
|
||||
|
||||
return (
|
||||
<div className="m-0 h-full w-full bg-secondary p-0">
|
||||
<div className="text-container">
|
||||
<div className="relative z-20 flex w-full flex-col items-center justify-center gap-2">
|
||||
<h3
|
||||
className="pt-5 font-chivo text-2xl font-semibold"
|
||||
data-testid="mainpage_title"
|
||||
>
|
||||
{folders?.length > 1 ? "Empty folder" : "Start building"}
|
||||
</h3>
|
||||
<p className="pb-5 text-sm text-secondary-foreground">
|
||||
Begin with a template, or start from scratch.
|
||||
</p>
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={() => setOpenModal(true)}
|
||||
id="new-project-btn"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="plus"
|
||||
aria-hidden="true"
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<span className="hidden whitespace-nowrap font-semibold md:inline">
|
||||
New Flow
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmptyFolder;
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,16 +1,18 @@
|
|||
import CardsWrapComponent from "@/components/cardsWrapComponent";
|
||||
import ForwardedIconComponent from "@/components/genericIconComponent";
|
||||
import PaginatorComponent from "@/components/paginatorComponent";
|
||||
import { useGetFolderQuery } from "@/controllers/API/queries/folders/use-get-folder";
|
||||
import { ENABLE_DATASTAX_LANGFLOW } from "@/customization/feature-flags";
|
||||
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
||||
import { useFolderStore } from "@/stores/foldersStore";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import GridComponent from "../../components/grid";
|
||||
import GridSkeleton from "../../components/gridSkeleton";
|
||||
import HeaderComponent from "../../components/header";
|
||||
import ListComponent from "../../components/list";
|
||||
import ListSkeleton from "../../components/listSkeleton";
|
||||
import useFileDrop from "../../hooks/use-on-file-drop";
|
||||
import ModalsComponent from "../../oldComponents/modalsComponent";
|
||||
import EmptyFolder from "../emptyFolder";
|
||||
|
||||
const HomePage = ({ type }) => {
|
||||
const [view, setView] = useState<"grid" | "list">(() => {
|
||||
|
|
@ -25,9 +27,14 @@ const HomePage = ({ type }) => {
|
|||
const handleFileDrop = useFileDrop("flows");
|
||||
const [flowType, setFlowType] = useState<"flows" | "components">(type);
|
||||
const myCollectionId = useFolderStore((state) => state.myCollectionId);
|
||||
const [folderName, setFolderName] = useState("");
|
||||
const folders = useFolderStore((state) => state.folders);
|
||||
const folderName =
|
||||
folders.find((folder) => folder.id === folderId)?.name ??
|
||||
folders[0]?.name ??
|
||||
"";
|
||||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
|
||||
const { data: folderData, isFetching } = useGetFolderQuery({
|
||||
const { data: folderData, isLoading } = useGetFolderQuery({
|
||||
id: folderId ?? myCollectionId!,
|
||||
page: pageIndex,
|
||||
size: pageSize,
|
||||
|
|
@ -50,12 +57,6 @@ const HomePage = ({ type }) => {
|
|||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (folderData && folderData?.folder?.name) {
|
||||
setFolderName(folderData.folder.name);
|
||||
}
|
||||
}, [folderData, folderData?.folder?.name]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem("view", view);
|
||||
}, [view]);
|
||||
|
|
@ -70,6 +71,10 @@ const HomePage = ({ type }) => {
|
|||
setPageIndex(1);
|
||||
}, []);
|
||||
|
||||
const isEmptyFolder =
|
||||
flows?.find((flow) => flow.folder_id === (folderId ?? myCollectionId)) ===
|
||||
undefined;
|
||||
|
||||
return (
|
||||
<CardsWrapComponent
|
||||
onFileDrop={handleFileDrop}
|
||||
|
|
@ -110,11 +115,25 @@ const HomePage = ({ type }) => {
|
|||
setView={setView}
|
||||
setNewProjectModal={setNewProjectModal}
|
||||
setSearch={onSearch}
|
||||
isEmptyFolder={isEmptyFolder}
|
||||
/>
|
||||
|
||||
{flowType === "flows" ? (
|
||||
{isEmptyFolder ? (
|
||||
<EmptyFolder setOpenModal={setNewProjectModal} />
|
||||
) : (
|
||||
<div className="mt-6">
|
||||
{data && data.pagination.total > 0 ? (
|
||||
{isLoading ? (
|
||||
view === "grid" ? (
|
||||
<div className="mt-1 grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||
<GridSkeleton />
|
||||
<GridSkeleton />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col">
|
||||
<ListSkeleton />
|
||||
<ListSkeleton />
|
||||
</div>
|
||||
)
|
||||
) : data && data.pagination.total > 0 ? (
|
||||
view === "grid" ? (
|
||||
<div className="mt-1 grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||
{data.flows.map((flow) => (
|
||||
|
|
@ -128,39 +147,19 @@ const HomePage = ({ type }) => {
|
|||
))}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="pt-2 text-center">
|
||||
No saved or custom components. Learn more about{" "}
|
||||
) : flowType === "flows" ? (
|
||||
<div className="pt-2 text-center text-sm text-secondary-foreground">
|
||||
No flows in this folder.{" "}
|
||||
<a
|
||||
href="https://docs.langflow.org/components-custom-components"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="underline"
|
||||
onClick={() => setNewProjectModal(true)}
|
||||
className="cursor-pointer underline"
|
||||
>
|
||||
creating custom components
|
||||
Create a new flow
|
||||
</a>
|
||||
, or browse the store.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-6">
|
||||
{data && data.pagination.total > 0 ? (
|
||||
view === "grid" ? (
|
||||
<div className="mt-1 grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||
{data.flows.map((flow) => (
|
||||
<GridComponent key={flow.id} flowData={flow} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col">
|
||||
{data.flows.map((flow) => (
|
||||
<ListComponent key={flow.id} flowData={flow} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="pt-2 text-center">
|
||||
<div className="pt-2 text-center text-sm text-secondary-foreground">
|
||||
No saved or custom components. Learn more about{" "}
|
||||
<a
|
||||
href="https://docs.langflow.org/components-custom-components"
|
||||
|
|
@ -177,7 +176,7 @@ const HomePage = ({ type }) => {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{!isFetching && data.pagination.total >= 10 && (
|
||||
{!isLoading && !isEmptyFolder && data.pagination.total >= 10 && (
|
||||
<div className="relative flex justify-end px-3 py-6">
|
||||
<PaginatorComponent
|
||||
storeComponent={true}
|
||||
|
|
@ -191,6 +190,7 @@ const HomePage = ({ type }) => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ModalsComponent
|
||||
openModal={newProjectModal}
|
||||
setOpenModal={setNewProjectModal}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import CardsWrapComponent from "@/components/cardsWrapComponent";
|
||||
import FolderSidebarNav from "@/components/folderSidebarComponent";
|
||||
import SideBarFoldersButtonsComponent from "@/components/folderSidebarComponent/components/sideBarFolderButtons";
|
||||
import LoadingComponent from "@/components/loadingComponent";
|
||||
import { SidebarProvider } from "@/components/ui/sidebar";
|
||||
import { useDeleteFolders } from "@/controllers/API/queries/folders";
|
||||
import { useGetFolderQuery } from "@/controllers/API/queries/folders/use-get-folder";
|
||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||
import { LoadingPage } from "@/pages/LoadingPage";
|
||||
import useAlertStore from "@/stores/alertStore";
|
||||
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
||||
import { useFolderStore } from "@/stores/foldersStore";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Outlet, useParams } from "react-router-dom";
|
||||
import { PaginatedFolderType } from "../entities";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import useFileDrop from "../hooks/use-on-file-drop";
|
||||
import ModalsComponent from "../oldComponents/modalsComponent";
|
||||
import EmptyPage from "./emptyPage";
|
||||
|
|
@ -19,35 +19,19 @@ export default function CollectionPage(): JSX.Element {
|
|||
const [openDeleteFolderModal, setOpenDeleteFolderModal] = useState(false);
|
||||
const setFolderToEdit = useFolderStore((state) => state.setFolderToEdit);
|
||||
const navigate = useCustomNavigate();
|
||||
const { folderId } = useParams();
|
||||
const myCollectionId = useFolderStore((state) => state.myCollectionId);
|
||||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
const examples = useFlowsManagerStore((state) => state.examples);
|
||||
const handleFileDrop = useFileDrop("flow");
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const folderToEdit = useFolderStore((state) => state.folderToEdit);
|
||||
const showFolderModal = useFolderStore((state) => state.showFolderModal);
|
||||
const folders = useFolderStore((state) => state.folders);
|
||||
const setShowFolderModal = useFolderStore(
|
||||
(state) => state.setShowFolderModal,
|
||||
);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useEffect(() => {
|
||||
return () => queryClient.removeQueries({ queryKey: ["useGetFolder"] });
|
||||
}, []);
|
||||
|
||||
const { isFetching, data } = useGetFolderQuery({
|
||||
id: folderId ?? myCollectionId!,
|
||||
});
|
||||
|
||||
const [folderData, setFolderData] = useState<PaginatedFolderType | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setFolderData(data ?? null);
|
||||
}, [data]);
|
||||
|
||||
const { mutate } = useDeleteFolders();
|
||||
|
||||
const handleDeleteFolder = () => {
|
||||
|
|
@ -73,58 +57,41 @@ export default function CollectionPage(): JSX.Element {
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{(folderData?.flows?.items?.length !== 0 || folders?.length > 1) && (
|
||||
<aside
|
||||
className={`flex w-2/6 min-w-[220px] max-w-[20rem] flex-col border-r bg-background px-4 lg:inline ${
|
||||
showFolderModal ? "" : "hidden"
|
||||
}`}
|
||||
>
|
||||
<FolderSidebarNav
|
||||
<SidebarProvider>
|
||||
{flows &&
|
||||
examples &&
|
||||
folders &&
|
||||
(flows?.length !== examples?.length || folders?.length > 1) && (
|
||||
<SideBarFoldersButtonsComponent
|
||||
handleChangeFolder={(id: string) => {
|
||||
navigate(`all/folder/${id}`);
|
||||
setShowFolderModal(false);
|
||||
}}
|
||||
handleDeleteFolder={(item) => {
|
||||
setFolderToEdit(item);
|
||||
setOpenDeleteFolderModal(true);
|
||||
}}
|
||||
/>
|
||||
</aside>
|
||||
)}
|
||||
|
||||
{!isFetching && folderData ? (
|
||||
<div
|
||||
className={`relative mx-auto h-full w-full overflow-y-scroll ${
|
||||
showFolderModal ? "opacity-80 blur-[2px]" : ""
|
||||
}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (showFolderModal) {
|
||||
setShowFolderModal(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CardsWrapComponent
|
||||
onFileDrop={handleFileDrop}
|
||||
dragMessage={`Drop your file(s) here`}
|
||||
>
|
||||
{folderData && folderData?.flows?.items?.length !== 0 ? (
|
||||
<Outlet />
|
||||
) : (
|
||||
<EmptyPage
|
||||
setOpenModal={setOpenModal}
|
||||
setShowFolderModal={setShowFolderModal}
|
||||
folderData={folderData}
|
||||
/>
|
||||
)}
|
||||
</CardsWrapComponent>
|
||||
</div>
|
||||
) : (
|
||||
<LoadingPage />
|
||||
)}
|
||||
|
||||
)}
|
||||
<main className="flex flex-1">
|
||||
{flows && examples && folders ? (
|
||||
<div className={`relative mx-auto h-full w-full overflow-y-scroll`}>
|
||||
<CardsWrapComponent
|
||||
onFileDrop={handleFileDrop}
|
||||
dragMessage={`Drop your file(s) here`}
|
||||
>
|
||||
{flows?.length !== examples?.length || folders?.length > 1 ? (
|
||||
<Outlet />
|
||||
) : (
|
||||
<EmptyPage setOpenModal={setOpenModal} />
|
||||
)}
|
||||
</CardsWrapComponent>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<LoadingComponent remSize={30} />
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
<ModalsComponent
|
||||
openModal={openModal}
|
||||
setOpenModal={setOpenModal}
|
||||
|
|
@ -132,6 +99,6 @@ export default function CollectionPage(): JSX.Element {
|
|||
setOpenDeleteFolderModal={setOpenDeleteFolderModal}
|
||||
handleDeleteFolder={handleDeleteFolder}
|
||||
/>
|
||||
</>
|
||||
</SidebarProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,23 @@
|
|||
import { TEMPLATES_DATA } from "../constants";
|
||||
import { useTypesStore } from "@/stores/typesStore";
|
||||
import { FlowType } from "@/types/flow";
|
||||
import { nodeIconsLucide } from "@/utils/styleUtils";
|
||||
|
||||
export const getTemplateStyle = (flowData: {
|
||||
name: string;
|
||||
}): { icon: string; icon_bg_color: string } => {
|
||||
const { icon, icon_bg_color } = TEMPLATES_DATA.examples.find((example) =>
|
||||
flowData.name.includes(example.name),
|
||||
) ?? { icon: "circle-help", icon_bg_color: "bg-purple-300" };
|
||||
return { icon, icon_bg_color };
|
||||
export const useGetTemplateStyle = (
|
||||
flowData: FlowType,
|
||||
): { getIcon: () => string } => {
|
||||
const getIcon = () => {
|
||||
if (flowData.is_component) {
|
||||
const dataType = flowData.data?.nodes[0].data.type;
|
||||
const isGroup = !!flowData.data?.nodes[0].data.node?.flow;
|
||||
const icon = flowData.data?.nodes[0].data.node?.icon;
|
||||
const types = useTypesStore((state) => state.types);
|
||||
const name = nodeIconsLucide[dataType] ? dataType : types[dataType];
|
||||
const iconName = icon || (isGroup ? "group_components" : name);
|
||||
return iconName;
|
||||
} else {
|
||||
return flowData.icon ?? "Workflow";
|
||||
}
|
||||
};
|
||||
|
||||
return { getIcon };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,7 +17,4 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
|||
setStarterProjectId: (id) => set(() => ({ starterProjectId: id })),
|
||||
folders: [],
|
||||
setFolders: (folders) => set(() => ({ folders: folders })),
|
||||
showFolderModal: false,
|
||||
setShowFolderModal: (showFolderModal) =>
|
||||
set(() => ({ showFolderModal: showFolderModal })),
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -1281,8 +1281,6 @@
|
|||
left: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
text-shadow: 1px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
:root {
|
||||
|
|
@ -1326,7 +1324,7 @@
|
|||
var(--color-bg1);
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0.6;
|
||||
background-blend-mode: overlay;
|
||||
|
||||
svg {
|
||||
display: none;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
:root {
|
||||
--font-sans: "Inter", sans-serif;
|
||||
--font-mono: "JetBrains Mono", monospace;
|
||||
--font-chivo: "Chivo", sans-serif;
|
||||
|
||||
--foreground: 0 0% 0%; /* hsl(0, 0%, 0%) */
|
||||
--background: 0 0% 100%; /* hsl(0, 0%, 100%) */
|
||||
|
|
|
|||
|
|
@ -13,6 +13,4 @@ export type FoldersStoreType = {
|
|||
setStarterProjectId: (id: string) => void;
|
||||
folders: FolderType[];
|
||||
setFolders: (folders: FolderType[]) => void;
|
||||
showFolderModal: boolean;
|
||||
setShowFolderModal: (show: boolean) => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1596,6 +1596,7 @@ export const createNewFlow = (
|
|||
name: flow?.name ? flow.name : "Untitled document",
|
||||
data: flowData,
|
||||
id: "",
|
||||
icon: flow?.icon ?? undefined,
|
||||
is_component: flow?.is_component ?? false,
|
||||
folder_id: folderId,
|
||||
endpoint_name: flow?.endpoint_name ?? undefined,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import {
|
|||
Command,
|
||||
Compass,
|
||||
Copy,
|
||||
CopyPlus,
|
||||
CornerDownLeft,
|
||||
Cpu,
|
||||
CpuIcon,
|
||||
|
|
@ -96,6 +97,7 @@ import {
|
|||
Keyboard,
|
||||
Laptop2,
|
||||
Layers,
|
||||
LayoutGrid,
|
||||
LayoutPanelTop,
|
||||
Link,
|
||||
Link2,
|
||||
|
|
@ -127,6 +129,7 @@ import {
|
|||
PanelLeftClose,
|
||||
PanelLeftOpen,
|
||||
PanelRightClose,
|
||||
PanelRightOpen,
|
||||
Paperclip,
|
||||
PaperclipIcon,
|
||||
Pen,
|
||||
|
|
@ -792,6 +795,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
ArrowBigUp,
|
||||
PanelRightClose,
|
||||
Dot,
|
||||
LayoutGrid,
|
||||
StickyNote,
|
||||
note: StickyNote,
|
||||
RotateCcw,
|
||||
|
|
@ -808,7 +812,9 @@ export const nodeIconsLucide: iconsType = {
|
|||
ArrowUpRight,
|
||||
Scroll,
|
||||
Image,
|
||||
CopyPlus,
|
||||
Pen,
|
||||
PanelRightOpen,
|
||||
CornerDownLeft,
|
||||
ChevronsDownUp,
|
||||
OptionIcon,
|
||||
|
|
|
|||
|
|
@ -258,6 +258,7 @@ const config = {
|
|||
fontFamily: {
|
||||
sans: ["var(--font-sans)", ...fontFamily.sans],
|
||||
mono: ["var(--font-mono)", ...fontFamily.mono],
|
||||
chivo: ["var(--font-chivo)", ...fontFamily.sans],
|
||||
},
|
||||
boxShadow: {
|
||||
"frozen-ring": "0 0 10px 2px rgba(128, 190, 230, 0.5)",
|
||||
|
|
|
|||
|
|
@ -41,9 +41,17 @@ test("CRUD folders", async ({ page }) => {
|
|||
await page.getByText("Select All").first().isVisible();
|
||||
|
||||
await page.getByTestId("add-folder-button").click();
|
||||
await page.getByText("New Folder").last().isVisible();
|
||||
await page
|
||||
.locator("[data-testid='folder-sidebar']")
|
||||
.getByText("New Folder")
|
||||
.last()
|
||||
.isVisible();
|
||||
await page.waitForTimeout(1000);
|
||||
await page.getByText("New Folder").last().dblclick();
|
||||
await page
|
||||
.locator("[data-testid='folder-sidebar']")
|
||||
.getByText("New Folder")
|
||||
.last()
|
||||
.dblclick();
|
||||
|
||||
const element = await page.getByTestId("input-folder");
|
||||
await element.fill("new folder test name");
|
||||
|
|
@ -172,9 +180,17 @@ test("change flow folder", async ({ page }) => {
|
|||
await page.getByText("Select All").first().isVisible();
|
||||
|
||||
await page.getByTestId("add-folder-button").click();
|
||||
await page.getByText("New Folder").last().isVisible();
|
||||
await page
|
||||
.locator("[data-testid='folder-sidebar']")
|
||||
.getByText("New Folder")
|
||||
.last()
|
||||
.isVisible();
|
||||
await page.waitForTimeout(1000);
|
||||
await page.getByText("New Folder").last().dblclick();
|
||||
await page
|
||||
.locator("[data-testid='folder-sidebar']")
|
||||
.getByText("New Folder")
|
||||
.last()
|
||||
.dblclick();
|
||||
await page.getByTestId("input-folder").fill("new folder test name");
|
||||
await page.keyboard.press("Enter");
|
||||
await page.getByText("new folder test name").last().isVisible();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue