import isNull from 'lodash/isNull' ; import isUndefined from 'lodash/isUndefined' ; import { memo , useCallback , useEffect , useState } from 'react' ; import { GoPackage } from 'react-icons/go' ; import { ContainerImage , RepositoryKind } from '../../types' ; import ButtonCopyToClipboard from '../common/ButtonCopyToClipboard' ; import ElementWithTooltip from '../common/ElementWithTooltip' ; import SeeAllModal from '../common/SeeAllModal' ; import SmallTitle from '../common/SmallTitle' ; import styles from './ContainersImages.module.css' ; interface Props { containers ? : ContainerImage [ ] | null ; packageId : string ; kind : RepositoryKind ; } interface ContainersList { items : JSX . Element [ ] ; itemsForModal : JSX . Element [ ] | JSX . Element ; } const ContainersImages = ( props ) => { const getBadge = ( ) : JSX . Element => ( < ElementWithTooltip className = { styles . tooltipIcon } element = { < span className = { `badge rounded-pill my-1 border ${ styles . badge }` } > Whitelisted < /span>} tooltipMessage="This image has been whitelisted by the publisher and it won't be scanned for security vulnerabilities." visibleTooltip active/ > ) ; const getAllContainers = useCallback ( ( ) : ContainersList | null => { if ( isUndefined ( props . containers ) || isNull ( props . containers ) || props . containers . length === 0 || props . kind === RepositoryKind . Container ) return null ; let items = [ ] ; let itemsForModal = [ ] ; props . containers . forEach ( ( containerImage , index ) => { const copyBtn = ( < > { ! isUndefined ( containerImage . image ) && ( < ButtonCopyToClipboard text = { containerImage . image } className = { `btn-link text-dark border-0 position-relative ${ styles . copyBtn }` } label = { `Copy ${ containerImage . name || containerImage . image } container image to clipboard` } />)} < />); items . push ( < div data - testid = "containerImageItem" className = "py-1 py-sm-0" key = { `container-${ index }-${ containerImage . image }` } role = "listitem" > < div className = "d-flex flex-row align-items-start mw-100" > < GoPackage className = { `text-muted me-2 ${ styles . icon }` } /> < div data - testid = "containerImage" className = { `text-truncate text-break ${ styles . containerImage }` } > { containerImage . name || containerImage . image } < /div> { copyBtn } < /div> { containerImage . whitelisted && ( < div className = { `d-flex flex-column mb-1 ${ styles . badgesWrapper }` } > < div className = "d-flex flex-row align-items-center" > < div className = { `border border-top-0 border-end-0 ${ styles . badgeDecorator } position-relative mx-1` } /> { getBadge ( ) } < /div> < /div>)} < /div>); itemsForModal . push ( < tr key = { `container-row-${ index }-${ containerImage . image }` } > < td > < div className = { `d-flex flex-row align-items-center` } > < div className = "mx-1" > < GoPackage className = "text-muted" / > < /div> < div data - testid = "containerImage" className = "text-truncate ps-1" > { containerImage . name || containerImage . image } < /div> { copyBtn } { containerImage . whitelisted && < div className = "ms-2 me-1" > { getBadge ( ) } < /div>} < /div> < /td> < /tr>); } ) ; return { items , itemsForModal : ( < table className = { `table table-striped table-bordered table-sm mb-0 ${ styles . table }` } > < thead > < tr className = { styles . tableTitle } > < th scope = "col" > < span className = "px-1" > Image < /span> < /th> < /tr> < /thead> < tbody className = { styles . body } > { itemsForModal } < /tbody> < /table>), } ; } , [ props . containers , props . kind ] ) ; const [ containers , setContainers ] = useState < ContainersList | null > ( getAllContainers ( ) ) ; useEffect ( ( ) => { setContainers ( getAllContainers ( ) ) ; } , [ props . containers , getAllContainers ] ) ; if ( isNull ( containers ) ) return null ; return ( < > < SmallTitle text = "Containers Images" / > < div className = "mb-3" > < SeeAllModal title = "Containers Images" { ... containers } packageId = { props . packageId } / > < /div> < />); } ; export default memo ( ContainersImages ) ;