From f65c634f459d68582de8a3e474c097ac9bbbbe77 Mon Sep 17 00:00:00 2001 From: evilpeach Date: Tue, 19 Mar 2024 17:24:10 +0700 Subject: [PATCH] feat: uptime & penalty section --- .../components/performance/PenaltyEvent.tsx | 27 ++++++ .../components/performance/PenaltySection.tsx | 22 +++-- .../performance/PenaltyStatusSection.tsx | 46 --------- .../performance/RecentBlocksLegends.tsx | 32 ++++--- .../components/performance/UptimeSection.tsx | 96 ++++++++++++++++--- .../components/performance/index.tsx | 57 +++++++---- .../components/validator-overview/index.tsx | 12 ++- src/lib/pages/validator-details/index.tsx | 2 +- src/lib/pages/validator-details/types.ts | 6 -- src/lib/providers/query-client.tsx | 3 +- src/lib/services/validator.ts | 6 +- src/lib/services/validatorService.ts | 2 +- src/lib/types/validator.ts | 11 +++ 13 files changed, 216 insertions(+), 106 deletions(-) create mode 100644 src/lib/pages/validator-details/components/performance/PenaltyEvent.tsx delete mode 100644 src/lib/pages/validator-details/components/performance/PenaltyStatusSection.tsx diff --git a/src/lib/pages/validator-details/components/performance/PenaltyEvent.tsx b/src/lib/pages/validator-details/components/performance/PenaltyEvent.tsx new file mode 100644 index 000000000..2547d14ad --- /dev/null +++ b/src/lib/pages/validator-details/components/performance/PenaltyEvent.tsx @@ -0,0 +1,27 @@ +import { Flex, Text } from "@chakra-ui/react"; + +import { ExplorerLink } from "lib/components/ExplorerLink"; +import { CustomIcon } from "lib/components/icon"; +import type { ValidatorUptimeResponse } from "lib/services/validator"; + +interface PenaltyEventProps { + event: ValidatorUptimeResponse["events"][0]; +} +export const PenaltyEvent = ({ event }: PenaltyEventProps) => ( + + + + + {event.isJailed ? "Jailed" : "Slashed"} at block height + + + + +); diff --git a/src/lib/pages/validator-details/components/performance/PenaltySection.tsx b/src/lib/pages/validator-details/components/performance/PenaltySection.tsx index 797d556f3..766487309 100644 --- a/src/lib/pages/validator-details/components/performance/PenaltySection.tsx +++ b/src/lib/pages/validator-details/components/performance/PenaltySection.tsx @@ -1,10 +1,14 @@ import { Flex, Heading, Text } from "@chakra-ui/react"; -import { PenaltyStatus } from "../../types"; +import type { ValidatorUptimeResponse } from "lib/services/validator"; -import { PenaltyStatusSection } from "./PenaltyStatusSection"; +import { PenaltyEvent } from "./PenaltyEvent"; -export const PenaltySection = () => ( +interface PenaltySectionProps { + penaltyEvents: ValidatorUptimeResponse["events"]; +} + +export const PenaltySection = ({ penaltyEvents }: PenaltySectionProps) => ( ( - - - + {penaltyEvents.length === 0 ? ( + + This validator never had any slash or jailed history within 90 days. + + ) : ( + penaltyEvents.map((event) => ( + + )) + )} ); diff --git a/src/lib/pages/validator-details/components/performance/PenaltyStatusSection.tsx b/src/lib/pages/validator-details/components/performance/PenaltyStatusSection.tsx deleted file mode 100644 index c31973c11..000000000 --- a/src/lib/pages/validator-details/components/performance/PenaltyStatusSection.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Flex, Text } from "@chakra-ui/react"; - -import { PenaltyStatus } from "../../types"; -import { ExplorerLink } from "lib/components/ExplorerLink"; -import { CustomIcon } from "lib/components/icon"; - -// TODO add block height in interface -interface PenaltyStatusSectionProps { - validatorName?: string; - status: PenaltyStatus; - hasBorder?: boolean; -} -export const PenaltyStatusSection = ({ - validatorName = "This validator", - status, - hasBorder = false, -}: PenaltyStatusSectionProps) => { - return ( - - {status === PenaltyStatus.Never ? ( - - {validatorName} never had any slash or jailed history within 90 days. - - ) : ( - - - - {status} at block height - - - - )} - - ); -}; diff --git a/src/lib/pages/validator-details/components/performance/RecentBlocksLegends.tsx b/src/lib/pages/validator-details/components/performance/RecentBlocksLegends.tsx index 652071482..eea90085c 100644 --- a/src/lib/pages/validator-details/components/performance/RecentBlocksLegends.tsx +++ b/src/lib/pages/validator-details/components/performance/RecentBlocksLegends.tsx @@ -1,15 +1,18 @@ import { Flex, Text } from "@chakra-ui/react"; +import type { ComputedUptime, Ratio } from "lib/types"; +import { formatRatio } from "lib/utils"; + const LegendItem = ({ label, color, value, - percent, + ratio, }: { label: string; color: string; - value: string; - percent: string; + value: number; + ratio: Ratio; }) => ( @@ -34,31 +37,38 @@ const LegendItem = ({ {value} - {percent} + {formatRatio(ratio)} ); -export const RecentBlocksLegends = () => { + +interface RecentBlocksLegendsProps { + uptime: ComputedUptime; +} + +export const RecentBlocksLegends = ({ + uptime: { signed, proposed, missed, signedRatio, proposedRatio, missedRatio }, +}: RecentBlocksLegendsProps) => { return ( ); diff --git a/src/lib/pages/validator-details/components/performance/UptimeSection.tsx b/src/lib/pages/validator-details/components/performance/UptimeSection.tsx index 469076cf4..022766ef8 100644 --- a/src/lib/pages/validator-details/components/performance/UptimeSection.tsx +++ b/src/lib/pages/validator-details/components/performance/UptimeSection.tsx @@ -1,24 +1,71 @@ -import { Button, Flex, Heading, Text } from "@chakra-ui/react"; +import { + Button, + Divider, + Flex, + Heading, + Menu, + MenuButton, + MenuItem, + MenuList, + Text, +} from "@chakra-ui/react"; +import { isUndefined } from "lodash"; +import { useMemo } from "react"; -import { PenaltyStatus } from "../../types"; import { useMobile } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; +import { Loading } from "lib/components/Loading"; import { ValueWithIcon } from "lib/components/ValueWithIcon"; +import type { ValidatorUptimeResponse } from "lib/services/validator"; +import type { ComputedUptime, Option, Ratio } from "lib/types"; +import { formatRatio } from "lib/utils"; -import { PenaltyStatusSection } from "./PenaltyStatusSection"; +import { PenaltyEvent } from "./PenaltyEvent"; import { RecentBlocksLegends } from "./RecentBlocksLegends"; import { RecentBlocksSection } from "./RecentBlocksSection"; interface UptimeSectionProps { - isDetailPage?: boolean; + uptimeData: Option; + uptimeBlock: number; + setUptimeBlock?: (block: number) => void; onViewMore?: () => void; } export const UptimeSection = ({ + uptimeData, + uptimeBlock, + setUptimeBlock, onViewMore, - isDetailPage = false, }: UptimeSectionProps) => { const isMobile = useMobile(); + + const computed = useMemo(() => { + const data = uptimeData?.uptime; + if (!data) + return { + signed: 0, + proposed: 0, + missed: 0, + signedRatio: 0 as Ratio, + proposedRatio: 0 as Ratio, + missedRatio: 0 as Ratio, + uptimeRatio: 0 as Ratio, + }; + + return { + signed: data.signedBlocks, + proposed: data.proposedBlocks, + missed: data.missedBlocks, + signedRatio: (data.signedBlocks / data.total) as Ratio, + proposedRatio: (data.proposedBlocks / data.total) as Ratio, + missedRatio: (data.missedBlocks / data.total) as Ratio, + uptimeRatio: ((data.signedBlocks + data.proposedBlocks) / + data.total) as Ratio, + }; + }, [uptimeData?.uptime]); + + if (isUndefined(uptimeData)) return ; + return ( Uptime - - Latest 100 Blocks - + {setUptimeBlock ? ( + + + + Latest {uptimeBlock} Blocks + + + + + setUptimeBlock(100)}> + Latest 100 Blocks + + setUptimeBlock(1000)}> + Latest 1000 Blocks + + setUptimeBlock(10000)}> + Latest 10000 Blocks + + + + ) : ( + + Latest 100 Blocks + + )} {onViewMore && !isMobile && (