refactor: Wrap forms into dashboard layout (#5697)

This commit is contained in:
Bruno Quaresma
2023-01-12 17:08:31 -03:00
committed by GitHub
parent bef6f67b70
commit 3861d1c555
11 changed files with 170 additions and 266 deletions

View File

@ -18,7 +18,7 @@ import { Route, Routes } from "react-router-dom"
import { selectPermissions } from "xServices/auth/authSelectors" import { selectPermissions } from "xServices/auth/authSelectors"
import { selectFeatureVisibility } from "xServices/entitlements/entitlementsSelectors" import { selectFeatureVisibility } from "xServices/entitlements/entitlementsSelectors"
import { XServiceContext } from "xServices/StateContext" import { XServiceContext } from "xServices/StateContext"
import { NavbarLayout } from "./components/NavbarLayout/NavbarLayout" import { DashboardLayout } from "./components/DashboardLayout/DashboardLayout"
import { RequireAuth } from "./components/RequireAuth/RequireAuth" import { RequireAuth } from "./components/RequireAuth/RequireAuth"
import { SettingsLayout } from "./components/SettingsLayout/SettingsLayout" import { SettingsLayout } from "./components/SettingsLayout/SettingsLayout"
import { DeploySettingsLayout } from "components/DeploySettingsLayout/DeploySettingsLayout" import { DeploySettingsLayout } from "components/DeploySettingsLayout/DeploySettingsLayout"
@ -134,132 +134,79 @@ export const AppRouter: FC = () => {
<Route path="login" element={<LoginPage />} /> <Route path="login" element={<LoginPage />} />
<Route path="setup" element={<SetupPage />} /> <Route path="setup" element={<SetupPage />} />
{/* Authenticated routes */} {/* Dashboard routes */}
<Route element={<RequireAuth />}> <Route element={<RequireAuth />}>
<Route index element={<IndexPage />} /> <Route element={<DashboardLayout />}>
<Route index element={<IndexPage />} />
<Route path="cli-auth" element={<CliAuthenticationPage />} /> <Route path="cli-auth" element={<CliAuthenticationPage />} />
<Route path="gitauth" element={<GitAuthPage />} /> <Route path="gitauth" element={<GitAuthPage />} />
<Route <Route path="workspaces" element={<WorkspacesPage />} />
path="workspaces"
element={
<NavbarLayout>
<WorkspacesPage />
</NavbarLayout>
}
/>
<Route path="starter-templates"> <Route path="starter-templates">
<Route <Route index element={<StarterTemplatesPage />} />
index <Route path=":exampleId" element={<StarterTemplatePage />} />
element={ </Route>
<NavbarLayout>
<StarterTemplatesPage />
</NavbarLayout>
}
/>
<Route <Route path="templates">
path=":exampleId" <Route index element={<TemplatesPage />} />
element={ <Route path="new" element={<CreateTemplatePage />} />
<NavbarLayout> <Route path=":template">
<StarterTemplatePage /> <Route
</NavbarLayout> index
} element={
/>
</Route>
<Route path="templates">
<Route
index
element={
<NavbarLayout>
<TemplatesPage />
</NavbarLayout>
}
/>
<Route path="new" element={<CreateTemplatePage />} />
<Route path=":template">
<Route
index
element={
<NavbarLayout>
<TemplateLayout> <TemplateLayout>
<TemplateSummaryPage /> <TemplateSummaryPage />
</TemplateLayout> </TemplateLayout>
</NavbarLayout> }
} />
/> <Route
<Route path="permissions"
path="permissions" element={
element={
<NavbarLayout>
<TemplateLayout> <TemplateLayout>
<TemplatePermissionsPage /> <TemplatePermissionsPage />
</TemplateLayout> </TemplateLayout>
</NavbarLayout>
}
/>
<Route path="workspace" element={<CreateWorkspacePage />} />
<Route path="settings" element={<TemplateSettingsPage />} />
<Route path="versions">
<Route
path=":version"
element={
<NavbarLayout>
<TemplateVersionPage />
</NavbarLayout>
} }
/> />
<Route path="workspace" element={<CreateWorkspacePage />} />
<Route path="settings" element={<TemplateSettingsPage />} />
<Route path="versions">
<Route path=":version" element={<TemplateVersionPage />} />
</Route>
</Route> </Route>
</Route> </Route>
</Route>
<Route path="users"> <Route path="users">
<Route <Route
index index
element={ element={
<NavbarLayout>
<UsersLayout> <UsersLayout>
<UsersPage /> <UsersPage />
</UsersLayout> </UsersLayout>
</NavbarLayout> }
} />
/> <Route path="create" element={<CreateUserPage />} />
<Route path="create" element={<CreateUserPage />} /> </Route>
</Route>
<Route path="/groups"> <Route path="/groups">
<Route <Route
index index
element={ element={
<NavbarLayout>
<UsersLayout> <UsersLayout>
<GroupsPage /> <GroupsPage />
</UsersLayout> </UsersLayout>
</NavbarLayout> }
} />
/> <Route path="create" element={<CreateGroupPage />} />
<Route path="create" element={<CreateGroupPage />} /> <Route path=":groupId" element={<GroupPage />} />
<Route <Route path=":groupId/settings" element={<SettingsGroupPage />} />
path=":groupId" </Route>
element={
<NavbarLayout>
<GroupPage />
</NavbarLayout>
}
/>
<Route path=":groupId/settings" element={<SettingsGroupPage />} />
</Route>
<Route path="/audit"> <Route path="/audit">
<Route <Route
index index
element={ element={
<NavbarLayout>
<RequirePermission <RequirePermission
isFeatureVisible={ isFeatureVisible={
featureVisibility[FeatureNames.AuditLog] && featureVisibility[FeatureNames.AuditLog] &&
@ -268,16 +215,14 @@ export const AppRouter: FC = () => {
> >
<AuditPage /> <AuditPage />
</RequirePermission> </RequirePermission>
</NavbarLayout> }
} />
/> </Route>
</Route>
<Route path="/settings/deployment"> <Route path="/settings/deployment">
<Route <Route
path="general" path="general"
element={ element={
<NavbarLayout>
<RequirePermission <RequirePermission
isFeatureVisible={Boolean( isFeatureVisible={Boolean(
permissions?.viewDeploymentConfig, permissions?.viewDeploymentConfig,
@ -287,13 +232,11 @@ export const AppRouter: FC = () => {
<GeneralSettingsPage /> <GeneralSettingsPage />
</DeploySettingsLayout> </DeploySettingsLayout>
</RequirePermission> </RequirePermission>
</NavbarLayout> }
} />
/> <Route
<Route path="security"
path="security" element={
element={
<NavbarLayout>
<RequirePermission <RequirePermission
isFeatureVisible={Boolean( isFeatureVisible={Boolean(
permissions?.viewDeploymentConfig, permissions?.viewDeploymentConfig,
@ -303,13 +246,11 @@ export const AppRouter: FC = () => {
<SecuritySettingsPage /> <SecuritySettingsPage />
</DeploySettingsLayout> </DeploySettingsLayout>
</RequirePermission> </RequirePermission>
</NavbarLayout> }
} />
/> <Route
<Route path="appearance"
path="appearance" element={
element={
<NavbarLayout>
<RequirePermission <RequirePermission
isFeatureVisible={Boolean( isFeatureVisible={Boolean(
permissions?.viewDeploymentConfig, permissions?.viewDeploymentConfig,
@ -319,13 +260,11 @@ export const AppRouter: FC = () => {
<AppearanceSettingsPage /> <AppearanceSettingsPage />
</DeploySettingsLayout> </DeploySettingsLayout>
</RequirePermission> </RequirePermission>
</NavbarLayout> }
} />
/> <Route
<Route path="network"
path="network" element={
element={
<NavbarLayout>
<RequirePermission <RequirePermission
isFeatureVisible={Boolean( isFeatureVisible={Boolean(
permissions?.viewDeploymentConfig, permissions?.viewDeploymentConfig,
@ -335,13 +274,11 @@ export const AppRouter: FC = () => {
<NetworkSettingsPage /> <NetworkSettingsPage />
</DeploySettingsLayout> </DeploySettingsLayout>
</RequirePermission> </RequirePermission>
</NavbarLayout> }
} />
/> <Route
<Route path="userauth"
path="userauth" element={
element={
<NavbarLayout>
<RequirePermission <RequirePermission
isFeatureVisible={Boolean( isFeatureVisible={Boolean(
permissions?.viewDeploymentConfig, permissions?.viewDeploymentConfig,
@ -351,13 +288,11 @@ export const AppRouter: FC = () => {
<UserAuthSettingsPage /> <UserAuthSettingsPage />
</DeploySettingsLayout> </DeploySettingsLayout>
</RequirePermission> </RequirePermission>
</NavbarLayout> }
} />
/> <Route
<Route path="gitauth"
path="gitauth" element={
element={
<NavbarLayout>
<RequirePermission <RequirePermission
isFeatureVisible={Boolean( isFeatureVisible={Boolean(
permissions?.viewDeploymentConfig, permissions?.viewDeploymentConfig,
@ -367,72 +302,51 @@ export const AppRouter: FC = () => {
<GitAuthSettingsPage /> <GitAuthSettingsPage />
</DeploySettingsLayout> </DeploySettingsLayout>
</RequirePermission> </RequirePermission>
</NavbarLayout> }
} />
/> </Route>
</Route>
<Route path="settings"> <Route path="settings">
<Route <Route
path="account" path="account"
element={ element={
<NavbarLayout>
<SettingsLayout> <SettingsLayout>
<AccountPage /> <AccountPage />
</SettingsLayout> </SettingsLayout>
</NavbarLayout> }
} />
/> <Route
<Route path="security"
path="security" element={
element={
<NavbarLayout>
<SettingsLayout> <SettingsLayout>
<SecurityPage /> <SecurityPage />
</SettingsLayout> </SettingsLayout>
</NavbarLayout> }
} />
/> <Route
<Route path="ssh-keys"
path="ssh-keys" element={
element={
<NavbarLayout>
<SettingsLayout> <SettingsLayout>
<SSHKeysPage /> <SSHKeysPage />
</SettingsLayout> </SettingsLayout>
</NavbarLayout>
}
/>
</Route>
<Route path="/@:username">
<Route path=":workspace">
<Route
index
element={
<NavbarLayout>
<WorkspacePage />
</NavbarLayout>
} }
/> />
</Route>
<Route path="schedule" element={<WorkspaceSchedulePage />} /> <Route path="/@:username">
<Route path=":workspace">
<Route path="terminal" element={<TerminalPage />} /> <Route index element={<WorkspacePage />} />
<Route path="schedule" element={<WorkspaceSchedulePage />} />
<Route <Route path="terminal" element={<TerminalPage />} />
path="builds/:buildNumber" <Route
element={ path="builds/:buildNumber"
<NavbarLayout> element={<WorkspaceBuildPage />}
<WorkspaceBuildPage /> />
</NavbarLayout> <Route
} path="change-version"
/> element={<WorkspaceChangeVersionPage />}
/>
<Route </Route>
path="change-version"
element={<WorkspaceChangeVersionPage />}
/>
</Route> </Route>
</Route> </Route>
</Route> </Route>

View File

@ -62,7 +62,7 @@ export const CreateUserForm: FC<
) )
return ( return (
<FullPageForm title="Create user" onCancel={onCancel}> <FullPageForm title="Create user">
<form onSubmit={form.handleSubmit} autoComplete="off"> <form onSubmit={form.handleSubmit} autoComplete="off">
<Stack spacing={1}> <Stack spacing={1}>
<TextField <TextField

View File

@ -1,13 +1,14 @@
import { makeStyles } from "@material-ui/core/styles" import { makeStyles } from "@material-ui/core/styles"
import { useActor } from "@xstate/react" import { useActor } from "@xstate/react"
import { Loader } from "components/Loader/Loader" import { Loader } from "components/Loader/Loader"
import { FC, PropsWithChildren, Suspense, useContext, useEffect } from "react" import { FC, Suspense, useContext, useEffect } from "react"
import { XServiceContext } from "../../xServices/StateContext" import { XServiceContext } from "../../xServices/StateContext"
import { Navbar } from "../Navbar/Navbar" import { Navbar } from "../Navbar/Navbar"
import { UpdateCheckBanner } from "components/UpdateCheckBanner/UpdateCheckBanner" import { UpdateCheckBanner } from "components/UpdateCheckBanner/UpdateCheckBanner"
import { Margins } from "components/Margins/Margins" import { Margins } from "components/Margins/Margins"
import { Outlet } from "react-router-dom"
export const NavbarLayout: FC<PropsWithChildren> = ({ children }) => { export const DashboardLayout: FC = () => {
const styles = useStyles() const styles = useStyles()
const xServices = useContext(XServiceContext) const xServices = useContext(XServiceContext)
const [authState] = useActor(xServices.authXService) const [authState] = useActor(xServices.authXService)
@ -38,7 +39,9 @@ export const NavbarLayout: FC<PropsWithChildren> = ({ children }) => {
</div> </div>
)} )}
<div className={styles.siteContent}> <div className={styles.siteContent}>
<Suspense fallback={<Loader />}>{children}</Suspense> <Suspense fallback={<Loader />}>
<Outlet />
</Suspense>
</div> </div>
</div> </div>
) )

View File

@ -30,5 +30,4 @@ export const Example = Template.bind({})
Example.args = { Example.args = {
title: "My Form", title: "My Form",
detail: "Lorem ipsum dolor", detail: "Lorem ipsum dolor",
onCancel: action("cancel"),
} }

View File

@ -1,39 +1,38 @@
import { makeStyles } from "@material-ui/core/styles" import { Margins } from "components/Margins/Margins"
import { FC, ReactNode } from "react" import { FC, ReactNode } from "react"
import { FormCloseButton } from "../FormCloseButton/FormCloseButton" import {
import { FormTitle } from "../FormTitle/FormTitle" PageHeader,
import { Margins } from "../Margins/Margins" PageHeaderTitle,
PageHeaderSubtitle,
} from "components/PageHeader/PageHeader"
import { makeStyles } from "@material-ui/core/styles"
export interface FullPageFormProps { export interface FullPageFormProps {
title: string title: string
detail?: ReactNode detail?: ReactNode
onCancel: () => void
} }
const useStyles = makeStyles(() => ({
root: {
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
},
}))
export const FullPageForm: FC<React.PropsWithChildren<FullPageFormProps>> = ({ export const FullPageForm: FC<React.PropsWithChildren<FullPageFormProps>> = ({
title, title,
detail, detail,
onCancel,
children, children,
}) => { }) => {
const styles = useStyles() const styles = useStyles()
return (
<main className={styles.root}>
<Margins size="small">
<FormTitle title={title} detail={detail} />
<FormCloseButton onClose={onCancel} />
{children} return (
</Margins> <Margins size="small">
</main> <PageHeader className={styles.pageHeader}>
<PageHeaderTitle>{title}</PageHeaderTitle>
{detail && <PageHeaderSubtitle>{detail}</PageHeaderSubtitle>}
</PageHeader>
<main>{children}</main>
</Margins>
) )
} }
const useStyles = makeStyles((theme) => ({
pageHeader: {
paddingBottom: theme.spacing(2),
},
}))

View File

@ -1,13 +1,12 @@
import { FormCloseButton } from "../FormCloseButton/FormCloseButton"
import { makeStyles } from "@material-ui/core/styles"
import Typography from "@material-ui/core/Typography"
import { Margins } from "components/Margins/Margins" import { Margins } from "components/Margins/Margins"
import { FC, ReactNode } from "react" import { FC, ReactNode } from "react"
import {
export interface FormTitleProps { PageHeader,
title: string PageHeaderTitle,
detail?: ReactNode PageHeaderSubtitle,
} } from "components/PageHeader/PageHeader"
import Button from "@material-ui/core/Button"
import { makeStyles } from "@material-ui/core/styles"
export interface FullPageHorizontalFormProps { export interface FullPageHorizontalFormProps {
title: string title: string
@ -21,35 +20,25 @@ export const FullPageHorizontalForm: FC<
const styles = useStyles() const styles = useStyles()
return ( return (
<> <Margins size="medium">
<header className={styles.title}> <PageHeader
<Margins size="medium"> actions={
<Typography variant="h3">{title}</Typography> <Button size="small" onClick={onCancel}>
{detail && <Typography variant="caption">{detail}</Typography>} Cancel
</Margins> </Button>
</header> }
>
<PageHeaderTitle>{title}</PageHeaderTitle>
{detail && <PageHeaderSubtitle>{detail}</PageHeaderSubtitle>}
</PageHeader>
<FormCloseButton onClose={onCancel} /> <main className={styles.form}>{children}</main>
</Margins>
<main className={styles.main}>
<Margins size="medium">{children}</Margins>
</main>
</>
) )
} }
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
title: { form: {
paddingTop: theme.spacing(6), marginTop: theme.spacing(1),
paddingBottom: theme.spacing(8),
[theme.breakpoints.down("sm")]: {
paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(4),
},
},
main: {
paddingBottom: theme.spacing(10),
}, },
})) }))

View File

@ -280,7 +280,7 @@ export const WorkspaceScheduleForm: FC<
} }
return ( return (
<FullPageForm onCancel={onCancel} title={Language.formTitle}> <FullPageForm title={Language.formTitle}>
<form onSubmit={form.handleSubmit} className={styles.form}> <form onSubmit={form.handleSubmit} className={styles.form}>
<Stack> <Stack>
{Boolean(submitScheduleError) && ( {Boolean(submitScheduleError) && (

View File

@ -39,7 +39,7 @@ export const CreateGroupPageView: FC<CreateGroupPageViewProps> = ({
return ( return (
<Margins> <Margins>
<FullPageForm title="Create group" onCancel={onCancel}> <FullPageForm title="Create group">
<form onSubmit={form.handleSubmit}> <form onSubmit={form.handleSubmit}>
<TextField <TextField
{...getFieldHelpers("name")} {...getFieldHelpers("name")}

View File

@ -54,7 +54,7 @@ const UpdateGroupForm: FC<{
const { t } = useTranslation("common") const { t } = useTranslation("common")
return ( return (
<FullPageForm title="Group settings" onCancel={onCancel}> <FullPageForm title="Group settings">
<form onSubmit={form.handleSubmit}> <form onSubmit={form.handleSubmit}>
<TextField <TextField
{...getFieldHelpers("name")} {...getFieldHelpers("name")}

View File

@ -54,7 +54,7 @@ export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
} }
return ( return (
<FullPageForm title={t("title")} onCancel={onCancel}> <FullPageForm title={t("title")}>
{Boolean(errors.getTemplateError) && ( {Boolean(errors.getTemplateError) && (
<Stack className={classes.errorContainer}> <Stack className={classes.errorContainer}>
<AlertBanner severity="error" error={errors.getTemplateError} /> <AlertBanner severity="error" error={errors.getTemplateError} />

View File

@ -21,7 +21,7 @@ export const WorkspaceChangeVersionPageView: FC<
const { workspace, templateVersions, template, error } = context const { workspace, templateVersions, template, error } = context
return ( return (
<FullPageForm title="Change version" onCancel={() => navigate(-1)}> <FullPageForm title="Change version">
<Stack> <Stack>
<Maybe condition={Boolean(error)}> <Maybe condition={Boolean(error)}>
<AlertBanner severity="error" error={error} /> <AlertBanner severity="error" error={error} />