diff --git a/ui/src/pages/PlainLayout/index.tsx b/ui/src/pages/PlainLayout/index.tsx new file mode 100644 index 000000000..6364219c6 --- /dev/null +++ b/ui/src/pages/PlainLayout/index.tsx @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* A layout that is nothing but a main landmark. + * + * Most pages reach their `main` through `SideNavLayout` or `Admin`. The rest — + * signing in, registering, recovering an account, the error pages — hang + * straight off `pages/Layout`, which wraps the header and would therefore put + * the navigation inside `main` if the landmark were added there. + * + * A pathless layout route solves that without touching a single page: the + * children keep their paths, their order and their guards, and gain a landmark + * they can be skipped to. + */ + +import { FC, memo } from 'react'; +import { Outlet } from 'react-router-dom'; + +const Index: FC = () => { + return ( +
+ +
+ ); +}; + +export default memo(Index); diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts index 8423fb7a3..66616ba3d 100644 --- a/ui/src/router/routes.ts +++ b/ui/src/router/routes.ts @@ -249,93 +249,101 @@ const routes: RouteNode[] = [ ], }, { - path: 'users/login', - page: 'pages/Users/Login', - guard: () => { - const notLogged = guard.notLogged(); - if (notLogged.ok) { - return notLogged; - } + // Pages with no layout of their own — signing in, recovering an + // account, the error pages. Wrapped so they reach a main landmark + // as well; see pages/PlainLayout. + page: 'pages/PlainLayout', + children: [ + { + path: 'users/login', + page: 'pages/Users/Login', + guard: () => { + const notLogged = guard.notLogged(); + if (notLogged.ok) { + return notLogged; + } - return guard.notActivated(); - }, - }, - { - path: 'users/register', - page: 'pages/Users/Register', - guard: () => { - const allowNew = guard.allowNewRegistration(); - if (!allowNew.ok) { - return allowNew; - } - const notLogged = guard.notLogged(); - if (notLogged.ok) { - const sa = guard.singUpAgent(); - if (!sa.ok) { - return sa; - } - } - return notLogged; - }, - }, - { - path: 'users/logout', - page: 'pages/Users/Logout', - guard: () => { - return guard.loggedRedirectHome(); - }, - }, - { - path: 'users/account-recovery', - page: 'pages/Users/AccountForgot', - guard: () => { - return guard.notLogged(); - }, - }, - { - path: 'users/change-email', - page: 'pages/Users/ChangeEmail', - }, - { - path: 'users/password-reset', - page: 'pages/Users/PasswordReset', - }, - { - path: 'users/account-activation', - page: 'pages/Users/ActiveEmail', - }, - { - path: 'users/account-activation/success', - page: 'pages/Users/ActivationResult', - guard: () => { - return guard.activated(); - }, - }, - { - path: '/users/account-activation/failed', - page: 'pages/Users/ActivationResult', - guard: () => { - return guard.notActivated(); - }, - }, - { - path: '/users/confirm-new-email', - page: 'pages/Users/ConfirmNewEmail', - }, - { - path: '/users/account-suspended', - page: 'pages/Users/Suspended', - guard: () => { - return guard.notLogged(); - }, - }, - { - path: '/users/confirm-email', - page: 'pages/Users/OauthBindEmail', - }, - { - path: '/users/auth-landing', - page: 'pages/Users/AuthCallback', + return guard.notActivated(); + }, + }, + { + path: 'users/register', + page: 'pages/Users/Register', + guard: () => { + const allowNew = guard.allowNewRegistration(); + if (!allowNew.ok) { + return allowNew; + } + const notLogged = guard.notLogged(); + if (notLogged.ok) { + const sa = guard.singUpAgent(); + if (!sa.ok) { + return sa; + } + } + return notLogged; + }, + }, + { + path: 'users/logout', + page: 'pages/Users/Logout', + guard: () => { + return guard.loggedRedirectHome(); + }, + }, + { + path: 'users/account-recovery', + page: 'pages/Users/AccountForgot', + guard: () => { + return guard.notLogged(); + }, + }, + { + path: 'users/change-email', + page: 'pages/Users/ChangeEmail', + }, + { + path: 'users/password-reset', + page: 'pages/Users/PasswordReset', + }, + { + path: 'users/account-activation', + page: 'pages/Users/ActiveEmail', + }, + { + path: 'users/account-activation/success', + page: 'pages/Users/ActivationResult', + guard: () => { + return guard.activated(); + }, + }, + { + path: '/users/account-activation/failed', + page: 'pages/Users/ActivationResult', + guard: () => { + return guard.notActivated(); + }, + }, + { + path: '/users/confirm-new-email', + page: 'pages/Users/ConfirmNewEmail', + }, + { + path: '/users/account-suspended', + page: 'pages/Users/Suspended', + guard: () => { + return guard.notLogged(); + }, + }, + { + path: '/users/confirm-email', + page: 'pages/Users/OauthBindEmail', + }, + { + path: '/users/auth-landing', + page: 'pages/Users/AuthCallback', + }, + ], }, // for admin { @@ -464,24 +472,32 @@ const routes: RouteNode[] = [ ], }, { - path: '/user-center/auth', - page: 'pages/UserCenter/Auth', - guard: () => { - const notLogged = guard.notLogged(); - return notLogged; - }, - }, - { - path: '/user-center/auth-failed', - page: 'pages/UserCenter/AuthFailed', - }, - { - path: '*', - page: 'pages/404', - }, - { - path: '50x', - page: 'pages/50X', + // Pages with no layout of their own — signing in, recovering an + // account, the error pages. Wrapped so they reach a main landmark + // as well; see pages/PlainLayout. + page: 'pages/PlainLayout', + children: [ + { + path: '/user-center/auth', + page: 'pages/UserCenter/Auth', + guard: () => { + const notLogged = guard.notLogged(); + return notLogged; + }, + }, + { + path: '/user-center/auth-failed', + page: 'pages/UserCenter/AuthFailed', + }, + { + path: '*', + page: 'pages/404', + }, + { + path: '50x', + page: 'pages/50X', + }, + ], }, // ai { @@ -532,12 +548,20 @@ const routes: RouteNode[] = [ ], }, { - path: '/users/unsubscribe', - page: 'pages/Users/Unsubscribe', - }, - { - path: '403', - page: 'pages/403', + // Pages with no layout of their own — signing in, recovering an + // account, the error pages. Wrapped so they reach a main landmark + // as well; see pages/PlainLayout. + page: 'pages/PlainLayout', + children: [ + { + path: '/users/unsubscribe', + page: 'pages/Users/Unsubscribe', + }, + { + path: '403', + page: 'pages/403', + }, + ], }, ], },