setJustification(e.target.value)}
@@ -122,4 +133,4 @@ const Binary: React.FC<{ arbitrable: `0x${string}`; voteIDs: string[] }> = ({ ar
);
};
-export default Binary;
+export default Classic;
diff --git a/web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx b/web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx
index 4504b438c..3fc351993 100644
--- a/web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx
+++ b/web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx
@@ -112,7 +112,9 @@ const VotingHistory: React.FC<{ arbitrable?: `0x${string}` }> = ({ arbitrable })
{localRounds.at(currentTab)?.totalVoted === rounds.at(currentTab)?.nbVotes
? "All jurors voted"
- : localRounds.at(currentTab)?.totalVoted + " jurors voted out of " + rounds.at(currentTab)?.nbVotes}
+ : localRounds.at(currentTab)?.totalVoted.toString() +
+ ` vote${localRounds.at(currentTab)?.totalVoted.toString() === "1" ? "" : "s"} cast out of ` +
+ rounds.at(currentTab)?.nbVotes}
0 &&
- !voted ? (
- draw.voteID)} />
- ) : (
-
+ const [isPopupOpen, setIsPopupOpen] = useState(false);
+ useLockBodyScroll(isPopupOpen);
+ const lastPeriodChange = disputeData?.dispute?.lastPeriodChange;
+ const timesPerPeriod = disputeData?.dispute?.court?.timesPerPeriod;
+ const finalDate =
+ !isUndefined(currentPeriodIndex) &&
+ !isUndefined(timesPerPeriod) &&
+ getPeriodEndTimestamp(lastPeriodChange, currentPeriodIndex, timesPerPeriod);
+
+ return (
+ <>
+ {isPopupOpen && (
+
+ )}
+ {drawData &&
+ !isUndefined(arbitrable) &&
+ currentPeriodIndex === Periods.vote &&
+ drawData.draws?.length > 0 &&
+ !voted ? (
+ draw.voteID)} />
+ ) : (
+
+ )}
+ >
);
};
diff --git a/web/src/pages/Courts/CourtDetails/StakePanel/InputDisplay.tsx b/web/src/pages/Courts/CourtDetails/StakePanel/InputDisplay.tsx
index f969070ec..b43e5aa7e 100644
--- a/web/src/pages/Courts/CourtDetails/StakePanel/InputDisplay.tsx
+++ b/web/src/pages/Courts/CourtDetails/StakePanel/InputDisplay.tsx
@@ -5,10 +5,8 @@ import { formatEther } from "viem";
import { useDebounce } from "react-use";
import { useAccount } from "wagmi";
import { Field } from "@kleros/ui-components-library";
-
import { useParsedAmount } from "hooks/useParsedAmount";
-import { usePNKBalance } from "queries/usePNKBalance";
-import { useKlerosCoreGetJurorBalance } from "hooks/contracts/generated";
+import { useKlerosCoreGetJurorBalance, usePnkBalanceOf } from "hooks/contracts/generated";
import StakeWithdrawButton, { ActionType } from "./StakeWithdrawButton";
import { isUndefined } from "utils/index";
import { EnsureChain } from "components/EnsureChain";
@@ -39,17 +37,30 @@ interface IInputDisplay {
action: ActionType;
isSending: boolean;
setIsSending: (arg0: boolean) => void;
+ setIsPopupOpen: (arg0: boolean) => void;
+ amount: string;
+ setAmount: (arg0: string) => void;
}
-const InputDisplay: React.FC = ({ action, isSending, setIsSending }) => {
- const [amount, setAmount] = useState("");
+const InputDisplay: React.FC = ({
+ action,
+ isSending,
+ setIsSending,
+ setIsPopupOpen,
+ amount,
+ setAmount,
+}) => {
const [debouncedAmount, setDebouncedAmount] = useState("");
useDebounce(() => setDebouncedAmount(amount), 500, [amount]);
const parsedAmount = useParsedAmount(debouncedAmount);
const { id } = useParams();
const { address } = useAccount();
- const { data: balance } = usePNKBalance(address);
+ const { data: balance } = usePnkBalanceOf({
+ enabled: !isUndefined(address),
+ args: [address ?? "0x"],
+ watch: true,
+ });
const parsedBalance = formatEther(balance ?? 0n);
const { data: jurorBalance } = useKlerosCoreGetJurorBalance({
enabled: !isUndefined(address),
@@ -93,6 +104,7 @@ const InputDisplay: React.FC = ({ action, isSending, setIsSending
setAmount,
isSending,
setIsSending,
+ setIsPopupOpen,
}}
/>
diff --git a/web/src/pages/Courts/CourtDetails/StakePanel/JurorStakeDisplay.tsx b/web/src/pages/Courts/CourtDetails/StakePanel/JurorStakeDisplay.tsx
index 6e0ed5686..f047c3b23 100644
--- a/web/src/pages/Courts/CourtDetails/StakePanel/JurorStakeDisplay.tsx
+++ b/web/src/pages/Courts/CourtDetails/StakePanel/JurorStakeDisplay.tsx
@@ -9,7 +9,7 @@ import DiceIcon from "svgs/icons/dice.svg";
import LockerIcon from "svgs/icons/locker.svg";
import PNKIcon from "svgs/icons/pnk.svg";
import { useCourtDetails } from "queries/useCourtDetails";
-import { useJurorBalance } from "queries/useJurorBalance";
+import { useKlerosCoreGetJurorBalance } from "hooks/contracts/generated";
const format = (value: bigint | undefined): string => (value !== undefined ? formatEther(value) : "0");
@@ -32,7 +32,11 @@ const formatBigIntPercentage = (numerator: bigint, denominator: bigint): string
const JurorBalanceDisplay = () => {
const { id } = useParams();
const { address } = useAccount();
- const { data: jurorBalance } = useJurorBalance(address, id);
+ const { data: jurorBalance } = useKlerosCoreGetJurorBalance({
+ enabled: !isUndefined(address),
+ args: [address ?? "0x", BigInt(id ?? 0)],
+ watch: true,
+ });
const { data: courtDetails } = useCourtDetails(id);
const stakedByAllJurors = courtDetails?.court?.stake;
diff --git a/web/src/pages/Courts/CourtDetails/StakePanel/StakeWithdrawButton.tsx b/web/src/pages/Courts/CourtDetails/StakePanel/StakeWithdrawButton.tsx
index 3c94541d8..962c44184 100644
--- a/web/src/pages/Courts/CourtDetails/StakePanel/StakeWithdrawButton.tsx
+++ b/web/src/pages/Courts/CourtDetails/StakePanel/StakeWithdrawButton.tsx
@@ -10,8 +10,8 @@ import {
usePnkIncreaseAllowance,
usePreparePnkIncreaseAllowance,
useKlerosCoreGetJurorBalance,
+ usePnkAllowance,
} from "hooks/contracts/generated";
-import { usePNKAllowance } from "queries/usePNKAllowance";
import { wrapWithToast } from "utils/wrapWithToast";
import { isUndefined } from "utils/index";
import { EnsureChain } from "components/EnsureChain";
@@ -28,11 +28,20 @@ interface IActionButton {
action: ActionType;
setIsSending: (arg0: boolean) => void;
setAmount: (arg0: string) => void;
+ setIsPopupOpen: (arg0: boolean) => void;
}
-const StakeWithdrawButton: React.FC = ({ parsedAmount, action, setAmount, isSending, setIsSending }) => {
+const StakeWithdrawButton: React.FC = ({
+ parsedAmount,
+ action,
+ setAmount,
+ isSending,
+ setIsSending,
+ setIsPopupOpen,
+}) => {
const { id } = useParams();
const { address } = useAccount();
+ const klerosCore = getKlerosCore({});
const { data: balance } = usePnkBalanceOf({
enabled: !isUndefined(address),
args: [address!],
@@ -43,7 +52,11 @@ const StakeWithdrawButton: React.FC = ({ parsedAmount, action, se
args: [address ?? "0x", BigInt(id ?? 0)],
watch: true,
});
- const { data: allowance } = usePNKAllowance(address);
+ const { data: allowance } = usePnkAllowance({
+ enabled: !isUndefined(address),
+ args: [address ?? "0x", klerosCore.address],
+ watch: true,
+ });
const publicClient = usePublicClient();
const isStaking = action === ActionType.stake;
@@ -62,7 +75,6 @@ const StakeWithdrawButton: React.FC = ({ parsedAmount, action, se
return 0n;
}, [jurorBalance, parsedAmount, isAllowance, isStaking]);
- const klerosCore = getKlerosCore({});
const { config: increaseAllowanceConfig } = usePreparePnkIncreaseAllowance({
enabled: isAllowance && !isUndefined(klerosCore) && !isUndefined(targetStake) && !isUndefined(allowance),
args: [klerosCore?.address, BigInt(targetStake ?? 0) - BigInt(allowance ?? 0)],
@@ -87,11 +99,10 @@ const StakeWithdrawButton: React.FC = ({ parsedAmount, action, se
const handleStake = () => {
if (typeof setStake !== "undefined") {
setIsSending(true);
- wrapWithToast(async () => await setStake().then((response) => response.hash), publicClient)
- .then(() => {
- setAmount("");
- })
- .finally(() => setIsSending(false));
+ wrapWithToast(async () => await setStake().then((response) => response.hash), publicClient).finally(() => {
+ setIsSending(false);
+ setIsPopupOpen(true);
+ });
}
};
diff --git a/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx b/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx
index 640d2c146..d814e9e56 100644
--- a/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx
+++ b/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx
@@ -1,18 +1,22 @@
import React, { useState } from "react";
import styled from "styled-components";
-
+import { useLockBodyScroll } from "react-use";
import Tag from "components/Tag";
import JurorBalanceDisplay from "./JurorStakeDisplay";
import InputDisplay from "./InputDisplay";
import { ActionType } from "./StakeWithdrawButton";
+import Popup, { PopupType } from "components/Popup/index";
+import BalanceIcon from "assets/svgs/icons/balance.svg";
-const StakePanel: React.FC<{ courtName: string }> = ({
- courtName = "General Court",
-}) => {
+const StakePanel: React.FC<{ courtName: string; id: string }> = ({ courtName = "General Court", id }) => {
+ const [amount, setAmount] = useState("");
const [isSending, setIsSending] = useState(false);
+ const [isPopupOpen, setIsPopupOpen] = useState(false);
const [isActive, setIsActive] = useState(true);
const [action, setAction] = useState(ActionType.stake);
+ useLockBodyScroll(isPopupOpen);
+
const handleClick = (action: ActionType) => {
setIsActive(action === ActionType.stake);
setAction(action);
@@ -22,25 +26,30 @@ const StakePanel: React.FC<{ courtName: string }> = ({
return (
- handleClick(ActionType.stake)}
- />
- handleClick(ActionType.withdraw)}
- />
+ handleClick(ActionType.stake)} />
+ handleClick(ActionType.withdraw)} />
-
+
+ {isPopupOpen && (
+
+ )}
);
};
@@ -48,6 +57,7 @@ const StakePanel: React.FC<{ courtName: string }> = ({
export default StakePanel;
const Container = styled.div`
+ position: relative;
width: 100%;
margin-top: 32px;
display: flex;
diff --git a/web/src/pages/Courts/CourtDetails/index.tsx b/web/src/pages/Courts/CourtDetails/index.tsx
index a0fb7e591..82abe751d 100644
--- a/web/src/pages/Courts/CourtDetails/index.tsx
+++ b/web/src/pages/Courts/CourtDetails/index.tsx
@@ -5,7 +5,7 @@ import { useParams } from "react-router-dom";
import { Card, Breadcrumb } from "@kleros/ui-components-library";
import { useCourtPolicy } from "queries/useCourtPolicy";
import { useCourtTree, CourtTreeQuery } from "queries/useCourtTree";
-
+import { isUndefined } from "utils/index";
import Stats from "./Stats";
import Description from "./Description";
import StakePanel from "./StakePanel";
@@ -30,7 +30,7 @@ const CourtDetails: React.FC = () => {
-
+
@@ -60,11 +60,7 @@ interface IItem {
id: string;
}
-const getCourtsPath = (
- node: CourtTreeQuery["court"],
- id: string | undefined,
- path: IItem[] = []
-): IItem[] | null => {
+const getCourtsPath = (node: CourtTreeQuery["court"], id: string | undefined, path: IItem[] = []): IItem[] | null => {
if (!node || !id) return null;
if (node.id === id) {
diff --git a/web/src/pages/Dashboard/Courts/CourtCard.tsx b/web/src/pages/Dashboard/Courts/CourtCard.tsx
index 822d898a1..b7d385d57 100644
--- a/web/src/pages/Dashboard/Courts/CourtCard.tsx
+++ b/web/src/pages/Dashboard/Courts/CourtCard.tsx
@@ -33,7 +33,7 @@ const tooltipMsg =
"The locked stake of incoherent jurors is redistributed as incentives for " +
"the coherent jurors.";
-const format = (value: bigint | undefined): string => (value !== undefined ? formatEther(value) : "0");
+export const format = (value: bigint | undefined): string => (value !== undefined ? formatEther(value) : "0");
interface ICourtCard {
id: string;
diff --git a/web/src/pages/Dashboard/JurorInfo/Coherency.tsx b/web/src/pages/Dashboard/JurorInfo/Coherency.tsx
index 6554d9a3a..22b553844 100644
--- a/web/src/pages/Dashboard/JurorInfo/Coherency.tsx
+++ b/web/src/pages/Dashboard/JurorInfo/Coherency.tsx
@@ -18,22 +18,38 @@ const tooltipMsg =
" using the number of times you have been coherent and the total cases you" +
" have been in.";
+const levelTitles = [
+ { scoreRange: [0, 20], level: 0, title: "Diogenes" },
+ { scoreRange: [20, 40], level: 1, title: "Pythagoras" },
+ { scoreRange: [40, 60], level: 2, title: "Socrates" },
+ { scoreRange: [60, 80], level: 3, title: "Plato" },
+ { scoreRange: [80, 100], level: 4, title: "Aristotle" },
+];
+
const Coherency: React.FC = () => {
const { address } = useAccount();
const { data } = useUserQuery(address?.toLowerCase());
const totalCoherent = parseInt(data?.user?.totalCoherent) ?? 0;
const totalResolvedDisputes = parseInt(data?.user?.totalResolvedDisputes) ?? 1;
const coherencyScore = calculateCoherencyScore(totalCoherent, totalResolvedDisputes);
+ const roundedCoherencyScore = Math.round(coherencyScore * 100);
+
+ const { level, title } =
+ levelTitles.find(({ scoreRange }) => {
+ return roundedCoherencyScore >= scoreRange[0] && roundedCoherencyScore < scoreRange[1];
+ }) ?? levelTitles[0];
return (
- Aristotle
-
-
+ {title}
+
+
diff --git a/web/src/pages/Dashboard/JurorInfo/TokenRewards.tsx b/web/src/pages/Dashboard/JurorInfo/TokenRewards.tsx
index 19679c151..4edf67170 100644
--- a/web/src/pages/Dashboard/JurorInfo/TokenRewards.tsx
+++ b/web/src/pages/Dashboard/JurorInfo/TokenRewards.tsx
@@ -1,7 +1,6 @@
import React from "react";
import styled from "styled-components";
-import _ETH from "assets/svgs/styled/eth.svg";
-import _PNK from "assets/svgs/styled/pnk.svg";
+import GradientTokenIcons from "components/GradientTokenIcons";
const RewardContainer = styled.div`
display: flex;
@@ -10,14 +9,6 @@ const RewardContainer = styled.div`
gap: 8px;
`;
-const ETH = styled(_ETH)`
- stroke: ${({ theme }) => theme.secondaryBlue};
-`;
-
-const PNK = styled(_PNK)`
- stroke: ${({ theme }) => theme.secondaryBlue};
-`;
-
const StyledH1 = styled.h1`
margin: 0;
`;
@@ -31,7 +22,7 @@ interface ITokenRewards {
const TokenRewards: React.FC = ({ token, amount, value }) => {
return (
- {token === "ETH" ? : }
+ {token && }
{amount} {token}