feat: add primitive profile settings

This commit is contained in:
Ali BARIN
2022-03-29 20:50:01 +02:00
committed by Ömer Faruk Aydın
parent 68e5e6d011
commit 140734b32c
13 changed files with 279 additions and 57 deletions

View File

@@ -2,6 +2,7 @@ import * as React from 'react';
import { useNavigate } from 'react-router-dom';
import MenuItem from '@mui/material/MenuItem';
import Menu, { MenuProps } from '@mui/material/Menu';
import { Link } from 'react-router-dom';
import * as URLS from 'config/urls';
import useAuthentication from 'hooks/useAuthentication';
@@ -49,7 +50,18 @@ function AccountDropdownMenu(props: AccountDropdownMenuProps): React.ReactElemen
open={open}
onClose={onClose}
>
<MenuItem onClick={logout}>{formatMessage('accountDropdownMenu.logout')}</MenuItem>
<MenuItem
component={Link}
to={URLS.SETTINGS_DASHBOARD}
>
{formatMessage('accountDropdownMenu.settings')}
</MenuItem>
<MenuItem
onClick={logout}
>
{formatMessage('accountDropdownMenu.logout')}
</MenuItem>
</Menu>
);
}

View File

@@ -4,21 +4,28 @@ import { SwipeableDrawerProps } from '@mui/material/SwipeableDrawer';
import Toolbar from '@mui/material/Toolbar';
import List from '@mui/material/List';
import Divider from '@mui/material/Divider';
import AppsIcon from '@mui/icons-material/Apps';
import SwapCallsIcon from '@mui/icons-material/SwapCalls';
import HistoryIcon from '@mui/icons-material/History';
import ExploreIcon from '@mui/icons-material/Explore';
import useMediaQuery from '@mui/material/useMediaQuery';
import ListItemLink from 'components/ListItemLink';
import HideOnScroll from 'components/HideOnScroll';
import useFormatMessage from 'hooks/useFormatMessage';
import * as URLS from 'config/urls';
import { Drawer as BaseDrawer } from './style';
const iOS = typeof navigator !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent);
export default function Drawer(props: SwipeableDrawerProps): React.ReactElement {
type DrawerLink = {
Icon: React.ElementType;
primary: string;
to: string;
};
type DrawerProps = {
links: DrawerLink[];
bottomLinks?: DrawerLink[];
} & SwipeableDrawerProps;
export default function Drawer(props: DrawerProps): React.ReactElement {
const { links = [], bottomLinks = [], ...drawerProps } = props;
const theme = useTheme();
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'), { noSsr: true });
const formatMessage = useFormatMessage();
@@ -31,46 +38,43 @@ export default function Drawer(props: SwipeableDrawerProps): React.ReactElement
return (
<BaseDrawer
{...props}
{...drawerProps}
disableBackdropTransition={!iOS}
disableDiscovery={iOS}
variant={matchSmallScreens ? 'temporary' : 'permanent'}
>
{/* keep the following encapsulating `div` to have `space-between` children */}
<div>
<HideOnScroll unmountOnExit>
<Toolbar />
</HideOnScroll>
<List sx={{ py: 0, mt: 3 }}>
{links.map(({ Icon, primary, to }, index) => (
<ListItemLink
icon={<SwapCallsIcon htmlColor={theme.palette.primary.main} />}
primary={formatMessage('drawer.flows')}
to={URLS.FLOWS}
onClick={closeOnClick}
/>
<ListItemLink
icon={<AppsIcon htmlColor={theme.palette.primary.main} />}
primary={formatMessage('drawer.apps')}
to={URLS.APPS}
onClick={closeOnClick}
/>
<ListItemLink
icon={<HistoryIcon htmlColor={theme.palette.primary.main} />}
primary={formatMessage('drawer.executions')}
to={URLS.EXECUTIONS}
onClick={closeOnClick}
/>
<ListItemLink
icon={<ExploreIcon htmlColor={theme.palette.primary.main} />}
primary={formatMessage('drawer.explore')}
to={URLS.EXPLORE}
key={`${to}-${index}`}
icon={<Icon htmlColor={theme.palette.primary.main} />}
primary={formatMessage(primary)}
to={to}
onClick={closeOnClick}
/>
))}
</List>
<Divider />
</div>
<List sx={{ py: 0, mt: 3 }}>
{bottomLinks.map(({ Icon, primary, to }, index) => (
<ListItemLink
key={`${to}-${index}`}
icon={<Icon htmlColor={theme.palette.primary.main} />}
primary={formatMessage(primary)}
to={to}
onClick={closeOnClick}
/>
))}
</List>
</BaseDrawer>
);
}

View File

@@ -1,4 +1,5 @@
import { styled, Theme, CSSObject } from '@mui/material/styles';
import { drawerClasses } from '@mui/material/Drawer';
import MuiSwipeableDrawer from '@mui/material/SwipeableDrawer';
const drawerWidth = 300;
@@ -35,11 +36,19 @@ export const Drawer = styled(MuiSwipeableDrawer)(
boxSizing: 'border-box',
...(open && {
...openedMixin(theme),
'& .MuiDrawer-paper': openedMixin(theme),
[`& .${drawerClasses.paper}`]: {
...openedMixin(theme),
display: 'flex',
justifyContent: 'space-between'
},
}),
...(!open && {
...closedMixin(theme),
'& .MuiDrawer-paper': closedMixin(theme),
[`& .${drawerClasses.paper}`]: {
...closedMixin(theme),
display: 'flex',
justifyContent: 'space-between'
},
}),
}),
);

View File

@@ -7,7 +7,7 @@ type FormProps = {
defaultValues?: UseFormProps['defaultValues'];
onSubmit?: SubmitHandler<FieldValues>;
render?: (props: UseFormReturn) => React.ReactNode;
}
};
const noop = () => null;

View File

@@ -1,16 +1,44 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import { useTheme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import AppsIcon from '@mui/icons-material/Apps';
import SwapCallsIcon from '@mui/icons-material/SwapCalls';
import HistoryIcon from '@mui/icons-material/History';
import ExploreIcon from '@mui/icons-material/Explore';
import Box from '@mui/material/Box';
import * as URLS from 'config/urls';
import AppBar from 'components/AppBar';
import Drawer from 'components/Drawer';
import Toolbar from '@mui/material/Toolbar';
type PublicLayoutProps = {
children: React.ReactNode;
}
const drawerLinks = [
{
Icon: SwapCallsIcon,
primary: 'drawer.flows',
to: URLS.FLOWS,
},
{
Icon: AppsIcon,
primary: 'drawer.apps',
to: URLS.APPS,
},
{
Icon: HistoryIcon,
primary: 'drawer.executions',
to: URLS.EXECUTIONS,
},
{
Icon: ExploreIcon,
primary: 'drawer.explore',
to: URLS.EXPLORE,
},
];
export default function PublicLayout({ children }: PublicLayoutProps): React.ReactElement {
const theme = useTheme();
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('lg'), { noSsr: true });
@@ -25,6 +53,7 @@ export default function PublicLayout({ children }: PublicLayoutProps): React.Rea
<Box sx={{ display: 'flex', }}>
<Drawer
links={drawerLinks}
open={isDrawerOpen}
onOpen={openDrawer}
onClose={closeDrawer}

View File

@@ -14,7 +14,7 @@ type ListItemLinkProps = {
export default function ListItemLink(props: ListItemLinkProps): React.ReactElement {
const { icon, primary, to, onClick } = props;
const selected = useMatch({ path: to, end: false });
const selected = useMatch({ path: to, end: true });
const CustomLink = React.useMemo(
() =>

View File

@@ -0,0 +1,62 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import { useTheme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
import * as URLS from 'config/urls';
import AppBar from 'components/AppBar';
import Drawer from 'components/Drawer';
type SettingsLayoutProps = {
children: React.ReactNode;
}
const drawerLinks = [
{
Icon: AccountCircleIcon,
primary: 'settingsDrawer.myProfile',
to: URLS.SETTINGS_PROFILE,
},
];
const drawerBottomLinks = [
{
Icon: ArrowBackIosNewIcon,
primary: 'settingsDrawer.goBack',
to: '/',
}
];
export default function SettingsLayout({ children }: SettingsLayoutProps): React.ReactElement {
const theme = useTheme();
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('lg'), { noSsr: true });
const [isDrawerOpen, setDrawerOpen] = React.useState(!matchSmallScreens);
const openDrawer = () => setDrawerOpen(true);
const closeDrawer = () => setDrawerOpen(false);
return (
<>
<AppBar drawerOpen={isDrawerOpen} onDrawerOpen={openDrawer} onDrawerClose={closeDrawer} />
<Box sx={{ display: 'flex', }}>
<Drawer
links={drawerLinks}
bottomLinks={drawerBottomLinks}
open={isDrawerOpen}
onOpen={openDrawer}
onClose={closeDrawer}
/>
<Box sx={{ flex: 1, }}>
<Toolbar />
{children}
</Box>
</Box>
</>
);
}

View File

@@ -29,5 +29,10 @@ export const FLOWS = '/flows';
export const FLOW = (flowId: string): string => `/editor/${flowId}`;
export const FLOW_PATTERN = '/flows/:flowId';
export const SETTINGS = '/settings';
export const SETTINGS_DASHBOARD = SETTINGS;
export const SETTINGS_PROFILE = '/settings/profile';
export const PROFILE = 'profile';
export const DASHBOARD = FLOWS;

View File

@@ -1,12 +1,15 @@
{
"brandText": "Automatisch",
"searchPlaceholder": "Search",
"accountDropdownMenu.settings": "Settings",
"accountDropdownMenu.logout": "Logout",
"drawer.dashboard": "Dashboard",
"drawer.flows": "Flows",
"drawer.apps": "My Apps",
"drawer.executions": "Executions",
"drawer.explore": "Explore",
"settingsDrawer.myProfile": "My Profile",
"settingsDrawer.goBack": "Go to the dashboard",
"app.connectionCount": "{count} connections",
"app.flowCount": "{count} flows",
"app.addConnection": "Add connection",
@@ -38,5 +41,11 @@
"flowStep.actionType": "Action",
"flows.create": "Create flow",
"flows.title": "Flows",
"executions.title": "Executions"
"executions.title": "Executions",
"profileSettings.title": "My Profile",
"profileSettings.email": "E-mail",
"profileSettings.updateEmail": "Update e-mail",
"profileSettings.newPassword": "New password",
"profileSettings.confirmNewPassword": "Confirm new password",
"profileSettings.updatePassword": "Update password"
}

View File

@@ -1,7 +1,6 @@
import * as React from 'react';
import { useParams } from 'react-router-dom';
import { useQuery } from '@apollo/client';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import type { IExecutionStep } from '@automatisch/types';
@@ -39,14 +38,12 @@ export default function Execution(): React.ReactElement {
}, [executionId, fetchMore, pageInfo]);
return (
<Box sx={{ py: 3 }}>
<Container>
<Grid container item sx={{ mb: [2, 5] }} columnSpacing={1.5} rowGap={3}>
<Container sx={{ py: 3 }}>
<Grid container item sx={{ mb: [2, 5] }} rowGap={3}>
{executionSteps?.map((executionStep) => (
<ExecutionStep key={executionStep.id} executionStep={executionStep} step={executionStep.step} />
))}
</Grid>
</Container>
</Box>
);
};

View File

@@ -0,0 +1,77 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Grid from '@mui/material/Grid';
import Button from '@mui/material/Button';
import PageTitle from 'components/PageTitle';
import Container from 'components/Container';
import Form from 'components/Form';
import TextField from 'components/TextField';
import useFormatMessage from 'hooks/useFormatMessage';
const StyledForm = styled(Form)`
display: flex;
align-items: end;
flex-direction: column;
`;
function ProfileSettings() {
const formatMessage = useFormatMessage();
return (
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
<Grid container item xs={12} sm={9} md={6}>
<Grid item xs={12} sx={{ mb: [2, 5] }} >
<PageTitle>
{formatMessage('profileSettings.title')}
</PageTitle>
</Grid>
<Grid item xs={12} justifyContent="flex-end">
<StyledForm>
<TextField
fullWidth
name="email"
label={formatMessage('profileSettings.email')}
margin="normal"
/>
<Button
variant="contained"
sx={{ mt: 2 }}
type="submit"
>
{formatMessage('profileSettings.updateEmail')}
</Button>
</StyledForm>
<StyledForm>
<TextField
fullWidth
name="password"
label={formatMessage('profileSettings.newPassword')}
margin="normal"
/>
<TextField
fullWidth
name="confirmPassword"
label={formatMessage('profileSettings.confirmNewPassword')}
margin="normal"
/>
<Button
variant="contained"
sx={{ mt: 2 }}
type="submit"
>
{formatMessage('profileSettings.updatePassword')}
</Button>
</StyledForm>
</Grid>
</Grid>
</Container>
);
}
export default ProfileSettings;

View File

@@ -11,6 +11,7 @@ import Explore from 'pages/Explore';
import Login from 'pages/Login';
import EditorRoutes from 'pages/Editor/routes';
import * as URLS from 'config/urls';
import settingsRoutes from './settingsRoutes';
export default (
<Routes>
@@ -34,6 +35,10 @@ export default (
<Route path="/" element={<Navigate to={URLS.FLOWS} />} />
<Route path={`${URLS.SETTINGS}`}>
{settingsRoutes}
</Route>
<Route element={<Layout><div>404</div></Layout>} />
</Routes>
);

View File

@@ -0,0 +1,13 @@
import { Route, Navigate } from 'react-router-dom';
import SettingsLayout from 'components/SettingsLayout';
import ProfileSettings from 'pages/ProfileSettings';
import * as URLS from 'config/urls';
export default (
<>
<Route path={URLS.SETTINGS_PROFILE} element={<SettingsLayout><ProfileSettings /></SettingsLayout>} />
<Route path={URLS.SETTINGS} element={<Navigate to={URLS.SETTINGS_PROFILE} />} />
</>
);