diff --git a/web/src/assets/svgs/footer/secured-by-kleros.svg b/web/src/assets/svgs/footer/secured-by-kleros.svg
index eac640c7a..cceaf5c05 100644
--- a/web/src/assets/svgs/footer/secured-by-kleros.svg
+++ b/web/src/assets/svgs/footer/secured-by-kleros.svg
@@ -1,12 +1,10 @@
-
} />
- 0 ? filteredTabs[0].path : ""} replace />
- )
- }
- />
- 0 ? filteredTabs[0].path : ""} replace />} />
-
-
-
+ <>
+ {policy ? (
+
+
+
+
+
+
+
+ 0 ? filteredTabs[0].path : ""} replace />} />
+
+
+
+ ) : null}
+ >
);
};
const formatMarkdown = (markdown?: string) =>
- markdown ? (
- {typeof markdown === "string" ? markdown.replace(/\n/g, " \n") : markdown}
- ) : (
-
- );
+ markdown ? {markdown.replace(/\n/g, " \n")} : ;
export default Description;
diff --git a/web/src/pages/Courts/CourtDetails/StakePanel/InputDisplay.tsx b/web/src/pages/Courts/CourtDetails/StakePanel/InputDisplay.tsx
index 6a28e9685..1ea779a74 100644
--- a/web/src/pages/Courts/CourtDetails/StakePanel/InputDisplay.tsx
+++ b/web/src/pages/Courts/CourtDetails/StakePanel/InputDisplay.tsx
@@ -26,12 +26,6 @@ const StyledField = styled(NumberInputField)`
const LabelArea = styled.div`
display: flex;
justify-content: space-between;
-
- ${landscapeStyle(
- () => css`
- width: 92%;
- `
- )}
`;
const StyledLabel = styled.label`
@@ -45,12 +39,6 @@ const InputArea = styled.div`
align-items: center;
gap: 12px;
width: 100%;
-
- ${landscapeStyle(
- () => css`
- width: 92%;
- `
- )}
`;
const InputFieldAndButton = styled.div`
diff --git a/web/src/pages/Courts/CourtDetails/StakePanel/Simulator/index.tsx b/web/src/pages/Courts/CourtDetails/StakePanel/Simulator/index.tsx
index 134045dbe..8ebc79bcd 100644
--- a/web/src/pages/Courts/CourtDetails/StakePanel/Simulator/index.tsx
+++ b/web/src/pages/Courts/CourtDetails/StakePanel/Simulator/index.tsx
@@ -31,7 +31,6 @@ import { Divider } from "components/Divider";
const Container = styled.div`
display: flex;
flex-direction: column;
- max-width: 480px;
background-color: ${({ theme }) => theme.lightBlue};
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
padding: 20px;
diff --git a/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx b/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx
index 2171058a2..11e6696cd 100644
--- a/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx
+++ b/web/src/pages/Courts/CourtDetails/StakePanel/index.tsx
@@ -13,15 +13,13 @@ import Simulator from "./Simulator";
const Container = styled.div`
position: relative;
- width: 100%;
display: flex;
flex-direction: column;
gap: 28px;
${landscapeStyle(
() => css`
- flex-direction: row;
- justify-content: space-between;
+ flex-direction: column;
`
)};
`;
@@ -29,12 +27,6 @@ const Container = styled.div`
const LeftArea = styled.div`
display: flex;
flex-direction: column;
-
- ${landscapeStyle(
- () => css`
- width: 50%;
- `
- )};
`;
const TagArea = styled.div`
diff --git a/web/src/pages/Courts/CourtDetails/Stats.tsx b/web/src/pages/Courts/CourtDetails/Stats.tsx
index 15941591f..f84dcce4d 100644
--- a/web/src/pages/Courts/CourtDetails/Stats.tsx
+++ b/web/src/pages/Courts/CourtDetails/Stats.tsx
@@ -1,41 +1,31 @@
-import React, { useMemo, useState } from "react";
-import styled, { css } from "styled-components";
-import { landscapeStyle } from "styles/landscapeStyle";
+import React from "react";
+import styled from "styled-components";
import { responsiveSize } from "styles/responsiveSize";
import { useParams } from "react-router-dom";
-import { Accordion, DropdownSelect } from "@kleros/ui-components-library";
+import { Accordion } from "@kleros/ui-components-library";
import EthereumIcon from "svgs/icons/ethereum.svg";
import BalanceIcon from "svgs/icons/law-balance.svg";
import MinStake from "svgs/icons/min-stake.svg";
-import VotesPerPNKIcon from "svgs/icons/votes-per-pnk.svg";
import PNKIcon from "svgs/icons/pnk.svg";
import PNKRedistributedIcon from "svgs/icons/redistributed-pnk.svg";
import VoteStake from "svgs/icons/vote-stake.svg";
-import PNKUSDIcon from "svgs/icons/pnk-usd.svg";
-import PNKETHIcon from "svgs/icons/pnk-eth.svg";
import ChartIcon from "svgs/icons/chart.svg";
import { CoinIds } from "consts/coingecko";
import { useCoinPrice } from "hooks/useCoinPrice";
import { useCourtDetails, CourtDetailsQuery } from "queries/useCourtDetails";
-import { useHomePageExtraStats } from "queries/useHomePageExtraStats";
import { calculateSubtextRender } from "utils/calculateSubtextRender";
import { formatETH, formatPNK, formatUnitsWei, formatUSD } from "utils/format";
import { isUndefined } from "utils/index";
-import { beautifyStatNumber, unbeautifyStatNumber } from "utils/beautifyStatNumber";
import StatDisplay, { IStatDisplay } from "components/StatDisplay";
import { StyledSkeleton } from "components/StyledSkeleton";
-import WithHelpTooltip from "components/WithHelpTooltip";
-import Info from "./Info";
const StyledAccordion = styled(Accordion)`
- width: 100%;
- margin-top: ${responsiveSize(24, 32)};
> * > button {
justify-content: unset;
background-color: ${({ theme }) => theme.whiteBackground} !important;
@@ -49,7 +39,10 @@ const StyledAccordion = styled(Accordion)`
}
//adds padding to body container
> * > div > div {
- padding: 0;
+ padding: 0 24px;
+ }
+ [class*="accordion-item"] {
+ margin: 0;
}
`;
@@ -61,13 +54,7 @@ const TimeDisplayContainer = styled.div`
`;
const AllTimeContainer = styled(TimeDisplayContainer)`
- padding-top: ${responsiveSize(12, 20)};
-`;
-
-const TimeSelectorContainer = styled(TimeDisplayContainer)`
- padding-top: 12px;
- padding-bottom: 12px;
- flex-wrap: wrap;
+ padding: ${responsiveSize(12, 16)} 0;
`;
const StyledAllTimeText = styled.p`
@@ -83,30 +70,16 @@ const StyledChartIcon = styled(ChartIcon)`
}
`;
-const StyledCard = styled.div`
- width: auto;
- height: fit-content;
- display: grid;
- gap: 32px;
- grid-template-columns: repeat(auto-fit, minmax(156px, 1fr));
- padding-top: ${responsiveSize(28, 32)};
- padding-bottom: ${responsiveSize(20, 0)};
-
- ${landscapeStyle(
- () => css`
- gap: 16px;
- `
- )}
+const AccordionContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
`;
-const StyledDropdownSelect = styled(DropdownSelect)`
- margin-right: 16px;
- small {
- color: ${({ theme }) => theme.primaryText};
- }
- svg {
- fill: ${({ theme }) => theme.primaryText};
- }
+const StyledCard = styled.div`
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20px 0;
`;
interface IStat {
@@ -122,9 +95,9 @@ const stats: IStat[] = [
{
title: "Min Stake",
coinId: 0,
- getText: (data) => formatPNK(data?.minStake),
+ getText: (data) => `${formatPNK(data?.minStake)} PNK`,
getSubtext: (data, coinPrice) => formatUSD(Number(formatUnitsWei(data?.minStake)) * (coinPrice ?? 0)),
- color: "purple",
+ color: "blue",
icon: MinStake,
},
{
@@ -132,13 +105,13 @@ const stats: IStat[] = [
coinId: 0,
getText: (data) => {
const stake = BigInt((data?.minStake * data?.alpha) / 1e4);
- return formatPNK(stake);
+ return `${formatPNK(stake)} PNK`;
},
getSubtext: (data, coinPrice) => {
const stake = BigInt((data?.minStake * data?.alpha) / 1e4);
return formatUSD(Number(formatUnitsWei(stake)) * (coinPrice ?? 0));
},
- color: "purple",
+ color: "blue",
icon: VoteStake,
},
{
@@ -146,156 +119,62 @@ const stats: IStat[] = [
coinId: 1,
getText: (data) => {
const jurorReward = formatUnitsWei(data?.feeForJuror);
- return jurorReward;
+ return `${jurorReward} ETH`;
},
getSubtext: (data, coinPrice) => formatUSD(Number(formatUnitsWei(data?.feeForJuror)) * (coinPrice ?? 0)),
- color: "purple",
+ color: "blue",
icon: EthereumIcon,
},
- {
- title: "Active Jurors",
- getText: (data) => data?.numberStakedJurors,
- color: "purple",
- icon: PNKRedistributedIcon,
- },
{
title: "PNK Staked",
coinId: 0,
- getText: (data) => formatPNK(data?.stake),
+ getText: (data) => `${formatPNK(data?.stake)} PNK`,
getSubtext: (data, coinPrice) => formatUSD(Number(formatUnitsWei(data?.stake)) * (coinPrice ?? 0)),
- color: "purple",
+ color: "green",
icon: PNKIcon,
},
+ {
+ title: "Active Jurors",
+ getText: (data) => data?.numberStakedJurors,
+ color: "green",
+ icon: PNKRedistributedIcon,
+ },
{
title: "Cases",
getText: (data) => data?.numberDisputes,
- color: "orange",
+ color: "green",
icon: BalanceIcon,
},
{
title: "In Progress",
getText: (data) => data?.numberDisputes - data?.numberClosedDisputes,
- color: "orange",
+ color: "green",
icon: BalanceIcon,
},
{
title: "Total ETH paid",
coinId: 1,
- getText: (data) => formatETH(data?.paidETH),
+ getText: (data) => `${formatETH(data?.paidETH)} ETH`,
getSubtext: (data, coinPrice) => formatUSD(Number(formatUnitsWei(data?.paidETH)) * (coinPrice ?? 0)),
- color: "blue",
+ color: "purple",
icon: EthereumIcon,
},
{
title: "PNK redistributed",
coinId: 0,
- getText: (data) => formatPNK(data?.paidPNK),
+ getText: (data) => `${formatPNK(data?.paidPNK)} PNK`,
getSubtext: (data, coinPrice) => formatUSD(Number(formatUnitsWei(data?.paidPNK)) * (coinPrice ?? 0)),
color: "purple",
icon: PNKRedistributedIcon,
},
];
-interface ITimeframedStatData {
- treeExpectedRewardPerPnk: number;
- treeVotesPerPnk: number;
- treeDisputesPerPnk: number;
-}
-
-interface ITimeframedStat {
- title: string | React.ReactNode;
- coinId?: number;
- getText: (data: ITimeframedStatData) => string;
- getSubtext?: (data: CourtDetailsQuery["court"], coinPrice?: number) => string;
- color: IStatDisplay["color"];
- icon: React.FC>;
-}
-
-const timeRanges = [
- { value: 30, text: "Last 30 days" },
- { value: 90, text: "Last 90 days" },
- { value: 180, text: "Last 180 days" },
- { value: "allTime", text: "All Time" },
-];
-
const Stats = () => {
const { id } = useParams();
const { data } = useCourtDetails(id);
- const [selectedRange, setSelectedRange] = useState(timeRanges[0].value);
- const timeframedCourtData = useHomePageExtraStats(selectedRange);
const coinIds = [CoinIds.PNK, CoinIds.ETH];
const { prices: pricesData } = useCoinPrice(coinIds);
- const foundCourt = useMemo(() => {
- return timeframedCourtData?.data?.courts?.find((c) => c.id === id);
- }, [timeframedCourtData, id]);
-
- const handleTimeRangeChange = (value: string | number) => {
- setSelectedRange(value);
- };
-
- const timeframedStats: ITimeframedStat[] = [
- {
- title: (
-
- PNK for 1 Vote
-
- ),
- coinId: 0,
- getText: (data) => beautifyStatNumber(data?.treeVotesPerPnk, true),
- getSubtext: (data, coinPrice) =>
- formatUSD(unbeautifyStatNumber(beautifyStatNumber(data?.treeVotesPerPnk, true)) * (coinPrice ?? 0)),
- color: "orange",
- icon: VotesPerPNKIcon,
- },
- {
- title: (
-
- PNK for 1 USD
-
- ),
- coinId: 0,
- getText: (data) => {
- const treeExpectedRewardPerPnk = data?.treeExpectedRewardPerPnk;
- const ethPriceUSD = pricesData ? pricesData[CoinIds.ETH]?.price : undefined;
- if (!ethPriceUSD || !treeExpectedRewardPerPnk) return "N/A";
- const pnkNeeded = treeExpectedRewardPerPnk * ethPriceUSD;
- return beautifyStatNumber(pnkNeeded, true);
- },
- getSubtext: (data, coinPrice) => {
- const treeExpectedRewardPerPnk = data?.treeExpectedRewardPerPnk;
- const ethPriceUSD = pricesData ? pricesData[CoinIds.ETH]?.price : undefined;
- if (!ethPriceUSD || !treeExpectedRewardPerPnk) return "N/A";
- const pnkNeeded = treeExpectedRewardPerPnk * ethPriceUSD;
- return formatUSD(unbeautifyStatNumber(beautifyStatNumber(pnkNeeded, true)) * (coinPrice ?? 0));
- },
- color: "purple",
- icon: PNKUSDIcon,
- },
- {
- title: (
-
- PNK for 1 ETH
-
- ),
- coinId: 0,
- getText: (data) => {
- const treeExpectedRewardPerPnk = data?.treeExpectedRewardPerPnk;
- if (!treeExpectedRewardPerPnk) return "N/A";
- const pnkNeeded = treeExpectedRewardPerPnk;
- return beautifyStatNumber(pnkNeeded, true);
- },
- getSubtext: (data, coinPrice) => {
- const treeExpectedRewardPerPnk = data?.treeExpectedRewardPerPnk;
- if (!treeExpectedRewardPerPnk) return "N/A";
- const pnkNeeded = treeExpectedRewardPerPnk;
- return formatUSD(unbeautifyStatNumber(beautifyStatNumber(pnkNeeded, true)) * (coinPrice ?? 0));
- },
- color: "blue",
- icon: PNKETHIcon,
- },
- ];
-
return (
{
{
title: "Statistics",
body: (
- <>
-
-
- All time
-
-
- {stats.map(({ title, coinId, getText, getSubtext, color, icon }) => {
- const coinPrice = !isUndefined(pricesData) ? pricesData[coinIds[coinId!]]?.price : undefined;
- return (
- }
- subtext={calculateSubtextRender(data?.court, getSubtext, coinPrice)}
- />
- );
- })}
-
-
-
- ({
- value: range.value,
- text: range.text,
- }))}
- defaultValue={selectedRange}
- callback={handleTimeRangeChange}
- />
-
-
-
- {timeframedStats.map(({ title, coinId, getText, getSubtext, color, icon }) => {
- const coinPrice = !isUndefined(pricesData) ? pricesData[coinIds[coinId!]]?.price : undefined;
- return (
- }
- subtext={calculateSubtextRender(foundCourt, getSubtext, coinPrice)}
- />
- );
- })}
-
- >
+
+
+
+
+ Parameters
+
+
+ {stats.slice(0, 3).map(({ title, coinId, getText, getSubtext, color, icon }) => {
+ const coinPrice = !isUndefined(pricesData) ? pricesData[coinIds[coinId!]]?.price : undefined;
+ return (
+ }
+ subtext={calculateSubtextRender(data?.court, getSubtext, coinPrice)}
+ isSmallDisplay={true}
+ />
+ );
+ })}
+
+
+
+
+
+ Activity
+
+
+ {stats.slice(3, 7).map(({ title, coinId, getText, getSubtext, color, icon }) => {
+ const coinPrice = !isUndefined(pricesData) ? pricesData[coinIds[coinId!]]?.price : undefined;
+ return (
+ }
+ subtext={calculateSubtextRender(data?.court, getSubtext, coinPrice)}
+ isSmallDisplay={true}
+ />
+ );
+ })}
+
+
+
+
+
+ Total Rewards
+
+
+ {stats.slice(7, 9).map(({ title, coinId, getText, getSubtext, color, icon }) => {
+ const coinPrice = !isUndefined(pricesData) ? pricesData[coinIds[coinId!]]?.price : undefined;
+ return (
+ }
+ subtext={calculateSubtextRender(data?.court, getSubtext, coinPrice)}
+ isSmallDisplay={true}
+ />
+ );
+ })}
+
+
+
),
},
]}
diff --git a/web/src/pages/Courts/CourtDetails/index.tsx b/web/src/pages/Courts/CourtDetails/index.tsx
index 2d1ee9777..540cdc663 100644
--- a/web/src/pages/Courts/CourtDetails/index.tsx
+++ b/web/src/pages/Courts/CourtDetails/index.tsx
@@ -20,6 +20,7 @@ import LatestCases from "components/LatestCases";
import Staking from "components/Popup/MiniGuides/Staking";
import ScrollTop from "components/ScrollTop";
import { StyledSkeleton } from "components/StyledSkeleton";
+import { Divider } from "components/Divider";
import Description from "./Description";
import StakePanel from "./StakePanel";
@@ -33,13 +34,7 @@ const CourtHeader = styled.h1`
justify-content: space-between;
gap: 24px;
flex-wrap: wrap;
- margin-bottom: 24px;
-
- ${landscapeStyle(
- () => css`
- margin-bottom: 32px;
- `
- )};
+ margin-bottom: 16px;
`;
const CourtInfo = styled.div`
@@ -64,7 +59,6 @@ const ButtonContainer = styled.div`
const StyledCard = styled(Card)`
padding: ${responsiveSize(16, 32)};
- padding-bottom: 16px;
margin-top: 12px;
width: 100%;
height: auto;
@@ -78,6 +72,24 @@ const StyledBreadcrumb = styled(Breadcrumb)`
}
`;
+const StakePanelAndStats = styled.div`
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ margin-top: 24px;
+ gap: 20px;
+ flex-wrap: wrap;
+
+ ${landscapeStyle(
+ () => css`
+ & > * {
+ flex: 1 1 calc(50% - 10px);
+ max-width: calc(50% - 10px);
+ }
+ `
+ )}
+`;
+
const CourtDetails: React.FC = () => {
const { id } = useParams();
const { data: policy } = useCourtPolicy(id);
@@ -111,8 +123,11 @@ const CourtDetails: React.FC = () => {
/>
-
-
+
+
+
+
+
diff --git a/web/src/pages/Home/CourtOverview/ExtraStats.tsx b/web/src/pages/Home/CourtOverview/ExtraStats.tsx
index c75517ed7..8d1f6253b 100644
--- a/web/src/pages/Home/CourtOverview/ExtraStats.tsx
+++ b/web/src/pages/Home/CourtOverview/ExtraStats.tsx
@@ -17,6 +17,12 @@ const StyledCard = styled.div`
justify-content: center;
`;
+const StyledLabel = styled.label`
+ margin-top: 24px;
+ font-size: 14px;
+ font-weight: 600;
+`;
+
interface IStat {
title: string;
getText: (data) => string;
@@ -44,15 +50,12 @@ const stats: IStat[] = [
const timeRanges = [
{ value: 7, text: "Last 7 days" },
{ value: 30, text: "Last 30 days" },
- { value: 90, text: "Last 90 days" },
- // we can uncomment these as the contract deployment time increases
- // { value: 180, text: "Last 180 days" },
- // { value: 365, text: "Last 365 days" },
+ { value: 180, text: "Last 180 days" },
{ value: "allTime", text: "All Time" },
];
const ExtraStats = () => {
- const [selectedRange, setSelectedRange] = useState(timeRanges[0].value);
+ const [selectedRange, setSelectedRange] = useState(timeRanges[1].value);
const data = useHomePageExtraStats(selectedRange);
const handleTimeRangeChange = (value: string | number) => {
@@ -77,9 +80,13 @@ const ExtraStats = () => {
}
icon={LawBalance}
/>
- {stats.map(({ title, getText, icon }) => (
-
- ))}
+ {data.data?.mostDisputedCourt?.numberDisputes === 0 ? (
+ No activity in this period
+ ) : (
+ stats.map(({ title, getText, icon }) => (
+
+ ))
+ )}
);
};
diff --git a/web/src/pages/Home/CourtOverview/Stats.tsx b/web/src/pages/Home/CourtOverview/Stats.tsx
index d7d7ecedb..b194aaf6d 100644
--- a/web/src/pages/Home/CourtOverview/Stats.tsx
+++ b/web/src/pages/Home/CourtOverview/Stats.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import styled, { css } from "styled-components";
+import styled from "styled-components";
import { Card } from "@kleros/ui-components-library";
@@ -16,7 +16,6 @@ import { calculateSubtextRender } from "utils/calculateSubtextRender";
import { formatETH, formatPNK, formatUnitsWei, formatUSD } from "utils/format";
import { isUndefined } from "utils/index";
-import { landscapeStyle } from "styles/landscapeStyle";
import { responsiveSize } from "styles/responsiveSize";
import StatDisplay, { IStatDisplay } from "components/StatDisplay";
@@ -25,19 +24,10 @@ import { StyledSkeleton } from "components/StyledSkeleton";
const StyledCard = styled(Card)`
width: auto;
height: fit-content;
- gap: 32px;
- padding: ${responsiveSize(16, 30)};
- padding-left: ${responsiveSize(16, 35)};
- padding-bottom: 16px;
+ gap: 32px 0;
+ padding: ${responsiveSize(16, 32)};
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
-
- ${landscapeStyle(
- () => css`
- padding-bottom: 0px;
- gap: 0px;
- `
- )}
`;
const getLastOrZero = (src: HomePageQuery["counters"], stat: HomePageQueryDataPoints) =>
@@ -109,6 +99,7 @@ const Stats = () => {
{...{ title, color, icon }}
text={data ? getText(data["counters"]) : }
subtext={calculateSubtextRender(data ? data["counters"] : undefined, getSubtext, coinPrice)}
+ isSmallDisplay={false}
/>
);
})}
diff --git a/web/src/utils/userLevelCalculation.ts b/web/src/utils/userLevelCalculation.ts
index 734e6917b..a25aa8884 100644
--- a/web/src/utils/userLevelCalculation.ts
+++ b/web/src/utils/userLevelCalculation.ts
@@ -7,11 +7,11 @@ interface ILevelCriteria {
}
const levelCriteria: ILevelCriteria[] = [
- { level: 0, title: "Diogenes", minDisputes: 3, minScore: 0, maxScore: 49 },
- { level: 1, title: "Pythagoras", minDisputes: 0, minScore: 0, maxScore: 70 },
- { level: 2, title: "Socrates", minDisputes: 3, minScore: 71, maxScore: 80 },
- { level: 3, title: "Plato", minDisputes: 7, minScore: 81, maxScore: 90 },
{ level: 4, title: "Aristotle", minDisputes: 10, minScore: 91, maxScore: 100 },
+ { level: 3, title: "Plato", minDisputes: 7, minScore: 81, maxScore: 90 },
+ { level: 2, title: "Socrates", minDisputes: 3, minScore: 71, maxScore: 80 },
+ { level: 1, title: "Pythagoras", minDisputes: 0, minScore: 0, maxScore: 70 },
+ { level: 0, title: "Diogenes", minDisputes: 3, minScore: 0, maxScore: 49 },
];
export const getUserLevelData = (coherenceScore: number, totalResolvedDisputes: number) => {
@@ -21,7 +21,7 @@ export const getUserLevelData = (coherenceScore: number, totalResolvedDisputes:
coherenceScore >= criteria.minScore &&
coherenceScore <= criteria.maxScore
) {
- return levelCriteria.find(({ level }) => level === criteria.level);
+ return criteria;
}
}