types#Organization TypeScript Examples

The following examples show how to use types#Organization. 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: userStateUtils.ts    From crossfeed with Creative Commons Zero v1.0 Universal 5 votes vote down vote up
getExtendedOrg = (
  org: Organization | OrganizationTag | null,
  user: AuthUser | null
) => {
  const current: CurrentOrganization | null =
    org ?? user?.roles[0]?.organization ?? null;
  return current;
}
Example #2
Source File: Organizations.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 5 votes vote down vote up
Organizations: React.FC = () => {
  const { user, apiGet, apiPost } = useAuthContext();
  const [organizations, setOrganizations] = useState<Organization[]>([]);
  const classes = useStyles();

  const fetchOrganizations = useCallback(async () => {
    try {
      const rows = await apiGet<Organization[]>('/organizations/');
      setOrganizations(rows);
    } catch (e) {
      console.error(e);
    }
  }, [apiGet]);

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

  return (
    <div>
      <div className={classes.header}>
        <h1 className={classes.headerLabel}>Organizations</h1>
      </div>
      <div className={oldClasses.root}>
        <OrganizationList></OrganizationList>
        {user?.userType === 'globalAdmin' && (
          <>
            <ImportExport<Organization>
              name="organizations"
              fieldsToExport={[
                'name',
                'rootDomains',
                'ipBlocks',
                'isPassive',
                'tags'
              ]}
              onImport={async (results) => {
                // TODO: use a batch call here instead.
                const createdOrganizations = [];
                for (const result of results) {
                  createdOrganizations.push(
                    await apiPost('/organizations/', {
                      body: {
                        ...result,
                        // These fields are initially parsed as strings, so they need
                        // to be converted to arrays.
                        ipBlocks: (
                          ((result.ipBlocks as unknown) as string) || ''
                        ).split(','),
                        rootDomains: (
                          ((result.rootDomains as unknown) as string) || ''
                        ).split(','),
                        tags: (((result.tags as unknown) as string) || '')
                          .split(',')
                          .map((tag) => ({
                            name: tag
                          }))
                      }
                    })
                  );
                }
                setOrganizations(organizations.concat(...createdOrganizations));
              }}
              getDataToExport={() =>
                organizations.map(
                  (org) =>
                    ({
                      ...org,
                      tags: org.tags.map((tag) => tag.name)
                    } as any)
                )
              }
            />
          </>
        )}
      </div>
    </div>
  );
}
Example #3
Source File: Header.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
HeaderNoCtx: React.FC<ContextType> = (props) => {
  const { searchTerm, setSearchTerm } = props;
  const classes = useStyles();
  const history = useHistory();
  const location = useLocation();
  const {
    currentOrganization,
    setOrganization,
    showAllOrganizations,
    setShowAllOrganizations,
    user,
    logout,
    apiGet
  } = useAuthContext();
  const [navOpen, setNavOpen] = useState(false);
  const [organizations, setOrganizations] = useState<
    (Organization | OrganizationTag)[]
  >([]);
  const theme = useTheme();
  const isSmall = useMediaQuery(theme.breakpoints.down('md'));

  let userLevel = 0;
  if (user && user.isRegistered) {
    if (user.userType === 'standard') {
      userLevel = STANDARD_USER;
    } else {
      userLevel = GLOBAL_ADMIN;
    }
  }

  const fetchOrganizations = useCallback(async () => {
    try {
      const rows = await apiGet<Organization[]>('/organizations/');
      let tags: (OrganizationTag | Organization)[] = [];
      if (userLevel === GLOBAL_ADMIN) {
        tags = await apiGet<OrganizationTag[]>('/organizations/tags');
      }
      setOrganizations(tags.concat(rows));
    } catch (e) {
      console.error(e);
    }
  }, [apiGet, setOrganizations, userLevel]);

  React.useEffect(() => {
    if (userLevel > 0) {
      fetchOrganizations();
    }
  }, [fetchOrganizations, userLevel]);

  const navItems: NavItemType[] = [
    {
      title: 'Overview',
      path: '/',
      users: ALL_USERS,
      exact: true
    },
    {
      title: 'Inventory',
      path: '/inventory',
      users: ALL_USERS,
      exact: false
    },
    { title: 'Feeds', path: '/feeds', users: ALL_USERS, exact: false },
    {
      title: 'Scans',
      path: '/scans',
      users: GLOBAL_ADMIN,
      exact: true
    }
  ].filter(({ users }) => (users & userLevel) > 0);

  const userMenu: NavItemType = {
    title: (
      <div className={classes.userLink}>
        <UserIcon /> My Account <ArrowDropDown />
      </div>
    ),
    path: '#',
    exact: false,
    nested: [
      {
        title: 'Manage Organizations',
        path: '/organizations',
        users: GLOBAL_ADMIN,
        exact: true
      },
      {
        title: 'My Organizations',
        path: '/organizations',
        users: STANDARD_USER,
        exact: true
      },
      {
        title: 'Manage Users',
        path: '/users',
        users: GLOBAL_ADMIN,
        exact: true
      },
      {
        title: 'My Settings',
        path: '/settings',
        users: ALL_USERS,
        exact: true
      },
      {
        title: 'Logout',
        path: '/settings',
        users: ALL_USERS,
        onClick: logout,
        exact: true
      }
    ].filter(({ users }) => (users & userLevel) > 0)
  };

  const userItemsSmall: NavItemType[] = [
    {
      title: 'My Account',
      path: '#',
      users: ALL_USERS,
      exact: true
    },
    {
      title: 'Manage Organizations',
      path: '/organizations',
      users: GLOBAL_ADMIN,
      exact: true
    },
    {
      title: 'My Organizations',
      path: '/organizations',
      users: STANDARD_USER,
      exact: true
    },
    {
      title: 'Manage Users',
      path: '/users',
      users: GLOBAL_ADMIN,
      exact: true
    },
    {
      title: 'My Settings',
      path: '/settings',
      users: ALL_USERS,
      exact: true
    },
    {
      title: 'Logout',
      path: '/',
      users: ALL_USERS,
      onClick: logout,
      exact: true
    }
  ].filter(({ users }) => (users & userLevel) > 0);

  const desktopNavItems: JSX.Element[] = navItems.map((item) => (
    <NavItem key={item.title.toString()} {...item} />
  ));

  const navItemsToUse = () => {
    if (isSmall) {
      return userItemsSmall;
    } else {
      return navItems;
    }
  };

  return (
    <div>
      <AppBar position="static" elevation={0}>
        <div className={classes.inner}>
          <Toolbar>
            <Link to="/">
              <img
                src={logo}
                className={classes.logo}
                alt="Crossfeed Icon Navigate Home"
              />
            </Link>
            <div className={classes.lgNav}>{desktopNavItems.slice()}</div>

            <div className={classes.spacing} />

            {userLevel > 0 && (
              <>
                <SearchBar
                  initialValue={searchTerm}
                  value={searchTerm}
                  onChange={(value) => {
                    if (location.pathname !== '/inventory')
                      history.push('/inventory?q=' + value);
                    setSearchTerm(value, {
                      shouldClearFilters: false,
                      autocompleteResults: false
                    });
                  }}
                />
                {organizations.length > 1 && (
                  <>
                    <div className={classes.spacing} />
                    <Autocomplete
                      options={[{ name: 'All Organizations' }].concat(
                        organizations
                      )}
                      autoComplete={false}
                      className={classes.selectOrg}
                      classes={{
                        option: classes.option
                      }}
                      value={
                        showAllOrganizations
                          ? { name: 'All Organizations' }
                          : currentOrganization ?? undefined
                      }
                      filterOptions={(options, state) => {
                        // If already selected, show all
                        if (
                          options.find(
                            (option) =>
                              option.name.toLowerCase() ===
                              state.inputValue.toLowerCase()
                          )
                        ) {
                          return options;
                        }
                        return options.filter((option) =>
                          option.name
                            .toLowerCase()
                            .includes(state.inputValue.toLowerCase())
                        );
                      }}
                      disableClearable
                      blurOnSelect
                      selectOnFocus
                      getOptionLabel={(option) => option.name}
                      renderOption={(option) => (
                        <React.Fragment>{option.name}</React.Fragment>
                      )}
                      onChange={(
                        event: any,
                        value:
                          | Organization
                          | {
                              name: string;
                            }
                          | undefined
                      ) => {
                        if (value && 'id' in value) {
                          setOrganization(value);
                          setShowAllOrganizations(false);
                        } else {
                          setShowAllOrganizations(true);
                        }
                      }}
                      renderInput={(params) => (
                        <TextField
                          {...params}
                          variant="outlined"
                          inputProps={{
                            ...params.inputProps,
                            id: 'autocomplete-input',
                            autoComplete: 'new-password' // disable autocomplete and autofill
                          }}
                        />
                      )}
                    />
                  </>
                )}
                {isSmall ? null : <NavItem {...userMenu} />}
              </>
            )}
            <IconButton
              edge="start"
              className={classes.menuButton}
              aria-label="toggle mobile menu"
              color="inherit"
              onClick={() => setNavOpen((open) => !open)}
            >
              <MenuIcon />
            </IconButton>
          </Toolbar>
        </div>
      </AppBar>

      <Drawer
        anchor="right"
        open={navOpen}
        onClose={() => setNavOpen(false)}
        data-testid="mobilenav"
      >
        <List className={classes.mobileNav}>
          {navItemsToUse().map(({ title, path, nested, onClick }) => (
            <React.Fragment key={title.toString()}>
              {path && (
                <ListItem
                  button
                  exact
                  component={NavLink}
                  to={path}
                  activeClassName={classes.activeMobileLink}
                  onClick={onClick ? onClick : undefined}
                >
                  {title}
                </ListItem>
              )}
              {nested?.map((nested) => (
                <ListItem
                  button
                  exact
                  key={nested.title.toString()}
                  component={NavLink}
                  to={nested.onClick ? '#' : nested.path}
                  activeClassName={classes.activeMobileLink}
                  onClick={nested.onClick ? nested.onClick : undefined}
                >
                  {nested.title}
                </ListItem>
              ))}
            </React.Fragment>
          ))}
        </List>
      </Drawer>
    </div>
  );
}
Example #4
Source File: OrganizationForm.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
OrganizationForm: React.FC<{
  organization?: Organization;
  open: boolean;
  setOpen: (open: boolean) => void;
  onSubmit: (values: Object) => Promise<void>;
  type: string;
  parent?: Organization;
}> = ({ organization, onSubmit, type, open, setOpen, parent }) => {
  const defaultValues = () => ({
    name: organization ? organization.name : '',
    rootDomains: organization ? organization.rootDomains.join(', ') : '',
    ipBlocks: organization ? organization.ipBlocks.join(', ') : '',
    isPassive: organization ? organization.isPassive : false,
    tags: []
  });

  const [values, setValues] = useState<OrganizationFormValues>(defaultValues);

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

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

  return (
    <Dialog
      open={open}
      onClose={() => setOpen(false)}
      aria-labelledby="form-dialog-title"
      maxWidth="xs"
      fullWidth
    >
      <DialogTitle id="form-dialog-title">
        Create new {parent ? 'Team' : 'Organization'}
      </DialogTitle>
      <DialogContent>
        <TextField
          margin="dense"
          id="name"
          name="name"
          label="Organization Name"
          type="text"
          fullWidth
          value={values.name}
          onChange={onTextChange}
        />
        <TextField
          margin="dense"
          id="rootDomains"
          name="rootDomains"
          label="Root Domains"
          type="text"
          fullWidth
          value={values.rootDomains}
          onChange={onTextChange}
        />
        <TextField
          margin="dense"
          id="ipBlocks"
          name="ipBlocks"
          label="IP Blocks"
          type="text"
          fullWidth
          value={values.ipBlocks}
          onChange={onTextChange}
        />
        <br></br>
        <br></br>
        <FormControlLabel
          control={
            <Switch
              checked={values.isPassive}
              name="isPassive"
              onChange={(e) => {
                onChange(e.target.name, e.target.checked);
              }}
              color="primary"
            />
          }
          label="Passive Mode"
        />
      </DialogContent>
      <DialogActions>
        <Button variant="outlined" onClick={() => setOpen(false)}>
          Cancel
        </Button>
        <Button
          variant="contained"
          color="primary"
          onClick={async () => {
            await onSubmit({
              rootDomains:
                values.rootDomains === ''
                  ? []
                  : values.rootDomains
                      .split(',')
                      .map((domain) => domain.trim()),
              ipBlocks:
                values.ipBlocks === ''
                  ? []
                  : values.ipBlocks.split(',').map((ip) => ip.trim()),
              name: values.name,
              isPassive: values.isPassive,
              tags: values.tags,
              parent: parent ? parent.id : undefined
            });
            if (!organization) setValues(defaultValues);
            setOpen(false);
          }}
        >
          Save
        </Button>
      </DialogActions>
    </Dialog>
  );
}
Example #5
Source File: OrganizationList.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
OrganizationList: React.FC<{
  parent?: Organization;
}> = ({ parent }) => {
  const { apiPost, apiGet, setFeedbackMessage, user } = useAuthContext();
  const [organizations, setOrganizations] = useState<Organization[]>([]);
  const [dialogOpen, setDialogOpen] = useState(false);
  const history = useHistory();
  const classes = useStyles();

  const onSubmit = async (body: Object) => {
    try {
      const org = await apiPost('/organizations/', {
        body
      });
      setOrganizations(organizations.concat(org));
    } catch (e) {
      setFeedbackMessage({
        message:
          e.status === 422
            ? 'Error when submitting organization entry.'
            : e.message ?? e.toString(),
        type: 'error'
      });
      console.error(e);
    }
  };

  const fetchOrganizations = useCallback(async () => {
    try {
      const rows = await apiGet<Organization[]>('/organizations/');
      setOrganizations(rows);
    } catch (e) {
      console.error(e);
    }
  }, [apiGet]);

  React.useEffect(() => {
    if (!parent) fetchOrganizations();
    else {
      setOrganizations(parent.children);
    }
  }, [fetchOrganizations, parent]);

  return (
    <>
      <Grid
        container
        spacing={2}
        style={{ margin: '0 auto', marginTop: '1rem', maxWidth: '1000px' }}
      >
        {user?.userType === 'globalAdmin' && (
          <Grid item>
            <Paper
              elevation={0}
              classes={{ root: classes.cardRoot }}
              style={{ border: '1px dashed #C9C9C9', textAlign: 'center' }}
              onClick={() => setDialogOpen(true)}
            >
              <h1>Create New {parent ? 'Team' : 'Organization'}</h1>
              <p>
                <Add></Add>
              </p>
            </Paper>
          </Grid>
        )}
        {organizations.map((org) => (
          // TODO: Add functionality to delete organizations
          <Grid item key={org.id}>
            <Paper
              elevation={0}
              classes={{ root: classes.cardRoot }}
              onClick={() => {
                history.push('/organizations/' + org.id);
              }}
            >
              <h1>{org.name}</h1>
              <p>{org.userRoles ? org.userRoles.length : 0} members</p>
              {org.tags && org.tags.length > 0 && (
                <p>Tags: {org.tags.map((tag) => tag.name).join(', ')}</p>
              )}
            </Paper>
          </Grid>
        ))}
      </Grid>
      <OrganizationForm
        onSubmit={onSubmit}
        open={dialogOpen}
        setOpen={setDialogOpen}
        type="create"
        parent={parent}
      ></OrganizationForm>
    </>
  );
}
Example #6
Source File: AuthContextProvider.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
AuthContextProvider: React.FC = ({ children }) => {
  const [authUser, setAuthUser] = useState<AuthUser | null>(null);
  const [token, setToken] = usePersistentState<string | null>('token', null);
  const [org, setOrg] = usePersistentState<
    Organization | OrganizationTag | null
  >('organization', null);
  const [showAllOrganizations, setShowAllOrganizations] = usePersistentState<
    boolean
  >('showAllOrganizations', false);
  const [feedbackMessage, setFeedbackMessage] = useState<{
    message: string;
    type: AlertProps['severity'];
  } | null>(null);
  const cookies = useMemo(() => new Cookies(), []);

  const logout = useCallback(async () => {
    const shouldReload = !!token;

    localStorage.clear();
    await Auth.signOut();
    cookies.remove('crossfeed-token', {
      domain: process.env.REACT_APP_COOKIE_DOMAIN
    });

    if (shouldReload) {
      // Refresh the page only if the token was previously defined
      // (i.e. it is now invalid / has expired now).
      window.location.reload();
    }
  }, [cookies, token]);

  const handleError = useCallback(
    async (e: Error) => {
      if (e.message.includes('401')) {
        // Unauthorized, log out user
        await logout();
      }
    },
    [logout]
  );

  const api = useApi(handleError);
  const { apiGet, apiPost } = api;

  const getProfile = useCallback(async () => {
    const user: User = await apiGet<User>('/users/me');
    setAuthUser({
      ...user,
      isRegistered: user.firstName !== ''
    });
  }, [setAuthUser, apiGet]);

  const setProfile = useCallback(
    async (user: User) => {
      setAuthUser({
        ...user,
        isRegistered: user.firstName !== ''
      });
    },
    [setAuthUser]
  );

  const refreshUser = useCallback(async () => {
    if (!token && process.env.REACT_APP_USE_COGNITO) {
      const session = await Auth.currentSession();
      const { token } = await apiPost<{ token: string; user: User }>(
        '/auth/callback',
        {
          body: {
            token: session.getIdToken().getJwtToken()
          }
        }
      );
      setToken(token);
    }
  }, [apiPost, setToken, token]);

  const extendedOrg = useMemo(() => {
    return getExtendedOrg(org, authUser);
  }, [org, authUser]);

  const maximumRole = useMemo(() => {
    return getMaximumRole(authUser);
  }, [authUser]);

  const touVersion = useMemo(() => {
    return getTouVersion(maximumRole);
  }, [maximumRole]);

  const userMustSign = useMemo(() => {
    return getUserMustSign(authUser, touVersion);
  }, [authUser, touVersion]);

  useEffect(() => {
    refreshUser();
    // eslint-disable-next-line
  }, []);

  useEffect(() => {
    if (!token) {
      setAuthUser(null);
    } else {
      getProfile();
    }
  }, [token, getProfile]);

  return (
    <AuthContext.Provider
      value={{
        user: authUser,
        token,
        setUser: setProfile,
        refreshUser,
        setOrganization: setOrg,
        currentOrganization: extendedOrg,
        showAllOrganizations: showAllOrganizations,
        setShowAllOrganizations: setShowAllOrganizations,
        login: setToken,
        logout,
        setLoading: () => {},
        maximumRole,
        touVersion,
        userMustSign,
        setFeedbackMessage,
        ...api
      }}
    >
      {api.loading && (
        <div className="cisa-crossfeed-loading">
          <div></div>
          <div></div>
        </div>
      )}
      {feedbackMessage && (
        <Snackbar
          open={!!feedbackMessage}
          autoHideDuration={5000}
          onClose={() => setFeedbackMessage(null)}
        >
          <Alert
            onClose={() => setFeedbackMessage(null)}
            severity={feedbackMessage.type}
          >
            {feedbackMessage.message}
          </Alert>
        </Snackbar>
      )}
      {children}
    </AuthContext.Provider>
  );
}
Example #7
Source File: ScansView.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
ScansView: React.FC = () => {
  const { apiGet, apiPost, apiDelete } = useAuthContext();
  const [showModal, setShowModal] = useState<Boolean>(false);
  const [selectedRow, setSelectedRow] = useState<number>(0);
  const [scans, setScans] = useState<Scan[]>([]);
  const [organizationOptions, setOrganizationOptions] = useState<
    OrganizationOption[]
  >([]);
  const [tags, setTags] = useState<OrganizationTag[]>([]);
  const [scanSchema, setScanSchema] = useState<ScanSchema>({});

  const columns: Column<Scan>[] = [
    {
      Header: 'Run',
      id: 'run',
      Cell: ({ row }: { row: { index: number } }) => (
        <div
          style={{ textAlign: 'center' }}
          onClick={() => {
            runScan(row.index);
          }}
        >
          <FaPlayCircle />
        </div>
      ),
      disableFilters: true
    },
    {
      Header: 'Name',
      accessor: 'name',
      width: 200,
      id: 'name',
      disableFilters: true
    },
    {
      Header: 'Tags',
      accessor: ({ tags }) => tags.map((tag) => tag.name).join(', '),
      width: 150,
      minWidth: 150,
      id: 'tags',
      disableFilters: true
    },
    {
      Header: 'Mode',
      accessor: ({ name }) =>
        scanSchema[name] && scanSchema[name].isPassive ? 'Passive' : 'Active',
      width: 150,
      minWidth: 150,
      id: 'mode',
      disableFilters: true
    },
    {
      Header: 'Frequency',
      accessor: ({ frequency, isSingleScan }) => {
        let val, unit;
        if (frequency < 60 * 60) {
          val = frequency / 60;
          unit = 'minute';
        } else if (frequency < 60 * 60 * 24) {
          val = frequency / (60 * 60);
          unit = 'hour';
        } else {
          val = frequency / (60 * 60 * 24);
          unit = 'day';
        }
        if (isSingleScan) {
          return 'Single Scan';
        }
        return `Every ${val} ${unit}${val === 1 ? '' : 's'}`;
      },
      width: 200,
      id: 'frequency',
      disableFilters: true
    },
    {
      Header: 'Last Run',
      accessor: (args: Scan) => {
        return !args.lastRun ||
          new Date(args.lastRun).getTime() === new Date(0).getTime()
          ? 'None'
          : `${formatDistanceToNow(parseISO(args.lastRun))} ago`;
      },
      width: 200,
      id: 'lastRun',
      disableFilters: true
    },
    {
      Header: 'Edit',
      id: 'edit',
      Cell: ({ row }: CellProps<Scan>) => (
        <Link to={`/scans/${row.original.id}`} style={{ color: 'black' }}>
          <FaEdit />
        </Link>
      ),
      disableFilters: true
    },
    {
      Header: 'Delete',
      id: 'delete',
      Cell: ({ row }: { row: { index: number } }) => (
        <span
          onClick={() => {
            setShowModal(true);
            setSelectedRow(row.index);
          }}
        >
          <FaTimes />
        </span>
      ),
      disableFilters: true
    },
    {
      Header: 'Description',
      accessor: ({ name }) => scanSchema[name]?.description,
      width: 200,
      maxWidth: 200,
      id: 'description',
      disableFilters: true
    }
  ];
  const [errors, setErrors] = useState<Errors>({});

  const [values] = useState<ScanFormValues>({
    name: 'censys',
    arguments: '{}',
    organizations: [],
    frequency: 1,
    frequencyUnit: 'minute',
    isGranular: false,
    isUserModifiable: false,
    isSingleScan: false,
    tags: []
  });

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

  const fetchScans = useCallback(async () => {
    try {
      const { scans, organizations, schema } = await apiGet<{
        scans: Scan[];
        organizations: Organization[];
        schema: ScanSchema;
      }>('/scans/');
      const tags = await apiGet<OrganizationTag[]>(`/organizations/tags`);
      setScans(scans);
      setScanSchema(schema);
      setOrganizationOptions(
        organizations.map((e) => ({ label: e.name, value: e.id }))
      );
      setTags(tags);
    } catch (e) {
      console.error(e);
    }
  }, [apiGet]);

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

  const onSubmit = async (body: ScanFormValues) => {
    try {
      // For now, parse the arguments as JSON. We'll want to add a GUI for this in the future
      body.arguments = JSON.parse(body.arguments);
      setFrequency(body);

      const scan = await apiPost('/scans/', {
        body: {
          ...body,
          organizations: body.organizations
            ? body.organizations.map((e) => e.value)
            : [],
          tags: body.tags ? body.tags.map((e) => ({ id: e.value })) : []
        }
      });
      setScans(scans.concat(scan));
    } catch (e) {
      setErrors({
        global: e.message ?? e.toString()
      });
      console.log(e);
    }
  };

  const invokeScheduler = async () => {
    setErrors({ ...errors, scheduler: '' });
    try {
      await apiPost('/scheduler/invoke', { body: {} });
    } catch (e) {
      console.error(e);
      setErrors({ ...errors, scheduler: 'Invocation failed.' });
    }
  };

  /**
   * Manually runs a single scan, then immediately invokes the
   * scheduler so the scan is run.
   * @param index Row index
   */
  const runScan = async (index: number) => {
    const row = scans[index];
    try {
      await apiPost(`/scans/${row.id}/run`, { body: {} });
    } catch (e) {
      console.error(e);
      setErrors({ ...errors, scheduler: 'Run failed.' });
    }
    await invokeScheduler();
  };

  return (
    <>
      <Table<Scan> columns={columns} data={scans} fetchData={fetchScans} />
      <br></br>
      <Button type="submit" outline onClick={invokeScheduler}>
        Manually run scheduler
      </Button>
      {errors.scheduler && <p className={classes.error}>{errors.scheduler}</p>}
      <h2>Add a scan</h2>
      {errors.global && <p className={classes.error}>{errors.global}</p>}
      <ScanForm
        organizationOption={organizationOptions}
        tags={tags}
        propValues={values}
        onSubmit={onSubmit}
        type="create"
        scanSchema={scanSchema}
      ></ScanForm>
      <ImportExport<Scan>
        name="scans"
        fieldsToExport={['name', 'arguments', 'frequency']}
        onImport={async (results) => {
          // TODO: use a batch call here instead.
          const createdScans = [];
          for (const result of results) {
            createdScans.push(
              await apiPost('/scans/', {
                body: {
                  ...result,
                  // These fields are initially parsed as strings, so they need
                  // to be converted to objects.
                  arguments: JSON.parse(
                    ((result.arguments as unknown) as string) || ''
                  )
                }
              })
            );
          }
          setScans(scans.concat(...createdScans));
        }}
        getDataToExport={() =>
          scans.map((scan) => ({
            ...scan,
            arguments: JSON.stringify(scan.arguments)
          }))
        }
      />

      {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 scan?</h2>}
            >
              <p>
                Are you sure you would like to delete the{' '}
                <code>{scans[selectedRow].name}</code> scan?
              </p>
            </Modal>
          </ModalContainer>
        </div>
      )}
    </>
  );
}
Example #8
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>
  );
}