Skip to content

Commit 74acb03

Browse files
authored
Merge branch 'dev' into feat/batch-disputes
2 parents d5547dd + 3507ef2 commit 74acb03

File tree

24 files changed

+651
-166
lines changed

24 files changed

+651
-166
lines changed

kleros-sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"ts-node": "^10.9.2",
4141
"typescript": "^5.6.3",
4242
"viem": "^2.24.1",
43-
"vitest": "^1.6.0"
43+
"vitest": "^1.6.1"
4444
},
4545
"dependencies": {
4646
"@reality.eth/reality-eth-lib": "^3.2.60",

web-devtools/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"@yornaath/batshit": "^0.10.0",
6161
"graphql": "^16.9.0",
6262
"graphql-request": "^7.1.2",
63-
"next": "14.2.21",
63+
"next": "14.2.28",
6464
"react": "^18.3.1",
6565
"react-dom": "^18.3.1",
6666
"react-is": "^18.3.1",

web/src/components/AllCasesButton.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from "react";
2+
3+
import styled from "styled-components";
4+
5+
import DocIcon from "svgs/icons/doc.svg";
6+
7+
import { encodeURIFilter } from "utils/uri";
8+
import { getDescriptiveCourtName } from "utils/getDescriptiveCourtName";
9+
10+
import { BlueIconTextButtonContainer } from "./BlueIconTextButtonContainer";
11+
import { InternalLink } from "./InternalLink";
12+
13+
const StyledDocIcon = styled(DocIcon)`
14+
width: 16px;
15+
height: 16px;
16+
margin-right: 8px;
17+
`;
18+
19+
const IconAndTextContainer = styled.div`
20+
display: inline-block;
21+
`;
22+
23+
interface IAllCasesButton {
24+
courtId?: string;
25+
courtName?: string;
26+
}
27+
28+
const AllCasesButton: React.FC<IAllCasesButton> = ({ courtId, courtName }) => {
29+
const filter = courtId ? { court: courtId } : {};
30+
const link = `/cases/display/1/desc/${encodeURIFilter(filter)}`;
31+
const labelText = courtId ? `All Cases in ${getDescriptiveCourtName(courtName)}` : "All Cases";
32+
33+
return (
34+
<InternalLink to={link}>
35+
<BlueIconTextButtonContainer>
36+
<IconAndTextContainer>
37+
<StyledDocIcon />
38+
{labelText}
39+
</IconAndTextContainer>
40+
</BlueIconTextButtonContainer>
41+
</InternalLink>
42+
);
43+
};
44+
45+
export default AllCasesButton;

web/src/components/BlueIconTextButtonContainer.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,28 @@ export const BlueIconTextButtonContainer = styled.div`
55
${hoverShortTransitionTiming}
66
display: flex;
77
align-items: center;
8+
text-align: center;
89
font-size: 14px;
910
font-weight: 400;
1011
gap: 8px;
1112
cursor: pointer;
12-
color: ${({ theme }) => theme.primaryBlue};
1313
1414
svg path {
1515
fill: ${({ theme }) => theme.primaryBlue};
1616
}
1717
18+
label {
19+
margin-top: 1px;
20+
color: ${({ theme }) => theme.primaryBlue};
21+
}
22+
1823
&:hover {
19-
color: ${({ theme }) => theme.secondaryBlue};
2024
svg path {
2125
fill: ${({ theme }) => theme.secondaryBlue};
2226
}
27+
label {
28+
cursor: pointer;
29+
color: ${({ theme }) => theme.secondaryBlue};
30+
}
2331
}
2432
`;

web/src/components/CasesDisplay/index.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from "react";
22
import styled from "styled-components";
33

44
import { useLocation } from "react-router-dom";
5+
import { useAccount } from "wagmi";
56

67
import ArrowIcon from "svgs/icons/arrow.svg";
78

@@ -29,6 +30,12 @@ const StyledLabel = styled.label`
2930
font-size: ${responsiveSize(14, 16)};
3031
`;
3132

33+
const LinksContainer = styled.div`
34+
display: flex;
35+
flex-direction: row;
36+
gap: 16px;
37+
`;
38+
3239
interface ICasesDisplay extends ICasesGrid {
3340
numberDisputes?: number;
3441
numberClosedDisputes?: number;
@@ -48,15 +55,25 @@ const CasesDisplay: React.FC<ICasesDisplay> = ({
4855
totalPages,
4956
}) => {
5057
const location = useLocation();
58+
const { isConnected } = useAccount();
59+
const profileLink = isConnected ? `/profile/1/desc/all` : null;
60+
5161
return (
5262
<div {...{ className }}>
5363
<TitleContainer className="title">
5464
<StyledTitle>{title}</StyledTitle>
55-
{location.pathname.startsWith("/cases/display/1/desc/all") ? (
56-
<StyledArrowLink to={"/resolver"}>
57-
Create a case <ArrowIcon />
58-
</StyledArrowLink>
59-
) : null}
65+
<LinksContainer>
66+
{location.pathname.startsWith("/cases/display") && profileLink ? (
67+
<StyledArrowLink to={profileLink}>
68+
My Cases <ArrowIcon />
69+
</StyledArrowLink>
70+
) : null}
71+
{location.pathname.startsWith("/cases/display") ? (
72+
<StyledArrowLink to={"/resolver"}>
73+
Create a case <ArrowIcon />
74+
</StyledArrowLink>
75+
) : null}
76+
</LinksContainer>
6077
</TitleContainer>
6178
<Search />
6279
<StatsAndFilters totalDisputes={numberDisputes || 0} closedDisputes={numberClosedDisputes || 0} />

web/src/components/ConnectWallet/AccountDisplay.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ const Container = styled.div`
1616
flex-direction: column;
1717
justify-content: space-between;
1818
height: auto;
19-
align-items: flex-start;
2019
gap: 8px;
2120
align-items: center;
2221
background-color: ${({ theme }) => theme.whiteBackground};
@@ -101,10 +100,8 @@ const ChainConnectionContainer = styled.div`
101100

102101
const StyledIdenticon = styled(Identicon)<{ size: `${number}` }>`
103102
align-items: center;
104-
svg {
105-
width: ${({ size }) => size + "px"};
106-
height: ${({ size }) => size + "px"};
107-
}
103+
width: ${({ size }) => size + "px"} !important;
104+
height: ${({ size }) => size + "px"} !important;
108105
`;
109106

110107
const StyledAvatar = styled.img<{ size: `${number}` }>`
@@ -115,12 +112,16 @@ const StyledAvatar = styled.img<{ size: `${number}` }>`
115112
height: ${({ size }) => size + "px"};
116113
`;
117114

115+
const StyledSmallLabel = styled.label`
116+
font-size: 14px !important;
117+
`;
118+
118119
interface IIdenticonOrAvatar {
119120
size?: `${number}`;
120121
address?: `0x${string}`;
121122
}
122123

123-
export const IdenticonOrAvatar: React.FC<IIdenticonOrAvatar> = ({ size = "16", address: propAddress }) => {
124+
export const IdenticonOrAvatar: React.FC<IIdenticonOrAvatar> = ({ size = "20", address: propAddress }) => {
124125
const { address: defaultAddress } = useAccount();
125126
const address = propAddress || defaultAddress;
126127

@@ -142,9 +143,10 @@ export const IdenticonOrAvatar: React.FC<IIdenticonOrAvatar> = ({ size = "16", a
142143

143144
interface IAddressOrName {
144145
address?: `0x${string}`;
146+
smallDisplay?: boolean;
145147
}
146148

147-
export const AddressOrName: React.FC<IAddressOrName> = ({ address: propAddress }) => {
149+
export const AddressOrName: React.FC<IAddressOrName> = ({ address: propAddress, smallDisplay }) => {
148150
const { address: defaultAddress } = useAccount();
149151
const address = propAddress || defaultAddress;
150152

@@ -153,7 +155,9 @@ export const AddressOrName: React.FC<IAddressOrName> = ({ address: propAddress }
153155
chainId: 1,
154156
});
155157

156-
return <label>{data ?? (isAddress(address!) ? shortenAddress(address) : address)}</label>;
158+
const content = data ?? (isAddress(address!) ? shortenAddress(address) : address);
159+
160+
return smallDisplay ? <StyledSmallLabel>{content}</StyledSmallLabel> : <label>{content}</label>;
157161
};
158162

159163
export const ChainDisplay: React.FC = () => {
@@ -166,7 +170,7 @@ const AccountDisplay: React.FC = () => {
166170
return (
167171
<Container>
168172
<AccountContainer>
169-
<IdenticonOrAvatar size="32" />
173+
<IdenticonOrAvatar size="20" />
170174
<AddressOrName />
171175
</AccountContainer>
172176
<ChainConnectionContainer>

web/src/components/HowItWorks.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const HowItWorks: React.FC<IHowItWorks> = ({ isMiniGuideOpen, toggleMiniGuide, M
1515
<>
1616
<BlueIconTextButtonContainer onClick={toggleMiniGuide}>
1717
<BookOpenIcon />
18-
How it works
18+
<label>How it works</label>
1919
</BlueIconTextButtonContainer>
2020
{isMiniGuideOpen && <MiniGuideComponent toggleMiniGuide={toggleMiniGuide} />}
2121
</>

web/src/components/JurorsLeaderboardButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const JurorsLeaderboardButton: React.FC = () => {
1010
<InternalLink to={"/jurors/1/desc/all"}>
1111
<BlueIconTextButtonContainer>
1212
<RankingIcon />
13-
Jurors Leaderboard
13+
<label>Jurors Leaderboard</label>
1414
</BlueIconTextButtonContainer>
1515
</InternalLink>
1616
);

web/src/components/LatestCases.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import DisputeView from "components/DisputeView";
1111
import { SkeletonDisputeCard } from "components/StyledSkeleton";
1212

1313
import { Dispute_Filter } from "../graphql/graphql";
14+
import AllCasesButton from "./AllCasesButton";
1415

1516
const Container = styled.div`
1617
margin-top: ${responsiveSize(28, 48)};
@@ -29,18 +30,34 @@ const DisputeContainer = styled.div`
2930
gap: var(--gap);
3031
`;
3132

32-
const LatestCases: React.FC<{ filters?: Dispute_Filter }> = ({ filters }) => {
33+
const ButtonContainer = styled.div`
34+
display: flex;
35+
margin-top: 16px;
36+
justify-content: center;
37+
`;
38+
39+
interface ILatestCases {
40+
title?: string;
41+
filters?: Dispute_Filter;
42+
courtName?: string;
43+
}
44+
45+
const LatestCases: React.FC<ILatestCases> = ({ title = "Latest Cases", filters, courtName }) => {
3346
const { data } = useCasesQuery(0, 3, filters);
3447
const disputes: DisputeDetailsFragment[] = useMemo(() => data?.disputes as DisputeDetailsFragment[], [data]);
48+
const courtId = typeof filters?.court === "string" ? filters?.court : undefined;
3549

3650
return isUndefined(disputes) || disputes.length > 0 ? (
3751
<Container>
38-
<Title>Latest Cases</Title>
52+
<Title>{title}</Title>
3953
<DisputeContainer>
4054
{isUndefined(disputes)
4155
? Array.from({ length: 3 }).map((_, index) => <SkeletonDisputeCard key={index} />)
4256
: disputes.map((dispute) => <DisputeView key={dispute.id} {...dispute} overrideIsList />)}
4357
</DisputeContainer>
58+
<ButtonContainer>
59+
<AllCasesButton {...{ courtId, courtName }} />
60+
</ButtonContainer>
4461
</Container>
4562
) : null;
4663
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import { useGraphqlBatcher } from "context/GraphqlBatcher";
3+
import { graphql } from "src/graphql";
4+
import { TopStakedJurorsByCourtQuery, OrderDirection } from "src/graphql/graphql";
5+
6+
const topStakedJurorsByCourtQuery = graphql(`
7+
query TopStakedJurorsByCourt(
8+
$courtId: ID!
9+
$skip: Int
10+
$first: Int
11+
$orderBy: JurorTokensPerCourt_orderBy
12+
$orderDirection: OrderDirection
13+
$search: String
14+
) {
15+
jurorTokensPerCourts(
16+
where: { court_: { id: $courtId }, effectiveStake_gt: 0, juror_: { userAddress_contains_nocase: $search } }
17+
skip: $skip
18+
first: $first
19+
orderBy: $orderBy
20+
orderDirection: $orderDirection
21+
) {
22+
court {
23+
id
24+
}
25+
juror {
26+
id
27+
userAddress
28+
}
29+
effectiveStake
30+
}
31+
}
32+
`);
33+
34+
export const useTopStakedJurorsByCourt = (
35+
courtId: string,
36+
skip: number,
37+
first: number,
38+
orderBy: string,
39+
orderDirection: OrderDirection,
40+
search = ""
41+
) => {
42+
const { graphqlBatcher } = useGraphqlBatcher();
43+
return useQuery<TopStakedJurorsByCourtQuery>({
44+
queryKey: ["TopStakedJurorsByCourt", courtId, skip, first, orderBy, orderDirection, search],
45+
staleTime: 10 * 60 * 1000,
46+
queryFn: () =>
47+
graphqlBatcher.fetch({
48+
id: crypto.randomUUID(),
49+
document: topStakedJurorsByCourtQuery,
50+
variables: {
51+
courtId,
52+
skip,
53+
first,
54+
orderBy,
55+
orderDirection,
56+
search: search.toLowerCase(),
57+
},
58+
}),
59+
});
60+
};

0 commit comments

Comments
 (0)