import React, { useState, useCallback } from 'react';
import useDeepCompareEffect from 'use-deep-compare-effect';
import { useDispatch, useSelector } from 'react-redux';
import {
Bullseye, Split, SplitItem, Button, ActionList,
ActionListItem, Dropdown, DropdownItem, KebabToggle,
} from '@patternfly/react-core';
import { TableVariant, fitContent, TableText } from '@patternfly/react-table';
import { PencilAltIcon } from '@patternfly/react-icons';
import { STATUS } from 'foremanReact/constants';
import { translate as __ } from 'foremanReact/common/I18n';
import { urlBuilder } from 'foremanReact/common/urlHelpers';
import PropTypes from 'prop-types';
import TableWrapper from '../../../../components/Table/TableWrapper';
import onSelect from '../../../../components/Table/helpers';
import {
selectCVComponents,
selectCVComponentsStatus,
selectCVComponentsError,
selectCVComponentAddStatus,
selectCVComponentRemoveStatus,
} from '../ContentViewDetailSelectors';
import getContentViewDetails, {
addComponent, getContentViewComponents,
removeComponent,
} from '../ContentViewDetailActions';
import AddedStatusLabel from '../../../../components/AddedStatusLabel';
import ComponentVersion from './ComponentVersion';
import ComponentEnvironments from './ComponentEnvironments';
import ContentViewIcon from '../../components/ContentViewIcon';
import { ADDED, ALL_STATUSES, NOT_ADDED } from '../../ContentViewsConstants';
import SelectableDropdown from '../../../../components/SelectableDropdown/SelectableDropdown';
import '../../../../components/EditableTextInput/editableTextInput.scss';
import ComponentContentViewAddModal from './ComponentContentViewAddModal';
import ComponentContentViewBulkAddModal from './ComponentContentViewBulkAddModal';
import { hasPermission } from '../../helpers';
import InactiveText from '../../components/InactiveText';
import { truncate } from '../../../../utils/helpers';
const ContentViewComponents = ({ cvId, details }) => {
const response = useSelector(state => selectCVComponents(state, cvId));
const status = useSelector(state => selectCVComponentsStatus(state, cvId));
const error = useSelector(state => selectCVComponentsError(state, cvId));
const { results, ...metadata } = response;
const componentAddedStatus = useSelector(state => selectCVComponentAddStatus(state, cvId));
const componentRemovedStatus = useSelector(state => selectCVComponentRemoveStatus(state, cvId));
const [rows, setRows] = useState([]);
const [searchQuery, updateSearchQuery] = useState('');
const [statusSelected, setStatusSelected] = useState(ALL_STATUSES);
const [versionEditing, setVersionEditing] = useState(false);
const [compositeCvEditing, setCompositeCvEditing] = useState(null);
const [componentCvEditing, setComponentCvEditing] = useState(null);
const [componentLatest, setComponentLatest] = useState(false);
const [componentId, setComponentId] = useState(null);
const [componentVersionId, setComponentVersionId] = useState(null);
const [selectedComponentsToAdd, setSelectedComponentsToAdd] = useState(null);
const [bulkAdding, setBulkAdding] = useState(false);
const [bulkActionOpen, setBulkActionOpen] = useState(false);
const dispatch = useDispatch();
const resetFilters = () => setStatusSelected(ALL_STATUSES);
const columnHeaders = [
{ title: __('Type'), transforms: [fitContent] },
{ title: __('Name') },
{ title: __('Version') },
{ title: __('Environments') },
{ title: __('Repositories') },
{ title: __('Status') },
{ title: __('Description') },
];
const loading = status === STATUS.PENDING;
const addComponentsResolved = componentAddedStatus === STATUS.RESOLVED;
const removeComponentsResolved = componentRemovedStatus === STATUS.RESOLVED;
const { permissions } = details || {};
const bulkRemoveEnabled = () => rows.some(row => row.selected && row.added);
const bulkAddEnabled = () => rows.some(row => row.selected && !row.added);
const onAdd = useCallback(({
componentCvId, published, added, latest,
}) => {
if (published) { // If 1 or more versions present, open a modal to let user select version
dispatch(getContentViewDetails(componentCvId, 'bulk_add'));
setVersionEditing(true);
setCompositeCvEditing(cvId);
setComponentCvEditing(componentCvId);
if (added) {
setComponentVersionId(published?.id);
} else {
setComponentVersionId(null);
}
setComponentLatest(latest);
setComponentId(added);
} else { // if no versions are present, default to always latest and add cv without modal
dispatch(addComponent({
compositeContentViewId: cvId,
components: [{ latest: true, content_view_id: componentCvId }],
}, () => dispatch(getContentViewDetails(cvId))));
}
}, [cvId, dispatch]);
const removeBulk = () => {
const componentIds = [];
rows.forEach(row => row.selected && componentIds.push(row.added));
dispatch(removeComponent({
compositeContentViewId: cvId,
component_ids: componentIds,
}, () => dispatch(getContentViewDetails(cvId))));
};
const addBulk = () => {
const rowsToAdd = rows.filter(row => row.selected && !row.added);
setSelectedComponentsToAdd(rowsToAdd);
setCompositeCvEditing(cvId);
setBulkAdding(true);
};
const onRemove = (componentIdToRemove) => {
dispatch(removeComponent({
compositeContentViewId: cvId,
component_ids: [componentIdToRemove],
}, () => dispatch(getContentViewDetails(cvId))));
};
const toggleBulkAction = () => {
setBulkActionOpen(!bulkActionOpen);
};
const buildRows = useCallback(() => {
const newRows = [];
results.forEach((componentCV) => {
const {
id: componentCvId, content_view: cv, content_view_version: cvVersion,
latest, component_content_view_versions: componentCvVersions,
} = componentCV;
const { environments, repositories, id: cvVersionId } = cvVersion || {};
const {
id,
name,
description,
} = cv;
const cells = [
{ title: },
{ title: {truncate(name)} },
{
title: (
{hasPermission(permissions, 'edit_content_views') && componentCvId && cvVersion &&
}
),
},
{ title: environments ? : },
{
title: cvVersionId ?
{repositories ? repositories.length : 0} :
0,
},
{
title: ,
},
{
title: description ?
{description} :
,
},
];
newRows.push({
componentCvId: id,
componentCvName: name,
added: componentCvId,
componentCvVersions,
published: cvVersion,
latest,
cells,
});
});
return newRows;
}, [onAdd, results, permissions]);
const actionResolver = (rowData, { _rowIndex }) => [
{
title: __('Add'),
isDisabled: rowData.added,
onClick: (_event, rowId, rowInfo) => {
onAdd({
componentCvId: rowInfo.componentCvId,
published: rowInfo.published,
added: rowInfo.added,
latest: rowInfo.latest,
});
},
},
{
title: __('Remove'),
isDisabled: !rowData.added,
onClick: (_event, rowId, rowInfo) => {
onRemove(rowInfo.added);
},
},
];
const dropdownItems = [
{__('Remove')}
,
];
const emptyContentTitle = __('No content views to add yet');
const emptyContentBody = __('Please create some content views.');
const emptySearchTitle = __('No matching content views found');
const emptySearchBody = __('Try changing your search settings.');
const activeFilters = [statusSelected];
const defaultFilters = [ALL_STATUSES];
useDeepCompareEffect(() => {
if (!loading && results) {
const newRows = buildRows(results);
setRows(newRows);
}
}, [results, response, buildRows, loading]);
return (
getContentViewComponents(cvId, params, statusSelected), [cvId, statusSelected])}
additionalListeners={[statusSelected, addComponentsResolved, removeComponentsResolved]}
actionButtons={hasPermission(permissions, 'edit_content_views') &&
status === STATUS.RESOLVED && rows.length !== 0 &&
<>
{hasPermission(permissions, 'edit_content_views') &&
}
isOpen={bulkActionOpen}
isPlain
ouiaId="cv-components-bulk-actions"
dropdownItems={dropdownItems}
/>
}
{versionEditing &&
}
{bulkAdding &&
setBulkAdding(false)}
aria-label="bulk_add_components_modal"
/>}
>
}
/>
);
};
ContentViewComponents.propTypes = {
cvId: PropTypes.number.isRequired,
details: PropTypes.shape({
label: PropTypes.string,
permissions: PropTypes.shape({}),
}),
};
ContentViewComponents.defaultProps = {
details: {
label: '',
permissions: {},
},
};
export default ContentViewComponents;