import React, { useEffect, useState } from 'react'; import { EmptyState, EmptyStateBody, EmptyStateIcon, EmptyStateSecondaryActions, EmptyStateVariant, Pagination, PaginationVariant, Title, Toolbar, ToolbarContent, ToolbarItem, } from '@patternfly/react-core'; import { PlusCircleIcon } from '@patternfly/react-icons'; import { ActionsColumn, ExpandableRowContent, TableComposable, Tbody, Td, Th, Thead, Tr, } from '@patternfly/react-table'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { Link, useNavigate } from 'react-router-dom'; import './ImagesTable.scss'; import ClonesTable from './ClonesTable'; import ErrorDetails from './ImageBuildErrorDetails'; import { ImageBuildStatus } from './ImageBuildStatus'; import ImageLink from './ImageLink'; import Release from './Release'; import Target from './Target'; import { AWS_S3_EXPIRATION_TIME_IN_HOURS } from '../../constants'; import { fetchComposes, fetchComposeStatus } from '../../store/actions/actions'; import { resolveRelPath } from '../../Utilities/path'; import { hoursToExpiration, timestampToDisplayString, } from '../../Utilities/time'; import DocumentationButton from '../sharedComponents/DocumentationButton'; 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(fetchComposeStatus(id)); }); }; /* Get all composes once on mount */ useEffect(() => { dispatch(fetchComposes(perPage, 0)); }, []); /* Reset the polling each time the composes in the store are updated */ useEffect(() => { 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(fetchComposes(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(fetchComposes(perPage, 0)); } // page should be reset to the first page when the page size is changed. setPerPage(perPage); setPage(1); }; const actions = (compose) => [ { title: 'Recreate image', onClick: () => navigate(resolveRelPath('imagewizard'), { state: { composeRequest: compose.request, initialStep: 'review' }, }), }, { title: ( Download compose request (.json) ), }, ]; const awsActions = (compose) => [ { title: 'Share to new region', onClick: () => navigate(resolveRelPath(`share`), { state: { composeId: compose.id }, }), }, ...actions(compose), ]; // 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/Updated 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)} = AWS_S3_EXPIRATION_TIME_IN_HOURS ? true : false } /> {compose.request.image_requests[0].upload_request .type === 'aws' ? ( ) : ( )} {compose.request.image_requests[0].upload_request .type === 'aws' ? ( ) : ( UUID
{id}
)} ); })}
)}
); }; ImagesTable.propTypes = { composes: PropTypes.object, composesGet: PropTypes.func, composeGetStatus: PropTypes.func, }; export default ImagesTable;