webpack/scenes/ContentViews/Details/Repositories/ContentViewRepositories.js in katello-4.8.4 vs webpack/scenes/ContentViews/Details/Repositories/ContentViewRepositories.js in katello-4.9.0.rc1
- old
+ new
@@ -1,11 +1,11 @@
import React, {
useCallback,
useEffect,
useState,
} from 'react';
-
+import { FormattedMessage } from 'react-intl';
import { translate as __ } from 'foremanReact/common/I18n';
import { urlBuilder } from 'foremanReact/common/urlHelpers';
import { STATUS } from 'foremanReact/constants';
import {
lowerCase,
@@ -37,10 +37,11 @@
Tbody,
Tr,
Th,
Td,
} from '@patternfly/react-table';
+import { useKatelloDocUrl } from '../../../../utils/useKatelloDocUrl';
import AddedStatusLabel from '../../../../components/AddedStatusLabel';
import SelectableDropdown from '../../../../components/SelectableDropdown';
import TableWrapper from '../../../../components/Table/TableWrapper';
import {
ADDED,
@@ -72,23 +73,40 @@
const repoTypeNames = {
docker: 'Container',
ostree: 'OSTree',
};
+const NoReposInOrgCallsToAction = () => (
+ <FormattedMessage
+ id="truly-empty-calls-to-action"
+ defaultMessage={__('{enableRedHatRepos} or {createACustomProduct}.')}
+ values={{
+ enableRedHatRepos: (
+ <a href="/redhat_repositories" id="empty-state-primary-action-enable-red-hat-repos">{__('Enable Red Hat repositories')}</a>
+ ),
+ createACustomProduct: (
+ <a href="/products/" id="empty-state-primary-action-create-a-custom-product">{__('create a custom product')}</a>
+ ),
+ }}
+ />
+);
+
const ContentViewRepositories = ({ cvId, details }) => {
const dispatch = useDispatch();
const response = useSelector(state => selectCVRepos(state, cvId), shallowEqual);
const { results, ...metadata } = response;
+ const { org_repository_count: orgRepositoryCount } = metadata;
+ const [isLoading, setLoading] = useState(false);
const status = useSelector(state => selectCVReposStatus(state, cvId), shallowEqual);
const error = useSelector(state => selectCVReposError(state, cvId), shallowEqual);
const repoTypesResponse = useSelector(state => selectRepoTypes(state), shallowEqual);
const repoTypesStatus = useSelector(state => selectRepoTypesStatus(state), shallowEqual);
const { permissions, generated_for: generatedFor, import_only: importOnly } = details;
const generatedContentView = generatedFor !== 'none';
const [searchQuery, updateSearchQuery] = useState('');
const [typeSelected, setTypeSelected] = useState(allRepositories);
- const [statusSelected, setStatusSelected] = useState(ALL_STATUSES);
+ const [statusSelected, setStatusSelected] = useState(ADDED);
// repoTypes object format: [displayed_value]: API_value
const [repoTypes, setRepoTypes] = useState({});
const [bulkActionOpen, setBulkActionOpen] = useState(false);
const { repository_ids: repositoryIds = [] } = details;
const resetFilters = () => {
@@ -119,14 +137,22 @@
__('Sync state'),
__('Content'),
__('Status'),
];
+ const documentationUrl = useKatelloDocUrl('Managing_Content', '#Products_and_Repositories_content-management');
+
useEffect(() => {
dispatch(getRepositoryTypes());
}, []); // eslint-disable-line react-hooks/exhaustive-deps
+ useEffect(() => {
+ if (status !== STATUS.PENDING) {
+ setLoading(false);
+ }
+ }, [status]);
+
// Get repo type filter selections dynamically from the API
useDeepCompareEffect(() => {
if (repoTypesStatus === STATUS.RESOLVED && repoTypesResponse) {
const allRepoTypes = {};
allRepoTypes[allRepositories] = 'all';
@@ -147,18 +173,20 @@
const onAdd = (repos) => {
dispatch(updateContentView(
cvId,
{ repository_ids: repositoryIds.concat(repos) },
- () =>
+ () => {
dispatch(getContentViewRepositories(
cvId,
typeSelected !== 'All repositories' ? {
content_type: repoTypes[typeSelected],
} : {},
- statusSelected,
- )),
+ ADDED,
+ ));
+ setStatusSelected(ADDED);
+ },
));
};
const onRemove = (repos) => {
const reposToDelete = [].concat(repos);
@@ -190,10 +218,15 @@
repositoryIds.includes(selectedRepo.id)).map(({ id }) => id);
selectNone();
onRemove(reposToDelete);
};
+ const handleShow = () => {
+ setLoading(true);
+ setStatusSelected(ALL_STATUSES);
+ };
+
const rowDropdownItems = ({ id }) => [
{
title: 'Add',
ouiaId: `add-repository-${id}`,
isDisabled: importOnly || generatedContentView || repositoryIds.includes(id),
@@ -215,12 +248,39 @@
const allParams = { ...params };
if (typeSelected !== 'All repositories') allParams.content_type = repoTypes[typeSelected];
return getContentViewRepositories(cvId, allParams, statusSelected);
}, [cvId, repoTypes, statusSelected, typeSelected]);
- const emptyContentTitle = __("You currently don't have any repositories to add to this content view.");
- const emptyContentBody = __('Please add some repositories.'); // needs link
+ const noResults = (results && results.length === 0) && !searchQuery && status === STATUS.RESOLVED;
+ const emptyContentOverride =
+ noResults && statusSelected === ADDED;
+ const noReposInOrg =
+ (orgRepositoryCount === 0);
+ const emptyContentTitles = {
+ addRepos: __('No repositories added yet'),
+ noReposInOrg: __('No repositories available to add'),
+ };
+ const emptyContentBodies = {
+ addRepos: __('Click to see repositories available to add.'),
+ noReposInOrg: '',
+ };
+
+ const showPrimaryAction = emptyContentOverride || noReposInOrg;
+ const primaryActionButton = noReposInOrg ? (
+ <span style={{ fontSize: 'larger' }}><NoReposInOrgCallsToAction /></span>
+ ) : (
+ <Button ouiaId="empty-state-primary-action-button" onClick={handleShow}>
+ {__('Show repositories')}
+ </Button>
+ );
+
+ const secondaryActionLink = noReposInOrg ? documentationUrl : undefined;
+ const secondaryActionTitle = noReposInOrg ? __('View documentation') : undefined;
+ const emptyContentTitle =
+ noReposInOrg ? emptyContentTitles.noReposInOrg : emptyContentTitles.addRepos;
+ const emptyContentBody =
+ noReposInOrg ? emptyContentBodies.noReposInOrg : emptyContentBodies.addRepos;
const emptySearchTitle = __('No matching repositories found');
const emptySearchBody = __('Try changing your search settings.');
const activeFilters = [typeSelected, statusSelected];
const defaultFilters = [allRepositories, ALL_STATUSES];
@@ -228,10 +288,12 @@
<DropdownItem ouiaId="bulk-remove-repositories" aria-label="bulk_remove" key="bulk_remove" isDisabled={!hasAddedSelected} component="button" onClick={removeBulk}>
{__('Remove')}
</DropdownItem>,
];
+ const loadingStatus = (isLoading || status === STATUS.PENDING) ? STATUS.PENDING : status;
+
return (
<TableWrapper
{...{
metadata,
emptyContentTitle,
@@ -239,16 +301,24 @@
emptySearchTitle,
emptySearchBody,
searchQuery,
updateSearchQuery,
error,
- status,
activeFilters,
defaultFilters,
selectedCount,
selectNone,
resetFilters,
+ emptyContentOverride,
+ showPrimaryAction,
+ primaryActionButton,
+ secondaryActionLink,
+ secondaryActionTitle,
}}
+ status={loadingStatus}
+ showSecondaryAction={noReposInOrg}
+ showSecondaryActionButton={noReposInOrg}
+ secondaryActionTargetBlank={noReposInOrg}
ouiaId="content-view-repositories-table"
{...selectAll}
variant={TableVariant.compact}
autocompleteEndpoint="/katello/api/v2/repositories"
bookmarkController="katello_content_view_repositories"