@patternfly/react-core#Bullseye JavaScript Examples

The following examples show how to use @patternfly/react-core#Bullseye. 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: utilities.js    From ibutsu-server with MIT License 7 votes vote down vote up
export function getSpinnerRow(columnCount) {
  return {
    heightAuto: true,
    cells: [
      {
        props: {colSpan: columnCount},
        title: <Bullseye><center><Spinner size="xl"/></center></Bullseye>
      }
    ]
  };
}
Example #2
Source File: Routes.js    From dbaas-ui with Apache License 2.0 6 votes vote down vote up
Routes = () => (
  <Suspense
    fallback={
      <Bullseye>
        <Spinner />
      </Bullseye>
    }
  >
    <Switch>
      <Route path="/">
        <HomePage />
      </Route>
      <Route>
        <OopsPage />
      </Route>
    </Switch>
  </Suspense>
)
Example #3
Source File: tablestates.js    From ibutsu-server with MIT License 6 votes vote down vote up
render() {
    return (
      <Bullseye>
        <EmptyState>
          <EmptyStateIcon icon={ErrorCircleOIcon} />
          <Title headingLevel="h5" size="lg">Error occurred fetching results</Title>
          {!!this.props.onClearFilters &&
          <React.Fragment>
            <EmptyStateBody>
              An error occurred while fetching results. Try a different set of filters.
            </EmptyStateBody>
            <EmptyStateSecondaryActions>
              <Button variant="link" onClick={this.props.onClearFilters}>Clear all filters</Button>
            </EmptyStateSecondaryActions>
          </React.Fragment>
          }
        </EmptyState>
      </Bullseye>
    );
  }
Example #4
Source File: tablestates.js    From ibutsu-server with MIT License 6 votes vote down vote up
render() {
    return (
      <Bullseye>
        <EmptyState>
          <EmptyStateIcon icon={SearchIcon} />
          <Title headingLevel="h5" size="lg">No results found</Title>
          {!!this.props.onClearFilters &&
          <React.Fragment>
            <EmptyStateBody>
              No results match this filter criteria. Clear all filters to show results.
            </EmptyStateBody>
            <EmptyStateSecondaryActions>
              <Button variant="link" onClick={this.props.onClearFilters}>Clear all filters</Button>
            </EmptyStateSecondaryActions>
          </React.Fragment>
          }
        </EmptyState>
      </Bullseye>
    );
  }
Example #5
Source File: Routes.js    From tasks-frontend with Apache License 2.0 6 votes vote down vote up
Routes = () => (
  <Suspense
    fallback={
      <Bullseye>
        <Spinner />
      </Bullseye>
    }
  >
    <Switch>
      <Route exact path="/" render={() => <TasksPage tab={0} />} />
      {/*<Redirect exact from="/" to={'/available'} />*/}
      <Route exact path="/available" render={() => <TasksPage tab={0} />} />
      <Route exact path="/executed" render={() => <TasksPage tab={1} />} />
      <Route
        exact
        path="/executed/:id"
        //render={() => <CompletedTaskDetails />}
        component={CompletedTaskDetails}
      />
      {/* Finally, catch all unmatched routes */}
      <Route>
        <Redirect to="/" />
      </Route>
    </Switch>
  </Suspense>
)
Example #6
Source File: NoResultsTable.js    From tasks-frontend with Apache License 2.0 6 votes vote down vote up
NoResultsTable = ({ type }) => (
  <EmptyTable>
    <Bullseye>
      <EmptyState variant={EmptyStateVariant.full}>
        <Title headingLevel="h5" size="lg">
          {`No matching ${type} found`}
        </Title>
        <EmptyStateBody>
          To continue, edit your filter settings and search again.
        </EmptyStateBody>
      </EmptyState>
    </Bullseye>
  </EmptyTable>
)
Example #7
Source File: Routes.js    From sed-frontend with Apache License 2.0 6 votes vote down vote up
Routes = () => (
  <Suspense
    fallback={
      <Bullseye>
        <Spinner />
      </Bullseye>
    }
  >
    <Authentication>
      <Switch>
        <Route path={paths.activationKeys} component={ActivationKeys} />
        <Route path={paths.connector} component={Dashboard} />
      </Switch>
    </Authentication>
  </Suspense>
)
Example #8
Source File: no-access.js    From sed-frontend with Apache License 2.0 6 votes vote down vote up
NoAccessView = () => (
  <Bullseye>
    <EmptyState>
      <EmptyStateIcon icon={LockIcon} />
      <Title headingLevel="h4" size="lg">
        Activation keys can only be accessed by organization administrators.
      </Title>
      <EmptyStateBody>
        If you already know your organization ID and activation key, you can
        register systems with RHC.
      </EmptyStateBody>
    </EmptyState>
  </Bullseye>
)
Example #9
Source File: ErrorBoundary.js    From ocp-advisor-frontend with Apache License 2.0 6 votes vote down vote up
render() {
    return this.state.hasError ? (
      <Bullseye>
        <ErrorState />
      </Bullseye>
    ) : (
      this.props.children
    );
  }
Example #10
Source File: Routes.js    From ocp-advisor-frontend with Apache License 2.0 6 votes vote down vote up
Routes = () => (
  <Suspense
    fallback={
      <Bullseye>
        <Spinner />
      </Bullseye>
    }
  >
    <Switch>
      {paths.map((path) => (
        <Route key={path.title} path={path.path} component={path.component} />
      ))}
      <Redirect exact from="/" to="/recommendations" />
      {/* Finally, catch all unmatched routes */}
      <Route
        path="*"
        component={() => (
          <EmptyState>
            <EmptyStateBody>
              <InvalidObject />
            </EmptyStateBody>
          </EmptyState>
        )}
      />
    </Switch>
  </Suspense>
)
Example #11
Source File: AvailableImagesTile.js    From edge-frontend with Apache License 2.0 6 votes vote down vote up
AvailableImageTile = ({ onNewImageClick }) => {
  const { isLoading, hasError, data } = useSelector(
    ({ imagesReducer }) => ({
      isLoading:
        imagesReducer?.isLoading !== undefined
          ? imagesReducer?.isLoading
          : true,
      hasError: imagesReducer?.hasError || false,
      data: imagesReducer?.data || null,
    }),
    shallowEqual
  );

  return (
    <AvailableImageTileBase>
      <CardBody>
        {isLoading ? (
          <Bullseye>
            <Spinner />
          </Bullseye>
        ) : hasError ? (
          data
        ) : (
          <Button variant="link" style={{ paddingLeft: 0 }}>
            {data.meta.count} images
          </Button>
        )}
      </CardBody>
      <CardFooter>
        <Button variant="primary" onClick={() => onNewImageClick()}>
          Create new image
        </Button>
      </CardFooter>
    </AvailableImageTileBase>
  );
}
Example #12
Source File: DeviceSummaryTile.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
DeviceSummaryTile = () => {
  const { isLoading, hasError, data } = useSelector(
    ({ deviceSummaryReducer }) => ({
      isLoading:
        deviceSummaryReducer?.isLoading !== undefined
          ? deviceSummaryReducer?.isLoading
          : true,
      hasError: deviceSummaryReducer?.hasError || false,
      data: deviceSummaryReducer?.data || null,
    }),
    shallowEqual
  );

  if (isLoading) {
    return (
      <Card className="tiles-card">
        <CardTitle>Device summary information</CardTitle>
        <CardBody>
          <Bullseye>
            <Spinner />
          </Bullseye>
        </CardBody>
      </Card>
    );
  }
  if (hasError) {
    return (
      <Card className="tiles-card">
        <CardTitle>Device summary information</CardTitle>
        <CardBody>{data}</CardBody>
      </Card>
    );
  }
  return (
    <DeviceSummaryTileBase
      orphaned={data['orphaned']}
      active={data['active']}
      noReports={data['noReports']}
      neverReported={data['neverReported']}
    />
  );
}
Example #13
Source File: Loading.js    From sed-frontend with Apache License 2.0 5 votes vote down vote up
Loading = () => {
  return (
    <Bullseye>
      <Spinner />
    </Bullseye>
  );
}
Example #14
Source File: App.js    From ocp-advisor-frontend with Apache License 2.0 5 votes vote down vote up
App = ({ useLogger, basename }) => {
  const intl = useIntl();
  const history = useHistory();
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const registry = getRegistry();
    registry.register({ notifications: notificationsReducer });
    insights.chrome.init();
    insights.chrome.auth.getUser().then(() => {
      setIsAuthenticated(true);
      setIsLoading(false);
    });
    insights.chrome.identifyApp('ocp-advisor');
    const unregister = insights.chrome.on('APP_NAVIGATION', (event) => {
      const targetUrl = event.domEvent?.href
        ?.replace(basename, '/')
        .replace(/^\/\//, '/');
      if (typeof targetUrl === 'string') {
        history.push(targetUrl);
      }
    });
    return () => unregister();
  }, []);
  return (
    <ErrorBoundary>
      {isLoading ? (
        <Bullseye>
          <Spinner />
        </Bullseye>
      ) : isAuthenticated ? (
        <Provider store={getStore(useLogger)}>
          <NotificationsPortal />
          <Routes />
        </Provider>
      ) : (
        <Bullseye>
          <MessageState
            variant="large"
            icon={LockIcon}
            title={intl.formatMessage(messages.permsTitle)}
            text={intl.formatMessage(messages.permsBody)}
          />
        </Bullseye>
      )}
    </ErrorBoundary>
  );
}
Example #15
Source File: Routes.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
Routes = () => {
  return (
    <Suspense
      fallback={
        <Bullseye>
          <Spinner size="xl" />
        </Bullseye>
      }
    >
      <Switch>
        <Route exact path={paths.groups} component={Groups} />
        <Route exact path={paths['groups-detail']} component={GroupsDetail} />
        {/* <Route path={paths['device-detail']} component={DeviceDetail} /> */}
        {/* <Route path={paths.canaries} component={Canaries} /> */}
        <Route exact path={paths['fleet-management']} component={Groups} />
        <Route
          exact
          path={paths['fleet-management-detail']}
          component={GroupsDetail}
        />
        <Route exact path={paths['inventory']} component={Inventory} />
        <Route path={paths['inventory-detail']} component={DeviceDetail} />
        <Route
          path={paths['manage-images-detail-version']}
          component={ImageDetail}
        />
        <Route path={paths['manage-images-detail']} component={ImageDetail} />
        <Route path={paths['manage-images']} component={Images} />

        {useFeatureFlags('fleet-management.custom-repos') && (
          <Route exact path={paths['repositories']} component={Repositories} />
        )}
        <Route
          exact
          path={paths['learning-resources']}
          component={LearningResources}
        />
        <Route>
          <Redirect to={paths['fleet-management']} />
        </Route>
      </Switch>
    </Suspense>
  );
}
Example #16
Source File: Routes.js    From access-requests-frontend with Apache License 2.0 5 votes vote down vote up
Routes = () => {
  const { getRegistry } = useContext(RegistryContext);
  const [isInternal, setIsInternal] = useState(true);

  useEffect(() => {
    insights.chrome.init();
    Promise.resolve(insights.chrome.auth.getUser()).then((user) => {
      setIsInternal(user?.identity?.user?.is_internal);
    });
  });

  const AccessRequestDetailsPageWrapper = () => (
    <AccessRequestDetailsPage
      isInternal={isInternal}
      getRegistry={getRegistry}
    />
  );
  const AccessRequestsPageWrapper = () => (
    <AccessRequestsPage isInternal={isInternal} getRegistry={getRegistry} />
  );

  return (
    <Suspense
      fallback={
        <Bullseye>
          <Spinner />
        </Bullseye>
      }
    >
      {isDev && (
        <ToggleSwitch
          id="toggle-view"
          label="Internal view"
          labelOff="External view"
          isChecked={isInternal}
          onChange={() => setIsInternal(!isInternal)}
        />
      )}
      <Switch>
        <Route path="/" exact component={AccessRequestsPageWrapper} />
        <Route
          path="/:requestId"
          exact
          component={AccessRequestDetailsPageWrapper}
        />
        <Route>
          <Redirect to="/" />
        </Route>
      </Switch>
    </Suspense>
  );
}
Example #17
Source File: App.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
App = (props) => {
  const { getRegistry } = useContext(RegistryContext);
  const [isLogged, setIsLogged] = useState(false);
  const history = useHistory();
  const [isAuth, setIsAuth] = useState(null);
  useEffect(() => {
    insights.chrome.init();
    // TODO change this to your appname
    insights.chrome.identifyApp('fleet-management');

    insights.chrome.on('APP_NAVIGATION', (event) =>
      history.push(`/${event.navId}`)
    );

    const registered = getRegistry().register({
      notifications: notificationsReducer,
    });
    (async () => {
      await insights.chrome.auth.getUser();
      setIsLogged(true);
    })();

    (async () => {
      const data = await insights.chrome.auth.getUser();
      setIsAuth(data.entitlements.smart_management.is_entitled);
    })();

    return () => {
      registered();
    };
  }, []);

  return (
    <Fragment>
      <NotificationPortal />
      {isAuth && isLogged ? (
        <Routes childProps={props} />
      ) : isAuth === null ? (
        <Bullseye>
          <Spinner size="xl" />
        </Bullseye>
      ) : (
        <AuthModal />
      )}
    </Fragment>
  );
}
Example #18
Source File: AddDeviceModal.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
AddDeviceModal = ({
  isModalOpen,
  setIsModalOpen,
  setIsCreateGroupModalOpen,
  reloadData,
  deviceIds,
}) => {
  const dispatch = useDispatch();
  const [response] = useApi({ api: getGroups });
  const { data, isLoading } = response;

  const handleAddDevices = (values) => {
    const { group } = values;
    const statusMessages = {
      onSuccess: {
        title: 'Success',
        description: `Device(s) have been added to ${group.toString()} successfully`,
      },
      onError: { title: 'Error', description: 'Failed to add device to group' },
    };

    apiWithToast(
      dispatch,
      () => addDevicesToGroup(parseInt(group.groupId), deviceIds),
      statusMessages
    );
  };
  return isLoading ? (
    <Backdrop>
      <Bullseye>
        <Spinner isSVG diameter="100px" />
      </Bullseye>
    </Backdrop>
  ) : (
    <Modal
      isOpen={isModalOpen}
      openModal={() => setIsModalOpen(false)}
      title="Add to group"
      submitLabel="Add"
      additionalMappers={{
        'search-input': {
          component: SearchInput,
          defaultOptions: data?.data || [],
        },
        'create-group-btn': {
          component: CreateGroupButton,
          openModal: () => {
            setIsCreateGroupModalOpen(true);
            setIsModalOpen(false);
          },
        },
      }}
      schema={createSchema(deviceIds)}
      onSubmit={handleAddDevices}
      reloadData={reloadData}
    />
  );
}
Example #19
Source File: ImageVersionDetail.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
ImageVersionDetail = ({ data, imageVersion }) => {
  const history = useHistory();
  const [isUpdateWizardOpen, setIsUpdateWizardOpen] = useState(false);
  const [imageData, setImageData] = useState({});

  useEffect(() => {
    setImageData(data);
  }, [data]);

  const openUpdateWizard = () => {
    history.push({
      pathname: history.location.pathname,
      search: new URLSearchParams({
        update_image: true,
      }).toString(),
    });
    setIsUpdateWizardOpen(true);
  };

  return (
    <Fragment>
      <PageHeader className="pf-m-light">
        <Stack hasGutter>
          <StackItem>
            <DetailsHead
              imageData={imageData}
              imageVersion={imageVersion}
              openUpdateWizard={openUpdateWizard}
            />
          </StackItem>
        </Stack>
        <StackItem>
          <Text>{data?.Description}</Text>
        </StackItem>
      </PageHeader>
      <ImageDetailTabs imageData={imageData} imageVersion={imageVersion} />
      {isUpdateWizardOpen && (
        <Suspense
          fallback={
            <Bullseye>
              <Spinner />
            </Bullseye>
          }
        >
          <UpdateImageWizard
            navigateBack={() => {
              history.push({ pathname: history.location.pathname });
              setIsUpdateWizardOpen(false);
            }}
            updateimageVersionId={data?.ID}
          />
        </Suspense>
      )}
    </Fragment>
  );
}
Example #20
Source File: index.js    From sed-frontend with Apache License 2.0 4 votes vote down vote up
SamplePage = () => {
  const history = useHistory();
  const { getRegistry } = useContext(RegistryContext);
  const [confirmChangesOpen, setConfirmChangesOpen] = useState(false);
  const [madeChanges, setMadeChanges] = useState(false);
  const [isEditing, setIsEditing] = useState(false);
  const dataRef = useRef();
  const dispatch = useDispatch();

  const activeStateLoaded = useSelector(
    ({ activeStateReducer }) => activeStateReducer?.loaded
  );
  const { useOpenSCAP, enableCloudConnector, hasInsights } = useSelector(
    ({ activeStateReducer }) => ({
      useOpenSCAP: activeStateReducer?.values?.useOpenSCAP,
      enableCloudConnector: activeStateReducer?.values?.enableCloudConnector,
      hasInsights: activeStateReducer?.values?.hasInsights,
    }),
    shallowEqual
  );
  const { systemsCount } = useSelector(
    ({ connectedSystemsReducer }) => ({
      systemsLoaded: connectedSystemsReducer?.loaded,
      systemsCount: connectedSystemsReducer?.total,
    }),
    shallowEqual
  );

  useEffect(() => {
    getRegistry().register({
      activeStateReducer,
      logReducer,
      connectedSystemsReducer,
    });
    dispatch(fetchCurrState());
    dispatch(fetchConnectedHosts());
  }, [getRegistry]);

  useEffect(() => {
    insights?.chrome?.appAction?.('cloud-connector-dashboard');
  }, []);

  return (
    <React.Fragment>
      <Route
        exact
        path={paths.logModal}
        render={() => (
          <Suspense
            fallback={
              <Bullseye>
                <Spinner />
              </Bullseye>
            }
          >
            <ConnectLog />
          </Suspense>
        )}
      />
      <PageHeader className="page-header">
        <Split hasGutter className="page-title">
          <SplitItem isFilled>
            <Flex>
              <FlexItem spacer={{ default: 'spacerSm' }}>
                <PageHeaderTitle title="Remote Host Configuration Manager" />
              </FlexItem>
              <FlexItem>
                <AboutRemoteHostConfigPopover />
              </FlexItem>
            </Flex>
          </SplitItem>
          <SplitItem>
            <Button onClick={() => history.push(paths.logModal)} variant="link">
              View log
            </Button>
          </SplitItem>
        </Split>
        <Stack hasGutter>
          <StackItem>
            Selections here affect Red Hat Enterprise Linux (RHEL) systems
            connected to Red Hat with remote host configuration (rhc). Upon
            saving changes, Ansible Playbooks are automatically pushed to
            connected systems to update the configuration of the connection to
            Red Hat.
          </StackItem>
          <StackItem>
            <a
              target="_blank"
              rel="noopener noreferrer"
              href={
                'https://access.redhat.com/documentation/en-us/red_hat_insights/2022/html-single/red_hat_connector_configuration_guide/index'
              }
            >
              Connecting with Red Hat
              {<ExternalLinkAltIcon className="pf-u-ml-sm" />}
            </a>
          </StackItem>
        </Stack>
      </PageHeader>
      <Page>
        <div className="dashboard__content">
          {activeStateLoaded ||
          (useOpenSCAP !== undefined && enableCloudConnector !== undefined) ? (
            <Services
              madeChanges={madeChanges}
              setConfirmChangesOpen={setConfirmChangesOpen}
              setMadeChanges={setMadeChanges}
              setIsEditing={setIsEditing}
              isEditing={isEditing}
              defaults={{
                useOpenSCAP,
                enableCloudConnector,
                hasInsights,
              }}
              onChange={(data) => {
                dataRef.current = data;
              }}
            />
          ) : (
            <Bullseye>
              <Spinner className="pf-u-p-lg" size="xl" />
            </Bullseye>
          )}
        </div>
      </Page>
      <ConfirmChangesModal
        isOpen={confirmChangesOpen}
        handleCancel={() => setConfirmChangesOpen(false)}
        systemsCount={systemsCount}
        data={dataRef.current}
        handleConfirm={() => {
          setConfirmChangesOpen(false);
          (async () => {
            const saveAction = saveCurrState(dataRef.current);
            dispatch(saveAction);
            await saveAction.payload;
            dispatch(
              addNotification({
                variant: 'success',
                title: 'Changes saved',
                description:
                  'Your service enablement changes were applied to connected systems',
              })
            );
            setMadeChanges(false);
            setIsEditing(false);
          })();
        }}
      />
    </React.Fragment>
  );
}
Example #21
Source File: run.js    From ibutsu-server with MIT License 4 votes vote down vote up
render() {
    let passed = 0, failed = 0, errors = 0, xfailed = 0, xpassed = 0, skipped = 0, not_run = 0;
    let created = 0;
    let calculatePasses = true;
    const { run, columns, rows, classificationTable, artifactTabs } = this.state;
    const jsonViewTheme = getTheme() === 'dark' ? 'tomorrow' : 'rjv-default';

    if (run.start_time) {
      created = new Date(run.start_time);
    }
    else {
      created = new Date(run.created);
    }
    if (run.summary) {
      if (run.summary.passes) {
        passed = run.summary.passes;
        calculatePasses = false;
      }
      if (run.summary.tests && calculatePasses) {
        passed = run.summary.tests;
      }
      if (run.summary.failures) {
        passed -= calculatePasses ? run.summary.failures : 0;
        failed = run.summary.failures;
      }
      if (run.summary.errors) {
        passed -= calculatePasses ? run.summary.errors : 0;
        errors = run.summary.errors;
      }
      if (run.summary.xfailures) {
        passed -= calculatePasses ? run.summary.xfailures : 0;
        xfailed = run.summary.xfailures;
      }
      if (run.summary.xpasses) {
        passed -= calculatePasses ? run.summary.xpasses : 0;
        xpassed = run.summary.xpasses;
      }
      if (run.summary.skips) {
        passed -= calculatePasses ? run.summary.skips : 0;
        skipped = run.summary.skips;
      }
      if (run.summary.not_run) {
        not_run = run.summary.not_run;
      }
      else if (run.summary.collected) {
        not_run = run.summary.collected - run.summary.tests;
      }
    }
    const pagination = {
      pageSize: this.state.pageSize,
      page: this.state.page,
      totalItems: this.state.totalItems
    }
    return (
      <React.Fragment>
        <PageSection variant={PageSectionVariants.light}>
          <TextContent>
            <Text component="h1" className="pf-c-title">Run {run.id}</Text>
          </TextContent>
        </PageSection>
        <PageSection>
          {!this.state.isRunValid &&
          <EmptyObject headingText="Run not found" returnLink="/runs" returnLinkText="Return to runs list" />
          }
          {this.state.isRunValid &&
            <Tabs activeKey={this.state.activeTab} onSelect={this.onTabSelect} isBox>
              <Tab eventKey={'summary'} title={<TabTitle icon={InfoCircleIcon} text="Summary" />}>
                <Card>
                  <CardBody style={{padding: 0}} id="run-detail">
                    <Grid style={{backgroundColor: '#fff'}}>
                      <GridItem span={6}>
                        <DataList selectedDataListItemId={null} aria-label="Run properties" style={{borderBottom: 'none', borderTop: 'none'}}>
                          <DataListItem aria-labelledby="Duration">
                            <DataListItemRow>
                              <DataListItemCells
                                dataListCells={[
                                  <DataListCell key={1} width={2}><strong>Duration:</strong></DataListCell>,
                                  <DataListCell key={2} width={4}>{round(run.duration)}s</DataListCell>
                                ]}
                              />
                            </DataListItemRow>
                          </DataListItem>
                          <DataListItem aria-labelledby="Started">
                            <DataListItemRow>
                              <DataListItemCells
                                dataListCells={[
                                  <DataListCell key={1} width={2}><strong>Started:</strong></DataListCell>,
                                  <DataListCell key={2} width={4}>{created.toLocaleString()}</DataListCell>
                                ]}
                              />
                            </DataListItemRow>
                          </DataListItem>
                          {run.metadata && run.metadata.component &&
                          <DataListItem aria-labelledby="Component">
                            <DataListItemRow>
                              <DataListItemCells
                                dataListCells={[
                                  <DataListCell key={1} width={2}><strong>Component:</strong></DataListCell>,
                                  <DataListCell key={2} width={4}>{run.metadata.component}</DataListCell>
                                ]}
                              />
                            </DataListItemRow>
                          </DataListItem>
                          }
                          {run.metadata && run.metadata.env &&
                            <DataListItem aria-labelledby="Environment">
                              <DataListItemRow>
                                <DataListItemCells
                                  dataListCells={[
                                    <DataListCell key={1} width={2}><strong>Environment:</strong></DataListCell>,
                                    <DataListCell key={2} width={4}>{run.metadata.env}</DataListCell>
                                  ]}
                                />
                              </DataListItemRow>
                            </DataListItem>
                          }
                          {run.metadata && run.metadata.tags &&
                            <DataListItem aria-labelledby="tags-label">
                              <DataListItemRow>
                                <DataListItemCells
                                  dataListCells={[
                                    <DataListCell key="tags-label" width={2}><strong>Tags:</strong></DataListCell>,
                                    <DataListCell key="tags-data" width={4}>
                                      <Flex>
                                        {run.metadata.tags.map((tag) => <FlexItem spacer={{ default: 'spacerXs' }} key={tag}><Label color="blue" variant="filled">{tag}</Label></FlexItem>)}
                                      </Flex>
                                    </DataListCell>
                                  ]}
                                />
                              </DataListItemRow>
                            </DataListItem>
                          }
                        {run.metadata && run.metadata.jenkins && run.metadata.jenkins.job_name &&
                          <DataListItem aria-labelledby="Jenkins Job Name">
                            <DataListItemRow>
                              <DataListItemCells
                                dataListCells={[
                                  <DataListCell key={1} width={2}><strong>Jenkins Job Name:</strong></DataListCell>,
                                  <DataListCell key={2} width={4}>{run.metadata.jenkins.job_name}</DataListCell>
                                ]}
                              />
                            </DataListItemRow>
                          </DataListItem>
                          }
                          {run.source &&
                            <DataListItem aria-labelledby="Source">
                              <DataListItemRow>
                                <DataListItemCells
                                  dataListCells={[
                                    <DataListCell key={1} width={2}><strong>Source:</strong></DataListCell>,
                                    <DataListCell key={2} width={4}>{run.source}</DataListCell>
                                  ]}
                                />
                              </DataListItemRow>
                            </DataListItem>
                          }
                        </DataList>
                      </GridItem>
                      <GridItem span={6}>
                        <DataList selectedDataListItemId={null} aria-label="Summary properties" style={{borderBottom: 0, borderTop: 0}}>
                          <DataListItem aria-labelledby="Summary">
                            <DataListItemRow>
                              <DataListItemCells
                                style={{paddingBottom: 0}}
                                dataListCells={[
                                  <DataListCell key={1} width={2}><strong>Summary:</strong></DataListCell>,
                                  <DataListCell key={2} width={4} style={{paddingTop: 0}}>
                                    <DataList selectedDataListItemId={null} aria-label="Summary" style={{borderBottom: 0, borderTop: 0}}>
                                      <DataListItem aria-labelledby="Total">
                                        <DataListItemRow>
                                          <DataListItemCells
                                            dataListCells={[
                                              <DataListCell key={1}>Total:</DataListCell>,
                                              <DataListCell key={2}>{run.summary.collected ? run.summary.collected : run.summary.tests}</DataListCell>
                                            ]}
                                          />
                                        </DataListItemRow>
                                      </DataListItem>
                                      <DataListItem aria-labelledby="Passed">
                                        <DataListItemRow>
                                          <DataListItemCells
                                            dataListCells={[
                                              <DataListCell key={1}>Passed:</DataListCell>,
                                              <DataListCell key={2}>{passed}</DataListCell>
                                            ]}
                                          />
                                        </DataListItemRow>
                                      </DataListItem>
                                      <DataListItem aria-labelledby="Failed">
                                        <DataListItemRow>
                                          <DataListItemCells
                                            dataListCells={[
                                              <DataListCell key={1}>Failed:</DataListCell>,
                                              <DataListCell key={2}>{failed}</DataListCell>
                                            ]}
                                          />
                                        </DataListItemRow>
                                      </DataListItem>
                                      <DataListItem aria-labelledby="Error">
                                        <DataListItemRow>
                                          <DataListItemCells
                                            dataListCells={[
                                              <DataListCell key={1}>Error:</DataListCell>,
                                              <DataListCell key={2}>{errors}</DataListCell>
                                            ]}
                                          />
                                        </DataListItemRow>
                                      </DataListItem>
                                      <DataListItem aria-labelledby="Xfailed">
                                        <DataListItemRow>
                                          <DataListItemCells
                                            dataListCells={[
                                              <DataListCell key={1}>Xfailed:</DataListCell>,
                                              <DataListCell key={2}>{xfailed}</DataListCell>
                                            ]}
                                          />
                                        </DataListItemRow>
                                      </DataListItem>
                                      <DataListItem aria-labelledby="Xpassed">
                                        <DataListItemRow>
                                          <DataListItemCells
                                            dataListCells={[
                                              <DataListCell key={1}>Xpassed:</DataListCell>,
                                              <DataListCell key={2}>{xpassed}</DataListCell>
                                            ]}
                                          />
                                        </DataListItemRow>
                                      </DataListItem>
                                      <DataListItem aria-labelledby="Skipped">
                                        <DataListItemRow>
                                          <DataListItemCells
                                            dataListCells={[
                                              <DataListCell key={1}>Skipped:</DataListCell>,
                                              <DataListCell key={2}>{skipped}</DataListCell>
                                            ]}
                                          />
                                        </DataListItemRow>
                                      </DataListItem>
                                      <DataListItem aria-labelledby="Not Run">
                                        <DataListItemRow>
                                          <DataListItemCells
                                            dataListCells={[
                                              <DataListCell key={1}>Not Run:</DataListCell>,
                                              <DataListCell key={2}>{not_run}</DataListCell>
                                            ]}
                                          />
                                        </DataListItemRow>
                                      </DataListItem>
                                    </DataList>
                                  </DataListCell>
                                ]}
                              />
                            </DataListItemRow>
                          </DataListItem>
                        </DataList>
                      </GridItem>
                    </Grid>
                  </CardBody>
                </Card>
              </Tab>
              <Tab eventKey={'results-list'} title={<TabTitle icon={CatalogIcon} text="Results List" />}>
                <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 Results</Text>
                        </TextContent>
                      </FlexItem>
                      <FlexItem>
                        <Button variant="secondary" onClick={this.refreshResults}>Refresh results</Button>
                      </FlexItem>
                      <FlexItem>
                        <Link to={`/results?run_id[eq]=${run.id}`} className="pf-c-button pf-m-primary" style={{marginLeft: '2px'}}>See all results <ChevronRightIcon /></Link>
                      </FlexItem>
                    </Flex>
                  </CardHeader>
                  <CardBody>
                    <FilterTable
                      columns={columns}
                      rows={rows}
                      pagination={pagination}
                      isEmpty={this.state.isEmpty}
                      isError={this.state.isError}
                      onSetPage={this.setPage}
                      onSetPageSize={this.pageSizeSelect}
                    />
                  </CardBody>
                </Card>
              </Tab>
              <Tab eventKey={'results-tree'} title={<TabTitle icon={RepositoryIcon} text="Results Tree" />}>
                <Card className="pf-u-mt-lg">
                  <CardBody>
                    <Grid gutter="sm">
                      {false && <GridItem span={12}>
                        <div style={{paddingTop: "1em"}}>
                          <TextInput value={this.state.treeSearch} type="text" onChange={this.onSearch} placeholder="Search tree..." aria-label="Filter tree" />
                        </div>
                      </GridItem>
                      }
                      {this.state.resultsTree.core.data.length === 0 &&
                        <GridItem span={12}>
                          <Bullseye><center><Spinner size="xl"/></center></Bullseye>
                        </GridItem>
                      }
                      {this.state.resultsTree.core.data !== 0 &&
                        <React.Fragment>
                          <GridItem span={5}>
                            <TreeView treeData={this.state.resultsTree} onChange={(e, data) => this.handleJSTreeChange(e, data)}/>
                          </GridItem>
                          <GridItem span={7}>
                            {this.state.testResult &&
                            <Card className={this.state.testResult.result}>
                              <CardHeader>
                                {this.state.testResult.test_id}
                                {this.state.testResult.metadata.markers &&
                                  <div style={{float: 'right'}}>
                                    {this.state.testResult.metadata.markers.map((marker) => {
                                      return <Badge isRead key={marker.name}>{marker.name}</Badge>;
                                    })}
                                  </div>
                                }
                              </CardHeader>
                              <CardBody style={{backgroundColor: "var(--pf-c-page__main-section--BackgroundColor)", paddingTop: "1.2em"}}>
                                <ResultView testResult={this.state.testResult}/>
                              </CardBody>
                            </Card>
                            }
                          </GridItem>
                        </React.Fragment>
                      }
                    </Grid>
                  </CardBody>
                </Card>
              </Tab>
              <Tab eventKey={'classify-failures'} title={<TabTitle icon={MessagesIcon} text="Classify Failures" />}>
                {classificationTable}
              </Tab>
              {artifactTabs}
              <Tab eventKey={'run-object'} title={<TabTitle icon={CodeIcon} text="Run Object" />}>
                <Card>
                  <CardBody>
                    <ReactJson src={run} name={null} iconStyle={"triangle"} collapseStringsAfterLength={120} enableClipboard={false} displayDataTypes={false} theme={jsonViewTheme} />
                  </CardBody>
                </Card>
              </Tab>
            </Tabs>
          }
        </PageSection>
      </React.Fragment>
    );
  }
Example #22
Source File: DeviceDetail.js    From edge-frontend with Apache License 2.0 4 votes vote down vote up
DeviceDetail = () => {
  const [imageId, setImageId] = useState(null);
  const { getRegistry } = useContext(RegistryContext);
  const { inventoryId, uuid } = useParams();
  const entity = useSelector(({ entityDetails }) => entityDetails?.entity);
  const groupName = useSelector(
    ({ groupsDetailReducer }) => groupsDetailReducer?.name
  );
  const deviceId = useSelector(
    ({ entityDetails }) => entityDetails?.entity?.id
  );

  const [imageData, setImageData] = useState();
  const [updateModal, setUpdateModal] = useState({
    isOpen: false,
    deviceData: null,
  });
  const [isDeviceStatusLoading, setIsDeviceStatusLoading] = useState(true);
  const [reload, setReload] = useState(false);
  useEffect(() => {
    insights.chrome.registerModule('inventory');
    insights.chrome?.hideGlobalFilter?.(true);
    insights.chrome.appAction('system-detail');
  }, []);

  useEffect(() => {
    (async () => {
      if (!entity?.display_name) {
        return;
      }
      const image_data = await getDeviceHasUpdate(deviceId);
      setImageData(image_data);
      setIsDeviceStatusLoading(false);
      setUpdateModal((prevState) => ({
        ...prevState,
        deviceData: [
          {
            display_name: entity.display_name,
            id: entity.id,
          },
        ],
        imageSetId: image_data?.ImageInfo?.Image?.ImageSetID,
      }));
      setImageId(image_data?.ImageInfo?.Image?.ID);
    })();
  }, [entity, reload]);

  useEffect(() => {
    insights?.chrome?.appObjectId?.(inventoryId);
  }, [inventoryId]);

  return (
    <>
      <DetailWrapper
        hideInvLink
        showTags
        onLoad={({ mergeWithDetail }) => {
          getRegistry().register({
            systemProfileStore,
            ...mergeWithDetail(deviceDetail),
          });
        }}
      >
        <PageHeader>
          <Breadcrumb ouiaId="systems-list">
            <BreadcrumbItem>
              <Link to={uuid ? `/groups` : '/inventory'}>
                {uuid ? 'Groups' : 'Systems'}
              </Link>
            </BreadcrumbItem>
            {uuid && (
              <BreadcrumbItem>
                {groupName ? (
                  <Link to={`/groups/${uuid}`}>{groupName}</Link>
                ) : (
                  <Skeleton size={SkeletonSize.xs} />
                )}
              </BreadcrumbItem>
            )}
            <BreadcrumbItem isActive>
              <div className="ins-c-inventory__detail--breadcrumb-name">
                {entity?.display_name || <Skeleton size={SkeletonSize.xs} />}
              </div>
            </BreadcrumbItem>
          </Breadcrumb>
          <InventoryDetailHead
            fallback=""
            actions={[
              {
                title: 'Update',
                isDisabled:
                  imageData?.UpdateTransactions?.[
                    imageData?.UpdateTransactions.length - 1
                  ]?.Status === 'BUILDING' ||
                  imageData?.UpdateTransactions?.[
                    imageData?.UpdateTransactions.length - 1
                  ]?.Status === 'CREATED' ||
                  !imageData?.ImageInfo?.UpdatesAvailable?.length > 0 ||
                  !updateModal.imageSetId,
                onClick: () => {
                  setUpdateModal((prevState) => ({
                    ...prevState,
                    isOpen: true,
                  }));
                },
              },
            ]}
            hideBack
            hideInvDrawer
          />

          {isDeviceStatusLoading ? (
            <Skeleton size={SkeletonSize.xs} />
          ) : imageData?.UpdateTransactions[
              imageData?.UpdateTransactions?.length - 1
            ]?.Status === 'BUILDING' ||
            imageData?.UpdateTransactions[
              imageData?.UpdateTransactions?.length - 1
            ]?.Status === 'CREATED' ? (
            <Status type="updating" isLabel={true} className="pf-u-mt-sm" />
          ) : imageData?.Device?.UpdateAvailable ? (
            <Status
              type="updateAvailable"
              isLabel={true}
              className="pf-u-mt-sm"
            />
          ) : (
            <Status type="running" isLabel={true} className="pf-u-mt-sm" />
          )}
        </PageHeader>
        <Grid gutter="md">
          <GridItem span={12}>
            <DeviceDetailTabs
              systemProfile={imageData}
              imageId={imageId}
              setUpdateModal={setUpdateModal}
              setReload={setReload}
            />
          </GridItem>
        </Grid>
        {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={() => setReload(true)}
            />
          </Suspense>
        )}
      </DetailWrapper>
    </>
  );
}
Example #23
Source File: Notification.js    From user-preferences-frontend with Apache License 2.0 4 votes vote down vote up
Notification = () => {
  const { bundleName } = useParams();
  const navigateTo = useChromePush();
  const dispatch = useDispatch();
  const store = useSelector(
    ({ notificationPreferences }) => notificationPreferences
  );
  const bundleDisplayTitle = notificationConfigForBundle(bundleName)?.title;

  useEffect(() => {
    register(notificationPreferences);
  }, []);

  useEffect(() => {
    (async () => {
      await insights.chrome.auth.getUser();
      if (bundleName) {
        dispatch(getNotificationSchema({ bundleName }));
      }
    })();
  }, [bundleName]);

  const { isLoaded, schema } = useMemo(() => {
    if (store?.loaded) {
      const schema = { ...store.schema };
      if (schema.fields) {
        schema.fields = [...schema.fields];
        schema.fields[0].sections = [...schema.fields[0].sections];
        schema.fields[0].sections.push({
          fields: unsubscribe,
        });
      } else {
        schema.fields = [];
      }

      return {
        isLoaded: true,
        schema: schema,
      };
    }
    return {
      isLoaded: false,
      schema: [],
    };
  }, [store]);

  const saveValues = useCallback(
    async ({ unsubscribe, ...values }) => {
      const action = saveNotificationValues({ bundleName, values });
      dispatch(action);
      try {
        await action.payload;
        dispatch(
          addNotification({
            dismissable: false,
            variant: 'success',
            title: `Notification preferences successfully saved`,
          })
        );
      } catch (e) {
        dispatch(
          addNotification({
            dismissable: false,
            variant: 'danger',
            title: `Notification preferences unsuccessfully saved`,
          })
        );
      }
    },
    [bundleName]
  );

  return (
    <React.Fragment>
      <PageHeader>
        <Split>
          <SplitItem isFilled>
            <PageHeaderTitle
              className="notif-page-header"
              title={`My Notifications | ${bundleDisplayTitle}`}
            />
            <StackItem>
              This service allows you to opt-in and out of receiving
              notifications. Your Organization Administrator has configured
              which notifications you can or can not receive in their{' '}
              <a
                onClick={(e) =>
                  navigateTo(e, `/settings/notifications/${bundleName}`)
                }
                href={`/settings/notifications/${bundleName}`}
              >
                Settings
              </a>
              .
            </StackItem>
          </SplitItem>
        </Split>
      </PageHeader>
      <Main className="pref-notification">
        <Stack hasGutter>
          <StackItem>
            <Card ouiaId="user-pref-notification-subscriptions-card">
              <CardHeader className="pf-u-pb-0"></CardHeader>
              <CardBody className="pref-notification_form">
                {isLoaded ? (
                  <FormRender
                    componentMapper={{
                      ...componentMapper,
                      [DESCRIPTIVE_CHECKBOX]: DescriptiveCheckbox,
                      [LOADER]: Loader,
                      [DATA_LIST]: DataListLayout,
                    }}
                    FormTemplate={(props) => (
                      <FormTemplate {...props} FormButtons={FormButtons} />
                    )}
                    schema={schema}
                    onSubmit={saveValues}
                  />
                ) : (
                  <Bullseye>
                    <Spinner />
                  </Bullseye>
                )}
              </CardBody>
            </Card>
          </StackItem>
        </Stack>
      </Main>
    </React.Fragment>
  );
}
Example #24
Source File: Email.js    From user-preferences-frontend with Apache License 2.0 4 votes vote down vote up
Email = () => {
  const dispatch = useDispatch();

  const [emailConfig, setEmailConfig] = useState({});
  const isLoaded = useLoaded(async () => {
    await insights.chrome.auth.getUser();
    register(emailPreferences);
    setEmailConfig(await calculateEmailConfig(config, dispatch));
  });

  const store = useSelector(({ emailPreferences }) => emailPreferences);

  const saveValues = async ({ unsubscribe, ...values }) => {
    const promises = Object.entries(emailConfig)
      .filter(([, { isVisible }]) => isVisible === true)
      .map(([application, { localFile, schema, url, apiName }]) => {
        if (
          !localFile &&
          !schema &&
          store?.[application]?.schema &&
          Object.keys(store?.[application]?.schema).length > 0
        ) {
          const action = saveEmailValues({ application, values, url, apiName });
          dispatch(action);

          return {
            promise: action.payload,
            meta: action.meta,
          };
        }
      })
      .filter(Boolean);

    const { success, error } = await distributeSuccessError(promises);
    dispatchMessages(dispatch, success, error);
  };

  const calculateSection = (key, schema) => {
    return getSection(key, schema, store?.[key], (isVisible) => {
      const { ...config } = emailConfig;
      if (isVisible === false) {
        delete config[key];
      } else {
        config[key] = {
          ...config[key],
          isVisible,
        };
      }

      setEmailConfig(config);
    });
  };

  return (
    <React.Fragment>
      <PageHeader>
        <PageHeaderTitle title="Email preferences" />
      </PageHeader>
      <Main className="pref-email">
        <Stack hasGutter>
          <StackItem>
            <YourInformation />
          </StackItem>
          <StackItem>
            <Card ouiaId="user-pref-email-subscriptions-card">
              <CardHeader className="pf-u-pb-0">
                <TextContent>
                  <Text component={TextVariants.h2}>Email subscriptions</Text>
                  <Text component={TextVariants.p}>
                    Select the emails you want to receive.
                  </Text>
                </TextContent>
              </CardHeader>
              <CardBody className="pref-email_form">
                {isLoaded ? (
                  <FormRender
                    componentMapper={{
                      ...componentMapper,
                      [DESCRIPTIVE_CHECKBOX]: DescriptiveCheckbox,
                      [LOADER]: Loader,
                      [DATA_LIST]: DataListLayout,
                    }}
                    FormTemplate={(props) => (
                      <FormTemplate {...props} FormButtons={FormButtons} />
                    )}
                    schema={{
                      fields: [
                        {
                          name: 'email-preferences',
                          component: DATA_LIST,
                          sections: Object.entries(emailConfig).map(
                            ([key, schema]) => calculateSection(key, schema)
                          ),
                        },
                      ],
                    }}
                    onSubmit={saveValues}
                  />
                ) : (
                  <Bullseye>
                    <Spinner />
                  </Bullseye>
                )}
              </CardBody>
            </Card>
          </StackItem>
        </Stack>
      </Main>
    </React.Fragment>
  );
}
Example #25
Source File: UpdateImageModal.js    From edge-frontend with Apache License 2.0 4 votes vote down vote up
UpdateImageModal = ({ updateCveModal, setUpdateCveModal, setReload }) => {
  const dispatch = useDispatch();

  const { getRegistry } = useContext(RegistryContext);
  const { data } = useSelector(
    ({ imageDetailReducer }) => ({
      data: imageDetailReducer?.data || null,
    }),
    shallowEqual
  );

  useEffect(() => {
    const registered = getRegistry().register({
      imageDetailReducer,
    });
    updateCveModal?.imageId &&
      loadImageDetail(dispatch, updateCveModal?.imageId);
    return () => registered();
  }, [dispatch]);

  const handleUpdateModal = () => {
    const payload = {
      Id: data?.image?.ID,
      description: data?.image?.Description,
      name: data?.image?.Name,
      version: data?.image?.Version + 1,
      architecture: 'x86_64',
      credentials: data?.image?.Installer.SshKey,
      username: data?.image?.Installer.Username,
      imageType: data?.image?.OutputTypes,
      'selected-packages': data?.image?.Packages?.map((pack) => ({
        name: pack.Name,
      })),
      release: data?.image?.Distribution,
    };
    handleClose();
    setReload(true);
    createNewImage(dispatch, payload, (resp) => {
      dispatch({
        ...addNotification({
          variant: 'info',
          title: 'Update image',
          description: `${resp.value.Name} image was added to the queue.`,
        }),
        meta: {
          polling: {
            id: `FETCH_IMAGE_${resp.value.ID}_BUILD_STATUS`,
            fetcher: () => getEdgeImageStatus(resp.value.ID),
            condition: (resp) => {
              switch (resp.Status) {
                case 'BUILDING':
                  return [true, ''];
                case 'ERROR':
                  return [false, 'failure'];
                default:
                  return [false, 'success'];
              }
            },
            onEvent: {
              failure: [
                (dispatch) =>
                  dispatch(
                    addNotification({
                      variant: 'danger',
                      title: 'Image build failed',
                      description: `${resp.value.Name} image build is completed unsuccessfully`,
                    })
                  ),
              ],
              success: [
                (dispatch) =>
                  dispatch(
                    addNotification({
                      variant: 'success',
                      title: 'Image is ready',
                      description: `${resp.value.Name} image build is completed`,
                    })
                  ),
                (dispatch) => loadEdgeImageSets(dispatch),
              ],
            },
          },
        },
      });
      loadEdgeImageSets(dispatch);
      dispatch(
        addImageToPoll({ name: data?.image?.Name, id: data?.image?.ID })
      );
    });
  };

  const handleClose = () => {
    setUpdateCveModal((prevState) => ({ ...prevState, isOpen: false }));
  };

  return data ? (
    <Modal
      variant="medium"
      title={`Update image: ${data?.image?.Name}`}
      description="Review the information and click Create image to start the build process"
      isOpen={updateCveModal.isOpen}
      onClose={handleClose}
      //onSubmit={handleUpdateModal}
      actions={[
        <Button key="confirm" variant="primary" onClick={handleUpdateModal}>
          Create Image
        </Button>,
        <Button key="cancel" variant="link" onClick={handleClose}>
          Cancel
        </Button>,
      ]}
    >
      <TextContent>
        <TextListItem component={TextVariants.h3}>
          <Text component={'b'}>Details</Text>
        </TextListItem>
        <TextList component={TextListVariants.dl}>
          <TextListItem component={TextListItemVariants.dt}>Name</TextListItem>
          <TextListItem component={TextListItemVariants.dd}>
            {data?.image?.Name}
          </TextListItem>
          <TextListItem component={TextListItemVariants.dt}>
            Version
          </TextListItem>
          <TextListItem component={TextListItemVariants.dd}>
            {data?.image?.Version + 1}
          </TextListItem>
          <TextListItem component={TextListItemVariants.dt}>
            Description
          </TextListItem>
          <TextListItem component={TextListItemVariants.dd}>
            {data?.image?.Description}
          </TextListItem>
        </TextList>
        <TextListItem component={TextVariants.h3}>
          <Text component={'b'}>Output</Text>
        </TextListItem>
        <TextList component={TextListVariants.dl}>
          <TextListItem component={TextListItemVariants.dt}>
            Release
          </TextListItem>
          <TextListItem component={TextListItemVariants.dd}>
            {releaseMapper[data?.image?.Distribution]}
          </TextListItem>
          <TextListItem component={TextListItemVariants.dt}>
            Output type
          </TextListItem>
          <TextListItem component={TextListItemVariants.dd}>
            {imageTypeMapper[data?.image?.ImageType]}
          </TextListItem>
        </TextList>
        <TextListItem component={TextVariants.h3}>
          <Text component={'b'}>Packages</Text>
        </TextListItem>
        <TextList component={TextListVariants.dl}>
          <TextListItem component={TextListItemVariants.dt}>
            Updated
          </TextListItem>
          <TextListItem
            className="pf-u-pl-lg"
            component={TextListItemVariants.dd}
          >
            {updateCveModal?.cveCount}
          </TextListItem>
        </TextList>
      </TextContent>
    </Modal>
  ) : (
    <Backdrop>
      <Bullseye>
        <Spinner isSVG diameter="100px" />
      </Bullseye>
    </Backdrop>
  );
}
Example #26
Source File: Inventory.js    From edge-frontend with Apache License 2.0 4 votes vote down vote up
Inventory = () => {
  const [response, fetchDevices] = useApi({
    api: getInventory,
    tableReload: true,
  });
  const { data, isLoading, hasError } = response;
  const [isAddDeviceModalOpen, setIsAddDeviceModalOpen] = useState(false);
  const [isRemoveDeviceModalOpen, setIsRemoveDeviceModalOpen] = useState(false);
  const [deviceId, setDeviceId] = useState([]);
  const [checkedDeviceIds, setCheckedDeviceIds] = useState([]);
  const [isRowSelected, setIsRowSelected] = useState(false);
  const [hasModalSubmitted, setHasModalSubmitted] = useState(false);
  const [isCreateGroupModalOpen, setIsCreateGroupModalOpen] = useState(false);
  const [updateModal, setUpdateModal] = useState({
    isOpen: false,
    deviceData: null,
    imageData: null,
  });

  const history = useHistory();

  const handleAddDevicesToGroup = (ids, isRow) => {
    setIsAddDeviceModalOpen(true);
    isRow ? setDeviceId(ids) : setCheckedDeviceIds(ids);
    setIsRowSelected(isRow);
  };

  const handleRemoveDevicesFromGroup = (ids, isRow) => {
    setIsRemoveDeviceModalOpen(true);
    isRow ? setDeviceId(ids) : setCheckedDeviceIds(ids);
    setIsRowSelected(isRow);
  };

  return (
    <Fragment>
      <PageHeader className="pf-m-light">
        <PageHeaderTitle title="Systems" />
      </PageHeader>
      <Main className="edge-devices">
        <DeviceTable
          isSystemsView={true}
          data={data?.data?.devices}
          count={data?.count}
          isLoading={isLoading}
          hasError={hasError}
          setUpdateModal={setUpdateModal}
          handleAddDevicesToGroup={handleAddDevicesToGroup}
          handleRemoveDevicesFromGroup={handleRemoveDevicesFromGroup}
          hasCheckbox={true}
          selectedItems={setCheckedDeviceIds}
          kebabItems={[
            {
              isDisabled: !(checkedDeviceIds.length > 0),
              title: 'Add to group',
              onClick: () =>
                handleAddDevicesToGroup(
                  checkedDeviceIds.map((device) => ({
                    ID: device.deviceID,
                    name: device.display_name,
                  })),
                  false
                ),
            },
          ]}
          hasModalSubmitted={hasModalSubmitted}
          setHasModalSubmitted={setHasModalSubmitted}
          fetchDevices={fetchDevices}
        />
      </Main>
      {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>
      )}
      {isAddDeviceModalOpen && (
        <AddDeviceModal
          isModalOpen={isAddDeviceModalOpen}
          setIsModalOpen={setIsAddDeviceModalOpen}
          setIsCreateGroupModalOpen={setIsCreateGroupModalOpen}
          reloadData={() => {
            fetchDevices();
            setTimeout(() => setHasModalSubmitted(true), 800);
          }}
          deviceIds={isRowSelected ? deviceId : checkedDeviceIds}
        />
      )}
      {isCreateGroupModalOpen && (
        <CreateGroupModal
          isModalOpen={isCreateGroupModalOpen}
          setIsModalOpen={setIsCreateGroupModalOpen}
          reloadData={() => {
            fetchDevices();
            setTimeout(() => setHasModalSubmitted(true), 800);
          }}
          deviceIds={isRowSelected ? deviceId : checkedDeviceIds}
        />
      )}
      {isRemoveDeviceModalOpen && (
        <RemoveDeviceModal
          isModalOpen={isRemoveDeviceModalOpen}
          setIsModalOpen={setIsRemoveDeviceModalOpen}
          reloadData={() => {
            fetchDevices();
            setTimeout(() => setHasModalSubmitted(true), 800);
          }}
          deviceInfo={isRowSelected ? deviceId : checkedDeviceIds}
        />
      )}
    </Fragment>
  );
}
Example #27
Source File: UpdateDeviceModal.js    From edge-frontend with Apache License 2.0 4 votes vote down vote up
UpdateDeviceModal = ({ updateModal, setUpdateModal, refreshTable }) => {
  const [imageData, setImageData] = useState(null);
  const dispatch = useDispatch();
  const isMultiple = updateModal.deviceData.length > 1;
  const deviceId = updateModal.deviceData.map((device) => device.id);
  const deviceName = isMultiple
    ? updateModal.deviceData.map((device) => device.display_name)
    : updateModal?.deviceData[0]?.display_name;

  useEffect(() => {
    updateModal?.imageSetId
      ? getImageSet({
          id: updateModal.imageSetId,
          q: {
            limit: 1,
            sort_by: '-created_at',
            status: 'SUCCESS',
          },
        }).then((data) => setImageData(data.Data.images[0]))
      : getImageData(updateModal.imageId).then((data) =>
          setImageData(data.Data.images[0])
        );
  }, []);

  const handleUpdateModal = async () => {
    try {
      await updateDeviceLatestImage({
        DevicesUUID: deviceId,
      });
      dispatch({
        ...addNotification({
          variant: 'info',
          title: 'Updating device',
          description: isMultiple
            ? ` ${deviceName.length} systems were added to the queue.`
            : ` ${deviceName} was added to the queue.`,
        }),
      });
    } catch (err) {
      dispatch({
        ...addNotification({
          variant: 'danger',
          title: 'Updating a device was unsuccessful',
          description: `Response: ${err.statusText}`,
        }),
      });
    }

    handleClose();
    refreshTable ? refreshTable() : null;
  };

  const handleClose = () => {
    setUpdateModal((prevState) => {
      return {
        ...prevState,
        isOpen: false,
      };
    });
  };

  const WarningText = () => (
    <TextContent className="pf-u-pt-md">
      <Text
        style={{ color: 'var(--pf-global--palette--gold-500)' }}
        component="small"
      >
        <ExclamationTriangleIcon /> After the update is installed, the device
        will apply the changes.
      </Text>
    </TextContent>
  );

  const Description = () => (
    <TextContent>
      <Text>
        Update{' '}
        <span className="pf-u-font-weight-bold pf-u-font-size-md">
          {isMultiple ? `${deviceName.length} systems` : deviceName}
        </span>{' '}
        to latest version of the image linked to it.
      </Text>
    </TextContent>
  );

  const updateToDetails = {
    title: `Update to version ${imageData?.image.Version}`,
    rows: [
      { title: 'Image Name', value: imageData?.image.Name },
      { title: 'Version', value: imageData?.image.Version },
      {
        title: 'Created',
        value: <DateFormat date={imageData?.image.CreatedAt} />,
      },
      {
        title: 'Release',
        value: distributionMapper[imageData?.image.Distribution],
      },
    ],
  };

  const packageDetails = {
    title: `Changes from version ${imageData?.image.Version - 1}`,
    rows: [
      { title: 'Added', value: imageData?.update_added || 0 },
      { title: 'Removed', value: imageData?.update_removed || 0 },
      { title: 'Updated', value: imageData?.update_updated || 0 },
    ],
  };

  const updateSchema = {
    fields: [
      {
        component: componentTypes.PLAIN_TEXT,
        name: 'description',
        label: Description(),
      },
      {
        component: componentTypes.PLAIN_TEXT,
        name: 'update-details',
        label: BuildModalReview({
          reviewObject: updateToDetails,
          key: 'update-details',
        }),
      },
      {
        component: componentTypes.PLAIN_TEXT,
        name: 'package-details',
        label: BuildModalReview({
          reviewObject: packageDetails,
          key: 'package-details',
        }),
      },
      {
        component: componentTypes.PLAIN_TEXT,
        name: 'warning-text',
        label: WarningText(),
      },
    ],
  };

  return (
    <>
      {imageData ? (
        <Modal
          size="medium"
          title={`Update system${
            isMultiple ? 's' : ''
          } to latest image version`}
          isOpen={updateModal.isOpen}
          openModal={() =>
            setUpdateModal((prevState) => ({ ...prevState, isOpen: false }))
          }
          submitLabel="Update Device"
          schema={updateSchema}
          onSubmit={handleUpdateModal}
          reloadData={refreshTable}
        />
      ) : (
        <Backdrop>
          <Bullseye>
            <Spinner isSVG diameter="100px" />
          </Bullseye>
        </Backdrop>
      )}
    </>
  );
}
Example #28
Source File: GroupTable.js    From edge-frontend with Apache License 2.0 4 votes vote down vote up
GroupTable = ({
  data,
  count,
  isLoading,
  hasError,
  handleCreateModal,
  handleRenameModal,
  handleDeleteModal,
  fetchGroups,
}) => {
  const [updateModal, setUpdateModal] = useState({
    isOpen: false,
    deviceData: null,
    imageData: null,
  });

  const actionResolver = (rowData) => {
    if (!rowData?.rowInfo) return [];
    const { id, title, devices, devicesImageInfo } = rowData?.rowInfo;
    const hasUpdate = devicesImageInfo?.some((image) => image.UpdateAvailable);

    return (
      id && [
        {
          title: 'Rename',
          onClick: () => handleRenameModal(id, title),
        },
        {
          title: 'Delete',
          onClick: () => handleDeleteModal(id, title),
        },
        {
          title: 'Update',
          onClick: () =>
            setUpdateModal((prevState) => ({
              ...prevState,
              deviceData: devices.map((device) => ({
                id: device.UUID,
                display_name: device.Name,
              })),
              imageId: devices.find((device) => device?.ImageID).ImageID,
              isOpen: true,
            })),
          isDisabled:
            devices.length > 0
              ? !(rowData?.rowInfo?.hasValidUpdate && hasUpdate)
              : true,
        },
      ]
    );
  };

  const buildRows = data?.map((rowData) => {
    const { ID, Name, Devices } = rowData?.DeviceGroup;
    let { DevicesImageInfo } = rowData;
    if (!DevicesImageInfo) {
      DevicesImageInfo = [];
    }
    const systems = Devices ?? [];
    const image = (
      <div>
        <Tooltip
          content={
            <div>
              {DevicesImageInfo.map((device, index) => (
                <p key={index}>{device.Name}</p>
              ))}
            </div>
          }
        >
          <span>Multiple images</span>
        </Tooltip>
      </div>
    );

    return {
      rowInfo: {
        id: ID,
        title: Name,
        image:
          DevicesImageInfo.length === 0
            ? '-'
            : DevicesImageInfo.length > 1
            ? 'Multiple images'
            : DevicesImageInfo[0]?.Name,
        devicesImageInfo: rowData.DevicesImageInfo,
        devices: Devices,
        hasValidUpdate: rowData?.DeviceGroup?.ValidUpdate,
      },
      cells: [
        {
          title: <Link to={`${paths['fleet-management']}/${ID}`}>{Name}</Link>,
        },
        {
          title: systems.length,
        },
        {
          title:
            DevicesImageInfo.length === 0
              ? '-'
              : DevicesImageInfo.length > 1
              ? image
              : DevicesImageInfo[0]?.Name,
        },
      ],
    };
  });

  return (
    <>
      <GeneralTable
        apiFilterSort={true}
        isUseApi={true}
        loadTableData={fetchGroups}
        filters={filters}
        tableData={{
          count,
          data,
          isLoading,
          hasError,
        }}
        columnNames={columns}
        rows={buildRows}
        actionResolver={actionResolver}
        areActionsDisabled={() => false}
        defaultSort={{ index: 0, direction: 'asc' }}
        emptyFilterState={{
          title: 'No matching groups found',
          body: 'To continue, edit your filter settings and try again',
        }}
        toolbarButtons={[
          {
            title: 'Create group',
            click: handleCreateModal,
          },
        ]}
      />
      {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={fetchGroups}
          />
        </Suspense>
      )}
    </>
  );
}
Example #29
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}
        />
      )}
    </>
  );
}