webpack/scenes/AlternateContentSources/MainTable/ACSTable.js in katello-4.5.1 vs webpack/scenes/AlternateContentSources/MainTable/ACSTable.js in katello-4.6.0.rc1

- old
+ new

@@ -1,28 +1,142 @@ -import React, { useState, useCallback } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useHistory, useLocation } from 'react-router-dom'; +import { useDispatch, useSelector } from 'react-redux'; import { translate as __ } from 'foremanReact/common/I18n'; -import { Button } from '@patternfly/react-core'; -import { TableVariant, Thead, Tbody, Tr, Th, Td } from '@patternfly/react-table'; +import { STATUS } from 'foremanReact/constants'; +import { + Button, + Drawer, + DrawerActions, + DrawerCloseButton, + DrawerContent, + DrawerContentBody, + DrawerHead, + DrawerPanelContent, + Text, + TextContent, + TextList, + TextListItem, + TextListItemVariants, + TextListVariants, + TextVariants, +} from '@patternfly/react-core'; +import { TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; import TableWrapper from '../../../components/Table/TableWrapper'; import { - selectAlternateContentSources, selectAlternateContentSourcesError, + selectAlternateContentSources, + selectAlternateContentSourcesError, selectAlternateContentSourcesStatus, } from '../ACSSelectors'; import { useTableSort } from '../../../components/Table/TableHooks'; -import getAlternateContentSources, { deleteACS, refreshACS } from '../ACSActions'; +import getAlternateContentSources, { deleteACS, getACSDetails, refreshACS } from '../ACSActions'; import ACSCreateWizard from '../Create/ACSCreateWizard'; import LastSync from '../../ContentViews/Details/Repositories/LastSync'; +import ACSExpandableDetails from '../Details/ACSExpandableDetails'; +import './ACSTable.scss'; 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 { results, ...metadata } = response; + const { pathname } = useLocation(); + const { push } = useHistory(); + const acsId = pathname.split('/')[3]; + const [expandedId, setExpandedId] = useState(acsId); + const [isExpanded, setIsExpanded] = useState(false); + const drawerRef = useRef(null); + + useEffect(() => { + if (acsId) { + dispatch(getACSDetails(acsId)); + setIsExpanded(true); + } + }, [dispatch, acsId]); + + const onExpand = () => { + if (drawerRef.current) drawerRef.current.focus(); + }; + + const onDelete = (id) => { + dispatch(deleteACS(id, () => + dispatch(getAlternateContentSources()))); + }; + + const onRefresh = (id) => { + dispatch(refreshACS(id, () => + dispatch(getAlternateContentSources()))); + }; + + const createButtonOnclick = () => { + setIsCreateWizardOpen(true); + }; + + const onClick = (id) => { + setExpandedId(id); + }; + + const onCloseClick = () => { + setExpandedId(null); + push('/labs/alternate_content_sources'); + setIsExpanded(false); + }; + + const PanelContent = () => { + if (!resolved) return <></>; + const acs = results?.find(source => source?.id === Number(expandedId)); + const { last_refresh: lastTask } = acs ?? {}; + const { last_refresh_words: lastRefreshWords, started_at: startedAt } = lastTask ?? {}; + return ( + <DrawerPanelContent defaultSize="50%"> + <DrawerHead> + {results && isExpanded && + <span ref={drawerRef}> + <TextContent> + <Text component={TextVariants.h1}> + {acs?.name} + </Text> + <TextList component={TextListVariants.dl}> + <TextListItem component={TextListItemVariants.dt}> + {__('Last refresh :')} + </TextListItem> + <TextListItem + aria-label="name_text_value" + component={TextListItemVariants.dd} + > + <LastSync + startedAt={startedAt} + lastSync={lastTask} + lastSyncWords={lastRefreshWords} + emptyMessage="N/A" + /> + </TextListItem> + </TextList> + </TextContent> + <ACSExpandableDetails /> + </span>} + <DrawerActions> + <Button + ouiaId="refresh-acs" + onClick={() => onRefresh(acs?.id)} + variant="secondary" + isSmall + aria-label="refresh_acs" + > + {__('Refresh source')} + </Button> + <DrawerCloseButton onClick={onCloseClick} /> + </DrawerActions> + </DrawerHead> + </DrawerPanelContent> + ); + }; + const columnHeaders = [ __('Name'), __('Type'), __('Last Refresh'), ]; @@ -48,24 +162,10 @@ ...params, }), [apiSortParams], ); - const onDelete = (id) => { - dispatch(deleteACS(id, () => - dispatch(getAlternateContentSources()))); - }; - - const onRefresh = (id) => { - dispatch(refreshACS(id, () => - dispatch(getAlternateContentSources()))); - }; - - const createButtonOnclick = () => { - setIsCreateWizardOpen(true); - }; - const rowDropdownItems = ({ id }) => [ { title: __('Delete'), ouiaId: `remove-acs-${id}`, onClick: () => { @@ -84,77 +184,105 @@ const emptyContentTitle = __("You currently don't have any alternate content sources."); const emptyContentBody = __('An alternate content source can be added by using the "Add source" button above.'); const emptySearchTitle = __('No matching alternate content sources found'); const emptySearchBody = __('Try changing your search settings.'); /* eslint-disable react/no-array-index-key */ + return ( - <TableWrapper - {...{ - metadata, - emptyContentTitle, - emptyContentBody, - emptySearchTitle, - emptySearchBody, - searchQuery, - updateSearchQuery, - error, - status, - fetchItems, - }} - ouiaId="alternate-content-sources-table" - variant={TableVariant.compact} - additionalListeners={[activeSortColumn, activeSortDirection]} - autocompleteEndpoint="/alternate_content_sources/auto_complete_search" - actionButtons={ - <> - <Button ouiaId="create-acs" onClick={createButtonOnclick} variant="primary" aria-label="create_acs"> - {__('Add source')} - </Button> - {isCreateWizardOpen && - <ACSCreateWizard - show={isCreateWizardOpen} - setIsOpen={setIsCreateWizardOpen} - /> - } - </> - } - > - <Thead> - <Tr> - {columnHeaders.map(col => ( - <Th - key={col} - sort={COLUMNS_TO_SORT_PARAMS[col] ? pfSortParams(col) : undefined} - > - {col} - </Th> - ))} - </Tr> - </Thead> - <Tbody> - {results?.map((acs, index) => { - const { - name, - alternate_content_source_type: acsType, - last_refresh: lastTask, - } = acs; - const { last_refresh_words: lastRefreshWords, started_at: startedAt } = lastTask ?? {}; - return ( - <Tr key={index}> - <Td>{name}</Td> - <Td>{acsType}</Td> - <Td><LastSync startedAt={startedAt} lastSync={lastTask} lastSyncWords={lastRefreshWords} emptyMessage="N/A" /></Td> - <Td - actions={{ - items: rowDropdownItems(acs), - }} - /> - </Tr> - ); - }) - } - </Tbody> - </TableWrapper> + <Drawer isExpanded={isExpanded} isInline onExpand={onExpand}> + <DrawerContent panelContent={<PanelContent />}> + <DrawerContentBody style={{ paddingBottom: '5%' }}> + <TableWrapper + {...{ + metadata, + emptyContentTitle, + emptyContentBody, + emptySearchTitle, + emptySearchBody, + searchQuery, + updateSearchQuery, + error, + status, + fetchItems, + }} + ouiaId="alternate-content-sources-table" + variant={TableVariant.compact} + additionalListeners={[activeSortColumn, activeSortDirection]} + autocompleteEndpoint="/alternate_content_sources/auto_complete_search" + actionButtons={ + <> + <Button + ouiaId="create-acs" + onClick={createButtonOnclick} + variant="primary" + aria-label="create_acs" + > + {__('Add source')} + </Button> + {isCreateWizardOpen && + <ACSCreateWizard + show={isCreateWizardOpen} + setIsOpen={setIsCreateWizardOpen} + /> + } + </> + } + > + <Thead> + <Tr> + {columnHeaders.map(col => ( + <Th + key={col} + sort={COLUMNS_TO_SORT_PARAMS[col] ? pfSortParams(col) : undefined} + > + {col} + </Th> + ))} + </Tr> + </Thead> + <Tbody> + {results?.map((acs, index) => { + const { + name, + id, + alternate_content_source_type: acsType, + last_refresh: lastTask, + } = acs; + const { + last_refresh_words: lastRefreshWords, + started_at: startedAt, + } = lastTask ?? {}; + return ( + <Tr key={index}> + <Td onClick={() => { + onClick(id); + push(`/labs/alternate_content_sources/${id}/details`); + }} + > + <Text component="a">{name}</Text> + </Td> + <Td>{acsType}</Td> + <Td><LastSync + startedAt={startedAt} + lastSync={lastTask} + lastSyncWords={lastRefreshWords} + emptyMessage="N/A" + /> + </Td> + <Td + actions={{ + items: rowDropdownItems(acs), + }} + /> + </Tr> + ); + }) + } + </Tbody> + </TableWrapper> + </DrawerContentBody> + </DrawerContent> + </Drawer> ); /* eslint-enable react/no-array-index-key */ }; export default ACSTable;