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:
Lucas Oliveira 2024-08-28 13:52:11 -03:00 committed by GitHub
commit d6537b724e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 221 additions and 282 deletions

View file

@ -3,10 +3,6 @@ import { Cookies } from "react-cookie";
import { RouterProvider } from "react-router-dom";
import "reactflow/dist/style.css";
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 {
useAutoLogin,
@ -34,8 +30,6 @@ export default function App() {
const { mutate: mutateAutoLogin } = useAutoLogin();
useGetVersionQuery();
const { mutate: mutateRefresh } = useRefreshAccessToken();
const isLoginPage = location.pathname.includes("login");
@ -79,23 +73,7 @@ export default function App() {
});
}, []);
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 && !isLoginPage) {
mutateRefresh({ refresh_token: refreshToken });
}
}, accessTokenTimer * 1000);
return () => clearInterval(intervalId);
}, [isLoginPage]);
useGetVersionQuery();
useSaveConfig();
return (

View file

@ -2,17 +2,20 @@ 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);
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const autoLogin = useAuthStore((state) => state.autoLogin);
const isAdmin = useAuthStore((state) => state.isAdmin);
const isLoginPage = location.pathname.includes("login");
const logout = useAuthStore((state) => state.logout);
if (!isAuthenticated && !isLoginPage) {
logout();
if (!isAuthenticated) {
return (
<div className="flex h-screen w-screen items-center justify-center">
<LoadingComponent remSize={30} />
</div>
);
} else if ((userData && !isAdmin) || autoLogin) {
return <Navigate to="/" replace />;
} else {

View file

@ -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 { useEffect } from "react";
import { Cookies } from "react-cookie";
export const ProtectedRoute = ({ children }) => {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const hasToken = !!localStorage.getItem(LANGFLOW_AUTO_LOGIN_OPTION);
const isLoginPage = location.pathname.includes("login");
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();
} else {
return children;

View file

@ -34,11 +34,6 @@ export const MenuBar = ({}: {}): JSX.Element => {
const addFlow = useAddFlow();
const setErrorData = useAlertStore((state) => state.setErrorData);
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 redo = useFlowsManagerStore((state) => state.redo);
const saveLoading = useFlowsManagerStore((state) => state.saveLoading);

View file

@ -15,7 +15,6 @@ import { useLogout } from "@/controllers/API/queries/auth";
import useAuthStore from "@/stores/authStore";
import useAlertStore from "../../stores/alertStore";
import { useDarkStore } from "../../stores/darkStore";
import { useLocationStore } from "../../stores/locationStore";
import { useStoreStore } from "../../stores/storeStore";
import IconComponent, { ForwardedIconComponent } from "../genericIconComponent";
import { Button } from "../ui/button";
@ -48,29 +47,22 @@ export default function Header(): JSX.Element {
const setDark = useDarkStore((state) => state.setDark);
const stars = useDarkStore((state) => state.stars);
const routeHistory = useLocationStore((state) => state.routeHistory);
const profileImageUrl = `${BASE_URL_API}files/profile_pictures/${
userData?.profile_image ?? "Space/046-rocket.svg"
}`;
const redirectToLastLocation = () => {
const lastVisitedIndex = routeHistory
.reverse()
.findIndex((path) => path !== location.pathname);
const lastFlowVisited = routeHistory[lastVisitedIndex];
lastFlowVisited ? navigate(lastFlowVisited) : navigate("/all");
const canGoBack = location.key !== "default";
if (canGoBack) {
navigate(-1);
} else {
navigate("/", { replace: true });
}
};
const visitedFlowPathBefore = () => {
const last100VisitedPaths = routeHistory.slice(-99);
return last100VisitedPaths.some((path) => path.includes("/flow/"));
};
const showArrowReturnIcon =
LOCATIONS_TO_RETURN.some((path) => location.pathname.includes(path)) &&
visitedFlowPathBefore();
const showArrowReturnIcon = LOCATIONS_TO_RETURN.some((path) =>
location.pathname.includes(path),
);
const handleLogout = () => {
mutationLogout(undefined, {

View file

@ -1,4 +1,3 @@
import Header from "../headerComponent";
import { Separator } from "../ui/separator";
export default function PageLayout({
@ -15,25 +14,22 @@ export default function PageLayout({
betaIcon?: boolean;
}) {
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 w-full items-center justify-between gap-4 space-y-0.5 py-8 pb-2">
<div className="flex w-full flex-col">
<h2
className="text-2xl font-bold tracking-tight"
data-testid="mainpage_title"
>
{title}
{betaIcon && <span className="store-beta-icon">BETA</span>}
</h2>
<p className="text-muted-foreground">{description}</p>
</div>
<div className="flex-shrink-0">{button && button}</div>
<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 flex-col">
<h2
className="text-2xl font-bold tracking-tight"
data-testid="mainpage_title"
>
{title}
{betaIcon && <span className="store-beta-icon">BETA</span>}
</h2>
<p className="text-muted-foreground">{description}</p>
</div>
<Separator className="my-6 flex" />
{children}
<div className="flex-shrink-0">{button && button}</div>
</div>
<Separator className="my-6 flex" />
{children}
</div>
);
}

View file

@ -1,6 +1,5 @@
import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants";
import useAuthStore from "@/stores/authStore";
import { useUtilityStore } from "@/stores/utilityStore";
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios";
import { useContext, useEffect } from "react";
import { Cookies } from "react-cookie";
@ -89,13 +88,13 @@ function ApiInterceptor() {
// Request interceptor to add access token to every request
const requestInterceptor = api.interceptors.request.use(
(config) => {
const checkRequest = checkDuplicateRequestAndStoreRequest(config);
const controller = new AbortController();
if (!checkRequest) {
controller.abort("Duplicate Request");
console.error("Duplicate Request");
try {
checkDuplicateRequestAndStoreRequest(config);
} catch (e) {
const error = e as Error;
controller.abort(error.message);
console.error(error.message);
}
const accessToken = cookies.get(LANGFLOW_ACCESS_TOKEN);

View file

@ -21,13 +21,11 @@ export function checkDuplicateRequestAndStoreRequest(config) {
currentTime - parseInt(lastRequestTime, 10) < 300 &&
lastCurrentUrl === currentUrl
) {
return false;
throw new Error("Duplicate request: " + lastUrl);
}
localStorage.setItem("lastUrlCalled", config.url ?? "");
localStorage.setItem("lastMethodCalled", config.method ?? "");
localStorage.setItem("lastRequestTime", currentTime.toString());
localStorage.setItem("lastCurrentUrl", currentUrl);
return true;
}

View file

@ -9,35 +9,22 @@ import { UseRequestProcessor } from "../../services/request-processor";
interface BuildsQueryParams {
flowId?: string;
nodeId?: string;
}
export const useGetBuildsQuery: useQueryFunctionType<
BuildsQueryParams,
AxiosResponse<{ vertex_builds: FlowPoolType }>
> = ({}) => {
> = (params) => {
const { query } = UseRequestProcessor();
const setFlowPool = useFlowStore((state) => state.setFlowPool);
const currentFlow = useFlowStore((state) => state.currentFlow);
const getBuildsFn = async (
params: BuildsQueryParams,
): Promise<AxiosResponse<{ vertex_builds: FlowPoolType }>> => {
const responseFn = async () => {
const config = {};
config["params"] = { flow_id: params.flowId };
if (params.nodeId) {
config["params"] = { nodeId: params.nodeId };
}
return await api.get<any>(`${getURL("BUILDS")}`, config);
};
const responseFn = async () => {
const response = await getBuildsFn({
flowId: currentFlow!.id,
});
const response = await api.get<any>(`${getURL("BUILDS")}`, config);
if (currentFlow) {
const flowPool = response.data.vertex_builds;
@ -47,10 +34,14 @@ export const useGetBuildsQuery: useQueryFunctionType<
return response;
};
const queryResult = query(["useGetBuildsQuery"], responseFn, {
placeholderData: keepPreviousData,
refetchOnWindowFocus: false,
});
const queryResult = query(
["useGetBuildsQuery", { key: params.flowId }],
responseFn,
{
placeholderData: keepPreviousData,
refetchOnWindowFocus: false,
},
);
return queryResult;
};

View file

@ -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;

View file

@ -7,7 +7,6 @@ import {
import { cloneDeep } from "lodash";
import { useContext, useEffect, useRef, useState } from "react";
import IconComponent from "../../components/genericIconComponent";
import Header from "../../components/headerComponent";
import LoadingComponent from "../../components/loadingComponent";
import PaginatorComponent from "../../components/paginatorComponent";
import ShadTooltip from "../../components/shadTooltipComponent";
@ -246,7 +245,6 @@ export default function AdminPage() {
return (
<>
<Header />
{userData && (
<div className="admin-page-panel flex h-full flex-col pb-8">
<div className="main-page-nav-arrangement">

View file

@ -10,7 +10,6 @@ import {
TIMEOUT_ERROR_MESSAGE,
} from "@/constants/constants";
import { useGetHealthQuery } from "@/controllers/API/queries/health";
import useTrackLastVisitedPath from "@/hooks/use-track-last-visited-path";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useUtilityStore } from "@/stores/utilityStore";
import { cn } from "@/utils/utils";
@ -20,8 +19,6 @@ import { ErrorBoundary } from "react-error-boundary";
import { Outlet } from "react-router-dom";
export function AppWrapperPage() {
useTrackLastVisitedPath();
const isLoading = useFlowsManagerStore((state) => state.isLoading);
const healthCheckMaxRetries = useFlowsManagerStore(

View 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>
);
}

View file

@ -143,7 +143,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
};
}, [lastCopiedSelection, lastSelection, takeSnapshot, selectionMenuVisible]);
const { isFetching, refetch } = useGetBuildsQuery({});
const { isFetching } = useGetBuildsQuery({ flowId: currentFlowId });
const showCanvas =
Object.keys(templates).length > 0 &&
@ -151,8 +151,6 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
!isFetching;
useEffect(() => {
refetch();
useFlowStore.setState({ autoSaveFlow });
if (checkOldComponents({ nodes })) {
setNoticeData({
title:
@ -161,6 +159,10 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
}
}, [currentFlowId]);
useEffect(() => {
useFlowStore.setState({ autoSaveFlow });
});
function handleUndo(e: KeyboardEvent) {
if (!isWrappedWithClass(e, "noflow")) {
e.preventDefault();

View file

@ -8,7 +8,6 @@ import { customStringify } from "@/utils/reactflowUtils";
import { useEffect } from "react";
import { useBlocker, useNavigate, useParams } from "react-router-dom";
import FlowToolbar from "../../components/chatComponent";
import Header from "../../components/headerComponent";
import { useDarkStore } from "../../stores/darkStore";
import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
@ -81,7 +80,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
}
};
awaitgetTypes();
}, [id, flows]);
}, [id, flows, currentFlowId]);
useEffect(() => {
setOnFlowPage(true);
@ -94,7 +93,6 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
return (
<>
<Header />
<div className="flow-page-positioning">
{currentFlow && (
<div className="flex h-full overflow-hidden">

View file

@ -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 { useTypesStore } from "@/stores/typesStore";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
import { useNavigate, useParams } from "react-router-dom";
import LoadingComponent from "../../components/loadingComponent";
import { getComponent } from "../../controllers/API";
import IOModal from "../../modals/IOModal";
@ -8,7 +11,6 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
import cloneFLowWithParent from "../../utils/storeUtils";
export default function PlaygroundPage() {
const getFlowById = useFlowsManagerStore((state) => state.getFlowById);
const flows = useFlowsManagerStore((state) => state.flows);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
@ -21,19 +23,38 @@ export default function PlaygroundPage() {
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
useEffect(() => {
if (flows) {
const flow = getFlowById(id!);
if (flow) {
setCurrentFlow(flow);
} else {
if (validApiKey)
getFlowData().then((flow) => {
setCurrentFlow(flow);
});
const awaitgetTypes = async () => {
if (flows && currentFlowId === "") {
const isAnExistingFlow = flows.find((flow) => flow.id === id);
if (!isAnExistingFlow) {
if (validApiKey) {
getFlowData().then((flow) => {
setCurrentFlow(flow);
});
} else {
navigate("/");
}
}
setCurrentFlow(isAnExistingFlow);
} else if (!flows) {
setIsLoading(true);
await refreshFlows(undefined);
await getTypes();
setIsLoading(false);
}
}
};
awaitgetTypes();
}, [id, flows, validApiKey]);
return (

View file

@ -1,6 +1,5 @@
import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows";
import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
import useFlowStore from "@/stores/flowStore";
import { useTypesStore } from "@/stores/typesStore";
import { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
@ -10,7 +9,6 @@ import Page from "../FlowPage/components/PageComponent";
export default function ViewPage() {
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage);
const { id } = useParams();
const navigate = useNavigate();
useGetGlobalVariables();
@ -43,15 +41,6 @@ export default function ViewPage() {
awaitgetTypes();
}, [id, flows]);
useEffect(() => {
setOnFlowPage(true);
return () => {
setOnFlowPage(false);
setCurrentFlow(undefined);
};
}, [id]);
return (
<div className="flow-page-positioning">
<Page view />

View file

@ -1,17 +1,18 @@
import React, { lazy } from "react";
import { lazy } from "react";
import {
createBrowserRouter,
createRoutesFromElements,
Navigate,
Outlet,
Route,
} from "react-router-dom";
import { ProtectedAdminRoute } from "./components/authAdminGuard";
import { ProtectedRoute } from "./components/authGuard";
import { ProtectedLoginRoute } from "./components/authLoginGuard";
import { AuthSettingsGuard } from "./components/authSettingsGuard";
import { CatchAllRoute } from "./components/catchAllRoutes";
import { StoreGuard } from "./components/storeGuard";
import { AppWrapperPage } from "./pages/AppWrapperPage";
import { DashboardWrapperPage } from "./pages/DashboardWrapperPage";
import FlowPage from "./pages/FlowPage";
import LoginPage from "./pages/LoginPage";
import MyCollectionComponent from "./pages/MainPage/components/myCollectionComponent";
@ -34,132 +35,104 @@ const PlaygroundPage = lazy(() => import("./pages/Playground"));
const SignUp = lazy(() => import("./pages/SignUpPage"));
const router = createBrowserRouter(
createRoutesFromElements([
<Route path="" element={<AppWrapperPage />}>
<Route path="/" element={<AppWrapperPage />}>
<Route
path="/"
path=""
element={
<ProtectedRoute>
<HomePage />
<Outlet />
</ProtectedRoute>
}
>
<Route index element={<Navigate replace to={"all"} />} />
<Route
path="flows/"
element={<MyCollectionComponent key="flows" type="flow" />}
>
<Route path="" element={<DashboardWrapperPage />}>
<Route path="" element={<HomePage />}>
<Route index element={<Navigate replace to={"all"} />} />
<Route
path="flows/"
element={<MyCollectionComponent key="flows" type="flow" />}
>
<Route
path="folder/:folderId"
element={<MyCollectionComponent key="flows" type="flow" />}
/>
</Route>
<Route
path="components/"
element={
<MyCollectionComponent key="components" type="component" />
}
>
<Route
path="folder/:folderId"
element={
<MyCollectionComponent key="components" type="component" />
}
/>
</Route>
<Route
path="all/"
element={<MyCollectionComponent key="all" type="all" />}
>
<Route
path="folder/:folderId"
element={<MyCollectionComponent key="all" type="all" />}
/>
</Route>
</Route>
<Route path="/settings" element={<SettingsPage />}>
<Route index element={<Navigate replace to={"general"} />} />
<Route path="global-variables" element={<GlobalVariablesPage />} />
<Route path="api-keys" element={<ApiKeysPage />} />
<Route
path="general/:scrollId?"
element={
<AuthSettingsGuard>
<GeneralPage />
</AuthSettingsGuard>
}
/>
<Route path="shortcuts" element={<ShortcutsPage />} />
<Route path="messages" element={<MessagesPage />} />
</Route>
<Route
path="folder/:folderId"
element={<MyCollectionComponent key="flows" type="flow" />}
/>
</Route>
<Route
path="components/"
element={<MyCollectionComponent key="components" type="component" />}
>
<Route
path="folder/:folderId"
path="/store"
element={
<MyCollectionComponent key="components" type="component" />
<StoreGuard>
<StorePage />
</StoreGuard>
}
/>
<Route
path="/store/:id/"
element={
<StoreGuard>
<StorePage />
</StoreGuard>
}
/>
<Route path="/account">
<Route path="delete" element={<DeleteAccountPage />}></Route>
</Route>
<Route
path="/admin"
element={
<ProtectedAdminRoute>
<AdminPage />
</ProtectedAdminRoute>
}
/>
</Route>
<Route
path="all/"
element={<MyCollectionComponent key="all" type="all" />}
>
<Route
path="folder/:folderId"
element={<MyCollectionComponent key="all" type="all" />}
/>
<Route path="/flow/:id/">
<Route path="" element={<DashboardWrapperPage />}>
<Route path="folder/:folderId/" element={<FlowPage />} />
<Route path="" element={<FlowPage />} />
</Route>
<Route path="view" element={<ViewPage />} />
</Route>
<Route path="/playground/:id/">
<Route path="" element={<PlaygroundPage />} />
</Route>
</Route>
<Route
path="/settings"
element={
<ProtectedRoute>
<SettingsPage />
</ProtectedRoute>
}
>
<Route index element={<Navigate replace to={"general"} />} />
<Route path="global-variables" element={<GlobalVariablesPage />} />
<Route path="api-keys" element={<ApiKeysPage />} />
<Route
path="general/:scrollId?"
element={
<AuthSettingsGuard>
<GeneralPage />
</AuthSettingsGuard>
}
/>
<Route path="shortcuts" element={<ShortcutsPage />} />
<Route path="messages" element={<MessagesPage />} />
</Route>
<Route
path="/store"
element={
<ProtectedRoute>
<StoreGuard>
<StorePage />
</StoreGuard>
</ProtectedRoute>
}
/>
<Route
path="/store/:id/"
element={
<ProtectedRoute>
<StoreGuard>
<StorePage />
</StoreGuard>
</ProtectedRoute>
}
/>
<Route path="/playground/:id/">
<Route
path=""
element={
<ProtectedRoute>
<PlaygroundPage />
</ProtectedRoute>
}
/>
</Route>
<Route path="/flow/:id/">
<Route
path="folder/:folderId/"
element={
<ProtectedRoute>
<FlowPage />
</ProtectedRoute>
}
/>
<Route
path=""
element={
<ProtectedRoute>
<FlowPage />
</ProtectedRoute>
}
/>
<Route
path="view"
element={
<ProtectedRoute>
<ViewPage />
</ProtectedRoute>
}
/>
</Route>
<Route
path="*"
element={
<ProtectedRoute>
<CatchAllRoute />
</ProtectedRoute>
}
/>
<Route
path="/login"
element={
@ -184,24 +157,7 @@ const router = createBrowserRouter(
</ProtectedLoginRoute>
}
/>
<Route
path="/admin"
element={
<ProtectedAdminRoute>
<AdminPage />
</ProtectedAdminRoute>
}
/>
<Route path="/account">
<Route
path="delete"
element={
<ProtectedRoute>
<DeleteAccountPage />
</ProtectedRoute>
}
></Route>
</Route>
<Route path="*" element={<Navigate replace to="/" />} />
</Route>,
]),
);

View file

@ -1,4 +1,4 @@
import { expect, test } from "@playwright/test";
import { test } from "@playwright/test";
import * as dotenv from "dotenv";
import path from "path";
@ -70,6 +70,8 @@ test("should delete rows from table message", async ({ page }) => {
timeout: 30000,
});
await page.waitForTimeout(2000);
await page.getByTestId("user-profile-settings").last().click();
await page.getByText("Settings").last().click();
await page.getByText("Messages").last().click();