refactor: routes and back button (#3600)
* Changed ProtectedRoute to handle refresh token * Created DashboardWrapperPage to insert header into elements * Changed routes to be contained by only one ProtectedRoute * Removed refresh and get version query of App.tsx * Added loading if user not authenticated in ProtectedAdminRoute * Changed page layout to not contain header * Changed AdminPage and FlowPage to not have headers * Removed unused variables * Refactored redirectToLastLocation of headerComponent * Removed unused track last visited path * changed viewPage to not set onFlowPage since it's used only on header * Added flow fetching into Playground page * Fixed back button not working between flows * Changed duplicate requests to show which request failed * Refactored useGetBuilds to remove duplicated request * Re-added get version query and config query * [autofix.ci] apply automated fixes * Fix tests that rely on autosave delay --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
ffff5720d8
commit
d6537b724e
19 changed files with 221 additions and 282 deletions
|
|
@ -3,10 +3,6 @@ import { Cookies } from "react-cookie";
|
||||||
import { RouterProvider } from "react-router-dom";
|
import { RouterProvider } from "react-router-dom";
|
||||||
import "reactflow/dist/style.css";
|
import "reactflow/dist/style.css";
|
||||||
import LoadingComponent from "./components/loadingComponent";
|
import LoadingComponent from "./components/loadingComponent";
|
||||||
import {
|
|
||||||
LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS,
|
|
||||||
LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV,
|
|
||||||
} from "./constants/constants";
|
|
||||||
import { AuthContext } from "./contexts/authContext";
|
import { AuthContext } from "./contexts/authContext";
|
||||||
import {
|
import {
|
||||||
useAutoLogin,
|
useAutoLogin,
|
||||||
|
|
@ -34,8 +30,6 @@ export default function App() {
|
||||||
|
|
||||||
const { mutate: mutateAutoLogin } = useAutoLogin();
|
const { mutate: mutateAutoLogin } = useAutoLogin();
|
||||||
|
|
||||||
useGetVersionQuery();
|
|
||||||
|
|
||||||
const { mutate: mutateRefresh } = useRefreshAccessToken();
|
const { mutate: mutateRefresh } = useRefreshAccessToken();
|
||||||
|
|
||||||
const isLoginPage = location.pathname.includes("login");
|
const isLoginPage = location.pathname.includes("login");
|
||||||
|
|
@ -79,23 +73,7 @@ export default function App() {
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useGetVersionQuery();
|
||||||
const envRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV;
|
|
||||||
const automaticRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS;
|
|
||||||
|
|
||||||
const accessTokenTimer = isNaN(envRefreshTime)
|
|
||||||
? automaticRefreshTime
|
|
||||||
: envRefreshTime;
|
|
||||||
|
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
if (isAuthenticated && !isLoginPage) {
|
|
||||||
mutateRefresh({ refresh_token: refreshToken });
|
|
||||||
}
|
|
||||||
}, accessTokenTimer * 1000);
|
|
||||||
|
|
||||||
return () => clearInterval(intervalId);
|
|
||||||
}, [isLoginPage]);
|
|
||||||
|
|
||||||
useSaveConfig();
|
useSaveConfig();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,20 @@ import useAuthStore from "@/stores/authStore";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import { Navigate } from "react-router-dom";
|
import { Navigate } from "react-router-dom";
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
|
import LoadingComponent from "../loadingComponent";
|
||||||
|
|
||||||
export const ProtectedAdminRoute = ({ children }) => {
|
export const ProtectedAdminRoute = ({ children }) => {
|
||||||
const { userData } = useContext(AuthContext);
|
const { userData } = useContext(AuthContext);
|
||||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||||
const autoLogin = useAuthStore((state) => state.autoLogin);
|
const autoLogin = useAuthStore((state) => state.autoLogin);
|
||||||
const isAdmin = useAuthStore((state) => state.isAdmin);
|
const isAdmin = useAuthStore((state) => state.isAdmin);
|
||||||
const isLoginPage = location.pathname.includes("login");
|
|
||||||
const logout = useAuthStore((state) => state.logout);
|
|
||||||
|
|
||||||
if (!isAuthenticated && !isLoginPage) {
|
if (!isAuthenticated) {
|
||||||
logout();
|
return (
|
||||||
|
<div className="flex h-screen w-screen items-center justify-center">
|
||||||
|
<LoadingComponent remSize={30} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
} else if ((userData && !isAdmin) || autoLogin) {
|
} else if ((userData && !isAdmin) || autoLogin) {
|
||||||
return <Navigate to="/" replace />;
|
return <Navigate to="/" replace />;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,40 @@
|
||||||
import { LANGFLOW_AUTO_LOGIN_OPTION } from "@/constants/constants";
|
import {
|
||||||
|
LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||||
|
LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV,
|
||||||
|
LANGFLOW_AUTO_LOGIN_OPTION,
|
||||||
|
} from "@/constants/constants";
|
||||||
|
import { useRefreshAccessToken } from "@/controllers/API/queries/auth";
|
||||||
import useAuthStore from "@/stores/authStore";
|
import useAuthStore from "@/stores/authStore";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { Cookies } from "react-cookie";
|
||||||
|
|
||||||
export const ProtectedRoute = ({ children }) => {
|
export const ProtectedRoute = ({ children }) => {
|
||||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||||
const hasToken = !!localStorage.getItem(LANGFLOW_AUTO_LOGIN_OPTION);
|
const hasToken = !!localStorage.getItem(LANGFLOW_AUTO_LOGIN_OPTION);
|
||||||
const isLoginPage = location.pathname.includes("login");
|
|
||||||
const logout = useAuthStore((state) => state.logout);
|
const logout = useAuthStore((state) => state.logout);
|
||||||
|
|
||||||
if (!isAuthenticated && hasToken && !isLoginPage) {
|
const cookies = new Cookies();
|
||||||
|
const refreshToken = cookies.get("refresh_token");
|
||||||
|
const { mutate: mutateRefresh } = useRefreshAccessToken();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const envRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV;
|
||||||
|
const automaticRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS;
|
||||||
|
|
||||||
|
const accessTokenTimer = isNaN(envRefreshTime)
|
||||||
|
? automaticRefreshTime
|
||||||
|
: envRefreshTime;
|
||||||
|
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
if (isAuthenticated) {
|
||||||
|
mutateRefresh({ refresh_token: refreshToken });
|
||||||
|
}
|
||||||
|
}, accessTokenTimer * 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!isAuthenticated && hasToken) {
|
||||||
logout();
|
logout();
|
||||||
} else {
|
} else {
|
||||||
return children;
|
return children;
|
||||||
|
|
|
||||||
|
|
@ -34,11 +34,6 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
const addFlow = useAddFlow();
|
const addFlow = useAddFlow();
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||||
const setLockChat = useFlowStore((state) => state.setLockChat);
|
|
||||||
const setIsBuilding = useFlowStore((state) => state.setIsBuilding);
|
|
||||||
const revertBuiltStatusFromBuilding = useFlowStore(
|
|
||||||
(state) => state.revertBuiltStatusFromBuilding,
|
|
||||||
);
|
|
||||||
const undo = useFlowsManagerStore((state) => state.undo);
|
const undo = useFlowsManagerStore((state) => state.undo);
|
||||||
const redo = useFlowsManagerStore((state) => state.redo);
|
const redo = useFlowsManagerStore((state) => state.redo);
|
||||||
const saveLoading = useFlowsManagerStore((state) => state.saveLoading);
|
const saveLoading = useFlowsManagerStore((state) => state.saveLoading);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import { useLogout } from "@/controllers/API/queries/auth";
|
||||||
import useAuthStore from "@/stores/authStore";
|
import useAuthStore from "@/stores/authStore";
|
||||||
import useAlertStore from "../../stores/alertStore";
|
import useAlertStore from "../../stores/alertStore";
|
||||||
import { useDarkStore } from "../../stores/darkStore";
|
import { useDarkStore } from "../../stores/darkStore";
|
||||||
import { useLocationStore } from "../../stores/locationStore";
|
|
||||||
import { useStoreStore } from "../../stores/storeStore";
|
import { useStoreStore } from "../../stores/storeStore";
|
||||||
import IconComponent, { ForwardedIconComponent } from "../genericIconComponent";
|
import IconComponent, { ForwardedIconComponent } from "../genericIconComponent";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
|
@ -48,29 +47,22 @@ export default function Header(): JSX.Element {
|
||||||
const setDark = useDarkStore((state) => state.setDark);
|
const setDark = useDarkStore((state) => state.setDark);
|
||||||
const stars = useDarkStore((state) => state.stars);
|
const stars = useDarkStore((state) => state.stars);
|
||||||
|
|
||||||
const routeHistory = useLocationStore((state) => state.routeHistory);
|
|
||||||
|
|
||||||
const profileImageUrl = `${BASE_URL_API}files/profile_pictures/${
|
const profileImageUrl = `${BASE_URL_API}files/profile_pictures/${
|
||||||
userData?.profile_image ?? "Space/046-rocket.svg"
|
userData?.profile_image ?? "Space/046-rocket.svg"
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const redirectToLastLocation = () => {
|
const redirectToLastLocation = () => {
|
||||||
const lastVisitedIndex = routeHistory
|
const canGoBack = location.key !== "default";
|
||||||
.reverse()
|
if (canGoBack) {
|
||||||
.findIndex((path) => path !== location.pathname);
|
navigate(-1);
|
||||||
|
} else {
|
||||||
const lastFlowVisited = routeHistory[lastVisitedIndex];
|
navigate("/", { replace: true });
|
||||||
lastFlowVisited ? navigate(lastFlowVisited) : navigate("/all");
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const visitedFlowPathBefore = () => {
|
const showArrowReturnIcon = LOCATIONS_TO_RETURN.some((path) =>
|
||||||
const last100VisitedPaths = routeHistory.slice(-99);
|
location.pathname.includes(path),
|
||||||
return last100VisitedPaths.some((path) => path.includes("/flow/"));
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const showArrowReturnIcon =
|
|
||||||
LOCATIONS_TO_RETURN.some((path) => location.pathname.includes(path)) &&
|
|
||||||
visitedFlowPathBefore();
|
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
mutationLogout(undefined, {
|
mutationLogout(undefined, {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import Header from "../headerComponent";
|
|
||||||
import { Separator } from "../ui/separator";
|
import { Separator } from "../ui/separator";
|
||||||
|
|
||||||
export default function PageLayout({
|
export default function PageLayout({
|
||||||
|
|
@ -15,8 +14,6 @@ export default function PageLayout({
|
||||||
betaIcon?: boolean;
|
betaIcon?: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen w-full flex-col">
|
|
||||||
<Header />
|
|
||||||
<div className="flex h-full w-full flex-col justify-between overflow-auto bg-background px-16">
|
<div className="flex h-full w-full flex-col justify-between overflow-auto bg-background px-16">
|
||||||
<div className="flex w-full items-center justify-between gap-4 space-y-0.5 py-8 pb-2">
|
<div className="flex w-full items-center justify-between gap-4 space-y-0.5 py-8 pb-2">
|
||||||
<div className="flex w-full flex-col">
|
<div className="flex w-full flex-col">
|
||||||
|
|
@ -34,6 +31,5 @@ export default function PageLayout({
|
||||||
<Separator className="my-6 flex" />
|
<Separator className="my-6 flex" />
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants";
|
import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants";
|
||||||
import useAuthStore from "@/stores/authStore";
|
import useAuthStore from "@/stores/authStore";
|
||||||
import { useUtilityStore } from "@/stores/utilityStore";
|
|
||||||
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios";
|
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios";
|
||||||
import { useContext, useEffect } from "react";
|
import { useContext, useEffect } from "react";
|
||||||
import { Cookies } from "react-cookie";
|
import { Cookies } from "react-cookie";
|
||||||
|
|
@ -89,13 +88,13 @@ function ApiInterceptor() {
|
||||||
// Request interceptor to add access token to every request
|
// Request interceptor to add access token to every request
|
||||||
const requestInterceptor = api.interceptors.request.use(
|
const requestInterceptor = api.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
const checkRequest = checkDuplicateRequestAndStoreRequest(config);
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
|
try {
|
||||||
if (!checkRequest) {
|
checkDuplicateRequestAndStoreRequest(config);
|
||||||
controller.abort("Duplicate Request");
|
} catch (e) {
|
||||||
console.error("Duplicate Request");
|
const error = e as Error;
|
||||||
|
controller.abort(error.message);
|
||||||
|
console.error(error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessToken = cookies.get(LANGFLOW_ACCESS_TOKEN);
|
const accessToken = cookies.get(LANGFLOW_ACCESS_TOKEN);
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,11 @@ export function checkDuplicateRequestAndStoreRequest(config) {
|
||||||
currentTime - parseInt(lastRequestTime, 10) < 300 &&
|
currentTime - parseInt(lastRequestTime, 10) < 300 &&
|
||||||
lastCurrentUrl === currentUrl
|
lastCurrentUrl === currentUrl
|
||||||
) {
|
) {
|
||||||
return false;
|
throw new Error("Duplicate request: " + lastUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem("lastUrlCalled", config.url ?? "");
|
localStorage.setItem("lastUrlCalled", config.url ?? "");
|
||||||
localStorage.setItem("lastMethodCalled", config.method ?? "");
|
localStorage.setItem("lastMethodCalled", config.method ?? "");
|
||||||
localStorage.setItem("lastRequestTime", currentTime.toString());
|
localStorage.setItem("lastRequestTime", currentTime.toString());
|
||||||
localStorage.setItem("lastCurrentUrl", currentUrl);
|
localStorage.setItem("lastCurrentUrl", currentUrl);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,35 +9,22 @@ import { UseRequestProcessor } from "../../services/request-processor";
|
||||||
|
|
||||||
interface BuildsQueryParams {
|
interface BuildsQueryParams {
|
||||||
flowId?: string;
|
flowId?: string;
|
||||||
nodeId?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useGetBuildsQuery: useQueryFunctionType<
|
export const useGetBuildsQuery: useQueryFunctionType<
|
||||||
BuildsQueryParams,
|
BuildsQueryParams,
|
||||||
AxiosResponse<{ vertex_builds: FlowPoolType }>
|
AxiosResponse<{ vertex_builds: FlowPoolType }>
|
||||||
> = ({}) => {
|
> = (params) => {
|
||||||
const { query } = UseRequestProcessor();
|
const { query } = UseRequestProcessor();
|
||||||
|
|
||||||
const setFlowPool = useFlowStore((state) => state.setFlowPool);
|
const setFlowPool = useFlowStore((state) => state.setFlowPool);
|
||||||
const currentFlow = useFlowStore((state) => state.currentFlow);
|
const currentFlow = useFlowStore((state) => state.currentFlow);
|
||||||
|
|
||||||
const getBuildsFn = async (
|
const responseFn = async () => {
|
||||||
params: BuildsQueryParams,
|
|
||||||
): Promise<AxiosResponse<{ vertex_builds: FlowPoolType }>> => {
|
|
||||||
const config = {};
|
const config = {};
|
||||||
config["params"] = { flow_id: params.flowId };
|
config["params"] = { flow_id: params.flowId };
|
||||||
|
|
||||||
if (params.nodeId) {
|
const response = await api.get<any>(`${getURL("BUILDS")}`, config);
|
||||||
config["params"] = { nodeId: params.nodeId };
|
|
||||||
}
|
|
||||||
|
|
||||||
return await api.get<any>(`${getURL("BUILDS")}`, config);
|
|
||||||
};
|
|
||||||
|
|
||||||
const responseFn = async () => {
|
|
||||||
const response = await getBuildsFn({
|
|
||||||
flowId: currentFlow!.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (currentFlow) {
|
if (currentFlow) {
|
||||||
const flowPool = response.data.vertex_builds;
|
const flowPool = response.data.vertex_builds;
|
||||||
|
|
@ -47,10 +34,14 @@ export const useGetBuildsQuery: useQueryFunctionType<
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryResult = query(["useGetBuildsQuery"], responseFn, {
|
const queryResult = query(
|
||||||
|
["useGetBuildsQuery", { key: params.flowId }],
|
||||||
|
responseFn,
|
||||||
|
{
|
||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return queryResult;
|
return queryResult;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import { useEffect } from "react";
|
|
||||||
import { useLocation } from "react-router-dom";
|
|
||||||
import { useLocationStore } from "../stores/locationStore";
|
|
||||||
|
|
||||||
function useTrackLastVisitedPath() {
|
|
||||||
const location = useLocation();
|
|
||||||
const setHistory = useLocationStore((state) => state.setRouteHistory);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setHistory(location.pathname);
|
|
||||||
}, [location]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useTrackLastVisitedPath;
|
|
||||||
|
|
@ -7,7 +7,6 @@ import {
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import { useContext, useEffect, useRef, useState } from "react";
|
import { useContext, useEffect, useRef, useState } from "react";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import Header from "../../components/headerComponent";
|
|
||||||
import LoadingComponent from "../../components/loadingComponent";
|
import LoadingComponent from "../../components/loadingComponent";
|
||||||
import PaginatorComponent from "../../components/paginatorComponent";
|
import PaginatorComponent from "../../components/paginatorComponent";
|
||||||
import ShadTooltip from "../../components/shadTooltipComponent";
|
import ShadTooltip from "../../components/shadTooltipComponent";
|
||||||
|
|
@ -246,7 +245,6 @@ export default function AdminPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
|
||||||
{userData && (
|
{userData && (
|
||||||
<div className="admin-page-panel flex h-full flex-col pb-8">
|
<div className="admin-page-panel flex h-full flex-col pb-8">
|
||||||
<div className="main-page-nav-arrangement">
|
<div className="main-page-nav-arrangement">
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import {
|
||||||
TIMEOUT_ERROR_MESSAGE,
|
TIMEOUT_ERROR_MESSAGE,
|
||||||
} from "@/constants/constants";
|
} from "@/constants/constants";
|
||||||
import { useGetHealthQuery } from "@/controllers/API/queries/health";
|
import { useGetHealthQuery } from "@/controllers/API/queries/health";
|
||||||
import useTrackLastVisitedPath from "@/hooks/use-track-last-visited-path";
|
|
||||||
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
||||||
import { useUtilityStore } from "@/stores/utilityStore";
|
import { useUtilityStore } from "@/stores/utilityStore";
|
||||||
import { cn } from "@/utils/utils";
|
import { cn } from "@/utils/utils";
|
||||||
|
|
@ -20,8 +19,6 @@ import { ErrorBoundary } from "react-error-boundary";
|
||||||
import { Outlet } from "react-router-dom";
|
import { Outlet } from "react-router-dom";
|
||||||
|
|
||||||
export function AppWrapperPage() {
|
export function AppWrapperPage() {
|
||||||
useTrackLastVisitedPath();
|
|
||||||
|
|
||||||
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
||||||
|
|
||||||
const healthCheckMaxRetries = useFlowsManagerStore(
|
const healthCheckMaxRetries = useFlowsManagerStore(
|
||||||
|
|
|
||||||
11
src/frontend/src/pages/DashboardWrapperPage/index.tsx
Normal file
11
src/frontend/src/pages/DashboardWrapperPage/index.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import Header from "@/components/headerComponent";
|
||||||
|
import { Outlet } from "react-router-dom";
|
||||||
|
|
||||||
|
export function DashboardWrapperPage() {
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen w-full flex-col">
|
||||||
|
<Header />
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -143,7 +143,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
|
||||||
};
|
};
|
||||||
}, [lastCopiedSelection, lastSelection, takeSnapshot, selectionMenuVisible]);
|
}, [lastCopiedSelection, lastSelection, takeSnapshot, selectionMenuVisible]);
|
||||||
|
|
||||||
const { isFetching, refetch } = useGetBuildsQuery({});
|
const { isFetching } = useGetBuildsQuery({ flowId: currentFlowId });
|
||||||
|
|
||||||
const showCanvas =
|
const showCanvas =
|
||||||
Object.keys(templates).length > 0 &&
|
Object.keys(templates).length > 0 &&
|
||||||
|
|
@ -151,8 +151,6 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
|
||||||
!isFetching;
|
!isFetching;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refetch();
|
|
||||||
useFlowStore.setState({ autoSaveFlow });
|
|
||||||
if (checkOldComponents({ nodes })) {
|
if (checkOldComponents({ nodes })) {
|
||||||
setNoticeData({
|
setNoticeData({
|
||||||
title:
|
title:
|
||||||
|
|
@ -161,6 +159,10 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
|
||||||
}
|
}
|
||||||
}, [currentFlowId]);
|
}, [currentFlowId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
useFlowStore.setState({ autoSaveFlow });
|
||||||
|
});
|
||||||
|
|
||||||
function handleUndo(e: KeyboardEvent) {
|
function handleUndo(e: KeyboardEvent) {
|
||||||
if (!isWrappedWithClass(e, "noflow")) {
|
if (!isWrappedWithClass(e, "noflow")) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import { customStringify } from "@/utils/reactflowUtils";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useBlocker, useNavigate, useParams } from "react-router-dom";
|
import { useBlocker, useNavigate, useParams } from "react-router-dom";
|
||||||
import FlowToolbar from "../../components/chatComponent";
|
import FlowToolbar from "../../components/chatComponent";
|
||||||
import Header from "../../components/headerComponent";
|
|
||||||
import { useDarkStore } from "../../stores/darkStore";
|
import { useDarkStore } from "../../stores/darkStore";
|
||||||
import useFlowStore from "../../stores/flowStore";
|
import useFlowStore from "../../stores/flowStore";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
|
|
@ -81,7 +80,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
awaitgetTypes();
|
awaitgetTypes();
|
||||||
}, [id, flows]);
|
}, [id, flows, currentFlowId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOnFlowPage(true);
|
setOnFlowPage(true);
|
||||||
|
|
@ -94,7 +93,6 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
|
||||||
<div className="flow-page-positioning">
|
<div className="flow-page-positioning">
|
||||||
{currentFlow && (
|
{currentFlow && (
|
||||||
<div className="flex h-full overflow-hidden">
|
<div className="flex h-full overflow-hidden">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
|
import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows";
|
||||||
|
import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
|
||||||
import { useStoreStore } from "@/stores/storeStore";
|
import { useStoreStore } from "@/stores/storeStore";
|
||||||
|
import { useTypesStore } from "@/stores/typesStore";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import LoadingComponent from "../../components/loadingComponent";
|
import LoadingComponent from "../../components/loadingComponent";
|
||||||
import { getComponent } from "../../controllers/API";
|
import { getComponent } from "../../controllers/API";
|
||||||
import IOModal from "../../modals/IOModal";
|
import IOModal from "../../modals/IOModal";
|
||||||
|
|
@ -8,7 +11,6 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
import cloneFLowWithParent from "../../utils/storeUtils";
|
import cloneFLowWithParent from "../../utils/storeUtils";
|
||||||
|
|
||||||
export default function PlaygroundPage() {
|
export default function PlaygroundPage() {
|
||||||
const getFlowById = useFlowsManagerStore((state) => state.getFlowById);
|
|
||||||
const flows = useFlowsManagerStore((state) => state.flows);
|
const flows = useFlowsManagerStore((state) => state.flows);
|
||||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||||
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
|
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||||
|
|
@ -21,19 +23,38 @@ export default function PlaygroundPage() {
|
||||||
return newFlow;
|
return newFlow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
useGetGlobalVariables();
|
||||||
|
|
||||||
|
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||||
|
const { mutateAsync: refreshFlows } = useGetRefreshFlows();
|
||||||
|
const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading);
|
||||||
|
const getTypes = useTypesStore((state) => state.getTypes);
|
||||||
|
|
||||||
// Set flow tab id
|
// Set flow tab id
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (flows) {
|
const awaitgetTypes = async () => {
|
||||||
const flow = getFlowById(id!);
|
if (flows && currentFlowId === "") {
|
||||||
if (flow) {
|
const isAnExistingFlow = flows.find((flow) => flow.id === id);
|
||||||
setCurrentFlow(flow);
|
|
||||||
} else {
|
if (!isAnExistingFlow) {
|
||||||
if (validApiKey)
|
if (validApiKey) {
|
||||||
getFlowData().then((flow) => {
|
getFlowData().then((flow) => {
|
||||||
setCurrentFlow(flow);
|
setCurrentFlow(flow);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
navigate("/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setCurrentFlow(isAnExistingFlow);
|
||||||
|
} else if (!flows) {
|
||||||
|
setIsLoading(true);
|
||||||
|
await refreshFlows(undefined);
|
||||||
|
await getTypes();
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
awaitgetTypes();
|
||||||
}, [id, flows, validApiKey]);
|
}, [id, flows, validApiKey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows";
|
import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows";
|
||||||
import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
|
import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
|
||||||
import useFlowStore from "@/stores/flowStore";
|
|
||||||
import { useTypesStore } from "@/stores/typesStore";
|
import { useTypesStore } from "@/stores/typesStore";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
|
@ -10,7 +9,6 @@ import Page from "../FlowPage/components/PageComponent";
|
||||||
export default function ViewPage() {
|
export default function ViewPage() {
|
||||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||||
|
|
||||||
const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage);
|
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
useGetGlobalVariables();
|
useGetGlobalVariables();
|
||||||
|
|
@ -43,15 +41,6 @@ export default function ViewPage() {
|
||||||
awaitgetTypes();
|
awaitgetTypes();
|
||||||
}, [id, flows]);
|
}, [id, flows]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setOnFlowPage(true);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
setOnFlowPage(false);
|
|
||||||
setCurrentFlow(undefined);
|
|
||||||
};
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flow-page-positioning">
|
<div className="flow-page-positioning">
|
||||||
<Page view />
|
<Page view />
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
import React, { lazy } from "react";
|
import { lazy } from "react";
|
||||||
import {
|
import {
|
||||||
createBrowserRouter,
|
createBrowserRouter,
|
||||||
createRoutesFromElements,
|
createRoutesFromElements,
|
||||||
Navigate,
|
Navigate,
|
||||||
|
Outlet,
|
||||||
Route,
|
Route,
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import { ProtectedAdminRoute } from "./components/authAdminGuard";
|
import { ProtectedAdminRoute } from "./components/authAdminGuard";
|
||||||
import { ProtectedRoute } from "./components/authGuard";
|
import { ProtectedRoute } from "./components/authGuard";
|
||||||
import { ProtectedLoginRoute } from "./components/authLoginGuard";
|
import { ProtectedLoginRoute } from "./components/authLoginGuard";
|
||||||
import { AuthSettingsGuard } from "./components/authSettingsGuard";
|
import { AuthSettingsGuard } from "./components/authSettingsGuard";
|
||||||
import { CatchAllRoute } from "./components/catchAllRoutes";
|
|
||||||
import { StoreGuard } from "./components/storeGuard";
|
import { StoreGuard } from "./components/storeGuard";
|
||||||
import { AppWrapperPage } from "./pages/AppWrapperPage";
|
import { AppWrapperPage } from "./pages/AppWrapperPage";
|
||||||
|
import { DashboardWrapperPage } from "./pages/DashboardWrapperPage";
|
||||||
import FlowPage from "./pages/FlowPage";
|
import FlowPage from "./pages/FlowPage";
|
||||||
import LoginPage from "./pages/LoginPage";
|
import LoginPage from "./pages/LoginPage";
|
||||||
import MyCollectionComponent from "./pages/MainPage/components/myCollectionComponent";
|
import MyCollectionComponent from "./pages/MainPage/components/myCollectionComponent";
|
||||||
|
|
@ -34,15 +35,17 @@ const PlaygroundPage = lazy(() => import("./pages/Playground"));
|
||||||
const SignUp = lazy(() => import("./pages/SignUpPage"));
|
const SignUp = lazy(() => import("./pages/SignUpPage"));
|
||||||
const router = createBrowserRouter(
|
const router = createBrowserRouter(
|
||||||
createRoutesFromElements([
|
createRoutesFromElements([
|
||||||
<Route path="" element={<AppWrapperPage />}>
|
<Route path="/" element={<AppWrapperPage />}>
|
||||||
<Route
|
<Route
|
||||||
path="/"
|
path=""
|
||||||
element={
|
element={
|
||||||
<ProtectedRoute>
|
<ProtectedRoute>
|
||||||
<HomePage />
|
<Outlet />
|
||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
<Route path="" element={<DashboardWrapperPage />}>
|
||||||
|
<Route path="" element={<HomePage />}>
|
||||||
<Route index element={<Navigate replace to={"all"} />} />
|
<Route index element={<Navigate replace to={"all"} />} />
|
||||||
<Route
|
<Route
|
||||||
path="flows/"
|
path="flows/"
|
||||||
|
|
@ -55,7 +58,9 @@ const router = createBrowserRouter(
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
<Route
|
||||||
path="components/"
|
path="components/"
|
||||||
element={<MyCollectionComponent key="components" type="component" />}
|
element={
|
||||||
|
<MyCollectionComponent key="components" type="component" />
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Route
|
<Route
|
||||||
path="folder/:folderId"
|
path="folder/:folderId"
|
||||||
|
|
@ -74,14 +79,7 @@ const router = createBrowserRouter(
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
<Route path="/settings" element={<SettingsPage />}>
|
||||||
path="/settings"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<SettingsPage />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Route index element={<Navigate replace to={"general"} />} />
|
<Route index element={<Navigate replace to={"general"} />} />
|
||||||
<Route path="global-variables" element={<GlobalVariablesPage />} />
|
<Route path="global-variables" element={<GlobalVariablesPage />} />
|
||||||
<Route path="api-keys" element={<ApiKeysPage />} />
|
<Route path="api-keys" element={<ApiKeysPage />} />
|
||||||
|
|
@ -99,67 +97,42 @@ const router = createBrowserRouter(
|
||||||
<Route
|
<Route
|
||||||
path="/store"
|
path="/store"
|
||||||
element={
|
element={
|
||||||
<ProtectedRoute>
|
|
||||||
<StoreGuard>
|
<StoreGuard>
|
||||||
<StorePage />
|
<StorePage />
|
||||||
</StoreGuard>
|
</StoreGuard>
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/store/:id/"
|
path="/store/:id/"
|
||||||
element={
|
element={
|
||||||
<ProtectedRoute>
|
|
||||||
<StoreGuard>
|
<StoreGuard>
|
||||||
<StorePage />
|
<StorePage />
|
||||||
</StoreGuard>
|
</StoreGuard>
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route path="/playground/:id/">
|
<Route path="/account">
|
||||||
|
<Route path="delete" element={<DeleteAccountPage />}></Route>
|
||||||
|
</Route>
|
||||||
<Route
|
<Route
|
||||||
path=""
|
path="/admin"
|
||||||
element={
|
element={
|
||||||
<ProtectedRoute>
|
<ProtectedAdminRoute>
|
||||||
<PlaygroundPage />
|
<AdminPage />
|
||||||
</ProtectedRoute>
|
</ProtectedAdminRoute>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/flow/:id/">
|
<Route path="/flow/:id/">
|
||||||
<Route
|
<Route path="" element={<DashboardWrapperPage />}>
|
||||||
path="folder/:folderId/"
|
<Route path="folder/:folderId/" element={<FlowPage />} />
|
||||||
element={
|
<Route path="" element={<FlowPage />} />
|
||||||
<ProtectedRoute>
|
</Route>
|
||||||
<FlowPage />
|
<Route path="view" element={<ViewPage />} />
|
||||||
</ProtectedRoute>
|
</Route>
|
||||||
}
|
<Route path="/playground/:id/">
|
||||||
/>
|
<Route path="" element={<PlaygroundPage />} />
|
||||||
<Route
|
</Route>
|
||||||
path=""
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<FlowPage />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="view"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<ViewPage />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
|
||||||
path="*"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<CatchAllRoute />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
<Route
|
||||||
path="/login"
|
path="/login"
|
||||||
element={
|
element={
|
||||||
|
|
@ -184,24 +157,7 @@ const router = createBrowserRouter(
|
||||||
</ProtectedLoginRoute>
|
</ProtectedLoginRoute>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route path="*" element={<Navigate replace to="/" />} />
|
||||||
path="/admin"
|
|
||||||
element={
|
|
||||||
<ProtectedAdminRoute>
|
|
||||||
<AdminPage />
|
|
||||||
</ProtectedAdminRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path="/account">
|
|
||||||
<Route
|
|
||||||
path="delete"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<DeleteAccountPage />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
</Route>
|
|
||||||
</Route>,
|
</Route>,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { test } from "@playwright/test";
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
|
|
@ -70,6 +70,8 @@ test("should delete rows from table message", async ({ page }) => {
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
await page.getByTestId("user-profile-settings").last().click();
|
await page.getByTestId("user-profile-settings").last().click();
|
||||||
await page.getByText("Settings").last().click();
|
await page.getByText("Settings").last().click();
|
||||||
await page.getByText("Messages").last().click();
|
await page.getByText("Messages").last().click();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue