import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; import { capitalize, upperCase, omit } from 'lodash'; import { translate as __ } from 'foremanReact/common/I18n'; import { STATUS } from 'foremanReact/constants'; import { Button, Checkbox, Drawer, DrawerActions, DrawerCloseButton, DrawerContent, DrawerContentBody, DrawerHead, DrawerPanelContent, DrawerPanelBody, Dropdown, DropdownItem, KebabToggle, Text, TextContent, TextList, TextListItem, TextListItemVariants, TextListVariants, TextVariants, } from '@patternfly/react-core'; import { TableVariant, Tbody, Td, Th, Thead, Tr, ActionsColumn } from '@patternfly/react-table'; import { useSelectionSet } from 'foremanReact/components/PF4/TableIndexPage/Table/TableHooks'; import { useTableSort } from 'foremanReact/components/PF4/Helpers/useTableSort'; import TableWrapper from '../../../components/Table/TableWrapper'; import { selectAlternateContentSources, selectAlternateContentSourcesError, selectAlternateContentSourcesStatus, } from '../ACSSelectors'; import getAlternateContentSources, { deleteACS, bulkDeleteACS, getACSDetails, refreshACS, bulkRefreshACS } from '../ACSActions'; import ACSCreateWizard from '../Create/ACSCreateWizard'; import LastSync from '../../ContentViews/Details/Repositories/LastSync'; import ACSExpandableDetails from '../Details/ACSExpandableDetails'; import './ACSTable.scss'; import Loading from '../../../components/Loading'; import EmptyStateMessage from '../../../components/Table/EmptyStateMessage'; import { hasPermission } from '../../ContentViews/helpers'; const ACSTable = () => { const response = useSelector(selectAlternateContentSources); const status = useSelector(selectAlternateContentSourcesStatus); const error = useSelector(selectAlternateContentSourcesError); const resolved = status === STATUS.RESOLVED; const [searchQuery, updateSearchQuery] = useState(''); const [isCreateWizardOpen, setIsCreateWizardOpen] = useState(false); const dispatch = useDispatch(); const metadata = omit(response, ['results']); const { can_create: canCreate = false, can_edit: canEdit = false, can_delete: canDelete = false, can_view: canView = false, results, } = response; const { pathname } = useLocation(); const { push } = useHistory(); const [acsId, setAcsId] = useState(pathname.split('/')[2]); const [expandedId, setExpandedId] = useState(acsId); const [isExpanded, setIsExpanded] = useState(false); const drawerRef = useRef(null); const [kebabOpen, setKebabOpen] = useState(false); const [detailsKebabOpen, setDetailsKebabOpen] = useState(false); const [deleting, setDeleting] = useState(false); const renderActionButtons = status === STATUS.RESOLVED && !!results?.length; const { selectOne, isSelected, isSelectable: _isSelectable, selectedCount, selectionSet, ...selectionSetVars } = useSelectionSet({ results, metadata, }); useEffect(() => { if (acsId) { dispatch(getACSDetails(acsId)); setExpandedId(acsId); setIsExpanded(true); } }, [dispatch, acsId]); const onExpand = () => drawerRef.current && drawerRef.current.focus(); const onDelete = (id) => { setDeleting(true); dispatch(deleteACS(id, () => { setDeleting(false); if (id?.toString() === acsId?.toString()) { push('/alternate_content_sources'); } else { dispatch(getAlternateContentSources()); } })); }; const onRefresh = (id) => { dispatch(refreshACS(id, () => dispatch(getAlternateContentSources()))); }; const onBulkDelete = (ids) => { setDeleting(true); dispatch(bulkDeleteACS({ ids }, () => { setDeleting(false); if (acsId && ids.has(Number(acsId))) { push('/alternate_content_sources'); } else { dispatch(getAlternateContentSources()); } })); }; const onBulkRefresh = (ids) => { dispatch(bulkRefreshACS({ ids }, () => dispatch(getAlternateContentSources()))); }; const createButtonOnclick = () => { setIsCreateWizardOpen(true); }; const onCloseClick = () => { setExpandedId(null); setAcsId(null); window.history.replaceState(null, '', '/alternate_content_sources'); setIsExpanded(false); }; const onClick = (id) => { if (Number(id) === Number(expandedId)) { onCloseClick(); } else { setExpandedId(id); setAcsId(id); window.history.replaceState(null, '', `/alternate_content_sources/${id}/details`); setIsExpanded(true); } }; const isSingleSelected = rowId => (Number(rowId) === Number(acsId) || Number(rowId) === Number(expandedId)); const customStyle = { borderLeft: '5px solid var(--pf-global--primary-color--100)', }; const PanelContent = () => { if (!resolved) return <>; const acs = results?.find(source => source?.id === Number(expandedId)); if (!acs && isExpanded) { setExpandedId(null); setIsExpanded(false); } const { last_refresh: lastTask, permissions } = acs ?? {}; const { last_refresh_words: lastRefreshWords, started_at: startedAt } = lastTask ?? {}; return ( {results && isExpanded &&
{acs?.name} {__('Last refresh :')}
} {error && } {hasPermission(permissions, 'edit_alternate_content_sources') && <> } isOpen={detailsKebabOpen} ouiaId="acs-details-actions" isPlain dropdownItems={[ { setDetailsKebabOpen(false); onDelete(acs?.id); }} > {__('Delete')} ]} /> }
); }; const columnHeaders = [ __('Name'), __('Type'), __('Last refresh'), ]; const COLUMNS_TO_SORT_PARAMS = { [columnHeaders[0]]: 'name', [columnHeaders[1]]: 'alternate_content_source_type', }; const { pfSortParams, apiSortParams, activeSortColumn, activeSortDirection, } = useTableSort({ allColumns: columnHeaders, columnsToSortParams: COLUMNS_TO_SORT_PARAMS, initialSortColumnName: 'Name', }); const fetchItems = useCallback( params => getAlternateContentSources({ ...apiSortParams, ...params, }), [apiSortParams], ); const actionsWithPermissions = (acs) => { const { id, permissions } = acs; const deleteAction = { title: __('Delete'), ouiaId: `remove-acs-${id}`, onClick: () => { onDelete(id); }, }; const refreshAction = { title: __('Refresh'), ouiaId: `refresh-acs-${id}`, onClick: () => { onRefresh(id); }, }; return [ ...(hasPermission(permissions, 'destroy_alternate_content_sources') ? [deleteAction] : []), ...(hasPermission(permissions, 'edit_alternate_content_sources') ? [refreshAction] : []), ]; }; const primaryActionButton = ( ); const emptyContentTitle = __("You currently don't have any alternate content sources."); const emptyContentBody = canCreate ? __('An alternate content source can be added by using the "Add source" button below.') : ''; const emptySearchTitle = __('No matching alternate content sources found'); const emptySearchBody = __('Try changing your search settings.'); const showPrimaryAction = canCreate; /* eslint-disable react/no-array-index-key */ if (deleting) { return ; } return (
} style={{ minHeight: '80vH' }}> {renderActionButtons && canCreate && } {renderActionButtons && (canEdit || canDelete) && } isOpen={kebabOpen} ouiaId="acs-bulk-actions" isPlain dropdownItems={[ { setKebabOpen(false); onBulkRefresh(selectionSet); }} > {__('Refresh')} , { setKebabOpen(false); onBulkDelete(selectionSet); }} > {__('Delete')} , ]} />} {isCreateWizardOpen && } } displaySelectAllCheckbox={renderActionButtons} hideSearch={!canView} > {columnHeaders.map(col => ( {col} ))} {results?.map((acs, index) => { const { name, id, alternate_content_source_type: acsType, last_refresh: lastTask, permissions, } = acs; const { last_refresh_words: lastRefreshWords, started_at: startedAt, } = lastTask ?? {}; return ( selectOne(selected, id)} /> onClick(id)} component="a" ouiaId={`acs-link-text-${index}`}>{name} {acsType === 'rhui' ? upperCase(acsType) : capitalize(acsType)} {(hasPermission(permissions, 'destroy_alternate_content_sources') || hasPermission(permissions, 'edit_alternate_content_sources')) ? : } ); }) }
); /* eslint-enable react/no-array-index-key */ }; export default ACSTable;