Upgrade to material ui 4
Add user management and roles - TBA Menu Label Renames - TBA
This commit is contained in:
		@@ -10,14 +10,12 @@ import orange from '@material-ui/core/colors/orange';
 | 
			
		||||
import red from '@material-ui/core/colors/red';
 | 
			
		||||
import green from '@material-ui/core/colors/green';
 | 
			
		||||
 | 
			
		||||
import JssProvider from 'react-jss/lib/JssProvider';
 | 
			
		||||
import { create } from 'jss';
 | 
			
		||||
import { StylesProvider, jssPreset } from '@material-ui/styles';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  MuiThemeProvider,
 | 
			
		||||
  createMuiTheme,
 | 
			
		||||
  createGenerateClassName,
 | 
			
		||||
  jssPreset,
 | 
			
		||||
  createMuiTheme
 | 
			
		||||
} from '@material-ui/core/styles';
 | 
			
		||||
 | 
			
		||||
// Our theme
 | 
			
		||||
@@ -35,20 +33,17 @@ const theme = createMuiTheme({
 | 
			
		||||
// JSS instance
 | 
			
		||||
const jss = create(jssPreset());
 | 
			
		||||
 | 
			
		||||
// Class name generator.
 | 
			
		||||
const generateClassName = createGenerateClassName();
 | 
			
		||||
 | 
			
		||||
class App extends Component {
 | 
			
		||||
	render() {
 | 
			
		||||
	   return (
 | 
			
		||||
		 <JssProvider jss={jss} generateClassName={generateClassName}>
 | 
			
		||||
		 <StylesProvider jss={jss}>
 | 
			
		||||
			<MuiThemeProvider theme={theme}>
 | 
			
		||||
        <SnackbarNotification>
 | 
			
		||||
				  <CssBaseline />
 | 
			
		||||
          <AppRouting />
 | 
			
		||||
        </SnackbarNotification>
 | 
			
		||||
			</MuiThemeProvider>
 | 
			
		||||
		 </JssProvider>
 | 
			
		||||
		 </StylesProvider>
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ import NTPConfiguration from './containers/NTPConfiguration';
 | 
			
		||||
import OTAConfiguration from './containers/OTAConfiguration';
 | 
			
		||||
import APConfiguration from './containers/APConfiguration';
 | 
			
		||||
import SignInPage from './containers/SignInPage';
 | 
			
		||||
import UserConfiguration from './containers/UserConfiguration';
 | 
			
		||||
import Security from './containers/Security';
 | 
			
		||||
 | 
			
		||||
class AppRouting extends Component {
 | 
			
		||||
 | 
			
		||||
@@ -31,7 +31,7 @@ class AppRouting extends Component {
 | 
			
		||||
          <AuthenticatedRoute exact path="/ap-configuration" component={APConfiguration} />
 | 
			
		||||
          <AuthenticatedRoute exact path="/ntp-configuration" component={NTPConfiguration} />
 | 
			
		||||
          <AuthenticatedRoute exact path="/ota-configuration" component={OTAConfiguration} />
 | 
			
		||||
          <AuthenticatedRoute exact path="/user-configuration" component={UserConfiguration} />
 | 
			
		||||
          <AuthenticatedRoute exact path="/security" component={Security} />
 | 
			
		||||
          <Redirect to="/" />
 | 
			
		||||
        </Switch>
 | 
			
		||||
      </AuthenticationWrapper>
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ import SystemUpdateIcon from  '@material-ui/icons/SystemUpdate';
 | 
			
		||||
import AccessTimeIcon from '@material-ui/icons/AccessTime';
 | 
			
		||||
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
 | 
			
		||||
import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna';
 | 
			
		||||
import PeopleIcon from '@material-ui/icons/People';
 | 
			
		||||
import LockIcon from '@material-ui/icons/Lock';
 | 
			
		||||
 | 
			
		||||
import { APP_NAME } from '../constants/App';
 | 
			
		||||
import { withAuthenticationContext } from '../authentication/Context.js';
 | 
			
		||||
@@ -112,31 +112,31 @@ class MenuAppBar extends React.Component {
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <WifiIcon />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText primary="WiFi Configuration" />
 | 
			
		||||
            <ListItemText primary="WiFi Connection" />
 | 
			
		||||
          </ListItem>
 | 
			
		||||
          <ListItem button component={Link} to='/ap-configuration'>
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <SettingsInputAntennaIcon />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText primary="AP Configuration" />
 | 
			
		||||
            <ListItemText primary="Access Point" />
 | 
			
		||||
          </ListItem>
 | 
			
		||||
          <ListItem button component={Link} to='/ntp-configuration'>
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <AccessTimeIcon />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText primary="NTP Configuration" />
 | 
			
		||||
            <ListItemText primary="Network Time" />
 | 
			
		||||
          </ListItem>
 | 
			
		||||
          <ListItem button component={Link} to='/ota-configuration'>
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <SystemUpdateIcon />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText primary="OTA Configuration" />
 | 
			
		||||
            <ListItemText primary="OTA Updates" />
 | 
			
		||||
          </ListItem>
 | 
			
		||||
          <ListItem button component={Link} to='/user-configuration'>
 | 
			
		||||
          <ListItem button component={Link} to='/security'>
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <PeopleIcon />
 | 
			
		||||
              <LockIcon />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText primary="User Configuration" />
 | 
			
		||||
            <ListItemText primary="Security" />
 | 
			
		||||
          </ListItem>          
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <ListItem button onClick={authenticationContext.signOut}>
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ function SectionContent(props) {
 | 
			
		||||
  const { children, classes, title } = props;
 | 
			
		||||
  return (
 | 
			
		||||
      <Paper className={classes.content}>
 | 
			
		||||
        <Typography variant="h4">
 | 
			
		||||
        <Typography variant="h6">
 | 
			
		||||
          {title}
 | 
			
		||||
        </Typography>
 | 
			
		||||
        {children}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,17 +7,18 @@ import Typography from '@material-ui/core/Typography';
 | 
			
		||||
import List from '@material-ui/core/List';
 | 
			
		||||
import ListItem from '@material-ui/core/ListItem';
 | 
			
		||||
import ListItemText from '@material-ui/core/ListItemText';
 | 
			
		||||
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
 | 
			
		||||
import Avatar from '@material-ui/core/Avatar';
 | 
			
		||||
import Divider from '@material-ui/core/Divider';
 | 
			
		||||
import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna';
 | 
			
		||||
import DeviceHubIcon from '@material-ui/icons/DeviceHub';
 | 
			
		||||
import ComputerIcon from '@material-ui/icons/Computer';
 | 
			
		||||
 | 
			
		||||
import {restComponent} from '../components/RestComponent';
 | 
			
		||||
import { restComponent } from '../components/RestComponent';
 | 
			
		||||
import SectionContent from '../components/SectionContent'
 | 
			
		||||
 | 
			
		||||
import * as Highlight from '../constants/Highlight';
 | 
			
		||||
import { AP_STATUS_ENDPOINT }  from  '../constants/Endpoints';
 | 
			
		||||
import { AP_STATUS_ENDPOINT } from '../constants/Endpoints';
 | 
			
		||||
 | 
			
		||||
const styles = theme => ({
 | 
			
		||||
  ["apStatus_" + Highlight.SUCCESS]: {
 | 
			
		||||
@@ -42,40 +43,48 @@ class APStatus extends Component {
 | 
			
		||||
    this.props.loadData();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  apStatusHighlight(data){
 | 
			
		||||
  apStatusHighlight(data) {
 | 
			
		||||
    return data.active ? Highlight.SUCCESS : Highlight.IDLE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  apStatus(data){
 | 
			
		||||
  apStatus(data) {
 | 
			
		||||
    return data.active ? "Active" : "Inactive";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createListItems(data, classes){
 | 
			
		||||
  createListItems(data, classes) {
 | 
			
		||||
    return (
 | 
			
		||||
      <Fragment>
 | 
			
		||||
        <ListItem>
 | 
			
		||||
          <Avatar className={classes["apStatus_" + this.apStatusHighlight(data)]}>
 | 
			
		||||
            <SettingsInputAntennaIcon />
 | 
			
		||||
          </Avatar>
 | 
			
		||||
          <ListItemAvatar>
 | 
			
		||||
            <Avatar className={classes["apStatus_" + this.apStatusHighlight(data)]}>
 | 
			
		||||
              <SettingsInputAntennaIcon />
 | 
			
		||||
            </Avatar>
 | 
			
		||||
          </ListItemAvatar>
 | 
			
		||||
          <ListItemText primary="Status" secondary={this.apStatus(data)} />
 | 
			
		||||
        </ListItem>
 | 
			
		||||
        <Divider variant="inset" component="li" />
 | 
			
		||||
        <ListItem>
 | 
			
		||||
          <Avatar>IP</Avatar>
 | 
			
		||||
          <ListItemAvatar>
 | 
			
		||||
            <Avatar>IP</Avatar>
 | 
			
		||||
          </ListItemAvatar>
 | 
			
		||||
          <ListItemText primary="IP Address" secondary={data.ip_address} />
 | 
			
		||||
        </ListItem>
 | 
			
		||||
        <Divider variant="inset" component="li" />
 | 
			
		||||
        <ListItem>
 | 
			
		||||
          <Avatar>
 | 
			
		||||
            <DeviceHubIcon />
 | 
			
		||||
          </Avatar>
 | 
			
		||||
          <ListItemAvatar>
 | 
			
		||||
            <Avatar>
 | 
			
		||||
              <DeviceHubIcon />
 | 
			
		||||
            </Avatar>
 | 
			
		||||
          </ListItemAvatar>
 | 
			
		||||
          <ListItemText primary="MAC Address" secondary={data.mac_address} />
 | 
			
		||||
        </ListItem>
 | 
			
		||||
        <Divider variant="inset" component="li" />
 | 
			
		||||
        <ListItem>
 | 
			
		||||
          <Avatar>
 | 
			
		||||
            <ComputerIcon />
 | 
			
		||||
          </Avatar>
 | 
			
		||||
          <ListItemAvatar>
 | 
			
		||||
            <Avatar>
 | 
			
		||||
              <ComputerIcon />
 | 
			
		||||
            </Avatar>
 | 
			
		||||
          </ListItemAvatar>
 | 
			
		||||
          <ListItemText primary="AP Clients" secondary={data.station_num} />
 | 
			
		||||
        </ListItem>
 | 
			
		||||
        <Divider variant="inset" component="li" />
 | 
			
		||||
@@ -83,8 +92,8 @@ class APStatus extends Component {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderAPStatus(data, classes){
 | 
			
		||||
    return  (
 | 
			
		||||
  renderAPStatus(data, classes) {
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
        <List>
 | 
			
		||||
          <Fragment>
 | 
			
		||||
@@ -99,30 +108,30 @@ class APStatus extends Component {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { data, fetched, errorMessage, classes }  = this.props;
 | 
			
		||||
    const { data, fetched, errorMessage, classes } = this.props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <SectionContent title="AP Status">
 | 
			
		||||
        {
 | 
			
		||||
         !fetched ?
 | 
			
		||||
         <div>
 | 
			
		||||
           <LinearProgress className={classes.fetching}/>
 | 
			
		||||
           <Typography variant="h4" className={classes.fetching}>
 | 
			
		||||
             Loading...
 | 
			
		||||
          !fetched ?
 | 
			
		||||
            <div>
 | 
			
		||||
              <LinearProgress className={classes.fetching} />
 | 
			
		||||
              <Typography variant="h4" className={classes.fetching}>
 | 
			
		||||
                Loading...
 | 
			
		||||
           </Typography>
 | 
			
		||||
         </div>
 | 
			
		||||
       :
 | 
			
		||||
        data ? this.renderAPStatus(data, classes)
 | 
			
		||||
       :
 | 
			
		||||
        <div>
 | 
			
		||||
          <Typography variant="h4" className={classes.fetching}>
 | 
			
		||||
            {errorMessage}
 | 
			
		||||
          </Typography>
 | 
			
		||||
          <Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
 | 
			
		||||
            Refresh
 | 
			
		||||
            </div>
 | 
			
		||||
            :
 | 
			
		||||
            data ? this.renderAPStatus(data, classes)
 | 
			
		||||
              :
 | 
			
		||||
              <div>
 | 
			
		||||
                <Typography variant="h4" className={classes.fetching}>
 | 
			
		||||
                  {errorMessage}
 | 
			
		||||
                </Typography>
 | 
			
		||||
                <Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
 | 
			
		||||
                  Refresh
 | 
			
		||||
          </Button>
 | 
			
		||||
        </div>
 | 
			
		||||
      }
 | 
			
		||||
              </div>
 | 
			
		||||
        }
 | 
			
		||||
      </SectionContent>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,30 +1,27 @@
 | 
			
		||||
import React, { Component } from 'react';
 | 
			
		||||
 | 
			
		||||
import { USERS_ENDPOINT }  from  '../constants/Endpoints';
 | 
			
		||||
import {restComponent} from '../components/RestComponent';
 | 
			
		||||
import SectionContent from '../components/SectionContent';
 | 
			
		||||
import { USERS_ENDPOINT } from '../constants/Endpoints';
 | 
			
		||||
import { restComponent } from '../components/RestComponent';
 | 
			
		||||
import ManageUsersForm from '../forms/ManageUsersForm';
 | 
			
		||||
 | 
			
		||||
class ManageUsers extends Component {
 | 
			
		||||
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
      this.props.loadData();
 | 
			
		||||
    this.props.loadData();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { data, fetched, errorMessage } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <SectionContent title="Manage Users">
 | 
			
		||||
      	<ManageUsersForm
 | 
			
		||||
          users={data}
 | 
			
		||||
          usersFetched={fetched}
 | 
			
		||||
          errorMessage={errorMessage}
 | 
			
		||||
          onSubmit={this.props.saveData}
 | 
			
		||||
          onReset={this.props.loadData}
 | 
			
		||||
          handleValueChange={this.props.handleValueChange}
 | 
			
		||||
          handleCheckboxChange={this.props.handleCheckboxChange}
 | 
			
		||||
        />
 | 
			
		||||
      </SectionContent>
 | 
			
		||||
      <ManageUsersForm
 | 
			
		||||
        userData={data}
 | 
			
		||||
        userDataFetched={fetched}
 | 
			
		||||
        errorMessage={errorMessage}
 | 
			
		||||
        onSubmit={this.props.saveData}
 | 
			
		||||
        onReset={this.props.loadData}
 | 
			
		||||
        setData={this.props.setData}
 | 
			
		||||
        handleValueChange={this.props.handleValueChange}
 | 
			
		||||
      />
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import LinearProgress from '@material-ui/core/LinearProgress';
 | 
			
		||||
import Typography from '@material-ui/core/Typography';
 | 
			
		||||
import List from '@material-ui/core/List';
 | 
			
		||||
import ListItem from '@material-ui/core/ListItem';
 | 
			
		||||
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
 | 
			
		||||
import ListItemText from '@material-ui/core/ListItemText';
 | 
			
		||||
import Avatar from '@material-ui/core/Avatar';
 | 
			
		||||
import Divider from '@material-ui/core/Divider';
 | 
			
		||||
@@ -16,10 +17,10 @@ import TimerIcon from '@material-ui/icons/Timer';
 | 
			
		||||
import UpdateIcon from '@material-ui/icons/Update';
 | 
			
		||||
import AvTimerIcon from '@material-ui/icons/AvTimer';
 | 
			
		||||
 | 
			
		||||
import { isSynchronized, ntpStatusHighlight, ntpStatus }  from  '../constants/NTPStatus';
 | 
			
		||||
import { isSynchronized, ntpStatusHighlight, ntpStatus } from '../constants/NTPStatus';
 | 
			
		||||
import * as Highlight from '../constants/Highlight';
 | 
			
		||||
import { unixTimeToTimeAndDate } from '../constants/TimeFormat';
 | 
			
		||||
import { NTP_STATUS_ENDPOINT }  from  '../constants/Endpoints';
 | 
			
		||||
import { NTP_STATUS_ENDPOINT } from '../constants/Endpoints';
 | 
			
		||||
import { restComponent } from '../components/RestComponent';
 | 
			
		||||
import SectionContent from '../components/SectionContent';
 | 
			
		||||
 | 
			
		||||
@@ -51,52 +52,61 @@ class NTPStatus extends Component {
 | 
			
		||||
    this.props.loadData();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createListItems(data, classes){
 | 
			
		||||
  createListItems(data, classes) {
 | 
			
		||||
    return (
 | 
			
		||||
      <Fragment>
 | 
			
		||||
        <ListItem>
 | 
			
		||||
          <Avatar className={classes["ntpStatus_" + ntpStatusHighlight(data)]}>
 | 
			
		||||
            <UpdateIcon />
 | 
			
		||||
          </Avatar>
 | 
			
		||||
        <ListItem >
 | 
			
		||||
          <ListItemAvatar>
 | 
			
		||||
            <Avatar className={classes["ntpStatus_" + ntpStatusHighlight(data)]}>
 | 
			
		||||
              <UpdateIcon />
 | 
			
		||||
            </Avatar>
 | 
			
		||||
          </ListItemAvatar>
 | 
			
		||||
          <ListItemText primary="Status" secondary={ntpStatus(data)} />
 | 
			
		||||
        </ListItem>
 | 
			
		||||
        <Divider variant="inset" component="li" />
 | 
			
		||||
        { isSynchronized(data) &&
 | 
			
		||||
        {isSynchronized(data) &&
 | 
			
		||||
          <Fragment>
 | 
			
		||||
            <ListItem>
 | 
			
		||||
              <Avatar>
 | 
			
		||||
                <AccessTimeIcon />
 | 
			
		||||
              </Avatar>
 | 
			
		||||
              <ListItemAvatar>
 | 
			
		||||
                <Avatar>
 | 
			
		||||
                  <AccessTimeIcon />
 | 
			
		||||
                </Avatar>
 | 
			
		||||
              </ListItemAvatar>
 | 
			
		||||
              <ListItemText primary="Time Now" secondary={unixTimeToTimeAndDate(data.now)} />
 | 
			
		||||
            </ListItem>
 | 
			
		||||
            <Divider variant="inset" component="li" />
 | 
			
		||||
            <ListItem>
 | 
			
		||||
              <Avatar>
 | 
			
		||||
                <SwapVerticalCircleIcon />
 | 
			
		||||
              </Avatar>
 | 
			
		||||
              <ListItemText primary="Last Sync" secondary={data.last_sync > 0 ? unixTimeToTimeAndDate(data.last_sync) : "never" } />
 | 
			
		||||
              <ListItemAvatar>
 | 
			
		||||
                <Avatar>
 | 
			
		||||
                  <SwapVerticalCircleIcon />
 | 
			
		||||
                </Avatar></ListItemAvatar>
 | 
			
		||||
              <ListItemText primary="Last Sync" secondary={data.last_sync > 0 ? unixTimeToTimeAndDate(data.last_sync) : "never"} />
 | 
			
		||||
            </ListItem>
 | 
			
		||||
            <Divider variant="inset" component="li" />
 | 
			
		||||
          </Fragment>
 | 
			
		||||
        }
 | 
			
		||||
        <ListItem>
 | 
			
		||||
          <Avatar>
 | 
			
		||||
            <DNSIcon />
 | 
			
		||||
          </Avatar>
 | 
			
		||||
          <ListItemAvatar>
 | 
			
		||||
            <Avatar>
 | 
			
		||||
              <DNSIcon />
 | 
			
		||||
            </Avatar>
 | 
			
		||||
          </ListItemAvatar>
 | 
			
		||||
          <ListItemText primary="NTP Server" secondary={data.server} />
 | 
			
		||||
        </ListItem>
 | 
			
		||||
        <Divider variant="inset" component="li" />
 | 
			
		||||
        <ListItem>
 | 
			
		||||
          <Avatar>
 | 
			
		||||
            <TimerIcon />
 | 
			
		||||
          </Avatar>
 | 
			
		||||
          <ListItemAvatar>
 | 
			
		||||
            <Avatar>
 | 
			
		||||
              <TimerIcon />
 | 
			
		||||
            </Avatar></ListItemAvatar>
 | 
			
		||||
          <ListItemText primary="Sync Interval" secondary={moment.duration(data.interval, 'seconds').humanize()} />
 | 
			
		||||
        </ListItem>
 | 
			
		||||
        <Divider variant="inset" component="li" />
 | 
			
		||||
        <ListItem>
 | 
			
		||||
          <Avatar>
 | 
			
		||||
            <AvTimerIcon />
 | 
			
		||||
          </Avatar>
 | 
			
		||||
          <ListItemAvatar>
 | 
			
		||||
            <Avatar>
 | 
			
		||||
              <AvTimerIcon />
 | 
			
		||||
            </Avatar></ListItemAvatar>
 | 
			
		||||
          <ListItemText primary="Uptime" secondary={moment.duration(data.uptime, 'seconds').humanize()} />
 | 
			
		||||
        </ListItem>
 | 
			
		||||
        <Divider variant="inset" component="li" />
 | 
			
		||||
@@ -104,8 +114,8 @@ class NTPStatus extends Component {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderNTPStatus(data, classes){
 | 
			
		||||
    return  (
 | 
			
		||||
  renderNTPStatus(data, classes) {
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
        <List>
 | 
			
		||||
          {this.createListItems(data, classes)}
 | 
			
		||||
@@ -118,30 +128,30 @@ class NTPStatus extends Component {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { data, fetched, errorMessage, classes }  = this.props;
 | 
			
		||||
    const { data, fetched, errorMessage, classes } = this.props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <SectionContent title="NTP Status">
 | 
			
		||||
        {
 | 
			
		||||
         !fetched ?
 | 
			
		||||
         <div>
 | 
			
		||||
           <LinearProgress className={classes.fetching}/>
 | 
			
		||||
           <Typography variant="h4" className={classes.fetching}>
 | 
			
		||||
             Loading...
 | 
			
		||||
          !fetched ?
 | 
			
		||||
            <div>
 | 
			
		||||
              <LinearProgress className={classes.fetching} />
 | 
			
		||||
              <Typography variant="h4" className={classes.fetching}>
 | 
			
		||||
                Loading...
 | 
			
		||||
           </Typography>
 | 
			
		||||
         </div>
 | 
			
		||||
       :
 | 
			
		||||
        data ? this.renderNTPStatus(data, classes)
 | 
			
		||||
       :
 | 
			
		||||
        <div>
 | 
			
		||||
          <Typography variant="h4" className={classes.fetching}>
 | 
			
		||||
            {errorMessage}
 | 
			
		||||
          </Typography>
 | 
			
		||||
          <Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
 | 
			
		||||
            Refresh
 | 
			
		||||
            </div>
 | 
			
		||||
            :
 | 
			
		||||
            data ? this.renderNTPStatus(data, classes)
 | 
			
		||||
              :
 | 
			
		||||
              <div>
 | 
			
		||||
                <Typography variant="h4" className={classes.fetching}>
 | 
			
		||||
                  {errorMessage}
 | 
			
		||||
                </Typography>
 | 
			
		||||
                <Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
 | 
			
		||||
                  Refresh
 | 
			
		||||
          </Button>
 | 
			
		||||
        </div>
 | 
			
		||||
      }
 | 
			
		||||
              </div>
 | 
			
		||||
        }
 | 
			
		||||
      </SectionContent>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,14 @@ import React, { Component } from 'react';
 | 
			
		||||
import MenuAppBar from '../components/MenuAppBar';
 | 
			
		||||
import ManageUsers from './ManageUsers';
 | 
			
		||||
 | 
			
		||||
class UserConfiguration extends Component {
 | 
			
		||||
class Security extends Component {
 | 
			
		||||
  render() {
 | 
			
		||||
    return (
 | 
			
		||||
        <MenuAppBar sectionTitle="User Configuration">
 | 
			
		||||
        <MenuAppBar sectionTitle="Security">
 | 
			
		||||
          <ManageUsers />
 | 
			
		||||
        </MenuAppBar>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default UserConfiguration
 | 
			
		||||
export default Security
 | 
			
		||||
@@ -8,6 +8,7 @@ import Typography from '@material-ui/core/Typography';
 | 
			
		||||
import List from '@material-ui/core/List';
 | 
			
		||||
import ListItem from '@material-ui/core/ListItem';
 | 
			
		||||
import ListItemText from '@material-ui/core/ListItemText';
 | 
			
		||||
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
 | 
			
		||||
 | 
			
		||||
import Avatar from '@material-ui/core/Avatar';
 | 
			
		||||
import Divider from '@material-ui/core/Divider';
 | 
			
		||||
@@ -63,9 +64,11 @@ class WiFiStatus extends Component {
 | 
			
		||||
    return (
 | 
			
		||||
      <Fragment>
 | 
			
		||||
        <ListItem>
 | 
			
		||||
          <Avatar className={classes["wifiStatus_" + connectionStatusHighlight(data)]}>
 | 
			
		||||
            <WifiIcon />
 | 
			
		||||
          </Avatar>
 | 
			
		||||
          <ListItemAvatar>
 | 
			
		||||
            <Avatar className={classes["wifiStatus_" + connectionStatusHighlight(data)]}>
 | 
			
		||||
              <WifiIcon />
 | 
			
		||||
            </Avatar>
 | 
			
		||||
          </ListItemAvatar>
 | 
			
		||||
          <ListItemText primary="Connection Status" secondary={connectionStatus(data)} />
 | 
			
		||||
        </ListItem>
 | 
			
		||||
        <Divider variant="inset" component="li" />
 | 
			
		||||
@@ -73,40 +76,52 @@ class WiFiStatus extends Component {
 | 
			
		||||
          isConnected(data) &&
 | 
			
		||||
          <Fragment>
 | 
			
		||||
            <ListItem>
 | 
			
		||||
              <Avatar>
 | 
			
		||||
                <SettingsInputAntennaIcon />
 | 
			
		||||
              </Avatar>
 | 
			
		||||
              <ListItemAvatar>
 | 
			
		||||
                <Avatar>
 | 
			
		||||
                  <SettingsInputAntennaIcon />
 | 
			
		||||
                </Avatar>
 | 
			
		||||
              </ListItemAvatar>
 | 
			
		||||
              <ListItemText primary="SSID" secondary={data.ssid} />
 | 
			
		||||
            </ListItem>
 | 
			
		||||
            <Divider variant="inset" component="li" />
 | 
			
		||||
            <ListItem>
 | 
			
		||||
              <Avatar>IP</Avatar>
 | 
			
		||||
              <ListItemAvatar>
 | 
			
		||||
                <Avatar>IP</Avatar>
 | 
			
		||||
              </ListItemAvatar>
 | 
			
		||||
              <ListItemText primary="IP Address" secondary={data.local_ip} />
 | 
			
		||||
            </ListItem>
 | 
			
		||||
            <Divider variant="inset" component="li" />
 | 
			
		||||
            <ListItem>
 | 
			
		||||
              <Avatar>
 | 
			
		||||
                <DeviceHubIcon />
 | 
			
		||||
              </Avatar>
 | 
			
		||||
              <ListItemAvatar>
 | 
			
		||||
                <Avatar>
 | 
			
		||||
                  <DeviceHubIcon />
 | 
			
		||||
                </Avatar>
 | 
			
		||||
              </ListItemAvatar>
 | 
			
		||||
              <ListItemText primary="MAC Address" secondary={data.mac_address} />
 | 
			
		||||
            </ListItem>
 | 
			
		||||
            <Divider variant="inset" component="li" />
 | 
			
		||||
            <ListItem>
 | 
			
		||||
              <Avatar>#</Avatar>
 | 
			
		||||
              <ListItemAvatar>
 | 
			
		||||
                <Avatar>#</Avatar>
 | 
			
		||||
              </ListItemAvatar>
 | 
			
		||||
              <ListItemText primary="Subnet Mask" secondary={data.subnet_mask} />
 | 
			
		||||
            </ListItem>
 | 
			
		||||
            <Divider variant="inset" component="li" />
 | 
			
		||||
            <ListItem>
 | 
			
		||||
              <Avatar>
 | 
			
		||||
                <SettingsInputComponentIcon />
 | 
			
		||||
              </Avatar>
 | 
			
		||||
              <ListItemAvatar>
 | 
			
		||||
                <Avatar>
 | 
			
		||||
                  <SettingsInputComponentIcon />
 | 
			
		||||
                </Avatar>
 | 
			
		||||
              </ListItemAvatar>
 | 
			
		||||
              <ListItemText primary="Gateway IP" secondary={data.gateway_ip ? data.gateway_ip : "none"} />
 | 
			
		||||
            </ListItem>
 | 
			
		||||
            <Divider variant="inset" component="li" />
 | 
			
		||||
            <ListItem>
 | 
			
		||||
              <Avatar>
 | 
			
		||||
                <DNSIcon />
 | 
			
		||||
              </Avatar>
 | 
			
		||||
              <ListItemAvatar>
 | 
			
		||||
                <Avatar>
 | 
			
		||||
                  <DNSIcon />
 | 
			
		||||
                </Avatar>
 | 
			
		||||
              </ListItemAvatar>
 | 
			
		||||
              <ListItemText primary="DNS Server IP" secondary={this.dnsServers(data)} />
 | 
			
		||||
            </ListItem>
 | 
			
		||||
            <Divider variant="inset" component="li" />
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,26 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
 | 
			
		||||
import { ValidatorForm } from 'react-material-ui-form-validator';
 | 
			
		||||
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
 | 
			
		||||
 | 
			
		||||
import { withStyles } from '@material-ui/core/styles';
 | 
			
		||||
import Button from '@material-ui/core/Button';
 | 
			
		||||
import LinearProgress from '@material-ui/core/LinearProgress';
 | 
			
		||||
import Typography from '@material-ui/core/Typography';
 | 
			
		||||
 | 
			
		||||
import Table from '@material-ui/core/Table';
 | 
			
		||||
import TableBody from '@material-ui/core/TableBody';
 | 
			
		||||
import TableCell from '@material-ui/core/TableCell';
 | 
			
		||||
import TableFooter from '@material-ui/core/TableFooter';
 | 
			
		||||
import TableHead from '@material-ui/core/TableHead';
 | 
			
		||||
import TableRow from '@material-ui/core/TableRow';
 | 
			
		||||
import IconButton from '@material-ui/core/IconButton';
 | 
			
		||||
import Chip from '@material-ui/core/Chip';
 | 
			
		||||
 | 
			
		||||
import EditIcon from '@material-ui/icons/Edit';
 | 
			
		||||
import DeleteIcon from '@material-ui/icons/Delete';
 | 
			
		||||
import Chip from '@material-ui/core/Chip';
 | 
			
		||||
import IconButton from '@material-ui/core/IconButton';
 | 
			
		||||
 | 
			
		||||
import SectionContent from '../components/SectionContent';
 | 
			
		||||
import UserForm from './UserForm';
 | 
			
		||||
 | 
			
		||||
const styles = theme => ({
 | 
			
		||||
  loadingSettings: {
 | 
			
		||||
@@ -43,95 +46,198 @@ const styles = theme => ({
 | 
			
		||||
    margin: theme.spacing.unit,
 | 
			
		||||
  },
 | 
			
		||||
  table: {
 | 
			
		||||
    '& td, & th': {padding: theme.spacing.unit}    
 | 
			
		||||
  }    
 | 
			
		||||
    '& td, & th': { padding: theme.spacing.unit }
 | 
			
		||||
  },
 | 
			
		||||
  actions: {
 | 
			
		||||
    color: theme.palette.text.secondary,
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function compareUsers(a, b) {
 | 
			
		||||
  if (a.username < b.username) {
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  if (a.username > b.username) {
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ManageUsersForm extends React.Component {
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { classes, users, usersFetched, errorMessage, onSubmit, onReset } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
        {
 | 
			
		||||
          !usersFetched ?
 | 
			
		||||
  constructor(props) {
 | 
			
		||||
    super(props);
 | 
			
		||||
    this.state = {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createUser = () => {
 | 
			
		||||
    this.setState({
 | 
			
		||||
      creating: true,
 | 
			
		||||
      user: {
 | 
			
		||||
        username: "",
 | 
			
		||||
        password: "",
 | 
			
		||||
        roles: []
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  uniqueUsername = username => {
 | 
			
		||||
    return !this.props.userData.users.find(u => u.username === username);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  usersValid = username => {
 | 
			
		||||
    return !!this.props.userData.users.find(u => u.roles.includes("admin"));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  startEditingUser = user => {
 | 
			
		||||
    this.setState({
 | 
			
		||||
      creating: false,
 | 
			
		||||
      user
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  cancelEditingUser = () => {
 | 
			
		||||
    this.setState({
 | 
			
		||||
      user: undefined
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sortedUsers(users) {
 | 
			
		||||
    return users.sort(compareUsers);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  doneEditingUser = () => {
 | 
			
		||||
    const { user } = this.state;
 | 
			
		||||
    const { userData } = this.props;
 | 
			
		||||
    let { users } = userData;
 | 
			
		||||
    users = users.filter(u => u.username !== user.username);
 | 
			
		||||
    users.push(user);
 | 
			
		||||
    this.props.setData({ ...userData, users });
 | 
			
		||||
    this.setState({
 | 
			
		||||
      user: undefined
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleUserValueChange = name => event => {
 | 
			
		||||
    const { user } = this.state;
 | 
			
		||||
    if (user) {
 | 
			
		||||
      this.setState({
 | 
			
		||||
        user: {
 | 
			
		||||
          ...user, [name]: event.target.value
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { classes, userData, userDataFetched, errorMessage, onSubmit, onReset, handleValueChange } = this.props;
 | 
			
		||||
    const { user, creating } = this.state;
 | 
			
		||||
    return (
 | 
			
		||||
      <SectionContent title="Manage Users">
 | 
			
		||||
        {
 | 
			
		||||
          !userDataFetched ?
 | 
			
		||||
            <div className={classes.loadingSettings}>
 | 
			
		||||
              <LinearProgress className={classes.loadingSettingsDetails} />
 | 
			
		||||
              <Typography variant="h4" className={classes.loadingSettingsDetails}>
 | 
			
		||||
                Loading...
 | 
			
		||||
              </Typography>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            : users ?
 | 
			
		||||
 | 
			
		||||
              <ValidatorForm onSubmit={onSubmit}>
 | 
			
		||||
                <Table className={classes.table}>
 | 
			
		||||
                  <TableHead>
 | 
			
		||||
                    <TableRow>
 | 
			
		||||
                      <TableCell>Username</TableCell>
 | 
			
		||||
                      <TableCell>Password</TableCell>
 | 
			
		||||
                      <TableCell align="center">Role(s)</TableCell>
 | 
			
		||||
                      <TableCell align="center">Action</TableCell>
 | 
			
		||||
                    </TableRow>
 | 
			
		||||
                  </TableHead>
 | 
			
		||||
                  <TableBody>
 | 
			
		||||
                    {users.users.map(user => (
 | 
			
		||||
                      <TableRow key={user.username}>
 | 
			
		||||
                        <TableCell component="th" scope="row">
 | 
			
		||||
                          {user.username}
 | 
			
		||||
                        </TableCell>
 | 
			
		||||
                        <TableCell>{user.password}</TableCell>
 | 
			
		||||
                        <TableCell align="center">
 | 
			
		||||
                          <Chip label={user.role} className={classes.chip} />
 | 
			
		||||
            :
 | 
			
		||||
            userData ?
 | 
			
		||||
              user ?
 | 
			
		||||
                <UserForm
 | 
			
		||||
                  user={user}
 | 
			
		||||
                  creating={creating}
 | 
			
		||||
                  roles={userData.roles}
 | 
			
		||||
                  onDoneEditing={this.doneEditingUser}
 | 
			
		||||
                  onCancelEditing={this.cancelEditingUser}
 | 
			
		||||
                  handleValueChange={this.handleUserValueChange}
 | 
			
		||||
                  uniqueUsername={this.uniqueUsername}
 | 
			
		||||
                />
 | 
			
		||||
                :
 | 
			
		||||
                <ValidatorForm onSubmit={onSubmit}>  
 | 
			
		||||
                  <Table className={classes.table}>
 | 
			
		||||
                    <TableHead>
 | 
			
		||||
                      <TableRow>
 | 
			
		||||
                        <TableCell>Username</TableCell>
 | 
			
		||||
                        <TableCell align="center">Role(s)</TableCell>
 | 
			
		||||
                        <TableCell align="center">Action</TableCell>
 | 
			
		||||
                      </TableRow>
 | 
			
		||||
                    </TableHead>
 | 
			
		||||
                    <TableBody>
 | 
			
		||||
                      {this.sortedUsers(userData.users).map(user => (
 | 
			
		||||
                        <TableRow key={user.username}>
 | 
			
		||||
                          <TableCell component="th" scope="row">
 | 
			
		||||
                            {user.username}
 | 
			
		||||
                          </TableCell>
 | 
			
		||||
                          <TableCell align="center">
 | 
			
		||||
                            {
 | 
			
		||||
                              user.roles.map(role => (
 | 
			
		||||
                                <Chip label={role} className={classes.chip} />
 | 
			
		||||
                              ))
 | 
			
		||||
                            }
 | 
			
		||||
                          </TableCell>
 | 
			
		||||
                          <TableCell align="center">
 | 
			
		||||
                            <IconButton aria-label="Delete">
 | 
			
		||||
                              <DeleteIcon />
 | 
			
		||||
                            </IconButton>
 | 
			
		||||
                            <IconButton aria-label="Edit" onClick={() => this.startEditingUser(user)}>
 | 
			
		||||
                              <EditIcon />
 | 
			
		||||
                            </IconButton>
 | 
			
		||||
                          </TableCell>
 | 
			
		||||
                        </TableRow>
 | 
			
		||||
                      ))}
 | 
			
		||||
                    </TableBody>
 | 
			
		||||
                    <TableFooter>
 | 
			
		||||
                      <TableRow>
 | 
			
		||||
                        <TableCell colSpan={2}>
 | 
			
		||||
                          {
 | 
			
		||||
                            !this.usersValid() &&
 | 
			
		||||
                            <Typography variant="body1" color="error">
 | 
			
		||||
                              You must have at least one admin user configured.
 | 
			
		||||
                            </Typography>
 | 
			
		||||
                          }
 | 
			
		||||
                        </TableCell>
 | 
			
		||||
                        <TableCell align="center">
 | 
			
		||||
                          <IconButton aria-label="Delete">
 | 
			
		||||
                            <DeleteIcon />
 | 
			
		||||
                          </IconButton>
 | 
			
		||||
                          <IconButton aria-label="Edit">
 | 
			
		||||
                            <EditIcon />
 | 
			
		||||
                          </IconButton>
 | 
			
		||||
                          <Button variant="contained" color="secondary" className={classes.button} onClick={this.createUser}>
 | 
			
		||||
                            Add User
 | 
			
		||||
                        </Button>
 | 
			
		||||
                        </TableCell>
 | 
			
		||||
                      </TableRow>
 | 
			
		||||
                    ))}
 | 
			
		||||
                  </TableBody>
 | 
			
		||||
                </Table>
 | 
			
		||||
 | 
			
		||||
                <Button variant="contained" color="primary" className={classes.button} type="submit">
 | 
			
		||||
                  Save
 | 
			
		||||
                </Button>
 | 
			
		||||
                <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>
 | 
			
		||||
                  Reset
 | 
			
		||||
      		      </Button>
 | 
			
		||||
 | 
			
		||||
              </ValidatorForm>
 | 
			
		||||
 | 
			
		||||
                    </TableFooter>
 | 
			
		||||
                  </Table>
 | 
			
		||||
                  <Button variant="contained" color="primary" className={classes.button} type="submit">
 | 
			
		||||
                    Save
 | 
			
		||||
                  </Button>
 | 
			
		||||
                  <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>
 | 
			
		||||
                    Reset
 | 
			
		||||
      		        </Button>
 | 
			
		||||
                </ValidatorForm>
 | 
			
		||||
              :
 | 
			
		||||
 | 
			
		||||
              <div className={classes.loadingSettings}>
 | 
			
		||||
              <SectionContent title="Manage Users">
 | 
			
		||||
                <Typography variant="h4" className={classes.loadingSettingsDetails}>
 | 
			
		||||
                  {errorMessage}
 | 
			
		||||
                </Typography>
 | 
			
		||||
                <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>
 | 
			
		||||
                  Reset
 | 
			
		||||
      		</Button>
 | 
			
		||||
              </div>
 | 
			
		||||
      		      </Button>
 | 
			
		||||
              </SectionContent>
 | 
			
		||||
        }
 | 
			
		||||
      </div>
 | 
			
		||||
      </SectionContent>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ManageUsersForm.propTypes = {
 | 
			
		||||
  classes: PropTypes.object.isRequired,
 | 
			
		||||
  users: PropTypes.object,
 | 
			
		||||
  usersFetched: PropTypes.bool.isRequired,
 | 
			
		||||
  userData: PropTypes.object,
 | 
			
		||||
  userDataFetched: PropTypes.bool.isRequired,
 | 
			
		||||
  errorMessage: PropTypes.string,
 | 
			
		||||
  onSubmit: PropTypes.func.isRequired,
 | 
			
		||||
  onReset: PropTypes.func.isRequired,
 | 
			
		||||
  handleValueChange: PropTypes.func.isRequired,
 | 
			
		||||
  handleCheckboxChange: PropTypes.func.isRequired,
 | 
			
		||||
  setData: PropTypes.func.isRequired,
 | 
			
		||||
  handleValueChange: PropTypes.func.isRequired
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default withStyles(styles)(ManageUsersForm);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										112
									
								
								interface/src/forms/UserForm.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								interface/src/forms/UserForm.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
 | 
			
		||||
import { withStyles } from '@material-ui/core/styles';
 | 
			
		||||
import Button from '@material-ui/core/Button';
 | 
			
		||||
 | 
			
		||||
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
 | 
			
		||||
import PasswordValidator from '../components/PasswordValidator';
 | 
			
		||||
 | 
			
		||||
import Input from '@material-ui/core/Input';
 | 
			
		||||
import InputLabel from '@material-ui/core/InputLabel';
 | 
			
		||||
import MenuItem from '@material-ui/core/MenuItem';
 | 
			
		||||
import FormControl from '@material-ui/core/FormControl';
 | 
			
		||||
import Select from '@material-ui/core/Select';
 | 
			
		||||
import Chip from '@material-ui/core/Chip';
 | 
			
		||||
 | 
			
		||||
const styles = theme => ({
 | 
			
		||||
  textField: {
 | 
			
		||||
    width: "100%"
 | 
			
		||||
  },
 | 
			
		||||
  checkboxControl: {
 | 
			
		||||
    width: "100%"
 | 
			
		||||
  },
 | 
			
		||||
  chips: {
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    flexWrap: 'wrap',
 | 
			
		||||
  },
 | 
			
		||||
  chip: {
 | 
			
		||||
    marginRight: theme.spacing.unit,
 | 
			
		||||
  },
 | 
			
		||||
  button: {
 | 
			
		||||
    marginRight: theme.spacing.unit * 2,
 | 
			
		||||
    marginTop: theme.spacing.unit * 2,
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
class UserForm extends React.Component {
 | 
			
		||||
 | 
			
		||||
  componentWillMount() {
 | 
			
		||||
    ValidatorForm.addValidationRule('uniqueUsername', this.props.uniqueUsername);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { classes, user, roles, creating, handleValueChange, onDoneEditing, onCancelEditing } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <ValidatorForm onSubmit={onDoneEditing}>
 | 
			
		||||
        <TextValidator
 | 
			
		||||
          validators={creating ? ['required', 'uniqueUsername', 'matchRegexp:^[a-zA-Z0-9_\\.]{1,24}$'] : []}
 | 
			
		||||
          errorMessages={creating ? ['Username is required', "That username already exists", "Must be 1-24 characters: alpha numberic, '_' or '.'"] : []}
 | 
			
		||||
          name="username"
 | 
			
		||||
          label="Username"
 | 
			
		||||
          className={classes.textField}
 | 
			
		||||
          value={user.username}
 | 
			
		||||
          disabled={!creating}
 | 
			
		||||
          onChange={handleValueChange('username')}
 | 
			
		||||
          margin="normal"
 | 
			
		||||
        />
 | 
			
		||||
        <PasswordValidator
 | 
			
		||||
          validators={['required', 'matchRegexp:^.{0,64}$']}
 | 
			
		||||
          errorMessages={['Password is required', 'Password must be 64 characters or less']}
 | 
			
		||||
          name="password"
 | 
			
		||||
          label="Password"
 | 
			
		||||
          className={classes.textField}
 | 
			
		||||
          value={user.password}
 | 
			
		||||
          onChange={handleValueChange('password')}
 | 
			
		||||
          margin="normal"
 | 
			
		||||
        />
 | 
			
		||||
        <FormControl className={classes.textField}>
 | 
			
		||||
          <InputLabel htmlFor="roles">Roles</InputLabel>
 | 
			
		||||
          <Select
 | 
			
		||||
            multiple
 | 
			
		||||
            value={user.roles}
 | 
			
		||||
            onChange={handleValueChange('roles')}
 | 
			
		||||
            input={<Input id="roles" />}
 | 
			
		||||
            renderValue={selected => (
 | 
			
		||||
              <div className={classes.chips}>
 | 
			
		||||
                {selected.map(value => (
 | 
			
		||||
                  <Chip key={value} label={value} className={classes.chip} />
 | 
			
		||||
                ))}
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
          >
 | 
			
		||||
            {roles.map(name => (
 | 
			
		||||
              <MenuItem key={name} value={name}>
 | 
			
		||||
                {name}
 | 
			
		||||
              </MenuItem>
 | 
			
		||||
            ))}
 | 
			
		||||
          </Select>
 | 
			
		||||
        </FormControl>
 | 
			
		||||
        <Button variant="contained" color="primary" className={classes.button} type="submit">
 | 
			
		||||
          Save
 | 
			
		||||
        </Button>
 | 
			
		||||
        <Button variant="contained" color="secondary" className={classes.button} onClick={onCancelEditing}>
 | 
			
		||||
          Back
 | 
			
		||||
      	</Button>
 | 
			
		||||
      </ValidatorForm>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UserForm.propTypes = {
 | 
			
		||||
  classes: PropTypes.object.isRequired,
 | 
			
		||||
  user: PropTypes.object.isRequired,
 | 
			
		||||
  creating: PropTypes.bool.isRequired,
 | 
			
		||||
  roles: PropTypes.array.isRequired,
 | 
			
		||||
  onDoneEditing: PropTypes.func.isRequired,
 | 
			
		||||
  onCancelEditing: PropTypes.func.isRequired,
 | 
			
		||||
  uniqueUsername: PropTypes.func.isRequired,
 | 
			
		||||
  handleValueChange: PropTypes.func.isRequired
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default withStyles(styles)(UserForm);
 | 
			
		||||
		Reference in New Issue
	
	Block a user