diff --git a/jams-react-client/package.json b/jams-react-client/package.json index 753c7cbfb72e1401875bd47ad75b4fa65e96d1fa..cace6c4f237bc918c2550c50f2290180028207fe 100644 --- a/jams-react-client/package.json +++ b/jams-react-client/package.json @@ -16,6 +16,7 @@ "image-to-base64": "^2.1.1", "lodash": "^4.17.19", "material-ui-popup-state": "^1.6.1", + "notistack": "^0.9.17", "package.json": "^2.0.1", "perfect-scrollbar": "1.5.0", "prop-types": "15.7.2", diff --git a/jams-react-client/src/views/Blueprint/Blueprint.js b/jams-react-client/src/views/Blueprint/Blueprint.js new file mode 100644 index 0000000000000000000000000000000000000000..30d9a7d3c7ca4d53baf0eac0f693a1c03fe4e6b6 --- /dev/null +++ b/jams-react-client/src/views/Blueprint/Blueprint.js @@ -0,0 +1,99 @@ +import React from "react"; +// @material-ui/core components +import { makeStyles } from "@material-ui/core/styles"; + + +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 EditBlueprintPermissions from "./EditBlueprintPermissions" +import EditBlueprintConfiguration from "./EditBlueprintConfiguration" + +import { + infoColor +} from "assets/jss/material-dashboard-react.js"; + + +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}`, + }; +} + +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 Blueprint(props) { + const classes = useStyles(); + + const [value, setValue] = React.useState(0); + + 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="Permissions" {...a11yProps(0)} /> + <Tab label="Configuration" {...a11yProps(1)} /> + </Tabs> + </AppBar> + <TabPanel value={value} index={0}> + <EditBlueprintPermissions blueprintName={props.blueprintName} /> + </TabPanel> + <TabPanel value={value} index={1}> + <EditBlueprintConfiguration blueprintName={props.blueprintName} /> + </TabPanel> + </div> + ); +} diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.js b/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.js new file mode 100644 index 0000000000000000000000000000000000000000..28ee9c356c0db3944821326bcd0352e60a53bfa4 --- /dev/null +++ b/jams-react-client/src/views/Blueprint/EditBlueprintConfiguration.js @@ -0,0 +1,387 @@ +import React, { useCallback } from 'react'; +import { useHistory } from 'react-router-dom'; +// @material-ui/core components +import { makeStyles } from "@material-ui/core/styles"; +import Checkbox from '@material-ui/core/Checkbox'; +import InputLabel from "@material-ui/core/InputLabel"; +// core components +import Grid from '@material-ui/core/Grid'; +import GridItem from "components/Grid/GridItem.js"; +import GridContainer from "components/Grid/GridContainer.js"; +import CustomInput from "components/CustomInput/CustomInput.js"; +import Button from "components/CustomButtons/Button.js"; +import Card from "components/Card/Card.js"; +import CardHeader from "components/Card/CardHeader.js"; +import CardIcon from "components/Card/CardIcon.js"; +import CardAvatar from "components/Card/CardAvatar.js"; +import CardBody from "components/Card/CardBody.js"; +import CardFooter from "components/Card/CardFooter.js"; +import Devices from "components/Devices/Devices.js"; +import Search from "@material-ui/icons/Search"; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import Input from '@material-ui/core/Input'; +import InputAdornment from '@material-ui/core/InputAdornment'; +import Slider from '@material-ui/core/Slider'; +import Typography from "@material-ui/core/Typography"; + +import DnsOutlinedIcon from '@material-ui/icons/DnsOutlined'; +import AccountCircleIcon from '@material-ui/icons/AccountCircle'; +import VpnKeyOutlinedIcon from '@material-ui/icons/VpnKeyOutlined'; +import StorageOutlinedIcon from '@material-ui/icons/StorageOutlined'; +import LanguageOutlinedIcon from '@material-ui/icons/LanguageOutlined'; + +import { hexToRgb, blackColor } from "assets/jss/material-dashboard-react.js"; + +import BuildOutlinedIcon from '@material-ui/icons/BuildOutlined'; + +import axios from "axios" +import configApiCall from "../../api" +import { api_path_blueprint_configuration } from "../../globalUrls" + +import Snackbar from '@material-ui/core/Snackbar'; + +import MuiAlert from '@material-ui/lab/Alert'; + +import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; + +import { debounce } from "lodash"; + +const CONTAINER_HEIGHT = 200 + +const fileUpload = require('fuctbase64'); + + + +const styles = (theme)=> ( { + ...dashboardStyle, + root: { + flexGrow: 1 + }, + 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" + }, + input: { + display: 'none', + }, + profileAsBackground: { + backgroundSize: "100% 100%", + width: "80px", + height: "80px", + }, + centerIconMiddle: { + position:"relative", + top: "20px", + left: "15px" + }, + editProfilePicture: { + borderRadius: "50%", + width: "200px", + height: "200px", + boxShadow: + "0 6px 8px -12px rgba(" + + hexToRgb(blackColor) + + ", 0.56), 0 4px 25px 0px rgba(" + + hexToRgb(blackColor) + + ", 0.12), 0 8px 10px -5px rgba(" + + hexToRgb(blackColor) + + ", 0.2)", + }, + dialogPaper: { + minHeight: '60vh', + maxHeight: '60vh', + minWidth: '80vh', + maxWidth: '80vh' + } + }); + +const useStyles = makeStyles(styles); + +function Alert(props) { + return <MuiAlert elevation={6} variant="filled" {...props} />; +} + +export default function EditBlueprintConfiguration(props) { + const classes = useStyles(); + const history = useHistory(); + + const [blueprintName, setBlueprintName] = React.useState("") + const [uPnP, setUPnP] = React.useState(true) + const [turn, setTurn] = React.useState(true) + const [turnServerAddress, setTurnServerAddress] = React.useState("") + const [turnUsername, setTurnUsername] = React.useState("") + const [turnPassword, setTurnPassword] = React.useState("") + const [bootstrap, setBootstrap] = React.useState(true) + const [bootstrapName, setBootstrapName] = React.useState("") + const [dhtProxy, setDhtProxy] = React.useState("") + const [dhtProxyName, setDhtProxyName] = React.useState("") + const [pushNotifications, setPushNotifications] = React.useState(false) + + const [open, setOpen] = React.useState(false) + const [message, setMessage] = React.useState(false) + const [severity, setSeverity] = React.useState("success") + + React.useEffect(()=>{ + setBlueprintName(props.blueprintName) + + axios(configApiCall(api_path_blueprint_configuration, 'GET', {"queryString": props.blueprintName}, null)).then((response) => { + response.data.map((blueprint) => { + setUPnP(blueprint["uPnP"]) + setTurn(blueprint["turn"]) + setTurnServerAddress(blueprint["turnServerAddress"]) + setTurnUsername(blueprint["turnUsername"]) + setTurnPassword(blueprint['turnPassword']) + setBootstrap(blueprint["bootstrap"]) + setBootstrapName(blueprint["bootstrapName"]) + setDhtProxy(blueprint["dhtProxy"]) + setDhtProxyName(blueprint["dhtProxyName"]) + setPushNotifications(blueprint["pushNotifications"]) + }) + }).catch((error) => { + console.log("Error fetching blueprint permissions : " + props.username + " " + error) + }) + }, []) + + const handleUpdateConfiguration = () => { + const data = { + 'uPnP': uPnP, + 'turn': turn, + 'turnServerAddress': turnServerAddress, + 'turnUsername': turnUsername, + 'turnPassword': turnPassword, + 'bootstrap': bootstrap, + 'dhtProxy': dhtProxy, + 'dhtProxyName': dhtProxyName, + 'pushNotifications': pushNotifications + } + + axios(configApiCall(api_path_blueprint_configuration, 'PUT', data, null)).then((response) => { + setOpen(false) + setSeverity("success") + setOpen(true) + setMessage('Updated blueprint configuration successfully !') + }).catch((error) => { + setOpen(false) + setSeverity("error") + setOpen(true) + setMessage('Error updating blueprint configuration ' + error + '!') + }) + } + + const initUpdateConfiguration = useCallback(debounce((searchValue) => handleUpdateConfiguration(), 500), []) + + const handleClose = () => { + setOpen(false) + } + + return( + <div> + <Snackbar + anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} + open={open} + onClose={handleClose} + message={message} + key={'bottomright'} + > + <Alert onClose={handleClose} severity={severity}> + {message} + </Alert> + </Snackbar> + <GridContainer> + <GridItem xs={12} sm={12} md={4}> + <Card profile> + <CardHeader color="info" stats icon> + <CardIcon color="info"> + <BuildOutlinedIcon /> + </CardIcon> + <p className={classes.cardCategory}>Permissions</p> + <h3 className={classes.cardTitle}>{props.blueprintName}</h3> + </CardHeader> + <CardBody profile> + <div className={classes.root}> + <Grid container spacing={2}> + <Grid item xs={12} sm={12} md={12}> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={uPnP} color="primary" onChange={e => { + setUPnP(e.target.checked); + handleUpdateConfiguration(); + }} name="uPnP" />} + label="UPnP" + /> + </FormGroup> + </Grid> + <Grid item xs={12} sm={12} md={12}> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={turn} color="primary" onChange={e => { + setTurn(e.target.checked); + handleUpdateConfiguration(); + }} name="turn" />} + label="TURN" + /> + </FormGroup> + </Grid> + <Grid item xs={12} sm={12} md={4}> + </Grid> + <Grid item xs={12} sm={12} md={8}> + <FormGroup row> + <FormControl className={classes.margin} size="large"> + <InputLabel htmlFor="turnServerAddress">TURN Server address</InputLabel> + <Input + disabled={!turn} + id="turnServerAddress" + placeholder={turnServerAddress} + startAdornment={ + <InputAdornment position="start"> + <DnsOutlinedIcon /> + </InputAdornment> + } + onChange={e => { + setTurnServerAddress(e.target.value); + initUpdateConfiguration(); + }} + /> + </FormControl> + </FormGroup> + <FormGroup row> + <FormControl className={classes.margin} size="large"> + <InputLabel htmlFor="turnUsername">TURN username</InputLabel> + <Input + disabled={!turn} + id="turnUsername" + placeholder={turnUsername} + startAdornment={ + <InputAdornment position="start"> + <AccountCircleIcon /> + </InputAdornment> + } + onChange={e => { + setTurnUsername(e.target.value); + initUpdateConfiguration(); + }} + /> + </FormControl> + </FormGroup> + <FormGroup row> + <FormControl className={classes.margin} size="large"> + <InputLabel htmlFor="turnPassword">TURN password</InputLabel> + <Input + disabled={!turn} + id="turnPassword" + placeholder={turnPassword} + startAdornment={ + <InputAdornment position="start"> + <VpnKeyOutlinedIcon /> + </InputAdornment> + } + onChange={e => { + setTurnPassword(e.target.value); + initUpdateConfiguration(); + }} + /> + </FormControl> + </FormGroup> + </Grid> + <Grid item xs={12} sm={12} md={12}> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={bootstrap} color="primary" onChange={e => { + setBootstrap(e.target.checked); + handleUpdateConfiguration(); + }} name="bootstrap" />} + label="Bootstrap" + /> + </FormGroup> + </Grid> + <Grid item xs={12} sm={12} md={4}> + </Grid> + <Grid item xs={12} sm={12} md={8}> + <FormGroup row> + <FormControl className={classes.margin} size="large"> + <InputLabel htmlFor="bootstrapName">Bootstrap servername name</InputLabel> + <Input + disabled={!bootstrap} + id="bootstrapName" + placeholder={bootstrapName} + startAdornment={ + <InputAdornment position="start"> + <StorageOutlinedIcon /> + </InputAdornment> + } + onChange={e => { + setBootstrapName(e.target.value); + initUpdateConfiguration(); + }} + /> + </FormControl> + </FormGroup> + </Grid> + <Grid item xs={12} sm={12} md={12}> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={dhtProxy} color="primary" onChange={e => { + setDhtProxy(e.target.checked); + handleUpdateConfiguration(); + }} name="dhtProxy" />} + label="Dht proxy" + /> + </FormGroup> + </Grid> + <Grid item xs={12} sm={12} md={4}> + </Grid> + <Grid item xs={12} sm={12} md={8}> + <FormGroup row> + <FormControl className={classes.margin} size="large"> + <InputLabel htmlFor="dhtProxyName">DHT proxy name</InputLabel> + <Input + disabled={!dhtProxy} + id="dhtProxyName" + placeholder={dhtProxyName} + startAdornment={ + <InputAdornment position="start"> + <LanguageOutlinedIcon /> + </InputAdornment> + } + onChange={e => { + setDhtProxyName(e.target.value); + initUpdateConfiguration(); + }} + /> + </FormControl> + </FormGroup> + </Grid> + <Grid item xs={12} sm={12} md={12}> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={pushNotifications} color="primary" onChange={e => { + setPushNotifications(e.target.checked); + handleUpdateConfiguration(); + }} name="pushNotifications" />} + label="Push notifications" + /> + </FormGroup> + </Grid> + </Grid> + </div> + </CardBody> + </Card> + </GridItem> + </GridContainer> + </div> + ); +} \ No newline at end of file diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js new file mode 100644 index 0000000000000000000000000000000000000000..4d447d5f05d4b216f68359328ac58ba2400afdaf --- /dev/null +++ b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js @@ -0,0 +1,284 @@ +import React from "react"; +import { useHistory } from 'react-router-dom'; +// @material-ui/core components +import { makeStyles } from "@material-ui/core/styles"; +import Checkbox from '@material-ui/core/Checkbox'; +import InputLabel from "@material-ui/core/InputLabel"; +// core components +import Grid from '@material-ui/core/Grid'; +import GridItem from "components/Grid/GridItem.js"; +import GridContainer from "components/Grid/GridContainer.js"; +import CustomInput from "components/CustomInput/CustomInput.js"; +import Button from "components/CustomButtons/Button.js"; +import Card from "components/Card/Card.js"; +import CardHeader from "components/Card/CardHeader.js"; +import CardIcon from "components/Card/CardIcon.js"; +import CardAvatar from "components/Card/CardAvatar.js"; +import CardBody from "components/Card/CardBody.js"; +import CardFooter from "components/Card/CardFooter.js"; +import Devices from "components/Devices/Devices.js"; +import Search from "@material-ui/icons/Search"; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import Input from '@material-ui/core/Input'; +import InputAdornment from '@material-ui/core/InputAdornment'; +import Slider from '@material-ui/core/Slider'; +import Typography from "@material-ui/core/Typography"; + +import Snackbar from '@material-ui/core/Snackbar'; + +import { hexToRgb, blackColor } from "assets/jss/material-dashboard-react.js"; + + +import PriorityHighOutlinedIcon from '@material-ui/icons/PriorityHighOutlined'; + +import axios from "axios" +import configApiCall from "../../api" +import { api_path_blueprint_permissions } from "../../globalUrls" + +import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; + +import MuiAlert from '@material-ui/lab/Alert'; + +const CONTAINER_HEIGHT = 200 + +const fileUpload = require('fuctbase64'); + + + +const styles = (theme)=> ( { + ...dashboardStyle, + root: { + flexGrow: 1 + }, + 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" + }, + input: { + display: 'none', + }, + profileAsBackground: { + backgroundSize: "100% 100%", + width: "80px", + height: "80px", + }, + centerIconMiddle: { + position:"relative", + top: "20px", + left: "15px" + }, + editProfilePicture: { + borderRadius: "50%", + width: "200px", + height: "200px", + boxShadow: + "0 6px 8px -12px rgba(" + + hexToRgb(blackColor) + + ", 0.56), 0 4px 25px 0px rgba(" + + hexToRgb(blackColor) + + ", 0.12), 0 8px 10px -5px rgba(" + + hexToRgb(blackColor) + + ", 0.2)", + }, + dialogPaper: { + minHeight: '60vh', + maxHeight: '60vh', + minWidth: '80vh', + maxWidth: '80vh' + } + }); + +const useStyles = makeStyles(styles); + +function Alert(props) { + return <MuiAlert elevation={6} variant="filled" {...props} />; +} + +export default function EditBlueprintPermissions(props) { + const classes = useStyles(); + const history = useHistory(); + + const [blueprintName, setBlueprintName] = React.useState("") + const [acceptIncomingCalls, setAcceptIncomingCalls] = React.useState(false) + const [acceptOutgoingCalls, setAcceptOutgoingCalls] = React.useState(false) + const [autoAnswer, setAutoAnswer] = React.useState(false) + const [activateChat, setActivateChat] = React.useState(false) + const [manageContacts, setManageContacts] = React.useState(false) + const [restrictAccesParameters, setRestrictAccesParameters] = React.useState(false) + const [allowNewAccount, setAllowNewAccount] = React.useState(false) + const [allowConnection, setAllowConnection] = React.useState(false) + + const [open, setOpen] = React.useState(false) + const [message, setMessage] = React.useState(false) + const [severity, setSeverity] = React.useState("success") + + React.useEffect(()=>{ + setBlueprintName(props.blueprintName) + axios(configApiCall(api_path_blueprint_permissions, 'GET', {"queryString": props.blueprintName}, null)).then((response) => { + response.data.map((blueprint) => { + setAcceptIncomingCalls(blueprint["acceptIncomingCalls"]) + setAcceptOutgoingCalls(blueprint["acceptOutgoingCalls"]) + setAutoAnswer(blueprint["autoAnswer"]) + setActivateChat(blueprint["activateChat"]) + setManageContacts(blueprint['manageContacts']) + setRestrictAccesParameters(blueprint["restrictAccesParameters"]) + setAllowNewAccount(blueprint["allowNewAccount"]) + setAllowConnection(blueprint["allowConnection"]) + }) + }).catch((error) => { + console.log("Error fetching blueprint permissions : " + props.blueprintName + " " + error) + }) + }, []) + + const handleUpdatePermissions = () => { + const data = { + 'acceptIncomingCalls': acceptIncomingCalls, + 'acceptOutgoingCalls': acceptOutgoingCalls, + 'autoAnswer': autoAnswer, + 'activateChat': activateChat, + 'manageContacts': manageContacts, + 'restrictAccesParameters': restrictAccesParameters, + 'allowNewAccount': allowNewAccount, + 'allowConnection': allowConnection + } + + axios(configApiCall(api_path_blueprint_permissions, 'PUT', data, null)).then((response) => { + setOpen(false) + setSeverity("success") + setOpen(true) + setMessage('Updated blueprint permissions successfully !') + }).catch((error) => { + setOpen(false) + setSeverity("error") + setOpen(true) + setMessage('Error updating blueprint permissions ' + error + '!') + }) + } + + const handleClose = () => { + setOpen(false) + } + + return( + <div> + <Snackbar + anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} + open={open} + onClose={handleClose} + message={message} + key={'bottomright'} + > + <Alert onClose={handleClose} severity={severity}> + {message} + </Alert> + </Snackbar> + <GridContainer> + <GridItem xs={12} sm={12} md={4}> + <Card profile> + <CardHeader color="info" stats icon> + <CardIcon color="info"> + <PriorityHighOutlinedIcon /> + </CardIcon> + <p className={classes.cardCategory}>Permissions</p> + <h3 className={classes.cardTitle}>{props.blueprintName}</h3> + </CardHeader> + <CardBody profile> + <div className={classes.root}> + <Grid container spacing={5}> + <Grid item xs={12} sm={12} md={6}> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={acceptIncomingCalls} color="primary" onChange={e => { + setAcceptIncomingCalls(e.target.checked); + handleUpdatePermissions(); + }} name="acceptIncomingCalls" />} + label="Accept incoming calls" + /> + </FormGroup> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={acceptOutgoingCalls} color="primary" onChange={e => { + setAcceptOutgoingCalls(e.target.checked); + handleUpdatePermissions(); + }} name="acceptOutgoingCalls" />} + label="Accept outgoing calls" + /> + </FormGroup> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={autoAnswer} color="primary" onChange={e => { + setAutoAnswer(e.target.checked); + handleUpdatePermissions(); + }} name="autoAnswer" />} + label="Auto-answer" + /> + </FormGroup> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={activateChat} color="primary" onChange={e => { + setActivateChat(e.target.checked); + handleUpdatePermissions(); + }} name="activateChat" />} + label="Activate chat" + /> + </FormGroup> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={manageContacts} color="primary" onChange={e => { + setManageContacts(e.target.checked); + handleUpdatePermissions(); + }} name="manageContacts" />} + label="Manage contacts" + /> + </FormGroup> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={restrictAccesParameters} color="primary" onChange={e => { + setRestrictAccesParameters(e.target.checked); + handleUpdatePermissions(); + }} name="restrictAccesParameters" />} + label="Restrict acces to parameters with password" + /> + </FormGroup> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={allowNewAccount} color="primary" onChange={e => { + setAllowNewAccount(e.target.checked); + handleUpdatePermissions(); + }} name="allowNewAccount" />} + label="Allow new account creation" + /> + </FormGroup> + <FormGroup row> + <FormControlLabel + control={<Checkbox checked={allowConnection} color="primary" onChange={e => { + setAllowConnection(e.target.checked); + handleUpdatePermissions(); + }} name="allowConnection" />} + label="Allow connection with other account" + /> + </FormGroup> + </Grid> + </Grid> + </div> + </CardBody> + </Card> + </GridItem> + </GridContainer> + </div> + ); +} \ No newline at end of file diff --git a/jams-react-client/src/views/Blueprints/Blueprints.js b/jams-react-client/src/views/Blueprints/Blueprints.js index 43996fab325d6629cdc75fca366305cfa4ecd285..d1915fcfd59372a5fcc92321f9006e5d4b824fcc 100644 --- a/jams-react-client/src/views/Blueprints/Blueprints.js +++ b/jams-react-client/src/views/Blueprints/Blueprints.js @@ -1,4 +1,6 @@ -import React from "react"; +import React, { useCallback, useEffect } from "react"; +import { useHistory } from 'react-router-dom'; +import { Switch, Route, Redirect } from "react-router-dom"; // @material-ui/core components import { makeStyles } from "@material-ui/core/styles"; import InputLabel from "@material-ui/core/InputLabel"; @@ -7,15 +9,56 @@ import GridItem from "components/Grid/GridItem.js"; import GridContainer from "components/Grid/GridContainer.js"; import CustomInput from "components/CustomInput/CustomInput.js"; import Button from "components/CustomButtons/Button.js"; +import IconButton from '@material-ui/core/IconButton'; import Card from "components/Card/Card.js"; import CardHeader from "components/Card/CardHeader.js"; import CardAvatar from "components/Card/CardAvatar.js"; import CardBody from "components/Card/CardBody.js"; import CardFooter from "components/Card/CardFooter.js"; +import UserProfile from "views/UserProfile/UserProfile.js" +import Divider from '@material-ui/core/Divider'; -import noProfilePicture from "assets/img/faces/no-profile-picture.png"; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import Input from '@material-ui/core/Input'; +import InputAdornment from '@material-ui/core/InputAdornment'; + +import PersonIcon from '@material-ui/icons/Person'; +import PermIdentityIcon from '@material-ui/icons/PermIdentity'; +import PhoneOutlinedIcon from '@material-ui/icons/PhoneOutlined'; +import GroupIcon from '@material-ui/icons/Group'; +import Search from "@material-ui/icons/Search"; +import DeleteOutlineIcon from '@material-ui/icons/DeleteOutline'; + +import AllInbox from "@material-ui/icons/AllInbox" +import MailOutlineIcon from '@material-ui/icons/MailOutline'; +import axios from "axios"; +import configApiCall from "api.js"; +import auth from 'auth.js' +import { api_path_blueprints } from "globalUrls"; + +import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; +import KeyboardReturnIcon from '@material-ui/icons/KeyboardReturn'; + +import jami from "assets/img/faces/jami.png"; + +import LinearProgress from '@material-ui/core/LinearProgress'; + +import headerLinksStyle from "assets/jss/material-dashboard-react/components/headerLinksStyle.js"; + +import Blueprint from 'views/Blueprint/Blueprint' + +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +import { debounce } from "lodash"; const styles = { + ...headerLinksStyle, cardCategoryWhite: { color: "rgba(255,255,255,.62)", margin: "0", @@ -31,6 +74,15 @@ const styles = { fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", marginBottom: "3px", textDecoration: "none" + }, + deleteIcon : { + float: "right" + }, + search : { + width: "90%" + }, + loading: { + width: '100%', } }; @@ -38,48 +90,226 @@ const useStyles = makeStyles(styles); export default function Blueprints() { const classes = useStyles(); - return ( - <div> - <GridContainer> - <GridItem xs={12} sm={12} md={4}> - <Card profile> - <CardAvatar profile> - <a href="#pablo" onClick={e => e.preventDefault()}> - <img src={noProfilePicture} alt="..." /> - </a> - </CardAvatar> - <CardBody profile> - <h6 className={classes.cardCategory}>CEO</h6> - <h4 className={classes.cardTitle}>Cyrille Béraud</h4> - <p className={classes.description}> - 13 devices - </p> - <Button color="primary" round> - Edit - </Button> - </CardBody> - </Card> - </GridItem> - <GridItem xs={12} sm={12} md={4}> - <Card profile> - <CardAvatar profile> - <a href="#pablo" onClick={e => e.preventDefault()}> - <img src={noProfilePicture} alt="..." /> - </a> - </CardAvatar> - <CardBody profile> - <h6 className={classes.cardCategory}>CEO</h6> - <h4 className={classes.cardTitle}>Christophe Villemaire</h4> - <p className={classes.description}> - 10 devices - </p> - <Button color="primary" round> - Edit - </Button> - </CardBody> - </Card> - </GridItem> - </GridContainer> - </div> - ); + const history = useHistory(); + const [blueprints, setBlueprints] = React.useState([]) + const [selectedBlueprint, setSelectedBlueprint] = React.useState(false) + const [selectedBlueprintName, setSelectedBlueprintName] = React.useState("") + const [loading, setLoading] = React.useState(false) + const [progress, setProgress] = React.useState(0); + const [searchValue, setSearchValue] = React.useState(false) + + const [blueprintName, setBlueprintName] = React.useState() + const [blueprintNameExits, setBlueprintNameExits,] = React.useState() + const [open, setOpen] = React.useState(false) + + const [removedBlueprint, setRemovedBlueprint] = React.useState() + const [openRemoveDialog, setOpenRemoveDialog] = React.useState() + + useEffect(() => { + setLoading(true) + const timer = setInterval(() => { + setProgress((oldProgress) => { + if (oldProgress === 100) { + return 0; + } + const diff = Math.random() * 10; + return Math.min(oldProgress + diff, 100); + }); + }, 500); + axios(configApiCall(api_path_blueprints, 'GET', {"queryString":"*"}, null)).then((response)=> { + setBlueprints(response.data) + setLoading(false) + }).catch((error) =>{ + setBlueprints([]) + console.log(error); + if(error.response.status == 401){ + auth.authenticated = false + history.push('/') + } + }); + return () => { + clearInterval(timer); + }; + }, []); + + function redirectToBlueprint(e, blueprintNameRedirect) { + e.preventDefault() + setSelectedBlueprint(true) + setSelectedBlueprintName(blueprintNameRedirect) + } + function redirectToBlueprints(e) { + e.preventDefault() + setSelectedBlueprint(false); + history.push('/admin/blueprints') + } + + const handleCheckBlueprintNameExists = (searchBlueprintNameValue) => { + axios(configApiCall(api_path_blueprints, 'GET', {"queryString": searchBlueprintNameValue}, null)).then((response)=>{ + setBlueprintNameExits(false) + }).catch((error) =>{ + setBlueprintNameExits(true) + }); + } + + const initCheckBlueprintNameExists = useCallback(debounce((searchBlueprintNameValue) => handleCheckBlueprintNameExists(searchBlueprintNameValue), 500), []) + + const handleClose = () => { + setOpen(false); + }; + + const handleCreateUser = () => { + axios(configApiCall(api_path_blueprints, 'POST', {"queryString": blueprintName}, null)).then((response)=>{ + console.log("Successfully create " + blueprintName) + }).catch((error) =>{ + console.log("Could not create " + blueprintName + " " + error) + }); + setOpen(false); + history.push('/admin/blueprints') + } + + const handleRemoveBlueprint = (blueprintRemovedName) => { + setRemovedBlueprint(blueprintRemovedName) + setOpenRemoveDialog(true) + } + + const removeBlueprint = () => { + axios(configApiCall(api_path_blueprints, 'DELETE', {"queryString": removedBlueprint}, null)).then((response)=>{ + console.log("Successfully create " + removedBlueprint) + }).catch((error) =>{ + console.log("Could not create " + removedBlueprint + " " + error) + }); + setOpenRemoveDialog(false); + history.push('/admin/blueprints') + } + + if (!auth.hasAdminScope()) { + return ( + <div> + <h4>You are not allowed to access this section. Please contact your administrator to get administrator privileges.</h4> + </div> + ) + } else if(selectedBlueprint && auth.hasAdminScope()){ + return ( + <div> + <Button variant="contained" color="info" href="#contained-buttons" onClick={redirectToBlueprints} style={{color: "white"}}> + <KeyboardReturnIcon /> return to blueprints + </Button> + <Blueprint blueprintName={selectedBlueprintName}/> + </div> + ) + } + else { + return ( + <div> + <Dialog + open={open} + onClose={handleClose} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + <DialogTitle id="alert-dialog-title">{"Create blueprint"}</DialogTitle> + <DialogContent> + <DialogContentText id="alert-dialog-description"> + <FormControl className={classes.margin} size="large" error={blueprintNameExits}> + <InputLabel htmlFor="blueprintName">Blueprint name</InputLabel> + <Input + id="blueprintName" + placeholder={blueprintName} + startAdornment={ + <InputAdornment position="start"> + <AllInbox /> + </InputAdornment> + } + onChange={e => { + const searchBlueprintNameValue = e.target.value; + setBlueprintName(searchBlueprintNameValue); + initCheckBlueprintNameExists(blueprintName); + }} + /> + </FormControl> + </DialogContentText> + </DialogContent> + <DialogActions> + <Button onClick={handleClose} color="danger"> + Cancel + </Button> + <Button onClick={handleCreateUser} color="primary" autoFocus> + Create + </Button> + </DialogActions> + </Dialog> + <Dialog + open={openRemoveDialog} + onClose={() => setOpenRemoveDialog(false)} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + <DialogTitle id="alert-dialog-title">{"Remove blueprint"}</DialogTitle> + <DialogContent> + <DialogContentText id="alert-dialog-description"> + Are you sure you want to delete <strong>{removedBlueprint}</strong> ? + </DialogContentText> + </DialogContent> + <DialogActions> + <Button onClick={() => setOpenRemoveDialog(false)} color="primary"> + Cancel + </Button> + <Button onClick={removeBlueprint} color="danger" autoFocus> + Remove + </Button> + </DialogActions> + </Dialog> + <GridContainer> + <GridItem xs={12} sm={12} md={12}> + {auth.isLocalDirectory() && + <Button variant="contained" color="primary" href="#contained-buttons" onClick={e => setOpen(true)}> + <AddCircleOutlineIcon /> Create blueprint + </Button> + } + <div className={classes.searchWrapper}> + <CustomInput + formControlProps={{ + className: classes.margin + " " + classes.search + }} + inputProps={{ + placeholder: "Search blueprints ...", + inputProps: { + "aria-label": "Search blueprints" + }, + onKeyUp: (e) => setSearchValue(e.target.value), + }} + /> + <Search /> + <div className={classes.loading}> + {loading && <LinearProgress variant="determinate" value={progress} />} + </div> + </div> + </GridItem> + { + blueprints.map(blueprint => + <GridItem xs={12} sm={12} md={2} key={blueprint.blueprintName}> + + <Card profile> + <CardBody profile> + <a href="#" onClick={(e) => redirectToBlueprint(e, blueprint.blueprintName)}> + + <h3 className={classes.cardTitle}>{blueprint.blueprintName ? blueprint.blueprintName : 'No blueprint name'}</h3> + <strong>Description:</strong> + <p>{blueprint.description ? blueprint.description : 'No description'}</p> + <ul> + <li><GroupIcon fontSize='small' style={{marginRight: "10px"}} /> {blueprint.usersCount ? blueprint.usersCount + ' user(s)' : 'No users count'}</li> + </ul> + </a> + </CardBody> + <CardFooter> + <IconButton color="secondary" onClick={ () => {handleRemoveBlueprint(blueprint.blueprintName)}}><DeleteOutlineIcon /></IconButton> + </CardFooter> + </Card> + + </GridItem>) + } + </GridContainer> + </div> + ); + } }