diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx
index 191964fd..4edf2cd8 100644
--- a/src/components/Banner.tsx
+++ b/src/components/Banner.tsx
@@ -3,12 +3,15 @@ import { Link } from 'react-router-dom';
import classes from '../pages/PageStyles.module.css';
import { useGameManager } from '../hooks/useGameMangers';
-import { GameStatus } from '../types/common';
+import { ContestStatus, GameStatus, VotingStage } from '../types/common';
import { ReactNode } from 'react';
import { useMobile } from '../hooks/useBreakpoint';
+import { useVoting } from '../hooks/useVoting';
+import { secondsToLongDate } from '../utils/time';
export const Banner = () => {
const { gm, isLoadingGm, gmError } = useGameManager();
+ const { contestStatus, contest, votingStage } = useVoting();
const isMobile = useMobile();
if (isLoadingGm) return ;
@@ -119,12 +122,67 @@ export const Banner = () => {
);
}
- if (gm.currentRound.gameStatus === GameStatus.Completed) {
+ const roundOver = gm.currentRound.gameStatus === GameStatus.Completed;
+
+ if (
+ (roundOver && contestStatus === ContestStatus.Populating) ||
+ (roundOver && contestStatus === ContestStatus.None)
+ ) {
return (
+ Preview Portfolios
+
+ }
+ infoBtn={
+
+ }
+ />
+
+ );
+ }
+
+ const voteIsIdle =
+ roundOver &&
+ contestStatus === ContestStatus.None &&
+ votingStage === VotingStage.None;
+
+ const voteIsSetup =
+ roundOver &&
+ contestStatus === ContestStatus.Voting &&
+ votingStage === VotingStage.Initiated;
+
+ const voteIsActive =
+ roundOver &&
+ contestStatus === ContestStatus.Voting &&
+ votingStage === VotingStage.Active;
+
+ const voteIsClosed =
+ votingStage >= VotingStage.Closed ||
+ contestStatus >= ContestStatus.Finalized;
+
+ if (voteIsIdle || voteIsSetup) {
+ return (
+
+ {
infoBtn={
+ }
+ />
+
+ );
+ }
+
+ if (voteIsActive) {
+ return (
+
+
+ Vote Now
+
+ }
+ infoBtn={
+
+ }
+ />
+
+ );
+ }
+
+ if (voteIsClosed) {
+ return (
+
+
+ See Results
+
+ }
+ infoBtn={
+
}
/>
diff --git a/src/components/dashboard/ship/PortfolioReport.tsx b/src/components/dashboard/ship/PortfolioReport.tsx
index e77fc917..39ad09d9 100644
--- a/src/components/dashboard/ship/PortfolioReport.tsx
+++ b/src/components/dashboard/ship/PortfolioReport.tsx
@@ -19,7 +19,6 @@ import { GAME_TOKEN } from '../../../constants/gameSetup';
import {
IconChevronDown,
IconChevronUp,
- IconExclamationCircle,
IconExternalLink,
IconSquare,
IconSquareCheck,
@@ -76,8 +75,6 @@ export const PortfolioReport = ({
onReportSubmit?: () => void;
reportData?: ReportData | null;
}) => {
- const theme = useMantineTheme();
-
const form = useForm({
initialValues: defaultValues,
validate: zodResolver(portfolioReportSchema),
@@ -93,15 +90,7 @@ export const PortfolioReport = ({
);
- if (error || !grants)
- return (
- }
- description={error?.message || 'Grant Data failed to load'}
- bg={theme.colors.pink[8]}
- />
- );
+ if (error || !grants) return null;
if (grants.length === 0)
return (
diff --git a/src/components/voting/ConfirmationPanel.tsx b/src/components/voting/ConfirmationPanel.tsx
index 00ebb607..96ed36e3 100644
--- a/src/components/voting/ConfirmationPanel.tsx
+++ b/src/components/voting/ConfirmationPanel.tsx
@@ -267,7 +267,17 @@ export const ConfirmationPanel = ({
);
})}
-
+
Total Vote:{' '}
diff --git a/src/components/voting/PreVoting.tsx b/src/components/voting/PreVoting.tsx
new file mode 100644
index 00000000..08c75f89
--- /dev/null
+++ b/src/components/voting/PreVoting.tsx
@@ -0,0 +1,38 @@
+import { Group, Paper, Text, useMantineTheme } from '@mantine/core';
+import { MainSection, PageTitle } from '../../layout/Sections';
+import { ShipBadge } from '../RoleBadges';
+
+export const PreVoting = () => {
+ const theme = useMantineTheme();
+ return (
+
+
+
+
+
+
+ Voting is not open yet!
+
+
+
+ Stay Tuned!
+
+
+ The voting round for Grant Ships is opened after the allocation round.
+
+
+ How it works
+
+
+ Ships receive funds from a DAO to distribute as grants for projects in
+ one round. At the round's end, DAO members allocate votes to ships
+ based on their performance.
+
+
+ Ships then receive funds in the next round based on the proportion of
+ votes they received.
+
+
+
+ );
+};
diff --git a/src/components/voting/ShipVotingPanel.tsx b/src/components/voting/ShipVotingPanel.tsx
index c15bdf39..1dc5dba1 100644
--- a/src/components/voting/ShipVotingPanel.tsx
+++ b/src/components/voting/ShipVotingPanel.tsx
@@ -1,20 +1,17 @@
import { UseFormReturnType } from '@mantine/form';
import { ShipsCardUI } from '../../types/ui';
import { VotingFormValues } from '../../pages/Vote';
-import { useQuery } from '@tanstack/react-query';
-import { getShipGrants } from '../../queries/getShipGrants';
import { useUserData } from '../../hooks/useUserState';
import { useVoting } from '../../hooks/useVoting';
import { useMemo } from 'react';
-import { getRecentPortfolioReport } from '../../queries/getRecordsByTag';
-import { ADDR } from '../../constants/addresses';
+import { PostedRecord } from '../../queries/getRecordsByTag';
import { formatEther } from 'viem';
import { Avatar, Box, Flex, Paper, Text, useMantineTheme } from '@mantine/core';
import { PortfolioReport } from '../dashboard/ship/PortfolioReport';
import { ContestStatus, ReportStatus, VotingStage } from '../../types/common';
-import { Tag } from '../../constants/tags';
import { VotingFooter } from './VotingFooter';
import { FacilitatorFooter } from './FacilitatorFooter';
+import { DashGrant } from '../../resolvers/grantResolvers';
export const ShipVotingPanel = ({
ship,
@@ -22,7 +19,11 @@ export const ShipVotingPanel = ({
index,
nextStep,
prevStep,
+ grants,
+ recentRecord,
}: {
+ grants: DashGrant[] | null;
+ recentRecord?: PostedRecord | null;
ship: ShipsCardUI;
form: UseFormReturnType<
VotingFormValues,
@@ -32,47 +33,20 @@ export const ShipVotingPanel = ({
nextStep: () => void;
prevStep: () => void;
}) => {
- const {
- data: grants,
- error,
- isLoading: isLoadingGrants,
- } = useQuery({
- queryKey: [`portfolio-${ship.id}`],
- queryFn: () => getShipGrants(ship.id as string),
- enabled: !!ship.id,
- });
-
- const {
- contest,
- contestStatus,
- isLoadingVoting,
- refetchGsVotes,
- votingStage,
- } = useVoting();
+ const { contest, contestStatus, refetchGsVotes, votingStage } = useVoting();
const theme = useMantineTheme();
- const { userData, userLoading } = useUserData();
+ const { userData } = useUserData();
const shipChoiceId = useMemo(() => {
return contest?.choices.find((choice) => choice.shipId === ship.id)?.id;
}, [contest?.choices, ship]);
- const { data: recentRecord, isLoading: isLoadingRecord } = useQuery({
- queryKey: [`ship-portfolio-${ship.id}`],
- queryFn: () =>
- getRecentPortfolioReport(
- `${Tag.ShipSubmitReport}-${ADDR.VOTE_CONTEST}-${ship.id}`
- ),
- enabled: !!ship.id,
- });
-
const totalAmount = formatEther(
BigInt(ship.amtAllocated) +
BigInt(ship.amtAvailable) +
BigInt(ship.amtDistributed)
);
- const isLoading =
- isLoadingRecord || isLoadingVoting || isLoadingGrants || userLoading;
return (
@@ -103,14 +77,14 @@ export const ShipVotingPanel = ({
- {contestStatus === ContestStatus.Populating && !isLoading && (
+ {contestStatus === ContestStatus.Populating && (
{
+ const theme = useMantineTheme();
+ const colors = [
+ theme.colors.blue[5],
+ theme.colors.violet[5],
+ theme.colors.pink[5],
+ ];
+
+ const totalUserVotes = useMemo(() => {
+ return voter.votes.reduce((acc, vote) => {
+ return acc + BigInt(vote?.amount ? vote.amount : 0);
+ }, 0n);
+ }, [voter]);
+
+ const consolidated = useMemo(() => {
+ return choices.map((choice) => {
+ const vote = voter.votes.find((vote) => choice.id === vote.choice.id);
+ return { ...choice, vote };
+ });
+ }, [voter, choices]);
+
+ return (
+
+
+
+
+
+ {consolidated.map((choice, index) => {
+ return (
+
+ );
+ })}
+
+ );
+};
+
+const ShipChoiceVoteBar = ({
+ choice,
+ totalVotes,
+ voteAmount,
+ reason,
+ color,
+ tokenSymbol,
+ didVote,
+}: {
+ choice: CondensedChoiceData;
+ totalVotes: bigint;
+ voteAmount: bigint;
+ reason?: string | null;
+ color: string;
+ tokenSymbol?: string;
+ didVote?: boolean;
+}) => {
+ const votePercentage = formatBigIntPercentage(voteAmount, totalVotes);
+ return (
+
+
+
+
+
+
+ {votePercentage}% Voted ({formatEther(voteAmount)}){' '}
+ {tokenSymbol || ''}
+
+
+
+ }
+ showLabel={}
+ classNames={{
+ root: classes.embedTextBox,
+ control: classes.embedTextControl,
+ }}
+ maxHeight={24}
+ >
+ {!didVote && (
+
+ Did not vote
+
+ )}
+ {didVote && !reason && (
+
+ No reason given
+
+ )}
+ {didVote && reason && (
+
+ {reason}
+
+ )}
+
+
+ );
+};
diff --git a/src/components/voting/VoteResultsPanel.tsx b/src/components/voting/VoteResultsPanel.tsx
new file mode 100644
index 00000000..ad7c776a
--- /dev/null
+++ b/src/components/voting/VoteResultsPanel.tsx
@@ -0,0 +1,188 @@
+import {
+ Avatar,
+ Box,
+ Divider,
+ Flex,
+ Group,
+ Progress,
+ Stack,
+ Text,
+ useMantineTheme,
+} from '@mantine/core';
+import { useVoting } from '../../hooks/useVoting';
+import { ShipsCardUI } from '../../types/ui';
+import { useMemo } from 'react';
+import { MainSection, PageTitle } from '../../layout/Sections';
+import { formatBigIntPercentage } from '../../utils/helpers';
+import { formatEther } from 'viem';
+import { CondensedChoiceData } from '../../pages/Vote';
+import { getContestVoters } from '../../queries/getVoters';
+import { VoteCard } from './VoteCard';
+import { useQuery } from '@tanstack/react-query';
+
+export const VoteResultsPanel = ({ ships }: { ships: ShipsCardUI[] }) => {
+ const { contest, userVotes, tokenData } = useVoting();
+
+ const theme = useMantineTheme();
+
+ const consolidated = useMemo(() => {
+ if (!ships || !userVotes || !contest) return [];
+
+ return ships.map((ship) => {
+ const shipChoice = contest?.choices.find((c) => c.shipId === ship.id);
+
+ const userVote = userVotes.find((v) => v.choice_id === shipChoice?.id);
+
+ return { ...ship, vote: userVote, choice: shipChoice };
+ });
+ }, [ships, userVotes, contest]);
+
+ const totals = useMemo(() => {
+ if (!consolidated || !contest) return null;
+
+ const totalUserVotes =
+ consolidated && consolidated.length > 0
+ ? consolidated.reduce((acc, ship) => {
+ if (!ship.vote) return acc;
+ return acc + BigInt(ship.vote.amount);
+ }, 0n)
+ : 0n;
+ const totalVotes = contest?.choices?.length
+ ? contest?.choices.reduce((acc, choice) => {
+ return acc + BigInt(choice.voteTally);
+ }, 0n)
+ : 0n;
+
+ return {
+ totalUserVotes,
+ totalVotes,
+ };
+ }, [consolidated, contest]);
+
+ const condensed = useMemo(() => {
+ return consolidated?.map((ship) => ({
+ shipImg: ship.imgUrl,
+ shipName: ship.name,
+ id: ship.choice?.id as string,
+ }));
+ }, [consolidated]);
+
+ const colors = [
+ theme.colors.blue[5],
+ theme.colors.violet[5],
+ theme.colors.pink[5],
+ ];
+
+ const hasUserVoted = userVotes && userVotes.length > 0;
+
+ return (
+
+
+
+ {hasUserVoted ? 'Your vote has been submitted!' : 'Voting is Complete!'}
+
+
+ {hasUserVoted && (
+
+
+ Your Vote
+
+ {consolidated.map((ship, index) => {
+ const percentage = totals?.totalUserVotes
+ ? formatBigIntPercentage(
+ BigInt(ship.vote?.amount || 0),
+ totals?.totalUserVotes
+ )
+ : '0';
+ const tokenAmount = formatEther(BigInt(ship.vote?.amount || 0));
+
+ return (
+
+
+
+
+ {ship.name}
+
+
+
+
+ {Number(percentage)}% Voted ({tokenAmount}{' '}
+ {tokenData.tokenSymbol})
+
+
+ );
+ })}
+
+
+ Total:{' '}
+
+ {formatEther(totals?.totalUserVotes || 0n)}{' '}
+ {tokenData.tokenSymbol}
+
+
+ )}
+
+
+ Total Vote Results
+
+ {consolidated?.map((ship, index) => {
+ const percentage = totals?.totalVotes
+ ? formatBigIntPercentage(
+ BigInt(ship.choice?.voteTally),
+ totals?.totalVotes
+ )
+ : '0';
+ const tokenAmount = formatEther(BigInt(ship.choice?.voteTally));
+ return (
+
+
+
+
+ {ship.name}
+
+
+
+
+ {Number(percentage)}% ({tokenAmount} {tokenData.tokenSymbol})
+
+
+ );
+ })}
+
+
+ Total:{' '}
+
+ {formatEther(totals?.totalVotes || 0n)} {tokenData.tokenSymbol}
+
+
+
+
+
+ All Votes
+
+
+
+ );
+};
+
+const AllVotes = ({ choices }: { choices: CondensedChoiceData[] }) => {
+ const { contest, tokenData } = useVoting();
+ const { data: voters } = useQuery({
+ queryKey: ['gs-voters'],
+ queryFn: () => getContestVoters(contest?.id as string),
+ enabled: !!contest,
+ });
+
+ return (
+
+ {voters?.map((voter) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/voting/VotingFooter.tsx b/src/components/voting/VotingFooter.tsx
index ef74f234..062a58f5 100644
--- a/src/components/voting/VotingFooter.tsx
+++ b/src/components/voting/VotingFooter.tsx
@@ -36,7 +36,7 @@ export const VotingFooter = ({
= {
GM_FACTORY: '0x14e32E7893D6A1fA5f852d8B2fE8c57A2aB670ba',
GS_FACTORY: '0x8D994BEef251e30C858e44eCE3670feb998CA77a',
HATS_POSTER: '0x4F0dc1C7d91d914d921F3C9C188F4454AE260317',
- VOTE_CONTEST: '0xF55108489EE3FE27AE60795d5816E0C95683B049',
+ VOTE_CONTEST: '0x009C84Ba52F109Ed3559FA39a60b5b8A2f167ec0',
} as const;
export const ADDR_PROD: Record = {
diff --git a/src/layout/DesktopNav/DesktopNav.tsx b/src/layout/DesktopNav/DesktopNav.tsx
index 0cdcb5ff..ddbc488d 100644
--- a/src/layout/DesktopNav/DesktopNav.tsx
+++ b/src/layout/DesktopNav/DesktopNav.tsx
@@ -7,7 +7,7 @@ import {
IconInfoCircle,
} from '@tabler/icons-react';
import classes from './DesktoNavStyles.module.css';
-import Logo from '../../assets/Logo.svg';
+import Logo from '../../assets/Logo.svg?react';
import { ConnectButton } from './ConnectButton';
import { Link, useLocation } from 'react-router-dom';
@@ -147,7 +147,7 @@ export function DesktopNav() {
-
+
{!isTablet && (
diff --git a/src/pages/CreateShip.tsx b/src/pages/CreateShip.tsx
index 9811bc73..42bb4eed 100644
--- a/src/pages/CreateShip.tsx
+++ b/src/pages/CreateShip.tsx
@@ -1,6 +1,6 @@
import { useState } from 'react';
-import { useAccount, useWatchContractEvent } from 'wagmi';
+import { useAccount, useChainId, useWatchContractEvent } from 'wagmi';
import Registry from '../abi/Registry.json';
import { ADDR } from '../constants/addresses';
@@ -21,6 +21,7 @@ export type ProfileData = {
export const CreateShip = () => {
const { address } = useAccount();
+ const chainId = useChainId();
const [step, setStep] = useState(0);
const [profileData, setProfileData, removeProfileStorage] = useLocalStorage<
@@ -34,9 +35,9 @@ export const CreateShip = () => {
useWatchContractEvent({
abi: Registry,
- address: ADDR.Registry,
+ address: ADDR.REGISTRY,
eventName: 'ProfileCreated',
- syncConnectedChain: true,
+ chainId,
pollingInterval: 100,
onError: (error) => {
console.error('error', error);
diff --git a/src/pages/Vote.tsx b/src/pages/Vote.tsx
index 019e82cd..85ee9dea 100644
--- a/src/pages/Vote.tsx
+++ b/src/pages/Vote.tsx
@@ -1,14 +1,12 @@
-import { useEffect, useMemo, useState } from 'react';
+import { useEffect, useState } from 'react';
import { MainSection, PageTitle } from '../layout/Sections';
import {
- Avatar,
Box,
- Divider,
+ Button,
Flex,
Group,
- Paper,
- Progress,
- Spoiler,
+ Modal,
+ Skeleton,
Stack,
Stepper,
Text,
@@ -25,31 +23,117 @@ import { VoteTimesIndicator } from '../components/voting/VoteTimesIndicator';
import { ShipVotingPanel } from '../components/voting/ShipVotingPanel';
import { ConfirmationPanel } from '../components/voting/ConfirmationPanel';
import { useVoting } from '../hooks/useVoting';
-import { ShipsCardUI } from '../types/ui';
-import { formatBigIntPercentage } from '../utils/helpers';
-import { Address, formatEther } from 'viem';
-import classes from '../components/feed/FeedStyles.module.css';
-import { IconChevronDown, IconChevronUp } from '@tabler/icons-react';
-import { GsVoter, getContestVoters } from '../queries/getVoters';
-import { AddressAvatar } from '../components/AddressAvatar';
+import { VoteResultsPanel } from '../components/voting/VoteResultsPanel';
+import { AppAlert } from '../components/UnderContruction';
+import { getShipGrants } from '../queries/getShipGrants';
+import {
+ PostedRecord,
+ getRecentPortfolioReport,
+} from '../queries/getRecordsByTag';
+import { Tag } from '../constants/tags';
+import { ADDR } from '../constants/addresses';
+import { ContestStatus, GameStatus, VotingStage } from '../types/common';
+import { PreVoting } from '../components/voting/PreVoting';
+import { useGameManager } from '../hooks/useGameMangers';
+import Logo from '../assets/Logo.svg?react';
+
+import { IconExclamationCircle } from '@tabler/icons-react';
+import { DashGrant } from '../resolvers/grantResolvers';
export type VotingFormValues = z.infer;
+export type CondensedChoiceData = {
+ id: string;
+ shipName: string;
+ shipImg: string;
+};
+
+const bigVoteQuery = async () => {
+ const ships = await getShipsPageData();
+
+ const shipVoteData = await Promise.all(
+ ships.map(async (ship) => {
+ const [grants, recentRecord] = await Promise.all([
+ getShipGrants(ship.id),
+ getRecentPortfolioReport(
+ `${Tag.ShipSubmitReport}-${ADDR.VOTE_CONTEST}-${ship.id}`
+ ),
+ ]);
+
+ return { ...ship, grants, recentRecord };
+ })
+ );
+
+ return shipVoteData;
+};
+
export const Vote = () => {
- const [step, setStep] = useState(0);
- const { data: ships } = useQuery({
+ const {
+ data: ships,
+ isLoading,
+ error,
+ } = useQuery({
queryKey: ['ships-page'],
- queryFn: getShipsPageData,
+ queryFn: bigVoteQuery,
});
- const { userVotes } = useVoting();
+ const { userVotes, votingStage, contestStatus } = useVoting();
+ const { currentRound } = useGameManager();
- const isLaptop = useLaptop();
+ if (isLoading) {
+ return ;
+ }
- const isTablet = useTablet();
+ if (error) {
+ return (
+
+ );
+ }
- const isMobile = useMobile();
+ const hasVotes = userVotes && userVotes.length > 0;
+
+ if (!currentRound) {
+ return null;
+ }
+
+ if (
+ currentRound?.gameStatus < GameStatus.Completed &&
+ contestStatus <= ContestStatus.Populating
+ ) {
+ return ;
+ }
+
+ if (!ships) {
+ return ;
+ }
+ if (hasVotes || votingStage >= VotingStage.Closed) {
+ return ;
+ }
+
+ return ;
+};
+
+const VotingOpen = ({
+ ships,
+}: {
+ ships: {
+ grants: DashGrant[] | null;
+ recentRecord: PostedRecord | null;
+ id: string;
+ name: string;
+ status: GameStatus;
+ imgUrl: string;
+ description: string;
+ amtAllocated: string;
+ amtDistributed: string;
+ amtAvailable: string;
+ balance: string;
+ }[];
+}) => {
const form = useForm({
initialValues: {
ships: [],
@@ -58,6 +142,15 @@ export const Vote = () => {
validateInputOnBlur: true,
});
+ const [step, setStep] = useState(0);
+ const [modalOpen, setModalOpen] = useState(false);
+
+ const isLaptop = useLaptop();
+
+ const isTablet = useTablet();
+
+ const isMobile = useMobile();
+
useEffect(
() => {
if (!ships) return;
@@ -72,360 +165,191 @@ export const Vote = () => {
[ships]
);
- if (!ships) {
- return null;
- }
-
- const hasVotes = userVotes && userVotes.length > 0;
-
- if (hasVotes) {
- return ;
- }
+ useEffect(() => {
+ const hasSeenHelp = JSON.parse(
+ localStorage.getItem('has-seen-help') || 'false'
+ );
+ if (!hasSeenHelp) {
+ setModalOpen(true);
+ }
+ }, []);
- const nextStep = () =>
+ const nextStep = () => {
setStep((current) => (current < 3 ? current + 1 : current));
+ };
const prevStep = () =>
setStep((current) => (current > 0 ? current - 1 : current));
+ const closeModal = () => {
+ localStorage.setItem('has-seen-help', JSON.stringify(true));
+ setModalOpen(false);
+ };
return (
-
-
-
- {ships?.map((ship, index) => (
+
+
+
+
+ }
+ onClick={() => setModalOpen(true)}
+ >
+ Help
+
+
+ {ships?.map((ship, index) => (
+
+
+
+ ))}
-
+
- ))}
-
-
-
-
+
+
{!isLaptop && (
)}
+
+ Grant Ships Voting
+
+ }
+ >
+
+
);
};
-const VoteConfirmationPanel = ({ ships }: { ships: ShipsCardUI[] }) => {
- const { contest, userVotes, tokenData } = useVoting();
-
- const theme = useMantineTheme();
-
- const consolidated = useMemo(() => {
- if (!ships || !userVotes || !contest) return [];
-
- return ships.map((ship) => {
- const shipChoice = contest?.choices.find((c) => c.shipId === ship.id);
-
- const userVote = userVotes.find((v) => v.choice_id === shipChoice?.id);
-
- return { ...ship, vote: userVote, choice: shipChoice };
- });
- }, [ships, userVotes, contest]);
-
- const totals = useMemo(() => {
- if (!consolidated || !contest) return null;
-
- const totalUserVotes =
- consolidated && consolidated.length > 0
- ? consolidated.reduce((acc, ship) => {
- if (!ship.vote) return acc;
- return acc + BigInt(ship.vote.amount);
- }, 0n)
- : 0n;
- const totalVotes = contest?.choices?.length
- ? contest?.choices.reduce((acc, choice) => {
- return acc + BigInt(choice.voteTally);
- }, 0n)
- : 0n;
-
- return {
- totalUserVotes,
- totalVotes,
- };
- }, [consolidated, contest]);
-
- const condensed = useMemo(() => {
- return consolidated?.map((ship) => ({
- shipImg: ship.imgUrl,
- shipName: ship.name,
- id: ship.choice?.id as string,
- }));
- }, [consolidated]);
-
- const colors = [
- theme.colors.blue[5],
- theme.colors.violet[5],
- theme.colors.pink[5],
- ];
- return (
-
+const LoadingSkeleton = () => (
+
+
-
- Your vote has been submitted!
-
-
-
-
- Your Vote
-
- {consolidated.map((ship, index) => {
- const percentage = totals?.totalUserVotes
- ? formatBigIntPercentage(
- BigInt(ship.vote?.amount || 0),
- totals?.totalUserVotes
- )
- : '0';
- const tokenAmount = formatEther(BigInt(ship.vote?.amount || 0));
-
- return (
-
-
-
-
- {ship.name}
-
-
-
-
- {Number(percentage)}% Voted ({tokenAmount}{' '}
- {tokenData.tokenSymbol})
-
-
- );
- })}
-
-
- Total:{' '}
-
- {formatEther(totals?.totalUserVotes || 0n)} {tokenData.tokenSymbol}
-
-
-
-
- Total Vote Results
-
- {consolidated?.map((ship, index) => {
- const percentage = totals?.totalVotes
- ? formatBigIntPercentage(
- BigInt(ship.choice?.voteTally),
- totals?.totalVotes
- )
- : '0';
- const tokenAmount = formatEther(BigInt(ship.choice?.voteTally));
- return (
-
-
-
-
- {ship.name}
-
-
-
-
- {Number(percentage)}% ({tokenAmount} {tokenData.tokenSymbol})
-
-
- );
- })}
-
-
- Total:{' '}
-
- {formatEther(totals?.totalVotes || 0n)} {tokenData.tokenSymbol}
+
+
+
+ Ship Portfolio Report
-
-
-
-
- All Votes
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- );
-};
-
-type CondensedChoiceData = {
- id: string;
- shipName: string;
- shipImg: string;
-};
+
+);
-const AllVotes = ({ choices }: { choices: CondensedChoiceData[] }) => {
- const { contest, tokenData } = useVoting();
- const { data: voters } = useQuery({
- queryKey: ['gs-voters'],
- queryFn: () => getContestVoters(contest?.id as string),
- enabled: !!contest,
- });
-
- return (
-
- {voters?.map((voter) => (
-
- ))}
-
- );
-};
-
-const VoteCard = ({
- voter,
- choices,
- tokenSymbol,
-}: {
- voter: GsVoter;
- choices: CondensedChoiceData[];
- tokenSymbol?: string;
-}) => {
+const InfoModalContent = ({ closeModal }: { closeModal: () => void }) => {
const theme = useMantineTheme();
- const colors = [
- theme.colors.blue[5],
- theme.colors.violet[5],
- theme.colors.pink[5],
- ];
-
- const totalUserVotes = useMemo(() => {
- return voter.votes.reduce((acc, vote) => {
- return acc + BigInt(vote?.amount ? vote.amount : 0);
- }, 0n);
- }, [voter]);
-
- const consolidated = useMemo(() => {
- return choices.map((choice) => {
- const vote = voter.votes.find((vote) => choice.id === vote.choice.id);
- return { ...choice, vote };
- });
- }, [voter, choices]);
-
return (
-
-
-
+
+
+
+
+
+
+ Welcome!
+
+
+ We are excited to have you here for the first "Gaming on Arbitrum"
+ Voting Round
+
+
+
+
+ How to vote
+
+
+ Read through each ship's (grant program) portfolio and evaluate the
+ projects funded. Leave a comment and an estimated %. When you reach
+ the final stage, you will be able to review your votes and submit
+ them.
+
+
+
+ The total percentages will determine the funding each ship receives in
+ the following round.
+
+
+ If you have any questions or need help, please reach out to us on{' '}
+ Discord or{' '}
+ Telegram.
+
+
+
+
-
- {consolidated.map((choice, index) => {
- return (
-
- );
- })}
-
- );
-};
-
-const ShipChoiceVoteBar = ({
- choice,
- totalVotes,
- voteAmount,
- reason,
- color,
- tokenSymbol,
- didVote,
-}: {
- choice: CondensedChoiceData;
- totalVotes: bigint;
- voteAmount: bigint;
- reason?: string | null;
- color: string;
- tokenSymbol?: string;
- didVote?: boolean;
-}) => {
- const votePercentage = formatBigIntPercentage(voteAmount, totalVotes);
- return (
-
-
-
-
-
-
- {votePercentage}% Voted ({formatEther(voteAmount)}){' '}
- {tokenSymbol || ''}
-
-
-
- }
- showLabel={}
- classNames={{
- root: classes.embedTextBox,
- control: classes.embedTextControl,
- }}
- maxHeight={24}
- >
- {!didVote && (
-
- Did not vote
-
- )}
- {didVote && !reason && (
-
- No reason given
-
- )}
- {didVote && reason && (
-
- {reason}
-
- )}
-
);
};
diff --git a/src/resolvers/grantResolvers.ts b/src/resolvers/grantResolvers.ts
index 6f9a878d..67103214 100644
--- a/src/resolvers/grantResolvers.ts
+++ b/src/resolvers/grantResolvers.ts
@@ -155,7 +155,7 @@ export const resolveMilestones = async (
};
export const resolveMilestoneReviewReason = async (pointer?: string) => {
- if (!pointer) {
+ if (!pointer || pointer === 'NULL') {
return null;
}
diff --git a/vite.config.ts b/vite.config.ts
index f179a006..ddd97a87 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -11,10 +11,7 @@ export default defineConfig({
nodePolyfills({
protocolImports: true,
}),
- svgr({
- svgrOptions: { exportType: 'default', ref: true },
- include: '**/*.svg',
- }),
+ svgr(),
],
define: {
// globalThis: 'window',