@mui/icons-material#Check TypeScript Examples

The following examples show how to use @mui/icons-material#Check. 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: RadioButton.tsx    From frontend with MIT License 6 votes vote down vote up
function RadioButton({ checked, label, muiRadioButtonProps, value }: RadioButtonProps) {
  return (
    <StyledRadioButton>
      <FormControlLabel
        value={value}
        className={`${classes.radioWrapper} ${checked ? classes.checked : null}`}
        sx={checked ? {} : undefined}
        label={<Typography className={classes.label}>{label}</Typography>}
        control={
          <Radio
            icon={<div className={classes.circle} />}
            checkedIcon={
              <Check
                color="primary"
                sx={
                  checked
                    ? {
                        width: 30,
                        height: 30,
                        border: `1px solid ${theme.palette.primary.main}`,
                        backgroundColor: theme.palette.primary.main,
                        borderRadius: theme.borders.round,
                        color: theme.palette.common.white,
                      }
                    : undefined
                }
              />
            }
            {...muiRadioButtonProps}
          />
        }
      />
    </StyledRadioButton>
  )
}
Example #2
Source File: SupportersGrid.tsx    From frontend with MIT License 5 votes vote down vote up
renderCell = (params: GridRenderCellParams) =>
  params.value ? <Check color="primary" /> : <Clear color="action" />
Example #3
Source File: EditableField.tsx    From abrechnung with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function EditableField({
    value,
    onChange,
    validate = undefined,
    helperText = undefined,
    onStopEdit = undefined,
    canEdit = true,
    ...props
}) {
    const [currentValue, setValue] = useState("");
    const [editing, setEditing] = useState(false);
    const [error, setError] = useState(false);
    const classes = useStyles();

    useEffect(() => {
        setValue(value);
    }, [value]);

    const onSave = () => {
        if (!error) {
            onChange(currentValue);
            setValue("");
            setEditing(false);
        }
    };

    const startEditing = () => {
        setValue(value);
        setEditing(true);
    };

    const stopEditing = () => {
        setValue(value);
        setEditing(false);
        if (onStopEdit) {
            onStopEdit();
        }
    };

    const onValueChange = (event) => {
        setValue(event.target.value);
        if (validate) {
            setError(!validate(event.target.value));
        }
    };

    const onKeyUp = (key) => {
        if (key.keyCode === 13) {
            onSave();
        }
    };

    return (
        <div className={classes.root}>
            <DisabledTextField
                error={error}
                value={currentValue}
                disabled={!editing}
                onChange={onValueChange}
                className={classes.input}
                helperText={error ? helperText : null}
                onKeyUp={onKeyUp}
                {...props}
            />
            {canEdit &&
                (editing ? (
                    <>
                        <IconButton color="primary" onClick={onSave}>
                            <Check />
                        </IconButton>
                        <IconButton color="secondary" onClick={stopEditing}>
                            <Close />
                        </IconButton>
                    </>
                ) : (
                    <IconButton color="primary" onClick={startEditing}>
                        <Edit />
                    </IconButton>
                ))}
        </div>
    );
}
Example #4
Source File: SessionList.tsx    From abrechnung with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function SessionList() {
    // TODO: fix editing functions
    const [editedSessions, setEditedSessions] = useState({});
    const [sessionToDelete, setSessionToDelete] = useState({
        show: false,
        toDelete: null,
    });
    const user = useRecoilValue(userData);
    const sessions = user.sessions;

    useTitle("Abrechnung - Sessions");

    const editSession = (id) => {
        if (!editedSessions.hasOwnProperty(id)) {
            const newSessions = {
                ...editedSessions,
                [id]: sessions.find((session) => session.id === id)?.name,
            };
            setEditedSessions(newSessions);
        }
    };

    const stopEditSession = (id) => {
        if (editedSessions.hasOwnProperty(id)) {
            let newEditedSessions = { ...editedSessions };
            delete newEditedSessions[id];
            setEditedSessions(newEditedSessions);
        }
    };

    const closeDeleteSessionModal = () => {
        setSessionToDelete({ show: false, toDelete: null });
    };

    const performRename = (id) => {
        if (editedSessions.hasOwnProperty(id)) {
            renameSession({
                sessionID: id,
                name: editedSessions[id],
            }).catch((err) => {
                toast.error(err);
            });
            stopEditSession(id);
        }
    };

    const openDeleteSessionModal = (id) => {
        setSessionToDelete({ show: true, toDelete: id });
    };

    const confirmDeleteSession = () => {
        if (sessionToDelete.toDelete !== null) {
            deleteSession({ sessionID: sessionToDelete.toDelete }).catch((err) => {
                toast.error(err);
            });
            setSessionToDelete({ show: false, toDelete: null });
        }
    };

    const handleEditChange = (id, value) => {
        const newEditedSessions = { ...editedSessions, [id]: value };
        setEditedSessions(newEditedSessions);
    };

    const onKeyUp = (id) => (key) => {
        if (key.keyCode === 13) {
            performRename(id);
        }
    };

    return (
        <MobilePaper>
            <Typography component="h3" variant="h5">
                Login Sessions
            </Typography>
            <List>
                {sessions.map((session) => {
                    if (editedSessions.hasOwnProperty(session.id)) {
                        return (
                            <ListItem key={session.id}>
                                <TextField
                                    margin="normal"
                                    variant="standard"
                                    fullWidth
                                    onKeyUp={onKeyUp(session.id)}
                                    value={editedSessions[session.id]}
                                    onChange={(event) => handleEditChange(session.id, event.target.value)}
                                />
                                <ListItemSecondaryAction>
                                    <Button onClick={() => performRename(session.id)}>
                                        <Check />
                                    </Button>
                                    <Button onClick={() => stopEditSession(session.id)}>
                                        <Close />
                                    </Button>
                                </ListItemSecondaryAction>
                            </ListItem>
                        );
                    } else {
                        return (
                            <ListItem key={session.id}>
                                <ListItemText
                                    primary={session.name}
                                    secondary={
                                        <>
                                            <span>
                                                Valid until{" "}
                                                {DateTime.fromISO(session.valid_until).toLocaleString(
                                                    DateTime.DATETIME_FULL
                                                ) && "indefinitely"}
                                                ,{" "}
                                            </span>
                                            <span>
                                                Last seen on{" "}
                                                {DateTime.fromISO(session.last_seen).toLocaleString(
                                                    DateTime.DATETIME_FULL
                                                )}
                                            </span>
                                        </>
                                    }
                                />
                                <ListItemSecondaryAction>
                                    <IconButton onClick={() => editSession(session.id)}>
                                        <Edit />
                                    </IconButton>
                                    <IconButton onClick={() => openDeleteSessionModal(session.id)}>
                                        <Delete />
                                    </IconButton>
                                </ListItemSecondaryAction>
                            </ListItem>
                        );
                    }
                })}
            </List>
            <Dialog open={sessionToDelete.show} onClose={closeDeleteSessionModal}>
                <DialogTitle>Delete Session?</DialogTitle>

                <DialogContent>
                    <DialogContentText>
                        {sessionToDelete.toDelete !== null
                            ? `Are you sure you want to delete session ${
                                  sessions.find((session) => session.id === sessionToDelete.toDelete)?.name
                              }`
                            : null}
                    </DialogContentText>
                </DialogContent>

                <DialogActions>
                    <Button color="secondary" onClick={confirmDeleteSession}>
                        Yes pls
                    </Button>
                    <Button color="primary" onClick={closeDeleteSessionModal}>
                        No
                    </Button>
                </DialogActions>
            </Dialog>
        </MobilePaper>
    );
}
Example #5
Source File: AuthButton.tsx    From multi-downloader-nx with MIT License 4 votes vote down vote up
AuthButton: React.FC = () => {
  const snackbar = useSnackbar();

  const [open, setOpen] = React.useState(false);

  const [username, setUsername] = React.useState('');
  const [password, setPassword] = React.useState('');

  const [usernameError, setUsernameError] = React.useState(false);
  const [passwordError, setPasswordError] = React.useState(false);

  const messageChannel = React.useContext(messageChannelContext);

  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState<Error|undefined>(undefined);
  const [authed, setAuthed] = React.useState(false);

  const checkAuth = async () => {
    console.log(await messageChannel?.checkToken());
    setAuthed((await messageChannel?.checkToken())?.isOk ?? false);
  }

  React.useEffect(() => { checkAuth() }, []);

  const handleSubmit = async () => {
    if (!messageChannel)
      throw new Error('Invalid state'); //The components to confirm only render if the messageChannel is not undefinded
    if (username.trim().length === 0)
      return setUsernameError(true);
    if (password.trim().length === 0)
      return setPasswordError(true);
    setUsernameError(false);
    setPasswordError(false);
    setLoading(true);

    const res = await messageChannel.auth({ username, password });
    if (res.isOk) {
      setOpen(false);
      snackbar.enqueueSnackbar('Logged in', {
        variant: 'success'
      });
      setUsername('');
      setPassword('');
    } else {
      setError(res.reason);
    }
    await checkAuth();
    setLoading(false);
  }

  return <Require value={messageChannel}>
    <Dialog open={open}>
      <Dialog open={!!error}>
        <DialogTitle>Error during Authentication</DialogTitle>
        <DialogContentText>
          {error?.name}
          {error?.message}
        </DialogContentText>
        <DialogActions>
          <Button onClick={() => setError(undefined)}>Close</Button>
        </DialogActions>
      </Dialog>
      <DialogTitle sx={{ flexGrow: 1 }}>Authentication</DialogTitle>
      <DialogContent>
        <DialogContentText>
          Here, you need to enter your username (most likely your Email) and your password.<br />
          These information are not stored anywhere and are only used to authenticate with the service once.
        </DialogContentText>
        <TextField
          error={usernameError}
          helperText={usernameError ? 'Please enter something before submiting' : undefined}
          margin="dense"
          id="username"
          label="Username"
          type="text"
          fullWidth
          variant="standard"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
          disabled={loading}
        />
        <TextField
          error={passwordError}
          helperText={passwordError ? 'Please enter something before submiting' : undefined}
          margin="dense"
          id="password"
          label="Password"
          type="password"
          fullWidth
          variant="standard"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          disabled={loading}
        />
      </DialogContent>
      <DialogActions>
        {loading && <CircularProgress size={30}/>}
        <Button disabled={loading} onClick={() => setOpen(false)}>Close</Button>
        <Button disabled={loading} onClick={() => handleSubmit()}>Authenticate</Button>
      </DialogActions>
    </Dialog>
    <Button startIcon={authed ? <Check />: <Close />} variant="contained" onClick={() => setOpen(true)}>Authenticate</Button>
  </Require>
}
Example #6
Source File: EXPCalc.tsx    From genshin-optimizer with MIT License 4 votes vote down vote up
export default function EXPCalc() {
  const [{ mora, level, curExp, goUnder, books, books: { advice, experience, wit } }, setState] = useDBState("ToolDisplayExpCalc", initExpCalc)

  let milestoneLvl = milestone.find(lvl => lvl > level)!
  let expReq = -curExp
  for (let i = level; i < Math.min(milestoneLvl, levelExp.length); i++)  expReq += levelExp[i]
  let bookResult = calculateBooks(wit, experience, advice, expReq, goUnder) || []
  let [numWit = 0, numExperience = 0, numAdvice = 0] = bookResult
  let bookResultObj = { advice: numAdvice, experience: numExperience, wit: numWit }
  let expFromBooks = numWit * 20000 + numExperience * 5000 + numAdvice * 1000
  let moraCost = expFromBooks / 5
  let expDiff = expReq - expFromBooks
  let finalMora = mora - moraCost
  let finalExp = expFromBooks + curExp
  let finalLvl = level
  for (; finalLvl < Math.min(milestoneLvl, levelExp.length); finalLvl++) {
    if (levelExp[finalLvl] <= finalExp) finalExp -= levelExp[finalLvl]
    else break;
  }
  if (finalLvl === milestoneLvl) finalExp = 0

  let invalidText: Displayable = ""

  if (finalMora < 0)
    invalidText = <span>You don't have enough <b>Mora</b> for this operation.</span>
  else if (bookResult.length === 0)
    invalidText = <span>You don't have enough <b>EXP. books</b> to level to the next milestone.</span>
  else if (level === 90)
    invalidText = "You are at the maximum level."
  return <CardDark>
    <Grid container sx={{ px: 2, py: 1 }} spacing={2} >
      <Grid item>
        <ImgIcon src={booksData.wit.img} sx={{ fontSize: "2em" }} />
      </Grid>
      <Grid item flexGrow={1}>
        <Typography variant="h6">Experience Calculator</Typography>
      </Grid>
      <Grid item>
        <ButtonGroup>
          <Button color="primary" disabled={!goUnder} onClick={() => setState({ goUnder: false })}>Full Level</Button>
          <Button color="primary" disabled={goUnder} onClick={() => setState({ goUnder: true })}>Don't fully level</Button>
        </ButtonGroup>
      </Grid>
    </Grid>
    <Divider />

    <CardContent>
      <Grid container spacing={1}>
        <Grid item>
          <Typography>
            <span>This calculator tries to calculate the amount of exp books required to get to the next milestone level. </span>
            {goUnder ? "It will try to get as close to the milestone level as possible, so you can grind the rest of the exp without any waste." :
              "It will try to calculate the amount of books needed to minimize as much exp loss as possible."}
          </Typography>
        </Grid>
        <Grid item xs={6} md={3} >
          <ButtonGroup sx={{ display: "flex" }}>
            <TextButton>Current Level</TextButton>
            <CustomNumberInputButtonGroupWrapper sx={{ flexBasis: 30, flexGrow: 1 }}>
              <CustomNumberInput
                value={level}
                onChange={(val) => setState({ level: clamp(val, 0, 90) })}
                sx={{ px: 2 }}
              />
            </CustomNumberInputButtonGroupWrapper >
          </ButtonGroup>
        </Grid>
        <Grid item xs={6} md={3} >
          <ButtonGroup sx={{ display: "flex" }}>
            <TextButton>Current EXP.</TextButton>
            <CustomNumberInputButtonGroupWrapper sx={{ flexBasis: 30, flexGrow: 1 }}>
              <CustomNumberInput
                value={curExp}
                onChange={(val) => setState({ curExp: clamp(val, 0, (levelExp[level] || 1) - 1) })}
                endAdornment={`/${levelExp[level] || 0}`}
                sx={{ px: 2 }}
              />
            </CustomNumberInputButtonGroupWrapper>
          </ButtonGroup>
        </Grid>
        <Grid item xs={6} md={3} ><CardLight>
          <Box sx={{ p: 1, display: "flex", justifyContent: "space-between" }}>
            <Typography>
              Next Milestone Level:
            </Typography>
            <Typography>
              <b>{milestoneLvl}</b>
            </Typography>
          </Box>
        </CardLight></Grid>
        <Grid item xs={6} md={3} ><CardLight>
          <Box sx={{ p: 1, display: "flex", justifyContent: "space-between" }}>
            <Typography>
              EXP. to milestone:
            </Typography>
            <Typography>
              <span><strong>{expFromBooks}</strong> / <strong>{expReq}</strong></span>
            </Typography>
          </Box>
        </CardLight></Grid>
        {Object.entries(books).map(([bookKey]) => {
          return <Grid item xs={12} md={4} key={bookKey}>
            <BookDisplay bookKey={bookKey} value={books[bookKey]} setValue={b => setState({ books: { ...books, [bookKey]: b } })} required={bookResultObj[bookKey]} />
          </Grid>
        })}
        <Grid item xs={12} md={4} >
          <ButtonGroup sx={{ display: "flex" }}>
            <TextButton>Current Mora</TextButton>
            <CustomNumberInputButtonGroupWrapper sx={{ flexBasis: 30, flexGrow: 1 }}>
              <CustomNumberInput
                value={mora}
                onChange={(val) => setState({ mora: Math.max(val ?? 0, 0) })}
                sx={{ px: 2 }}
              />
            </CustomNumberInputButtonGroupWrapper>
          </ButtonGroup>
        </Grid>
        <Grid item xs={12} md={4} ><CardLight>
          <Box sx={{ p: 1, display: "flex", justifyContent: "space-between" }}>
            <Typography>Mora Cost: </Typography>
            <Typography><b>{moraCost}</b></Typography>
          </Box>
        </CardLight></Grid>
        <Grid item xs={12} md={4} ><CardLight>
          <Box sx={{ p: 1, display: "flex", justifyContent: "space-between" }}>
            <Typography>EXP {!goUnder ? "Waste" : "Diff"}: </Typography>
            <Typography><b><ColorText color={expDiff < 0 ? `error` : `success`}>{expDiff}</ColorText></b></Typography>
          </Box>
        </CardLight></Grid>
        <Grid item xs={12} md={4} ><CardLight>
          <Box sx={{ p: 1, display: "flex", justifyContent: "space-between" }}>
            <Typography>Final Mora: </Typography>
            <Typography><b><ColorText color={finalMora < 0 ? `error` : `success`}>{finalMora}</ColorText></b></Typography>
          </Box>
        </CardLight></Grid>
        <Grid item xs={12} md={4} ><CardLight>
          <Box sx={{ p: 1, display: "flex", justifyContent: "space-between" }}>
            <Typography>Final Level: </Typography>
            <Typography><b><ColorText color="success">{finalLvl}</ColorText></b></Typography>
          </Box>
        </CardLight></Grid>
        <Grid item xs={12} md={4} ><CardLight>
          <Box sx={{ p: 1, display: "flex", justifyContent: "space-between" }}>
            <Typography>Final EXP: </Typography>
            <Typography><b><ColorText color={finalExp < 0 ? `error` : `success`}>{finalExp}</ColorText></b></Typography>
          </Box>
        </CardLight></Grid>
      </Grid>
    </CardContent>
    <Divider />
    <CardContent sx={{ py: 1 }}>
      <Grid container spacing={2}>
        <Grid item flexGrow={1}>
          {!!invalidText && <Alert variant="filled" severity="error" >{invalidText}</Alert>}
        </Grid>
        <Grid item xs="auto"><Button disabled={!!invalidText}
          onClick={() => setState({
            level: finalLvl,
            curExp: finalExp,
            books: objectMap(bookResultObj, (val, bookKey) => books[bookKey] - val) as any,
            mora: finalMora
          })}
          color="success"
          startIcon={<Check />}
          sx={{ height: "100%" }}
        >Apply</Button>
        </Grid>
      </Grid>
    </CardContent>
  </CardDark >
}
Example #7
Source File: Config.tsx    From NekoMaid with MIT License 4 votes vote down vote up
configs.push({
  title: lang.config.serverConfig,
  component () {
    const plugin = usePlugin()
    const globalData = useGlobalData()
    const [flag, update] = useState(0)
    const [info, setInfo] = useState<Record<string, string>>({ })
    const [open, setOpen] = useState(false)
    const [canGetData, setCanGetData] = useState(true)
    const [loading, setLoading] = useState(false)
    const setValue = (field: string, value: any, isGlobal = true) => {
      plugin.emit('server:set', field, value)
      success()
      if (isGlobal) {
        (globalData as any)[field] = value
        update(flag + 1)
        location.reload()
      }
    }
    const createEditButtom = (field: string, isGlobal?: boolean, isInt = true) => <IconButton
      onClick={() => dialog(
        {
          content: lang.inputValue,
          input: isInt
            ? {
                error: true,
                type: 'number',
                helperText: lang.invalidValue,
                validator: (it: string) => /^\d+$/.test(it) && +it >= 0
              }
            : { }
        }).then(res => res != null && setValue(field, isInt ? parseInt(res as any) : (res || null), isGlobal))}
    ><Edit /></IconButton>

    const infoElm: JSX.Element[] = []
    for (const key in info) {
      const name = (lang.config as any)[key]
      infoElm.push(<ListItem key={key} sx={{ pl: 4 }}>
        <ListItemText
          primary={key === 'isAikarFlags' ? <Link href='https://mcflags.emc.gs' target='_blank' rel='noopener'>{name}</Link> : name}
          secondary={info[key].toString()}
        />
      </ListItem>)
    }

    return <List>
      <CircularLoading loading={loading} />
      <ListItem secondaryAction={globalData.canSetMaxPlayers
        ? createEditButtom('maxPlayers')
        : undefined}>
        <ListItemText primary={lang.config.maxPlayers + ': ' + globalData.maxPlayers} />
      </ListItem>
      <ListItem secondaryAction={createEditButtom('spawnRadius')}>
        <ListItemText primary={lang.config.spawnRadius + ': ' + globalData.spawnRadius} />
      </ListItem>
      <ListItem secondaryAction={createEditButtom('motd', false, false)}>
        <ListItemText primary={lang.config.motd} />
      </ListItem>
      <ListItem secondaryAction={<Switch checked={globalData.hasWhitelist} onChange={e => setValue('hasWhitelist', e.target.checked)} />}>
        <ListItemText primary={lang.config.whitelist} />
      </ListItem>
      {canGetData && <>
        <ListItemButton onClick={() => {
          if (infoElm.length) setOpen(!open)
          else {
            setLoading(true)
            plugin.emit('server:fetchInfo', (data: any) => {
              setLoading(false)
              if (!data) {
                failed(lang.unsupported)
                setCanGetData(false)
                return
              }
              setInfo(data)
              setOpen(true)
            })
          }
        }}>
        <ListItemIcon><Equalizer /></ListItemIcon>
          <ListItemText primary={lang.info} />
          {open ? <ExpandLess /> : <ExpandMore />}
        </ListItemButton>
        <Collapse in={open} timeout='auto' unmountOnExit>
          <List component='div' dense disablePadding>{infoElm}</List>
        </Collapse>
      </>}
    </List>
  }
},
{
  title: lang.history,
  component () {
    const [cur, update] = useState(0)
    const list: ServerRecord[] = JSON.parse(localStorage.getItem('NekoMaid:servers') || '[]')
    return <List>
      {list.sort((a, b) => b.time - a.time).map(it => {
        const i = it.address.indexOf('?')
        return <ListItem
          disablePadding
          key={it.address}
          secondaryAction={<IconButton edge='end' size='small' onClick={() => {
            localStorage.setItem('NekoMaid:servers', JSON.stringify(list.filter(s => s.address !== it.address)))
            success()
            update(cur + 1)
          }}><Delete /></IconButton>}
        >
          <ListItemButton onClick={() => {
            location.hash = ''
            location.search = it.address
          }} dense>
            <ListItemAvatar><Avatar src={it.icon} variant='rounded'><HelpOutline /></Avatar></ListItemAvatar>
            <ListItemText primary={<Tooltip title={it.address.slice(i + 1)}>
              <span>{it.address.slice(0, i)}</span></Tooltip>} secondary={dayjs(it.time).fromNow()} />
          </ListItemButton>
        </ListItem>
      })}
    </List>
  }
},
{
  title: lang.config.theme,
  component () {
    const color = localStorage.getItem('NekoMaid:color') || 'blue'
    return <CardContent sx={{ textAlign: 'center' }}>
      <Box>
        <ToggleButtonGroup exclusive value={localStorage.getItem('NekoMaid:colorMode') || ''} onChange={(_, it) => {
          localStorage.setItem('NekoMaid:colorMode', it)
          location.reload()
        }}>
          <ToggleButton value='light'><Brightness7 /> {lang.config.light}</ToggleButton>
          <ToggleButton value=''><SettingsBrightness /> {lang.config.system}</ToggleButton>
          <ToggleButton value='dark'><Brightness4 /> {lang.config.dark}</ToggleButton>
        </ToggleButtonGroup>
      </Box>
      <Paper sx={{ marginTop: 2, width: '176px', overflow: 'hidden', display: 'inline-block' }}>
        {Object.keys(colors).slice(1, 17).map((key, i) => {
          const checked = color === key
          const elm = <Box
            key={key}
            onClick={() => {
              localStorage.setItem('NekoMaid:color', key)
              location.reload()
            }}
            sx={{
              backgroundColor: (colors as any)[key][600],
              width: '44px',
              height: '44px',
              display: 'inline-block',
              cursor: 'pointer'
            }}
          ><Check htmlColor='white' sx={{ top: '10px', position: 'relative', opacity: checked ? 1 : 0 }} /></Box>
          return (i + 1) % 4 === 0 ? <React.Fragment key={key}>{elm}<br /></React.Fragment> : elm
        })}
      </Paper>
    </CardContent>
  }
})
Example #8
Source File: Dashboard.tsx    From NekoMaid with MIT License 4 votes vote down vote up
Dashboard: React.FC = () => {
  const plugin = usePlugin()
  const { version, hasGeoIP } = useGlobalData()
  const [status, setStatus] = useState<Status[]>([])
  const [current, setCurrent] = useState<CurrentStatus | undefined>()

  useEffect(() => {
    const offSetStatus = plugin.once('dashboard:info', setStatus)
    const offCurrent = plugin.on('dashboard:current', (data: CurrentStatus) => setCurrent(old => {
      if (old && isEqual(old.players, data.players)) data.players = old.players
      return data
    }))
    plugin.switchPage('dashboard')
    return () => {
      offSetStatus()
      offCurrent()
    }
  }, [])

  const playerCount = current?.players?.length || 0
  const prev = status[status.length - 1]?.players || 0
  const percent = (prev ? playerCount / prev - 1 : playerCount) * 100
  const tpsColor = !current || current.tps >= 18 ? green : current.tps >= 15 ? yellow : red
  return <Box sx={{ minHeight: '100%', py: 3 }}>
    <Toolbar />
    <Container maxWidth={false}>
      <Grid container spacing={3}>
        <Grid item lg={3} sm={6} xl={3} xs={12}>
          <TopCard
            title={lang.dashboard.version}
            content={current ? version : <Skeleton animation='wave' width={150} />}
            icon={<Handyman />}
            color={orange[600]}
          >
            <Box sx={{ pt: 2, display: 'flex', alignItems: 'flex-end' }}>
              {!current || current.behinds < 0
                ? <Refresh htmlColor={blue[900]} />
                : current?.behinds === 0
                  ? <Check htmlColor={green[900]} />
                  : <Update htmlColor={yellow[900]} />}
              <Typography color='textSecondary' variant='caption'>&nbsp;{!current || current.behinds === -3
                ? lang.dashboard.updateChecking
                : current.behinds < 0
                  ? <Link underline='hover' color='inherit' sx={{ cursor: 'pointer' }} onClick={() => {
                    toast(lang.dashboard.updateChecking)
                    plugin.emit('dashboard:checkUpdate')
                  }}>{lang.dashboard.updateFailed}</Link>
                  : current.behinds === 0 ? lang.dashboard.updated : lang.dashboard.behinds(current.behinds)}</Typography>
            </Box>
          </TopCard>
        </Grid>
        <Grid item lg={3} sm={6} xl={3} xs={12}>
          <TopCard
            title={lang.dashboard.onlinePlayers}
            content={current ? playerCount : <Skeleton animation='wave' width={150} />}
            icon={<People />}
            color={deepPurple[600]}
          >
            <Box sx={{ pt: 2, display: 'flex', alignItems: 'flex-end' }}>
              {percent === 0 ? <Remove color='primary' /> : percent < 0 ? <ArrowDownward color='error' /> : <ArrowUpward color='success' />}
              <Typography
                sx={{ color: (percent === 0 ? blue : percent < 0 ? red : green)[900], mr: 1 }}
                variant='body2'
              >{Math.abs(percent).toFixed(0)}%</Typography>
              <Typography color='textSecondary' variant='caption'>{lang.dashboard.lastHour}</Typography>
            </Box>
          </TopCard>
        </Grid>
        <Grid item lg={3} sm={6} xl={3} xs={12}>
          <TopCard
            title='TPS'
            content={current ? (current.tps === -1 ? '?' : current.tps.toFixed(2)) : <Skeleton animation='wave' width={150} />}
            icon={!current || current.tps >= 18 || current.tps === -1
              ? <SentimentVerySatisfied />
              : current.tps >= 15 ? <SentimentSatisfied /> : <SentimentDissatisfied />}
            color={tpsColor[600]}
          >
            <Box sx={{ pt: 2.1, display: 'flex', alignItems: 'flex-end' }}>
              <Typography
                sx={{ color: tpsColor[900], mr: 1 }}
                variant='body2'
              >{!current || current.mspt === -1 ? '?' : current.mspt.toFixed(2) + 'ms'}</Typography>
              <Typography color='textSecondary' variant='caption'>{lang.dashboard.mspt}</Typography>
            </Box>
          </TopCard>
        </Grid>
        <Grid item lg={3} sm={6} xl={3} xs={12}>
          <TopCard
            title={lang.dashboard.uptime}
            content={current ? <Uptime time={current.time} /> : <Skeleton animation='wave' width={150} />}
            icon={<AccessTime />}
            color={blue[600]}
          >
            <Box sx={{ pt: 2.7, display: 'flex', alignItems: 'center' }}>
              <Typography color='textSecondary' variant='caption' sx={{ marginRight: 1 }}>{lang.dashboard.memory}</Typography>
              <Tooltip title={current?.totalMemory ? prettyBytes(current.memory) + ' / ' + prettyBytes(current.totalMemory) : ''}>
                <LinearProgress
                  variant='determinate'
                  value={current?.totalMemory ? current.memory / current.totalMemory * 100 : 0}
                  sx={{ flex: '1' }}
                />
              </Tooltip>
            </Box>
          </TopCard>
        </Grid>
        <Grid item lg={8} md={12} xl={9} xs={12}>{useMemo(() => <Charts data={status} />, [status])}</Grid>
        <Grid item lg={4} md={12} xl={3} xs={12}><Players players={current?.players} /></Grid>
        {hasGeoIP && current?.players && typeof current.players[0] !== 'string' && <Grid item xs={12}>
          <Accordion TransitionProps={{ unmountOnExit: true }} disableGutters>
            <AccordionSummary expandIcon={<ExpandMore />}>
              <Typography>{lang.dashboard.playersDistribution}</Typography>
            </AccordionSummary>
            <Divider />
            <WorldMap players={current.players as Player[]} />
          </Accordion>
        </Grid>}
      </Grid>
    </Container>
  </Box>
}
Example #9
Source File: DetailsModal.tsx    From frontend with MIT License 4 votes vote down vote up
function DetailsModal() {
  const { getDialogs } = DialogStore
  const handleClose = () => DialogStore.hide()
  const { t } = useTranslation()

  return (
    <>
      {getDialogs.map(({ id, show, title, row }) => {
        return (
          <Dialog
            key={id}
            onClose={handleClose}
            open={show}
            maxWidth="md"
            PaperProps={{ elevation: 5 }}
            BackdropProps={{ style: { opacity: 0.3 } }}>
            {title && <DialogTitle>{title}</DialogTitle>}
            <DialogContent dividers>
              {/* TODO: Extract concrete implementation and use generic one */}
              <Grid item xs={12}>
                <List>
                  <ListItem>
                    <ListItemText
                      primary={`${row.getValue(row.id, 'name')}`}
                      secondary={row.row.person.company}
                    />
                  </ListItem>
                  <ListItem>
                    <ListItemText primary={row.row.person.email} secondary={row.row.person.phone} />
                  </ListItem>
                  <ListItem>{dateFormatter(row.row.createdAt)}</ListItem>
                  <ListItem>
                    <Typography variant="body2">{row.row.message || row.row.comment}</Typography>
                  </ListItem>
                  <ListItem>
                    <Typography variant="caption">{row.row.person.id}</Typography>
                  </ListItem>
                  {'associationMember' in row.row &&
                    [
                      'associationMember',
                      'benefactorCampaign',
                      'benefactorPlatform',
                      'companyOtherText',
                      'companySponsor',
                      'companyVolunteer',
                      'partnerBussiness',
                      'partnerNpo',
                      'partnerOtherText',
                      'roleAssociationMember',
                      'roleBenefactor',
                      'roleCompany',
                      'rolePartner',
                      'roleVolunteer',
                      'volunteerBackend',
                      'volunteerDesigner',
                      'volunteerDevOps',
                      'volunteerFinancesAndAccounts',
                      'volunteerFrontend',
                      'volunteerLawyer',
                      'volunteerMarketing',
                      'volunteerProjectManager',
                      'volunteerQa',
                      'volunteerSecurity',
                    ].map((k, i) => (
                      <ListItem key={i}>
                        <ListItemText
                          primary={k}
                          secondary={row.row[k] ? <Check color="primary" /> : <Clear />}
                        />
                      </ListItem>
                    ))}
                </List>
              </Grid>
              {/*  */}
            </DialogContent>
            <DialogActions>
              <Button autoFocus onClick={handleClose} color="primary">
                {t('common:close')}
              </Button>
            </DialogActions>
          </Dialog>
        )
      })}
    </>
  )
}
Example #10
Source File: Vault.tsx    From NekoMaid with MIT License 1 votes vote down vote up
PermissionDialog: React.FC<{ plugin: Plugin, id: string | undefined, isGroup: boolean, onClose: () => void }> = ({ plugin, id, onClose, isGroup }) => {
  const [value, setValue] = useState('')
  const [status, setStatus] = useState<boolean | undefined>(false)
  const [options, setOptions] = useState<string[]>([])
  useEffect(() => {
    if (!id) return
    setValue('')
    setStatus(false)
    plugin.emit('vault:getAllPermissions', (it: any) => setOptions(it.sort()))
  }, [id])
  const queryStatus = useMemo(() => throttle((value: string) => plugin.emit('vault:permission', setStatus, id, value, 0, isGroup), 500), [id, isGroup])
  return <Dialog open={!!id} onClose={onClose}>
    <DialogTitle>{lang.vault.editorTitle}</DialogTitle>
    <DialogContent sx={{ overflow: 'hidden' }}>
      <DialogContentText>{lang.vault.permissionInput}: <span className='bold' style={{ }}>
        ({isGroup ? lang.vault.permissionGroup : minecraft['entity.minecraft.player']}: {id})</span></DialogContentText>
      <Autocomplete
        freeSolo
        options={options}
        sx={{ marginTop: 1 }}
        inputValue={value}
        renderInput={params => <TextField {...params as any} label={lang.vault.permission} />}
        onInputChange={(_, it) => {
          setValue(it)
          setStatus(undefined)
          queryStatus(it)
        }}
      />
      <Box sx={{ display: 'flex', alignItems: 'center', marginTop: 1 }}>
        {lang.status}:{status == null
          ? <CircularProgress size={20} sx={{ margin: '5px' }}/>
          : status ? <Check color='success' /> : <Close color='error' />}
        &nbsp;{status != null && <Button
          disabled={!value}
          variant='outlined'
          size='small'
          onClick={() => plugin.emit('vault:permission', (res: boolean) => {
            action(res)
            setStatus(undefined)
            queryStatus(value)
          }, id, value, status ? 2 : 1, isGroup)}
        >{lang.vault[status ? 'removePermission' : 'addPermission']}</Button>}
      </Box>
    </DialogContent>
    <DialogActions><Button onClick={onClose}>{minecraft['gui.back']}</Button></DialogActions>
  </Dialog>
}