diff --git a/jams-react-client/src/globalUrls.js b/jams-react-client/src/globalUrls.js index ec31b55739bdaaff8d3a5c7658efcffc36849557..d5e36808c09b931af867249ac851ab5839e89851 100644 --- a/jams-react-client/src/globalUrls.js +++ b/jams-react-client/src/globalUrls.js @@ -24,7 +24,7 @@ const api_path_get_server_status = '/api/info'; const api_path_get_post_configuration_auth_service = '/api/configuration/authservice'; const api_path_get_post_configuration_global_settings = '/api/configuration/globalsettings'; const api_path_post_configuration_change_password = '/api/admin/user'; -const api_path_post_configuration_register_license = '/api/subscription'; +const api_path_post_configuration_register_license = '/api/admin/subscription'; const api_path_get_subscription_status = '/api/admin/subscription'; const api_path_get_directories = '/api/auth/directories'; const api_path_get_needs_update = '/api/admin/update'; diff --git a/jams-react-client/src/layouts/Admin.js b/jams-react-client/src/layouts/Admin.js index 9427644037aa9b0a4cb7c6ba5c6e3b1189051150..ecc4883b17126727ae25a8bb8755ec1ecd03a151 100644 --- a/jams-react-client/src/layouts/Admin.js +++ b/jams-react-client/src/layouts/Admin.js @@ -38,6 +38,7 @@ const switchRoutes = ( <Redirect from="/admin/u" to="/admin/users" /> <Redirect from="/admin/g" to="/admin/groups" /> <Redirect from="/admin/b" to="/admin/blueprints" /> + <Redirect from="/admin/s" to="/admin/settings" /> </Switch> ); diff --git a/jams-react-client/src/layouts/SignUp.js b/jams-react-client/src/layouts/SignUp.js index c311d4bd1b0ed7a83c87a829bc6923df82603c29..51d3424ba810b117dcd86544f2af45ace1b97cf0 100644 --- a/jams-react-client/src/layouts/SignUp.js +++ b/jams-react-client/src/layouts/SignUp.js @@ -80,9 +80,9 @@ export default function SignUp(props) { }else if (step === 1) { return <CaSetup setError={setError} setErrorMessage={setErrorMessage} /> }else if (step === 2) { - return <IdentityManagement setError={setError} setErrorMessage={setErrorMessage} /> + return <IdentityManagement setError={setError} setErrorMessage={setErrorMessage}/> }else if (step === 3) { - return <ServerParameters setError={setError} setErrorMessage={setErrorMessage} /> + return <ServerParameters setError={setError} setErrorMessage={setErrorMessage}/> } } return ( diff --git a/jams-react-client/src/routes.js b/jams-react-client/src/routes.js index 9f4f58fa1407adbe2479e80213f5aa49e1e7ac16..c3d6e7fb81872ecb1a5fa8a3b26d7e211c21c040 100644 --- a/jams-react-client/src/routes.js +++ b/jams-react-client/src/routes.js @@ -18,11 +18,13 @@ // @material-ui/icons import Person from "@material-ui/icons/Person"; import Group from "@material-ui/icons/Group"; -import AllInbox from "@material-ui/icons/AllInbox" +import AllInbox from "@material-ui/icons/AllInbox"; +import SettingsIcon from '@material-ui/icons/Settings'; // core components/views for Admin layout import Users from "views/Users/Users.js"; import Groups from "views/Groups/Groups.js"; import Blueprints from "views/Blueprints/Blueprints.js"; +import Settings from "views/Settings/Settings.js"; // core components/views for RTL layout const dashboardRoutes = [ @@ -50,6 +52,14 @@ const dashboardRoutes = [ component: Blueprints, layout: "/admin" }, + { + path: "/settings", + name: "Settings", + rtlName: "حضانة بيض", + icon: SettingsIcon, + component: Settings, + layout: "/admin" + }, ]; export default dashboardRoutes; diff --git a/jams-react-client/src/views/Settings/Settings.js b/jams-react-client/src/views/Settings/Settings.js new file mode 100644 index 0000000000000000000000000000000000000000..6174f4527d137621dce7e1a58ac6b0aeaad33a1c --- /dev/null +++ b/jams-react-client/src/views/Settings/Settings.js @@ -0,0 +1,111 @@ +import React from "react"; +// @material-ui/core components +import { makeStyles } from "@material-ui/core/styles"; +// core components +import IdentityManagement from "components/IdentityManagement/IdentityManagement.js"; +import ServerParameters from "components/ServerParameters/ServerParameters.js"; +import UpdatePassword from "./UpdatePassword"; +import Subscription from "./Subscription" + + +import PropTypes from 'prop-types'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +import { + infoColor +} from "assets/jss/material-dashboard-react.js"; +import MuiAlert from "@material-ui/lab/Alert"; + + +function TabPanel(props) { + const { children, value, index, ...other } = props; + + return ( + <div + role="tabpanel" + hidden={value !== index} + id={`simple-tabpanel-${index}`} + aria-labelledby={`simple-tab-${index}`} + {...other} + > + {value === index && ( + <Box p={3}> + <Typography>{children}</Typography> + </Box> + )} + </div> + ); +} + +TabPanel.propTypes = { + children: PropTypes.node, + index: PropTypes.any.isRequired, + value: PropTypes.any.isRequired, +}; + +function a11yProps(index) { + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, + }; +} + +function Alert(props) { + return <MuiAlert elevation={6} variant="filled" {...props} />; +} + + +const styles = { + cardCategoryWhite: { + color: "rgba(255,255,255,.62)", + margin: "0", + fontSize: "14px", + marginTop: "0", + marginBottom: "0" + }, + cardTitleWhite: { + color: "#FFFFFF", + marginTop: "0px", + minHeight: "auto", + fontWeight: "300", + fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", + marginBottom: "3px", + textDecoration: "none" + } +}; + +const useStyles = makeStyles(styles); + +export default function Settings(props) { + const classes = useStyles(); + + const [value, setValue] = React.useState(0); + const [error, setError] = React.useState(false); + const [severity, setSeverity] = React.useState("error"); + const [errorMessage, setErrorMessage] = React.useState("Test"); + + const handleChange = (event, newValue) => { + setValue(newValue); + }; + return ( + <div> + <AppBar position="static" style={{ background: infoColor[0] }}> + <Tabs value={value} onChange={handleChange} aria-label="simple tabs example"> + <Tab label="Admin Password" {...a11yProps(0)} /> + <Tab label="Subscription" {...a11yProps(1)} /> + </Tabs> + </AppBar> + <TabPanel value={value} index={0}> + <UpdatePassword username="admin" setError={setError} setErrorMessage={setErrorMessage} setSeverity={setSeverity}/> + </TabPanel> + <TabPanel value={value} index={1}> + <Subscription setError={setError} setErrorMessage={setErrorMessage} setSeverity={setSeverity}/> + </TabPanel> + {error && errorMessage && <Alert severity={severity}>{errorMessage}</Alert>} + </div> + ); +} diff --git a/jams-react-client/src/views/Settings/Subscription.js b/jams-react-client/src/views/Settings/Subscription.js new file mode 100644 index 0000000000000000000000000000000000000000..6deaa4ef2e4c6121b3da0964ec188c7d4b995b7b --- /dev/null +++ b/jams-react-client/src/views/Settings/Subscription.js @@ -0,0 +1,129 @@ +import React, {useEffect, useState} from "react"; +import { useHistory } from "react-router-dom"; +import { useFormik } from 'formik'; +import * as Yup from 'yup'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; + +import axios from 'axios'; +import configApiCall from '../../api' +import { api_path_post_configuration_register_license } from '../../globalUrls' + +import auth from '../../auth' + +const useStyles = makeStyles((theme) => ({ + paper: { + marginTop: theme.spacing(8), + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + }, + avatar: { + margin: theme.spacing(1), + backgroundColor: theme.palette.secondary.main, + }, + form: { + width: '100%', // Fix IE 11 issue. + marginTop: theme.spacing(1), + }, + submit: { + margin: theme.spacing(3, 0, 2), + }, +})); + +export default function Subscription(props) { + const classes = useStyles(); + const history = useHistory(); + const [activated, setActivated] = React.useState(false); + + useEffect(()=>{ + axios(configApiCall(api_path_post_configuration_register_license, "GET", null, null)).then((response)=>{ + if(response.data["activated"] == true) setActivated(true); + }).catch((error)=>{ + if(error.response.status == 500){ + props.setErrorMessage("An error occured while getting license information!"); + props.setSeverity("error"); + props.setError(true); + } + }) + }) + + /** + * Formik Validation Fields + */ + const formik = useFormik({ + initialValues: { + subscriptionCode: '', + }, + validationSchema: Yup.object({ + subscriptionCode: Yup.string().required('Subscription code is required'), + }), + + onSubmit: values => { + handleSubmit(values); + }, + }); + + function handleSubmit(values) { + //e.preventDefault(); + const jsonData = { + "base64License": values.subscriptionCode + } + + axios(configApiCall(api_path_post_configuration_register_license, "POST", jsonData, null)).then(()=>{ + props.setErrorMessage("License registred successfully!"); + props.setSeverity("success"); + props.setError(true); + }).catch((error)=>{ + if(error.response.status == 500){ + props.setErrorMessage("A generic occurred while trying to load your license or your license could not be found!"); + props.setSeverity("error"); + props.setError(true); + } + }) + } + + if(!activated){ + return ( + <form className={classes.form} noValidate onSubmit={formik.handleSubmit}> + <Typography variant="h5" gutterBottom color="primary">Paste your JAMS Enterprise subscription code received from the Jami store.</Typography> + <Grid container spacing={3}> + <Grid item xs={12}> + <TextField + variant="outlined" + margin="normal" + required + fullWidth + id="subscriptionCode" + label="Subscription Code" + name="subscriptionCode" + autoComplete="subscriptionCode" + autoFocus + {...formik.getFieldProps('subscriptionCode')} + /> + {formik.touched.subscriptionCode && formik.errors.subscriptionCode ? (<span>{formik.errors.subscriptionCode}</span>) : null} + </Grid> + </Grid> + + <Button + type="submit" + fullWidth + variant="contained" + color="primary" + className={classes.submit} + > + Register + </Button> + + </form> + ); + }else{ + return( + <div><p>Your license is already activated!</p></div> + ); + } + +} \ No newline at end of file diff --git a/jams-react-client/src/views/Settings/UpdatePassword.js b/jams-react-client/src/views/Settings/UpdatePassword.js new file mode 100644 index 0000000000000000000000000000000000000000..b950b7d7029d41a79116ec5b6692ea5c35c2e371 --- /dev/null +++ b/jams-react-client/src/views/Settings/UpdatePassword.js @@ -0,0 +1,129 @@ +import React from 'react'; +import { useHistory } from "react-router-dom"; +import { useFormik } from 'formik'; +import * as Yup from 'yup'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; + +import axios from 'axios'; +import configApiCall from '../../api' +import { api_path_put_update_user } from '../../globalUrls' + +import auth from '../../auth' + +const useStyles = makeStyles((theme) => ({ + paper: { + marginTop: theme.spacing(8), + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + }, + avatar: { + margin: theme.spacing(1), + backgroundColor: theme.palette.secondary.main, + }, + form: { + width: '100%', // Fix IE 11 issue. + marginTop: theme.spacing(1), + }, + submit: { + margin: theme.spacing(3, 0, 2), + }, +})); + +export default function UpdatePassword(props) { + const classes = useStyles(); + const history = useHistory(); + + const changePassword = (values) => { + axios(configApiCall(api_path_put_update_user+'?username='+props.username+'&password='+values.password, 'PUT', null, null)).then(()=>{ + props.setSeverity("success"); + props.setErrorMessage("Admin password updated successfully!"); + props.setError(true); + }).catch((error)=> { + props.setSeverity("error"); + props.setErrorMessage("Updating user " + props.username + ' password failed with error: ' + error); + props.setError(true); + }); + } + + /** + * Formik Validation Fields + */ + const formik = useFormik({ + initialValues: { + password: '', + confirmPassword: '', + + }, + validationSchema: Yup.object({ + password: Yup.string() + .max(50, 'Must be 50 characters or less') + .required('Password is required'), + confirmPassword: Yup.string() + .oneOf([Yup.ref('password'), null], 'Passwords must match') + .max(50, 'Must be 50 characters or less') + .required('Password confirmation is required'), + }), + + onSubmit: values => { + changePassword(values); + }, + }); + + return ( + <form className={classes.form} onSubmit={formik.handleSubmit}> + <Typography variant="h5" gutterBottom color="primary">Enter the following information below to change your admin password.</Typography> + <Grid container spacing={3}> + <Grid item xs={6}> + <TextField + variant="outlined" + margin="normal" + required + fullWidth + name="password" + label="Password" + type="password" + id="password" + autoComplete="new-password" + {...formik.getFieldProps('password')} + /> + {formik.touched.password && formik.errors.password ? (<span>{formik.errors.password}</span>) : null} + + </Grid> + + <Grid item xs={6}> + + <TextField + variant="outlined" + margin="normal" + required + fullWidth + name="confirmPassword" + label="Confirm password" + type="password" + id="confirmPassword" + autoComplete="new-password" + {...formik.getFieldProps('confirmPassword')} + /> + {formik.touched.confirmPassword && formik.errors.confirmPassword ? (<span>{formik.errors.confirmPassword}</span>) : null} + + </Grid> + </Grid> + + <Button + type="submit" + fullWidth + variant="contained" + color="primary" + className={classes.submit} + > + Change Admin Password + </Button> + + </form> + ); +} \ No newline at end of file