@material-ui/core#InputAdornment TypeScript Examples

The following examples show how to use @material-ui/core#InputAdornment. 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: TokenSymbol.tsx    From parity-bridges-ui with GNU General Public License v3.0 6 votes vote down vote up
TokenSymbol = ({ position = 'start' }: Props): React.ReactElement => {
  const { targetChainDetails, sourceChainDetails } = useSourceTarget();
  const { isBridged } = useGUIContext();

  let chainTokens = targetChainDetails.apiConnection.api.registry.chainTokens;
  if (!isBridged) {
    chainTokens = sourceChainDetails.apiConnection.api.registry.chainTokens;
  }

  return <InputAdornment position={position}>{chainTokens}</InputAdornment>;
}
Example #2
Source File: index.tsx    From frontegg-react with MIT License 6 votes vote down vote up
appendAdornment = (
  props: Partial<MaterialTextFieldProps> | undefined,
  at: keyof MaterialInputProps,
  iconAction: InputProps['iconAction'],
  icon: InputProps['prefixIcon'] | InputProps['suffixIcon']
) => {
  const position: InputAdornmentProps['position'] = at === 'startAdornment' ? 'start' : 'end';

  return {
    ...props,
    InputProps: {
      [at]: (
        <InputAdornment onClick={iconAction} position={position}>
          {iconAction ? <IconButton>{icon}</IconButton> : icon}
        </InputAdornment>
      ),
    },
  };
}
Example #3
Source File: Searchbar.tsx    From homebase-app with MIT License 6 votes vote down vote up
SearchInput: React.FC<{ search: any }> = ({ search }) => {
  return (
    <StyledInput
      id="standard-search"
      label="Search field"
      type="search"
      placeholder="Search"
      onChange={(e) => search(e.target.value)}
      InputProps={{
        startAdornment: (
          <InputAdornment position="start">
            <SearchIcon color="secondary" />
          </InputAdornment>
        ),
      }}
    />
  );
}
Example #4
Source File: Autocomplete.test.tsx    From abacus with GNU General Public License v2.0 6 votes vote down vote up
test('it shows as loading data', () => {
  const isLoading = true

  const { container } = render(
    <MockFormik initialValues={{ name: 'no_name' }}>
      <Field
        component={Autocomplete}
        name='name'
        id='name'
        fullWidth
        options={[]}
        loading={isLoading}
        renderInput={(params: AutocompleteRenderInputParams) => {
          return (
            <MuiTextField
              {...params}
              placeholder='wp_username'
              helperText='Use WordPress.com username.'
              variant='outlined'
              required
              label='Owner'
              InputProps={{
                ...autocompleteInputProps(params, isLoading),
                startAdornment: <InputAdornment position='start'>@</InputAdornment>,
              }}
              InputLabelProps={{
                shrink: true,
              }}
            />
          )
        }}
      />
    </MockFormik>,
  )
  expect(container).toMatchSnapshot('loading')
})
Example #5
Source File: Registration.tsx    From glific-frontend with GNU Affero General Public License v3.0 6 votes vote down vote up
InfoAdornment = (
  <InputAdornment position="end" className={styles.InputAdornment}>
    <Tooltip
      title="You can customize your Glific account URL as shown in preview"
      placement="right"
      tooltipClass={styles.Tooltip}
    >
      <InfoIcon />
    </Tooltip>
  </InputAdornment>
)
Example #6
Source File: FilterInput.tsx    From ow-mod-manager with MIT License 6 votes vote down vote up
FilterInput: React.FunctionComponent<Props> = ({
  value,
  onChange,
  label,
}) => {
  const [filterText, setFilterText] = useState(value);
  const debouncedFilterText = useDebounce(filterText, 200);

  useEffect(() => {
    onChange(debouncedFilterText);
  }, [debouncedFilterText, onChange]);

  return (
    <OutlinedInput
      margin="dense"
      onChange={({ currentTarget }) => {
        setFilterText(currentTarget.value);
      }}
      value={filterText}
      placeholder={label}
      color="secondary"
      startAdornment={
        <InputAdornment position="start">
          <SearchIcon />
        </InputAdornment>
      }
      endAdornment={
        filterText !== '' && (
          <InputAdornment position="end">
            <IconButton onClick={() => setFilterText('')} size="small">
              <CloseIcon fontSize="small" />
            </IconButton>
          </InputAdornment>
        )
      }
    />
  );
}
Example #7
Source File: SearchBar.tsx    From log4brains with Apache License 2.0 6 votes vote down vote up
export function SearchBar(props: SearchBarProps) {
  const { InputProps, InputLabelProps, open, onClear, ...params } = props;
  const classes = useStyles(props);
  return (
    <InputBase
      placeholder="Search..."
      classes={{
        root: classes.inputRoot,
        input: classes.inputInput
      }}
      startAdornment={
        <InputAdornment position="start">
          <SearchIcon />
        </InputAdornment>
      }
      endAdornment={
        // eslint-disable-next-line react/destructuring-assignment
        <Fade in={open && props.inputProps.value !== ""}>
          <InputAdornment position="end">
            <IconButton
              onClick={(event) => onClear(event)}
              size="small"
              title="Clear"
              className={classes.clearIcon}
            >
              <ClearIcon />
            </IconButton>
          </InputAdornment>
        </Fade>
      }
      ref={InputProps.ref}
      {...params}
    />
  );
}
Example #8
Source File: Index.stories.tsx    From anchor-web-app with Apache License 2.0 6 votes vote down vote up
textFieldInputProps = {
  endAdornment: (
    <InputAdornment position="end">
      <Tooltip color="error" title="Error Tooltip Content" placement="top">
        <Warning />
      </Tooltip>
    </InputAdornment>
  ),
}
Example #9
Source File: AdvancedSettings.tsx    From lobis-frontend with MIT License 5 votes vote down vote up
function AdvancedSettings({ open, handleClose, slippage, recipientAddress, onRecipientAddressChange, onSlippageChange }: IAdvancedSettingsProps) {
    return (
        <Modal id="hades" open={open} onClose={handleClose} hideBackdrop>
            <Paper className="ohm-card ohm-popover">
                <div className="cross-wrap">
                    <IconButton onClick={handleClose}>
                        <SvgIcon color="primary" component={XIcon} />
                    </IconButton>
                </div>

                <Box className="card-content">
                    <InputLabel htmlFor="slippage">
                        <p className="input-lable">Slippage</p>
                    </InputLabel>
                    <FormControl variant="outlined" color="primary" fullWidth>
                        <OutlinedInput
                            id="slippage"
                            value={slippage}
                            onChange={onSlippageChange}
                            fullWidth
                            type="number"
                            //@ts-ignore
                            max="100"
                            min="100"
                            className="bond-input"
                            endAdornment={
                                <InputAdornment position="end">
                                    <p className="percent">%</p>
                                </InputAdornment>
                            }
                        />
                        <div className="help-text">
                            <p className="text-bond-desc">Transaction may revert if price changes by more than slippage %</p>
                        </div>
                    </FormControl>
                </Box>
            </Paper>
        </Modal>
    );
}
Example #10
Source File: index.tsx    From wonderland-frontend with MIT License 5 votes vote down vote up
function ChooseToken({ open, handleClose, handleSelect, bond }: IChooseTokenProps) {
    const { tokens, loading } = useTokens();

    const [quantity, setQuantity] = useState("");

    const filtredTokens = tokens.filter(({ name, address, isAvax }) => {
        let addressTest = true;

        if (quantity && quantity.length === 42) {
            addressTest = address.toLocaleLowerCase() === quantity.toLowerCase();
        }

        let nameTest = true;

        if (quantity && quantity.length < 10) {
            nameTest = name.toLowerCase().includes(quantity.toLowerCase());
        }

        let lpFilter = true;

        if (bond.name === mim.name) {
            lpFilter = mimToken.address !== address;
        }

        if (bond.name === wavax.name) {
            lpFilter = isAvax ? false : wavaxToken.address !== address;
        }

        return nameTest && addressTest && lpFilter;
    });

    return (
        <Modal id="hades" open={open} onClose={handleClose} hideBackdrop>
            <Paper className="ohm-card ohm-popover choose-token-poper">
                <div className="cross-wrap">
                    <IconButton onClick={handleClose}>
                        <SvgIcon color="primary" component={XIcon} />
                    </IconButton>
                </div>
                <Box>
                    <div className="choose-token-poper-header">
                        <p className="choose-token-poper-header-title">Choose token</p>
                        <OutlinedInput
                            placeholder="Search name or paste address"
                            className="choose-token-poper-header-input"
                            value={quantity}
                            onChange={e => setQuantity(e.target.value)}
                            labelWidth={0}
                            startAdornment={
                                <InputAdornment position="start">
                                    <Box display="flex" alignItems="center" justifyContent="center" width={"24px"}>
                                        <img src={IconsSearch} style={{ height: "24px", width: "24px" }} />
                                    </Box>
                                </InputAdornment>
                            }
                        />
                    </div>

                    <div className="choose-token-poper-body">
                        {filtredTokens.map(token => (
                            <div onClick={() => handleSelect(token)} key={token.address} className="choose-token-poper-body-item">
                                <img className="choose-token-poper-body-item-img" src={token.img} alt="" />
                                <div className="choose-token-poper-body-item-desc">
                                    <p className="choose-token-poper-body-item-name">{token.name}</p>
                                    <div className="choose-token-poper-body-item-balance">{loading ? <Skeleton width="50px" /> : <p>{trim(token.balance, 6)}</p>}</div>
                                </div>
                            </div>
                        ))}
                    </div>
                </Box>
            </Paper>
        </Modal>
    );
}
Example #11
Source File: Autocomplete.test.tsx    From abacus with GNU General Public License v2.0 5 votes vote down vote up
test('null is a valid value', async () => {
  const isLoading = false
  const options: AutocompleteItem[] = [
    {
      name: 'Hello World',
      value: 'hello_world',
    },
  ]

  const { container } = render(
    <MockFormik initialValues={{ name: null }}>
      <Field
        component={Autocomplete}
        name='name'
        id='name'
        fullWidth
        options={options}
        loading={isLoading}
        renderInput={(params: AutocompleteRenderInputParams) => {
          return (
            <MuiTextField
              {...params}
              placeholder='wp_username'
              helperText='Use WordPress.com username.'
              variant='outlined'
              required
              label='Owner'
              InputProps={{
                ...autocompleteInputProps(params, isLoading),
                startAdornment: <InputAdornment position='start'>@</InputAdornment>,
              }}
              InputLabelProps={{
                shrink: true,
              }}
            />
          )
        }}
      />
    </MockFormik>,
  )
  await act(async () => {
    fireEvent.click(screen.getByRole('textbox'))
  })
  expect(container).toMatchSnapshot('selected no value')
})
Example #12
Source File: settings_button.tsx    From jupyter-extensions with Apache License 2.0 5 votes vote down vote up
render() {
    return (
      <React.Fragment>
        <IconButton
          title="Settings"
          color="inherit"
          onClick={() => this._onClick()}
        >
          <SettingsIcon fontSize="small" />
        </IconButton>

        <Dialog
          open={this.state.open}
          onClose={() => this._onClose()}
          fullWidth
        >
          <DialogTitle>Settings</DialogTitle>
          <DialogContent>
            <Grid container alignItems="center" spacing={2}>
              <Grid item xs={11}>
                <Typography id="input-slider">Sync Frequency</Typography>
              </Grid>
              <Grid item xs={1}>
                <Tooltip title="Time between each sync with remote repository. After each sync, the extension will wait before syncing again.">
                  <HelpOutlineIcon color="disabled" />
                </Tooltip>
              </Grid>
              <Grid item xs={1}>
                <TimerIcon />
              </Grid>
              <Grid item xs={7}>
                <Slider
                  value={
                    typeof this.state.value === 'number'
                      ? this.state.value / (this.maxInterval / 100)
                      : 0
                  }
                  onChange={(event, value) =>
                    this._onSliderChange(event, value)
                  }
                  aria-labelledby="sync-interval-slider"
                />
              </Grid>
              <Grid item xs={4}>
                <Input
                  value={this.state.value}
                  onChange={event => this._onInputChange(event)}
                  onBlur={() => this._onBlur()}
                  margin="dense"
                  inputProps={{
                    step: 1,
                    min: 0,
                    max: this.maxInterval,
                    type: 'number',
                    'aria-labelledby': 'sync-interval-slider',
                  }}
                  endAdornment={
                    <InputAdornment position="end">seconds</InputAdornment>
                  }
                />
              </Grid>
            </Grid>
          </DialogContent>
          <DialogActions>
            <Button onClick={() => this._onClose()} color="primary">
              Cancel
            </Button>
            <Button
              onClick={() => this._onSave()}
              color="primary"
              variant="contained"
            >
              Save
            </Button>
          </DialogActions>
        </Dialog>
      </React.Fragment>
    );
  }
Example #13
Source File: OnCallList.tsx    From backstage-plugin-opsgenie with MIT License 5 votes vote down vote up
SchedulesGrid = ({ schedules, cardsPerPage }: { schedules: Schedule[], cardsPerPage: number }) => {
    const classes = useStyles();
    const [results, setResults] = React.useState(schedules);
    const [search, setSearch] = React.useState("");
    const [page, setPage] = React.useState(1);
    const [offset, setOffset] = React.useState(0);
    const handleChange = (_: React.ChangeEvent<unknown>, value: number) => {
        setOffset((value - 1) * cardsPerPage);
        setPage(value);
    };
    const debouncedSearch = useDebounce(search, 300);

    // debounced search
    useEffect(
        () => {
            if (!debouncedSearch) {
                setResults(schedules);
                return;
            }

            const filtered = schedules.filter(schedule => {
                return schedule.name.toLowerCase().includes(debouncedSearch.toLowerCase());
            });
            setResults(filtered);
        },
        [debouncedSearch, schedules]
    );

    return (
        <div>
            <TextField
                fullWidth
                variant="outlined"
                className={classes.search}
                placeholder="Team…"
                InputProps={{
                    startAdornment: (
                        <InputAdornment position="start">
                            <SearchIcon />
                        </InputAdornment>
                    )
                }}
                onChange={e => setSearch(e.target.value)}
            />

            <ItemCardGrid classes={{ root: classes.onCallItemGrid }}>
                {results.filter((_, i) => i >= offset && i < offset + cardsPerPage).map(schedule => <OnCallForScheduleCard key={schedule.id} schedule={schedule} />)}
            </ItemCardGrid>

            <Pagination
                className={classes.pagination}
                count={Math.ceil(results.length / cardsPerPage)}
                page={page}
                onChange={handleChange}
                showFirstButton
                showLastButton
            />
        </div>
    );
}
Example #14
Source File: BoundaryDropdown.tsx    From prism-frontend with MIT License 5 votes vote down vote up
SearchField = forwardRef(
  (
    {
      // important this isn't called `value` since this would confuse <Select/>
      // the main purpose of wrapping this text-field is for this very purpose.
      search,
      setSearch,
    }: {
      search: string;
      setSearch: (val: string) => void;
    },
    ref: TextFieldProps['ref'],
  ) => {
    const styles = useStyles();
    return (
      <TextField
        ref={ref}
        onKeyDown={e => e.stopPropagation()}
        className={styles.searchField}
        value={search}
        onChange={e => {
          setSearch(e.target.value);
          // when something is selected, and the user tries to search, this field deselects for some reason,
          // thus reselect on change. Important to capture target as it's null inside timeout.
          const { target } = e;
          setTimeout(() => {
            target.focus();
          }, TIMEOUT_ANIMATION_DELAY);
        }}
        InputProps={{
          startAdornment: (
            <InputAdornment position="end">
              <Search />
            </InputAdornment>
          ),
        }}
      />
    );
  },
)
Example #15
Source File: UpdatableField.tsx    From clearflask with Apache License 2.0 5 votes vote down vote up
render() {
    return (
      <div className={this.props.classes.wrapper}>
        <TextField
          value={(this.state.value === undefined ? this.props.value : this.state.value) || ''}
          onChange={e => this.setState({ value: e.target.value })}
          type={!this.props.isPassword || this.state.revealPassword ? 'text' : 'password'}
          disabled={this.state.isSubmitting}
          helperText={this.props.helperText}
          InputProps={{
            readOnly: this.props.isToken ? true : undefined,
            endAdornment: this.props.isPassword ? (
              <InputAdornment position='end'>
                <IconButton
                  aria-label='Toggle password visibility'
                  onClick={() => this.setState({ revealPassword: !this.state.revealPassword })}
                  disabled={this.state.isSubmitting}
                >
                  {this.state.revealPassword ? <VisibilityIcon fontSize='small' /> : <VisibilityOffIcon fontSize='small' />}
                </IconButton>
              </InputAdornment>
            ) : (this.props.isToken ? (
              <InputAdornment position='end'>
                <IconButton
                  aria-label='Reset'
                  onClick={() => this.setState({ value: randomUuid().replace(/[^a-zA-Z0-9]+/g, '') })}
                  disabled={this.state.isSubmitting}
                >
                  <KeyRefreshIcon fontSize='small' />
                </IconButton>
              </InputAdornment>
            ) : undefined),
          }}
        />
        <SubmitButton
          aria-label="Save"
          color='primary'
          style={{
            visibility: !this.state.value || this.state.value === this.props.value
              ? 'hidden' : undefined
          }}
          isSubmitting={this.state.isSubmitting}
          onClick={() => {
            this.setState({ isSubmitting: true });
            this.props.onSave(this.state.value || '')
              .then(() => this.setState({
                isSubmitting: false,
                value: this.props.value,
              }))
              .catch(e => this.setState({
                isSubmitting: false,
              }));
          }}
        >Save</SubmitButton>
      </div>
    );
  }
Example #16
Source File: AdvancedSettings.tsx    From wonderland-frontend with MIT License 5 votes vote down vote up
function AdvancedSettings({ open, handleClose, slippage, onSlippageChange }: IAdvancedSettingsProps) {
    const [value, setValue] = useState(slippage);

    useEffect(() => {
        let timeount: any = null;
        clearTimeout(timeount);

        timeount = setTimeout(() => onSlippageChange(value), 1000);
        return () => clearTimeout(timeount);
    }, [value]);

    return (
        <Modal id="hades" open={open} onClose={handleClose} hideBackdrop>
            <Paper className="ohm-card ohm-popover">
                <div className="cross-wrap">
                    <IconButton onClick={handleClose}>
                        <SvgIcon color="primary" component={XIcon} />
                    </IconButton>
                </div>

                <p className="hades-title">Settings</p>

                <Box className="card-content">
                    <InputLabel htmlFor="slippage">
                        <p className="input-lable">Slippage</p>
                    </InputLabel>
                    <FormControl variant="outlined" color="primary" fullWidth>
                        <OutlinedInput
                            id="slippage"
                            value={value}
                            onChange={(e: any) => setValue(e.target.value)}
                            fullWidth
                            type="number"
                            className="bond-input"
                            endAdornment={
                                <InputAdornment position="end">
                                    <p className="percent">%</p>
                                </InputAdornment>
                            }
                        />
                        <div className="help-text">
                            <p className="text-bond-desc">Transaction may revert if price changes by more than slippage %</p>
                        </div>
                    </FormControl>
                </Box>
            </Paper>
        </Modal>
    );
}
Example #17
Source File: MapSearch.tsx    From covid19testing-map with GNU General Public License v3.0 5 votes vote down vote up
MapSearch = ({ map }: SearchProps) => {
  const classes = useStyles();

  const [searchBox, setSearchBox] = useState<any>();

  const onLoad = (ref: any) => {
    setSearchBox(ref);
  };

  const onPlacesChanged = () => {
    if (searchBox.getPlaces()[0] !== undefined) {
      map.fitBounds(searchBox.getPlaces()[0].geometry.viewport);
    }
  };

  const { isLoaded, loadError } = useLoadScript({
    googleMapsApiKey: REACT_APP_GCP_MAPS_API_KEY,
    libraries,
  });

  const handleSearchClick = React.useCallback(() => {
    trackUiClick('Search');
  }, []);

  const renderSearchBox = () => {
    return (
      <div id="search-input">
        <StandaloneSearchBox onLoad={onLoad} onPlacesChanged={onPlacesChanged}>
          <Input
            placeholder='Search places like "Albany", "NYU", or "94025"'
            className={classes.input}
            inputProps={{ 'aria-label': 'search google maps' }}
            onClick={handleSearchClick}
            startAdornment={
              <InputAdornment position="start">
                <SearchIcon />
              </InputAdornment>
            }
          />
        </StandaloneSearchBox>
      </div>
    );
  };
  if (loadError) {
    return <div>Search bar cannot be loaded right now, sorry.</div>;
  }

  return isLoaded ? renderSearchBox() : <div />;
}
Example #18
Source File: AdvancedSettings.tsx    From rugenerous-frontend with MIT License 5 votes vote down vote up
function AdvancedSettings({
  open,
  handleClose,
  slippage,
  recipientAddress,
  onRecipientAddressChange,
  onSlippageChange,
}: IAdvancedSettingsProps) {
  const [value, setValue] = useState(slippage);

  useEffect(() => {
    let timeount: any = null;
    clearTimeout(timeount);

    timeount = setTimeout(() => onSlippageChange(value), 1000);
    return () => clearTimeout(timeount);
  }, [value]);

  return (
    <Modal id="hades" open={open} onClose={handleClose} hideBackdrop>
      <Paper className="ohm-card ohm-popover">
        <div className="cross-wrap">
          <IconButton onClick={handleClose}>
            <SvgIcon color="primary" component={XIcon} />
          </IconButton>
        </div>

        <p className="hades-title">Settings</p>

        <Box className="card-content">
          <InputLabel htmlFor="slippage">
            <p className="input-lable">Slippage</p>
          </InputLabel>
          <FormControl variant="outlined" color="primary" fullWidth>
            <OutlinedInput
              id="slippage"
              value={value}
              onChange={(e: any) => setValue(e.target.value)}
              fullWidth
              type="number"
              className="bond-input"
              endAdornment={
                <InputAdornment position="end">
                  <p className="percent">%</p>
                </InputAdornment>
              }
            />
            <div className="help-text">
              <p className="text-bond-desc">Transaction may revert if price changes by more than slippage %</p>
            </div>
          </FormControl>

          <InputLabel htmlFor="recipient">
            <p className="input-lable">Recipient Address</p>
          </InputLabel>
          <FormControl variant="outlined" color="primary" fullWidth>
            <OutlinedInput
              className="bond-input"
              id="recipient"
              value={recipientAddress}
              onChange={onRecipientAddressChange}
              type="text"
            />
            <div className="help-text">
              <p className="text-bond-desc">
                Choose recipient address. By default, this is your currently connected address
              </p>
            </div>
          </FormControl>
        </Box>
      </Paper>
    </Modal>
  );
}
Example #19
Source File: EntitySearchBar.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
EntitySearchBar = () => {
  const classes = useStyles();

  const { filters, updateFilters } = useEntityList();
  const [search, setSearch] = useState(filters.text?.value ?? '');

  useDebounce(
    () => {
      updateFilters({
        text: search.length ? new EntityTextFilter(search) : undefined,
      });
    },
    250,
    [search, updateFilters],
  );

  return (
    <Toolbar className={classes.searchToolbar}>
      <FormControl>
        <Input
          aria-label="search"
          id="input-with-icon-adornment"
          className={classes.input}
          placeholder="Search"
          autoComplete="off"
          onChange={event => setSearch(event.target.value)}
          value={search}
          startAdornment={
            <InputAdornment position="start">
              <Search />
            </InputAdornment>
          }
          endAdornment={
            <InputAdornment position="end">
              <IconButton
                aria-label="clear search"
                onClick={() => setSearch('')}
                edge="end"
                disabled={search.length === 0}
              >
                <Clear />
              </IconButton>
            </InputAdornment>
          }
        />
      </FormControl>
    </Toolbar>
  );
}
Example #20
Source File: TextInput.stories.tsx    From anchor-web-app with Apache License 2.0 5 votes vote down vote up
textAdornmentInputProps = {
  endAdornment: <InputAdornment position="end">UST</InputAdornment>,
}
Example #21
Source File: ProductCreate.tsx    From ra-enterprise-demo with MIT License 5 votes vote down vote up
ProductCreate: FC<CreateProps> = props => {
    useDefineAppLocation('catalog.products.create');
    const classes = useStyles();
    return (
        <Create actions={<CreateActions />} {...props}>
            <TabbedForm>
                <FormTab label="resources.products.tabs.image">
                    <TextInput source="image" fullWidth validate={required()} />
                    <TextInput
                        source="thumbnail"
                        fullWidth
                        validate={required()}
                    />
                </FormTab>
                <FormTab label="resources.products.tabs.details" path="details">
                    <TextInput source="reference" validate={required()} />
                    <NumberInput
                        source="price"
                        validate={required()}
                        className={classes.price}
                        InputProps={{
                            startAdornment: (
                                <InputAdornment position="start">
                                    €
                                </InputAdornment>
                            ),
                        }}
                    />
                    <NumberInput
                        source="width"
                        validate={required()}
                        className={classes.width}
                        formClassName={classes.widthFormGroup}
                        InputProps={{
                            endAdornment: (
                                <InputAdornment position="start">
                                    cm
                                </InputAdornment>
                            ),
                        }}
                    />
                    <NumberInput
                        source="height"
                        validate={required()}
                        className={classes.height}
                        formClassName={classes.heightFormGroup}
                        InputProps={{
                            endAdornment: (
                                <InputAdornment position="start">
                                    cm
                                </InputAdornment>
                            ),
                        }}
                    />
                    <ReferenceInput
                        source="category_id"
                        reference="categories"
                        allowEmpty
                    >
                        <SelectInput source="name" />
                    </ReferenceInput>
                    <NumberInput
                        source="stock"
                        validate={required()}
                        className={classes.stock}
                    />
                </FormTab>
                <FormTab
                    label="resources.products.tabs.description"
                    path="description"
                >
                    <MarkdownInput source="description" label="" />
                </FormTab>
            </TabbedForm>
        </Create>
    );
}
Example #22
Source File: Input.tsx    From prompts-ai with MIT License 5 votes vote down vote up
export default function Input(props: Props) {
    const dispatch = useDispatch();

    const inputValue = useSelector(
        (state: RootState) => {
            const workspace = state.editor.present.workspaces.find(w => w.id === state.editor.present.currentWorkspaceId)!;
            return workspace.conversations.find(c => c.id === props.conversationId)!.inputValue;
        }
    );
    const hasStarted = useSelector(
        (state: RootState) => {
            const workspace = state.editor.present.workspaces.find(w => w.id === state.editor.present.currentWorkspaceId)!;
            return workspace.conversations.find(c => c.id === props.conversationId)!.parts.some(c => c.submitted);
        }
    );
    const onSend = () => {
        dispatch(sendMessageInConversationAsync(props.conversationId));
        props.afterSend();
        dispatch(normalizeConversations());
    }

    const onInputChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>) => {
        dispatch(updateConversationInputValue({conversationId: props.conversationId, inputValue: event.currentTarget.value}));
    }
    return <TextField multiline
                      label={'Message (Cmd+Enter to send)'}
                      InputLabelProps={{
                          shrink: true,
                      }}
                      placeholder={hasStarted ? 'Your response' : 'Start a conversation'}
                      value={inputValue}
                      onChange={onInputChange}
                      onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => {
                          if (event.metaKey && event.key === 'Enter') {
                              onSend();
                          }
                      }}
                      onKeyUp={(event: React.KeyboardEvent<HTMLDivElement>) => {
                          if (event.ctrlKey && event.key === 'Enter') {
                              onSend();
                          }
                      }}
                      variant={'outlined'}
                      fullWidth={true}
                      InputProps={{
                          endAdornment: (<InputAdornment position="end">
                              <IconButton edge="end" onClick={onSend}>
                                  <SendIcon />
                              </IconButton>
                          </InputAdornment>)
                      }}
    />;
}
Example #23
Source File: LabelDialog.tsx    From knboard with MIT License 5 votes vote down vote up
LabelDialog = () => {
  const theme = useTheme();
  const dispatch = useDispatch();
  const open = useSelector((state: RootState) => state.label.dialogOpen);
  const labels = useSelector(selectAllLabels);
  const [searchValue, setSearchValue] = useState("");
  const [creating, setCreating] = useState(false);
  const xsDown = useMediaQuery(theme.breakpoints.down("xs"));

  const filteredLabels = labels.filter((label) =>
    label.name.toLowerCase().match(searchValue.trim().toLowerCase())
  );

  const handleClose = () => {
    dispatch(setDialogOpen(false));
  };

  return (
    <Dialog
      open={open}
      onClose={handleClose}
      maxWidth="sm"
      fullWidth
      fullScreen={xsDown}
    >
      <Close onClose={handleClose} />
      <DialogTitle id="edit-labels">Edit labels</DialogTitle>
      <Container>
        <Flex
          css={css`
            align-items: flex-end;
            ${creating && "margin-bottom: 1rem;"}
          `}
        >
          <LabelCount>
            {filteredLabels.length} label{filteredLabels.length !== 1 && "s"}
          </LabelCount>
          <div>
            <Hidden xsDown implementation="css">
              <TextField
                value={searchValue}
                onChange={(e) => setSearchValue(e.target.value)}
                placeholder="Search labels"
                InputProps={{
                  startAdornment: (
                    <InputAdornment
                      position="start"
                      css={css`
                        color: #ccc;
                      `}
                    >
                      <FontAwesomeIcon icon={faSearch} />
                    </InputAdornment>
                  ),
                }}
              />
            </Hidden>
          </div>
          <Button
            size="small"
            color="primary"
            variant="contained"
            onClick={() => setCreating(true)}
            css={css`
              margin-right: 1rem;
            `}
          >
            New label
          </Button>
        </Flex>
        {creating && <LabelCreate setCreating={setCreating} />}
        <Table>
          {filteredLabels.map((label) => (
            <LabelRow key={label.id + label.color + label.name} label={label} />
          ))}
        </Table>
      </Container>
    </Dialog>
  );
}
Example #24
Source File: App.tsx    From isitworththecost with MIT License 4 votes vote down vote up
function App() {
    const classes = useStyles()

    const [values, setValues] = React.useState({
        timeCost: { value: 25, unit: 'dollars', period: 'hour' },
        serviceCost: { value: 125, unit: 'dollars', period: 'month' },
        trainingTime: { value: 2, unit: 'hour', period: null },
        timeSavings: { value: 60, unit: 'min', period: 'day' },
        peopleCount: { value: 1, unit: null, period: null },

        savingPeriodCost: 'year',
        savingPeriodPeople: 'day',
        paybackPeriod: 'day',
    })

    const [costs, setCosts] = React.useState({
        employeePerYear: 0,
        servicePerYear: 0,
        trainingPerYear: 0,
        savingsPerYear: 0,
        freeTimePerYear: 0,
        paybackTimePerYear: 0,
    })

    const handleChange = (prop: string, key: string | null = null) => (
        event: ChangeEvent<HTMLInputElement | { value: unknown }>,
    ): void => {
        let val: any = event.target.value
        if (key === null) {
            setValues({
                ...values,
                [prop]: val,
            })
        } else {
            if (key === 'value' && (val < 0 || isNaN(val))) {
                val = 0
            }
            setValues({
                ...values,
                [prop]: {
                    //@ts-ignore
                    value: values[prop].value,
                    //@ts-ignore
                    unit: values[prop].unit,
                    //@ts-ignore
                    period: values[prop].period,
                    //@ts-ignore
                    [key]: val,
                },
            })
        }
    }

    useEffect(() => {
        // save this to state for now for ease of visibility
        const employeePerYear =
            values.timeCost.value * periodToYear(values.timeCost.period, 1)
        const servicePerYear =
            values.serviceCost.value *
            periodToYear(values.serviceCost.period, 1)

        // assumes amortisation period of 1 year
        const trainingPerYear =
            unitToYear(values.trainingTime.unit, values.trainingTime.value) *
            employeePerYear *
            values.peopleCount.value

        const freeTimePerYear =
            periodToYear(
                values.timeSavings.period,
                unitToYear(values.timeSavings.unit, values.timeSavings.value),
            ) * values.peopleCount.value

        const savingsPerYear =
            employeePerYear * freeTimePerYear - servicePerYear - trainingPerYear

        const paybackTimePerYear =
            (trainingPerYear + servicePerYear) / employeePerYear

        setCosts({
            employeePerYear,
            servicePerYear,
            trainingPerYear,
            savingsPerYear,
            freeTimePerYear,
            paybackTimePerYear,
        })
    }, [values])

    return (
        <Container maxWidth={'md'}>
            <Paper className={classes.root} variant={'outlined'}>
                <div className={classes.heading}>
                    <TopControls />
                    <Typography variant="h2" component="h1">
                        Is it worth the cost?
                    </Typography>
                    <Typography variant="h5" component="p" gutterBottom>
                        A simple check on whether purchasing a service is worth
                        the cost.
                    </Typography>
                </div>
                <Grid container>
                    <Grid item xs={12} md={6}>
                        <h2>Basics</h2>
                        <p>1. Cost of your time or an employees time.</p>
                        <FormControl
                            className={clsx(classes.margin, classes.textField)}
                            variant="outlined"
                        >
                            <InputLabel htmlFor="time-cost">
                                Time Cost
                            </InputLabel>
                            <OutlinedInput
                                id="time-cost"
                                value={values.timeCost.value}
                                type="number"
                                onChange={handleChange('timeCost', 'value')}
                                startAdornment={
                                    <InputAdornment position="start">
                                        <AttachMoneyIcon />
                                    </InputAdornment>
                                }
                                labelWidth={80}
                            />
                        </FormControl>
                        <FormControl
                            variant="outlined"
                            className={classes.margin}
                        >
                            <InputLabel htmlFor="time-cost-unit">
                                per
                            </InputLabel>
                            <Select
                                native
                                value={values.timeCost.period}
                                onChange={handleChange('timeCost', 'period')}
                                labelWidth={40}
                                inputProps={{
                                    name: 'per',
                                    id: 'time-cost-unit',
                                }}
                            >
                                <option value={'hour'}>hour</option>
                                <option value={'day'}>day</option>
                                <option value={'week'}>week</option>
                                <option value={'month'}>month</option>
                                <option value={'year'}>year</option>
                            </Select>
                        </FormControl>
                        <p>2. Cost of the service under consideration.</p>
                        <FormControl
                            className={clsx(classes.margin, classes.textField)}
                            variant="outlined"
                        >
                            <InputLabel htmlFor="service-cost">
                                Service Cost
                            </InputLabel>
                            <OutlinedInput
                                id="service-cost"
                                value={values.serviceCost.value}
                                type="number"
                                onChange={handleChange('serviceCost', 'value')}
                                startAdornment={
                                    <InputAdornment position="start">
                                        <AttachMoneyIcon />
                                    </InputAdornment>
                                }
                                labelWidth={95}
                            />
                        </FormControl>
                        <FormControl
                            variant="outlined"
                            className={classes.margin}
                        >
                            <InputLabel htmlFor="service-cost-period">
                                per
                            </InputLabel>
                            <Select
                                native
                                value={values.serviceCost.period}
                                onChange={handleChange('serviceCost', 'period')}
                                labelWidth={40}
                                inputProps={{
                                    name: 'per',
                                    id: 'service-cost-period',
                                }}
                            >
                                {/*<option value={'hour'}>hour</option>*/}
                                <option value={'day'}>day</option>
                                <option value={'week'}>week</option>
                                <option value={'month'}>month</option>
                                <option value={'year'}>year</option>
                            </Select>
                        </FormControl>
                        <p>
                            3. Estimate the training time required (one person).
                        </p>
                        <FormControl
                            fullWidth
                            className={clsx(classes.margin, classes.textField)}
                            variant="outlined"
                        >
                            <InputLabel htmlFor="training-time">
                                Training Time
                            </InputLabel>
                            <OutlinedInput
                                id="training-time"
                                value={values.trainingTime.value}
                                type="number"
                                onChange={handleChange('trainingTime', 'value')}
                                startAdornment={
                                    <InputAdornment position="start">
                                        <AccessTimeIcon />
                                    </InputAdornment>
                                }
                                labelWidth={105}
                            />
                        </FormControl>
                        <FormControl
                            variant="outlined"
                            className={classes.margin}
                        >
                            <Select
                                native
                                value={values.trainingTime.unit}
                                onChange={handleChange('trainingTime', 'unit')}
                                inputProps={{
                                    name: 'per',
                                    id: 'training-time-unit',
                                }}
                            >
                                <option value={'hour'}>hours</option>
                                <option value={'day'}>days</option>
                                <option value={'week'}>weeks</option>
                                <option value={'month'}>months</option>
                                {/*<option value={'year'}>years</option>*/}
                            </Select>
                        </FormControl>
                        <p>
                            4. Estimate the time this service will save (one
                            person).
                        </p>
                        <FormControl
                            className={clsx(classes.margin, classes.textField)}
                            variant="outlined"
                        >
                            <InputLabel htmlFor="time-savings">
                                Time Saved
                            </InputLabel>
                            <OutlinedInput
                                id="time-savings"
                                type="number"
                                value={values.timeSavings.value}
                                onChange={handleChange('timeSavings', 'value')}
                                startAdornment={
                                    <InputAdornment position="start">
                                        <AccessTimeIcon />
                                    </InputAdornment>
                                }
                                labelWidth={80}
                            />
                        </FormControl>
                        <FormControl
                            variant="outlined"
                            className={classes.margin}
                        >
                            <Select
                                native
                                value={values.timeSavings.unit}
                                onChange={handleChange('timeSavings', 'unit')}
                            >
                                <option value={'minute'}>minutes</option>
                                <option value={'hour'}>hours</option>
                                <option value={'day'}>days</option>
                                <option value={'week'}>weeks</option>
                                <option value={'month'}>months</option>
                            </Select>
                        </FormControl>
                        <FormControl
                            variant="outlined"
                            className={classes.margin}
                        >
                            <InputLabel htmlFor="time-savings-period">
                                per
                            </InputLabel>
                            <Select
                                id={'time-savings-period'}
                                native
                                value={values.timeSavings.period}
                                onChange={handleChange('timeSavings', 'period')}
                                labelWidth={40}
                            >
                                <option value={'hour'}>hour</option>
                                <option value={'day'}>day</option>
                                <option value={'week'}>week</option>
                                <option value={'month'}>month</option>
                                <option value={'year'}>year</option>
                            </Select>
                        </FormControl>
                        <p>5. Number of people using the service.</p>
                        <FormControl
                            className={clsx(classes.margin, classes.textField)}
                            variant="outlined"
                        >
                            <InputLabel htmlFor="people-count">
                                People
                            </InputLabel>
                            <OutlinedInput
                                id="people-count"
                                type="number"
                                value={values.peopleCount.value}
                                onChange={handleChange('peopleCount', 'value')}
                                startAdornment={
                                    <InputAdornment position="start">
                                        <EmojiPeopleIcon />
                                    </InputAdornment>
                                }
                                labelWidth={50}
                            />
                        </FormControl>
                    </Grid>
                    <Grid item xs={12} md={6}>
                        <Breakdown
                            values={values}
                            costs={costs}
                            handleChange={handleChange}
                        />
                    </Grid>
                </Grid>
            </Paper>
        </Container>
    )
}
Example #25
Source File: Settings.tsx    From swap-ui with Apache License 2.0 4 votes vote down vote up
function SettingsDetails() {
  const styles = useStyles();

  const { slippage, setSlippage, fairOverride, setFairOverride } =
    useSwapContext();
  const [showSettingsDialog, setShowSettingsDialog] = useState(false);
  const fair = useSwapFair();
  const { swapClient } = useDexContext();

  const setSlippageHandler = (value?: number) => {
    setSlippage(!value || value < 0 ? 0 : value);
  };

  return (
    <div style={{ padding: "15px", width: "305px" }}>
      <Typography style={{ fontWeight: "bold" }}>Settings</Typography>
      <div>
        <div style={{ marginTop: "10px" }}>
          <Typography color="textSecondary" style={{ fontSize: "12px" }}>
            Slippage tolerance
          </Typography>
          <TextField
            type="number"
            placeholder="Error tolerance percentage"
            value={slippage}
            onChange={(e) => setSlippageHandler(parseFloat(e.target.value))}
            style={{
              display: "flex",
              justifyContent: "center",
              flexDirection: "column",
            }}
            InputProps={{
              endAdornment: <InputAdornment position="end">%</InputAdornment>,
            }}
          />
        </div>
        <div style={{ marginTop: "10px" }}>
          <Typography color="textSecondary" style={{ fontSize: "12px" }}>
            Fair price
          </Typography>
          <div style={{ display: "flex" }}>
            <TextField
              type="number"
              placeholder="Fair price override"
              value={fair}
              onChange={(e) => setFairOverride(parseFloat(e.target.value))}
              style={{
                marginRight: "10px",
                flex: 1,
                display: "flex",
                justifyContent: "center",
                flexDirection: "column",
              }}
              disabled={fairOverride === null}
            />
            <Button
              component="div"
              variant="contained"
              onClick={() => {
                if (fair === undefined) {
                  console.error("Fair is undefined");
                  return;
                }
                if (fairOverride === null) {
                  setFairOverride(fair);
                } else {
                  setFairOverride(null);
                }
              }}
              className={
                fairOverride === null
                  ? styles.fairAutoSelected
                  : styles.fairAuto
              }
            >
              Auto
            </Button>
          </div>
        </div>
        <div style={{ margin: "10px 0px" }}>
          <CloseNewAccountsSwitch />
        </div>
        <Button
          variant="contained"
          fullWidth
          disabled={swapClient.program.provider.wallet.publicKey === null}
          onClick={() => setShowSettingsDialog(true)}
        >
          Manage Dex Accounts
        </Button>
      </div>
      <OpenOrdersDialog
        open={showSettingsDialog}
        onClose={() => setShowSettingsDialog(false)}
      />
    </div>
  );
}
Example #26
Source File: UserEdit.tsx    From clearflask with Apache License 2.0 4 votes vote down vote up
render() {
    const userId = this.props.userId || this.state.createdUserId;

    const isMe = !!this.props.loggedInUser && this.props.loggedInUser.userId === userId;
    const isModOrAdminLoggedIn = this.props.server.isModOrAdminLoggedIn();

    var content;
    if (!userId) {
      // Create form
      if (!isModOrAdminLoggedIn) return null;
      content = (
        <div key='create-form' className={this.props.classes.section}>
          <PanelTitle text={this.props.t('create-user')} />
          <Grid container alignItems='center' className={this.props.classes.item}>
            <Grid item xs={12} sm={6}><Typography>{this.props.t('avatar')}</Typography></Grid>
            <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
              <AvatarDisplay user={{
                name: this.state.displayName || '',
              }} size={40} />
            </Grid>
          </Grid>
          <Grid container alignItems='center' className={this.props.classes.item}>
            <Grid item xs={12} sm={6}><Typography>{this.props.t('displayname')}</Typography></Grid>
            <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
              <TextField
                value={this.state.displayName || ''}
                onChange={e => this.setState({ displayName: e.target.value })}
              />
              <Button aria-label="Create" color='primary' style={{
                visibility:
                  !this.state.displayName ? 'hidden' : undefined
              }} onClick={async () => {
                if (!this.state.displayName || !isModOrAdminLoggedIn) {
                  return;
                }
                const newUserAdmin = await (await this.props.server.dispatchAdmin()).userCreateAdmin({
                  projectId: this.props.server.getProjectId(),
                  userCreateAdmin: { name: this.state.displayName },
                });
                this.setState({
                  createdUserId: newUserAdmin.userId,
                  userAdmin: newUserAdmin,
                  displayName: undefined,
                });
              }}>{this.props.t('save')}</Button>
            </Grid>
          </Grid>
        </div>
      );
    } else if (!isModOrAdminLoggedIn && !isMe) {
      // View only
      content = (
        <div key='view-only' className={this.props.classes.section}>
          <PanelTitle text={this.props.t('info')} />
          <Grid container alignItems='center' className={this.props.classes.item}>
            <Grid item xs={12} sm={6}><Typography>{this.props.t('avatar')}</Typography></Grid>
            <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
              <AvatarDisplay user={this.props.user} size={40} />
            </Grid>
          </Grid>
          <Grid container alignItems='center' className={this.props.classes.item}>
            <Grid item xs={12} sm={6}><Typography>{this.props.t('displayname')}</Typography></Grid>
            <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
              {DisplayUserName(this.props.user)}
            </Grid>
          </Grid>
          <Grid container alignItems='center' className={this.props.classes.item}>
            <Grid item xs={12} sm={6}><Typography>{this.props.t('registered')}</Typography></Grid>
            <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
              <TimeAgo date={this.props.user?.created || 0} />
            </Grid>
          </Grid>
        </div>
      );
    } else {
      // Edit form (for both self and by admin/mod)
      var user: Client.UserMe | Admin.UserAdmin | undefined;
      var balance: number | undefined;
      if (this.props.loggedInUser?.userId === userId) {
        user = this.props.loggedInUser;
        balance = this.props.loggedInUserBalance;
      } else {
        user = this.state.userAdmin;
        balance = this.state.userAdmin?.balance;
        if (this.userAdminFetchedForUserId !== userId) {
          this.userAdminFetchedForUserId = userId;
          this.props.server.dispatchAdmin().then(d => d.userGetAdmin({
            projectId: this.props.server.getProjectId(),
            userId,
          }))
            .then(userAdmin => this.setState({
              userAdmin,
              userAdminStatus: Status.FULFILLED,
            }))
            .catch(e => this.setState({
              userAdminStatus: Status.REJECTED,
            }));
        }
      }

      if (!user) {
        return (<LoadingPage />);
      }

      const balanceAdjustmentHasError = !!this.state.balanceAdjustment && (!parseInt(this.state.balanceAdjustment) || !+this.state.balanceAdjustment || parseInt(this.state.balanceAdjustment) !== parseFloat(this.state.balanceAdjustment));

      const browserPushControl = this.renderBrowserPushControl(isMe, user);
      // const androidPushControl = this.renderMobilePushControl(MobileNotificationDevice.Android);
      // const iosPushControl = this.renderMobilePushControl(MobileNotificationDevice.Ios);
      const emailControl = this.renderEmailControl(isMe, user);

      const isPushOrAnon = !user.email && !user.isExternal;

      const categoriesWithSubscribe = (this.props.categories || []).filter(c => !!c.subscription);

      content = (
        <React.Fragment key='edit-user'>
          <div className={this.props.classes.section}>
            <PanelTitle text={this.props.t('account')} />
            <Grid container alignItems='center' className={this.props.classes.item}>
              <Grid item xs={12} sm={6}><Typography>{this.props.t('avatar')}</Typography></Grid>
              <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
                <AvatarDisplay user={{
                  ...user,
                  ...(this.state.displayName !== undefined ? {
                    name: this.state.displayName,
                  } : {}),
                  ...(this.state.email !== undefined ? {
                    email: this.state.email,
                  } : {}),
                }} size={40} />
              </Grid>
            </Grid>
            <Grid container alignItems='center' className={this.props.classes.item}>
              <Grid item xs={12} sm={6}><Typography>{this.props.t('displayname')}</Typography></Grid>
              <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
                {!!user.isExternal ? (
                  <Tooltip title={this.props.t('cannot-be-changed')} placement='top-start'>
                    <Typography>{user.name || 'None'}</Typography>
                  </Tooltip>
                ) : (
                  <>
                    <TextField
                      id='displayName'
                      error={!user.name}
                      value={(this.state.displayName === undefined ? user.name : this.state.displayName) || ''}
                      onChange={e => this.setState({ displayName: e.target.value })}
                    />
                    <Button aria-label={this.props.t('save')} color='primary' style={{
                      visibility:
                        !this.state.displayName
                          || this.state.displayName === user.name
                          ? 'hidden' : undefined
                    }} onClick={async () => {
                      if (!this.state.displayName
                        || !user
                        || this.state.displayName === user.name) {
                        return;
                      }
                      if (isModOrAdminLoggedIn) {
                        const newUserAdmin = await (await this.props.server.dispatchAdmin()).userUpdateAdmin({
                          projectId: this.props.server.getProjectId(),
                          userId: userId!,
                          userUpdateAdmin: { name: this.state.displayName },
                        });
                        this.setState({ displayName: undefined, userAdmin: newUserAdmin });
                      } else {
                        await (await this.props.server.dispatch()).userUpdate({
                          projectId: this.props.server.getProjectId(),
                          userId: userId!,
                          userUpdate: { name: this.state.displayName },
                        });
                        this.setState({ displayName: undefined });
                      }
                    }}>{this.props.t('save')}</Button>
                  </>
                )}
              </Grid>
            </Grid>
            <Grid container alignItems='center' className={this.props.classes.item}>
              <Grid item xs={12} sm={6}><Typography>{this.props.t('email')}</Typography></Grid>
              <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
                {!!user.isExternal ? (
                  <Tooltip title={this.props.t('cannot-be-changed')} placement='top-start'>
                    <Typography>{user.email || this.props.t('none')}</Typography>
                  </Tooltip>
                ) : (
                  <>
                    <TextField
                      id='email'
                      value={(this.state.email === undefined ? user.email : this.state.email) || ''}
                      onChange={e => this.setState({ email: e.target.value })}
                      autoFocus={!!this.state.createdUserId}
                    />
                    <Button aria-label={this.props.t('save')} color='primary' style={{
                      visibility:
                        !this.state.email
                          || this.state.email === user.email
                          ? 'hidden' : undefined
                    }} onClick={async () => {
                      if (!this.state.email
                        || !user
                        || this.state.email === user.email) {
                        return;
                      }
                      if (isModOrAdminLoggedIn) {
                        const newUserAdmin = await (await this.props.server.dispatchAdmin()).userUpdateAdmin({
                          projectId: this.props.server.getProjectId(),
                          userId: userId!,
                          userUpdateAdmin: { email: this.state.email },
                        });
                        this.setState({ email: undefined, userAdmin: newUserAdmin });
                      } else {
                        await (await this.props.server.dispatch()).userUpdate({
                          projectId: this.props.server.getProjectId(),
                          userId: userId!,
                          userUpdate: { email: this.state.email },
                        });
                        this.setState({ email: undefined });
                      }
                    }}>{this.props.t('save')}</Button>
                  </>
                )}
              </Grid>
            </Grid>
            {!user.isExternal && (
              <Grid container alignItems='center' className={this.props.classes.item}>
                <Grid item xs={12} sm={6}><Typography>{this.props.t('password-0')}</Typography></Grid>
                <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
                  <TextField
                    id='password'
                    value={this.state.password || ''}
                    onChange={e => this.setState({ password: e.target.value })}
                    type={this.state.revealPassword ? 'text' : 'password'}
                    disabled={!this.state.email && !user.email}
                    InputProps={{
                      endAdornment: (
                        <InputAdornment position='end'>
                          <IconButton
                            aria-label='Toggle password visibility'
                            onClick={() => this.setState({ revealPassword: !this.state.revealPassword })}
                            disabled={!this.state.email && !user.email}
                          >
                            {this.state.revealPassword ? <VisibilityIcon fontSize='small' /> : <VisibilityOffIcon fontSize='small' />}
                          </IconButton>
                        </InputAdornment>
                      ),
                    }}
                  />
                  <Button aria-label={this.props.t('save')} color='primary' style={{
                    visibility:
                      !this.state.password
                        || this.state.password === user.name
                        ? 'hidden' : undefined
                  }} onClick={async () => {
                    if (!this.state.password
                      || !user) {
                      return;
                    }
                    if (isModOrAdminLoggedIn) {
                      const newUserAdmin = await (await this.props.server.dispatchAdmin()).userUpdateAdmin({
                        projectId: this.props.server.getProjectId(),
                        userId: userId!,
                        userUpdateAdmin: { password: this.state.password },
                      });
                      this.setState({ password: undefined, userAdmin: newUserAdmin });
                    } else {
                      await (await this.props.server.dispatch()).userUpdate({
                        projectId: this.props.server.getProjectId(),
                        userId: userId!,
                        userUpdate: { password: this.state.password },
                      });
                      this.setState({ password: undefined });
                    }
                  }}>{this.props.t('save')}</Button>
                </Grid>
              </Grid>
            )}
            {this.props.credits && (
              <Grid container alignItems='center' className={this.props.classes.item}>
                <Grid item xs={12} sm={6}><Typography>{this.props.t('balance')}</Typography></Grid>
                <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
                  <CreditView val={balance || 0} credits={this.props.credits} />
                  {isMe && !!this.props.credits?.creditPurchase?.redirectUrl && (
                    <Button
                      component={'a' as any}
                      className={this.props.classes.linkGetMore}
                      color='primary'
                      href={this.props.credits.creditPurchase.redirectUrl}
                      target='_blank'
                      underline='none'
                      rel='noopener nofollow'
                    >
                      {this.props.credits.creditPurchase.buttonTitle || 'Get more'}
                    </Button>
                  )}
                  {isModOrAdminLoggedIn && (
                    <>
                      <IconButton onClick={() => this.setState({ transactionCreateOpen: !this.state.transactionCreateOpen })}>
                        <EditIcon />
                      </IconButton>
                      <Collapse in={this.state.transactionCreateOpen}>
                        <div>
                          <TextField
                            label='Adjustment amount'
                            value={this.state.balanceAdjustment || ''}
                            error={balanceAdjustmentHasError}
                            helperText={balanceAdjustmentHasError ? 'Invalid number' : (
                              !this.state.balanceAdjustment ? undefined : (
                                <CreditView
                                  val={+this.state.balanceAdjustment}
                                  credits={this.props.credits}
                                />
                              ))}
                            onChange={e => this.setState({ balanceAdjustment: e.target.value })}
                          />
                          <TextField
                            label='Transaction note'
                            value={this.state.balanceDescription || ''}
                            onChange={e => this.setState({ balanceDescription: e.target.value })}
                          />
                          <Button aria-label="Save" color='primary' style={{
                            visibility:
                              (this.state.balanceAdjustment || 0) === 0
                                ? 'hidden' : undefined
                          }} onClick={async () => {
                            if (this.state.balanceAdjustment === undefined
                              || +this.state.balanceAdjustment === 0
                              || !user) {
                              return;
                            }
                            const dispatcher = await this.props.server.dispatchAdmin();
                            const newUserAdmin = await dispatcher.userUpdateAdmin({
                              projectId: this.props.server.getProjectId(),
                              userId: userId!,
                              userUpdateAdmin: {
                                transactionCreate: {
                                  amount: +this.state.balanceAdjustment,
                                  summary: this.state.balanceDescription,
                                }
                              },
                            });
                            this.setState({
                              userAdmin: newUserAdmin,
                              transactionCreateOpen: false,
                              balanceAdjustment: undefined,
                              balanceDescription: undefined,
                            });
                          }}>Save</Button>
                        </div>
                      </Collapse>
                    </>
                  )}
                </Grid>
              </Grid>
            )}
            {isModOrAdminLoggedIn && (
              <>
                <Grid container alignItems='center' className={this.props.classes.item}>
                  <Grid item xs={12} sm={6}><Typography>{this.props.t('is-moderator')}</Typography></Grid>
                  <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
                    <FormControlLabel
                      control={(
                        <Switch
                          color='default'
                          checked={!!user.isMod}
                          onChange={async (e, checked) => {
                            const dispatcher = await this.props.server.dispatchAdmin();
                            const newUserAdmin = await dispatcher.userUpdateAdmin({
                              projectId: this.props.server.getProjectId(),
                              userId: userId!,
                              userUpdateAdmin: { isMod: !user?.isMod },
                            });
                            this.setState({ password: undefined, userAdmin: newUserAdmin });
                          }}
                        />
                      )}
                      label={(
                        <FormHelperText component='span'>
                          {user.isMod ? this.props.t('yes') : this.props.t('no')}
                        </FormHelperText>
                      )}
                    />
                  </Grid>
                </Grid>
                <Grid container alignItems='center' className={this.props.classes.item}>
                  <Grid item xs={12} sm={6}><Typography>{this.props.t('user-id')}</Typography></Grid>
                  <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
                    <Typography>{userId}</Typography>
                  </Grid>
                </Grid>
              </>
            )}
            {!!isMe && !this.props.suppressSignOut && (
              <Grid container alignItems='center' className={this.props.classes.item}>
                <Grid item xs={12} sm={6}><Typography>
                  {this.props.t('sign-out-of-your-account')}
                  {!!isPushOrAnon && (
                    <Collapse in={!!this.state.signoutWarnNoEmail}>
                      <Alert
                        variant='outlined'
                        severity='warning'
                      >
                        {this.props.t('please-add-an-email-before')}
                      </Alert>
                    </Collapse>
                  )}
                </Typography></Grid>
                <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
                  <Button
                    disabled={!!isPushOrAnon && !!this.state.signoutWarnNoEmail}
                    onClick={() => {
                      if (isPushOrAnon) {
                        this.setState({ signoutWarnNoEmail: true });
                      } else {
                        this.props.server.dispatch().then(d => d.userLogout({
                          projectId: this.props.server.getProjectId(),
                        }));
                      }
                    }}
                  >Sign out</Button>
                </Grid>
              </Grid>
            )}
            <Grid container alignItems='center' className={this.props.classes.item}>
              <Grid item xs={12} sm={6}><Typography>{this.props.t('delete-account')}</Typography></Grid>
              <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
                <Button
                  onClick={() => this.setState({ deleteDialogOpen: true })}
                >Delete</Button>
                <Dialog
                  open={!!this.state.deleteDialogOpen}
                  onClose={() => this.setState({ deleteDialogOpen: false })}
                >
                  <DialogTitle>Delete account?</DialogTitle>
                  <DialogContent>
                    <DialogContentText>{isMe
                      ? 'By deleting your account, you will be signed out of your account and your account will be permanently deleted including all of your data.'
                      : 'Are you sure you want to permanently delete this user?'}</DialogContentText>
                  </DialogContent>
                  <DialogActions>
                    <Button onClick={() => this.setState({ deleteDialogOpen: false })}>Cancel</Button>
                    <Button style={{ color: this.props.theme.palette.error.main }} onClick={async () => {
                      if (isModOrAdminLoggedIn) {
                        await (await this.props.server.dispatchAdmin()).userDeleteAdmin({
                          projectId: this.props.server.getProjectId(),
                          userId: userId!,
                        });
                      } else {
                        await (await this.props.server.dispatch()).userDelete({
                          projectId: this.props.server.getProjectId(),
                          userId: userId!,
                        });
                      }
                      this.props.onDeleted?.();
                      this.setState({ deleteDialogOpen: false });
                    }}>{this.props.t('delete')}</Button>
                  </DialogActions>
                </Dialog>
              </Grid>
            </Grid>
          </div>
          <div className={this.props.classes.section}>
            <PanelTitle text={this.props.t('notifications')} />
            {browserPushControl && (
              <Grid container alignItems='center' className={this.props.classes.item}>
                <Grid item xs={12} sm={6}><Typography>{this.props.t('browser-desktop-messages')}</Typography></Grid>
                <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>{browserPushControl}</Grid>
              </Grid>
            )}
            {/* {androidPushControl && (
              <Grid container alignItems='center' className={this.props.classes.item}>
                <Grid item xs={12} sm={6}><Typography>Android Push messages</Typography></Grid>
                <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>{androidPushControl}</Grid>
              </Grid>
            )}
            {iosPushControl && (
              <Grid container alignItems='center' className={this.props.classes.item}>
                <Grid item xs={12} sm={6}><Typography>Apple iOS Push messages</Typography></Grid>
                <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>{iosPushControl}</Grid>
              </Grid>
            )} */}
            {emailControl && (
              <Grid container alignItems='center' className={this.props.classes.item}>
                <Grid item xs={12} sm={6}>
                  <Typography>
                    {this.props.t('email')}
                    {user.email !== undefined && (<Typography variant='caption'>&nbsp;({truncateWithElipsis(20, user.email)})</Typography>)}
                  </Typography>
                </Grid>
                <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>{emailControl}</Grid>
              </Grid>
            )}
            {categoriesWithSubscribe.map(category => !!user && (
              <Grid container alignItems='center' className={this.props.classes.item}>
                <Grid item xs={12} sm={6}>
                  <Typography>{this.props.t('new-category', { category: category.name })}</Typography>
                </Grid>
                <Grid item xs={12} sm={6} className={this.props.classes.itemControls}>
                  {this.renderCategorySubscribeControl(category, isMe, user)}
                </Grid>
              </Grid>
            ))}
          </div>
        </React.Fragment>
      );
    }

    return (
      <div className={classNames(this.props.className, this.props.classes.settings)}>
        {content}
      </div>
    );
  }
Example #27
Source File: TechDocsSearch.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
TechDocsSearchBar = (props: TechDocsSearchProps) => {
  const { entityId, debounceTime = 150 } = props;
  const [open, setOpen] = useState(false);
  const navigate = useNavigate();
  const {
    term,
    setTerm,
    setFilters,
    result: { loading, value: searchVal },
  } = useSearch();
  const classes = useStyles();
  const [options, setOptions] = useState<any[]>([]);
  useEffect(() => {
    let mounted = true;

    if (mounted && searchVal) {
      // TODO: Change this into getting only subset of search results from the BE in the first place
      // once pagination is implemented for search engines
      // See: https://github.com/backstage/backstage/issues/6062
      const searchResults = searchVal.results.slice(0, 10);
      setOptions(searchResults);
    }
    return () => {
      mounted = false;
    };
  }, [loading, searchVal]);

  const [value, setValue] = useState<string>(term);

  useDebounce(() => setTerm(value), debounceTime, [value]);

  // Update the filter context when the entityId changes, e.g. when the search
  // bar continues to be rendered, navigating between different TechDocs sites.
  const { kind, name, namespace } = entityId;
  useEffect(() => {
    setFilters(prevFilters => {
      return {
        ...prevFilters,
        kind,
        namespace,
        name,
      };
    });
  }, [kind, namespace, name, setFilters]);

  const handleQuery = (e: ChangeEvent<HTMLInputElement>) => {
    if (!open) {
      setOpen(true);
    }
    setValue(e.target.value);
  };

  const handleSelection = (_: any, selection: TechDocsSearchResult | null) => {
    if (selection?.document) {
      const { location } = selection.document;
      navigate(location);
    }
  };

  return (
    <Autocomplete
      classes={{ root: classes.root }}
      data-testid="techdocs-search-bar"
      size="small"
      open={open}
      getOptionLabel={() => ''}
      filterOptions={x => {
        return x; // This is needed to get renderOption to be called after options change. Bug in material-ui?
      }}
      onClose={() => {
        setOpen(false);
      }}
      onFocus={() => {
        setOpen(true);
      }}
      onChange={handleSelection}
      blurOnSelect
      noOptionsText="No results found"
      value={null}
      options={options}
      renderOption={({ document, highlight }) => (
        <TechDocsSearchResultListItem
          result={document}
          lineClamp={3}
          asListItem={false}
          asLink={false}
          title={document.title}
          highlight={highlight}
        />
      )}
      loading={loading}
      renderInput={params => (
        <TextField
          {...params}
          data-testid="techdocs-search-bar-input"
          variant="outlined"
          fullWidth
          placeholder={`Search ${entityId.name} docs`}
          value={value}
          onChange={handleQuery}
          InputProps={{
            ...params.InputProps,
            startAdornment: (
              <InputAdornment position="start">
                <IconButton aria-label="Query" disabled>
                  <SearchIcon />
                </IconButton>
              </InputAdornment>
            ),
            endAdornment: (
              <React.Fragment>
                {loading ? (
                  <CircularProgress color="inherit" size={20} />
                ) : null}
                {params.InputProps.endAdornment}
              </React.Fragment>
            ),
          }}
        />
      )}
    />
  );
}
Example #28
Source File: ProductEdit.tsx    From ra-enterprise-demo with MIT License 4 votes vote down vote up
ProductEdit: FC<EditProps> = props => {
    const { resource, id } = props;

    const classes = useStyles();
    const notify = useNotify();

    const { loading } = useLock(
        resource as string,
        id as string,
        'todousername',
        {
            onFailure: () => {
                notify('ra-realtime.notification.lock.lockedBySomeoneElse');
            },
        }
    );

    if (loading) {
        return <CircularProgress />;
    }

    return (
        <Edit
            {...props}
            className={classes.root}
            title={<ProductTitle />}
            actions={<EditActions />}
            component="div"
        >
            <ProductEditFormWithPreview toolbar={<CustomToolbar />}>
                <Poster />
                <TextInput source="image" fullWidth />
                <TextInput source="thumbnail" fullWidth />
                <AccordionSection
                    label="resources.products.tabs.description"
                    data-tour-id="description-tab"
                    fullWidth
                >
                    <MarkdownInput source="description" label="" />
                </AccordionSection>
                <AccordionSection
                    label="resources.products.tabs.details"
                    fullWidth
                >
                    <TextInput source="reference" />
                    <NumberInput
                        source="price"
                        className={classes.price}
                        InputProps={{
                            startAdornment: (
                                <InputAdornment position="start">
                                    €
                                </InputAdornment>
                            ),
                        }}
                    />
                    <NumberInput
                        source="width"
                        className={classes.width}
                        formClassName={classes.widthFormGroup}
                        InputProps={{
                            endAdornment: (
                                <InputAdornment position="start">
                                    cm
                                </InputAdornment>
                            ),
                        }}
                    />
                    <NumberInput
                        source="height"
                        className={classes.height}
                        formClassName={classes.heightFormGroup}
                        InputProps={{
                            endAdornment: (
                                <InputAdornment position="start">
                                    cm
                                </InputAdornment>
                            ),
                        }}
                    />
                    <ReferenceInput source="category_id" reference="categories">
                        <SelectInput source="name" />
                    </ReferenceInput>
                    <NumberInput source="stock" className={classes.stock} />
                </AccordionSection>
                <AccordionSection
                    label="resources.products.tabs.reviews"
                    fullWidth
                >
                    <ReferenceManyField
                        label=""
                        reference="reviews"
                        target="product_id"
                        pagination={<ReferenceManyFieldPagination />}
                        fullWidth
                    >
                        <Datagrid>
                            <DateField source="date" />
                            <CustomerReferenceField />
                            <StarRatingField />
                            <TextField
                                source="comment"
                                cellClassName={classes.comment}
                            />
                            <TextField source="status" />
                            <EditButton />
                        </Datagrid>
                    </ReferenceManyField>
                </AccordionSection>
            </ProductEditFormWithPreview>
        </Edit>
    );
}
Example #29
Source File: Detail.tsx    From Demae with MIT License 4 votes vote down vote up
Edit = ({ sku, onClose }: { sku: SKU, onClose: () => void }) => {
	const theme = useTheme();
	const [setProcessing] = useProcessing()
	const [provider] = useProviderBlank()
	const [product] = useAdminProviderProductDraft()
	const [images, setImages] = useState<File[]>([])
	const [name] = useTextField(sku?.name)
	const [caption] = useTextField(sku?.caption)
	const [price, setPrice] = useTextField(String(sku?.price), { inputProps: { pattern: "^[0-9]*$" } })
	const [taxRate, setTaxRate] = useTextField(String(sku?.taxRate), { inputProps: { pattern: "^([1-9]|[1-9][0-9])$" } })

	const [currency, setCurrency] = useSelect(sku.currency)
	const [inventory, setInventory] = useSelect(sku.inventory.type)
	const [stockValue, setStockValue] = useSelect(sku.inventory.value || "in_stock")
	const [quantity] = useTextField(String(sku.inventory.quantity || 0))

	const [description, setDescription] = useState(sku?.description || "");
	const [selectedTab, setSelectedTab] = useState<"write" | "preview">("write");

	const [showDrawer, closeDrawer] = useDrawer()
	const [showSnackbar] = useSnackbar()

	const currencyMenu = useMenu(SupportedCurrencies.map(c => {
		return {
			label: `${c.code} ${c.symbol}`,
			value: c.code,
		}
	}))

	const inventoryMenu = useMenu([
		{
			label: "Bucket",
			value: "bucket"
		},
		{
			label: "Finite",
			value: "finite"
		},
		{
			label: "Infinite",
			value: "infinite"
		}
	])

	const stockValueMenu = useMenu([
		{
			label: "In Stock",
			value: "in_stock"
		},
		{
			label: "Limited",
			value: "limited"
		},
		{
			label: "Out Of Stock",
			value: "out_of_stock"
		}
	])

	useEdit(async (event) => {
		event.preventDefault()
		if (!provider) return
		if (!product) return
		if (!sku) return
		setProcessing(true)
		const batch = firebase.firestore().batch()
		const uploadedImages = await Promise.all(uploadImages(images))

		const priceValue = price.value as string
		const taxRateValue = taxRate.value as string
		sku.name = name.value as string
		sku.caption = caption.value as string
		sku.description = description
		sku.price = Number(priceValue.replace(",", ""))
		sku.taxRate = Number(taxRateValue.replace(",", ""))
		sku.currency = currency.value as CurrencyCode
		sku.inventory = {
			type: inventory.value as StockType,
			value: stockValue.value as StockValue,
			quantity: Number(quantity.value)
		}
		sku.productReference = product.documentReference
		const nowPrice = product.price || {}
		var productPrice = nowPrice[sku.currency] || Infinity
		productPrice = Math.min(productPrice, sku.price)
		productPrice = Math.max(productPrice, 0)

		if (uploadedImages.length) {
			const fileterd = uploadedImages.filter(image => !!image) as StorageFile[]
			const images = fileterd.map(value => value.data())
			batch.set(sku.documentReference, {
				...sku.data(),
				images: firebase.firestore.FieldValue.arrayUnion(...images)
			}, { merge: true })
		} else {
			batch.set(sku.documentReference, {
				...sku.data(),
			}, { merge: true })
		}

		batch.update(product.documentReference, {
			price: {
				...nowPrice,
				[sku.currency]: productPrice
			}
		})

		await batch.commit()
		setProcessing(false)
		onClose()

		if (!sku.isAvailable) {
			if (sku.inventory.type === "finite") {
				const snapshot = await provider.products.collectionReference
					.doc(product.id)
					.collection("skus")
					.doc(sku.id)
					.collection("stocks")
					.get()
				const count = snapshot.docs.reduce((prev, current) => {
					return prev + current.data()!["count"]
				}, 0)
				if (count > 0) {
					showDrawer(
						<ActionSheet title="Do you want to make the SKU available?" actions={
							[
								{
									title: "YES",
									handler: async () => {
										sku.isAvailable = true
										await sku.update()
										showSnackbar("success", `You must publish your product for the changes to take effect.`)
										closeDrawer()
									}
								}
							]
						} />
					)
				}
			} else {
				showDrawer(
					<ActionSheet title="Do you want to make the SKU available?" actions={
						[
							{
								title: "YES",
								handler: async () => {
									sku.isAvailable = true
									await sku.update()
									showSnackbar("success", `You must publish your product for the changes to take effect.`)
									closeDrawer()
								}
							}
						]
					} />
				)
			}
		}
	})

	const uploadImages = (files: File[]) => {
		return files.map(file => {
			return uploadImage(file)
		})
	}

	const extension = (type: string) => {
		if (type === "image/jpeg") return "jpg"
		if (type === "image/png") return "png"
	}

	const uploadImage = (file: File): Promise<StorageFile | undefined> => {
		const id = firebase.firestore().collection("/dummy").doc().id
		const ref = firebase.storage().ref(product!.documentReference.path + `/images/${id}.${extension(file.type)}`)
		return new Promise((resolve, reject) => {
			ref.put(file).then(async (snapshot) => {
				if (snapshot.state === "success") {
					const storageFile = new StorageFile()
					if (snapshot.metadata.contentType) {
						storageFile.mimeType = snapshot.metadata.contentType
					}
					storageFile.path = ref.fullPath
					resolve(storageFile)
				} else {
					reject(undefined)
				}
			})
		})
	}

	const updatedAt = Dayjs(sku.updatedAt.toDate())
	return (
		<Paper elevation={0} style={{
			height: "100%",
			width: "100%",
			background: "inherit"
		}}>
			<Box width="100%" padding={2}>
				<Paper elevation={0} square={false} style={{
					height: "100%",
					width: "100%",
					marginBottom: theme.spacing(2)
				}}>
					<Box padding={2} height="100%">
						<article>
							<Box paddingBottom={1} display="flex">
								<Box flexGrow={1}>
									<Typography variant="h2">{sku.name}</Typography>
									<Box color="text.secondary">
										<Typography variant="caption">
											{`ID: ${sku.id}`} - {updatedAt.format("YYYY-MM-DD HH:mm:ss")}
										</Typography>
									</Box>
									<Box display="flex" alignItems="center" paddingY={1}>
										<Typography variant="subtitle1" style={{
											marginRight: theme.spacing(0.5),
										}}>Inventory Type</Typography>
										<Chip label={sku.inventory.type} variant="outlined" />
									</Box>
								</Box>
							</Box>
							<Divider />
							<Box paddingY={2}>
								<MediaController URLs={sku.imageURLs()} isEditing maxCount={MAXIMUM_NUMBER_OF_IMAGES}
									onDrop={(files) => {
										setImages(files)
									}}
									onDeleteImage={async (props) => {
										const { index } = props
										showDrawer(
											<ActionSheet title="Do you want to delete the image?" actions={
												[
													{
														title: "Delete",
														handler: async () => {
															const image = sku.images[index]
															const imageData = image.data()
															await sku.documentReference.update({
																images: firebase.firestore.FieldValue.arrayRemove(imageData)
															})
															showSnackbar("success", "The image has been removed.")
															closeDrawer()
														}
													}
												]
											} />
										)
									}}
									onDeleteUploadImage={(props) => {
										const { index } = props
										const _images = images.filter((value, idx) => index !== idx)
										setImages(_images)
									}}
									onError={() => {
										showSnackbar("error", `The maximum number of images is ${MAXIMUM_NUMBER_OF_IMAGES}.`)
									}} />
								<Box paddingTop={2}>
									<Box paddingBottom={2}>
										<Typography variant="subtitle1" gutterBottom>Name</Typography>
										<TextField variant="outlined" margin="dense" required {...name} fullWidth />
									</Box>
									<Box paddingBottom={2}>
										<Typography variant="subtitle1" gutterBottom>Caption</Typography>
										<TextField variant="outlined" margin="dense" required {...caption} fullWidth />
									</Box>
									<Box paddingBottom={2}>
										<Typography variant="subtitle1" gutterBottom>Description</Typography>
										<ReactMde
											value={description}
											onChange={setDescription}
											selectedTab={selectedTab}
											onTabChange={setSelectedTab}
											generateMarkdownPreview={markdown =>
												Promise.resolve(converter.makeHtml(markdown))
											}
										/>
									</Box>
									<Box paddingBottom={2}>
										<Typography variant="subtitle1" gutterBottom>Price</Typography>
										<Box display="flex">
											<FormControl variant="outlined" size="small" margin="dense">
												<Select variant="outlined" {...currency} >
													{currencyMenu}
												</Select>
											</FormControl>
											<TextField variant="outlined" margin="dense" required {...price} style={{
												marginLeft: "8px"
											}} />
										</Box>
									</Box>
									<Box paddingBottom={2}>
										<Typography variant="subtitle1" gutterBottom>TaxRate</Typography>
										<Box display="flex">
											<TextField variant="outlined" margin="dense" required {...taxRate} InputProps={{
												endAdornment: <InputAdornment position="end">%</InputAdornment>
											}} />
										</Box>
									</Box>
									<Box paddingBottom={2}>
										<Typography variant="subtitle1" gutterBottom>Inventory</Typography>
										<FormControl variant="outlined" margin="dense" size="small">
											<Select variant="outlined" {...inventory} >
												{inventoryMenu}
											</Select>
										</FormControl>
										{inventory.value === "bucket" &&
											<FormControl variant="outlined" margin="dense" size="small" style={{ marginLeft: "8px" }}>
												<Select variant="outlined" {...stockValue}>
													{stockValueMenu}
												</Select>
											</FormControl>
										}
									</Box>
								</Box>
							</Box>
						</article>
					</Box>
				</Paper >
			</Box>

			{inventory.value === "finite" &&
				<Box padding={2} width="100%">
					<Typography variant="h2" gutterBottom>Inventory</Typography>
					<Paper elevation={0} square={false} style={{
						height: "100%",
						marginBottom: theme.spacing(2)
					}}>
						<Box>
							<InventoryTableRow sku={sku} />
						</Box>
					</Paper>
					<Typography variant="body2" gutterBottom>Manage inventory, including different sizes and colors.</Typography>
				</Box>
			}
		</Paper>
	)
}