refactor: Cleanup up unused frontend files using Knip (#7506)

* extrasidebar cleanup

* shad component cleanup

* components and types batch

* controllers cleanup

* helpers and table components

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Mike Fortman 2025-04-17 10:19:22 -05:00 committed by GitHub
commit 960d46250f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
76 changed files with 12 additions and 4011 deletions

View file

@ -14,22 +14,18 @@
"@million/lint": "^1.0.0-rc.26",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-form": "^0.0.3",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-menubar": "^1.0.4",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slider": "^1.2.1",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.7",
"@tabler/icons-react": "^3.6.0",
"@tailwindcss/forms": "^0.5.7",
@ -3038,6 +3034,7 @@
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.0.3.tgz",
"integrity": "sha512-kgE+Z/haV6fxE5WqIXj05KkaXa3OkZASoTDy25yX2EIp/x0c54rOH/vFr5nOZTg7n7T1z8bSyXmiVIFP9bbhPQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
@ -3066,6 +3063,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
"integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10"
}
@ -3074,6 +3072,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
"integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
@ -3091,6 +3090,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz",
"integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
@ -3108,6 +3108,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
"integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-layout-effect": "1.0.1"
@ -3126,6 +3127,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz",
"integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-primitive": "1.0.3"
@ -3149,6 +3151,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
"integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-slot": "1.0.2"
@ -3172,6 +3175,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1"
@ -3190,6 +3194,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
"integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
@ -3289,37 +3294,6 @@
}
}
},
"node_modules/@radix-ui/react-menubar": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.6.tgz",
"integrity": "sha512-FHq7+3DlXwh/7FOM4i0G4bC4vPjiq89VEEvNF4VMLchGnaUuUbE5uKXMUCjdKaOghEEMeiKa5XCa2Pk4kteWmg==",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-collection": "1.1.2",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-direction": "1.1.0",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-menu": "2.1.6",
"@radix-ui/react-primitive": "2.0.2",
"@radix-ui/react-roving-focus": "1.1.2",
"@radix-ui/react-use-controllable-state": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.6.tgz",
@ -3455,29 +3429,6 @@
}
}
},
"node_modules/@radix-ui/react-progress": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.2.tgz",
"integrity": "sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA==",
"dependencies": {
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-primitive": "2.0.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-roving-focus": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz",
@ -3576,6 +3527,7 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.2.3.tgz",
"integrity": "sha512-nNrLAWLjGESnhqBqcCNW4w2nn7LxudyMzeB6VgdyAnFLC6kfQgnAjSL2v6UkQTnDctJBlxrmxfplWS4iYjdUTw==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.0",
"@radix-ui/primitive": "1.1.1",
@ -3678,30 +3630,6 @@
}
}
},
"node_modules/@radix-ui/react-toggle": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.2.tgz",
"integrity": "sha512-lntKchNWx3aCHuWKiDY+8WudiegQvBpDRAYL8dKLRvKEH8VOpl0XX6SSU/bUBqIRJbcTy4+MW06Wv8vgp10rzQ==",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-primitive": "2.0.2",
"@radix-ui/react-use-controllable-state": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.8.tgz",

View file

@ -9,22 +9,18 @@
"@million/lint": "^1.0.0-rc.26",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-form": "^0.0.3",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-menubar": "^1.0.4",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slider": "^1.2.1",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.7",
"@tabler/icons-react": "^3.6.0",
"@tailwindcss/forms": "^0.5.7",

View file

@ -1,4 +0,0 @@
export const convertToTableRows = (obj: Object) => {
const tokensArray = [Object.values(obj)[0]];
return tokensArray;
};

View file

@ -1,33 +0,0 @@
import { useEffect } from "react";
export function useOnClickOutside(ref, handler) {
useEffect(() => {
const listener = (event: Event) => {
// Do nothing if clicking ref's element or its children
if (!ref.current || ref.current.contains(event.target)) {
return;
}
handler(event);
};
// Attach the listener to the document
document.addEventListener("mousedown", listener, { passive: true });
// Attach the listener to the react-flow instance
const reactFlowContainer = document.querySelector(".react-flow");
if (reactFlowContainer) {
reactFlowContainer.addEventListener("mousedown", listener, {
passive: true,
});
}
// Clean up the listener when the component is unmounted
return () => {
document.removeEventListener("mousedown", listener);
if (reactFlowContainer) {
reactFlowContainer.removeEventListener("mousedown", listener);
}
};
}, [ref, handler]); // Rerun only if ref or handler changes
}

View file

@ -1,72 +0,0 @@
import { useState } from "react";
import { AccordionComponentType } from "../../../../types/components";
import IconComponent from "../../../common/genericIconComponent";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "../../../ui/custom-accordion";
export default function FolderAccordionComponent({
trigger,
open = [],
keyValue,
options,
}: AccordionComponentType): JSX.Element {
const [value, setValue] = useState(
open.length === 0 ? "" : getOpenAccordion(),
);
function getOpenAccordion(): string {
let value = "";
open.forEach((el) => {
if (el == trigger) {
value = trigger;
}
});
return value;
}
function handleClick(): void {
value === "" ? setValue(keyValue!) : setValue("");
}
return (
<>
<Accordion
type="single"
className="w-full"
value={value}
onValueChange={setValue}
>
<AccordionItem value={keyValue!} className="">
<AccordionTrigger
onClick={() => {
handleClick();
}}
className="px-2"
>
{trigger}
</AccordionTrigger>
<AccordionContent>
{options!.map((option, index) => (
<div
key={index}
className="flex cursor-pointer px-2 py-1 hover:bg-muted-foreground/10"
>
<IconComponent
name={option.icon}
className="relative top-[1.5px] mr-2 h-4 w-4"
aria-hidden="true"
/>
<span className="truncate">{option.title}</span>
</div>
))}
</AccordionContent>
</AccordionItem>
</Accordion>
</>
);
}

View file

@ -1,14 +0,0 @@
import TableAutoCellRender from "../tableComponent/components/tableAutoCellRender";
export default function ArrayReader({ array }: { array: any[] }): JSX.Element {
//TODO check array type
return (
<div>
<ul>
{array.map((item, index) => (
<li key={index}>{<TableAutoCellRender value={item} />}</li>
))}
</ul>
</div>
);
}

View file

@ -1,13 +0,0 @@
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { useEffect } from "react";
export const CatchAllRoute = () => {
const navigate = useCustomNavigate();
// Redirect to the root ("/") when the catch-all route is matched
useEffect(() => {
navigate("/");
}, []);
return null;
};

View file

@ -1,21 +0,0 @@
import ForwardedIconComponent from "../genericIconComponent";
const FetchIconComponent = ({
source,
name,
}: {
source: string;
name: string;
}) => {
return (
<div>
{source ? (
<img src={source} alt={name} />
) : (
<ForwardedIconComponent name="Unknown" />
)}
</div>
);
};
export default FetchIconComponent;

View file

@ -1,31 +0,0 @@
import { Transition } from "@headlessui/react";
import IconComponent from "../../genericIconComponent";
export default function ChatTrigger({}): JSX.Element {
return (
<Transition
show={true}
appear={true}
enter="transition ease-out duration-300"
enterFrom="translate-y-96"
enterTo="translate-y-0"
leave="transition ease-in duration-300"
leaveFrom="translate-y-0"
leaveTo="translate-y-96"
>
<button
className={
"shadow-round-btn-shadow hover:shadow-round-btn-shadow message-button cursor-pointer"
}
>
<div className="flex gap-3">
<IconComponent
name="Zap"
className={"message-button-icon h-6 w-6 transition-all"}
/>
</div>
</button>
</Transition>
);
}

View file

@ -1,54 +0,0 @@
import useAddFlow from "@/hooks/flows/use-add-flow";
import { getComponent } from "../../../../controllers/API";
import { storeComponent } from "../../../../types/store";
import cloneFlowWithParent from "../../../../utils/storeUtils";
const useInstallComponent = (
data: storeComponent,
name: string,
isStore: boolean,
downloadsCount: number,
setDownloadsCount: (value: any) => void,
setLoading: (value: boolean) => void,
setSuccessData: (value: { title: string }) => void,
setErrorData: (value: { title: string; list: string[] }) => void,
) => {
const addFlow = useAddFlow();
const handleInstall = () => {
const temp = downloadsCount;
setDownloadsCount((old) => Number(old) + 1);
setLoading(true);
getComponent(data.id)
.then((res) => {
const newFlow = cloneFlowWithParent(res, res.id, data.is_component);
addFlow({ flow: newFlow })
.then((id) => {
setSuccessData({
title: `${name} ${isStore ? "Downloaded" : "Installed"} Successfully.`,
});
setLoading(false);
})
.catch((error) => {
setLoading(false);
setErrorData({
title: `Error ${isStore ? "downloading" : "installing"} the ${name}`,
list: [error.response.data.detail],
});
});
})
.catch((err) => {
setLoading(false);
setErrorData({
title: `Error ${isStore ? "downloading" : "installing"} the ${name}`,
list: [err.response.data.detail],
});
setDownloadsCount(temp);
});
};
return { handleInstall };
};
export default useInstallComponent;

View file

@ -1,191 +0,0 @@
import { track } from "@/customization/utils/analytics";
import { useState } from "react";
import { Control } from "react-hook-form";
import useAlertStore from "../../../stores/alertStore";
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
import { FlowType } from "../../../types/flow";
import { getInputsAndOutputs } from "../../../utils/storeUtils";
import { cn } from "../../../utils/utils";
import IconComponent from "../../common/genericIconComponent";
import ShadTooltip from "../../common/shadTooltipComponent";
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "../../ui/card";
import { Checkbox } from "../../ui/checkbox";
import { FormControl, FormField } from "../../ui/form";
import useDragStart from "./hooks/use-on-drag-start";
import { convertTestName } from "./utils/convert-test-name";
export default function CollectionCardComponent({
data,
disabled = false,
onClick,
control,
}: {
data: FlowType;
disabled?: boolean;
onClick?: () => void;
control?: Control<any, any>;
}) {
const setErrorData = useAlertStore((state) => state.setErrorData);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
const getFlowById = useFlowsManagerStore((state) => state.getFlowById);
// const [openPlayground, setOpenPlayground] = useState(false);
const [loadingPlayground, setLoadingPlayground] = useState(false);
const selectedFlowsComponentsCards = useFlowsManagerStore(
(state) => state.selectedFlowsComponentsCards,
);
function hasPlayground(flow?: FlowType) {
if (!flow) {
return false;
}
const { inputs, outputs } = getInputsAndOutputs(flow?.data?.nodes ?? []);
return inputs.length > 0 || outputs.length > 0;
}
const playground = !(data.is_component ?? false);
const isSelectedCard =
selectedFlowsComponentsCards?.includes(data?.id) ?? false;
const { onDragStart } = useDragStart(data);
const handlePlaygroundClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
e.stopPropagation();
track("Playground Button Clicked", { flowId: data.id });
setLoadingPlayground(true);
if (data) {
if (!hasPlayground(data)) {
setErrorData({
title: "Error",
list: ["This flow doesn't have a playground."],
});
setLoadingPlayground(false);
return;
}
setCurrentFlow(data);
// setOpenPlayground(true);
setLoadingPlayground(false);
} else {
setErrorData({
title: "Error",
list: ["Error getting flow data."],
});
}
};
return (
<>
<Card
onDragStart={onDragStart}
draggable
data-testid={`card-${convertTestName(data.name)}`}
//TODO check color schema
className={cn(
"group relative flex h-[11rem] flex-col justify-between overflow-hidden",
!data.is_component &&
"hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#5f5f5f0e]",
disabled ? "pointer-events-none opacity-50" : "",
onClick ? "cursor-pointer" : "",
isSelectedCard ? "border border-selected" : "",
)}
onClick={onClick}
>
<div>
<CardHeader>
<div>
<CardTitle className="flex w-full items-start justify-between gap-3 text-xl">
<IconComponent
className={cn(
"visible flex-shrink-0",
data.is_component
? "mx-0.5 h-6 w-6 text-component-icon"
: "h-7 w-7 flex-shrink-0 text-flow-icon",
)}
name={data.is_component ? "ToyBrick" : "Group"}
/>
<ShadTooltip content={data.name}>
<div className="w-full truncate pr-3">{data.name}</div>
</ShadTooltip>
{control && (
<div
className="flex"
onClick={(e) => {
e.stopPropagation();
}}
>
<FormField
control={control}
name={`${data.id}`}
defaultValue={false}
render={({ field }) => (
<FormControl>
<Checkbox
data-testid={`checkbox-component`}
aria-label="checkbox-component"
checked={field.value}
onCheckedChange={field.onChange}
className="h-5 w-5 border border-ring data-[state=checked]:border-selected data-[state=checked]:bg-selected"
/>
</FormControl>
)}
/>
</div>
)}
</CardTitle>
</div>
<div className="flex gap-2">
<div className="flex w-full flex-1 flex-wrap gap-2"></div>
</div>
<CardDescription className="pb-2 pt-2">
<div className="truncate-doubleline">{data.description}</div>
</CardDescription>
</CardHeader>
</div>
<CardFooter>
<div className="z-50 flex w-full items-center justify-between gap-2">
<div className="flex w-full flex-wrap items-end justify-end gap-2">
{/* {playground && (
<Button
disabled={loadingPlayground || !hasPlayground(data)}
key={data.id}
tabIndex={-1}
variant="primary"
size="sm"
className="gap-2 whitespace-nowrap bg-muted"
data-testid={"playground-flow-button-" + data.id}
onClick={handlePlaygroundClick}
>
{!loadingPlayground ? (
<IconComponent
name="BotMessageSquareIcon"
className="h-4 w-4 select-none"
/>
) : (
<Loading className="h-4 w-4 text-medium-indigo" />
)}
Playground
</Button>
)} */}
</div>
</div>
</CardFooter>
</Card>
{/* {openPlayground && (
<IOModal
key={data.id}
cleanOnClose={true}
open={openPlayground}
setOpen={setOpenPlayground}
>
<></>
</IOModal>
)} */}
</>
);
}

View file

@ -1,3 +0,0 @@
export function convertTestName(name: string): string {
return name.replace(/ /g, "-").toLowerCase();
}

View file

@ -1,77 +0,0 @@
import { useState } from "react";
import { dropdownButtonPropsType } from "../../../types/components";
import IconComponent from "../../common/genericIconComponent";
import { Button } from "../../ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "../../ui/dropdown-menu";
export default function DropdownButton({
firstButtonName,
onFirstBtnClick,
options,
plusButton = false,
dropdownOptions = true,
isFetchingFolders = false,
}: dropdownButtonPropsType): JSX.Element {
const [showOptions, setShowOptions] = useState<boolean>(false);
return (
<div>
<DropdownMenu open={showOptions}>
<DropdownMenuTrigger asChild>
<Button
id="new-project-btn"
variant="primary"
className={
"relative" + dropdownOptions ? "pl-[12px]" : "pl-[12px] pr-10"
}
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
onFirstBtnClick();
}}
disabled={isFetchingFolders}
>
{plusButton && (
<IconComponent name="Plus" className="main-page-nav-button" />
)}
{firstButtonName}
{dropdownOptions && (
<div
className="absolute right-2 items-center text-muted-foreground"
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
setShowOptions(!showOptions);
}}
>
{!showOptions ? (
<IconComponent name="ChevronDown" />
) : (
<IconComponent name="ChevronUp" />
)}
</div>
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
onPointerDownOutside={(event) => {
event.stopPropagation();
event.preventDefault();
setShowOptions(!showOptions);
}}
>
{options.map(({ name, onBtnClick }, index) => (
<DropdownMenuItem onClick={onBtnClick} key={index}>
{name}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}

View file

@ -1,49 +0,0 @@
import { cn } from "@/utils/utils";
export const getInputClassName = ({
disabled,
password,
setSelectedOption,
selectedOption,
pwdVisible,
value,
editNode,
setSelectedOptions,
isSelected,
areOptionsSelected,
className,
}) => {
const classes = {
base: className || "",
password:
password &&
(!setSelectedOption || selectedOption === "") &&
!pwdVisible &&
value !== ""
? "text-clip password"
: "",
editNode: editNode ? "input-edit-node" : "",
paddingRight: (() => {
if (password && (setSelectedOption || setSelectedOptions))
return "pr-[70px]";
if (
(!password && (setSelectedOption || setSelectedOptions)) ||
(password && !(setSelectedOption || setSelectedOptions))
)
return "pr-8";
return "";
})(),
selected:
isSelected || areOptionsSelected
? "font-jetbrains text-sm font-medium text-foreground"
: "",
};
return cn(
classes.base,
classes.password,
classes.editNode,
classes.paddingRight,
classes.selected,
);
};

View file

@ -1,154 +0,0 @@
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import RenderIcons from "@/components/common/renderIconComponent";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useKeyboardShortcut } from "@/hooks/use-overlap-shortcuts";
import { useShortcutsStore } from "@/stores/shortcuts";
import { cn } from "@/utils/utils";
import { useCallback } from "react";
export const DropdownMenuInputList = ({
index,
dropdownOpen,
setDropdownOpen,
editNode,
handleDuplicateInput,
removeInput,
canDelete,
}: {
index: number;
dropdownOpen: number | null;
setDropdownOpen: (open: number) => void;
editNode: boolean;
handleDuplicateInput: (
index: number,
e: React.MouseEvent<HTMLDivElement> | KeyboardEvent,
) => void;
removeInput: (
index: number,
e: React.MouseEvent<HTMLDivElement> | KeyboardEvent,
) => void;
canDelete: boolean;
}) => {
const shortcuts = useShortcutsStore((state) => state.shortcuts);
const shortcutKeys = {
duplicate:
shortcuts.find((obj) => obj.name === "Duplicate")?.shortcut || "",
delete: shortcuts.find((obj) => obj.name === "Delete")?.shortcut || "",
};
const handleShortcut = useCallback(
(shortcutName: string, event: KeyboardEvent) => {
if (shortcutName === "duplicate") {
handleDuplicateInput(index, event);
} else if (shortcutName === "delete" && canDelete) {
removeInput(index, event);
}
setDropdownOpen(-1);
},
[index, handleDuplicateInput, removeInput, setDropdownOpen],
);
useKeyboardShortcut({
shortcutKeys,
isEnabled: dropdownOpen === index,
onShortcut: handleShortcut,
preventDefault: true,
stopPropagation: true,
});
return (
<>
<DropdownMenu
open={dropdownOpen === index}
onOpenChange={(open) => setDropdownOpen(open ? index : -1)}
>
<DropdownMenuTrigger
asChild
tabIndex={index}
className={cn(
"absolute bg-background transition-opacity peer-focus:opacity-0",
editNode ? "translate-x-[14rem]" : "translate-x-60",
)}
>
<Button
variant="ghost"
data-testid={`input-list-dropdown-menu-${index}-${editNode ? "edit" : "view"}`}
size={editNode ? "iconSm" : "iconMd"}
className={cn("group")}
autoFocus={false}
>
<ForwardedIconComponent
name="Ellipsis"
aria-hidden="true"
className="icon-size text-muted-foreground group-hover:text-foreground"
/>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-[185px]" side="bottom" align="start">
<DropdownMenuItem
onClick={(e) => {
handleDuplicateInput(index, e);
e.stopPropagation();
}}
className="cursor-pointer"
data-testid={`input-list-dropdown-menu-${index}-duplicate`}
>
<ForwardedIconComponent
name="CopyPlus"
aria-hidden="true"
className="mr-2 h-4 w-4"
/>
<span>Duplicate</span>
<div className="flex grow content-end justify-end self-center text-[12px]">
<span
className={`flex content-end items-center rounded-sm bg-muted px-1.5 py-[0.1em] text-muted-foreground`}
>
<RenderIcons
filteredShortcut={shortcuts
.find((obj) => obj.name === "Duplicate")
?.shortcut!?.split("+")}
/>
</span>
</div>
</DropdownMenuItem>
{canDelete && (
<DropdownMenuItem
onClick={(e) => {
removeInput(index, e);
e.stopPropagation();
}}
className="cursor-pointer text-destructive"
data-testid={`input-list-dropdown-menu-${index}-delete`}
>
<ForwardedIconComponent
name="Trash2"
aria-hidden="true"
className="mr-2 h-4 w-4"
/>
<span>Delete</span>
<div className="flex grow content-end justify-end self-center text-[12px]">
<span
className={`flex content-end items-center rounded-sm px-1.5 py-[0.1em] text-muted-foreground`}
>
<ForwardedIconComponent
name="Delete"
className="h-4 w-4 stroke-2 text-red-400"
/>
</span>
</div>
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
</>
);
};

View file

@ -1,20 +0,0 @@
import { cn } from "@/utils/utils";
export default function ResetColumns({
resetGrid,
}: {
resetGrid: () => void;
}): JSX.Element {
return (
<div className={cn("absolute bottom-4 left-6")}>
<span
className="cursor-pointer underline"
onClick={() => {
resetGrid();
}}
>
Reset Columns
</span>
</div>
);
}

View file

@ -1,9 +0,0 @@
import Loading from "../../../ui/loading";
export default function LoadingOverlay() {
return (
<div className="flex h-full w-full items-center justify-center bg-background align-middle">
<Loading />
</div>
);
}

View file

@ -1,28 +0,0 @@
import { CustomCellEditorProps } from "ag-grid-react";
import { uniqueId } from "lodash";
import ToggleShadComponent from "../../../toggleShadComponent";
export default function TableToggleCellEditor({
value,
onValueChange,
colDef,
}: CustomCellEditorProps) {
value =
(typeof value === "string" && value.toLowerCase() === "true") ||
value === true
? true
: false;
return (
<div className="flex h-full items-center px-2">
<ToggleShadComponent
value={value}
handleOnNewValue={(data) => {
onValueChange?.(data.value);
}}
editNode={true}
id={"toggle" + colDef?.colId + uniqueId()}
disabled={false}
/>
</div>
);
}

View file

@ -1,9 +0,0 @@
import { CustomTooltipProps } from "ag-grid-react";
export default function TableTooltipRender({ value }: CustomTooltipProps) {
return (
<div className="z-45 overflow-y-auto rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1">
{value}
</div>
);
}

View file

@ -1,15 +0,0 @@
export default function TextInputComponent({
text,
emissor,
}: {
text: string;
emissor: string;
}) {
return (
<div>
<strong> {emissor}</strong>
<br></br>
<span>{text}</span>
</div>
);
}

View file

@ -1,19 +0,0 @@
import { cn } from "@/utils/utils";
export const getTextAreaContentClasses = ({
editNode,
disabled,
password,
value,
textAreaContentClasses,
}) => {
return cn(
textAreaContentClasses.base,
editNode ? textAreaContentClasses.editNode : textAreaContentClasses.normal,
disabled && textAreaContentClasses.disabled,
password !== undefined &&
password &&
value !== "" &&
textAreaContentClasses.password,
);
};

View file

@ -1,15 +0,0 @@
export default function TextOutputComponent({
text,
emissor,
}: {
text: string;
emissor: string;
}) {
return (
<div>
<strong>{emissor}</strong>
<br></br>
<div className="w-80 break-all">{text}</div>
</div>
);
}

View file

@ -1,49 +0,0 @@
import { cn } from "@/utils/utils";
interface BorderBeamProps {
className?: string;
size?: number;
duration?: number;
borderWidth?: number;
anchor?: number;
colorFrom?: string;
colorTo?: string;
delay?: number;
}
export const BorderBeam = ({
className,
size = 200,
duration = 15,
anchor = 90,
borderWidth = 1.5,
colorFrom = "#ffaa40",
colorTo = "#9c40ff",
delay = 0,
}: BorderBeamProps) => {
return (
<div
style={
{
"--size": size,
"--duration": duration,
"--anchor": anchor,
"--border-width": borderWidth,
"--color-from": colorFrom,
"--color-to": colorTo,
"--delay": `-${delay}s`,
} as React.CSSProperties
}
className={cn(
"pointer-events-none absolute inset-0 rounded-[inherit] [border:calc(var(--border-width)*1px)_solid_transparent]",
// mask styles
"![mask-clip:padding-box,border-box] ![mask-composite:intersect] [mask:linear-gradient(transparent,transparent),linear-gradient(white,white)]",
// pseudo styles
"after:absolute after:aspect-square after:w-[calc(var(--size)*1px)] after:animate-border-beam after:[animation-delay:var(--delay)] after:[background:linear-gradient(to_left,var(--color-from),var(--color-to),transparent)] after:[offset-anchor:calc(var(--anchor)*1%)_50%] after:[offset-path:rect(0_auto_auto_0_round_calc(var(--size)*1px))]",
className,
)}
/>
);
};

View file

@ -1,11 +0,0 @@
"use client";
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
const Collapsible = CollapsiblePrimitive.Root;
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
export { Collapsible, CollapsibleContent, CollapsibleTrigger };

View file

@ -1,79 +0,0 @@
"use client";
import { Check, ChevronsUpDown } from "lucide-react";
import * as React from "react";
import { cn } from "../../utils/utils";
import { Button } from "./button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "./command";
import { Popover, PopoverContent, PopoverTrigger } from "./popover";
export function Combobox({
items,
onChange,
}: {
items: { value: string; label: string }[];
onChange: (value: string[]) => void;
}) {
const [open, setOpen] = React.useState(false);
const [value, setValue] = React.useState<string[]>([]);
React.useEffect(() => {
onChange(value);
}, [value]);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="w-[200px] justify-between"
>
{value
? items.find((framework) => value.includes(framework.value))?.label
: "Select filter..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command>
<CommandInput placeholder="Search filters..." />
<CommandEmpty>No filters found.</CommandEmpty>
<CommandGroup>
{items.map((framework) => (
<CommandItem
key={framework.value}
value={framework.value}
onSelect={(currentValue) => {
setValue((old) => {
if (old.includes(currentValue)) {
return old.filter((item) => item !== currentValue);
}
return [...old, currentValue];
});
}}
>
<Check
className={cn(
"mr-2 h-4 w-4",
value.includes(framework.value)
? "opacity-100"
: "opacity-0",
)}
/>
{framework.label}
</CommandItem>
))}
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
);
}

View file

@ -1,55 +0,0 @@
"use client";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDownIcon } from "@radix-ui/react-icons";
import * as React from "react";
import { cn } from "../../utils/utils";
const Accordion = AccordionPrimitive.Root;
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item ref={ref} className={cn("", className)} {...props} />
));
AccordionItem.displayName = "AccordionItem";
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger asChild ref={ref} {...props}>
<div
className={cn(
"flex flex-1 cursor-pointer items-center justify-between border-[1px] py-2 text-sm font-medium data-[state=closed]:rounded-md data-[state=open]:rounded-t-md data-[state=open]:border-b-0 data-[state=open]:bg-muted [&[data-state=open]>svg]:rotate-180",
className,
)}
>
{children}
<ChevronDownIcon className="h-4 w-4 font-bold text-primary transition-transform duration-200" />
</div>
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className={cn(
"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden border-[1px] text-sm data-[state=open]:rounded-b-md data-[state=open]:border-t-0 data-[state=open]:bg-muted",
className,
)}
{...props}
>
<div className="pt-0">{children}</div>
</AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };

View file

@ -1,176 +0,0 @@
import * as LabelPrimitive from "@radix-ui/react-label";
import { Slot } from "@radix-ui/react-slot";
import * as React from "react";
import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext,
} from "react-hook-form";
import { cn } from "../../utils/utils";
import { Label } from "./label";
const Form = FormProvider;
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue,
);
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
);
};
const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext();
const fieldState = getFieldState(fieldContext.name, formState);
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>");
}
const { id } = itemContext;
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
};
type FormItemContextValue = {
id: string;
};
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue,
);
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId();
return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
);
});
FormItem.displayName = "FormItem";
const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField();
return (
<Label
ref={ref}
className={cn(error && "text-destructive", className)}
htmlFor={formItemId}
{...props}
/>
);
});
FormLabel.displayName = "FormLabel";
const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } =
useFormField();
return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
);
});
FormControl.displayName = "FormControl";
const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField();
return (
<p
ref={ref}
id={formDescriptionId}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
);
});
FormDescription.displayName = "FormDescription";
const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;
if (!body) {
return null;
}
return (
<p
ref={ref}
id={formMessageId}
className={cn("text-sm font-medium text-destructive", className)}
{...props}
>
{body}
</p>
);
});
FormMessage.displayName = "FormMessage";
export {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
useFormField,
};

View file

@ -1,236 +0,0 @@
"use client";
import * as MenubarPrimitive from "@radix-ui/react-menubar";
import * as React from "react";
import { cn } from "../../utils/utils";
import IconComponent from "../common/genericIconComponent";
const MenubarMenu = MenubarPrimitive.Menu;
const MenubarGroup = MenubarPrimitive.Group;
const MenubarPortal = MenubarPrimitive.Portal;
const MenubarSub = MenubarPrimitive.Sub;
const MenubarRadioGroup = MenubarPrimitive.RadioGroup;
const Menubar = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Root>
>(({ className, ...props }, ref) => (
<MenubarPrimitive.Root
ref={ref}
className={cn(
"flex h-10 items-center space-x-1 rounded-md border bg-background p-1",
className,
)}
{...props}
/>
));
Menubar.displayName = MenubarPrimitive.Root.displayName;
const MenubarTrigger = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<MenubarPrimitive.Trigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
className,
)}
{...props}
/>
));
MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName;
const MenubarSubTrigger = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubTrigger> & {
inset?: boolean;
}
>(({ className, inset, children, ...props }, ref) => (
<MenubarPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
inset && "pl-8",
className,
)}
{...props}
>
{children}
<IconComponent name="ChevronRight" className="ml-auto h-4 w-4" />
</MenubarPrimitive.SubTrigger>
));
MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName;
const MenubarSubContent = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<MenubarPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1",
className,
)}
{...props}
/>
));
MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName;
const MenubarContent = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content>
>(
(
{ className, align = "start", alignOffset = -4, sideOffset = 8, ...props },
ref,
) => (
<MenubarPrimitive.Portal>
<MenubarPrimitive.Content
ref={ref}
align={align}
alignOffset={alignOffset}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in slide-in-from-top-1",
className,
)}
{...props}
/>
</MenubarPrimitive.Portal>
),
);
MenubarContent.displayName = MenubarPrimitive.Content.displayName;
const MenubarItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<MenubarPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className,
)}
{...props}
/>
));
MenubarItem.displayName = MenubarPrimitive.Item.displayName;
const MenubarCheckboxItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<MenubarPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenubarPrimitive.ItemIndicator>
<IconComponent name="Check" className="h-4 w-4" />
</MenubarPrimitive.ItemIndicator>
</span>
{children}
</MenubarPrimitive.CheckboxItem>
));
MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName;
const MenubarRadioItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<MenubarPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenubarPrimitive.ItemIndicator>
<IconComponent name="Circle" className="h-2 w-2 fill-current" />
</MenubarPrimitive.ItemIndicator>
</span>
{children}
</MenubarPrimitive.RadioItem>
));
MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName;
const MenubarLabel = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Label> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<MenubarPrimitive.Label
ref={ref}
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
className,
)}
{...props}
/>
));
MenubarLabel.displayName = MenubarPrimitive.Label.displayName;
const MenubarSeparator = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Separator>
>(({ className, ...props }, ref) => (
<MenubarPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
));
MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName;
const MenubarShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
className,
)}
{...props}
/>
);
};
MenubarShortcut.displayname = "MenubarShortcut";
export {
Menubar,
MenubarCheckboxItem,
MenubarContent,
MenubarGroup,
MenubarItem,
MenubarLabel,
MenubarMenu,
MenubarPortal,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
MenubarShortcut,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
};

View file

@ -1,114 +0,0 @@
"use client";
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import { cn } from "@/utils/utils";
import * as React from "react";
interface MorphingMenuProps {
trigger: React.ReactNode;
items: {
icon?: string;
label: string;
onClick?: () => void;
}[];
className?: string;
buttonClassName?: string;
itemsClassName?: string;
variant?: "large" | "small";
}
const MorphingMenu = React.forwardRef<HTMLDivElement, MorphingMenuProps>(
(
{ trigger, items, className, buttonClassName, itemsClassName, variant },
ref,
) => {
const [isOpen, setIsOpen] = React.useState(false);
// Calculate menu height: header (40px) + (items * 36px) + padding (16px)
const menuHeight = (variant == "large" ? 40 : 32) + items.length * 32 + 8;
return (
<div
ref={ref}
className={cn(
"relative flex w-fit select-none flex-col items-center justify-center whitespace-nowrap transition-all",
variant === "large" ? "h-10" : "h-8",
isOpen ? "w-40" : variant === "large" ? "w-36" : "w-[134px]",
className,
)}
>
<div
style={{
height: isOpen
? `${menuHeight}px`
: variant === "large"
? "40px"
: "32px",
}}
className={cn(
"absolute right-0 top-0 z-50 flex w-full flex-col items-start overflow-hidden bg-primary text-sm font-semibold text-primary-foreground transition-all duration-200",
!isOpen && "hover:bg-primary-hover",
variant === "large" ? "rounded-md" : "rounded-lg",
buttonClassName,
)}
>
<div
className={cn(
"flex w-full shrink-0 cursor-pointer items-center justify-between gap-2 pl-3 pr-3 transition-all",
variant === "large" ? "h-10" : "h-8 text-[13px] font-medium",
)}
onClick={() => setIsOpen(!isOpen)}
>
{trigger}
<div className="flex h-4 w-4 items-center justify-center">
<ForwardedIconComponent
name="ChevronDown"
className={cn(
"absolute h-4 w-4 transition-all",
isOpen && "opacity-0",
)}
/>
<ForwardedIconComponent
name="X"
className={cn(
"absolute h-4 w-4 opacity-0 transition-all",
isOpen && "opacity-100",
)}
/>
</div>
</div>
<div
className={cn(
"flex w-full flex-col gap-0 px-2 font-medium",
itemsClassName,
)}
>
{items.map((item, index) => (
<div
key={index}
className="relative flex h-8 cursor-pointer select-none items-center gap-2 rounded-sm px-2 text-sm outline-none transition-colors hover:bg-primary-hover"
onClick={() => {
item.onClick?.();
setIsOpen(false);
}}
>
{item.icon && (
<ForwardedIconComponent
name={item.icon}
className="h-4 w-4"
/>
)}
{item.label}
</div>
))}
</div>
</div>
</div>
);
},
);
MorphingMenu.displayName = "MorphingMenu";
export { MorphingMenu };
export type { MorphingMenuProps };

View file

@ -1,27 +0,0 @@
"use client";
import * as ProgressPrimitive from "@radix-ui/react-progress";
import * as React from "react";
import { cn } from "../../utils/utils";
const Progress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
>(({ className, value, ...props }, ref) => (
<ProgressPrimitive.Root
ref={ref}
className={cn(
"relative h-4 w-full overflow-hidden rounded-full bg-secondary",
className,
)}
{...props}
>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
));
Progress.displayName = ProgressPrimitive.Root.displayName;
export { Progress };

View file

@ -1,104 +0,0 @@
import { useEffect, useRef, useState } from "react";
import { cn } from "../../utils/utils";
export default function RenameLabel({
value,
setValue,
className,
rename,
setRename,
}) {
const [internalState, setInternalState] = useState(false);
const [componentValue, setComponentValue] = useState(value);
const [isRename, setIsRename] = rename
? [rename, setRename]
: [internalState, setInternalState];
useEffect(() => {
if (value) setComponentValue(value);
}, [value]);
useEffect(() => {
if (isRename) {
setComponentValue(value);
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
setIsRename(false);
setValue("");
}
});
if (inputRef.current) {
setTimeout(() => {
inputRef.current?.focus();
}, 100);
}
}
resizeInput();
return () => {
if (isRename) document.removeEventListener("keydown", () => {});
};
}, [isRename]);
const inputRef = useRef<HTMLInputElement | null>(null);
const resizeInput = () => {
const input = inputRef.current;
if (input) {
const span = document.createElement("span");
span.style.position = "absolute";
span.style.visibility = "hidden";
span.style.whiteSpace = "pre";
span.style.font = window.getComputedStyle(input).font;
span.textContent = input.value;
document.body.appendChild(span);
const textWidth = span.getBoundingClientRect().width;
document.body.removeChild(span);
input.style.width = `${textWidth + 16}px`;
}
};
const handleBlur = () => {
setIsRename(false);
if (componentValue !== "") {
setValue(componentValue);
}
};
const handleChange = (event) => {
setComponentValue(event.target.value);
};
const handleDoubleClick = () => {
setIsRename(true);
setComponentValue(value);
};
const renderInput = () => (
<input
ref={inputRef}
onInput={resizeInput}
className={cn(
"nopan nodelete nodrag noflow rounded-md bg-transparent px-2 outline-ring hover:outline focus:border-none focus:outline active:outline",
className,
)}
onBlur={handleBlur}
value={componentValue}
onChange={handleChange}
/>
);
const renderSpan = () => (
<div className="flex items-center gap-2">
<span
className={cn("truncate px-2 text-left", className)}
onDoubleClick={handleDoubleClick}
>
{value}
</span>
</div>
);
return <div>{isRename ? renderInput() : renderSpan()}</div>;
}

View file

@ -1,140 +0,0 @@
"use client";
import * as SheetPrimitive from "@radix-ui/react-dialog";
import { cva, type VariantProps } from "class-variance-authority";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "../../utils/utils";
const Sheet = SheetPrimitive.Root;
const SheetTrigger = SheetPrimitive.Trigger;
const SheetClose = SheetPrimitive.Close;
const SheetPortal = SheetPrimitive.Portal;
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
ref={ref}
/>
));
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
const sheetVariants = cva(
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
{
variants: {
side: {
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
},
},
defaultVariants: {
side: "right",
},
},
);
interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side }), className)}
{...props}
>
{children}
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
));
SheetContent.displayName = SheetPrimitive.Content.displayName;
const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className,
)}
{...props}
/>
);
SheetHeader.displayName = "SheetHeader";
const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className,
)}
{...props}
/>
);
SheetFooter.displayName = "SheetFooter";
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold text-foreground", className)}
{...props}
/>
));
SheetTitle.displayName = SheetPrimitive.Title.displayName;
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
SheetDescription.displayName = SheetPrimitive.Description.displayName;
export {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetOverlay,
SheetPortal,
SheetTitle,
SheetTrigger,
};

View file

@ -1,27 +0,0 @@
"use client";
import { cn } from "@/utils/utils";
import * as SliderPrimitive from "@radix-ui/react-slider";
import * as React from "react";
const Slider = React.forwardRef<
React.ElementRef<typeof SliderPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
<SliderPrimitive.Root
ref={ref}
className={cn(
"relative flex w-full touch-none select-none items-center",
className,
)}
{...props}
>
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
<SliderPrimitive.Range className="absolute h-full bg-primary" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
</SliderPrimitive.Root>
));
Slider.displayName = SliderPrimitive.Root.displayName;
export { Slider };

View file

@ -1,45 +0,0 @@
"use client";
import * as TogglePrimitive from "@radix-ui/react-toggle";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
import { cn } from "../../utils/utils";
const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-transparent",
outline:
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
},
size: {
default: "h-10 px-3",
sm: "h-9 px-2.5",
lg: "h-11 px-5",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
const Toggle = React.forwardRef<
React.ElementRef<typeof TogglePrimitive.Root>,
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
VariantProps<typeof toggleVariants>
>(({ className, variant, size, ...props }, ref) => (
<TogglePrimitive.Root
ref={ref}
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
));
Toggle.displayName = TogglePrimitive.Root.displayName;
export { Toggle, toggleVariants };

View file

@ -1,11 +0,0 @@
export const validateWebhookData = (res) => {
if (!res?.data?.vertex_builds) {
return false;
}
return Object.keys(res?.data?.vertex_builds).some(
(key) =>
key.includes("Webhook") &&
Array.isArray(res?.data?.vertex_builds[key]) &&
res?.data?.vertex_builds[key]?.length > 0,
);
};

View file

@ -1 +0,0 @@
export * from "./use-post-download-multiple-flows";

View file

@ -1,95 +0,0 @@
import buildQueryStringUrl from "@/controllers/utils/create-query-param-string";
import useAlertStore from "@/stores/alertStore";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useTypesStore } from "@/stores/typesStore";
import { useMutationFunctionType } from "@/types/api";
import { FlowType, PaginatedFlowsType } from "@/types/flow";
import {
extractFieldsFromComponenents,
processFlows,
} from "@/utils/reactflowUtils";
import { UseMutationOptions } from "@tanstack/react-query";
import { AxiosError } from "axios";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
interface GetFlowsParams {
components_only?: boolean;
get_all?: boolean;
header_flows?: boolean;
folder_id?: string;
remove_example_flows?: boolean;
page?: number;
size?: number;
}
const addQueryParams = (url: string, params: GetFlowsParams): string => {
return buildQueryStringUrl(url, params);
};
export const useGetRefreshFlows: useMutationFunctionType<
undefined,
GetFlowsParams
> = (options) => {
const { mutate } = UseRequestProcessor();
const setFlows = useFlowsManagerStore((state) => state.setFlows);
const setErrorData = useAlertStore((state) => state.setErrorData);
const getRefreshFlowsFn = async (
params: GetFlowsParams,
): Promise<FlowType[] | PaginatedFlowsType> => {
const url = addQueryParams(`${getURL("FLOWS")}/`, params);
const { data } = await api.get<FlowType[]>(url);
return data;
};
const mutationFn = async (
params?: GetFlowsParams,
): Promise<FlowType[] | PaginatedFlowsType> => {
try {
await getRefreshFlowsFn(params!).then(async (dbDataFlows) => {
const dbDataComponents = await getRefreshFlowsFn({
components_only: true,
get_all: true,
});
if (dbDataComponents) {
const { data } = processFlows(dbDataComponents as FlowType[]);
useTypesStore.setState((state) => ({
data: { ...state.data, ["saved_components"]: data },
ComponentFields: extractFieldsFromComponenents({
...state.data,
["saved_components"]: data,
}),
}));
}
if (dbDataFlows) {
const flows = Array.isArray(dbDataFlows)
? dbDataFlows
: dbDataFlows.items;
setFlows(flows);
return flows;
}
});
return [];
} catch (e) {
if (e instanceof AxiosError && e.status !== 403) {
setErrorData({
title: "Could not load flows from database",
});
}
throw e;
}
};
const mutation = mutate(["useGetRefreshFlows"], mutationFn, {
...(options as UseMutationOptions<any, any, void, unknown>),
});
return mutation;
};

View file

@ -1,40 +0,0 @@
import { useMutationFunctionType } from "@/types/api";
import { UseMutationResult } from "@tanstack/react-query";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
interface IPostDownloadMultipleFlows {
flow_ids: string[];
}
export const usePostDownloadMultipleFlows: useMutationFunctionType<
undefined,
IPostDownloadMultipleFlows
> = (options?) => {
const { mutate } = UseRequestProcessor();
const postDownloadMultipleFlowsFn = async (
payload: IPostDownloadMultipleFlows,
): Promise<any> => {
const response = await api.post<any>(
`${getURL("FLOWS")}/download/`,
payload.flow_ids,
{ responseType: "blob" },
);
return response.data;
};
const mutation: UseMutationResult<
IPostDownloadMultipleFlows,
any,
IPostDownloadMultipleFlows
> = mutate(
["usePostDownloadMultipleFlows"],
postDownloadMultipleFlowsFn,
options,
);
return mutation;
};

View file

@ -1,63 +0,0 @@
import { useMessagesStore } from "@/stores/messagesStore";
import { UseMutationResult } from "@tanstack/react-query";
import { ColDef, ColGroupDef } from "ag-grid-community";
import { extractColumnsFromRows } from "../../../../utils/utils";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
interface MessagesQueryParams {
id?: string;
mode: "intersection" | "union";
excludedFields?: string[];
params?: object;
}
interface MessagesResponse {
rows: Array<object>;
columns: Array<ColDef | ColGroupDef>;
}
export const useGetMessagesMutation = (
options?: any,
): UseMutationResult<
MessagesResponse,
unknown,
MessagesQueryParams,
unknown
> => {
const { mutate } = UseRequestProcessor();
const getMessagesFn = async (
payload: MessagesQueryParams,
): Promise<MessagesResponse> => {
const { id, mode, excludedFields, params } = payload;
const config = {};
if (id) {
config["params"] = { flow_id: id };
}
if (params) {
config["params"] = { ...config["params"], ...params };
}
const data = await api.get<any>(`${getURL("MESSAGES")}`, config);
const columns = extractColumnsFromRows(data.data, mode, excludedFields);
useMessagesStore.getState().setMessages(data.data);
return { rows: data.data, columns };
};
// Cast the mutation to the correct type
const mutation = mutate(
["useGetMessagesMutation"],
getMessagesFn,
options,
) as UseMutationResult<
MessagesResponse,
unknown,
MessagesQueryParams,
unknown
>;
return mutation;
};

View file

@ -1 +0,0 @@
export * from "./use-get-starter-projects";

View file

@ -1,43 +0,0 @@
import { useQueryFunctionType } from "@/types/api";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
export interface IStarterProjectsDataArray {
name: string;
last_used_at: string | null;
total_uses: number;
is_active: boolean;
id: string;
api_key: string;
user_id: string;
created_at: string;
}
interface IApiQueryResponse {
total_count: number;
user_id: string;
api_keys: Array<IStarterProjectsDataArray>;
}
export const useGetStarterProjectsQuery: useQueryFunctionType<
undefined,
IApiQueryResponse
> = (options) => {
const { query } = UseRequestProcessor();
const getStarterProjectsFn = async () => {
return await api.get<IApiQueryResponse>(`${getURL("STARTER_PROJECTS")}/`);
};
const responseFn = async () => {
const { data } = await getStarterProjectsFn();
return data;
};
const queryResult = query(["useGetStarterProjectsQuery"], responseFn, {
...options,
});
return queryResult;
};

View file

@ -1,5 +0,0 @@
export const getClassNamesFilePreview = (inputFocus) => {
return `flex w-full items-center gap-4 rounded-t-lg bg-background px-14 py-5 overflow-auto custom-scroll border ${
inputFocus ? "border-ring" : ""
}`;
};

View file

@ -1,54 +0,0 @@
import ShortUniqueId from "short-unique-id";
import {
ALLOWED_IMAGE_INPUT_EXTENSIONS,
FS_ERROR_TEXT,
SN_ERROR_TEXT,
} from "../../../../../../constants/constants";
import useAlertStore from "../../../../../../stores/alertStore";
import handleFileUpload from "../helpers/handle-file-upload";
export const useHandleFileChange = (setFiles, currentFlowId) => {
const setErrorData = useAlertStore((state) => state.setErrorData);
const handleFileChange = async (
event: React.ChangeEvent<HTMLInputElement>,
) => {
const fileInput = event.target;
const file = fileInput.files?.[0];
if (file) {
const fileExtension = file.name.split(".").pop()?.toLowerCase();
if (
!fileExtension ||
!ALLOWED_IMAGE_INPUT_EXTENSIONS.includes(fileExtension)
) {
setErrorData({
title: "Error uploading file",
list: [FS_ERROR_TEXT, SN_ERROR_TEXT],
});
return;
}
const uid = new ShortUniqueId();
const id = uid.randomUUID(10);
const type = file.type.split("/")[0];
const blob = file;
setFiles((prevFiles) => [
...prevFiles,
{ file: blob, loading: true, error: false, id, type },
]);
handleFileUpload(blob, currentFlowId, setFiles, id);
}
// Clear the file input value to ensure the change event is triggered even for the same file
fileInput.value = "";
};
return {
handleFileChange,
};
};
export default useHandleFileChange;

View file

@ -1,66 +0,0 @@
import useFlowStore from "@/stores/flowStore";
import { AxiosResponse } from "axios";
import { useEffect } from "react";
import ShortUniqueId from "short-unique-id";
import {
ALLOWED_IMAGE_INPUT_EXTENSIONS,
FS_ERROR_TEXT,
SN_ERROR_TEXT,
} from "../../../../../../constants/constants";
import useAlertStore from "../../../../../../stores/alertStore";
import { UploadFileTypeAPI } from "../../../../../../types/api";
const useUpload = (
uploadFile: (
file: File,
id: string,
) => Promise<AxiosResponse<UploadFileTypeAPI>>,
currentFlowId: string,
setFiles: any,
) => {
const setErrorData = useAlertStore((state) => state.setErrorData);
const isBuilding = useFlowStore((state) => state.isBuilding);
useEffect(() => {
const handlePaste = (event: ClipboardEvent): void => {
if (isBuilding) {
return;
}
const items = event.clipboardData?.items;
if (items) {
for (let i = 0; i < items.length; i++) {
const type = items[0].type.split("/")[0];
const uid = new ShortUniqueId();
const blob = items[i].getAsFile();
if (blob) {
const fileExtension = blob.name.split(".").pop()?.toLowerCase();
if (
!fileExtension ||
!ALLOWED_IMAGE_INPUT_EXTENSIONS.includes(fileExtension)
) {
setErrorData({
title: "Error uploading file",
list: [FS_ERROR_TEXT, SN_ERROR_TEXT],
});
return;
}
const id = uid.randomUUID(3);
setFiles((prevFiles) => [
...prevFiles,
{ file: blob, loading: true, error: false, id, type },
]);
}
}
}
};
document.addEventListener("paste", handlePaste);
return () => {
document.removeEventListener("paste", handlePaste);
};
}, [uploadFile, currentFlowId, isBuilding]);
return null;
};
export default useUpload;

View file

@ -1,72 +0,0 @@
import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
import { useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { tomorrow } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { programmingLanguages } from "../../../../../../constants/constants";
import { Props } from "../../../../../../types/components";
export function CodeBlock({ language, value }: Props): JSX.Element {
const [isCopied, setIsCopied] = useState<Boolean>(false);
const copyToClipboard = (): void => {
if (!navigator.clipboard || !navigator.clipboard.writeText) {
return;
}
navigator.clipboard.writeText(value).then(() => {
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 2000);
});
};
const downloadAsFile = (): void => {
const fileExtension = programmingLanguages[language] || ".file";
const suggestedFileName = `${"generated-code"}${fileExtension}`;
const fileName = window.prompt("enter file name", suggestedFileName);
if (!fileName) {
// user pressed cancel on prompt
return;
}
const blob = new Blob([value], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = fileName;
link.href = url;
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
return (
<div className="codeblock font-sans text-[16px]">
<div className="code-block-modal">
<span className="code-block-modal-span">{language}</span>
<div className="flex items-center">
<button className="code-block-modal-button" onClick={copyToClipboard}>
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />}
{isCopied ? "Copied!" : "Copy Code"}
</button>
<button className="code-block-modal-button" onClick={downloadAsFile}>
<IconDownload size={18} />
</button>
</div>
</div>
<SyntaxHighlighter
className="overflow-auto"
language={language}
style={tomorrow}
customStyle={{ margin: 0 }}
>
{value}
</SyntaxHighlighter>
</div>
);
}
CodeBlock.displayName = "CodeBlock";

View file

@ -1,38 +0,0 @@
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { track } from "@/customization/utils/analytics";
import useAddFlow from "@/hooks/flows/use-add-flow";
import { useParams } from "react-router-dom";
import {
Card,
CardContent,
CardDescription,
CardTitle,
} from "../../../../components/ui/card";
export default function NewFlowCardComponent() {
const addFlow = useAddFlow();
const navigate = useCustomNavigate();
const { folderId } = useParams();
const handleClick = () => {
addFlow({ new_blank: true }).then((id) => {
navigate(`/flow/${id}${folderId ? `/folder/${folderId}` : ""}`);
});
track("New Flow Created", { template: "Blank Flow" });
};
return (
<Card
onClick={handleClick}
className="h-64 w-80 cursor-pointer bg-background pt-4"
data-testid="blank-flow"
>
<CardContent className="h-full w-full">
<div className="flex h-full w-full flex-col items-center justify-center rounded-md bg-muted align-middle bg-dotted-spacing-6 bg-dotted-muted-foreground bg-dotted-radius-px"></div>
</CardContent>
<CardDescription className="px-6 pb-4">
<CardTitle className="text-lg text-primary">Blank Flow</CardTitle>
</CardDescription>
</Card>
);
}

View file

@ -1,23 +0,0 @@
import { useNavigate } from "react-router-dom";
import { track } from "../../../../customization/utils/analytics";
import useAddFlow from "../../../../hooks/flows/use-add-flow";
import { FlowType } from "../../../../types/flow";
import { updateIds } from "../../../../utils/reactflowUtils";
export function useFlowCardClick() {
const navigate = useNavigate();
const addFlow = useAddFlow();
const handleFlowCardClick = async (flow: FlowType, folderIdUrl: string) => {
try {
updateIds(flow.data!);
const id = await addFlow({ flow });
navigate(`/flow/${id}/folder/${folderIdUrl}`);
track("New Flow Created", { template: `${flow.name} Template` });
} catch (error) {
console.error("Error handling flow card click:", error);
}
};
return handleFlowCardClick;
}

View file

@ -1,152 +0,0 @@
/// <reference types="vite-plugin-svgr/client" />
import { useParams } from "react-router-dom";
import BlogPost from "../../../../assets/undraw_blog_post_re_fy5x.svg?react";
import ChatBot from "../../../../assets/undraw_chat_bot_re_e2gj.svg?react";
import PromptChaining from "../../../../assets/undraw_cloud_docs_re_xjht.svg?react";
import HierarchicalTasks from "../../../../assets/undraw_educator_re_ju47.svg?react";
import ComplexAgent from "../../../../assets/undraw_firmware_re_fgdy.svg?react";
import SequentialTasks from "../../../../assets/undraw_project_completed_re_jr7u.svg?react";
import APIRequest from "../../../../assets/undraw_real_time_analytics_re_yliv.svg?react";
import BasicPrompt from "../../../../assets/undraw_short_bio_re_fmx0.svg?react";
import TransferFiles from "../../../../assets/undraw_transfer_files_re_a2a9.svg?react";
import {
Card,
CardContent,
CardDescription,
CardTitle,
} from "../../../../components/ui/card";
import { useFolderStore } from "../../../../stores/foldersStore";
import { UndrawCardComponentProps } from "../../../../types/components";
import { useFlowCardClick } from "../hooks/use-redirect-flow-card-click";
export default function UndrawCardComponent({
flow,
}: UndrawCardComponentProps): JSX.Element {
const { folderId } = useParams();
const myCollectionId = useFolderStore((state) => state.myCollectionId);
const folderIdUrl = folderId ?? myCollectionId;
const handleFlowCardClick = useFlowCardClick();
function selectImage() {
switch (flow.name) {
case "Blog Writer":
return (
<BlogPost
style={{
width: "65%",
height: "65%",
}}
preserveAspectRatio="xMidYMid meet"
/>
);
case "Basic Prompting (Hello, World)":
return (
<BasicPrompt
style={{
width: "65%",
height: "65%",
}}
preserveAspectRatio="xMidYMid meet"
/>
);
case "Memory Chatbot":
return (
<ChatBot
style={{
width: "70%",
height: "70%",
}}
preserveAspectRatio="xMidYMid meet"
/>
);
case "API requests":
return (
<APIRequest
style={{
width: "70%",
height: "70%",
}}
preserveAspectRatio="xMidYMid meet"
/>
);
case "Document QA":
return (
<TransferFiles
style={{
width: "80%",
height: "80%",
}}
preserveAspectRatio="xMidYMid meet"
/>
);
case "Vector Store RAG":
return (
<PromptChaining
style={{
width: "80%",
height: "80%",
}}
preserveAspectRatio="xMidYMid meet"
/>
);
case "Simple Agent":
return (
<SequentialTasks
style={{
width: "80%",
height: "80%",
}}
preserveAspectRatio="xMidYMid meet"
/>
);
case "Travel Planning Agents":
return (
<HierarchicalTasks
style={{
width: "80%",
height: "80%",
}}
preserveAspectRatio="xMidYMid meet"
/>
);
case "Dynamic Agent":
return (
<ComplexAgent
style={{
width: "80%",
height: "80%",
}}
preserveAspectRatio="xMidYMid meet"
/>
);
default:
return (
<TransferFiles
style={{
width: "80%",
height: "80%",
}}
preserveAspectRatio="xMidYMid meet"
/>
);
}
}
return (
<Card
onClick={() => handleFlowCardClick(flow, folderIdUrl!)}
className="h-64 w-80 cursor-pointer bg-background pt-4"
>
<CardContent className="h-full w-full">
<div className="flex h-full w-full flex-col items-center justify-center rounded-md bg-muted p-1 align-middle">
{selectImage()}
</div>
</CardContent>
<CardDescription className="px-6 pb-4">
<CardTitle className="text-lg text-primary">{flow.name}</CardTitle>
</CardDescription>
</Card>
);
}

View file

@ -1,51 +0,0 @@
import { Disclosure } from "@headlessui/react";
import IconComponent from "../../../../components/common/genericIconComponent";
import { DisclosureComponentType } from "../../../../types/components";
export default function DisclosureComponent({
button: { title, icon, buttons = [] },
isChild = true,
children,
defaultOpen,
}: DisclosureComponentType): JSX.Element {
return (
<Disclosure as="div" defaultOpen={defaultOpen} key={title}>
{({ open }) => (
<>
<div>
<Disclosure.Button
className={
isChild
? "components-disclosure-arrangement-child"
: "components-disclosure-arrangement"
}
data-testid={`disclosure-${title.toLocaleLowerCase()}`}
>
<div className={"flex gap-4" + (isChild ? " pl-2" : "")}>
{/* BUG ON THIS ICON */}
<IconComponent name={icon} />
<span className="components-disclosure-title">{title}</span>
</div>
<div className="components-disclosure-div">
{buttons.map((btn, index) => (
<button key={index} onClick={btn.onClick}>
<IconComponent name={btn.icon} />
</button>
))}
<div>
<IconComponent
name="ChevronRight"
className={`${
open || defaultOpen ? "rotate-90 transform" : ""
} h-4 w-4 text-foreground`}
/>
</div>
</div>
</Disclosure.Button>
</div>
<Disclosure.Panel as="div">{children}</Disclosure.Panel>
</>
)}
</Disclosure>
);
}

View file

@ -1,51 +0,0 @@
import { Disclosure } from "@headlessui/react";
import IconComponent from "../../../../components/common/genericIconComponent";
import { DisclosureComponentType } from "../../../../types/components";
export default function ParentDisclosureComponent({
button: { title, icon, buttons = [], beta },
children,
defaultOpen,
testId,
}: DisclosureComponentType): JSX.Element {
return (
<Disclosure as="div" defaultOpen={defaultOpen} key={title}>
{({ open }) => (
<>
<div>
<Disclosure.Button
className="parent-disclosure-arrangement"
data-testid={testId}
>
<div className="flex items-baseline gap-1 align-baseline">
<span className="text-sm font-medium">{title}</span>
{beta && (
<div className="h-fit rounded-full bg-beta-background px-2 py-1 text-xs/3 font-semibold text-beta-foreground-soft">
Beta
</div>
)}
</div>
<div className="components-disclosure-div">
{buttons.map((btn, index) => (
<button key={index} onClick={btn.onClick}>
<IconComponent name={btn.icon} />
</button>
))}
<div>
<IconComponent
skipFallback
name={open ? "chevron-down" : "chevron-right"}
className={`${
open || defaultOpen ? "" : ""
} h-4 w-4 text-foreground`}
/>
</div>
</div>
</Disclosure.Button>
</div>
<Disclosure.Panel as="div">{children}</Disclosure.Panel>
</>
)}
</Disclosure>
);
}

View file

@ -1,62 +0,0 @@
import ShadTooltip from "@/components/common/shadTooltipComponent";
import { getNodeIcon, nodeColors } from "@/utils/styleUtils";
import { removeCountFromString } from "@/utils/utils";
import DisclosureComponent from "../../DisclosureComponent";
import SidebarDraggableComponent from "../sideBarDraggableComponent";
import sensitiveSort from "../utils/sensitive-sort";
export function SidebarCategoryComponent({
search,
getFilterEdge,
category,
name,
onDragStart,
}) {
return (
<DisclosureComponent
isChild={false}
defaultOpen={
getFilterEdge.length !== 0 || search.length !== 0 ? true : false
}
button={{
title: name,
icon: name,
}}
>
<div className="side-bar-components-gap">
{Object.keys(category)
.sort((a, b) =>
sensitiveSort(category[a].display_name, category[b].display_name),
)
.map((SBItemName: string, idx) => (
<ShadTooltip
content={category[SBItemName].display_name}
side="right"
key={idx}
>
<SidebarDraggableComponent
sectionName={name as string}
apiClass={category[SBItemName]}
key={idx}
onDragStart={(event) =>
onDragStart(event, {
//split type to remove type in nodes saved with same name removing it's
type: removeCountFromString(SBItemName),
node: category[SBItemName],
})
}
color={nodeColors[name]}
itemName={SBItemName}
//convert error to boolean
error={!!category[SBItemName].error}
display_name={category[SBItemName].display_name}
official={
category[SBItemName].official === false ? false : true
}
/>
</ShadTooltip>
))}
</div>
</DisclosureComponent>
);
}

View file

@ -1,342 +0,0 @@
import { ENABLE_INTEGRATIONS } from "@/customization/feature-flags";
import { useStoreStore } from "@/stores/storeStore";
import { cloneDeep } from "lodash";
import { useEffect, useState } from "react";
import IconComponent from "../../../../components/common/genericIconComponent";
import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator";
import {
BUNDLES_SIDEBAR_FOLDER_NAMES,
PRIORITY_SIDEBAR_ORDER,
} from "../../../../constants/constants";
import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore";
import { useTypesStore } from "../../../../stores/typesStore";
import { APIClassType, APIObjectType } from "../../../../types/api";
import ParentDisclosureComponent from "../ParentDisclosureComponent";
import { SidebarCategoryComponent } from "./SidebarCategoryComponent";
import { useUtilityStore } from "@/stores/utilityStore";
import { SidebarFilterComponent } from "./sidebarFilterComponent";
import { sortKeys } from "./utils";
export default function ExtraSidebar(): JSX.Element {
const data = useTypesStore((state) => state.data);
const templates = useTypesStore((state) => state.templates);
const getFilterEdge = useFlowStore((state) => state.getFilterEdge);
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
const hasStore = useStoreStore((state) => state.hasStore);
const filterType = useFlowStore((state) => state.filterType);
const featureFlags = useUtilityStore((state) => state.featureFlags);
const setErrorData = useAlertStore((state) => state.setErrorData);
const [dataFilter, setFilterData] = useState(data);
const [search, setSearch] = useState("");
function onDragStart(
event: React.DragEvent<any>,
data: { type: string; node?: APIClassType },
): void {
//start drag event
var crt = event.currentTarget.cloneNode(true);
crt.style.position = "absolute";
crt.style.width = "215px";
crt.style.top = "-500px";
crt.style.right = "-500px";
crt.classList.add("cursor-grabbing");
document.body.appendChild(crt);
event.dataTransfer.setDragImage(crt, 0, 0);
event.dataTransfer.setData("genericNode", JSON.stringify(data));
}
function normalizeString(str: string): string {
return str.toLowerCase().replace(/_/g, " ").replace(/\s+/g, "");
}
function searchInMetadata(metadata: any, searchTerm: string): boolean {
if (!metadata || typeof metadata !== "object") return false;
return Object.entries(metadata).some(([key, value]) => {
if (typeof value === "string") {
return (
normalizeString(key).includes(searchTerm) ||
normalizeString(value).includes(searchTerm)
);
}
if (typeof value === "object") {
return searchInMetadata(value, searchTerm);
}
return false;
});
}
function handleSearchInput(e: string) {
if (e === "") {
setFilterData(data);
return;
}
const searchTerm = normalizeString(e);
setFilterData((_) => {
let ret: APIObjectType = {};
Object.keys(data).forEach((d: keyof APIObjectType) => {
ret[d] = {};
let keys = Object.keys(data[d]).filter((nd) => {
const item = data[d][nd];
return (
normalizeString(nd).includes(searchTerm) ||
normalizeString(item.display_name).includes(searchTerm) ||
normalizeString(d.toString()).includes(searchTerm) ||
(item.metadata && searchInMetadata(item.metadata, searchTerm))
);
});
keys.forEach((element) => {
ret[d][element] = data[d][element];
});
});
return ret;
});
}
useEffect(() => {
// show components with error on load
let errors: string[] = [];
Object.keys(templates).forEach((component) => {
if (templates[component].error) {
errors.push(component);
}
});
if (errors.length > 0)
setErrorData({ title: " Components with errors: ", list: errors });
}, []);
function handleBlur() {
// check if search is search to reset fitler on click input
if ((!search && search === "") || search === "search") {
setFilterData(data);
setFilterEdge([]);
setSearch("");
}
}
useEffect(() => {
if (getFilterEdge.length !== 0) {
setSearch("");
}
if (getFilterEdge.length === 0 && search === "") {
setSearch("");
setFilterData(data);
}
}, [getFilterEdge, data]);
useEffect(() => {
handleSearchInput(search);
}, [data]);
useEffect(() => {
if (getFilterEdge?.length > 0) {
setFilterData((_) => {
let dataClone = cloneDeep(data);
let ret = {};
Object.keys(dataClone).forEach((d: keyof APIObjectType, i) => {
ret[d] = {};
if (getFilterEdge.some((x) => x.family === d)) {
ret[d] = dataClone[d];
const filtered = getFilterEdge
.filter((x) => x.family === d)
.pop()
.type.split(",");
for (let i = 0; i < filtered.length; i++) {
filtered[i] = filtered[i].trimStart();
}
if (filtered.some((x) => x !== "")) {
let keys = Object.keys(dataClone[d]).filter((nd) =>
filtered.includes(nd),
);
Object.keys(dataClone[d]).forEach((element) => {
if (!keys.includes(element)) {
delete ret[d][element];
}
});
}
}
});
setSearch("");
return ret;
});
}
}, [getFilterEdge, data]);
return (
<div className="side-bar-arrangement">
<div className="side-bar-search-div-placement">
<Input
onFocusCapture={() => handleBlur()}
value={search}
type="text"
name="search"
id="search"
placeholder="Search"
className="nopan nodelete nodrag noflow input-search"
onChange={(event) => {
handleSearchInput(event.target.value);
// Set search input state
setSearch(event.target.value);
}}
readOnly
onClick={() =>
document?.getElementById("search")?.removeAttribute("readonly")
}
/>
<div
className="search-icon"
onClick={() => {
if (search) {
setFilterData(data);
setSearch("");
}
}}
>
<IconComponent
name={search ? "X" : "Search"}
className={`h-5 w-5 stroke-[1.5] text-primary ${
search ? "cursor-pointer" : "cursor-default"
}`}
aria-hidden="true"
/>
</div>
</div>
<Separator />
<div className="side-bar-components-div-arrangement">
<div className="parent-disclosure-arrangement">
<div className="flex w-full flex-col items-start justify-between gap-2.5">
<span className="text-sm font-medium">Components</span>
{filterType && (
<SidebarFilterComponent
isInput={!!filterType.source}
type={filterType.type}
resetFilters={() => {
setFilterEdge([]);
setFilterData(data);
}}
/>
)}
</div>
</div>
<Separator />
{Object.keys(dataFilter)
.sort(sortKeys)
.filter((x) => PRIORITY_SIDEBAR_ORDER.includes(x))
.map((SBSectionName: keyof APIObjectType, index) =>
Object.keys(dataFilter[SBSectionName]).length > 0 ? (
<SidebarCategoryComponent
key={`DisclosureComponent${index + search + JSON.stringify(getFilterEdge)}`}
search={search}
getFilterEdge={getFilterEdge}
category={dataFilter[SBSectionName]}
name={SBSectionName}
onDragStart={onDragStart}
/>
) : (
<div key={index}></div>
),
)}
{(ENABLE_INTEGRATIONS || featureFlags?.mvp_components) && (
<ParentDisclosureComponent
defaultOpen={true}
key={`${search.length !== 0}-${getFilterEdge.length !== 0}-Bundle`}
button={{
title: "Integrations",
icon: "unknown",
}}
testId="bundle-extended-disclosure"
>
{Object.keys(dataFilter)
.sort(sortKeys)
.filter((x) => BUNDLES_SIDEBAR_FOLDER_NAMES.includes(x))
.map((SBSectionName: keyof APIObjectType, index) =>
Object.keys(dataFilter[SBSectionName]).length > 0 ? (
<SidebarCategoryComponent
key={`DisclosureComponent${index + search + JSON.stringify(getFilterEdge)}`}
search={search}
getFilterEdge={getFilterEdge}
category={dataFilter[SBSectionName]}
name={SBSectionName}
onDragStart={onDragStart}
/>
) : (
<div key={index}></div>
),
)}
</ParentDisclosureComponent>
)}
<ParentDisclosureComponent
defaultOpen={search.length !== 0 || getFilterEdge.length !== 0}
key={`${search.length !== 0}-${getFilterEdge.length !== 0}-Advanced`}
button={{
title: "Experimental",
icon: "unknown",
beta: true,
}}
testId="extended-disclosure"
>
{Object.keys(dataFilter)
.sort(sortKeys)
.filter(
(x) =>
!PRIORITY_SIDEBAR_ORDER.includes(x) &&
!BUNDLES_SIDEBAR_FOLDER_NAMES.includes(x),
)
.map((SBSectionName: keyof APIObjectType, index) =>
Object.keys(dataFilter[SBSectionName]).length > 0 ? (
<SidebarCategoryComponent
key={`DisclosureComponent${index + search + JSON.stringify(getFilterEdge)}`}
search={search}
getFilterEdge={getFilterEdge}
category={dataFilter[SBSectionName]}
name={SBSectionName}
onDragStart={onDragStart}
/>
) : (
<div key={index}></div>
),
)}
{hasStore && (
<a
target={"_blank"}
href="https://langflow.store"
className="components-disclosure-arrangement"
draggable="false"
>
<div className="flex gap-4">
{/* BUG ON THIS ICON */}
<IconComponent
name="Sparkles"
strokeWidth={1.5}
className="w-[22px] text-primary"
/>
<span className="components-disclosure-title">
Discover More
</span>
</div>
<div className="components-disclosure-div">
<div>
<IconComponent
name="Link"
className="h-4 w-4 text-foreground"
/>
</div>
</div>
</a>
)}
</ParentDisclosureComponent>
</div>
</div>
);
}

View file

@ -1,164 +0,0 @@
import useDeleteFlow from "@/hooks/flows/use-delete-flow";
import { DragEventHandler, forwardRef, useRef, useState } from "react";
import IconComponent from "../../../../../components/common/genericIconComponent";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
} from "../../../../../components/ui/select-custom";
import { useDarkStore } from "../../../../../stores/darkStore";
import useFlowsManagerStore from "../../../../../stores/flowsManagerStore";
import { APIClassType } from "../../../../../types/api";
import {
createFlowComponent,
downloadNode,
getNodeId,
} from "../../../../../utils/reactflowUtils";
import { removeCountFromString } from "../../../../../utils/utils";
export const SidebarDraggableComponent = forwardRef(
(
{
sectionName,
display_name,
itemName,
error,
color,
onDragStart,
apiClass,
official,
}: {
sectionName: string;
apiClass: APIClassType;
display_name: string;
itemName: string;
error: boolean;
color: string;
onDragStart: DragEventHandler<HTMLDivElement>;
official: boolean;
},
ref,
) => {
const [open, setOpen] = useState(false);
const { deleteFlow } = useDeleteFlow();
const flows = useFlowsManagerStore((state) => state.flows);
const version = useDarkStore((state) => state.version);
const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 });
const popoverRef = useRef<HTMLDivElement>(null);
const handlePointerDown = (e) => {
if (!open) {
const rect = popoverRef.current?.getBoundingClientRect() ?? {
left: 0,
top: 0,
};
setCursorPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
}
};
function handleSelectChange(value: string) {
switch (value) {
case "share":
break;
case "download":
const type = removeCountFromString(itemName);
downloadNode(
createFlowComponent(
{ id: getNodeId(type), type, node: apiClass },
version,
),
);
break;
case "delete":
const flowId = flows?.find((f) => f.name === display_name);
if (flowId) deleteFlow({ id: flowId.id });
break;
}
}
return (
<Select
onValueChange={handleSelectChange}
onOpenChange={(change) => setOpen(change)}
open={open}
key={itemName}
>
<div
onPointerDown={handlePointerDown}
onContextMenuCapture={(e) => {
e.preventDefault();
setOpen(true);
}}
key={itemName}
data-tooltip-id={itemName}
>
<div
draggable={!error}
className={
"side-bar-components-border bg-background" +
(error ? " cursor-not-allowed select-none" : "")
}
style={{
borderLeftColor: color,
}}
onDragStart={onDragStart}
onDragEnd={() => {
document.body.removeChild(
document.getElementsByClassName("cursor-grabbing")[0],
);
}}
>
<div
data-testid={sectionName + display_name}
id={sectionName + display_name}
className="side-bar-components-div-form"
>
<span className="side-bar-components-text">{display_name}</span>
<div ref={popoverRef}>
<IconComponent
name="Menu"
className="side-bar-components-icon"
/>
<SelectTrigger></SelectTrigger>
<SelectContent
position="popper"
side="bottom"
sideOffset={-25}
style={{
position: "absolute",
left: cursorPos.x,
top: cursorPos.y,
}}
>
<SelectItem value={"download"}>
<div className="flex">
<IconComponent
name="Download"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Download{" "}
</div>{" "}
</SelectItem>
{!official && (
<SelectItem value={"delete"}>
<div className="flex">
<IconComponent
name="Trash2"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Delete{" "}
</div>{" "}
</SelectItem>
)}
</SelectContent>
</div>
</div>
</div>
</div>
</Select>
);
},
);
export default SidebarDraggableComponent;

View file

@ -1,34 +0,0 @@
import { APIClassType } from "@/types/api";
import IconComponent from "../../../../../components/common/genericIconComponent";
export default function NoteDraggableComponent() {
function onDragStart(event: React.DragEvent<any>): void {
const noteNode: APIClassType = {
description: "",
display_name: "",
documentation: "",
template: {},
};
event.dataTransfer.setData(
"noteNode",
JSON.stringify({ node: noteNode, type: "note" }),
);
}
return (
<div
draggable
className={"cursor-grab rounded-l-md bg-background p-2"}
onDragStart={onDragStart}
>
<div
data-testid={"note_component"}
id={"note component"}
className="flex w-full items-center justify-between rounded-md border border-dashed border-ring px-3 py-1 text-sm"
>
<IconComponent name="StickyNote" className="pr-2" />
<span className="side-bar-components-text">Add Note</span>
<IconComponent name="Menu" className="side-bar-components-icon" />
</div>
</div>
);
}

View file

@ -1,26 +0,0 @@
import { PRIORITY_SIDEBAR_ORDER } from "../../../../constants/constants";
export function sortKeys(a: string, b: string) {
// Define the order of specific keys
const indexA = PRIORITY_SIDEBAR_ORDER.indexOf(a.toLowerCase());
const indexB = PRIORITY_SIDEBAR_ORDER.indexOf(b.toLowerCase());
// Check if both keys are in the predefined order
if (indexA !== -1 && indexB !== -1) {
return indexA - indexB;
}
// If only 'a' is in the predefined order, it should come first
if (indexA !== -1) {
return -1;
}
// If only 'b' is in the predefined order, it should come first
if (indexB !== -1) {
return 1;
}
// If neither 'a' nor 'b' are in the predefined order, sort them alphabetically
return a.localeCompare(b);
}

View file

@ -9,10 +9,10 @@ import ShadTooltip from "@/components/common/shadTooltipComponent";
import { Button } from "@/components/ui/button";
import { SidebarHeader, SidebarTrigger } from "@/components/ui/sidebar";
import { memo } from "react";
import { SidebarFilterComponent } from "../../../extraSidebarComponent/sidebarFilterComponent";
import { SidebarHeaderComponentProps } from "../../types";
import FeatureToggles from "../featureTogglesComponent";
import { SearchInput } from "../searchInput";
import { SidebarFilterComponent } from "../sidebarFilterComponent";
export const SidebarHeaderComponent = memo(function SidebarHeaderComponent({
showConfig,

View file

@ -22,7 +22,6 @@ import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore";
import { useTypesStore } from "../../../../stores/typesStore";
import { APIClassType } from "../../../../types/api";
import sensitiveSort from "../extraSidebarComponent/utils/sensitive-sort";
import isWrappedWithClass from "../PageComponent/utils/is-wrapped-with-class";
import { CategoryGroup } from "./components/categoryGroup";
import NoResultsMessage from "./components/emptySearchComponent";
@ -35,6 +34,7 @@ import { applyLegacyFilter } from "./helpers/apply-legacy-filter";
import { combinedResultsFn } from "./helpers/combined-results";
import { filteredDataFn } from "./helpers/filtered-data";
import { normalizeString } from "./helpers/normalize-string";
import sensitiveSort from "./helpers/sensitive-sort";
import { traditionalSearchMetadata } from "./helpers/traditional-search-metadata";
import { UniqueInputsComponents } from "./types";

View file

@ -1,54 +0,0 @@
export const TEMPLATES_DATA = {
examples: [
{
name: "Basic Prompting (Hello, World)",
icon: "BotMessageSquare",
icon_bg_color: "bg-blue-500",
},
{
name: "Memory Chatbot",
icon: "MessagesSquare",
icon_bg_color: "bg-purple-500",
},
{
name: "Vector Store RAG",
icon: "Database",
icon_bg_color: "bg-green-500",
},
{
name: "Travel Planning Agents",
icon: "Plane",
icon_bg_color: "bg-yellow-500",
},
{
name: "Dynamic Agent",
icon: "Users",
icon_bg_color: "bg-red-500",
},
{
name: "Blog Writer",
icon: "FileText",
icon_bg_color: "bg-indigo-500",
},
{
name: "Sequential Tasks Agent",
icon: "ListOrdered",
icon_bg_color: "bg-pink-500",
},
{
name: "Hierarchical Tasks Agent",
icon: "GitFork",
icon_bg_color: "bg-orange-500",
},
{
name: "Simple Agent",
icon: "Users",
icon_bg_color: "bg-teal-500",
},
{
name: "Document QA",
icon: "FileText",
icon_bg_color: "bg-cyan-500",
},
],
};

View file

@ -1,43 +0,0 @@
import useUploadFlow from "@/hooks/flows/use-upload-flow";
import { CONSOLE_ERROR_MSG } from "../../../constants/alerts_constants";
import useAlertStore from "../../../stores/alertStore";
const useDropdownOptions = ({
navigate,
is_component,
}: {
navigate: (url: string) => void;
is_component: boolean;
}) => {
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData);
const uploadFlow = useUploadFlow();
const handleImportFromJSON = () => {
uploadFlow({
isComponent: is_component,
})
.then((id) => {
setSuccessData({
title: `${is_component ? "Component" : "Flow"} uploaded successfully`,
});
if (!is_component) navigate("/flow/" + id);
})
.catch((error) => {
setErrorData({
title: CONSOLE_ERROR_MSG,
list: [error],
});
});
};
const dropdownOptions = [
{
name: "Import from JSON",
onBtnClick: handleImportFromJSON,
},
];
return [...dropdownOptions];
};
export default useDropdownOptions;

View file

@ -1,28 +0,0 @@
import cloneDeep from "lodash/cloneDeep";
import { useEffect } from "react";
import { FlowType } from "../../../types/flow";
const useFilteredFlows = (
flowsFromFolder: FlowType[],
searchFlowsComponents: string,
setAllFlows: (value: any[]) => void,
) => {
useEffect(() => {
const newFlows = cloneDeep(flowsFromFolder || []);
const filteredFlows = newFlows.filter(
(f) =>
f.name.toLowerCase().includes(searchFlowsComponents.toLowerCase()) ||
f.description
.toLowerCase()
.includes(searchFlowsComponents.toLowerCase()),
);
if (searchFlowsComponents === "") {
setAllFlows(flowsFromFolder);
} else {
setAllFlows(filteredFlows);
}
}, [flowsFromFolder, searchFlowsComponents, setAllFlows]);
};
export default useFilteredFlows;

View file

@ -1,30 +0,0 @@
import { useCallback } from "react";
import { FlowType } from "../../../types/flow";
const useSelectAll = (
flowsFromFolder: FlowType[],
getValues: () => Record<string, boolean>,
setValue: (key: string, value: boolean) => void,
) => {
const handleSelectAll = useCallback(
(select) => {
const flowsFromFolderIds = flowsFromFolder?.map((f) => f.id);
if (select) {
Object.keys(getValues()).forEach((key) => {
if (!flowsFromFolderIds?.includes(key)) return;
setValue(key, true);
});
return;
}
Object.keys(getValues()).forEach((key) => {
setValue(key, false);
});
},
[flowsFromFolder, getValues, setValue],
);
return { handleSelectAll };
};
export default useSelectAll;

View file

@ -1,20 +0,0 @@
import { useEffect } from "react";
const useSelectedFlows = (
entireFormValues: Record<string, boolean> | undefined,
setSelectedFlowsComponentsCards: (
selectedFlowsComponentsCards: string[],
) => void,
) => {
useEffect(() => {
if (!entireFormValues || Object.keys(entireFormValues).length === 0) return;
const selectedFlows = Object.keys(entireFormValues).filter((key) => {
return entireFormValues[key] === true;
});
setSelectedFlowsComponentsCards(selectedFlows);
}, [entireFormValues, setSelectedFlowsComponentsCards]);
};
export default useSelectedFlows;

View file

@ -1,10 +0,0 @@
export const getNameByType = (type: string) => {
switch (type) {
case "all":
return "Component or Flow";
case "component":
return "Component";
default:
return "Flow";
}
};

View file

@ -1,23 +0,0 @@
import { useFolderStore } from "../../../stores/foldersStore";
import { downloadFlowsFromFolders } from "../services";
export function handleDownloadFolderFn(folderId: string) {
downloadFlowsFromFolders(folderId).then((data) => {
const folders = useFolderStore.getState().folders;
const folder = folders.find((f) => f.id === folderId);
data.folder_name = folder?.name || "folder";
data.folder_description = folder?.description || "";
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
JSON.stringify(data),
)}`;
const link = document.createElement("a");
link.href = jsonString;
link.download = `${data.folder_name}.json`;
link.click();
});
}

View file

@ -1,197 +0,0 @@
import {
useResetPassword,
useUpdateUser,
} from "@/controllers/API/queries/auth";
import * as Form from "@radix-ui/react-form";
import { cloneDeep } from "lodash";
import { useContext, useState } from "react";
import IconComponent from "../../components/common/genericIconComponent";
import Header from "../../components/headerComponent";
import InputComponent from "../../components/inputComponent";
import { Button } from "../../components/ui/button";
import {
EDIT_PASSWORD_ALERT_LIST,
EDIT_PASSWORD_ERROR_ALERT,
SAVE_ERROR_ALERT,
SAVE_SUCCESS_ALERT,
} from "../../constants/alerts_constants";
import { CONTROL_PATCH_USER_STATE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
import useAlertStore from "../../stores/alertStore";
import {
inputHandlerEventType,
patchUserInputStateType,
} from "../../types/components";
import { gradients } from "../../utils/styleUtils";
import GradientChooserComponent from "../SettingsPage/pages/GeneralPage/components/ProfilePictureForm/components/profilePictureChooserComponent";
export default function ProfileSettingsPage(): JSX.Element {
const [inputState, setInputState] = useState<patchUserInputStateType>(
CONTROL_PATCH_USER_STATE,
);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData);
const { userData, setUserData } = useContext(AuthContext);
const { password, cnfPassword, gradient } = inputState;
const { mutate: mutateResetPassword } = useResetPassword();
const { mutate: mutatePatchUser } = useUpdateUser();
async function handlePatchUser() {
if (password !== cnfPassword) {
setErrorData({
title: EDIT_PASSWORD_ERROR_ALERT,
list: [EDIT_PASSWORD_ALERT_LIST],
});
return;
}
if (password !== "") {
mutateResetPassword(
{ user_id: userData!.id, password: { password } },
{
onSuccess: successUpdates,
onError: errorUpdates,
},
);
}
if (gradient !== "") {
mutatePatchUser(
{ user_id: userData!.id, user: { profile_image: gradient } },
{
onSuccess: successUpdates,
onError: errorUpdates,
},
);
}
}
const errorUpdates = (error) => {
setErrorData({
title: SAVE_ERROR_ALERT,
list: [(error as any).response.data.detail],
});
};
const successUpdates = () => {
if (gradient !== "") {
let newUserData = cloneDeep(userData);
newUserData!.profile_image = gradient;
setUserData(newUserData);
}
handleInput({ target: { name: "password", value: "" } });
handleInput({ target: { name: "cnfPassword", value: "" } });
setSuccessData({ title: SAVE_SUCCESS_ALERT });
};
function handleInput({
target: { name, value },
}: inputHandlerEventType): void {
setInputState((prev) => ({ ...prev, [name]: value }));
}
return (
<>
<Header />
<div className="community-page-arrangement">
<div className="community-page-nav-arrangement">
<span className="community-page-nav-title">
<IconComponent name="User" className="w-6" />
Profile Settings
</span>
</div>
<span className="community-page-description-text">
Change your profile settings like your password and your profile
picture.
</span>
<Form.Root
onSubmit={(event) => {
handlePatchUser();
const data = Object.fromEntries(new FormData(event.currentTarget));
event.preventDefault();
}}
className="flex h-full flex-col px-6 pb-16"
>
<div className="flex h-full flex-col gap-4">
<div className="flex gap-4">
<div className="mb-3 w-96">
<Form.Field name="password">
<Form.Label className="data-[invalid]:label-invalid">
Password{" "}
</Form.Label>
<InputComponent
id="pasword"
onChange={(value) => {
handleInput({ target: { name: "password", value } });
}}
value={password}
isForm
password={true}
placeholder="Password"
className="w-full"
/>
<Form.Message match="valueMissing" className="field-invalid">
Please enter your password
</Form.Message>
</Form.Field>
</div>
<div className="mb-3 w-96">
<Form.Field name="cnfPassword">
<Form.Label className="data-[invalid]:label-invalid">
Confirm Password{" "}
</Form.Label>
<InputComponent
id="cnfPassword"
onChange={(value) => {
handleInput({ target: { name: "cnfPassword", value } });
}}
value={cnfPassword}
isForm
password={true}
placeholder="Confirm Password"
className="w-full"
/>
<Form.Message className="field-invalid" match="valueMissing">
Please confirm your password
</Form.Message>
</Form.Field>
</div>
</div>
<Form.Field name="gradient">
<Form.Label className="data-[invalid]:label-invalid">
Profile Gradient{" "}
</Form.Label>
<div className="mt-4 w-[1010px]">
<GradientChooserComponent
value={
gradient == ""
? (userData?.profile_image ??
gradients[
parseInt(userData?.id ?? "", 30) % gradients.length
])
: gradient
}
onChange={(value) => {
handleInput({ target: { name: "gradient", value } });
}}
/>
</div>
</Form.Field>
</div>
<div className="flex w-full justify-end">
<div className="w-32">
<Form.Submit asChild>
<Button className="mr-3 mt-6 w-full" type="submit">
Save
</Button>
</Form.Submit>
</div>
</div>
</Form.Root>
</div>
</>
);
}

View file

@ -1,68 +0,0 @@
import * as Form from "@radix-ui/react-form";
import GradientChooserComponent from "../../../../../../components/gradientChooserComponent";
import { Button } from "../../../../../../components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "../../../../../../components/ui/card";
import { gradients } from "../../../../../../utils/styleUtils";
type ProfileGradientFormComponentProps = {
gradient: string;
handleInput: (event: any) => void;
handlePatchGradient: (gradient: string) => void;
userData: any;
};
const ProfileGradientFormComponent = ({
gradient,
handleInput,
handlePatchGradient,
userData,
}: ProfileGradientFormComponentProps) => {
return (
<>
<Form.Root
onSubmit={(event) => {
handlePatchGradient(gradient);
event.preventDefault();
}}
>
<Card x-chunk="dashboard-04-chunk-1">
<CardHeader>
<CardTitle>Profile Gradient</CardTitle>
<CardDescription>
Choose the gradient that appears as your profile picture.
</CardDescription>
</CardHeader>
<CardContent>
<div className="py-2">
<GradientChooserComponent
value={
gradient == ""
? (userData?.profile_image ??
gradients[
parseInt(userData?.id ?? "", 30) % gradients.length
])
: gradient
}
onChange={(value) => {
handleInput({ target: { name: "gradient", value } });
}}
/>
</div>
</CardContent>
<CardFooter className="border-t px-6 py-4">
<Form.Submit asChild>
<Button type="submit">Save</Button>
</Form.Submit>
</CardFooter>
</Card>
</Form.Root>
</>
);
};
export default ProfileGradientFormComponent;

View file

@ -1,71 +0,0 @@
import { CONTROL_PATCH_USER_STATE } from "@/constants/constants";
import { AuthContext } from "@/contexts/authContext";
import { usePostAddApiKey } from "@/controllers/API/queries/api-keys";
import useAlertStore from "@/stores/alertStore";
import { useStoreStore } from "@/stores/storeStore";
import { inputHandlerEventType } from "@/types/components";
import { useContext, useState } from "react";
import { useParams } from "react-router-dom";
import StoreApiKeyFormComponent from "../StoreApiKeyPage/components/StoreApiKeyForm";
import useScrollToElement from "../hooks/use-scroll-to-element";
const StoreApiKeyPage = () => {
const { scrollId } = useParams();
const [inputState, setInputState] = useState(CONTROL_PATCH_USER_STATE);
const { storeApiKey } = useContext(AuthContext);
useScrollToElement(scrollId);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData);
const {
validApiKey,
hasApiKey,
loadingApiKey,
updateHasApiKey: setHasApiKey,
updateValidApiKey: setValidApiKey,
updateLoadingApiKey: setLoadingApiKey,
} = useStoreStore();
const { mutate: addApiKey } = usePostAddApiKey({
onSuccess: () => {
setSuccessData({ title: "API key saved successfully" });
setHasApiKey(true);
setValidApiKey(true);
setLoadingApiKey(false);
handleInput({ target: { name: "apikey", value: "" } });
},
onError: (error) => {
setErrorData({
title: "API key save error",
list: [(error as any)?.response?.data?.detail],
});
setHasApiKey(false);
setValidApiKey(false);
setLoadingApiKey(false);
},
});
const handleSaveKey = (apikey: string) => {
if (apikey) {
addApiKey({ key: apikey });
storeApiKey(apikey);
}
};
const handleInput = ({ target: { name, value } }: inputHandlerEventType) => {
setInputState((prev) => ({ ...prev, [name]: value }));
};
return (
<StoreApiKeyFormComponent
apikey={inputState.apikey}
handleInput={handleInput}
handleSaveKey={handleSaveKey}
loadingApiKey={loadingApiKey}
validApiKey={validApiKey}
hasApiKey={hasApiKey}
/>
);
};
export default StoreApiKeyPage;

View file

@ -1,43 +0,0 @@
type SuspenseImageComponentProps = { src: string };
const imgCache = {
__cache: {},
read(src) {
if (!this.__cache[src]) {
this.__cache[src] = new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
this.__cache[src] = true;
resolve(true);
};
img.onerror = () => {
delete this.__cache[src]; // Remove failed cache entry
reject(new Error("Image failed to load"));
};
img.src = src;
});
}
if (this.__cache[src] instanceof Promise) {
throw this.__cache[src];
}
return this.__cache[src];
},
};
const SuspenseImageComponent = ({
src,
...rest
}: SuspenseImageComponentProps) => {
try {
imgCache.read(src);
} catch (promise) {
if (promise instanceof Promise) {
throw promise;
}
throw new Error("Unexpected error in image loading");
}
return <img src={src} {...rest} />;
};
export default SuspenseImageComponent;

View file

@ -1,12 +0,0 @@
import { FlowType } from "../flow";
export type sidebarNavigationItemType = {
name: string;
href: string;
icon: React.ForwardRefExoticComponent<React.SVGProps<SVGSVGElement>>;
current: boolean;
};
export type localStorageUserType = {
components: { [key: string]: FlowType };
};

View file

@ -1,5 +0,0 @@
export interface SidebarCategory {
display_name: string;
name: string;
icon: string;
}

View file

@ -1,6 +0,0 @@
const template: { [char: string]: string } = {};
export type TemplateContextType = {
templates: typeof template;
setTemplates: (newState: {}) => void;
};

View file

@ -1,21 +0,0 @@
export type ChatInputType = {
result: string;
};
export type ChatOutputType = {
message: string;
sender: string;
sender_name: string;
};
export type FlowPoolObjectType = {
timestamp: string;
valid: boolean;
params: any;
data: { artifacts: any; results: any | ChatOutputType | ChatInputType };
id: string;
};
export type FlowPoolType = {
[key: string]: Array<FlowPoolObjectType>;
};

View file

@ -1,4 +0,0 @@
export type LocationStoreType = {
routeHistory: string[];
setRouteHistory: (location: string) => void;
};