{disputeDetails?.question?.trim() ? (
diff --git a/web/src/components/DisputeView/CardLabels/index.tsx b/web/src/components/DisputeView/CardLabels/index.tsx
index 523539ddf..d6ca94a56 100644
--- a/web/src/components/DisputeView/CardLabels/index.tsx
+++ b/web/src/components/DisputeView/CardLabels/index.tsx
@@ -22,11 +22,12 @@ import { ClassicContribution } from "src/graphql/graphql";
import Label, { IColors } from "./Label";
import RewardsAndFundLabel from "./RewardsAndFundLabel";
-const Container = styled.div<{ isList: boolean }>`
+const Container = styled.div<{ isList: boolean; isOverview: boolean }>`
display: flex;
gap: 8px;
flex-direction: column;
align-items: end;
+
${({ isList }) =>
!isList &&
css`
@@ -36,7 +37,16 @@ const Container = styled.div<{ isList: boolean }>`
flex-direction: row;
align-items: center;
`}
+
+ ${({ isOverview }) =>
+ isOverview &&
+ css`
+ margin-top: 0;
+ flex-direction: row;
+ width: auto;
+ `}
`;
+
const RewardsContainer = styled.div`
display: flex;
gap: 4px 8px;
@@ -47,6 +57,7 @@ interface ICardLabels {
disputeId: string;
round: number;
isList: boolean;
+ isOverview?: boolean;
}
const LabelArgs: Record
>; color: IColors }> = {
@@ -73,7 +84,7 @@ const getFundingRewards = (contributions: ClassicContribution[], closed: boolean
return Number(formatUnits(BigInt(contribution), 18));
};
-const CardLabel: React.FC = ({ disputeId, round, isList }) => {
+const CardLabel: React.FC = ({ disputeId, round, isList, isOverview = false }) => {
const { address } = useAccount();
const { data: labelInfo, isLoading } = useLabelInfoQuery(address?.toLowerCase(), disputeId);
const localRounds = getLocalRounds(labelInfo?.dispute?.disputeKitDispute);
@@ -139,7 +150,7 @@ const CardLabel: React.FC = ({ disputeId, round, isList }) => {
}, [contributionRewards, shifts]);
return (
-
+
{isLoading ? (
) : (
diff --git a/web/src/components/DisputeView/DisputeInfo/DisputeInfoCard.tsx b/web/src/components/DisputeView/DisputeInfo/DisputeInfoCard.tsx
index 6bc83242d..0f1818fda 100644
--- a/web/src/components/DisputeView/DisputeInfo/DisputeInfoCard.tsx
+++ b/web/src/components/DisputeView/DisputeInfo/DisputeInfoCard.tsx
@@ -60,7 +60,7 @@ const DisputeInfoCard: React.FC = ({ isOverview, showLabels, f
item.display ? : null
)}
- {showLabels ? : null}
+ {showLabels ? : null}
);
};
diff --git a/web/src/components/Verdict/DisputeTimeline.tsx b/web/src/components/Verdict/DisputeTimeline.tsx
index 2bbc53879..754f1dadf 100644
--- a/web/src/components/Verdict/DisputeTimeline.tsx
+++ b/web/src/components/Verdict/DisputeTimeline.tsx
@@ -1,14 +1,13 @@
import React, { useMemo } from "react";
import styled, { useTheme } from "styled-components";
-import Skeleton from "react-loading-skeleton";
import { useParams } from "react-router-dom";
import { _TimelineItem1, CustomTimeline } from "@kleros/ui-components-library";
-import CalendarIcon from "svgs/icons/calendar.svg";
import ClosedCaseIcon from "svgs/icons/check-circle-outline.svg";
import NewTabIcon from "svgs/icons/new-tab.svg";
+import GavelExecutedIcon from "svgs/icons/gavel-executed.svg";
import { Periods } from "consts/periods";
import { usePopulatedDisputeData } from "hooks/queries/usePopulatedDisputeData";
@@ -21,8 +20,6 @@ import { useVotingHistory } from "queries/useVotingHistory";
import { ClassicRound } from "src/graphql/graphql";
import { getTxnExplorerLink } from "src/utils";
-import { responsiveSize } from "styles/responsiveSize";
-
import { StyledClosedCircle } from "components/StyledIcons/ClosedCircleIcon";
import { ExternalLink } from "../ExternalLink";
@@ -37,24 +34,6 @@ const StyledTimeline = styled(CustomTimeline)`
width: 100%;
`;
-const EnforcementContainer = styled.div`
- display: flex;
- gap: 8px;
- margin-top: ${responsiveSize(12, 24)};
- fill: ${({ theme }) => theme.secondaryText};
-
- small {
- font-weight: 400;
- line-height: 19px;
- color: ${({ theme }) => theme.secondaryText};
- }
-`;
-
-const StyledCalendarIcon = styled(CalendarIcon)`
- width: 14px;
- height: 14px;
-`;
-
const StyledNewTabIcon = styled(NewTabIcon)`
margin-bottom: 2px;
path {
@@ -84,73 +63,95 @@ const useItems = (disputeDetails?: DisputeDetailsQuery, arbitrable?: `0x${string
const localRounds: ClassicRound[] = getLocalRounds(votingHistory?.dispute?.disputeKitDispute) as ClassicRound[];
const rounds = votingHistory?.dispute?.rounds;
const theme = useTheme();
- const txnExplorerLink = useMemo(() => {
+ const txnDisputeCreatedLink = useMemo(() => {
return getTxnExplorerLink(votingHistory?.dispute?.transactionHash ?? "");
}, [votingHistory]);
+ const txnEnforcementLink = useMemo(() => {
+ return getTxnExplorerLink(disputeDetails?.dispute?.rulingTransactionHash ?? "");
+ }, [disputeDetails]);
return useMemo(() => {
const dispute = disputeDetails?.dispute;
- if (dispute) {
- const rulingOverride = dispute.overridden;
- const currentPeriodIndex = Periods[dispute.period];
-
- return localRounds?.reduce(
- (acc, { winningChoice }, index) => {
- const isOngoing = index === localRounds.length - 1 && currentPeriodIndex < 3;
- const roundTimeline = rounds?.[index].timeline;
-
- const icon = dispute.ruled && !rulingOverride && index === localRounds.length - 1 ? ClosedCaseIcon : "";
- const answers = disputeData?.answers;
- acc.push({
- title: `Jury Decision - Round ${index + 1}`,
- party: isOngoing ? "Voting is ongoing" : getVoteChoice(winningChoice, answers),
- subtitle: isOngoing
- ? ""
- : `${formatDate(roundTimeline?.[Periods.vote])} / ${
- votingHistory?.dispute?.rounds.at(index)?.court.name
- }`,
- rightSided: true,
- variant: theme.secondaryPurple,
- Icon: icon !== "" ? icon : undefined,
- });
-
- if (index < localRounds.length - 1) {
- acc.push({
- title: "Appealed",
- party: "",
- subtitle: formatDate(roundTimeline?.[Periods.appeal]),
- rightSided: true,
- Icon: StyledClosedCircle,
- });
- } else if (rulingOverride && dispute.currentRuling !== winningChoice) {
- acc.push({
- title: "Won by Appeal",
- party: getVoteChoice(dispute.currentRuling, answers),
- subtitle: formatDate(roundTimeline?.[Periods.appeal]),
- rightSided: true,
- Icon: ClosedCaseIcon,
- });
- }
-
- return acc;
- },
- [
- {
- title: "Dispute created",
- party: (
-
-
-
- ),
- subtitle: formatDate(votingHistory?.dispute?.createdAt),
- rightSided: true,
- variant: theme.secondaryPurple,
- },
- ]
- );
+ if (!dispute) return;
+
+ const rulingOverride = dispute.overridden;
+ const currentPeriodIndex = Periods[dispute.period];
+
+ const base: TimelineItems = [
+ {
+ title: "Dispute created",
+ party: (
+
+
+
+ ),
+ subtitle: formatDate(votingHistory?.dispute?.createdAt),
+ rightSided: true,
+ variant: theme.secondaryPurple,
+ },
+ ];
+
+ const items = localRounds?.reduce<_TimelineItem1[]>((acc, { winningChoice }, index) => {
+ const isOngoing = index === localRounds.length - 1 && currentPeriodIndex < 3;
+ const roundTimeline = rounds?.[index].timeline;
+ const icon = dispute.ruled && !rulingOverride && index === localRounds.length - 1 ? ClosedCaseIcon : undefined;
+ const answers = disputeData?.answers;
+
+ acc.push({
+ title: `Jury Decision - Round ${index + 1}`,
+ party: isOngoing ? "Voting is ongoing" : getVoteChoice(winningChoice, answers),
+ subtitle: isOngoing ? "" : `${formatDate(roundTimeline?.[Periods.vote])} / ${rounds?.[index]?.court.name}`,
+ rightSided: true,
+ variant: theme.secondaryPurple,
+ Icon: icon,
+ });
+
+ if (index < localRounds.length - 1) {
+ acc.push({
+ title: "Appealed",
+ party: "",
+ subtitle: formatDate(roundTimeline?.[Periods.appeal]),
+ rightSided: true,
+ Icon: StyledClosedCircle,
+ });
+ } else if (rulingOverride && dispute.currentRuling !== winningChoice) {
+ acc.push({
+ title: "Won by Appeal",
+ party: getVoteChoice(dispute.currentRuling, answers),
+ subtitle: formatDate(roundTimeline?.[Periods.appeal]),
+ rightSided: true,
+ Icon: ClosedCaseIcon,
+ });
+ }
+
+ return acc;
+ }, []);
+
+ if (dispute.ruled) {
+ items.push({
+ title: "Enforcement",
+ party: (
+
+
+
+ ),
+ subtitle: `${formatDate(dispute.rulingTimestamp)} / ${rounds?.at(-1)?.court.name}`,
+ rightSided: true,
+ Icon: GavelExecutedIcon,
+ });
}
- return;
- }, [disputeDetails, disputeData, localRounds, theme, rounds, votingHistory, txnExplorerLink]);
+
+ return [...base, ...items] as TimelineItems;
+ }, [
+ disputeDetails,
+ disputeData,
+ localRounds,
+ theme,
+ rounds,
+ votingHistory,
+ txnDisputeCreatedLink,
+ txnEnforcementLink,
+ ]);
};
interface IDisputeTimeline {
@@ -160,33 +161,8 @@ interface IDisputeTimeline {
const DisputeTimeline: React.FC = ({ arbitrable }) => {
const { id } = useParams();
const { data: disputeDetails } = useDisputeDetailsQuery(id);
- const { data: votingHistory } = useVotingHistory(id);
const items = useItems(disputeDetails, arbitrable);
- const transactionExplorerLink = useMemo(() => {
- return getTxnExplorerLink(disputeDetails?.dispute?.rulingTransactionHash ?? "");
- }, [disputeDetails]);
-
- return (
-
- {items && }
- {disputeDetails?.dispute?.ruled && (
-
-
-
- Enforcement:{" "}
- {disputeDetails.dispute.rulingTimestamp ? (
-
- {formatDate(disputeDetails.dispute.rulingTimestamp)}
-
- ) : (
-
- )}{" "}
- / {votingHistory?.dispute?.rounds.at(-1)?.court.name}
-
-
- )}
-
- );
+ return {items && };
};
export default DisputeTimeline;
diff --git a/web/src/components/Verdict/FinalDecision.tsx b/web/src/components/Verdict/FinalDecision.tsx
index d58a216ce..7c1bb4864 100644
--- a/web/src/components/Verdict/FinalDecision.tsx
+++ b/web/src/components/Verdict/FinalDecision.tsx
@@ -12,10 +12,9 @@ import { REFETCH_INTERVAL } from "consts/index";
import { Periods } from "consts/periods";
import { useReadKlerosCoreCurrentRuling } from "hooks/contracts/generated";
import { usePopulatedDisputeData } from "hooks/queries/usePopulatedDisputeData";
-import { useVotingHistory } from "hooks/queries/useVotingHistory";
+import { VotingHistoryQuery } from "hooks/queries/useVotingHistory";
import { useVotingContext } from "hooks/useVotingContext";
import { getLocalRounds } from "utils/getLocalRounds";
-import { isUndefined } from "utils/index";
import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
@@ -25,7 +24,6 @@ import { Divider } from "../Divider";
import { StyledArrowLink } from "../StyledArrowLink";
import AnswerDisplay from "./Answer";
-import RulingAndRewardsIndicators from "./RulingAndRewardsIndicators";
const Container = styled.div`
width: 100%;
@@ -61,11 +59,11 @@ const JuryDecisionTag = styled.small`
`;
const StyledDivider = styled(Divider)`
- margin: 16px 0px;
+ margin: 16px 0 0;
${landscapeStyle(
() => css`
- margin: 24px 0px;
+ margin: 24px 0 0;
`
)}
`;
@@ -81,15 +79,15 @@ const ReStyledArrowLink = styled(StyledArrowLink)`
interface IFinalDecision {
arbitrable?: `0x${string}`;
+ votingHistory: VotingHistoryQuery | undefined;
}
-const FinalDecision: React.FC = ({ arbitrable }) => {
+const FinalDecision: React.FC = ({ arbitrable, votingHistory }) => {
const { id } = useParams();
const { isDisconnected } = useAccount();
const { data: populatedDisputeData } = usePopulatedDisputeData(id, arbitrable);
const { data: disputeDetails } = useDisputeDetailsQuery(id);
const { wasDrawn, hasVoted, isLoading, isCommitPeriod, isVotingPeriod, commited, isHiddenVotes } = useVotingContext();
- const { data: votingHistory } = useVotingHistory(id);
const localRounds = getLocalRounds(votingHistory?.dispute?.disputeKitDispute);
const ruled = disputeDetails?.dispute?.ruled ?? false;
const periodIndex = Periods[disputeDetails?.dispute?.period ?? "evidence"];
@@ -101,25 +99,17 @@ const FinalDecision: React.FC = ({ arbitrable }) => {
const currentRuling = Number(currentRulingArray?.[0] ?? 0);
const answer = populatedDisputeData?.answers?.find((answer) => BigInt(answer.id) === BigInt(currentRuling));
- const rounds = votingHistory?.dispute?.rounds;
- const jurorRewardsDispersed = useMemo(() => Boolean(rounds?.every((round) => round.jurorRewardsDispersed)), [rounds]);
const buttonText = useMemo(() => {
- if (!wasDrawn || isDisconnected) return "Check how the jury voted";
+ if (!wasDrawn || isDisconnected) return "Check votes";
if (isCommitPeriod && !commited) return "Commit your vote";
if (isVotingPeriod && isHiddenVotes && commited && !hasVoted) return "Reveal your vote";
if (isVotingPeriod && !isHiddenVotes && !hasVoted) return "Cast your vote";
- return "Check how the jury voted";
+ return "Check votes";
}, [wasDrawn, hasVoted, isCommitPeriod, isVotingPeriod, commited, isHiddenVotes, isDisconnected]);
return (
- {!isUndefined(Boolean(disputeDetails?.dispute?.ruled)) || jurorRewardsDispersed ? (
-
- ) : null}
{ruled && (
The jury decided in favor of:
@@ -140,15 +130,15 @@ const FinalDecision: React.FC = ({ arbitrable }) => {
)}
)}
+ {isLoading && !isDisconnected ? (
+
+ ) : (
+
+ {buttonText}
+
+ )}
- {isLoading && !isDisconnected ? (
-
- ) : (
-
- {buttonText}
-
- )}
);
};
diff --git a/web/src/components/Verdict/index.tsx b/web/src/components/Verdict/index.tsx
index b3ee84d7e..09c179f81 100644
--- a/web/src/components/Verdict/index.tsx
+++ b/web/src/components/Verdict/index.tsx
@@ -3,6 +3,8 @@ import styled from "styled-components";
import { responsiveSize } from "styles/responsiveSize";
+import { VotingHistoryQuery } from "src/graphql/graphql";
+
import DisputeTimeline from "./DisputeTimeline";
import FinalDecision from "./FinalDecision";
@@ -14,13 +16,14 @@ const Container = styled.div`
interface IVerdict {
arbitrable?: `0x${string}`;
+ votingHistory: VotingHistoryQuery | undefined;
}
-const Verdict: React.FC = ({ arbitrable }) => {
+const Verdict: React.FC = ({ arbitrable, votingHistory }) => {
return (
-
-
+
+
);
};
diff --git a/web/src/pages/Cases/CaseDetails/Overview/index.tsx b/web/src/pages/Cases/CaseDetails/Overview/index.tsx
index 32e9cd0f6..0180342c0 100644
--- a/web/src/pages/Cases/CaseDetails/Overview/index.tsx
+++ b/web/src/pages/Cases/CaseDetails/Overview/index.tsx
@@ -56,10 +56,10 @@ const Overview: React.FC = ({ arbitrable, courtID, currentPeriodIndex
return (
<>
-
+
-
+