import PropTypes from 'prop-types'; import React, { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Link, useNavigate } from 'react-router-dom'; import { ActionsColumn, ExpandableRowContent, TableComposable, Tbody, Td, Th, Thead, Tr, } from '@patternfly/react-table'; import { EmptyState, EmptyStateBody, EmptyStateIcon, EmptyStateSecondaryActions, EmptyStateVariant, Pagination, PaginationVariant, Title, Toolbar, ToolbarContent, ToolbarItem, } from '@patternfly/react-core'; import { PlusCircleIcon } from '@patternfly/react-icons'; import './ImagesTable.scss'; import ImageBuildStatus from './ImageBuildStatus'; import Release from './Release'; import Target from './Target'; import ImageLink from './ImageLink'; import ErrorDetails from './ImageBuildErrorDetails'; import DocumentationButton from '../sharedComponents/DocumentationButton'; import { composeGetStatus, composesGet } from '../../store/actions/actions'; const ImagesTable = () => { const [page, setPage] = useState(1); const [perPage, setPerPage] = useState(10); const [expandedComposeIds, setExpandedComposeIds] = useState([]); const isExpanded = (compose) => expandedComposeIds.includes(compose.id); const handleToggle = (compose, isExpanding) => { if (isExpanding) { setExpandedComposeIds([...expandedComposeIds, compose.id]); } else { setExpandedComposeIds( expandedComposeIds.filter((id) => id !== compose.id) ); } }; const composes = useSelector((state) => state.composes); const dispatch = useDispatch(); const navigate = useNavigate(); const pollComposeStatuses = () => { Object.entries(composes.byId).map(([id, compose]) => { /* Skip composes that have been complete */ if ( compose.image_status.status === 'success' || compose.image_status.status === 'failure' ) { return; } dispatch(composeGetStatus(id)); }); }; useEffect(() => { dispatch(composesGet(perPage, 0)); const intervalId = setInterval(() => pollComposeStatuses(), 8000); // clean up interval on unmount return () => clearInterval(intervalId); }, []); const onSetPage = (_, page) => { // if the next page's composes haven't been fetched from api yet // then fetch them with proper page index and offset if (composes.count > composes.allIds.length) { const pageIndex = page - 1; const offset = pageIndex * perPage; dispatch(composesGet(perPage, offset)); } setPage(page); }; const onPerPageSelect = (_, perPage) => { // if the new per page quantity is greater than the number of already fetched composes fetch more composes // if all composes haven't already been fetched if ( composes.count > composes.allIds.length && perPage > composes.allIds.length ) { dispatch(composesGet(perPage, 0)); } // page should be reset to the first page when the page size is changed. setPerPage(perPage); setPage(1); }; const timestampToDisplayString = (ts) => { // timestamp has format 2021-04-27 12:31:12.794809 +0000 UTC // must be converted to ms timestamp and then reformatted to Apr 27, 2021 if (!ts) { return ''; } // get YYYY-MM-DD format const date = ts.slice(0, 10); const ms = Date.parse(date); const options = { month: 'short', day: 'numeric', year: 'numeric' }; const tsDisplay = new Intl.DateTimeFormat('en-US', options).format(ms); return tsDisplay; }; const actions = (compose) => [ { title: 'Recreate image', onClick: () => navigate('/imagewizard', { state: { composeRequest: compose.request, initialStep: 'review' }, }), }, { title: ( Download compose request (.json) ), }, ]; // the state.page is not an index so must be reduced by 1 get the starting index const itemsStartInclusive = (page - 1) * perPage; const itemsEndExclusive = itemsStartInclusive + perPage; return ( {(composes.allIds.length === 0 && ( Create an image Create OS images for deployment in Amazon Web Services, Microsoft Azure and Google Cloud Platform. Images can include a custom package set and an activation key to automate the registration process. Create image )) || ( Create image Image name Created Release Target Status Instance {composes.allIds .slice(itemsStartInclusive, itemsEndExclusive) .map((id, rowIndex) => { const compose = composes.byId[id]; return ( handleToggle(compose, !isExpanded(compose)), }} /> {compose.request.image_name || id} {timestampToDisplayString(compose.created_at)} UUID
{id}
); })}
)}
); }; ImagesTable.propTypes = { composes: PropTypes.object, composesGet: PropTypes.func, composeGetStatus: PropTypes.func, }; export default ImagesTable;