react-icons/fa#FaTimes TypeScript Examples

The following examples show how to use react-icons/fa#FaTimes. 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: Profile.tsx    From vitty-extension with GNU General Public License v3.0 6 votes vote down vote up
Profile: React.FC<any> = ({ onClose, onLogOut, name, email }) => {
  const handleClick = (): void => {
    const newURL = 'https://vitty.dscvit.com'
    void chrome.tabs.create({ url: newURL, active: true })
  }
  return (
    <div className='modal' onClick={onClose}>
      <div className='modal-content' onClick={e => e.stopPropagation()}>
        <div className='modal-header'>
          <h3>Profile</h3>
          <FaTimes onClick={onClose} />
        </div>
        <div className='modal-body'>
          {name !== null && <div className='modal-message'><span>Name:</span> {name}</div>}
          <div className='modal-message'><span>Email:</span> {email}</div>
          <div className='modal-buttons'>
            <button className='modal-yes' onClick={onLogOut}>Log Out</button>
          </div>
          <div className='modal-tip'>Tip: To edit timetable, go to <span onClick={handleClick}>vitty.dscvit.com</span></div>
        </div>
      </div>
    </div>
  )
}
Example #2
Source File: SocialLink.tsx    From convoychat with GNU General Public License v3.0 6 votes vote down vote up
SocialLink: React.FC<ISocialLink> = ({ url, type, onDelete }) => {
  let { pathname = url } = parseURL(url) || {};

  if (type === "website") pathname = url;

  return (
    <StyledSocialLink align="center" gap="large" nowrap type={type}>
      <div className="sociallink__icon">{ICON_MAP[type]()}</div>
      <a href={sanitizeUrl(url)}>
        {DOMPurify.sanitize(pathname?.replace(/^\//, ""))}
      </a>
      <IconButton icon={<FaTimes />} onClick={onDelete} />
    </StyledSocialLink>
  );
}
Example #3
Source File: ColumnFilter.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
ColumnFilter: React.FC<Props> = ({
  column: { filterValue, setFilter, id }
}) => {
  const [search, setSearch] = useState('');

  const onSubmit: React.FormEventHandler = (e) => {
    e.preventDefault();
    setFilter(search);
  };

  const clear: React.MouseEventHandler = (e) => {
    setSearch('');
    setFilter('');
  };

  return (
    <form onSubmit={onSubmit} className={classes.root}>
      <TextInput
        id={id}
        name={id}
        type="text"
        value={search}
        onChange={(e) => {
          setSearch(e.target.value);
        }}
        className={classes.input}
      />
      <FaSearch className={classes.searchIcon}></FaSearch>
      {filterValue && (
        <button type="button" className={classes.clearFilter} onClick={clear}>
          <FaTimes />
        </button>
      )}
    </form>
  );
}
Example #4
Source File: CreateRoom.tsx    From convoychat with GNU General Public License v3.0 5 votes vote down vote up
CreateRoom: React.FC = () => {
  const { state, dispatch } = useModalContext();
  const { register, handleSubmit, errors: formErrors } = useForm<Inputs>();

  const [createRoom, { loading }] = useCreateRoomMutation({
    refetchQueries: [{ query: ListCurrentUserRoomsDocument }],
  });

  const closeModal = () => {
    dispatch({ type: "CLOSE", modal: "CreateRoom" });
  };

  const onSubmit = async (data: Inputs) => {
    try {
      await createRoom({
        variables: {
          name: data.roomName,
        },
      });
      closeModal();
    } catch (err) {
      console.log(err);
    }
  };

  return (
    <Modal
      isOpen={state.isCreateRoomModalOpen}
      closeTimeoutMS={300}
      onRequestClose={closeModal}
      contentLabel="Create New Room"
      className="ModalContent"
      overlayClassName="ModalOverlay"
    >
      <h2>Create New Room</h2>
      <small className="textcolor--gray">Give it a cool name</small>
      <Spacer gap="xlarge" />

      <form onSubmit={handleSubmit(onSubmit)}>
        <Input
          type="text"
          name="roomName"
          label="Room Name"
          placeholder="Anurag's room"
          icon={FaUsers}
          errors={formErrors}
          inputRef={register({ required: "Room name is required" })}
        />

        <ButtonGroup gap="medium" float="right">
          <Button onClick={closeModal} variant="danger" icon={FaTimes}>
            Cancel
          </Button>
          <Button isLoading={loading} icon={FaUsers}>
            Create room
          </Button>
        </ButtonGroup>
      </form>
    </Modal>
  );
}
Example #5
Source File: InviteMembers.tsx    From convoychat with GNU General Public License v3.0 4 votes vote down vote up
InviteMembers: React.FC<IInviteMembers> = ({ roomId }) => {
  const [selectedMembers, setSelectedMembers] = useState<any>({});
  const { state, dispatch } = useModalContext();

  const { register, handleSubmit, errors: formErrors } = useForm<Inputs>();
  const onSubmit = async (data: Inputs) => {};

  const { data: allUsers } = useListUsersQuery();
  const [
    createInvitationLink,
    { data: invitationLink },
  ] = useCreateInvitationLinkMutation({});

  const [inviteMembers, { loading: isLoading }] = useInviteMembersMutation();

  const toggleMemberSelection = (member: IMember) => {
    if (selectedMembers[member.id]) {
      let copy = { ...selectedMembers };
      delete copy[member.id];
      setSelectedMembers({ ...copy });
    } else {
      setSelectedMembers({ ...selectedMembers, [member.id]: member });
    }
  };

  const closeModal = () => {
    dispatch({ type: "CLOSE", modal: "InviteMembers" });
  };

  useEffect(() => {
    if (state.isInviteMembersModalOpen) {
      createInvitationLink({ variables: { roomId } });
    }
  }, [state.isInviteMembersModalOpen, roomId]);

  const selectedMembersIds = Object.keys(selectedMembers);

  return (
    <Modal
      closeTimeoutMS={300}
      isOpen={state.isInviteMembersModalOpen}
      onRequestClose={closeModal}
      contentLabel="Create New Room"
      className="ModalContent"
      overlayClassName="ModalOverlay"
    >
      <h2>Invite Members</h2>
      <small className="textcolor--gray">yeah... yeah spam them</small>

      <Spacer gap="huge" />

      <Input
        type="text"
        icon={FaLink}
        postfixIcon={FaCopy}
        placeholder="invitation link"
        defaultValue={invitationLink?.invitation?.link}
        onPostfixIconClick={e => copyToClipboard(e.value)}
        label={
          <span>
            Copy Invitation Link{" "}
            <span className="textcolor--gray">(expires after 24hours)</span>
          </span>
        }
      />

      <Spacer gap="large" />

      <div>
        <Input
          type="text"
          name="username"
          label="Find Users"
          placeholder="bear grylls"
          icon={FaSearch}
          errors={formErrors}
          inputRef={register({ required: "Username is required" })}
        />

        {allUsers && (
          <MemberSelector
            members={allUsers?.users}
            selectedMembers={selectedMembers}
            onMemberClick={toggleMemberSelection}
          />
        )}

        <Spacer gap="xlarge" />

        <ButtonGroup gap="medium" float="right">
          <Button onClick={closeModal} variant="danger" icon={FaTimes}>
            Cancel
          </Button>
          <Button
            icon={FaPaperPlane}
            isLoading={isLoading}
            disabled={selectedMembersIds.length < 1}
            onClick={() => {
              inviteMembers({
                variables: { roomId, members: selectedMembersIds },
              });
            }}
          >
            Invite members ({selectedMembersIds.length})
          </Button>
        </ButtonGroup>
      </div>
    </Modal>
  );
}
Example #6
Source File: UserSettings.tsx    From convoychat with GNU General Public License v3.0 4 votes vote down vote up
UserSettings: React.FC = () => {
  const { user } = useAuthContext();
  const { state, dispatch } = useModalContext();
  const [color, setColor] = useState<string>("");
  const [socialLinks, setSocialLinks] = useState<Omit<UserLinks, "__typename">>(
    {}
  );

  const handleSocialLinks = (e: any) => {
    setSocialLinks({ ...socialLinks, [e.type]: e.value });
  };
  const handleColorChange = (color: any) => {
    setColor(color.hex);
  };
  const closeModal = () => dispatch({ modal: "UserSettings", type: "CLOSE" });

  const [setUserSettings, { loading }] = useSetUserSettingsMutation({
    onError(err) {
      console.log(err);
    },
    onCompleted() {
      closeModal();
    },
  });

  useEffect(() => {
    setColor(user?.color);
    setSocialLinks({
      github: user?.links?.github,
      twitter: user?.links?.twitter,
      instagram: user?.links?.instagram,
      website: user?.links?.website,
    });
  }, [user?.color, user?.links]);

  const onSubmit = () => {
    setUserSettings({
      variables: {
        color: color,
        ...socialLinks,
      },
    });
  };

  return (
    <Modal
      isOpen={state.isUserSettingsModalOpen}
      closeTimeoutMS={300}
      onRequestClose={closeModal}
      contentLabel="User Settings"
      className="ModalContent user-settings__modal"
      overlayClassName="ModalOverlay"
    >
      <h2>User Settings</h2>
      <small className="textcolor--gray">
        Who does not loves a useless modal
      </small>
      <Spacer gap="xlarge" />

      <UserSettingsStyles>
        <p style={{ fontSize: 14 }}>Add your personal color</p>

        <ColorPreview
          color={color}
          handleColorChange={handleColorChange}
          preview={
            <Message>
              <Message.MetaInfo
                author={{
                  color: color,
                  avatarUrl: user?.avatarUrl,
                  name: user?.name,
                }}
              />
              <Message.Content>Hello world</Message.Content>
            </Message>
          }
        />
        <Spacer gap="huge" />

        <SocialLinkInput onSubmit={handleSocialLinks} />

        <Spacer gap="xlarge" />
        <section className="social-links__grid">
          {Object.keys(socialLinks).map((type: ILinkTypes) => {
            return socialLinks[type] ? (
              <SocialLink
                key={type}
                type={type}
                url={socialLinks[type]}
                onDelete={() => {
                  setSocialLinks({
                    ...socialLinks,
                    [type]: null,
                  });
                }}
              />
            ) : null;
          })}
        </section>

        <Spacer gap="huge" />

        <ButtonGroup gap="medium" float="right">
          <Button onClick={closeModal} variant="danger" icon={FaTimes}>
            Cancel
          </Button>
          <Button isLoading={loading} onClick={onSubmit} icon={FaSave}>
            Save Changes
          </Button>
        </ButtonGroup>
      </UserSettingsStyles>
    </Modal>
  );
}
Example #7
Source File: LastNotificationsModal.tsx    From hub with Apache License 2.0 4 votes vote down vote up
LastNotificationsModal = (props: Props) => {
  const notificationsWithErrors: WebhookNotification[] = props.notifications.filter(
    (notif: WebhookNotification) => notif.error
  );

  return (
    <>
      <Modal
        className="d-inline-block"
        buttonType="btn badge btn-outline-secondary"
        buttonContent={
          <>
            <GrConnect className={`me-2 ${styles.icon}`} />
            <span>Show last notifications</span>
          </>
        }
        modalDialogClassName={styles.modalDialog}
        header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Last notifications</div>}
        footerClassName={styles.modalFooter}
      >
        <div className="m-3">
          <table className={`table table-striped table-bordered table-sm mb-0 ${styles.table}`}>
            <thead>
              <tr>
                <th scope="col">Notification id</th>
                <th scope="col">Created at</th>
                <th scope="col">Processed</th>
                <th scope="col">Processed at</th>
                <th scope="col">Succeeded</th>
              </tr>
            </thead>
            <tbody>
              {props.notifications.map((item: WebhookNotification) => (
                <tr data-testid="lastNotificationCell" key={`lastNotif_${item.notificationId}`}>
                  <td className="align-middle">{item.notificationId}</td>
                  <td className="align-middle">{moment.unix(item.createdAt).format('YYYY/MM/DD HH:mm:ss (Z)')}</td>
                  <td className="align-middle text-center">
                    {item.processed && <FaCheck className="text-success" data-testid="processedIcon" />}
                  </td>
                  <td className="align-middle">
                    {!isNull(item.processedAt) && moment.unix(item.processedAt).format('YYYY/MM/DD HH:mm:ss (Z)')}
                  </td>
                  <td className="align-middle text-center">
                    {item.processed && (
                      <>
                        {item.error ? (
                          <FaTimes className="text-danger" data-testid="failedIcon" />
                        ) : (
                          <FaCheck className="text-success" data-testid="succeededIcon" />
                        )}
                      </>
                    )}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>

          {notificationsWithErrors.length > 0 && (
            <>
              <div className="h5 mt-5 mb-4 text-uppercase fw-bold">Errors logs</div>

              <table className={`table table-striped table-bordered table-sm mb-0 ${styles.table}`}>
                <thead>
                  <tr>
                    <th scope="col">Notification id</th>
                    <th scope="col">Error</th>
                  </tr>
                </thead>
                <tbody>
                  {notificationsWithErrors.map((item: WebhookNotification) => (
                    <tr data-testid="lastNotificationErrorCell" key={`lastNotifError_${item.notificationId}`}>
                      <td>{item.notificationId}</td>

                      <td>{item.error}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </>
          )}
        </div>
      </Modal>
      {notificationsWithErrors.length > 0 && (
        <FaExclamation className="ms-1 text-warning" data-testid="lastNotifAlert" />
      )}
    </>
  );
}
Example #8
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 #9
Source File: Settings.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
Settings: React.FC = () => {
  const { logout, user, setUser, apiPost, apiDelete } = useAuthContext();
  const [showModal, setShowModal] = useState<Boolean>(false);
  const [apiKey, setApiKey] = useState<string>('');

  const generateApiKey = async () => {
    if (!user) return;
    const apiKey = await apiPost<
      ApiKey & {
        key: string;
      }
    >('/api-keys');
    setUser({ ...user, apiKeys: user.apiKeys.concat([apiKey]) });
    setApiKey(apiKey.key);
    setShowModal(true);
  };

  const deleteApiKey = async (key: string) => {
    if (!user) return;
    await apiDelete<ApiKey>('/api-keys/' + key);
    setUser({
      ...user,
      apiKeys: user.apiKeys.filter((k) => k.id !== key)
    });
  };

  const columns: Column<ApiKey>[] = [
    {
      Header: 'Key',
      accessor: ({ lastFour }) => '*'.repeat(12) + lastFour,
      width: 200,
      disableFilters: true,
      id: 'key'
    },
    {
      Header: 'Date Created',
      accessor: ({ createdAt }) =>
        `${formatDistanceToNow(parseISO(createdAt))} ago`,
      width: 50,
      minWidth: 50,
      id: 'createdAt',
      disableFilters: true
    },
    {
      Header: 'Last Used',
      accessor: ({ lastUsed }) =>
        lastUsed ? `${formatDistanceToNow(parseISO(lastUsed))} ago` : 'None',
      width: 50,
      minWidth: 50,
      id: 'lastUsed',
      disableFilters: true
    },
    {
      Header: 'Delete',
      id: 'delete',
      Cell: ({ row }: { row: { index: number } }) => (
        <span
          onClick={() => {
            if (!user) return;
            deleteApiKey(user.apiKeys[row.index].id);
          }}
        >
          <FaTimes />
        </span>
      ),
      disableFilters: true
    }
  ];

  return (
    <div className={classes.root}>
      <h1>My Account</h1>
      <h2>Name: {user && user.fullName}</h2>
      <h2>Email: {user && user.email}</h2>
      <h2>
        Member of:{' '}
        {user &&
          (user.roles || [])
            .filter((role) => role.approved)
            .map((role) => role.organization.name)
            .join(', ')}
      </h2>
      <h2>API Keys:</h2>
      <p>
        <a
          href="https://docs.crossfeed.cyber.dhs.gov/api-reference"
          rel="noopener noreferrer"
          target="_blank"
        >
          Read API documentation
        </a>
      </p>
      {(!user?.apiKeys || user.apiKeys.length === 0) && <p>No API Keys</p>}
      {user?.apiKeys && user.apiKeys.length > 0 && (
        <Table<ApiKey> columns={columns} data={user?.apiKeys} />
      )}
      <br></br>
      <Button type="button" onClick={generateApiKey}>
        Generate API Key
      </Button>
      <br></br>
      <br></br>

      {showModal && (
        <div>
          <Overlay />
          <ModalContainer>
            <Modal
              actions={
                <>
                  <Button
                    type="button"
                    onClick={() => {
                      setShowModal(false);
                    }}
                  >
                    Ok
                  </Button>
                </>
              }
              title={<h2>Copy API Key</h2>}
            >
              <p>
                Please copy your API key now, as you will not be able to see it
                again:
              </p>
              <code>{apiKey}</code>
            </Modal>
          </ModalContainer>
        </div>
      )}
      {user?.userType === 'globalAdmin' && (
        <>
          <a href={`${process.env.REACT_APP_API_URL}/matomo/index.php`}>
            <Button type="button">Matomo</Button>
          </a>
          <br />
          <br />
        </>
      )}
      <Button type="button" onClick={logout}>
        Logout
      </Button>
    </div>
  );
}
Example #10
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>
  );
}