import React, { useState, useEffect, useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { TableVariant, TableText, Tbody, Thead, Td, Tr, Th } from '@patternfly/react-table'; import { Checkbox, Dropdown, DropdownItem, Grid, KebabToggle, GridItem, Button } from '@patternfly/react-core'; import { translate as __ } from 'foremanReact/common/I18n'; import { urlBuilder } from 'foremanReact/common/urlHelpers'; import { STATUS } from 'foremanReact/constants'; import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; import { first } from 'lodash'; import { useSelectionSet } from '../../../../components/Table/TableHooks'; import TableWrapper from '../../../../components/Table/TableWrapper'; import InactiveText from '../../components/InactiveText'; import ContentViewVersionEnvironments from './ContentViewVersionEnvironments'; import ContentViewVersionErrata from './ContentViewVersionErrata'; import ContentViewVersionContent from './ContentViewVersionContent'; import getContentViewDetails, { getContentViewVersions } from '../ContentViewDetailActions'; import { selectCVVersions, selectCVVersionsStatus, selectCVVersionsError, selectCVDetails, } from '../ContentViewDetailSelectors'; import getEnvironmentPaths from '../../components/EnvironmentPaths/EnvironmentPathActions'; import ContentViewVersionPromote from '../Promote/ContentViewVersionPromote'; import TaskPresenter from '../../components/TaskPresenter/TaskPresenter'; import RemoveCVVersionWizard from './Delete/RemoveCVVersionWizard'; import { hasPermission } from '../../helpers'; import BulkDeleteModal from './BulkDelete/BulkDeleteModal'; import { selectEnvironmentPathsStatus } from '../../components/EnvironmentPaths/EnvironmentPathSelectors'; import PublishContentViewWizard from '../../Publish/PublishContentViewWizard'; import CVVersionCompare from './Compare/CVVersionCompare'; const ContentViewVersions = ({ cvId, details }) => { const response = useSelector(state => selectCVVersions(state, cvId)); const { results, ...metadata } = response; const { versions: cvDetails } = useSelector(state => selectCVDetails(state, cvId)); const firstIdWithActiveHistory = results?.find(({ active_history: activeHistory }) => activeHistory?.length)?.id; const status = useSelector(state => selectCVVersionsStatus(state, cvId)); const error = useSelector(state => selectCVVersionsError(state, cvId)); const envStatus = useSelector(state => selectEnvironmentPathsStatus(state, cvId)); const dispatch = useDispatch(); const [searchQuery, updateSearchQuery] = useState(''); const [versionIdToPromote, setVersionIdToPromote] = useState(''); const [versionNameToPromote, setVersionNameToPromote] = useState(''); const [versionIdToRemove, setVersionIdToRemove] = useState(''); const [versionNameToRemove, setVersionNameToRemove] = useState(''); const [versionEnvironments, setVersionEnvironments] = useState([]); const [bulkDeleteModalOpen, setBulkDeleteModalOpen] = useState(false); const [isPublishModalOpen, setIsPublishModalOpen] = useState(false); const [promoting, setPromoting] = useState(false); const [removingFromEnv, setRemovingFromEnv] = useState(false); const [deleteVersion, setDeleteVersion] = useState(false); const [currentStep, setCurrentStep] = useState(1); const { permissions } = details; const [kebabOpen, setKebabOpen] = useState(false); const hasActionPermissions = hasPermission(permissions, 'promote_or_remove_content_views'); const hasPublishCvPermissions = hasPermission(permissions, 'publish_content_views'); const renderActionButtons = hasActionPermissions && status === STATUS.RESOLVED && !!results?.length; const renderPublishCvModal = hasPublishCvPermissions && status === STATUS.RESOLVED && isPublishModalOpen; const { selectOne, isSelected, isSelectable: _isSelectable, selectedCount, selectionSet, ...selectionSetVars } = useSelectionSet({ results, metadata, }); const showPrimaryAction = true; const primaryActionButton = ( ); const versionOneId = String([...selectionSet].sort()[0]); const versionTwoId = String([...selectionSet].sort()[1]); const fetchItems = useCallback((params) => { selectionSet.clear(); return getContentViewVersions(cvId, params); }, [cvId, selectionSet]); const columnHeaders = [ '', __('Version'), __('Environments'), __('Packages'), __('Errata'), __('Additional content'), __('Description'), ]; const versionOneLabel = String(cvDetails?.find(version => Number(version.id) === Number(versionOneId))?.version); const versionTwoLabel = String(cvDetails?.find(version => Number(version.id) === Number(versionTwoId))?.version); useEffect( () => { if (envStatus !== STATUS.RESOLVED) { dispatch(getEnvironmentPaths()); } }, // We only want this to fire once // eslint-disable-next-line react-hooks/exhaustive-deps [dispatch], ); const buildCells = (cvVersion) => { const { version, description, id: versionId, environments, rpm_count: packageCount, errata_counts: errataCounts, active_history: activeHistory, } = cvVersion; if (activeHistory?.length) { return [ '', {__('Version ')}{version}, , '', '', '', description ? {description} : , ]; } return [ selectOne(selected, versionId)} />, {__('Version ')}{version}, , Number(packageCount) ? {packageCount} : , , , description ? {description} : , ]; }; const onPromote = ({ cvVersionId, cvVersionName, cvVersionEnvironments }) => { setVersionIdToPromote(cvVersionId); setVersionNameToPromote(cvVersionName); setVersionEnvironments(cvVersionEnvironments); setPromoting(true); }; const onRemoveFromEnv = ({ cvVersionId, cvVersionName, cvVersionEnvironments, deleting, }) => { setVersionIdToRemove(cvVersionId); setVersionNameToRemove(cvVersionName); setVersionEnvironments(cvVersionEnvironments); setRemovingFromEnv(true); setDeleteVersion(deleting); }; const rowDropdownItems = ({ version, id: versionId, environments, }) => [ { title: __('Promote'), onClick: () => { onPromote({ cvVersionId: versionId, cvVersionName: version, cvVersionEnvironments: environments, }); }, }, { title: __('Remove from environments'), onClick: () => { onRemoveFromEnv({ cvVersionId: versionId, cvVersionName: version, cvVersionEnvironments: environments, deleting: false, }); }, }, { title: __('Delete'), onClick: () => { selectionSet.clear(); selectOne(true, versionId); setBulkDeleteModalOpen(true); }, }, ]; const emptyContentTitle = __("You currently don't have any versions for this content view."); const emptyContentBody = __('Versions will appear here when the content view is published.'); const emptySearchTitle = __('No matching version found'); const emptySearchBody = __('Try changing your search settings.'); const selectedVersionIds = { versionOneId, versionTwoId, }; const [hasTwoVersions, setHasTwoVersions] = useState(false); if (hasTwoVersions) { const versionLabels = { versionOneLabel, versionTwoLabel, }; return ( ); } return ( {renderPublishCvModal && { if (step3) dispatch(getContentViewDetails(cvId)); setIsPublishModalOpen(false); }} aria-label="publish_content_view_modal" />} {renderActionButtons && ( } isOpen={kebabOpen} isPlain dropdownItems={[ { setKebabOpen(false); setBulkDeleteModalOpen(true); }} > {__('Delete')} ]} /> )} } displaySelectAllCheckbox={renderActionButtons} > {bulkDeleteModalOpen && selectionSet.has(id))} onClose={() => { selectionSet.clear(); setBulkDeleteModalOpen(false); }} /> } {promoting && } {removingFromEnv && } {columnHeaders.map((title, index) => { if (index === 0 && !hasActionPermissions) return undefined; return {title}; })} {results?.map((cvVersion) => { const hasHistory = !!cvVersion?.active_history?.length; const cells = buildCells(cvVersion); return ( {cells.map((cell, index) => { if (index === 0 && !hasActionPermissions) return undefined; return ( {cell} ); })} {(!hasHistory && hasActionPermissions) && } ); })} ); }; ContentViewVersions.propTypes = { cvId: PropTypes.number.isRequired, details: PropTypes.shape({ permissions: PropTypes.shape({}), }).isRequired, }; export default ContentViewVersions;