From 8acb48418b653e0ffe3937b8294e8f3320562098 Mon Sep 17 00:00:00 2001 From: Saravanakumar J N Date: Fri, 31 Jan 2025 13:14:45 +0530 Subject: [PATCH 1/9] added orchestrator voting list tab with mock data --- components/OrchestratorVotingList/index.tsx | 356 ++++++++++++++++++++ lib/orchestrartor.ts | 4 + pages/index.tsx | 66 +++- pages/orchestrators.tsx | 54 ++- 4 files changed, 460 insertions(+), 20 deletions(-) create mode 100644 components/OrchestratorVotingList/index.tsx create mode 100644 lib/orchestrartor.ts diff --git a/components/OrchestratorVotingList/index.tsx b/components/OrchestratorVotingList/index.tsx new file mode 100644 index 00000000..f341f3d8 --- /dev/null +++ b/components/OrchestratorVotingList/index.tsx @@ -0,0 +1,356 @@ +import Table from "@components/Table"; +import { textTruncate } from "@lib/utils"; +import { Badge, Box, Flex, Link as A, Text } from "@livepeer/design-system"; +import { CheckIcon, Cross2Icon, MinusIcon } from "@radix-ui/react-icons"; +import Link from "next/link"; +import numeral from "numeral"; +import QRCode from "qrcode.react"; +import { useMemo } from "react"; + +import { ExplorerTooltip } from "@components/ExplorerTooltip"; +import { useEnsData } from "hooks"; + +const OrchestratorVotingList = ({ + data, + pageSize = 10, +}: { + pageSize: number; + data: any; +}) => { + data = [ + { + id: "0x75fbf65a3dfe93545c9768f163e59a02daf08d36", + noOfProposalsVotedOn: 200, + noOfVotesCasted: 100, + mostRecentVotes: [true, false, false, true, null], + votingTurnout: 0.5, + }, + { + id: "0x75fbf65a3dfe93545c9768f163e59a02daf08d36", + noOfProposalsVotedOn: 200, + noOfVotesCasted: 100, + mostRecentVotes: [true, false, false, true, null], + votingTurnout: 0.5, + }, + { + id: "0x75fbf65a3dfe93545c9768f163e59a02daf08d36", + noOfProposalsVotedOn: 200, + noOfVotesCasted: 100, + mostRecentVotes: [true, false, false, true, null], + votingTurnout: 0.5, + }, + ]; + pageSize = 2; + + const mappedData = useMemo(() => { + return data?.map((row) => { + return { + ...row, + }; + }); + }, [data]); + + const columns = useMemo( + () => [ + { + Header: ( + + The account which is actively coordinating transcoders and + receiving fees/rewards. + + } + > + + Orchestrator + + + ), + accessor: "id", + Cell: ({ row }) => { + const identity = useEnsData(row.values.id); + + return ( + + + + + {+row.id + 1} + + + + {identity?.avatar ? ( + + ) : ( + + )} + {identity?.name ? ( + + + {textTruncate(identity.name, 20, "…")} + + + {row.values.id.substring(0, 6)} + + + ) : ( + + {row.values.id.replace(row.values.id.slice(7, 37), "…")} + + )} + {/* {(row?.original?.daysSinceChangeParams ?? + Number.MAX_VALUE) < 30 && ( + + + + + + )} */} + + + + + ); + }, + }, + { + Header: ( + // {/* tooltip content here */}} + // > + + Number of Proposals Voted On + + // + ), + accessor: "noOfProposalsVotedOn", + Cell: ({ row }) => ( + + + {numeral(row.values.noOfProposalsVotedOn).format("0,0")} + + + ), + sortType: "number", + }, + { + Header: ( + // {/* tooltip content here */}} + // > + + Number of Votes Casted + + // + ), + accessor: "noOfVotesCasted", + Cell: ({ row }) => ( + + + {numeral(row.values.noOfVotesCasted).format("0,0")} + + + ), + sortType: "number", + }, + { + Header: ( + // {/* tooltip content here */}} + // > + + Most Recent Votes + + // + ), + accessor: "mostRecentVotes", + Cell: ({ row }) => ( + + + {row.values.mostRecentVotes?.map((mostRecentVote, index) => { + let icon = + mostRecentVote === true ? ( + + ) : mostRecentVote === false ? ( + + ) : mostRecentVote === null ? ( + + ) : null; + + return ( + + {icon} + + ); + })} + + + ), + }, + { + Header: ( + // {/* tooltip content here */}} + // > + + Voting Turnout + + // + ), + accessor: "votingTurnout", + Cell: ({ row }) => ( + + + {numeral(row.values.votingTurnout).format("0.0%")} + + + ), + sortType: "number", + }, + ], + [] + ); + + return ( + + ); +}; + +export default OrchestratorVotingList; diff --git a/lib/orchestrartor.ts b/lib/orchestrartor.ts new file mode 100644 index 00000000..9a9ecbe9 --- /dev/null +++ b/lib/orchestrartor.ts @@ -0,0 +1,4 @@ +export enum OrchestratorTabs { + "Yield Overview" = "Yield Overview", + "Voting History" = "Voting History", +} diff --git a/pages/index.tsx b/pages/index.tsx index a99644fa..219b5401 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -13,6 +13,11 @@ import { Container, Flex, Heading, + Tabs, + TabsList, + TabsTrigger, + TabsContent, + Text, } from "@livepeer/design-system"; import { ArrowRightIcon } from "@modulz/radix-icons"; import Link from "next/link"; @@ -30,6 +35,8 @@ import { HomeChartData } from "@lib/api/types/get-chart-data"; import { EnsIdentity } from "@lib/api/types/get-ens"; import { useChartData } from "hooks"; import "react-circular-progressbar/dist/styles.css"; +import OrchestratorVotingList from "@components/OrchestratorVotingList"; +import { OrchestratorTabs } from "@lib/orchestrartor"; const Panel = ({ children }) => ( { - {!orchestrators?.transcoders || !protocol?.protocol ? ( - - - - ) : ( - - - - )} + + + + Yield Overview + + + Voting History + + + + {!orchestrators?.transcoders || !protocol?.protocol ? ( + + + + ) : ( + + + + )} + + + + + + + { )} - - - + + + + Yield Overview + + + Voting History + + + + + + + + + + + + + From 4e21ea81caba1f9d5a36d56130a15a4b5157bac0 Mon Sep 17 00:00:00 2001 From: Shrey kalra Date: Thu, 13 Feb 2025 08:47:25 +0530 Subject: [PATCH 2/9] added ui for vting history view --- layouts/account.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/layouts/account.tsx b/layouts/account.tsx index 5c88a8e9..6b74ab06 100644 --- a/layouts/account.tsx +++ b/layouts/account.tsx @@ -9,6 +9,7 @@ import { useContractRead } from "wagmi"; import BottomDrawer from "@components/BottomDrawer"; import DelegatingView from "@components/DelegatingView"; import HistoryView from "@components/HistoryView"; +import VotingHistoryView from "@components/VotingHistoryView"; import OrchestratingView from "@components/OrchestratingView"; import { checkAddressEquality } from "@lib/utils"; import { @@ -30,6 +31,7 @@ import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; import { useWindowSize } from "react-use"; import { useAccountAddress, useEnsData, useExplorerStore } from "../hooks"; +import VotingHistory from "pages/accounts/[account]/voting_history"; export interface TabType { name: string; @@ -37,9 +39,9 @@ export interface TabType { isActive?: boolean; } -type TabTypeEnum = "delegating" | "orchestrating" | "history"; +type TabTypeEnum = "delegating" | "orchestrating" | "history" |"voting_history"; -const ACCOUNT_VIEWS: TabTypeEnum[] = ["delegating", "orchestrating", "history"]; +const ACCOUNT_VIEWS: TabTypeEnum[] = ["delegating", "orchestrating", "history", "voting_history"]; const AccountLayout = ({ account, @@ -287,6 +289,7 @@ const AccountLayout = ({ /> )} {view === "history" && } + {view === "voting_history" && } {(isOrchestrator || isMyDelegate || isDelegatingAndIsMyAccountView) && (width > 1020 ? ( @@ -360,6 +363,11 @@ function getTabs( href: `/accounts/${account}/history`, isActive: view === "history", }, + { + name: "Voting History", + href: `/accounts/${account}/voting_history`, + isActive: view === "voting_history", + }, ]; if (isOrchestrator || isMyDelegate) { tabs.unshift({ From df6f568c8163f69d76986b22e553677dfbee3657 Mon Sep 17 00:00:00 2001 From: Shrey kalra Date: Thu, 13 Feb 2025 08:48:08 +0530 Subject: [PATCH 3/9] added ui for vting history view --- components/VotingHistoryView/index.tsx | 794 ++++++++++++++++++++ pages/accounts/[account]/voting_history.tsx | 70 ++ 2 files changed, 864 insertions(+) create mode 100644 components/VotingHistoryView/index.tsx create mode 100644 pages/accounts/[account]/voting_history.tsx diff --git a/components/VotingHistoryView/index.tsx b/components/VotingHistoryView/index.tsx new file mode 100644 index 00000000..adc041f5 --- /dev/null +++ b/components/VotingHistoryView/index.tsx @@ -0,0 +1,794 @@ +import Spinner from "@components/Spinner"; +import { + Box, + Card as CardBase, + Flex, + Link as A, + styled, +} from "@livepeer/design-system"; +import { ExternalLinkIcon } from "@modulz/radix-icons"; +import { useTransactionsQuery } from "apollo"; +import dayjs from "dayjs"; +import { CHAIN_INFO, DEFAULT_CHAIN_ID } from "lib/chains"; +import { useRouter } from "next/router"; +import numeral from "numeral"; +import { useMemo } from "react"; +import InfiniteScroll from "react-infinite-scroll-component"; + +const Card = styled(CardBase, { + length: {}, + border: "1px solid $neutral3", + mb: "$2", + p: "$4", +}); + +const Index = () => { + const router = useRouter(); + const query = router.query; + const account = query.account as string; + + const firstData = [ + { + title: "Proposals voted on", + data: "45", + }, + { + title: "Voting turnout", + data: "72.18 %", + } + ] + + + + const SecondData = [ + { + title: "Livepeer LLM SPE", + dateAndTime: "08/27/2024 9:32:40 am - Round #3497", + status: "Active", + orchestratorId: "0xf4e8ef0763bcb2b1af693f5970a00050a6ac7e1b" + }, + { + title: "Livepeer LLM SPE", + dateAndTime: "08/27/2024 9:32:40 am - Round #3497", + status: "Defeated", + orchestratorId: "0xf4e8ef0763bcb2b1af693f5970a00050a6ac7e1b" + }, + { + title: "Livepeer LLM SPE", + dateAndTime: "08/27/2024 9:32:40 am - Round #3497", + status: "Executed", + orchestratorId: "0xf4e8ef0763bcb2b1af693f5970a00050a6ac7e1b" + }, + ] + + const getBackgroundColorByStatus = (status: string) => { + let bgColor = "#212322"; + switch (status) { + case "Active": + bgColor = "#16271F" + break; + case "Defeated": + bgColor = "#321C1D"; + break; + case "Executed": + bgColor = "#212322"; + break; + default: + break; + } + return bgColor; + } + + function shortenAddress(address:string) { + if (address.length < 10) return address; // Handle short addresses + + const first = address.slice(0, 6); // Get the '0x' + first 4 characters + const last = address.slice(-4); // Get last 4 characters + + return `${first}...${last}`; // Return formatted string +} + + + const { data, loading, error, fetchMore, stopPolling } = useTransactionsQuery( + { + variables: { + account: account.toLowerCase(), + first: 10, + skip: 0, + }, + notifyOnNetworkStatusChange: true, + } + ); + + return ( +
+
+ {firstData.map(el => { + return ( +
+
{el.title.toUpperCase()}
+
{el.data}
+
+ + ) + })} +
+
+ {SecondData.map(el => { + return ( +
+
{el.title}
+
{el.dateAndTime}
+
Proposed by livepeer.eth
+
{el.status}
+ +
+ + ) + })} +
+
+ ) +}; + +export default Index; + +// function renderSwitch(event: any, i: number) { +// switch (event.__typename) { +// case "BondEvent": +// return ( +// +// +// +// +// Delegated with{" "} +// {event.newDelegate.id.replace( +// event.newDelegate.id.slice(7, 37), +// "…" +// )} +// +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// {" "} +// +// +{numeral(event.additionalAmount).format("0.0a")} +// {" "} +// LPT +// +// +// +// ); +// case "NewRoundEvent": +// return ( +// +// +// +// Initialized round +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// Round # +// +// {event.round.id} +// +// +// +// +// ); +// case "RebondEvent": +// return ( +// +// +// +// +// Redelegated with{" "} +// {event.delegate.id.replace(event.delegate.id.slice(7, 37), "…")} +// +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// {" "} +// +// +{numeral(event.amount).format("0.0a")} +// {" "} +// LPT +// +// +// +// ); +// case "UnbondEvent": +// return ( +// +// +// +// +// Undelegated from{" "} +// {event.delegate.id.replace(event.delegate.id.slice(7, 37), "…")} +// +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// {" "} +// +// -{numeral(event.amount).format("0.0a")} +// {" "} +// LPT +// +// +// +// ); +// case "RewardEvent": +// return ( +// +// +// +// +// Claimed inflationary token reward +// +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// {" "} +// +// +{numeral(event.rewardTokens).format("0.00a")} +// {" "} +// LPT +// +// +// +// ); +// case "TranscoderUpdateEvent": +// return ( +// +// +// +// Updated orchestrator cut +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// +// +// {event.rewardCut / 10000}% R +// {" "} +// +// +// +// {(100 - event.feeShare / 10000) +// .toFixed(2) +// .replace(/[.,]00$/, "")} +// % F +// {" "} +// +// +// +// +// ); +// case "WithdrawStakeEvent": +// return ( +// +// +// +// Withdrew undelegated tokens +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// {" "} +// +// {numeral(event.amount).format("0.00a")} +// {" "} +// LPT +// +// +// +// ); +// case "WithdrawFeesEvent": +// return ( +// +// +// +// Withdrew earned fees +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// {" "} +// +// {numeral(event.amount).format("0.000a")} +// {" "} +// ETH +// +// +// +// ); +// case "WinningTicketRedeemedEvent": +// return ( +// +// +// +// Redeemed winning ticket +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// {" "} +// +// +{numeral(event.faceValue).format("0.000a")} +// {" "} +// ETH +// +// +// +// ); +// case "DepositFundedEvent": +// return ( +// +// +// +// Deposit funded +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// {" "} +// +// +{numeral(event.amount).format("0.00a")} +// {" "} +// ETH +// +// +// +// ); +// case "ReserveFundedEvent": +// // Ignore funded reserve events where amount is 0 +// // (unable to do this on the graphql query as of now) +// if (+event.amount === 0) { +// return; +// } +// return ( +// +// +// +// Reserve funded +// +// {dayjs +// .unix(event.transaction.timestamp) +// .format("MM/DD/YYYY h:mm:ss a")}{" "} +// - Round #{event.round.id} +// +// +// +// {event.transaction.id.replace( +// event.transaction.id.slice(6, 62), +// "…" +// )} +// +// +// +// +// +// {" "} +// +// +{numeral(event.amount).format("0.00a")} +// {" "} +// ETH +// +// +// +// ); +// default: +// return null; +// } +// } diff --git a/pages/accounts/[account]/voting_history.tsx b/pages/accounts/[account]/voting_history.tsx new file mode 100644 index 00000000..fe230aaa --- /dev/null +++ b/pages/accounts/[account]/voting_history.tsx @@ -0,0 +1,70 @@ +import AccountLayout from "@layouts/account"; +import { getLayout } from "@layouts/main"; +import { getAccount, getSortedOrchestrators } from "@lib/api/ssr"; +import { EnsIdentity } from "@lib/api/types/get-ens"; +import { + AccountQueryResult, + getApollo, + OrchestratorsSortedQueryResult, +} from "apollo"; + +type PageProps = { + account: AccountQueryResult["data"]; + sortedOrchestrators: OrchestratorsSortedQueryResult["data"]; + fallback: { [key: string]: EnsIdentity }; +}; + +const VotingHistory = ({ account, sortedOrchestrators }: PageProps) => ( + +); + +VotingHistory.getLayout = getLayout; + +export const getStaticPaths = async () => { + const { sortedOrchestrators } = await getSortedOrchestrators(); + + return { + paths: + sortedOrchestrators?.data?.transcoders?.map((t) => ({ + params: { account: t.id }, + })) ?? [], + fallback: "blocking", + }; +}; + +export const getStaticProps = async (context) => { + try { + const client = getApollo(); + const { account, fallback } = await getAccount( + client, + context.params?.account?.toString().toLowerCase() + ); + + const { sortedOrchestrators, fallback: sortedOrchestratorsFallback } = + await getSortedOrchestrators(client); + + if (!account.data || !sortedOrchestrators.data) { + return null; + } + + const props: PageProps = { + account: account.data, + sortedOrchestrators: sortedOrchestrators.data, + fallback: { + ...sortedOrchestratorsFallback, + ...fallback, + }, + }; + + return { + props, + revalidate: 600, + }; + } catch (e) { + console.error(e); + } + + return null; +}; + +export default VotingHistory; From 6c5a906dd8602950d7463936cd1a3ed6662f211d Mon Sep 17 00:00:00 2001 From: Shrey kalra Date: Mon, 17 Feb 2025 13:05:57 +0530 Subject: [PATCH 4/9] added data layer --- components/VotingHistoryView/index.tsx | 693 +------------------------ cube/cube-client.ts | 47 ++ package.json | 2 + 3 files changed, 73 insertions(+), 669 deletions(-) create mode 100644 cube/cube-client.ts diff --git a/components/VotingHistoryView/index.tsx b/components/VotingHistoryView/index.tsx index adc041f5..ed2b732a 100644 --- a/components/VotingHistoryView/index.tsx +++ b/components/VotingHistoryView/index.tsx @@ -8,6 +8,7 @@ import { } from "@livepeer/design-system"; import { ExternalLinkIcon } from "@modulz/radix-icons"; import { useTransactionsQuery } from "apollo"; +import { CUBE_TYPE, getCubeData } from "cube/cube-client"; import dayjs from "dayjs"; import { CHAIN_INFO, DEFAULT_CHAIN_ID } from "lib/chains"; import { useRouter } from "next/router"; @@ -89,20 +90,33 @@ const Index = () => { } - const { data, loading, error, fetchMore, stopPolling } = useTransactionsQuery( - { - variables: { - account: account.toLowerCase(), - first: 10, - skip: 0, - }, - notifyOnNetworkStatusChange: true, - } - ); +const fetchingData = async()=>{ + const response = await getCubeData({ + "measures": [ + "IcpSystemParameters.count" + ], + "timeDimensions": [ + { + "dimension": "IcpSystemParameters.date" + } + ], + "order": { + "IcpSystemParameters.count": "desc" + }, + "dimensions": [ + "IcpSystemParameters.canister_id" + ] +}, { type: CUBE_TYPE.SERVER }) +const CUBE_BASE_URL = process.env.CUBE_BASE_URL!; +console.log('response from cube CUBE_BASE_URL', response,CUBE_BASE_URL); +} return (
+ {firstData.map(el => { return (
@@ -133,662 +147,3 @@ const Index = () => { export default Index; -// function renderSwitch(event: any, i: number) { -// switch (event.__typename) { -// case "BondEvent": -// return ( -// -// -// -// -// Delegated with{" "} -// {event.newDelegate.id.replace( -// event.newDelegate.id.slice(7, 37), -// "…" -// )} -// -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// {" "} -// -// +{numeral(event.additionalAmount).format("0.0a")} -// {" "} -// LPT -// -// -// -// ); -// case "NewRoundEvent": -// return ( -// -// -// -// Initialized round -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// Round # -// -// {event.round.id} -// -// -// -// -// ); -// case "RebondEvent": -// return ( -// -// -// -// -// Redelegated with{" "} -// {event.delegate.id.replace(event.delegate.id.slice(7, 37), "…")} -// -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// {" "} -// -// +{numeral(event.amount).format("0.0a")} -// {" "} -// LPT -// -// -// -// ); -// case "UnbondEvent": -// return ( -// -// -// -// -// Undelegated from{" "} -// {event.delegate.id.replace(event.delegate.id.slice(7, 37), "…")} -// -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// {" "} -// -// -{numeral(event.amount).format("0.0a")} -// {" "} -// LPT -// -// -// -// ); -// case "RewardEvent": -// return ( -// -// -// -// -// Claimed inflationary token reward -// -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// {" "} -// -// +{numeral(event.rewardTokens).format("0.00a")} -// {" "} -// LPT -// -// -// -// ); -// case "TranscoderUpdateEvent": -// return ( -// -// -// -// Updated orchestrator cut -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// -// -// {event.rewardCut / 10000}% R -// {" "} -// -// -// -// {(100 - event.feeShare / 10000) -// .toFixed(2) -// .replace(/[.,]00$/, "")} -// % F -// {" "} -// -// -// -// -// ); -// case "WithdrawStakeEvent": -// return ( -// -// -// -// Withdrew undelegated tokens -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// {" "} -// -// {numeral(event.amount).format("0.00a")} -// {" "} -// LPT -// -// -// -// ); -// case "WithdrawFeesEvent": -// return ( -// -// -// -// Withdrew earned fees -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// {" "} -// -// {numeral(event.amount).format("0.000a")} -// {" "} -// ETH -// -// -// -// ); -// case "WinningTicketRedeemedEvent": -// return ( -// -// -// -// Redeemed winning ticket -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// {" "} -// -// +{numeral(event.faceValue).format("0.000a")} -// {" "} -// ETH -// -// -// -// ); -// case "DepositFundedEvent": -// return ( -// -// -// -// Deposit funded -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// {" "} -// -// +{numeral(event.amount).format("0.00a")} -// {" "} -// ETH -// -// -// -// ); -// case "ReserveFundedEvent": -// // Ignore funded reserve events where amount is 0 -// // (unable to do this on the graphql query as of now) -// if (+event.amount === 0) { -// return; -// } -// return ( -// -// -// -// Reserve funded -// -// {dayjs -// .unix(event.transaction.timestamp) -// .format("MM/DD/YYYY h:mm:ss a")}{" "} -// - Round #{event.round.id} -// -// -// -// {event.transaction.id.replace( -// event.transaction.id.slice(6, 62), -// "…" -// )} -// -// -// -// -// -// {" "} -// -// +{numeral(event.amount).format("0.00a")} -// {" "} -// ETH -// -// -// -// ); -// default: -// return null; -// } -// } diff --git a/cube/cube-client.ts b/cube/cube-client.ts new file mode 100644 index 00000000..3ef7422a --- /dev/null +++ b/cube/cube-client.ts @@ -0,0 +1,47 @@ +import cubejs, { QueryType } from "@cubejs-client/core"; + +export enum CUBE_TYPE { + SERVER = "SERVER", + CLIENT = "CLIENT", + PUBLIC = "PUBLIC", +} + +const CUBE_BASE_URL ="https://cube.dev.analytics.pyor.xyz" ; +let cubePublicAuthToken = process.env.CUBE_PUBLIC_TOKEN || ""; + +const cubejsApiClient = cubejs("CUBEJS-API-TOKEN", { + apiUrl: `/api/services/cube/cubejs-api/v1`, +}); + +const cubejsApiPublic = cubejs(cubePublicAuthToken, { + apiUrl: `${CUBE_BASE_URL}/cubejs-api/v1`, +}); + +export async function getCubeData( + query: any, + options: { + type: CUBE_TYPE; + headerData?: { token: any }; + } = { + type: CUBE_TYPE.CLIENT, + } +) { + let cubejsApi = + options.type === CUBE_TYPE.CLIENT + ? cubejsApiPublic + : options.type === CUBE_TYPE.SERVER + ? cubejs(options.headerData?.token, { + apiUrl: `${CUBE_BASE_URL}/cubejs-api/v1`, + }) + : cubejsApiPublic; + + try { + const resultSet: any = await cubejsApi.load(query); + const response = resultSet.loadResponse.results; + return response; + } catch (error) { + console.error(error); + } +} + + diff --git a/package.json b/package.json index 827f7eed..da3f602b 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,8 @@ }, "dependencies": { "@apollo/client": "^3.5.8", + "@cubejs-client/core": "^1.2.4", + "@cubejs-client/react": "^1.2.4", "@graphql-tools/delegate": "8.8.0", "@graphql-tools/links": "^8.3.0", "@graphql-tools/schema": "8.5.0", From 41c4daa82b6ed226b63386f8805beb0540a3ceef Mon Sep 17 00:00:00 2001 From: Shrey kalra Date: Fri, 21 Feb 2025 10:55:39 +0530 Subject: [PATCH 5/9] changes done for ssr --- components/OrchestratorVotingList/index.tsx | 126 ++++-------- components/VotingHistoryView/index.tsx | 206 +++++++++++--------- cube/queryGenrator.ts | 47 +++++ pages/accounts/[account]/voting_history.tsx | 6 +- pages/index.tsx | 93 ++++++++- pages/orchestrators.tsx | 94 ++++++++- 6 files changed, 372 insertions(+), 200 deletions(-) create mode 100644 cube/queryGenrator.ts diff --git a/components/OrchestratorVotingList/index.tsx b/components/OrchestratorVotingList/index.tsx index f341f3d8..ca8ec657 100644 --- a/components/OrchestratorVotingList/index.tsx +++ b/components/OrchestratorVotingList/index.tsx @@ -10,45 +10,18 @@ import { useMemo } from "react"; import { ExplorerTooltip } from "@components/ExplorerTooltip"; import { useEnsData } from "hooks"; -const OrchestratorVotingList = ({ - data, - pageSize = 10, -}: { - pageSize: number; - data: any; -}) => { - data = [ - { - id: "0x75fbf65a3dfe93545c9768f163e59a02daf08d36", - noOfProposalsVotedOn: 200, - noOfVotesCasted: 100, - mostRecentVotes: [true, false, false, true, null], - votingTurnout: 0.5, - }, - { - id: "0x75fbf65a3dfe93545c9768f163e59a02daf08d36", - noOfProposalsVotedOn: 200, - noOfVotesCasted: 100, - mostRecentVotes: [true, false, false, true, null], - votingTurnout: 0.5, - }, - { - id: "0x75fbf65a3dfe93545c9768f163e59a02daf08d36", - noOfProposalsVotedOn: 200, - noOfVotesCasted: 100, - mostRecentVotes: [true, false, false, true, null], - votingTurnout: 0.5, - }, - ]; - pageSize = 2; - const mappedData = useMemo(() => { - return data?.map((row) => { - return { - ...row, - }; - }); - }, [data]); + +type VoterSummary = { + id: string; + noOfProposalsVotedOn: number; + noOfVotesCasted: number; + mostRecentVotes: (string | null)[]; + votingTurnout: number; +}; + +const OrchestratorVotingList = ({ initialVoterData }: { initialVoterData?: VoterSummary[] }) => { + const pageSize = 10; const columns = useMemo( () => [ @@ -157,20 +130,6 @@ const OrchestratorVotingList = ({ {row.values.id.replace(row.values.id.slice(7, 37), "…")} )} - {/* {(row?.original?.daysSinceChangeParams ?? - Number.MAX_VALUE) < 30 && ( - - - - - - )} */} @@ -180,10 +139,6 @@ const OrchestratorVotingList = ({ }, { Header: ( - // {/* tooltip content here */}} - // > Number of Proposals Voted On - // ), accessor: "noOfProposalsVotedOn", Cell: ({ row }) => ( @@ -213,10 +167,6 @@ const OrchestratorVotingList = ({ }, { Header: ( - // {/* tooltip content here */}} - // > Number of Votes Casted - // ), accessor: "noOfVotesCasted", Cell: ({ row }) => ( @@ -246,10 +195,6 @@ const OrchestratorVotingList = ({ }, { Header: ( - // {/* tooltip content here */}} - // > {row.values.mostRecentVotes?.map((mostRecentVote, index) => { + let icon = - mostRecentVote === true ? ( + mostRecentVote == "for" ? ( - ) : mostRecentVote === false ? ( + ) : mostRecentVote == "against" ? ( - ) : mostRecentVote === null ? ( + ) : mostRecentVote == "abstain" ? ( ) : null; @@ -279,13 +225,13 @@ const OrchestratorVotingList = ({ {/* tooltip content here */}} - // > Voting Turnout - // ), accessor: "votingTurnout", Cell: ({ row }) => ( @@ -341,16 +282,21 @@ const OrchestratorVotingList = ({ ], [] ); - - return ( -
- ); + if (initialVoterData) { + return ( +
+ ); + } else { + return null; + } }; + + export default OrchestratorVotingList; diff --git a/components/VotingHistoryView/index.tsx b/components/VotingHistoryView/index.tsx index ed2b732a..90c330ad 100644 --- a/components/VotingHistoryView/index.tsx +++ b/components/VotingHistoryView/index.tsx @@ -1,67 +1,25 @@ -import Spinner from "@components/Spinner"; import { - Box, Card as CardBase, - Flex, Link as A, styled, + Flex, } from "@livepeer/design-system"; -import { ExternalLinkIcon } from "@modulz/radix-icons"; -import { useTransactionsQuery } from "apollo"; import { CUBE_TYPE, getCubeData } from "cube/cube-client"; -import dayjs from "dayjs"; -import { CHAIN_INFO, DEFAULT_CHAIN_ID } from "lib/chains"; +import { getAccountVotingHistory } from "cube/queryGenrator"; import { useRouter } from "next/router"; -import numeral from "numeral"; -import { useMemo } from "react"; -import InfiniteScroll from "react-infinite-scroll-component"; +import { useEffect, useState } from "react"; +import { QuestionMarkCircledIcon } from "@radix-ui/react-icons"; +import Spinner from "@components/Spinner"; -const Card = styled(CardBase, { - length: {}, - border: "1px solid $neutral3", - mb: "$2", - p: "$4", -}); const Index = () => { const router = useRouter(); const query = router.query; const account = query.account as string; - const firstData = [ - { - title: "Proposals voted on", - data: "45", - }, - { - title: "Voting turnout", - data: "72.18 %", - } - ] - - - - const SecondData = [ - { - title: "Livepeer LLM SPE", - dateAndTime: "08/27/2024 9:32:40 am - Round #3497", - status: "Active", - orchestratorId: "0xf4e8ef0763bcb2b1af693f5970a00050a6ac7e1b" - }, - { - title: "Livepeer LLM SPE", - dateAndTime: "08/27/2024 9:32:40 am - Round #3497", - status: "Defeated", - orchestratorId: "0xf4e8ef0763bcb2b1af693f5970a00050a6ac7e1b" - }, - { - title: "Livepeer LLM SPE", - dateAndTime: "08/27/2024 9:32:40 am - Round #3497", - status: "Executed", - orchestratorId: "0xf4e8ef0763bcb2b1af693f5970a00050a6ac7e1b" - }, - ] - + const [votingTurnOut, setVotingTurnOut] = useState(); + const [votingData, setVotingData] = useState() + const [isLoading, setIsLoading] = useState(false); const getBackgroundColorByStatus = (status: string) => { let bgColor = "#212322"; switch (status) { @@ -80,66 +38,120 @@ const Index = () => { return bgColor; } - function shortenAddress(address:string) { + function shortenAddress(address: string) { if (address.length < 10) return address; // Handle short addresses const first = address.slice(0, 6); // Get the '0x' + first 4 characters const last = address.slice(-4); // Get last 4 characters return `${first}...${last}`; // Return formatted string -} - - -const fetchingData = async()=>{ - const response = await getCubeData({ - "measures": [ - "IcpSystemParameters.count" - ], - "timeDimensions": [ - { - "dimension": "IcpSystemParameters.date" - } - ], - "order": { - "IcpSystemParameters.count": "desc" - }, - "dimensions": [ - "IcpSystemParameters.canister_id" - ] -}, { type: CUBE_TYPE.SERVER }) -const CUBE_BASE_URL = process.env.CUBE_BASE_URL!; -console.log('response from cube CUBE_BASE_URL', response,CUBE_BASE_URL); -} + } + + + + const fetchingData = async () => { + setIsLoading(true); + try { + const query = getAccountVotingHistory(account); + const response = await getCubeData(query, { type: CUBE_TYPE.SERVER }); + const data = response[0].data; + if (data.length > 0) { + setVotingTurnOut(data[0]['LivepeerVoteType.votingTurnout']); + setVotingData(data); + } + setIsLoading(false) + } catch (error) { + setIsLoading(false) + } + } + + useEffect(() => { + fetchingData(); + }, []) + const getDateTimeAndRound = (date: string, round: string): string => { + // Parse the date string to a Date object + const dateObj = new Date(date); + + // Function to format the date to "MM/DD/YYYY h:mm:ss a" + const formatDate = (date: Date): string => { + const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + const hours = date.getHours(); + const minutes = date.getMinutes(); + const seconds = date.getSeconds(); + const ampm = hours >= 12 ? 'pm' : 'am'; + + const day = date.getDate(); + const month = months[date.getMonth()]; + const year = date.getFullYear(); + const formattedTime = `${month} ${day}, ${year} ${hours % 12 || 12}:${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + seconds : seconds} ${ampm}`; + + return formattedTime; + }; + + // Round logic (In case the round value needs transformation, it's done here) + const roundNumber = round.split("-")[0]; // Assuming round is in the format "3466-01-01T00:00:00.000", just using the first part + + // Format date + const formattedDate = formatDate(dateObj); + + // Return the final output in the required format + return `${formattedDate} - Round #${roundNumber}`; + } + + if (isLoading) { + return ( + + + + ) + } return (
- - {firstData.map(el => { - return ( -
-
{el.title.toUpperCase()}
-
{el.data}
-
- ) - })} -
-
- {SecondData.map(el => { - return ( -
-
{el.title}
-
{el.dateAndTime}
-
Proposed by livepeer.eth
-
{el.status}
-
{shortenAddress(el.orchestratorId)}
+
+
+
PROPOSALS VOTED ON
+
+ +
+
+
5
+
+ +
+
+
VOTING TURNOUT
+
+
+
+
{votingTurnOut}
+
- ) - })} +
+
+ {votingData && + // @ts-ignore + votingData.map(el => { + return ( +
+
{el['LivepeerVoteType.nameOfProposal']}
+
{getDateTimeAndRound(el['LivepeerVoteType.date'], el['LivepeerVoteType.round'])}
+
Proposed by livepeer.eth
+ {/*
{el.status}
*/} + +
+ ) + })}
) diff --git a/cube/queryGenrator.ts b/cube/queryGenrator.ts new file mode 100644 index 00000000..21d71c27 --- /dev/null +++ b/cube/queryGenrator.ts @@ -0,0 +1,47 @@ +export const getAccountVotingHistory =(id:string) =>{ + + return `{ + "measures": [ + "LivepeerVoteType.count", + "LivepeerVoteType.votingTurnout" + ], + "order": { + "LivepeerVoteType.count": "desc" + }, + "dimensions": [ + "LivepeerVoteType.date", + "LivepeerVoteType.voter", + "LivepeerVoteType.nameOfProposal", + "LivepeerVoteType.voteType", + "LivepeerVoteType.round" + ], + "filters": [ + { + "member": "LivepeerVoteType.voter", + "operator": "equals", + "values": [ + "${id}" + ] + } + ] +}`} + + +export const getOrchestratorsVotingHistory=()=>{ + return `{ + "measures": [ + "LivepeerVoteProposals.count", + "LivepeerVoteProposals.numOfProposals", + "LivepeerVoteProposals.numOfVoteCasted" + ], + "order": { + "LivepeerVoteProposals.count": "desc" + }, + "dimensions": [ + "LivepeerVoteProposals.date", + "LivepeerVoteProposals.voter", + "LivepeerVoteProposals.eventTxnsHash", + "LivepeerVoteProposals.voteType" + ] +}` +} \ No newline at end of file diff --git a/pages/accounts/[account]/voting_history.tsx b/pages/accounts/[account]/voting_history.tsx index fe230aaa..428e32ad 100644 --- a/pages/accounts/[account]/voting_history.tsx +++ b/pages/accounts/[account]/voting_history.tsx @@ -1,12 +1,12 @@ -import AccountLayout from "@layouts/account"; import { getLayout } from "@layouts/main"; -import { getAccount, getSortedOrchestrators } from "@lib/api/ssr"; -import { EnsIdentity } from "@lib/api/types/get-ens"; +import AccountLayout from "@layouts/account"; import { AccountQueryResult, getApollo, OrchestratorsSortedQueryResult, } from "apollo"; +import { getAccount, getSortedOrchestrators } from "@lib/api/ssr"; +import { EnsIdentity } from "@lib/api/types/get-ens"; type PageProps = { account: AccountQueryResult["data"]; diff --git a/pages/index.tsx b/pages/index.tsx index 219b5401..93b866c0 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -37,6 +37,8 @@ import { useChartData } from "hooks"; import "react-circular-progressbar/dist/styles.css"; import OrchestratorVotingList from "@components/OrchestratorVotingList"; import { OrchestratorTabs } from "@lib/orchestrartor"; +import { getOrchestratorsVotingHistory } from "cube/queryGenrator"; +import { CUBE_TYPE, getCubeData } from "cube/cube-client"; const Panel = ({ children }) => ( { +const Home = ({ orchestrators, events, protocol, initialVoterData }: PageProps) => { const allEvents = useMemo( () => events?.transactions @@ -414,10 +417,7 @@ const Home = ({ orchestrators, events, protocol }: PageProps) => { - + @@ -471,6 +471,70 @@ const Home = ({ orchestrators, events, protocol }: PageProps) => { ); }; +type VoteProposal = { + "LivepeerVoteProposals.date": string; + "LivepeerVoteProposals.voter": string; + "LivepeerVoteProposals.eventTxnsHash": string; + "LivepeerVoteProposals.voteType": string; + "LivepeerVoteProposals.count": string; + "LivepeerVoteProposals.numOfProposals": string; + "LivepeerVoteProposals.numOfVoteCasted": string; +}; + +type VoterSummary = { + id: string; + noOfProposalsVotedOn: number; + noOfVotesCasted: number; + mostRecentVotes: (string | null)[]; + votingTurnout: number; +}; + + +// Function to get unique voter IDs +const getUniqueVoters = (data: VoteProposal[]): string[] => { + const voterSet = new Set(data.map(proposal => proposal["LivepeerVoteProposals.voter"])); + return Array.from(voterSet); +}; + +// Function to group data by voter +const groupByVoter = (data: VoteProposal[], voterId: string): VoteProposal[] => { + return data.filter(proposal => proposal["LivepeerVoteProposals.voter"] === voterId); +}; + +// Function to process vote proposals and generate voter summary +const processVoteProposals = (proposals: VoteProposal[]): VoterSummary => { + const sortedVotes = proposals.sort((a, b) => + new Date(b["LivepeerVoteProposals.date"]).getTime() - new Date(a["LivepeerVoteProposals.date"]).getTime() + ); + + const mostRecentVotes = sortedVotes.slice(0, 5).map(vote => vote["LivepeerVoteProposals.voteType"] || null); + + const noOfProposalsVotedOn = proposals.length; + const noOfVotesCasted = proposals.reduce((acc, vote) => acc + parseInt(vote["LivepeerVoteProposals.numOfVoteCasted"], 10), 0); + + const votingTurnout = noOfProposalsVotedOn ? noOfVotesCasted / noOfProposalsVotedOn : 0; + + return { + id: proposals[0]["LivepeerVoteProposals.voter"], + noOfProposalsVotedOn, + noOfVotesCasted, + mostRecentVotes, + votingTurnout, + }; +}; + +// Function to get voter summaries for all unique voters +const getVoterSummaries = (data: VoteProposal[]): VoterSummary[] => { + const uniqueVoters = getUniqueVoters(data); + return uniqueVoters.map(voterId => { + const groupedProposals = groupByVoter(data, voterId); + return processVoteProposals(groupedProposals); + }); +}; + + + + export const getStaticProps = async () => { const errorProps = { props: {}, @@ -482,6 +546,24 @@ export const getStaticProps = async () => { const { events, fallback: eventsFallback } = await getEvents(client); const protocol = await getProtocol(client); + + const query = getOrchestratorsVotingHistory(); + const response = await getCubeData(query, { type: CUBE_TYPE.SERVER }); + + // Log the response to check the structure of the data + + if (!response || !response[0] || !response[0].data) { + return { + props: { + initialVoterData: [], + }, + }; + } + + const data = response[0].data; + + const voterSummaries = getVoterSummaries(data); + if (!orchestrators.data || !events.data || !protocol.data) { return errorProps; } @@ -491,6 +573,7 @@ export const getStaticProps = async () => { events: events.data, protocol: protocol.data, fallback: {}, + initialVoterData:voterSummaries // fallback: { ...fallback, ...eventsFallback }, }; diff --git a/pages/orchestrators.tsx b/pages/orchestrators.tsx index 609c4cfe..acea6ec3 100644 --- a/pages/orchestrators.tsx +++ b/pages/orchestrators.tsx @@ -25,14 +25,17 @@ import { } from "../apollo"; import OrchestratorVotingList from "@components/OrchestratorVotingList"; import { OrchestratorTabs } from "@lib/orchestrartor"; +import { getOrchestratorsVotingHistory } from "cube/queryGenrator"; +import { CUBE_TYPE, getCubeData } from "cube/cube-client"; type PageProps = { orchestrators: OrchestratorsQueryResult["data"]; protocol: ProtocolQueryResult["data"]; fallback: { [key: string]: EnsIdentity }; + initialVoterData:any }; -const OrchestratorsPage = ({ orchestrators, protocol }: PageProps) => { +const OrchestratorsPage = ({ orchestrators, protocol , initialVoterData}: PageProps) => { return ( <> @@ -100,10 +103,7 @@ const OrchestratorsPage = ({ orchestrators, protocol }: PageProps) => { - + @@ -113,8 +113,90 @@ const OrchestratorsPage = ({ orchestrators, protocol }: PageProps) => { ); }; +type VoteProposal = { + "LivepeerVoteProposals.date": string; + "LivepeerVoteProposals.voter": string; + "LivepeerVoteProposals.eventTxnsHash": string; + "LivepeerVoteProposals.voteType": string; + "LivepeerVoteProposals.count": string; + "LivepeerVoteProposals.numOfProposals": string; + "LivepeerVoteProposals.numOfVoteCasted": string; +}; + +type VoterSummary = { + id: string; + noOfProposalsVotedOn: number; + noOfVotesCasted: number; + mostRecentVotes: (string | null)[]; + votingTurnout: number; +}; + + +// Function to get unique voter IDs +const getUniqueVoters = (data: VoteProposal[]): string[] => { + const voterSet = new Set(data.map(proposal => proposal["LivepeerVoteProposals.voter"])); + return Array.from(voterSet); +}; + +// Function to group data by voter +const groupByVoter = (data: VoteProposal[], voterId: string): VoteProposal[] => { + return data.filter(proposal => proposal["LivepeerVoteProposals.voter"] === voterId); +}; + +// Function to process vote proposals and generate voter summary +const processVoteProposals = (proposals: VoteProposal[]): VoterSummary => { + const sortedVotes = proposals.sort((a, b) => + new Date(b["LivepeerVoteProposals.date"]).getTime() - new Date(a["LivepeerVoteProposals.date"]).getTime() + ); + + const mostRecentVotes = sortedVotes.slice(0, 5).map(vote => vote["LivepeerVoteProposals.voteType"] || null); + + const noOfProposalsVotedOn = proposals.length; + const noOfVotesCasted = proposals.reduce((acc, vote) => acc + parseInt(vote["LivepeerVoteProposals.numOfVoteCasted"], 10), 0); + + const votingTurnout = noOfProposalsVotedOn ? noOfVotesCasted / noOfProposalsVotedOn : 0; + + return { + id: proposals[0]["LivepeerVoteProposals.voter"], + noOfProposalsVotedOn, + noOfVotesCasted, + mostRecentVotes, + votingTurnout, + }; +}; + +// Function to get voter summaries for all unique voters +const getVoterSummaries = (data: VoteProposal[]): VoterSummary[] => { + const uniqueVoters = getUniqueVoters(data); + return uniqueVoters.map(voterId => { + const groupedProposals = groupByVoter(data, voterId); + return processVoteProposals(groupedProposals); + }); +}; + + + export const getStaticProps = async () => { try { + + const query = getOrchestratorsVotingHistory(); + const response = await getCubeData(query, { type: CUBE_TYPE.SERVER }); + + // Log the response to check the structure of the data + + if (!response || !response[0] || !response[0].data) { + return { + props: { + initialVoterData: [], + }, + }; + } + + const data = response[0].data; + + const voterSummaries = getVoterSummaries(data); + + const client = getApollo(); const { orchestrators, fallback } = await getOrchestrators(client); const protocol = await getProtocol(client); @@ -123,10 +205,12 @@ export const getStaticProps = async () => { return null; } + const props: PageProps = { orchestrators: orchestrators.data, protocol: protocol.data, fallback, + initialVoterData:voterSummaries }; return { From fe630ba832d5331d125faa100a5cde8669f6f2ab Mon Sep 17 00:00:00 2001 From: Shrey kalra Date: Fri, 28 Feb 2025 10:22:06 +0530 Subject: [PATCH 6/9] updated query --- components/VotingHistoryView/index.tsx | 70 +++++++++++++++++++++---- cube/queryGenrator.ts | 22 ++++---- package.json | 2 +- public/img/Vector.png | Bin 0 -> 380 bytes 4 files changed, 75 insertions(+), 19 deletions(-) create mode 100644 public/img/Vector.png diff --git a/components/VotingHistoryView/index.tsx b/components/VotingHistoryView/index.tsx index 90c330ad..8d6f2829 100644 --- a/components/VotingHistoryView/index.tsx +++ b/components/VotingHistoryView/index.tsx @@ -10,6 +10,8 @@ import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { QuestionMarkCircledIcon } from "@radix-ui/react-icons"; import Spinner from "@components/Spinner"; +import Image from "next/image"; + const Index = () => { @@ -17,9 +19,11 @@ const Index = () => { const query = router.query; const account = query.account as string; + const [proposalVotedOn, setProposalVotedOn] = useState(); const [votingTurnOut, setVotingTurnOut] = useState(); const [votingData, setVotingData] = useState() const [isLoading, setIsLoading] = useState(false); + const getBackgroundColorByStatus = (status: string) => { let bgColor = "#212322"; switch (status) { @@ -38,6 +42,39 @@ const Index = () => { return bgColor; } + const getTextStyleByStatus = (status: string) => { + const stylesMap: Record = { + Active: { + color: "#51A7FD", + backgroundColor: "#11233E", + maxWidth: 80, + justifyContent: 'center', + display: 'flex', + borderRadius: 8 + }, + Defeated: { + color: "#FF6468", + backgroundColor: "#3C181A", + maxWidth: 80, + justifyContent: 'center', + display: 'flex', + borderRadius: 8 + }, + Executed: { + color: "#4ABF87", + backgroundColor: "#10291E", + maxWidth: 80, + justifyContent: 'center', + display: 'flex', + borderRadius: 8 + }, + }; + + return stylesMap[status] || {}; // Returns styles if status is found, otherwise returns an empty object + }; + + + function shortenAddress(address: string) { if (address.length < 10) return address; // Handle short addresses @@ -56,7 +93,8 @@ const Index = () => { const response = await getCubeData(query, { type: CUBE_TYPE.SERVER }); const data = response[0].data; if (data.length > 0) { - setVotingTurnOut(data[0]['LivepeerVoteType.votingTurnout']); + setVotingTurnOut(data[0]['LivepeerProposalStatus.votingTurnout']); + setProposalVotedOn(data[0]["LivepeerProposalStatus.proposalVotedOn"]); setVotingData(data); } setIsLoading(false) @@ -124,7 +162,7 @@ const Index = () => {
-
5
+
{proposalVotedOn}
@@ -141,14 +179,28 @@ const Index = () => {
{votingData && // @ts-ignore - votingData.map(el => { + votingData.map((el, index) => { return ( -
-
{el['LivepeerVoteType.nameOfProposal']}
-
{getDateTimeAndRound(el['LivepeerVoteType.date'], el['LivepeerVoteType.round'])}
-
Proposed by livepeer.eth
- {/*
{el.status}
*/} -
{shortenAddress(el['LivepeerVoteType.voter'])}
+
+
{el['LivepeerProposalStatus.nameOfProposal']}
+
{getDateTimeAndRound(el['LivepeerProposalStatus.date'], el['LivepeerProposalStatus.round'])}
+
Proposed by livepeer.eth
+
{el["LivepeerProposalStatus.status"]}
+
) })} diff --git a/cube/queryGenrator.ts b/cube/queryGenrator.ts index 21d71c27..3d53d984 100644 --- a/cube/queryGenrator.ts +++ b/cube/queryGenrator.ts @@ -2,22 +2,26 @@ export const getAccountVotingHistory =(id:string) =>{ return `{ "measures": [ - "LivepeerVoteType.count", - "LivepeerVoteType.votingTurnout" + "LivepeerProposalStatus.count", + "LivepeerProposalStatus.votingTurnout", + "LivepeerProposalStatus.proposalVotedOn" ], "order": { - "LivepeerVoteType.count": "desc" + "LivepeerProposalStatus.count": "desc" }, "dimensions": [ - "LivepeerVoteType.date", - "LivepeerVoteType.voter", - "LivepeerVoteType.nameOfProposal", - "LivepeerVoteType.voteType", - "LivepeerVoteType.round" + "LivepeerProposalStatus.date", + "LivepeerProposalStatus.round", + "LivepeerProposalStatus.eventTxnHash", + "LivepeerProposalStatus.nameOfProposal", + "LivepeerProposalStatus.voteType", + "LivepeerProposalStatus.status", + "LivepeerProposalStatus.proposedBy", + "LivepeerProposalStatus.voter" ], "filters": [ { - "member": "LivepeerVoteType.voter", + "member": "LivepeerProposalStatus.voter", "operator": "equals", "values": [ "${id}" diff --git a/package.json b/package.json index da3f602b..08ac9801 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ }, "dependencies": { "@apollo/client": "^3.5.8", - "@cubejs-client/core": "^1.2.4", + "@cubejs-client/core": "^1.2.9", "@cubejs-client/react": "^1.2.4", "@graphql-tools/delegate": "8.8.0", "@graphql-tools/links": "^8.3.0", diff --git a/public/img/Vector.png b/public/img/Vector.png new file mode 100644 index 0000000000000000000000000000000000000000..639d88d5905dd06817195a8e51529ac4046d3da2 GIT binary patch literal 380 zcmeAS@N?(olHy`uVBq!ia0vp^iXhCv1|-9u9Lfh$oCO|{#XvC&5N33pW|#mJWGoJH zcVbv~PUa<$!;&U>cv7h@-A}df%(9^{+q=ND7bZ@>U0|AG0DgFnnqV|Tz=9&xm zEqJ4%Ch(!5h$*|-y?Tkhx6*C!cDRIc1fAs0uA05Bx3pky7_*dr|iDZ91 zx}fpQ;Z3V{Y-8TSs*=oqQ8eJY(SsUhv5teCzBwFcR?S)6{kW?A!NmC7B!P~N-&HzX z;uqZ4doSe7v^+zeeewmt4_XVgANU?}Indp*l3`~{XTvY2Cxwi{93HnKgw Date: Fri, 28 Feb 2025 16:08:57 +0530 Subject: [PATCH 7/9] added page size in props --- components/OrchestratorVotingList/index.tsx | 3 +-- pages/index.tsx | 2 +- pages/orchestrators.tsx | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/components/OrchestratorVotingList/index.tsx b/components/OrchestratorVotingList/index.tsx index ca8ec657..ef9fede6 100644 --- a/components/OrchestratorVotingList/index.tsx +++ b/components/OrchestratorVotingList/index.tsx @@ -20,8 +20,7 @@ type VoterSummary = { votingTurnout: number; }; -const OrchestratorVotingList = ({ initialVoterData }: { initialVoterData?: VoterSummary[] }) => { - const pageSize = 10; +const OrchestratorVotingList = ({ initialVoterData , pageSize = 10}: { initialVoterData?: VoterSummary[] , pageSize:number}) => { const columns = useMemo( () => [ diff --git a/pages/index.tsx b/pages/index.tsx index 93b866c0..fbb1ee4f 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -417,7 +417,7 @@ const Home = ({ orchestrators, events, protocol, initialVoterData }: PageProps) - + diff --git a/pages/orchestrators.tsx b/pages/orchestrators.tsx index acea6ec3..2fcb482e 100644 --- a/pages/orchestrators.tsx +++ b/pages/orchestrators.tsx @@ -103,7 +103,7 @@ const OrchestratorsPage = ({ orchestrators, protocol , initialVoterData}: PagePr - + From 3f8b3e5dd9577cc3c8590fb55f4cdb318af813e6 Mon Sep 17 00:00:00 2001 From: Shrey kalra Date: Mon, 7 Apr 2025 13:05:55 +0530 Subject: [PATCH 8/9] updated ui fixes --- components/OrchestratorVotingList/index.tsx | 81 +++++++------ pages/orchestrators.tsx | 127 +++++++++++--------- 2 files changed, 116 insertions(+), 92 deletions(-) diff --git a/components/OrchestratorVotingList/index.tsx b/components/OrchestratorVotingList/index.tsx index ef9fede6..84de0bdd 100644 --- a/components/OrchestratorVotingList/index.tsx +++ b/components/OrchestratorVotingList/index.tsx @@ -20,7 +20,7 @@ type VoterSummary = { votingTurnout: number; }; -const OrchestratorVotingList = ({ initialVoterData , pageSize = 10}: { initialVoterData?: VoterSummary[] , pageSize:number}) => { +const OrchestratorVotingList = ({ initialVoterData, pageSize = 10 }: { initialVoterData?: VoterSummary[], pageSize: number }) => { const columns = useMemo( () => [ @@ -138,15 +138,19 @@ const OrchestratorVotingList = ({ initialVoterData , pageSize = 10}: { initial }, { Header: ( - + The total number of governance proposals this orchestrator has participated in by casting a vote. + + } > - Number of Proposals Voted On - + + Number of Proposals Voted On + + + ), accessor: "noOfProposalsVotedOn", Cell: ({ row }) => ( @@ -166,15 +170,18 @@ const OrchestratorVotingList = ({ initialVoterData , pageSize = 10}: { initial }, { Header: ( - - Number of Votes Casted + + The total count of individual votes submitted by this orchestrator across all proposals. + + } + > + + Number of Votes Casted + ), accessor: "noOfVotesCasted", Cell: ({ row }) => ( @@ -194,16 +201,18 @@ const OrchestratorVotingList = ({ initialVoterData , pageSize = 10}: { initial }, { Header: ( - - Most Recent Votes + + A list of up to 5 of the orchestrator’s most recent votes, marked as [✓] for For, [✗] for Against, and [–] for Abstain. + + } + > + + Most Recent Votes - // + ), accessor: "mostRecentVotes", Cell: ({ row }) => ( @@ -252,15 +261,19 @@ const OrchestratorVotingList = ({ initialVoterData , pageSize = 10}: { initial }, { Header: ( - - Voting Turnout + + The percentage of total governance proposals this orchestrator voted on, showing how actively they participate in protocol decisions. + + } + > + + Voting Turnout + + ), accessor: "votingTurnout", Cell: ({ row }) => ( diff --git a/pages/orchestrators.tsx b/pages/orchestrators.tsx index 2fcb482e..0476a882 100644 --- a/pages/orchestrators.tsx +++ b/pages/orchestrators.tsx @@ -9,10 +9,6 @@ import { Container, Flex, Heading, - TabsTrigger, - Tabs, - TabsList, - TabsContent, Text, } from "@livepeer/design-system"; import { ArrowRightIcon } from "@modulz/radix-icons"; @@ -27,15 +23,22 @@ import OrchestratorVotingList from "@components/OrchestratorVotingList"; import { OrchestratorTabs } from "@lib/orchestrartor"; import { getOrchestratorsVotingHistory } from "cube/queryGenrator"; import { CUBE_TYPE, getCubeData } from "cube/cube-client"; +import { + Tabs, + TabList, + Tab, + TabPanels, + TabPanel, +} from "@reach/tabs"; type PageProps = { orchestrators: OrchestratorsQueryResult["data"]; protocol: ProtocolQueryResult["data"]; fallback: { [key: string]: EnsIdentity }; - initialVoterData:any + initialVoterData: any }; -const OrchestratorsPage = ({ orchestrators, protocol , initialVoterData}: PageProps) => { +const OrchestratorsPage = ({ orchestrators, protocol, initialVoterData }: PageProps) => { return ( <> @@ -58,54 +61,62 @@ const OrchestratorsPage = ({ orchestrators, protocol , initialVoterData}: PagePr {(process.env.NEXT_PUBLIC_NETWORK == "MAINNET" || process.env.NEXT_PUBLIC_NETWORK == "ARBITRUM_ONE") && ( - - - - )} + + + + )} - - - Yield Overview - - - Voting History - - - - - - - - - - - - + {({ selectedIndex, focusedIndex }) => { + let getTabStyle = (index) => ({ + borderBottom: `4px solid ${selectedIndex === index + ? "#6ec08d" + : focusedIndex === index + ? "#141716" + : "#141716" + }` + , + backgroundColor: '#141716', borderWidth: 0, borderBottomWidth: 1 , + paddingBottom:12 + }); + return ( + <> + + + Yield Overview + + + Voting History + + + + + + + + + + + + + + + + ); + }} @@ -181,9 +192,9 @@ export const getStaticProps = async () => { const query = getOrchestratorsVotingHistory(); const response = await getCubeData(query, { type: CUBE_TYPE.SERVER }); - + // Log the response to check the structure of the data - + if (!response || !response[0] || !response[0].data) { return { props: { @@ -191,12 +202,12 @@ export const getStaticProps = async () => { }, }; } - + const data = response[0].data; - + const voterSummaries = getVoterSummaries(data); - - + + const client = getApollo(); const { orchestrators, fallback } = await getOrchestrators(client); const protocol = await getProtocol(client); @@ -205,12 +216,12 @@ export const getStaticProps = async () => { return null; } - + const props: PageProps = { orchestrators: orchestrators.data, protocol: protocol.data, fallback, - initialVoterData:voterSummaries + initialVoterData: voterSummaries }; return { From 37754959cccab4a0f2a34521b29606935fd0de80 Mon Sep 17 00:00:00 2001 From: Shrey kalra Date: Thu, 10 Apr 2025 16:36:02 +0530 Subject: [PATCH 9/9] final changes --- components/VotingHistoryView/index.tsx | 66 ++++++++++++++++++-------- pages/index.tsx | 64 ++++++++++++++++++++++--- pages/orchestrators.tsx | 19 ++++---- 3 files changed, 112 insertions(+), 37 deletions(-) diff --git a/components/VotingHistoryView/index.tsx b/components/VotingHistoryView/index.tsx index 8d6f2829..91eac400 100644 --- a/components/VotingHistoryView/index.tsx +++ b/components/VotingHistoryView/index.tsx @@ -3,6 +3,7 @@ import { Link as A, styled, Flex, + Box, } from "@livepeer/design-system"; import { CUBE_TYPE, getCubeData } from "cube/cube-client"; import { getAccountVotingHistory } from "cube/queryGenrator"; @@ -11,6 +12,7 @@ import { useEffect, useState } from "react"; import { QuestionMarkCircledIcon } from "@radix-ui/react-icons"; import Spinner from "@components/Spinner"; import Image from "next/image"; +import { ExplorerTooltip } from "@components/ExplorerTooltip"; @@ -158,9 +160,21 @@ const Index = () => {
PROPOSALS VOTED ON
-
- -
+ + The total number of governance proposals this orchestrator has participated in by casting a vote. + + } + > + + + +
{proposalVotedOn}
@@ -168,11 +182,23 @@ const Index = () => {
VOTING TURNOUT
-
- -
+ + The percentage of total governance proposals this orchestrator voted on, showing how actively they participate in protocol decisions. + + } + > + + + +
-
{votingTurnOut}
+
{votingTurnOut}%
@@ -187,20 +213,20 @@ const Index = () => {
Proposed by livepeer.eth
{el["LivepeerProposalStatus.status"]}
- -
- {shortenAddress(el['LivepeerProposalStatus.voter'])} -
- - +
+
+ {shortenAddress(el['LivepeerProposalStatus.voter'])} +
+ +
-
+
) })} diff --git a/pages/index.tsx b/pages/index.tsx index fbb1ee4f..241f8b40 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -13,10 +13,6 @@ import { Container, Flex, Heading, - Tabs, - TabsList, - TabsTrigger, - TabsContent, Text, } from "@livepeer/design-system"; import { ArrowRightIcon } from "@modulz/radix-icons"; @@ -40,6 +36,14 @@ import { OrchestratorTabs } from "@lib/orchestrartor"; import { getOrchestratorsVotingHistory } from "cube/queryGenrator"; import { CUBE_TYPE, getCubeData } from "cube/cube-client"; +import { + Tabs, + TabList, + Tab, + TabPanels, + TabPanel, +} from "@reach/tabs"; + const Panel = ({ children }) => ( + {({ selectedIndex, focusedIndex }) => { + let getTabStyle = (index) => ({ + borderBottom: `4px solid ${selectedIndex === index + ? "#6ec08d" + : focusedIndex === index + ? "#141716" + : "#141716" + }` + , + backgroundColor: '#141716', borderWidth: 0, borderBottomWidth: 1 , + paddingBottom:12 + }); + return ( + <> + + + Yield Overview + + + Voting History + + + + + + + + + + + + + + + + ); + }} + + + {/* @@ -420,7 +470,7 @@ const Home = ({ orchestrators, events, protocol, initialVoterData }: PageProps) - + */} { const mostRecentVotes = sortedVotes.slice(0, 5).map(vote => vote["LivepeerVoteProposals.voteType"] || null); - const noOfProposalsVotedOn = proposals.length; - const noOfVotesCasted = proposals.reduce((acc, vote) => acc + parseInt(vote["LivepeerVoteProposals.numOfVoteCasted"], 10), 0); + const noOfProposalsVotedOn =Number(proposals[0]['LivepeerVoteProposals.numOfProposals'] || 0); + const noOfVotesCasted = Number(proposals[0]['LivepeerVoteProposals.numOfVoteCasted']|| 0); const votingTurnout = noOfProposalsVotedOn ? noOfVotesCasted / noOfProposalsVotedOn : 0; diff --git a/pages/orchestrators.tsx b/pages/orchestrators.tsx index 0476a882..721d7b98 100644 --- a/pages/orchestrators.tsx +++ b/pages/orchestrators.tsx @@ -79,14 +79,14 @@ const OrchestratorsPage = ({ orchestrators, protocol, initialVoterData }: PagePr {({ selectedIndex, focusedIndex }) => { let getTabStyle = (index) => ({ borderBottom: `4px solid ${selectedIndex === index - ? "#6ec08d" - : focusedIndex === index - ? "#141716" - : "#141716" + ? "#6ec08d" + : focusedIndex === index + ? "#141716" + : "#141716" }` - , - backgroundColor: '#141716', borderWidth: 0, borderBottomWidth: 1 , - paddingBottom:12 + , + backgroundColor: '#141716', borderWidth: 0, borderBottomWidth: 1, + paddingBottom: 12 }); return ( <> @@ -162,9 +162,8 @@ const processVoteProposals = (proposals: VoteProposal[]): VoterSummary => { const mostRecentVotes = sortedVotes.slice(0, 5).map(vote => vote["LivepeerVoteProposals.voteType"] || null); - const noOfProposalsVotedOn = proposals.length; - const noOfVotesCasted = proposals.reduce((acc, vote) => acc + parseInt(vote["LivepeerVoteProposals.numOfVoteCasted"], 10), 0); - + const noOfProposalsVotedOn =Number(proposals[0]['LivepeerVoteProposals.numOfProposals'] || 0); + const noOfVotesCasted = Number(proposals[0]['LivepeerVoteProposals.numOfVoteCasted']|| 0); const votingTurnout = noOfProposalsVotedOn ? noOfVotesCasted / noOfProposalsVotedOn : 0; return {