@mui/material#InputLabel TypeScript Examples

The following examples show how to use @mui/material#InputLabel. 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: BlockEditor.tsx    From NekoMaid with MIT License 6 votes vote down vote up
BlockSelector: React.FC<{ worlds: string[] }> = ({ worlds }) => {
  const his = useHistory()
  const [world, setWorld] = useState(worlds[0])
  const [x, setX] = useState('0')
  const [y, setY] = useState('0')
  const [z, setZ] = useState('0')
  return <Grid container>
    <Grid item xs={6}>
      <FormControl variant='standard' fullWidth>
        <InputLabel id='nekomaid-block-editor-world'>{lang.world}</InputLabel>
        <Select
          labelId='nekomaid-block-editor-world'
          value={world}
          label={lang.world}
          onChange={e => setWorld(e.target.value)}
        >
          {worlds.map(it => <MenuItem key={it} value={it}>{it}</MenuItem>)}
        </Select>
      </FormControl>
    </Grid>
    <Grid item xs={2}><TextField variant='standard' label='X' type='number' fullWidth value={x} onChange={e => setX(e.target.value)} /></Grid>
    <Grid item xs={2}><TextField variant='standard' label='Y' type='number' fullWidth value={y} onChange={e => setY(e.target.value)} /></Grid>
    <Grid item xs={2}><TextField variant='standard' label='Z' type='number' fullWidth value={z} onChange={e => setZ(e.target.value)} /></Grid>
    <Grid item xs={12} sx={{ marginTop: 3, textAlign: 'center' }}>
      <Button
        variant='contained'
        onClick={() => his.push(`/NekoMaid/block/${world}/${parseFloat(x) | 0}/${parseFloat(y) | 0}/${parseFloat(z) | 0}`)}
      >{minecraft['gui.done']}</Button>
    </Grid>
  </Grid>
}
Example #2
Source File: CampaignTypeSelect.tsx    From frontend with MIT License 6 votes vote down vote up
export default function CampaignTypeSelect({ name = 'campaignTypeId' }) {
  const { t } = useTranslation()
  const { data } = useCampaignTypesList()
  const [field, meta] = useField(name)

  const helperText = meta.touched ? translateError(meta.error as TranslatableField, t) : ''
  return (
    <FormControl
      fullWidth
      size="small"
      variant="outlined"
      error={Boolean(meta.error) && Boolean(meta.touched)}>
      <InputLabel>{t('campaigns:campaign.type')}</InputLabel>
      <Select fullWidth defaultValue="" label={t('campaigns:campaign.type')} {...field}>
        <MenuItem value="" disabled>
          {t('campaigns:campaign.type')}
        </MenuItem>
        {data?.map((campaignType, index) => (
          <MenuItem key={index} value={campaignType.id}>
            {campaignType.name}
          </MenuItem>
        ))}
      </Select>
      {helperText && <FormHelperText error>{helperText}</FormHelperText>}
    </FormControl>
  )
}
Example #3
Source File: Select.tsx    From Cromwell with MIT License 6 votes vote down vote up
/** @internal */
export function Select(props: TSelectProps & MuiSelectProps<string | number>) {
  const { className, style, variant = 'filled' } = props;
  return (
    <FormControl
      fullWidth={props.fullWidth}
      style={props.style}
      className={props.className}
    >
      {props.label && (
        <InputLabel style={{
          marginTop: '8px',
        }}>{props.label}</InputLabel>
      )}
      <MuiSelect
        {...props}
        variant={variant}
        className={className}
        style={style}
      >
        {props.options?.map((option) => {
          const label = typeof option === 'object' ? option.label : option;
          const value = typeof option === 'object' ? option.value : option;
          return (
            <MenuItem value={value} key={value + ''}>{label}</MenuItem>
          )
        })}
      </MuiSelect>
    </FormControl>
  )
}
Example #4
Source File: SelectCountry.tsx    From frontend with MIT License 6 votes vote down vote up
export default function SelectCountry({ name = 'countryId' }) {
  const { t } = useTranslation()
  const { data } = useCountriesList()
  const [field, meta] = useField(name)

  const helperText = meta.touched ? translateError(meta.error as TranslatableField, t) : ''
  return (
    <FormControl
      fullWidth
      size="small"
      variant="outlined"
      error={Boolean(meta.error) && Boolean(meta.touched)}>
      <InputLabel>Държава</InputLabel>
      <Select fullWidth defaultValue="" label="Държава" {...field}>
        <MenuItem value="" disabled>
          Избери държава
        </MenuItem>
        {data?.map((country) => (
          <MenuItem key={country.id} value={country.id}>
            {country.name}
          </MenuItem>
        ))}
      </Select>
      {helperText && <FormHelperText error>{helperText}</FormHelperText>}
    </FormControl>
  )
}
Example #5
Source File: TextFilter.tsx    From Tachidesk-WebUI with Mozilla Public License 2.0 5 votes vote down vote up
export default function TextFilter(props: Props) {
    const {
        state,
        name,
        position,
        group,
        updateFilterValue,
        update,
    } = props;
    const [Search, setsearch] = React.useState(state || '');
    let typingTimer: NodeJS.Timeout;

    function doneTyping(e: React.ChangeEvent<HTMLInputElement>) {
        const upd = update.filter((el: {
            position: number; group: number | undefined;
        }) => !(position === el.position && group === el.group));
        updateFilterValue([...upd, { position, state: e.target.value, group }]);
    }

    function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
        setsearch(e.target.value);

        clearTimeout(typingTimer);
        typingTimer = setTimeout(() => { doneTyping(e); }, 2500);
    }

    if (state !== undefined) {
        return (
            <Box key={`${name}`} sx={{ display: 'flex', flexDirection: 'row', minWidth: 120 }}>
                <>
                    <SearchIcon
                        sx={{
                            margin: 'auto',
                        }}
                    />
                    <FormControl fullWidth>
                        <InputLabel sx={{ margin: '10px 0 10px 0' }}>
                            {name}
                        </InputLabel>
                        <Input
                            name={name}
                            value={Search || ''}
                            onChange={handleChange}
                            sx={{ margin: '10px 0 10px 0' }}
                        />
                    </FormControl>
                </>
            </Box>
        );
    }
    return (<></>);
}
Example #6
Source File: RadioGroupSelector.tsx    From console with GNU Affero General Public License v3.0 5 votes vote down vote up
RadioGroupSelector = ({
  selectorOptions = [],
  currentSelection,
  label,
  id,
  name,
  onChange,
  tooltip = "",
  disableOptions = false,
  classes,
  displayInColumn = false,
}: RadioGroupProps) => {
  return (
    <Grid container alignItems={"center"}>
      <Grid item xs>
        <InputLabel htmlFor={id} className={classes.inputLabel}>
          <span>{label}</span>
          {tooltip !== "" && (
            <div className={classes.tooltipContainer}>
              <Tooltip title={tooltip} placement="top-start">
                <div>
                  <HelpIcon />
                </div>
              </Tooltip>
            </div>
          )}
        </InputLabel>
      </Grid>
      <Grid item xs className={classes.radioOptionsLayout}>
        <RadioGroup
          aria-label={id}
          id={id}
          name={name}
          value={currentSelection}
          onChange={onChange}
          row={!displayInColumn}
          style={{ display: "block", textAlign: "right" }}
        >
          {selectorOptions.map((selectorOption) => {
            return (
              <FormControlLabel
                key={`rd-${name}-${selectorOption.value}`}
                value={selectorOption.value}
                control={<RadioButton />}
                label={selectorOption.label}
                disabled={disableOptions}
                className={clsx(classes.optionLabel, {
                  [classes.checkedOption]:
                    selectorOption.value === currentSelection,
                })}
              />
            );
          })}
        </RadioGroup>
      </Grid>
    </Grid>
  );
}
Example #7
Source File: Sort.tsx    From cli with Apache License 2.0 5 votes vote down vote up
SortRenderer: FunctionComponent<SortProps> = (props) => {
  const {controller, criteria} = props;
  const [state, setState] = React.useState(controller.state);

  useEffect(
    () => controller.subscribe(() => setState(controller.state)),
    [controller]
  );

  const getCurrentCriterion = () =>
    criteria.find(
      ([, criterion]) =>
        state.sortCriteria === buildCriterionExpression(criterion)
    )!;

  const getCriterionFromName = (name: string) =>
    criteria.find(([criterionName]) => criterionName === name)!;

  return (
    <Box>
      <FormControl>
        <InputLabel id="sort-by-label">Sort by</InputLabel>
        <Select
          labelId="sort-by-label"
          label="Sort by"
          id="sort-by"
          onChange={(e) =>
            controller.sortBy(getCriterionFromName(e.target.value as string)[1])
          }
          defaultValue={getCurrentCriterion()[0]}
        >
          {criteria.map(([criterionName]) => (
            <MenuItem key={criterionName} value={criterionName}>
              {criterionName}
            </MenuItem>
          ))}
        </Select>
      </FormControl>
    </Box>
  );
}
Example #8
Source File: MultiSelect.tsx    From multi-downloader-nx with MIT License 5 votes vote down vote up
MultiSelect: React.FC<MultiSelectProps> = (props) => {
  const theme = useTheme();

  return <div>
    <FormControl sx={{ m: 1, width: 300 }}>
      <InputLabel id="multi-select-label">{props.title}</InputLabel>
      <Select
        labelId="multi-select-label"
        id="multi-select"
        multiple
        value={(props.selected ?? [])}
        onChange={e => {
          const val = typeof e.target.value === "string" ? e.target.value.split(",") : e.target.value;
          if (props.allOption && val.includes('all')) {
            if (props.values.length === val.length - 1)
              props.onChange([]);
            else 
              props.onChange(props.values);
          } else {
            props.onChange(val);
          }
        }}
        input={<OutlinedInput id="select-multiple-chip" label={props.title} />}
        renderValue={(selected) => (
          selected.join(', ')
        )}
        MenuProps={MenuProps}
      >
        {props.values.concat(props.allOption ? 'all' : []).map((name) => (
          <MenuItem
            key={`${props.title}_${name}`}
            value={name}
            style={getStyles(name, props.selected, theme)}
          >
            {name}
          </MenuItem>
        ))}
      </Select>
    </FormControl>
  </div>
}
Example #9
Source File: Importer.tsx    From your_spotify with GNU General Public License v3.0 5 votes vote down vote up
export default function Importer() {
  const dispatch = useAppDispatch();
  const imports = useSelector(selectImportStates);
  const [importType, setImportType] = useState<ImporterStateTypes>(ImporterStateTypes.privacy);

  const fetch = useCallback(
    async (force = false) => {
      dispatch(getImports(force));
    },
    [dispatch],
  );

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

  const running = useMemo(() => imports?.find((st) => st.status === 'progress'), [imports]);
  const Component = useMemo(
    () => (importType ? ImportTypeToComponent[importType] : null),
    [importType],
  );

  if (!imports) {
    return <CircularProgress />;
  }

  return (
    <div>
      <div>
        {running && (
          <div>
            <Text>Importing...</Text>
            <div className={s.progress}>
              <Text>
                {running.current} / {running.total}
              </Text>
            </div>
            <LinearProgress
              style={{ width: '100%' }}
              variant="determinate"
              value={(running.current / running.total) * 100}
            />
          </div>
        )}
      </div>
      {!running && (
        <div>
          <FormControl className={s.selectimport}>
            <InputLabel id="import-type-select">Import type</InputLabel>
            <Select
              labelId="import-type-select"
              value={importType}
              label="Import type"
              onChange={(ev) => setImportType(ev.target.value as ImporterStateTypes)}>
              {Object.values(ImporterStateTypes).map((typ) => (
                <MenuItem value={typ} key={typ}>
                  {typ}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
          {Component && <Component />}
        </div>
      )}
      {imports.length > 0 && <ImportHistory />}
    </div>
  );
}
Example #10
Source File: CategorySelect.tsx    From mojito_pdm with Creative Commons Attribution Share Alike 4.0 International 5 votes vote down vote up
CategorySelect: React.FC = () => {
    const theme = useTheme()
    const [category, setCategory] = useRecoilState(CarState.categorySearch)

    const handleChange = (event: SelectChangeEvent) => {
        setCategory(event.target.value)
    };

    return (
        <>
            <div>
                <FormControl variant="outlined" sx={{margin: theme.spacing(1), minWidth: 240}} color="error">
                    <InputLabel sx={{color: "white"}}>Category</InputLabel>
                    <Select sx={{color: "white"}}
                            labelId="demo-simple-select-outlined-label"
                            id="demo-simple-select-outlined"
                            value={category}
                            onChange={handleChange}
                            label="Category"
                    >
                        <MenuItem value="">
                            <em>All</em>
                        </MenuItem>
                        <MenuItem value={"Sports"}>Sports</MenuItem>
                        <MenuItem value={"Compacts"}>Compacts</MenuItem>
                        <MenuItem value={"Muscle"}>Muscle</MenuItem>
                        <MenuItem value={"Sedan"}>Sedan</MenuItem>
                        <MenuItem value={"Coupe"}>Coupé</MenuItem>
                        <MenuItem value={"Super"}>Super</MenuItem>
                        <MenuItem value={"SUV"}>SUV</MenuItem>
                        <MenuItem value={"Vans"}>Vans</MenuItem>
                        <MenuItem value={"Offroad"}>Offroad</MenuItem>
                        <MenuItem value={"Sports Classics"}>Sports Classics</MenuItem>
                        <MenuItem value={"Motorcycles"}>Motorcycles</MenuItem>
                    </Select>
                </FormControl>
            </div>
        </>
    )
}
Example #11
Source File: FileList.tsx    From frontend with MIT License 5 votes vote down vote up
function FileList({ files, onDelete, onSetFileRole, filesRole = [] }: Props) {
  const setFileRole = (file: File) => {
    return (event: SelectChangeEvent<CampaignFileRole>) => {
      if (Object.values(CampaignFileRole).includes(event.target.value as CampaignFileRole)) {
        onSetFileRole(file, event.target.value as CampaignFileRole)
      }
    }
  }

  return (
    <List dense>
      {files.map((file, key) => (
        <ListItem
          key={key}
          secondaryAction={
            <IconButton edge="end" aria-label="delete" onClick={() => onDelete && onDelete(file)}>
              <Delete />
            </IconButton>
          }>
          <ListItemAvatar>
            <Avatar>
              <UploadFile />
            </Avatar>
          </ListItemAvatar>
          <ListItemText primary={file.type} />
          <ListItemText primary={file.name} />
          <FormControl>
            <InputLabel id="choose-type-label">{'Избери роля'}</InputLabel>
            <Select<CampaignFileRole>
              id="choose-type"
              label="Избери роля"
              labelId="choose-type-label"
              value={
                filesRole.find((f) => f.file === file.name)?.role ?? CampaignFileRole.background
              }
              onChange={setFileRole(file)}>
              {Object.values(CampaignFileRole).map((role) => (
                <MenuItem key={role} value={role}>
                  {role}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
        </ListItem>
      ))}
    </List>
  )
}
Example #12
Source File: StyleField.tsx    From Cromwell with MIT License 5 votes vote down vote up
export function StyleField(props: {
    dataType: 'px' | 'string' | 'color' | 'select';
    handleStyleChange: (name: keyof React.CSSProperties, value: any) => void;
    data: TCromwellBlockData;
    name: keyof React.CSSProperties;
    label: string;
    className?: string;
    style?: React.CSSProperties;
    options?: string[];
}) {
    let val = props.data?.style?.[props.name];
    if (props.dataType === 'px') {
        if (val) val = parseFloat(val);
        if (isNaN(val)) val = undefined;
    }

    let noWrapper = true;
    let Field: React.ComponentType<any> = TextField;
    if (props.dataType === 'color') {
        Field = ColorPicker;
    }
    if (props.dataType === 'select') {
        Field = Select;
        noWrapper = false;
    }

    let content = (
        <Field
            onChange={e => {
                let value: any = e?.target?.value ?? e;
                if (props.dataType === 'px') {
                    if (value) value = parseFloat(value);
                    if (isNaN(value) || value === '' || value === null) value = undefined;
                    if (value !== undefined) value += 'px';
                }
                props.handleStyleChange(props.name, value);
            }}
            variant="standard"
            value={val ?? ''}
            type={props.dataType === 'px' ? 'number' : 'string'}
            label={noWrapper ? props.label : undefined}
            className={noWrapper ? props.className : undefined}
            style={noWrapper ? props.style : undefined}
        >{props.options?.map(opt => <MenuItem value={opt} key={opt}>{opt}</MenuItem>)}</Field>
    );

    if (props.dataType === 'select') {
        content = (
            <FormControl
                className={props.className}
                style={props.style}
            >
                <InputLabel>{props.label}</InputLabel>
                {content}
            </FormControl>
        )
    }

    return content;
}
Example #13
Source File: SettingsView.tsx    From react-flight-tracker with MIT License 4 votes vote down vote up
SettingsView: React.FC<Props> = (props) => {

  // Fields
  const contextName: string = ViewKeys.SettingsView;

  // Contexts
  const systemContext = useContext(SystemContext);
  const appContext = useContext(AppContext);

  const getSetting = (key: string, type: string) => {

    const value = systemContext.getSetting(key)
    if (typeof (value) === type)
      return value;

    return false;
  };

  const handleChange = (e: SelectChangeEvent) => {
    appContext.changeTheme(e.target.value);
  };

  const handleSettingsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    systemContext.storeSetting(e.target.name, e.target.checked);
  };

  const renderAppSettings = () => {

    return (
      <Card>

        <CardContent>

          <Typography
            variant={'h6'}
            gutterBottom={true}>
            {'App settings'}
          </Typography>

          <FormGroup>
            <FormControl
              color='secondary'
              variant="filled"
              sx={{ m: 1, minWidth: 120 }}>

              <InputLabel
                id="demo-simple-select-filled-label">
                Theme change
              </InputLabel>
              <Select
                labelId="demo-simple-select-filled-label"
                id="demo-simple-select-filled"
                value={appContext.activeThemeName}
                onChange={handleChange}>

                <MenuItem
                  value={ThemeKeys.DarkTheme}>
                  {ThemeKeys.DarkTheme}
                </MenuItem>
                <MenuItem
                  value={ThemeKeys.LightTheme}>
                  {ThemeKeys.LightTheme}
                </MenuItem>
                <MenuItem
                  value={ThemeKeys.PineappleTheme}>
                  {ThemeKeys.PineappleTheme}
                </MenuItem>
              </Select>
            </FormControl>

          </FormGroup>
        </CardContent>
      </Card>
    );
  };

  const renderMapSettings = () => {

    return (
      <Card>

        <CardContent>

          <Typography
            variant={'h6'}
            gutterBottom={true}>
            {'Map settings'}
          </Typography>

          <FormGroup>
            <FormControlLabel
              control={
                <Switch
                  color='secondary'
                  name={SettingKeys.ShowDataOverlayOnMap}
                  checked={getSetting(SettingKeys.ShowDataOverlayOnMap, 'boolean')}
                  onChange={handleSettingsChange} />
              }
              label="Show data overlay on map"
            />
            <FormControlLabel
              control={
                <Switch
                  color='secondary'
                  name={SettingKeys.ShowLogOverlayOnMap}
                  checked={getSetting(SettingKeys.ShowLogOverlayOnMap, 'boolean')}
                  onChange={handleSettingsChange} />
              }
              label="Show log overlay on map"
            />
          </FormGroup>
        </CardContent>
      </Card>
    );
  };

  return (

    <ViewContainer
      isScrollLocked={true}>

      {renderAppSettings()}

      <Box sx={{ height: (theme) => theme.spacing(1) }} />

      {renderMapSettings()}
    </ViewContainer>
  );
}
Example #14
Source File: select.tsx    From Search-Next with GNU General Public License v3.0 4 votes vote down vote up
Select: React.FC<SelectProps> = ({
  label,
  size = 'small',
  options,
  optionsConfig,
  helperText,
  error,
  inputRef,
  ...props
}) => {
  const theme = createTheme();

  return (
    <Box sx={{ minWidth: 120 }}>
      <FormControl fullWidth>
        <InputLabel
          id={`mui-select-label-${label}`}
          className={css`
            &.MuiFormLabel-root {
              transform: translate(14px, 9px) scale(1);
            }
            &.Mui-focused,
            &.MuiFormLabel-filled {
              transform: translate(14px, -9px) scale(0.75);
            }
          `}
        >
          {label}
        </InputLabel>
        <StyledSelect
          {...props}
          labelId={`mui-select-label-${label}`}
          id={`mui-select-${label}`}
          inputRef={inputRef}
          label={label}
          size={size}
          error={error}
          onClose={(e) => e.stopPropagation()}
          MenuProps={{
            sx: {
              '& .MuiPaper-root': {
                backgroundColor: 'rgba(253, 253, 253, 0.8)',
                backdropFilter: 'blur(8px)',
                borderRadius: '4px',
                marginTop: theme.spacing(1),
                minWidth: 140,
                color:
                  theme.palette.mode === 'light'
                    ? 'rgb(55, 65, 81)'
                    : theme.palette.grey[300],
                boxShadow:
                  'rgb(255, 255, 255) 0px 0px 0px 0px, rgba(0, 0, 0, 0.05) 0px 0px 0px 1px, rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px',
                '& .MuiMenu-list': {
                  padding: '0 4px',
                },
                '& .MuiMenuItem-root': {
                  borderRadius: '4px',
                  padding: '4px 8px',
                  margin: '4px 0',
                  fontSize: '14px',
                  transition: 'all 0.3s',
                  '& .MuiSvgIcon-root': {
                    fontSize: '14px',
                  },
                  '&:active': {
                    backgroundColor: alpha(
                      theme.palette.primary.main,
                      theme.palette.action.selectedOpacity,
                    ),
                  },
                },
              },
            },
          }}
        >
          {options.map((i) => {
            const label =
              optionsConfig && optionsConfig.label
                ? i[optionsConfig.label]
                : i.label;
            const value =
              optionsConfig && optionsConfig.value
                ? i[optionsConfig.value]
                : i.value;
            return (
              <MenuItem key={value} value={value}>
                {label}
              </MenuItem>
            );
          })}
        </StyledSelect>
        <FormHelperText error={error}>{helperText}</FormHelperText>
      </FormControl>
    </Box>
  );
}
Example #15
Source File: RegionSelectWrapper.tsx    From console with GNU Affero General Public License v3.0 4 votes vote down vote up
RegionSelectWrapper = ({
  label,
  onChange,
  id,
  name,
  type,
  tooltip = "",
  index = 0,
  error = "",
  required = false,
  overlayId,
  overlayIcon = null,
  overlayObject = null,
  extraInputProps = {},
  overlayAction,
  noLabelMinWidth = false,
  classes,
  className = "",
}: RegionSelectBoxProps) => {
  const inputClasses = inputStyles();

  let inputProps: any = {
    "data-index": index,
    ...extraInputProps,
    name: name,
    id: id,
    classes: inputClasses,
  };

  return (
    <React.Fragment>
      <Grid
        container
        className={clsx(
          className !== "" ? className : "",
          error !== "" ? classes.errorInField : classes.inputBoxContainer
        )}
      >
        {label !== "" && (
          <InputLabel
            htmlFor={id}
            className={
              noLabelMinWidth ? classes.noMinWidthLabel : classes.inputLabel
            }
          >
            <span>
              {label}
              {required ? "*" : ""}
            </span>
            {tooltip !== "" && (
              <div className={classes.tooltipContainer}>
                <Tooltip title={tooltip} placement="top-start">
                  <div className={classes.tooltip}>
                    <HelpIcon />
                  </div>
                </Tooltip>
              </div>
            )}
          </InputLabel>
        )}

        <div className={classes.textBoxContainer}>
          <RegionSelect
            type={type}
            inputProps={inputProps}
            onChange={onChange}
          />
          {overlayIcon && (
            <div
              className={`${classes.overlayAction} ${
                label !== "" ? "withLabel" : ""
              }`}
            >
              <IconButton
                onClick={
                  overlayAction
                    ? () => {
                        overlayAction();
                      }
                    : () => null
                }
                id={overlayId}
                size={"small"}
                disableFocusRipple={false}
                disableRipple={false}
                disableTouchRipple={false}
              >
                {overlayIcon}
              </IconButton>
            </div>
          )}
          {overlayObject && (
            <div
              className={`${classes.overlayAction} ${
                label !== "" ? "withLabel" : ""
              }`}
            >
              {overlayObject}
            </div>
          )}
        </div>
      </Grid>
    </React.Fragment>
  );
}
Example #16
Source File: ItemViewer.tsx    From NekoMaid with MIT License 4 votes vote down vote up
ItemEditor: React.FC = () => {
  const plugin = usePlugin()
  const theme = useTheme()
  const [item, setItem] = useState<Item | undefined>()
  const [types, setTypes] = useState<string[]>([])
  const [tab, setTab] = useState(0)
  const [level, setLevel] = useState(1)
  const [enchantment, setEnchantment] = useState<string | undefined>()
  const [nbtText, setNBTText] = useState('')
  const nbt: NBT = item?.nbt ? parse(item.nbt) : { id: 'minecraft:' + (item?.type || 'air').toLowerCase(), Count: new Byte(1) } as any
  useEffect(() => {
    if (!item || types.length) return
    plugin.emit('item:fetch', (a: string[], b: string[]) => {
      setTypes(a)
      enchantments = b
    })
  }, [item])
  useEffect(() => {
    _setItem = (it: any) => {
      setItem(it)
      setNBTText(it.nbt ? stringify(parse(it.nbt), { pretty: true }) : '')
    }
    return () => { _setItem = null }
  }, [])
  const cancel = () => {
    setItem(undefined)
    if (_resolve) {
      _resolve(false)
      _resolve = null
    }
  }
  const update = () => {
    const newItem: any = { ...item }
    if (nbt) {
      newItem.nbt = stringify(nbt as any)
      setNBTText(stringify(nbt, { pretty: true }))
    }
    setItem(newItem)
  }
  const isAir = item?.type === 'AIR'
  const name = nbt?.tag?.display?.Name
  const enchantmentMap: Record<string, true> = { }
  return <Dialog open={!!item} onClose={cancel}>
    <DialogTitle>{lang.itemEditor.title}</DialogTitle>
    <DialogContent sx={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center' }}>
      {item && <Box sx={{ display: 'flex', width: '100%', justifyContent: 'center' }}>
        <ItemViewer item={item} />
        <Autocomplete
          options={types}
          sx={{ maxWidth: 300, marginLeft: 1, flexGrow: 1 }}
          value={item?.type}
          onChange={(_, it) => {
            item.type = it || 'AIR'
            if (nbt) nbt.id = 'minecraft:' + (it ? it.toLowerCase() : 'air')
            update()
          }}
          getOptionLabel={it => {
            const locatedName = getName(it.toLowerCase())
            return (locatedName ? locatedName + ' ' : '') + it
          }}
          renderInput={(params) => <TextField {...params} label={lang.itemEditor.itemType} size='small' variant='standard' />}
        />
      </Box>}
      <Tabs centered value={tab} onChange={(_, it) => setTab(it)} sx={{ marginBottom: 2 }}>
        <Tab label={lang.itemEditor.baseAttribute} disabled={isAir} />
        <Tab label={minecraft['container.enchant']} disabled={isAir} />
        <Tab label='NBT' disabled={isAir} />
      </Tabs>
      {nbt && tab === 0 && <Grid container spacing={1} rowSpacing={1}>
        <Grid item xs={12} md={6}><TextField
          fullWidth
          label={lang.itemEditor.count}
          type='number'
          variant='standard'
          value={nbt.Count}
          disabled={isAir}
          onChange={e => {
            nbt.Count = new Byte(item!.amount = parseInt(e.target.value))
            update()
          }}
        /></Grid>
        <Grid item xs={12} md={6}><TextField
          fullWidth
          label={lang.itemEditor.damage}
          type='number'
          variant='standard'
          value={nbt.tag?.Damage}
          disabled={isAir}
          onChange={e => {
            set(nbt, 'tag.Damage', parseInt(e.target.value))
            update()
          }}
        /></Grid>
        <Grid item xs={12} md={6}>
          <TextField
            fullWidth
            label={lang.itemEditor.displayName}
            variant='standard'
            disabled={isAir}
            value={name ? stringifyTextComponent(JSON.parse(name)) : ''}
            onChange={e => {
              set(nbt, 'tag.display.Name', JSON.stringify(item!.name = e.target.value))
              update()
            }}
          />
          <FormControlLabel
            label={minecraft['item.unbreakable']}
            disabled={isAir}
            checked={nbt.tag?.Unbreakable?.value === 1}
            control={<Checkbox
              checked={nbt.tag?.Unbreakable?.value === 1}
              onChange={e => {
                set(nbt, 'tag.Unbreakable', new Byte(+e.target.checked))
                update()
              }} />
            }
          />
        </Grid>
        <Grid item xs={12} md={6}><TextField
          fullWidth
          multiline
          label={lang.itemEditor.lore}
          variant='standard'
          maxRows={5}
          disabled={isAir}
          value={nbt.tag?.display?.Lore?.map(l => stringifyTextComponent(JSON.parse(l)))?.join('\n') || ''}
          onChange={e => {
            set(nbt, 'tag.display.Lore', e.target.value.split('\n').map(text => JSON.stringify(text)))
            update()
          }}
        /></Grid>
      </Grid>}
      {nbt && tab === 1 && <Grid container spacing={1} sx={{ width: '100%' }}>
        {nbt.tag?.Enchantments?.map((it, i) => {
          enchantmentMap[it.id] = true
          return <Grid item key={i}><Chip label={getEnchantmentName(it)} onDelete={() => {
            nbt?.tag?.Enchantments?.splice(i, 1)
            update()
          }} /></Grid>
        })}
        <Grid item><Chip label={lang.itemEditor.newEnchantment} color='primary' onClick={() => {
          setEnchantment('')
          setLevel(1)
        }} /></Grid>
        <Dialog onClose={() => setEnchantment(undefined)} open={enchantment != null}>
          <DialogTitle>{lang.itemEditor.newEnchantmentTitle}</DialogTitle>
          <DialogContent>
            <Box component='form' sx={{ display: 'flex', flexWrap: 'wrap' }}>
              <FormControl variant='standard' sx={{ m: 1, minWidth: 120 }}>
                <InputLabel htmlFor='item-editor-enchantment-selector'>{minecraft['container.enchant']}</InputLabel>
                <Select
                  id='item-editor-enchantment-selector'
                  label={minecraft['container.enchant']}
                  value={enchantment || ''}
                  onChange={e => setEnchantment(e.target.value)}
                >{enchantments
                  .filter(e => !(e in enchantmentMap))
                  .map(it => <MenuItem key={it} value={it}>{getEnchantmentName(it)}</MenuItem>)}
                </Select>
              </FormControl>
              <FormControl sx={{ m: 1, minWidth: 120 }}>
                <TextField
                  label={lang.itemEditor.level}
                  type='number'
                  variant='standard'
                  value={level}
                  onChange={e => setLevel(parseInt(e.target.value))}
                />
              </FormControl>
            </Box>
          </DialogContent>
          <DialogActions>
            <Button onClick={() => setEnchantment(undefined)}>{minecraft['gui.cancel']}</Button>
            <Button disabled={!enchantment || isNaN(level)} onClick={() => {
              if (nbt) {
                if (!nbt.tag) nbt.tag = { Damage: new Int(0) }
                ;(nbt.tag.Enchantments || (nbt.tag.Enchantments = [])).push({ id: enchantment!, lvl: new Short(level) })
              }
              setEnchantment(undefined)
              update()
            }}>{minecraft['gui.ok']}</Button>
          </DialogActions>
        </Dialog>
      </Grid>}
    </DialogContent>
    {nbt && tab === 2 && <Box sx={{
      '& .CodeMirror': { width: '100%' },
      '& .CodeMirror-dialog, .CodeMirror-scrollbar-filler': { backgroundColor: theme.palette.background.paper + '!important' }
    }}>
      <UnControlled
        value={nbtText}
        options={{
          mode: 'javascript',
          phrases: lang.codeMirrorPhrases,
          theme: theme.palette.mode === 'dark' ? 'material' : 'one-light'
        }}
        onChange={(_: any, __: any, nbt: string) => {
          const n = parse(nbt) as any as NBT
          const newItem: any = { ...item, nbt }
          if (n.Count?.value != null) newItem.amount = n.Count.value
          setItem(newItem)
        }}
      />
    </Box>}
    <DialogActions>
      <Button onClick={cancel}>{minecraft['gui.cancel']}</Button>
      <Button onClick={() => {
        setItem(undefined)
        if (_resolve) {
          _resolve(!item || item.type === 'AIR' ? null : item)
          _resolve = null
        }
      }}>{minecraft['gui.ok']}</Button>
    </DialogActions>
  </Dialog>
}
Example #17
Source File: Withdraw.tsx    From wrap.scrt.network with MIT License 4 votes vote down vote up
export default function Withdraw({
  token,
  secretjs,
  secretAddress,
  balances,
  onSuccess,
  onFailure,
}: {
  token: Token;
  secretjs: SecretNetworkClient | null;
  secretAddress: string;
  balances: Map<string, string>;
  onSuccess: (txhash: string) => any;
  onFailure: (error: any) => any;
}) {
  const [targetAddress, setTargetAddress] = useState<string>("");
  const [loadingTx, setLoading] = useState<boolean>(false);
  const [selectedChainIndex, setSelectedChainIndex] = useState<number>(0);
  const inputRef = useRef<any>();
  const maxButtonRef = useRef<any>();

  const sourceChain = chains["Secret Network"];
  const targetChain =
    chains[token.withdrawals[selectedChainIndex].target_chain_name];

  const availableBalance =
    balances.get(token.withdrawals[selectedChainIndex].from_denom) || "";

  useEffect(() => {
    (async () => {
      while (!window.keplr || !window.getOfflineSignerOnlyAmino) {
        await sleep(100);
      }

      // Find address on target chain
      const { chain_id: targetChainId } =
        chains[token.withdrawals[selectedChainIndex].target_chain_name];
      if (token.withdrawals[selectedChainIndex].target_chain_name === "Terra") {
        await suggestTerraToKeplr(window.keplr);
      }
      await window.keplr.enable(targetChainId);
      const targetOfflineSigner =
        window.getOfflineSignerOnlyAmino(targetChainId);
      const targetFromAccounts = await targetOfflineSigner.getAccounts();
      setTargetAddress(targetFromAccounts[0].address);
    })();
  }, [selectedChainIndex]);

  return (
    <>
      <div style={{ padding: "1.5em" }}>
        <div
          style={{
            display: "flex",
            placeItems: "center",
            gap: "0.5em",
          }}
        >
          <Typography>
            Withdraw <strong>{token.name}</strong> from{" "}
            <strong>Secret Network</strong> to
          </Typography>
          <If condition={token.withdrawals.length === 1}>
            <Then>
              <Typography sx={{ marginLeft: "-0.2em" }}>
                <strong>
                  {token.withdrawals[selectedChainIndex].target_chain_name}
                </strong>
              </Typography>
            </Then>
            <Else>
              <FormControl>
                <Select
                  value={selectedChainIndex}
                  onChange={(e) =>
                    setSelectedChainIndex(Number(e.target.value))
                  }
                >
                  {token.withdrawals.map((chain, index) => (
                    <MenuItem value={index} key={index}>
                      <div
                        style={{
                          display: "flex",
                          gap: "0.5em",
                          placeItems: "center",
                        }}
                      >
                        <Avatar
                          src={chains[chain.target_chain_name].chain_image}
                          sx={{
                            marginLeft: "0.3em",
                            width: "1em",
                            height: "1em",
                            boxShadow: "rgba(0, 0, 0, 0.15) 0px 6px 10px",
                          }}
                        />
                        <strong>{chain.target_chain_name}</strong>
                      </div>
                    </MenuItem>
                  ))}
                </Select>
              </FormControl>
            </Else>
          </If>
        </div>
        <br />
        <div
          style={{
            display: "flex",
            placeContent: "space-between",
            placeItems: "center",
            gap: "1em",
          }}
        >
          <Typography sx={{ fontWeight: "bold" }}>From:</Typography>
          <CopyableAddress
            address={secretAddress}
            explorerPrefix={sourceChain.explorer_account}
          />
        </div>
        <div
          style={{
            display: "flex",
            placeContent: "space-between",
            placeItems: "center",
            gap: "1em",
          }}
        >
          <Typography sx={{ fontWeight: "bold" }}>To:</Typography>
          <CopyableAddress
            address={targetAddress}
            explorerPrefix={targetChain.explorer_account}
          />
        </div>
        <br />
        <div
          style={{
            display: "flex",
            placeItems: "center",
            gap: "0.3em",
            marginBottom: "0.8em",
          }}
        >
          <Typography sx={{ fontSize: "0.8em", fontWeight: "bold" }}>
            Available to Withdraw:
          </Typography>
          <Typography
            sx={{
              fontSize: "0.8em",
              opacity: 0.8,
              cursor: "pointer",
            }}
            onClick={() => {
              maxButtonRef.current.click();
            }}
          >
            {(() => {
              if (availableBalance === "") {
                return <CircularProgress size="0.6em" />;
              }

              const prettyBalance = new BigNumber(availableBalance)
                .dividedBy(`1e${token.decimals}`)
                .toFormat();

              if (prettyBalance === "NaN") {
                return "Error";
              }

              return `${prettyBalance} ${token.name}`;
            })()}
          </Typography>
        </div>
        <FormControl sx={{ width: "100%" }} variant="standard">
          <InputLabel htmlFor="Amount to Withdraw">
            Amount to Withdraw
          </InputLabel>
          <Input
            autoFocus
            id="Amount to Withdraw"
            fullWidth
            type="text"
            inputRef={inputRef}
            startAdornment={
              <InputAdornment position="start">
                <Avatar
                  src={token.image}
                  sx={{
                    width: "1em",
                    height: "1em",
                    boxShadow: "rgba(0, 0, 0, 0.15) 0px 6px 10px",
                  }}
                />
              </InputAdornment>
            }
            endAdornment={
              <InputAdornment position="end">
                <Button
                  ref={maxButtonRef}
                  style={{
                    padding: "0.1em 0.5em",
                    minWidth: 0,
                  }}
                  onClick={() => {
                    if (availableBalance === "") {
                      return;
                    }

                    const prettyBalance = new BigNumber(availableBalance)
                      .dividedBy(`1e${token.decimals}`)
                      .toFormat();

                    if (prettyBalance === "NaN") {
                      return;
                    }

                    inputRef.current.value = prettyBalance;
                  }}
                >
                  MAX
                </Button>
              </InputAdornment>
            }
          />
        </FormControl>
      </div>
      <div
        style={{
          display: "flex",
          placeContent: "center",
          marginBottom: "0.4em",
        }}
      >
        <LoadingButton
          variant="contained"
          sx={{
            padding: "0.5em 0",
            width: "10em",
            fontWeight: "bold",
            fontSize: "1.2em",
          }}
          loading={loadingTx}
          onClick={async () => {
            if (!secretjs) {
              console.error("No secretjs");
              return;
            }

            if (!inputRef?.current?.value) {
              console.error("Empty withdraw");
              return;
            }

            const normalizedAmount = (inputRef.current.value as string).replace(
              /,/g,
              ""
            );

            if (!(Number(normalizedAmount) > 0)) {
              console.error(`${normalizedAmount} not bigger than 0`);
              return;
            }

            setLoading(true);

            const amount = new BigNumber(normalizedAmount)
              .multipliedBy(`1e${token.decimals}`)
              .toFixed(0, BigNumber.ROUND_DOWN);

            const { withdraw_channel_id, withdraw_gas } =
              chains[token.withdrawals[selectedChainIndex].target_chain_name];
            try {
              const tx = await secretjs.tx.broadcast(
                [
                  new MsgTransfer({
                    sender: secretAddress,
                    receiver: targetAddress,
                    sourceChannel: withdraw_channel_id,
                    sourcePort: "transfer",
                    token: {
                      amount,
                      denom: token.withdrawals[selectedChainIndex].from_denom,
                    },
                    timeoutTimestampSec: String(
                      Math.floor(Date.now() / 1000) + 15 * 60
                    ), // 15 minute timeout
                  }),
                ],
                {
                  gasLimit: withdraw_gas,
                  gasPriceInFeeDenom: 0.25,
                  feeDenom: "uscrt",
                }
              );

              if (tx.code === 0) {
                inputRef.current.value = "";
                onSuccess(tx.transactionHash);
              } else {
                onFailure(tx.rawLog);
              }
            } catch (e) {
              onFailure(e);
            } finally {
              setLoading(false);
            }
          }}
        >
          Withdraw
        </LoadingButton>
      </div>
    </>
  );
}
Example #18
Source File: Form.tsx    From frontend with MIT License 4 votes vote down vote up
export default function Form() {
  const router = useRouter()
  const { t } = useTranslation()
  let id = router.query.id
  const [beneficiaryType, setBeneficiaryType] = useState<string>('')
  const [personId, setPersonId] = useState<string>('')
  const [companyId, setCompanyId] = useState<string>('')
  const [coordinatorId, setCoordinatorId] = useState<string>('')
  const [cityId, setCityId] = useState<string>('')
  const [countryCode, setCountryCode] = useState<string>('')
  const people = usePeopleList().data
  const companies = useCompaniesList().data
  const coordinators = useCoordinatorsList().data
  const coordinatorRelations = Object.values(PersonRelation)
  const cities = useCitiesList().data
  const countries = useCountriesList().data

  let initialValues: BeneficiaryFormData = {
    type: beneficiaryType,
    personId: personId,
    companyId: companyId,
    coordinatorId: coordinatorId,
    countryCode: countryCode,
    cityId: cityId,
    description: '',
    publicData: '',
    privateData: '',
    coordinatorRelation: 'none',
    campaigns: [],
  }

  if (id) {
    id = String(id)
    const { data }: UseQueryResult<BeneficiaryListResponse> = useViewBeneficiary(id)
    initialValues = {
      type: data?.type,
      cityId: data?.cityId || '',
      companyId: data?.companyId || '',
      coordinatorId: data?.coordinatorId || '',
      countryCode: data?.countryCode || '',
      description: data?.description || '',
      personId: data?.personId || '',
      privateData: data?.privateData || '',
      publicData: data?.publicData || '',
      coordinatorRelation: data?.coordinatorRelation || '',
      campaigns: data?.campaigns || [],
    }
  }
  const mutationFn = id ? useEditBeneficiary(id) : useCreateBeneficiary()

  const mutation = useMutation<
    AxiosResponse<BeneficiaryListResponse>,
    AxiosError<ApiErrors>,
    BeneficiaryFormData
  >({
    mutationFn,
    onError: () => AlertStore.show(t('documents:alerts:error'), 'error'),
    onSuccess: () => {
      AlertStore.show(id ? t('documents:alerts:edit') : t('documents:alerts:create'), 'success')
      router.push(routes.admin.beneficiary.index)
    },
  })

  async function onSubmit(data: BeneficiaryFormData) {
    mutation.mutateAsync(data)
  }

  return (
    <GenericForm
      onSubmit={onSubmit}
      initialValues={initialValues}
      validationSchema={validationSchema}>
      <Box sx={{ height: '62.6vh', marginBottom: '9%' }}>
        <Typography variant="h5" component="h2" sx={{ textAlign: 'center' }}>
          {id ? t('beneficiary:forms:edit-heading') : t('beneficiary:forms:add-heading')}
        </Typography>
        <Grid container spacing={2}>
          <Grid item xs={12}>
            <InputLabel>{t('beneficiary:grid:type')}</InputLabel>
            <Select
              fullWidth
              sx={{ height: '55%' }}
              name="type"
              defaultValue={initialValues.type}
              onChange={(e: SelectChangeEvent) => {
                setBeneficiaryType(e.target.value)
              }}>
              <MenuItem value="">
                <em>None</em>
              </MenuItem>
              {Object.values(LegalEntityType)?.map((type) => {
                return (
                  <MenuItem key={type} value={type}>
                    {t('beneficiary:grid:' + type)}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={6}>
            <InputLabel>{t('beneficiary:grid:individual')}</InputLabel>
            <Select
              fullWidth
              sx={{ height: '55%' }}
              name="personId"
              defaultValue={initialValues.personId}
              onChange={(e: SelectChangeEvent) => {
                setPersonId(e.target.value)
              }}>
              <MenuItem value="">
                <em>None</em>
              </MenuItem>
              {people?.map((person) => {
                return (
                  <MenuItem key={person.id} value={person.id}>
                    {person.firstName + ' ' + person.lastName}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={6}>
            <InputLabel>{t('beneficiary:grid:company')}</InputLabel>
            <Select
              fullWidth
              sx={{ height: '55%' }}
              name="companyId"
              defaultValue={initialValues.personId}
              onChange={(e: SelectChangeEvent) => {
                setCompanyId(e.target.value)
              }}>
              <MenuItem value="">
                <em>None</em>
              </MenuItem>
              {companies?.map((company) => {
                return (
                  <MenuItem key={company.id} value={company.id}>
                    {company.companyName}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={6}>
            <InputLabel>{t('beneficiary:grid:coordinatorRelation')}</InputLabel>
            <Select
              fullWidth
              sx={{ height: '55%' }}
              name="coordinatorRelation"
              defaultValue={initialValues.coordinatorRelation}
              onChange={(e: SelectChangeEvent) => {
                setPersonId(e.target.value)
              }}>
              <MenuItem value="">
                <em>None</em>
              </MenuItem>
              {coordinatorRelations?.map((relation) => {
                return (
                  <MenuItem key={relation} value={relation}>
                    {relation}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={6}>
            <InputLabel>{t('beneficiary:grid:coordinator')}</InputLabel>
            <Select
              fullWidth
              sx={{ height: '55%' }}
              name="coordinatorId"
              defaultValue={initialValues.coordinatorId}
              onChange={(e: SelectChangeEvent) => {
                setCoordinatorId(e.target.value)
              }}>
              <MenuItem value="">
                <em>None</em>
              </MenuItem>
              {coordinators?.map((coordinator) => {
                return (
                  <MenuItem key={coordinator.id} value={coordinator.id}>
                    {coordinator.person.firstName} {coordinator.person.lastName}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={6}>
            <InputLabel>{t('beneficiary:grid:countryCode')}</InputLabel>
            <Select
              fullWidth
              sx={{ height: '55%' }}
              name="countryCode"
              defaultValue={initialValues.countryCode}
              onChange={(e: SelectChangeEvent) => {
                setCountryCode(e.target.value)
              }}>
              <MenuItem value="">
                <em>None</em>
              </MenuItem>
              {countries?.map((country) => {
                return (
                  <MenuItem key={country.id} value={country.countryCode}>
                    {country.countryCode} - {country.name}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={6}>
            <InputLabel>{t('beneficiary:grid:city')}</InputLabel>
            <Select
              fullWidth
              sx={{ height: '55%' }}
              name="cityId"
              defaultValue={initialValues.cityId}
              onChange={(e: SelectChangeEvent) => {
                setCityId(e.target.value)
              }}>
              <MenuItem value="">
                <em>None</em>
              </MenuItem>
              {cities?.map((city) => {
                return (
                  <MenuItem key={city.id} value={city.id}>
                    {city.name}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={6}>
            <FormTextField
              type="text"
              name="description"
              autoComplete="target-amount"
              label={t('beneficiary:grid:description')}
              multiline
              rows={1.5}
              defaultValue={initialValues.description}
            />
          </Grid>
          <Grid item xs={6}>
            <SubmitButton fullWidth label={t('documents:cta:submit')} />
          </Grid>
          <Grid item xs={6}>
            <Link href={routes.admin.beneficiary.index} passHref>
              <Button>{t('documents:cta:cancel')}</Button>
            </Link>
          </Grid>
        </Grid>
      </Box>
    </GenericForm>
  )
}
Example #19
Source File: Deposit.tsx    From wrap.scrt.network with MIT License 4 votes vote down vote up
export default function Deposit({
  token,
  secretAddress,
  onSuccess,
  onFailure,
}: {
  token: Token;
  secretAddress: string;
  onSuccess: (txhash: string) => any;
  onFailure: (error: any) => any;
}) {
  const [sourceAddress, setSourceAddress] = useState<string>("");
  const [availableBalance, setAvailableBalance] = useState<string>("");
  const [loadingTx, setLoading] = useState<boolean>(false);
  const [sourceCosmJs, setSourceCosmJs] =
    useState<SigningStargateClient | null>(null);
  const [selectedChainIndex, setSelectedChainIndex] = useState<number>(0);
  const [fetchBalanceInterval, setFetchBalanceInterval] = useState<any>(null);
  const inputRef = useRef<any>();
  const maxButtonRef = useRef<any>();

  const sourceChain =
    chains[token.deposits[selectedChainIndex].source_chain_name];
  const targetChain = chains["Secret Network"];

  const fetchSourceBalance = async (sourceAddress: string) => {
    const url = `${
      chains[token.deposits[selectedChainIndex].source_chain_name].lcd
    }/bank/balances/${sourceAddress}`;
    try {
      const response = await fetch(url);
      const result: {
        height: string;
        result: Array<{ denom: string; amount: string }>;
      } = await response.json();

      const balance =
        result.result.find(
          (c) => c.denom === token.deposits[selectedChainIndex].from_denom
        )?.amount || "0";

      setAvailableBalance(balance);
    } catch (e) {
      console.error(`Error while trying to query ${url}:`, e);
      setAvailableBalance("Error");
    }
  };

  useEffect(() => {
    setAvailableBalance("");

    if (!sourceAddress) {
      return;
    }

    if (fetchBalanceInterval) {
      clearInterval(fetchBalanceInterval);
    }

    fetchSourceBalance(sourceAddress);
    const interval = setInterval(
      () => fetchSourceBalance(sourceAddress),
      10_000
    );
    setFetchBalanceInterval(interval);

    return () => clearInterval(interval);
  }, [sourceAddress]);

  useEffect(() => {
    (async () => {
      while (!window.keplr || !window.getOfflineSignerOnlyAmino) {
        await sleep(100);
      }

      if (["LUNA", "UST"].includes(token.name.toUpperCase())) {
        await suggestTerraToKeplr(window.keplr);
      }
      // Initialize cosmjs on the target chain, because it has sendIbcTokens()
      const { chain_id, rpc, bech32_prefix } =
        chains[token.deposits[selectedChainIndex].source_chain_name];
      await window.keplr.enable(chain_id);
      const sourceOfflineSigner = window.getOfflineSignerOnlyAmino(chain_id);
      const depositFromAccounts = await sourceOfflineSigner.getAccounts();
      setSourceAddress(depositFromAccounts[0].address);
      const cosmjs = await SigningStargateClient.connectWithSigner(
        rpc,
        sourceOfflineSigner,
        { prefix: bech32_prefix, broadcastPollIntervalMs: 10_000 }
      );
      setSourceCosmJs(cosmjs);
    })();
  }, [selectedChainIndex]);

  return (
    <>
      <div style={{ padding: "1.5em" }}>
        <div
          style={{
            display: "flex",
            placeItems: "center",
            gap: token.deposits.length === 1 ? "0.3em" : "0.5em",
          }}
        >
          <Typography>
            Deposit <strong>{token.name}</strong> from
          </Typography>
          <If condition={token.deposits.length === 1}>
            <Then>
              <Typography>
                <strong>
                  {token.deposits[selectedChainIndex].source_chain_name}
                </strong>
              </Typography>
            </Then>
            <Else>
              <FormControl>
                <Select
                  value={selectedChainIndex}
                  onChange={(e) =>
                    setSelectedChainIndex(Number(e.target.value))
                  }
                >
                  {token.deposits.map((chain, index) => (
                    <MenuItem value={index} key={index}>
                      <div
                        style={{
                          display: "flex",
                          gap: "0.5em",
                          placeItems: "center",
                        }}
                      >
                        <Avatar
                          src={chains[chain.source_chain_name].chain_image}
                          sx={{
                            marginLeft: "0.3em",
                            width: "1em",
                            height: "1em",
                            boxShadow: "rgba(0, 0, 0, 0.15) 0px 6px 10px",
                          }}
                        />
                        <strong>{chain.source_chain_name}</strong>
                      </div>
                    </MenuItem>
                  ))}
                </Select>
              </FormControl>
            </Else>
          </If>
          <Typography>
            to <strong>Secret Network</strong>
          </Typography>
        </div>
        <br />
        <div
          style={{
            display: "flex",
            placeContent: "space-between",
            placeItems: "center",
            gap: "1em",
          }}
        >
          <Typography sx={{ fontWeight: "bold" }}>From:</Typography>
          <CopyableAddress
            address={sourceAddress}
            explorerPrefix={sourceChain.explorer_account}
          />
        </div>
        <div
          style={{
            display: "flex",
            placeContent: "space-between",
            placeItems: "center",
            gap: "1em",
          }}
        >
          <Typography sx={{ fontWeight: "bold" }}>To:</Typography>
          <CopyableAddress
            address={secretAddress}
            explorerPrefix={targetChain.explorer_account}
          />
        </div>
        <br />
        <div
          style={{
            display: "flex",
            placeItems: "center",
            gap: "0.3em",
            marginBottom: "0.8em",
          }}
        >
          <Typography sx={{ fontSize: "0.8em", fontWeight: "bold" }}>
            Available to Deposit:
          </Typography>
          <Typography
            sx={{
              fontSize: "0.8em",
              opacity: 0.8,
              cursor: "pointer",
            }}
            onClick={() => {
              maxButtonRef.current.click();
            }}
          >
            {(() => {
              if (availableBalance === "") {
                return <CircularProgress size="0.6em" />;
              }

              const prettyBalance = new BigNumber(availableBalance)
                .dividedBy(`1e${token.decimals}`)
                .toFormat();

              if (prettyBalance === "NaN") {
                return "Error";
              }

              return `${prettyBalance} ${token.name}`;
            })()}
          </Typography>
        </div>
        <FormControl sx={{ width: "100%" }} variant="standard">
          <InputLabel htmlFor="Amount to Deposit">Amount to Deposit</InputLabel>
          <Input
            autoFocus
            id="Amount to Deposit"
            fullWidth
            type="text"
            inputRef={inputRef}
            startAdornment={
              <InputAdornment position="start">
                <Avatar
                  src={token.image}
                  sx={{
                    width: "1em",
                    height: "1em",
                    boxShadow: "rgba(0, 0, 0, 0.15) 0px 6px 10px",
                  }}
                />
              </InputAdornment>
            }
            endAdornment={
              <InputAdornment position="end">
                <Button
                  ref={maxButtonRef}
                  style={{
                    padding: "0.1em 0.5em",
                    minWidth: 0,
                  }}
                  onClick={() => {
                    if (availableBalance === "") {
                      return;
                    }

                    const prettyBalance = new BigNumber(availableBalance)
                      .dividedBy(`1e${token.decimals}`)
                      .toFormat();

                    if (prettyBalance === "NaN") {
                      return;
                    }

                    inputRef.current.value = prettyBalance;
                  }}
                >
                  MAX
                </Button>
              </InputAdornment>
            }
          />
        </FormControl>
      </div>
      <div
        style={{
          display: "flex",
          placeContent: "center",
          marginBottom: "0.4em",
        }}
      >
        <LoadingButton
          variant="contained"
          sx={{
            padding: "0.5em 0",
            width: "10em",
            fontWeight: "bold",
            fontSize: "1.2em",
          }}
          loading={loadingTx}
          onClick={async () => {
            if (!sourceCosmJs) {
              console.error("No cosmjs");
              return;
            }

            if (!inputRef?.current?.value) {
              console.error("Empty deposit");
              return;
            }

            const normalizedAmount = (inputRef.current.value as string).replace(
              /,/g,
              ""
            );

            if (!(Number(normalizedAmount) > 0)) {
              console.error(`${normalizedAmount} not bigger than 0`);
              return;
            }

            setLoading(true);

            const amount = new BigNumber(normalizedAmount)
              .multipliedBy(`1e${token.decimals}`)
              .toFixed(0, BigNumber.ROUND_DOWN);

            const { deposit_channel_id, deposit_gas } =
              chains[token.deposits[selectedChainIndex].source_chain_name];
            try {
              const { transactionHash } = await sourceCosmJs.sendIbcTokens(
                sourceAddress,
                secretAddress,
                {
                  amount,
                  denom: token.deposits[selectedChainIndex].from_denom,
                },
                "transfer",
                deposit_channel_id,
                undefined,
                Math.floor(Date.now() / 1000) + 15 * 60, // 15 minute timeout
                gasToFee(deposit_gas)
              );
              inputRef.current.value = "";
              onSuccess(transactionHash);
            } catch (e) {
              onFailure(e);
            } finally {
              setLoading(false);
            }
          }}
        >
          Deposit
        </LoadingButton>
      </div>
    </>
  );
}
Example #20
Source File: PSBTDetail.tsx    From sapio-studio with Mozilla Public License 2.0 4 votes vote down vote up
export function PSBTDetail(props: IProps) {
    const psbt_selection_form = React.useRef<HTMLSelectElement>(null);
    const [psbt, setPSBT] = React.useState<Bitcoin.Psbt>(props.psbts[0]!);
    const [flash, setFlash] = React.useState<JSX.Element | null>(null);
    if (props.psbts.length === 0) return null;
    function show_flash(
        msg: string | JSX.Element,
        color: string,
        onclick?: () => void
    ) {
        const click = onclick ?? (() => null);
        const elt = (
            <h3 style={{ color: color }} onClick={click}>
                {msg}
            </h3>
        );
        setFlash(elt);
        setTimeout(() => setFlash(<div></div>), 2000);
    }
    const psbt_handler = new PSBTHandler(show_flash);

    const selectable_psbts = props.psbts.map((w, i) => (
        <MenuItem key={i} value={i}>
            {i} -- {w.toBase64().substr(0, 16)}...
        </MenuItem>
    ));
    const [idx, set_idx] = React.useState(0);
    React.useEffect(() => {
        if (idx < props.psbts.length && idx >= 0) {
            setPSBT(props.psbts[idx]!);
        }
    }, [idx, props.psbts]);
    // missing horizontal
    return (
        <div className="PSBTDetail">
            <InputLabel id="label-select-psbt">PSBT Selection</InputLabel>
            <Select
                labelId="label-select-psbt"
                label="PSBT Selection"
                variant="outlined"
                ref={psbt_selection_form}
                onChange={() => {
                    const idx: number =
                        parseInt(psbt_selection_form.current?.value ?? '0') ??
                        0;
                    set_idx(idx);
                }}
            >
                {selectable_psbts}
            </Select>
            {flash}
            <Hex
                className="txhex"
                value={psbt.toBase64()}
                label="Selected PSBT"
            ></Hex>
            <div className="PSBTActions">
                <Tooltip title="Save PSBT to Disk">
                    <IconButton
                        aria-label="save-psbt-disk"
                        onClick={() => psbt_handler.save_psbt(psbt.toBase64())}
                    >
                        <SaveIcon style={{ color: red[500] }} />
                    </IconButton>
                </Tooltip>
                <Tooltip title="Sign PSBT using Node">
                    <IconButton
                        aria-label="sign-psbt-node"
                        onClick={async () => {
                            const new_psbt = await psbt_handler.sign_psbt(
                                psbt.toBase64()
                            );
                            // TODO: Confirm this saves to model?
                            psbt.combine(new_psbt);
                            setPSBT(psbt);
                        }}
                    >
                        <VpnKeyIcon style={{ color: yellow[500] }} />
                    </IconButton>
                </Tooltip>
                <Tooltip title="Combine PSBT from File">
                    <IconButton
                        aria-label="combine-psbt-file"
                        onClick={async () => {
                            // TODO: Confirm this saves to model?
                            await psbt_handler.combine_psbt(psbt);
                            setPSBT(psbt);
                        }}
                    >
                        <MergeTypeIcon style={{ color: purple[500] }} />
                    </IconButton>
                </Tooltip>
                <Tooltip title="Finalize and Broadcast PSBT with Node">
                    <IconButton
                        aria-label="combine-psbt-file"
                        onClick={async () => {
                            await psbt_handler.finalize_psbt(psbt.toBase64());
                            setPSBT(psbt);
                        }}
                    >
                        <SendIcon style={{ color: orange[500] }} />
                    </IconButton>
                </Tooltip>
                <div></div>
            </div>
        </div>
    );
}
Example #21
Source File: EditForm.tsx    From frontend with MIT License 4 votes vote down vote up
export default function EditForm() {
  const queryClient = useQueryClient()
  const router = useRouter()
  const id = router.query.id
  const { data }: UseQueryResult<WithdrawalEditResponse> = useWithdrawal(String(id))
  const { data: bankAccounts } = useBankAccountsList()
  const { data: campaigns } = useCampaignList()
  const { data: personList } = usePersonList()
  const { data: vaults } = useVaultsList()

  const [bankAccountId, setBankAccountId] = useState(data?.bankAccountId)
  const [vaultId, setVaultId] = useState(data?.sourceVaultId)
  const [campaignId, setCampaignId] = useState(data?.sourceCampaignId)
  const [approvedById, setApprovedById] = useState(data?.approvedById)

  const { t } = useTranslation()

  const mutationFn = useEditWithdrawal(String(id))

  const initialValues: WithdrawalInput = {
    status: WithdrawalStatus.initial,
    currency: data?.currency,
    amount: data?.amount,
    reason: data?.reason,
    sourceVaultId: vaultId,
    sourceCampaignId: campaignId,
    bankAccountId: bankAccountId,
    documentId: '',
    approvedById: approvedById,
  }

  const mutation = useMutation<
    AxiosResponse<WithdrawalResponse>,
    AxiosError<ApiErrors>,
    WithdrawalInput
  >({
    mutationFn,
    onError: () => AlertStore.show(t('withdrawals:alerts:error'), 'error'),
    onSuccess: () => {
      queryClient.invalidateQueries(endpoints.withdrawals.getWithdrawal(String(id)).url)
      AlertStore.show(t('withdrawals:alerts:edit'), 'success')
      router.push(routes.admin.withdrawals.index)
    },
  })

  function handleSubmit(values: WithdrawalInput) {
    const data: WithdrawalInput = {
      status: WithdrawalStatus.initial,
      currency: values.currency,
      amount: values.amount,
      reason: values.reason,
      sourceVaultId: vaultId,
      sourceCampaignId: campaignId,
      bankAccountId: bankAccountId,
      documentId: 'ff89a831-34da-4b2d-91bc-742247efd9b8',
      approvedById: approvedById,
    }
    mutation.mutate(data)
  }

  return (
    <GenericForm
      onSubmit={handleSubmit}
      initialValues={initialValues}
      validationSchema={validationSchema}>
      <Box>
        <Typography variant="h5" component="h2" sx={{ marginBottom: 2, textAlign: 'center' }}>
          {t('withdrawals:form-heading')}
        </Typography>
        <Grid container spacing={2} sx={{ width: 600, margin: '0 auto' }}>
          <Grid item xs={12}>
            <FormTextField
              type="number"
              label="wihdrawals:Сума"
              name="amount"
              autoComplete="amount"
            />
          </Grid>
          <Grid item xs={12}>
            <FormTextField
              type="string"
              label="wihdrawals:Причина"
              name="reason"
              autoComplete="reason"
            />
          </Grid>
          <Grid item xs={12}>
            <CurrencySelect />
          </Grid>
          <Grid item xs={12}>
            <InputLabel htmlFor="my-input">Банков акаунт</InputLabel>
            <Select
              fullWidth
              id="bankAccountId"
              name="bankAccountId"
              value={bankAccountId}
              onChange={(event) => setBankAccountId(event.target.value)}>
              {bankAccounts?.map((acc) => {
                return (
                  <MenuItem key={acc.id} value={acc.id}>
                    {acc.accountHolderName}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={12}>
            <InputLabel htmlFor="my-input">Кампании</InputLabel>
            <Select
              fullWidth
              id="campaignId"
              name="campaignId"
              value={campaignId}
              onChange={(event) => setCampaignId(event.target.value)}>
              {campaigns?.map((camp) => {
                return (
                  <MenuItem key={camp.id} value={camp.id}>
                    {camp.title}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={12}>
            <InputLabel htmlFor="my-input">Трезор</InputLabel>
            <Select
              fullWidth
              id="vaultId"
              name="vaultId"
              value={vaultId}
              onChange={(event) => setVaultId(event.target.value)}>
              {vaults?.map((acc) => {
                return (
                  <MenuItem key={acc.id} value={acc.id}>
                    {acc.name}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={12}>
            <InputLabel htmlFor="my-input">Одобрен от</InputLabel>
            <Select
              fullWidth
              id="approvedById"
              name="approvedById"
              value={approvedById}
              onChange={(event) => setApprovedById(event.target.value)}>
              {personList?.map((acc) => {
                return (
                  <MenuItem key={acc.id} value={acc.id}>
                    {acc.firstName} {acc.lastName}
                  </MenuItem>
                )
              })}
            </Select>
          </Grid>
          <Grid item xs={6}>
            <SubmitButton fullWidth label={t('withdrawals:cta:submit')} />
          </Grid>
          <Grid item xs={6}>
            <Link href={routes.admin.withdrawals.index} passHref>
              <Button fullWidth={true}>{t('withdrawals:cta:cancel')}</Button>
            </Link>
          </Grid>
        </Grid>
      </Box>
    </GenericForm>
  )
}
Example #22
Source File: ArrayEnumSelect.tsx    From firecms with MIT License 4 votes vote down vote up
/**
 * This fields renders a dropdown with multiple selection.
 *
 * This is one of the internal components that get mapped natively inside forms
 * and tables to the specified properties.
 * @category Form fields
 */
export function ArrayEnumSelect({
                                    name,
                                    value,
                                    setValue,
                                    error,
                                    showError,
                                    disabled,
                                    property,
                                    includeDescription,
                                    autoFocus
                                }: FieldProps<EnumType[]>) {

    const classes = formStyles();

    if (!property.of) {
        throw Error("Using wrong component ArrayEnumSelect");
    }

    if (property.of.dataType !== "string" && property.of.dataType !== "number") {
        throw Error("Field misconfiguration: array field of type string or number");
    }

    const enumValues = property.of.config?.enumValues;
    if (!enumValues) {
        console.error(property);
        throw Error("Field misconfiguration: array field of type string or number needs to have enumValues");
    }
    useClearRestoreValue({
        property,
        value,
        setValue
    });

    const validValue = !!value && Array.isArray(value);
    return (
        <FormControl
            variant="filled"
            fullWidth
            required={property.validation?.required}
            error={showError}
        >

            <InputLabel id={`${name}-multiselect-label`}
                        classes={{
                            root: classes.inputLabel,
                            shrink: classes.shrinkInputLabel
                        }}>
                <LabelWithIcon property={property}/>
            </InputLabel>

            <MuiSelect
                multiple
                sx={{
                    minHeight: "64px"
                }}
                variant={"filled"}
                labelId={`${name}-multiselect-label`}
                value={validValue ? value.map(v => v.toString()) : []}
                autoFocus={autoFocus}
                disabled={disabled}
                onChange={(evt: any) => {
                    let newValue;
                    if (property.of?.dataType === "number")
                        newValue = evt.target.value ? evt.target.value.map((e: any) => parseFloat(e)) : [];
                    else
                        newValue = evt.target.value;
                    return setValue(
                        newValue
                    );
                }}
                renderValue={(selected: any) => (
                    <ArrayEnumPreview value={selected}
                                      name={name}
                                      enumValues={enumValues}
                                      size={"regular"}/>
                )}>

                {enumToObjectEntries(enumValues)
                    .map(([enumKey, labelOrConfig]) => {
                        const checked = validValue && value.map(v => v.toString()).includes(enumKey.toString());
                        return (
                            <MenuItem key={`form-select-${name}-${enumKey}`}
                                      value={enumKey}
                                      disabled={isEnumValueDisabled(labelOrConfig)}
                                      dense={true}>
                                <Checkbox checked={checked}/>
                                <ListItemText primary={
                                    <EnumValuesChip
                                        enumKey={enumKey}
                                        enumValues={enumValues}
                                        small={true}/>
                                }/>
                            </MenuItem>
                        );
                    })}
            </MuiSelect>

            {includeDescription &&
            <FieldDescription property={property}/>}

            {showError && <FormHelperText>{error}</FormHelperText>}

        </FormControl>
    );
}
Example #23
Source File: Form.tsx    From frontend with MIT License 4 votes vote down vote up
export default function EditForm() {
  const [type, setType] = useState('donation')
  const [status, setStatus] = useState('initial')
  const [provider, setProvider] = useState('none')
  const [currency, setCurrency] = useState('')
  const [vault, setVault] = useState('')
  const router = useRouter()
  const { t } = useTranslation()

  let id = router.query.id

  const vaults = useVaultsList().data

  let initialValues: DonationInput = {
    type: 'donation',
    status: 'initial',
    provider: 'none',
    currency: '',
    amount: 0,
    targetVaultId: '',
    extCustomerId: '',
    extPaymentIntentId: '',
    extPaymentMethodId: '',
  }

  if (id) {
    id = String(id)
    const { data }: UseQueryResult<DonationResponse> = useDonation(id)

    if (data) {
      initialValues = {
        type: data?.type.toString(),
        status: data?.status.toString(),
        provider: data?.provider.toString(),
        currency: data?.currency.toString(),
        amount: data?.amount,
        targetVaultId: data?.targetVaultId,
        extCustomerId: data?.extCustomerId,
        extPaymentIntentId: data?.extPaymentIntentId,
        extPaymentMethodId: data?.extPaymentMethodId,
      }
    }
  }

  const mutationFn = id ? useEditDonation(id) : useCreateDonation()

  const mutation = useMutation<
    AxiosResponse<DonationResponse>,
    AxiosError<ApiErrors>,
    DonationInput
  >({
    mutationFn,
    onError: () => AlertStore.show(t('donations:alerts:error'), 'error'),
    onSuccess: () => {
      AlertStore.show(id ? t('donations:alerts:edit') : t('donations:alerts:create'), 'success')
      router.push(routes.admin.donations.index)
    },
  })

  async function onSubmit(data: DonationInput) {
    type ? (data.type = type) : ''
    status ? (data.status = status) : ''
    provider ? (data.provider = provider) : ''
    currency ? (data.currency = currency) : ''
    vault ? (data.targetVaultId = vault) : ''

    mutation.mutate(data)
  }

  return (
    <GenericForm
      onSubmit={onSubmit}
      initialValues={initialValues}
      validationSchema={validationSchema}>
      <Box sx={{ marginTop: '5%', height: '62.6vh' }}>
        <Typography variant="h5" component="h2" sx={{ marginBottom: 2, textAlign: 'center' }}>
          {id ? t('donations:edit-form-heading') : t('donations:form-heading')}
        </Typography>
        <Grid container spacing={2} sx={{ width: 600, margin: '0 auto' }}>
          <Grid item xs={6}>
            <FormControl fullWidth size="small">
              <InputLabel id="labelType">{t('donations:type')}</InputLabel>
              <Select
                labelId="labelType"
                label={t('donations:type')}
                id="type"
                name="type"
                value={initialValues.type}
                onChange={(e) => setType(e.target.value)}
                disabled={id ? true : false}>
                {validDonationTypes.map((type) => {
                  return (
                    <MenuItem key={type} value={type}>
                      {type}
                    </MenuItem>
                  )
                })}
              </Select>
            </FormControl>
          </Grid>
          <Grid item xs={6}>
            <FormControl fullWidth size="small">
              <InputLabel id="labelStatus">{t('donations:status')}</InputLabel>
              <Select
                labelId="labelStatus"
                label={t('donations:status')}
                id="status"
                name="status"
                value={initialValues.status}
                onChange={(e) => {
                  setStatus(e.target.value)
                  console.log(e.target.value)
                }}>
                {validDonationStatuses.map((stat) => {
                  return (
                    <MenuItem key={stat} value={stat}>
                      {stat}
                    </MenuItem>
                  )
                })}
              </Select>
            </FormControl>
          </Grid>
          <Grid item xs={6}>
            <FormControl fullWidth size="small">
              <InputLabel id="labelProvider">{t('donations:provider')}</InputLabel>
              <Select
                labelId="labelProvider"
                label={t('donations:provider')}
                id="provider"
                name="provider"
                value={initialValues.provider}
                onChange={(e) => setProvider(e.target.value)}
                disabled={id ? true : false}>
                {validProviders.map((prov) => {
                  return (
                    <MenuItem key={prov} value={prov}>
                      {prov}
                    </MenuItem>
                  )
                })}
              </Select>
            </FormControl>
          </Grid>
          <Grid item xs={6}>
            <FormControl fullWidth size="small">
              <InputLabel id="labelVault">{t('donations:vault')}</InputLabel>
              <Select
                labelId="labelVault"
                label={t('donations:vault')}
                id="targetVaultId"
                name="targetVaultId"
                value={initialValues.targetVaultId}
                onChange={(e) => setVault(e.target.value)}
                disabled={id ? true : false}>
                {vaults?.map((vault) => {
                  return (
                    <MenuItem key={vault.id} value={vault.id}>
                      {vault.name}
                    </MenuItem>
                  )
                })}
              </Select>
            </FormControl>
          </Grid>
          <Grid item xs={12}>
            <FormTextField
              type="text"
              label={t('donations:ext-customer-id')}
              name="extCustomerId"
              disabled={id ? true : false}
            />
          </Grid>
          <Grid item xs={6}>
            <FormTextField
              type="text"
              label={t('donations:ext-payment-intent-id')}
              name="extPaymentIntentId"
              disabled={id ? true : false}
            />
          </Grid>
          <Grid item xs={6}>
            <FormTextField
              type="text"
              label={t('donations:ext-payment-method-id')}
              name="extPaymentMethodId"
              disabled={id ? true : false}
            />
          </Grid>
          <Grid item xs={6}>
            <FormTextField
              type="number"
              label={t('donations:amount')}
              name="amount"
              disabled={id ? true : false}
            />
          </Grid>
          <Grid item xs={6}>
            <FormControl fullWidth size="small">
              <InputLabel id="labelCurrency">{t('donations:currency')}</InputLabel>
              <Select
                labelId="labelCurrency"
                label={t('donations:currency')}
                id="currency"
                name="currency"
                value={initialValues.currency}
                onChange={(e) => setCurrency(e.target.value)}
                disabled={id ? true : false}>
                {validCurrencies.map((currency) => {
                  return (
                    <MenuItem key={currency} value={currency}>
                      {currency}
                    </MenuItem>
                  )
                })}
              </Select>
            </FormControl>
          </Grid>
          <Grid item xs={6}>
            <SubmitButton fullWidth label={t('donations:cta:submit')} />
          </Grid>
          <Grid item xs={6}>
            <Link passHref href={routes.admin.donations.index}>
              <Button>{t('donations:cta:cancel')}</Button>
            </Link>
          </Grid>
        </Grid>
      </Box>
    </GenericForm>
  )
}
Example #24
Source File: TransactionList.tsx    From abrechnung with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function TransactionList({ group }) {
    const [speedDialOpen, setSpeedDialOpen] = useState(false);
    const toggleSpeedDial = () => setSpeedDialOpen((currValue) => !currValue);

    const [showTransferCreateDialog, setShowTransferCreateDialog] = useState(false);
    const [showPurchaseCreateDialog, setShowPurchaseCreateDialog] = useState(false);
    const transactions = useRecoilValue(transactionsSeenByUser(group.id));
    const currentUser = useRecoilValue(userData);
    const userPermissions = useRecoilValue(currUserPermissions(group.id));
    const userAccounts = useRecoilValue(accountsOwnedByUser({ groupID: group.id, userID: currentUser.id }));
    const groupAccountMap = useRecoilValue(accountIDsToName(group.id));

    const theme: Theme = useTheme();
    const isSmallScreen = useMediaQuery(theme.breakpoints.down("md"));

    const [filteredTransactions, setFilteredTransactions] = useState([]);

    const [searchValue, setSearchValue] = useState("");

    const [sortMode, setSortMode] = useState("last_changed"); // last_changed, description, value, billed_at

    useEffect(() => {
        let filtered = transactions;
        if (searchValue != null && searchValue !== "") {
            filtered = transactions.filter((t) => t.filter(searchValue, groupAccountMap));
        }
        filtered = [...filtered].sort(getTransactionSortFunc(sortMode));

        setFilteredTransactions(filtered);
    }, [searchValue, setFilteredTransactions, sortMode, transactions, userAccounts]);

    useTitle(`${group.name} - Transactions`);

    const openPurchaseCreateDialog = () => {
        setShowPurchaseCreateDialog(true);
    };

    const openTransferCreateDialog = () => {
        setShowTransferCreateDialog(true);
    };

    return (
        <>
            <MobilePaper>
                <Box
                    sx={{
                        display: "flex",
                        flexDirection: { xs: "column", sm: "column", md: "row", lg: "row" },
                        alignItems: { md: "flex-end" },
                        pl: "16px",
                        justifyContent: "space-between",
                    }}
                >
                    <Box sx={{ display: "flex-item" }}>
                        <Box sx={{ minWidth: "56px", pt: "16px" }}>
                            <SearchIcon sx={{ color: "action.active" }} />
                        </Box>
                        <Input
                            value={searchValue}
                            onChange={(e) => setSearchValue(e.target.value)}
                            placeholder="Search…"
                            inputProps={{
                                "aria-label": "search",
                            }}
                            sx={{ pt: "16px" }}
                            endAdornment={
                                <InputAdornment position="end">
                                    <IconButton
                                        aria-label="clear search input"
                                        onClick={(e) => setSearchValue("")}
                                        edge="end"
                                    >
                                        <Clear />
                                    </IconButton>
                                </InputAdornment>
                            }
                        />
                        <FormControl variant="standard" sx={{ minWidth: 120, ml: 3 }}>
                            <InputLabel id="select-sort-by-label">Sort by</InputLabel>
                            <Select
                                labelId="select-sort-by-label"
                                id="select-sort-by"
                                label="Sort by"
                                onChange={(evt) => setSortMode(evt.target.value)}
                                value={sortMode}
                            >
                                <MenuItem value="last_changed">Last changed</MenuItem>
                                <MenuItem value="description">Description</MenuItem>
                                <MenuItem value="value">Value</MenuItem>
                                <MenuItem value="billed_at">Date</MenuItem>
                            </Select>
                        </FormControl>
                    </Box>
                    {!isSmallScreen && (
                        <Box sx={{ display: "flex-item" }}>
                            <div style={{ padding: "8px" }}>
                                <Add color="primary" />
                            </div>
                            <Tooltip title="Create Purchase">
                                <IconButton color="primary" onClick={openPurchaseCreateDialog}>
                                    <PurchaseIcon />
                                </IconButton>
                            </Tooltip>
                            <Tooltip title="Create Transfer">
                                <IconButton color="primary" onClick={openTransferCreateDialog}>
                                    <TransferIcon />
                                </IconButton>
                            </Tooltip>
                        </Box>
                    )}
                </Box>
                <Divider sx={{ mt: 1 }} />
                <List>
                    {transactions.length === 0 ? (
                        <Alert severity="info">No Transactions</Alert>
                    ) : (
                        filteredTransactions.map((transaction) => (
                            <TransactionListEntry key={transaction.id} group={group} transaction={transaction} />
                        ))
                    )}
                </List>
                <TransferCreateModal
                    group={group}
                    show={showTransferCreateDialog}
                    onClose={(evt, reason) => {
                        if (reason !== "backdropClick") {
                            setShowTransferCreateDialog(false);
                        }
                    }}
                />
                <PurchaseCreateModal
                    group={group}
                    show={showPurchaseCreateDialog}
                    onClose={(evt, reason) => {
                        if (reason !== "backdropClick") {
                            setShowPurchaseCreateDialog(false);
                        }
                    }}
                />
            </MobilePaper>
            {userPermissions.can_write && (
                <SpeedDial
                    ariaLabel="Create Account"
                    sx={{ position: "fixed", bottom: 20, right: 20 }}
                    icon={<SpeedDialIcon />}
                    // onClose={() => setSpeedDialOpen(false)}
                    // onOpen={() => setSpeedDialOpen(true)}
                    onClick={toggleSpeedDial}
                    open={speedDialOpen}
                >
                    <SpeedDialAction
                        icon={<PurchaseIcon />}
                        tooltipTitle="Purchase"
                        tooltipOpen
                        onClick={openPurchaseCreateDialog}
                    />
                    <SpeedDialAction
                        icon={<TransferIcon />}
                        tooltipTitle="Transfer"
                        tooltipOpen
                        onClick={openTransferCreateDialog}
                    />
                </SpeedDial>
            )}
        </>
    );
}
Example #25
Source File: CustomIdField.tsx    From firecms with MIT License 4 votes vote down vote up
export function CustomIdField<M, UserType>
({ schema, status, onChange, error, entity }: {
    schema: EntitySchema<M>,
    status: EntityStatus,
    onChange: Function,
    error: boolean,
    entity: Entity<M> | undefined
}) {

    const classes = formStyles();

    const disabled = status === "existing" || !schema.customId;
    const idSetAutomatically = status !== "existing" && !schema.customId;

    const hasEnumValues = typeof schema.customId === "object";

    const snackbarContext = useSnackbarController();
    const { copy } = useClipboard({
        onSuccess: (text) => snackbarContext.open({
            type: "success",
            message: `Copied ${text}`
        })
    });

    const appConfig: FireCMSContext<UserType> | undefined = useFireCMSContext();
    const inputProps = {
        className: classes.input,
        endAdornment: entity
? (
            <InputAdornment position="end">

                <IconButton onClick={(e) => copy(entity.id)}
                            aria-label="copy-id"
                            size="large">
                    <Tooltip title={"Copy"}>
                        <svg
                            className={"MuiSvgIcon-root MuiSvgIcon-fontSizeSmall"}
                            fill={"currentColor"}
                            width="20" height="20" viewBox="0 0 24 24">
                            <path
                                d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
                        </svg>
                    </Tooltip>
                </IconButton>

                {appConfig?.entityLinkBuilder &&
                <a href={appConfig.entityLinkBuilder({ entity })}
                   rel="noopener noreferrer"
                   target="_blank">
                    <IconButton onClick={(e) => e.stopPropagation()}
                                aria-label="go-to-datasource" size="large">
                        <Tooltip title={"Open in the console"}>
                            <OpenInNewIcon fontSize={"small"}/>
                        </Tooltip>
                    </IconButton>
                </a>}

            </InputAdornment>
        )
: undefined
    };

    const fieldProps: any = {
        label: idSetAutomatically ? "ID is set automatically" : "ID",
        disabled: disabled,
        name: "id",
        type: null,
        value: entity && status === "existing" ? entity.id : undefined,
        variant: "filled"
    };

    return (
        <FormControl fullWidth
                     error={error}
                     {...fieldProps}
                     key={"custom-id-field"}>

            {hasEnumValues && schema.customId &&
            <>
                <InputLabel id={"id-label"}>{fieldProps.label}</InputLabel>
                <MuiSelect
                    labelId={"id-label"}
                    className={classes.select}
                    error={error}
                    {...fieldProps}
                    onChange={(event: any) => onChange(event.target.value)}>
                    {Object.entries(schema.customId).map(([key, label]) =>
                        <MenuItem
                            key={`custom-id-item-${key}`}
                            value={key}>
                            {`${key} - ${label}`}
                        </MenuItem>)}
                </MuiSelect>
            </>}

            {!hasEnumValues &&
            <MuiTextField {...fieldProps}
                          error={error}
                          InputProps={inputProps}
                          helperText={schema.customId === "optional" ? "Leave this blank to autogenerate an ID" : "ID of the new document"}
                          onChange={(event) => {
                              let value = event.target.value;
                              if (value) value = value.trim();
                              return onChange(value.length ? value : undefined);
                          }}/>}

            <ErrorMessage name={"id"}
                          component="div">
                {(_) => "You need to specify an ID"}
            </ErrorMessage>

        </FormControl>
    );
}
Example #26
Source File: search.tsx    From Cromwell with MIT License 4 votes vote down vote up
SearchPage: TPageWithLayout<SearchPageProps> = (props) => {
    const filterInput = useRef<TPostFilter>({});
    const listId = 'Blog_list_01';
    const publishSort = useRef<"ASC" | "DESC">('DESC');
    const forceUpdate = useForceUpdate();
    const titleSearchId = "post-filter-search";

    const updateList = () => {
        const list = getBlockInstance<TCList>(listId)?.getContentInstance();
        list?.clearState();
        list?.init();
        list?.updateData();
    }

    const handleChangeTags = (event: any, newValue?: (TTag | undefined | string)[]) => {
        filterInput.current.tagIds = newValue?.map(tag => (tag as TTag)?.id);
        forceUpdate();
        updateList();
    }

    const handleGetPosts = (params: TPagedParams<TPost>) => {
        params.orderBy = 'publishDate';
        params.order = publishSort.current;
        return handleGetFilteredPosts(params, filterInput.current);
    }

    const handleChangeSort = (event: SelectChangeEvent<unknown>) => {
        if (event.target.value === 'Newest') publishSort.current = 'DESC';
        if (event.target.value === 'Oldest') publishSort.current = 'ASC';
        updateList();
    }

    const handleTagClick = (tag?: TTag) => {
        if (!tag) return;
        if (filterInput.current.tagIds?.length === 1 &&
            filterInput.current.tagIds[0] === tag.id) return;
        handleChangeTags(null, [tag]);
        forceUpdate();
    }

    const handleTitleInput = debounce(400, () => {
        filterInput.current.titleSearch = (document.getElementById(titleSearchId) as HTMLInputElement)?.value ?? undefined;
        updateList();
    });

    return (
        <CContainer className={commonStyles.content} id="search_01">
            <CContainer className={styles.filter} id="search_02">
                <div className={styles.filterLeft}>
                    <TextField
                        className={styles.filterItem}
                        placeholder="Search by title"
                        id={titleSearchId}
                        variant="standard"
                        onChange={handleTitleInput}
                    />
                    <Autocomplete
                        multiple
                        freeSolo
                        value={filterInput.current.tagIds?.map(id => props.tags?.find(tag => tag.id === id)) ?? []}
                        className={styles.filterItem}
                        options={props.tags ?? []}
                        getOptionLabel={(option: any) => option?.name ?? ''}
                        style={{ width: 300 }}
                        onChange={handleChangeTags}
                        renderInput={(params) => (
                            <TextField
                                {...params}
                                variant="standard"
                                placeholder="Tags"
                            />
                        )}
                    />
                </div>
                <FormControl className={styles.filterItem}>
                    <InputLabel className={styles.sortLabel}>Sort</InputLabel>
                    <Select
                        style={{ width: '100px' }}
                        onChange={handleChangeSort}
                        variant="standard"
                        defaultValue='Newest'
                    >
                        {['Newest', 'Oldest'].map(sort => (
                            <MenuItem value={sort} key={sort}>{sort}</MenuItem>
                        ))}
                    </Select>
                </FormControl>
            </CContainer>
            <CContainer style={{ marginBottom: '20px' }} id="search_03">
                <CList<TPost>
                    id={listId}
                    ListItem={(props) => (
                        <div className={styles.postWrapper}>
                            <PostCard onTagClick={handleTagClick} data={props.data} key={props.data?.id} />
                        </div>
                    )}
                    usePagination
                    useShowMoreButton
                    useQueryPagination
                    disableCaching
                    pageSize={20}
                    scrollContainerSelector={`.${layoutStyles.Layout}`}
                    firstBatch={props.posts}
                    loader={handleGetPosts}
                    cssClasses={{
                        page: styles.postList
                    }}
                    elements={{
                        pagination: Pagination
                    }}
                />
            </CContainer>
        </CContainer>
    );
}
Example #27
Source File: Language.tsx    From GTAV-NativeDB with MIT License 4 votes vote down vote up
export function CodeGenOptionComponent<TSettings>(props:CodeGenOptionComponentProps<TSettings>) {
  const { type, label, prop, value, onChange } = props

  switch (type) {
    case 'boolean':
      return (
        <FormControlLabel
          control={
            <Checkbox
              name={prop}
              checked={value}
              onChange={onChange}
            />
          }
          sx={{ userSelect: 'none' }}
          label={label}
        />
      )
    case 'combo':
      const options = props.options
      return (
        <FormControl fullWidth>
          <InputLabel id={`${prop}-label`}>{label}</InputLabel>
          <Select
            id={`${prop}-select`}
            labelId={`${prop}-label`}
            name={prop}
            value={value}
            onChange={onChange}
            label={label}
          >
            {options.map(({ label, value }) => (
              <MenuItem key={value} value={value}>
                {label}
              </MenuItem>
            ))}
          </Select>
        </FormControl>
      )
    case 'string':
      return (
        <TextField
          label={label}
          name={prop}
          onChange={(onChange as unknown as ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>)}
          value={value}
        />
      )
  }
}
Example #28
Source File: QueryMultiSelector.tsx    From console with GNU Affero General Public License v3.0 4 votes vote down vote up
QueryMultiSelector = ({
  elements,
  name,
  label,
  tooltip = "",
  keyPlaceholder = "",
  valuePlaceholder = "",
  onChange,
  withBorder = false,
  classes,
}: IQueryMultiSelector) => {
  const [currentKeys, setCurrentKeys] = useState<string[]>([""]);
  const [currentValues, setCurrentValues] = useState<string[]>([""]);
  const bottomList = createRef<HTMLDivElement>();

  // Use effect to get the initial values from props
  useEffect(() => {
    if (
      currentKeys.length === 1 &&
      currentKeys[0] === "" &&
      currentValues.length === 1 &&
      currentValues[0] === "" &&
      elements &&
      elements !== ""
    ) {
      const elementsSplit = elements.split("&");
      let keys = [];
      let values = [];

      elementsSplit.forEach((element: string) => {
        const splittedVals = element.split("=");
        if (splittedVals.length === 2) {
          keys.push(splittedVals[0]);
          values.push(splittedVals[1]);
        }
      });

      keys.push("");
      values.push("");

      setCurrentKeys(keys);
      setCurrentValues(values);
    }
  }, [currentKeys, currentValues, elements]);

  // Use effect to send new values to onChange
  useEffect(() => {
    const refScroll = bottomList.current;
    if (refScroll && currentKeys.length > 1) {
      refScroll.scrollIntoView(false);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentKeys]);

  // We avoid multiple re-renders / hang issue typing too fast
  const firstUpdate = useRef(true);
  useLayoutEffect(() => {
    if (firstUpdate.current) {
      firstUpdate.current = false;
      return;
    }
    debouncedOnChange();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentKeys, currentValues]);

  // If the last input is not empty, we add a new one
  const addEmptyLine = () => {
    if (
      currentKeys[currentKeys.length - 1].trim() !== "" &&
      currentValues[currentValues.length - 1].trim() !== ""
    ) {
      const keysList = [...currentKeys];
      const valuesList = [...currentValues];

      keysList.push("");
      valuesList.push("");

      setCurrentKeys(keysList);
      setCurrentValues(valuesList);
    }
  };

  // Onchange function for input box, we get the dataset-index & only update that value in the array
  const onChangeKey = (e: ChangeEvent<HTMLInputElement>) => {
    e.persist();

    let updatedElement = [...currentKeys];
    const index = get(e.target, "dataset.index", 0);
    updatedElement[index] = e.target.value;

    setCurrentKeys(updatedElement);
  };

  const onChangeValue = (e: ChangeEvent<HTMLInputElement>) => {
    e.persist();

    let updatedElement = [...currentValues];
    const index = get(e.target, "dataset.index", 0);
    updatedElement[index] = e.target.value;

    setCurrentValues(updatedElement);
  };

  // Debounce for On Change
  const debouncedOnChange = debounce(() => {
    let queryString = "";

    currentKeys.forEach((keyVal, index) => {
      if (currentKeys[index] && currentValues[index]) {
        let insertString = `${keyVal}=${currentValues[index]}`;
        if (index !== 0) {
          insertString = `&${insertString}`;
        }
        queryString = `${queryString}${insertString}`;
      }
    });

    onChange(queryString);
  }, 500);

  const inputs = currentValues.map((element, index) => {
    return (
      <Grid
        item
        xs={12}
        className={classes.lineInputBoxes}
        key={`query-pair-${name}-${index.toString()}`}
      >
        <InputBoxWrapper
          id={`${name}-key-${index.toString()}`}
          label={""}
          name={`${name}-${index.toString()}`}
          value={currentKeys[index]}
          onChange={onChangeKey}
          index={index}
          placeholder={keyPlaceholder}
        />
        <span className={classes.queryDiv}>:</span>
        <InputBoxWrapper
          id={`${name}-value-${index.toString()}`}
          label={""}
          name={`${name}-${index.toString()}`}
          value={currentValues[index]}
          onChange={onChangeValue}
          index={index}
          placeholder={valuePlaceholder}
          overlayIcon={index === currentValues.length - 1 ? <AddIcon /> : null}
          overlayAction={() => {
            addEmptyLine();
          }}
        />
      </Grid>
    );
  });

  return (
    <React.Fragment>
      <Grid item xs={12} className={classes.fieldContainer}>
        <InputLabel className={classes.inputLabel}>
          <span>{label}</span>
          {tooltip !== "" && (
            <div className={classes.tooltipContainer}>
              <Tooltip title={tooltip} placement="top-start">
                <HelpIcon className={classes.tooltip} />
              </Tooltip>
            </div>
          )}
        </InputLabel>
        <Grid
          item
          xs={12}
          className={`${withBorder ? classes.inputWithBorder : ""}`}
        >
          {inputs}
          <div ref={bottomList} />
        </Grid>
      </Grid>
    </React.Fragment>
  );
}
Example #29
Source File: EpisodeListing.tsx    From multi-downloader-nx with MIT License 4 votes vote down vote up
EpisodeListing: React.FC = () => {
  const [store, dispatch] = useStore();

  const [season, setSeason] = React.useState<'all'|string>('all');

  const seasons = React.useMemo(() => {
    const s: string[] = [];
    for (const {season} of store.episodeListing) {
      if (s.includes(season))
        continue;
      s.push(season);
    }
    return s;
  }, [ store.episodeListing ])

  const [selected, setSelected] = React.useState<string[]>([]);

  React.useEffect(() => {
    setSelected(parseSelect(store.downloadOptions.e));
  }, [ store.episodeListing ])

  const close = () => {
    dispatch({
      type: 'episodeListing',
      payload: []
    });
    dispatch({
      type: 'downloadOptions',
      payload: {
        ...store.downloadOptions,
        e: `${([...new Set([...parseSelect(store.downloadOptions.e), ...selected])]).join(',')}`
      }
    })
  }

  return <Dialog open={store.episodeListing.length > 0} onClose={close} scroll='paper' maxWidth='xl' sx={{ p: 2 }}>
      <Box sx={{ display: 'grid', gridTemplateColumns: '1fr 200px 20px' }}>
        <Typography color='text.primary' variant="h5" sx={{ textAlign: 'center', alignItems: 'center', justifyContent: 'center', display: 'flex' }}>
          Episodes
        </Typography>
        <FormControl sx={{ mr: 2, mt: 2 }}>
          <InputLabel id='seasonSelectLabel'>Season</InputLabel>
          <Select labelId="seasonSelectLabel" label='Season' value={season} onChange={(e) => setSeason(e.target.value)}>
            <MenuItem value='all'>Show all Epsiodes</MenuItem>
            {seasons.map((a, index) => {
              return <MenuItem value={a} key={`MenuItem_SeasonSelect_${index}`}>
                {a}
              </MenuItem>
            })}
          </Select>
        </FormControl>
      </Box>
      <List>
        <ListItem sx={{ display: 'grid', gridTemplateColumns: '25px 1fr 5fr' }}>
          <Checkbox
            indeterminate={store.episodeListing.some(a => selected.includes(a.e)) && !store.episodeListing.every(a => selected.includes(a.e))}
            checked={store.episodeListing.every(a => selected.includes(a.e))}
            onChange={() => {
              if (selected.length > 0) {
                setSelected([]);
              } else {
                setSelected(store.episodeListing.map(a => a.e));
              }
            }}
          />
        </ListItem>
        {store.episodeListing.filter((a) => season === 'all' ? true : a.season === season).map((item, index, { length }) => {
          const e = isNaN(parseInt(item.e)) ? item.e : parseInt(item.e);
          const isSelected = selected.includes(e.toString());
          return <Box {...{ mouseData: isSelected }} key={`Episode_List_Item_${index}`} sx={{
              backdropFilter: isSelected ? 'brightness(1.5)' : '',
              '&:hover': {
                backdropFilter: 'brightness(1.5)'
              }
            }}
            onClick={() => {
              let arr: string[] = [];
              if (isSelected) {
                arr = [...selected.filter(a => a !== e.toString())];
              } else {
                arr = [...selected, e.toString()];
              }
              setSelected(arr.filter(a => a.length > 0));
            }}>
            <ListItem sx={{ display: 'grid', gridTemplateColumns: '25px 50px 1fr 5fr' }}>
              { isSelected ? <CheckBox /> : <CheckBoxOutlineBlank /> }
              <Typography color='text.primary' sx={{ textAlign: 'center' }}>
                {e}
              </Typography>
              <img style={{ width: 'inherit', maxHeight: '200px', minWidth: '150px' }} src={item.img}></img>
              <Box sx={{ display: 'flex', flexDirection: 'column', pl: 1 }}>
                <Box sx={{ display: 'grid', gridTemplateColumns: '1fr min-content' }}>
                  <Typography color='text.primary' variant="h5">
                    {item.name}
                  </Typography>
                  <Typography color='text.primary'>
                    {item.time.startsWith('00:') ? item.time.slice(3) : item.time}
                  </Typography>
                </Box>
                <Typography color='text.primary'>
                  {item.description}
                </Typography>
              </Box>
            </ListItem>
            {index < length - 1 && <Divider />}
          </Box>
        })}
      </List>
  </Dialog>
}