@material-ui/core#RadioGroup TypeScript Examples

The following examples show how to use @material-ui/core#RadioGroup. 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: radio_input.tsx    From jupyter-extensions with Apache License 2.0 6 votes vote down vote up
/** Funtional Component for Radio input fields */
export function RadioInput(props: RadioInputProps) {
  const { options, ...groupProps } = props;
  return (
    <ThemeProvider theme={theme}>
      <RadioGroup {...groupProps}>
        {options &&
          options.map((o, i) => (
            <FormControlLabel
              key={i}
              value={o.value}
              control={<Radio />}
              label={o.text}
              className={css.primaryTextColor}
            />
          ))}
      </RadioGroup>
    </ThemeProvider>
  );
}
Example #2
Source File: LabeledRadio.tsx    From UsTaxes with GNU Affero General Public License v3.0 6 votes vote down vote up
export function LabeledRadio<A>(props: LabeledRadioProps<A>): ReactElement {
  const { label, name, values, useGrid = true, sizes = { xs: 12 } } = props

  const classes = useStyles()
  const { control } = useFormContext<A>()

  return (
    <ConditionallyWrap
      condition={useGrid}
      wrapper={(children) => (
        <Grid item {...sizes}>
          {children}
        </Grid>
      )}
    >
      <Controller
        name={name}
        render={({ field: { value, onChange } }) => (
          <div className={classes.root}>
            <FormControl component="fieldset">
              <FormLabel>{label}</FormLabel>
              <RadioGroup name={name} value={value} onChange={onChange}>
                {values.map(([rowLabel, rowValue], i) => (
                  <FormControlLabel
                    key={i}
                    value={rowValue}
                    control={<Radio color="primary" />}
                    label={rowLabel}
                  />
                ))}
              </RadioGroup>
            </FormControl>
          </div>
        )}
        control={control}
      />
    </ConditionallyWrap>
  )
}
Example #3
Source File: TemplateDemoControls.tsx    From clearflask with Apache License 2.0 6 votes vote down vote up
render() {
    return (
      <RadioGroup
        className={this.props.classes.extraControls}
        value={this.props.value}
        onChange={(e, val) => this.props.onChange(val)}
      >
        {Object.keys(demoOptions).map(option => (
          <FormControlLabel key={option} value={option} control={<Radio color='primary' />}
            label={<FormHelperText component='span'>{option}</FormHelperText>} />
        ))}
      </RadioGroup>
    );
  }
Example #4
Source File: RoadmapControls.tsx    From clearflask with Apache License 2.0 6 votes vote down vote up
render() {
    return (
      <RadioGroup
        className={this.props.classes.extraControls}
        value={this.state.type}
        onChange={(e, val) => {
          switch (val) {
            case 'development':
              this.setState({ type: val });
              this.props.templater.demoBoardPreset('development');
              break;
            case 'funding':
              this.setState({ type: val });
              this.props.templater.demoBoardPreset('funding');
              break;
            case 'design':
              this.setState({ type: val });
              this.props.templater.demoBoardPreset('design');
              break;
          }
        }}
      >
        <FormControlLabel value='development' control={<Radio color='primary' />} label='Development' />
        <FormControlLabel value='funding' control={<Radio color='primary' />} label='Custom' />
        {/* <FormControlLabel value='design' control={<Radio color='primary' />} label="Design" /> */}
      </RadioGroup>
    );
  }
Example #5
Source File: PrioritizationControlsCredits.tsx    From clearflask with Apache License 2.0 6 votes vote down vote up
render() {
    return (
      <RadioGroup
        className={this.props.classes.extraControls}
        value={this.state.fundingType}
        onChange={this.handleChangeFundingType.bind(this)}
      >
        <FormControlLabel value='currency' control={<Radio color='primary' />}
          label={<FormHelperText component='span'>Currency</FormHelperText>} />
        <FormControlLabel value='time' control={<Radio color='primary' />}
          label={<FormHelperText component='span'>{this.props.forContentCreator ? 'Time' : 'Development time'}</FormHelperText>} />
        <FormControlLabel value={this.props.forContentCreator ? 'heart' : 'beer'} control={<Radio color='primary' />}
          label={<FormHelperText component='span'>Customize</FormHelperText>} />
      </RadioGroup>
    );
  }
Example #6
Source File: Settings.tsx    From backstage with Apache License 2.0 6 votes vote down vote up
Settings = () => {
  const { type, handleChangeType } = useRandomJoke();
  const JOKE_TYPES: JokeType[] = ['any' as JokeType, 'programming' as JokeType];
  return (
    <FormControl component="fieldset">
      <FormLabel component="legend">Joke Type</FormLabel>
      <RadioGroup
        aria-label="joke type"
        value={type}
        onChange={e => handleChangeType(e.target.value)}
      >
        {JOKE_TYPES.map(t => (
          <FormControlLabel
            key={t}
            value={t}
            control={<Radio />}
            label={upperFirst(t)}
          />
        ))}
      </RadioGroup>
    </FormControl>
  );
}
Example #7
Source File: SQFormRadioButtonGroup.tsx    From SQForm with MIT License 5 votes vote down vote up
function SQFormRadioButtonGroup({
  name,
  onChange,
  shouldDisplayInRow = false,
  size = 'auto',
  groupLabel,
  children,
}: SQFormRadioButtonGroupProps): React.ReactElement {
  const {
    fieldState: {isFieldError, isFieldRequired},
    formikField: {field},
    fieldHelpers: {handleChange, handleBlur, HelperTextComponent},
  } = useForm<
    RadioButtonInputItemProps['value'],
    React.ChangeEvent<HTMLInputElement>
  >({
    name,
    onChange,
  });

  const childrenToRadioGroupItems = () => {
    return children.map((radioOption) => {
      const {label, value, isDisabled, InputProps} = radioOption;
      return (
        <SQFormRadioButtonGroupItem
          label={label}
          value={value}
          isDisabled={isDisabled}
          isRowDisplay={shouldDisplayInRow}
          InputProps={InputProps}
          key={`SQFormRadioButtonGroupItem_${value}`}
        />
      );
    });
  };

  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>
        <RadioGroup
          value={field.value}
          row={shouldDisplayInRow}
          aria-label={`SQFormRadioButtonGroup_${name}`}
          name={name}
          onChange={handleChange}
        >
          {childrenToRadioGroupItems()}
        </RadioGroup>
        <FormHelperText>{HelperTextComponent}</FormHelperText>
      </FormControl>
    </Grid>
  );
}
Example #8
Source File: RiskCriteria.tsx    From listo with MIT License 5 votes vote down vote up
RiskCriteria = ({
  text,
  options,
  handleRiskOption,
  description,
}: Props) => {
  const classes = useStyles({});

  if (!options) {
    // TODO: this should be moved to pre-validation
    return null;
  }

  const selectedOption = options.find(o => o.selected);
  const value = selectedOption ? selectedOption.text : UNSELECTED_KEY;

  const ExpansionPanelDetails = withStyles(theme => ({
    root: {
      padding: theme.spacing(2),
      backgroundColor: '#f5f9fe',
    },
  }))(MuiExpansionPanelDetails);

  return (
    <React.Fragment>
      <Paper className={classes.root}>
        <Grid container spacing={2}>
          <Grid item xs={12}>
            <ExpansionPanel>
              <ExpansionPanelSummary
                expandIcon={<ExpandMoreIcon />}
                aria-controls="panel1a-content"
              >
                <Typography>{text}</Typography>
              </ExpansionPanelSummary>
              <ExpansionPanelDetails>
                <Typography>{description}</Typography>
              </ExpansionPanelDetails>
            </ExpansionPanel>
          </Grid>
          <Grid item xs={12}>
            <FormControl>
              <RadioGroup onChange={handleRiskOption} value={value}>
                {options.map(option => (
                  <FormControlLabel
                    key={option.text}
                    value={option.text}
                    control={<Radio />}
                    label={option.text}
                  />
                ))}
                <FormControlLabel
                  value={UNSELECTED_KEY}
                  control={<Radio />}
                  style={{ display: 'none' }}
                  label="Hidden"
                />
              </RadioGroup>
            </FormControl>
          </Grid>
        </Grid>
      </Paper>
    </React.Fragment>
  );
}
Example #9
Source File: RadioInput.tsx    From glific-frontend with GNU Affero General Public License v3.0 5 votes vote down vote up
RadioInput: React.SFC<RadioInputProps> = ({
  labelYes = 'Yes',
  labelNo = 'No',
  row = true,
  field,
  form: { touched, errors, setFieldValue, values },
  radioTitle,
  handleChange,
}) => {
  const selectedValue = values[field.name];

  const isChecked = (value: any) => selectedValue === value;

  const handleRadioChange = (value: boolean) => {
    setFieldValue(field.name, value);
    if (handleChange) {
      handleChange(value);
    }
  };

  let radioGroupLabel: any;
  if (radioTitle) {
    radioGroupLabel = <FormLabel component="legend">{radioTitle}</FormLabel>;
  }

  return (
    <FormControl component="fieldset">
      {radioGroupLabel}
      <RadioGroup row={row} name="radio-buttons">
        <FormControlLabel
          value={1}
          control={
            <Radio
              color="primary"
              onClick={() => handleRadioChange(true)}
              checked={isChecked(true)}
            />
          }
          label={labelYes}
          className={styles.Label}
        />
        <FormControlLabel
          value={0}
          control={
            <Radio
              color="primary"
              onClick={() => handleRadioChange(false)}
              checked={isChecked(false)}
            />
          }
          label={labelNo}
          className={styles.Label}
        />
      </RadioGroup>
      {errors[field.name] && touched[field.name] ? (
        <FormHelperText className={styles.DangerText}>{errors[field.name]}</FormHelperText>
      ) : null}
    </FormControl>
  );
}
Example #10
Source File: Language.tsx    From back-home-safe with GNU General Public License v3.0 5 votes vote down vote up
StyledRadioGroup = styled(RadioGroup)`
  &.MuiFormGroup-root {
    display: block;
  }
`
Example #11
Source File: VersioningStrategy.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
export function VersioningStrategy() {
  const navigate = useNavigate();
  const { project } = useProjectContext();
  const { getParsedQuery, getQueryParamsWithUpdates } = useQueryHandler();

  useEffect(() => {
    const { parsedQuery } = getParsedQuery();

    if (!parsedQuery.versioningStrategy && !project.isProvidedViaProps) {
      const { queryParams } = getQueryParamsWithUpdates({
        updates: [
          { key: 'versioningStrategy', value: project.versioningStrategy },
        ],
      });

      navigate(`?${queryParams}`, { replace: true });
    }
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  return (
    <FormControl
      component="fieldset"
      required
      disabled={project.isProvidedViaProps}
    >
      <FormLabel component="legend">Versioning strategy</FormLabel>

      <RadioGroup
        data-testid={TEST_IDS.form.versioningStrategy.radioGroup}
        aria-label="calendar-strategy"
        name="calendar-strategy"
        value={project.versioningStrategy}
        onChange={event => {
          const { queryParams } = getQueryParamsWithUpdates({
            updates: [{ key: 'versioningStrategy', value: event.target.value }],
          });

          navigate(`?${queryParams}`, { replace: true });
        }}
      >
        <FormControlLabel
          value={VERSIONING_STRATEGIES.semver}
          control={<Radio />}
          label="Semantic versioning"
        />

        <FormControlLabel
          value={VERSIONING_STRATEGIES.calver}
          control={<Radio />}
          label="Calendar versioning"
        />
      </RadioGroup>
    </FormControl>
  );
}
Example #12
Source File: AlertSnoozeForm.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
AlertSnoozeForm = forwardRef<
  HTMLFormElement,
  AlertSnoozeFormProps
>(({ onSubmit, disableSubmit }, ref) => {
  const classes = useStyles();
  const [duration, setDuration] = useState<Maybe<Duration>>(Duration.P7D);

  useEffect(() => disableSubmit(false), [disableSubmit]);

  const onFormSubmit: FormEventHandler = e => {
    e.preventDefault();
    if (duration) {
      const repeatInterval = 1;
      const today = DateTime.now().toFormat(DEFAULT_DATE_FORMAT);
      onSubmit({
        intervals: intervalsOf(duration, today, repeatInterval),
      });
    }
  };

  const onSnoozeDurationChange = (
    _: ChangeEvent<HTMLInputElement>,
    value: string,
  ) => {
    setDuration(value as Duration);
  };

  return (
    <form ref={ref} onSubmit={onFormSubmit}>
      <FormControl component="fieldset" fullWidth>
        <Typography color="textPrimary">
          <b>For how long?</b>
        </Typography>
        <Box mb={1}>
          <RadioGroup
            name="snooze-alert-options"
            value={duration}
            onChange={onSnoozeDurationChange}
          >
            {AlertSnoozeOptions.map(option => (
              <FormControlLabel
                key={`snooze-alert-option-${option.duration}`}
                label={option.label}
                value={option.duration}
                control={<Radio className={classes.radio} />}
              />
            ))}
          </RadioGroup>
        </Box>
      </FormControl>
    </form>
  );
})
Example #13
Source File: ShippingMethods.tsx    From storefront with MIT License 5 votes vote down vote up
ShippingMethods: React.VFC<Props> = ({
  availableShippingMethods,
  chosenShippingMethods,
  onSubmit,
}) => {
  const [updateShippingMethod, { loading }] = useUpdateShippingMethodMutation({
    refetchQueries: ['Cart'],
  });

  const handleChange = (ev: React.ChangeEvent<HTMLInputElement>) => {
    updateShippingMethod({ variables: { shippingMethods: [ev.target.value] } });
  };

  return (
    <>
      <RadioGroup
        name="shippingMethod"
        value={chosenShippingMethods?.[0] ?? undefined}
        onChange={handleChange}
      >
        <Grid container spacing={2}>
          {availableShippingMethods?.[0]?.rates?.map(
            (rate) =>
              rate != null && (
                <Grid key={rate.id} item xs={12} lg={6}>
                  <Box
                    component="label"
                    htmlFor={`shippingMethod-${rate.id}`}
                    sx={{
                      alignItems: 'center',
                      backgroundColor: 'background.paper',
                      cursor: 'pointer',
                      display: 'flex',
                      flexDirection: 'row',
                      p: 2,
                    }}
                  >
                    <div>
                      <Radio value={rate.id} id={`shippingMethod-${rate.id}`} disabled={loading} />
                    </div>
                    <Box sx={{ flexGrow: 1, ml: 2 }}>
                      <Typography>{rate.label}</Typography>
                      <Price>{rate.cost}</Price>
                    </Box>
                  </Box>
                </Grid>
              ),
          )}
        </Grid>
      </RadioGroup>
      <Box sx={{ mt: 2 }}>
        <Button
          type="submit"
          color="primary"
          disabled={chosenShippingMethods == null}
          loading={loading}
          onClick={onSubmit}
        >
          Continue to Payment Method
        </Button>
      </Box>
    </>
  );
}
Example #14
Source File: Organization.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
Organization: React.FC = () => {
  const {
    apiGet,
    apiPut,
    apiPost,
    user,
    setFeedbackMessage
  } = useAuthContext();
  const { organizationId } = useParams<{ organizationId: string }>();
  const [organization, setOrganization] = useState<OrganizationType>();
  const [tags, setTags] = useState<AutocompleteType[]>([]);
  const [userRoles, setUserRoles] = useState<Role[]>([]);
  const [scanTasks, setScanTasks] = useState<ScanTask[]>([]);
  const [scans, setScans] = useState<Scan[]>([]);
  const [scanSchema, setScanSchema] = useState<ScanSchema>({});
  const [newUserValues, setNewUserValues] = useState<{
    firstName: string;
    lastName: string;
    email: string;
    organization?: OrganizationType;
    role: string;
  }>({
    firstName: '',
    lastName: '',
    email: '',
    role: ''
  });
  const classes = useStyles();
  const [tagValue, setTagValue] = React.useState<AutocompleteType | null>(null);
  const [inputValue, setInputValue] = React.useState('');
  const [dialog, setDialog] = React.useState<{
    open: boolean;
    type?: 'rootDomains' | 'ipBlocks' | 'tags';
    label?: string;
  }>({ open: false });

  const dateAccessor = (date?: string) => {
    return !date || new Date(date).getTime() === new Date(0).getTime()
      ? 'None'
      : `${formatDistanceToNow(parseISO(date))} ago`;
  };

  const userRoleColumns: Column<Role>[] = [
    {
      Header: 'Name',
      accessor: ({ user }) => user.fullName,
      width: 200,
      disableFilters: true,
      id: 'name'
    },
    {
      Header: 'Email',
      accessor: ({ user }) => user.email,
      width: 150,
      minWidth: 150,
      id: 'email',
      disableFilters: true
    },
    {
      Header: 'Role',
      accessor: ({ approved, role, user }) => {
        if (approved) {
          if (user.invitePending) {
            return 'Invite pending';
          } else if (role === 'admin') {
            return 'Administrator';
          } else {
            return 'Member';
          }
        }
        return 'Pending approval';
      },
      width: 50,
      minWidth: 50,
      id: 'approved',
      disableFilters: true
    },
    {
      Header: () => {
        return (
          <div style={{ justifyContent: 'flex-center' }}>
            <Button color="secondary" onClick={() => setDialog({ open: true })}>
              <ControlPoint style={{ marginRight: '10px' }}></ControlPoint>
              Add member
            </Button>
          </div>
        );
      },
      id: 'action',
      Cell: ({ row }: { row: { index: number } }) => {
        const isApproved =
          !organization?.userRoles[row.index] ||
          organization?.userRoles[row.index].approved;
        return (
          <>
            {isApproved ? (
              <Button
                onClick={() => {
                  removeUser(row.index);
                }}
                color="secondary"
              >
                <p>Remove</p>
              </Button>
            ) : (
              <Button
                onClick={() => {
                  approveUser(row.index);
                }}
                color="secondary"
              >
                <p>Approve</p>
              </Button>
            )}
          </>
        );
      },
      disableFilters: true
    }
  ];

  const scanColumns: Column<Scan>[] = [
    {
      Header: 'Name',
      accessor: 'name',
      width: 150,
      id: 'name',
      disableFilters: true
    },
    {
      Header: 'Description',
      accessor: ({ name }) => scanSchema[name] && scanSchema[name].description,
      width: 200,
      minWidth: 200,
      id: 'description',
      disableFilters: true
    },
    {
      Header: 'Mode',
      accessor: ({ name }) =>
        scanSchema[name] && scanSchema[name].isPassive ? 'Passive' : 'Active',
      width: 150,
      minWidth: 150,
      id: 'mode',
      disableFilters: true
    },
    {
      Header: 'Action',
      id: 'action',
      maxWidth: 100,
      Cell: ({ row }: { row: { index: number } }) => {
        if (!organization) return;
        const enabled = organization.granularScans.find(
          (scan) => scan.id === scans[row.index].id
        );
        return (
          <Button
            type="button"
            onClick={() => {
              updateScan(scans[row.index], !enabled);
            }}
          >
            {enabled ? 'Disable' : 'Enable'}
          </Button>
        );
      },
      disableFilters: true
    }
  ];

  const scanTaskColumns: Column<ScanTask>[] = [
    {
      Header: 'ID',
      accessor: 'id',
      disableFilters: true
    },
    {
      Header: 'Status',
      accessor: 'status',
      disableFilters: true
    },
    {
      Header: 'Type',
      accessor: 'type',
      disableFilters: true
    },
    {
      Header: 'Name',
      accessor: ({ scan }) => scan?.name,
      disableFilters: true
    },
    {
      Header: 'Created At',
      accessor: ({ createdAt }) => dateAccessor(createdAt),
      disableFilters: true,
      disableSortBy: true
    },
    {
      Header: 'Requested At',
      accessor: ({ requestedAt }) => dateAccessor(requestedAt),
      disableFilters: true,
      disableSortBy: true
    },
    {
      Header: 'Started At',
      accessor: ({ startedAt }) => dateAccessor(startedAt),
      disableFilters: true,
      disableSortBy: true
    },
    {
      Header: 'Finished At',
      accessor: ({ finishedAt }) => dateAccessor(finishedAt),
      disableFilters: true,
      disableSortBy: true
    },
    {
      Header: 'Output',
      accessor: 'output',
      disableFilters: true
    }
  ];

  const fetchOrganization = useCallback(async () => {
    try {
      const organization = await apiGet<OrganizationType>(
        `/organizations/${organizationId}`
      );
      organization.scanTasks.sort(
        (a, b) =>
          new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
      );
      setOrganization(organization);
      setUserRoles(organization.userRoles);
      setScanTasks(organization.scanTasks);
      const tags = await apiGet<OrganizationTag[]>(`/organizations/tags`);
      setTags(tags);
    } catch (e) {
      console.error(e);
    }
  }, [apiGet, setOrganization, organizationId]);

  const fetchScans = useCallback(async () => {
    try {
      const response = await apiGet<{
        scans: Scan[];
        schema: ScanSchema;
      }>('/granularScans/');
      let { scans } = response;
      const { schema } = response;

      if (user?.userType !== 'globalAdmin')
        scans = scans.filter(
          (scan) =>
            scan.name !== 'censysIpv4' && scan.name !== 'censysCertificates'
        );

      setScans(scans);
      setScanSchema(schema);
    } catch (e) {
      console.error(e);
    }
  }, [apiGet, user]);

  const approveUser = async (user: number) => {
    try {
      await apiPost(
        `/organizations/${organization?.id}/roles/${organization?.userRoles[user].id}/approve`,
        { body: {} }
      );
      const copy = userRoles.map((role, id) =>
        id === user ? { ...role, approved: true } : role
      );
      setUserRoles(copy);
    } catch (e) {
      console.error(e);
    }
  };

  const removeUser = async (user: number) => {
    try {
      await apiPost(
        `/organizations/${organization?.id}/roles/${userRoles[user].id}/remove`,
        { body: {} }
      );
      const copy = userRoles.filter((_, ind) => ind !== user);
      setUserRoles(copy);
    } catch (e) {
      console.error(e);
    }
  };

  const updateOrganization = async (body: any) => {
    try {
      const org = await apiPut('/organizations/' + organization?.id, {
        body: organization
      });
      setOrganization(org);
      setFeedbackMessage({
        message: 'Organization successfully updated',
        type: 'success'
      });
    } catch (e) {
      setFeedbackMessage({
        message:
          e.status === 422
            ? 'Error updating organization'
            : e.message ?? e.toString(),
        type: 'error'
      });
      console.error(e);
    }
  };

  const updateScan = async (scan: Scan, enabled: boolean) => {
    try {
      if (!organization) return;
      await apiPost(
        `/organizations/${organization?.id}/granularScans/${scan.id}/update`,
        {
          body: {
            enabled
          }
        }
      );
      setOrganization({
        ...organization,
        granularScans: enabled
          ? organization.granularScans.concat([scan])
          : organization.granularScans.filter(
              (granularScan) => granularScan.id !== scan.id
            )
      });
    } catch (e) {
      setFeedbackMessage({
        message:
          e.status === 422 ? 'Error updating scan' : e.message ?? e.toString(),
        type: 'error'
      });
      console.error(e);
    }
  };

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

  const onInviteUserSubmit = async () => {
    try {
      const body = {
        firstName: newUserValues.firstName,
        lastName: newUserValues.lastName,
        email: newUserValues.email,
        organization: organization?.id,
        organizationAdmin: newUserValues.role === 'admin'
      };
      const user: User = await apiPost('/users/', {
        body
      });
      const newRole = user.roles[user.roles.length - 1];
      newRole.user = user;
      if (userRoles.find((role) => role.user.id === user.id)) {
        setUserRoles(
          userRoles.map((role) => (role.user.id === user.id ? newRole : role))
        );
      } else {
        setUserRoles(userRoles.concat([newRole]));
      }
    } catch (e) {
      setFeedbackMessage({
        message:
          e.status === 422 ? 'Error inviting user' : e.message ?? e.toString(),
        type: 'error'
      });
      console.log(e);
    }
  };

  const onInviteUserTextChange: React.ChangeEventHandler<
    HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
  > = (e) => onInviteUserChange(e.target.name, e.target.value);

  const onInviteUserChange = (name: string, value: any) => {
    setNewUserValues((values) => ({
      ...values,
      [name]: value
    }));
  };
  const filter = createFilterOptions<AutocompleteType>();

  const ListInput = (props: {
    type: 'rootDomains' | 'ipBlocks' | 'tags';
    label: string;
  }) => {
    if (!organization) return null;
    const elements: (string | OrganizationTag)[] = organization[props.type];
    return (
      <div className={classes.headerRow}>
        <label>{props.label}</label>
        <span>
          {elements &&
            elements.map((value: string | OrganizationTag, index: number) => (
              <Chip
                className={classes.chip}
                key={index}
                label={typeof value === 'string' ? value : value.name}
                onDelete={() => {
                  organization[props.type].splice(index, 1);
                  setOrganization({ ...organization });
                }}
              ></Chip>
            ))}
          <Chip
            label="ADD"
            variant="outlined"
            color="secondary"
            onClick={() => {
              setDialog({
                open: true,
                type: props.type,
                label: props.label
              });
            }}
          />
        </span>
      </div>
    );
  };

  if (!organization) return null;

  const views = [
    <Paper className={classes.settingsWrapper} key={0}>
      <Dialog
        open={dialog.open}
        onClose={() => setDialog({ open: false })}
        aria-labelledby="form-dialog-title"
        maxWidth="xs"
        fullWidth
      >
        <DialogTitle id="form-dialog-title">
          Add {dialog.label && dialog.label.slice(0, -1)}
        </DialogTitle>
        <DialogContent>
          {dialog.type === 'tags' ? (
            <>
              <DialogContentText>
                Select an existing tag or add a new one.
              </DialogContentText>
              <Autocomplete
                value={tagValue}
                onChange={(event, newValue) => {
                  if (typeof newValue === 'string') {
                    setTagValue({
                      name: newValue
                    });
                  } else {
                    setTagValue(newValue);
                  }
                }}
                filterOptions={(options, params) => {
                  const filtered = filter(options, params);
                  // Suggest the creation of a new value
                  if (
                    params.inputValue !== '' &&
                    !filtered.find(
                      (tag) =>
                        tag.name?.toLowerCase() ===
                        params.inputValue.toLowerCase()
                    )
                  ) {
                    filtered.push({
                      name: params.inputValue,
                      title: `Add "${params.inputValue}"`
                    });
                  }
                  return filtered;
                }}
                selectOnFocus
                clearOnBlur
                handleHomeEndKeys
                options={tags}
                getOptionLabel={(option) => {
                  return option.name ?? '';
                }}
                renderOption={(option) => {
                  if (option.title) return option.title;
                  return option.name ?? '';
                }}
                fullWidth
                freeSolo
                renderInput={(params) => (
                  <TextField {...params} variant="outlined" />
                )}
              />
            </>
          ) : (
            <TextField
              autoFocus
              margin="dense"
              id="name"
              label={dialog.label && dialog.label.slice(0, -1)}
              type="text"
              fullWidth
              onChange={(e) => setInputValue(e.target.value)}
            />
          )}
        </DialogContent>
        <DialogActions>
          <Button variant="outlined" onClick={() => setDialog({ open: false })}>
            Cancel
          </Button>
          <Button
            variant="contained"
            color="primary"
            onClick={() => {
              if (dialog.type && dialog.type !== 'tags') {
                if (inputValue) {
                  organization[dialog.type].push(inputValue);
                  setOrganization({ ...organization });
                }
              } else {
                if (tagValue) {
                  if (!organization.tags) organization.tags = [];
                  organization.tags.push(tagValue as any);
                  setOrganization({ ...organization });
                }
              }
              setDialog({ open: false });
              setInputValue('');
              setTagValue(null);
            }}
          >
            Add
          </Button>
        </DialogActions>
      </Dialog>
      <TextField
        value={organization.name}
        disabled
        variant="filled"
        InputProps={{
          className: classes.orgName
        }}
      ></TextField>
      <ListInput label="Root Domains" type="rootDomains"></ListInput>
      <ListInput label="IP Blocks" type="ipBlocks"></ListInput>
      <ListInput label="Tags" type="tags"></ListInput>
      <div className={classes.headerRow}>
        <label>Passive Mode</label>
        <span>
          <SwitchInput
            checked={organization.isPassive}
            onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
              setOrganization({
                ...organization,
                isPassive: event.target.checked
              });
            }}
            color="primary"
          />
        </span>
      </div>
      <div className={classes.buttons}>
        <Link to={`/organizations`}>
          <Button
            variant="outlined"
            style={{ marginRight: '10px', color: '#565C65' }}
          >
            Cancel
          </Button>
        </Link>
        <Button
          variant="contained"
          onClick={updateOrganization}
          style={{ background: '#565C65', color: 'white' }}
        >
          Save
        </Button>
      </div>
    </Paper>,
    <React.Fragment key={1}>
      <Table<Role> columns={userRoleColumns} data={userRoles} />
      <Dialog
        open={dialog.open}
        onClose={() => setDialog({ open: false })}
        aria-labelledby="form-dialog-title"
        maxWidth="xs"
        fullWidth
      >
        <DialogTitle id="form-dialog-title">Add Member</DialogTitle>
        <DialogContent>
          <p style={{ color: '#3D4551' }}>
            Organization members can view Organization-specific vulnerabilities,
            domains, and notes. Organization administrators can additionally
            manage members and update the organization.
          </p>
          <TextField
            margin="dense"
            id="firstName"
            name="firstName"
            label="First Name"
            type="text"
            fullWidth
            value={newUserValues.firstName}
            onChange={onInviteUserTextChange}
            variant="filled"
            InputProps={{
              className: classes.textField
            }}
          />
          <TextField
            margin="dense"
            id="lastName"
            name="lastName"
            label="Last Name"
            type="text"
            fullWidth
            value={newUserValues.lastName}
            onChange={onInviteUserTextChange}
            variant="filled"
            InputProps={{
              className: classes.textField
            }}
          />
          <TextField
            margin="dense"
            id="email"
            name="email"
            label="Email"
            type="text"
            fullWidth
            value={newUserValues.email}
            onChange={onInviteUserTextChange}
            variant="filled"
            InputProps={{
              className: classes.textField
            }}
          />
          <br></br>
          <br></br>
          <FormLabel component="legend">Role</FormLabel>
          <RadioGroup
            aria-label="role"
            name="role"
            value={newUserValues.role}
            onChange={onInviteUserTextChange}
          >
            <FormControlLabel
              value="standard"
              control={<Radio color="primary" />}
              label="Standard"
            />
            <FormControlLabel
              value="admin"
              control={<Radio color="primary" />}
              label="Administrator"
            />
          </RadioGroup>
        </DialogContent>
        <DialogActions>
          <Button variant="outlined" onClick={() => setDialog({ open: false })}>
            Cancel
          </Button>
          <Button
            variant="contained"
            color="primary"
            onClick={async () => {
              onInviteUserSubmit();
              setDialog({ open: false });
            }}
          >
            Add
          </Button>
        </DialogActions>
      </Dialog>
    </React.Fragment>,
    <React.Fragment key={2}>
      <OrganizationList parent={organization}></OrganizationList>
    </React.Fragment>,
    <React.Fragment key={3}>
      <Table<Scan> columns={scanColumns} data={scans} fetchData={fetchScans} />
      <h2>Organization Scan History</h2>
      <Table<ScanTask> columns={scanTaskColumns} data={scanTasks} />
    </React.Fragment>
  ];

  let navItems = [
    {
      title: 'Settings',
      path: `/organizations/${organizationId}`,
      exact: true
    },
    {
      title: 'Members',
      path: `/organizations/${organizationId}/members`
    }
  ];

  if (!organization.parent) {
    navItems = navItems.concat([
      // { title: 'Teams', path: `/organizations/${organizationId}/teams` },
      { title: 'Scans', path: `/organizations/${organizationId}/scans` }
    ]);
  }

  return (
    <div>
      <div className={classes.header}>
        <h1 className={classes.headerLabel}>
          <Link to="/organizations">Organizations</Link>
          {organization.parent && (
            <>
              <ChevronRight></ChevronRight>
              <Link to={'/organizations/' + organization.parent.id}>
                {organization.parent.name}
              </Link>
            </>
          )}
          <ChevronRight
            style={{
              verticalAlign: 'middle',
              lineHeight: '100%',
              fontSize: '26px'
            }}
          ></ChevronRight>
          <span style={{ color: '#07648D' }}>{organization.name}</span>
        </h1>
        <Subnav
          items={navItems}
          styles={{
            background: '#F9F9F9'
          }}
        ></Subnav>
      </div>
      <div className={classes.root}>
        <Switch>
          <Route
            path="/organizations/:organizationId"
            exact
            render={() => views[0]}
          />
          <Route
            path="/organizations/:organizationId/members"
            render={() => views[1]}
          />
          <Route
            path="/organizations/:organizationId/teams"
            render={() => views[2]}
          />
          <Route
            path="/organizations/:organizationId/scans"
            render={() => views[3]}
          />
        </Switch>
      </div>
    </div>
  );
}
Example #15
Source File: Users.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
Users: React.FC = () => {
  const { apiGet, apiPost, apiDelete } = useAuthContext();
  const [showModal, setShowModal] = useState<Boolean>(false);
  const [selectedRow, setSelectedRow] = useState<number>(0);
  const [users, setUsers] = useState<User[]>([]);

  const columns: Column<User>[] = [
    {
      Header: 'Name',
      accessor: 'fullName',
      width: 200,
      disableFilters: true,
      id: 'name'
    },
    {
      Header: 'Email',
      accessor: 'email',
      width: 150,
      minWidth: 150,
      id: 'email',
      disableFilters: true
    },
    {
      Header: 'Organizations',
      accessor: ({ roles }) =>
        roles &&
        roles
          .filter((role) => role.approved)
          .map((role) => role.organization.name)
          .join(', '),
      id: 'organizations',
      width: 200,
      disableFilters: true
    },
    {
      Header: 'User type',
      accessor: ({ userType }) =>
        userType === 'standard'
          ? 'Standard'
          : userType === 'globalView'
          ? 'Global View'
          : 'Global Admin',
      width: 50,
      minWidth: 50,
      id: 'userType',
      disableFilters: true
    },
    {
      Header: 'Date ToU Signed',
      accessor: ({ dateAcceptedTerms }) =>
        dateAcceptedTerms
          ? `${formatDistanceToNow(parseISO(dateAcceptedTerms))} ago`
          : 'None',
      width: 50,
      minWidth: 50,
      id: 'dateAcceptedTerms',
      disableFilters: true
    },
    {
      Header: 'ToU Version',
      accessor: 'acceptedTermsVersion',
      width: 50,
      minWidth: 50,
      id: 'acceptedTermsVersion',
      disableFilters: true
    },
    {
      Header: 'Last Logged In',
      accessor: ({ lastLoggedIn }) =>
        lastLoggedIn
          ? `${formatDistanceToNow(parseISO(lastLoggedIn))} ago`
          : 'None',
      width: 50,
      minWidth: 50,
      id: 'lastLoggedIn',
      disableFilters: true
    },
    {
      Header: 'Delete',
      id: 'delete',
      Cell: ({ row }: { row: { index: number } }) => (
        <span
          onClick={() => {
            setShowModal(true);
            setSelectedRow(row.index);
          }}
        >
          <FaTimes />
        </span>
      ),
      disableFilters: true
    }
  ];
  const [errors, setErrors] = useState<Errors>({});

  const [values, setValues] = useState<{
    firstName: string;
    lastName: string;
    email: string;
    organization?: Organization;
    userType: string;
  }>({
    firstName: '',
    lastName: '',
    email: '',
    userType: ''
  });

  const fetchUsers = useCallback(async () => {
    try {
      const rows = await apiGet<User[]>('/users/');
      setUsers(rows);
    } catch (e) {
      console.error(e);
    }
  }, [apiGet]);

  const deleteRow = async (index: number) => {
    try {
      const row = users[index];
      await apiDelete(`/users/${row.id}`, { body: {} });
      setUsers(users.filter((user) => user.id !== row.id));
    } catch (e) {
      setErrors({
        global:
          e.status === 422 ? 'Unable to delete user' : e.message ?? e.toString()
      });
      console.log(e);
    }
  };

  const onSubmit: React.FormEventHandler = async (e) => {
    e.preventDefault();
    try {
      const body = {
        firstName: values.firstName,
        lastName: values.lastName,
        email: values.email,
        userType: values.userType
      };
      const user = await apiPost('/users/', {
        body
      });
      setUsers(users.concat(user));
    } catch (e) {
      setErrors({
        global:
          e.status === 422
            ? 'Error when submitting user entry.'
            : e.message ?? e.toString()
      });
      console.log(e);
    }
  };

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

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

  React.useEffect(() => {
    document.addEventListener('keyup', (e) => {
      //Escape
      if (e.keyCode === 27) {
        setShowModal(false);
      }
    });
  }, [apiGet]);

  return (
    <div className={classes.root}>
      <h1>Users</h1>
      <Table<User> columns={columns} data={users} fetchData={fetchUsers} />
      <h2>Invite a user</h2>
      <form onSubmit={onSubmit} className={classes.form}>
        {errors.global && <p className={classes.error}>{errors.global}</p>}
        <Label htmlFor="firstName">First Name</Label>
        <TextInput
          required
          id="firstName"
          name="firstName"
          className={classes.textField}
          type="text"
          value={values.firstName}
          onChange={onTextChange}
        />
        <Label htmlFor="lastName">Last Name</Label>
        <TextInput
          required
          id="lastName"
          name="lastName"
          className={classes.textField}
          type="text"
          value={values.lastName}
          onChange={onTextChange}
        />
        <Label htmlFor="email">Email</Label>
        <TextInput
          required
          id="email"
          name="email"
          className={classes.textField}
          type="text"
          value={values.email}
          onChange={onTextChange}
        />
        <Label htmlFor="userType">User Type</Label>
        <RadioGroup
          aria-label="User Type"
          name="userType"
          value={values.userType}
          onChange={onTextChange}
        >
          <FormControlLabel
            value="standard"
            control={<Radio color="primary" />}
            label="Standard"
          />
          <FormControlLabel
            value="globalView"
            control={<Radio color="primary" />}
            label="Global View"
          />
          <FormControlLabel
            value="globalAdmin"
            control={<Radio color="primary" />}
            label="Global Administrator"
          />
        </RadioGroup>
        <br></br>
        <Button type="submit">Invite User</Button>
      </form>
      <ImportExport<
        | User
        | {
            roles: string;
          }
      >
        name="users"
        fieldsToExport={['firstName', 'lastName', 'email', 'roles', 'userType']}
        onImport={async (results) => {
          // TODO: use a batch call here instead.
          const createdUsers = [];
          for (const result of results) {
            const parsedRoles: {
              organization: string;
              role: string;
            }[] = JSON.parse(result.roles as string);
            const body: any = result;
            // For now, just create role with the first organization
            if (parsedRoles.length > 0) {
              body.organization = parsedRoles[0].organization;
              body.organizationAdmin = parsedRoles[0].role === 'admin';
            }
            try {
              createdUsers.push(
                await apiPost('/users/', {
                  body
                })
              );
            } catch (e) {
              // Just continue when an error occurs
              console.error(e);
            }
          }
          setUsers(users.concat(...createdUsers));
        }}
        getDataToExport={() =>
          users.map((user) => ({
            ...user,
            roles: JSON.stringify(
              user.roles.map((role) => ({
                organization: role.organization.id,
                role: role.role
              }))
            )
          }))
        }
      />

      {showModal && (
        <div>
          <Overlay />
          <ModalContainer>
            <Modal
              actions={
                <>
                  <Button
                    outline
                    type="button"
                    onClick={() => {
                      setShowModal(false);
                    }}
                  >
                    Cancel
                  </Button>
                  <Button
                    type="button"
                    onClick={() => {
                      deleteRow(selectedRow);
                      setShowModal(false);
                    }}
                  >
                    Delete
                  </Button>
                </>
              }
              title={<h2>Delete user?</h2>}
            >
              <p>
                Are you sure you would like to delete{' '}
                <code>{users[selectedRow].fullName}</code>?
              </p>
            </Modal>
          </ModalContainer>
        </div>
      )}
    </div>
  );
}
Example #16
Source File: AlertDismissForm.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
AlertDismissForm = forwardRef<
  HTMLFormElement,
  AlertDismissFormProps
>(({ onSubmit, disableSubmit }, ref) => {
  const classes = useStyles();
  const [other, setOther] = useState<Maybe<string>>(null);
  const [feedback, setFeedback] = useState<Maybe<string>>(null);
  const [reason, setReason] = useState<AlertDismissReason>(
    AlertDismissReason.Resolved,
  );

  const onFormSubmit: FormEventHandler = e => {
    e.preventDefault();
    if (reason) {
      onSubmit({
        other: other,
        reason: reason,
        feedback: feedback,
      });
    }
  };

  const onReasonChange = (_: ChangeEvent<HTMLInputElement>, value: string) => {
    if (other) {
      setOther(null);
    }
    setReason(value as AlertDismissReason);
  };

  const onOtherChange = (e: ChangeEvent<HTMLInputElement>) => {
    return e.target.value
      ? setOther(e.target.value as AlertDismissReason)
      : setOther(null);
  };

  const onFeedbackChange = (e: ChangeEvent<HTMLInputElement>) => {
    return e.target.value
      ? setFeedback(e.target.value as AlertDismissReason)
      : setFeedback(null);
  };

  useEffect(() => {
    function validateDismissForm() {
      if (reason === AlertDismissReason.Other) {
        if (other) {
          disableSubmit(false);
        } else {
          disableSubmit(true);
        }
      } else if (reason) {
        disableSubmit(false);
      } else {
        disableSubmit(true);
      }
    }

    validateDismissForm();
  }, [reason, other, disableSubmit]);

  return (
    <form ref={ref} onSubmit={onFormSubmit}>
      <FormControl component="fieldset" fullWidth>
        <Typography color="textPrimary">
          <b>Reason for dismissing?</b>
        </Typography>
        <Box mb={1}>
          <RadioGroup
            name="dismiss-alert-reasons"
            value={reason}
            onChange={onReasonChange}
          >
            {AlertDismissOptions.map(option => (
              <FormControlLabel
                key={`dismiss-alert-option-${option.reason}`}
                label={option.label}
                value={option.reason}
                control={<Radio className={classes.radio} />}
              />
            ))}
          </RadioGroup>
          <Collapse in={reason === AlertDismissReason.Other}>
            <Box ml={4}>
              <TextField
                id="dismiss-alert-option-other"
                variant="outlined"
                multiline
                fullWidth
                rows={4}
                value={other ?? ''}
                onChange={onOtherChange}
              />
            </Box>
          </Collapse>
        </Box>
        <Typography gutterBottom>
          <b>Any other feedback you can provide?</b>
        </Typography>
        <TextField
          id="dismiss-alert-feedback"
          variant="outlined"
          multiline
          rows={4}
          fullWidth
          value={feedback ?? ''}
          onChange={onFeedbackChange}
        />
      </FormControl>
    </form>
  );
})
Example #17
Source File: Settings.tsx    From back-home-safe with GNU General Public License v3.0 4 votes vote down vote up
Settings = () => {
  const { t } = useTranslation("main_screen");
  const { hasCameraSupport } = useCamera();
  const { autoRemoveRecordDay, setAutoRemoveRecordDay } = useTravelRecord();
  const { incognito, setIncognito, value } = useData();
  const [languageOpen, setLanguageOpen] = useState(false);
  const { language, setLanguage } = useI18n();

  const handleLanguageClick = () => {
    setLanguageOpen(!languageOpen);
  };

  const handleExportData = () => {
    const dataStr =
      "data:text/json;charset=utf-8," +
      encodeURIComponent(JSON.stringify(value));
    const downloadAnchorNode = document.createElement("a");
    downloadAnchorNode.setAttribute("href", dataStr);
    downloadAnchorNode.setAttribute("download", "export.json");
    document.body.appendChild(downloadAnchorNode); // required for firefox
    downloadAnchorNode.click();
    downloadAnchorNode.remove();
  };

  return (
    <PageWrapper>
      <Header name={t("setting.name")} />
      <ContentWrapper>
        <StyledList
          subheader={
            <ListSubheader>{t("setting.section.common")}</ListSubheader>
          }
        >
          {hasCameraSupport ? (
            <StyledLink to="/cameraSetting">
              <ListItem button>
                <ListItemText primary={t("setting.item.camera_setting")} />
              </ListItem>
            </StyledLink>
          ) : (
            <ListItem button disabled>
              <ListItemText primary={t("setting.item.camera_setting")} />
            </ListItem>
          )}
          <StyledLink to="/confirmPageSetting">
            <ListItem button>
              <ListItemText primary={t("setting.item.confirm_page_setting")} />
            </ListItem>
          </StyledLink>
          <ListItem>
            <ListItemText primary={t("setting.item.auto_delete_record")} />
            <ListItemSecondaryAction>
              <Select
                labelId="cameraId"
                id="demo-simple-select"
                value={autoRemoveRecordDay}
                onChange={(e) => {
                  setAutoRemoveRecordDay(e.target.value as number);
                }}
              >
                {range(1, 100).map((day) => (
                  <MenuItem value={day} key={day}>
                    {day}{" "}
                    {day === 1 ? t("setting.form.day") : t("setting.form.days")}
                  </MenuItem>
                ))}
              </Select>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t("setting.item.incognito_mode.name")}
              secondary={t("setting.item.incognito_mode.explanation")}
            />
            <ListItemSecondaryAction>
              <Switch
                checked={incognito}
                onChange={(e) => {
                  setIncognito(e.target.checked);
                }}
                color="primary"
              />
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem button onClick={handleLanguageClick}>
            <ListItemText primary={t("setting.item.language")} />
            {languageOpen ? <ExpandLess /> : <ExpandMore />}
          </ListItem>
          <Collapse in={languageOpen} timeout="auto" unmountOnExit>
            <ListItem>
              <RadioGroup
                aria-label="language"
                name="language"
                value={language}
                onChange={(event) => {
                  setLanguage(event.target.value as languageType);
                }}
              >
                <FormControlLabel
                  value={languageType["ZH-HK"]}
                  control={<Radio />}
                  label="繁體中文"
                />
                <FormControlLabel
                  value={languageType.EN}
                  control={<Radio />}
                  label="English"
                />
              </RadioGroup>
            </ListItem>
          </Collapse>
        </StyledList>
        <Divider />
        <StyledList
          subheader={<ListSubheader>{t("setting.section.lab")}</ListSubheader>}
        >
          <StyledLink to="/qrGenerator">
            <ListItem button>
              <ListItemText primary={t("setting.item.qr_generator")} />
            </ListItem>
          </StyledLink>
          <StyledLink to="/vaccinationQRReader">
            <ListItem button>
              <ListItemText primary={t("setting.item.vaccinationQRReader")} />
            </ListItem>
          </StyledLink>
          <ListItem onClick={handleExportData}>
            <ListItemText primary={t("setting.item.export_data")} />
          </ListItem>
          <ListItem button>
            <ListItemText
              primary={t("setting.item.reset")}
              onClick={clearAllData}
            />
          </ListItem>
        </StyledList>
        <Divider />
        <StyledList
          subheader={
            <ListSubheader>
              {t("setting.section.version")}: {__APP_VERSION__}
            </ListSubheader>
          }
        >
          <StyledExternalLink
            href="https://gitlab.com/codogo-b/back-home-safe"
            target="_blank"
          >
            <ListItem button>
              <ListItemText primary={t("setting.item.about_us")} />
            </ListItem>
          </StyledExternalLink>
          <StyledLink to="/disclaimer">
            <ListItem button>
              <ListItemText primary={t("setting.item.disclaimer")} />
            </ListItem>
          </StyledLink>
          <StyledExternalLink
            href="https://gitlab.com/codogo-b/back-home-safe/-/blob/master/CHANGELOG.md"
            target="_blank"
          >
            <ListItem button>
              <ListItemText primary={t("setting.item.change_log")} />
            </ListItem>
          </StyledExternalLink>
          <StyledExternalLink
            href="https://gitlab.com/codogo-b/back-home-safe/-/issues"
            target="_blank"
          >
            <ListItem button>
              <ListItemText primary={t("setting.item.report_issue")} />
            </ListItem>
          </StyledExternalLink>
        </StyledList>
      </ContentWrapper>
    </PageWrapper>
  );
}
Example #18
Source File: CardSettings.tsx    From fishbowl with MIT License 4 votes vote down vote up
function CardSettings(props: {
  cardPlayStyle: GameCardPlayStyleEnum
  setCardPlayStyle?: (cardPlayStyle: GameCardPlayStyleEnum) => void
  debouncedSetWordList?: (wordList: string) => void
}) {
  const { t } = useTranslation()
  const currentPlayer = React.useContext(CurrentPlayerContext)
  const currentGame = React.useContext(CurrentGameContext)
  const [wordList, setWordList] = React.useState("")
  const canConfigureSettings = currentPlayer.role === PlayerRole.Host

  const wordListLength = parseWordList(wordList).length

  return (
    <>
      <Grid item>
        <FormControl component="fieldset" disabled={!canConfigureSettings}>
          <RadioGroup
            value={props.cardPlayStyle}
            onChange={({ target: { value } }) => {
              props.setCardPlayStyle &&
                props.setCardPlayStyle(value as GameCardPlayStyleEnum)
            }}
          >
            <FormControlLabel
              value={GameCardPlayStyleEnum.PlayersSubmitWords}
              control={<Radio color="primary"></Radio>}
              label={t(
                "settings.cards.cardStyle.playersSubmit",
                "Players submit words (default)"
              )}
            ></FormControlLabel>
            <FormControlLabel
              value={GameCardPlayStyleEnum.HostProvidesWords}
              control={<Radio color="primary"></Radio>}
              label={t(
                "settings.cards.cardStyle.hostProvides",
                "Host provides words"
              )}
            ></FormControlLabel>
          </RadioGroup>
        </FormControl>
      </Grid>
      {props.cardPlayStyle === GameCardPlayStyleEnum.PlayersSubmitWords && (
        <>
          <Grid item />
          <Grid item>
            <SubmissionsPerPlayerInput
              value={String(currentGame.num_entries_per_player || "")}
            />
          </Grid>
          <Grid item>
            <LetterInput value={currentGame.starting_letter || ""} />
          </Grid>
          <Grid item>
            <ScreenCardsCheckbox value={Boolean(currentGame.screen_cards)} />
          </Grid>
        </>
      )}
      {props.cardPlayStyle === GameCardPlayStyleEnum.HostProvidesWords &&
        canConfigureSettings && (
          <Grid item>
            <TextField
              autoFocus
              value={wordList}
              onChange={({ target: { value } }) => {
                setWordList(value)
                props.debouncedSetWordList && props.debouncedSetWordList(value)
              }}
              fullWidth
              label={t("settings.cards.words.label", "Words")}
              multiline
              rows={5}
              variant="outlined"
              placeholder={t(
                "settings.cards.words.placeholder",
                "Comma separated list of words here..."
              )}
            ></TextField>
            <Box
              display="flex"
              flexDirection="row-reverse"
              pt={0.5}
              color={grey[600]}
            >
              {t("settings.cards.words.helper", "{{ count }} word detected", {
                count: wordListLength,
                defaultValue_plural: "{{ count }} words detected",
              })}
            </Box>
          </Grid>
        )}
    </>
  )
}
Example #19
Source File: InteractiveOptions.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
InteractiveOptions: React.SFC<InteractiveOptionsProps> = ({
  isAddButtonChecked,
  templateType,
  inputFields,
  form,
  onAddClick,
  onRemoveClick,
  onTemplateTypeChange,
  onInputChange,
  onGlobalButtonInputChange,
  onListItemAddClick,
  onListItemRemoveClick,
  disabled = false,
  translation,
  disabledType,
}) => {
  const { values, errors, touched, setFieldValue } = form;

  const handleAddClick = (helper: any, type: string) => {
    const obj = type === LIST ? { title: '', options: [] } : { value: '' };
    helper.push(obj);
    onAddClick(true, type);
  };

  const handleRemoveClick = (helper: any, idx: number) => {
    helper.remove(idx);
    onRemoveClick(idx);
  };

  const getButtons = (index: number, arrayHelpers: any) => {
    let template: any = null;
    if (templateType === LIST) {
      template = (
        <ListReplyTemplate
          translation={translation && translation.items[index]}
          key={index}
          index={index}
          inputFields={inputFields}
          form={form}
          onListAddClick={() => handleAddClick(arrayHelpers, LIST)}
          onListRemoveClick={() => handleRemoveClick(arrayHelpers, index)}
          onListItemAddClick={(options: Array<any>) => onListItemAddClick(index, options)}
          onListItemRemoveClick={(itemIndex: number) => onListItemRemoveClick(index, itemIndex)}
          onInputChange={(value: string, payload: any) =>
            onInputChange(LIST, index, value, payload, setFieldValue)
          }
        />
      );
    }

    if (templateType === QUICK_REPLY) {
      template = (
        <QuickReplyTemplate
          translation={translation && translation[index]}
          key={index}
          index={index}
          inputFields={inputFields}
          form={form}
          onInputChange={(value: string, payload: any) =>
            onInputChange(QUICK_REPLY, index, value, payload, setFieldValue)
          }
          onAddClick={() => handleAddClick(arrayHelpers, QUICK_REPLY)}
          onRemoveClick={() => handleRemoveClick(arrayHelpers, index)}
        />
      );
    }
    return template;
  };

  const radioTemplateType = (
    <div>
      <RadioGroup
        aria-label="template-type"
        name="template-type"
        row
        value={templateType}
        onChange={(event) => onTemplateTypeChange(event.target.value)}
      >
        <div className={styles.RadioLabelWrapper}>
          <FormControlLabel
            value={QUICK_REPLY}
            control={
              <Radio
                disabled={disabledType}
                color="primary"
                checkedIcon={<ApprovedIcon className={styles.CheckedIcon} />}
                size="small"
              />
            }
            className={templateType === QUICK_REPLY ? styles.SelectedLabel : ''}
            classes={{ root: styles.RadioLabel }}
            label="Reply buttons"
          />
        </div>
        <div className={styles.RadioLabelWrapper}>
          <FormControlLabel
            value={LIST}
            control={
              <Radio
                disabled={disabledType}
                color="primary"
                checkedIcon={<ApprovedIcon className={styles.CheckedIcon} />}
                size="small"
              />
            }
            className={templateType === LIST ? styles.SelectedLabel : ''}
            classes={{ root: styles.RadioLabel }}
            label="List message"
          />
        </div>
      </RadioGroup>
      {templateType && templateType === LIST && (
        <div className={styles.GlobalButton}>
          {translation && <div className={styles.Translation}>{translation.globalButton}</div>}
          <FormControl
            fullWidth
            error={!!(errors.globalButton && touched.globalButton)}
            className={styles.FormControl}
          >
            <TextField
              placeholder="List header"
              variant="outlined"
              label="List header*"
              className={styles.TextField}
              onChange={(e: any) => {
                setFieldValue('globalButton', e.target.value);
                onGlobalButtonInputChange(e.target.value);
              }}
              value={values.globalButton}
              error={!!errors.globalButton && touched.globalButton}
            />
            {errors.globalButton && touched.globalButton && (
              <FormHelperText>{errors.globalButton}</FormHelperText>
            )}
          </FormControl>
        </div>
      )}

      {templateType && (
        <div className={templateType === QUICK_REPLY ? styles.TemplateFields : ''}>
          <FieldArray
            name="templateButtons"
            render={(arrayHelpers) =>
              values.templateButtons.map((row: any, index: any) => getButtons(index, arrayHelpers))
            }
          />
        </div>
      )}
    </div>
  );

  return <div>{isAddButtonChecked && !disabled && radioTemplateType}</div>;
}
Example #20
Source File: TemplateOptions.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
TemplateOptions: React.SFC<TemplateOptionsProps> = ({
  isAddButtonChecked,
  templateType,
  inputFields,
  form: { touched, errors, values },
  onAddClick,
  onRemoveClick,
  onTemplateTypeChange,
  onInputChange,
  disabled = false,
}) => {
  const buttonTitle = 'Button Title';
  const buttonValue = 'Button Value';
  const buttonTitles: any = {
    CALL_TO_ACTION: 'Call to action',
    QUICK_REPLY: 'Quick Reply',
  };

  const handleAddClick = (helper: any, type: boolean) => {
    const obj = type ? { type: '', value: '', title: '' } : { value: '' };
    helper.push(obj);
    onAddClick();
  };

  const handleRemoveClick = (helper: any, idx: number) => {
    helper.remove(idx);
    onRemoveClick(idx);
  };

  const addButton = (helper: any, type: boolean = false) => {
    const title = templateType ? buttonTitles[templateType] : '';
    const buttonClass =
      templateType === QUICK_REPLY ? styles.QuickReplyAddButton : styles.CallToActionAddButton;
    return (
      <Button
        className={buttonClass}
        variant="outlined"
        color="primary"
        onClick={() => handleAddClick(helper, type)}
      >
        Add {title}
      </Button>
    );
  };

  const getButtons = (row: any, index: number, arrayHelpers: any) => {
    const { type, title, value }: any = row;
    let template: any = null;

    const isError = (key: string) =>
      !!(
        errors.templateButtons &&
        touched.templateButtons &&
        errors.templateButtons[index] &&
        errors.templateButtons[index][key]
      );

    if (templateType === CALL_TO_ACTION) {
      template = (
        <div className={styles.CallToActionContainer} key={index.toString()}>
          <div className={styles.CallToActionWrapper}>
            <div>
              <div className={styles.RadioStyles}>
                <FormControl fullWidth error={isError('type')} className={styles.FormControl}>
                  <RadioGroup
                    aria-label="action-radio-buttons"
                    name="action-radio-buttons"
                    row
                    value={type}
                    onChange={(e: any) => onInputChange(e, row, index, 'type')}
                    className={styles.RadioGroup}
                  >
                    <FormControlLabel
                      value="phone_number"
                      control={
                        <Radio
                          color="primary"
                          disabled={
                            disabled ||
                            (index === 0 &&
                              inputFields.length > 1 &&
                              inputFields[0].type !== 'phone_number') ||
                            (index > 0 &&
                              inputFields[0].type &&
                              inputFields[0].type === 'phone_number')
                          }
                        />
                      }
                      label="Phone number"
                    />
                    <FormControlLabel
                      value="url"
                      control={
                        <Radio
                          color="primary"
                          disabled={
                            disabled ||
                            (index === 0 &&
                              inputFields.length > 1 &&
                              inputFields[0].type !== 'url') ||
                            (index > 0 && inputFields[0].type && inputFields[0].type === 'url')
                          }
                        />
                      }
                      label="URL"
                    />
                  </RadioGroup>
                  {errors.templateButtons &&
                  touched.templateButtons &&
                  touched.templateButtons[index] ? (
                    <FormHelperText>{errors.templateButtons[index]?.type}</FormHelperText>
                  ) : null}
                </FormControl>
              </div>
              <div>
                {inputFields.length > 1 ? (
                  <DeleteIcon onClick={() => handleRemoveClick(arrayHelpers, index)} />
                ) : null}
              </div>
            </div>
            <div className={styles.TextFieldWrapper}>
              <FormControl fullWidth error={isError('title')} className={styles.FormControl}>
                <TextField
                  disabled={disabled}
                  title={title}
                  defaultValue={value}
                  placeholder={buttonTitle}
                  variant="outlined"
                  label={buttonTitle}
                  onBlur={(e: any) => onInputChange(e, row, index, 'title')}
                  className={styles.TextField}
                  error={isError('title')}
                />
                {errors.templateButtons &&
                touched.templateButtons &&
                touched.templateButtons[index] ? (
                  <FormHelperText>{errors.templateButtons[index]?.title}</FormHelperText>
                ) : null}
              </FormControl>
            </div>
            <div className={styles.TextFieldWrapper}>
              <FormControl fullWidth error={isError('value')} className={styles.FormControl}>
                <TextField
                  title={value}
                  defaultValue={value}
                  disabled={disabled}
                  placeholder={buttonValue}
                  variant="outlined"
                  label={buttonValue}
                  onBlur={(e: any) => onInputChange(e, row, index, 'value')}
                  className={styles.TextField}
                  error={isError('value')}
                />
                {errors.templateButtons &&
                touched.templateButtons &&
                touched.templateButtons[index] ? (
                  <FormHelperText>{errors.templateButtons[index]?.value}</FormHelperText>
                ) : null}
              </FormControl>
            </div>
          </div>
          <div>
            {inputFields.length === index + 1 && inputFields.length !== 2
              ? addButton(arrayHelpers, true)
              : null}
          </div>
        </div>
      );
    }

    if (templateType === QUICK_REPLY) {
      template = (
        <div className={styles.QuickReplyContainer} key={index.toString()}>
          <div className={styles.QuickReplyWrapper}>
            <FormControl fullWidth error={isError('value')} className={styles.FormControl}>
              <TextField
                disabled={disabled}
                defaultValue={value}
                title={title}
                placeholder={`Quick reply ${index + 1} title`}
                label={`Quick reply ${index + 1} title`}
                variant="outlined"
                onBlur={(e: any) => onInputChange(e, row, index, 'value')}
                className={styles.TextField}
                error={isError('value')}
                InputProps={{
                  endAdornment: inputFields.length > 1 && !disabled && (
                    <CrossIcon
                      className={styles.RemoveIcon}
                      title="Remove"
                      onClick={() => handleRemoveClick(arrayHelpers, index)}
                    />
                  ),
                }}
              />
              {errors.templateButtons &&
              touched.templateButtons &&
              touched.templateButtons[index] ? (
                <FormHelperText>{errors.templateButtons[index]?.value}</FormHelperText>
              ) : null}
            </FormControl>
          </div>
          <div>
            {inputFields.length === index + 1 && inputFields.length !== 3
              ? addButton(arrayHelpers)
              : null}
          </div>
        </div>
      );
    }
    return template;
  };

  const radioTemplateType = (
    <div>
      <RadioGroup
        aria-label="template-type"
        name="template-type"
        row
        value={templateType}
        onChange={(event) => onTemplateTypeChange(event.target.value)}
      >
        <div className={styles.RadioLabelWrapper}>
          <FormControlLabel
            value={CALL_TO_ACTION}
            control={<Radio color="primary" disabled={disabled} />}
            label="Call to actions"
            classes={{ root: styles.RadioLabel }}
          />
          <Tooltip title={GUPSHUP_CALL_TO_ACTION} placement="right" tooltipClass={styles.Tooltip}>
            <InfoIcon />
          </Tooltip>
        </div>
        <div className={styles.RadioLabelWrapper}>
          <FormControlLabel
            value={QUICK_REPLY}
            control={<Radio color="primary" disabled={disabled} />}
            label="Quick replies"
            className={styles.RadioLabel}
          />
          <Tooltip title={GUPSHUP_QUICK_REPLY} placement="right" tooltipClass={styles.Tooltip}>
            <InfoIcon />
          </Tooltip>
        </div>
      </RadioGroup>

      {templateType ? (
        <div
          className={
            templateType === QUICK_REPLY
              ? styles.QuickTemplateFields
              : styles.CallToActionTemplateFields
          }
        >
          <FieldArray
            name="templateButtons"
            render={(arrayHelpers) =>
              values.templateButtons.map((row: any, index: any) =>
                getButtons(row, index, arrayHelpers)
              )
            }
          />
        </div>
      ) : null}
    </div>
  );

  return <div>{isAddButtonChecked && radioTemplateType}</div>;
}
Example #21
Source File: CreateGame.tsx    From planning-poker with MIT License 4 votes vote down vote up
CreateGame = () => {
  const history = useHistory();
  const [gameName, setGameName] = useState('Avengers');
  const [createdBy, setCreatedBy] = useState('SuperHero');
  const [gameType, setGameType] = useState(GameType.Fibonacci);

  const handleSubmit = async (event: FormEvent) => {
    event.preventDefault();
    const game: NewGame = {
      name: gameName,
      createdBy: createdBy,
      gameType: gameType,
      createdAt: new Date(),
    };
    const newGameId = await addNewGame(game);
    history.push(`/game/${newGameId}`);
  };

  return (
    <Grow in={true} timeout={1000}>
      <form onSubmit={handleSubmit}>
        <Card variant='outlined' className='CreateGameCard'>
          <CardHeader
            className='CreateGameCardHeader'
            title='Create New Session'
            titleTypographyProps={{ variant: 'h4' }}
          />
          <CardContent className='CreateGameCardContent'>
            <TextField
              className='CreateGameTextField'
              required
              id='filled-required'
              label='Session Name'
              placeholder='Enter a session name'
              defaultValue={gameName}
              variant='outlined'
              onChange={(event: ChangeEvent<HTMLInputElement>) => setGameName(event.target.value)}
            />
            <TextField
              className='CreateGameTextField'
              required
              id='filled-required'
              label='Your Name'
              placeholder='Enter your name'
              defaultValue={createdBy}
              variant='outlined'
              onChange={(event: ChangeEvent<HTMLInputElement>) => setCreatedBy(event.target.value)}
            />
            <RadioGroup
              aria-label='gender'
              name='gender1'
              value={gameType}
              onChange={(
                event: ChangeEvent<{
                  name?: string | undefined;
                  value: any;
                }>
              ) => setGameType(event.target.value)}
            >
              <FormControlLabel
                value={GameType.Fibonacci}
                control={<Radio color='primary' size='small' />}
                label='Fibonacci (0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89)'
              />
              <FormControlLabel
                value={GameType.ShortFibonacci}
                control={<Radio color='primary' size='small' />}
                label='Short Fibonacci (0, ½, 1, 2, 3, 5, 8, 13, 20, 40, 100)'
              />
              <FormControlLabel
                value={GameType.TShirt}
                control={<Radio color='primary' size='small' />}
                label='T-Shirt (XXS, XS, S, M, L, XL, XXL)'
              />
            </RadioGroup>
          </CardContent>
          <CardActions className='CreateGameCardAction'>
            <Button type='submit' variant='contained' color='primary' className='CreateGameButton'>
              Create
            </Button>
          </CardActions>
        </Card>
      </form>
    </Grow>
  );
}
Example #22
Source File: PaymentMethods.tsx    From storefront with MIT License 4 votes vote down vote up
PaymentMethods: React.VFC<Props> = ({
  customer,
  onSubmit,
  paymentMethod: initialPaymentMethod,
}) => {
  const creditCardFormRef = useRef<HTMLFormElement>(null);

  const [paymentMethod, setPaymentMethod] = useState(initialPaymentMethod);
  const [loading, setLoading] = useState(false);

  const { data: { paymentGateways } = { data: undefined }, loading: paymentGatewaysLoading } =
    usePaymentGatewaysQuery();

  const handleSubmitCreditCard = async (values: CreditCardFormData) => {
    setLoading(true);
    const data = await makePayment(process.env.BRAINTREE_TOKENIZATION_KEY, {
      billingAddress: {
        postalCode: customer.billing?.postcode ?? undefined,
      },
      cvv: values.ccCsc,
      expirationDate: values.ccExp,
      number: values.ccNumber,
    });
    setLoading(false);
    onSubmit({
      creditCard: {
        cardType: data.cardType,
        lastFour: data.lastFour,
      },
      paymentMethod,
      paymentNonce: data.nonce,
    });
  };

  const handleSubmit = () => {
    if (creditCardFormRef.current != null) {
      if (creditCardFormRef.current.checkValidity()) {
        // The following line is not working anymore for some reason:
        // creditCardFormRef.current.dispatchEvent(new Event('submit', { cancelable: true }));
        // So I have overridden the `submit` method, see in `CreditCardForm`
        creditCardFormRef.current.submit();
      } else {
        creditCardFormRef.current.reportValidity();
      }
    }
    if (paymentMethod === 'braintree_paypal') {
      onSubmit({ paymentMethod });
    }
  };

  const handleChange = (ev: React.ChangeEvent<HTMLInputElement>) => {
    setPaymentMethod(ev.target.value);
  };

  return (
    <>
      <RadioGroup name="paymentMode" value={paymentMethod} onChange={handleChange}>
        {paymentGateways?.nodes?.map(
          (paymentGateway) =>
            paymentGateway != null && (
              <Box
                key={paymentGateway.id}
                component="label"
                htmlFor={`paymentMode-${paymentGateway.id}`}
                sx={{
                  alignItems: 'center',
                  backgroundColor: 'background.paper',
                  cursor: 'pointer',
                  display: 'flex',
                  mb: 2,
                  p: 2,
                }}
              >
                <div>
                  <Radio value={paymentGateway.id} id={`paymentMode-${paymentGateway.id}`} />
                </div>
                <Box sx={{ flexGrow: 1, ml: 2 }}>
                  <Typography variant="h5">{paymentGateway.title}</Typography>
                  {paymentGateway.icon != null ? (
                    <img src={paymentGateway.icon} alt="" height="24" />
                  ) : (
                    paymentGateway.id === 'braintree_cc' && (
                      <>
                        <SvgIcon component={AmexSvg} viewBox="0 0 30 30" fontSize="large" />{' '}
                        <SvgIcon component={VisaSvg} viewBox="0 0 30 30" fontSize="large" />
                      </>
                    )
                  )}
                  {!isBlank(paymentGateway.description) && (
                    <Typography variant="body2">{paymentGateway.description}</Typography>
                  )}
                  {paymentGateway.id === 'braintree_cc' && paymentMethod === paymentGateway.id && (
                    <CreditCardForm ref={creditCardFormRef} onSubmit={handleSubmitCreditCard} />
                  )}
                </Box>
              </Box>
            ),
        )}
      </RadioGroup>
      <Box sx={{ mt: 2 }}>
        <Button
          type="submit"
          color="primary"
          loading={paymentGatewaysLoading || loading}
          onClick={handleSubmit}
        >
          Continue to Review Your Order
        </Button>
      </Box>
    </>
  );
}
Example #23
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>
  );
}