@material-ui/core#FormGroup TypeScript Examples

The following examples show how to use @material-ui/core#FormGroup. 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: Settings.tsx    From swap-ui with Apache License 2.0 6 votes vote down vote up
function CloseNewAccountsSwitch() {
  const styles = useStyles();
  const { isClosingNewAccounts, setIsClosingNewAccounts } = useSwapContext();

  return (
    <FormGroup style={{ display: "none" }} row>
      <FormControlLabel
        classes={{ label: styles.closeAccountSwitchLabel }}
        labelPlacement="start"
        style={{
          display: "flex",
          justifyContent: "space-between",
          marginLeft: 0,
          width: "100%",
        }}
        control={
          <Switch
            checked={isClosingNewAccounts}
            onChange={() => setIsClosingNewAccounts(!isClosingNewAccounts)}
            color="primary"
          />
        }
        label="Close new accounts"
      />
    </FormGroup>
  );
}
Example #2
Source File: SenderFilters.tsx    From parity-bridges-ui with GNU General Public License v3.0 6 votes vote down vote up
export default function SenderFilters({
  setFilterInput,
  setShowEmpty,
  setShowCompanion,
  showEmpty,
  showCompanion
}: Props) {
  const classes = useStyles();
  const { isBridged } = useGUIContext();

  const handleChange = useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      setFilterInput(event.target.value);
    },
    [setFilterInput]
  );

  return (
    <div className={classes.filters}>
      <SenderFilterInput handleChange={handleChange} />
      <Divider />
      <FormControl component="fieldset">
        <FormGroup aria-label="position" row>
          <SenderActionSwitch name="show empty" label="show empty" callback={setShowEmpty} checked={showEmpty} />
          {isBridged && (
            <SenderActionSwitch
              name="show companion"
              label="show companion"
              callback={setShowCompanion}
              checked={showCompanion}
            />
          )}
        </FormGroup>
      </FormControl>
    </div>
  );
}
Example #3
Source File: index.tsx    From aqualink-app with MIT License 6 votes vote down vote up
Agreements = ({
  agreementsChecked,
  handleChange,
  classes,
}: AgreementsProps) => {
  return (
    <div className={classes.agreements}>
      <Typography variant="h5">Agree to:</Typography>
      <FormGroup>
        {agreements.map(({ id, label }) => (
          <FormControlLabel
            key={label}
            className={classes.formControlLabel}
            control={
              <Checkbox
                color="primary"
                checked={agreementsChecked[id]}
                onChange={() => handleChange(id)}
              />
            }
            label={label}
          />
        ))}
      </FormGroup>
    </div>
  );
}
Example #4
Source File: LabeledCheckbox.tsx    From UsTaxes with GNU Affero General Public License v3.0 5 votes vote down vote up
export function LabeledCheckbox<TFormValues>(
  props: LabeledCheckboxProps<TFormValues>
): ReactElement {
  const { label, name, useGrid = true, sizes = { xs: 12 } } = props
  const { control } = useFormContext<TFormValues>()

  return (
    <ConditionallyWrap
      condition={useGrid}
      wrapper={(children) => (
        <Grid item {...sizes}>
          {children}
        </Grid>
      )}
    >
      <Controller
        name={name}
        render={({ field: { value, onChange } }) => (
          <FormControl component="fieldset">
            <FormGroup>
              <FormControlLabel
                control={
                  <Checkbox
                    name={name}
                    checked={value as boolean}
                    onChange={(_, checked) => onChange(checked)}
                    color="primary"
                  />
                }
                label={label}
                value={value}
              />
            </FormGroup>
          </FormControl>
        )}
        control={control}
      />
    </ConditionallyWrap>
  )
}
Example #5
Source File: CreateRoom.tsx    From cards-against-formality-pwa with BSD 2-Clause "Simplified" License 5 votes vote down vote up
function DeckSelector({ decks, onChange }: { decks: any[], onChange: (decks: string[]) => void }) {
  const [deckOptions, setDeckOptions] = useState<{ name: string; _id: string, value?: boolean }[]>([]);
  const [isExpanded, setIsExpanded] = useState(false);
  const [isAllSelected, setIsAllSelected] = useState(false);
  const toggleSelectAll = useCallback(() => {
    setDeckOptions(prevDeck => {
      prevDeck.forEach(deck => deck.value = !isAllSelected);
      return [...prevDeck];
    });

    setIsAllSelected(!isAllSelected);
  }, [isAllSelected])

  useEffect(() => {
    if (decks) {
      setDeckOptions(decks.map(deck => {
        return { value: deck.name.includes('Base'), ...deck }
      }));
    }
  }, [decks]);

  useEffect(() => {
    onChange(deckOptions.filter(deck => deck.value).map(deck => deck._id));
  }, [deckOptions, onChange]);


  function _onChange(e: React.ChangeEvent<HTMLInputElement>) {
    setDeckOptions(prevDeck => {
      const deck = prevDeck.find(deck => deck._id === e.target.name);
      if (deck) {
        deck.value = e.target.checked;
      }
      return [...prevDeck];
    });
  }

  if (!decks?.length) {
    return null;
  }

  return <ExpansionPanel expanded={isExpanded} onChange={() => { setIsExpanded(prev => !prev) }}>
    <ExpansionPanelSummary
      expandIcon={<ExpandMoreIcon />}
      aria-controls="panel1bh-content"
      id="panel1bh-header"
    >
      <Typography>Available Decks!</Typography>
    </ExpansionPanelSummary>
    <ExpansionPanelDetails>
      <FormControl required component="fieldset" error={!deckOptions.some(deck => deck.value)}>
        <FormControlLabel
          control={<Checkbox checked={isAllSelected} onChange={toggleSelectAll} name="Select all" />}
          label="Select all"
        />
        <Divider />
        <FormLabel component="legend">Select which decks you would like to play with</FormLabel>
        <FormGroup className="deck-checkbox-group">
          {deckOptions.map(deck => {
            return <FormControlLabel
              key={deck._id}
              control={<Checkbox checked={deck.value} onChange={_onChange} name={deck._id} />}
              label={deck.name}
            />
          })}
        </FormGroup>
        <FormHelperText>You must select at least one</FormHelperText>
      </FormControl>
    </ExpansionPanelDetails>
  </ExpansionPanel>
}
Example #6
Source File: index.tsx    From back-home-safe with GNU General Public License v3.0 5 votes vote down vote up
StyledForm = styled(FormGroup)`
  padding: 8px 16px;
`
Example #7
Source File: FacetFilter.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 5 votes vote down vote up
FacetFilter: React.FC<Props> = (props) => {
  const classes = useStyles();
  const { options, selected, onSelect, onDeselect } = props;

  const handleChange = (
    e: React.ChangeEvent<HTMLInputElement>,
    value: string
  ) => {
    e.persist();
    if (e.target.checked) {
      onSelect(value);
    } else {
      onDeselect(value);
    }
  };

  return (
    <>
      <FormGroup classes={{ root: classes.root }}>
        {/* <input className={classes.inp} placeholder="Filter" /> */}
        {options.map((opt) => (
          <FormControlLabel
            classes={{ label: classes.label, root: classes.formControl }}
            key={opt.value}
            control={
              <Checkbox
                checked={selected.includes(opt.value)}
                onChange={(e) => handleChange(e, opt.value)}
              />
            }
            label={
              <>
                <span>{opt.value}</span>
                <span className={classes.count}>{opt.count}</span>
              </>
            }
          />
        ))}
      </FormGroup>
    </>
  );
}
Example #8
Source File: KubernetesMigrationDismissForm.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
KubernetesMigrationDismissForm = forwardRef<
  HTMLFormElement,
  KubernetesMigrationDismissFormProps
>(({ onSubmit, disableSubmit, alert }, ref) => {
  const [services, setServices] = useState<Entity[]>(alert.data.services);

  const onFormSubmit: FormEventHandler = e => {
    /* Remember to prevent default form behavior */
    e.preventDefault();
    onSubmit({ services: services });
  };

  const onCheckboxChange = (
    e: ChangeEvent<HTMLInputElement>,
    checked: boolean,
  ) => {
    if (checked) {
      const service = findAlways(
        alert.data.services,
        s => s.id === e.target.value,
      );
      setServices(prevServices => prevServices.concat(service));
    } else {
      setServices(prevServices =>
        prevServices.filter(p => p.id !== e.target.value),
      );
    }
  };

  /* Submit button is disabled by default. Use props.disableSubmit to toggle disabled state. */
  useEffect(() => {
    if (services.length) {
      disableSubmit(false);
    } else {
      disableSubmit(true);
    }
  }, [services, disableSubmit]);

  return (
    /* All custom forms must accept a ref and implement an onSubmit handler. */
    <form ref={ref} onSubmit={onFormSubmit}>
      <FormControl component="fieldset" fullWidth>
        <Typography color="textPrimary">
          <b>Or choose which services to dismiss this alert for.</b>
        </Typography>
        <FormGroup>
          {alert.data.services.map((service, index) => (
            <FormControlLabel
              key={`example-option-${index}`}
              label={service.id}
              value={service.id}
              control={
                <Checkbox
                  color="primary"
                  checked={services.some(p => p.id === service.id)}
                  onChange={onCheckboxChange}
                />
              }
            />
          ))}
        </FormGroup>
      </FormControl>
    </form>
  );
})
Example #9
Source File: ContactForm.tsx    From storefront with MIT License 4 votes vote down vote up
ContactForm: React.VFC = () => {
  const { addAlert } = useUI();
  const [shouldSend, setShouldSend] = useState(true);

  const [contact, { loading }] = useContactMutation();

  const { control, formState, handleSubmit, reset } = useForm<ContactFormData>({
    defaultValues: {
      acceptance: false,
      email: '',
      name: '',
      message: '',
      phone: '',
      subject: '',
    },
    mode: 'onChange',
    resolver: yupResolver(validationSchema),
  });

  const onSubmit = handleSubmit(async (values) => {
    if (shouldSend) {
      try {
        const { data } = await contact({ variables: values });
        if (data?.contact?.message != null) {
          addAlert(data.contact.message);
          reset();
        }
      } catch (error) {
        if (isApolloError(error)) {
          addAlert(error.message, { severity: 'error' });
        }
      }
    }
  });

  return (
    <form onSubmit={onSubmit}>
      <Grid container spacing={2}>
        <Grid item xs={6}>
          {/* Name */}
          <Controller
            control={control}
            name="name"
            render={({ field }) => (
              <TextField
                required
                error={'name' in formState.errors}
                helperText={formState.errors.name?.message}
                label="Name"
                type="text"
                {...field}
              />
            )}
          />
        </Grid>
        <Grid item xs={6}>
          {/* Email */}
          <Controller
            control={control}
            name="email"
            render={({ field }) => (
              <TextField
                required
                error={'email' in formState.errors}
                helperText={formState.errors.email?.message}
                label="Email"
                type="email"
                {...field}
              />
            )}
          />
        </Grid>
      </Grid>
      <Grid container spacing={2}>
        <Grid item xs={6}>
          {/* Phone */}
          <Controller
            control={control}
            name="phone"
            render={({ field }) => (
              <TextField
                error={'phone' in formState.errors}
                helperText={formState.errors.phone?.message}
                label="Phone"
                type="text"
                {...field}
              />
            )}
          />
        </Grid>
        <Grid item xs={6}>
          {/* Subject */}
          <Controller
            control={control}
            name="subject"
            render={({ field }) => (
              <TextField
                error={'subject' in formState.errors}
                helperText={formState.errors.subject?.message}
                label="Subject"
                type="text"
                {...field}
              />
            )}
          />
        </Grid>
      </Grid>
      {/* Message */}
      <Controller
        control={control}
        name="message"
        render={({ field }) => (
          <TextField
            multiline
            required
            error={'message' in formState.errors}
            helperText={formState.errors.message?.message}
            label="Message"
            rows={6}
            {...field}
          />
        )}
      />
      {/* Acceptance */}
      <FormGroup>
        <FormControlLabel
          control={
            <Controller
              control={control}
              name="acceptance"
              render={({ field }) => (
                <Checkbox
                  checked={field.value}
                  inputRef={field.ref}
                  name={field.name}
                  onBlur={() => field.onBlur()}
                  onChange={(e) => field.onChange(e.target.checked)}
                />
              )}
            />
          }
          label="I agree that my submitted data is being collected and stored."
        />
      </FormGroup>
      {/* Acceptance */}
      <FormGroup>
        <FormControlLabel
          control={<Checkbox onChange={(e) => setShouldSend(!e.target.checked)} />}
          label="If you are a spambot, you should check this."
        />
      </FormGroup>
      <Button type="submit" color="primary" disabled={!formState.isValid} loading={loading}>
        Submit
      </Button>
    </form>
  );
}
Example #10
Source File: SharePopup.tsx    From prompts-ai with MIT License 4 votes vote down vote up
export default function SharePopup() {
    const classes = useStyles();

    const [open, setOpen] = React.useState(true);
    const [loading, setLoading] = React.useState(false);
    const [link, setLink] = React.useState<string | undefined>(undefined);
    const [includeExamples, setIncludeExamples] = React.useState(true);

    const completionParameters = useSelector(selectCompletionParameters);
    const examples = useSelector(selectExamples);

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

    return <Dialog
        open={open}
        onClose={handleClose}
        fullWidth={true}
        maxWidth={'sm'}
    >
        <DialogTitle id="scroll-dialog-title">
            Share Prompt
            <IconButton aria-label="close" className={classes.closeButton} onClick={handleClose}>
                <CloseIcon />
            </IconButton>
        </DialogTitle>
        <DialogContent dividers>
            <DialogContent
                id="scroll-dialog-description"
                tabIndex={-1}
            >
                <form onSubmit={(event) => {
                    event.preventDefault();
                    setLoading(true);
                    SharedPrompt.create({
                        engine: completionParameters.engine,
                        maxTokens: completionParameters.maxTokens,
                        stop: completionParameters.stop,
                        prompt: completionParameters.prompt,
                        temperature: completionParameters.temperature,
                        topP: completionParameters.topP,
                        presencePenalty: completionParameters.presencePenalty,
                        frequencyPenalty: completionParameters.frequencyPenalty,
                        examples: (includeExamples ? examples : undefined),
                    }).then(onFulfilled => {
                        setLoading(false);
                        setLink(onFulfilled.id)
                    });
                }}>
                    <Typography>If you edit your prompt after sharing a link, the shared prompt won't change.</Typography>
                    <Box mt={1}>
                        { link && (
                            <TextField disabled value={link} variant={'outlined'} size={'small'} fullWidth />
                        )}
                        { !link && (
                            <Box>
                                <FormGroup row>
                                    <FormControlLabel
                                        control={<Switch checked={includeExamples} onChange={(event: React.ChangeEvent<{}>, value: boolean) => {
                                            setIncludeExamples(value);
                                        }} name="includeExamples" />}
                                        label="Include Examples"
                                    />
                                </FormGroup>
                                <Button disabled={loading} type="submit" variant="contained" color="primary">
                                    {loading && (
                                        <CircularProgress size={24} className={classes.buttonProgress}/>
                                    )}
                                    Create Link
                                </Button>
                            </Box>
                        )}
                    </Box>
                </form>

            </DialogContent>
        </DialogContent>
    </Dialog>;
}
Example #11
Source File: CardSettingsFooter.tsx    From neodash with Apache License 2.0 4 votes vote down vote up
NeoCardSettingsFooter = ({ type, fields, reportSettings, reportSettingsOpen, onToggleReportSettings,
    onCreateNotification, onReportSettingUpdate }) => {

    const [reportSettingsText, setReportSettingsText] = React.useState(reportSettings);

    // Variables related to customizing report settings
    const [customReportStyleModalOpen, setCustomReportStyleModalOpen] = React.useState(false);
    const settingToCustomize = "styleRules";

    const debouncedReportSettingUpdate = useCallback(
        debounce(onReportSettingUpdate, 250),
        [],
    );

    const updateSpecificReportSetting = (field: string, value: any) => {
        const entry = {}
        entry[field] = value;
        setReportSettingsText(update(reportSettingsText, entry));
        debouncedReportSettingUpdate(field, value);
    };

    useEffect(() => {
        // Reset text to the dashboard state when the page gets reorganized.
        setReportSettingsText(reportSettings);
    }, [JSON.stringify(reportSettings)])

    const settings = REPORT_TYPES[type]["settings"];

    // If there are no advanced settings, render nothing.
    if (Object.keys(settings).length == 0) {
        return <div></div>
    }

    // Else, build the advanced settings view.
    const advancedReportSettings = <div style={{ marginLeft: "5px" }}>
        {Object.keys(settings).map(setting =>
            <NeoSetting key={setting} name={setting}
                value={reportSettingsText[setting]}
                type={settings[setting]["type"]}
                label={settings[setting]["label"]}
                defaultValue={settings[setting]["default"]}
                choices={settings[setting]["values"]}
                onChange={(e) => updateSpecificReportSetting(setting, e)}
            />
        )}
    </div>

    return <div>
        <NeoCustomReportStyleModal
            settingName={settingToCustomize}
            settingValue={reportSettings[settingToCustomize]}
            type={type}
            fields={fields}
            customReportStyleModalOpen={customReportStyleModalOpen}
            setCustomReportStyleModalOpen={setCustomReportStyleModalOpen}
            onReportSettingUpdate={onReportSettingUpdate}
        ></NeoCustomReportStyleModal>
        <table style={{ borderTop: "1px dashed lightgrey", background: reportSettingsOpen ? "#f6f6f6" : "inherit", width: "100%" }}>
            <tbody>
                <tr>
                    <td>
                        <FormGroup >
                            <FormControlLabel style={{ marginLeft: "5px", marginBottom: "10px" }}
                                control={<Switch
                                    checked={reportSettingsOpen} onChange={onToggleReportSettings} color="default" />}
                                labelPlacement="end"
                                label={<div style={{ fontSize: "12px", color: "grey" }}>Advanced settings</div>} />
                        </FormGroup>
                    </td>
                    {RULE_BASED_REPORT_CUSTOMIZATIONS[type] ? <td>
                        <Tooltip title="Set rule-based styling" aria-label="">
                            <IconButton size="small" style={{ float: "right", marginRight: "10px" }} aria-label="custom styling"
                                onClick={(e) => {
                                    setCustomReportStyleModalOpen(true); // Open the modal.
                                }}>
                                <TuneIcon></TuneIcon>
                            </IconButton>
                        </Tooltip>
                    </td> : <></>}
                </tr>
                <tr>
                    <td colSpan={2} style={{ maxWidth: "100%" }}>
                        {reportSettingsOpen ? advancedReportSettings : <div></div>}
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
}
Example #12
Source File: Dashboard.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
DashboardUI: React.FC<ContextType & { location: any }> = (
  props
) => {
  const {
    current,
    setCurrent,
    resultsPerPage,
    setResultsPerPage,
    filters,
    addFilter,
    removeFilter,
    results,
    facets,
    clearFilters,
    sortDirection,
    sortField,
    setSort,
    totalPages,
    totalResults,
    setSearchTerm,
    searchTerm,
    noResults
  } = props;
  const classes = useStyles();
  const [selectedDomain, setSelectedDomain] = useState('');
  const [resultsScrolled, setResultsScrolled] = useState(false);
  const {
    apiPost,
    apiPut,
    setLoading,
    showAllOrganizations,
    currentOrganization
  } = useAuthContext();

  const search:
    | (SavedSearch & {
        editing?: boolean;
      })
    | undefined = localStorage.getItem('savedSearch')
    ? JSON.parse(localStorage.getItem('savedSearch')!)
    : undefined;

  const [showSaveSearch, setShowSaveSearch] = useState<Boolean>(
    search && search.editing ? true : false
  );

  const [savedSearchValues, setSavedSearchValues] = useState<
    Partial<SavedSearch> & {
      name: string;
      vulnerabilityTemplate: Partial<Vulnerability>;
    }
  >(
    search
      ? search
      : {
          name: '',
          vulnerabilityTemplate: {},
          createVulnerabilities: false
        }
  );

  const onTextChange: React.ChangeEventHandler<
    HTMLInputElement | HTMLSelectElement
  > = (e) => onChange(e.target.name, e.target.value);

  const onChange = (name: string, value: any) => {
    setSavedSearchValues((values) => ({
      ...values,
      [name]: value
    }));
  };

  const onVulnerabilityTemplateChange = (e: any) => {
    (savedSearchValues.vulnerabilityTemplate as any)[e.target.name] =
      e.target.value;
    setSavedSearchValues(savedSearchValues);
  };

  const handleResultScroll = (e: React.UIEvent<HTMLElement>) => {
    if (e.currentTarget.scrollTop > 0) {
      setResultsScrolled(true);
    } else {
      setResultsScrolled(false);
    }
  };

  useEffect(() => {
    if (props.location.search === '') {
      // Search on initial load
      setSearchTerm('');
    }
    return () => {
      localStorage.removeItem('savedSearch');
    };
  }, [setSearchTerm, props.location.search]);

  useBeforeunload((event) => {
    localStorage.removeItem('savedSearch');
  });

  const fetchDomainsExport = async (): Promise<string> => {
    try {
      const body: any = {
        current,
        filters,
        resultsPerPage,
        searchTerm,
        sortDirection,
        sortField
      };
      if (!showAllOrganizations && currentOrganization) {
        if ('rootDomains' in currentOrganization)
          body.organizationId = currentOrganization.id;
        else body.tagId = currentOrganization.id;
      }
      const { url } = await apiPost('/search/export', {
        body
      });
      return url!;
    } catch (e) {
      console.error(e);
      return '';
    }
  };

  return (
    <div className={classes.root}>
      <FilterDrawer
        addFilter={addFilter}
        removeFilter={removeFilter}
        filters={filters}
        facets={facets}
        clearFilters={filters.length > 0 ? () => clearFilters([]) : undefined}
      />
      <div className={classes.contentWrapper}>
        <Subnav
          items={[
            { title: 'Search Results', path: '/inventory', exact: true },
            { title: 'All Domains', path: '/inventory/domains' },
            { title: 'All Vulnerabilities', path: '/inventory/vulnerabilities' }
          ]}
          styles={{
            paddingLeft: '0%'
          }}
        >
          <FilterTags filters={filters} removeFilter={removeFilter} />
        </Subnav>
        <SortBar
          sortField={sortField}
          sortDirection={sortDirection}
          setSort={setSort}
          isFixed={resultsScrolled}
          saveSearch={
            filters.length > 0 || searchTerm
              ? () => setShowSaveSearch(true)
              : undefined
          }
          existingSavedSearch={search}
        />
        {noResults && (
          <NoResults
            message={"We don't see any results that match your criteria."}
          ></NoResults>
        )}
        <div className={classes.content}>
          <div className={classes.panel} onScroll={handleResultScroll}>
            {results.map((result) => (
              <ResultCard
                key={result.id.raw}
                {...result}
                onDomainSelected={(id) => setSelectedDomain(id)}
                selected={result.id.raw === selectedDomain}
              />
            ))}
          </div>
          <div className={classes.panel}>
            {selectedDomain && <DomainDetails domainId={selectedDomain} />}
          </div>
        </div>
        <Paper classes={{ root: classes.pagination }}>
          <span>
            <strong>
              {(totalResults === 0
                ? 0
                : (current - 1) * resultsPerPage + 1
              ).toLocaleString()}{' '}
              -{' '}
              {Math.min(
                (current - 1) * resultsPerPage + resultsPerPage,
                totalResults
              ).toLocaleString()}
            </strong>{' '}
            of <strong>{totalResults.toLocaleString()}</strong>
          </span>
          <Pagination
            count={totalPages}
            page={current}
            onChange={(_, page) => setCurrent(page)}
            color="primary"
            size="small"
          />
          <FormControl
            variant="outlined"
            className={classes.pageSize}
            size="small"
          >
            <Typography id="results-per-page-label">
              Results per page:
            </Typography>
            <Select
              id="teststa"
              labelId="results-per-page-label"
              value={resultsPerPage}
              onChange={(e) => setResultsPerPage(e.target.value as number)}
            >
              {[15, 45, 90].map((perPage) => (
                <MenuItem key={perPage} value={perPage}>
                  {perPage}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
          <USWDSButton
            className={classes.exportButton}
            outline
            type="button"
            onClick={() =>
              exportCSV(
                {
                  name: 'domains',
                  getDataToExport: fetchDomainsExport
                },
                setLoading
              )
            }
          >
            Export Results
          </USWDSButton>
        </Paper>
      </div>

      {showSaveSearch && (
        <div>
          <Overlay />
          <ModalContainer>
            <Modal
              className={classes.saveSearchModal}
              actions={
                <>
                  <USWDSButton
                    outline
                    type="button"
                    onClick={() => {
                      setShowSaveSearch(false);
                    }}
                  >
                    Cancel
                  </USWDSButton>
                  <USWDSButton
                    type="button"
                    onClick={async () => {
                      const body = {
                        body: {
                          ...savedSearchValues,
                          searchTerm,
                          filters,
                          count: totalResults,
                          searchPath: window.location.search,
                          sortField,
                          sortDirection
                        }
                      };
                      if (search) {
                        await apiPut('/saved-searches/' + search.id, body);
                      } else {
                        await apiPost('/saved-searches/', body);
                      }
                      setShowSaveSearch(false);
                    }}
                  >
                    Save
                  </USWDSButton>
                </>
              }
              title={search ? <h2>Update Search</h2> : <h2>Save Search</h2>}
            >
              <FormGroup>
                <Label htmlFor="name">Name Your Search</Label>
                <TextInput
                  required
                  id="name"
                  name="name"
                  type="text"
                  value={savedSearchValues.name}
                  onChange={onTextChange}
                />
                <p>When a new result is found:</p>
                {/* <FormControlLabel
                  control={
                    <Checkbox
                      // checked={gilad}
                      // onChange={handleChange}
                      name="email"
                    />
                  }
                  label="Email me"
                /> */}
                <FormControlLabel
                  control={
                    <Checkbox
                      checked={savedSearchValues.createVulnerabilities}
                      onChange={(e) =>
                        onChange(e.target.name, e.target.checked)
                      }
                      id="createVulnerabilities"
                      name="createVulnerabilities"
                    />
                  }
                  label="Create a vulnerability"
                />
                {savedSearchValues.createVulnerabilities && (
                  <>
                    <Label htmlFor="title">Title</Label>
                    <TextInput
                      required
                      id="title"
                      name="title"
                      type="text"
                      value={savedSearchValues.vulnerabilityTemplate.title}
                      onChange={onVulnerabilityTemplateChange}
                    />
                    <Label htmlFor="description">Description</Label>
                    <TextareaAutosize
                      required
                      id="description"
                      name="description"
                      style={{ padding: 10 }}
                      rowsMin={2}
                      value={
                        savedSearchValues.vulnerabilityTemplate.description
                      }
                      onChange={onVulnerabilityTemplateChange}
                    />
                    <Label htmlFor="description">Severity</Label>
                    <Dropdown
                      id="severity"
                      name="severity"
                      onChange={onVulnerabilityTemplateChange}
                      value={
                        savedSearchValues.vulnerabilityTemplate
                          .severity as string
                      }
                      style={{ display: 'inline-block', width: '150px' }}
                    >
                      <option value="None">None</option>
                      <option value="Low">Low</option>
                      <option value="Medium">Medium</option>
                      <option value="High">High</option>
                      <option value="Critical">Critical</option>
                    </Dropdown>
                  </>
                )}
                {/* <h3>Collaborators</h3>
                <p>
                  Collaborators can view vulnerabilities, and domains within
                  this search. Adding a team will make all members
                  collaborators.
                </p>
                <button className={classes.addButton} >
                  <AddCircleOutline></AddCircleOutline> ADD
                </button> */}
              </FormGroup>
            </Modal>
          </ModalContainer>
        </div>
      )}
    </div>
  );
}
Example #13
Source File: chart-filters.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
export function ChartFilters(props: ChartFiltersProps) {
  const {
    analysis,
    cicdConfiguration,
    initialFetchFilter,
    currentFetchFilter,
    onChangeFetchFilter,
    updateFetchFilter,
    initialViewOptions,
    onChangeViewOptions,
  } = props;

  const classes = useStyles();

  const [internalRef] = useState<InternalRef>({ first: true });

  const [useNowAsToDate, setUseNowAsToDate] = useState(true);
  const [toDate, setToDate] = useState(initialFetchFilter.toDate);
  const [fromDate, setFromDate] = useState(initialFetchFilter.fromDate);

  const [branch, setBranch] = useState(initialFetchFilter.branch);

  const statusValues: ReadonlyArray<StatusSelection> =
    cicdConfiguration.availableStatuses;
  const [selectedStatus, setSelectedStatus] = useState(
    initialFetchFilter.status,
  );

  const [viewOptions, setViewOptions] = useState(initialViewOptions);

  const setLowercaseNames = useCallback(
    (lowercaseNames: boolean) => {
      setViewOptions(old => ({ ...old, lowercaseNames }));
    },
    [setViewOptions],
  );

  const setNormalizeTimeRange = useCallback(
    (normalizeTimeRange: boolean) => {
      setViewOptions(old => ({ ...old, normalizeTimeRange }));
    },
    [setViewOptions],
  );

  const setHideLimit = useCallback(
    (value: number) => {
      setViewOptions(old => ({ ...old, hideLimit: value }));
    },
    [setViewOptions],
  );

  const setCollapseLimit = useCallback(
    (value: number) => {
      setViewOptions(old => ({ ...old, collapsedLimit: value }));
    },
    [setViewOptions],
  );

  const setChartType = useCallback(
    (statusType: FilterStatusType, chartTypes: ChartTypes) => {
      setViewOptions(old => ({
        ...old,
        chartTypes: { ...old.chartTypes, [statusType]: chartTypes },
      }));
    },
    [setViewOptions],
  );
  const setChartTypeSpecific = useMemo(
    () =>
      Object.fromEntries(
        statusTypes.map(
          status =>
            [
              status,
              (chartTypes: ChartTypes) => setChartType(status, chartTypes),
            ] as const,
        ),
      ),
    [setChartType],
  );

  useEffect(() => {
    onChangeViewOptions(viewOptions);
  }, [onChangeViewOptions, viewOptions]);

  useEffect(() => {
    if (internalRef.first) {
      // Skip calling onChangeFetchFilter first time
      internalRef.first = false;
      return;
    }
    onChangeFetchFilter({
      toDate,
      fromDate,
      branch,
      status: selectedStatus,
    });
  }, [
    internalRef,
    toDate,
    fromDate,
    branch,
    selectedStatus,
    onChangeFetchFilter,
  ]);

  const toggleUseNowAsDate = useCallback(() => {
    setUseNowAsToDate(!useNowAsToDate);
    if (!DateTime.fromJSDate(toDate).hasSame(DateTime.now(), 'day')) {
      setToDate(new Date());
    }
  }, [useNowAsToDate, toDate]);

  const hasFetchFilterChanges = useMemo(
    () =>
      !currentFetchFilter ||
      !isSameChartFilter(
        {
          toDate,
          fromDate,
          branch,
          status: selectedStatus,
        },
        currentFetchFilter,
      ),
    [toDate, fromDate, branch, selectedStatus, currentFetchFilter],
  );

  const updateFilter = useCallback(() => {
    updateFetchFilter({
      toDate,
      fromDate,
      branch,
      status: selectedStatus,
    });
  }, [toDate, fromDate, branch, selectedStatus, updateFetchFilter]);

  const inrefferedStatuses = analysis?.statuses ?? selectedStatus;

  return (
    <MuiPickersUtilsProvider utils={LuxonUtils}>
      <Card className={classes.rootCard}>
        <CardHeader
          action={
            <Button
              size="small"
              color="secondary"
              variant="contained"
              onClick={updateFilter}
              disabled={!hasFetchFilterChanges}
            >
              Update
            </Button>
          }
          title={
            <Typography variant="subtitle2" className={classes.header}>
              Fetching options
            </Typography>
          }
        />
        <CardContent>
          <Typography
            variant="subtitle2"
            className={`${classes.title} ${classes.title}`}
          >
            Date range
          </Typography>
          <KeyboardDatePicker
            autoOk
            variant="inline"
            inputVariant="outlined"
            label="From date"
            format="yyyy-MM-dd"
            value={fromDate}
            InputAdornmentProps={{ position: 'start' }}
            onChange={date => setFromDate(date?.toJSDate() ?? new Date())}
          />
          <br />
          <FormControl component="fieldset">
            <FormGroup>
              <FormControlLabel
                control={
                  <Switch
                    checked={useNowAsToDate}
                    onChange={toggleUseNowAsDate}
                  />
                }
                label={<Label>To today</Label>}
              />
              {useNowAsToDate ? null : (
                <KeyboardDatePicker
                  autoOk
                  variant="inline"
                  inputVariant="outlined"
                  label="To date"
                  format="yyyy-MM-dd"
                  value={toDate}
                  InputAdornmentProps={{ position: 'start' }}
                  onChange={date => setToDate(date?.toJSDate() ?? new Date())}
                />
              )}
            </FormGroup>
          </FormControl>
          <Typography
            variant="subtitle2"
            className={`${classes.title} ${classes.title}`}
          >
            Branch
          </Typography>
          <ButtonSwitch<string>
            values={branchValues}
            selection={branch}
            onChange={setBranch}
          />
          <Typography
            variant="subtitle2"
            className={`${classes.title} ${classes.title}`}
          >
            Status
          </Typography>
          <ButtonSwitch<string>
            values={statusValues}
            multi
            vertical
            selection={selectedStatus}
            onChange={setSelectedStatus}
          />
        </CardContent>
      </Card>
      <Card className={classes.rootCard}>
        <CardHeader
          title={
            <Typography variant="subtitle2" className={classes.header}>
              View options
            </Typography>
          }
        />
        <CardContent>
          <Toggle
            checked={viewOptions.lowercaseNames}
            setChecked={setLowercaseNames}
          >
            <Tooltip
              arrow
              title={
                'Lowercasing names can reduce duplications ' +
                'when stage names have changed casing'
              }
            >
              <Label>Lowercase names</Label>
            </Tooltip>
          </Toggle>
          <Toggle
            checked={viewOptions.normalizeTimeRange}
            setChecked={setNormalizeTimeRange}
          >
            <Tooltip
              arrow
              title={
                'All charts will use the same x-axis. ' +
                'This reduces confusion when stages have been altered over time ' +
                'and only appear in a part of the time range.'
              }
            >
              <Label>Normalize time range</Label>
            </Tooltip>
          </Toggle>
          <DurationSlider
            header="Hide under peak"
            value={viewOptions.hideLimit}
            setValue={setHideLimit}
          />
          <DurationSlider
            header="Collapse under peak"
            value={viewOptions.collapsedLimit}
            setValue={setCollapseLimit}
          />
          <Typography
            variant="subtitle2"
            className={`${classes.title} ${classes.title}`}
          >
            Chart styles
          </Typography>
          {inrefferedStatuses.map(status => (
            <Grid key={status} container spacing={0}>
              <Grid item>
                <ButtonSwitch<ChartType>
                  values={chartTypeValues}
                  selection={viewOptions.chartTypes[status as FilterStatusType]}
                  onChange={setChartTypeSpecific[status]}
                  multi
                />
              </Grid>
              <Grid item className={classes.buttonDescription}>
                <div>{status}</div>
              </Grid>
            </Grid>
          ))}
        </CardContent>
      </Card>
    </MuiPickersUtilsProvider>
  );
}
Example #14
Source File: index.tsx    From vscode-crossnote with GNU Affero General Public License v3.0 4 votes vote down vote up
function OCRWidget(props: WidgetArgs) {
  const classes = useStyles(props);
  const { t } = useTranslation();
  const [canvas, setCanvas] = useState<HTMLCanvasElement>(null);
  // https://github.com/tesseract-ocr/tesseract/wiki/Data-Files#data-files-for-version-400-november-29-2016
  const [link, setLink] = useState<string>("");
  const [imageDataURL, setImageDataURL] = useState<string>("");
  const [ocrDataURL, setOCRDataURL] = useState<string>("");
  const [imageDropAreaElement, setImageDropAreaElement] = useState<
    HTMLInputElement
  >(null);
  const [isProcessing, setIsProcessing] = useState<boolean>(false);
  const [ocrProgresses, setOCRProgresses] = useState<OCRProgress[]>([]);
  const [selectedLanguages, setSelectedLanguages] = useState<string[]>(
    getInitialLanguages()
  );
  const [grayscaleChecked, setGrayscaleChecked] = useState<boolean>(true);

  useEffect(() => {
    if (canvas && imageDataURL) {
      const imageObject = new Image();
      const context = canvas.getContext("2d");
      imageObject.onload = function () {
        canvas.width = imageObject.width;
        canvas.height = imageObject.height;
        context.clearRect(0, 0, canvas.width, canvas.height);
        if (grayscaleChecked) {
          context.fillStyle = "#FFF";
          context.fillRect(0, 0, canvas.width, canvas.height);
          context.globalCompositeOperation = "luminosity";
        }
        context.drawImage(imageObject, 0, 0);
        setOCRDataURL(canvas.toDataURL());
      };
      imageObject.onerror = (error) => {
        throw error;
      };
      imageObject.setAttribute("crossOrigin", "anonymous");
      imageObject.src = imageDataURL;
    }
  }, [canvas, imageDataURL, grayscaleChecked]);

  function clickDropArea(e: any) {
    e.preventDefault();
    e.stopPropagation();
    if (!imageDropAreaElement || isProcessing) return;
    imageDropAreaElement.onchange = function (event) {
      const target = event.target as any;
      const files = target.files || [];
      if (files.length) {
        try {
          const file = files[0] as File;
          const reader = new FileReader();
          reader.readAsDataURL(file);
          reader.onload = () => {
            setImageDataURL(reader.result as string);
          };
          reader.onerror = (error) => {
            throw error;
          };
        } catch (error) {}
      }
    };
    imageDropAreaElement.click();
  }

  function startOCRFromLink() {
    try {
      setImageDataURL(link);
    } catch (error) {}
  }

  function ocr(input: File | string | HTMLCanvasElement) {
    const worker = createWorker({
      logger: (m: OCRProgress) => {
        setOCRProgresses((ocrProgresses) => {
          if (
            ocrProgresses.length &&
            ocrProgresses[ocrProgresses.length - 1].status === m.status
          ) {
            return [...ocrProgresses.slice(0, ocrProgresses.length - 1), m];
          } else {
            return [...ocrProgresses, m];
          }
        });
      },
    });

    (async () => {
      setIsProcessing(true);
      let languagesArr = selectedLanguages;
      if (languagesArr.length === 0) {
        languagesArr = ["eng"];
      }

      await worker.load();
      await worker.loadLanguage(languagesArr.join("+"));
      await worker.initialize(languagesArr.join("+"));
      const {
        data: { text },
      } = await worker.recognize(input);
      props.replaceSelf("\n" + text);
      await worker.terminate();
      setIsProcessing(false);
    })();
  }

  function toggleLanguage(lang: string) {
    setSelectedLanguages((selectedLanguages) => {
      const offset = selectedLanguages.indexOf(lang);
      if (offset >= 0) {
        selectedLanguages.splice(offset, 1);
        selectedLanguages = [...selectedLanguages];
      } else {
        selectedLanguages = [...selectedLanguages, lang];
      }
      return selectedLanguages;
    });
  }

  if (props.isPreview) {
    return <span></span>;
  }

  if (isProcessing) {
    return (
      <Card elevation={2} className={clsx(classes.card)}>
        <Typography variant={"h5"}>{t("general/Processing")}</Typography>
        {/*<Typography variant={"body1"}>{t("general/please-wait")}</Typography>*/}
        <List>
          {ocrProgresses.length > 0 && (
            <ListItem>
              <ListItemText>
                {t(
                  "tesseract/" + ocrProgresses[ocrProgresses.length - 1].status
                )}
              </ListItemText>
              <ListItemSecondaryAction>
                {Math.floor(
                  ocrProgresses[ocrProgresses.length - 1].progress * 100
                ).toString() + "%"}
              </ListItemSecondaryAction>
            </ListItem>
          )}
        </List>
      </Card>
    );
  }

  if (imageDataURL) {
    return (
      <Card elevation={2} className={clsx(classes.card)}>
        <Box className={clsx(classes.section)}>
          <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
            {t("widget/crossnote.ocr/recognize-text-in-languages")}
          </Typography>
          <FormGroup>
            <FormControlLabel
              control={
                <Checkbox
                  checked={selectedLanguages.indexOf("eng") >= 0}
                  onChange={() => toggleLanguage("eng")}
                  value="eng"
                />
              }
              label="English"
            />
            <FormControlLabel
              control={
                <Checkbox
                  checked={selectedLanguages.indexOf("chi_sim") >= 0}
                  onChange={() => toggleLanguage("chi_sim")}
                  value="chi_sim"
                />
              }
              label="简体中文"
            />
            <FormControlLabel
              control={
                <Checkbox
                  checked={selectedLanguages.indexOf("chi_tra") >= 0}
                  onChange={() => toggleLanguage("chi_tra")}
                  value="chi_tra"
                />
              }
              label="繁體中文"
            />
            <FormControlLabel
              control={
                <Checkbox
                  checked={selectedLanguages.indexOf("jpn") >= 0}
                  onChange={() => toggleLanguage("jpn")}
                  value="jpn"
                />
              }
              label="日本語"
            />
          </FormGroup>
        </Box>
        <Box className={clsx(classes.section)}>
          <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
            {t("widget/crossnote.ocr/extra-settings")}
          </Typography>
          <FormControlLabel
            control={
              <Switch
                checked={grayscaleChecked}
                onChange={() => {
                  setGrayscaleChecked(!grayscaleChecked);
                }}
                color={"primary"}
              ></Switch>
            }
            label={t("widget/crossnote.ocr/grayscale")}
          ></FormControlLabel>
        </Box>
        <Box className={clsx(classes.canvasWrapper)}>
          <canvas
            className={clsx(classes.canvas)}
            ref={(element) => setCanvas(element)}
          ></canvas>
        </Box>
        <ButtonGroup>
          <Button
            onClick={() => {
              setImageDataURL("");
              setOCRDataURL("");
            }}
          >
            {t("general/go-back")}
          </Button>
          <Button
            color={"primary"}
            onClick={() => ocr(ocrDataURL)}
            disabled={!ocrDataURL}
          >
            {t("widget/crossnote.ocr/start-ocr")}
          </Button>
        </ButtonGroup>
      </Card>
    );
  }

  return (
    <Card elevation={2} className={clsx(classes.card)}>
      <Typography variant={"h5"}>{t("widget/crossnote.ocr/ocr")}</Typography>
      <Box className={clsx(classes.actionButtons)}>
        <Tooltip title={t("general/Delete")}>
          <IconButton onClick={() => props.removeSelf()}>
            <TrashCan></TrashCan>
          </IconButton>
        </Tooltip>
      </Box>
      <Box className={clsx(classes.section)}>
        <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
          {t("general/Link")}
        </Typography>
        <Input
          margin={"dense"}
          placeholder={t("widget/crossnote.image/image-url-placeholder")}
          value={link}
          onChange={(event) => {
            setLink(event.target.value);
          }}
          onKeyDown={(event) => {
            if (event.which === 13) {
              startOCRFromLink();
            }
          }}
          fullWidth={true}
        ></Input>
      </Box>
      <Typography
        variant={"subtitle1"}
        style={{ marginTop: "16px", textAlign: "center" }}
      >
        {t("widget/crossnote.auth/Or")}
      </Typography>
      <Box className={clsx(classes.section)}>
        <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
          {t("widget/crossnote.ocr/local-image")}
        </Typography>
        <Box
          className={clsx(
            classes.dropArea,
            isProcessing ? classes.disabled : null
          )}
          onClick={clickDropArea}
        >
          <Typography>
            {isProcessing
              ? t("utils/uploading-image")
              : t("widget/crossnote.image/click-here-to-browse-image-file")}
          </Typography>
        </Box>
      </Box>
      <input
        type="file"
        // multiple
        style={{ display: "none" }}
        ref={(element: HTMLInputElement) => {
          setImageDropAreaElement(element);
        }}
      ></input>
    </Card>
  );
}
Example #15
Source File: ShippingForm.tsx    From storefront with MIT License 4 votes vote down vote up
ShippingForm = React.forwardRef<HTMLFormElement, Props>(
  (
    {
      defaultValues,
      onShippingSameAsBillingChange,
      onSubmit: onSubmitProp,
      shipToDifferentAddress = false,
    },
    ref,
  ) => {
    const [updateCustomer, { loading }] = useUpdateCustomerMutation({
      refetchQueries: ['Cart', 'Customer'],
    });

    const [shippingSameAsBilling, setShippingSameAsBilling] = useState(!shipToDifferentAddress);

    const { control, formState, handleSubmit, reset } = useForm<CustomerAddressInput>({
      defaultValues: {
        address1: defaultValues?.address1 ?? '',
        address2: defaultValues?.address2 ?? '',
        city: defaultValues?.city ?? '',
        company: defaultValues?.company ?? '',
        country: defaultValues?.country ?? CountriesEnum.DE,
        firstName: defaultValues?.firstName ?? '',
        lastName: defaultValues?.lastName ?? '',
        postcode: defaultValues?.postcode ?? '',
        state: defaultValues?.state ?? '',
      },
      resolver: yupResolver(validationSchema),
    });

    useEffect(() => {
      reset({
        address1: defaultValues?.address1 ?? '',
        address2: defaultValues?.address2 ?? '',
        city: defaultValues?.city ?? '',
        company: defaultValues?.company ?? '',
        country: defaultValues?.country ?? CountriesEnum.DE,
        firstName: defaultValues?.firstName ?? '',
        lastName: defaultValues?.lastName ?? '',
        postcode: defaultValues?.postcode ?? '',
        state: defaultValues?.state ?? '',
      });
    }, [defaultValues, reset]);

    const onSubmit = handleSubmit(async (shipping) => {
      await updateCustomer({ variables: { shipping, shippingSameAsBilling } });
      onSubmitProp();
    });

    const handleShippingSameAsBillingChange = async (ev: React.ChangeEvent<HTMLInputElement>) => {
      const nextShippingSameAsBilling = ev.target.checked;
      await updateCustomer({ variables: { shippingSameAsBilling: nextShippingSameAsBilling } });
      setShippingSameAsBilling(nextShippingSameAsBilling);
      onShippingSameAsBillingChange(nextShippingSameAsBilling);
      reset();
    };

    const handleChange =
      (onChange: (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void) =>
      (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
        onChange(event);

        if (shippingSameAsBilling) {
          setShippingSameAsBilling(false);
          onShippingSameAsBillingChange(false);
        }
      };

    return (
      <>
        <Typography gutterBottom variant="h3">
          Shipping Address
        </Typography>
        <Box sx={{ mb: 4 }}>
          <FormGroup>
            <FormControlLabel
              control={
                <Checkbox
                  name="shippingSameAsBilling"
                  checked={shippingSameAsBilling}
                  onChange={handleShippingSameAsBillingChange}
                />
              }
              label="Same as Billing address"
            />
          </FormGroup>
          <Divider />
        </Box>
        <form ref={ref} onSubmit={onSubmit}>
          <Grid container spacing={2}>
            <Grid item xs={6}>
              {/* First Name */}
              <Controller
                control={control}
                name="firstName"
                render={({ field }) => (
                  <TextField
                    required
                    autoComplete="given-name"
                    error={'firstName' in formState.errors}
                    helperText={formState.errors.firstName?.message}
                    label="First name"
                    type="text"
                    {...field}
                    onChange={handleChange(field.onChange)}
                  />
                )}
              />
            </Grid>
            <Grid item xs={6}>
              {/* Last Name */}
              <Controller
                control={control}
                name="lastName"
                render={({ field }) => (
                  <TextField
                    required
                    autoComplete="family-name"
                    error={'lastName' in formState.errors}
                    helperText={formState.errors.lastName?.message}
                    label="Last name"
                    type="text"
                    {...field}
                    onChange={handleChange(field.onChange)}
                  />
                )}
              />
            </Grid>
          </Grid>
          {/* Company */}
          <Controller
            control={control}
            name="company"
            render={({ field }) => (
              <TextField
                autoComplete="organization"
                error={'company' in formState.errors}
                helperText={formState.errors.company?.message}
                label="Company"
                type="text"
                {...field}
                onChange={handleChange(field.onChange)}
              />
            )}
          />
          {/* Country */}
          <Controller
            control={control}
            name="country"
            render={({ field }) => (
              <CountrySelect
                required
                error={'country' in formState.errors}
                helperText={formState.errors.country?.message}
                label="Country"
                {...field}
                onChange={handleChange(field.onChange)}
              />
            )}
          />
          {/* Address */}
          <Controller
            control={control}
            name="address1"
            render={({ field }) => (
              <TextField
                required
                autoComplete="address-line1"
                error={'address1' in formState.errors}
                helperText={formState.errors.address1?.message}
                label="Street address"
                placeholder="House number and street name"
                type="text"
                {...field}
                onChange={handleChange(field.onChange)}
              />
            )}
          />
          <Controller
            control={control}
            name="address2"
            render={({ field }) => (
              <TextField
                autoComplete="address-line2"
                error={'address2' in formState.errors}
                helperText={formState.errors.address2?.message}
                placeholder="Apartment, suite, unit etc. (optional)"
                type="text"
                {...field}
                onChange={handleChange(field.onChange)}
              />
            )}
          />
          <Grid container spacing={2}>
            <Grid item xs={3}>
              {/* Postcode */}
              <Controller
                control={control}
                name="postcode"
                render={({ field }) => (
                  <TextField
                    required
                    autoComplete="postal-code"
                    error={'postcode' in formState.errors}
                    helperText={formState.errors.postcode?.message}
                    label="Postcode"
                    type="text"
                    {...field}
                    onChange={handleChange(field.onChange)}
                  />
                )}
              />
            </Grid>
            <Grid item xs={9}>
              {/* City */}
              <Controller
                control={control}
                name="city"
                render={({ field }) => (
                  <TextField
                    required
                    autoComplete="address-level2"
                    error={'city' in formState.errors}
                    helperText={formState.errors.city?.message}
                    label="City"
                    type="text"
                    {...field}
                    onChange={handleChange(field.onChange)}
                  />
                )}
              />
            </Grid>
          </Grid>
          {/* State */}
          <Controller
            control={control}
            name="state"
            render={({ field }) => (
              <TextField
                autoComplete="address-level1"
                error={'state' in formState.errors}
                helperText={formState.errors.state?.message}
                label="State"
                type="text"
                {...field}
                onChange={handleChange(field.onChange)}
              />
            )}
          />
          <Box sx={{ mt: 2 }}>
            <Button type="submit" color="primary" loading={loading}>
              Continue to Shipping Method
            </Button>
          </Box>
        </form>
      </>
    );
  },
)
Example #16
Source File: CheckoutReview.tsx    From storefront with MIT License 4 votes vote down vote up
CheckoutReview: React.VFC<Props> = ({
  cart,
  creditCard,
  customer,
  loading,
  onSubmit,
  paymentMethod,
  paymentNonce,
}) => {
  const [customerNote, setCustomerNote] = useState<string>();
  const [acceptTerms, setAcceptsTerms] = useState(false);

  const [{ loading: paymentLoading }, handlePayment] = useAsyncFn(async (nonce?: string) => {
    const response = await fetch('/api/payment', {
      method: 'post',
      credentials: 'include',
      headers: {
        'content-type': 'application/json',
      },
      body: JSON.stringify({
        nonce,
        total: cart.total == null ? undefined : parseFloat(cart.total),
      }),
    });
    const data = (await response.json()) as PaymentResponse;
    onSubmit({
      customerNote,
      metaData: [
        {
          key: '_merchant_account_id',
          value: process.env.BRAINTREE_MERCHANT_ID,
        },
        {
          key: '_wc_braintree_environment',
          value: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox',
        },
        {
          key: '_transaction_status',
          value: data.transaction.status,
        },
      ],
      transactionId: data.transaction.id,
    });
  });

  const handleSubmit = () => {
    handlePayment(paymentNonce);
  };

  if (loading || paymentLoading) {
    return <Loader />;
  }

  return (
    <>
      <Grid container spacing={4}>
        <Grid item xs={6}>
          <Box
            sx={{
              alignItems: 'center',
              display: 'flex',
              flexDirection: 'row',
              justifyContent: 'space-between',
              mb: 1,
            }}
          >
            <Typography variant="h4">Billing Address</Typography>
            <IconButton href="/checkout/billing-address" size="small">
              <Edit fontSize="inherit" />
            </IconButton>
          </Box>
          <Divider />
          <Box sx={{ mt: 2 }}>
            <AddressSummary address={customer.billing} />
          </Box>
        </Grid>
        <Grid item xs={6}>
          <Box
            sx={{
              alignItems: 'center',
              display: 'flex',
              flexDirection: 'row',
              justifyContent: 'space-between',
              mb: 1,
            }}
          >
            <Typography variant="h4">Shipping Address</Typography>
            <IconButton href="/checkout/shipping-address" size="small">
              <Edit fontSize="inherit" />
            </IconButton>
          </Box>
          <Divider />
          <Box sx={{ mt: 2 }}>
            <AddressSummary address={customer.shipping} />
          </Box>
        </Grid>
        <Grid item xs={6}>
          <Box
            sx={{
              alignItems: 'center',
              display: 'flex',
              flexDirection: 'row',
              justifyContent: 'space-between',
              mb: 1,
            }}
          >
            <Typography variant="h4">Shipping Method</Typography>
            <IconButton href="/checkout/shipping-options" size="small">
              <Edit fontSize="inherit" />
            </IconButton>
          </Box>
          <Divider />
          <Box sx={{ mt: 2 }}>
            <ShippingSummary
              availableShippingMethods={cart.availableShippingMethods}
              chosenShippingMethods={cart.chosenShippingMethods}
            />
          </Box>
        </Grid>
        <Grid item xs={6}>
          <Box
            sx={{
              alignItems: 'center',
              display: 'flex',
              flexDirection: 'row',
              justifyContent: 'space-between',
              mb: 1,
            }}
          >
            <Typography variant="h4">Payment Method</Typography>
            <IconButton href="/checkout/payment" size="small">
              <Edit fontSize="inherit" />
            </IconButton>
          </Box>
          <Divider />
          <Box sx={{ mt: 2 }}>
            <PaymentSummary chosenPaymentMethod={paymentMethod} creditCard={creditCard} />
          </Box>
        </Grid>
      </Grid>
      <Box sx={{ mt: 6 }}>
        <TextField
          multiline
          label="Note"
          placeholder="Please sign my prints"
          rows={3}
          onChange={(ev) => setCustomerNote(ev.target.value)}
        />
        <FormGroup>
          <FormControlLabel
            control={
              <Checkbox
                name="acceptTerms"
                checked={acceptTerms}
                onChange={(ev) => setAcceptsTerms(ev.target.checked)}
              />
            }
            label={
              <>
                With your order, you agree to have read and understood our{' '}
                <Link href="/terms">Terms &amp; Conditions</Link> your{' '}
                <Link href="/revocation">Right of Recission</Link> and our{' '}
                <Link href="/privacy-policy">Privacy Policy</Link>.
              </>
            }
          />
        </FormGroup>
      </Box>
      <Box sx={{ mt: 6 }}>
        {paymentMethod === 'braintree_paypal' ? (
          <PaypalButton
            cart={cart}
            disabled={!acceptTerms}
            shipping={customer.shipping}
            paymentClientToken={process.env.BRAINTREE_TOKENIZATION_KEY}
            onAuthorize={(nonce) => handlePayment(nonce)}
          />
        ) : (
          <Button fullWidth color="primary" disabled={!acceptTerms} onClick={handleSubmit}>
            Place your order
          </Button>
        )}
      </Box>
    </>
  );
}
Example #17
Source File: exposedPopulationAnalysis.tsx    From prism-frontend with MIT License 4 votes vote down vote up
ExposedPopulationAnalysis = ({
  result,
  id,
  extent,
  exposure,
}: AnalysisProps) => {
  const { startDate: selectedDate } = useSelector(dateRangeSelector);
  const isDataTableDrawerActive = useSelector(isDataTableDrawerActiveSelector);
  const analysisExposureLoading = useSelector(
    isExposureAnalysisLoadingSelector,
  );
  const data = useSelector(analysisResultSelector);

  const { t } = useSafeTranslation();
  const dispatch = useDispatch();

  const runExposureAnalysis = async () => {
    if (data) {
      dispatch(clearAnalysisResult());
    }

    if (!id || !extent || !exposure) {
      return;
    }

    if (!selectedDate) {
      throw new Error('Date must be given to run analysis');
    }

    const params: ExposedPopulationDispatchParams = {
      exposure,
      date: selectedDate,
      statistic: AggregationOperations.Sum,
      extent,
      wfsLayerId: id as LayerKey,
    };

    dispatch(requestAndStoreExposedPopulation(params));
  };

  // Since the exposure analysis doesn't have predefined table in configurations
  // and need to have a `TableKey` will use this util function to handle such case
  // used timestamp to avoid any potential rare name collision
  const generateUniqueTableKey = (activityName: string) => {
    return `${activityName}_${Date.now()}`;
  };

  const ResultSwitches = () => {
    const features = data?.featureCollection.features;
    const hasNoData = features?.length === 0;

    const handleTableViewChange = (e: React.ChangeEvent<HTMLInputElement>) => {
      dispatch(setIsDataTableDrawerActive(e.target.checked));
      dispatch(
        setCurrentDataDefinition({
          id: generateUniqueTableKey('exposure_analysis') as TableKey,
          title: data?.getTitle(t) || '',
          table: '',
          legendText: t(data?.legendText || ''),
        }),
      );
    };

    return (
      <>
        <FormGroup>
          <AnalysisFormControlLabel
            control={
              <Switch
                color="primary"
                disabled={hasNoData}
                checked={isDataTableDrawerActive}
                onChange={handleTableViewChange}
              />
            }
            label={t('Table View')}
          />
        </FormGroup>

        {hasNoData && (
          <Grid item>
            <Typography align="center" variant="h5">
              {t('No population was exposed')}
            </Typography>
          </Grid>
        )}
      </>
    );
  };

  if (!result || !(result instanceof ExposedPopulationResult)) {
    return (
      <>
        <AnalysisButton
          variant="contained"
          color="primary"
          size="small"
          onClick={runExposureAnalysis}
        >
          {t('Exposure Analysis')}
        </AnalysisButton>

        {analysisExposureLoading && <LinearProgress />}
      </>
    );
  }

  return (
    <>
      <AnalysisButton
        variant="contained"
        color="secondary"
        size="small"
        onClick={() => dispatch(clearAnalysisResult())}
      >
        {t('Clear Analysis')}
      </AnalysisButton>

      <ResultSwitches />
    </>
  );
}
Example #18
Source File: index.tsx    From prism-frontend with MIT License 4 votes vote down vote up
function Analyser({ extent, classes }: AnalyserProps) {
  const dispatch = useDispatch();
  const map = useSelector(mapSelector);
  const selectedLayers = useSelector(layersSelector);
  const {
    updateHistory,
    removeKeyFromUrl,
    resetAnalysisParams,
    updateAnalysisParams,
    getAnalysisParams,
  } = useUrlHistory();

  const availableDates = useSelector(availableDatesSelector);
  const analysisResult = useSelector(analysisResultSelector);

  const isAnalysisLoading = useSelector(isAnalysisLoadingSelector);
  const isMapLayerActive = useSelector(isAnalysisLayerActiveSelector);

  const {
    analysisHazardLayerId: hazardLayerIdFromUrl,
    analysisBaselineLayerId: baselineLayerIdFromUrl,
    analysisDate: selectedDateFromUrl,
    analysisStatistic: selectedStatisticFromUrl,
    analysisThresholdAbove: aboveThresholdFromUrl,
    analysisThresholdBelow: belowThresholdFromUrl,
    analysisAdminLevel: adminLevelFromUrl,
    analysisStartDate: selectedStartDateFromUrl,
    analysisEndDate: selectedEndDateFromUrl,
  } = getAnalysisParams();

  // form elements
  const [hazardLayerId, setHazardLayerId] = useState<LayerKey | undefined>(
    hazardLayerIdFromUrl,
  );
  const [statistic, setStatistic] = useState(
    (selectedStatisticFromUrl as AggregationOperations) ||
      AggregationOperations.Mean,
  );
  const [baselineLayerId, setBaselineLayerId] = useState<LayerKey | undefined>(
    baselineLayerIdFromUrl,
  );
  const [selectedDate, setSelectedDate] = useState<number | null>(null);
  const [belowThreshold, setBelowThreshold] = useState(
    belowThresholdFromUrl || '',
  );
  const [aboveThreshold, setAboveThreshold] = useState(
    aboveThresholdFromUrl || '',
  );
  const [thresholdError, setThresholdError] = useState<string | null>(null);

  const [isAnalyserFormOpen, setIsAnalyserFormOpen] = useState<boolean>(
    hazardLayerIdFromUrl !== undefined,
  );
  const [isTableViewOpen, setIsTableViewOpen] = useState(true);

  // for polygon intersection analysis
  const [adminLevel, setAdminLevel] = useState<AdminLevelType>(
    Number(adminLevelFromUrl || '1') as AdminLevelType,
  );
  const [startDate, setStartDate] = useState<number | null>(null);
  const [endDate, setEndDate] = useState<number | null>(null);

  // find layer for the given adminLevel
  const adminLevelLayer = getAdminLevelLayer(adminLevel);
  const adminLevelLayerData = useSelector(
    // if we couldn't find an admin layer, just return undefined
    adminLevelLayer ? layerDataSelector(adminLevelLayer.id) : () => undefined,
  ) as LayerData<BoundaryLayerProps> | undefined;

  // get variables derived from state
  const selectedHazardLayer = hazardLayerId
    ? (LayerDefinitions[hazardLayerId] as WMSLayerProps)
    : null;
  const hazardDataType: HazardDataType | null = selectedHazardLayer
    ? selectedHazardLayer.geometry || RasterType.Raster
    : null;
  const availableHazardDates = selectedHazardLayer
    ? getPossibleDatesForLayer(selectedHazardLayer, availableDates)?.map(
        d => new Date(d),
      ) || []
    : undefined;

  const BASELINE_URL_LAYER_KEY = 'baselineLayerId';
  const preSelectedBaselineLayer = selectedLayers.find(
    l => l.type === 'admin_level_data',
  );
  const [previousBaselineId, setPreviousBaselineId] = useState<
    LayerKey | undefined
  >(preSelectedBaselineLayer?.id);

  const { t } = useSafeTranslation();

  // check if there is any available date from the url, otherwise use last available date for the selected hazard layer
  const lastAvailableHazardDate = availableHazardDates
    ? getDateFromList(
        selectedDateFromUrl ? new Date(selectedDateFromUrl) : null,
        availableHazardDates,
      )?.getTime() || null
    : null;
  const lastAvailableHazardStartDate = availableHazardDates
    ? getDateFromList(
        selectedStartDateFromUrl ? new Date(selectedStartDateFromUrl) : null,
        availableHazardDates,
      )?.getTime() || null
    : null;
  const lastAvailableHazardEndDate = availableHazardDates
    ? getDateFromList(
        selectedEndDateFromUrl ? new Date(selectedEndDateFromUrl) : null,
        availableHazardDates,
      )?.getTime() || null
    : null;
  const { translatedColumns } = useAnalysisTableColumns(analysisResult);

  // set default date after dates finish loading and when hazard layer changes
  useEffect(() => {
    if (isNil(lastAvailableHazardDate)) {
      setSelectedDate(null);
    } else {
      setSelectedDate(lastAvailableHazardDate);
    }
    if (isNil(lastAvailableHazardStartDate)) {
      setStartDate(null);
    } else {
      setStartDate(lastAvailableHazardStartDate);
    }
    if (isNil(lastAvailableHazardEndDate)) {
      setEndDate(null);
    } else {
      setEndDate(lastAvailableHazardEndDate);
    }
  }, [
    availableDates,
    hazardLayerId,
    lastAvailableHazardDate,
    lastAvailableHazardStartDate,
    lastAvailableHazardEndDate,
  ]);

  const onOptionChange = <T extends string>(
    setterFunc: Dispatch<SetStateAction<T>>,
  ) => (event: React.ChangeEvent<HTMLInputElement>) => {
    const value = event.target.value as T;
    setterFunc(value);
    return value;
  };
  // specially for threshold values, also does error checking
  const onThresholdOptionChange = (thresholdType: 'above' | 'below') => (
    event: React.ChangeEvent<HTMLInputElement>,
  ) => {
    const setterFunc =
      thresholdType === 'above' ? setAboveThreshold : setBelowThreshold;
    const changedOption = onOptionChange(setterFunc)(event);
    // setting a value doesn't update the existing value until next render, therefore we must decide whether to access the old one or the newly change one here.
    const aboveThresholdValue = parseFloat(
      thresholdType === 'above' ? changedOption : aboveThreshold,
    );
    const belowThresholdValue = parseFloat(
      thresholdType === 'below' ? changedOption : belowThreshold,
    );
    if (belowThresholdValue > aboveThresholdValue) {
      setThresholdError('Below threshold is larger than above threshold!');
    } else {
      setThresholdError(null);
    }
  };

  const statisticOptions = Object.entries(AggregationOperations)
    .filter(([, value]) => value !== AggregationOperations.Sum) // sum is used only for exposure analysis.
    .map(([key, value]) => (
      <FormControlLabel
        key={key}
        value={value}
        control={
          <Radio
            className={classes.radioOptions}
            color="default"
            size="small"
          />
        }
        label={t(key)}
      />
    ));
  const activateUniqueBoundary = (forceAdminLevel?: BoundaryLayerProps) => {
    if (forceAdminLevel) {
      // remove displayed boundaries
      getDisplayBoundaryLayers().forEach(l => {
        if (l.id !== forceAdminLevel.id) {
          safeDispatchRemoveLayer(map, l, dispatch);
        }
      });

      safeDispatchAddLayer(
        map,
        { ...forceAdminLevel, isPrimary: true },
        dispatch,
      );
      return;
    }

    if (!baselineLayerId) {
      throw new Error('Layer should be selected to run analysis');
    }

    const baselineLayer = LayerDefinitions[
      baselineLayerId
    ] as AdminLevelDataLayerProps;

    if (baselineLayer.boundary) {
      const boundaryLayer = LayerDefinitions[
        baselineLayer.boundary
      ] as BoundaryLayerProps;
      // remove displayed boundaries
      getDisplayBoundaryLayers().forEach(l => {
        if (l.id !== boundaryLayer.id) {
          safeDispatchRemoveLayer(map, l, dispatch);
        }
      });

      safeDispatchAddLayer(
        map,
        { ...boundaryLayer, isPrimary: true },
        dispatch,
      );
    } else {
      getDisplayBoundaryLayers().forEach(l => {
        safeDispatchAddLayer(map, l, dispatch);
      });
    }
  };

  const deactivateUniqueBoundary = () => {
    if (!baselineLayerId) {
      throw new Error('Layer should be selected to run analysis');
    }
    const baselineLayer = LayerDefinitions[
      baselineLayerId
    ] as AdminLevelDataLayerProps;

    if (baselineLayer.boundary) {
      const boundaryLayer = LayerDefinitions[
        baselineLayer.boundary
      ] as BoundaryLayerProps;
      if (!getDisplayBoundaryLayers().includes(boundaryLayer)) {
        safeDispatchRemoveLayer(map, boundaryLayer, dispatch);
      }
    }

    getDisplayBoundaryLayers().forEach(l => {
      safeDispatchAddLayer(map, l, dispatch);
    });
  };

  const clearAnalysis = () => {
    dispatch(clearAnalysisResult());

    resetAnalysisParams();

    if (previousBaselineId) {
      const previousBaseline = LayerDefinitions[
        previousBaselineId
      ] as AdminLevelDataLayerProps;
      updateHistory(BASELINE_URL_LAYER_KEY, previousBaselineId);
      safeDispatchAddLayer(map, previousBaseline, dispatch);
      // check isMapLayerActive on analysis clear
      // to avoid miss behaviour on boundary layers
      dispatch(setIsMapLayerActive(true));
    }
  };

  const shareAnalysis = () => {
    copyTextToClipboard(window.location.href).then(() => {
      dispatch(
        addNotification({
          message: 'Link to this analysis copied to clipboard!',
          type: 'success',
        }),
      );
    });
  };

  const onMapSwitchChange = (e: ChangeEvent<HTMLInputElement>) => {
    dispatch(setIsMapLayerActive(e.target.checked));

    // hazard layer doesn't needs a display boundary
    // because it is already a vector
    if (hazardDataType === GeometryType.Polygon) {
      return;
    }

    if (isMapLayerActive) {
      deactivateUniqueBoundary();
      // check for previous baseline and bring it back
      if (previousBaselineId) {
        const previousBaseline = LayerDefinitions[
          previousBaselineId
        ] as AdminLevelDataLayerProps;
        updateHistory(BASELINE_URL_LAYER_KEY, previousBaselineId);
        safeDispatchAddLayer(map, previousBaseline, dispatch);
      }
    } else {
      // check for previous baseline and remove it before...
      if (previousBaselineId) {
        const previousBaseline = LayerDefinitions[
          previousBaselineId
        ] as AdminLevelDataLayerProps;
        removeKeyFromUrl(BASELINE_URL_LAYER_KEY);
        safeDispatchRemoveLayer(map, previousBaseline, dispatch);
      }

      // activating the unique boundary layer
      activateUniqueBoundary();
    }
  };

  const runAnalyser = async () => {
    if (preSelectedBaselineLayer) {
      setPreviousBaselineId(preSelectedBaselineLayer.id);
      removeKeyFromUrl(BASELINE_URL_LAYER_KEY);
      // no need to safely dispatch remove we are sure
      dispatch(removeLayer(preSelectedBaselineLayer));
    }

    if (analysisResult) {
      clearAnalysis();
    }

    if (!extent) {
      return;
    } // hasn't been calculated yet

    if (!selectedHazardLayer) {
      throw new Error('Hazard layer should be selected to run analysis');
    }

    if (hazardDataType === GeometryType.Polygon) {
      if (!startDate) {
        throw new Error('Date Range must be given to run analysis');
      }
      if (!endDate) {
        throw new Error('Date Range must be given to run analysis');
      }
      if (!adminLevelLayer || !adminLevelLayerData) {
        // technically we can't get here because the run analaysis button
        // is disabled while the admin level data loads
        // but we have to put this in so the typescript compiler
        // doesn't throw an error when we try to access the data
        // property of adminLevelLayerData
        throw new Error('Admin level data is still loading');
      }

      const params: PolygonAnalysisDispatchParams = {
        hazardLayer: selectedHazardLayer,
        adminLevel,
        adminLevelLayer,
        adminLevelData: adminLevelLayerData.data,
        startDate,
        endDate,
        extent,
      };
      activateUniqueBoundary(adminLevelLayer);
      // update history
      updateAnalysisParams({
        analysisHazardLayerId: hazardLayerId,
        analysisAdminLevel: adminLevel.toString(),
        analysisStartDate: moment(startDate).format(DEFAULT_DATE_FORMAT),
        analysisEndDate: moment(endDate).format(DEFAULT_DATE_FORMAT),
        analysisStatistic: statistic,
      });
      dispatch(requestAndStorePolygonAnalysis(params));
    } else {
      if (!selectedDate) {
        throw new Error('Date must be given to run analysis');
      }

      if (!baselineLayerId) {
        throw new Error('Baseline layer should be selected to run analysis');
      }

      const selectedBaselineLayer = LayerDefinitions[
        baselineLayerId
      ] as AdminLevelDataLayerProps;

      activateUniqueBoundary();

      const params: AnalysisDispatchParams = {
        hazardLayer: selectedHazardLayer,
        baselineLayer: selectedBaselineLayer,
        date: selectedDate,
        statistic,
        extent,
        threshold: {
          above: parseFloat(aboveThreshold) || undefined,
          below: parseFloat(belowThreshold) || undefined,
        },
      };

      // update history
      updateAnalysisParams({
        analysisHazardLayerId: hazardLayerId,
        analysisBaselineLayerId: baselineLayerId,
        analysisDate: moment(selectedDate).format(DEFAULT_DATE_FORMAT),
        analysisStatistic: statistic,
        analysisThresholdAbove: aboveThreshold || undefined,
        analysisThresholdBelow: belowThreshold || undefined,
      });

      dispatch(requestAndStoreAnalysis(params));
    }
  };

  return (
    <div className={classes.analyser}>
      <Button
        variant="contained"
        color="primary"
        onClick={() => {
          setIsAnalyserFormOpen(!isAnalyserFormOpen);
        }}
      >
        <BarChart fontSize="small" />
        <Typography variant="body2" className={classes.analyserLabel}>
          {t('Run Analysis')}
        </Typography>
        <ArrowDropDown fontSize="small" />
      </Button>

      <Box
        className={classes.analyserMenu}
        width={isAnalyserFormOpen ? 'min-content' : 0}
        padding={isAnalyserFormOpen ? '10px' : 0}
      >
        {isAnalyserFormOpen ? (
          <div>
            <div className={classes.newAnalyserContainer}>
              <div className={classes.analyserOptions}>
                <Typography variant="body2">{t('Hazard Layer')}</Typography>
                <LayerDropdown
                  type="wms"
                  value={hazardLayerId}
                  setValue={setHazardLayerId}
                  className={classes.selector}
                  placeholder="Choose hazard layer"
                />
              </div>

              {hazardDataType === GeometryType.Polygon && (
                <>
                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">Admin Level</Typography>
                    <SimpleDropdown
                      value={adminLevel}
                      options={range(getAdminLevelCount()).map(i => [
                        (i + 1) as AdminLevelType,
                        `Admin ${i + 1}`,
                      ])}
                      onChange={setAdminLevel}
                    />
                  </div>

                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">{t('Date Range')}</Typography>
                    <div className={classes.dateRangePicker}>
                      <Typography variant="body2">{t('Start')}</Typography>
                      <DatePicker
                        selected={startDate ? new Date(startDate) : null}
                        onChange={date =>
                          setStartDate(date?.getTime() || startDate)
                        }
                        maxDate={new Date()}
                        todayButton="Today"
                        peekNextMonth
                        showMonthDropdown
                        showYearDropdown
                        dropdownMode="select"
                        customInput={<Input />}
                        popperClassName={classes.calendarPopper}
                        includeDates={availableHazardDates}
                      />
                    </div>
                    <div className={classes.dateRangePicker}>
                      <Typography variant="body2">{t('End')}</Typography>
                      <DatePicker
                        selected={endDate ? new Date(endDate) : null}
                        onChange={date =>
                          setEndDate(date?.getTime() || endDate)
                        }
                        maxDate={new Date()}
                        todayButton="Today"
                        peekNextMonth
                        showMonthDropdown
                        showYearDropdown
                        dropdownMode="select"
                        customInput={<Input />}
                        popperClassName={classes.calendarPopper}
                        includeDates={availableHazardDates}
                      />
                    </div>
                  </div>
                </>
              )}

              {hazardDataType === RasterType.Raster && (
                <>
                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">{t('Statistic')}</Typography>
                    <FormControl component="div">
                      <RadioGroup
                        name="statistics"
                        value={statistic}
                        onChange={onOptionChange(setStatistic)}
                        row
                      >
                        {statisticOptions}
                      </RadioGroup>
                    </FormControl>
                  </div>
                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">
                      {t('Baseline Layer')}
                    </Typography>
                    <LayerDropdown
                      type="admin_level_data"
                      value={baselineLayerId || undefined}
                      setValue={setBaselineLayerId}
                      className={classes.selector}
                      placeholder="Choose baseline layer"
                    />
                  </div>
                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">{t('Threshold')}</Typography>
                    <TextField
                      id="filled-number"
                      error={!!thresholdError}
                      helperText={thresholdError}
                      className={classes.numberField}
                      label={t('Below')}
                      type="number"
                      value={belowThreshold}
                      onChange={onThresholdOptionChange('below')}
                      variant="filled"
                    />
                    <TextField
                      id="filled-number"
                      label={t('Above')}
                      className={classes.numberField}
                      value={aboveThreshold}
                      onChange={onThresholdOptionChange('above')}
                      type="number"
                      variant="filled"
                    />
                  </div>
                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">{t('Date')}</Typography>
                    <DatePicker
                      locale={t('date_locale')}
                      dateFormat="PP"
                      selected={selectedDate ? new Date(selectedDate) : null}
                      onChange={date =>
                        setSelectedDate(date?.getTime() || selectedDate)
                      }
                      maxDate={new Date()}
                      todayButton={t('Today')}
                      peekNextMonth
                      showMonthDropdown
                      showYearDropdown
                      dropdownMode="select"
                      customInput={<Input />}
                      popperClassName={classes.calendarPopper}
                      includeDates={availableHazardDates}
                    />
                  </div>
                </>
              )}
            </div>

            {!isAnalysisLoading &&
              analysisResult &&
              (analysisResult instanceof BaselineLayerResult ||
                analysisResult instanceof PolygonAnalysisResult) && (
                <>
                  <FormGroup>
                    <FormControlLabel
                      control={
                        <Switch
                          color="default"
                          checked={isMapLayerActive}
                          onChange={onMapSwitchChange}
                        />
                      }
                      label={t('Map View')}
                    />
                    <FormControlLabel
                      control={
                        <Switch
                          color="default"
                          checked={isTableViewOpen}
                          onChange={e => setIsTableViewOpen(e.target.checked)}
                        />
                      }
                      label={t('Table View')}
                    />
                  </FormGroup>
                  {isTableViewOpen && (
                    <AnalysisTable
                      tableData={analysisResult.tableData}
                      columns={translatedColumns}
                    />
                  )}
                  <Button
                    className={classes.innerAnalysisButton}
                    onClick={() =>
                      downloadCSVFromTableData(
                        analysisResult,
                        translatedColumns,
                        selectedDate,
                      )
                    }
                  >
                    <Typography variant="body2">{t('Download')}</Typography>
                  </Button>
                  <Button
                    className={classes.innerAnalysisButton}
                    onClick={clearAnalysis}
                  >
                    <Typography variant="body2">
                      {t('Clear Analysis')}
                    </Typography>
                  </Button>
                  <Button
                    className={classes.innerAnalysisButton}
                    onClick={shareAnalysis}
                  >
                    <Typography variant="body2">
                      {t('Share Analysis')}
                    </Typography>
                  </Button>
                </>
              )}
            {(!analysisResult ||
              analysisResult instanceof ExposedPopulationResult) && (
              <Button
                className={classes.innerAnalysisButton}
                onClick={runAnalyser}
                disabled={
                  !!thresholdError || // if there is a threshold error
                  isAnalysisLoading || // or analysis is currently loading
                  !hazardLayerId || // or hazard layer hasn't been selected
                  (hazardDataType === GeometryType.Polygon
                    ? !startDate || !endDate || !adminLevelLayerData
                    : !selectedDate || !baselineLayerId) // or date hasn't been selected // or baseline layer hasn't been selected
                }
              >
                <Typography variant="body2">{t('Run Analysis')}</Typography>
              </Button>
            )}
            {isAnalysisLoading ? <LinearProgress /> : null}
          </div>
        ) : null}
      </Box>
    </div>
  );
}
Example #19
Source File: SQFormCheckboxGroup.tsx    From SQForm with MIT License 4 votes vote down vote up
function SQFormCheckboxGroup({
  name,
  groupLabel,
  onChange,
  shouldDisplayInRow = false,
  shouldUseSelectAll = false,
  size = 'auto',
  children,
}: SQFormCheckboxGroupProps): JSX.Element {
  const {
    fieldState: {isFieldError, isFieldRequired},
    fieldHelpers: {handleChange, handleBlur, HelperTextComponent},
  } = useForm<CheckboxOption['value'][], React.ChangeEvent<HTMLInputElement>>({
    name,
    onChange,
  });

  const {setFieldValue} = useFormikContext();

  const handleSelectAllChange = (
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    if (!event.target.checked) {
      setFieldValue(name, []);
      return;
    }

    const enabledGroupValues = children.reduce(
      (acc: string[], checkboxOption: CheckboxOption) => {
        const {value, isDisabled} = checkboxOption;
        if (!isDisabled) {
          return [...acc, String(value)];
        }

        return acc;
      },
      []
    );
    setFieldValue(name, enabledGroupValues);
  };

  const childrenToCheckboxGroupItems = () => {
    const providedCheckboxItems = children.map((checkboxOption) => {
      const {label, value, isDisabled, inputProps} = checkboxOption;

      return (
        <SQFormCheckboxGroupItem
          groupName={name}
          label={label}
          value={value}
          isRowDisplay={shouldDisplayInRow}
          onChange={handleChange}
          isDisabled={isDisabled}
          inputProps={inputProps}
          key={`SQFormCheckboxGroupItem_${value}`}
        />
      );
    });
    if (shouldUseSelectAll) {
      return [
        <SQFormCheckbox
          name={`${name}SelectAll`}
          label="All"
          onChange={handleSelectAllChange}
          key={`${name}_selectAll`}
        />,
        ...providedCheckboxItems,
      ];
    }

    return providedCheckboxItems;
  };

  return (
    <Grid item sm={size}>
      <FormControl
        component="fieldset"
        required={isFieldRequired}
        error={isFieldError}
        onBlur={handleBlur}
      >
        <FormLabel
          component="legend"
          classes={{
            root: 'MuiInputLabel-root',
            asterisk: 'MuiInputLabel-asterisk',
          }}
        >
          {groupLabel}
        </FormLabel>
        <FormGroup row={shouldDisplayInRow}>
          {childrenToCheckboxGroupItems()}
        </FormGroup>
        <FormHelperText>{HelperTextComponent}</FormHelperText>
      </FormControl>
    </Grid>
  );
}
Example #20
Source File: Game.tsx    From Lux-Viewer-2021 with Apache License 2.0 4 votes vote down vote up
GameComponent = () => {
  const [notifWindowOpen, setNotifWindowOpen] = useState(false);
  const [replayData, setReplayData] = useState(null);
  const [notifMsg, setNotifMsg] = useState('');
  const [running, setRunning] = useState(false);
  const [useKaggleReplay, setUseKaggleReplay] = useState(true);
  const [playbackSpeed, _setPlaybackSpeed] = useState(1);
  const [replayVersion, setReplayVersion] = useState('');
  const [warningMessage, setWarningMessage] = useState('');
  const setPlaybackSpeed = (speed: number) => {
    if (speed >= 0.5 && speed <= 32) {
      _setPlaybackSpeed(speed);
      main.speed = speed;
    }
  };
  const url = new URL(window.location.href);
  const searchlist = url.search.slice(1).split('&');
  let scale =
    searchlist.length > 0 && searchlist[0].split('=')[0] === 'scale'
      ? parseFloat(searchlist[0].split('=')[1])
      : 1.5;
  if (isNaN(scale)) {
    scale = 1.5;
  }
  let zoom = 1 / scale;
  let scaleSize = scale / 10;
  const [visualScale, _setVisualScale] = useState(scale / 4);
  const setVisualScale = (scale: number) => {
    if (scale >= scaleSize && scale <= 2) {
      _setVisualScale(scale);
    }
  };
  const [isReady, setReady] = useState(false);
  const [warningsPanelOpen, setWarningsPanelOpen] = useState(false);
  const [selectedTileData, setTileData] = useState<FrameTileData>(null);
  const [trackedUnitID, setTrackedUnitID] = useState<string>(null);
  const [game, setGame] = useState<Phaser.Game>(null);
  const [main, setMain] = useState<MainScene>(null);
  const [configs, setConfigs] = useState<LuxMatchConfigs>(null);
  const [sliderConfigs, setSliderConfigs] = useState({
    step: 1,
    min: 0,
    max: 1000,
  });

  const [turn, setTurn] = useState(0);
  const [currentFrame, setFrame] = useState<Frame>(null);
  const [uploading, setUploading] = useState(false);
  const fileInput = React.createRef<HTMLInputElement>();

  // If the game changes, put a setup callback to set up controller configs
  useEffect(() => {
    if (game) {
      game.events.on('setup', () => {
        // @ts-ignore
        const main: MainScene = game.scene.scenes[0];
        setMain(main);
        const configs = main.luxgame.configs;
        setConfigs(configs as LuxMatchConfigs);

        setSliderConfigs({
          min: 0,
          max: Math.min(configs.parameters.MAX_DAYS, main.frames.length - 1),
          step: 1,
        });
        setReady(true);
      });
    }
  }, [game]);

  // If play is toggled (running) or playback speed is changed, we update the playback
  useEffect(() => {
    if (running && configs) {
      let currTurn = turn;
      const interval = setInterval(() => {
        if (
          currTurn >=
          Math.min(configs.parameters.MAX_DAYS, main.frames.length - 1)
        ) {
          setRunning(false);
          return;
        }
        currTurn += 1;
        moveToTurn(currTurn);

        setTurn(currTurn);
      }, 1000 / playbackSpeed);
      return () => clearInterval(interval);
    }
  }, [running, playbackSpeed]);

  // if game loaded is ready, move to turn 0 and load that turn's frame
  useEffect(() => {
    if (isReady) {
      moveToTurn(0);
    }
  }, [isReady]);

  // whenever the main scene is changed or visualScale is changed, call main to change the visual scale appropriately.
  useEffect(() => {
    if (main && visualScale) {
      main.overallScale = visualScale;
      if (main.activeImageTile) {
        // main.activeImageTile.setY(main.originalTileY);
        // main.activeImageTile.clearTint();
        // main.activeImageTile = null;
        // main.originalTileY
      }
      // move to current turn to rerender all objects appropriately
      moveToTurn(turn);
      // TODO: do a on scale change instead inside main
      main.floorImageTiles.forEach((info, hash) => {
        [info.source, info.overlay, info.roadOverlay].forEach(
          (tileImage, i) => {
            const pos = hashToMapPosition(hash);
            const ps = mapCoordsToIsometricPixels(pos.x, pos.y, {
              scale: main.overallScale,
              width: main.mapWidth,
              height: main.mapHeight,
            });
            tileImage.setScale(main.defaultScales.block * main.overallScale);
            tileImage.setX(ps[0]);
            tileImage.setY(ps[1]);
            if (tileImage == main.activeImageTile) {
              main.originalTileY = tileImage.y;
            }
            if (tileImage == main.hoverImageTile) {
              main.originalHoverImageTileY = tileImage.y;
            }
          }
        );
      });
      const ps = mapCoordsToIsometricPixels(
        main.mapWidth / 2,
        main.mapWidth / 2,
        {
          scale: main.overallScale,
          width: main.mapWidth,
          height: main.mapHeight,
        }
      );
      // [main.islandbaseImage, main.islandbaseNightImage].forEach((tileImage) => {
      //   tileImage.setX(ps[0]);
      //   let f = 32.3;
      //   if (main.mapWidth <= 16) f = 31.7;
      //   tileImage.setY(ps[1] + main.overallScale * main.mapWidth * f);
      //   tileImage.setScale(
      //     main.defaultScales.islandBase * main.overallScale * main.mapWidth
      //   );
      // });
    }
  }, [main, visualScale]);

  /** handle the change of the slider to move turns */
  const handleSliderChange = (_event: any, newValue: number) => {
    setRunning(false);
    moveToTurn(newValue);
  };

  /** Move to a specific turn and render that turn's frame */
  const moveToTurn = (turn: number) => {
    setTurn(turn);
    main.renderFrame(turn);

    setFrame(main.frames[turn]);
    //render the right bg color
    const colors = [
      '00AFBD',
      '438D91',
      '846D68',
      'A55D53',
      '704A60',
      '4D3D59',
      '2C2E33',
    ];
    const canvasWrapper = document
      .getElementById('content')
      .getElementsByTagName('canvas')[0];
    const dayLength = main.luxgame.configs.parameters.DAY_LENGTH;
    const cycleLength =
      dayLength + main.luxgame.configs.parameters.NIGHT_LENGTH;
    let idx = 0;
    if (
      turn % cycleLength >= dayLength - 5 &&
      turn % cycleLength < dayLength + 1
    ) {
      idx = (turn % cycleLength) - (dayLength - 5);
    } else if (
      turn % cycleLength >= dayLength + 1 &&
      turn % cycleLength < cycleLength - 1
    ) {
      idx = 6;
    } else if (turn % cycleLength >= cycleLength - 1) {
      idx = 5;
    } else if (turn % cycleLength < 5 && turn > 5) {
      idx = 6 - ((turn % cycleLength) + 2);
    }
    canvasWrapper.style.transition = `background-color linear ${
      1 / main.speed
    }s`;
    canvasWrapper.style.backgroundColor = `#${colors[idx]}`;
  };

  /** track a unit by id */
  const trackUnit = (id: string) => {
    setTrackedUnitID(id);
    main.untrackUnit();
    main.trackUnit(id);
  };
  const untrackUnit = (id: string) => {
    setTrackedUnitID(null);
    main.untrackUnit(true);
  };

  /** load game given json replay data */
  const loadGame = (jsonReplayData: any) => {
    let versionMisMatch = false;
    let versionvals = ['x', 'x'];
    setReplayVersion(jsonReplayData.version);
    if (jsonReplayData.version !== undefined) {
      versionvals = jsonReplayData.version.split('.');
      if (
        versionvals[0] !== clientConfigs.version[0] ||
        versionvals[1] !== clientConfigs.version[2]
      ) {
        versionMisMatch = true;
      }
    }
    if (versionMisMatch) {
      let warningMessage = `Replay file works on version ${versionvals[0]}.${versionvals[1]}.x but client is on version ${clientConfigs.version}. The visualizer will not be able to parse this replay file. Download an older visualizer with version ${versionvals[0]}.${versionvals[1]}.x here to watch the replay: https://github.com/Lux-AI-Challenge/LuxViewer2021/releases`;
      setWarningMessage(warningMessage);
      return;
    }
    if (game) {
      game.destroy(true, false);
    }
    setReady(false);
    setReplayData(jsonReplayData);
    const newgame = createGame({
      replayData: jsonReplayData,
      handleTileClicked,
      handleUnitTracked,
      zoom,
    });
    setGame(newgame);
  };

  /** handle uploaded files */
  const handleUpload = () => {
    setUploading(true);
    setUseKaggleReplay(false);
    if (fileInput.current.files.length) {
      const file = fileInput.current.files[0];
      const name = file.name;
      const meta = name.split('.');

      if (meta[meta.length - 1] === 'json') {
        file
          .text()
          .then(JSON.parse)
          .then((data) => {
            setUploading(false);
            data = parseReplayData(data);
            loadGame(data);
          })
          .catch((err) => {
            console.error(err);
            alert(err);
          });
      }
    }
  };
  useEffect(() => {
    //@ts-ignore
    if (window.kaggle) {
      // check if window.kaggle.environment is valid and usable
      if (
        //@ts-ignore
        window.kaggle.environment &&
        //@ts-ignore
        window.kaggle.environment.steps.length > 1
      ) {
        console.log('Embedded kaggle replay detected, parsing it');
        //@ts-ignore
        let replay = window.kaggle.environment;
        replay = parseReplayData(replay);
        loadGame(replay);
      } else {
        console.log(
          'Kaggle detected, but no replay, listening for postMessage'
        );
        // add this listener only once
        window.addEventListener(
          'message',
          (event) => {
            // Ensure the environment names match before updating.
            try {
              if (event.data.environment.name == 'lux_ai_2021') {
                // updateContext(event.data);
                let replay = event.data.environment;
                console.log('post message:');
                console.log(event.data);
                replay = parseReplayData(replay);
                loadGame(replay);
                const el = document.getElementsByTagName('html');
                if (window.innerWidth * 0.65 <= 768) {
                  el[0].style.fontSize = '6pt';
                }
                if (window.innerWidth * 0.65 <= 1280) {
                  el[0].style.fontSize = '8pt';
                }
              }
            } catch (err) {
              console.error('Could not parse game');
              console.error(err);
            }
          },
          false
        );
      }
    }
    // change root font size depending on window size
    const el = document.getElementsByTagName('html');
    if (window.innerWidth <= 768) {
      // set the font size of root html smaller since this is being viewed on the kaggle page
      el[0].style.fontSize = '6pt';
    } else if (window.innerWidth <= 1280) {
      el[0].style.fontSize = '8pt';
    }
    // loadGame(parseReplayData(debug_replay));
  }, []);
  useEffect(() => {
    const handleKeyDown = (event: globalThis.KeyboardEvent) => {
      switch (event.key) {
        case 'ArrowUp':
          setPlaybackSpeed(playbackSpeed * 2);
          break;
        case 'ArrowDown':
          setPlaybackSpeed(playbackSpeed / 2);
          break;
        case 'ArrowRight':
          setRunning(false);
          if (
            turn < Math.min(configs.parameters.MAX_DAYS, main.frames.length - 1)
          ) {
            moveToTurn(turn + 1);
          }
          break;
        case 'ArrowLeft':
          setRunning(false);
          if (turn > 0) {
            moveToTurn(turn - 1);
          }
          break;
      }
    };
    document.addEventListener('keydown', handleKeyDown);
    return () => {
      document.removeEventListener('keydown', handleKeyDown);
    };
  }, [turn, playbackSpeed, main, configs]);

  const handleTileClicked = (data: FrameTileData) => {
    setTileData(data);
    // deal with unit tracking, which unfortunately has data fragmented between react and the phaser scene
  };
  const handleUnitTracked = (id: string) => {
    setTrackedUnitID(id);
  };

  const [debugOn, _setDebug] = useState(true);
  const setDebug = (
    e: React.ChangeEvent<HTMLInputElement>,
    checked: boolean
  ) => {
    _setDebug(checked);
    main.debug = checked;
    moveToTurn(turn);
  };
  const renderDebugModeButton = () => {
    return (
      <FormGroup row className="debug-mode-button-wrapper">
        <FormControlLabel
          control={
            <Switch checked={debugOn} onChange={setDebug} name="checkedA" />
          }
          label="Debug Mode"
        />
      </FormGroup>
    );
  };
  let sidetextAnnotations = [];
  if (currentFrame && currentFrame.annotations) {
    sidetextAnnotations = currentFrame.annotations.filter((v) => {
      return (
        v.command.length > 2 &&
        v.command.split(' ')[0] === Game.ACTIONS.DEBUG_ANNOTATE_SIDETEXT
      );
    });
  }
  return (
    <div className="Game">
      <ThemeProvider theme={theme}>
        <div id="content"></div>
        {!isReady && warningMessage === '' && (
          <div className="upload-no-replay-wrapper">
            <p>Welcome to the Lux AI Season 1 Visualizer</p>
            <div>
              <Button
                className="upload-btn"
                color="secondary"
                variant="contained"
                onClick={() => {
                  fileInput.current.click();
                }}
              >
                <span className="upload-text">Upload a replay</span>
                <img className="upload-icon-no-replay" src={UploadSVG} />
              </Button>
              <p></p>
              <input
                accept=".json, .luxr"
                type="file"
                style={{ display: 'none' }}
                onChange={handleUpload}
                ref={fileInput}
              />
            </div>
          </div>
        )}
        {warningMessage !== '' && (
          <div className="upload-no-replay-wrapper">
            <p>{warningMessage}</p>
          </div>
        )}

        <div id="version-number">
          {replayVersion && (
            <>
              <strong>Replay Version: </strong>
              {replayVersion}
              <br></br>
            </>
          )}
          <strong>Client Version: </strong>
          {clientConfigs.version}
        </div>
        {isReady && (
          <div>
            <Controller
              turn={turn}
              moveToTurn={moveToTurn}
              handleUpload={handleUpload}
              running={running}
              isReady={isReady}
              setRunning={setRunning}
              playbackSpeed={playbackSpeed}
              setPlaybackSpeed={setPlaybackSpeed}
              fileInput={fileInput}
              sliderConfigs={sliderConfigs}
              handleSliderChange={handleSliderChange}
            />
            {debugOn && sidetextAnnotations.length > 0 && (
              <div className="debug-sidetext">
                <h4>Debug Text</h4>
                {sidetextAnnotations
                  .sort((v) => v.agentID)
                  .map((v) => {
                    return (
                      <div className={`sidetext-${v.agentID}`}>
                        {v.command.split(' ').slice(1).join(' ').split("'")[1]}
                      </div>
                    );
                  })}
              </div>
            )}
            <Button
              className="warnings-button"
              onClick={() => {
                setWarningsPanelOpen(true);
              }}
            >
              Warnings ({currentFrame.errors.length})
            </Button>
            <WarningsPanel
              panelOpen={warningsPanelOpen}
              closePanel={() => {
                setWarningsPanelOpen(false);
              }}
              turn={turn}
              warnings={currentFrame.errors}
            />
            <div className="tile-stats-wrapper">
              {selectedTileData && (
                <TileStats
                  {...selectedTileData}
                  cities={currentFrame.cityData}
                  trackUnit={trackUnit}
                  untrackUnit={untrackUnit}
                  trackedUnitID={trackedUnitID}
                />
              )}
            </div>
            <div className="global-stats-wrapper">
              {main && (
                <GlobalStats
                  currentFrame={currentFrame}
                  turn={turn}
                  accumulatedStats={main.accumulatedStats}
                  teamDetails={replayData.teamDetails}
                  staticGlobalStats={main.globalStats}
                />
              )}
            </div>
            {renderDebugModeButton()}
            <ZoomInOut
              className="zoom-in-out"
              handleZoomIn={() => {
                setVisualScale(visualScale + scaleSize);
              }}
              handleZoomOut={() => {
                setVisualScale(visualScale - scaleSize);
              }}
            />
            <div className="map-meta-wrapper">
              <p>
                <strong>Map Size:</strong>{' '}
                {(main.pseudomatch.state.game as Game).map.width}x
                {(main.pseudomatch.state.game as Game).map.height}
              </p>
              <p>
                <strong>Map Seed:</strong>{' '}
                {(main.pseudomatch.state.game as Game).configs.seed}
              </p>
            </div>
          </div>
        )}
      </ThemeProvider>
    </div>
  );
}
Example #21
Source File: Content.tsx    From Demae with MIT License 4 votes vote down vote up
Form = ({ provider }: { provider: ProviderDraft }) => {
	const classes = useStyles()
	const [setProcessing] = useProcessing()
	const [setMessage] = useSnackbar()
	const [thumbnail, setThumbnail] = useState<File | undefined>()
	const [cover, setCover] = useState<File | undefined>()
	const [name] = useTextField(provider.name)
	const [caption] = useTextField(provider.caption)
	const [description] = useTextField(provider.description)
	const providerCapabilities = provider.capabilities || []
	const [capabilities, setCapabilities] = useState<{ [key in Capability]: boolean }>({
		"download": providerCapabilities.includes("download"),
		"instore": providerCapabilities.includes("instore"),
		"online": providerCapabilities.includes("online"),
		"pickup": providerCapabilities.includes("pickup")
	})

	const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
		setCapabilities({ ...capabilities, [event.target.name]: event.target.checked });
	};

	const capabilitiesError = Object.values(capabilities).filter((v) => v).length < 1;

	const uploadThumbnail = (file: File): Promise<StorageFile | undefined> => {
		const ref = firebase.storage().ref(provider.documentReference.path + "/thumbnail.jpg")
		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 uploadCover = (file: File): Promise<StorageFile | undefined> => {
		const ref = firebase.storage().ref(provider.documentReference.path + "/cover.jpg")
		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 [isEditing, setEdit] = useEdit(async (event) => {
		event.preventDefault()
		if (!provider) return
		setProcessing(true)
		if (thumbnail) {
			const thumbnailImage = await uploadThumbnail(thumbnail)
			if (thumbnailImage) {
				provider.thumbnailImage = thumbnailImage
			}
		}
		if (cover) {
			const coverImage = await uploadCover(cover)
			if (coverImage) {
				provider.coverImage = coverImage
			}
		}
		try {
			provider.name = name.value as string
			provider.caption = caption.value as string
			provider.description = description.value as string
			provider.capabilities = Object.keys(capabilities).filter(value => capabilities[value]) as Capability[]
			await provider.save()
		} catch (error) {
			console.log(error)
		}
		setProcessing(false)
		setMessage("success", "Change your provider informations.")
		setEdit(false)
	})

	useContentToolbar(() => {
		if (!provider) return <></>
		if (isEditing) {
			return (
				<Box display="flex" flexGrow={1} justifyContent="space-between" paddingX={1}>
					<Button variant="outlined" color="primary" size="small" onClick={() => setEdit(false)}>Cancel</Button>
					<Button variant="contained" color="primary" size="small" type="submit" disabled={capabilitiesError}
					>Save</Button>
				</Box>
			)
		}
		return (
			<Box display="flex" flexGrow={1} justifyContent="space-between" paddingX={1}>
				<Box display="flex" flexGrow={1} justifyContent="flex-end">
					<Button variant="outlined" color="primary" size="small" onClick={() => setEdit(true)}>Edit</Button>
				</Box>
			</Box>
		)
	})

	if (isEditing) {
		return (
			<Container maxWidth="sm">
				<Box padding={2}>
					<Typography variant="h1" gutterBottom>Shop</Typography>
					<Paper>
						<Box display="flex" position="relative" flexGrow={1}>
							<Box display="flex" flexGrow={1} height={300}>
								<DndCard
									url={provider?.coverImageURL()}
									onDrop={(files) => {
										const file = files[0] as File
										setCover(file)
									}} />
							</Box>
							<Box display="flex" position="absolute" zIndex={1050} flexGrow={1} width={120} height={120} border={2} borderColor="white" borderRadius="50%" bottom={-16} left={16} style={{ overflow: "hidden" }}>
								<DndCard
									url={provider?.thumbnailImageURL()}
									onDrop={(files) => {
										const file = files[0] as File
										setThumbnail(file)
									}} />
							</Box>
						</Box>
						<Box padding={2} paddingTop={5}>
							<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>
								<TextField variant="outlined" margin="dense" required fullWidth multiline rows={8} {...description} />
							</Box>
						</Box>

						<Box paddingX={2} paddingBottom={1}>
							<Typography variant="subtitle1" gutterBottom>Sales method</Typography>
							<FormControl required error={capabilitiesError} component="fieldset">
								<FormLabel component="legend">Please select at least one selling method</FormLabel>
								<FormGroup>
									<FormControlLabel
										control={<Checkbox checked={capabilities.download} onChange={handleChange} name="download" />}
										label="Download"
									/>
									<FormControlLabel
										control={<Checkbox checked={capabilities.instore} onChange={handleChange} name="instore" />}
										label="In-Store Sales"
									/>
									<FormControlLabel
										control={<Checkbox checked={capabilities.online} onChange={handleChange} name="online" />}
										label="Online Sales"
									/>
									<FormControlLabel
										control={<Checkbox checked={capabilities.pickup} onChange={handleChange} name="pickup" />}
										label="Pickup"
									/>
								</FormGroup>
								<FormHelperText>You can choose multiple sales methods.</FormHelperText>
							</FormControl>
						</Box>
					</Paper>
					<Box padding={1}>
						<Typography variant="body2" color="textSecondary" gutterBottom>ID: {provider.id}</Typography>
					</Box>
				</Box>
			</Container>
		)
	}

	return (
		<Container maxWidth="sm">
			<Box padding={2}>
				<Typography variant="h1" gutterBottom>Shop</Typography>
				<Paper>
					<Box display="flex" position="relative" flexGrow={1}>
						<Box display="flex" flexGrow={1} height={300}>
							<Avatar variant="square" src={provider.coverImageURL()} style={{
								minHeight: "300px",
								width: "100%"
							}}>
								<ImageIcon />
							</Avatar>
						</Box>
						<Box display="flex" position="absolute" zIndex={1050} flexGrow={1} width={120} height={120} border={2} borderColor="white" borderRadius="50%" bottom={-16} left={16} style={{ overflow: "hidden" }}>
							<Avatar variant="square" src={provider.thumbnailImageURL()} style={{
								minHeight: "64px",
								height: "100%",
								width: "100%"
							}}>
								<ImageIcon />
							</Avatar>
						</Box>
					</Box>

					<Box padding={2} paddingTop={5}>
						<Box paddingBottom={2}>
							<Typography variant="subtitle1" gutterBottom>Name</Typography>
							<Typography variant="body1" gutterBottom>{provider.name}</Typography>
						</Box>
						<Box paddingBottom={2}>
							<Typography variant="subtitle1" gutterBottom>Caption</Typography>
							<Typography variant="body1" gutterBottom>{provider.caption}</Typography>
						</Box>
						<Box paddingBottom={2}>
							<Typography variant="subtitle1" gutterBottom>Description</Typography>
							<Typography variant="body1" gutterBottom>{provider.description}</Typography>
						</Box>
					</Box>

					<Box paddingX={2} paddingBottom={1}>
						<Typography variant="subtitle1" gutterBottom>Sales method</Typography>
						<FormControl disabled component="fieldset">
							<FormGroup>
								<FormControlLabel
									control={<Checkbox checked={capabilities.download} onChange={handleChange} name="download" />}
									label="Download"
								/>
								<FormControlLabel
									control={<Checkbox checked={capabilities.instore} onChange={handleChange} name="instore" />}
									label="In-Store Sales"
								/>
								<FormControlLabel
									control={<Checkbox checked={capabilities.online} onChange={handleChange} name="online" />}
									label="Online Sales"
								/>
								<FormControlLabel
									control={<Checkbox checked={capabilities.pickup} onChange={handleChange} name="pickup" />}
									label="Pickup"
								/>
							</FormGroup>
						</FormControl>
					</Box>
				</Paper>
				<Box padding={1}>
					<Typography variant="body2" color="textSecondary" gutterBottom>ID: {provider.id}</Typography>
				</Box>
			</Box>
		</Container>
	)
}