Skip to content

Commit 7de6cca

Browse files
committed
feat(web): all lists page, refactors, styling
1 parent db457ac commit 7de6cca

File tree

26 files changed

+1064
-8
lines changed

26 files changed

+1064
-8
lines changed

web/src/app.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import StyledComponentsProvider from "context/StyledComponentsProvider";
1010
import RefetchOnBlock from "context/RefetchOnBlock";
1111
import Layout from "layout/index";
1212
import Home from "./pages/Home";
13+
import AllLists from "./pages/AllLists";
1314

1415
const App: React.FC = () => {
1516
return (
@@ -21,6 +22,7 @@ const App: React.FC = () => {
2122
<SentryRoutes>
2223
<Route path="/" element={<Layout />}>
2324
<Route index element={<Home />} />
25+
<Route path="lists/*" element={<AllLists />} />
2426
<Route path="*" element={<h1>404 not found</h1>} />
2527
</Route>
2628
</SentryRoutes>
Lines changed: 9 additions & 0 deletions
Loading
Lines changed: 10 additions & 0 deletions
Loading
Lines changed: 19 additions & 0 deletions
Loading
Lines changed: 11 additions & 0 deletions
Loading

web/src/assets/svgs/icons/home.svg

Lines changed: 10 additions & 0 deletions
Loading
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React from "react";
2+
import styled, { useTheme } from "styled-components";
3+
import { useNavigate, useParams } from "react-router-dom";
4+
import { useWindowSize } from "react-use";
5+
import { DropdownSelect } from "@kleros/ui-components-library";
6+
import { useIsList } from "context/IsListProvider";
7+
import ListIcon from "svgs/icons/list.svg";
8+
import GridIcon from "svgs/icons/grid.svg";
9+
import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle";
10+
import { decodeURIFilter, encodeURIFilter, useRootPath } from "utils/uri";
11+
12+
const Container = styled.div`
13+
display: flex;
14+
justify-content: end;
15+
gap: 12px;
16+
width: fit-content;
17+
`;
18+
19+
const StyledGridIcon = styled(GridIcon)`
20+
cursor: pointer;
21+
transition: filter 0.2s ease;
22+
fill: ${({ theme }) => theme.primaryBlue};
23+
width: 16px;
24+
height: 16px;
25+
overflow: hidden;
26+
`;
27+
28+
const IconsContainer = styled.div`
29+
display: flex;
30+
justify-content: center;
31+
align-items: center;
32+
gap: 4px;
33+
`;
34+
35+
const StyledListIcon = styled(ListIcon)`
36+
cursor: pointer;
37+
transition: filter 0.2s ease;
38+
fill: ${({ theme }) => theme.primaryBlue};
39+
width: 16px;
40+
height: 16px;
41+
overflow: hidden;
42+
`;
43+
44+
const Filters: React.FC = () => {
45+
const theme = useTheme();
46+
const { order, filter } = useParams();
47+
const { ruled, period, ...filterObject } = decodeURIFilter(filter ?? "all");
48+
const navigate = useNavigate();
49+
const location = useRootPath();
50+
51+
const handleStatusChange = (value: string | number) => {
52+
const parsedValue = JSON.parse(value as string);
53+
const encodedFilter = encodeURIFilter({ ...filterObject, ...parsedValue });
54+
navigate(`${location}/1/${order}/${encodedFilter}`);
55+
};
56+
57+
const handleOrderChange = (value: string | number) => {
58+
const encodedFilter = encodeURIFilter({ ruled, period, ...filterObject });
59+
navigate(`${location}/1/${value}/${encodedFilter}`);
60+
};
61+
62+
const { width } = useWindowSize();
63+
const { isList, setIsList } = useIsList();
64+
const screenIsBig = width > BREAKPOINT_LANDSCAPE;
65+
66+
return (
67+
<Container>
68+
<DropdownSelect
69+
smallButton
70+
simpleButton
71+
items={[
72+
{ value: "desc", text: "Most Active" },
73+
{ value: "desc", text: "Newest" },
74+
{ value: "asc", text: "Oldest" },
75+
]}
76+
defaultValue={order}
77+
callback={handleOrderChange}
78+
/>
79+
{screenIsBig ? (
80+
<IconsContainer>
81+
{isList ? (
82+
<StyledGridIcon onClick={() => setIsList(false)} />
83+
) : (
84+
<StyledListIcon
85+
onClick={() => {
86+
if (screenIsBig) {
87+
setIsList(true);
88+
}
89+
}}
90+
/>
91+
)}
92+
</IconsContainer>
93+
) : null}
94+
</Container>
95+
);
96+
};
97+
98+
export default Filters;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React, { useMemo } from "react";
2+
import styled from "styled-components";
3+
import { useWindowSize } from "react-use";
4+
import { useParams } from "react-router-dom";
5+
import { SkeletonRegistryCard, SkeletonRegistryListItem } from "../StyledSkeleton";
6+
import { StandardPagination } from "@kleros/ui-components-library";
7+
import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle";
8+
import { useIsList } from "context/IsListProvider";
9+
import { isUndefined } from "utils/index";
10+
import { decodeURIFilter } from "utils/uri";
11+
// import { RegistryDetailsFragment } from "queries/useCasesQuery";
12+
import RegistryCard from "components/RegistryCard";
13+
14+
const GridContainer = styled.div`
15+
--gap: 24px;
16+
display: grid;
17+
grid-template-columns: repeat(auto-fill, minmax(min(100%, max(348px, (100% - var(--gap) * 2)/3)), 1fr));
18+
align-items: center;
19+
gap: var(--gap);
20+
`;
21+
22+
const ListContainer = styled.div`
23+
display: flex;
24+
flex-direction: column;
25+
justify-content: center;
26+
gap: 8px;
27+
`;
28+
29+
const StyledPagination = styled(StandardPagination)`
30+
margin-top: 24px;
31+
margin-left: auto;
32+
margin-right: auto;
33+
`;
34+
35+
export interface IRegistriesGrid {
36+
registries?: [];
37+
currentPage: number;
38+
setCurrentPage: (newPage: number) => void;
39+
registriesPerPage: number;
40+
totalPages: number;
41+
}
42+
43+
const RegistriesGrid: React.FC<IRegistriesGrid> = ({
44+
registries,
45+
registriesPerPage,
46+
totalPages,
47+
currentPage,
48+
setCurrentPage,
49+
}) => {
50+
const { filter } = useParams();
51+
const decodedFilter = decodeURIFilter(filter ?? "all");
52+
const { id: searchValue } = decodedFilter;
53+
const { isList } = useIsList();
54+
const { width } = useWindowSize();
55+
const screenIsBig = useMemo(() => width > BREAKPOINT_LANDSCAPE, [width]);
56+
57+
return (
58+
<>
59+
{isList && screenIsBig ? (
60+
<ListContainer>
61+
{isUndefined(registries)
62+
? [...Array(registriesPerPage)].map((_, i) => <SkeletonRegistryListItem key={i} />)
63+
: registries.map((registry) => {
64+
return <RegistryCard key={registry.id} {...registry} />;
65+
})}
66+
</ListContainer>
67+
) : (
68+
<GridContainer>
69+
{isUndefined(registries)
70+
? [...Array(registriesPerPage)].map((_, i) => <SkeletonRegistryCard key={i} />)
71+
: registries.map((registry) => {
72+
return <RegistryCard key={registry.id} {...registry} overrideIsList />;
73+
})}
74+
</GridContainer>
75+
)}
76+
77+
{isUndefined(searchValue) ? (
78+
<StyledPagination
79+
currentPage={currentPage}
80+
numPages={Math.ceil(totalPages ?? 0)}
81+
callback={(page: number) => setCurrentPage(page)}
82+
/>
83+
) : null}
84+
</>
85+
);
86+
};
87+
88+
export default RegistriesGrid;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React, { useState } from "react";
2+
import styled, { css } from "styled-components";
3+
import { landscapeStyle } from "styles/landscapeStyle";
4+
import { useNavigate, useParams } from "react-router-dom";
5+
import { useDebounce } from "react-use";
6+
import { Searchbar, DropdownSelect } from "@kleros/ui-components-library";
7+
import { decodeURIFilter, encodeURIFilter, useRootPath } from "utils/uri";
8+
import { StyledGlobeIcon } from "../StyledIcons/GlobeIcon";
9+
import { StyledEthereumIcon } from "../StyledIcons/EthereumIcon";
10+
import { StyledGnosisIcon } from "../StyledIcons/GnosisIcon";
11+
import { StyledPolygonIcon } from "../StyledIcons/PolygonIcon";
12+
13+
const Container = styled.div`
14+
display: flex;
15+
flex-direction: column;
16+
gap: 16px;
17+
margin-bottom: 16px;
18+
19+
${landscapeStyle(
20+
() =>
21+
css`
22+
flex-direction: row;
23+
`
24+
)}
25+
`;
26+
27+
const SearchBarContainer = styled.div`
28+
width: 100%;
29+
display: flex;
30+
flex-wrap: wrap;
31+
gap: 8px;
32+
z-index: 0;
33+
`;
34+
35+
const StyledSearchbar = styled(Searchbar)`
36+
flex: 1;
37+
flex-basis: 310px;
38+
input {
39+
font-size: 16px;
40+
height: 45px;
41+
padding-top: 0px;
42+
padding-bottom: 0px;
43+
}
44+
`;
45+
46+
const Search: React.FC = () => {
47+
const { page, order, filter } = useParams();
48+
const location = useRootPath();
49+
const decodedFilter = decodeURIFilter(filter ?? "all");
50+
const { id: searchValue, ...filterObject } = decodedFilter;
51+
const [search, setSearch] = useState(searchValue ?? "");
52+
const navigate = useNavigate();
53+
useDebounce(
54+
() => {
55+
const newFilters = search === "" ? { ...filterObject } : { ...filterObject, id: search };
56+
const encodedFilter = encodeURIFilter(newFilters);
57+
navigate(`${location}/${page}/${order}/${encodedFilter}`);
58+
},
59+
500,
60+
[search]
61+
);
62+
63+
return (
64+
<Container>
65+
<SearchBarContainer>
66+
<StyledSearchbar
67+
type="text"
68+
placeholder="Search By ID"
69+
value={search}
70+
onChange={(e) => setSearch(e.target.value)}
71+
/>
72+
</SearchBarContainer>
73+
<DropdownSelect
74+
items={[
75+
{ text: "All Networks", value: 1, Icon: StyledGlobeIcon },
76+
{ text: "Ethereum", value: 2, Icon: StyledEthereumIcon },
77+
{ text: "Gnosis", value: 3, Icon: StyledGnosisIcon },
78+
{ text: "Polygon", value: 4, Icon: StyledPolygonIcon },
79+
]}
80+
defaultValue={1}
81+
callback={() => {}}
82+
/>
83+
<DropdownSelect
84+
items={[
85+
{ text: "All Status", dot: "grey", value: 1 },
86+
{ text: "Pending", dot: "blue", value: 2 },
87+
{ text: "Disputed", dot: "purple", value: 3 },
88+
{ text: "Included", dot: "green", value: 4 },
89+
{ text: "Removed", dot: "red", value: 5 },
90+
]}
91+
defaultValue={1}
92+
callback={() => {}}
93+
/>
94+
</Container>
95+
);
96+
};
97+
98+
export default Search;

0 commit comments

Comments
 (0)