Skip to content

Commit 763ce33

Browse files
authored
Merge pull request #1821 from kleros/feat/search-bar-courts-page
feat: add the search bar in courts page
2 parents 6d09c7d + 605b082 commit 763ce33

File tree

6 files changed

+174
-72
lines changed

6 files changed

+174
-72
lines changed

web/src/components/Verdict/Answer.tsx

+3-13
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
11
import React from "react";
2-
import styled from "styled-components";
32

43
import { Answer } from "@kleros/kleros-sdk/src/dataMappings/utils/disputeDetailsTypes";
54

6-
import { AnswerDescription, AnswerTitle, AnswerTitleAndDescription } from "../DisputePreview/DisputeContext";
7-
8-
const Container = styled.div`
9-
display: flex;
10-
flex-direction: row;
11-
flex-wrap: wrap;
12-
align-items: center;
13-
gap: 6px;
14-
`;
5+
import { AnswerTitle, AnswerTitleAndDescription } from "../DisputePreview/DisputeContext";
156

167
interface IAnswer {
178
answer?: Answer;
@@ -24,12 +15,11 @@ const AnswerDisplay: React.FC<IAnswer> = ({ answer, currentRuling }) => {
2415
{answer ? (
2516
<AnswerTitleAndDescription dir="auto">
2617
<AnswerTitle>{answer.title}</AnswerTitle>
27-
<AnswerDescription>{answer.description.trim() ? ` - ${answer.description}` : null}</AnswerDescription>
2818
</AnswerTitleAndDescription>
2919
) : (
30-
<Container>
20+
<AnswerTitleAndDescription>
3121
{currentRuling !== 0 ? <small>Answer 0x{currentRuling}</small> : <small>Refuse to Arbitrate</small>}
32-
</Container>
22+
</AnswerTitleAndDescription>
3323
)}
3424
</>
3525
);

web/src/components/Verdict/FinalDecision.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ const Container = styled.div`
3333

3434
const JuryContainer = styled.div`
3535
display: flex;
36-
flex-direction: row;
37-
flex-wrap: wrap;
3836
align-items: center;
3937
gap: 5px 7px;
40-
flex: 1;
38+
flex-wrap: wrap;
39+
4140
h3 {
4241
line-height: 21px;
4342
margin-bottom: 0px;
4443
}
44+
4545
> div {
4646
flex: 1;
4747
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import React, { useState, useMemo } from "react";
2+
import styled, { css } from "styled-components";
3+
4+
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
5+
import { useNavigate, useParams } from "react-router-dom";
6+
7+
import { Card, DropdownCascader, Searchbar } from "@kleros/ui-components-library";
8+
9+
import { isUndefined } from "utils/index";
10+
11+
import { useCourtTree, rootCourtToItems } from "queries/useCourtTree";
12+
13+
import { isKlerosUniversity } from "src/consts";
14+
15+
import { hoverShortTransitionTiming } from "styles/commonStyles";
16+
import { landscapeStyle } from "styles/landscapeStyle";
17+
import { responsiveSize } from "styles/responsiveSize";
18+
19+
import { StyledSkeleton } from "components/StyledSkeleton";
20+
21+
import StakeMaintenanceButtons from "../StakeMaintenanceButton";
22+
23+
const Container = styled.div`
24+
width: 100%;
25+
display: flex;
26+
justify-content: space-between;
27+
align-items: center;
28+
gap: 8px 16px;
29+
flex-wrap: wrap;
30+
`;
31+
32+
const StyledDropdownCascader = styled(DropdownCascader)`
33+
width: ${responsiveSize(200, 240)};
34+
> button {
35+
width: 100%;
36+
}
37+
`;
38+
39+
const SearchBarContainer = styled.div`
40+
display: flex;
41+
flex-wrap: wrap;
42+
position: relative;
43+
${landscapeStyle(
44+
() => css`
45+
flex: 1;
46+
`
47+
)}
48+
`;
49+
50+
const StyledSearchbar = styled(Searchbar)`
51+
width: 100%;
52+
input {
53+
font-size: 16px;
54+
height: 45px;
55+
padding-top: 0px;
56+
padding-bottom: 0px;
57+
}
58+
`;
59+
60+
const SearchResultsContainer = styled(OverlayScrollbarsComponent)`
61+
position: absolute;
62+
margin-top: 45px;
63+
max-height: 400px;
64+
border: 1px solid ${({ theme }) => theme.stroke};
65+
width: 100%;
66+
flex-direction: column;
67+
border-radius: 4px;
68+
overflow-y: auto;
69+
z-index: 1;
70+
background-color: ${({ theme }) => theme.whiteBackground};
71+
`;
72+
73+
const StyledCard = styled(Card)<{ selected: boolean }>`
74+
${hoverShortTransitionTiming}
75+
height: auto;
76+
width: 100%;
77+
padding: ${({ selected }) => (selected ? "16px 13px" : "16px")};
78+
cursor: pointer;
79+
border: none;
80+
border-left: ${({ selected, theme }) => (selected ? `3px solid ${theme.primaryBlue}` : "none")};
81+
background-color: ${({ selected, theme }) => (selected ? theme.mediumBlue : "transparent")};
82+
83+
:hover {
84+
background-color: ${({ theme }) => theme.mediumBlue};
85+
}
86+
`;
87+
88+
const CourtParentSpan = styled.span`
89+
color: ${({ theme }) => theme.secondaryText}EE;
90+
`;
91+
92+
const CourtNameSpan = styled.span`
93+
color: ${({ theme }) => theme.primaryText};
94+
`;
95+
96+
function flattenCourts(court, parent = null) {
97+
const current = {
98+
...court,
99+
parentName: parent?.name ?? null,
100+
};
101+
const children = (court.children || []).flatMap((child) => flattenCourts(child, current));
102+
return [current, ...children];
103+
}
104+
105+
const TopSearch: React.FC = () => {
106+
const { data } = useCourtTree();
107+
const navigate = useNavigate();
108+
const { id: currentCourtId } = useParams();
109+
const items = useMemo(() => !isUndefined(data) && [rootCourtToItems(data.court)], [data]);
110+
const isUniversity = isKlerosUniversity();
111+
const [search, setSearch] = useState("");
112+
113+
const filteredCourts = useMemo(() => {
114+
if (!data?.court) return [];
115+
const courts = flattenCourts(data.court).filter((c) => c.name.toLowerCase().includes(search.toLowerCase()));
116+
const selectedCourt = courts.find((c) => c.id === currentCourtId);
117+
if (!selectedCourt) return courts;
118+
119+
return [selectedCourt, ...courts.filter((c) => c.id !== currentCourtId)];
120+
}, [data, search, currentCourtId]);
121+
122+
return (
123+
<Container>
124+
{items ? (
125+
<>
126+
<StyledDropdownCascader
127+
items={items}
128+
onSelect={(path) => navigate(path.toString())}
129+
placeholder="Select Court"
130+
/>
131+
<SearchBarContainer>
132+
<StyledSearchbar
133+
dir="auto"
134+
type="text"
135+
placeholder="Search"
136+
value={search}
137+
onChange={(e) => setSearch(e.target.value)}
138+
/>
139+
{search && filteredCourts.length > 0 && (
140+
<SearchResultsContainer>
141+
{filteredCourts.map((court) => (
142+
<StyledCard
143+
key={court.id}
144+
selected={court.id === currentCourtId}
145+
onClick={() => {
146+
navigate(`/courts/${court.id}`);
147+
setSearch("");
148+
}}
149+
>
150+
{court.parentName && <CourtParentSpan>{court.parentName} / </CourtParentSpan>}
151+
<CourtNameSpan>{court.name}</CourtNameSpan>
152+
</StyledCard>
153+
))}
154+
</SearchResultsContainer>
155+
)}
156+
</SearchBarContainer>
157+
</>
158+
) : (
159+
<StyledSkeleton width={240} height={42} />
160+
)}
161+
{isUniversity ? null : <StakeMaintenanceButtons />}
162+
</Container>
163+
);
164+
};
165+
166+
export default TopSearch;

web/src/pages/Courts/CourtDetails/index.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { Divider } from "components/Divider";
2525
import Description from "./Description";
2626
import StakePanel from "./StakePanel";
2727
import Stats from "./Stats";
28+
import TopSearch from "./TopSearch";
2829

2930
const Container = styled.div``;
3031

@@ -113,6 +114,7 @@ const CourtDetails: React.FC = () => {
113114

114115
return (
115116
<Container>
117+
<TopSearch />
116118
<StyledCard>
117119
<CourtHeader>
118120
<CourtInfo>

web/src/pages/Courts/TopSearch.tsx

-54
This file was deleted.

web/src/pages/Courts/index.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { responsiveSize } from "styles/responsiveSize";
77
import { Routes, Route, Navigate } from "react-router-dom";
88

99
import CourtDetails from "./CourtDetails";
10-
import TopSearch from "./TopSearch";
1110

1211
const Container = styled.div`
1312
width: 100%;
@@ -26,7 +25,6 @@ const Container = styled.div`
2625
const Courts: React.FC = () => {
2726
return (
2827
<Container>
29-
<TopSearch />
3028
<Routes>
3129
<Route path="/:id/*" element={<CourtDetails />} />
3230
<Route path="*" element={<Navigate to="1" replace />} />

0 commit comments

Comments
 (0)