@material-ui/lab#AlertTitle JavaScript Examples

The following examples show how to use @material-ui/lab#AlertTitle. 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: ImageErrorAlert.js    From treetracker-admin-client with GNU Affero General Public License v3.0 6 votes vote down vote up
function ImageErrorAlert({
  alertHeight,
  alertWidth,
  alertPadding,
  alertPosition,
  alertTextSize,
  alertTitleSize,
}) {
  return (
    <Alert
      severity="error"
      style={{
        flexDirection: 'column',
        position: alertPosition,
        width: alertWidth,
        height: alertHeight,
        padding: alertPadding,
      }}
    >
      <AlertTitle style={{ fontSize: alertTitleSize, whiteSpace: 'nowrap' }}>
        Failed to load image
      </AlertTitle>
      <div>
        <p style={{ fontSize: alertTextSize }}>
          please try — <strong>reloading the page</strong>
        </p>

        <p style={{ fontSize: alertTextSize }}>
          or — <strong>report the issue to an admin</strong>
        </p>
      </div>
    </Alert>
  );
}
Example #2
Source File: AlertMessage.js    From reddish with MIT License 6 votes vote down vote up
AlertMessage = ({ severity, error, clearError }) => {
  const classes = useAlertStyles();

  if (!error) {
    return null;
  }

  return (
    <div className={classes.root}>
      <Alert severity={severity} onClose={clearError}>
        <AlertTitle>Error</AlertTitle>
        {error}
      </Alert>
    </div>
  );
}
Example #3
Source File: ErrorMessage.js    From stack-underflow with MIT License 6 votes vote down vote up
AlertMessage = ({ errorMsg, clearErrorMsg }) => {
  const classes = useAlertStyles();

  if (!errorMsg) {
    return null;
  }

  return (
    <div className={classes.root}>
      <Alert severity="error" onClose={clearErrorMsg}>
        <AlertTitle>Error</AlertTitle>
        {errorMsg}
      </Alert>
    </div>
  );
}
Example #4
Source File: AlertBox.js    From to-view-list with MIT License 6 votes vote down vote up
AlertBox = ({ severity, message, clearError, title }) => {
  const classes = useAlertStyles();

  return (
    <div className={classes.root}>
      <Alert severity={severity} onClose={clearError}>
        <AlertTitle>{title || 'Error'}</AlertTitle>
        {message}
      </Alert>
    </div>
  );
}
Example #5
Source File: DemoCredsBox.js    From to-view-list with MIT License 6 votes vote down vote up
DemoCredsBox = () => {
  const classes = useAlertStyles();

  return (
    <div className={classes.root}>
      <Alert severity="info">
        <AlertTitle>Demo Account Credentials</AlertTitle>
        {demoCreds}
      </Alert>
    </div>
  );
}
Example #6
Source File: ErrorFallback.js    From akashlytics-deploy with GNU General Public License v3.0 6 votes vote down vote up
export function ErrorFallback({ error, resetErrorBoundary }) {
  const classes = useStyles();

  return (
    <div className={classes.root} role="alert">
      <Typography variant="h4" className={classes.heading}>
        Something went wrong:
      </Typography>

      <Alert severity="error" className={classes.alert}>
        <AlertTitle>Error</AlertTitle>
        {error.message}
      </Alert>

      <Button variant="contained" color="primary" onClick={resetErrorBoundary}>
        Try again
      </Button>
    </div>
  );
}
Example #7
Source File: Announcement.jsx    From redive_linebot with MIT License 6 votes vote down vote up
TopNews = props => {
  const { breakingNews } = props;
  const levels = ["success", "info", "warning", "error"];

  if (Object.keys(breakingNews).length === 0) {
    breakingNews.level = "success";
    breakingNews.title = "乾淨無比";
    breakingNews.content = "這個作者很懶,什麼話都沒說~";
    breakingNews.create_time = new Date().toString();
  }

  return (
    <Alert severity={levels.indexOf(breakingNews.level) === -1 ? "warning" : breakingNews.level}>
      <AlertTitle>{breakingNews.title}</AlertTitle>
      {breakingNews.content} <br />
      <Typography variant="caption" color="textSecondary">
        {new Date(breakingNews.create_time).toLocaleString()}
      </Typography>
    </Alert>
  );
}
Example #8
Source File: Notification.js    From scholar-front-end with MIT License 6 votes vote down vote up
function Notification() {
    const [open, setOpen] = React.useState(true);

    const classes = useStyles();
    return (
        <div className={classes.root}>
        <Collapse in={open}>
        <Alert severity="info" action={
            <IconButton aria-label="close" size="small" onClick={()=>{
                setOpen(false);
            }}>
                <CloseIcon fontSize="inherit"/>
            </IconButton>
        }>
            <AlertTitle>Price Drop</AlertTitle>
            <p className={classes.inform}>All Courses At <strong> 399 </strong>/-</p>
        </Alert>
        </Collapse>
    </div>
    )
}
Example #9
Source File: InspectClients.js    From management-center with Apache License 2.0 5 votes vote down vote up
Clients = (props) => {
	const classes = useStyles();
	const context = useContext(WebSocketContext);
	const dispatch = useDispatch();
	const history = useHistory();
	const confirm = useConfirm();
	const { enqueueSnackbar } = useSnackbar();
	const { client: brokerClient } = context;

	const { inspectFeature, userProfile, roles = [], clients = [], onSort, sortBy, sortDirection } = props;

	const onUpdateUserRoles = async (user, roles = []) => {
		if (!roles) {
			roles = [];
		}
		const rolenames = roles.map((role) => role.value);
		await brokerClient.updateUserRoles(user, rolenames);
		const clients = await brokerClient.inspectListClients();
		dispatch(updateInspectClients(clients));
	};

	const onSelectClient = async (username) => {
		const client = await brokerClient.inspectGetClient(username);
		dispatch(updateInspectClient(client));
		history.push(`/admin/inspect/clients/detail/${username}`);
	};

	return (
		<div>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<RouterLink className={classes.breadcrumbLink} color="inherit" to="/admin">
					Admin
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Inspect
				</Typography>
			</Breadcrumbs>
			{/* TODO: Quick hack to detect whether feature is supported */}
			{inspectFeature?.error ? <><br/><Alert severity="warning">
				<AlertTitle>{inspectFeature.error.title}</AlertTitle>
				{inspectFeature.error.message}
			</Alert></> : null}
			{!inspectFeature?.error && inspectFeature?.supported === false ? <><br/><Alert severity="warning">
				<AlertTitle>Feature not available</AlertTitle>
				Make sure that this feature is included in your MMC license.
			</Alert></> : null}
			<br />
			<br />
			
			{ createClientsTable(clients, classes, props, onUpdateUserRoles, onSelectClient) }
		</div>
	);
}
Example #10
Source File: Notify.jsx    From redive_linebot with MIT License 5 votes vote down vote up
Notify = () => {
  const classes = useStyles();
  const option = useOption();
  const { loading, isBinding, option: optionData, isLoggedIn } = option;

  useEffect(() => {
    window.document.title = "訂閱通知設定頁面";
  }, []);

  return (
    <React.Fragment>
      <Grid container direction="column" className={classes.root}>
        <Grid container item>
          <Grid item>
            <Description />
          </Grid>
          <Grid item>
            <IconButton color="secondary" onClick={option.handleOpen}>
              <SettingsIcon />
            </IconButton>
          </Grid>
        </Grid>
        <Grid item>
          {isBinding ? (
            <Alert severity="success">綁定中!需更改設定請點擊標題右邊齒輪!</Alert>
          ) : (
            <Alert severity="warning">
              <AlertTitle>注意!</AlertTitle>
              {isLoggedIn
                ? "尚未綁定LINE Notify!點擊標題右邊齒輪進行設定吧!"
                : "尚未登入!請先點擊右上角的登入鈕"}
            </Alert>
          )}
        </Grid>
        {isBinding
          ? optionData.subData.map((data, index) => (
              <Grid item key={index}>
                <CardOption
                  handleChange={event =>
                    option.handleSwitch(data.key, event.target.checked ? 1 : 0)
                  }
                  {...data}
                />
              </Grid>
            ))
          : null}
        <Grid item>
          <ActionDialog {...option} />
        </Grid>
      </Grid>
      {
        <Backdrop className={classes.backdrop} open={loading}>
          <CircularProgress color="inherit" />
        </Backdrop>
      }
    </React.Fragment>
  );
}
Example #11
Source File: withAuthWrapper.js    From nextjs-todo-list with MIT License 4 votes vote down vote up
withAuthWrapper = (Component) => (props) => {
  const [loading, setLoading] = useState(false);
  const [errorMessage, setErrorMessage] = useState('');
  const { activateAuth } = useContext(AuthContext);
  const firebase = useContext(FirebaseContext);

  useEffect(() => {
    redirectIfAuthenticated();
  }, []);

  if (errorMessage) {
    setTimeout(() => {
      setErrorMessage('');
    }, 5000);
  }

  const saveAuthAndRedirect = (data) => {
    try {
      const { user } = data;
      let { idToken = null } = data;
      if (!idToken) idToken = user.uid;
      activateAuth(user, idToken);
      setLoading(false);
      Router.push('/');
    } catch (error) {
      console.log({ error });
    }
  };

  const onGoogleSignIn = async () => {
    setLoading(true);
    firebase
      .doAuthWithGoogle()
      .then((resp) => {
        saveAuthAndRedirect(resp);
      })
      .catch((error) => {
        const { message } = error;
        setLoading(false);
        setErrorMessage(message);
      });
  };

  const onGithubSignIn = async () => {
    setLoading(true);
    await firebase
      .doAuthWithGithub()
      .then((resp) => saveAuthAndRedirect(resp))
      .catch((error) => {
        setLoading(false);
        const { message } = error;
        setErrorMessage(message);
      });
  };

  return (
    <MainLayout>
      <div className="container">
        <div className="main">
          {errorMessage && (
            <Alert severity="error">
              <AlertTitle>Error</AlertTitle>
              {errorMessage}
            </Alert>
          )}
          <Component
            {...props}
            loading={loading}
            onGoogleSignIn={onGoogleSignIn}
            onGithubSignIn={onGithubSignIn}
          />
        </div>
      </div>
      <style jsx>
        {`
          .container {
            width: 100%;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
          }
        `}
      </style>
    </MainLayout>
  );
}
Example #12
Source File: index.jsx    From redive_linebot with MIT License 4 votes vote down vote up
TradeOrder = () => {
  const isLoggedIn = liff.isLoggedIn();
  const seletEl = useRef(null);
  const chargeEl = useRef(null);
  const [{ data = [], loading }, fetchItems] = useAxios("/api/Inventory", { manual: true });
  const [{ data: createResponse, loading: createLoading, error }, createOrder] = useAxios(
    {
      url: "/api/Trade",
      method: "POST",
    },
    { manual: true }
  );
  const [{ open, message, severity }, { handleOpen, handleClose }] = useHintBar();
  const query = useQuery();
  const targetId = query.get("target_id");
  const { userId } = liff.getContext();

  useEffect(() => {
    if (!isLoggedIn) return;

    fetchItems();
    return () => {};
  }, [isLoggedIn, fetchItems]);

  useEffect(() => {
    if (!targetId) {
      handleOpen("未指定交易對象", "error");
    }

    return () => {};
  }, [targetId, handleOpen]);

  useEffect(() => {
    (() => {
      if (!error) return;
      handleOpen(get(error, "response.data.message"), "error");
    })();
    return () => {};
  }, [error]);

  const pageLoading = loading || createLoading;

  if (!isLoggedIn) {
    return <AlertLogin />;
  }

  const handleCancel = () => {
    if (liff.isInClient()) {
      liff.closeWindow();
    } else {
      window.location.href = "/";
    }
  };

  const handleSubmit = () => {
    let payload = {
      targetId,
      itemId: parseInt(seletEl.current.value),
      charge: parseInt(chargeEl.current.value),
    };

    if (get(payload, "charge", 0) <= 0) {
      handleOpen("請輸入收費金額", "error");
    } else {
      createOrder({
        data: payload,
      });
    }
  };

  if (createResponse) {
    return <TradeCreateResult marketId={get(createResponse, "marketId")} />;
  }

  const isSelf = userId === targetId;
  const selfAlert = (
    <Grid item>
      <Alert severity="error">
        <AlertTitle>錯誤</AlertTitle>
        您不能與自己進行交易
      </Alert>
    </Grid>
  );

  return (
    <Grid container direction="column" spacing={2}>
      {pageLoading && <DotsLoading />}
      {isSelf && selfAlert}
      <Grid item>
        <Alert severity="warning">
          <AlertTitle>注意</AlertTitle>
          <Typography variant="inherit" component="p">
            1. 請確認您的交易對象是否已經在您的好友列表中,如果沒有,請先加入好友
          </Typography>
          <Typography variant="inherit">
            2. 交易對象為指令自動帶出,如果您不是要跟對方交易,請直接關閉視窗
          </Typography>
        </Alert>
      </Grid>
      <Grid item container spacing={2}>
        <Grid item xs={12}>
          <TextField label="交易對象" value={targetId} disabled variant="outlined" fullWidth />
        </Grid>
        <Grid item xs={12}>
          <TextField
            select
            label="選擇商品"
            SelectProps={{
              native: true,
            }}
            inputRef={seletEl}
            fullWidth
            variant="outlined"
            helperText="只會列出您擁有的商品"
            autoFocus
            disabled={isSelf}
          >
            {data.map(item => (
              <option key={item.itemId} value={item.itemId}>
                {item.name}
              </option>
            ))}
          </TextField>
        </Grid>
        <Grid item xs={12}>
          <TextField
            label="要求女神石"
            type="number"
            variant="outlined"
            defaultValue={0}
            fullWidth
            inputRef={chargeEl}
            helperText="請輸入要求的女神石數量,對方將會支付相應的女神石"
            disabled={isSelf}
          />
        </Grid>
        <Grid item xs={6}>
          <Button
            variant="contained"
            color="secondary"
            fullWidth
            onClick={handleCancel}
            disabled={isSelf}
          >
            取消交易
          </Button>
        </Grid>
        <Grid item xs={6}>
          <Button
            variant="contained"
            color="primary"
            fullWidth
            onClick={handleSubmit}
            disabled={isSelf}
          >
            送出交易
          </Button>
        </Grid>
      </Grid>
      <HintSnackBar {...{ open, message, severity, handleClose }} />
    </Grid>
  );
}
Example #13
Source File: BattleSign.jsx    From redive_linebot with MIT License 4 votes vote down vote up
BattleSign = () => {
  const [{ isError, isSuccess }, send] = useSendMessage();
  let { week, boss } = useParams();
  const location = useLocation();
  const classes = useStyles();
  const [state, setState] = useState({
    week: week || 1,
    boss: boss || 1,
    type: "1",
    damage: "",
    comment: "",
    maxDamage: 0,
  });
  const [alert, setAlert] = useState({ open: false, message: "" });
  const [Hotkeys, setHotKeys] = useState([]);

  useEffect(() => {
    window.document.title = "自訂報名內容";
  }, []);

  useEffect(() => {
    if (!isError) return;

    setAlert({ open: true, message: "發送失敗,不過幫你複製起來了!可直接到LINE貼上!" });
  }, [isError]);

  useEffect(() => {
    if (!isSuccess) return;
    window.liff.closeWindow();
  }, [isSuccess]);

  useEffect(() => {
    const querys = new window.URLSearchParams(location.search);
    let damage = querys.get("damage") || 0;

    setHotKeys([
      { title: "物理一刀", damage, comment: "物理一刀殺", type: "1" },
      { title: "法刀一刀", damage, comment: "法隊一刀殺", type: "1" },
    ]);

    setState({ ...state, maxDamage: parseInt(damage) });
  }, [location.search]);

  const alertClose = () => {
    setAlert({ ...alert, open: false, message: "" });
  };

  const handleDamage = event => {
    let damage = event.target.value;
    damage = /^\d+$/.test(damage) ? parseInt(damage) : "";
    setState({ ...state, damage });
  };

  const handleComment = event => {
    setState({ ...state, comment: event.target.value });
  };

  const handleType = event => {
    setState({ ...state, type: event.target.value });
  };

  return (
    <React.Fragment>
      <Grid container className={classes.gridBox}>
        <Grid item xs={12}>
          <Typography variant="h4" component="p">
            報名面版
          </Typography>
        </Grid>
        <Grid item xs={12}>
          <Alert severity="info">
            <AlertTitle>
              {state.week} 周 {state.boss} 王
            </AlertTitle>
            請注意是否為要 <strong>報名的王</strong> 以及 <strong>周次</strong>
          </Alert>
        </Grid>
      </Grid>
      <Grid container className={classes.gridBox} direction="column">
        <Grid item>
          <Typography variant="body1">快速鍵</Typography>
        </Grid>
        <Grid item>
          <ButtonGroup variant="text" color="primary" aria-label="text primary button group">
            {Hotkeys.map((hotkey, index) => (
              <Button
                key={index}
                onClick={() =>
                  setState({
                    ...state,
                    damage: hotkey.damage,
                    comment: hotkey.comment,
                    type: hotkey.type,
                  })
                }
              >
                {hotkey.title}
              </Button>
            ))}
          </ButtonGroup>
        </Grid>
      </Grid>
      <Grid container className={classes.gridBox} justify="space-around">
        <Grid item xs={12} sm={3}>
          <TextField
            select
            fullWidth
            label="刀種"
            value={state.type}
            onChange={handleType}
            SelectProps={{
              native: true,
            }}
            variant="outlined"
          >
            {SaberTypes.map(data => (
              <option key={data.value} value={data.value}>
                {data.title}
              </option>
            ))}
          </TextField>
        </Grid>
        <Grid container item xs={12} sm={3} direction="column">
          <Grid item>
            <TextField
              label="預計傷害"
              fullWidth
              type="number"
              variant="outlined"
              value={state.damage}
              onChange={handleDamage}
            />
          </Grid>
          <Grid item>
            <PrettoSlider
              min={0}
              max={state.maxDamage}
              step={window.Math.floor(state.maxDamage / 100)}
              value={parseInt(state.damage)}
              onChange={(event, value) => setState({ ...state, damage: value.toString() })}
            />
          </Grid>
        </Grid>
        <Grid item xs={12} sm={3}>
          <TextField
            label="備註留言"
            fullWidth
            variant="outlined"
            value={state.comment}
            onChange={handleComment}
          />
        </Grid>
        <Grid item xs={12}>
          <CopyToClipboard text={genMessage(state)}>
            <Button
              fullWidth
              variant="contained"
              color="primary"
              onClick={() => send(genMessage(state))}
            >
              送出
            </Button>
          </CopyToClipboard>
        </Grid>
      </Grid>
      <Snackbar open={alert.open} autoHideDuration={6000} onClose={alertClose}>
        <Alert elevation={6} variant="filled" onClose={alertClose} severity="warning">
          {alert.message}
        </Alert>
      </Snackbar>
    </React.Fragment>
  );
}
Example #14
Source File: EditProfile.js    From lifebank with MIT License 4 votes vote down vote up
EditProfilePage = () => {
  const { t } = useTranslation('translations')
  const classes = useStyles()
  const history = useHistory()
  const [, { logout }] = useUser()
  const [currentUser] = useUser()
  const [openSnackbar, setOpenSnackbar] = useState(false)
  const theme = useTheme()
  const isDesktop = useMediaQuery(theme.breakpoints.up('md'), {
    defaultMatches: false
  })
  const [
    loadProfile,
    { error: errorProfile, loading, data: { profile: { profile } = {} } = {} }
  ] = useLazyQuery(PROFILE_QUERY, { fetchPolicy: 'network-only' })
 
  const [
    revokeConsent,
    {
      error: errorRevokeConsent,
      loading: revokeConsentLoading,
      data: { revoke_consent: revokeConsentResult } = {}
    }
  ] = useMutation(REVOKE_CONSENT_MUTATION)

  const [
    grantConsent,
    {
      error: errorGrantConsent,
      loading: grantConsentLoading,
      data: { grant_consent: grantConsentResult } = {}
    }
  ] = useMutation(GRANT_CONSENT_MUTATION)

  const [
    editProfile,
    { error: errorEditResults, loading: editLoading, data: { edit_profile: editProfileResult } = {} }
  ] = useMutation(EDIT_PROFILE_MUTATION)

  const [setUsername] = useMutation(SET_USERNAME)

  const handleConsentChange = () => {
    profile?.consent ? revokeConsent() : grantConsent()
  }

  const handleUpdateUser = (userEdited, userNameEdited, account) => {
    editProfile({
      variables: {
        profile: userEdited
      }
    })
    if (account && userNameEdited) {
      setUsername({
        variables: {
          account: account,
          username: userNameEdited
        }
      })
    }
  }
  const handleCloseSnackBar = () => {
    setOpenSnackbar({ ...openSnackbar, show: false })
  }
  useEffect(() => {
    if (!currentUser) {
      return
    }

    loadProfile()
  }, [currentUser, loadProfile])

  useEffect(() => {
    if (grantConsentResult || revokeConsentResult) {
      loadProfile()
    }
  }, [grantConsentResult, revokeConsentResult, loadProfile])

  useEffect(() => {
    if (!editProfileResult) return

    const { success } = editProfileResult

    if (success) {
      history.push({
        pathname: '/profile',
        state: true
      })

    } else if (!success) {
      setOpenSnackbar({
        show: true,
        message: t('editProfile.duringSaveProfileData'),
        severity: 'error'
      })
    }
  }, [t, history, editProfileResult])


  useEffect(() => {
    if (errorProfile) {
      if (errorProfile.message === 'GraphQL error: Could not verify JWT: JWTExpired') {
        logout()
        history.push('/')
      } else history.push('/internal-error')
    }
  }, [logout, history, errorProfile])

  useEffect(() => {
    if (errorRevokeConsent || errorGrantConsent || errorEditResults) {
      setOpenSnackbar({
        show: true,
        message: t('editProfile.duringSaveProfileData'),
        severity: 'error'
      })
    }

  }, [t, errorRevokeConsent, errorGrantConsent, errorEditResults])


  return (
    <>
      {isDesktop && (
        <Box className={classes.wrapper}>
          {loading && <CircularProgress />}
          {!loading && currentUser && profile?.role === 'donor' && (
            <Suspense fallback={<CircularProgress />}>
              <EditProfileDonor
                profile={profile}
                onConsentChange={handleConsentChange}
                loading={grantConsentLoading || revokeConsentLoading || editLoading}
                onSubmit={handleUpdateUser}
              />
            </Suspense>
          )}
          {!loading && currentUser && profile?.role === 'sponsor' && (
            <Suspense fallback={<CircularProgress />}>
              <EditProfileSponsor
                profile={profile}
                onSubmit={handleUpdateUser}
                loading={editLoading}
              />
            </Suspense>
          )}
          {!loading && currentUser && profile?.role === 'lifebank' && (
            <Suspense fallback={<CircularProgress />}>
              <EditProfileBank
                profile={profile}
                userName={profile.username}
                onSubmit={handleUpdateUser}
                loading={editLoading}
              />
            </Suspense>
          )}
        </Box >
      )}

      {!isDesktop && (
        <Box className={classes.wrapperMobile}>
          {loading && <CircularProgress />}
          {!loading && currentUser && profile?.role === 'lifebank' && (
            <Suspense fallback={<CircularProgress />}>
              <EditProfileBankMobile
                profile={profile}
                userName={profile.username}
                onSubmit={handleUpdateUser}
                loading={editLoading}
              />
            </Suspense>
          )}
          {!loading && currentUser && profile?.role === 'sponsor' && (
            <Suspense fallback={<CircularProgress />}>
              <EditProfileSponsorMobile
                profile={profile}
                onSubmit={handleUpdateUser}
                loading={editLoading}
              />
            </Suspense>
          )}
          {!loading && currentUser && profile?.role === 'donor' && (
            <Suspense fallback={<CircularProgress />}>
              <EditProfileDonor
                profile={profile}
                onConsentChange={handleConsentChange}
                loading={grantConsentLoading || revokeConsentLoading || editLoading}
                onSubmit={handleUpdateUser}
              />
            </Suspense>
          )}
        </Box>
      )}
      <Snackbar open={openSnackbar.show} autoHideDuration={4000} onClose={handleCloseSnackBar}>
        <Alert
          severity={openSnackbar.severity}
          action={
            <IconButton
              aria-label="close"
              color="inherit"
              size="small"
              onClick={handleCloseSnackBar}
            >
              <CloseIcon fontSize="inherit" />
            </IconButton>
          }
        >
          <AlertTitle>{t('editProfile.error')}</AlertTitle>
          {openSnackbar.message}
        </Alert>
      </Snackbar>
    </>
  )
}
Example #15
Source File: TopicTree.js    From management-center with Apache License 2.0 4 votes vote down vote up
TopicTree = ({ topicTree, lastUpdated, currentConnectionName, settings }) => {
	const classes = useStyles();
	const [messageHistory, setMessageHistory] = React.useState([]);
	const [selectedNode, setSelectedNode] = React.useState({});
	const [selectedNodeId, setSelectedNodeId] = React.useState('');

	useEffect(() => {
		const parts = selectedNodeId.split('/');
		parts.shift();
		let current = topicTree.topicTree;
		parts.forEach((part, index) => {
			if (current[part]) {
				current = current[part];
			}
		});
		current = Object.assign({}, current);
		current._received = Date.now();
		if (current._message !== selectedNode?._message // if new message
			&& current._messagesCounter > selectedNode?._messagesCounter) // quick fix
			{
			setSelectedNode(current);
			messageHistory.unshift(current);
			setMessageHistory(messageHistory.slice(0, 51));
		}
	})

	const onLabelClick = (node) => {
		setSelectedNode(node);
		setSelectedNodeId(node.id);
		setMessageHistory([]);
	};

	const data = generateTreeData('topic-tree-root', 'Topic Tree', topicTree);

	useEffect(() => {
		setSelectedNode(null);
		setSelectedNodeId('');
		setMessageHistory([]);
	}, [currentConnectionName]);

	const renderTree = (node) => (
		<StyledTreeItem
			nodeId={node.id}
			labelText={node.name}
			onLabelClick={() => {
				onLabelClick(node);
			}}
			//   labelIcon={InfoIcon}
			message={isJSON(node._message) ? null : node._message}
			topicsCounter={node._topicsCounter}
			labelInfo={node._messagesCounter}
			//   color="#e3742f"
			//   bgColor="#fcefe3"
		>
			{/* <TreeItem key={node.id} nodeId={node.id} label={node.name}> */}
			{Array.isArray(node.children) ? node.children.map((childNode) => renderTree(childNode)) : null}
			{/* </TreeItem> */}
		</StyledTreeItem>
	);

	return (
		<div>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<RouterLink className={classes.breadcrumbLink} to="/system">
					System
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Topic Tree
				</Typography>
			</Breadcrumbs>
			{(settings?.topicTreeEnabled === false) ? <><br/><Alert severity="warning">
				<AlertTitle>Topic tree not enabled</AlertTitle>
				The MMC is currently not collecting topic tree data. If you want to collect data, please enable the topic tree feature in the settings page.
				Note that if you enable this setting and the MMC is collecting topic tree data, the performance of the MMC backend might decrease.
			</Alert></> : null}
			<br />

			<Grid container spacing={3}>
				<Grid item xs={6}>
					<Paper className={classes.paper}>
						<TreeView
							className={classes.root}
							defaultCollapseIcon={<ExpandMoreIcon />}
							defaultExpandIcon={<ChevronRightIcon />}
							// defaultExpanded={["topic-tree-root"]}
						>
							{renderTree(data)}
						</TreeView>
					</Paper>
				</Grid>
				<Grid item xs={6}>
					<Paper className={classes.paper}>
						<TableContainer component={Paper} className={classes.table}>
							<Table size="medium">
								<TableBody>
									{selectedNode?._name && (
										<TableRow>
											<TableCell>
												<strong>Name</strong>
											</TableCell>
											<TableCell>{selectedNode?._name}</TableCell>
										</TableRow>
									)}
									{selectedNode?._topic && (
										<TableRow>
											<TableCell>
												<strong>Topic</strong>
											</TableCell>
											<TableCell>{selectedNode?._topic}</TableCell>
										</TableRow>
									)}
									{selectedNode?._created && (
										<TableRow>
											<TableCell>
												<strong>Created</strong>
											</TableCell>
											<TableCell>{moment(selectedNode?._created).format('LLLL')}</TableCell>
										</TableRow>
									)}
									{selectedNode?._lastModified && (
										<TableRow>
											<TableCell>
												<strong>Last modified</strong>
											</TableCell>
											<TableCell>{moment(selectedNode?._lastModified).format('LLLL')}</TableCell>
										</TableRow>
									)}
									{typeof selectedNode?._qos === 'number' && (
										<TableRow>
											<TableCell>
												<strong>QoS</strong>
											</TableCell>
											<TableCell>{selectedNode?._qos}</TableCell>
										</TableRow>
									)}
									{(selectedNode?._retain === false || selectedNode?._retain === true) && (
										<TableRow>
											<TableCell>
												<strong>Retain</strong>
											</TableCell>
											<TableCell>{selectedNode?._retain ? 'yes' : 'no'}</TableCell>
										</TableRow>
									)}
									{selectedNode?._topicsCounter >= 0 && (
										<TableRow>
											<TableCell>
												<strong>Sub topics</strong>
											</TableCell>
											<TableCell>{selectedNode?._topicsCounter}</TableCell>
										</TableRow>
									)}
									{selectedNode?._messagesCounter && (
										<TableRow>
											<TableCell>
												<strong>Total messages</strong>
											</TableCell>
											<TableCell>{selectedNode?._messagesCounter}</TableCell>
										</TableRow>
									)}
									{/* {selectedNode?._message && selectedNode?._message.startsWith('{') && ( */}
									{selectedNode?._message && (
										<TableRow>
											<TableCell 
												className={classes.payloadHistory}
											>
												<strong>Payload</strong>
											</TableCell>
											<TableCell>
												<TextareaAutosize
													className={classes.payloadDetail}
													rows={5}
													value={
														isJSON(selectedNode?._message)
														? prettifyJSON(selectedNode?._message)
														: selectedNode?._message
													}
												/>
											</TableCell>
										</TableRow>
									)}
									{/* {selectedNode?._message && !selectedNode?._message.startsWith('{') && (
										<TableRow>
											<TableCell>
												<strong>Payload</strong>
											</TableCell>
											<TableCell>{selectedNode?._message}</TableCell>
										</TableRow>
									)} */}
								</TableBody>
							</Table>
						</TableContainer>
						<TableContainer component={Paper} className={classes.table}>
							<Table size="medium">
								<TableBody>
									{/* TODO: extract as component */}
									{messageHistory ? messageHistory.map((entry, index) => {
										if (index > 0) {
											// if (entry?._message && entry?._message.startsWith('{')) {
											if (entry?._message) {
												return <TableRow>
													<TableCell 
														className={classes.payloadHistory}
													>
														<strong>{moment(entry._received).format('HH:mm:ss:SSS')}</strong>
													</TableCell>
													<TableCell>
														<TextareaAutosize
															className={classes.payloadDetail}
															rows={5}
															value={
																isJSON(entry?._message)
																? prettifyJSON(entry?._message)
																: entry?._message
															}
														/>
													</TableCell>
												</TableRow>
											// } else if(entry?._message && !entry?._message.startsWith('{')) {
											// 	return <TableRow>
											// 		<TableCell>
											// 			<strong>{moment(entry._received).format('HH:mm:ss:SSS')}</strong>
											// 		</TableCell>
											// 		<TableCell>{entry?._message}</TableCell>
											// 	</TableRow>
											}
										}
									}) : null }
								</TableBody>
							</Table>
						</TableContainer>
					</Paper>
				</Grid>
			</Grid>
			{topicTree && <div style={{
				fontSize: '0.9em',
				position: 'absolute',
				right: '15px',
				top: '70px'
			}}>
				Topic tree last updated at: {moment(lastUpdated).format('hh:mm:ss a')}
			</div>}
		</div>
	);
}
Example #16
Source File: Streams.js    From management-center with Apache License 2.0 4 votes vote down vote up
Streams = (props) => {
	const classes = useStyles();
	const context = useContext(WebSocketContext);
	const dispatch = useDispatch();
	const history = useHistory();
	const confirm = useConfirm();
	const { enqueueSnackbar } = useSnackbar();
	const { client: brokerClient } = context;
	const { streamprocessingFeature, connectionID, streams = [], onSort, sortBy, sortDirection } = props;
	const [replayStreamEditorOpen, setReplayStreamEditorOpen] = React.useState(false);
	const [replayStream, setReplayStream] = React.useState({});

	const handleClickReplayStreamEditorOpen = () => {
		setReplayStreamEditorOpen(true);
	};
  
	const handleReplayStreamEditorClose = () => {
		setReplayStreamEditorOpen(false);
	};

	const handleReplay = async (stream, { replayTopic, gte, lte, reverse, limit, speed }) => {
		try {
			await brokerClient.replayStream({
				streamname: stream.streamname,
				replayTopic,
				gte,
				lte,
				reverse,
				limit,
				speed
			});
			const streams = await brokerClient.listStreams();
			dispatch(updateStreams(streams));
			enqueueSnackbar(`Successfully started replay of stream "${stream.streamname}"`, {
				variant: 'success'
			});
			setReplayStreamEditorOpen(false);
		} catch (error) {
			enqueueSnackbar(`Error starting replay of stream "${stream.streamname}". Reason: ${error}`, {
				variant: 'error'
			});
			setReplayStreamEditorOpen(false);
		}
	}

	const onNewStream = () => {
		history.push('/streams/new');
	};

	const onReload = async () => {
		const streams = await brokerClient.listStreams();
		dispatch(updateStreams(streams));
	}

	const onDisableStream = async (streamname) => {
		await confirm({
			title: 'Confirm stream disable',
			description: `Do you really want to disable stream "${streamname}"?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		await brokerClient.disableStream(streamname);
		const streams = await brokerClient.listStreams();
		enqueueSnackbar('Stream successfully disabled', {
			variant: 'success'
		});
		dispatch(updateStreams(streams));
	};

	const onEnableStream = async (streamname) => {
		await brokerClient.enableStream(streamname);
		const streams = await brokerClient.listStreams();
		enqueueSnackbar('Stream successfully enabled', {
			variant: 'success'
		});
		dispatch(updateStreams(streams));
	};

	const onEnableProcessStream = async (streamname) => {
		await brokerClient.processStream(streamname, true);
		const streams = await brokerClient.listStreams();
		enqueueSnackbar('Stream processing successfully enabled', {
			variant: 'success'
		});
		dispatch(updateStreams(streams));
	};

	const onDisableProcessStream = async (streamname) => {
		await brokerClient.processStream(streamname, false);
		const streams = await brokerClient.listStreams();
		enqueueSnackbar('Stream processing successfully disabled', {
			variant: 'success'
		});
		dispatch(updateStreams(streams));
	};

	const onEnablePersistStream = async (streamname) => {
		await brokerClient.persistStream(streamname, true);
		const streams = await brokerClient.listStreams();
		enqueueSnackbar('Stream persistence successfully enabled', {
			variant: 'success'
		});
		dispatch(updateStreams(streams));
	};

	const onDisablePersistStream = async (streamname) => {
		await brokerClient.persistStream(streamname, false);
		const streams = await brokerClient.listStreams();
		enqueueSnackbar('Stream persistence successfully disabled', {
			variant: 'success'
		});
		dispatch(updateStreams(streams));
	};

	const onSelectStream = async (streamname) => {
		const stream = await brokerClient.getStream(streamname);
		dispatch(updateStream(stream));
		history.push(`/streams/detail/${streamname}`);
	};

	const onClearStreamMessages = async (streamname) => {
		await confirm({
			title: 'Confirm clear all stream messages',
			description: `Do you really want to clear all messages in the stream "${streamname}"?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		try {
			await brokerClient.clearStreamMessages(streamname);
			enqueueSnackbar(`Messages from stream "${streamname}" successfully cleared.`, {
				variant: 'success'
			});
		} catch(error) {
			enqueueSnackbar(`Error clearing messages from "${streamname}". Reason: ${error}`, {
				variant: 'error'
			});
		}
	};

	const onReplayStream = async (stream) => {
		setReplayStream(stream);
		setReplayStreamEditorOpen(true);
	};

	const onDeleteStream = async (streamname) => {
		await confirm({
			title: 'Confirm stream deletion',
			description: `Do you really want to delete stream "${streamname}"?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		await brokerClient.deleteStream(streamname);
		enqueueSnackbar(`Stream "${streamname}" successfully deleted`, {
			variant: 'success'
		});
		const streams = await brokerClient.listStreams();
		dispatch(updateStreams(streams));
	};

	return (
		<div>
			<ReplayStreamDialog 
				stream={replayStream} 
				open={replayStreamEditorOpen} 
				handleReplay={handleReplay}
				handleClose={handleReplayStreamEditorClose}
			/>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Streams
				</Typography>
			</Breadcrumbs>
			{/* TODO: Quick hack to detect whether feature is supported */}
			{streamprocessingFeature?.supported === false ? <><br/><Alert severity="warning">
				<AlertTitle>Enterprise Solution feature</AlertTitle>
				Streams are a premium feature. For more information visit <a className={classes.link} href="https://www.cedalo.com">cedalo.com</a> or contact us at <a className={classes.link} href="mailto:[email protected]">[email protected]</a>.
			</Alert></> : null}
			{streamprocessingFeature?.supported !== false && <Grid container spacing={1} alignItems="flex-end">
				<Grid item xs={6}>
					<Button
						variant="outlined"
						color="default"
						size="small"
						className={classes.button}
						startIcon={<AddIcon />}
						onClick={(event) => {
							event.stopPropagation();
							onNewStream();
						}}
					>
						New Stream
					</Button>
				</Grid>
				<Grid item xs={6}>
					<Box display="flex" flexDirection="row-reverse">
						<Tooltip title="Reload streams">
							<IconButton
								color="secondary"
								aria-label="Reload streams"
								component="span"
								onClick={(event) => {
									event.stopPropagation();
									onReload();
								}}
							>
								<ReloadIcon />
							</IconButton>
						</Tooltip>
					</Box>
				</Grid>
			</Grid>}
			<br />
			{streamprocessingFeature?.supported !== false && streams && streams.length > 0 ? (
				<div>
					<Hidden xsDown implementation="css">
						<TableContainer component={Paper} className={classes.tableContainer}>
							<Table size="medium">
								<TableHead>
									<TableRow>
										{STREAM_TABLE_COLUMNS.map((column) => (
											<TableCell
												key={column.id}
												sortDirection={sortBy === column.id ? sortDirection : false}
											>
												{/* <TableSortLabel
                      active={sortBy === column.id}
                      direction={sortDirection}
                      onClick={() => onSort(column.id)}
                    > */}
												{column.key}
												{/* </TableSortLabel> */}
											</TableCell>
										))}
										<TableCell />
									</TableRow>
								</TableHead>
								<TableBody>
									{streams &&
										streams.map((stream) => (
											<StyledTableRow
												hover
												key={stream.streamname}
												onClick={(event) => {
													if (
														event.target.nodeName?.toLowerCase() === 'td'
													) {
														onSelectStream(stream.streamname);
													}
												}}
											>
												<TableCell>{stream.streamname}</TableCell>
												<TableCell>{stream.textdescription}</TableCell>
												<TableCell>{stream.sourcetopic}</TableCell>
												<TableCell>{stream.targettopic}</TableCell>
												<TableCell>{stream.targetqos}</TableCell>
												<TableCell>{stream.ttl}</TableCell>
												{/* <TableCell>{stream.replaying ? "replaying" : "stopped"}</TableCell> */}
												<TableCell>
													<Tooltip title="Process stream">
														<Switch
															checked={
																typeof stream.process === 'undefined' ||
																stream.process === true
															}
															onClick={(event) => {
																event.stopPropagation();
																if (event.target.checked) {
																	onEnableProcessStream(stream.streamname);
																} else {
																	onDisableProcessStream(stream.streamname);
																}
															}}
														/>
													</Tooltip>
												</TableCell>
												<TableCell>
													<Tooltip title="Persist stream">
														<Switch
															checked={
																typeof stream.persist === 'undefined' ||
																stream.persist === true
															}
															onClick={(event) => {
																event.stopPropagation();
																if (event.target.checked) {
																	onEnablePersistStream(stream.streamname);
																} else {
																	onDisablePersistStream(stream.streamname);
																}
															}}
														/>
													</Tooltip>
												</TableCell>
												<TableCell>
													<Tooltip title="Activate / Deactivate stream">
														<Switch
															checked={
																typeof stream.active === 'undefined' ||
																stream.active === true
															}
															onClick={(event) => {
																event.stopPropagation();
																if (event.target.checked) {
																	onEnableStream(stream.streamname);
																} else {
																	onDisableStream(stream.streamname);
																}
															}}
														/>
													</Tooltip>
												</TableCell>
												<TableCell align="right">
													<Tooltip title="Clear stream messages">
														<IconButton
															disabled={!stream.persist}
															size="small"
															onClick={(event) => {
																event.stopPropagation();
																onClearStreamMessages(stream.streamname);
															}}
														>
															<ClearStreamIcon fontSize="small" />
														</IconButton>
													</Tooltip>
													<Tooltip title="Replay stream">
														<IconButton
															disabled={!stream.persist}
															size="small"
															onClick={(event) => {
																event.stopPropagation();
																onReplayStream(stream);
															}}
														>
															<ReplayIcon fontSize="small" />
														</IconButton>
													</Tooltip>
													<Tooltip title="Delete stream">
														<IconButton
															size="small"
															onClick={(event) => {
																event.stopPropagation();
																onDeleteStream(stream.streamname);
															}}
														>
															<DeleteIcon fontSize="small" />
														</IconButton>
													</Tooltip>
												</TableCell>
											</StyledTableRow>
										))}
								</TableBody>
							</Table>
						</TableContainer>
					</Hidden>
					<Hidden smUp implementation="css">
						<Paper>
							<List className={classes.root}>
								{streams.map((stream) => (
									<React.Fragment>
										<ListItem
											alignItems="flex-start"
											onClick={(event) => onSelectStream(stream.streamname)}
										>
											<ListItemText
												primary={<span>{stream.streamname}</span>}
												secondary={
													<React.Fragment>
														<Typography
															component="span"
															variant="body2"
															className={classes.inline}
															color="textPrimary"
														>
															{stream.streamname}
														</Typography>
													</React.Fragment>
												}
											/>
											<ListItemSecondaryAction>
												<IconButton
													edge="end"
													size="small"
													onClick={(event) => {
														event.stopPropagation();
														onSelectStream(stream.streamname);
													}}
													aria-label="edit"
												>
													<EditIcon fontSize="small" />
												</IconButton>

												<IconButton
													edge="end"
													size="small"
													onClick={(event) => {
														event.stopPropagation();
														onDeleteStream(stream.streamname);
													}}
													aria-label="delete"
												>
													<DeleteIcon fontSize="small" />
												</IconButton>
											</ListItemSecondaryAction>
										</ListItem>
										<Divider />
									</React.Fragment>
								))}
							</List>
						</Paper>
					</Hidden>
				</div>
			) : (
				<div>No streams found</div>
			)}
		</div>
	);
}
Example #17
Source File: Status.js    From management-center with Apache License 2.0 4 votes vote down vote up
Status = ({ brokerLicense, brokerLicenseLoading, lastUpdated, systemStatus, defaultClient, currentConnection, currentConnectionName }) => {
	const classes = useStyles();
	const totalMessages = parseInt(systemStatus?.$SYS?.broker?.messages?.sent);
	const publishMessages = (parseInt(systemStatus?.$SYS?.broker?.publish?.messages?.sent) / totalMessages) * 100;
	const otherMessages =
		((totalMessages - parseInt(systemStatus?.$SYS?.broker?.publish?.messages?.sent)) / totalMessages) * 100;

	const data = {
		datasets: [
			{
				data: [publishMessages, otherMessages],
				backgroundColor: [colors.indigo[500], colors.red[600], colors.orange[600]],
				borderWidth: 8,
				borderColor: colors.common.white,
				hoverBorderColor: colors.common.white
			}
		],
		labels: ['PUBLISH', 'Other']
	};

	const dataDescriptions = [
		{
			title: 'PUBLISH',
			value: Math.round(publishMessages),
			icon: MessageIcon,
			color: colors.indigo[500]
		},
		{
			title: 'Other',
			value: Math.round(otherMessages),
			icon: MessageIcon,
			color: colors.red[600]
		}
	];

	return (
		<div>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<RouterLink className={classes.breadcrumbLink} to="/system">
					System
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Status
				</Typography>
			</Breadcrumbs>
			<br />
			{systemStatus?.$SYS ? <Container maxWidth={false}>
				<Grid container spacing={3}>
					<Grid item xs={12}>
						<Typography variant="h5" component="div" gutterBottom>
							{currentConnectionName}
						</Typography>
					</Grid>
					<Grid item lg={3} sm={6} xl={3} xs={12}>
						<Info
							label="Clients total"
							value={systemStatus?.$SYS?.broker?.clients?.total}
							icon={<ClientIcon />}
						/>
					</Grid>
					<Grid item lg={3} sm={6} xl={3} xs={12}>
						<Info
							label="Subscriptions"
							value={systemStatus?.$SYS?.broker?.subscriptions?.count}
							icon={<SubscriptionIcon />}
						/>
					</Grid>
					<Grid item lg={3} sm={6} xl={3} xs={12}>
						<Info
							label="PUBLISH received"
							value={systemStatus?.$SYS?.broker?.publish?.messages?.received}
							icon={<MessageIcon />}
						/>
					</Grid>
					<Grid item lg={3} sm={6} xl={3} xs={12}>
						<Info
							label="PUBLISH sent"
							value={systemStatus?.$SYS?.broker?.publish?.messages?.sent}
							icon={<MessageIcon />}
						/>
					</Grid>
					<Grid item lg={3} sm={6} xl={3} xs={12}>
						<Info
							label="Bytes received"
							value={systemStatus?.$SYS?.broker?.bytes?.received}
							icon={<DataReceivedIcon />}
						/>
					</Grid>
					<Grid item lg={3} sm={6} xl={3} xs={12}>
						<Info
							label="Bytes sent"
							value={systemStatus?.$SYS?.broker?.bytes?.sent}
							icon={<DataSentIcon />}
						/>
					</Grid>
					<Grid item lg={3} sm={6} xl={3} xs={12}>
						<Info
							label="Total received"
							value={systemStatus?.$SYS?.broker?.messages?.received}
							icon={<MessageIcon />}
						/>
					</Grid>
					<Grid item lg={3} sm={6} xl={3} xs={12}>
						<Info
							label="Total messages sent"
							value={systemStatus?.$SYS?.broker?.messages?.sent}
							icon={<MessageIcon />}
						/>
					</Grid>
					<Grid item lg={4} sm={4} xl={4} xs={12}>
						<TableContainer component={Paper}>
							<Table className={classes.table}>
								<TableHead>
									<TableCell colSpan={2}>Broker</TableCell>
								</TableHead>
								<TableBody>
									<TableRow key="version">
										<TableCell component="th" scope="row">
											Broker version
										</TableCell>
										<TableCell align="right">{systemStatus?.$SYS?.broker?.version}</TableCell>
									</TableRow>
									<TableRow key="uptime">
										<TableCell component="th" scope="row">
											Uptime
										</TableCell>
										<TableCell align="right">{systemStatus?.$SYS?.broker?.uptime}</TableCell>
									</TableRow>
									<TableRow key="url">
										<TableCell component="th" scope="row">
											URL
										</TableCell>
										<TableCell align="right">{currentConnection?.url}</TableCell>
									</TableRow>
								</TableBody>
							</Table>
						</TableContainer>
					</Grid>
					<Grid item lg={4} sm={4} xl={4} xs={12}>
						<TableContainer component={Paper}>
							<Table className={classes.table}>
								<TableHead>
									<TableCell colSpan={2}>Clients</TableCell>
								</TableHead>
								<TableBody>
									<TableRow key="clients-total">
										<TableCell component="th" scope="row">
											Total clients
										</TableCell>
										<TableCell align="right">
											{systemStatus?.$SYS?.broker?.clients?.total}
										</TableCell>
									</TableRow>
									<TableRow key="clients-active">
										<TableCell component="th" scope="row">
											Active clients
										</TableCell>
										<TableCell align="right">
											{systemStatus?.$SYS?.broker?.clients?.active}
										</TableCell>
									</TableRow>
									<TableRow key="clients-connected">
										<TableCell component="th" scope="row">
											Connected clients
										</TableCell>
										<TableCell align="right">
											{systemStatus?.$SYS?.broker?.clients?.connected}
										</TableCell>
									</TableRow>
								</TableBody>
							</Table>
						</TableContainer>
					</Grid>
					<Grid item lg={4} sm={4} xl={4} xs={12}>
						<TableContainer component={Paper}>
							<Table className={classes.table}>
								<TableHead>
									<TableCell colSpan={2}>Messages</TableCell>
								</TableHead>
								<TableBody>
									<TableRow key="messsages-received">
										<TableCell component="th" scope="row">
											Received messages
										</TableCell>
										<TableCell align="right">
											{systemStatus?.$SYS?.broker?.messages?.received}
										</TableCell>
									</TableRow>
									<TableRow key="messsages-sent">
										<TableCell component="th" scope="row">
											Sent messages
										</TableCell>
										<TableCell align="right">
											{systemStatus?.$SYS?.broker?.messages?.sent}
										</TableCell>
									</TableRow>
									<TableRow key="messsages-stored">
										<TableCell component="th" scope="row">
											Stored messages
										</TableCell>
										<TableCell align="right">
											{systemStatus?.$SYS?.broker?.messages?.stored}
										</TableCell>
									</TableRow>
								</TableBody>
							</Table>
						</TableContainer>
					</Grid>
					{/* <Grid item lg={3} sm={3} xl={3} xs={12}>
						<Chart
							title="Sent messages by type"
							data={data}
							dataDescriptions={dataDescriptions}
						/>
					</Grid> */}
					<Grid item lg={12} sm={12} xl={12} xs={12}>
						{brokerLicenseLoading ? 
						<Alert severity="info">
							<AlertTitle>Loading license information</AlertTitle>
							<CircularProgress color="secondary" />
						</Alert> : null}

						{brokerLicense ? <LicenseTable license={brokerLicense} /> : null}

					</Grid>
				</Grid>
			</Container> : <Alert severity="warning">
				<AlertTitle>System status information not accessible</AlertTitle>
				We couldn't retrieve the system status information.
				Please make sure that the user "{defaultClient?.username}" has the rights to read the "$SYS" topic on the selected broker.
			</Alert>
			}
			{systemStatus?.$SYS && <div style={{
				fontSize: '0.9em',
				position: 'absolute',
				right: '15px',
				top: '70px'
			}}>
				Dashboard last updated at: {moment(lastUpdated).format('hh:mm:ss a')}
			</div>}
		</div>
	);
}
Example #18
Source File: Roles.js    From management-center with Apache License 2.0 4 votes vote down vote up
Roles = (props) => {
	const classes = useStyles();
	const context = useContext(WebSocketContext);
	const dispatch = useDispatch();
	const history = useHistory();
	const confirm = useConfirm();
	const { enqueueSnackbar } = useSnackbar();
	const { client } = context;

	const [page, setPage] = React.useState(0);
	const [rowsPerPage, setRowsPerPage] = React.useState(10);

	const handleChangePage = async (event, newPage) => {
		setPage(newPage);
		const count = rowsPerPage;
		const offset = newPage * rowsPerPage;
		const roles = await client.listRoles(true, count, offset);
		dispatch(updateRoles(roles));
	};

	const handleChangeRowsPerPage = async (event) => {
		const rowsPerPage = parseInt(event.target.value, 10);
		setRowsPerPage(rowsPerPage);
		setPage(0);
		const roles = await client.listRoles(true, rowsPerPage, 0);
		dispatch(updateRoles(roles));
	};

	const onEditDefaultACLAccess = () => {
		history.push('/security/acl');
	}

	const onNewRole = () => {
		history.push('/security/roles/new');
	};

	const onDeleteRole = async (rolename) => {
		await confirm({
			title: 'Confirm role deletion',
			description: `Do you really want to delete the role "${rolename}"?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		if (rolename === 'admin') {
			await confirm({
				title: 'Confirm default role deletion',
				description: `Are you sure? You are about to delete the default role for the current Mosquitto instance.`,
				cancellationButtonProps: {
					variant: 'contained'
				},
				confirmationButtonProps: {
					color: 'primary',
					variant: 'contained'
				}
			});
		}
		await client.deleteRole(rolename);
		enqueueSnackbar('Role successfully deleted', {
			variant: 'success'
		});
		const roles = await client.listRoles(true, rowsPerPage, page * rowsPerPage);
		dispatch(updateRoles(roles));
		const clients = await client.listClients();
		dispatch(updateClients(clients));
		const groups = await client.listGroups();
		dispatch(updateGroups(groups));
	};

	const onSelectRole = async (rolename) => {
		const role = await client.getRole(rolename);
		dispatch(updateRole(role));
		history.push(`/security/roles/detail/${rolename}`);
	};

	const { dynamicsecurityFeature, defaultACLAccess, roles = [], onSort, sortBy, sortDirection } = props;

	return (
		<div>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<RouterLink className={classes.breadcrumbLink} to="/security">
					Security
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Roles
				</Typography>
			</Breadcrumbs>
			{/* TODO: Quick hack to detect whether feature is supported */}
			{dynamicsecurityFeature?.supported === false ? <><br/><Alert severity="warning">
				<AlertTitle>Feature not available</AlertTitle>
				Make sure that the broker connected has the dynamic security enabled.
			</Alert></> : null}
			<br />
			{dynamicsecurityFeature?.supported !== false && <><Button
				variant="outlined"
				color="default"
				size="small"
				className={classes.button}
				startIcon={<AddIcon />}
				onClick={(event) => {
					event.stopPropagation();
					onNewRole();
				}}
			>
				New Role
			</Button>
			<Button
				variant="outlined"
				color="default"
				size="small"
				className={classes.button}
				startIcon={<EditIcon />}
				onClick={onEditDefaultACLAccess}
			>
				Edit default ACL access
			</Button>
			<br />
			<br />
			</>}
			{dynamicsecurityFeature?.supported !== false && roles?.roles?.length > 0 ? (
				<div>
					<Hidden xsDown implementation="css">
						<TableContainer component={Paper}>
							<Table>
								<TableHead>
									<TableRow>
										{ROLE_TABLE_COLUMNS.map((column) => (
											<TableCell
												key={column.id}
												sortDirection={sortBy === column.id ? sortDirection : false}
											>
												<TableSortLabel
													active={sortBy === column.id}
													direction={sortDirection}
													onClick={() => onSort(column.id)}
												>
													{column.key}
												</TableSortLabel>
											</TableCell>
										))}
										<TableCell />
									</TableRow>
								</TableHead>
								<TableBody>
									{roles.roles.map((role) => (
										<TableRow
											hover
											key={role.rolename}
											onClick={() => onSelectRole(role.rolename)}
											style={{ cursor: 'pointer' }}
										>
											<TableCell>{role.rolename}</TableCell>
											<TableCell>{role.textname}</TableCell>
											<TableCell>{role.textdescription}</TableCell>
											<TableCell align="right">
												{/* <IconButton
						size="small"
                        onClick={(event) => {
                          event.stopPropagation();
                          onDeleteRole(role.rolename);
                        }}
                      >
                        <EditIcon fontSize="small" />
                      </IconButton> */}
												<Tooltip title="Delete role">
													<IconButton
														size="small"
														onClick={(event) => {
															event.stopPropagation();
															onDeleteRole(role.rolename);
														}}
													>
														<DeleteIcon fontSize="small" />
													</IconButton>
												</Tooltip>
											</TableCell>
										</TableRow>
									))}
								</TableBody>
								<TableFooter>
									<TableRow>
										<TablePagination
											rowsPerPageOptions={[5, 10, 25]}
											colSpan={8}
											count={roles?.totalCount}
											rowsPerPage={rowsPerPage}
											page={page}
											onChangePage={handleChangePage}
											onChangeRowsPerPage={handleChangeRowsPerPage}
										/>
									</TableRow>
								</TableFooter>
							</Table>
						</TableContainer>
					</Hidden>
					<Hidden smUp implementation="css">
						<Paper>
							<List className={classes.root}>
								{roles.roles.map((role) => (
									<React.Fragment>
										<ListItem alignItems="flex-start">
											<ListItemText
												primary={<span>{role.rolename}</span>}
												//   secondary={
												//     <React.Fragment>
												//       <Typography
												//         component="span"
												//         variant="body2"
												//         className={classes.inline}
												//         color="textPrimary"
												//       >
												//         Role details
												//       </Typography>
												//     </React.Fragment>
												//   }
											/>
											<ListItemSecondaryAction>
												<IconButton edge="end" size="small" aria-label="edit">
													<EditIcon fontSize="small" />
												</IconButton>
												<IconButton edge="end" size="small" aria-label="delete">
													<DeleteIcon fontSize="small" />
												</IconButton>
											</ListItemSecondaryAction>
										</ListItem>
										<Divider />
									</React.Fragment>
								))}
							</List>
						</Paper>
					</Hidden>
				</div>
			) : (
				<div>No roles found</div>
			)}
			{/* <Fab
				color="primary"
				aria-label="add"
				className={classes.fab}
				onClick={(event) => {
					event.stopPropagation();
					onNewRole();
				}}
			>
				<AddIcon />
			</Fab> */}
		</div>
	);
}
Example #19
Source File: LicenseTable.js    From management-center with Apache License 2.0 4 votes vote down vote up
LicenseTable = (props) => {
	const classes = useStyles();
	const { license } = props;

	return (
		<div>
			{
				!license && 
				<Alert severity="info">
					<AlertTitle>No license information available</AlertTitle>
					This broker does not provide any license information.
				</Alert>
			}
			{license && license.edition && (
				<TableContainer component={Paper} className={classes.tableContainer}>
					<Table size="medium">
						<TableBody>
							<TableRow>
								<TableCell>
									<b>Edition</b>
								</TableCell>
								<TableCell>{license.edition === 'pro' ? getPremium() : license.edition}</TableCell>
							</TableRow>
							<TableRow>
								<TableCell>
									<b>Issued by</b>
								</TableCell>
								<TableCell>{license.issuedBy}</TableCell>
							</TableRow>
							{isPremiumLicense(license) && (
								<TableRow>
									<TableCell>
										<b>Issued to</b>
									</TableCell>
									<TableCell>{license.issuedTo}</TableCell>
								</TableRow>
							)}
							{isPremiumLicense(license) && (
								<TableRow>
									<TableCell>
										<b>Valid since</b>
									</TableCell>
									<TableCell>{moment(license.validSince).format('LLLL')}</TableCell>
								</TableRow>
							)}
							{isPremiumLicense(license) && (
								<TableRow>
									<TableCell>
										<b>Valid until</b>
									</TableCell>
									<TableCell>{moment(license.validUntil).format('LLLL')}</TableCell>
								</TableRow>
							)}
							{/* <TableRow>
								<TableCell>
									<b>Max. broker connections</b>
								</TableCell>
								<TableCell>{license.maxBrokerConnections}</TableCell>
							</TableRow> */}
							{/* <TableRow>
								<TableCell>
									<b>Advanced REST API</b>
								</TableCell>
								<TableCell>{createFeatureIcon('rest-api', license)}</TableCell>
							</TableRow>
							<TableRow>
								<TableCell>
									<b>Custom Theme</b>
								</TableCell>
								<TableCell>{createFeatureIcon('white-labeling', license)}</TableCell>
							</TableRow> */}
							{/* <TableRow>
				<TableCell>
				<b>? Import / Export</b>
				</TableCell>
				<TableCell>
				{
					createFeatureIcon('import-export', license)
				}
				</TableCell>
			</TableRow> */}
							{/* <TableRow>
								<TableCell>
									<b>Multiple Connections</b>
								</TableCell>
								<TableCell>{createFeatureIcon('multiple-broker-connections', license)}</TableCell>
							</TableRow> */}
						</TableBody>
					</Table>
				</TableContainer>
			)}

			<br />
			{license?.features &&
				<TableContainer component={Paper} className={classes.tableContainer}>
					<Table size="medium">
						<TableHead>
							<TableRow>
								<TableCell>
									<b>Feature</b>
								</TableCell>
								<TableCell>
									<b>Version</b>
								</TableCell>
								{/* <TableCell>
									<b>Valid until</b>
								</TableCell>
								<TableCell>
									<b>Valid since</b>
								</TableCell> */}
							</TableRow>
							</TableHead>
						<TableBody>
							{license?.features.map(feature => (
							<TableRow>
								<TableCell>
									{feature.name}
								</TableCell>
								<TableCell>
									{feature.version}
								</TableCell>
								{/* <TableCell>
									{moment(feature.validSince).format('LLLL')}
								</TableCell>
								<TableCell>
									{moment(feature.validUntil).format('LLLL')}
								</TableCell> */}
							</TableRow>
						))}
						</TableBody>
					</Table>
				</TableContainer>
			}
		</div>
	);
}
Example #20
Source File: Groups.js    From management-center with Apache License 2.0 4 votes vote down vote up
Groups = (props) => {
	const classes = useStyles();
	const context = useContext(WebSocketContext);
	const dispatch = useDispatch();
	const history = useHistory();
	const confirm = useConfirm();
	const { enqueueSnackbar } = useSnackbar();
	const { client } = context;

	const [page, setPage] = React.useState(0);
	const [rowsPerPage, setRowsPerPage] = React.useState(10);

	const handleChangePage = async (event, newPage) => {
		setPage(newPage);
		const count = rowsPerPage;
		const offset = newPage * rowsPerPage;
		const groups = await client.listGroups(true, count, offset);
		dispatch(updateGroups(groups));
	};

	const handleChangeRowsPerPage = async (event) => {
		const rowsPerPage = parseInt(event.target.value, 10);
		setRowsPerPage(rowsPerPage);
		setPage(0);
		const groups = await client.listGroups(true, rowsPerPage, 0);
		dispatch(updateGroups(groups));
	};

	const onUpdateGroupClients = async (group, clients = []) => {
		if (!clients) {
			clients = [];
		}
		const clientNames = clients.map((client) => client.value);
		await client.updateGroupClients(group, clientNames);
		const groups = await client.listGroups(true, rowsPerPage, page * rowsPerPage);
		dispatch(updateGroups(groups));
		const clientsUpdated = await client.listClients();
		dispatch(updateClients(clientsUpdated));
	};

	const onUpdateGroupRoles = async (group, roles = []) => {
		if (!roles) {
			roles = [];
		}
		const rolenames = roles.map((role) => role.value);
		await client.updateGroupRoles(group, rolenames);
		const groups = await client.listGroups(true, rowsPerPage, page * rowsPerPage);
		dispatch(updateGroups(groups));
	};

	const onUpdateAnonymousGroup = async (groupname) => {
		await client.setAnonymousGroup(groupname);
		const group = await client.getAnonymousGroup();
		dispatch(updateAnonymousGroup(group));
		enqueueSnackbar('Anonymous group successfully set', {
			variant: 'success'
		});
	}

	const onSelectGroup = async (groupname) => {
		const group = await client.getGroup(groupname);
		dispatch(updateGroup(group));
		history.push(`/security/groups/detail/${groupname}`);
	};

	const onNewGroup = () => {
		history.push('/security/groups/new');
	};

	const onDeleteGroup = async (groupname) => {
		await confirm({
			title: 'Confirm group deletion',
			description: `Do you really want to delete the group "${groupname}"?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		await client.deleteGroup(groupname);
		enqueueSnackbar(`Group "${groupname}" successfully deleted`, {
			variant: 'success'
		});
		const groups = await client.listGroups(true, rowsPerPage, page * rowsPerPage);
		dispatch(updateGroups(groups));
		const clients = await client.listClients();
		dispatch(updateClients(clients));
	};

	const onRemoveClientFromGroup = async (username, group) => {
		await client.removeGroupClient(username, group);
		const groups = await client.listGroups(true, rowsPerPage, page * rowsPerPage);
		dispatch(updateGroups(groups));
	};

	const { dynamicsecurityFeature, anonymousGroup, groups = [], rolesAll = [], clientsAll = [], onSort, sortBy, sortDirection } = props;

	// TODO: probably extract into reducer
	const clientSuggestions = clientsAll
		.sort()
		.map((username) => ({
			label: username,
			value: username
		}));

	const roleSuggestions = rolesAll
		.sort()
		.map((rolename) => ({
			label: rolename,
			value: rolename
		}));

	return (
		<div>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<RouterLink className={classes.breadcrumbLink} to="/security">
					Security
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Groups
				</Typography>
			</Breadcrumbs>
			{/* TODO: Quick hack to detect whether feature is supported */}
			{dynamicsecurityFeature?.supported === false ? <><br/><Alert severity="warning">
				<AlertTitle>Feature not available</AlertTitle>
				Make sure that the broker connected has the dynamic security enabled.
			</Alert></> : null}
			<br />
			{dynamicsecurityFeature?.supported !== false && <><Button
				variant="outlined"
				color="default"
				size="small"
				className={classes.button}
				startIcon={<AddIcon />}
				onClick={(event) => {
					event.stopPropagation();
					onNewGroup();
				}}
			>
				New Group
			</Button>
			<br />
			<br />
			</>}
			{dynamicsecurityFeature?.supported !== false && groups?.groups?.length > 0 ? (
				<div>
					<Hidden xsDown implementation="css">
						<TableContainer component={Paper} className={classes.tableContainer}>
							<Table>
								<TableHead>
									<TableRow>
										{GROUP_TABLE_COLUMNS.map((column) => (
											<TableCell
												key={column.id}
												sortDirection={sortBy === column.id ? sortDirection : false}
											>
												<TableSortLabel
													active={sortBy === column.id}
													direction={sortDirection}
													onClick={() => onSort(column.id)}
												>
													{column.key}
												</TableSortLabel>
											</TableCell>
										))}
										<TableCell />
									</TableRow>
								</TableHead>
								<TableBody>
									{groups &&
										groups.groups.map((group) => (
											<TableRow
												hover
												key={group.groupname}
												onClick={(event) => {
													if (event.target.nodeName?.toLowerCase() === 'td') {
														onSelectGroup(group.groupname);
													}
												}}
												style={{ cursor: 'pointer' }}
											>
												<TableCell>{group.groupname}</TableCell>
												<TableCell>{group.textname}</TableCell>
												<TableCell>{group.textdescription}</TableCell>
												<TableCell className={classes.badges}>
													<AutoSuggest
														suggestions={clientSuggestions}
														values={group.clients.map((client) => ({
															label: client.username,
															value: client.username
														}))}
														handleChange={(value) => {
															onUpdateGroupClients(group, value);
														}}
													/>
												</TableCell>
												<TableCell className={classes.badges}>
													<AutoSuggest
														suggestions={roleSuggestions}
														values={group.roles.map((role) => ({
															label: role.rolename,
															value: role.rolename
														}))}
														handleChange={(value) => {
															onUpdateGroupRoles(group, value);
														}}
													/>
												</TableCell>
												<TableCell align="right">
													{/* <IconButton
						  size="small"
                          onClick={(event) => {
                            event.stopPropagation();
                            onSelectGroup(group.groupname);
                          }}
                        >
                          <EditIcon fontSize="small" />
                        </IconButton> */}

													<Tooltip title="Delete group">
														<IconButton
															size="small"
															onClick={(event) => {
																event.stopPropagation();
																onDeleteGroup(group.groupname);
															}}
														>
															<DeleteIcon fontSize="small" />
														</IconButton>
													</Tooltip>
												</TableCell>
											</TableRow>
										))}
								</TableBody>
								<TableFooter>
									<TableRow>
										<TablePagination
											rowsPerPageOptions={[5, 10, 25]}
											colSpan={8}
											count={groups?.totalCount}
											rowsPerPage={rowsPerPage}
											page={page}
											onChangePage={handleChangePage}
											onChangeRowsPerPage={handleChangeRowsPerPage}
										/>
									</TableRow>
								</TableFooter>
							</Table>
						</TableContainer>
					</Hidden>
					<Hidden smUp implementation="css">
						<Paper>
							<List className={classes.root}>
								{groups.groups.map((group) => (
									<React.Fragment>
										<ListItem
											alignItems="flex-start"
											onClick={(event) => onSelectGroup(group.groupname)}
										>
											<ListItemText
												primary={<span>{group.groupname}</span>}
												secondary={
													<React.Fragment>
														<Typography
															component="span"
															variant="body2"
															className={classes.inline}
															color="textPrimary"
														>
															{group.textname}
														</Typography>
														<span> — {group.textdescription} </span>
													</React.Fragment>
												}
											/>
											<ListItemSecondaryAction>
												<IconButton
													edge="end"
													size="small"
													onClick={(event) => {
														event.stopPropagation();
														onSelectGroup(group.groupname);
													}}
													aria-label="edit"
												>
													<EditIcon fontSize="small" />
												</IconButton>

												<IconButton
													edge="end"
													size="small"
													onClick={(event) => {
														event.stopPropagation();
														onDeleteGroup(group.groupname);
													}}
													aria-label="delete"
												>
													<DeleteIcon fontSize="small" />
												</IconButton>
											</ListItemSecondaryAction>
										</ListItem>
										<Divider />
									</React.Fragment>
								))}
							</List>
						</Paper>
					</Hidden>
					<AnonymousGroupSelect
						onUpdateAnonymousGroup={onUpdateAnonymousGroup}
					/>
				</div>
			) : (
				<div>No groups found</div>
			)}
			{/* <Fab
				color="primary"
				aria-label="add"
				className={classes.fab}
				onClick={(event) => {
					event.stopPropagation();
					onNewGroup();
				}}
			>
				<AddIcon />
			</Fab> */}
		</div>
	);
}
Example #21
Source File: Clients.js    From management-center with Apache License 2.0 4 votes vote down vote up
Clients = (props) => {
	const classes = useStyles();
	const context = useContext(WebSocketContext);
	const dispatch = useDispatch();
	const history = useHistory();
	const confirm = useConfirm();
	const { enqueueSnackbar } = useSnackbar();
	const { client: brokerClient } = context;

	const [page, setPage] = React.useState(0);
	const [rowsPerPage, setRowsPerPage] = React.useState(10);

	const handleChangePage = async (event, newPage) => {
		setPage(newPage);
		const count = rowsPerPage;
		const offset = newPage * count;
		const clients = await brokerClient.listClients(true, count, offset);
		dispatch(updateClients(clients));
	};

	const handleChangeRowsPerPage = async (event) => {
		const rowsPerPage = parseInt(event.target.value, 10);
		setRowsPerPage(rowsPerPage);
		setPage(0);
		const clients = await brokerClient.listClients(true, rowsPerPage, 0);
		dispatch(updateClients(clients));
	};

	const onUpdateClientGroups = async (client, groups = []) => {
		if (!groups) {
			groups = [];
		}
		if (groups.length === 0) {
			await confirm({
				title: 'Confirm remove client from all groups',
				description: `Do you really want to remove client "${client.username}" from all groups?`,
				cancellationButtonProps: {
					variant: 'contained'
				},
				confirmationButtonProps: {
					color: 'primary',
					variant: 'contained'
				}
			});
		}
		const groupnames = groups.map((group) => group.value);
		await brokerClient.updateClientGroups(client, groupnames);
		const clients = await brokerClient.listClients(true, rowsPerPage, page * rowsPerPage);
		dispatch(updateClients(clients));
		const groupsUpdated = await brokerClient.listGroups();
		dispatch(updateGroups(groupsUpdated));
	};

	const onUpdateClientRoles = async (client, roles = []) => {
		if (!roles) {
			roles = [];
		}
		const rolenames = roles.map((role) => role.value);
		await brokerClient.updateClientRoles(client, rolenames);
		const clients = await brokerClient.listClients(true, rowsPerPage, page * rowsPerPage);
		dispatch(updateClients(clients));
	};

	const onSelectClient = async (username) => {
		const client = await brokerClient.getClient(username);
		dispatch(updateClient(client));
		history.push(`/security/clients/detail/${username}`);
	};

	const onNewClient = () => {
		history.push('/security/clients/new');
	};

	const onEditClient = async (username) => {
		const client = await brokerClient.getClient(username);
		dispatch(updateClient(client));
		history.push(`/security/clients/detail/${username}/?action=edit`);
	};

	const onDeleteClient = async (username) => {
		await confirm({
			title: 'Confirm client deletion',
			description: `Do you really want to delete client "${username}"?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		if (username === 'cedalo') {
			await confirm({
				title: 'Confirm default client deletion',
				description: `Are you sure? You are about to delete the default client for the current Mosquitto instance.`,
				cancellationButtonProps: {
					variant: 'contained'
				},
				confirmationButtonProps: {
					color: 'primary',
					variant: 'contained'
				}
			});
		}
		await brokerClient.deleteClient(username);
		enqueueSnackbar(`Client "${username}" successfully deleted`, {
			variant: 'success'
		});
		const clients = await brokerClient.listClients(true, rowsPerPage, page * rowsPerPage);
		dispatch(updateClients(clients));
		const groups = await brokerClient.listGroups();
		dispatch(updateGroups(groups));
	};

	const onDisableClient = async (username) => {
		await confirm({
			title: 'Confirm client disable',
			description: `Do you really want to disable client "${username}"?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		await brokerClient.disableClient(username);
		const clients = await brokerClient.listClients(true, rowsPerPage, page * rowsPerPage);
		enqueueSnackbar('Client successfully disabled', {
			variant: 'success'
		});
		dispatch(updateClients(clients));
	};

	const onEnableClient = async (username) => {
		await brokerClient.enableClient(username);
		const clients = await brokerClient.listClients(true, rowsPerPage, page * rowsPerPage);
		enqueueSnackbar('Client successfully enabled', {
			variant: 'success'
		});
		dispatch(updateClients(clients));
	};

	const onRemoveClientFromGroup = async (client, group) => {
		await confirm({
			title: 'Remove client from group',
			description: `Do you really want to remove client "${client.username}" from group "${group}"?`
		});
		await client.removeGroupClient(client, group);
		const clients = await brokerClient.listClients(true, rowsPerPage, page * rowsPerPage);
		dispatch(updateClients(clients));
	};

	const { dynamicsecurityFeature, connectionID, defaultClient, groupsAll = [], rolesAll = [], clients = [], onSort, sortBy, sortDirection } = props;

	const groupSuggestions = groupsAll
		.sort()
		.map((groupname) => ({
			label: groupname,
			value: groupname
		}));

	const roleSuggestions = rolesAll
		.sort()
		.map((rolename) => ({
			label: rolename,
			value: rolename
		}));

	return (
		<div>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<RouterLink className={classes.breadcrumbLink} color="inherit" to="/security">
					Security
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Clients
				</Typography>
			</Breadcrumbs>
			{/* TODO: Quick hack to detect whether feature is supported */}
			{dynamicsecurityFeature?.supported === false ? <><br/><Alert severity="warning">
				<AlertTitle>Feature not available</AlertTitle>
				Make sure that the broker connected has the dynamic security enabled.
			</Alert></> : null}
			<br />
			{dynamicsecurityFeature?.supported !== false && <><Button
				variant="outlined"
				color="default"
				size="small"
				className={classes.button}
				startIcon={<AddIcon />}
				onClick={(event) => {
					event.stopPropagation();
					onNewClient();
				}}
			>
				New Client
			</Button>
			<br />
			<br />
			</>}
			
			{dynamicsecurityFeature?.supported !== false && clients?.clients?.length > 0 ? (
				<div>
					<Hidden xsDown implementation="css">
						<TableContainer component={Paper} className={classes.tableContainer}>
							<Table size="medium">
								<TableHead>
									<TableRow>
										{USER_TABLE_COLUMNS.map((column) => (
											<TableCell
												key={column.id}
												sortDirection={sortBy === column.id ? sortDirection : false}
											>
												{/* <TableSortLabel
                      active={sortBy === column.id}
                      direction={sortDirection}
                      onClick={() => onSort(column.id)}
                    > */}
												{column.key}
												{/* </TableSortLabel> */}
											</TableCell>
										))}
										<TableCell />
									</TableRow>
								</TableHead>
								<TableBody>
									{clients &&
										clients.clients.map((client) => (
											<StyledTableRow
												hover
												key={client.username}
												onClick={(event) => {
													if (
														event.target.nodeName?.toLowerCase() === 'td' ||
														defaultClient?.username === client.username
													) {
														onSelectClient(client.username);
													}
												}}
												style={{ cursor: 'pointer' }}
											>
												<TableCell>{client.username}</TableCell>
												<TableCell>{client.clientid}</TableCell>
												<TableCell>{client.textname}</TableCell>
												<TableCell>{client.textdescription}</TableCell>
												<TableCell className={classes.badges}>
													<AutoSuggest
														disabled={defaultClient?.username === client.username}
														suggestions={groupSuggestions}
														values={client.groups.map((group) => ({
															label: group.groupname,
															value: group.groupname
														}))}
														handleChange={(value) => {
															onUpdateClientGroups(client, value);
														}}
													/>
												</TableCell>
												<TableCell className={classes.badges}>
													<AutoSuggest
														disabled={defaultClient?.username === client.username}
														suggestions={roleSuggestions}
														values={client.roles.map((role) => ({
															label: role.rolename,
															value: role.rolename
														}))}
														handleChange={(value) => {
															onUpdateClientRoles(client, value);
														}}
													/>
												</TableCell>
												<TableCell align="right">
													{/* <IconButton
						  size="small"
                          onClick={(event) => {
                            event.stopPropagation();
                            onEditClient(client.username);
                          }}
                        >
                          <EditIcon fontSize="small" />
                        </IconButton> */}

													<Tooltip title="Delete client">
														<IconButton
															disabled={defaultClient?.username === client.username}
															size="small"
															onClick={(event) => {
																event.stopPropagation();
																onDeleteClient(client.username);
															}}
														>
															<DeleteIcon fontSize="small" />
														</IconButton>
													</Tooltip>
													<Tooltip title="Enable / disable client">
														<Switch
															disabled={defaultClient?.username === client.username}
															checked={
																typeof client.disabled === 'undefined' ||
																client.disabled === false
															}
															onClick={(event) => {
																event.stopPropagation();
																if (event.target.checked) {
																	onEnableClient(client.username);
																} else {
																	onDisableClient(client.username);
																}
															}}
														/>
													</Tooltip>
												</TableCell>
											</StyledTableRow>
										))}
								</TableBody>
								<TableFooter>
									<TableRow>
										<TablePagination
											rowsPerPageOptions={[5, 10, 25]}
											colSpan={8}
											count={clients?.totalCount}
											rowsPerPage={rowsPerPage}
											page={page}
											onChangePage={handleChangePage}
											onChangeRowsPerPage={handleChangeRowsPerPage}
										/>
									</TableRow>
								</TableFooter>
							</Table>
						</TableContainer>
					</Hidden>
					<Hidden smUp implementation="css">
						<Paper>
							<List className={classes.root}>
								{clients.clients.map((client) => (
									<React.Fragment>
										<ListItem
											alignItems="flex-start"
											onClick={(event) => onSelectClient(client.username)}
										>
											<ListItemText
												primary={<span>{client.username}</span>}
												secondary={
													<React.Fragment>
														<Typography
															component="span"
															variant="body2"
															className={classes.inline}
															color="textPrimary"
														>
															{client.textname}
														</Typography>
														<span> — {client.textdescription} </span>

														{/* <div className={classes.badges}>
                        {client.groups.map((group) => (
                          <Chip
                            // icon={<FaceIcon />}
                            size="small"
                            label={group}
                            onDelete={(event) => {
                              event.stopPropagation();
                              onRemoveClientFromGroup(client, group);
                            }}
                            color="secondary"
                          />
                        ))}
                      </div> */}
													</React.Fragment>
												}
											/>
											<ListItemSecondaryAction>
												<IconButton
													edge="end"
													size="small"
													onClick={(event) => {
														event.stopPropagation();
														onSelectClient(client.username);
													}}
													aria-label="edit"
												>
													<EditIcon fontSize="small" />
												</IconButton>

												<IconButton
													edge="end"
													size="small"
													onClick={(event) => {
														event.stopPropagation();
														onDeleteClient(client.username);
													}}
													aria-label="delete"
												>
													<DeleteIcon fontSize="small" />
												</IconButton>
											</ListItemSecondaryAction>
										</ListItem>
										<Divider />
									</React.Fragment>
								))}
							</List>
						</Paper>
					</Hidden>
				</div>
			) : (
				<div>No clients found</div>
			)}
			{/* <Fab
				color="primary"
				aria-label="add"
				className={classes.fab}
				onClick={(event) => {
					event.stopPropagation();
					onNewClient();
				}}
			>
				<AddIcon />
			</Fab> */}
		</div>
	);
}
Example #22
Source File: Users.js    From management-center with Apache License 2.0 4 votes vote down vote up
Users = (props) => {
	const classes = useStyles();
	const context = useContext(WebSocketContext);
	const dispatch = useDispatch();
	const history = useHistory();
	const confirm = useConfirm();
	const { enqueueSnackbar } = useSnackbar();
	const { client: brokerClient } = context;

	const { userManagementFeature, userProfile, roles = [], users = [], onSort, sortBy, sortDirection } = props;

	const onUpdateUserRoles = async (user, roles = []) => {
		if (!roles) {
			roles = [];
		}
		const rolenames = roles.map((role) => role.value);
		await brokerClient.updateUserRoles(user, rolenames);
		const users = await brokerClient.listUsers();
		dispatch(updateUsers(users));
	};

	const onSelectUser = async (username) => {
		const user = await brokerClient.getUser(username);
		dispatch(updateUser(user));
		history.push(`/admin/users/detail/${username}`);
	};

	const onNewUser = () => {
		history.push('/admin/users/new');
	};

	const onDeleteUser = async (username) => {
		await confirm({
			title: 'Confirm user deletion',
			description: `Do you really want to delete user "${username}"?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		if (username === userProfile.username) {
			await confirm({
				title: 'Confirm user deletion',
				description: `Are you sure? You are about to delete the user that you are currently using. If you proceed you will be logged out and cannot access the system any longer with that user.`,
				cancellationButtonProps: {
					variant: 'contained'
				},
				confirmationButtonProps: {
					color: 'primary',
					variant: 'contained'
				}
			});
			window.location.href = '/logout';
		}
		await brokerClient.deleteUser(username);
		enqueueSnackbar(`User "${username}" successfully deleted`, {
			variant: 'success'
		});
		const users = await brokerClient.listUsers();
		dispatch(updateUsers(users));
	};

	return (
		<div>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<RouterLink className={classes.breadcrumbLink} color="inherit" to="/admin">
					Admin
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Users
				</Typography>
			</Breadcrumbs>
			{/* TODO: Quick hack to detect whether feature is supported */}
			{userManagementFeature?.error ? <><br/><Alert severity="warning">
				<AlertTitle>{userManagementFeature.error.title}</AlertTitle>
				{userManagementFeature.error.message}
			</Alert></> : null}
			{!userManagementFeature?.error && userManagementFeature?.supported === false ? <><br/><Alert severity="warning">
				<AlertTitle>Feature not available</AlertTitle>
				Make sure that this feature is included in your MMC license.
			</Alert></> : null}
			<br />
			{!userManagementFeature?.error && userManagementFeature?.supported !== false && <><Button
				variant="outlined"
				color="default"
				size="small"
				className={classes.button}
				startIcon={<AddIcon />}
				onClick={(event) => {
					event.stopPropagation();
					onNewUser();
				}}
			>
				New User
			</Button>
			<br />
			<br />
			</>}
			
			{ createUserTable(users, classes, props, onDeleteUser, onUpdateUserRoles, onSelectUser) }
		</div>
	);
}
Example #23
Source File: UserNew.js    From management-center with Apache License 2.0 4 votes vote down vote up
UserNew = (props) => {
	const { users, userRoles = [], userManagementFeature } = props;
	const classes = useStyles();

	const [username, setUsername] = useState('');
	const [password, setPassword] = useState('');
	const [passwordConfirm, setPasswordConfirm] = useState('');
	const [roles, setRoles] = useState([]);

	const roleSuggestions = userRoles
		.sort()
		.map((rolename) => ({
			label: rolename,
			value: rolename
		}));

	const usernameExists = props?.users?.find((searchUser) => {
		return searchUser.username === username;
	});

	const passwordsMatch = password === passwordConfirm;

	const validate = () => {
		const valid = passwordsMatch 
			&& !usernameExists 
			&& username !== '' 
			&& password !== ''
			&& username.match(/^[0-9a-zA-Z]+$/);
		return valid;
	};

	const { enqueueSnackbar } = useSnackbar();
	const context = useContext(WebSocketContext);
	const dispatch = useDispatch();
	const history = useHistory();
	const confirm = useConfirm();
	const { client } = context;

	const onSaveUser = async () => {
		try {
			await client.createUser(username, password, roles);
			const users = await client.listUsers();
			dispatch(updateUsers(users));
			history.push(`/admin/users`);
			enqueueSnackbar(`User "${username}" successfully created.`, {
				variant: 'success'
			});
		} catch (error) {
			enqueueSnackbar(`Error creating user "${username}". Reason: ${error.message || error}`, {
				variant: 'error'
			});
			throw error;
		}
	};

	const onCancel = async () => {
		await confirm({
			title: 'Cancel user creation',
			description: `Do you really want to cancel creating this user?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		history.goBack();
	};

	return (
		<div>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<RouterLink className={classes.breadcrumbLink} to="/security">
					Admin
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Users
				</Typography>
			</Breadcrumbs>
			<br />
			{/* TODO: Quick hack to detect whether feature is supported */}
			{userManagementFeature?.error ? <><br /><Alert severity="warning">
				<AlertTitle>{userManagementFeature.error.title}</AlertTitle>
				{userManagementFeature.error.message}
			</Alert></> : null}
			{!userManagementFeature?.error && <div className={classes.root}>
				<Paper>
					<form className={classes.form} noValidate autoComplete="off">
						<div className={classes.margin}>
							<Grid container spacing={1} alignItems="flex-end">
								<Grid item xs={12}>
									<TextField
										error={usernameExists}
										helperText={usernameExists && 'A user with this username already exists.'}
										required
										id="username"
										label="User name"
										onChange={(event) => setUsername(event.target.value)}
										defaultValue=""
										variant="outlined"
										fullWidth
										className={classes.textField}
										InputProps={{
											startAdornment: (
												<InputAdornment position="start">
													<AccountCircle />
												</InputAdornment>
											)
										}}
									/>
								</Grid>
								<Grid item xs={12}>
									<TextField
										required
										id="password"
										label="Password"
										error={!passwordsMatch}
										helperText={!passwordsMatch && 'Passwords must match.'}
										onChange={(event) => setPassword(event.target.value)}
										defaultValue=""
										variant="outlined"
										fullWidth
										type="password"
										className={classes.textField}
										InputProps={{
											startAdornment: (
												<InputAdornment position="start">
													<PasswordIcon />
												</InputAdornment>
											)
										}}
									/>
								</Grid>
								<Grid item xs={12}>
									<TextField
										required
										id="password-confirm"
										label="Password Confirm"
										error={!passwordsMatch}
										helperText={!passwordsMatch && 'Passwords must match.'}
										onChange={(event) => setPasswordConfirm(event.target.value)}
										defaultValue=""
										variant="outlined"
										fullWidth
										type="password"
										className={classes.textField}
										InputProps={{
											startAdornment: (
												<InputAdornment position="start">
													<PasswordIcon />
												</InputAdornment>
											)
										}}
									/>
								</Grid>
								<Grid item xs={12}>
									<AutoSuggest
										placeholder="Select roles..."
										suggestions={roleSuggestions}
										values={roles?.map((role) => ({
											label: role,
											value: role
										}))}
										handleChange={(value) => {
											setRoles(value.map((role) => role.value));
										}}
									/>
								</Grid>
								<Grid container xs={12} alignItems="flex-start">
									<Grid item xs={12} className={classes.buttons}>
										<SaveCancelButtons
											onSave={onSaveUser}
											saveDisabled={!validate()}
											onCancel={onCancel}
										/>
									</Grid>
								</Grid>
							</Grid>
						</div>
					</form>
				</Paper>
			</div>}
		</div>
	);
}
Example #24
Source File: Clusters.js    From management-center with Apache License 2.0 4 votes vote down vote up
Clusters = (props) => {
	const classes = useStyles();
	const context = useContext(WebSocketContext);
	const dispatch = useDispatch();
	const history = useHistory();
	const confirm = useConfirm();
	const { enqueueSnackbar } = useSnackbar();
	const { client: brokerClient } = context;
	const [progressDialogOpen, setProgressDialogOpen] = React.useState(false);
	const { clusterManagementFeature, clusters = [], onSort, sortBy, sortDirection } = props;

	const onSelectCluster = async (clustername, numberOfNodes) => {
		setProgressDialogOpen(true);
		try {
			const cluster = await brokerClient.getCluster(clustername, numberOfNodes);
			dispatch(updateCluster(cluster));
			setProgressDialogOpen(false);
			history.push(`/admin/clusters/detail/${clustername}`);
		} catch(error) {
			enqueueSnackbar(`Cluster loading failed. Reason: ${error.message || error}`, {
				variant: 'error'
			});
		}
	};

	const onNewCluster = () => {
		history.push('/admin/clusters/new');
	};

	const onCheckHealthStatus = async (clustername) => {
		try {
			const healtStatus = await brokerClient.checkClusterHealthStatus(clustername);
			if (healtStatus.error) {
				const error = healtStatus.error;
				enqueueSnackbar(`Cluster health check failed. Reason: ${error.message || error}`, {
					variant: 'error'
				});
			} else {
				enqueueSnackbar(`Cluster "${clustername}" healthy`, {
					variant: 'success'
				});
			}
		} catch (error) {
			enqueueSnackbar(`Cluster health check failed. Reason: ${error.message || error}`, {
				variant: 'error'
			});
		}
	};

	const onDeleteCluster = async (clustername) => {
		await confirm({
			title: 'Confirm cluster deletion',
			description: `Do you really want to delete cluster "${clustername}"?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		await brokerClient.deleteCluster(clustername);
		enqueueSnackbar(`Cluster "${clustername}" successfully deleted`, {
			variant: 'success'
		});
		const clusters = await brokerClient.listClusters();
		dispatch(updateClusters(clusters));
	};

	return (
		<div>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<RouterLink className={classes.breadcrumbLink} color="inherit" to="/admin">
					Admin
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Clusters
				</Typography>
			</Breadcrumbs>
			{/* TODO: Quick hack to detect whether feature is supported */}
			{clusterManagementFeature?.error ? <><br/><Alert severity="warning">
				<AlertTitle>{clusterManagementFeature.error.title}</AlertTitle>
				{clusterManagementFeature.error.message}
			</Alert></> : null}
			{!clusterManagementFeature?.error && clusterManagementFeature?.supported === false ? <><br/><Alert severity="warning">
				<AlertTitle>Feature not available</AlertTitle>
				Make sure that this feature is included in your MMC license.
			</Alert></> : null}
			<br />
			{!clusterManagementFeature?.error && clusterManagementFeature?.supported !== false && <><Button
				variant="outlined"
				color="default"
				size="small"
				className={classes.button}
				startIcon={<AddIcon />}
				onClick={(event) => {
					event.stopPropagation();
					onNewCluster();
				}}
			>
				New Cluster
			</Button>
			<br />
			<br />
			<WaitDialog
				title='Loading cluster details'
				message='Note that this can take a while depending on the size and status of your cluster.'
				open={progressDialogOpen}
				handleClose={() => setProgressDialogOpen(false)}
			/>
			</>}
			
			{ createClusterTable(clusters, classes, props, onCheckHealthStatus, onDeleteCluster, onSelectCluster) }
		</div>
	);
}
Example #25
Source File: ClusterNew.js    From management-center with Apache License 2.0 4 votes vote down vote up
ClusterNew = (props) => {
	const { clusters, clusterManagementFeature } = props;
	const classes = useStyles();

	const [clustername, setClustername] = useState('Example');
	const [clusterDescription, setClusterDescription] = useState('Example cluster');
	const [node1, setNode1] = useState({
		port: 7000
	});
	const [node2, setNode2] = useState({
		port: 7000
	});
	const [node3, setNode3] = useState({
		port: 7000
	});

	const clusternameExists = props?.clusters?.find((searchCluster) => {
		return searchCluster.clustername === clustername;
	});

	const nodes = [
		node1, node2, node3
	];

	const validate = () => {
		const valid = !clusternameExists && clustername !== '';
		return valid;
	};

	const { enqueueSnackbar } = useSnackbar();
	const context = useContext(WebSocketContext);
	const dispatch = useDispatch();
	const history = useHistory();
	const confirm = useConfirm();
	const { client } = context;

	const onSaveCluster = async () => {
		try {
			await client.createCluster({
				clustername, 
				description: clusterDescription,
				nodes
			});
			const clusters = await client.listClusters();
			dispatch(updateClusters(clusters));
			history.push(`/admin/clusters`);
			enqueueSnackbar(`Cluster "${clustername}" successfully created.`, {
				variant: 'success'
			});
		} catch (error) {
			enqueueSnackbar(`Error creating cluster "${clustername}". Reason: ${error.message || error}`, {
				variant: 'error'
			});
			throw error;
		}
	};

	const onCancel = async () => {
		await confirm({
			title: 'Cancel cluster creation',
			description: `Do you really want to cancel creating this cluster?`,
			cancellationButtonProps: {
				variant: 'contained'
			},
			confirmationButtonProps: {
				color: 'primary',
				variant: 'contained'
			}
		});
		history.goBack();
	};

	return (
		<div>
			<Breadcrumbs aria-label="breadcrumb">
				<RouterLink className={classes.breadcrumbLink} to="/home">
					Home
				</RouterLink>
				<RouterLink className={classes.breadcrumbLink} to="/security">
					Admin
				</RouterLink>
				<Typography className={classes.breadcrumbItem} color="textPrimary">
					Clusters
				</Typography>
			</Breadcrumbs>
			<br />
			{/* TODO: Quick hack to detect whether feature is supported */}
			{clusterManagementFeature?.error ? <><br /><Alert severity="warning">
				<AlertTitle>{clusterManagementFeature.error.title}</AlertTitle>
				{clusterManagementFeature.error.message}
			</Alert></> : null}
			{!clusterManagementFeature?.error && <div className={classes.root}>
				<Paper>
					<form className={classes.form} noValidate autoComplete="off">
						<div className={classes.margin}>
							<Grid container spacing={1} alignItems="flex-end">
								<Grid item xs={12} sm={4}>
									<TextField
										error={clusternameExists}
										helperText={clusternameExists && 'A cluster with this clustername already exists.'}
										required
										id="clustername"
										label="Cluster name"
										onChange={(event) => setClustername(event.target.value)}
										defaultValue="Example"
										variant="outlined"
										fullWidth
										className={classes.textField}
										InputProps={{
											startAdornment: (
												<InputAdornment position="start">
													<ClusterIcon />
												</InputAdornment>
											)
										}}
									/>
								</Grid>
								<Grid item xs={12} sm={8}>
									<TextField
										required={false}
										id="description"
										label="Cluster description"
										onChange={(event) => setClusterDescription(event.target.value)}
										defaultValue="Example cluster"
										variant="outlined"
										fullWidth
										className={classes.textField}
									/>
								</Grid>
								<Grid item xs={12}>
									<Card variant="outlined">
										<CardHeader
											subheader="Node 1"
										/>
										<CardContent>
											<SelectNodeComponent
												defaultNode={node1}
											/>
										</CardContent>
									</Card>
									<br />
									<Card variant="outlined">
										<CardHeader
											subheader="Node 2"
										/>
										<CardContent>
											<SelectNodeComponent
												defaultNode={node2}
											/>
										</CardContent>
									</Card>
									<br />
									<Card variant="outlined">
										<CardHeader
											subheader="Node 3"
										/>
										<CardContent>
											<SelectNodeComponent
												defaultNode={node3}
											/>
										</CardContent>
									</Card>
								</Grid>
								<Grid container xs={12} alignItems="flex-start">
									<Grid item xs={12} className={classes.buttons}>
										<SaveCancelButtons
											onSave={onSaveCluster}
											saveDisabled={!validate()}
											onCancel={onCancel}
										/>
									</Grid>
								</Grid>
							</Grid>
						</div>
					</form>
				</Paper>
			</div>}
		</div>
	);
}
Example #26
Source File: MarkdownEdit.jsx    From markItDown with MIT License 4 votes vote down vote up
function MarkdownEdit({ content, changeContent }) {
  const [open, setOpen] = useState(false);
  const editorRef = useRef(null);

  useEffect(() => {
    if (content === "") {
      localStorage.setItem("markdown", placeholder);
    } else {
      localStorage.setItem("markdown", content);
    }
  }, [content]);

  const handleEditorChange = (event) => {
    event.preventDefault();
    changeContent(event.target.value);
  };

  const handleClearClick = () => {
    changeContent("");
    editorRef.current.focus();
  };

  const handleDownloadClick = () => {
    let blob = new Blob([content], {
      type: "text/plain",
    });
    let a = document.createElement("a");
    a.download = "markdown.md";
    a.href = window.URL.createObjectURL(blob);
    a.click();
  };

  const handleCopyClick = () => {
    copyToClipBoard("editor");
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
  };

  return (
    <div className="markdown-edit scroll">
      <div className="section-title">
        <h3>Markdown</h3>
        <div className="right-section">
          <Tooltip title="Download Markdown">
            <button onClick={handleDownloadClick} className="btn">
              <SaveIcon />
            </button>
          </Tooltip>
          <Tooltip title="Copy to Clipboard">
            <button onClick={handleCopyClick} className="btn">
              <CopyIcon />
            </button>
          </Tooltip>
          <Tooltip title="Clean">
            <button onClick={handleClearClick} className="btn">
              <CleanIcon />
            </button>
          </Tooltip>
        </div>
      </div>
      <textarea
        className="editable"
        value={content}
        onChange={handleEditorChange}
        id="editor"
        ref={editorRef}></textarea>

      <Snackbar open={open} autoHideDuration={2000} onClose={handleClose}>
        <Alert
          onClose={handleClose}
          severity="success"
          elevation={6}
          variant="filled">
          <AlertTitle>Copied</AlertTitle>
          The markdown is copied to your clipboard
        </Alert>
      </Snackbar>
    </div>
  );
}