@patternfly/react-core#DropdownItem JavaScript Examples

The following examples show how to use @patternfly/react-core#DropdownItem. 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: AddConnectionMenu.js    From cockpit-wicked with GNU General Public License v2.0 6 votes vote down vote up
AddConnectionMenu = () => {
    const [isOpen, setOpen] = useState(false);
    const [formComponent, setFormComponent] = useState(null);
    const Component = formComponents[formComponent];

    const toggle = () => {
        setOpen(!isOpen);
        if (isOpen) {
            const toggle = document.getElementById('add-buttons-toggle');
            toggle.focus();
        }
    };

    const dropdownItems = [
        <DropdownItem key="bond" component="button" onClick={() => setFormComponent('BondForm')}>
            {_("Bond")}
        </DropdownItem>,
        <DropdownItem key="bridge" component="button" onClick={() => setFormComponent('BridgeForm')}>
            {_("Bridge")}
        </DropdownItem>,
        <DropdownItem key="vlan" component="button" onClick={() => setFormComponent('VlanForm')}>
            {_("VLAN")}
        </DropdownItem>,
    ];

    return (
        <>
            <Dropdown
                onSelect={toggle}
                toggle={<DropdownToggle id="add-buttons-toggle" onToggle={toggle}>{_("Add")}</DropdownToggle>}
                isOpen={isOpen}
                dropdownItems={dropdownItems}
            />
            { Component && <Component isOpen onClose={() => setFormComponent(null)} /> }
        </>
    );
}
Example #2
Source File: widget-components.js    From ibutsu-server with MIT License 6 votes vote down vote up
render() {
    const { isOpen } = this.state;
    const { showDescription } = this.state;
    let dropdownItems = [];
    this.props.dropdownItems.forEach( (item) => {
      dropdownItems.push(<DropdownItem onClick={this.onSelect} key={item}> {item} </DropdownItem>)
    });
    return (
      <div data-id='widget-param-dropdown'>
        {showDescription &&
        <Text component='h3'>{this.props.description || this.props.tooltip || ""}</Text>
        }
        <Tooltip content={this.props.tooltip}>
          <Dropdown
            direction={this.direction}
            isOpen={isOpen}
            dropdownItems={dropdownItems}
            toggle={
              <DropdownToggle id="toggle-dropdown" onToggle={this.onToggle}>
                {this.state.value}
              </DropdownToggle>
            }
          />
        </Tooltip>
      </div>
    );
  }
Example #3
Source File: DetailsHeader.js    From edge-frontend with Apache License 2.0 6 votes vote down vote up
dropdownItems = (data, imageVersion, openUpdateWizard) => {
  const imageData = imageVersion ? imageVersion : data?.images?.[0];

  const actionsArray = [];

  imageData?.image?.ID &&
    actionsArray.push(
      <DropdownItem
        key="create-new-version-button"
        component="button"
        onClick={() => openUpdateWizard(imageData?.image?.ID)}
      >
        Create new version
      </DropdownItem>
    );

  imageData?.image?.Installer?.ImageBuildISOURL &&
    actionsArray.push(
      <DropdownItem key="download-button" component="button">
        <Text
          className="force-text-black remove-underline"
          component="a"
          href={imageData?.image?.Installer?.ImageBuildISOURL}
          rel="noopener noreferrer"
          target="_blank"
        >
          Download installable .iso for newest image
        </Text>
      </DropdownItem>
    );
  return actionsArray;
}
Example #4
Source File: ToolbarKebab.js    From edge-frontend with Apache License 2.0 6 votes vote down vote up
ToolbarKebab = ({ kebabItems }) => {
  const [kebabIsOpen, setKebabIsOpen] = useState(false);

  const dropdownItems = kebabItems.map(
    ({ title, isDisabled, onClick }, index) => (
      <DropdownItem
        key={index}
        onClick={onClick ? onClick : () => {}}
        isDisabled={isDisabled}
      >
        {title}
      </DropdownItem>
    )
  );

  return (
    <ToolbarItem>
      <Dropdown
        toggle={
          <KebabToggle onToggle={() => setKebabIsOpen((prev) => !prev)} />
        }
        isOpen={kebabIsOpen}
        isPlain
        dropdownItems={dropdownItems}
      />
    </ToolbarItem>
  );
}
Example #5
Source File: user-dropdown.js    From ibutsu-server with MIT License 6 votes vote down vote up
render() {
    const dropdownItems = [
      <DropdownItem key="profile" component={<Link to="/profile">Profile</Link>} />,
      <DropdownItem key="logout" component="button" onClick={this.logout}>Logout</DropdownItem>
    ];
    if (this.state.isSuperAdmin) {
      dropdownItems.splice(1, 0, <DropdownItem key="admin" component={<Link to="/admin">Administration</Link>} />);
    }
    return (
      <Dropdown
        onSelect={this.onDropdownSelect}
        toggle={
          <DropdownToggle
            id="user-dropdown-toggle"
            onToggle={this.onDropdownToggle}
            toggleIndicator={CaretDownIcon}
            icon={<UserIcon />}
            isPlain={true}
          >
            {this.state.displayName}
          </DropdownToggle>
        }
        isOpen={this.state.isDropdownOpen}
        dropdownItems={dropdownItems}
      />
    );
  }
Example #6
Source File: classification-dropdown.js    From ibutsu-server with MIT License 6 votes vote down vote up
render() {
    const testResult = this.state.testResult;
    return (
      <Dropdown
        toggle={<DropdownToggle onToggle={this.onClassificationToggle}>{CLASSIFICATION[testResult.metadata && testResult.metadata.classification] || '(unset)'}</DropdownToggle>}
        onSelect={this.onClassificationSelect}
        isOpen={this.state.isClassificationOpen}
        dropdownItems={Object.keys(CLASSIFICATION).map((key) => <DropdownItem key={key} value={key}>{CLASSIFICATION[key]}</DropdownItem>)}
      />
    )
  }
Example #7
Source File: classification-dropdown.js    From ibutsu-server with MIT License 6 votes vote down vote up
render() {
    const { selectedResults } = this.props;
    return (
      <Dropdown
        toggle={<DropdownToggle isDisabled={selectedResults.length === 0} onToggle={this.onClassificationToggle}>{'Classify Selected Failures'}</DropdownToggle>}
        onSelect={this.onClassificationSelect}
        isOpen={this.state.isClassificationOpen}
        dropdownItems={Object.keys(CLASSIFICATION).map((key) => <DropdownItem key={key} value={key}>{CLASSIFICATION[key]}</DropdownItem>)}
      />
    )
  }
Example #8
Source File: WirelessEssidSelect.js    From cockpit-wicked with GNU General Public License v2.0 5 votes vote down vote up
WirelessEssidSelect = ({ essid, setEssid, iface }) => {
    const [isOpen, setIsOpen] = useState(false);
    const [essidList, setEssidList] = useState(undefined);

    const refreshList = (name) => {
        fetchEssidList(name)
                .then(result => {
                    const list = [...new Set([...result])];
                    setEssidList(list.sort());
                })
                .catch(console.error);
    };

    const onToggle = isOpen => {
        if (isOpen) {
            setEssidList(undefined);
            refreshList(iface.name);
        }

        setIsOpen(isOpen);
    };

    const onSelect = (selection) => {
        setEssid(selection);
        setIsOpen(false);
    };

    const renderOptions = () => {
        if (!essidList) {
            return [
                <DropdownItem isDisabled key="scanning" icon={<Spinner size="md" />}>
                    {_("Scanning...")}
                </DropdownItem>
            ];
        }

        if (essidList.length === 0) {
            return [
                <DropdownItem isDisabled key="no-networks-found" icon={<ExclamationIcon />}>
                    {_("No networks found")}
                </DropdownItem>
            ];
        }

        return essidList.map(value => <DropdownItem key={value} onClick={() => onSelect(value)}>{value}</DropdownItem>);
    };

    return (
        <InputGroup>
            <TextInput id="essid" value={essid} onChange={setEssid} type="text" aria-label="Essid" />
            <Dropdown
              position={DropdownPosition.right}
              isOpen={isOpen}
              dropdownItems={renderOptions()}
              toggle={
                  <DropdownToggle id="essid-scanned-list" toggleIndicator={null} onToggle={onToggle} aria-label="Essid scanned list">
                      <SearchIcon />
                  </DropdownToggle>
              }
            />
        </InputGroup>
    );
}
Example #9
Source File: ImageDetailActions.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
ImageActions = ({ imageData, openUpdateWizard }) => {
  const [isOpen, setIsOpen] = useState(false);

  const dropdownItems = [
    <DropdownItem href={imageData?.Installer?.ImageBuildISOURL} key="link">
      <Text className="force-text-black">Download</Text>
    </DropdownItem>,
  ];

  const handleToggle = (isOpen) => setIsOpen(isOpen);

  const handleSelect = () => {
    setIsOpen((prevState) => !prevState);
  };

  const handleUpdate = () => {
    openUpdateWizard(imageData.ID);
  };

  return (
    <>
      <SplitItem>
        <Button onClick={handleUpdate} variant="secondary">
          Update
        </Button>
        {imageData?.Installer?.ImageBuildISOURL ? (
          <Dropdown
            position="right"
            onSelect={handleSelect}
            toggle={
              <KebabToggle onToggle={handleToggle} id="image-detail-kebab" />
            }
            isOpen={isOpen}
            isPlain
            dropdownItems={dropdownItems}
          />
        ) : null}
      </SplitItem>
    </>
  );
}
Example #10
Source File: BulkSelect.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
BulkSelect = ({
  checkedRows,
  handleBulkSelect,
  handlePageSelect,
  handleNoneSelect,
  displayedRowsLength,
}) => {
  const isAllSelected = checkedRows.length === displayedRowsLength;
  const isPartiallySelected = checkedRows.length > 0 ? null : false;
  const [selectAllToggle, setSelectAllToggle] = useState(false);

  return (
    <>
      <ToolbarItem variant="bulk-select">
        <Dropdown
          toggle={
            <DropdownToggle
              id="stacked-example-toggle"
              splitButtonItems={[
                <DropdownToggleCheckbox
                  id="example-checkbox-2"
                  key="split-checkbox"
                  aria-label="Select all"
                  isChecked={isAllSelected ? true : isPartiallySelected}
                  onChange={isAllSelected ? handleNoneSelect : handlePageSelect}
                >
                  {checkedRows.length > 0 && `${checkedRows.length} selected`}
                </DropdownToggleCheckbox>,
              ]}
              onToggle={() => setSelectAllToggle((prevState) => !prevState)}
            />
          }
          isOpen={selectAllToggle}
          dropdownItems={[
            <DropdownItem key="all" onClick={handleBulkSelect}>
              Select all
            </DropdownItem>,
            <DropdownItem
              key="page"
              onClick={handlePageSelect}
              isDisabled={isAllSelected}
            >
              Select page
            </DropdownItem>,
            <DropdownItem
              key="none"
              onClick={handleNoneSelect}
              isDisabled={checkedRows.length === 0}
            >
              Select none
            </DropdownItem>,
          ]}
        />
      </ToolbarItem>
    </>
  );
}
Example #11
Source File: certificateActions.jsx    From cockpit-certificates with GNU Lesser General Public License v2.1 5 votes vote down vote up
CertificateActions = ({ cas, certs, cert, certPath, addAlert, appOnValueChanged, idPrefix }) => {
    const [dropdownOpen, setDropdownOpen] = useState(false);
    const [showRemoveModal, setShowRemoveModal] = useState(false);
    const [showResubmitModal, setShowResubmitModal] = useState(false);

    const dropdownItems = [
        <DropdownItem
            key={`${idPrefix}-resubmit`}
            id={`${idPrefix}-resubmit`}
            onClick={() => setShowResubmitModal(true)}>
            {_("Resubmit")}
        </DropdownItem>,
        <DropdownItem className="pf-m-danger"
            key={`${idPrefix}-remove`}
            id={`${idPrefix}-remove`}
            onClick={() => setShowRemoveModal(true)}>
            {_("Remove")}
        </DropdownItem>,
    ];

    return (
        <>
            <Dropdown onSelect={() => setDropdownOpen(!dropdownOpen)}
                id={`${idPrefix}-action-kebab`}
                toggle={
                    <KebabToggle key={`${idPrefix}-action-kebab-toggle`}
                        onToggle={() => setDropdownOpen(!dropdownOpen)} />
                }
                isOpen={dropdownOpen}
                position="right"
                dropdownItems={dropdownItems}
                isPlain />

            {showRemoveModal &&
                <RemoveModal onClose={() => setShowRemoveModal(false)}
                    certs={certs}
                    cert={cert}
                    certPath={certPath}
                    addAlert={addAlert}
                    appOnValueChanged={appOnValueChanged}
                    idPrefix={idPrefix} />}
            {showResubmitModal &&
                <ResubmitCertificateModal onClose={() => setShowResubmitModal(false)}
                    cas={cas}
                    addAlert={addAlert}
                    cert={cert}
                    certPath={certPath} />}
        </>
    );
}
Example #12
Source File: Recommendation.js    From ocp-advisor-frontend with Apache License 2.0 4 votes vote down vote up
Recommendation = ({ rule, ack, clusters, match }) => {
  const intl = useIntl();
  const dispatch = useDispatch();
  const notify = (data) => dispatch(addNotification(data));
  const recId = match.params.recommendationId;
  const [disableRuleModalOpen, setDisableRuleModalOpen] = useState(false);
  const [actionsDropdownOpen, setActionsDropdownOpen] = useState(false);
  const [viewSystemsModalOpen, setViewSystemsModalOpen] = useState(false);

  // rule's info
  const {
    isError,
    isUninitialized,
    isLoading,
    isFetching,
    isSuccess,
    data,
    refetch,
  } = rule;
  // justification note, last time acknowledged, etc.
  const { data: ackData, isFetching: ackIsFetching, refetch: refetchAck } = ack;
  const ruleDate = new Date(ackData?.updated_at || ackData?.created_at);
  // affected and acked clusters lists
  const {
    data: clustersData,
    isFetching: clustersIsFetching,
    refetch: refetchClusters,
  } = clusters;

  const content =
    isSuccess && data ? adjustOCPRule(data.content, recId) : undefined;
  const ackedClusters =
    !clustersIsFetching && clustersData ? clustersData.disabled : undefined;

  const afterDisableFn = async () => {
    refetch();
    refetchAck();
    refetchClusters();
  };

  const handleModalToggle = (disableRuleModalOpen) => {
    setDisableRuleModalOpen(disableRuleModalOpen);
  };

  const enableRecForHosts = async ({ uuids }) => {
    try {
      const requests = uuids.map((uuid) =>
        enableRuleForCluster({ uuid, recId })
      );
      await Promise.all(requests);
      refetch();
      refetchAck();
      refetchClusters();
      notify({
        variant: 'success',
        timeout: true,
        dismissable: true,
        title: intl.formatMessage(messages.recSuccessfullyEnabledForCluster),
      });
    } catch (error) {
      notify({
        variant: 'danger',
        dismissable: true,
        title: intl.formatMessage(messages.error),
        description: `${error}`,
      });
    }
  };

  const enableRule = async (rule) => {
    try {
      await Delete(`${BASE_URL}/v2/ack/${rule.data.content.rule_id}/`);
      notify({
        variant: 'success',
        timeout: true,
        dismissable: true,
        title: intl.formatMessage(messages.recSuccessfullyEnabled),
      });
      refetch();
    } catch (error) {
      handleModalToggle(false);
      notify({
        variant: 'danger',
        dismissable: true,
        title: intl.formatMessage(messages.error),
        description: `${error}`,
      });
    }
  };

  const messagesValues = useMemo(
    () => (content ? mapContentToValues(intl, content) : {}),
    [intl, content]
  );

  return (
    <React.Fragment>
      {viewSystemsModalOpen && (
        <ViewHostAcks
          handleModalToggle={(toggleModal) =>
            setViewSystemsModalOpen(toggleModal)
          }
          isModalOpen={viewSystemsModalOpen}
          clusters={clusters}
          afterFn={() => refetchClusters()}
          recId={recId}
        />
      )}
      {disableRuleModalOpen && (
        <DisableRule
          handleModalToggle={handleModalToggle}
          isModalOpen={disableRuleModalOpen}
          rule={content}
          afterFn={afterDisableFn}
        />
      )}
      <PageHeader className="pageHeaderOverride">
        <Breadcrumbs current={content?.description || recId} />
      </PageHeader>
      {(isUninitialized || isLoading || isFetching) && (
        <Main>
          <Loading />
        </Main>
      )}
      {isError && (
        <Main>
          <ErrorState />
        </Main>
      )}
      {!(isUninitialized || isLoading || isFetching) && isSuccess && (
        <React.Fragment>
          <Main className="pf-m-light pf-u-pt-sm">
            <RuleDetails
              messages={formatMessages(
                intl,
                RuleDetailsMessagesKeys,
                messagesValues
              )}
              product={AdvisorProduct.ocp}
              rule={content}
              isDetailsPage
              header={
                <React.Fragment>
                  <PageHeaderTitle
                    title={
                      <React.Fragment>
                        {content.description} <RuleLabels rule={content} />
                      </React.Fragment>
                    }
                  />
                  <p>
                    {intl.formatMessage(messages.rulesDetailsPubishdate, {
                      date: (
                        <DateFormat
                          date={new Date(content.publish_date)}
                          type="onlyDate"
                        />
                      ),
                    })}
                    {content.tags &&
                      (Array.isArray(content.tags) ? (
                        <LabelGroup
                          className="categoryLabels"
                          numLabels={1}
                          isCompact
                        >
                          {content.tags.reduce((labels, tag) => {
                            if (RULE_CATEGORIES[tag]) {
                              labels.push(
                                <Label
                                  key={`label-${tag}`}
                                  color="blue"
                                  isCompact
                                >
                                  {
                                    FILTER_CATEGORIES.category.values[
                                      RULE_CATEGORIES[tag] - 1
                                    ].label
                                  }
                                </Label>
                              );
                            }
                            return labels;
                          }, [])}
                        </LabelGroup>
                      ) : (
                        <Label isCompact>{content.tags}</Label>
                      ))}
                  </p>
                </React.Fragment>
              }
              onVoteClick={async (rule, rating) =>
                await Post(`${BASE_URL}/v2/rating`, {}, { rule, rating })
              }
            >
              <Flex>
                <FlexItem align={{ default: 'alignRight' }}>
                  <Dropdown
                    className="ins-c-rec-details__actions_dropdown"
                    onSelect={() =>
                      setActionsDropdownOpen(!actionsDropdownOpen)
                    }
                    position="right"
                    ouiaId="actions"
                    toggle={
                      <DropdownToggle
                        onToggle={(actionsDropdownOpen) =>
                          setActionsDropdownOpen(actionsDropdownOpen)
                        }
                        toggleIndicator={CaretDownIcon}
                      >
                        {intl.formatMessage(messages.actions)}
                      </DropdownToggle>
                    }
                    isOpen={actionsDropdownOpen}
                    dropdownItems={
                      content?.disabled
                        ? [
                            <DropdownItem
                              key="link"
                              ouiaId="enable"
                              onClick={() => {
                                enableRule(rule);
                              }}
                            >
                              {intl.formatMessage(messages.enableRule)}
                            </DropdownItem>,
                          ]
                        : [
                            <DropdownItem
                              key="link"
                              ouiaId="disable"
                              onClick={() => {
                                handleModalToggle(true);
                              }}
                            >
                              {intl.formatMessage(messages.disableRule)}
                            </DropdownItem>,
                          ]
                    }
                  />
                </FlexItem>
              </Flex>
            </RuleDetails>
          </Main>
          <Main>
            <React.Fragment>
              {(content?.hosts_acked_count ||
                ackedClusters?.length > 0 ||
                content?.disabled) && (
                <Card className="cardOverride" ouiaId="hosts-acked">
                  <CardHeader>
                    <Title headingLevel="h4" size="xl">
                      <BellSlashIcon size="sm" />
                      &nbsp;
                      {intl.formatMessage(
                        (content?.hosts_acked_count ||
                          ackedClusters?.length > 0) &&
                          !content?.disabled
                          ? messages.ruleIsDisabledForClusters
                          : messages.ruleIsDisabled
                      )}
                    </Title>
                  </CardHeader>
                  <CardBody>
                    {(content?.hosts_acked_count ||
                      ackedClusters?.length > 0) &&
                    !content?.disabled ? (
                      <React.Fragment>
                        {intl.formatMessage(
                          messages.ruleIsDisabledForClustersBody,
                          {
                            clusters: ackedClusters?.length,
                          }
                        )}
                        {!clustersIsFetching && ackedClusters?.length > 0 ? (
                          <React.Fragment>
                            &nbsp;
                            <Button
                              isInline
                              variant="link"
                              onClick={() => setViewSystemsModalOpen(true)}
                              ouiaId="view-clusters"
                            >
                              {intl.formatMessage(messages.viewClusters)}
                            </Button>
                          </React.Fragment>
                        ) : (
                          <OneLineLoader />
                        )}
                      </React.Fragment>
                    ) : (
                      !ackIsFetching &&
                      ackData && (
                        <React.Fragment>
                          {ackData?.justification
                            ? intl.formatMessage(
                                messages.ruleIsDisabledWithJustificaiton,
                                {
                                  date: (
                                    <span>
                                      <DateFormat
                                        date={ruleDate}
                                        type="onlyDate"
                                      />
                                    </span>
                                  ),
                                  reason: ackData.justification,
                                }
                              )
                            : intl.formatMessage(
                                messages.ruleIsDisabledWithoutJustificaiton,
                                {
                                  date: (
                                    <span>
                                      <DateFormat
                                        date={ruleDate}
                                        type="onlyDate"
                                      />
                                    </span>
                                  ),
                                }
                              )}
                        </React.Fragment>
                      )
                    )}
                  </CardBody>
                  <CardFooter>
                    {(content?.hosts_acked_count ||
                      ackedClusters?.length > 0) &&
                    !content?.disabled ? (
                      !clustersIsFetching && ackedClusters ? (
                        <Button
                          isInline
                          variant="link"
                          onClick={() =>
                            enableRecForHosts({
                              uuids: ackedClusters.map((c) => c.cluster_id),
                            })
                          }
                          ouiaId="enable"
                        >
                          {intl.formatMessage(messages.enableRuleForClusters)}
                        </Button>
                      ) : (
                        <OneLineLoader />
                      )
                    ) : (
                      <Button
                        isInline
                        variant="link"
                        onClick={() => enableRule(rule)}
                        ouiaId="enable"
                      >
                        {intl.formatMessage(messages.enableRule)}
                      </Button>
                    )}
                  </CardFooter>
                </Card>
              )}
              {!content?.disabled && (
                <React.Fragment>
                  <Title className="titleOverride" headingLevel="h3" size="2xl">
                    {intl.formatMessage(messages.affectedClusters)}
                  </Title>
                  <AffectedClustersTable
                    query={clusters}
                    rule={content}
                    afterDisableFn={afterDisableFn}
                  />
                </React.Fragment>
              )}
              {content?.disabled && (
                <MessageState
                  icon={BellSlashIcon}
                  title={intl.formatMessage(messages.ruleIsDisabled)}
                  text={intl.formatMessage(messages.ruleIsDisabledBody)}
                />
              )}
            </React.Fragment>
          </Main>
        </React.Fragment>
      )}
    </React.Fragment>
  );
}
Example #13
Source File: test-history.js    From ibutsu-server with MIT License 4 votes vote down vote up
render() {
    const {
      columns,
      rows,
      onlyFailures,
      historySummary,
      dropdownSelection
    } = this.state;
    const pagination = {
      pageSize: this.state.pageSize,
      page: this.state.page,
      totalItems: this.state.totalItems
    }
    const dropdownValues = Object.assign({
      "1 Week": 0.25,
      "2 Weeks": 0.5,
      "1 Month": 1.0,
      "2 Months": 2.0,
      "3 Months": 3.0,
      "5 Months": 5.0
    })
    let dropdownItems = [];
    Object.keys(dropdownValues).forEach(key => {
      dropdownItems.push(
        <DropdownItem key={key} value={dropdownValues[key]} autoFocus={key === dropdownSelection}>
          {key}
        </DropdownItem>
      )
    });

    return (
      <Card className="pf-u-mt-lg">
        <CardHeader>
          <Flex style={{ width: '100%' }}>
            <FlexItem grow={{ default: 'grow' }}>
              <TextContent>
                <Text component="h2" className="pf-c-title pf-m-xl">
                  Test History
                </Text>
              </TextContent>
            </FlexItem>
            <FlexItem>
              <TextContent>
                <Checkbox id="only-failures" label="Only show failures/errors" isChecked={onlyFailures} aria-label="only-failures-checkbox" onChange={this.onFailuresCheck}/>
              </TextContent>
            </FlexItem>
            <FlexItem>
              <Dropdown
                toggle={<DropdownToggle isDisabled={false} onToggle={this.onDropdownToggle}>Time range</DropdownToggle>}
                onSelect={this.onDropdownSelect}
                isOpen={this.state.isDropdownOpen}
                dropdownItems={dropdownItems}
              />
            </FlexItem>
            <FlexItem>
              <Button variant="secondary" onClick={this.refreshResults}>Refresh results</Button>
            </FlexItem>
          </Flex>
        </CardHeader>
        <CardBody>
          <FilterTable
            columns={columns}
            rows={rows}
            pagination={pagination}
            isEmpty={this.state.isEmpty}
            isError={this.state.isError}
            onCollapse={this.onCollapse}
            onSetPage={this.setPage}
            onSetPageSize={this.pageSizeSelect}
            canSelectAll={false}
            variant={TableVariant.compact}
            activeFilters={this.state.filters}
            filters={[
              <Text key="summary" component="h4">
                Summary:&nbsp;
                {historySummary &&
                <RunSummary summary={historySummary}/>
                }
              </Text>,
              <Text key="last-passed" component="h4">Last passed:&nbsp;{this.state.lastPassedDate}</Text>,
            ]}
            onRemoveFilter={this.removeFilter}
            hideFilters={["project_id", "result", "test_id"]}
          />
        </CardBody>
      </Card>
    );
  }
Example #14
Source File: AccessRequestsTable.js    From access-requests-frontend with Apache License 2.0 4 votes vote down vote up
AccessRequestsTable = ({ isInternal }) => {
  const columns = isInternal
    ? [
        'Request ID',
        'Account number',
        'Start date',
        'End date',
        'Created',
        'Status',
      ]
    : [
        'Request ID',
        'First name',
        'Last name',
        'Start date',
        'End date',
        'Created',
        'Decision',
      ];

  // Sorting
  const [activeSortIndex, setActiveSortIndex] = React.useState(
    isInternal ? 4 : 5
  );
  const [activeSortDirection, setActiveSortDirection] = React.useState('desc');
  const onSort = (_ev, index, direction) => {
    setActiveSortIndex(index);
    setActiveSortDirection(direction);
  };

  // Pagination
  const [page, setPage] = React.useState(1);
  const [perPage, setPerPage] = React.useState(20);
  const AccessRequestsPagination = ({ id }) => (
    <Pagination
      itemCount={numRows}
      perPage={perPage}
      page={page}
      onSetPage={(_ev, pageNumber) => setPage(pageNumber)}
      id={'access-requests-table-pagination-' + id}
      variant={id}
      perPageOptions={[5, 10, 20, 50].map((n) => ({ title: n, value: n }))}
      onPerPageSelect={(_ev, perPage) => {
        setPage(1);
        setPerPage(perPage);
      }}
      isCompact={id === 'top'}
    />
  );

  AccessRequestsPagination.propTypes = {
    id: PropTypes.string,
  };

  // Filtering
  const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
  const [filterColumn, setFilterColumn] = React.useState(
    columns[isInternal ? 1 : 6]
  );
  const [isSelectOpen, setIsSelectOpen] = React.useState(false);
  const [statusSelections, setStatusSelections] = React.useState([]);

  // Harder than it needs to be to match rest of RBAC which doesn't wait
  // for user to click a button or press enter.
  const [accountFilter, setAccountFilter] = React.useState('');
  const [filtersDirty, setFiltersDirty] = React.useState(false);
  const hasFilters = statusSelections.length > 0 || accountFilter;

  // Row loading
  const [isLoading, setIsLoading] = React.useState(true);
  const [numRows, setNumRows] = React.useState(0);
  const [rows, setRows] = React.useState([]);
  const dispatch = useDispatch();
  const fetchAccessRequests = () => {
    setIsLoading(true);
    const listUrl = new URL(
      `${window.location.origin}${API_BASE}/cross-account-requests/`
    );

    isInternal
      ? listUrl.searchParams.append('query_by', 'user_id')
      : listUrl.searchParams.append('query_by', 'target_account');

    listUrl.searchParams.append('offset', (page - 1) * perPage);
    listUrl.searchParams.append('limit', perPage);
    // https://github.com/RedHatInsights/insights-rbac/blob/master/rbac/api/cross_access/view.py
    if (accountFilter) {
      listUrl.searchParams.append('account', accountFilter);
    }
    if (statusSelections.length > 0) {
      listUrl.searchParams.append('status', statusSelections.join(','));
    }
    const orderBy = `${activeSortDirection === 'desc' ? '-' : ''}${columns[
      activeSortIndex
    ]
      .toLowerCase()
      .replace(' ', '_')}`;
    listUrl.searchParams.append('order_by', orderBy);

    apiInstance
      .get(listUrl.href, { headers: { Accept: 'application/json' } })
      .then((res) => {
        setNumRows(res.meta.count);
        setRows(
          res.data.map((d) =>
            isInternal
              ? [
                  d.request_id,
                  d.target_account,
                  d.start_date,
                  d.end_date,
                  d.created,
                  d.status,
                ]
              : [
                  d.request_id,
                  d.first_name,
                  d.last_name,
                  d.start_date,
                  d.end_date,
                  d.created,
                  d.status,
                ]
          )
        );
        setIsLoading(false);
      })
      .catch((err) => {
        setIsLoading(false);
        dispatch(
          addNotification({
            variant: 'danger',
            title: 'Could not list access requests',
            description: err.message,
          })
        );
      });
  };
  const debouncedAccountFilter = useDebounce(accountFilter, 400);
  React.useEffect(() => {
    fetchAccessRequests();
  }, [
    debouncedAccountFilter,
    statusSelections,
    activeSortIndex,
    activeSortDirection,
    perPage,
    page,
  ]);

  // Modal actions
  const [openModal, setOpenModal] = React.useState({ type: null });
  const onModalClose = (isChanged) => {
    setOpenModal({ type: null });
    if (isChanged) {
      fetchAccessRequests();
    }
  };
  const modals = (
    <React.Fragment>
      {openModal.type === 'cancel' && (
        <CancelRequestModal
          requestId={openModal.requestId}
          onClose={onModalClose}
        />
      )}
      {['edit', 'create'].includes(openModal.type) && (
        <EditRequestModal
          variant={openModal.type}
          requestId={openModal.requestId}
          onClose={onModalClose}
        />
      )}
    </React.Fragment>
  );

  // Rendering
  const createButton = isInternal && (
    <Button variant="primary" onClick={() => setOpenModal({ type: 'create' })}>
      Create request
    </Button>
  );
  if (rows.length === 0 && !isLoading && !filtersDirty) {
    return (
      <Bullseye style={{ height: 'auto' }} className="pf-u-mt-lg">
        <EmptyState variant="large">
          <EmptyStateIcon icon={PlusCircleIcon} />
          <Title headingLevel="h3" size="lg">
            {isInternal ? 'No access requests' : 'You have no access requests'}
          </Title>
          <EmptyStateBody>
            {isInternal
              ? 'Click the button below to create an access request.'
              : 'You have no pending Red Hat access requests.'}
          </EmptyStateBody>
          {createButton}
        </EmptyState>
        {modals}
      </Bullseye>
    );
  }

  const selectLabelId = 'filter-status';
  const selectPlaceholder = `Filter by ${uncapitalize(
    columns[columns.length - 1]
  )}`;
  const clearFiltersButton = (
    <Button
      variant="link"
      onClick={() => {
        setStatusSelections([]);
        setAccountFilter('');
        setPage(1);
      }}
    >
      Clear filters
    </Button>
  );
  const toolbar = (
    <Toolbar id="access-requests-table-toolbar">
      <ToolbarContent>
        <ToolbarItem>
          <InputGroup>
            <Dropdown
              isOpen={isDropdownOpen}
              onSelect={(ev) => {
                setIsDropdownOpen(false);
                setFilterColumn(ev.target.value);
                setIsSelectOpen(false);
                setFiltersDirty(true);
              }}
              toggle={
                <DropdownToggle
                  onToggle={(isOpen) => setIsDropdownOpen(isOpen)}
                >
                  <FilterIcon /> {filterColumn}
                </DropdownToggle>
              }
              // https://marvelapp.com/prototype/257je526/screen/74764732
              dropdownItems={(isInternal ? [1, 5] : [6])
                .map((i) => columns[i])
                .map((colName) => (
                  // Filterable columns are RequestID, AccountID, and Status
                  <DropdownItem
                    key={colName}
                    value={colName}
                    component="button"
                  >
                    {capitalize(colName)}
                  </DropdownItem>
                ))}
            />
            {['Status', 'Decision'].includes(filterColumn) && (
              <React.Fragment>
                <span id={selectLabelId} hidden>
                  {selectPlaceholder}
                </span>
                <Select
                  aria-labelledby={selectLabelId}
                  variant="checkbox"
                  aria-label="Select statuses"
                  onToggle={(isOpen) => setIsSelectOpen(isOpen)}
                  onSelect={(_ev, selection) => {
                    setFiltersDirty(true);
                    if (statusSelections.includes(selection)) {
                      setStatusSelections(
                        statusSelections.filter((s) => s !== selection)
                      );
                    } else {
                      setStatusSelections([...statusSelections, selection]);
                    }
                    setPage(1);
                  }}
                  isOpen={isSelectOpen}
                  selections={Array.from(statusSelections)}
                  isCheckboxSelectionBadgeHidden
                  placeholderText={selectPlaceholder}
                >
                  {statuses.map((status) => (
                    <SelectOption key={status} value={status}>
                      {capitalize(status)}
                    </SelectOption>
                  ))}
                </Select>
              </React.Fragment>
            )}
            {filterColumn === 'Account number' && (
              <form
                style={{ display: 'flex' }}
                onSubmit={(ev) => ev.preventDefault()}
              >
                <TextInput
                  name={`${filterColumn}-filter`}
                  id={`${filterColumn}-filter`}
                  type="search"
                  iconVariant="search"
                  placeholder={`Filter by ${uncapitalize(filterColumn)}`}
                  aria-label={`${filterColumn} search input`}
                  value={accountFilter}
                  onChange={(val) => {
                    setAccountFilter(val), setFiltersDirty(true), setPage(1);
                  }}
                />
              </form>
            )}
          </InputGroup>
        </ToolbarItem>
        <ToolbarItem>{createButton}</ToolbarItem>
        <ToolbarItem variant="pagination" align={{ default: 'alignRight' }}>
          <AccessRequestsPagination id="top" />
        </ToolbarItem>
      </ToolbarContent>
      <ToolbarContent>
        <ChipGroup categoryName="Status">
          {statusSelections.map((status) => (
            <Chip
              key={status}
              onClick={() => {
                setStatusSelections(
                  statusSelections.filter((s) => s !== status)
                );
                setPage(1);
              }}
            >
              {status}
            </Chip>
          ))}
        </ChipGroup>
        {accountFilter && (
          <ChipGroup categoryName="Account number">
            <Chip
              onClick={() => {
                setAccountFilter(''), setPage(1);
              }}
            >
              {accountFilter}
            </Chip>
          </ChipGroup>
        )}
        {hasFilters && clearFiltersButton}
      </ToolbarContent>
    </Toolbar>
  );
  function getColumnWidth(columnIndex) {
    if (isInternal) {
      return columnIndex === 0 ? 30 : 15;
    }

    return [0, 6].includes(columnIndex) ? 20 : 10;
  }
  const { url } = useRouteMatch();
  const table = (
    <TableComposable aria-label="Access requests table" variant="compact">
      <Thead>
        <Tr>
          {columns.map((column, columnIndex) => (
            <Th
              key={columnIndex}
              {...(!column.includes('name') &&
                column !== 'Decision' && {
                  sort: {
                    sortBy: {
                      index: activeSortIndex,
                      direction: activeSortDirection,
                    },
                    onSort,
                    columnIndex,
                  },
                })}
              width={getColumnWidth(columnIndex)}
            >
              {column}
            </Th>
          ))}
          {isInternal && <Th />}
        </Tr>
      </Thead>
      <Tbody>
        {isLoading
          ? [...Array(rows.length || perPage).keys()].map((i) => (
              <Tr key={i}>
                {columns.map((name, j) => (
                  <Td key={j} dataLabel={name}>
                    <div
                      style={{ height: '30px' }}
                      className="ins-c-skeleton ins-c-skeleton__md"
                    >
                      {' '}
                    </div>
                  </Td>
                ))}
              </Tr>
            ))
          : rows.map((row, rowIndex) => (
              <Tr key={rowIndex}>
                <Td dataLabel={columns[0]}>
                  <Link to={`${url}${url.endsWith('/') ? '' : '/'}${row[0]}`}>
                    {row[0]}
                  </Link>
                </Td>
                <Td dataLabel={columns[1]}>{row[1]}</Td>
                <Td dataLabel={columns[2]}>{row[2]}</Td>
                <Td dataLabel={columns[3]}>{row[3]}</Td>
                <Td dataLabel={columns[4]}>{row[4]}</Td>
                {isInternal ? (
                  <Td dataLabel={columns[5]}>
                    <StatusLabel
                      requestId={row[0]}
                      status={row[5]}
                      onLabelClick={() => {
                        setStatusSelections([
                          ...statusSelections.filter((s) => s !== status),
                          status,
                        ]);
                        setPage(1);
                      }}
                      hideActions
                    />
                  </Td>
                ) : (
                  <Td dataLabel={columns[5]}>{row[5]}</Td>
                )}
                {isInternal ? (
                  // Different actions based on status
                  <Td
                    actions={getInternalActions(row[5], row[0], setOpenModal)}
                  />
                ) : (
                  <Td dataLabel={columns[6]}>
                    <StatusLabel requestId={row[0]} status={row[6]} />
                  </Td>
                )}
              </Tr>
            ))}
        {rows.length === 0 && hasFilters && (
          <Tr>
            <Td colSpan={columns.length}>
              <EmptyState variant="small">
                <EmptyStateIcon icon={SearchIcon} />
                <Title headingLevel="h2" size="lg">
                  No matching requests found
                </Title>
                <EmptyStateBody>
                  No results match the filter criteria. Remove all filters or
                  clear all filters to show results.
                </EmptyStateBody>
                {clearFiltersButton}
              </EmptyState>
            </Td>
          </Tr>
        )}
      </Tbody>
    </TableComposable>
  );

  return (
    <React.Fragment>
      {toolbar}
      {table}
      <AccessRequestsPagination id="bottom" />
      {modals}
    </React.Fragment>
  );
}
Example #15
Source File: ClusterHeader.js    From ocp-advisor-frontend with Apache License 2.0 4 votes vote down vote up
ClusterHeader = ({ clusterId, clusterData, clusterInfo }) => {
  const location = window.location;
  const [isOpen, setIsOpen] = useState(false);
  const intl = useIntl();
  // subscribe to the cluster data query
  const {
    isUninitialized: isUninitializedCluster,
    isFetching: isFetchingCluster,
    data: cluster,
  } = clusterData;

  const {
    isUninitialized: isUninitializedInfo,
    isFetching: isFetchingInfo,
    data: info,
  } = clusterInfo;

  const redirectOCM = (clusterId) => {
    location.assign(
      location.origin +
        (location.pathname.includes('beta') ? `/beta` : '') +
        `/openshift/details/${clusterId}`
    );
  };

  const dropDownItems = [
    <DropdownItem key="link" onClick={() => redirectOCM(clusterId)}>
      <snap>{intl.formatMessage(messages.clusterDetailsRedirect)}</snap>
    </DropdownItem>,
  ];

  return (
    <Grid id="cluster-header" md={12} hasGutter>
      <GridItem span={8}>
        <Title
          size="2xl"
          headingLevel="h1"
          id="cluster-header-title"
          ouiaId="cluster-name"
        >
          {isUninitializedInfo || isFetchingInfo ? (
            <Skeleton size="sm" />
          ) : (
            info?.display_name || clusterId
          )}
        </Title>
      </GridItem>
      <GridItem span={4} id="cluster-header-dropdown">
        <Dropdown
          position="right"
          onSelect={() => setIsOpen(!isOpen)}
          autoFocus={false}
          isOpen={isOpen}
          toggle={
            <DropdownToggle
              id="toggle-id-2"
              onToggle={(isOpen) => setIsOpen(isOpen)}
            >
              {intl.formatMessage(messages.dropDownActionSingleCluster)}
            </DropdownToggle>
          }
          dropdownItems={dropDownItems}
        />
      </GridItem>
      <GridItem>
        <Stack>
          <StackItem id="cluster-header-uuid">
            <span>UUID:</span> <span>{clusterId}</span>
          </StackItem>
          <StackItem id="cluster-header-last-seen">
            <span>{intl.formatMessage(messages.lastSeen)}: </span>
            <span>
              {isUninitializedCluster || isFetchingCluster ? (
                <OneLineLoader />
              ) : cluster?.report?.meta?.last_checked_at ? (
                <DateFormat
                  date={cluster?.report?.meta?.last_checked_at}
                  type="exact"
                />
              ) : (
                intl.formatMessage(messages.unknown)
              )}
            </span>
          </StackItem>
        </Stack>
      </GridItem>
    </Grid>
  );
}
Example #16
Source File: GroupsDetail.js    From edge-frontend with Apache License 2.0 4 votes vote down vote up
GroupsDetail = () => {
  const dispatch = useDispatch();
  const params = useParams();
  const history = useHistory();
  const { groupId } = params;

  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
  const [isAddModalOpen, setIsAddModalOpen] = useState(false);
  const [removeModal, setRemoveModal] = useState({
    isOpen: false,
    name: '',
    deviceId: null,
  });
  const [updateModal, setUpdateModal] = useState({
    isOpen: false,
    deviceData: null,
    imageData: null,
  });
  const [response, fetchDevices] = useApi({
    api: getGroupById,
    id: groupId,
    tableReload: true,
  });
  const { data, isLoading, hasError } = response;
  const groupName = data?.DeviceGroup?.Name;
  const [deviceIds, getDeviceIds] = useState([]);
  const [hasModalSubmitted, setHasModalSubmitted] = useState(false);
  const [modalState, setModalState] = useState({ id: null, name: '' });
  const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
  const [isRenameModalOpen, setIsRenameModalOpen] = useState(false);

  const handleDeleteModal = (id, name) => {
    setModalState({ id, name });
    setIsDeleteModalOpen(true);
  };

  const handleRenameModal = (id, name) => {
    setModalState({ id, name });
    setIsRenameModalOpen(true);
  };

  const removeDeviceLabel = () =>
    `Do you want to remove ${
      deviceIds.length > 0
        ? `${deviceIds.length} system${deviceIds.length === 1 ? '' : 's'}`
        : `${removeModal.name}`
    } from ${groupName}?`;

  useEffect(() => {
    history.push({
      pathname: history.location.pathname,
      search: stateToUrlSearch('add_system_modal=true', isAddModalOpen),
    });
  }, [isAddModalOpen]);

  const handleSingleDeviceRemoval = () => {
    const statusMessages = {
      onSuccess: {
        title: 'Success',
        description: `${removeModal.name} has been removed successfully`,
      },
      onError: { title: 'Error', description: 'Failed to remove device' },
    };
    apiWithToast(
      dispatch,
      () => removeDeviceFromGroupById(groupId, removeModal.deviceId),
      statusMessages
    );
    setTimeout(() => setHasModalSubmitted(true), 800);
  };

  const handleBulkDeviceRemoval = () => {
    const statusMessages = {
      onSuccess: {
        title: 'Success',
        description: `${deviceIds.length} systems have been removed successfully`,
      },
      onError: { title: 'Error', description: 'failed to remove systems' },
    };
    apiWithToast(
      dispatch,
      () =>
        removeDevicesFromGroup(
          parseInt(groupId),
          deviceIds.map((device) => ({ ID: device.deviceID }))
        ),
      statusMessages
    );
    setTimeout(() => setHasModalSubmitted(true), 800);
  };

  return (
    <>
      <PageHeader className="pf-m-light">
        {groupName ? (
          <Breadcrumb>
            <BreadcrumbItem>
              <Link to={`${paths['fleet-management']}`}>Groups</Link>
            </BreadcrumbItem>
            <BreadcrumbItem>{groupName}</BreadcrumbItem>
          </Breadcrumb>
        ) : (
          <Breadcrumb isActive>
            <Skeleton width="100px" />
          </Breadcrumb>
        )}
        <Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
          <FlexItem>
            {groupName ? (
              <PageHeaderTitle title={groupName} />
            ) : (
              <Skeleton width="150px" />
            )}
          </FlexItem>
          <FlexItem>
            <Dropdown
              position={DropdownPosition.right}
              toggle={
                <DropdownToggle
                  id="image-set-details-dropdown"
                  toggleIndicator={CaretDownIcon}
                  onToggle={(newState) => setIsDropdownOpen(newState)}
                  isDisabled={false}
                >
                  Actions
                </DropdownToggle>
              }
              isOpen={isDropdownOpen}
              dropdownItems={[
                <DropdownItem
                  key="delete-device-group"
                  onClick={() => handleDeleteModal(groupId, groupName)}
                >
                  Delete group
                </DropdownItem>,
                <DropdownItem
                  key="rename-device-group"
                  onClick={() => handleRenameModal(groupId, groupName)}
                >
                  Rename group
                </DropdownItem>,
                <DropdownItem
                  key="update-all-devices"
                  isDisabled={canUpdateSelectedDevices({
                    deviceData: data?.DevicesView?.devices?.map((device) => ({
                      imageSetId: device?.ImageSetID,
                    })),
                    imageData: data?.DevicesView?.devices?.some(
                      (device) => device.ImageID
                    ),
                  })}
                  onClick={() => {
                    setIsDropdownOpen(false);
                    setUpdateModal((prevState) => ({
                      ...prevState,
                      isOpen: true,
                      deviceData: data?.DevicesView?.devices?.map((device) => ({
                        id: device?.DeviceUUID,
                        display_name:
                          device?.DeviceName === ''
                            ? 'localhost'
                            : device?.DeviceName,
                      })),
                      imageSetId: data?.DevicesView?.devices.find(
                        (device) => device.ImageSetID
                      )?.ImageSetID,
                    }));
                  }}
                >
                  Update
                </DropdownItem>,
              ]}
            />
          </FlexItem>
        </Flex>
      </PageHeader>
      <Main className="edge-devices">
        {!emptyStateNoFliters(
          isLoading,
          data?.DeviceGroup?.Devices.length,
          history
        ) ? (
          <DeviceTable
            data={data?.DevicesView?.devices || []}
            count={data?.DevicesView?.total}
            isLoading={isLoading}
            hasError={hasError}
            hasCheckbox={true}
            handleSingleDeviceRemoval={handleSingleDeviceRemoval}
            kebabItems={[
              {
                isDisabled: !(deviceIds.length > 0),
                title: 'Remove from group',
                onClick: () =>
                  setRemoveModal({
                    name: '',
                    deviceId: null,
                    isOpen: true,
                  }),
              },
              {
                isDisabled: canUpdateSelectedDevices({
                  deviceData: deviceIds,
                  imageData: deviceIds[0]?.updateImageData,
                }),
                title: 'Update selected',
                onClick: () =>
                  setUpdateModal((prevState) => ({
                    ...prevState,
                    isOpen: true,
                    deviceData: [...deviceIds],
                    imageSetId: deviceIds.find((device) => device?.imageSetId)
                      .imageSetId,
                  })),
              },
            ]}
            selectedItems={getDeviceIds}
            setRemoveModal={setRemoveModal}
            setIsAddModalOpen={setIsAddModalOpen}
            setUpdateModal={setUpdateModal}
            hasModalSubmitted={hasModalSubmitted}
            setHasModalSubmitted={setHasModalSubmitted}
            fetchDevices={fetchDevices}
            isAddSystemsView={true}
          />
        ) : (
          <Flex justifyContent={{ default: 'justifyContentCenter' }}>
            <Empty
              icon="plus"
              title="Add systems to the group"
              body="Create groups to help manage your systems more effectively."
              primaryAction={{
                text: 'Add systems',
                click: () => setIsAddModalOpen(true),
              }}
              secondaryActions={[
                {
                  type: 'link',
                  title: 'Learn more about system groups',
                  link: 'https://access.redhat.com/documentation/en-us/edge_management/2022/html-single/working_with_systems_in_the_edge_management_application/index',
                },
              ]}
            />
          </Flex>
        )}
      </Main>
      {isAddModalOpen && (
        <AddSystemsToGroupModal
          groupId={groupId}
          closeModal={() => setIsAddModalOpen(false)}
          isOpen={isAddModalOpen}
          reloadData={fetchDevices}
          groupName={data?.DeviceGroup?.Name}
        />
      )}
      {removeModal.isOpen && (
        <Modal
          isOpen={removeModal.isOpen}
          openModal={() => setRemoveModal(false)}
          title={'Remove from group'}
          submitLabel={'Remove'}
          variant="danger"
          schema={{
            fields: [
              {
                component: componentTypes.PLAIN_TEXT,
                name: 'warning-text',
                label: removeDeviceLabel(),
              },
            ],
          }}
          onSubmit={
            removeModal.deviceId
              ? handleSingleDeviceRemoval
              : handleBulkDeviceRemoval
          }
          reloadData={fetchDevices}
        />
      )}

      {updateModal.isOpen && (
        <Suspense
          fallback={
            <Bullseye>
              <Spinner />
            </Bullseye>
          }
        >
          <UpdateDeviceModal
            navigateBack={() => {
              history.push({ pathname: history.location.pathname });
              setUpdateModal((prevState) => {
                return {
                  ...prevState,
                  isOpen: false,
                };
              });
            }}
            setUpdateModal={setUpdateModal}
            updateModal={updateModal}
            refreshTable={fetchDevices}
          />
        </Suspense>
      )}
      {isDeleteModalOpen && (
        <DeleteGroupModal
          isModalOpen={isDeleteModalOpen}
          setIsModalOpen={setIsDeleteModalOpen}
          reloadData={() => history.push(paths['fleet-management'])}
          modalState={modalState}
        />
      )}
      {isRenameModalOpen && (
        <RenameGroupModal
          isModalOpen={isRenameModalOpen}
          setIsModalOpen={setIsRenameModalOpen}
          reloadData={() => fetchDevices()}
          modalState={modalState}
        />
      )}
    </>
  );
}
Example #17
Source File: AccessRequestDetailsPage.js    From access-requests-frontend with Apache License 2.0 4 votes vote down vote up
BaseAccessRequestDetailsPage = ({ isInternal }) => {
  const [request, setRequest] = React.useState();
  const { requestId } = useParams();
  const dispatch = useDispatch();
  React.useEffect(() => {
    apiInstance
      .get(
        `${API_BASE}/cross-account-requests/${requestId}/${
          isInternal ? '?query_by=user_id' : '?query_by=target_account'
        }`,
        { headers: { Accept: 'application/json' } }
      )
      .then((res) => {
        if (res.errors) {
          throw Error(res.errors.map((e) => e.detail).join('\n'));
        }
        setRequest(res);
      })
      .catch((err) => {
        dispatch(
          addNotification({
            variant: 'danger',
            title: 'Could not load access request',
            description: err.message,
          })
        );
      });
  }, []);

  // Modal actions
  const [openModal, setOpenModal] = React.useState({ type: null });
  const onModalClose = () => setOpenModal({ type: null });
  const actions = getInternalActions(
    request && request.status,
    requestId,
    setOpenModal
  );
  const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);

  const requestDisplayProps = [
    ...(isInternal
      ? ['request_id', 'target_account']
      : ['first_name', 'last_name']),
    'start_date',
    'end_date',
    'created',
  ];
  return (
    <React.Fragment>
      <PageSection variant="light">
        <Breadcrumb>
          <BreadcrumbItem
            render={() => (
              <Link to={isInternal ? '/' : '/access-requests'}>
                {!isInternal && 'Red Hat '}Access Requests
              </Link>
            )}
          />
          <BreadcrumbItem>{requestId}</BreadcrumbItem>
        </Breadcrumb>
        <Flex direction={{ default: 'column', md: 'row' }}>
          <FlexItem grow={{ default: 'grow' }}>
            <Title headingLevel="h1" size="2xl" className="pf-u-pt-md">
              {requestId}
            </Title>
          </FlexItem>
          {isInternal && actions.items.length > 0 && (
            <FlexItem alignSelf={{ default: 'alignRight' }}>
              <Dropdown
                position="right"
                toggle={
                  <KebabToggle
                    onToggle={() => setIsDropdownOpen(!isDropdownOpen)}
                    id="actions-toggle"
                  />
                }
                isOpen={isDropdownOpen}
                isPlain
                dropdownItems={actions.items.map(({ title, onClick }) => (
                  <DropdownItem
                    key={title}
                    component="button"
                    onClick={onClick}
                  >
                    {title}
                  </DropdownItem>
                ))}
                isDisabled={actions.disable}
              />
            </FlexItem>
          )}
        </Flex>
      </PageSection>
      <PageSection>
        <Flex
          spaceItems={{ xl: 'spaceItemsLg' }}
          direction={{ default: 'column', lg: 'row' }}
        >
          <FlexItem
            flex={{ default: 'flex_1' }}
            alignSelf={{ default: 'alignSelfStretch' }}
          >
            <Card ouiaId="request-details" style={{ height: '100%' }}>
              <CardTitle>
                <Title headingLevel="h2" size="xl">
                  Request details
                </Title>
              </CardTitle>
              <CardBody>
                {!request ? (
                  <Spinner size="xl" />
                ) : (
                  <React.Fragment>
                    <div className="pf-u-pb-md">
                      {isInternal ? (
                        <div>
                          <label>
                            <b>Request status</b>
                          </label>
                          <br />
                          <Label
                            className="pf-u-mt-sm"
                            {...getLabelProps(request.status)}
                          >
                            {capitalize(request.status)}
                          </Label>
                        </div>
                      ) : (
                        <React.Fragment>
                          <label>
                            <b>Request decision</b>
                          </label>
                          <br />
                          <StatusLabel
                            requestId={requestId}
                            status={request.status}
                          />
                        </React.Fragment>
                      )}
                    </div>
                    {requestDisplayProps.map((prop, key) => (
                      <div className="pf-u-pb-md" key={key}>
                        <label>
                          <b>
                            {capitalize(
                              prop.replace(/_/g, ' ').replace('id', 'ID')
                            )}
                          </b>
                        </label>
                        <br />
                        <div>{request[prop]}</div>
                      </div>
                    ))}
                  </React.Fragment>
                )}
              </CardBody>
            </Card>
          </FlexItem>
          <FlexItem
            flex={{ default: 'flex_3' }}
            grow={{ default: 'grow' }}
            alignSelf={{ default: 'alignSelfStretch' }}
          >
            <Card ouiaId="request-roles" style={{ height: '100%' }}>
              <CardTitle>
                <Title headingLevel="h2" size="xl">
                  Roles requested
                </Title>
              </CardTitle>
              <CardBody>
                {!request ? (
                  <Spinner size="xl" />
                ) : (
                  <MUARolesTable roles={request.roles} />
                )}
              </CardBody>
            </Card>
          </FlexItem>
        </Flex>
      </PageSection>
      {openModal.type === 'cancel' && (
        <CancelRequestModal requestId={requestId} onClose={onModalClose} />
      )}
      {openModal.type === 'edit' && (
        <EditRequestModal
          variant="edit"
          requestId={requestId}
          onClose={onModalClose}
        />
      )}
    </React.Fragment>
  );
}
Example #18
Source File: MUARolesTable.js    From access-requests-frontend with Apache License 2.0 4 votes vote down vote up
MUARolesTable = ({
  roles: selectedRoles,
  setRoles: setSelectedRoles,
}) => {
  const isReadOnly = setSelectedRoles === undefined;
  const columns = ['Role name', 'Role description', 'Permissions'];
  const [rows, setRows] = React.useState(Array.from(rolesCache));
  const [applications, setApplications] = React.useState(applicationsCache);
  React.useEffect(() => {
    if (rolesCache.length === 0 || applicationsCache.length === 0) {
      apiInstance
        .get(
          `${API_BASE}/roles/?limit=9999&order_by=display_name&add_fields=groups_in_count`,
          { headers: { Accept: 'application/json' } }
        )
        .then(({ data }) => {
          data.forEach((role) => {
            role.isExpanded = false;
            role.permissions = role.accessCount;
          });
          rolesCache = data.map((role) => Object.assign({}, role));
          setRows(data);

          // Build application filter from data
          const apps = Array.from(
            data
              .map((role) => role.applications)
              .flat()
              .reduce((acc, cur) => {
                acc.add(cur);
                return acc;
              }, new Set())
          ).sort();
          applicationsCache = apps;
          setApplications(apps);
        })
        .catch((err) =>
          dispatch(
            addNotification({
              variant: 'danger',
              title: 'Could not fetch roles list',
              description: err.message,
            })
          )
        );
    }
  }, []);

  // Sorting
  const [activeSortIndex, setActiveSortIndex] = React.useState('name');
  const [activeSortDirection, setActiveSortDirection] = React.useState('asc');
  const onSort = (_ev, index, direction) => {
    setActiveSortIndex(index);
    setActiveSortDirection(direction);
  };

  // Filtering
  const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
  const [filterColumn, setFilterColumn] = React.useState(columns[0]);
  const [isSelectOpen, setIsSelectOpen] = React.useState(false);
  const [appSelections, setAppSelections] = React.useState([]);
  const [nameFilter, setNameFilter] = React.useState('');
  const hasFilters = appSelections.length > 0 || nameFilter;
  const selectLabelId = 'filter-application';
  const selectPlaceholder = 'Filter by application';

  const selectedNames = selectedRoles.map((role) => role.display_name);
  const filteredRows = rows
    .filter((row) =>
      appSelections.length > 0
        ? row.applications.find((app) => appSelections.includes(app))
        : true
    )
    .filter((row) => row.name.toLowerCase().includes(nameFilter))
    .filter((row) =>
      isReadOnly ? selectedNames.includes(row.display_name) : true
    );

  // Pagination
  const [page, setPage] = React.useState(1);
  const [perPage, setPerPage] = React.useState(10);
  const AccessRequestsPagination = ({ id }) => (
    <Pagination
      itemCount={filteredRows.length}
      perPage={perPage}
      page={page}
      onSetPage={(_ev, pageNumber) => setPage(pageNumber)}
      id={'access-requests-roles-table-pagination-' + id}
      variant={id}
      onPerPageSelect={(_ev, perPage) => {
        setPage(1);
        setPerPage(perPage);
      }}
      isCompact={id === 'top'}
    />
  );
  AccessRequestsPagination.propTypes = {
    id: PropTypes.string,
  };
  const pagedRows = filteredRows
    .sort((a, b) => {
      if (typeof a[activeSortIndex] === 'number') {
        // numeric sort
        if (activeSortDirection === 'asc') {
          return a[activeSortIndex] - b[activeSortIndex];
        }

        return b[activeSortIndex] - a[activeSortIndex];
      } else {
        // string sort
        if (activeSortDirection === 'asc') {
          return (a[activeSortIndex] + '').localeCompare(b[activeSortIndex]);
        }

        return (b[activeSortIndex] + '').localeCompare(a[activeSortIndex]);
      }
    })
    .slice((page - 1) * perPage, page * perPage);

  // Selecting
  const [isBulkSelectOpen, setIsBulkSelectOpen] = React.useState(false);
  const anySelected = selectedRoles.length > 0;
  const someChecked = anySelected ? null : false;
  const isChecked =
    selectedRoles.length === filteredRows.length && selectedRoles.length > 0
      ? true
      : someChecked;
  const onSelect = (_ev, isSelected, rowId) => {
    const changed = pagedRows[rowId].display_name;
    if (isSelected) {
      setSelectedRoles(selectedRoles.concat(changed));
    } else {
      setSelectedRoles(selectedRoles.filter((role) => role !== changed));
    }
  };

  const onSelectAll = (_ev, isSelected) => {
    if (isSelected) {
      setSelectedRoles(filteredRows.map((row) => row.display_name));
    } else {
      setSelectedRoles([]);
    }
  };

  const clearFiltersButton = (
    <Button
      variant="link"
      onClick={() => {
        setAppSelections([]);
        setNameFilter('');
      }}
    >
      Clear filters
    </Button>
  );
  const roleToolbar = isReadOnly ? null : (
    <Toolbar id="access-requests-roles-table-toolbar">
      <ToolbarContent>
        <ToolbarItem>
          <Dropdown
            onSelect={() => setIsBulkSelectOpen(!isBulkSelectOpen)}
            position="left"
            toggle={
              <DropdownToggle
                splitButtonItems={[
                  <DropdownToggleCheckbox
                    key="a"
                    id="example-checkbox-2"
                    aria-label={anySelected ? 'Deselect all' : 'Select all'}
                    isChecked={isChecked}
                    onClick={() => onSelectAll(null, !anySelected)}
                  />,
                ]}
                onToggle={(isOpen) => setIsBulkSelectOpen(isOpen)}
                isDisabled={rows.length === 0}
              >
                {selectedRoles.length !== 0 && (
                  <React.Fragment>
                    {selectedRoles.length} selected
                  </React.Fragment>
                )}
              </DropdownToggle>
            }
            isOpen={isBulkSelectOpen}
            dropdownItems={[
              <DropdownItem key="0" onClick={() => onSelectAll(null, false)}>
                Select none (0 items)
              </DropdownItem>,
              <DropdownItem
                key="1"
                onClick={() =>
                  setSelectedRoles(
                    selectedRoles.concat(pagedRows.map((r) => r.display_name))
                  )
                }
              >
                Select page ({Math.min(pagedRows.length, perPage)} items)
              </DropdownItem>,
              <DropdownItem key="2" onClick={() => onSelectAll(null, true)}>
                Select all ({filteredRows.length} items)
              </DropdownItem>,
            ]}
          />
        </ToolbarItem>
        <ToolbarItem>
          <InputGroup>
            <Dropdown
              isOpen={isDropdownOpen}
              onSelect={(ev) => {
                setIsDropdownOpen(false);
                setFilterColumn(ev.target.value);
                setIsSelectOpen(false);
              }}
              toggle={
                <DropdownToggle
                  onToggle={(isOpen) => setIsDropdownOpen(isOpen)}
                >
                  <FilterIcon /> {filterColumn}
                </DropdownToggle>
              }
              dropdownItems={['Role name', 'Application'].map((colName) => (
                // Filterable columns are RequestID, AccountID, and Status
                <DropdownItem key={colName} value={colName} component="button">
                  {capitalize(colName)}
                </DropdownItem>
              ))}
            />
            {filterColumn === 'Application' ? (
              <React.Fragment>
                <span id={selectLabelId} hidden>
                  {selectPlaceholder}
                </span>
                <Select
                  aria-labelledby={selectLabelId}
                  variant="checkbox"
                  aria-label="Select applications"
                  onToggle={(isOpen) => setIsSelectOpen(isOpen)}
                  onSelect={(_ev, selection) => {
                    if (appSelections.includes(selection)) {
                      setAppSelections(
                        appSelections.filter((s) => s !== selection)
                      );
                    } else {
                      setAppSelections([...appSelections, selection]);
                    }
                  }}
                  isOpen={isSelectOpen}
                  selections={appSelections}
                  isCheckboxSelectionBadgeHidden
                  placeholderText={selectPlaceholder}
                  style={{ maxHeight: '400px', overflowY: 'auto' }}
                >
                  {applications.map((app) => (
                    <SelectOption key={app} value={app}>
                      {capitalize(app.replace(/-/g, ' '))}
                    </SelectOption>
                  ))}
                </Select>
              </React.Fragment>
            ) : (
              <TextInput
                name="rolesSearch"
                id="rolesSearch"
                type="search"
                iconVariant="search"
                aria-label="Search input"
                placeholder="Filter by role name"
                value={nameFilter}
                onChange={(val) => setNameFilter(val)}
              />
            )}
          </InputGroup>
        </ToolbarItem>
        <ToolbarItem variant="pagination" align={{ default: 'alignRight' }}>
          <AccessRequestsPagination id="top" />
        </ToolbarItem>
      </ToolbarContent>
      {hasFilters && (
        <ToolbarContent>
          {nameFilter && (
            <ChipGroup categoryName="Role name">
              <Chip onClick={() => setNameFilter('')}>{nameFilter}</Chip>
            </ChipGroup>
          )}
          {appSelections.length > 0 && (
            <ChipGroup categoryName="Status">
              {appSelections.map((status) => (
                <Chip
                  key={status}
                  onClick={() =>
                    setAppSelections(appSelections.filter((s) => s !== status))
                  }
                >
                  {status}
                </Chip>
              ))}
            </ChipGroup>
          )}
          {clearFiltersButton}
        </ToolbarContent>
      )}
    </Toolbar>
  );

  const expandedColumns = ['Application', 'Resource type', 'Operation'];
  const dispatch = useDispatch();
  const onExpand = (row) => {
    row.isExpanded = !row.isExpanded;
    setRows([...rows]);
    if (!row.access) {
      apiInstance
        .get(`${API_BASE}/roles/${row.uuid}/`, {
          headers: { Accept: 'application/json' },
        })
        .then((res) => {
          row.access = res.access.map((a) => a.permission.split(':'));
          setRows([...rows]);
        })
        .catch((err) =>
          dispatch(
            addNotification({
              variant: 'danger',
              title: `Could not fetch permission list for ${row.name}.`,
              description: err.message,
            })
          )
        );
    }
  };
  const roleTable = (
    <TableComposable aria-label="My user access roles" variant="compact">
      <Thead>
        <Tr>
          {!isReadOnly && <Th />}
          <Th
            width={30}
            sort={{
              sortBy: {
                index: activeSortIndex,
                direction: activeSortDirection,
              },
              onSort,
              columnIndex: 'name',
            }}
          >
            {columns[0]}
          </Th>
          <Th
            width={50}
            sort={{
              sortBy: {
                index: activeSortIndex,
                direction: activeSortDirection,
              },
              onSort,
              columnIndex: 'description',
            }}
          >
            {columns[1]}
          </Th>
          <Th
            width={10}
            sort={{
              sortBy: {
                index: activeSortIndex,
                direction: activeSortDirection,
              },
              onSort,
              columnIndex: 'permissions',
            }}
            modifier="nowrap"
          >
            {columns[2]}
          </Th>
        </Tr>
      </Thead>
      {rows.length === 0 &&
        [...Array(perPage).keys()].map((i) => (
          <Tbody key={i}>
            <Tr>
              {!isReadOnly && <Td />}
              {columns.map((col, key) => (
                <Td dataLabel={col} key={key}>
                  <div
                    style={{ height: '22px' }}
                    className="ins-c-skeleton ins-c-skeleton__md"
                  >
                    {' '}
                  </div>
                </Td>
              ))}
            </Tr>
          </Tbody>
        ))}
      {pagedRows.map((row, rowIndex) => (
        <Tbody key={rowIndex}>
          <Tr>
            {!isReadOnly && (
              <Td
                select={{
                  rowIndex,
                  onSelect,
                  isSelected: selectedRoles.find((r) => r === row.display_name),
                }}
              />
            )}
            <Td dataLabel={columns[0]}>{row.display_name}</Td>
            <Td dataLabel={columns[1]} className="pf-m-truncate">
              <Tooltip entryDelay={1000} content={row.description}>
                <span className="pf-m-truncate pf-c-table__text">
                  {row.description}
                </span>
              </Tooltip>
            </Td>
            <Td
              dataLabel={columns[2]}
              className={css(
                'pf-c-table__compound-expansion-toggle',
                row.isExpanded && 'pf-m-expanded'
              )}
            >
              <button
                type="button"
                className="pf-c-table__button"
                onClick={() => onExpand(row)}
              >
                {row.permissions}
              </button>
            </Td>
          </Tr>
          <Tr isExpanded={row.isExpanded} borders={false}>
            {!isReadOnly && <Td />}
            <Td className="pf-u-p-0" colSpan={3}>
              <TableComposable isCompact className="pf-m-no-border-rows">
                <Thead>
                  <Tr>
                    {expandedColumns.map((col) => (
                      <Th key={col}>{col}</Th>
                    ))}
                  </Tr>
                </Thead>
                <Tbody>
                  {Array.isArray(row.access)
                    ? row.access.map((permissions) => (
                        <Tr key={permissions.join(':')}>
                          <Td dataLabel={expandedColumns[0]}>
                            {permissions[0]}
                          </Td>
                          <Td dataLabel={expandedColumns[1]}>
                            {permissions[1]}
                          </Td>
                          <Td dataLabel={expandedColumns[2]}>
                            {permissions[2]}
                          </Td>
                        </Tr>
                      ))
                    : [...Array(row.permissions).keys()].map((i) => (
                        <Tr key={i}>
                          {expandedColumns.map((val) => (
                            <Td key={val} dataLabel={val}>
                              <div
                                style={{ height: '22px' }}
                                className="ins-c-skeleton ins-c-skeleton__sm"
                              >
                                {' '}
                              </div>
                            </Td>
                          ))}
                        </Tr>
                      ))}
                </Tbody>
              </TableComposable>
            </Td>
          </Tr>
        </Tbody>
      ))}
      {pagedRows.length === 0 && hasFilters && (
        <Tr>
          <Td colSpan={columns.length}>
            <EmptyState variant="small">
              <EmptyStateIcon icon={SearchIcon} />
              <Title headingLevel="h2" size="lg">
                No matching requests found
              </Title>
              <EmptyStateBody>
                No results match the filter criteria. Remove all filters or
                clear all filters to show results.
              </EmptyStateBody>
              {clearFiltersButton}
            </EmptyState>
          </Td>
        </Tr>
      )}
    </TableComposable>
  );

  return (
    <React.Fragment>
      {!isReadOnly && (
        <React.Fragment>
          <Title headingLevel="h2">Select roles</Title>
          <p>Select the roles you would like access to.</p>
        </React.Fragment>
      )}
      {roleToolbar}
      {roleTable}
      {isReadOnly && <AccessRequestsPagination id="bottom" />}
    </React.Fragment>
  );
}