Skip to content

Commit

Permalink
Merge pull request #72 from NicolasMarqui/settings
Browse files Browse the repository at this point in the history
DA-79 and DA-80: Added settings page and added theming for drawer
  • Loading branch information
saul-data authored Jan 15, 2022
2 parents a0819c0 + bac1ec5 commit 7eab806
Show file tree
Hide file tree
Showing 25 changed files with 1,832 additions and 190 deletions.
18 changes: 12 additions & 6 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import TeamDetail from './pages/TeamDetail';
import MemberDetail from './pages/MemberDetail';
import TeamGroup from './pages/TeamGroup';
import Teams from './pages/Teams';
import Settings from './pages/Settings';
import EnvironmentDetail from './pages/EnvironmentDetail';

export const ColorModeContext = React.createContext({
toggleColorMode: () => {},
Expand Down Expand Up @@ -61,11 +63,7 @@ function App() {
)}
autoHideDuration={60000}>
<Box className="app" backgroundColor="background.main">
<UserAuth
refreshTokenUrl="/refreshtoken"
LogincallbackUrl="/loginCallback"
loginUrl="/webapp/login"
logoutUrl="/webapp/logout">
<UserAuth refreshTokenUrl="/refreshtoken" LogincallbackUrl="/loginCallback" loginUrl="/webapp/login" logoutUrl="/webapp/logout">
<Route exact path="/congratulations">
<Congratulations />
</Route>
Expand All @@ -75,7 +73,9 @@ function App() {
<Route exact path="/login">
<LoginUser />
</Route>
<PrivateRoute exact path={['/', '/teams', '/teams/:teamId', '/myaccount/:memberId', '/teams/access/:accessId']}>
<PrivateRoute
exact
path={['/', '/teams', '/teams/:teamId', '/myaccount/:memberId', '/teams/access/:accessId', '/settings', '/settings/environment/:environmentId']}>
<Layout>
<Route exact path="/">
<Pipelines />
Expand All @@ -92,6 +92,12 @@ function App() {
<Route exact path="/myaccount/:memberId">
<MemberDetail />
</Route>
<Route exact path="/settings">
<Settings />
</Route>
<Route exact path="/settings/environment/:environmentId">
<EnvironmentDetail />
</Route>
</Layout>
</PrivateRoute>
<PrivateRoute exact path="/logout">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Box, Button, Grid, TextField, Typography } from '@mui/material';
import { useSnackbar } from 'notistack';
import { useForm } from 'react-hook-form';
import { useAddEnvironment } from '../../../graphql/addEnvironment';

const AddEnvironmentDrawer = ({ handleClose, refreshData }) => {
// Hooks
const addEnvironment = useAddEnvironment();

const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const { register, handleSubmit } = useForm();

async function onSubmit(data) {
const allData = {
input: {
name: data.name,
description: data.description,
},
};

let response = await addEnvironment(allData);
if (response && response.name) {
handleClose();
closeSnackbar();
refreshData();
enqueueSnackbar(`Environment added: ${data.name}`, { variant: 'success' });
} else {
response.errors.map((err) => enqueueSnackbar(err.message, { variant: 'error' }));
}
}

return (
<form onSubmit={handleSubmit(onSubmit)}>
<Box position="relative" style={{ maxWidth: '400px', margin: 'auto' }}>
<Box sx={{ p: '4.125rem' }}>
<Box position="absolute" top="26px" right="39px" display="flex" alignItems="center">
<Button onClick={handleClose} style={{ paddingLeft: '16px', paddingRight: '16px' }} variant="text" startIcon={<FontAwesomeIcon icon={faTimes} />}>
Close
</Button>
</Box>

<Box mt={3}>
<Typography component="h2" variant="h2">
Add environment
</Typography>

<TextField
label="Name"
id="name"
size="small"
required
sx={{ mt: 2, mb: 2, fontSize: '.75rem', display: 'flex' }}
{...register('name', { required: true })}
/>
<TextField
label="Description"
id="description"
size="small"
required
sx={{ mb: 2, fontSize: '.75rem', display: 'flex' }}
{...register('description', { required: true })}
/>

<Grid mt={4} display="flex" alignItems="center">
<Button type="submit" variant="contained" color="primary" style={{ width: '100%' }}>
Save
</Button>
</Grid>
</Box>
</Box>
</Box>
</form>
);
};

export default AddEnvironmentDrawer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { useEffect } from 'react';
import { Box, Typography, Button, Grid } from '@mui/material';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { useUpdateDeactivateEnvironment } from '../../../graphql/updateDeactivateEnvironment';
import { useUpdateActivateEnvironment } from '../../../graphql/updateActivateEnvironment';
import { useSnackbar } from 'notistack';

const DeactivateEnvironmentDrawer = ({ environment, handleClose, refreshData }) => {
const { closeSnackbar } = useSnackbar();

// Clear snackbar on load
useEffect(() => {
closeSnackbar();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// Deactivate environment hook
const deactivate = useDeactivate(environment.id, handleClose, refreshData);

// Activate environment hook
const activate = useActivate(environment.id, handleClose, refreshData);

return (
<Box position="relative">
<Box sx={{ p: '4.125rem' }}>
<Box position="absolute" top="26px" right="39px" display="flex" alignItems="center">
<Button variant="text" onClick={handleClose} style={{ paddingLeft: '16px', paddingRight: '16px' }} startIcon={<FontAwesomeIcon icon={faTimes} />}>
Close
</Button>
</Box>

<Typography component="h2" variant="h2">
{environment.active === true ? 'Deactivate' : 'Activate'} environment - {environment.name}
</Typography>

<Typography variant="body2" sx={{ mt: 2 }}>
You are about to {environment.active === true ? 'deactivate' : 'activate'} a environment, would you like to continue?
</Typography>

<Grid mt={4} display="flex" alignItems="center">
<Button onClick={environment.active === true ? deactivate : activate} variant="contained" color="primary" sx={{ mr: 2 }}>
Yes
</Button>
<Button onClick={handleClose} variant="contained" color="primary">
No
</Button>
</Grid>
</Box>
</Box>
);
};

export default DeactivateEnvironmentDrawer;

// ------ Custom hooks

const useDeactivate = (environment_id, handleClose, refreshData) => {
// GraphQL hook
const updateDeactivateEnvironment = useUpdateDeactivateEnvironment();

const { enqueueSnackbar, closeSnackbar } = useSnackbar();

return async function () {
let response = await updateDeactivateEnvironment({ environment_id });

if (response.r === 'error') {
closeSnackbar();
enqueueSnackbar("Can't deactivate user: " + response.msg, {
variant: 'error',
});
} else if (response.errors) {
closeSnackbar();
response.errors.map((err) => enqueueSnackbar(err.message, { variant: 'error' }));
} else {
closeSnackbar();
enqueueSnackbar(`Success`, { variant: 'success' });
handleClose();
refreshData();
}
};
};

const useActivate = (environment_id, handleClose, refreshData) => {
// GraphQL hook
const updateActivateEnvironment = useUpdateActivateEnvironment();

const { enqueueSnackbar, closeSnackbar } = useSnackbar();

return async function () {
let response = await updateActivateEnvironment({ environment_id });

if (response.r === 'error') {
closeSnackbar();
enqueueSnackbar("Can't Activate user: " + response.msg, {
variant: 'error',
});
} else if (response.errors) {
closeSnackbar();
response.errors.map((err) => enqueueSnackbar(err.message, { variant: 'error' }));
} else {
closeSnackbar();
enqueueSnackbar(`Success`, { variant: 'success' });
handleClose();
refreshData();
}
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Box, Button, Grid, Typography } from '@mui/material';
import { useSnackbar } from 'notistack';
import { useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { useUpdateDeleteEnvironment } from '../../../graphql/updateDeleteEnvironment';

const DeleteEnvironmentDrawer = ({ environment, handleClose }) => {
const { closeSnackbar } = useSnackbar();

// Clear snackbar on load
useEffect(() => {
closeSnackbar();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// Delete environment hook
const deleteEnvironment = useDelete(environment.id);

return (
<Box position="relative">
<Box sx={{ p: '4.125rem 2.5rem 4.125rem 4.125rem' }}>
<Box position="absolute" top="26px" right="39px" display="flex" alignItems="center">
<Button variant="text" startIcon={<FontAwesomeIcon icon={faTimes} onClick={handleClose} />}>
Close
</Button>
</Box>

<Typography component="h2" variant="h2">
Delete environment - {environment.name}
</Typography>

<Typography variant="body2" sx={{ mt: 2 }}>
Are you are about to delete an environment, would you like to continue?
</Typography>

<Grid mt={4} display="flex" alignItems="center">
<Button variant="contained" color="primary" sx={{ mr: 2 }} onClick={deleteEnvironment}>
Yes
</Button>
<Button variant="contained" color="primary" onClick={handleClose}>
No
</Button>
</Grid>

<Typography color="rgba(248, 0, 0, 1)" lineHeight="15.23px" sx={{ mt: '2.56rem' }} variant="subtitle2">
Warning: this action can’t be undone.
</Typography>
</Box>
</Box>
);
};

export default DeleteEnvironmentDrawer;

const useDelete = (environment_id) => {
// GraphQL hook
const updateDeleteEnvironment = useUpdateDeleteEnvironment();

const { enqueueSnackbar, closeSnackbar } = useSnackbar();

// React router
const history = useHistory();

return async function () {
let response = await updateDeleteEnvironment({ environment_id });

if (response.r === 'error') {
closeSnackbar();
enqueueSnackbar("Can't delete environment: " + response.msg, {
variant: 'error',
});
} else if (response.errors) {
closeSnackbar();
response.errors.map((err) => enqueueSnackbar(err.message, { variant: 'error' }));
} else {
closeSnackbar();
enqueueSnackbar(`Success`, { variant: 'success' });
history.push('/settings');
}
};
};
3 changes: 2 additions & 1 deletion frontend/src/components/Layout/Layout.component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Layout = ({ children }) => {
<AppBar
elevation={0}
position="fixed"
sx={{ zIndex: (theme) => theme.zIndex.drawer + 1, background: (theme) => theme.palette.background.secondary, borderBottom: 1, borderColor: 'divider' }}>
sx={{ zIndex: (theme) => theme.zIndex.drawer, background: (theme) => theme.palette.background.secondary, borderBottom: 1, borderColor: 'divider' }}>
<Toolbar>
<Navbar />
</Toolbar>
Expand All @@ -26,6 +26,7 @@ const Layout = ({ children }) => {
sx={{
width: drawerWidth,
flexShrink: 0,
zIndex: 1100,
[`& .MuiDrawer-paper`]: { width: drawerWidth, boxSizing: 'border-box', backgroundColor: 'background.main' },
}}>
<Toolbar />
Expand Down
36 changes: 36 additions & 0 deletions frontend/src/graphql/addEnvironment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { gql, GraphQLClient } from 'graphql-request';
import { useGlobalAuthState } from '../Auth/UserAuth';

const graphlqlEndpoint = process.env.REACT_APP_GRAPHQL_ENDPOINT_PRIVATE;

const AddEnvironment = gql`
mutation addEnvironment($input: AddEnvironmentInput!) {
addEnvironment(input: $input) {
id
name
description
}
}
`;

export const useAddEnvironment = () => {
const authState = useGlobalAuthState();
const jwt = authState.authToken.get();

const headers = {
Authorization: 'Bearer ' + jwt,
};

const client = new GraphQLClient(graphlqlEndpoint, {
headers,
});

return async (input) => {
try {
const res = await client.request(AddEnvironment, input);
return res?.addEnvironment;
} catch (error) {
return JSON.parse(JSON.stringify(error, undefined, 2)).response;
}
};
};
Loading

0 comments on commit 7eab806

Please sign in to comment.