import React, { useState, useEffect, useCallback } from 'react';
import useDeepCompareEffect from 'use-deep-compare-effect';
import PropTypes from 'prop-types';
import { shallowEqual, useSelector, useDispatch } from 'react-redux';
import { capitalize, omit, isEqual } from 'lodash';
import { TableVariant } from '@patternfly/react-table';
import {
Tabs, Tab, TabTitleText, Split, SplitItem, Select, SelectVariant,
SelectOption, Button, Dropdown, DropdownItem, KebabToggle, Flex, FlexItem,
Bullseye, DatePicker, ChipGroup, Chip, Text,
} from '@patternfly/react-core';
import { STATUS } from 'foremanReact/constants';
import { translate as __ } from 'foremanReact/common/I18n';
import onSelect from '../../../../components/Table/helpers';
import TableWrapper from '../../../../components/Table/TableWrapper';
import {
selectCVFilterErratumID,
selectCVFilterErratumIDStatus,
selectCVFilterErratumIDError,
selectCVFilters, selectCVFilterDetails, selectCVFiltersStatus,
} from '../ContentViewDetailSelectors';
import getContentViewDetails, {
addCVFilterRule, removeCVFilterRule, getCVFilterErrata,
deleteContentViewFilterRules, addContentViewFilterRules,
} from '../ContentViewDetailActions';
import AddedStatusLabel from '../../../../components/AddedStatusLabel';
import ErratumTypeLabel from '../../../../components/ErratumTypeLabel';
import AffectedRepositoryTable from './AffectedRepositories/AffectedRepositoryTable';
import { ADDED, ALL_STATUSES, NOT_ADDED, ERRATA_TYPES } from '../../ContentViewsConstants';
import SelectableDropdown from '../../../../components/SelectableDropdown/SelectableDropdown';
import { dateFormat, dateParse } from './CVErrataDateFilterContent';
import { hasPermission } from '../../helpers';
const CVErrataIDFilterContent = ({
cvId, filterId, showAffectedRepos, setShowAffectedRepos, details,
}) => {
const dispatch = useDispatch();
const { results: filterResults } =
useSelector(state => selectCVFilters(state, cvId), shallowEqual);
const response = useSelector(state =>
selectCVFilterErratumID(state, cvId, filterId), shallowEqual);
const status = useSelector(state =>
selectCVFilterErratumIDStatus(state, cvId, filterId), shallowEqual);
const filterLoad = useSelector(state =>
selectCVFiltersStatus(state, cvId), shallowEqual);
const error = useSelector(state =>
selectCVFilterErratumIDError(state, cvId, filterId), shallowEqual);
const filterDetails = useSelector(state =>
selectCVFilterDetails(state, cvId, filterId), shallowEqual);
const { repositories = [] } = filterDetails;
const [rows, setRows] = useState([]);
const [searchQuery, updateSearchQuery] = useState('');
const [activeTabKey, setActiveTabKey] = useState(0);
const filterLoaded = filterLoad === 'RESOLVED';
const loading = status === STATUS.PENDING;
const [bulkActionOpen, setBulkActionOpen] = useState(false);
const toggleBulkAction = () => setBulkActionOpen(prevState => !prevState);
const hasAddedSelected = rows.some(({ selected, added }) => selected && added);
const hasNotAddedSelected = rows.some(({ selected, added }) => selected && !added);
const [statusSelected, setStatusSelected] = useState(ALL_STATUSES);
const [typeSelectOpen, setTypeSelectOpen] = useState(false);
const [selectedTypes, setSelectedTypes] = useState(ERRATA_TYPES);
const [startDate, setStartDate] = useState('');
const [endDate, setEndDate] = useState('');
const activeFilters = [statusSelected, selectedTypes, startDate, endDate];
const defaultFilters = [ALL_STATUSES, ERRATA_TYPES, '', ''];
const [apiStartDate, setApiStartDate] = useState('');
const [apiEndDate, setApiEndDate] = useState('');
const [dateType, setDateType] = useState('issued');
const [dateTypeSelectOpen, setDateTypeSelectOpen] = useState(false);
const [startEntry, setStartEntry] = useState(false);
const [endEntry, setEndEntry] = useState(false);
const metadata = omit(response, ['results']);
const { permissions } = details;
const columnHeaders = [
__('Errata ID'),
__('Type'),
__('Issued'),
__('Updated'),
__('Severity'),
__('Synopsis'),
__('Status'),
];
const buildRows = useCallback((results) => {
const newRows = [];
const filterRules = filterResults.find(({ id }) => id === Number(filterId))?.rules || [];
results.forEach((errata) => {
const {
id,
errata_id: errataId,
type,
issued,
updated,
severity,
title,
filter_ids: filterIds,
...rest
} = errata;
const added = filterIds.includes(parseInt(filterId, 10));
const cells = [
{ title: errataId },
{ title: },
{ title: issued },
{ title: updated },
{ title: severity || 'N/A' },
{ title },
{ title: },
];
newRows.push({
cells,
erratumId: errataId,
erratumRuleId: filterRules?.find(({ errata_id: filterErrataId }) =>
filterErrataId === errataId)?.id,
added,
...rest,
errataId,
});
});
return newRows.sort(({ added: addedA }, { added: addedB }) => {
if (addedA === addedB) return 0;
return addedA ? -1 : 1;
});
}, [filterResults, filterId]);
const bulkAdd = () => {
setBulkActionOpen(false);
const addData = rows.filter(({ selected, added }) =>
selected && !added).map(({ erratumId }) => ({ errata_ids: [erratumId] })); // eslint-disable-line max-len
dispatch(addContentViewFilterRules(filterId, addData, () =>
dispatch(getContentViewDetails(cvId))));
};
const bulkRemove = () => {
setBulkActionOpen(false);
const erratumRuleIds =
rows.filter(({ selected, added }) =>
selected && added).map(({ erratumRuleId }) => erratumRuleId);
dispatch(deleteContentViewFilterRules(filterId, erratumRuleIds, () =>
dispatch(getContentViewDetails(cvId))));
};
useEffect(() => {
if (!repositories.length && showAffectedRepos) {
setActiveTabKey(1);
} else {
setActiveTabKey(0);
}
}, [showAffectedRepos, repositories.length]);
useDeepCompareEffect(() => {
const { results } = response;
if (!loading && results && filterLoaded) {
const newRows = buildRows(results);
setRows(newRows);
}
}, [response, loading, filterLoaded, buildRows]);
const actionResolver = ({ added }) => [
{
title: __('Add'),
isDisabled: added,
onClick: (_event, _rowId, { erratumId }) => {
dispatch(addCVFilterRule(filterId, { errata_ids: [erratumId] }, () =>
dispatch(getContentViewDetails(cvId))));
},
},
{
title: __('Remove'),
isDisabled: !added,
onClick: (_event, _rowId, { erratumRuleId }) => {
dispatch(removeCVFilterRule(filterId, erratumRuleId, () =>
dispatch(getContentViewDetails(cvId))));
},
},
];
const validAPIDate = (date) => {
if (!date || date === '') return true;
const split = date.split('/');
if (split.length !== 3) {
return false;
}
const [month, day, year] = split;
return month && month.length === 2 && day && day.length === 2 && year && year.length === 4;
};
const singleSelection = selection => (selectedTypes.length === 1
&& selectedTypes.includes(selection));
const onTypeSelect = (selection) => {
if (selectedTypes.includes(selection)) {
if (selectedTypes.length === 1) return;
setSelectedTypes(selectedTypes.filter(e => e !== selection));
} else setSelectedTypes([...selectedTypes, selection]);
setTypeSelectOpen(false);
};
const setValidStartDate = (e, value) => {
setStartDate(value);
if (validAPIDate(value)) setApiStartDate(value);
};
const setValidEndDate = (e, value) => {
setEndDate(value);
if (validAPIDate(value)) setApiEndDate(value);
};
const getCVFilterErrataWithOptions = useCallback((params = {}) => {
let apiParams = { ...params, types: selectedTypes };
if (dateType) apiParams = { ...apiParams, date_type: dateType };
if (apiStartDate) apiParams = { ...apiParams, start_date: apiStartDate };
if (apiEndDate) apiParams = { ...apiParams, end_date: apiEndDate };
return getCVFilterErrata(cvId, filterId, apiParams, statusSelected);
}, [cvId, filterId, statusSelected, selectedTypes, dateType, apiStartDate, apiEndDate]);
const resetFilters = () => {
setValidStartDate('');
setValidEndDate('');
setSelectedTypes(ERRATA_TYPES);
setDateType('issued');
setStatusSelected(ALL_STATUSES);
};
const resetFiltersDisabled =
startDate === '' &&
endDate === '' &&
isEqual(selectedTypes, ERRATA_TYPES) &&
dateType === 'issued' &&
statusSelected === ALL_STATUSES;
const emptyContentTitle = __('No errata filter rules yet');
const emptyContentBody = __('No errata to add yet');
const emptySearchTitle = __('No matching filter rules found.');
const emptySearchBody = __('Try changing your search settings.');
const invalidDateFormat = __('Enter a valid date: MM/DD/YYYY');
return (
setActiveTabKey(eventKey)}
>
{__('Errata')}}
>
getCVFilterErrataWithOptions(params), [getCVFilterErrataWithOptions])}
actionButtons={hasPermission(permissions, 'edit_content_views') &&
status === STATUS.RESOLVED && rows.length !== 0 &&
{hasPermission(permissions, 'edit_content_views') &&
}
{hasPermission(permissions, 'edit_content_views') &&
}
isOpen={bulkActionOpen}
ouiaId="cv-errata-id-bulk-action-dropdown"
isPlain
dropdownItems={[
{__('Remove')}
]
}
/>
}
}
nodesBelowSearch={status === STATUS.RESOLVED && rows.length !== 0 &&
<>
setStartEntry(true)}
onBlur={() => setStartEntry(false)}
>
{__('to')}
setEndEntry(true)}
onBlur={() => setEndEntry(false)}
>
setStatusSelected(ALL_STATUSES)} isReadOnly={statusSelected === ALL_STATUSES}>
{statusSelected}
setValidStartDate('')} isReadOnly={startDate === ''}>
{startDate || __('ANY')}
{__('to')}
setValidEndDate('')} isReadOnly={endDate === ''}>
{endDate || __('ANY')}
{selectedTypes.map(type => (
onTypeSelect(type)}
isReadOnly={singleSelection(type)}
>
{capitalize(type)}
))}
>
}
/>
{(repositories.length || showAffectedRepos) &&
{__('Affected repositories')}}
>
}
);
};
CVErrataIDFilterContent.propTypes = {
cvId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
filterId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
showAffectedRepos: PropTypes.bool.isRequired,
setShowAffectedRepos: PropTypes.func.isRequired,
details: PropTypes.shape({
permissions: PropTypes.shape({}),
}).isRequired,
};
export default CVErrataIDFilterContent;