feat: Implemented logo display
Signed-off-by: Raul Kele <raulkeleblk@gmail.com>
This commit is contained in:
parent
ac782c04d0
commit
2dc4a35c87
@ -102,7 +102,7 @@ describe('Tags details', () => {
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<TagDetails />);
|
||||
await waitFor(() => expect(error).toBeCalledTimes(2));
|
||||
await waitFor(() => expect(error).toBeCalledTimes(1));
|
||||
});
|
||||
it('should show tag details metadata', async () => {
|
||||
// @ts-ignore
|
||||
|
@ -63,11 +63,11 @@ const endpoints = {
|
||||
repoList: ({ pageNumber = 1, pageSize = 15 } = {}) =>
|
||||
`/v2/_zot/ext/search?query={RepoListWithNewestImage(requestedPage: {limit:${pageSize} offset:${
|
||||
(pageNumber - 1) * pageSize
|
||||
}}){Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Description Licenses Title Source IsSigned Documentation History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}} Vendor Labels} DownloadCount}}`,
|
||||
}}){Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Description Licenses Logo Title Source IsSigned Documentation History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}} Vendor Labels} DownloadCount}}`,
|
||||
detailedRepoInfo: (name) =>
|
||||
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Digest Tag LastUpdated Vendor Size Platform {Os Arch} } Summary {Name LastUpdated Size Platforms {Os Arch} Vendors NewestImage {RepoName Layers {Size Digest} Digest Tag Title Documentation DownloadCount Source Description Licenses History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}}}}}}`,
|
||||
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Digest Tag LastUpdated Vendor Size Platform {Os Arch} } Summary {Name LastUpdated Size Platforms {Os Arch} Vendors NewestImage {RepoName Layers {Size Digest} Digest Tag Logo Title Documentation DownloadCount Source Description Licenses History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}}}}}}`,
|
||||
detailedImageInfo: (name, tag) =>
|
||||
`/v2/_zot/ext/search?query={Image(image: "${name}:${tag}"){RepoName Tag Digest LastUpdated Size ConfigDigest Platform {Os Arch} Vendor Licenses History {Layer {Size Digest Score} HistoryDescription {Created CreatedBy Author Comment EmptyLayer} }}}`,
|
||||
`/v2/_zot/ext/search?query={Image(image: "${name}:${tag}"){RepoName Tag Digest LastUpdated Size ConfigDigest Platform {Os Arch} Vendor Licenses Logo}}`,
|
||||
vulnerabilitiesForRepo: (name) =>
|
||||
`/v2/_zot/ext/search?query={CVEListForImage(image: "${name}"){Tag, CVEList {Id Title Description Severity PackageList {Name InstalledVersion FixedVersion}}}}`,
|
||||
layersDetailsForImage: (name) =>
|
||||
@ -87,7 +87,7 @@ const endpoints = {
|
||||
if (filter.HasToBeSigned) filterParam += ` HasToBeSigned: ${filter.HasToBeSigned}`;
|
||||
filterParam += '}';
|
||||
if (Object.keys(filter).length === 0) filterParam = '';
|
||||
return `/v2/_zot/ext/search?query={GlobalSearch(${searchParam}, ${paginationParam} ${filterParam}) {Repos {Name LastUpdated Size Platforms { Os Arch } NewestImage { Tag Description IsSigned Licenses Vendor Labels } DownloadCount}}}`;
|
||||
return `/v2/_zot/ext/search?query={GlobalSearch(${searchParam}, ${paginationParam} ${filterParam}) {Repos {Name LastUpdated Size Platforms { Os Arch } NewestImage { Tag Description IsSigned Logo Licenses Vendor Labels } DownloadCount}}}`;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -114,6 +114,7 @@ function Explore() {
|
||||
platforms={item.platforms}
|
||||
key={index}
|
||||
lastUpdated={item.lastUpdated}
|
||||
logo={item.logo}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
@ -44,7 +44,7 @@ function FilterCard(props) {
|
||||
const filterRows = filters;
|
||||
return filterRows.map((filter, index) => {
|
||||
return (
|
||||
<Tooltip key={index} title={filter.tooltip ?? filter.label} placement="right">
|
||||
<Tooltip key={index} title={filter.tooltip ?? filter.label} placement="top" arrow>
|
||||
<FormControlLabel
|
||||
componentsProps={{ typography: { variant: 'body2' } }}
|
||||
control={<Checkbox />}
|
||||
|
@ -96,7 +96,7 @@ function Home() {
|
||||
homeData.slice(0, 4).map((item, index) => {
|
||||
return (
|
||||
<Grid item xs={3} key={index}>
|
||||
<PreviewCard name={item.name} lastUpdated={item.lastUpdated} isSigned={item.isSigned} />
|
||||
<PreviewCard name={item.name} lastUpdated={item.lastUpdated} isSigned={item.isSigned} logo={item.logo} />
|
||||
</Grid>
|
||||
);
|
||||
})
|
||||
@ -141,6 +141,7 @@ function Home() {
|
||||
platforms={item.platforms}
|
||||
key={index}
|
||||
lastUpdated={item.lastUpdated}
|
||||
logo={item.logo}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
@ -12,6 +12,7 @@ import repocube4 from '../assets/repocube-4.png';
|
||||
//icons
|
||||
import GppBadOutlinedIcon from '@mui/icons-material/GppBadOutlined';
|
||||
import GppGoodOutlinedIcon from '@mui/icons-material/GppGoodOutlined';
|
||||
import { isEmpty } from 'lodash';
|
||||
//import GppMaybeOutlinedIcon from '@mui/icons-material/GppMaybeOutlined';
|
||||
|
||||
// temporary utility to get image
|
||||
@ -73,7 +74,7 @@ const useStyles = makeStyles(() => ({
|
||||
function PreviewCard(props) {
|
||||
const classes = useStyles();
|
||||
const navigate = useNavigate();
|
||||
const { name, isSigned } = props;
|
||||
const { name, isSigned, logo } = props;
|
||||
|
||||
const goToDetails = () => {
|
||||
navigate(`/image/${encodeURIComponent(name)}`);
|
||||
@ -139,7 +140,7 @@ function PreviewCard(props) {
|
||||
img: classes.avatar
|
||||
}}
|
||||
component="img"
|
||||
image={randomImage()}
|
||||
image={!isEmpty(logo) ? `data:image/png;base64, ${logo}` : randomImage()}
|
||||
alt="icon"
|
||||
/>
|
||||
<Tooltip title={name} placement="top">
|
||||
|
@ -18,6 +18,7 @@ import repocube4 from '../assets/repocube-4.png';
|
||||
import GppBadOutlinedIcon from '@mui/icons-material/GppBadOutlined';
|
||||
import GppGoodOutlinedIcon from '@mui/icons-material/GppGoodOutlined';
|
||||
import { Markdown } from 'utilities/MarkdowntojsxWrapper';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
// temporary utility to get image
|
||||
const randomIntFromInterval = (min, max) => {
|
||||
@ -90,12 +91,7 @@ const useStyles = makeStyles(() => ({
|
||||
function RepoCard(props) {
|
||||
const classes = useStyles();
|
||||
const navigate = useNavigate();
|
||||
const { name, vendor, platforms, description, downloads, isSigned, lastUpdated, version } = props;
|
||||
|
||||
//function that returns a random element from an array
|
||||
// function getRandom(list) {
|
||||
// return list[Math.floor(Math.random() * list.length)];
|
||||
// }
|
||||
const { name, vendor, platforms, description, downloads, isSigned, lastUpdated, version, logo } = props;
|
||||
|
||||
const goToDetails = () => {
|
||||
navigate(`/image/${encodeURIComponent(name)}`);
|
||||
@ -197,7 +193,7 @@ function RepoCard(props) {
|
||||
img: classes.avatar
|
||||
}}
|
||||
component="img"
|
||||
image={randomImage()}
|
||||
image={!isEmpty(logo) ? `data:image/png;base64, ${logo}` : randomImage()}
|
||||
alt="icon"
|
||||
/>
|
||||
<Tooltip title={name} placement="top">
|
||||
|
@ -19,6 +19,7 @@ import repocube4 from '../assets/repocube-4.png';
|
||||
import { TabContext, TabList, TabPanel } from '@mui/lab';
|
||||
import RepoDetailsMetadata from './RepoDetailsMetadata';
|
||||
import Loading from './Loading';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
// @ts-ignore
|
||||
const useStyles = makeStyles(() => ({
|
||||
@ -141,12 +142,13 @@ function RepoDetails() {
|
||||
platforms: repoInfo.Summary?.Platforms,
|
||||
vendors: repoInfo.Summary?.Vendors,
|
||||
newestTag: repoInfo.Summary?.NewestImage,
|
||||
description: repoInfo.Summary?.NewestImage.Description,
|
||||
title: repoInfo.Summary?.NewestImage.Title,
|
||||
source: repoInfo.Summary?.NewestImage.Source,
|
||||
downloads: repoInfo.Summary?.NewestImage.DownloadCount,
|
||||
overview: repoInfo.Summary?.NewestImage.Documentation,
|
||||
license: repoInfo.Summary?.NewestImage.Licenses
|
||||
description: repoInfo.Summary?.NewestImage?.Description,
|
||||
title: repoInfo.Summary?.NewestImage?.Title,
|
||||
source: repoInfo.Summary?.NewestImage?.Source,
|
||||
downloads: repoInfo.Summary?.NewestImage?.DownloadCount,
|
||||
overview: repoInfo.Summary?.NewestImage?.Documentation,
|
||||
license: repoInfo.Summary?.NewestImage?.Licenses,
|
||||
logo: repoInfo.Summary?.NewestImage?.Logo
|
||||
};
|
||||
setRepoDetailData(imageData);
|
||||
setTags(imageData.images);
|
||||
@ -163,19 +165,6 @@ function RepoDetails() {
|
||||
abortController.abort();
|
||||
};
|
||||
}, [name]);
|
||||
//function that returns a random element from an array
|
||||
// function getRandom(list) {
|
||||
// return list[Math.floor(Math.random() * list.length)];
|
||||
// }
|
||||
|
||||
// const signatureCheck = () => {
|
||||
// const unverifiedSignature = <Chip label="Unverified Signature" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppBadOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
|
||||
// const untrustedSignature = <Chip label="Untrusted Signature" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppMaybeOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
|
||||
// const verifiedSignature = <Chip label="Verified Signature" sx={{backgroundColor: "#E8F5E9",color: "#388E3C",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppGoodOutlinedIcon sx={{ color: "#388E3C!important" }} />}/>;
|
||||
|
||||
// const arrSignature = [unverifiedSignature, untrustedSignature, verifiedSignature]
|
||||
// return(getRandom(arrSignature));
|
||||
// }
|
||||
|
||||
const platformChips = () => {
|
||||
// @ts-ignore
|
||||
@ -234,22 +223,6 @@ function RepoDetails() {
|
||||
);
|
||||
};
|
||||
|
||||
// const renderDependencies = () => {
|
||||
// return (<Card className={classes.card}>
|
||||
// <CardContent>
|
||||
// <Typography variant="h4" align="left">Dependecies ({dependencies || '---'})</Typography>
|
||||
// </CardContent>
|
||||
// </Card>);
|
||||
// };
|
||||
|
||||
// const renderDependents = () => {
|
||||
// return (<Card className={classes.card}>
|
||||
// <CardContent>
|
||||
// <Typography variant="h4" align="left">Dependents ({dependents || '---'})</Typography>
|
||||
// </CardContent>
|
||||
// </Card>);
|
||||
// };
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading ? (
|
||||
@ -267,7 +240,9 @@ function RepoDetails() {
|
||||
img: classes.avatar
|
||||
}}
|
||||
component="img"
|
||||
image={randomImage()}
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
image={!isEmpty(repoDetailData?.logo) ? `data:image/png;base64, ${repoDetailData?.logo}` : randomImage()}
|
||||
alt="icon"
|
||||
/>
|
||||
<Typography variant="h3" className={classes.repoName}>
|
||||
@ -304,12 +279,6 @@ function RepoDetails() {
|
||||
>
|
||||
<Tab value="Overview" label="Overview" className={classes.tabContent} />
|
||||
<Tab value="Tags" label="Tags" className={classes.tabContent} />
|
||||
{/* <Tab value="Dependencies" label={`${dependencies || 0} Dependencies`} className={classes.tabContent}/>
|
||||
<Tab value="Dependents" label={`${dependents || 0} Dependents`} className={classes.tabContent}/>
|
||||
<Tab value="Vulnerabilities" label="Vulnerabilities" className={classes.tabContent}/>
|
||||
<Tab value="6" label="Tab 6" className={classes.tabContent}/>
|
||||
<Tab value="7" label="Tab 7" className={classes.tabContent}/>
|
||||
<Tab value="8" label="Tab 8" className={classes.tabContent}/> */}
|
||||
</TabList>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
@ -319,15 +288,6 @@ function RepoDetails() {
|
||||
<TabPanel value="Tags" className={classes.tabPanel}>
|
||||
<Tags tags={tags} />
|
||||
</TabPanel>
|
||||
{/* <TabPanel value="Dependencies" className={classes.tabPanel}>
|
||||
{renderDependencies()}
|
||||
</TabPanel>
|
||||
<TabPanel value="Dependents" className={classes.tabPanel}>
|
||||
{renderDependents()}
|
||||
</TabPanel>
|
||||
<TabPanel value="Vulnerabilities" className={classes.tabPanel}>
|
||||
{renderVulnerabilities()}
|
||||
</TabPanel> */}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
|
@ -35,6 +35,8 @@ import VulnerabilitiesDetails from './VulnerabilitiesDetails';
|
||||
import HistoryLayers from './HistoryLayers';
|
||||
import DependsOn from './DependsOn';
|
||||
import IsDependentOn from './IsDependentOn';
|
||||
import { isEmpty } from 'lodash';
|
||||
import Loading from './Loading';
|
||||
|
||||
// @ts-ignore
|
||||
const useStyles = makeStyles(() => ({
|
||||
@ -133,8 +135,7 @@ const randomImage = () => {
|
||||
|
||||
function TagDetails() {
|
||||
const [imageDetailData, setImageDetailData] = useState({});
|
||||
// @ts-ignore
|
||||
//const [isLoading, setIsLoading] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [selectedTab, setSelectedTab] = useState('Layers');
|
||||
const abortController = useMemo(() => new AbortController(), []);
|
||||
|
||||
@ -148,6 +149,7 @@ function TagDetails() {
|
||||
// if same-page navigation because of tag update, following 2 lines help ux
|
||||
setSelectedTab('Layers');
|
||||
window?.scrollTo(0, 0);
|
||||
setIsLoading(true);
|
||||
api
|
||||
.get(`${host()}${endpoints.detailedImageInfo(name, tag)}`, abortController.signal)
|
||||
.then((response) => {
|
||||
@ -162,11 +164,12 @@ function TagDetails() {
|
||||
platform: imageInfo.Platform,
|
||||
vendor: imageInfo.Vendor,
|
||||
history: imageInfo.History,
|
||||
license: imageInfo.Licenses
|
||||
license: imageInfo.Licenses,
|
||||
logo: imageInfo.Logo
|
||||
};
|
||||
setImageDetailData(imageData);
|
||||
setFullName(imageData.name + ':' + imageData.tag);
|
||||
//setIsLoading(false);
|
||||
setIsLoading(false);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
@ -177,10 +180,6 @@ function TagDetails() {
|
||||
abortController.abort();
|
||||
};
|
||||
}, [name, tag]);
|
||||
//function that returns a random element from an array
|
||||
// function getRandom(list) {
|
||||
// return list[Math.floor(Math.random() * list.length)];
|
||||
// }
|
||||
|
||||
// const signatureCheck = () => {
|
||||
// const unverifiedSignature = <Chip label="Unverified Signature" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppBadOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
|
||||
@ -206,134 +205,152 @@ function TagDetails() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.pageWrapper}>
|
||||
<Card className={classes.cardRoot}>
|
||||
<CardContent>
|
||||
<Grid container className={classes.header}>
|
||||
<Grid item xs={8}>
|
||||
<Stack alignItems="center" direction="row" spacing={2}>
|
||||
<CardMedia
|
||||
classes={{
|
||||
root: classes.media,
|
||||
img: classes.avatar
|
||||
}}
|
||||
component="img"
|
||||
image={randomImage()}
|
||||
alt="icon"
|
||||
/>
|
||||
<Typography variant="h3" className={classes.repoName}>
|
||||
{name}:{tag}
|
||||
</Typography>
|
||||
{/* {vulnerabilityCheck()}
|
||||
<>
|
||||
{isLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<div className={classes.pageWrapper}>
|
||||
<Card className={classes.cardRoot}>
|
||||
<CardContent>
|
||||
<Grid container className={classes.header}>
|
||||
<Grid item xs={8}>
|
||||
<Stack alignItems="center" direction="row" spacing={2}>
|
||||
<CardMedia
|
||||
classes={{
|
||||
root: classes.media,
|
||||
img: classes.avatar
|
||||
}}
|
||||
component="img"
|
||||
image={
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
!isEmpty(imageDetailData?.logo) ? `data:image/ png;base64, ${imageDetailData?.logo}` : randomImage()
|
||||
}
|
||||
alt="icon"
|
||||
/>
|
||||
<Typography variant="h3" className={classes.repoName}>
|
||||
{name}:{tag}
|
||||
</Typography>
|
||||
{/* {vulnerabilityCheck()}
|
||||
{signatureCheck()} */}
|
||||
{/* <BookmarkIcon sx={{color:"#52637A"}}/> */}
|
||||
</Stack>
|
||||
<Typography
|
||||
pt={1}
|
||||
sx={{ fontSize: 16, lineHeight: '1.5rem', color: 'rgba(0, 0, 0, 0.6)', paddingLeft: '4rem' }}
|
||||
gutterBottom
|
||||
align="left"
|
||||
>
|
||||
DIGEST:{' '}
|
||||
{
|
||||
// @ts-ignore
|
||||
imageDetailData?.digest
|
||||
}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Stack direction="row">
|
||||
<Grid item xs={10}>
|
||||
<Typography variant="body1" sx={{ color: '#52637A', fontSize: '1rem', paddingTop: '0.75rem' }}>
|
||||
Pull this image
|
||||
{/* <BookmarkIcon sx={{color:"#52637A"}}/> */}
|
||||
</Stack>
|
||||
<Typography
|
||||
pt={1}
|
||||
sx={{ fontSize: 16, lineHeight: '1.5rem', color: 'rgba(0, 0, 0, 0.6)', paddingLeft: '4rem' }}
|
||||
gutterBottom
|
||||
align="left"
|
||||
>
|
||||
DIGEST:{' '}
|
||||
{
|
||||
// @ts-ignore
|
||||
imageDetailData?.digest
|
||||
}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={2}>
|
||||
<IconButton
|
||||
aria-label="copy"
|
||||
onClick={() => navigator.clipboard.writeText(pullString)}
|
||||
data-testid="pullcopy-btn"
|
||||
>
|
||||
<ContentCopyIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Stack>
|
||||
<FormControl sx={{ m: 1, paddingLeft: '1.5rem' }} variant="outlined">
|
||||
<Select
|
||||
className={classes.inputForm}
|
||||
value={pullString}
|
||||
onChange={handleSelectionChange}
|
||||
inputProps={{ 'aria-label': 'Without label' }}
|
||||
sx={{ m: 1, width: '20.625rem', borderRadius: '0.5rem', color: '#14191F', alignContent: 'left' }}
|
||||
>
|
||||
<MenuItem value={`docker pull ${hostRoot()}/${fullName}`}>
|
||||
docker pull {hostRoot()}/{fullName}
|
||||
</MenuItem>
|
||||
<MenuItem value={`podman pull ${hostRoot()}/${fullName}`}>
|
||||
podman pull {hostRoot()}/{fullName}
|
||||
</MenuItem>
|
||||
<MenuItem value={`skopeo copy docker://${hostRoot()}/${fullName}`}>
|
||||
skopeo copy docker://{hostRoot()}/{fullName}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container>
|
||||
<Grid item xs={8} className={classes.tabs}>
|
||||
<TabContext value={selectedTab}>
|
||||
<Box>
|
||||
<TabList
|
||||
onChange={handleTabChange}
|
||||
TabIndicatorProps={{ className: classes.selectedTab }}
|
||||
sx={{ '& button.Mui-selected': { color: '#14191F', fontWeight: '600' } }}
|
||||
>
|
||||
<Tab value="Layers" label="Layers" className={classes.tabContent} />
|
||||
<Tab value="DependsOn" label="Uses" className={classes.tabContent} data-testid="dependencies-tab" />
|
||||
<Tab value="IsDependentOn" label="Used by" className={classes.tabContent} />
|
||||
<Tab value="Vulnerabilities" label="Vulnerabilities" className={classes.tabContent} />
|
||||
</TabList>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<TabPanel value="Layers" className={classes.tabPanel}>
|
||||
<HistoryLayers
|
||||
name={fullName}
|
||||
history={
|
||||
// @ts-ignore
|
||||
imageDetailData.history
|
||||
}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value="DependsOn" className={classes.tabPanel}>
|
||||
<DependsOn name={fullName} />
|
||||
</TabPanel>
|
||||
<TabPanel value="IsDependentOn" className={classes.tabPanel}>
|
||||
<IsDependentOn name={fullName} />
|
||||
</TabPanel>
|
||||
<TabPanel value="Vulnerabilities" className={classes.tabPanel}>
|
||||
<VulnerabilitiesDetails name={name} tag={tag} />
|
||||
</TabPanel>
|
||||
<Grid item xs={4} justifyContent="flex-start">
|
||||
<Stack direction="row">
|
||||
<Grid item xs={10}>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{ color: '#52637A', fontSize: '1rem', paddingTop: '0.75rem', textAlign: 'left' }}
|
||||
>
|
||||
Pull this image
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</TabContext>
|
||||
</Grid>
|
||||
<Grid item xs={4} className={classes.metadata}>
|
||||
<TagDetailsMetadata
|
||||
// @ts-ignore
|
||||
platform={getPlatform()}
|
||||
// @ts-ignore
|
||||
size={imageDetailData?.size}
|
||||
// @ts-ignore
|
||||
lastUpdated={imageDetailData?.lastUpdated}
|
||||
// @ts-ignore
|
||||
license={imageDetailData?.license}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<Grid item xs={2}>
|
||||
<IconButton
|
||||
aria-label="copy"
|
||||
onClick={() => navigator.clipboard.writeText(pullString)}
|
||||
data-testid="pullcopy-btn"
|
||||
>
|
||||
<ContentCopyIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Stack>
|
||||
<FormControl sx={{ m: 1 }} variant="outlined">
|
||||
<Select
|
||||
className={classes.inputForm}
|
||||
value={pullString}
|
||||
onChange={handleSelectionChange}
|
||||
inputProps={{ 'aria-label': 'Without label' }}
|
||||
sx={{ m: 1, width: '20.625rem', borderRadius: '0.5rem', color: '#14191F', alignContent: 'left' }}
|
||||
>
|
||||
<MenuItem value={`docker pull ${hostRoot()}/${fullName}`}>
|
||||
docker pull {hostRoot()}/{fullName}
|
||||
</MenuItem>
|
||||
<MenuItem value={`podman pull ${hostRoot()}/${fullName}`}>
|
||||
podman pull {hostRoot()}/{fullName}
|
||||
</MenuItem>
|
||||
<MenuItem value={`skopeo copy docker://${hostRoot()}/${fullName}`}>
|
||||
skopeo copy docker://{hostRoot()}/{fullName}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container>
|
||||
<Grid item xs={8} className={classes.tabs}>
|
||||
<TabContext value={selectedTab}>
|
||||
<Box>
|
||||
<TabList
|
||||
onChange={handleTabChange}
|
||||
TabIndicatorProps={{ className: classes.selectedTab }}
|
||||
sx={{ '& button.Mui-selected': { color: '#14191F', fontWeight: '600' } }}
|
||||
>
|
||||
<Tab value="Layers" label="Layers" className={classes.tabContent} />
|
||||
<Tab
|
||||
value="DependsOn"
|
||||
label="Uses"
|
||||
className={classes.tabContent}
|
||||
data-testid="dependencies-tab"
|
||||
/>
|
||||
<Tab value="IsDependentOn" label="Used by" className={classes.tabContent} />
|
||||
<Tab value="Vulnerabilities" label="Vulnerabilities" className={classes.tabContent} />
|
||||
</TabList>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<TabPanel value="Layers" className={classes.tabPanel}>
|
||||
<HistoryLayers
|
||||
name={fullName}
|
||||
history={
|
||||
// @ts-ignore
|
||||
imageDetailData.history
|
||||
}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value="DependsOn" className={classes.tabPanel}>
|
||||
<DependsOn name={fullName} />
|
||||
</TabPanel>
|
||||
<TabPanel value="IsDependentOn" className={classes.tabPanel}>
|
||||
<IsDependentOn name={fullName} />
|
||||
</TabPanel>
|
||||
<TabPanel value="Vulnerabilities" className={classes.tabPanel}>
|
||||
<VulnerabilitiesDetails name={name} tag={tag} />
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</TabContext>
|
||||
</Grid>
|
||||
<Grid item xs={4} className={classes.metadata}>
|
||||
<TagDetailsMetadata
|
||||
// @ts-ignore
|
||||
platform={getPlatform()}
|
||||
// @ts-ignore
|
||||
size={imageDetailData?.size}
|
||||
// @ts-ignore
|
||||
lastUpdated={imageDetailData?.lastUpdated}
|
||||
// @ts-ignore
|
||||
license={imageDetailData?.license}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ const mapToRepo = (responseRepo) => {
|
||||
licenses: responseRepo.NewestImage?.Licenses,
|
||||
size: responseRepo.Size,
|
||||
vendor: responseRepo.NewestImage?.Vendor,
|
||||
logo: responseRepo.NewestImage?.Logo,
|
||||
lastUpdated: responseRepo.LastUpdated,
|
||||
downloads: responseRepo.DownloadCount
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user