@mui/material#Paper JavaScript Examples

The following examples show how to use @mui/material#Paper. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: SearchNotFound.js    From Django-REST-Framework-React-BoilerPlate with MIT License 6 votes vote down vote up
export default function SearchNotFound({ searchQuery = '', ...other }) {
  return (
    <Paper {...other}>
      <Typography gutterBottom align="center" variant="subtitle1">
        Not found
      </Typography>
      <Typography variant="body2" align="center">
        No results found for &nbsp;
        <strong>&quot;{searchQuery}&quot;</strong>. Try checking for typos or using complete words.
      </Typography>
    </Paper>
  );
}
Example #2
Source File: ActionPaper.js    From react-saas-template with MIT License 6 votes vote down vote up
function ActionPaper(props) {
  const {
    theme,
    classes,
    title,
    content,
    maxWidth,
    actions,
    helpPadding,
    fullWidthActions
  } = props;
  return (
    <Box pt={1}>
      <Paper style={{ maxWidth: theme.breakpoints.values[maxWidth] }}>
        {title && <DialogTitle>{title}</DialogTitle>}
        {content && (
          <DialogContent
            classes={helpPadding ? { root: classes.helpPadding } : null}
          >
            {content}
          </DialogContent>
        )}
        {actions && (
          <Box pb={2} pr={2}>
            <DialogActions
              classes={{ action: fullWidthActions ? classes.fullWidth : null }}
            >
              {actions}
            </DialogActions>
          </Box>
        )}
      </Paper>
    </Box>
  );
}
Example #3
Source File: index.js    From fireact with MIT License 6 votes vote down vote up
DataTable = ({columns, rows, totalRows, pageSize, page, handlePageChane, handlePageSizeChange}) => {

    return (
        <>
            <TableContainer component={Paper}>
                <Table aria-label="simple table">
                    <TableHead>
                        <TableRow>
                        {columns.map((c,i) => 
                            <TableCell key={i} style={c.style}>{c.name}</TableCell>
                        )}
                        </TableRow>
                    </TableHead>
                    <TableBody>
                    {rows.map((r, i) =>
                        <TableRow key={i}>
                        {columns.map((c, k) =>
                            <TableCell key={k}>{r[c.field]}</TableCell>
                        )}
                        </TableRow>
                    )}
                    </TableBody>
                </Table>
            </TableContainer>
            <TablePagination
                rowsPerPageOptions={[10,20,50,100]}
                component="div"
                count={totalRows}
                rowsPerPage={pageSize}
                page={page}
                onPageChange={handlePageChane}
                onRowsPerPageChange={handlePageSizeChange}
            />
        </>
    )
}
Example #4
Source File: Subscription.js    From react-saas-template with MIT License 6 votes vote down vote up
function Subscription(props) {
  const {
    transactions,
    classes,
    openAddBalanceDialog,
    selectSubscription
  } = props;

  useEffect(selectSubscription, [selectSubscription]);

  return (
    <Paper>
      <List disablePadding>
        <SubscriptionInfo openAddBalanceDialog={openAddBalanceDialog} />
        <Divider className={classes.divider} />
        <SubscriptionTable transactions={transactions} />
      </List>
    </Paper>
  );
}
Example #5
Source File: LoadChart.js    From admin-web with GNU Affero General Public License v3.0 6 votes vote down vote up
function LoadChart(props) {
  const { classes, t, load } = props;

  return (
    <Paper className={classes.paper}>
      <div className={classes.root}>
        <Typography className={classes.chartTitle}>{t("Load")}</Typography>
        <ResponsiveContainer width="100%" height={200} >
          <BarChart data={load} margin={{ top: 0, right: 32, left: 10, bottom: 16 }}>
            <defs>
              <linearGradient id="gradientBlue2" x1="0" y1="0" x2="0" y2="1">
                <stop offset="5%" stopColor={"#2980B9"} />
                <stop offset="95%" stopColor={"#6DD5FA"} />
              </linearGradient>
            </defs>
            <CartesianGrid strokeDasharray="3 3" />
            <XAxis dataKey="time" />
            <YAxis />
            <Legend />
            <Tooltip labelStyle={{ color: 'black', fontSize: 18, paddingBottom: 4 }}/>
            <Bar
              dataKey="value"
              fill="url(#gradientBlue)"/>
          </BarChart>
        </ResponsiveContainer>
      </div>
    </Paper>
  );
}
Example #6
Source File: AccountInformationArea.js    From react-saas-template with MIT License 6 votes vote down vote up
function AccountInformationArea(props) {
  const { classes, toggleAccountActivation, isAccountActivated } = props;
  return (
    <Paper className={classes.paper}>
      <Toolbar className={classes.toolbar}>
        <Box display="flex" alignItems="center">
          <Box mr={2}>
            <ListItemText
              primary="Status"
              secondary={isAccountActivated ? "Activated" : "Not activated"}
              className="mr-2"
            />
          </Box>
          <ListItemIcon>
            <LoopIcon
              className={classNames(
                isAccountActivated ? classes.spin : null,
                classes.scaleMinus
              )}
            />
          </ListItemIcon>
        </Box>
        <ListItemSecondaryAction className={classes.listItemSecondaryAction}>
          <Switch
            color="secondary"
            checked={isAccountActivated}
            onClick={toggleAccountActivation}
            inputProps={{
              "aria-label": isAccountActivated
                ? "Deactivate Account"
                : "Activate Account"
            }}
          />
        </ListItemSecondaryAction>
      </Toolbar>
    </Paper>
  );
}
Example #7
Source File: MailQ.js    From admin-web with GNU Affero General Public License v3.0 6 votes vote down vote up
render() {
    const { classes, t } = this.props;
    const { snackbar, postfixMailq, gromoxMailq } = this.state;
    return (
      <TableViewContainer
        headline={t("Mail queue")}
        subtitle={t("mailq_sub")}
        href="https://docs.grommunio.com/admin/administration.html#id2"
        snackbar={snackbar}
        onSnackbarClose={() => this.setState({ snackbar: '' })}
      >  
        <div className={classes.logViewer}>
          <Paper elevation={1} className={classes.paper}>
            <Typography variant="h6" className={classes.header}>Postfix {t("mail queue")}</Typography>
            <pre
              className={classes.log}
            >
              {postfixMailq}
            </pre>
          </Paper>
          <Paper elevation={1} className={classes.paper}>
            <Typography variant="h6" className={classes.header}>Gromox {t("mail queue")}</Typography>
            <pre
              className={classes.log}
            >
              {gromoxMailq}
            </pre>
          </Paper>
        </div>
      </TableViewContainer>
    );
  }
Example #8
Source File: virtualized.js    From react-table-library with MIT License 5 votes vote down vote up
Component = () => {
  const data = { nodes: manyNodes };

  const materialTheme = getTheme(DEFAULT_OPTIONS, { isVirtualized: true });
  const theme = useTheme(materialTheme);

  const COLUMNS = [
    { label: 'Task', renderCell: (item) => item.name },
    {
      label: 'Deadline',
      renderCell: (item) =>
        item.deadline.toLocaleDateString('en-US', {
          year: 'numeric',
          month: '2-digit',
          day: '2-digit',
        }),
    },
    { label: 'Type', renderCell: (item) => item.type },
    {
      label: 'Complete',
      renderCell: (item) => item.isComplete.toString(),
    },
    { label: 'Tasks', renderCell: (item) => item.nodes?.length },
  ];

  const VIRTUALIZED_OPTIONS = {
    rowHeight: (_item, _index) => 54,
  };

  return (
    <>
      <Paper variant="outlined" square style={{ height: '300px' }}>
        <CompactTable
          columns={COLUMNS}
          virtualizedOptions={VIRTUALIZED_OPTIONS}
          data={data}
          theme={theme}
          layout={{ fixedHeader: true }}
        />
      </Paper>

      <br />
      <DocumentationSee anchor={'Features/' + key} />
    </>
  );
}
Example #9
Source File: CustomizedSlider.jsx    From matx-react with MIT License 5 votes vote down vote up
PaperRoot = styled(Paper)(({ theme }) => ({
  width: 300 + 24 * 2,
  padding: 24,
  "& .margin": {
    height: theme.spacing(3),
  },
}))
Example #10
Source File: index.js    From fireact with MIT License 5 votes vote down vote up
DataCreate = ({schema, validation, handleCreation, success, children}) => {
    const mountedRef = useRef(true);

    const [inSubmit, setInSubmit] = useState(false);
    const [result, setResult] = useState({
        response: null,
        error: null
    });

    useEffect(() => {
        return () => { 
            mountedRef.current = false
        }
    },[]);

    return (
        <form onSubmit={e => {
            e.preventDefault();
            setInSubmit(true);
            if(validation()){
                let data = {};
                schema.forEach(field => {
                    data[field.name] = e.target.elements[field.name][field.prop]
                });
                handleCreation(data).then(res => {
                    if (!mountedRef.current) return null
                    setResult({
                        response: true,
                        error: null
                    });
                    setInSubmit(false);
                }).catch(err => {
                    if (!mountedRef.current) return null
                    setResult({
                        response: false,
                        error: err
                    });
                    setInSubmit(false);
                })
            }else{
                setResult({
                    response: false,
                    error: 'Please fill in the form in the correct format.'
                })
                setInSubmit(false);
            }
            
        }}>
            <Paper>
                <Box p={2}>
                    <Stack spacing={3}>
                        {result.response?(
                            <>{success}</>
                        ):(
                            <>
                            {result.response === false && 
                                <Alert severity="error">{result.error}</Alert>
                            }
                            {children}
                            <Stack direction="row" spacing={1} mt={2}>
                                <Button variant="contained" type="submit" disabled={inSubmit}>{inSubmit && <Loader />} Create</Button>
                            </Stack>
                            </>
                        )}
                    </Stack>
                </Box>
            </Paper>
        </form>
    )
}
Example #11
Source File: About.js    From admin-web with GNU Affero General Public License v3.0 5 votes vote down vote up
render() {
    const { classes, about } = this.props;
    const { API, backend, schema } = about;


    return (
      <div className={classes.root}>
        <div className={classes.flexItem}>
          <Paper className={classes.paper}>
            <Dashboard className={classes.icon}/>
            <div className={classes.labeledData}>
              <Typography className={classes.data}>{process.env.REACT_APP_BUILD_VERSION}</Typography>
              <Typography className={classes.label}>Web UI</Typography>
            </div>
          </Paper>
        </div>
        <div className={classes.flexItem}>
          <Paper className={classes.paper}>
            <Power className={classes.icon}/>
            <div className={classes.labeledData}>
              <Typography className={classes.data}>{API}</Typography>
              <Typography className={classes.label}>API</Typography>
            </div>
          </Paper>
        </div>
        <div className={classes.flexItem}>
          <Paper className={classes.paper}>
            <Outlet className={classes.icon}/>
            <div className={classes.labeledData}>
              <Typography className={classes.data}>{backend}</Typography>
              <Typography className={classes.label}>Backend</Typography>
            </div>
          </Paper>
        </div>
        <div className={classes.flexItem}>
          <Paper className={classes.paper}>
            <StorageIcon className={classes.icon}/>
            <div className={classes.labeledData}>
              <Typography className={classes.data}>{this.formatBytes(schema)}</Typography>
              <Typography className={classes.label}>Database</Typography>
            </div>
          </Paper>
        </div>
      </div>
    );
  }
Example #12
Source File: index.js    From fireact with MIT License 5 votes vote down vote up
UserPageLayout = (props) => {
    const {
        title,
        children
    } = props

    const { setBreadcrumb } = useContext(BreadcrumbContext);
    useEffect(() => {
        setBreadcrumb([
            {
                to: "/",
                text: "Home",
                active: false
            },
            {
                to: "/user/profile",
                text: "User",
                active: false
            },
            {
                to: null,
                text: title,
                active: true
            }
        ]);
    },[setBreadcrumb, title]);
    
    
    

    return (
        <Container>
            <Paper>
                <Box p={2}>
                    {children}
                </Box>
            </Paper>
        </Container>
    )
}
Example #13
Source File: RoleDetails.js    From admin-web with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {
    const { classes, t, Users, Permissions, Domains, Orgs } = this.props;
    const { snackbar, role, autocompleteInput } = this.state;
    const { name, description, users, permissions } = role;
    const domains = [{ ID: '*', domainname: 'All'}].concat(Domains);
    const orgs = [{ ID: '*', name: 'All'}].concat(Orgs);
    const writable = this.context.includes(SYSTEM_ADMIN_WRITE);
    return (
      <ViewWrapper
        topbarTitle={t('Role')}
        snackbar={snackbar}
        onSnackbarClose={() => this.setState({ snackbar: '' })}
      >
        <Paper className={classes.paper} elevation={1}>
          <Grid container>
            <Typography
              color="primary"
              variant="h5"
            >
              {t('editHeadline', { item: 'Role' })}
            </Typography>
          </Grid>
          <FormControl className={classes.form}>
            <TextField 
              className={classes.input} 
              label={t("Name")} 
              fullWidth
              autoFocus
              value={name || ''}
              onChange={this.handleRoleInput('name')}
            />
            <TextField 
              className={classes.input} 
              label={t("Description")} 
              fullWidth
              multiline
              variant="outlined"
              rows={4}
              value={description || ''}
              onChange={this.handleRoleInput('description')}
            />
            <MagnitudeAutocomplete
              multiple
              inputValue={autocompleteInput}
              value={users || []}
              filterAttribute={'username'}
              onChange={this.handleAutocomplete('users')}
              onInputChange={this.handleACInput}
              className={classes.input} 
              options={Users || []}
              label={t('Users')}
              placeholder={t("Search users") + "..."}
            />
            {(permissions || []).map((permission, idx) =>
              <div key={idx} className={classes.row}>
                <TextField
                  select
                  label={t("Permission")}
                  value={permission.permission || ''}
                  onChange={this.handleSelectPermission(idx)}
                  fullWidth
                  variant="standard"
                >
                  {Permissions.map((name) => (
                    <MenuItem key={name} value={name}>
                      {name}
                    </MenuItem>
                  ))}
                </TextField>
                {permission.permission.includes('DomainAdmin') /*Read and Write*/ && <MagnitudeAutocomplete
                  value={permission.params}
                  getOptionLabel={(domainID) => domainID.domainname ||
                    (domains || []).find(d => d.ID === domainID)?.domainname || ''}
                  filterAttribute={'domainname'}
                  onChange={this.handleSetParams(idx, "domainname")}
                  inputValue={permission.autocompleteInput}
                  onInputChange={this.handleAutocompleteInput(idx)}
                  className={classes.rowTextfield} 
                  options={domains || []}
                  label={t('Params')}
                  placeholder={t("Search domains") + "..."}
                  variant="standard"
                  fullWidth
                  autoSelect
                />}
                {permission.permission === ORG_ADMIN && <MagnitudeAutocomplete
                  value={permission.params}
                  getOptionLabel={(orgID) => orgID.name ||
                    (orgs || []).find(o => o.ID === orgID)?.name || ''} // Because only ID is received
                  filterAttribute={'name'}
                  onChange={this.handleSetParams(idx, "name")}
                  inputValue={permission.autocompleteInput}
                  onInputChange={this.handleAutocompleteInput(idx)}
                  className={classes.rowTextfield} 
                  options={orgs || []}
                  label={t('Params')}
                  placeholder={t("Search organizations") + "..."}
                  variant="standard"
                  fullWidth
                  autoSelect
                />}
                <IconButton size="small" onClick={this.removeRow(idx)}>
                  <Delete fontSize="small" color="error" />
                </IconButton>
              </div>
            )}
            <Grid container justifyContent="center" className={classes.addButton}>
              <Button size="small" onClick={this.handleNewRow}>
                <Add color="primary" />
              </Button>
            </Grid>
          </FormControl>
          <Button
            color="secondary"
            onClick={() => this.props.history.push('/roles')}
            style={{ marginRight: 8 }}
          >
            {t('Back')}
          </Button>
          <Button
            variant="contained"
            color="primary"
            onClick={this.handleEdit}
            disabled={!writable}
          >
            {t('Save')}
          </Button>
        </Paper>
      </ViewWrapper>
    );
  }
Example #14
Source File: index.js    From fireact with MIT License 4 votes vote down vote up
NewAccount = () => {
    const title = 'Create New Account';
    const mountedRef = useRef(true);

    const [accountName, setAccountName] = useState({
        hasError: false,
        error: null,
        value: null
    });

    const [errorMessage, setErrorMessage] = useState(null);

    const [inSubmit, setInSubmit] = useState(false);

    const [redirect, setRedirect] = useState(null);
    const { setBreadcrumb } = useContext(BreadcrumbContext);

    useEffect(() => {
        setBreadcrumb([
            {
                to: "/",
                text: "Home",
                active: false
            },
            {
                to: null,
                text: title,
                active: true
            }
        ]);
    }, [setBreadcrumb, title]);

    useEffect(() => {
        return () => { 
            mountedRef.current = false
        }
    },[]);


    return (
        <Container>
            <Paper>
                <Box p={2}>
            {redirect === null && 
            <>

                            {errorMessage !== null && 
                                <Alert severity="error" dismissible={true} onDismiss={() => setErrorMessage(null)}>{errorMessage}</Alert>
                            }
                            <div className="card-body">
                                <Form handleSubmit={e =>{
                                    e.preventDefault();
                                    setInSubmit(true);
                                    setErrorMessage(null);
                                    const createAccount = CloudFunctions.httpsCallable('createAccount');
                                    createAccount({
                                        accountName: accountName.value,
                                    }).then(response => {
                                        if (!mountedRef.current) return null 
                                        const accountId = response.data.accountId;
                                        setRedirect('/account/'+accountId+'/billing/plan');
                                    }).catch(err => {
                                        if (!mountedRef.current) return null 
                                        setErrorMessage(err.message);
                                        setInSubmit(false);
                                    })
                                }}
                                disabled={accountName.hasError || accountName.value===null || inSubmit}
                                inSubmit={inSubmit}
                                enableDefaultButtons={true}>
                                    <Input label="Account Name" type="text" name="account-name" maxLen={100} required={true} changeHandler={setAccountName} fullWidth variant="outlined" />
                                </Form>
                            </div>

            </>
            }
            {redirect !== null &&
                <Redirect to={redirect}></Redirect>
            }
                </Box>
            </Paper>
        </Container>

    )
}
Example #15
Source File: Orgs.js    From admin-web with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {
    const { classes, t, orgs, tableState, handleMatch, handleRequestSort,
      handleAdd, handleAddingSuccess, handleAddingClose, handleAddingError,
      clearSnackbar, handleDelete, handleDeleteClose, handleDeleteError,
      handleDeleteSuccess, handleEdit } = this.props;
    const { order, orderBy, match, snackbar, adding, deleting } = tableState;
    const writable = this.context.includes(SYSTEM_ADMIN_WRITE);

    return (
      <TableViewContainer
        handleScroll={this.handleScroll}
        headline={t("Organizations")}
        href="https://docs.grommunio.com/admin/administration.html#organizations"
        subtitle={t("orgs_sub")}
        snackbar={snackbar}
        onSnackbarClose={clearSnackbar}
      >
        <Grid container alignItems="flex-end" className={classes.buttonGrid}>
          <Button
            variant="contained"
            color="primary"
            onClick={handleAdd}
            disabled={!writable}
          >
            {t("New organization")}
          </Button>
          <div className={classes.actions}>
            <TextField
              value={match}
              onChange={handleMatch}
              placeholder={t("Search organizations")}
              variant={"outlined"}
              InputProps={{
                startAdornment: (
                  <InputAdornment position="start">
                    <Search color="secondary" />
                  </InputAdornment>
                ),
              }}
              color="primary"
            />
          </div>
        </Grid>
        <Typography className={classes.count} color="textPrimary">
          {t("showingOrgs", { count: orgs.Orgs.length })}
        </Typography>
        <Paper elevation={1}>
          <Table size="small">
            <TableHead>
              <TableRow>
                {this.columns.map((column) => (
                  <TableCell key={column.value}>
                    <TableSortLabel
                      active={orderBy === column.value}
                      align="left"
                      direction={orderBy === column.value ? order : "asc"}
                      onClick={handleRequestSort(column.value)}
                    >
                      {t(column.label)}
                    </TableSortLabel>
                  </TableCell>
                ))}
                <TableCell padding="checkbox" />
              </TableRow>
            </TableHead>
            <TableBody>
              {orgs.Orgs.map((obj, idx) => 
                <TableRow key={idx} hover onClick={handleEdit('/orgs/' + obj.ID)}>
                  <TableCell>{obj.name}</TableCell>
                  <TableCell>{obj.description}</TableCell>
                  <TableCell align="right">
                    {writable && <IconButton onClick={handleDelete(obj)} size="large">
                      <Delete color="error" />
                    </IconButton>}
                  </TableCell>
                </TableRow>
              )}
            </TableBody>
          </Table>
          {orgs.Orgs.length < orgs.count && (
            <Grid container justifyContent="center">
              <CircularProgress
                color="primary"
                className={classes.circularProgress}
              />
            </Grid>
          )}
        </Paper>
        <AddOrg
          open={adding}
          onSuccess={handleAddingSuccess}
          onError={handleAddingError}
          onClose={handleAddingClose}
        />
        <GeneralDelete
          open={!!deleting}
          delete={this.props.delete}
          onSuccess={handleDeleteSuccess}
          onError={handleDeleteError}
          onClose={handleDeleteClose}
          item={deleting.name}
          id={deleting.ID}
        />
      </TableViewContainer>
    );
  }
Example #16
Source File: Layout1Customizer.jsx    From matx-react with MIT License 4 votes vote down vote up
Layout1Customizer = ({ settings, handleChange, handleControlChange }) => {
  return (
    <Fragment>
      <Box mb="16px" mx="12px">
        <ThemeName>Sidebar theme</ThemeName>
        <ToolbarContainer>
          {mainSidebarThemes.map((color, i) => (
            <Tooltip key={i} title={color} placement="top">
              <ToolbarContent
                color={color}
                onClick={() => handleChange('layout1Settings.leftSidebar.theme', color)}
              >
                {settings.layout1Settings.leftSidebar.theme === color && <Icon>done</Icon>}
                <div className={settings.themes[color].palette.type}></div>
              </ToolbarContent>
            </Tooltip>
          ))}
        </ToolbarContainer>
      </Box>

      <Box mb="32px" mx="12px">
        <ThemeName>Sidebar theme</ThemeName>
        <ToolbarContainer>
          {topbarThemes.map((color, i) => (
            <Tooltip key={i} title={color} placement="top">
              <ToolbarContent
                color={color}
                onClick={() => handleChange('layout1Settings.topbar.theme', color)}
              >
                {settings.layout1Settings.topbar.theme === color && <Icon>done</Icon>}
                <div className={settings.themes[color].palette.type}></div>
              </ToolbarContent>
            </Tooltip>
          ))}
        </ToolbarContainer>
      </Box>

      <Box mb="18px" mx="12px">
        <FormControl component="fieldset">
          <FormLabel component="legend">Sidebar mode</FormLabel>
          <RadioGroup
            aria-label="Sidebar"
            name="leftSidebar"
            value={settings.layout1Settings.leftSidebar.mode}
            onChange={handleControlChange('layout1Settings.leftSidebar.mode')}
          >
            <FormControlLabel value="full" control={<Radio />} label="Full" />
            <FormControlLabel value="close" control={<Radio />} label="Close" />
            <FormControlLabel value="compact" control={<Radio />} label="Compact" />
          </RadioGroup>
        </FormControl>
      </Box>

      <Box mb="32px" mx="12px">
        <ThemeName sx={{ mb: 4 }}>Sidebar background image</ThemeName>
        <div>
          <Grid container spacing={3}>
            {sidebarBG.map((bg, i) => (
              <Grid item xs={4} key={i}>
                <BadgeSelected
                  color="primary"
                  badgeContent={<Icon>done</Icon>}
                  invisible={settings.layout1Settings.leftSidebar.bgImgURL !== bg}
                  sx={{ width: '100%', height: '100%', cursor: 'pointer' }}
                >
                  <Paper onClick={() => handleChange('layout1Settings.leftSidebar.bgImgURL', bg)}>
                    <IMG src={bg} alt="" />
                  </Paper>
                </BadgeSelected>
              </Grid>
            ))}
          </Grid>
        </div>
      </Box>

      <Box mb="24px" mx="12px">
        <FormControl component="fieldset">
          <FormLabel component="legend">Topbar</FormLabel>
          <FormGroup>
            <FormControlLabel
              control={
                <Switch
                  checked={get(settings.layout1Settings.topbar, 'show')}
                  onChange={handleControlChange('layout1Settings.topbar.show')}
                />
              }
              label="Show"
            />

            <FormControlLabel
              control={
                <Switch
                  checked={get(settings.layout1Settings.topbar, 'fixed')}
                  onChange={handleControlChange('layout1Settings.topbar.fixed')}
                />
              }
              label="Fixed"
            />
          </FormGroup>
        </FormControl>
      </Box>
    </Fragment>
  );
}
Example #17
Source File: Servers.js    From admin-web with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {
    const { classes, t, servers, tableState, handleMatch, handleRequestSort,
      handleAdd, handleAddingSuccess, handleAddingClose, handleAddingError,
      handleDelete, handleDeleteClose, handleDeleteError,
      handleDeleteSuccess, handleEdit } = this.props;
    const { order, orderBy, match, adding, snackbar, deleting } = tableState;
    const writable = this.context.includes(SYSTEM_ADMIN_WRITE);

    return (
      <TableViewContainer
        handleScroll={this.handleScroll}
        headline={<span>
          {t("Servers")}
          <IconButton
            size="small"
            href="https://docs.grommunio.com/admin/administration.html#id1"
            target="_blank"
          >
            <HelpOutline fontSize="small"/>
          </IconButton>
        </span>
        }
        snackbar={snackbar || this.state.snackbar}
        onSnackbarClose={this.handleSnackbarClose}
      >
        <Grid container alignItems="flex-end" className={classes.buttonGrid}>
          <Button
            variant="contained"
            color="primary"
            onClick={handleAdd}
            disabled={!writable}
          >
            {t("New server")}
          </Button>
          <div className={classes.actions}>
            <TextField
              value={match}
              onChange={handleMatch}
              placeholder={t("Search")}
              variant="outlined"
              className={classes.textfield}
              InputProps={{
                startAdornment: (
                  <InputAdornment position="start">
                    <Search color="secondary" />
                  </InputAdornment>
                ),
              }}
              color="primary"
            />
          </div>
        </Grid>
        <div>
          <TextField
            value={servers.policy || 'round-robin'}
            onChange={this.handlePolicyChange}
            select
            label="Selection policy"
            className={classes.policy}
          >
            <MenuItem value={"round-robin"}>round-robin</MenuItem>
            <MenuItem value={"balanced"}>balanced</MenuItem>
            <MenuItem value={"first"}>first</MenuItem>
            <MenuItem value={"last"}>last</MenuItem>
            <MenuItem value={"random"}>random</MenuItem>
          </TextField>
        </div>
        <Typography className={classes.count} color="textPrimary">
          {t("showingServers", { count: servers.Servers.length })}
        </Typography>
        <Paper elevation={1}>
          <Table size="small">
            <TableHead>
              <TableRow>
                {this.columns.map((column) => (
                  <TableCell key={column.value}>
                    <TableSortLabel
                      active={orderBy === column.value}
                      align="left"
                      direction={orderBy === column.value ? order : "asc"}
                      onClick={handleRequestSort(column.value)}
                    >
                      {t(column.label)}
                    </TableSortLabel>
                  </TableCell>
                ))}
                <TableCell padding="checkbox" />
              </TableRow>
            </TableHead>
            <TableBody>
              {servers.Servers.map((obj, idx) =>
                <TableRow key={idx} hover onClick={handleEdit('/servers/' + obj.ID)}>
                  <TableCell>{obj.hostname}</TableCell>
                  <TableCell>{obj.extname}</TableCell>
                  <TableCell align="right">
                    {writable && <IconButton onClick={handleDelete(obj)} size="large">
                      <Delete color="error"/>
                    </IconButton>}
                  </TableCell>
                </TableRow>
              )}
            </TableBody>
          </Table>
          {(servers.Servers.length < servers.count) && <Grid container justifyContent="center">
            <CircularProgress color="primary" className={classes.circularProgress}/>
          </Grid>}
        </Paper>
        <AddServer
          open={adding}
          onSuccess={handleAddingSuccess}
          onError={handleAddingError}
          onClose={handleAddingClose}
        />
        <GeneralDelete
          open={!!deleting}
          delete={this.props.delete}
          onSuccess={handleDeleteSuccess}
          onError={handleDeleteError}
          onClose={handleDeleteClose}
          item={deleting.hostname}
          id={deleting.ID}
        />
      </TableViewContainer>
    );
  }
Example #18
Source File: CardView.jsx    From Edlib with GNU General Public License v3.0 4 votes vote down vote up
CardView = ({ resources, showDeleteButton = false, refetch }) => {
    const { classes } = useStyles();
    const { t } = useTranslation();

    return (
        <ViewContainer
            showDeleteButton={showDeleteButton}
            refetch={refetch}
            resources={resources}
        >
            {({ cogProps, setSelectedResource, resources }) => (
                <Grid container spacing={1}>
                    {resources.map((resource) => (
                        <Grid
                            key={resource.id}
                            item
                            xs={6}
                            lg={4}
                            className={classes.gridItem}
                        >
                            <Paper className={classes.paper}>
                                <Box
                                    display="flex"
                                    justifyContent="space-between"
                                >
                                    <Box>
                                        <div className={classes.title}>
                                            {resource.version.title}
                                        </div>
                                        <Box
                                            className={classes.subtitle}
                                            mt={1}
                                        >
                                            {getResourceName(resource)}
                                        </Box>
                                    </Box>
                                    <Box>
                                        <ResourceIcon
                                            contentTypeInfo={
                                                resource.contentTypeInfo
                                            }
                                            resourceVersion={resource.version}
                                            fontSizeRem={2}
                                        />
                                    </Box>
                                </Box>
                                <Box
                                    mt={1}
                                    display="flex"
                                    justifyContent="space-between"
                                >
                                    <Box
                                        display="flex"
                                        flexDirection="column"
                                        justifyContent="center"
                                    >
                                        <PublishedTag
                                            isPublished={
                                                resource.version.isPublished
                                            }
                                            isDraft={resource.version.isDraft}
                                        />
                                    </Box>
                                    <Box className={classes.buttons}>
                                        <ResourceEditCog
                                            {...cogProps(resource)}
                                        >
                                            {({ ref, onOpen }) => (
                                                <Button
                                                    size="small"
                                                    color="grey"
                                                    variant="contained"
                                                    style={{ minWidth: 0 }}
                                                    onClick={onOpen}
                                                    ref={ref}
                                                >
                                                    <MoreVertIcon />
                                                </Button>
                                            )}
                                        </ResourceEditCog>
                                        <Button
                                            size="small"
                                            color="secondary"
                                            variant="contained"
                                            onClick={() =>
                                                setSelectedResource(resource)
                                            }
                                        >
                                            {t('preview')}
                                        </Button>
                                    </Box>
                                </Box>
                            </Paper>
                        </Grid>
                    ))}
                </Grid>
            )}
        </ViewContainer>
    );
}
Example #19
Source File: PostContent.js    From react-saas-template with MIT License 4 votes vote down vote up
function PostContent(props) {
  const {
    pushMessageToSnackbar,
    setPosts,
    posts,
    openAddPostModal,
    classes,
  } = props;
  const [page, setPage] = useState(0);
  const [isDeletePostDialogOpen, setIsDeletePostDialogOpen] = useState(false);
  const [isDeletePostDialogLoading, setIsDeletePostDialogLoading] = useState(
    false
  );

  const closeDeletePostDialog = useCallback(() => {
    setIsDeletePostDialogOpen(false);
    setIsDeletePostDialogLoading(false);
  }, [setIsDeletePostDialogOpen, setIsDeletePostDialogLoading]);

  const deletePost = useCallback(() => {
    setIsDeletePostDialogLoading(true);
    setTimeout(() => {
      const _posts = [...posts];
      const index = _posts.find((element) => element.id === deletePost.id);
      _posts.splice(index, 1);
      setPosts(_posts);
      pushMessageToSnackbar({
        text: "Your post has been deleted",
      });
      closeDeletePostDialog();
    }, 1500);
  }, [
    posts,
    setPosts,
    setIsDeletePostDialogLoading,
    pushMessageToSnackbar,
    closeDeletePostDialog,
  ]);

  const onDelete = useCallback(() => {
    setIsDeletePostDialogOpen(true);
  }, [setIsDeletePostDialogOpen]);

  const handleChangePage = useCallback(
    (__, page) => {
      setPage(page);
    },
    [setPage]
  );

  const printImageGrid = useCallback(() => {
    if (posts.length > 0) {
      return (
        <Box p={1}>
          <Grid container spacing={1}>
            {posts
              .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
              .map((post) => (
                <Grid item xs={6} sm={4} md={3} key={post.id}>
                  <SelfAligningImage
                    src={post.src}
                    title={post.name}
                    timeStamp={post.timestamp}
                    options={[
                      {
                        name: "Delete",
                        onClick: () => {
                          onDelete(post);
                        },
                        icon: <DeleteIcon />,
                      },
                    ]}
                  />
                </Grid>
              ))}
          </Grid>
        </Box>
      );
    }
    return (
      <Box m={2}>
        <HighlightedInformation>
          No posts added yet. Click on &quot;NEW&quot; to create your first one.
        </HighlightedInformation>
      </Box>
    );
  }, [posts, onDelete, page]);

  return (
    <Paper>
      <Toolbar className={classes.toolbar}>
        <Typography variant="h6">Your Posts</Typography>
        <Button
          variant="contained"
          color="secondary"
          onClick={openAddPostModal}
          disableElevation
        >
          Add Post
        </Button>
      </Toolbar>
      <Divider />
      {printImageGrid()}
      <TablePagination
        component="div"
        count={posts.length}
        rowsPerPage={rowsPerPage}
        page={page}
        backIconButtonProps={{
          "aria-label": "Previous Page",
        }}
        nextIconButtonProps={{
          "aria-label": "Next Page",
        }}
        onPageChange={handleChangePage}
        classes={{
          select: classes.dNone,
          selectIcon: classes.dNone,
          actions: posts.length > 0 ? classes.dBlock : classes.dNone,
          caption: posts.length > 0 ? classes.dBlock : classes.dNone,
        }}
        labelRowsPerPage=""
      />
      <ConfirmationDialog
        open={isDeletePostDialogOpen}
        title="Confirmation"
        content="Do you really want to delete the post?"
        onClose={closeDeletePostDialog}
        loading={isDeletePostDialogLoading}
        onConfirm={deletePost}
      />
    </Paper>
  );
}
Example #20
Source File: ServicesChart.js    From admin-web with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {
    const { classes, Services, t } = this.props;
    const { starting, restarting, stoping, snackbar, service, action } = this.state;

    return (
      <div className={classes.root}>
        <Paper className={classes.paper}>
          <Table size="small">
            <TableHead>
              <TableRow>
                <TableCell>
                  {"Service"}
                </TableCell>
                <TableCell align="center" style={{ width: 124 }}>
                  {"State | Autostart"}
                </TableCell>
                <TableCell align="center" style={{ width: 132 }}>
                  {"Actions"}
                </TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {Services.map((service, idx) => (
                <TableRow key={idx} hover style={{cursor: "default"}}>
                  <TableCell>
                    <Tooltip
                      title={service.description ? <>
                        <Typography>{service.description}</Typography>
                        <Typography variant="caption">
                          {service.since ? `${t('since')} ${setDateTimeString(service.since)}` : ''}
                        </Typography>
                      </> : ''}
                      placement="top"
                    >
                      <Typography className={classes.serviceName}>
                        {service.name}
                      </Typography>
                    </Tooltip>
                  </TableCell>
                  <TableCell>
                    <Grid container justifyContent="center">
                      <div
                        style={{ marginRight: 4 }}
                        className={classes.label + " " + this.getChipColor(service.state)}
                      >
                        {service.state}
                      </div>
                      <div className={classes.label + " " +
                        this.getChipColor(service.autostart === "enabled" ? "active" : "error")}>
                        {service.autostart || 'error'}
                      </div>
                    </Grid>
                  </TableCell>
                  <TableCell align="right">
                    <Tooltip title={t("Enable/Disable")} placement="top">
                      <IconButton
                        onClick={this.handleServiceAction(service, service.autostart === "enabled" ? 
                          "disable" : "enable")}
                        className={classes.chipIcon}
                        size="large">
                        <Enable className={classes.iconButton} fontSize="small" />
                      </IconButton>
                    </Tooltip>
                    {stoping !== service.name ? (
                      <Tooltip title={t("Stop")} placement="top">
                        <IconButton
                          onClick={this.handleDialog(service, "stop")}
                          className={classes.chipIcon}
                          size="large">
                          <Stop className={classes.iconButton} fontSize="small" />
                        </IconButton>
                      </Tooltip>
                    ) : (
                      <IconButton className={classes.chipIcon} size="large">
                        <CircularProgress size={18} />
                      </IconButton>
                    )}
                    {restarting !== service.name ? (
                      <Tooltip title={t("Restart")}placement="top">
                        <IconButton
                          onClick={this.handleDialog(service, "restart")}
                          className={classes.chipIcon}
                          size="large">
                          <Restart
                            className={classes.iconButton}
                            fontSize="small"
                          />
                        </IconButton>
                      </Tooltip>
                    ) : (
                      <IconButton className={classes.chipIcon} size="large">
                        <CircularProgress size={18} />
                      </IconButton>
                    )}
                    {starting !== service.name ? (
                      <Tooltip title={t("Start")} placement="top">
                        <IconButton
                          onClick={this.handleServiceAction(service, "start")}
                          className={classes.chipIcon}
                          size="large">
                          <Start className={classes.iconButton} fontSize="small" />
                        </IconButton>
                      </Tooltip>
                    ) : (
                      <IconButton className={classes.chipIcon} size="large">
                        <CircularProgress size={18} />
                      </IconButton>
                    )}
                  </TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </Paper>
        <ConfirmRestartStop
          open={!!service}
          handleConfirm={this.handleServiceAction(service, action)}
          onClose={this.handleCloseDialog}
          service={service}
          action={action}
        />
        <Feedback
          snackbar={snackbar}
          onClose={() => this.setState({ snackbar: "" })}
        />
      </div>
    );
  }
Example #21
Source File: index.js    From fireact with MIT License 4 votes vote down vote up
Invite = () => {

    const { code } = useParams();

    const title = 'View Invite';
    const history = useHistory();
    const mountedRef = useRef(true);

    const [invite, setInvite] = useState(null); 
    const [error, setError] = useState(null);
    const [inSubmit, setInSubmit] = useState(false);
    const [success, setSuccess] = useState(false);
    const { setBreadcrumb } = useContext(BreadcrumbContext);

    useEffect(() => {
        setBreadcrumb([
            {
                to: "/",
                text: "Home",
                active: false
            },
            {
                to: null,
                text: title,
                active: true
            }
        ]);
        if(code){
            let isSubscribed = true;
            setError(null);
            const getInvite = CloudFunctions.httpsCallable('getInvite');
            getInvite({
                inviteId: code
            }).then(res => {
                if (!mountedRef.current) return null
                if(isSubscribed){
                    setInvite(res.data);
                }
            }).catch(err => {
                if (!mountedRef.current) return null
                if(isSubscribed){
                    setError(err.message);
                }
            });
            return () => (isSubscribed = false);
        }
    }, [code, setBreadcrumb, title]);

    useEffect(() => {
        return () => { 
            mountedRef.current = false
        }
    },[]);

    return (
        <>
            {success?(
                <Redirect to={"/account/"+invite.accountId+"/"}></Redirect>
            ):(
                <Paper>
                    <Box p={2}>
                        <Stack spacing={3}>
                            {error !== null && 
                                <Alert severity="danger">{error}</Alert>
                            }
                            {invite === null?(
                                <Loader text="Loading the invite..."></Loader>
                            ):(
                                <>
                                    <Typography>This invite will grant you access to <strong>{invite.accountName}</strong>. Do you want to accept it?</Typography>
                                    <Stack direction="row" spacing={1} mt={2}>
                                        <Button disabled={inSubmit} variant="contained" onClick={e => {
                                            e.preventDefault();
                                            setInSubmit(true);
                                            const acceptInvite = CloudFunctions.httpsCallable('acceptInvite');
                                            acceptInvite({
                                                inviteId: code
                                            }).then(res => {
                                                if (!mountedRef.current) return null
                                                setSuccess(true);
                                            }).catch(err => {
                                                if (!mountedRef.current) return null
                                                setError(err.message);
                                            });
                                        }}>{inSubmit && <Loader />}
                                        Yes, accept the invite
                                        </Button>
                                        <Button color="secondary" variant="contained" disabled={inSubmit} onClick={() => history.push('/')}>Ignore</Button>
                                    </Stack>
                                </>
                            )}
                        </Stack>
                    </Box>
                </Paper>
            )}
        </>
    )
}
Example #22
Source File: ClassDetails.js    From admin-web with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {
    const { classes, t, domain, _classes } = this.props;
    const writable = this.context.includes(DOMAIN_ADMIN_WRITE);
    const { _class, autocompleteInput, snackbar, stack } = this.state;
    const { classname, parentClasses, members, filters, children } = _class;
    return (
      <ViewWrapper
        topbarTitle={t('Groups')}
        snackbar={snackbar}
        onSnackbarClose={() => this.setState({ snackbar: '' })}
      >
        <Paper className={classes.paper} elevation={1}>
          <Grid container>
            <Typography
              color="primary"
              variant="h5"
            >
              {t('editHeadline', { item: 'Group' })}
            </Typography>
          </Grid>
          <FormControl className={classes.form}>
            <Breadcrumbs className={classes.breadcrumbs}>
              {stack.map((_class, idx) =>
                <Link
                  className={classes.breadcrumb}
                  key={_class.ID}
                  color="inherit"
                  onClick={this.handleBreadcrumb(_class.ID, idx)}
                >
                  {_class.classname}
                </Link>
              )}
            </Breadcrumbs>
            <TextField 
              className={classes.input} 
              label={t("Groupname")} 
              fullWidth 
              value={classname || ''}
              onChange={this.handleInput('classname')}
              autoFocus
              required
            />
            <MagnitudeAutocomplete
              value={parentClasses || []}
              filterAttribute={'classname'}
              inputValue={autocompleteInput}
              onChange={this.handleAutocomplete('parentClasses')}
              className={classes.input} 
              options={_classes || []}
              onInputChange={this.handleAutocompleteInput}
              label={t("Parent groups")}
              placeholder={t("Search groups") + "..."}
              multiple
              autoSelect
            />
            <TextField 
              className={classes.input} 
              label={t("Members (separate by comma)")} 
              fullWidth 
              value={members || ''}
              onChange={this.handleMemberInput}
            />
          </FormControl>
          <div>
            <Typography variant="body1">{t('Filters (All must be true)')}</Typography>
            {filters && filters.map((ANDFilter, ANDidx) =>
              <Accordion
                className={classes.panel}
                elevation={2 /* 1 has global overwrite */}
                key={ANDidx}
                defaultExpanded
              >
                <AccordionSummary>
                  <Grid container justifyContent="space-between">
                    <Typography body="body1">{t('Filter (One must be true)')}</Typography>
                    <IconButton onClick={this.handleRemoveAND(ANDidx)} size="large">
                      <Delete fontSize="small" color="error"/>
                    </IconButton>
                  </Grid>
                </AccordionSummary>
                <AccordionDetails>
                  <Grid container>
                    {ANDFilter.map((ORFilter, ORidx) =>  
                      <Grid item xs={12} key={ORidx} className={classes.grid}>
                        <Autocomplete
                          value={ORFilter.prop || ''}
                          inputValue={ORFilter.prop || ''}
                          onChange={this.handleAutocomplete(ANDidx, ORidx, 'prop')}
                          onInputChange={this.handleFilterInput(ANDidx, ORidx, 'prop')}
                          freeSolo
                          className={classes.flexTextfield} 
                          options={this.columns}
                          renderInput={(params) => (
                            <TextField
                              {...params}
                              label={t("Name of property to match")}
                            />
                          )}
                        />
                        <TextField
                          className={classes.flexTextfield} 
                          label={t("Comparison operator")}
                          value={ORFilter.op || ''}
                          onChange={this.handleFilterInput(ANDidx, ORidx, 'op')}
                          select
                        >
                          {this.operators.map(op =>
                            <MenuItem value={op.value} key={op.label}>{op.label}</MenuItem>
                          )}
                        </TextField>
                        <TextField
                          className={classes.flexTextfield} 
                          label={t("Compare value (binary operators)")}
                          value={ORFilter.val || ''}
                          onChange={this.handleFilterInput(ANDidx, ORidx, 'val')}
                        />
                        {filters[ANDidx].length > 1 &&
                        <IconButton onClick={this.handleRemoveOR(ANDidx, ORidx)} size="large">
                          <Delete fontSize="small" color="error"/>
                        </IconButton>}
                      </Grid>
                    )}
                    <Grid container justifyContent="center">
                      <Button variant="outlined" onClick={this.handleAddOR(ANDidx)}>{t('Add or-statement')}</Button>
                    </Grid>
                  </Grid>
                </AccordionDetails>
              </Accordion>
            )}
            <Grid container justifyContent="center">
              <Button variant="outlined" onClick={this.handleAddAND}>{t('Add and-statement')}</Button>
            </Grid>
          </div>
          <Typography variant="h6">{t('Children')}</Typography>
          <List>
            {children && children.map(child =>
              <ListItem
                key={child.ID}
                button
                onClick={this.handleChildClicked(child)}
              >
                <ListItemText primary={child.classname} />
              </ListItem>
            )}
          </List>
          <Button
            variant="text"
            color="secondary"
            onClick={this.handleNavigation(domain.ID + '/classes')}
            style={{ marginRight: 8 }}
          >
            {t('Back')}
          </Button>
          <Button
            variant="contained"
            color="primary"
            onClick={this.handleEdit}
            disabled={!writable}
          >
            {t('Save')}
          </Button>
        </Paper>
      </ViewWrapper>
    );
  }
Example #23
Source File: index.js    From fireact with MIT License 4 votes vote down vote up
Plans = () => {
    const title = 'Select a Plan';

    const countries = countryJSON.countries;

    const { userData, authUser } = useContext(AuthContext);
    const stripe = useStripe();
    const elements = useElements();
    const mountedRef = useRef(true);
    const { setBreadcrumb } = useContext(BreadcrumbContext);

    const CARD_ELEMENT_OPTIONS = {
        style: {
            base: {
              color: '#32325d',
              fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
              fontSmoothing: 'antialiased',
              fontSize: '16px',
              '::placeholder': {
                color: '#aab7c4'
              }
            },
            invalid: {
              color: '#fa755a',
              iconColor: '#fa755a'
            }
        },
        hidePostalCode: true
    };

    const [loading, setLoading] = useState(true);
    const [processing, setProcessing] = useState(false);
    const [plans, setPlans] = useState([]);
    const [selectedPlan, setSelectedPlan] = useState({id: 0});
    const [cardError, setCardError] = useState(null);
    const [errorMessage, setErrorMessage] = useState(null);
    const [country, setCountry] = useState("");
    const [countryError, setCountryError] = useState(null);
    const [state, setState] = useState("");
    const [states, setStates] = useState([]);
    const [stateError, setStateError] = useState(null);

    useEffect(() => {
        setBreadcrumb([
            {
                to: "/",
                text: "Home",
                active: false
            },
            {
                to: "/account/"+userData.currentAccount.id+"/",
                text: userData.currentAccount.name,
                active: false
            },      
            {
                to: null,
                text: title,
                active: true
            }
        ]);
        setLoading(true);

        const plansQuery = FirebaseAuth.firestore().collection('plans').orderBy('price', 'asc');
        plansQuery.get().then(planSnapShots => {
            if (!mountedRef.current) return null
            let p = [];
            planSnapShots.forEach(doc => {
                p.push({
                    'id': doc.id,
                    'name': doc.data().name,
                    'price': doc.data().price,
                    'currency': doc.data().currency,
                    'paymentCycle': doc.data().paymentCycle,
                    'features': doc.data().features,
                    'stripePriceId': doc.data().stripePriceId,
                    'current': (userData.currentAccount.planId===doc.id)?true:false
                });
            });
            if(p.length > 0){
                const ascendingOrderPlans = p.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
                setPlans(ascendingOrderPlans);
            }
            setLoading(false);
        });
    },[userData, setBreadcrumb, title]);

    useEffect(() => {
        return () => { 
            mountedRef.current = false
        }
    },[]);

    const subscribe = async(event) => {
        event.preventDefault();
        setProcessing(true);
        setErrorMessage(null);

        let hasError = false;
        let paymentMethodId = '';

        if(selectedPlan.price !== 0){
            if(country === ''){
                setCountryError('Please select a country.');
                hasError = true;
            }

            if(state === '' && countries[country] && countries[country].states){
                setStateError('Please select a state.');
                hasError = true;
            }

            setCardError(null);

            if (!stripe || !elements) {
                // Stripe.js has not loaded yet. Make sure to disable
                // form submission until Stripe.js has loaded.
                return;
            }
    
            // Get a reference to a mounted CardElement. Elements knows how
            // to find your CardElement because there can only ever be one of
            // each type of element.
            const cardElement = elements.getElement(CardElement);
    
            // Use your card Element with other Stripe.js APIs
            const {error, paymentMethod} = await stripe.createPaymentMethod({
                type: 'card',
                card: cardElement
            });
    
            if (error) {
                setCardError(error.message);
                hasError = true;
            } else {
                paymentMethodId = paymentMethod.id;
            }
        }

        
        if(!hasError){
            const createSubscription = CloudFunctions.httpsCallable('createSubscription');
            createSubscription({
                planId: selectedPlan.id,
                accountId: userData.currentAccount.id,
                paymentMethodId: paymentMethodId,
                billing: {
                    country: country,
                    state: state
                }
            }).then(res => {
                // physical page load to reload the account data
                if (!mountedRef.current) return null
                document.location = '/account/'+userData.currentAccount.id+'/';
            }).catch(err => {
                if (!mountedRef.current) return null
                setProcessing(false);
                setErrorMessage(err.message);
            });
        }else{
            setProcessing(false);
        }
    }

    return (
        <>
        {(!loading)?(
            <>{(userData.currentAccount.owner === authUser.user.uid)?(
            <>{plans.length > 0 ? (
            <Paper>
                <Box p={3} style={{textAlign: 'center'}} >
                    <h2>{title}</h2>
                    <Grid container spacing={3}>
                        
                            <>
                            {plans.map((plan,i) => 
                                <Grid container item xs={12} md={4} key={i} >
                                    <Card style={{
                                        width: '100%',
                                        display: 'flex',
                                        flexDirection: 'column',
                                        paddingBottom: '20px',
                                    }}>
                                        <CardHeader title={plan.name} subheader={"$"+plan.price+"/"+plan.paymentCycle} />
                                        <CardContent>
                                            <Divider />
                                            <ul style={{listStyleType: 'none', paddingLeft: '0px'}}>
                                            {plan.features.map((feature, i) => 
                                                <li key={i}>
                                                    <i className="fa fa-check" style={{color: "#2e7d32"}} /> {feature}
                                                </li>
                                            )}
                                            </ul>
                                        </CardContent>
                                        <CardActions style={{
                                            marginTop: 'auto',
                                            justifyContent: 'center',
                                        }}>
                                            {plan.current?(
                                                <Button color="success" variant="contained" disabled={true}>Current Plan</Button>
                                            ):(
                                                <Button color="success" variant={(plan.id!==selectedPlan.id)?"outlined":"contained"} onClick={() => {
                                                    for(let i=0; i<plans.length; i++){
                                                        if(plans[i].id === plan.id){
                                                            setSelectedPlan(plan);
                                                        }
                                                    }
                                                }}>{plan.id===selectedPlan.id && <><i className="fa fa-check" /> </>}{(plan.id!==selectedPlan.id)?"Select":"Selected"}</Button>    
                                            )}
                                        </CardActions>
                                    </Card>
                                </Grid>
                            )}
                            </>
                        
                    </Grid>
                    {selectedPlan.id !== 0 && selectedPlan.price > 0 && 
                        <div style={{justifyContent: 'center', marginTop: '50px'}}>
                            <h2>Billing Details</h2>
                            <Grid container spacing={3}>
                                <Grid container item xs={12}>
                                    <Card style={{
                                        width: '100%',
                                        paddingBottom: '20px',
                                    }}>
                                        <CardContent>
                                            <Container maxWidth="sm">
                                                <Stack spacing={3}>
                                                    {countryError !== null && 
                                                        <Alert severity="error" onClose={() => setCountryError(null)}>{countryError}</Alert>
                                                    }
                                                    <Autocomplete
                                                        value={(country !== '')?(countries.find(obj =>{
                                                            return obj.code === country
                                                        })):(null)}
                                                        options={countries}
                                                        autoHighlight
                                                        getOptionLabel={(option) => option.label}
                                                        renderOption={(props, option) => (
                                                            <Box component="li" sx={{ '& > img': { mr: 2, flexShrink: 0 } }} {...props}>
                                                                <img
                                                                    loading="lazy"
                                                                    width="20"
                                                                    src={`https://flagcdn.com/w20/${option.code.toLowerCase()}.png`}
                                                                    srcSet={`https://flagcdn.com/w40/${option.code.toLowerCase()}.png 2x`}
                                                                    alt=""
                                                                />
                                                                {option.label}
                                                            </Box>
                                                        )}
                                                        renderInput={(params) => (
                                                            <TextField
                                                                {...params}
                                                                label="Country"
                                                                inputProps={{
                                                                    ...params.inputProps,
                                                                    autoComplete: 'new-password',
                                                                }}
                                                            />
                                                        )}
                                                        onChange={(event, newValue) => {
                                                            if(newValue && newValue.code){
                                                                setCountry(newValue.code);
                                                                setState("");
                                                                if(newValue.states){
                                                                    setStates(newValue.states);
                                                                }else{
                                                                    setStates([]);
                                                                }
                                                                setCountryError(null);
                                                            }
                                                        }}
                                                    />
                                                    {states.length > 0 &&
                                                    <>
                                                        {stateError !== null && 
                                                            <Alert severity="error" onClose={() => setStateError(null)}>{stateError}</Alert>
                                                        }
                                                        <Autocomplete
                                                            value={(state !== '')?(states.find(obj =>{
                                                                return obj.code === state
                                                            })):(null)}
                                                            options={states}
                                                            autoHighlight
                                                            getOptionLabel={(option) => option.label}
                                                            renderOption={(props, option) => (
                                                                <Box component="li" {...props}>
                                                                    {option.label}
                                                                </Box>
                                                            )}
                                                            renderInput={(params) => (
                                                                <TextField
                                                                    {...params}
                                                                    label="State"
                                                                    inputProps={{
                                                                        ...params.inputProps,
                                                                        autoComplete: 'new-password',
                                                                    }}
                                                                />
                                                            )}
                                                            onChange={(event, newValue) => {
                                                                if(newValue && newValue.code){
                                                                    setState(newValue.code);
                                                                    setStateError(null);
                                                                }
                                                                
                                                            }}
                                                        />
                                                    </>
                                                    }
                                                    {cardError !== null && 
                                                        <Alert severity="error" onClose={() => setCardError(null)}>{cardError}</Alert>
                                                    }
                                                    <div style={{position: "relative", minHeight: '56px', padding: '15px'}}>
                                                        <CardElement options={CARD_ELEMENT_OPTIONS}></CardElement>
                                                        <fieldset style={{
                                                            borderColor: 'rgba(0, 0, 0, 0.23)',
                                                            borderStyle: 'solid',
                                                            borderWidth: '1px',
                                                            borderRadius: '4px',
                                                            position: 'absolute',
                                                            top: '-5px',
                                                            left: '0',
                                                            right: '0',
                                                            bottom: '0',
                                                            margin: '0',
                                                            padding: '0 8px',
                                                            overflow: 'hidden',
                                                            pointerEvents: 'none'
                                                            
                                                        }}></fieldset>
                                                    </div>
                                                </Stack>
                                            </Container>
                                        </CardContent>
                                    </Card>
                                </Grid>
                            </Grid>
                        </div>
                    }
                    {selectedPlan.id!==0 &&
                        <div style={{marginTop: '50px'}}>
                            <Container maxWidth="sm">
                                <Stack spacing={3}>
                                {errorMessage !== null && 
                                    <Alert severity="error" onClose={() => setErrorMessage(null)}>{errorMessage}</Alert>
                                }
                                <Button color="success" size="large" variant="contained" disabled={selectedPlan.id===0||processing?true:false} onClick={e => {
                                    subscribe(e);
                                }}>{processing?(<><Loader /> Processing...</>):(<>Subscribe Now</>)}</Button>
                                </Stack>
                            </Container>
                        </div>
                    }
                </Box>
            </Paper>
            ):(
                <Alert severity="warning">No plan is found.</Alert>
            )}</>
            ):(
                <Alert severity="error" >Access Denied.</Alert>
            )}</>
        ):(
            <Loader text="loading plans..." />
        )}
        </>

    )
}
Example #24
Source File: Dashboard.js    From admin-web with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {
    const {
      classes,
      t,
      cpuPercent,
      disks,
      memory,
      swap,
      swapPercent,
      load,
      statistics,
    } = this.props;
    const { snackbar } = this.state;

    return (
      <div className={classes.root}>
        <TopBar />
        <div className={classes.toolbar} />
        {config?.loadAntispamData && <Typography variant="h2" className={classes.pageTitle}>
          {t("Mail filter statistics")}
          <IconButton
            size="small"
            href="https://docs.grommunio.com/admin/administration.html#antispam"
            target="_blank"
          >
            <HelpOutline fontSize="small"/>
          </IconButton>
        </Typography>}
        {config?.loadAntispamData && <Typography variant="caption" className={classes.subtitle}>
          {t("mailfilter_sub")}
        </Typography>}
        <div className={classes.dashboardLayout}>
          {config?.loadAntispamData && <div className={classes.antispam}>
            <AntispamStatistics data={statistics}/>
          </div>}
          <div className={classes.services}>
            <ServicesChart/>
          </div>
          <div className={classes.headline}>
            <Typography variant="h2" className={classes.pageTitle}>
              {t("Performance")}
              <IconButton
                size="small"
                href="https://docs.grommunio.com/admin/administration.html#services"
                target="_blank"
              >
                <HelpOutline fontSize="small"/>
              </IconButton>
            </Typography>
            <Typography variant="caption" className={classes.subtitle}>
              {t("performance_sub")}
            </Typography>
          </div>
          <div className={classes.cpu}>
            <Paper elevation={1} className={classes.donutAndLineChart}>
              <div className={classes.donutChart}>
                <CPUPieChart cpuPercent={cpuPercent} />
              </div>
              <div className={classes.lineChart}>
                <CPULineChart cpuPercent={cpuPercent} />
              </div>
            </Paper>
          </div>
          <div className={classes.memory}>
            <Paper elevation={1} className={classes.donutAndLineChart}>
              <div className={classes.donutChart}>
                <MemoryPieChart memory={memory} />
              </div>
              <div className={classes.lineChart}>
                <MemoryChart memory={memory} />
              </div>
            </Paper>
          </div>
          <div className={classes.swap}>
            <Paper elevation={1} className={classes.donutAndLineChart}>
              {!!swapPercent && <div>
                <SwapPieChart swap={swap} swapPercent={swapPercent} />
              </div>}
              <div className={!swapPercent ? classes.fullChart : classes.lineChart}>
                <DisksChart disks={disks} />
              </div>
            </Paper>
          </div>
          <div className={classes.disk}>
            <LoadChart load={load} />
          </div>
        </div>
        <Typography variant="h2" className={classes.pageTitle}>
          {t("Versions")}
        </Typography>
        <About />
        <Feedback
          snackbar={snackbar}
          onClose={() => this.setState({ snackbar: "" })}
        />
      </div>
    );
  }
Example #25
Source File: index.js    From fireact with MIT License 4 votes vote down vote up
PaymentMethod = () => {
    const title = 'Update Payment Method';
    const mountedRef = useRef(true);
    const history = useHistory();

    const { userData, authUser } = useContext(AuthContext);
    const stripe = useStripe();
    const elements = useElements();
    const { setBreadcrumb } = useContext(BreadcrumbContext);

    const CARD_ELEMENT_OPTIONS = {
        style: {
            base: {
              color: '#32325d',
              fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
              fontSmoothing: 'antialiased',
              fontSize: '16px',
              '::placeholder': {
                color: '#aab7c4'
              }
            },
            invalid: {
              color: '#fa755a',
              iconColor: '#fa755a'
            }
        },
        hidePostalCode: true
    };

    const [processing, setProcessing] = useState(false);
    const [success, setSuccess] = useState(false);
    const [cardError, setCardError] = useState(null);
    const [errorMessage, setErrorMessage] = useState(null);

    const subscribe = async(event) => {
        event.preventDefault();
        setProcessing(true);
        setErrorMessage(null);
        setSuccess(false);

        let hasError = false;
        let paymentMethodId = '';

        setCardError(null);

        if (!stripe || !elements) {
            // Stripe.js has not loaded yet. Make sure to disable
            // form submission until Stripe.js has loaded.
            return;
        }

        // Get a reference to a mounted CardElement. Elements knows how
        // to find your CardElement because there can only ever be one of
        // each type of element.
        const cardElement = elements.getElement(CardElement);

        // Use your card Element with other Stripe.js APIs
        const {error, paymentMethod} = await stripe.createPaymentMethod({
            type: 'card',
            card: cardElement,
        });

        if (error) {
            setCardError(error.message);
            hasError = true;
        } else {
            paymentMethodId = paymentMethod.id;
        }

        
        if(!hasError){
            const updatePaymentMethod = CloudFunctions.httpsCallable('updatePaymentMethod');
            updatePaymentMethod({
                accountId: userData.currentAccount.id,
                paymentMethodId: paymentMethodId
            }).then(res => {
                if (!mountedRef.current) return null
                setSuccess(true);
                setProcessing(false);
            }).catch(err => {
                if (!mountedRef.current) return null
                setProcessing(false);
                setErrorMessage(err.message);
            });
        }else{
            setProcessing(false);
        }
    }

    useEffect(() => {
        setBreadcrumb([
            {
                to: "/",
                text: "Home",
                active: false
            },
            {
                to: "/account/"+userData.currentAccount.id+"/",
                text: userData.currentAccount.name,
                active: false
            },
            {
                to: "/account/"+userData.currentAccount.id+"/billing",
                text: 'Billing',
                active: false
            },   
            {
                to: null,
                text: title,
                active: true
            }
        ]);
    },[userData, setBreadcrumb, title]);

    useEffect(() => {
        return () => { 
            mountedRef.current = false
        }
    },[]);

    return (
        <>
            <Paper>
                <Box p={2}>
                    {userData.currentAccount.price > 0 ? (
                        <Stack spacing={3}>
                            {(userData.currentAccount.owner === authUser.user.uid)?(
                                <>
                                    {success && 
                                    <Alert severity="success" onClose={() => setSuccess(false)}>The payment method has been successfully updated.</Alert>
                                    }
                                    {errorMessage !== null && 
                                    <Alert severity="error" onClose={() => setErrorMessage(null)}>{errorMessage}</Alert>
                                    }
                                    {cardError !== null && 
                                        <Alert severity="error" onClose={() => setCardError(null)}>{cardError}</Alert>
                                    }
                                    <div style={{position: "relative", minHeight: '56px', padding: '15px', maxWidth: '500px'}}>
                                        <CardElement options={CARD_ELEMENT_OPTIONS}></CardElement>
                                        <fieldset style={{
                                            borderColor: 'rgba(0, 0, 0, 0.23)',
                                            borderStyle: 'solid',
                                            borderWidth: '1px',
                                            borderRadius: '4px',
                                            position: 'absolute',
                                            top: '-5px',
                                            left: '0',
                                            right: '0',
                                            bottom: '0',
                                            margin: '0',
                                            padding: '0 8px',
                                            overflow: 'hidden',
                                            pointerEvents: 'none'
                                            
                                        }}></fieldset>
                                    </div>
                                    <Stack direction="row" spacing={1} mt={2}>
                                        <Button variant="contained" disabled={processing} onClick={(e) => subscribe(e)}>
                                            {processing?(
                                                <><Loader /> Processing...</>
                                            ):(
                                                <>Save</>
                                            )}
                                        </Button>
                                        <Button variant="contained" color="secondary" disabled={processing} onClick={() => history.push("/account/"+userData.currentAccount.id+"/billing")}>Back</Button>
                                    </Stack>
                                </>
                            ):(
                                <Alert type="danger" message="Access Denied." dismissible={false} ></Alert>
                            )}
                        </Stack>
                    ):(
                        <Alert severity="error">The account doesn't support payment methods.</Alert>
                    )}
                </Box>
            </Paper>
        </>

    )
}
Example #26
Source File: LdapConfig.js    From admin-web with GNU Affero General Public License v3.0 4 votes vote down vote up
render() {
    const { classes, t } = this.props;
    const writable = this.context.includes(SYSTEM_ADMIN_WRITE);
    const { available, force, deleting, snackbar, server, bindUser, bindPass, starttls, baseDn, objectID, disabled,
      username, filter, templates, attributes, defaultQuota, displayName, searchAttributes,
      authBackendSelection, aliases, taskMessage, taskID } = this.state;
    return (
      <div className={classes.root}>
        <TopBar />
        <div className={classes.toolbar}></div>
        <form className={classes.base} onSubmit={this.handleSave}>
          <Typography variant="h2" className={classes.pageTitle}>
            {t("Directory")}
            <Tooltip
              className={classes.tooltip}
              title={t("ldap_settingsHelp")}
              placement="top"
            >
              <IconButton
                size="small"
                href="https://docs.grommunio.com/admin/administration.html#ldap"
                target="_blank"
              >
                <Help fontSize="small"/>
              </IconButton>
            </Tooltip>
          </Typography>
          <Typography variant="caption" className={classes.subtitle}>
            {t('ldap_sub')}
          </Typography>
          <Grid container className={classes.category}>
            <FormControlLabel
              control={
                <Switch
                  checked={!disabled}
                  onChange={this.handleActive}
                  name="disabled"
                  color="primary"
                />
              }
              label={<span>
                {t('LDAP enabled')}
                <Tooltip
                  className={classes.tooltip}
                  title={t("Enable LDAP service")}
                  placement="top"
                >
                  <IconButton size="small">
                    <Help fontSize="small"/>
                  </IconButton>
                </Tooltip>
              </span>}
            />
            <div className={classes.flexContainer}>
              <Tooltip placement="top" title={t("Synchronize already imported users")}>
                <Button
                  variant="contained"
                  color="primary"
                  style={{ marginRight: 16 }}
                  onClick={this.handleSync(false)}
                >
                  {t("Sync users")}
                </Button>
              </Tooltip>
              <Tooltip
                placement="top"
                title={t("ldap_import_tooltip")}
              >
                <Button
                  variant="contained"
                  color="primary"
                  style={{ marginRight: 16 }}
                  onClick={this.handleSync(true)}
                >
                  {t("Import users")}
                </Button>
              </Tooltip>
            </div>
          </Grid>
          <Typography
            color="inherit"
            variant="caption"
            style={{
              marginLeft: 16,
              color: available ? green['500'] : red['500'],
            }}
          >
            {!disabled && (available ? t('LDAP connectivity check passed') : t('LDAP connectivity check failed'))}
          </Typography>
          <Paper elevation={1} className={classes.paper}>
            <Typography variant="h6" className={classes.category}>{t('LDAP Server')}</Typography>
            <FormControl className={classes.formControl}>
              <div className={classes.flexRow}>
                <LdapTextfield
                  flex
                  label='LDAP Server'
                  autoFocus
                  placeholder="ldap://[::1]:389/"
                  onChange={this.handleInput('server')}
                  value={server || ''}
                  desc={t("ldap_server_desc")}
                  id="url"
                  name="url"
                  autoComplete="url"
                  InputLabelProps={{
                    shrink: true,
                  }}
                />
                <LdapTextfield
                  flex
                  label="LDAP Bind DN"
                  onChange={this.handleInput('bindUser')}
                  value={bindUser || ''}
                  desc={t("Distinguished Name used for binding")}
                  id="username"
                  name="username"
                  autoComplete="username"
                />
                <LdapTextfield
                  flex
                  label={t('LDAP Bind Password')}
                  onChange={this.handleInput('bindPass')}
                  value={bindPass || ''}
                  desc={t("ldap_password_desc")}
                  id="password"
                  name="password"
                  type="password"
                  autoComplete="current-password"
                />
                <FormControlLabel
                  control={
                    <Checkbox
                      checked={starttls || false}
                      onChange={this.handleCheckbox('starttls')}
                      name="starttls"
                      inputProps={{
                        autoComplete: 'starttls',
                        name: 'starttls',
                        id: 'starttls',
                      }}
                      color="primary"
                    />
                  }
                  label={<span>
                    {'STARTTLS'}
                    <Tooltip
                      className={classes.tooltip}
                      title="Whether to issue a StartTLS extended operation"
                      placement="top"
                    >
                      <IconButton size="small">
                        <Help fontSize="small"/>
                      </IconButton>
                    </Tooltip>
                  </span>}
                />
              </div>
              <LdapTextfield
                label='LDAP Base DN'
                onChange={this.handleInput('baseDn')}
                value={baseDn || ''}
                desc={t("Base DN to use for searches")}
                id="baseDn"
                name="baseDn"
                autoComplete="baseDn"
              />
            </FormControl>
          </Paper>
          <Paper elevation={1} className={classes.paper}>
            <Typography variant="h6" className={classes.category}>
              {t('User authentication mechanism')}
            </Typography>
            <FormControl className={classes.formControl}>
              <RadioGroup
                name="authBackendSelection"
                value={authBackendSelection}
                onChange={this.handleInput("authBackendSelection")}
                row
                className={classes.radioGroup}
                color="primary"
              >
                <FormControlLabel
                  value="externid"
                  control={<Radio color="primary"/>}
                  label={t("Automatic")}
                />
                <FormControlLabel value="always_mysql" control={<Radio color="primary"/>} label={t("Only MySQL")} />
                <FormControlLabel value="always_ldap" control={<Radio color="primary"/>} label={t("Only LDAP")} />
              </RadioGroup>
            </FormControl>
          </Paper>
          <Paper className={classes.paper} elevation={1}>
            <FormControl className={classes.formControl}>
              <Typography variant="h6" className={classes.category}>{t('Attribute Configuration')}</Typography>
              <LdapTextfield
                label={t('LDAP Template')}
                onChange={this.handleTemplate}
                value={templates}
                select
                desc={t("Mapping templates to use")}
                id="templates"
                name="templates"
                autoComplete="templates"
              >
                <MenuItem value='none'>{t('No template')}</MenuItem>
                <MenuItem value="OpenLDAP">OpenLDAP</MenuItem>
                <MenuItem value="ActiveDirectory">ActiveDirectory</MenuItem>
              </LdapTextfield>
              <LdapTextfield
                label={t('LDAP Filter')}
                onChange={this.handleInput('filter')}
                value={filter || ''}
                desc={t("LDAP search filter to apply to user lookup")}
                id="filter"
                name="filter"
                autoComplete="filter"
              />
              <LdapTextfield
                label={t('Unique Identifier Attribute')}
                onChange={this.handleInput('objectID')}
                value={objectID || ''}
                desc={t("ldap_oID_desc")}
                id="objectID"
                name="objectID"
                autoComplete="objectID"
              />
              <LdapTextfield
                label={t('LDAP Username Attribute')}
                onChange={this.handleInput('username')}
                value={username || ''}
                desc={t("ldap_username_desc")}
                id="username"
                name="username"
                autoComplete="username"
              />
              <LdapTextfield
                label={t('LDAP Display Name Attribute')}
                onChange={this.handleInput('displayName')}
                value={displayName || ''}
                desc={t("Name of the attribute that contains the name")}
                id="displayName"
                name="displayName"
                autoComplete="displayName"
              />
              <LdapTextfield
                label={t('LDAP Default Quota')}
                onChange={this.handleInput('defaultQuota')}
                value={defaultQuota}
                desc={t("ldap_defaultQuota_desc")}
                id="defaultQuota"
                name="defaultQuota"
                autoComplete="defaultQuota"
              />
              <LdapTextfield
                label={t('LDAP Aliases')}
                onChange={this.handleInput('aliases')}
                value={aliases}
                desc={t("LDAP alias mapping")}
                id="aliasMapping"
                name="aliasMapping"
                autoComplete="aliasMapping"
              />
            </FormControl>
          </Paper>
          <Paper elevation={1} className={classes.paper}>
            <Typography variant="h6" className={classes.category}>{t('LDAP Search Attributes')}</Typography>
            <Typography variant="caption" className={classes.category}>
              {t('ldap_attribute_desc')}
            </Typography>
            <Autocomplete
              value={searchAttributes || []}
              onChange={this.handleAutocomplete('searchAttributes')}
              className={classes.textfield}
              options={adminConfig.searchAttributes}
              multiple
              renderInput={(params) => (
                <TextField
                  {...params}
                />
              )}
            />
          </Paper>
          <Paper elevation={1} className={classes.paper}>
            <Typography variant="h6" className={classes.category}>
              {t('Custom Mapping')}
              <Tooltip
                className={classes.tooltip}
                title={t('ldap_mapping_desc')}
                placement="top"
              >
                <IconButton size="small">
                  <Help fontSize="small"/>
                </IconButton>
              </Tooltip>
            </Typography>
            {attributes.map((mapping, idx) =>
              <Grid className={classes.attribute} container alignItems="center" key={idx}>
                <LdapTextfield
                  label={t('Name')}
                  flex
                  onChange={this.handleAttributeInput('key', idx)}
                  value={mapping.key || ''}
                  desc={t("LDAP attribute to map")}
                />
                <Typography className={classes.spacer}>:</Typography>
                <LdapTextfield
                  label={t('Value')}
                  flex
                  onChange={this.handleAttributeInput('value', idx)}
                  value={mapping.value || ''}
                  desc={t("Name of the user property to map to")}
                />
                <IconButton
                  onClick={this.removeRow(idx)}
                  className={classes.removeButton}
                  size="large">
                  <Delete color="error" />
                </IconButton>
              </Grid>
            )}
            <Grid container justifyContent="center" className={classes.addButton}>
              <Button size="small" onClick={this.handleNewRow}>
                <Add color="primary" />
              </Button>
            </Grid>
          </Paper>
          <div className={classes.bottomRow}>
            <Button
              variant="contained"
              color="secondary"
              onClick={this.handleDelete}
              className={classes.deleteButton}
            >
              {t('Delete config')}
            </Button>
            <Button
              variant="contained"
              color="primary"
              type="submit"
              onClick={this.handleSave}
              disabled={!writable}
            >
              {t('Save')}
            </Button>
            <FormControlLabel
              className={classes.attribute}
              control={
                <Checkbox
                  checked={force || false}
                  onChange={this.handleCheckbox('force')}
                  name="disabled"
                  color="primary"
                />
              }
              label={<span>
                {t('Force config save')}
                <Tooltip
                  className={classes.tooltip}
                  title={t("Save LDAP configuration even if it's faulty")}
                  placement="top"
                >
                  <IconButton size="small">
                    <Help fontSize="small"/>
                  </IconButton>
                </Tooltip>
              </span>}
            />
          </div>
        </form>
        <DeleteConfig
          open={deleting}
          onSuccess={this.handleDeleteSuccess}
          onError={this.handleDeleteError}
          onClose={this.handleDeleteClose}
        />
        <TaskCreated
          message={taskMessage}
          taskID={taskID}
          onClose={this.handleTaskClose}
        />
        <Feedback
          snackbar={snackbar}
          onClose={() => this.setState({ snackbar: '' })}
        />
      </div>
    );
  }