Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add the search bar in courts page #1821

Merged
merged 6 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 3 additions & 13 deletions web/src/components/Verdict/Answer.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -24,12 +15,11 @@ const AnswerDisplay: React.FC<IAnswer> = ({ answer, currentRuling }) => {
{answer ? (
<AnswerTitleAndDescription dir="auto">
<AnswerTitle>{answer.title}</AnswerTitle>
<AnswerDescription>{answer.description.trim() ? ` - ${answer.description}` : null}</AnswerDescription>
</AnswerTitleAndDescription>
) : (
<Container>
<AnswerTitleAndDescription>
{currentRuling !== 0 ? <small>Answer 0x{currentRuling}</small> : <small>Refuse to Arbitrate</small>}
</Container>
</AnswerTitleAndDescription>
)}
</>
);
Expand Down
6 changes: 3 additions & 3 deletions web/src/components/Verdict/FinalDecision.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
163 changes: 163 additions & 0 deletions web/src/pages/Courts/CourtDetails/TopSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import React, { useState, useMemo } from "react";
import styled, { css } from "styled-components";

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 { hoverShortTransitionTiming } from "styles/commonStyles";

import { StyledSkeleton } from "components/StyledSkeleton";
import StakeMaintenanceButtons from "../StakeMaintenanceButton";

const Container = styled.div`
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px 16px;
flex-wrap: wrap;
`;

const StyledDropdownCascader = styled(DropdownCascader)`
width: ${responsiveSize(200, 240)};
> button {
width: 100%;
}
`;

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;
border: 1px solid ${({ theme }) => theme.stroke};
width: 100%;
flex-direction: column;
border-radius: 4px;
overflow-y: auto;
z-index: 1;
background-color: ${({ theme }) => theme.whiteBackground};
`;

const StyledCard = styled(Card)<{ selected: boolean }>`
${hoverShortTransitionTiming}
height: auto;
width: 100%;
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};
}
`;
alcercu marked this conversation as resolved.
Show resolved Hide resolved

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(() => {
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 (
<Container>
{items ? (
<>
<StyledDropdownCascader
items={items}
onSelect={(path) => navigate(path.toString())}
placeholder="Select Court"
/>
<SearchBarContainer>
<StyledSearchbar
dir="auto"
type="text"
placeholder="Search"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
alcercu marked this conversation as resolved.
Show resolved Hide resolved
{search && filteredCourts.length > 0 && (
<SearchResultsContainer>
{filteredCourts.map((court) => (
<StyledCard
key={court.id}
selected={court.id === currentCourtId}
onClick={() => {
navigate(`/courts/${court.id}`);
setSearch("");
}}
>
{court.parentName && <CourtParentSpan>{court.parentName} / </CourtParentSpan>}
<CourtNameSpan>{court.name}</CourtNameSpan>
</StyledCard>
))}
</SearchResultsContainer>
)}
alcercu marked this conversation as resolved.
Show resolved Hide resolved
</SearchBarContainer>
</>
) : (
<StyledSkeleton width={240} height={42} />
)}
{isUniversity ? null : <StakeMaintenanceButtons />}
</Container>
);
};

export default TopSearch;
2 changes: 2 additions & 0 deletions web/src/pages/Courts/CourtDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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``;

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

return (
<Container>
<TopSearch />
<StyledCard>
<CourtHeader>
<CourtInfo>
Expand Down
54 changes: 0 additions & 54 deletions web/src/pages/Courts/TopSearch.tsx

This file was deleted.

2 changes: 0 additions & 2 deletions web/src/pages/Courts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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%;
Expand All @@ -26,7 +25,6 @@ const Container = styled.div`
const Courts: React.FC = () => {
return (
<Container>
<TopSearch />
<Routes>
<Route path="/:id/*" element={<CourtDetails />} />
<Route path="*" element={<Navigate to="1" replace />} />
Expand Down
Loading