+ function download() {
+ const imageUrl = image;
+ // Fetch the image data
+ fetch(imageUrl)
+ .then((response) => response.blob())
+ .then((blob) => {
+ // Save the image using FileSaver.js
+ saveAs(blob, "image.jpg");
+ })
+ .catch((error) => {
+ setErrorList({ title: "There was an error downloading your image" });
+ console.error("Error downloading image:", error);
+ });
+ }
+
+ return image === "" ? (
+
+
+
+
+
- ) : (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
- >
- )
- );
-}
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/frontend/src/components/accordionComponent/composite/folderAccordionComponent/index.tsx b/src/frontend/src/components/accordionComponent/composite/folderAccordionComponent/index.tsx
new file mode 100644
index 000000000..d4fb95b5c
--- /dev/null
+++ b/src/frontend/src/components/accordionComponent/composite/folderAccordionComponent/index.tsx
@@ -0,0 +1,72 @@
+import { useState } from "react";
+import IconComponent from "../../../../components/genericIconComponent";
+import { AccordionComponentType } from "../../../../types/components";
+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 (
+ <>
+
+
+ {
+ handleClick();
+ }}
+ className="px-2"
+ >
+ {trigger}
+
+
+ {options!.map((option, index) => (
+
+
+ {option.title}
+
+ ))}
+
+
+
+ >
+ );
+}
diff --git a/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx b/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx
index 2745ba1bf..5da7d1461 100644
--- a/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx
+++ b/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx
@@ -2,16 +2,16 @@ import { useState } from "react";
import { registerGlobalVariable } from "../../controllers/API";
import BaseModal from "../../modals/baseModal";
import useAlertStore from "../../stores/alertStore";
-import { useGlobalVariablesStore } from "../../stores/globalVariables";
+import { useGlobalVariablesStore } from "../../stores/globalVariablesStore/globalVariables";
import { useTypesStore } from "../../stores/typesStore";
import { ResponseErrorDetailAPI } from "../../types/api";
-import { sortByName } from "../../utils/utils";
import ForwardedIconComponent from "../genericIconComponent";
import InputComponent from "../inputComponent";
import { Button } from "../ui/button";
import { Input } from "../ui/input";
import { Label } from "../ui/label";
import { Textarea } from "../ui/textarea";
+import sortByName from "./utils/sort-by-name";
//TODO IMPLEMENT FORM LOGIC
@@ -130,8 +130,8 @@ export default function AddNewVariableButton({ children }): JSX.Element {
setFields(value)}
selectedOptions={fields}
- password={false}
options={availableFields()}
+ password={false}
placeholder="Choose a field for the variable..."
id={"apply-to-fields"}
>
diff --git a/src/frontend/src/components/addNewVariableButtonComponent/utils/sort-by-name.tsx b/src/frontend/src/components/addNewVariableButtonComponent/utils/sort-by-name.tsx
new file mode 100644
index 000000000..f3dc06453
--- /dev/null
+++ b/src/frontend/src/components/addNewVariableButtonComponent/utils/sort-by-name.tsx
@@ -0,0 +1,3 @@
+export default function sortByName(stringList: string[]): string[] {
+ return stringList.sort((a, b) => a.localeCompare(b));
+}
diff --git a/src/frontend/src/components/arrayReaderComponent/index.tsx b/src/frontend/src/components/arrayReaderComponent/index.tsx
new file mode 100644
index 000000000..bcbfde010
--- /dev/null
+++ b/src/frontend/src/components/arrayReaderComponent/index.tsx
@@ -0,0 +1,12 @@
+export default function ArrayReader({ array }: { array: any[] }): JSX.Element {
+ //TODO check array type
+ return (
+
+
+ {array.map((item, index) => (
+ - {item}
+ ))}
+
+
+ );
+}
diff --git a/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx b/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx
new file mode 100644
index 000000000..e4425c61c
--- /dev/null
+++ b/src/frontend/src/components/cardComponent/components/dragCardComponent/index.tsx
@@ -0,0 +1,39 @@
+import { storeComponent } from "../../../../types/store";
+import { cn } from "../../../../utils/utils";
+import ForwardedIconComponent from "../../../genericIconComponent";
+import ShadTooltip from "../../../shadTooltipComponent";
+import { Card, CardHeader, CardTitle } from "../../../ui/card";
+
+export default function DragCardComponent({ data }: { data: storeComponent }) {
+ return (
+ <>
+
+
+
+ >
+ );
+}
diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx
index e73ead557..09b8ff833 100644
--- a/src/frontend/src/components/cardComponent/index.tsx
+++ b/src/frontend/src/components/cardComponent/index.tsx
@@ -1,4 +1,6 @@
import { useEffect, useState } from "react";
+import { createRoot } from "react-dom/client";
+import { Control } from "react-hook-form";
import { getComponent, postLikeComponent } from "../../controllers/API";
import IOModal from "../../modals/IOModal";
import DeleteConfirmationModal from "../../modals/deleteConfirmationModal";
@@ -6,11 +8,12 @@ import useAlertStore from "../../stores/alertStore";
import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { useStoreStore } from "../../stores/storeStore";
+import { FlowType } from "../../types/flow";
import { storeComponent } from "../../types/store";
import cloneFLowWithParent, {
getInputsAndOutputs,
} from "../../utils/storeUtils";
-import { cn, convertTestName } from "../../utils/utils";
+import { cn } from "../../utils/utils";
import IconComponent from "../genericIconComponent";
import ShadTooltip from "../shadTooltipComponent";
import { Button } from "../ui/button";
@@ -21,8 +24,11 @@ import {
CardHeader,
CardTitle,
} from "../ui/card";
+import { Checkbox } from "../ui/checkbox";
+import { FormControl, FormField } from "../ui/form";
import Loading from "../ui/loading";
-import { FlowType } from "../../types/flow";
+import { convertTestName } from "./utils/convert-test-name";
+import DragCardComponent from "./components/dragCardComponent";
export default function CollectionCardComponent({
data,
@@ -32,6 +38,8 @@ export default function CollectionCardComponent({
onClick,
onDelete,
playground,
+ control,
+ is_component,
}: {
data: storeComponent;
authorized?: boolean;
@@ -40,6 +48,8 @@ export default function CollectionCardComponent({
button?: JSX.Element;
playground?: boolean;
onDelete?: () => void;
+ control?: Control
;
+ is_component?: boolean;
}) {
const addFlow = useFlowsManagerStore((state) => state.addFlow);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
@@ -69,6 +79,10 @@ export default function CollectionCardComponent({
);
const [loadingPlayground, setLoadingPlayground] = useState(false);
+ const selectedFlowsComponentsCards = useFlowsManagerStore(
+ (state) => state.selectedFlowsComponentsCards,
+ );
+
const name = data.is_component ? "Component" : "Flow";
async function getFlowData() {
@@ -82,7 +96,6 @@ export default function CollectionCardComponent({
return false;
}
const { inputs, outputs } = getInputsAndOutputs(flow?.data?.nodes ?? []);
- console.log(inputs, outputs);
return inputs.length > 0 || outputs.length > 0;
}
@@ -177,36 +190,59 @@ export default function CollectionCardComponent({
}
}
+ const isSelectedCard =
+ selectedFlowsComponentsCards?.includes(data?.id) ?? false;
+
+ function onDragStart(event: React.DragEvent) {
+ let image: JSX.Element = ; // <== whatever you want here
+
+ var ghost = document.createElement("div");
+ ghost.style.transform = "translate(-10000px, -10000px)";
+ ghost.style.position = "absolute";
+ document.body.appendChild(ghost);
+ event.dataTransfer.setDragImage(ghost, 0, 0);
+ const root = createRoot(ghost);
+ root.render(image);
+ const flow = getFlowById(data.id);
+ if (flow) {
+ event.dataTransfer.setData("flow", JSON.stringify(data));
+ }
+ }
+
return (
<>
-
+
+
- {data.name}
+ {data.name}
{data?.metadata !== undefined && (
-
+
{data.private && (
@@ -249,19 +285,29 @@ export default function CollectionCardComponent({
)}
- {onDelete && data?.metadata === undefined && (
-
+
)}
@@ -368,11 +414,7 @@ export default function CollectionCardComponent({
authorized ? "Delete" : "Please review your API key."
}
>
- {
- onDelete();
- }}
- >
+
diff --git a/src/frontend/src/components/cardComponent/utils/convert-test-name.tsx b/src/frontend/src/components/cardComponent/utils/convert-test-name.tsx
new file mode 100644
index 000000000..068b7b585
--- /dev/null
+++ b/src/frontend/src/components/cardComponent/utils/convert-test-name.tsx
@@ -0,0 +1,3 @@
+export function convertTestName(name: string): string {
+ return name.replace(/ /g, "-").toLowerCase();
+}
diff --git a/src/frontend/src/components/cardsWrapComponent/index.tsx b/src/frontend/src/components/cardsWrapComponent/index.tsx
index f84abbb8b..c7ca01588 100644
--- a/src/frontend/src/components/cardsWrapComponent/index.tsx
+++ b/src/frontend/src/components/cardsWrapComponent/index.tsx
@@ -1,5 +1,6 @@
-import { useState } from "react";
+import { useEffect, useState } from "react";
import IconComponent from "../../components/genericIconComponent";
+import { cn } from "../../utils/utils";
export default function CardsWrapComponent({
onFileDrop,
@@ -11,6 +12,23 @@ export default function CardsWrapComponent({
dragMessage?: string;
}) {
const [isDragging, setIsDragging] = useState(false);
+ useEffect(() => {
+ // Function to handle visibility change
+ const handleVisibilityChange = () => {
+ if (document.visibilityState === "visible") {
+ // Reset hover state or perform any necessary actions when the tab becomes visible again
+ setIsDragging(false);
+ }
+ };
+
+ // Add event listener for visibility change
+ document.addEventListener("visibilitychange", handleVisibilityChange);
+
+ // Cleanup event listener on component unmount
+ return () => {
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
+ };
+ }, []);
const dragOver = (e) => {
e.preventDefault();
@@ -43,12 +61,12 @@ export default function CardsWrapComponent({
onDragEnter={dragEnter}
onDragLeave={dragLeave}
onDrop={onDrop}
- className={
- "h-full w-full " +
- (isDragging
- ? "mb-24 flex flex-col items-center justify-center gap-4 text-2xl font-light"
- : "")
- }
+ className={cn(
+ "h-full w-full",
+ isDragging
+ ? "mb-36 flex flex-col items-center justify-center gap-4 text-2xl font-light"
+ : "",
+ )}
>
{isDragging ? (
<>
diff --git a/src/frontend/src/components/chatComponent/index.tsx b/src/frontend/src/components/chatComponent/index.tsx
index 5de2198a0..81dade485 100644
--- a/src/frontend/src/components/chatComponent/index.tsx
+++ b/src/frontend/src/components/chatComponent/index.tsx
@@ -15,8 +15,8 @@ export default function FlowToolbar(): JSX.Element {
const hasIO = useFlowStore((state) => state.hasIO);
const hasStore = useStoreStore((state) => state.hasStore);
const validApiKey = useStoreStore((state) => state.validApiKey);
- const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
const hasApiKey = useStoreStore((state) => state.hasApiKey);
+ const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx
index 0b08aaccd..1e745f950 100644
--- a/src/frontend/src/components/codeTabsComponent/index.tsx
+++ b/src/frontend/src/components/codeTabsComponent/index.tsx
@@ -26,6 +26,8 @@ import {
TabsTrigger,
} from "../../components/ui/tabs";
import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants";
+import ExportModal from "../../modals/exportModal";
+import { Case } from "../../shared/components/caseComponent";
import { useDarkStore } from "../../stores/darkStore";
import useFlowStore from "../../stores/flowStore";
import { codeTabsPropsType } from "../../types/components";
@@ -43,7 +45,6 @@ import KeypairListComponent from "../keypairListComponent";
import ShadTooltip from "../shadTooltipComponent";
import { Label } from "../ui/label";
import { Switch } from "../ui/switch";
-import ExportModal from "../../modals/exportModal";
export default function CodeTabsComponent({
flow,
@@ -54,6 +55,7 @@ export default function CodeTabsComponent({
tweaks,
setActiveTweaks,
activeTweaks,
+ allowExport = false,
}: codeTabsPropsType) {
const [isCopied, setIsCopied] = useState(false);
const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null);
@@ -87,6 +89,10 @@ export default function CodeTabsComponent({
});
};
+ const type = (node, templateParam) => {
+ return node.data.node.template[templateParam].type;
+ };
+
const downloadAsFile = () => {
const fileExtension = tabs[activeTab].language || ".txt";
const suggestedFileName = `${"generated-code."}${fileExtension}`;
@@ -141,39 +147,43 @@ export default function CodeTabsComponent({
)}
-
2
- ? "hidden"
- : "relative top-[2.5px] flex gap-2"
- }
- >
-
-
-
-
-
-
- Export Flow
+
+
-
+ )}
+ {allowExport && (
+
+
+
+ Export Flow
+
+
+ )}
{Number(activeTab) < 4 && (
<>
@@ -276,107 +286,71 @@ export default function CodeTabsComponent({
- {node.data.node.template[
- templateField
- ].type === "str" &&
- !node.data.node.template[
- templateField
- ].options ? (
-
- {node.data.node.template[
+ {
- setData((old) => {
- let newInputList =
- cloneDeep(old);
- newInputList![
- i
- ].data.node.template[
- templateField
- ].value = target;
- return newInputList;
- });
- tweaks?.buildTweakObject!(
- node["data"]["id"],
- target,
- node.data.node.template[
- templateField
- ],
- );
- }}
- />
- ) : node.data.node.template[
+ ].options
+ }
+ >
+
-
+ {
- setData((old) => {
- let newInputList =
- cloneDeep(old);
- newInputList![
- i
- ].data.node.template[
- templateField
- ].value = target;
- return newInputList;
- });
- tweaks?.buildTweakObject!(
- node["data"]["id"],
- target,
- node.data.node
- .template[
- templateField
- ],
- );
- }}
- />
-
- ) : (
-
{
+ setData((old) => {
+ let newInputList =
+ cloneDeep(old);
+ newInputList![
+ i
+ ].data.node.template[
+ templateField
+ ].value = target;
+ return newInputList;
+ });
+ tweaks?.buildTweakObject!(
+ node["data"]["id"],
+ target,
node.data.node.template[
templateField
- ].password ?? false
- }
+ ],
+ );
+ }}
+ />
+
+
+
+
+
- )}
-
- ) : node.data.node.template[
- templateField
- ].type === "bool" ? (
+
+
+
+
+ {
+ setData((old) => {
+ let newInputList =
+ cloneDeep(old);
+ newInputList![
+ i
+ ].data.node.template[
+ templateField
+ ].value = target;
+ return newInputList;
+ });
+ tweaks?.buildTweakObject!(
+ node["data"]["id"],
+ target,
+ node.data.node.template[
+ templateField
+ ],
+ );
+ }}
+ />
+
+
+
+
{" "}
- ) : node.data.node.template[
- templateField
- ].type === "file" ? (
+
+
+
- ) : node.data.node.template[
- templateField
- ].type === "float" ? (
+
+
+
- ) : node.data.node.template[
- templateField
- ].type === "str" &&
- node.data.node.template[
- templateField
- ].options ? (
+
+
+
- ) : node.data.node.template[
- templateField
- ].type === "int" ? (
+
+
+
- ) : node.data.node.template[
- templateField
- ].type === "prompt" ? (
+
+
+
- ) : node.data.node.template[
- templateField
- ].type === "code" ? (
+
+
+
- ) : node.data.node.template[
- templateField
- ].type === "dict" ? (
+
+
+
- ) : node.data.node.template[
- templateField
- ].type === "NestedDict" ? (
+
+
+
- ) : node.data.node.template[
- templateField
- ].type === "Any" ? (
- "-"
- ) : (
-
- )}
+
+
+
+ <>->
+
diff --git a/src/frontend/src/components/csvOutputComponent/helpers/convert-data-function.ts b/src/frontend/src/components/csvOutputComponent/helpers/convert-data-function.ts
index 9c9211ac6..83a2316a6 100644
--- a/src/frontend/src/components/csvOutputComponent/helpers/convert-data-function.ts
+++ b/src/frontend/src/components/csvOutputComponent/helpers/convert-data-function.ts
@@ -1,7 +1,6 @@
export const convertCSVToData = (csvFile, csvSeparator: string) => {
const lines = csvFile.data.trim().split("\n");
const headers = lines[0].trim().split(csvSeparator);
-
const initialRowData: any = [];
const initialColDefs = headers.map((header) => ({
diff --git a/src/frontend/src/components/csvOutputComponent/index.tsx b/src/frontend/src/components/csvOutputComponent/index.tsx
index fcf59029c..a98d9c028 100644
--- a/src/frontend/src/components/csvOutputComponent/index.tsx
+++ b/src/frontend/src/components/csvOutputComponent/index.tsx
@@ -1,7 +1,6 @@
import "ag-grid-community/styles/ag-grid.css"; // Mandatory CSS required by the grid
import "ag-grid-community/styles/ag-theme-balham.css"; // Optional Theme applied to the grid
-import { AgGridReact } from "ag-grid-react";
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
import {
CSVError,
CSVNoDataError,
@@ -11,6 +10,7 @@ import { useDarkStore } from "../../stores/darkStore";
import { FlowPoolObjectType } from "../../types/chat";
import { NodeType } from "../../types/flow";
import ForwardedIconComponent from "../genericIconComponent";
+import TableComponent from "../tableComponent";
import Loading from "../ui/loading";
import { convertCSVToData } from "./helpers/convert-data-function";
@@ -54,8 +54,6 @@ function CsvOutputComponent({
const [colDefs, setColDefs] = useState([]);
const [status, setStatus] = useState("loading");
- var currentRowHeight: number;
- var minRowHeight = 25;
const defaultColDef = useMemo(() => {
return {
width: 200,
@@ -82,48 +80,6 @@ function CsvOutputComponent({
}
}, [separator]);
- const getRowHeight = useCallback(() => {
- return currentRowHeight;
- }, []);
-
- const onGridReady = useCallback((params: any) => {
- minRowHeight = params.api.getSizesForCurrentTheme().rowHeight;
- currentRowHeight = minRowHeight;
- }, []);
-
- const updateRowHeight = (params: { api: any }) => {
- const bodyViewport = document.querySelector(".ag-body-viewport");
- if (!bodyViewport) {
- return;
- }
- var gridHeight = bodyViewport.clientHeight;
- var renderedRowCount = params.api.getDisplayedRowCount();
-
- if (renderedRowCount * minRowHeight >= gridHeight) {
- if (currentRowHeight !== minRowHeight) {
- currentRowHeight = minRowHeight;
- params.api.resetRowHeights();
- }
- } else {
- currentRowHeight = Math.floor(gridHeight / renderedRowCount);
- params.api.resetRowHeights();
- }
- };
-
- const onFirstDataRendered = useCallback(
- (params: any) => {
- updateRowHeight(params);
- },
- [updateRowHeight],
- );
-
- const onGridSizeChanged = useCallback(
- (params: any) => {
- updateRowHeight(params);
- },
- [updateRowHeight],
- );
-
return (
{status === "nodata" && (
@@ -158,14 +114,10 @@ function CsvOutputComponent({
className={`${dark ? "ag-theme-balham-dark" : "ag-theme-balham"}`}
style={{ height: "100%", width: "100%" }}
>
-
diff --git a/src/frontend/src/components/dateReaderComponent/index.tsx b/src/frontend/src/components/dateReaderComponent/index.tsx
new file mode 100644
index 000000000..81c352f5a
--- /dev/null
+++ b/src/frontend/src/components/dateReaderComponent/index.tsx
@@ -0,0 +1,21 @@
+export default function DateReader({
+ date: dateString,
+}: {
+ date: string;
+}): JSX.Element {
+ const date = new Date(dateString);
+
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, "0"); // Months are 0-indexed in JavaScript
+ const day = String(date.getDate()).padStart(2, "0");
+
+ const hours = date.getHours();
+ const minutes = String(date.getMinutes()).padStart(2, "0");
+
+ const ampm = hours >= 12 ? "PM" : "AM";
+ const hours12 = hours > 12 ? hours - 12 : hours === 0 ? 12 : hours; // Convert to 12-hour format
+
+ const formattedDate = `${year}-${month}-${day} ${hours12}:${minutes} ${ampm}`;
+
+ return
{formattedDate};
+}
diff --git a/src/frontend/src/components/dropdownComponent/index.tsx b/src/frontend/src/components/dropdownComponent/index.tsx
index b17332268..8402d166e 100644
--- a/src/frontend/src/components/dropdownComponent/index.tsx
+++ b/src/frontend/src/components/dropdownComponent/index.tsx
@@ -59,7 +59,7 @@ export default function Dropdown({
? "dropdown-component-outline"
: "dropdown-component-false-outline",
"w-full justify-between font-normal",
- editNode ? "input-edit-node" : "py-2"
+ editNode ? "input-edit-node" : "py-2",
)}
>
@@ -78,6 +78,8 @@ export default function Dropdown({
)}
diff --git a/src/frontend/src/components/genericIconComponent/index.tsx b/src/frontend/src/components/genericIconComponent/index.tsx
index f4e6a49d6..9f7c687a1 100644
--- a/src/frontend/src/components/genericIconComponent/index.tsx
+++ b/src/frontend/src/components/genericIconComponent/index.tsx
@@ -7,7 +7,7 @@ import Loading from "../ui/loading";
import { useEffect, useState } from "react";
-const ForwardedIconComponent = memo(
+export const ForwardedIconComponent = memo(
forwardRef(
(
{
@@ -18,7 +18,7 @@ const ForwardedIconComponent = memo(
strokeWidth,
id = "",
}: IconComponentProps,
- ref
+ ref,
) => {
const [showFallback, setShowFallback] = useState(false);
@@ -65,8 +65,8 @@ const ForwardedIconComponent = memo(
/>
);
- }
- )
+ },
+ ),
);
export default ForwardedIconComponent;
diff --git a/src/frontend/src/components/headerComponent/components/menuBar/index.tsx b/src/frontend/src/components/headerComponent/components/menuBar/index.tsx
index 85a586b16..41f7b6d5b 100644
--- a/src/frontend/src/components/headerComponent/components/menuBar/index.tsx
+++ b/src/frontend/src/components/headerComponent/components/menuBar/index.tsx
@@ -8,10 +8,10 @@ import {
} from "../../../ui/dropdown-menu";
import { useNavigate } from "react-router-dom";
-import { Node } from "reactflow";
import { UPLOAD_ERROR_ALERT } from "../../../../constants/alerts_constants";
import { SAVED_HOVER } from "../../../../constants/constants";
import ExportModal from "../../../../modals/exportModal";
+import FlowLogsModal from "../../../../modals/flowLogsModal";
import FlowSettingsModal from "../../../../modals/flowSettingsModal";
import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore";
@@ -21,11 +21,7 @@ import IconComponent from "../../../genericIconComponent";
import ShadTooltip from "../../../shadTooltipComponent";
import { Button } from "../../../ui/button";
-export const MenuBar = ({
- removeFunction,
-}: {
- removeFunction: (nodes: Node[]) => void;
-}): JSX.Element => {
+export const MenuBar = ({}: {}): JSX.Element => {
const addFlow = useFlowsManagerStore((state) => state.addFlow);
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
const setErrorData = useAlertStore((state) => state.setErrorData);
@@ -34,7 +30,7 @@ export const MenuBar = ({
const redo = useFlowsManagerStore((state) => state.redo);
const saveLoading = useFlowsManagerStore((state) => state.saveLoading);
const [openSettings, setOpenSettings] = useState(false);
- const nodes = useFlowStore((state) => state.nodes);
+ const [openLogs, setOpenLogs] = useState(false);
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
const navigate = useNavigate();
const isBuilding = useFlowStore((state) => state.isBuilding);
@@ -70,14 +66,6 @@ export const MenuBar = ({
return currentFlow ? (
-
@@ -123,6 +111,18 @@ export const MenuBar = ({
/>
Settings
+ {
+ setOpenLogs(true);
+ }}
+ className="cursor-pointer"
+ >
+
+ Logs
+
{
@@ -132,7 +132,7 @@ export const MenuBar = ({
title: UPLOAD_ERROR_ALERT,
list: [error],
});
- }
+ },
);
}}
>
@@ -194,6 +194,7 @@ export const MenuBar = ({
open={openSettings}
setOpen={setOpenSettings}
>
+
{(currentFlow.updated_at || saveLoading) && (
{printByBuildStatus()}
diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx
index bc2537ea2..4f6c02bd8 100644
--- a/src/frontend/src/components/headerComponent/index.tsx
+++ b/src/frontend/src/components/headerComponent/index.tsx
@@ -3,14 +3,17 @@ import { FaDiscord, FaGithub } from "react-icons/fa";
import { RiTwitterXFill } from "react-icons/ri";
import { Link, useLocation, useNavigate, useParams } from "react-router-dom";
import AlertDropdown from "../../alerts/alertDropDown";
-import { USER_PROJECTS_HEADER } from "../../constants/constants";
+import {
+ LOCATIONS_TO_RETURN,
+ USER_PROJECTS_HEADER,
+} from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
-import { Node } from "reactflow";
import useAlertStore from "../../stores/alertStore";
import { useDarkStore } from "../../stores/darkStore";
import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
+import { useLocationStore } from "../../stores/locationStore";
import { useStoreStore } from "../../stores/storeStore";
import { gradients } from "../../utils/styleUtils";
import IconComponent from "../genericIconComponent";
@@ -29,6 +32,7 @@ import MenuBar from "./components/menuBar";
export default function Header(): JSX.Element {
const notificationCenter = useAlertStore((state) => state.notificationCenter);
const location = useLocation();
+
const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
const navigate = useNavigate();
const removeFlow = useFlowsManagerStore((store) => store.removeFlow);
@@ -40,60 +44,81 @@ export default function Header(): JSX.Element {
const setDark = useDarkStore((state) => state.setDark);
const stars = useDarkStore((state) => state.stars);
- async function checkForChanges(nodes: Node[]): Promise
{
+ const routeHistory = useLocationStore((state) => state.routeHistory);
+
+ async function checkForChanges(): Promise {
if (nodes.length === 0) {
await removeFlow(id!);
}
}
+ const redirectToLastLocation = () => {
+ const lastFlowVisitedIndex = routeHistory
+ .reverse()
+ .findIndex(
+ (path) => path.includes("/flow/") && path !== location.pathname,
+ );
+
+ const lastFlowVisited = routeHistory[lastFlowVisitedIndex];
+ lastFlowVisited && !location.pathname.includes("/flow")
+ ? navigate(lastFlowVisited)
+ : navigate("/all");
+ };
+
+ const visitedFlowPathBefore = () => {
+ const lastThreeVisitedPaths = routeHistory.slice(-3);
+ return lastThreeVisitedPaths.some((path) => path.includes("/flow/"));
+ };
+
+ const showArrowReturnIcon =
+ LOCATIONS_TO_RETURN.some((path) => location.pathname.includes(path)) &&
+ visitedFlowPathBefore();
+
return (
- checkForChanges(nodes)}>
+
⛓️
-
+ {showArrowReturnIcon && (
+
+ )}
+
+
+
- {/*
-
- */}
+
{hasStore && (