diff --git a/governance/cypress/cypress/tasks/changePeriod.js b/governance/cypress/cypress/tasks/changePeriod.js
index ad95e98d1..9b8fe9812 100644
--- a/governance/cypress/cypress/tasks/changePeriod.js
+++ b/governance/cypress/cypress/tasks/changePeriod.js
@@ -16,6 +16,7 @@ export async function changePeriod({ council, period }) {
const electionId = await proxy.spartan.connect(signer).getEpochIndex();
if (period === 'admin') {
+ console.log(block.timestamp);
await proxy[council]
.connect(signer)
.Epoch_setEpochDates(
diff --git a/governance/ui/src/components/CouncilInformation/CouncilInformation.tsx b/governance/ui/src/components/CouncilInformation/CouncilInformation.tsx
index c13eb61ef..522e38c29 100644
--- a/governance/ui/src/components/CouncilInformation/CouncilInformation.tsx
+++ b/governance/ui/src/components/CouncilInformation/CouncilInformation.tsx
@@ -1,4 +1,4 @@
-import { Button, Flex, Heading, Icon, Text } from '@chakra-ui/react';
+import { Flex, Heading, Icon, Text } from '@chakra-ui/react';
import councils, { CouncilSlugs } from '../../utils/councils';
import { CouncilImage } from '../CouncilImage';
import { Link } from 'react-router-dom';
@@ -56,8 +56,16 @@ export default function CouncilInformation({ activeCouncil }: { activeCouncil: C
>
Stipends: {council?.stipends}/month
-
-
-
+
+
diff --git a/governance/ui/src/components/CouncilMembers/CouncilMembers.tsx b/governance/ui/src/components/CouncilMembers/CouncilMembers.tsx
index d8fa06fb3..3bad09517 100644
--- a/governance/ui/src/components/CouncilMembers/CouncilMembers.tsx
+++ b/governance/ui/src/components/CouncilMembers/CouncilMembers.tsx
@@ -20,6 +20,7 @@ import { ArrowUpDownIcon } from '@chakra-ui/icons';
import SortArrows from '../SortArrows/SortArrows';
import { useGetCouncilMembers, useGetUserDetailsQuery } from '../../queries';
import TableLoading from '../TableLoading/TableLoading';
+import { sortUsers } from '../../utils/sort-users';
export default function CouncilMembers({ activeCouncil }: { activeCouncil: CouncilSlugs }) {
const [sortConfig, setSortConfig] = useState<[boolean, string]>([false, 'start']);
@@ -34,29 +35,8 @@ export default function CouncilMembers({ activeCouncil }: { activeCouncil: Counc
const nextEpoch = calculateNextEpoch(councilSchedule);
const sortedNominees = useMemo(() => {
- // TODO @dev
- // Sort user by voting power and add a place key to the object
- if (!!councilMemberDetails?.length) {
- if (sortConfig[1] === 'ranking') {
- return councilMemberDetails.reverse();
- }
- if (sortConfig[1] === 'name') {
- return councilMemberDetails.sort((a, b) => {
- if (a.username && b.username) {
- return sortConfig[0]
- ? a.username.localeCompare(b.username)
- : a.username.localeCompare(b.username) * -1;
- } else {
- return sortConfig[0]
- ? a?.address.localeCompare(b.address)
- : a?.address.localeCompare(b.address) * -1;
- }
- });
- }
- return councilMemberDetails;
- }
- return [];
- }, [sortConfig, councilMemberDetails]);
+ return sortUsers(activeCouncil, '', sortConfig, councilMemberDetails);
+ }, [sortConfig, councilMemberDetails, activeCouncil]);
return (
{
setSortConfig([!sortConfig[0], 'votes']);
- // sortedNominees = sortedNominees.sort((a, b) => {
- // TODO implement sorting for most votes when subgraph is ready
- // });
}}
>
Votes {sortConfig[1] === 'votes' && }
@@ -146,9 +123,6 @@ export default function CouncilMembers({ activeCouncil }: { activeCouncil: Counc
px="6"
onClick={() => {
setSortConfig([!sortConfig[0], 'votingPower']);
- // sortedNominees = sortedNominees.sort((a, b) => {
- // TODO implement sorting for most votes when subgraph is ready
- // });
}}
>
Voting Power{' '}
diff --git a/governance/ui/src/components/CouncilNominees/CouncilNominees.tsx b/governance/ui/src/components/CouncilNominees/CouncilNominees.tsx
index c0bb23ef2..4a6715258 100644
--- a/governance/ui/src/components/CouncilNominees/CouncilNominees.tsx
+++ b/governance/ui/src/components/CouncilNominees/CouncilNominees.tsx
@@ -23,7 +23,6 @@ import UserTableView from '../UserTableView/UserTableView';
import { useGetNomineesDetails } from '../../queries/useGetNomineesDetails';
import { useGetCurrentPeriod } from '../../queries/useGetCurrentPeriod';
import { useMemo, useState } from 'react';
-import { utils } from 'ethers';
import SortArrows from '../SortArrows/SortArrows';
import { useNetwork, useWallet } from '../../queries/useWallet';
import { CouncilImage } from '../CouncilImage';
@@ -32,6 +31,7 @@ import { CloseIcon } from '@chakra-ui/icons';
import { useVoteContext } from '../../context/VoteContext';
import { useGetEpochIndex, useGetHistoricalVotes } from '../../queries';
import { getVoteSelectionState } from '../../utils/localstorage';
+import { sortUsers } from '../../utils/sort-users';
export default function CouncilNominees({ activeCouncil }: { activeCouncil: CouncilSlugs }) {
const [search, setSearch] = useState('');
@@ -55,53 +55,10 @@ export default function CouncilNominees({ activeCouncil }: { activeCouncil: Coun
);
const council = councils.find((council) => council.slug === activeCouncil);
const epoch = calculateNextEpoch(councilSchedule, nextEpochDuration);
- const votesForCouncil = votes && votes[activeCouncil];
const sortedNominees = useMemo(() => {
- if (councilNomineesDetails?.length && votesForCouncil) {
- return councilNomineesDetails
- .map((nominee) => {
- const vote = votesForCouncil.find(
- (vote) =>
- epochId === vote.id && vote.voter.toLowerCase() === nominee.address.toLowerCase()
- );
- if (vote) return (nominee = { ...nominee, vote });
- return nominee;
- })
- .filter((nominee) => {
- if (utils.isAddress(search)) {
- return nominee.address.toLowerCase().includes(search);
- }
- if (search) {
- if (nominee.username) {
- return nominee.username.toLowerCase().includes(search);
- } else {
- return nominee.address.toLowerCase().includes(search);
- }
- }
- return true;
- })
- .sort((a, b) => {
- if (sortConfig[1] === 'name') {
- if (a.username && b.username) {
- return sortConfig[0]
- ? a.username.localeCompare(b.username)
- : a.username.localeCompare(b.username) * -1;
- }
- if (a.username && !b.username) {
- return -1;
- } else if (b.username && !a.username) {
- return 1;
- }
- return sortConfig[0]
- ? a.address.localeCompare(b.address)
- : a.address.localeCompare(b.address) * -1;
- }
- return 0;
- });
- }
- return [];
- }, [search, councilNomineesDetails, sortConfig, votesForCouncil, epochId]);
+ return sortUsers(activeCouncil, search, sortConfig, councilNomineesDetails, votes);
+ }, [search, councilNomineesDetails, sortConfig, activeCouncil, votes]);
return (
{
- setSortConfig([!sortConfig[0], 'ranking']);
- // sortedNominees = sortedNominees.sort((a, b) => {
- // TODO implement sorting for most votes when subgraph is ready
- // });
+ setSortConfig([true, 'ranking']);
}}
>
N°{' '}
@@ -232,9 +186,6 @@ export default function CouncilNominees({ activeCouncil }: { activeCouncil: Coun
pl="6"
onClick={() => {
setSortConfig([!sortConfig[0], 'votes']);
- // sortedNominees = sortedNominees.sort((a, b) => {
- // TODO implement sorting for most votes when subgraph is ready
- // });
}}
>
Votes {sortConfig[1] === 'votes' && }
@@ -248,9 +199,6 @@ export default function CouncilNominees({ activeCouncil }: { activeCouncil: Coun
pl="6"
onClick={() => {
setSortConfig([!sortConfig[0], 'votingPower']);
- // sortedNominees = sortedNominees.sort((a, b) => {
- // TODO implement sorting for most votes when subgraph is ready
- // });
}}
>
Voting Power{' '}
@@ -264,9 +212,9 @@ export default function CouncilNominees({ activeCouncil }: { activeCouncil: Coun
{!!sortedNominees?.length ? (
- sortedNominees.map((councilNominee, index) => (
+ sortedNominees.map((councilNominee) => (
))
) : isLoading ? (
@@ -289,3 +238,14 @@ export default function CouncilNominees({ activeCouncil }: { activeCouncil: Coun
);
}
+
+function totalVotingPowerForCouncil(council: CouncilSlugs) {
+ switch (council) {
+ case 'spartan':
+ return 'totalVotingPowerSpartan';
+ case 'ambassador':
+ return 'totalVotingPowerAmbassador';
+ case 'treasury':
+ return 'totalVotingPowerTreasury';
+ }
+}
diff --git a/governance/ui/src/components/UserTableView/UserTableView.tsx b/governance/ui/src/components/UserTableView/UserTableView.tsx
index eef5cef5c..71b52d192 100644
--- a/governance/ui/src/components/UserTableView/UserTableView.tsx
+++ b/governance/ui/src/components/UserTableView/UserTableView.tsx
@@ -9,52 +9,22 @@ import { prettyString } from '@snx-v3/format';
import { useGetEpochIndex, useGetUserBallot, useNetwork } from '../../queries';
import { getVoteSelectionState } from '../../utils/localstorage';
import { useVoteContext } from '../../context/VoteContext';
-
-function renderCorrectBorder(
- column: 'place' | 'name' | 'votes' | 'power' | 'badge',
- position: 'left' | 'right' | 'bottom',
- period: string | undefined,
- isSelected: boolean
-) {
- if (column === 'place') {
- if (position === 'left') {
- return isSelected ? '1px solid' : '';
- } else if (position === 'bottom') {
- return isSelected ? '1px solid' : '';
- }
- } else if (column === 'name') {
- if (position === 'left') {
- if (period === '2') {
- return '';
- }
- if (period === '0') {
- return '';
- }
- return isSelected ? '1px solid' : '';
- } else if (position === 'bottom') {
- return isSelected ? '1px solid' : '';
- }
- } else if (column === 'votes') {
- if (position === 'bottom') return isSelected ? '1px solid' : '';
- } else if (column === 'power') {
- if (position === 'bottom') return isSelected ? '1px solid' : '';
- } else if (column === 'badge') {
- if (position === 'bottom') return isSelected ? '1px solid' : '';
- if (position === 'right') return isSelected ? '1px solid' : '';
- }
-}
+import { BigNumber } from 'ethers';
+import { renderCorrectBorder } from '../../utils/table-border';
export default function UserTableView({
user,
activeCouncil,
place,
isSelectedForVoting,
+ totalVotingPower,
}: {
isSelectedForVoting?: boolean;
- place: number;
+ place?: number;
user: GetUserDetails;
activeCouncil: CouncilSlugs;
isNomination?: boolean;
+ totalVotingPower?: BigNumber;
}) {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
@@ -72,6 +42,9 @@ export default function UserTableView({
activeCouncil
);
const voteAddressState = typeof networkForState === 'string' ? networkForState : '';
+ const totalVotingPowerPercentage = totalVotingPower
+ ? user.voteResult?.votePower.mul(100).div(totalVotingPower).toNumber().toFixed(2)
+ : 'N/A';
return (
- {place < 10 ? (
+ {place === undefined ? (
+ '-'
+ ) : place <
+ (activeCouncil === 'spartan' ? 8 : activeCouncil === 'ambassador' ? 5 : 4) ? (
- {place + 1}
+ {place}
) : (
@@ -139,7 +115,7 @@ export default function UserTableView({
fontSize="sm"
fontWeight={700}
>
- 0
+ {user.voteResult?.votesReceived || 0}
)}
{councilIsInAdminOrVoting && (
@@ -151,7 +127,10 @@ export default function UserTableView({
fontSize="sm"
fontWeight={700}
>
- 0
+ {totalVotingPowerPercentage ? totalVotingPowerPercentage + '%' : 'N/A'}
+
+ {totalVotingPowerPercentage ? user.voteResult?.votePower.toString() : '—'}
+
)}
{councilPeriod === '2' && (
@@ -203,20 +182,6 @@ export default function UserTableView({
)}
- {councilPeriod === '0' && (
-
-
- 0
-
- |
- )}
);
}
diff --git a/governance/ui/src/queries/useGetEpochIndex.ts b/governance/ui/src/queries/useGetEpochIndex.ts
index 14663043f..e2158b874 100644
--- a/governance/ui/src/queries/useGetEpochIndex.ts
+++ b/governance/ui/src/queries/useGetEpochIndex.ts
@@ -11,7 +11,7 @@ export function useGetEpochIndex(council: CouncilSlugs) {
queryKey: ['epochId', council, network?.id],
queryFn: async () => {
return await getCouncilContract(council)
- .connect(motherShipProvider(network?.id || 13001))
+ .connect(motherShipProvider(network?.id || 2192))
.getEpochIndex();
},
staleTime: 900000,
diff --git a/governance/ui/src/queries/useGetHistoricalVotes.ts b/governance/ui/src/queries/useGetHistoricalVotes.ts
index 9445234dc..ed74c6d79 100644
--- a/governance/ui/src/queries/useGetHistoricalVotes.ts
+++ b/governance/ui/src/queries/useGetHistoricalVotes.ts
@@ -1,11 +1,11 @@
import { useQuery } from '@tanstack/react-query';
-import { useNetwork } from './useWallet';
import councils, { CouncilSlugs } from '../utils/councils';
+import { BigNumber } from 'ethers';
-const testnetURL = 'https://api.synthetix.io/v3/snax-testnet/votes';
+// const testnetURL = 'https://api.synthetix.io/v3/snax-testnet/votes';
const mainnetURL = 'https://api.synthetix.io/v3/snax/votes';
-export interface Vote {
+interface VoteRaw {
eventName: string;
chainId: number;
epochId: number;
@@ -13,40 +13,218 @@ export interface Vote {
blockTimestamp: number;
id: string;
transactionHash: string;
- votingPower: string;
+ votingPower?: string;
blockNumber: number;
- candidates: string[];
+ candidates?: string[];
contract: string;
}
+interface VoteParsed {
+ eventName: string;
+ chainId: number;
+ epochId: number;
+ voter: string;
+ blockTimestamp: number;
+ id: string;
+ transactionHash: string;
+ votingPower: BigNumber;
+ blockNumber: number;
+ candidates?: string[];
+ contract: string;
+}
+
+export interface Candidate {
+ votesReceived: number;
+ votePower: BigNumber;
+ candidate: string;
+}
+
+export interface VotesResult {
+ spartan: Record;
+ ambassador: Record;
+ treasury: Record;
+ totalVotingPowerSpartan: BigNumber;
+ totalVotingPowerAmbassador: BigNumber;
+ totalVotingPowerTreasury: BigNumber;
+}
+
export function useGetHistoricalVotes() {
- const { network } = useNetwork();
return useQuery({
- queryKey: ['historical-votes', network?.id],
+ queryKey: ['historical-votes'],
queryFn: async () => {
- try {
- const res = await fetch(network?.id !== 2192 ? mainnetURL : testnetURL);
- let votesRaw: Record = await res.json();
- if (!('spartan' in votesRaw)) {
- votesRaw = { spartan: votesRaw[0], ambassador: [], treasury: [] };
- }
- return councils.reduce(
- (cur, next) => {
- cur[next.slug] = votesRaw[next.slug].sort((a, b) => {
- if (a.epochId === b.epochId) {
- return a.blockTimestamp - b.blockTimestamp;
- }
- return a.epochId - b.epochId;
- });
- return cur;
- },
- {} as Record
- );
- } catch (err) {
- console.error(err);
- return { spartan: [], ambassador: [], treasury: [] } as Record;
- }
+ const res = await fetch(mainnetURL);
+ const votesRaw: Record = await res.json();
+ // TODO @dev remove for prod
+ // if (window.location.host.includes('localhost'))
+ // votesRaw.spartan = votesRaw.spartan.concat(fakeData);
+ const sortedEvents = councils.reduce(
+ (cur, next) => {
+ cur[next.slug] = votesRaw[next.slug].sort((a, b) => {
+ if (a.epochId === b.epochId) {
+ return a.blockTimestamp - b.blockTimestamp;
+ }
+ return a.epochId - b.epochId;
+ });
+ return cur;
+ },
+ {} as Record
+ );
+ const spartan = parseVotes(sortedEvents.spartan);
+ const ambassador = parseVotes(sortedEvents.ambassador);
+ const treasury = parseVotes(sortedEvents.treasury);
+
+ return {
+ spartan,
+ ambassador,
+ treasury,
+ totalVotingPowerSpartan: Object.keys(spartan).reduce(
+ (cur, candidate) => cur.add(spartan[candidate].votePower),
+ BigNumber.from(0)
+ ),
+ totalVotingPowerAmbassador: Object.keys(ambassador).reduce(
+ (cur, candidate) => cur.add(ambassador[candidate].votePower),
+ BigNumber.from(0)
+ ),
+ totalVotingPowerTreasury: Object.keys(treasury).reduce(
+ (cur, candidate) => cur.add(treasury[candidate].votePower),
+ BigNumber.from(0)
+ ),
+ };
},
staleTime: 900000,
});
}
+
+const parseVotes = (votes: VoteRaw[]) => {
+ return votes
+ .reduce((cur, next) => {
+ if (next.eventName === 'VoteWithdrawn') {
+ const previousVoteIndex = cur.findIndex((vote) => {
+ return vote.contract === next.contract && vote.voter === next.voter;
+ });
+ if (previousVoteIndex !== -1) {
+ cur.splice(previousVoteIndex, 1);
+ } else {
+ console.error('Could not find previous vote', cur[next.epochId], next);
+ }
+ } else {
+ const previousVoteIndex = cur.findIndex(
+ (vote) => vote.contract === next.contract && vote.voter === next.voter
+ );
+ if (previousVoteIndex !== -1) {
+ cur.splice(previousVoteIndex, 1);
+ }
+ cur.push({ ...next, votingPower: BigNumber.from(next.votingPower) });
+ }
+
+ return cur;
+ }, [] as VoteParsed[])
+ .reduce(
+ (cur, next) => {
+ const candidate = next.candidates ? next.candidates[0].toLowerCase() : '';
+ if (cur[candidate]) {
+ cur[candidate] = {
+ ...cur,
+ votePower: cur[candidate].votePower.add(next.votingPower),
+ votesReceived: cur[candidate].votesReceived + 1,
+ candidate,
+ };
+ } else {
+ cur[candidate] = { candidate, votePower: next.votingPower, votesReceived: 1 };
+ }
+
+ return cur;
+ },
+ {} as Record
+ );
+};
+
+// const fakeData: VoteRaw[] = [
+// {
+// eventName: 'VoteRecorded',
+// chainId: 2192,
+// epochId: 0,
+// voter: '0x48914229deDd5A9922f44441ffCCfC2Cb7856Ee9',
+// blockTimestamp: 1724766356000,
+// id: '0000595468-a8656-000000',
+// transactionHash: '0x3a995d6f45cd0f00c1a24aa4f9e4c816d39056c9dcb2ab61f9a864d6880b058b',
+// votingPower: '10',
+// blockNumber: 595468,
+// candidates: ['0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'],
+// contract: '0xbc85f11300a8ef619592fd678418ec4ef26fbdfd',
+// },
+// {
+// eventName: 'VoteRecorded',
+// chainId: 2192,
+// epochId: 0,
+// voter: '0x47872B16557875850a02C94B28d959515F894913',
+// blockTimestamp: 1724766356000,
+// id: '0000595468-a8656-000000',
+// transactionHash: '0x3a995d6f45cd0f00c1a24aa4f9e4c816d39056c9dcb2ab61f9a864d6880b058b',
+// votingPower: '10',
+// blockNumber: 595469,
+// candidates: ['0xc3Cf311e04c1f8C74eCF6a795Ae760dc6312F345'],
+// contract: '0xbc85f11300a8ef619592fd678418ec4ef26fbdfd',
+// },
+// {
+// eventName: 'VoteWithdrawn',
+// chainId: 2192,
+// epochId: 0,
+// voter: '0x48914229deDd5A9922f44441ffCCfC2Cb7856Ee9',
+// blockTimestamp: 1724766356000,
+// id: '0000595468-a8656-000000',
+// transactionHash: '0x3a995d6f45cd0f00c1a24aa4f9e4c816d39056c9dcb2ab61f9a864d6880b058b',
+// blockNumber: 5954610,
+// contract: '0xbc85f11300a8ef619592fd678418ec4ef26fbdfd',
+// },
+// {
+// eventName: 'VoteWithdrawn',
+// chainId: 2192,
+// epochId: 0,
+// voter: '0x47872B16557875850a02C94B28d959515F894913',
+// blockTimestamp: 1724766356000,
+// id: '0000595468-a8656-000000',
+// transactionHash: '0x3a995d6f45cd0f00c1a24aa4f9e4c816d39056c9dcb2ab61f9a864d6880b058b',
+// blockNumber: 595469,
+// contract: '0xbc85f11300a8ef619592fd678418ec4ef26fbdfd',
+// },
+// {
+// eventName: 'VoteRecorded',
+// chainId: 2192,
+// epochId: 0,
+// voter: '0x48914229deDd5A9922f44441ffCCfC2Cb7856Ee9',
+// blockTimestamp: 1724766356000,
+// id: '0000595468-a8656-000000',
+// transactionHash: '0x3a995d6f45cd0f00c1a24aa4f9e4c816d39056c9dcb2ab61f9a864d6880b058b',
+// votingPower: '10',
+// blockNumber: 595468,
+// candidates: ['0x98Ab20307fdABa1ce8b16d69d22461c6dbe85459'],
+// contract: '0xbc85f11300a8ef619592fd678418ec4ef26fbdfd',
+// },
+// {
+// eventName: 'VoteRecorded',
+// chainId: 2192,
+// epochId: 0,
+// voter: '0x47872B16557875850a02C94B28d959515F894913',
+// blockTimestamp: 1724766356000,
+// id: '0000595468-a8656-000000',
+// transactionHash: '0x3a995d6f45cd0f00c1a24aa4f9e4c816d39056c9dcb2ab61f9a864d6880b058b',
+// votingPower: '10',
+// blockNumber: 595469,
+// candidates: ['0x8F876c97AD1090004dC28294237003e571C76Ba7'],
+// contract: '0xbc85f11300a8ef619592fd678418ec4ef26fbdfd',
+// },
+// {
+// eventName: 'VoteRecorded',
+// chainId: 2192,
+// epochId: 0,
+// voter: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
+// blockTimestamp: 1724766356000,
+// id: '0000595468-a8656-000000',
+// transactionHash: '0x3a995d6f45cd0f00c1a24aa4f9e4c816d39056c9dcb2ab61f9a864d6880b058b',
+// votingPower: '10',
+// blockNumber: 595499,
+// candidates: ['0x8F876c97AD1090004dC28294237003e571C76Ba7'],
+// contract: '0xbc85f11300a8ef619592fd678418ec4ef26fbdfd',
+// },
+// ];
diff --git a/governance/ui/src/queries/useGetNextElectionSettings.ts b/governance/ui/src/queries/useGetNextElectionSettings.ts
index 57375d1b1..3729beee3 100644
--- a/governance/ui/src/queries/useGetNextElectionSettings.ts
+++ b/governance/ui/src/queries/useGetNextElectionSettings.ts
@@ -10,7 +10,7 @@ export function useGetNextElectionSettings(council: CouncilSlugs) {
queryKey: ['next-epoch-settings', council],
queryFn: async () => {
const schedule = await getCouncilContract(council)
- .connect(motherShipProvider(network?.id || 13001))
+ .connect(motherShipProvider(network?.id || 2192))
.getNextElectionSettings();
return Number(schedule.epochDuration.toString()) as number | undefined;
},
diff --git a/governance/ui/src/queries/useGetUserDetailsQuery.ts b/governance/ui/src/queries/useGetUserDetailsQuery.ts
index 226bb353a..9c6d478c9 100644
--- a/governance/ui/src/queries/useGetUserDetailsQuery.ts
+++ b/governance/ui/src/queries/useGetUserDetailsQuery.ts
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query';
import { motherShipProvider } from '../utils/providers';
import { profileContract } from '../utils/contracts';
import { utils } from 'ethers';
-import { Vote } from './useGetHistoricalVotes';
+import { Candidate } from './useGetHistoricalVotes';
export type GetUserDetails = {
address: string;
@@ -26,7 +26,8 @@ export type GetUserDetails = {
delegationPitch: string;
github: string;
council?: string;
- vote?: Vote;
+ voteResult?: Candidate;
+ place?: number;
};
type UserPitch = {
diff --git a/governance/ui/src/utils/sort-users.ts b/governance/ui/src/utils/sort-users.ts
new file mode 100644
index 000000000..6ea98e1c4
--- /dev/null
+++ b/governance/ui/src/utils/sort-users.ts
@@ -0,0 +1,103 @@
+import { BigNumber, utils } from 'ethers';
+import { Candidate, GetUserDetails } from '../queries';
+import { CouncilSlugs } from './councils';
+
+interface Votes {
+ spartan: Record;
+ ambassador: Record;
+ treasury: Record;
+ totalVotingPowerSpartan: BigNumber;
+ totalVotingPowerAmbassador: BigNumber;
+ totalVotingPowerTreasury: BigNumber;
+}
+
+export const sortUsers = (
+ activeCouncil: CouncilSlugs,
+ search: string,
+ sortConfig: [boolean, string],
+ councilNomineesDetails?: GetUserDetails[],
+ votes?: Votes
+) => {
+ if (councilNomineesDetails?.length && votes) {
+ const candidatesByVotePowerRanking = votes
+ ? Object.keys(votes[activeCouncil]).sort((a, b) => {
+ return votes[activeCouncil][b].votePower
+ .sub(votes[activeCouncil][a].votePower)
+ .toNumber();
+ })
+ : [];
+ return councilNomineesDetails
+ .map((nominee) => {
+ if (votes[activeCouncil][nominee.address.toLowerCase()]) {
+ return {
+ ...nominee,
+ voteResult: votes[activeCouncil][nominee.address.toLowerCase()],
+ place:
+ candidatesByVotePowerRanking.findIndex(
+ (candidate) => candidate.toLowerCase() === nominee.address.toLowerCase()
+ ) + 1,
+ };
+ }
+ return nominee;
+ })
+ .filter((nominee) => {
+ if (utils.isAddress(search)) {
+ return nominee.address.toLowerCase().includes(search);
+ }
+ if (search) {
+ if (nominee.username) {
+ return nominee.username.toLowerCase().includes(search);
+ } else {
+ return nominee.address.toLowerCase().includes(search);
+ }
+ }
+ return true;
+ })
+ .sort((a, b) => {
+ if (sortConfig[1] === 'name') {
+ if (a.username && b.username) {
+ return sortConfig[0]
+ ? a.username.localeCompare(b.username)
+ : a.username.localeCompare(b.username) * -1;
+ }
+ if (a.username && !b.username) {
+ return -1;
+ } else if (b.username && !a.username) {
+ return 1;
+ }
+ return sortConfig[0]
+ ? a.address.localeCompare(b.address)
+ : a.address.localeCompare(b.address) * -1;
+ }
+ if (sortConfig[1] === 'votingPower') {
+ if (sortConfig[0]) {
+ if (!b.voteResult?.votePower) return -1;
+ return b.voteResult?.votePower
+ .sub(a.voteResult?.votePower || BigNumber.from(0))
+ .toNumber();
+ } else {
+ if (!b.voteResult?.votePower) return 1;
+ return b.voteResult?.votePower
+ .sub(a.voteResult?.votePower || BigNumber.from(0))
+ .mul(-1)
+ .toNumber();
+ }
+ }
+ if (sortConfig[1] === 'votes') {
+ if (sortConfig[0]) {
+ return (b.voteResult?.votesReceived || 0) - (a.voteResult?.votesReceived || 0);
+ } else {
+ return ((b.voteResult?.votesReceived || 0) - (a.voteResult?.votesReceived || 0)) * -1;
+ }
+ }
+ if (sortConfig[1] === 'ranking') {
+ if (!b.voteResult?.votePower) return -1;
+ return b.voteResult?.votePower
+ .sub(a.voteResult?.votePower || BigNumber.from(0))
+ .toNumber();
+ }
+ return 0;
+ });
+ }
+ return [];
+};
diff --git a/governance/ui/src/utils/table-border.ts b/governance/ui/src/utils/table-border.ts
new file mode 100644
index 000000000..8bbf10591
--- /dev/null
+++ b/governance/ui/src/utils/table-border.ts
@@ -0,0 +1,33 @@
+export function renderCorrectBorder(
+ column: 'place' | 'name' | 'votes' | 'power' | 'badge',
+ position: 'left' | 'right' | 'bottom',
+ period: string | undefined,
+ isSelected: boolean
+) {
+ if (column === 'place') {
+ if (position === 'left') {
+ return isSelected ? '1px solid' : '';
+ } else if (position === 'bottom') {
+ return isSelected ? '1px solid' : '';
+ }
+ } else if (column === 'name') {
+ if (position === 'left') {
+ if (period === '2') {
+ return '';
+ }
+ if (period === '0') {
+ return '';
+ }
+ return isSelected ? '1px solid' : '';
+ } else if (position === 'bottom') {
+ return isSelected ? '1px solid' : '';
+ }
+ } else if (column === 'votes') {
+ if (position === 'bottom') return isSelected ? '1px solid' : '';
+ } else if (column === 'power') {
+ if (position === 'bottom') return isSelected ? '1px solid' : '';
+ } else if (column === 'badge') {
+ if (position === 'bottom') return isSelected ? '1px solid' : '';
+ if (position === 'right') return isSelected ? '1px solid' : '';
+ }
+}
diff --git a/governance/ui/webpack.config.js b/governance/ui/webpack.config.js
index 9e7265de9..e3636b5c4 100644
--- a/governance/ui/webpack.config.js
+++ b/governance/ui/webpack.config.js
@@ -179,7 +179,7 @@ module.exports = {
'process.env.BOARDROOM_KEY': JSON.stringify(
process.env.BOARDROOM_KEY ?? 'd9abe7a1ab45ace58e6bd91bb9771586'
),
- 'process.env.CI': JSON.stringify(process.env.CI ?? false),
+ 'process.env.CI': JSON.stringify(process.env.CI ?? 'false'),
})
),
resolve: {