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"