feat(explore): Implemented filter feature (#104)
feat: Refactored global search, implemented filter feature Signed-off-by: Raul Kele <raulkeleblk@gmail.com>
This commit is contained in:
parent
ca1c9b00cc
commit
c93447b0e4
@ -27,9 +27,9 @@ function App() {
|
||||
<Route element={<AuthWrapper isLoggedIn={isLoggedIn} redirect="/login" />}>
|
||||
<Route path="/" element={<Navigate to="/home" />} />
|
||||
<Route path="/home" element={<HomePage data={data} updateData={setData} />} />
|
||||
<Route path="/explore" element={<ExplorePage data={data} updateData={setData} />} />
|
||||
<Route path="/image/:name" element={<RepoPage updateData={setData} />} />
|
||||
<Route path="/image/:name/tag/:tag" element={<TagPage updateData={setData} />} />
|
||||
<Route path="/explore" element={<ExplorePage />} />
|
||||
<Route path="/image/:name" element={<RepoPage />} />
|
||||
<Route path="/image/:name/tag/:tag" element={<TagPage />} />
|
||||
</Route>
|
||||
<Route element={<AuthWrapper isLoggedIn={!isLoggedIn} redirect="/" />}>
|
||||
<Route
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { api } from 'api';
|
||||
import Explore from 'components/Explore';
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
// router mock
|
||||
@ -13,53 +13,54 @@ jest.mock('react-router-dom', () => ({
|
||||
}));
|
||||
|
||||
const StateExploreWrapper = (props) => {
|
||||
const [data, useData] = useState([]);
|
||||
const queryString = props.search || '';
|
||||
return (
|
||||
<MemoryRouter initialEntries={[queryString]}>
|
||||
<Explore data={data} updateData={useData} />
|
||||
<Explore />
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
const mockImageList = {
|
||||
RepoListWithNewestImage: [
|
||||
{
|
||||
Name: 'alpine',
|
||||
Size: '2806985',
|
||||
LastUpdated: '2022-08-09T17:19:53.274069586Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: 'w',
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
GlobalSearch: {
|
||||
Repos: [
|
||||
{
|
||||
Name: 'alpine',
|
||||
Size: '2806985',
|
||||
LastUpdated: '2022-08-09T17:19:53.274069586Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: 'w',
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'mongo',
|
||||
Size: '231383863',
|
||||
LastUpdated: '2022-08-02T01:30:49.193203152Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'nodeUnique',
|
||||
Size: '369311301',
|
||||
LastUpdated: '2022-08-23T00:20:40.144281895Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'mongo',
|
||||
Size: '231383863',
|
||||
LastUpdated: '2022-08-02T01:30:49.193203152Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'nodeUnique',
|
||||
Size: '369311301',
|
||||
LastUpdated: '2022-08-23T00:20:40.144281895Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
};
|
||||
afterEach(() => {
|
||||
// restore the spy created with spyOn
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import FilterCard from 'components/FilterCard';
|
||||
import React from 'react';
|
||||
import filterConstants from 'utilities/filterConstants';
|
||||
|
||||
const StateFilterCardWrapper = () => {
|
||||
return <FilterCard title="Products" filters={['Images', 'Plugins']} />;
|
||||
return <FilterCard title="Products" filters={filterConstants.osFilters} updateFilters={() => {}} />;
|
||||
};
|
||||
|
||||
describe('Filters components', () => {
|
||||
|
@ -29,7 +29,7 @@ it('renders the repository page component', () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="*" element={<RepoPage updateData={() => {}} />} />
|
||||
<Route path="*" element={<RepoPage />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
@ -20,7 +20,7 @@ it('renders the tags page component', async () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="*" element={<TagPage updateData={() => {}} />} />
|
||||
<Route path="*" element={<TagPage />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
18
src/api.js
18
src/api.js
@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import axios from 'axios';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
@ -65,16 +66,21 @@ const endpoints = {
|
||||
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Digest Tag Layers {Size Digest}} Summary {Name LastUpdated Size Platforms {Os Arch} Vendors NewestImage {RepoName Layers {Size Digest} Digest Tag Title Documentation DownloadCount Source Description History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}}}}}}`,
|
||||
vulnerabilitiesForRepo: (name) =>
|
||||
`/v2/_zot/ext/search?query={CVEListForImage(image: "${name}"){Tag, CVEList {Id Title Description Severity PackageList {Name InstalledVersion FixedVersion}}}}`,
|
||||
globalSearch: (searchQuery) =>
|
||||
`/v2/_zot/ext/search?query={GlobalSearch(query:"${searchQuery}") {Repos {Name LastUpdated Size Platforms { Os Arch } NewestImage { Tag Description Licenses Vendor Labels } DownloadCount}}}`,
|
||||
layersDetailsForImage: (name) =>
|
||||
`/v2/_zot/ext/search?query={Image(image: "${name}"){History {Layer {Size Digest Score} HistoryDescription {Created CreatedBy Author Comment EmptyLayer} }}}`,
|
||||
dependsOnForImage: (name) => `/v2/_zot/ext/search?query={BaseImageList(image: "${name}"){RepoName}}`,
|
||||
isDependentOnForImage: (name) => `/v2/_zot/ext/search?query={DerivedImageList(image: "${name}"){RepoName}}`,
|
||||
globalSearchPaginated: (searchQuery, pageNumber, pageSize) =>
|
||||
`/v2/_zot/ext/search?query={GlobalSearch(query:"${searchQuery}", requestedPage: {limit:${pageSize} offset:${
|
||||
(pageNumber - 1) * pageSize
|
||||
}}) {Repos {Name LastUpdated Size Platforms { Os Arch } NewestImage { Tag Description Licenses Vendor Labels } DownloadCount}}}`
|
||||
globalSearch: ({ searchQuery = '""', pageNumber = 1, pageSize = 15, filter = {} }) => {
|
||||
const searchParam = searchQuery !== '' ? `query:"${searchQuery}"` : `query:""`;
|
||||
const paginationParam = `requestedPage: {limit:${pageSize} offset:${(pageNumber - 1) * pageSize}}`;
|
||||
let filterParam = `,filter: {`;
|
||||
if (filter.Os) filterParam += ` Os:${!isEmpty(filter.Os) ? `"${filter.Os}"` : '""'}`;
|
||||
if (filter.Arch) filterParam += ` Arch:${!isEmpty(filter.Arch) ? `"${filter.Arch}"` : '""'}`;
|
||||
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 Licenses Vendor Labels } DownloadCount}}}`;
|
||||
}
|
||||
};
|
||||
|
||||
export { api, endpoints };
|
||||
|
@ -15,6 +15,9 @@ import { api, endpoints } from '../api';
|
||||
import { host } from '../host';
|
||||
import { mapToRepo } from 'utilities/objectModels.js';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import FilterCard from './FilterCard.jsx';
|
||||
import { isEmpty } from 'lodash';
|
||||
import filterConstants from 'utilities/filterConstants.js';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
gridWrapper: {
|
||||
@ -45,42 +48,51 @@ const useStyles = makeStyles(() => ({
|
||||
}
|
||||
}));
|
||||
|
||||
function Explore({ data, updateData }) {
|
||||
function Explore() {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [exploreData, setExploreData] = useState([]);
|
||||
// const [sortFilter, setSortFilter] = useState('');
|
||||
const [queryParams] = useSearchParams();
|
||||
const search = queryParams.get('search');
|
||||
// filtercard filters
|
||||
const [imageFilters, setImageFilters] = useState(false);
|
||||
const [osFilters, setOSFilters] = useState('');
|
||||
const [archFilters, setArchFilters] = useState('');
|
||||
const classes = useStyles();
|
||||
|
||||
useEffect(() => {
|
||||
if (!queryParams.get('search')) {
|
||||
api
|
||||
.get(`${host()}${endpoints.repoList}`)
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let repoList = response.data.data.RepoListWithNewestImage;
|
||||
let repoData = repoList.map((responseRepo) => {
|
||||
return mapToRepo(responseRepo);
|
||||
});
|
||||
updateData(repoData);
|
||||
setIsLoading(false);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
const buildFilterQuery = () => {
|
||||
let filter = {};
|
||||
// workaround until backend bugfix
|
||||
filter = !isEmpty(osFilters) ? { ...filter, Os: osFilters.toLocaleLowerCase() } : filter;
|
||||
filter = !isEmpty(archFilters) ? { ...filter, Arch: archFilters.toLocaleLowerCase() } : filter;
|
||||
if (imageFilters) {
|
||||
filter = { ...filter, HasToBeSigned: imageFilters };
|
||||
}
|
||||
}, [updateData, queryParams]);
|
||||
return filter;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
api
|
||||
.get(`${host()}${endpoints.globalSearch({ searchQuery: search, filter: buildFilterQuery() })}`)
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let repoList = response.data.data.GlobalSearch.Repos;
|
||||
let repoData = repoList.map((responseRepo) => {
|
||||
return mapToRepo(responseRepo);
|
||||
});
|
||||
setExploreData(repoData);
|
||||
setIsLoading(false);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}, [search, queryParams, imageFilters, osFilters, archFilters]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(false);
|
||||
}, []);
|
||||
|
||||
// Update component data based on response data, here filtering logic will be added
|
||||
useEffect(() => {
|
||||
setExploreData(data);
|
||||
}, [data]);
|
||||
|
||||
const renderRepoCards = () => {
|
||||
return (
|
||||
exploreData &&
|
||||
@ -105,19 +117,30 @@ function Explore({ data, updateData }) {
|
||||
);
|
||||
};
|
||||
|
||||
// const renderFilterCards = () => {
|
||||
// return (
|
||||
// <Stack spacing={2}>
|
||||
// <FilterCard title="Products" filters={['Images', 'Plugins']} />
|
||||
// <FilterCard title="Images" filters={['Verified publisher', 'Official images']} />
|
||||
// <FilterCard title="Operating system" filters={['Windows', 'Linux']} />
|
||||
// <FilterCard
|
||||
// title="Architectures"
|
||||
// filters={['ARM', 'ARM 64', 'IBM POWER', 'IBM Z', 'PowerPC 64 LE', 'x86', 'x86-64']}
|
||||
// />
|
||||
// </Stack>
|
||||
// );
|
||||
// };
|
||||
const renderFilterCards = () => {
|
||||
return (
|
||||
<Stack spacing={2}>
|
||||
<FilterCard
|
||||
title="Images"
|
||||
filters={filterConstants.imageFilters}
|
||||
filterValue={imageFilters}
|
||||
updateFilters={setImageFilters}
|
||||
/>
|
||||
<FilterCard
|
||||
title="Operating system"
|
||||
filters={filterConstants.osFilters}
|
||||
filterValue={osFilters}
|
||||
updateFilters={setOSFilters}
|
||||
/>
|
||||
<FilterCard
|
||||
title="Architectures"
|
||||
filters={filterConstants.archFilters}
|
||||
filterValue={archFilters}
|
||||
updateFilters={setArchFilters}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
// const handleSortChange = (event) => {
|
||||
// setSortFilter(event.target.value);
|
||||
@ -126,46 +149,46 @@ function Explore({ data, updateData }) {
|
||||
return (
|
||||
<Container maxWidth="lg">
|
||||
{isLoading && <Loading />}
|
||||
{!(exploreData && exploreData.length) ? (
|
||||
<Grid container className={classes.nodataWrapper}>
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<div style={{}}>
|
||||
<Alert style={{ marginTop: 10, width: '100%' }} variant="outlined" severity="warning">
|
||||
Looks like we don't have anything matching that search. Try searching something else.
|
||||
</Alert>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
) : (
|
||||
<Grid container className={classes.gridWrapper}>
|
||||
<Grid container item xs={12}>
|
||||
<Grid item xs={0}></Grid>
|
||||
<Grid item xs={12}>
|
||||
<Stack direction="row" className={classes.resultsRow}>
|
||||
<Typography variant="body2" className={classes.results}>
|
||||
Results {exploreData.length}
|
||||
</Typography>
|
||||
{/* <FormControl sx={{m:'1', minWidth:"4.6875rem"}} className={classes.sortForm} size="small">
|
||||
<Grid container className={classes.gridWrapper}>
|
||||
<Grid container item xs={12}>
|
||||
<Grid item xs={0}></Grid>
|
||||
<Grid item xs={12}>
|
||||
<Stack direction="row" className={classes.resultsRow}>
|
||||
<Typography variant="body2" className={classes.results}>
|
||||
Results {exploreData.length}
|
||||
</Typography>
|
||||
{/* <FormControl sx={{m:'1', minWidth:"4.6875rem"}} className={classes.sortForm} size="small">
|
||||
<InputLabel>Sort</InputLabel>
|
||||
<Select label="Sort" value={sortFilter} onChange={handleSortChange} MenuProps={{disableScrollLock: true}}>
|
||||
<MenuItem value='relevance'>Relevance</MenuItem>
|
||||
</Select>
|
||||
</FormControl> */}
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid container item xs={12} spacing={5} pt={1}>
|
||||
{/* <Grid item xs={3}>
|
||||
{renderFilterCards()}
|
||||
</Grid> */}
|
||||
<Grid item xs={12}>
|
||||
</Grid>
|
||||
<Grid container item xs={12} spacing={5} pt={1}>
|
||||
<Grid item xs={3}>
|
||||
{renderFilterCards()}
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
{!(exploreData && exploreData.length) ? (
|
||||
<Grid container className={classes.nodataWrapper}>
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<div style={{}}>
|
||||
<Alert style={{ marginTop: 10, width: '100%' }} variant="outlined" severity="warning">
|
||||
Looks like we don't have anything matching that search. Try searching something else.
|
||||
</Alert>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
) : (
|
||||
<Stack direction="column" spacing={2}>
|
||||
{renderRepoCards()}
|
||||
</Stack>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Card, CardContent, Checkbox, FormControlLabel, Stack, Typography } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
card: {
|
||||
@ -17,17 +17,43 @@ const useStyles = makeStyles(() => ({
|
||||
|
||||
function FilterCard(props) {
|
||||
const classes = useStyles();
|
||||
const { title, filters } = props;
|
||||
const { title, filters, updateFilters } = props;
|
||||
const [selectedFilter, setSelectedFilter] = useState(null);
|
||||
|
||||
const handleFilterClicked = (event, changedFilterLabel, changedFilterValue) => {
|
||||
const { checked } = event.target;
|
||||
|
||||
if (checked) {
|
||||
// updateFilters([...filterValue, changedFilterValue]);
|
||||
if (filters[0]?.type === 'boolean') {
|
||||
updateFilters(checked);
|
||||
} else {
|
||||
updateFilters(changedFilterValue);
|
||||
}
|
||||
setSelectedFilter(changedFilterLabel);
|
||||
} else {
|
||||
// updateFilters(filterValue.filter((e) => e !== changedFilterValue));
|
||||
if (filters[0]?.type === 'boolean') {
|
||||
updateFilters(checked);
|
||||
} else {
|
||||
updateFilters('');
|
||||
}
|
||||
setSelectedFilter(null);
|
||||
}
|
||||
};
|
||||
|
||||
const getFilterRows = () => {
|
||||
const filterRows = filters || ['ARM', 'ARM 64', 'IBM POWER', 'IBM Z', 'PowerPC 64 LE', 'x86', 'x86-64'];
|
||||
const filterRows = filters;
|
||||
return filterRows.map((filter, index) => {
|
||||
return (
|
||||
<FormControlLabel
|
||||
key={index}
|
||||
componentsProps={{ typography: { variant: 'body2' } }}
|
||||
control={<Checkbox />}
|
||||
label={filter}
|
||||
label={filter.label}
|
||||
id={title}
|
||||
checked={filter.label === selectedFilter}
|
||||
onChange={() => handleFilterClicked(event, filter.label, filter.value)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -68,7 +68,7 @@ const useStyles = makeStyles(() => ({
|
||||
}
|
||||
}));
|
||||
|
||||
function Header({ updateData }) {
|
||||
function Header() {
|
||||
const classes = useStyles();
|
||||
const path = useLocation().pathname;
|
||||
// const navigate = useNavigate();
|
||||
@ -97,7 +97,7 @@ function Header({ updateData }) {
|
||||
<Link to="/home" className={classes.logoWrapper}>
|
||||
<Avatar alt="zot" src={logo} className={classes.logo} variant="square" />
|
||||
</Link>
|
||||
{path !== '/' && <SearchSuggestion updateData={updateData} />}
|
||||
{path !== '/' && <SearchSuggestion />}
|
||||
<div></div>
|
||||
{/* <IconButton
|
||||
ref={anchorRef}
|
||||
|
@ -171,7 +171,7 @@ function RepoCard(props) {
|
||||
{signatureCheck()} */}
|
||||
{/* <Chip label="Verified licensee" sx={{ backgroundColor: "#E8F5E9", color: "#388E3C" }} variant="filled" onDelete={() => { return }} deleteIcon={vulnerabilityCheck()} /> */}
|
||||
</Stack>
|
||||
<Typography className={classes.versionLast} pt={1} sx={{ fontSize: 12 }} gutterBottom>
|
||||
<Typography className={classes.versionLast} pt={1} sx={{ fontSize: 12 }} gutterBottom noWrap>
|
||||
{description || 'N/A'}
|
||||
</Typography>
|
||||
<Stack alignItems="center" direction="row" spacing={2} pt={1}>
|
||||
|
@ -71,7 +71,7 @@ const useStyles = makeStyles(() => ({
|
||||
}
|
||||
}));
|
||||
|
||||
function SearchSuggestion({ updateData }) {
|
||||
function SearchSuggestion() {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [suggestionData, setSuggestionData] = useState([]);
|
||||
const navigate = useNavigate();
|
||||
@ -85,21 +85,7 @@ function SearchSuggestion({ updateData }) {
|
||||
const handleSearch = (event) => {
|
||||
const { key, type } = event;
|
||||
if (key === 'Enter' || type === 'click') {
|
||||
api
|
||||
.get(`${host()}${endpoints.globalSearch(searchQuery)}`)
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let repoList = response.data.data.GlobalSearch.Repos;
|
||||
let repoData = repoList.map((responseRepo) => {
|
||||
return mapToRepo(responseRepo);
|
||||
});
|
||||
updateData(repoData);
|
||||
navigate({ pathname: `/explore`, search: createSearchParams({ search: searchQuery }).toString() });
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
navigate({ pathname: `/explore`, search: createSearchParams({ search: searchQuery }).toString() });
|
||||
}
|
||||
};
|
||||
|
||||
@ -108,7 +94,7 @@ function SearchSuggestion({ updateData }) {
|
||||
setSearchQuery(value);
|
||||
if (value !== '' && value.length > 1) {
|
||||
api
|
||||
.get(`${host()}${endpoints.globalSearchPaginated(value, 1, 9)}`)
|
||||
.get(`${host()}${endpoints.globalSearch({ searchQuery: value, pageNumber: 1, pageSize: 9 })}`)
|
||||
.then((suggestionResponse) => {
|
||||
if (suggestionResponse.data.data.GlobalSearch.Repos) {
|
||||
const suggestionParsedData = suggestionResponse.data.data.GlobalSearch.Repos.map((el) => mapToRepo(el));
|
||||
|
@ -25,16 +25,16 @@ const useStyles = makeStyles(() => ({
|
||||
}
|
||||
}));
|
||||
|
||||
function ExplorePage({ data, updateData }) {
|
||||
function ExplorePage() {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Stack className={classes.pageWrapper} direction="column" data-testid="explore-container">
|
||||
<Header updateData={updateData} />
|
||||
<Header />
|
||||
<Container className={classes.container}>
|
||||
<Grid container className={classes.gridWrapper}>
|
||||
<Grid item className={classes.tile}>
|
||||
<Explore data={data} updateData={updateData} />
|
||||
<Explore />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
|
@ -30,7 +30,7 @@ function HomePage({ data, updateData }) {
|
||||
|
||||
return (
|
||||
<Stack className={classes.pageWrapper} direction="column" data-testid="homepage-container">
|
||||
<Header updateData={updateData} />
|
||||
<Header />
|
||||
<Container className={classes.container}>
|
||||
<Grid container className={classes.gridWrapper}>
|
||||
<Grid item className={classes.tile}>
|
||||
|
@ -29,12 +29,12 @@ const useStyles = makeStyles(() => ({
|
||||
}
|
||||
}));
|
||||
|
||||
function RepoPage({ updateData }) {
|
||||
function RepoPage() {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Stack direction="column" className={classes.pageWrapper} data-testid="repo-container">
|
||||
<Header updateData={updateData} />
|
||||
<Header />
|
||||
<Container className={classes.container}>
|
||||
<ExploreHeader />
|
||||
<Grid container className={classes.gridWrapper}>
|
||||
|
@ -29,12 +29,12 @@ const useStyles = makeStyles(() => ({
|
||||
}
|
||||
}));
|
||||
|
||||
function TagPage({ updateData }) {
|
||||
function TagPage() {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Stack direction="column" className={classes.pageWrapper} data-testid="tag-container">
|
||||
<Header updateData={updateData} />
|
||||
<Header />
|
||||
<Container className={classes.container}>
|
||||
<ExploreHeader />
|
||||
<Grid container className={classes.gridWrapper}>
|
||||
|
53
src/utilities/filterConstants.js
Normal file
53
src/utilities/filterConstants.js
Normal file
@ -0,0 +1,53 @@
|
||||
const osFilters = [
|
||||
{
|
||||
label: 'Windows',
|
||||
value: 'windows'
|
||||
},
|
||||
{
|
||||
label: 'Linux',
|
||||
value: 'linux'
|
||||
}
|
||||
];
|
||||
|
||||
const imageFilters = [
|
||||
{
|
||||
label: 'Signed Images',
|
||||
value: 'HasToBeSigned',
|
||||
type: 'boolean'
|
||||
}
|
||||
];
|
||||
|
||||
const archFilters = [
|
||||
{
|
||||
label: 'ARM',
|
||||
value: 'arm'
|
||||
},
|
||||
{
|
||||
label: 'ARM 64',
|
||||
value: 'arm64'
|
||||
},
|
||||
{
|
||||
label: 'IBM POWER',
|
||||
value: 'ppc64'
|
||||
},
|
||||
{
|
||||
label: 'IBM Z',
|
||||
value: 's390x'
|
||||
},
|
||||
{
|
||||
label: 'PowerPC 64 LE',
|
||||
value: 'ppc64le'
|
||||
},
|
||||
{
|
||||
label: 'x86',
|
||||
value: '386'
|
||||
},
|
||||
{
|
||||
label: 'x86-64',
|
||||
value: 'amd64'
|
||||
}
|
||||
];
|
||||
|
||||
const filterConstants = { osFilters, imageFilters, archFilters };
|
||||
|
||||
export default filterConstants;
|
Loading…
Reference in New Issue
Block a user