diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx
index 369a6f764..a58342799 100644
--- a/src/frontend/src/App.tsx
+++ b/src/frontend/src/App.tsx
@@ -1,90 +1,12 @@
-import { Suspense, useContext, useEffect } from "react";
-import { Cookies } from "react-cookie";
+import { Suspense } from "react";
import { RouterProvider } from "react-router-dom";
import "reactflow/dist/style.css";
-import LoadingComponent from "./components/loadingComponent";
-import { AuthContext } from "./contexts/authContext";
-import {
- useAutoLogin,
- useRefreshAccessToken,
-} from "./controllers/API/queries/auth";
-import { useGetVersionQuery } from "./controllers/API/queries/version";
-import useSaveConfig from "./hooks/use-save-config";
+import { LoadingPage } from "./pages/LoadingPage";
import router from "./routes";
-import useAlertStore from "./stores/alertStore";
-import useAuthStore from "./stores/authStore";
-import { useDarkStore } from "./stores/darkStore";
-import useFlowsManagerStore from "./stores/flowsManagerStore";
export default function App() {
- const { login, setUserData, getUser } = useContext(AuthContext);
- const setAutoLogin = useAuthStore((state) => state.setAutoLogin);
- const setLoading = useAlertStore((state) => state.setLoading);
- const refreshStars = useDarkStore((state) => state.refreshStars);
- const dark = useDarkStore((state) => state.dark);
- const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
- const cookies = new Cookies();
- const logout = useAuthStore((state) => state.logout);
-
- const refreshToken = cookies.get("refresh_token");
-
- const { mutate: mutateAutoLogin } = useAutoLogin();
-
- const { mutate: mutateRefresh } = useRefreshAccessToken();
-
- const isLoginPage = location.pathname.includes("login");
-
- useEffect(() => {
- if (!dark) {
- document.getElementById("body")!.classList.remove("dark");
- } else {
- document.getElementById("body")!.classList.add("dark");
- }
- }, [dark]);
-
- useEffect(() => {
- mutateAutoLogin(undefined, {
- onSuccess: async (user) => {
- if (user && user["access_token"]) {
- user["refresh_token"] = "auto";
- login(user["access_token"], "auto");
- setUserData(user);
- setAutoLogin(true);
- refreshStars();
- // mutateRefresh({ refresh_token: refreshToken });
- }
- },
- onError: (error) => {
- if (error.name !== "CanceledError") {
- setAutoLogin(false);
- if (!isLoginPage) {
- if (!isAuthenticated) {
- setLoading(false);
- useFlowsManagerStore.setState({ isLoading: false });
- logout();
- } else {
- mutateRefresh({ refresh_token: refreshToken });
- refreshStars();
- getUser();
- }
- }
- }
- },
- });
- }, []);
-
- useGetVersionQuery();
- useSaveConfig();
-
return (
- //need parent component with width and height
-
-
-
- }
- >
+ }>
);
diff --git a/src/frontend/src/components/authAdminGuard/index.tsx b/src/frontend/src/components/authAdminGuard/index.tsx
index 715b9bba2..f4a60dbaf 100644
--- a/src/frontend/src/components/authAdminGuard/index.tsx
+++ b/src/frontend/src/components/authAdminGuard/index.tsx
@@ -1,8 +1,8 @@
+import { LoadingPage } from "@/pages/LoadingPage";
import useAuthStore from "@/stores/authStore";
import { useContext } from "react";
import { Navigate } from "react-router-dom";
import { AuthContext } from "../../contexts/authContext";
-import LoadingComponent from "../loadingComponent";
export const ProtectedAdminRoute = ({ children }) => {
const { userData } = useContext(AuthContext);
@@ -11,11 +11,7 @@ export const ProtectedAdminRoute = ({ children }) => {
const isAdmin = useAuthStore((state) => state.isAdmin);
if (!isAuthenticated) {
- return (
-
-
-
- );
+ return ;
} else if ((userData && !isAdmin) || autoLogin) {
return ;
} else {
diff --git a/src/frontend/src/components/authGuard/index.tsx b/src/frontend/src/components/authGuard/index.tsx
index 550ec88e4..a64ba8c6d 100644
--- a/src/frontend/src/components/authGuard/index.tsx
+++ b/src/frontend/src/components/authGuard/index.tsx
@@ -25,14 +25,17 @@ export const ProtectedRoute = ({ children }) => {
? automaticRefreshTime
: envRefreshTime;
- const intervalId = setInterval(() => {
+ const intervalFunction = () => {
if (isAuthenticated) {
mutateRefresh({ refresh_token: refreshToken });
}
- }, accessTokenTimer * 1000);
+ };
+
+ const intervalId = setInterval(intervalFunction, accessTokenTimer * 1000);
+ intervalFunction();
return () => clearInterval(intervalId);
- }, []);
+ }, [isAuthenticated]);
if (!isAuthenticated && hasToken) {
logout();
diff --git a/src/frontend/src/components/folderSidebarComponent/hooks/use-on-file-drop.tsx b/src/frontend/src/components/folderSidebarComponent/hooks/use-on-file-drop.tsx
index 40b0d8e76..7e2393444 100644
--- a/src/frontend/src/components/folderSidebarComponent/hooks/use-on-file-drop.tsx
+++ b/src/frontend/src/components/folderSidebarComponent/hooks/use-on-file-drop.tsx
@@ -19,12 +19,12 @@ const useFileDrop = (folderId: string) => {
const flows = useFlowsManagerStore((state) => state.flows);
const saveFlow = useSaveFlow();
const { mutate: uploadFlowToFolder } = usePostUploadFlowToFolder();
- const handleFileDrop = async (e) => {
+ const handleFileDrop = async (e, folderId) => {
if (e.dataTransfer.types.some((type) => type === "Files")) {
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
const firstFile = e.dataTransfer.files[0];
if (firstFile.type === "application/json") {
- uploadFormData(firstFile);
+ uploadFormData(firstFile, folderId);
} else {
setErrorData({
title: WRONG_FILE_ERROR_ALERT,
@@ -94,7 +94,7 @@ const useFileDrop = (folderId: string) => {
}
e.preventDefault();
- handleFileDrop(e);
+ handleFileDrop(e, folderId);
};
const uploadFromDragCard = (flowId, folderId) => {
@@ -115,7 +115,7 @@ const useFileDrop = (folderId: string) => {
saveFlow(updatedFlow);
};
- const uploadFormData = (data) => {
+ const uploadFormData = (data, folderId) => {
const formData = new FormData();
formData.append("file", data);
setFolderDragging(false);
diff --git a/src/frontend/src/contexts/authContext.tsx b/src/frontend/src/contexts/authContext.tsx
index 59e4a9d32..5a8026629 100644
--- a/src/frontend/src/contexts/authContext.tsx
+++ b/src/frontend/src/contexts/authContext.tsx
@@ -8,7 +8,6 @@ import { useGetUserData } from "@/controllers/API/queries/auth";
import useAuthStore from "@/stores/authStore";
import { createContext, useEffect, useState } from "react";
import Cookies from "universal-cookie";
-import useAlertStore from "../stores/alertStore";
import { useStoreStore } from "../stores/storeStore";
import { Users } from "../types/api";
import { AuthContextType } from "../types/contexts/auth";
@@ -33,7 +32,6 @@ export function AuthProvider({ children }): React.ReactElement {
cookies.get(LANGFLOW_ACCESS_TOKEN) ?? null,
);
const [userData, setUserData] = useState(null);
- const setLoading = useAlertStore((state) => state.setLoading);
const [apiKey, setApiKey] = useState(
cookies.get(LANGFLOW_API_TOKEN),
);
@@ -71,7 +69,6 @@ export function AuthProvider({ children }): React.ReactElement {
},
onError: () => {
setUserData(null);
- setLoading(false);
},
},
);
diff --git a/src/frontend/src/controllers/API/queries/api-keys/use-get-api-keys.ts b/src/frontend/src/controllers/API/queries/api-keys/use-get-api-keys.ts
index 850fa7b91..89f4bc502 100644
--- a/src/frontend/src/controllers/API/queries/api-keys/use-get-api-keys.ts
+++ b/src/frontend/src/controllers/API/queries/api-keys/use-get-api-keys.ts
@@ -1,4 +1,3 @@
-import { useDarkStore } from "@/stores/darkStore";
import { useQueryFunctionType } from "@/types/api";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
@@ -24,7 +23,7 @@ interface IApiQueryResponse {
export const useGetApiKeysQuery: useQueryFunctionType<
undefined,
IApiQueryResponse
-> = (_, options) => {
+> = (options) => {
const { query } = UseRequestProcessor();
const getApiKeysFn = async () => {
diff --git a/src/frontend/src/controllers/API/queries/auth/use-get-autologin.ts b/src/frontend/src/controllers/API/queries/auth/use-get-autologin.ts
index a05311268..d4bb7c710 100644
--- a/src/frontend/src/controllers/API/queries/auth/use-get-autologin.ts
+++ b/src/frontend/src/controllers/API/queries/auth/use-get-autologin.ts
@@ -1,24 +1,57 @@
-import { UseMutationResult } from "@tanstack/react-query";
-import { useMutationFunctionType } from "../../../../types/api";
+import { AuthContext } from "@/contexts/authContext";
+import useAuthStore from "@/stores/authStore";
+import { AxiosError } from "axios";
+import { useContext } from "react";
+import { useQueryFunctionType, Users } from "../../../../types/api";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
-export const useAutoLogin: useMutationFunctionType = (
- options?,
+export interface AutoLoginResponse {
+ frontend_timeout: number;
+ auto_saving: boolean;
+ auto_saving_interval: number;
+ health_check_max_retries: number;
+}
+
+export const useGetAutoLogin: useQueryFunctionType = (
+ options,
) => {
- const { mutate } = UseRequestProcessor();
+ const { query } = UseRequestProcessor();
+ const { login, setUserData, getUser } = useContext(AuthContext);
+ const setAutoLogin = useAuthStore((state) => state.setAutoLogin);
+ const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
+ const logout = useAuthStore((state) => state.logout);
+ const isLoginPage = location.pathname.includes("login");
- const autoLoginFn = async (): Promise => {
- const res = await api.get(`${getURL("AUTOLOGIN")}`);
- return res.data;
- };
+ async function getAutoLoginFn(): Promise {
+ try {
+ const response = await api.get(`${getURL("AUTOLOGIN")}`);
+ const user = response.data;
+ if (user && user["access_token"]) {
+ user["refresh_token"] = "auto";
+ login(user["access_token"], "auto");
+ setUserData(user);
+ setAutoLogin(true);
+ }
+ } catch (e) {
+ const error = e as AxiosError;
+ if (error.name !== "CanceledError") {
+ setAutoLogin(false);
+ if (!isLoginPage) {
+ if (!isAuthenticated) {
+ await logout();
+ throw new Error("Unauthorized");
+ } else {
+ getUser();
+ }
+ }
+ }
+ }
+ return null;
+ }
- const mutation: UseMutationResult = mutate(
- ["useAutoLogin"],
- autoLoginFn,
- options,
- );
+ const queryResult = query(["useGetAutoLogin"], getAutoLoginFn, options);
- return mutation;
+ return queryResult;
};
diff --git a/src/frontend/src/controllers/API/queries/config/use-get-config.ts b/src/frontend/src/controllers/API/queries/config/use-get-config.ts
index 72a7e4318..975b21b3e 100644
--- a/src/frontend/src/controllers/API/queries/config/use-get-config.ts
+++ b/src/frontend/src/controllers/API/queries/config/use-get-config.ts
@@ -1,3 +1,5 @@
+import useFlowsManagerStore from "@/stores/flowsManagerStore";
+import axios from "axios";
import { useQueryFunctionType } from "../../../../types/api";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
@@ -10,18 +12,35 @@ export interface ConfigResponse {
health_check_max_retries: number;
}
-export const useGetConfigQuery: useQueryFunctionType<
- undefined,
- ConfigResponse
-> = (options) => {
+export const useGetConfig: useQueryFunctionType = (
+ options,
+) => {
+ const setAutoSaving = useFlowsManagerStore((state) => state.setAutoSaving);
+ const setAutoSavingInterval = useFlowsManagerStore(
+ (state) => state.setAutoSavingInterval,
+ );
+ const setHealthCheckMaxRetries = useFlowsManagerStore(
+ (state) => state.setHealthCheckMaxRetries,
+ );
+
const { query } = UseRequestProcessor();
const getConfigFn = async () => {
const response = await api.get(`${getURL("CONFIG")}`);
- return response["data"];
+ const data = response["data"];
+ if (data) {
+ const timeoutInMilliseconds = data.frontend_timeout
+ ? data.frontend_timeout * 1000
+ : 30000;
+ axios.defaults.baseURL = "";
+ axios.defaults.timeout = timeoutInMilliseconds;
+ setAutoSaving(data.auto_saving);
+ setAutoSavingInterval(data.auto_saving_interval);
+ setHealthCheckMaxRetries(data.health_check_max_retries);
+ }
};
- const queryResult = query(["useGetConfigQuery"], getConfigFn, options);
+ const queryResult = query(["useGetConfig"], getConfigFn, options);
return queryResult;
};
diff --git a/src/frontend/src/controllers/API/queries/health/use-get-health.ts b/src/frontend/src/controllers/API/queries/health/use-get-health.ts
index 959d2e202..c43ab488c 100644
--- a/src/frontend/src/controllers/API/queries/health/use-get-health.ts
+++ b/src/frontend/src/controllers/API/queries/health/use-get-health.ts
@@ -5,7 +5,7 @@ import {
import { useUtilityStore } from "@/stores/utilityStore";
import { createNewError503 } from "@/types/factory/axios-error-503";
import { keepPreviousData } from "@tanstack/react-query";
-import { AxiosError, AxiosHeaders } from "axios";
+import { AxiosError } from "axios";
import { useQueryFunctionType } from "../../../../types/api";
import { api } from "../../api";
import { UseRequestProcessor } from "../../services/request-processor";
@@ -21,7 +21,7 @@ interface getHealthResponse {
export const useGetHealthQuery: useQueryFunctionType<
undefined,
getHealthResponse
-> = (_, options) => {
+> = (options) => {
const { query } = UseRequestProcessor();
const setHealthCheckTimeout = useUtilityStore(
(state) => state.setHealthCheckTimeout,
diff --git a/src/frontend/src/controllers/API/queries/store/use-get-tags.ts b/src/frontend/src/controllers/API/queries/store/use-get-tags.ts
index 03cf88675..bf43a628d 100644
--- a/src/frontend/src/controllers/API/queries/store/use-get-tags.ts
+++ b/src/frontend/src/controllers/API/queries/store/use-get-tags.ts
@@ -13,7 +13,7 @@ type tagsQueryResponse = Array;
export const useGetTagsQuery: useQueryFunctionType<
undefined,
tagsQueryResponse
-> = (_, options) => {
+> = (options) => {
const { query } = UseRequestProcessor();
const getTagsFn = async () => {
diff --git a/src/frontend/src/controllers/API/queries/version/use-get-version.ts b/src/frontend/src/controllers/API/queries/version/use-get-version.ts
index 1947a8aa1..2aca9d8f2 100644
--- a/src/frontend/src/controllers/API/queries/version/use-get-version.ts
+++ b/src/frontend/src/controllers/API/queries/version/use-get-version.ts
@@ -12,7 +12,7 @@ interface versionQueryResponse {
export const useGetVersionQuery: useQueryFunctionType<
undefined,
versionQueryResponse
-> = (_, options) => {
+> = (options) => {
const { query } = UseRequestProcessor();
const getVersionFn = async () => {
diff --git a/src/frontend/src/hooks/use-save-config.ts b/src/frontend/src/hooks/use-save-config.ts
deleted file mode 100644
index 5df3674d4..000000000
--- a/src/frontend/src/hooks/use-save-config.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import useFlowsManagerStore from "@/stores/flowsManagerStore";
-import axios from "axios";
-import { useEffect } from "react";
-import { useGetConfigQuery } from "../controllers/API/queries/config/use-get-config";
-
-function useSaveConfig() {
- const { data } = useGetConfigQuery();
- const setAutoSaving = useFlowsManagerStore((state) => state.setAutoSaving);
- const setAutoSavingInterval = useFlowsManagerStore(
- (state) => state.setAutoSavingInterval,
- );
- const setHealthCheckMaxRetries = useFlowsManagerStore(
- (state) => state.setHealthCheckMaxRetries,
- );
-
- useEffect(() => {
- if (data) {
- const timeoutInMilliseconds = data.frontend_timeout
- ? data.frontend_timeout * 1000
- : 30000;
- axios.defaults.baseURL = "";
- axios.defaults.timeout = timeoutInMilliseconds;
- setAutoSaving(data.auto_saving);
- setAutoSavingInterval(data.auto_saving_interval);
- setHealthCheckMaxRetries(data.health_check_max_retries);
- }
- }, [data]);
-}
-
-export default useSaveConfig;
diff --git a/src/frontend/src/index.tsx b/src/frontend/src/index.tsx
index 95878233b..ca3d58b86 100644
--- a/src/frontend/src/index.tsx
+++ b/src/frontend/src/index.tsx
@@ -1,5 +1,4 @@
import ReactDOM from "react-dom/client";
-import ContextWrapper from "./contexts";
import reportWebVitals from "./reportWebVitals";
import "./style/classes.css";
@@ -16,9 +15,5 @@ const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement,
);
-root.render(
-
-
- ,
-);
+root.render();
reportWebVitals();
diff --git a/src/frontend/src/pages/AdminPage/LoginPage/index.tsx b/src/frontend/src/pages/AdminPage/LoginPage/index.tsx
index 68bd53755..e401f7ec0 100644
--- a/src/frontend/src/pages/AdminPage/LoginPage/index.tsx
+++ b/src/frontend/src/pages/AdminPage/LoginPage/index.tsx
@@ -1,5 +1,4 @@
import { useLoginUser } from "@/controllers/API/queries/auth";
-import { useFolderStore } from "@/stores/foldersStore";
import { useContext, useState } from "react";
import { Button } from "../../../components/ui/button";
import { Input } from "../../../components/ui/input";
@@ -17,8 +16,6 @@ export default function LoginAdminPage() {
const [inputState, setInputState] =
useState(CONTROL_LOGIN_STATE);
const { login } = useContext(AuthContext);
- const setLoading = useAlertStore((state) => state.setLoading);
- const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder);
const { password, username } = inputState;
const setErrorData = useAlertStore((state) => state.setErrorData);
@@ -38,9 +35,6 @@ export default function LoginAdminPage() {
mutate(user, {
onSuccess: (res) => {
- setSelectedFolder(null);
-
- setLoading(true);
login(res.access_token, "login", res.refresh_token);
},
onError: (error) => {
diff --git a/src/frontend/src/pages/AppInitPage/index.tsx b/src/frontend/src/pages/AppInitPage/index.tsx
new file mode 100644
index 000000000..a724a21b9
--- /dev/null
+++ b/src/frontend/src/pages/AppInitPage/index.tsx
@@ -0,0 +1,40 @@
+import { useGetAutoLogin } from "@/controllers/API/queries/auth";
+import { useGetConfig } from "@/controllers/API/queries/config/use-get-config";
+import { useGetVersionQuery } from "@/controllers/API/queries/version";
+import { useDarkStore } from "@/stores/darkStore";
+import useFlowsManagerStore from "@/stores/flowsManagerStore";
+import { useEffect } from "react";
+import { Outlet } from "react-router-dom";
+import { LoadingPage } from "../LoadingPage";
+
+export function AppInitPage() {
+ const dark = useDarkStore((state) => state.dark);
+ const refreshStars = useDarkStore((state) => state.refreshStars);
+ const isLoading = useFlowsManagerStore((state) => state.isLoading);
+
+ const { isFetched } = useGetAutoLogin();
+ useGetVersionQuery({ enabled: isFetched });
+ useGetConfig({ enabled: isFetched });
+
+ useEffect(() => {
+ if (isFetched) {
+ refreshStars();
+ }
+ }, [isFetched]);
+
+ useEffect(() => {
+ if (!dark) {
+ document.getElementById("body")!.classList.remove("dark");
+ } else {
+ document.getElementById("body")!.classList.add("dark");
+ }
+ }, [dark]);
+
+ return (
+ //need parent component with width and height
+ <>
+ {(isLoading || !isFetched) && }
+ {isFetched && }
+ >
+ );
+}
diff --git a/src/frontend/src/pages/AppWrapperPage/index.tsx b/src/frontend/src/pages/AppWrapperPage/index.tsx
index 65c16df8d..e1a0c7a93 100644
--- a/src/frontend/src/pages/AppWrapperPage/index.tsx
+++ b/src/frontend/src/pages/AppWrapperPage/index.tsx
@@ -1,7 +1,6 @@
import AlertDisplayArea from "@/alerts/displayArea";
import CrashErrorComponent from "@/components/crashErrorComponent";
import FetchErrorComponent from "@/components/fetchErrorComponent";
-import LoadingComponent from "@/components/loadingComponent";
import TimeoutErrorComponent from "@/components/timeoutErrorComponent";
import {
FETCH_ERROR_DESCRIPION,
@@ -12,15 +11,12 @@ import {
import { useGetHealthQuery } from "@/controllers/API/queries/health";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useUtilityStore } from "@/stores/utilityStore";
-import { cn } from "@/utils/utils";
import { AxiosError } from "axios";
import { useEffect, useMemo, useState } from "react";
import { ErrorBoundary } from "react-error-boundary";
import { Outlet } from "react-router-dom";
export function AppWrapperPage() {
- const isLoading = useFlowsManagerStore((state) => state.isLoading);
-
const healthCheckMaxRetries = useFlowsManagerStore(
(state) => state.healthCheckMaxRetries,
);
@@ -108,15 +104,6 @@ export function AppWrapperPage() {
>
<>
{modalErrorComponent}
-
-
-
-
>
diff --git a/src/frontend/src/pages/LoadingPage/index.tsx b/src/frontend/src/pages/LoadingPage/index.tsx
new file mode 100644
index 000000000..8f2e9e858
--- /dev/null
+++ b/src/frontend/src/pages/LoadingPage/index.tsx
@@ -0,0 +1,15 @@
+import LoadingComponent from "@/components/loadingComponent";
+import { cn } from "@/utils/utils";
+
+export function LoadingPage({ overlay = false }: { overlay?: boolean }) {
+ return (
+
+
+
+ );
+}
diff --git a/src/frontend/src/pages/LoginPage/index.tsx b/src/frontend/src/pages/LoginPage/index.tsx
index 52f2b5747..8059a2bf0 100644
--- a/src/frontend/src/pages/LoginPage/index.tsx
+++ b/src/frontend/src/pages/LoginPage/index.tsx
@@ -1,5 +1,4 @@
import { useLoginUser } from "@/controllers/API/queries/auth";
-import { useFolderStore } from "@/stores/foldersStore";
import * as Form from "@radix-ui/react-form";
import { useContext, useState } from "react";
import { Link } from "react-router-dom";
@@ -23,7 +22,6 @@ export default function LoginPage(): JSX.Element {
const { password, username } = inputState;
const { login } = useContext(AuthContext);
const setErrorData = useAlertStore((state) => state.setErrorData);
- const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder);
function handleInput({
target: { name, value },
@@ -41,8 +39,6 @@ export default function LoginPage(): JSX.Element {
mutate(user, {
onSuccess: (data) => {
- setSelectedFolder(null);
-
login(data.access_token, "login", data.refresh_token);
},
onError: (error) => {
diff --git a/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx b/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx
index d2718465f..ab6b72bdb 100644
--- a/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx
+++ b/src/frontend/src/pages/MainPage/components/componentsComponent/index.tsx
@@ -1,6 +1,4 @@
import { usePostDownloadMultipleFlows } from "@/controllers/API/queries/flows";
-import { useGetFolderQuery } from "@/controllers/API/queries/folders/use-get-folder";
-import { useGetFoldersQuery } from "@/controllers/API/queries/folders/use-get-folders";
import useDeleteFlow from "@/hooks/flows/use-delete-flow";
import { useEffect, useMemo, useState } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
@@ -13,6 +11,7 @@ import useAlertStore from "../../../../stores/alertStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
import { useFolderStore } from "../../../../stores/foldersStore";
import { FlowType } from "../../../../types/flow";
+import { FolderType } from "../../entities";
import useFileDrop from "../../hooks/use-on-file-drop";
import { getNameByType } from "../../utils/get-name-by-type";
import { sortFlows } from "../../utils/sort-flows";
@@ -28,11 +27,13 @@ import useSelectedFlows from "./hooks/use-selected-flows";
export default function ComponentsComponent({
type = "all",
+ currentFolder,
+ isLoading,
}: {
type?: string;
+ currentFolder?: FolderType;
+ isLoading: boolean;
}) {
- const isLoading = useFlowsManagerStore((state) => state.isLoading);
-
const { folderId } = useParams();
const setSuccessData = useAlertStore((state) => state.setSuccessData);
@@ -51,10 +52,6 @@ export default function ComponentsComponent({
);
const myCollectionId = useFolderStore((state) => state.myCollectionId);
- const { data: currentFolder, isLoading: isLoadingCurrentFolder } =
- useGetFolderQuery({
- id: folderId ?? myCollectionId ?? "",
- });
const flowsFromFolder = currentFolder?.flows ?? [];
const [filteredFlows, setFilteredFlows] =
@@ -71,10 +68,6 @@ export default function ComponentsComponent({
const name = getNameByType(type);
- const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder);
-
- const { isLoading: isLoadingFolders } = useGetFoldersQuery();
-
const [shouldSelectAll, setShouldSelectAll] = useState(true);
const cardTypes = useMemo(() => {
@@ -188,7 +181,6 @@ export default function ComponentsComponent({
const handleDeleteMultiple = () => {
deleteFlow({ id: selectedFlowsComponentsCards })
.then(() => {
- setSelectedFolder(null);
resetFilter();
setSelectedFlowsComponentsCards([]);
handleSelectAll(false);
@@ -218,12 +210,7 @@ export default function ComponentsComponent({
<>
handleSelectOptionsChange("delete")}
@@ -243,16 +230,11 @@ export default function ComponentsComponent({
data-testid="cards-wrapper"
>
- {!isLoading &&
- !isLoadingFolders &&
- !isLoadingCurrentFolder &&
- data?.length === 0 ? (
+ {!isLoading && data?.length === 0 ? (
) : (
- {data?.length > 0 &&
- isLoadingFolders === false &&
- isLoadingCurrentFolder === false ? (
+ {data?.length > 0 && isLoading === false ? (
<>
{data?.map((item) => (
diff --git a/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx b/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx
index c6ceb44c7..fba02db16 100644
--- a/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx
+++ b/src/frontend/src/pages/MainPage/components/myCollectionComponent/components/headerTabsSearchComponent/index.tsx
@@ -3,10 +3,13 @@ import useFlowsManagerStore from "../../../../../../stores/flowsManagerStore";
import InputSearchComponent from "../inputSearchComponent";
import TabsSearchComponent from "../tabsComponent";
-type HeaderTabsSearchComponentProps = {};
+type HeaderTabsSearchComponentProps = {
+ loading: boolean;
+};
-const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => {
- const isLoading = useFlowsManagerStore((state) => state.isLoading);
+const HeaderTabsSearchComponent = ({
+ loading,
+}: HeaderTabsSearchComponentProps) => {
const [tabActive, setTabActive] = useState("Flows");
const [inputValue, setInputValue] = useState("");
@@ -18,7 +21,7 @@ const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => {
<>
{
setSearchFlowsComponents(e.target.value);
@@ -33,7 +36,7 @@ const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => {
diff --git a/src/frontend/src/pages/MainPage/components/myCollectionComponent/index.tsx b/src/frontend/src/pages/MainPage/components/myCollectionComponent/index.tsx
index c58e1cd12..eddc2481f 100644
--- a/src/frontend/src/pages/MainPage/components/myCollectionComponent/index.tsx
+++ b/src/frontend/src/pages/MainPage/components/myCollectionComponent/index.tsx
@@ -1,3 +1,7 @@
+import { useGetFolderQuery } from "@/controllers/API/queries/folders/use-get-folder";
+import { useGetFoldersQuery } from "@/controllers/API/queries/folders/use-get-folders";
+import { useFolderStore } from "@/stores/foldersStore";
+import { useParams } from "react-router-dom";
import ComponentsComponent from "../componentsComponent";
import HeaderTabsSearchComponent from "./components/headerTabsSearchComponent";
@@ -6,11 +10,24 @@ type MyCollectionComponentProps = {
};
const MyCollectionComponent = ({ type }: MyCollectionComponentProps) => {
+ const { folderId } = useParams();
+ const myCollectionId = useFolderStore((state) => state.myCollectionId);
+
+ const { data, isLoading } = useGetFolderQuery({
+ id: folderId ?? myCollectionId ?? "",
+ });
+ const { isLoading: isLoadingFolders } = useGetFoldersQuery();
+
return (
<>
-
+
-
+
>
);
diff --git a/src/frontend/src/pages/Playground/index.tsx b/src/frontend/src/pages/Playground/index.tsx
index 09b4ae8c3..81155b7b3 100644
--- a/src/frontend/src/pages/Playground/index.tsx
+++ b/src/frontend/src/pages/Playground/index.tsx
@@ -4,7 +4,6 @@ import { useStoreStore } from "@/stores/storeStore";
import { useTypesStore } from "@/stores/typesStore";
import { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
-import LoadingComponent from "../../components/loadingComponent";
import { getComponent } from "../../controllers/API";
import IOModal from "../../modals/IOModal";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
@@ -59,11 +58,7 @@ export default function PlaygroundPage() {
return (
- {!currentSavedFlow ? (
-
-
-
- ) : (
+ {currentSavedFlow && (
{}} isPlayground>
<>>
diff --git a/src/frontend/src/routes.tsx b/src/frontend/src/routes.tsx
index 8626998c9..ded7e48bd 100644
--- a/src/frontend/src/routes.tsx
+++ b/src/frontend/src/routes.tsx
@@ -11,6 +11,8 @@ import { ProtectedRoute } from "./components/authGuard";
import { ProtectedLoginRoute } from "./components/authLoginGuard";
import { AuthSettingsGuard } from "./components/authSettingsGuard";
import { StoreGuard } from "./components/storeGuard";
+import ContextWrapper from "./contexts";
+import { AppInitPage } from "./pages/AppInitPage";
import { AppWrapperPage } from "./pages/AppWrapperPage";
import { DashboardWrapperPage } from "./pages/DashboardWrapperPage";
import FlowPage from "./pages/FlowPage";
@@ -35,129 +37,146 @@ const PlaygroundPage = lazy(() => import("./pages/Playground"));
const SignUp = lazy(() => import("./pages/SignUpPage"));
const router = createBrowserRouter(
createRoutesFromElements([
-
}>
-
-
-
- }
- >
- }>
- }>
- } />
- }
- >
+
+
+
+ }
+ >
+ }>
+ }>
+
+
+
+ }
+ >
+ }>
+ }>
+ } />
+ }
+ >
+ }
+ />
+
+
+ }
+ >
+
+ }
+ />
+
+ }
+ >
+ }
+ />
+
+
+
}>
+
} />
+
}
+ />
+
} />
+
+
+
+ }
+ />
+ } />
+ } />
+
}
- />
-
-
- }
- >
-
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+ }>
+
+
+
+
}
/>
-
}
- >
-
}
- />
+
+ }>
+ } />
+ } />
+
+
} />
+
+
+ } />
-
}>
-
} />
-
} />
-
} />
-
-
-
- }
- />
- } />
- } />
-
-
-
+
+
+
}
/>
-
-
+
+
+
}
/>
-
- }>
-
-
-
+
+
+
}
/>
-
-
- }>
- } />
- } />
-
- } />
-
-
- } />
+ } />
-
-
-
- }
- />
-
-
-
- }
- />
-
-
-
- }
- />
- } />
,
]),
);
diff --git a/src/frontend/src/stores/alertStore.ts b/src/frontend/src/stores/alertStore.ts
index 03a12050c..027b706a1 100644
--- a/src/frontend/src/stores/alertStore.ts
+++ b/src/frontend/src/stores/alertStore.ts
@@ -19,7 +19,6 @@ const useAlertStore = create((set, get) => ({
notificationCenter: false,
notificationList: [],
tempNotificationList: [],
- loading: true,
setErrorData: (newState: { title: string; list?: Array }) => {
if (newState.title && newState.title !== "") {
set({
@@ -151,9 +150,6 @@ const useAlertStore = create((set, get) => ({
),
});
},
- setLoading: (newState: boolean) => {
- set({ loading: newState });
- },
clearTempNotificationList: () => {
set({ tempNotificationList: [] });
},
diff --git a/src/frontend/src/stores/authStore.ts b/src/frontend/src/stores/authStore.ts
index 85fb717bc..c821baf85 100644
--- a/src/frontend/src/stores/authStore.ts
+++ b/src/frontend/src/stores/authStore.ts
@@ -10,7 +10,7 @@ const useAuthStore = create((set, get) => ({
isAuthenticated: !!cookies.get(LANGFLOW_ACCESS_TOKEN),
accessToken: cookies.get(LANGFLOW_ACCESS_TOKEN) ?? null,
userData: null,
- autoLogin: false,
+ autoLogin: null,
apiKey: cookies.get("apikey_tkn_lflw"),
authenticationErrorCount: 0,
diff --git a/src/frontend/src/stores/foldersStore.tsx b/src/frontend/src/stores/foldersStore.tsx
index 9c8b2a73c..05b281597 100644
--- a/src/frontend/src/stores/foldersStore.tsx
+++ b/src/frontend/src/stores/foldersStore.tsx
@@ -2,8 +2,6 @@ import { create } from "zustand";
import { FoldersStoreType } from "../types/zustand/folders";
export const useFolderStore = create((set, get) => ({
- selectedFolder: null,
- setSelectedFolder: (folder) => set(() => ({ selectedFolder: folder })),
loadingById: false,
setMyCollectionId: (myCollectionId) => {
set({ myCollectionId });
diff --git a/src/frontend/src/types/api/index.ts b/src/frontend/src/types/api/index.ts
index 34969c6ad..63cde21cb 100644
--- a/src/frontend/src/types/api/index.ts
+++ b/src/frontend/src/types/api/index.ts
@@ -248,7 +248,6 @@ export type ResponseErrorDetailAPI = {
};
export type useQueryFunctionType = T extends undefined
? (
- params?: T,
options?: Omit,
) => UseQueryResult
: (
diff --git a/src/frontend/src/types/zustand/alert/index.ts b/src/frontend/src/types/zustand/alert/index.ts
index 7b2837517..40ce29631 100644
--- a/src/frontend/src/types/zustand/alert/index.ts
+++ b/src/frontend/src/types/zustand/alert/index.ts
@@ -15,6 +15,4 @@ export type AlertStoreType = {
removeFromTempNotificationList: (index: string) => void;
clearNotificationList: () => void;
removeFromNotificationList: (index: string) => void;
- loading: boolean;
- setLoading: (newState: boolean) => void;
};
diff --git a/src/frontend/src/types/zustand/auth/index.ts b/src/frontend/src/types/zustand/auth/index.ts
index 70bbb131c..0257563b7 100644
--- a/src/frontend/src/types/zustand/auth/index.ts
+++ b/src/frontend/src/types/zustand/auth/index.ts
@@ -5,7 +5,7 @@ export interface AuthStoreType {
isAuthenticated: boolean;
accessToken: string | null;
userData: Users | null;
- autoLogin: boolean;
+ autoLogin: boolean | null;
apiKey: string | null;
authenticationErrorCount: number;
diff --git a/src/frontend/src/types/zustand/folders/index.ts b/src/frontend/src/types/zustand/folders/index.ts
index e0e809e9d..39755f0fb 100644
--- a/src/frontend/src/types/zustand/folders/index.ts
+++ b/src/frontend/src/types/zustand/folders/index.ts
@@ -1,8 +1,6 @@
import { FolderType } from "../../../pages/MainPage/entities";
export type FoldersStoreType = {
- selectedFolder: FolderType | null;
- setSelectedFolder: (folder: FolderType | null) => void;
myCollectionId: string | null;
setMyCollectionId: (value: string) => void;
folderToEdit: FolderType | null;
diff --git a/src/frontend/tests/end-to-end/folders.spec.ts b/src/frontend/tests/end-to-end/folders.spec.ts
index 18ce4d8e4..dc3ea3622 100644
--- a/src/frontend/tests/end-to-end/folders.spec.ts
+++ b/src/frontend/tests/end-to-end/folders.spec.ts
@@ -1,4 +1,4 @@
-import { test } from "@playwright/test";
+import { expect, test } from "@playwright/test";
import { readFileSync } from "fs";
test("CRUD folders", async ({ page }) => {
@@ -71,7 +71,7 @@ test("CRUD folders", async ({ page }) => {
await page.getByText("Folder deleted successfully").isVisible();
});
-test("add folder by drag and drop", async ({ page }) => {
+test("add a flow into a folder by drag and drop", async ({ page }) => {
await page.goto("/");
await page.waitForSelector("text=my collection", {
@@ -84,10 +84,10 @@ test("add folder by drag and drop", async ({ page }) => {
);
// Wait for the target element to be available before evaluation
- await page.waitForSelector(
- '//*[@id="root"]/div/div[2]/div[2]/div[3]/aside/nav/div/div[2]',
- );
+ await page.waitForSelector('[data-testid="sidebar-nav-My Projects"]', {
+ timeout: 100000,
+ });
// Create the DataTransfer and File
const dataTransfer = await page.evaluateHandle((data) => {
const dt = new DataTransfer();
@@ -100,15 +100,34 @@ test("add folder by drag and drop", async ({ page }) => {
}, jsonContent);
// Now dispatch
- await page.dispatchEvent(
- '//*[@id="root"]/div/div[2]/div[2]/div[3]/aside/nav/div/div[2]',
- "drop",
- {
- dataTransfer,
- },
- );
+ await page.getByTestId("sidebar-nav-My Projects").dispatchEvent("drop", {
+ dataTransfer,
+ });
- await page.getByText("Getting Started").first().isVisible();
+ await page.waitForTimeout(3000);
+
+ const genericNode = page.getByTestId("div-generic-node");
+ const elementCount = await genericNode?.count();
+ if (elementCount > 0) {
+ expect(true).toBeTruthy();
+ }
+
+ await page.getByTestId("sidebar-nav-My Projects").click();
+
+ await page.waitForTimeout(1000);
+
+ expect(
+ await page.locator("text=Getting Started:").last().isVisible(),
+ ).toBeTruthy();
+ expect(
+ await page.locator("text=Inquisitive Pike").last().isVisible(),
+ ).toBeTruthy();
+ expect(
+ await page.locator("text=Dreamy Bassi").last().isVisible(),
+ ).toBeTruthy();
+ expect(
+ await page.locator("text=Furious Faraday").last().isVisible(),
+ ).toBeTruthy();
});
test("change flow folder", async ({ page }) => {
diff --git a/src/frontend/tests/scheduled-end-to-end/actionsMainPage-shard-0.spec.ts b/src/frontend/tests/scheduled-end-to-end/actionsMainPage-shard-0.spec.ts
index e93be566b..bec55a35b 100644
--- a/src/frontend/tests/scheduled-end-to-end/actionsMainPage-shard-0.spec.ts
+++ b/src/frontend/tests/scheduled-end-to-end/actionsMainPage-shard-0.spec.ts
@@ -162,6 +162,12 @@ test("user should be able to duplicate a flow or a component", async ({
await page.getByText("Exit", { exact: true }).click();
}
+ const replaceButton = await page.getByTestId("replace-button").isVisible();
+
+ if (replaceButton) {
+ await page.getByTestId("replace-button").click();
+ }
+
await page.getByTestId("icon-ChevronLeft").last().click();
await page.getByRole("checkbox").nth(1).click();
diff --git a/src/frontend/tests/scheduled-end-to-end/auto-save-off.spec.ts b/src/frontend/tests/scheduled-end-to-end/auto-save-off.spec.ts
index 51030dba4..8101c0a3e 100644
--- a/src/frontend/tests/scheduled-end-to-end/auto-save-off.spec.ts
+++ b/src/frontend/tests/scheduled-end-to-end/auto-save-off.spec.ts
@@ -22,11 +22,11 @@ test("user should be able to manually save a flow when the auto_save is off", as
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
await page.waitForSelector('[data-testid="mainpage_title"]', {
- timeout: 30000,
+ timeout: 5000,
});
await page.waitForSelector('[id="new-project-btn"]', {
- timeout: 30000,
+ timeout: 5000,
});
let modalCount = 0;
@@ -46,12 +46,12 @@ test("user should be able to manually save a flow when the auto_save is off", as
}
await page.waitForSelector('[data-testid="blank-flow"]', {
- timeout: 30000,
+ timeout: 5000,
});
await page.getByTestId("blank-flow").click();
await page.waitForSelector('[data-testid="extended-disclosure"]', {
- timeout: 30000,
+ timeout: 5000,
});
await page.getByPlaceholder("Search").click();
@@ -66,7 +66,7 @@ test("user should be able to manually save a flow when the auto_save is off", as
await page.mouse.down();
await page.waitForSelector('[title="fit view"]', {
- timeout: 100000,
+ timeout: 5000,
});
await page.getByTitle("fit view").click();
@@ -77,7 +77,7 @@ test("user should be able to manually save a flow when the auto_save is off", as
await page.waitForSelector("text=loading", {
state: "hidden",
- timeout: 100000,
+ timeout: 5000,
});
await page.getByTestId("icon-ChevronLeft").last().click();
@@ -93,7 +93,7 @@ test("user should be able to manually save a flow when the auto_save is off", as
await page.getByText("Untitled document").first().click();
await page.waitForSelector('[data-testid="icon-ChevronLeft"]', {
- timeout: 100000,
+ timeout: 5000,
});
expect(await page.getByText("NVIDIA").isVisible()).toBeFalsy();
@@ -110,7 +110,7 @@ test("user should be able to manually save a flow when the auto_save is off", as
await page.mouse.down();
await page.waitForSelector('[title="fit view"]', {
- timeout: 100000,
+ timeout: 5000,
});
await page.getByTitle("fit view").click();
@@ -123,7 +123,7 @@ test("user should be able to manually save a flow when the auto_save is off", as
await page.waitForSelector("text=loading", {
state: "hidden",
- timeout: 100000,
+ timeout: 5000,
});
await page.waitForTimeout(5000);
@@ -142,7 +142,7 @@ test("user should be able to manually save a flow when the auto_save is off", as
await page.mouse.down();
await page.waitForSelector('[title="fit view"]', {
- timeout: 100000,
+ timeout: 5000,
});
await page.getByTitle("fit view").click();
@@ -150,10 +150,25 @@ test("user should be able to manually save a flow when the auto_save is off", as
await page.getByTestId("save-flow-button").click();
await page.getByTestId("icon-ChevronLeft").last().click();
+ const replaceButton = await page.getByTestId("replace-button").isVisible();
+
+ if (replaceButton) {
+ await page.getByTestId("replace-button").click();
+ }
+
+ const saveExitButton = await page
+ .getByText("Save And Exit", { exact: true })
+ .last()
+ .isVisible();
+
+ if (saveExitButton) {
+ await page.getByText("Save And Exit", { exact: true }).last().click();
+ }
+
await page.getByText("Untitled document").first().click();
await page.waitForSelector('[data-testid="icon-ChevronLeft"]', {
- timeout: 100000,
+ timeout: 5000,
});
await page.waitForTimeout(5000);
diff --git a/src/frontend/tests/scheduled-end-to-end/dragAndDrop.spec.ts b/src/frontend/tests/scheduled-end-to-end/dragAndDrop.spec.ts
index d308f313c..2c539b25a 100644
--- a/src/frontend/tests/scheduled-end-to-end/dragAndDrop.spec.ts
+++ b/src/frontend/tests/scheduled-end-to-end/dragAndDrop.spec.ts
@@ -42,18 +42,29 @@ test.describe("drag and drop test", () => {
}, jsonContent);
// Now dispatch
- await page.dispatchEvent(
- '//*[@id="root"]/div/div[2]/div[2]/div[3]/div',
- "drop",
- {
- dataTransfer,
- },
- );
+ await page.getByTestId("cards-wrapper").dispatchEvent("drop", {
+ dataTransfer,
+ });
+
+ await page.waitForTimeout(3000);
const genericNode = page.getByTestId("div-generic-node");
const elementCount = await genericNode?.count();
if (elementCount > 0) {
expect(true).toBeTruthy();
}
+
+ expect(
+ await page.locator("text=Getting Started:").last().isVisible(),
+ ).toBeTruthy();
+ expect(
+ await page.locator("text=Inquisitive Pike").last().isVisible(),
+ ).toBeTruthy();
+ expect(
+ await page.locator("text=Dreamy Bassi").last().isVisible(),
+ ).toBeTruthy();
+ expect(
+ await page.locator("text=Furious Faraday").last().isVisible(),
+ ).toBeTruthy();
});
});
diff --git a/src/frontend/tests/scheduled-end-to-end/store-shard-3.spec.ts b/src/frontend/tests/scheduled-end-to-end/store-shard-3.spec.ts
index c1cf62667..17225759e 100644
--- a/src/frontend/tests/scheduled-end-to-end/store-shard-3.spec.ts
+++ b/src/frontend/tests/scheduled-end-to-end/store-shard-3.spec.ts
@@ -96,12 +96,12 @@ test("should filter by type", async ({ page }) => {
expect(toyBrick).not.toBe(0);
await page.getByTestId("all-button-store").click();
- await page.waitForTimeout(8000);
+ await page.waitForTimeout(10000);
let iconGroupAllCount = await page.getByTestId("icon-Group")?.count();
- await page.waitForTimeout(1000);
+ await page.waitForTimeout(5000);
let toyBrickAllCount = await page.getByTestId("icon-ToyBrick")?.count();
- await page.waitForTimeout(1000);
+ await page.waitForTimeout(5000);
if (iconGroupAllCount === 0 || toyBrickAllCount === 0) {
expect(false).toBe(true);