Skip to content

Commit 47aaca4

Browse files
committed
feat: new overview design
1 parent 4adedb5 commit 47aaca4

File tree

6 files changed

+152
-144
lines changed

6 files changed

+152
-144
lines changed
Lines changed: 11 additions & 0 deletions
Loading

web/src/components/DisputePreview/DisputeContext.tsx

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, { useMemo } from "react";
22
import styled from "styled-components";
33

44
import { DisputeDetails } from "@kleros/kleros-sdk/src/dataMappings/utils/disputeDetailsTypes";
@@ -9,21 +9,30 @@ import { isUndefined } from "utils/index";
99

1010
import { responsiveSize } from "styles/responsiveSize";
1111

12+
import { DisputeDetailsQuery, VotingHistoryQuery } from "src/graphql/graphql";
13+
1214
import ReactMarkdown from "components/ReactMarkdown";
1315
import { StyledSkeleton } from "components/StyledSkeleton";
1416

1517
import { Divider } from "../Divider";
1618
import { ExternalLink } from "../ExternalLink";
1719

1820
import AliasDisplay from "./Alias";
21+
import RulingAndRewardsIndicators from "../Verdict/RulingAndRewardsIndicators";
1922

2023
const StyledH1 = styled.h1`
2124
margin: 0;
2225
word-wrap: break-word;
23-
font-size: ${responsiveSize(18, 24)};
26+
font-size: ${responsiveSize(20, 26)};
2427
line-height: 24px;
2528
`;
2629

30+
const TitleSection = styled.div`
31+
display: flex;
32+
flex-direction: column;
33+
gap: 12px;
34+
`;
35+
2736
const ReactMarkdownWrapper = styled.div`
2837
& p:first-of-type {
2938
margin: 0;
@@ -68,17 +77,36 @@ const AliasesContainer = styled.div`
6877

6978
interface IDisputeContext {
7079
disputeDetails?: DisputeDetails;
80+
dispute: DisputeDetailsQuery | undefined;
7181
isRpcError?: boolean;
82+
votingHistory: VotingHistoryQuery | undefined;
7283
}
7384

74-
export const DisputeContext: React.FC<IDisputeContext> = ({ disputeDetails, isRpcError = false }) => {
85+
export const DisputeContext: React.FC<IDisputeContext> = ({
86+
disputeDetails,
87+
dispute,
88+
isRpcError = false,
89+
votingHistory,
90+
}) => {
7591
const errMsg = isRpcError ? RPC_ERROR : INVALID_DISPUTE_DATA_ERROR;
92+
const rounds = votingHistory?.dispute?.rounds;
93+
const jurorRewardsDispersed = useMemo(() => Boolean(rounds?.every((round) => round.jurorRewardsDispersed)), [rounds]);
94+
console.log({ jurorRewardsDispersed }, disputeDetails);
7695

7796
return (
7897
<>
79-
<StyledH1 dir="auto">
80-
{isUndefined(disputeDetails) ? <StyledSkeleton /> : (disputeDetails?.title ?? errMsg)}
81-
</StyledH1>
98+
<TitleSection>
99+
<StyledH1 dir="auto">
100+
{isUndefined(disputeDetails) ? <StyledSkeleton /> : (disputeDetails?.title ?? errMsg)}
101+
</StyledH1>
102+
{!isUndefined(Boolean(dispute?.dispute?.ruled)) || jurorRewardsDispersed ? (
103+
<RulingAndRewardsIndicators
104+
ruled={Boolean(dispute?.dispute?.ruled)}
105+
jurorRewardsDispersed={jurorRewardsDispersed}
106+
/>
107+
) : null}
108+
<Divider />
109+
</TitleSection>
82110
{disputeDetails?.question?.trim() || disputeDetails?.description?.trim() ? (
83111
<div>
84112
{disputeDetails?.question?.trim() ? (
Lines changed: 85 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import React, { useMemo } from "react";
22
import styled, { useTheme } from "styled-components";
33

4-
import Skeleton from "react-loading-skeleton";
54
import { useParams } from "react-router-dom";
65

76
import { _TimelineItem1, CustomTimeline } from "@kleros/ui-components-library";
87

9-
import CalendarIcon from "svgs/icons/calendar.svg";
108
import ClosedCaseIcon from "svgs/icons/check-circle-outline.svg";
119
import NewTabIcon from "svgs/icons/new-tab.svg";
10+
import GavelExecutedIcon from "svgs/icons/gavel-executed.svg";
1211

1312
import { Periods } from "consts/periods";
1413
import { usePopulatedDisputeData } from "hooks/queries/usePopulatedDisputeData";
@@ -21,8 +20,6 @@ import { useVotingHistory } from "queries/useVotingHistory";
2120
import { ClassicRound } from "src/graphql/graphql";
2221
import { getTxnExplorerLink } from "src/utils";
2322

24-
import { responsiveSize } from "styles/responsiveSize";
25-
2623
import { StyledClosedCircle } from "components/StyledIcons/ClosedCircleIcon";
2724

2825
import { ExternalLink } from "../ExternalLink";
@@ -37,24 +34,6 @@ const StyledTimeline = styled(CustomTimeline)`
3734
width: 100%;
3835
`;
3936

40-
const EnforcementContainer = styled.div`
41-
display: flex;
42-
gap: 8px;
43-
margin-top: ${responsiveSize(12, 24)};
44-
fill: ${({ theme }) => theme.secondaryText};
45-
46-
small {
47-
font-weight: 400;
48-
line-height: 19px;
49-
color: ${({ theme }) => theme.secondaryText};
50-
}
51-
`;
52-
53-
const StyledCalendarIcon = styled(CalendarIcon)`
54-
width: 14px;
55-
height: 14px;
56-
`;
57-
5837
const StyledNewTabIcon = styled(NewTabIcon)`
5938
margin-bottom: 2px;
6039
path {
@@ -84,73 +63,95 @@ const useItems = (disputeDetails?: DisputeDetailsQuery, arbitrable?: `0x${string
8463
const localRounds: ClassicRound[] = getLocalRounds(votingHistory?.dispute?.disputeKitDispute) as ClassicRound[];
8564
const rounds = votingHistory?.dispute?.rounds;
8665
const theme = useTheme();
87-
const txnExplorerLink = useMemo(() => {
66+
const txnDisputeCreatedLink = useMemo(() => {
8867
return getTxnExplorerLink(votingHistory?.dispute?.transactionHash ?? "");
8968
}, [votingHistory]);
69+
const txnEnforcementLink = useMemo(() => {
70+
return getTxnExplorerLink(disputeDetails?.dispute?.rulingTransactionHash ?? "");
71+
}, [disputeDetails]);
9072

9173
return useMemo<TimelineItems | undefined>(() => {
9274
const dispute = disputeDetails?.dispute;
93-
if (dispute) {
94-
const rulingOverride = dispute.overridden;
95-
const currentPeriodIndex = Periods[dispute.period];
96-
97-
return localRounds?.reduce<TimelineItems>(
98-
(acc, { winningChoice }, index) => {
99-
const isOngoing = index === localRounds.length - 1 && currentPeriodIndex < 3;
100-
const roundTimeline = rounds?.[index].timeline;
101-
102-
const icon = dispute.ruled && !rulingOverride && index === localRounds.length - 1 ? ClosedCaseIcon : "";
103-
const answers = disputeData?.answers;
104-
acc.push({
105-
title: `Jury Decision - Round ${index + 1}`,
106-
party: isOngoing ? "Voting is ongoing" : getVoteChoice(winningChoice, answers),
107-
subtitle: isOngoing
108-
? ""
109-
: `${formatDate(roundTimeline?.[Periods.vote])} / ${
110-
votingHistory?.dispute?.rounds.at(index)?.court.name
111-
}`,
112-
rightSided: true,
113-
variant: theme.secondaryPurple,
114-
Icon: icon !== "" ? icon : undefined,
115-
});
116-
117-
if (index < localRounds.length - 1) {
118-
acc.push({
119-
title: "Appealed",
120-
party: "",
121-
subtitle: formatDate(roundTimeline?.[Periods.appeal]),
122-
rightSided: true,
123-
Icon: StyledClosedCircle,
124-
});
125-
} else if (rulingOverride && dispute.currentRuling !== winningChoice) {
126-
acc.push({
127-
title: "Won by Appeal",
128-
party: getVoteChoice(dispute.currentRuling, answers),
129-
subtitle: formatDate(roundTimeline?.[Periods.appeal]),
130-
rightSided: true,
131-
Icon: ClosedCaseIcon,
132-
});
133-
}
134-
135-
return acc;
136-
},
137-
[
138-
{
139-
title: "Dispute created",
140-
party: (
141-
<ExternalLink to={txnExplorerLink} rel="noopener noreferrer" target="_blank">
142-
<StyledNewTabIcon />
143-
</ExternalLink>
144-
),
145-
subtitle: formatDate(votingHistory?.dispute?.createdAt),
146-
rightSided: true,
147-
variant: theme.secondaryPurple,
148-
},
149-
]
150-
);
75+
if (!dispute) return;
76+
77+
const rulingOverride = dispute.overridden;
78+
const currentPeriodIndex = Periods[dispute.period];
79+
80+
const base: TimelineItems = [
81+
{
82+
title: "Dispute created",
83+
party: (
84+
<ExternalLink to={txnDisputeCreatedLink} rel="noopener noreferrer" target="_blank">
85+
<StyledNewTabIcon />
86+
</ExternalLink>
87+
),
88+
subtitle: formatDate(votingHistory?.dispute?.createdAt),
89+
rightSided: true,
90+
variant: theme.secondaryPurple,
91+
},
92+
];
93+
94+
const items = localRounds?.reduce<_TimelineItem1[]>((acc, { winningChoice }, index) => {
95+
const isOngoing = index === localRounds.length - 1 && currentPeriodIndex < 3;
96+
const roundTimeline = rounds?.[index].timeline;
97+
const icon = dispute.ruled && !rulingOverride && index === localRounds.length - 1 ? ClosedCaseIcon : undefined;
98+
const answers = disputeData?.answers;
99+
100+
acc.push({
101+
title: `Jury Decision - Round ${index + 1}`,
102+
party: isOngoing ? "Voting is ongoing" : getVoteChoice(winningChoice, answers),
103+
subtitle: isOngoing ? "" : `${formatDate(roundTimeline?.[Periods.vote])} / ${rounds?.[index]?.court.name}`,
104+
rightSided: true,
105+
variant: theme.secondaryPurple,
106+
Icon: icon,
107+
});
108+
109+
if (index < localRounds.length - 1) {
110+
acc.push({
111+
title: "Appealed",
112+
party: "",
113+
subtitle: formatDate(roundTimeline?.[Periods.appeal]),
114+
rightSided: true,
115+
Icon: StyledClosedCircle,
116+
});
117+
} else if (rulingOverride && dispute.currentRuling !== winningChoice) {
118+
acc.push({
119+
title: "Won by Appeal",
120+
party: getVoteChoice(dispute.currentRuling, answers),
121+
subtitle: formatDate(roundTimeline?.[Periods.appeal]),
122+
rightSided: true,
123+
Icon: ClosedCaseIcon,
124+
});
125+
}
126+
127+
return acc;
128+
}, []);
129+
130+
if (dispute.ruled) {
131+
items.push({
132+
title: "Enforcement",
133+
party: (
134+
<ExternalLink to={txnEnforcementLink} rel="noopener noreferrer" target="_blank">
135+
<StyledNewTabIcon />
136+
</ExternalLink>
137+
),
138+
subtitle: `${formatDate(dispute.rulingTimestamp)} / ${rounds?.at(-1)?.court.name}`,
139+
rightSided: true,
140+
Icon: GavelExecutedIcon,
141+
});
151142
}
152-
return;
153-
}, [disputeDetails, disputeData, localRounds, theme, rounds, votingHistory, txnExplorerLink]);
143+
144+
return [...base, ...items] as TimelineItems;
145+
}, [
146+
disputeDetails,
147+
disputeData,
148+
localRounds,
149+
theme,
150+
rounds,
151+
votingHistory,
152+
txnDisputeCreatedLink,
153+
txnEnforcementLink,
154+
]);
154155
};
155156

156157
interface IDisputeTimeline {
@@ -160,33 +161,8 @@ interface IDisputeTimeline {
160161
const DisputeTimeline: React.FC<IDisputeTimeline> = ({ arbitrable }) => {
161162
const { id } = useParams();
162163
const { data: disputeDetails } = useDisputeDetailsQuery(id);
163-
const { data: votingHistory } = useVotingHistory(id);
164164
const items = useItems(disputeDetails, arbitrable);
165165

166-
const transactionExplorerLink = useMemo(() => {
167-
return getTxnExplorerLink(disputeDetails?.dispute?.rulingTransactionHash ?? "");
168-
}, [disputeDetails]);
169-
170-
return (
171-
<Container>
172-
{items && <StyledTimeline {...{ items }} />}
173-
{disputeDetails?.dispute?.ruled && (
174-
<EnforcementContainer>
175-
<StyledCalendarIcon />
176-
<small>
177-
Enforcement:{" "}
178-
{disputeDetails.dispute.rulingTimestamp ? (
179-
<ExternalLink to={transactionExplorerLink} rel="noopener noreferrer" target="_blank">
180-
{formatDate(disputeDetails.dispute.rulingTimestamp)}
181-
</ExternalLink>
182-
) : (
183-
<Skeleton height={16} width={56} />
184-
)}{" "}
185-
/ {votingHistory?.dispute?.rounds.at(-1)?.court.name}
186-
</small>
187-
</EnforcementContainer>
188-
)}
189-
</Container>
190-
);
166+
return <Container>{items && <StyledTimeline {...{ items }} />}</Container>;
191167
};
192168
export default DisputeTimeline;

0 commit comments

Comments
 (0)