From 1ede3d4a6817cd04c7dec940bc1d2cfb206aaffc Mon Sep 17 00:00:00 2001 From: kemuru <102478601+kemuru@users.noreply.github.com> Date: Mon, 6 Jan 2025 19:50:36 +0100 Subject: [PATCH 1/4] feat: add the search bar in courts page --- web/src/pages/Courts/TopSearch.tsx | 105 ++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 15 deletions(-) diff --git a/web/src/pages/Courts/TopSearch.tsx b/web/src/pages/Courts/TopSearch.tsx index bc933f312..439f06b87 100644 --- a/web/src/pages/Courts/TopSearch.tsx +++ b/web/src/pages/Courts/TopSearch.tsx @@ -1,18 +1,12 @@ -import React, { useMemo } from "react"; -import styled from "styled-components"; - +import React, { useState, useMemo } from "react"; +import styled, { css } from "styled-components"; import { useNavigate } from "react-router-dom"; - -import { DropdownCascader } from "@kleros/ui-components-library"; - +import { Card, DropdownCascader, Searchbar } from "@kleros/ui-components-library"; import { isUndefined } from "utils/index"; - import { useCourtTree, rootCourtToItems } from "queries/useCourtTree"; - import { responsiveSize } from "styles/responsiveSize"; - +import { landscapeStyle } from "styles/landscapeStyle"; import { StyledSkeleton } from "components/StyledSkeleton"; - import StakeMaintenanceButtons from "./StakeMaintenanceButton"; import { isKlerosUniversity } from "src/consts"; @@ -21,6 +15,8 @@ const Container = styled.div` display: flex; justify-content: space-between; align-items: center; + gap: 8px 16px; + flex-wrap: wrap; `; const StyledDropdownCascader = styled(DropdownCascader)` @@ -30,19 +26,98 @@ const StyledDropdownCascader = styled(DropdownCascader)` } `; +const SearchBarContainer = styled.div` + display: flex; + flex-wrap: wrap; + position: relative; + ${landscapeStyle( + () => css` + flex: 1; + ` + )} +`; + +const StyledSearchbar = styled(Searchbar)` + width: 100%; + input { + font-size: 16px; + height: 45px; + padding-top: 0px; + padding-bottom: 0px; + } +`; + +const SearchResultsContainer = styled.div` + position: absolute; + margin-top: 45px; + max-height: 400px; + width: 100%; + flex-direction: column; + border-radius: 4px; + overflow-y: auto; + z-index: 1; + background-color: ${({ theme }) => theme.whiteBackground}; +`; + +const StyledCard = styled(Card)` + height: auto; + width: 100%; + padding: 16px; + color: ${({ theme }) => theme.primaryText}; + cursor: pointer; +`; + +function flattenCourts(court) { + return court ? [court, ...(court.children || []).flatMap(flattenCourts)] : []; +} + const TopSearch: React.FC = () => { const { data } = useCourtTree(); const navigate = useNavigate(); const items = useMemo(() => !isUndefined(data) && [rootCourtToItems(data.court)], [data]); const isUniversity = isKlerosUniversity(); + const [search, setSearch] = useState(""); + const filteredCourts = useMemo( + () => + data?.court ? flattenCourts(data.court).filter((c) => c.name.toLowerCase().includes(search.toLowerCase())) : [], + [data, search] + ); + return ( {items ? ( - navigate(path.toString())} - placeholder="Select Court" - /> + <> + navigate(path.toString())} + placeholder="Select Court" + /> + + setSearch(e.target.value)} + /> + {search && filteredCourts.length > 0 && ( + + {filteredCourts.map((court) => ( + { + navigate(`/courts/${court.id}`); + setSearch(""); + }} + > + {court.name} + + ))} + + )} + + ) : ( )} From dcfc3378af420dc3c228e9f0d81080fce6c33532 Mon Sep 17 00:00:00 2001 From: kemuru <102478601+kemuru@users.noreply.github.com> Date: Mon, 6 Jan 2025 20:14:58 +0100 Subject: [PATCH 2/4] chore: nitpick spaces in import --- web/src/pages/Courts/TopSearch.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/src/pages/Courts/TopSearch.tsx b/web/src/pages/Courts/TopSearch.tsx index 439f06b87..ac92d23ea 100644 --- a/web/src/pages/Courts/TopSearch.tsx +++ b/web/src/pages/Courts/TopSearch.tsx @@ -1,14 +1,20 @@ import React, { useState, useMemo } from "react"; import styled, { css } from "styled-components"; + import { useNavigate } from "react-router-dom"; + import { Card, DropdownCascader, Searchbar } from "@kleros/ui-components-library"; + +import { isKlerosUniversity } from "src/consts"; import { isUndefined } from "utils/index"; + import { useCourtTree, rootCourtToItems } from "queries/useCourtTree"; + import { responsiveSize } from "styles/responsiveSize"; import { landscapeStyle } from "styles/landscapeStyle"; + import { StyledSkeleton } from "components/StyledSkeleton"; import StakeMaintenanceButtons from "./StakeMaintenanceButton"; -import { isKlerosUniversity } from "src/consts"; const Container = styled.div` width: 100%; From 94b3a698b36d4c80ae081e633793e38dff9f7b32 Mon Sep 17 00:00:00 2001 From: kemuru <102478601+kemuru@users.noreply.github.com> Date: Wed, 8 Jan 2025 04:47:38 +0100 Subject: [PATCH 3/4] feat: better styling for dropdown, better wrapping for verdict answer, remove verdict description --- web/src/components/Verdict/Answer.tsx | 16 +----- web/src/components/Verdict/FinalDecision.tsx | 6 +- .../Courts/{ => CourtDetails}/TopSearch.tsx | 56 ++++++++++++++----- web/src/pages/Courts/CourtDetails/index.tsx | 2 + web/src/pages/Courts/index.tsx | 2 - 5 files changed, 50 insertions(+), 32 deletions(-) rename web/src/pages/Courts/{ => CourtDetails}/TopSearch.tsx (63%) diff --git a/web/src/components/Verdict/Answer.tsx b/web/src/components/Verdict/Answer.tsx index 47d17b66b..fe03b6b66 100644 --- a/web/src/components/Verdict/Answer.tsx +++ b/web/src/components/Verdict/Answer.tsx @@ -1,17 +1,8 @@ import React from "react"; -import styled from "styled-components"; import { Answer } from "@kleros/kleros-sdk/src/dataMappings/utils/disputeDetailsTypes"; -import { AnswerDescription, AnswerTitle, AnswerTitleAndDescription } from "../DisputePreview/DisputeContext"; - -const Container = styled.div` - display: flex; - flex-direction: row; - flex-wrap: wrap; - align-items: center; - gap: 6px; -`; +import { AnswerTitle, AnswerTitleAndDescription } from "../DisputePreview/DisputeContext"; interface IAnswer { answer?: Answer; @@ -24,12 +15,11 @@ const AnswerDisplay: React.FC = ({ answer, currentRuling }) => { {answer ? ( {answer.title} - {answer.description.trim() ? ` - ${answer.description}` : null} ) : ( - + {currentRuling !== 0 ? Answer 0x{currentRuling} : Refuse to Arbitrate} - + )} ); diff --git a/web/src/components/Verdict/FinalDecision.tsx b/web/src/components/Verdict/FinalDecision.tsx index f2a25320d..3191c53c8 100644 --- a/web/src/components/Verdict/FinalDecision.tsx +++ b/web/src/components/Verdict/FinalDecision.tsx @@ -33,15 +33,15 @@ const Container = styled.div` const JuryContainer = styled.div` display: flex; - flex-direction: row; - flex-wrap: wrap; align-items: center; gap: 5px 7px; - flex: 1; + flex-wrap: wrap; + h3 { line-height: 21px; margin-bottom: 0px; } + > div { flex: 1; } diff --git a/web/src/pages/Courts/TopSearch.tsx b/web/src/pages/Courts/CourtDetails/TopSearch.tsx similarity index 63% rename from web/src/pages/Courts/TopSearch.tsx rename to web/src/pages/Courts/CourtDetails/TopSearch.tsx index ac92d23ea..1ed9050cd 100644 --- a/web/src/pages/Courts/TopSearch.tsx +++ b/web/src/pages/Courts/CourtDetails/TopSearch.tsx @@ -1,7 +1,7 @@ import React, { useState, useMemo } from "react"; import styled, { css } from "styled-components"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { Card, DropdownCascader, Searchbar } from "@kleros/ui-components-library"; @@ -12,9 +12,10 @@ import { useCourtTree, rootCourtToItems } from "queries/useCourtTree"; import { responsiveSize } from "styles/responsiveSize"; import { landscapeStyle } from "styles/landscapeStyle"; +import { hoverShortTransitionTiming } from "styles/commonStyles"; import { StyledSkeleton } from "components/StyledSkeleton"; -import StakeMaintenanceButtons from "./StakeMaintenanceButton"; +import StakeMaintenanceButtons from "../StakeMaintenanceButton"; const Container = styled.div` width: 100%; @@ -57,6 +58,7 @@ const SearchResultsContainer = styled.div` position: absolute; margin-top: 45px; max-height: 400px; + border: 1px solid ${({ theme }) => theme.stroke}; width: 100%; flex-direction: column; border-radius: 4px; @@ -65,29 +67,54 @@ const SearchResultsContainer = styled.div` background-color: ${({ theme }) => theme.whiteBackground}; `; -const StyledCard = styled(Card)` +const StyledCard = styled(Card)<{ selected: boolean }>` + ${hoverShortTransitionTiming} height: auto; width: 100%; - padding: 16px; - color: ${({ theme }) => theme.primaryText}; + padding: ${({ selected }) => (selected ? "16px 13px" : "16px")}; cursor: pointer; + border: none; + border-left: ${({ selected, theme }) => (selected ? `3px solid ${theme.primaryBlue}` : "none")}; + background-color: ${({ selected, theme }) => (selected ? theme.mediumBlue : "transparent")}; + + :hover { + background-color: ${({ theme }) => theme.mediumBlue}; + } `; -function flattenCourts(court) { - return court ? [court, ...(court.children || []).flatMap(flattenCourts)] : []; +const CourtParentSpan = styled.span` + color: ${({ theme }) => theme.secondaryText}EE; +`; + +const CourtNameSpan = styled.span` + color: ${({ theme }) => theme.primaryText}; +`; + +function flattenCourts(court, parent = null) { + const current = { + ...court, + parentName: parent?.name ?? null, + }; + const children = (court.children || []).flatMap((child) => flattenCourts(child, current)); + return [current, ...children]; } const TopSearch: React.FC = () => { const { data } = useCourtTree(); const navigate = useNavigate(); + const { id: currentCourtId } = useParams(); const items = useMemo(() => !isUndefined(data) && [rootCourtToItems(data.court)], [data]); const isUniversity = isKlerosUniversity(); const [search, setSearch] = useState(""); - const filteredCourts = useMemo( - () => - data?.court ? flattenCourts(data.court).filter((c) => c.name.toLowerCase().includes(search.toLowerCase())) : [], - [data, search] - ); + + const filteredCourts = useMemo(() => { + if (!data?.court) return []; + const courts = flattenCourts(data.court).filter((c) => c.name.toLowerCase().includes(search.toLowerCase())); + const selectedCourt = courts.find((c) => c.id === currentCourtId); + if (!selectedCourt) return courts; + + return [selectedCourt, ...courts.filter((c) => c.id !== currentCourtId)]; + }, [data, search, currentCourtId]); return ( @@ -110,14 +137,15 @@ const TopSearch: React.FC = () => { {filteredCourts.map((court) => ( { navigate(`/courts/${court.id}`); setSearch(""); }} > - {court.name} + {court.parentName && {court.parentName} / } + {court.name} ))} diff --git a/web/src/pages/Courts/CourtDetails/index.tsx b/web/src/pages/Courts/CourtDetails/index.tsx index fd3f3d610..a72802353 100644 --- a/web/src/pages/Courts/CourtDetails/index.tsx +++ b/web/src/pages/Courts/CourtDetails/index.tsx @@ -25,6 +25,7 @@ import { Divider } from "components/Divider"; import Description from "./Description"; import StakePanel from "./StakePanel"; import Stats from "./Stats"; +import TopSearch from "./TopSearch"; const Container = styled.div``; @@ -113,6 +114,7 @@ const CourtDetails: React.FC = () => { return ( + diff --git a/web/src/pages/Courts/index.tsx b/web/src/pages/Courts/index.tsx index fb0419bcc..1efa1abee 100644 --- a/web/src/pages/Courts/index.tsx +++ b/web/src/pages/Courts/index.tsx @@ -7,7 +7,6 @@ import { responsiveSize } from "styles/responsiveSize"; import { Routes, Route, Navigate } from "react-router-dom"; import CourtDetails from "./CourtDetails"; -import TopSearch from "./TopSearch"; const Container = styled.div` width: 100%; @@ -26,7 +25,6 @@ const Container = styled.div` const Courts: React.FC = () => { return ( - } /> } /> From 605b08288ee1b91cfdf39bf21c78f20e53c4af2b Mon Sep 17 00:00:00 2001 From: alcercu <333aleix333@gmail.com> Date: Wed, 8 Jan 2025 08:53:03 +0100 Subject: [PATCH 4/4] refactor(web): add overlayscrollbar to courts search --- web/src/pages/Courts/CourtDetails/TopSearch.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/web/src/pages/Courts/CourtDetails/TopSearch.tsx b/web/src/pages/Courts/CourtDetails/TopSearch.tsx index 1ed9050cd..fed78ae7e 100644 --- a/web/src/pages/Courts/CourtDetails/TopSearch.tsx +++ b/web/src/pages/Courts/CourtDetails/TopSearch.tsx @@ -1,20 +1,23 @@ import React, { useState, useMemo } from "react"; import styled, { css } from "styled-components"; +import { OverlayScrollbarsComponent } from "overlayscrollbars-react"; import { useNavigate, useParams } from "react-router-dom"; import { Card, DropdownCascader, Searchbar } from "@kleros/ui-components-library"; -import { isKlerosUniversity } from "src/consts"; import { isUndefined } from "utils/index"; import { useCourtTree, rootCourtToItems } from "queries/useCourtTree"; -import { responsiveSize } from "styles/responsiveSize"; -import { landscapeStyle } from "styles/landscapeStyle"; +import { isKlerosUniversity } from "src/consts"; + import { hoverShortTransitionTiming } from "styles/commonStyles"; +import { landscapeStyle } from "styles/landscapeStyle"; +import { responsiveSize } from "styles/responsiveSize"; import { StyledSkeleton } from "components/StyledSkeleton"; + import StakeMaintenanceButtons from "../StakeMaintenanceButton"; const Container = styled.div` @@ -54,7 +57,7 @@ const StyledSearchbar = styled(Searchbar)` } `; -const SearchResultsContainer = styled.div` +const SearchResultsContainer = styled(OverlayScrollbarsComponent)` position: absolute; margin-top: 45px; max-height: 400px;