From ac0f78dcd18d0c97845610d9074964d97e59835d Mon Sep 17 00:00:00 2001 From: Justin Schreiner Date: Mon, 25 Nov 2024 11:58:04 -0700 Subject: [PATCH 01/24] add hash on poll id page --- src/pages/polls/[pollId]/index.tsx | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/pages/polls/[pollId]/index.tsx b/src/pages/polls/[pollId]/index.tsx index e931e64..7cfd067 100644 --- a/src/pages/polls/[pollId]/index.tsx +++ b/src/pages/polls/[pollId]/index.tsx @@ -185,15 +185,27 @@ export default function ViewPoll(props: Props): JSX.Element { {poll ? ( - - + + + + + + + The linked text document has the Blake2b-256 hash of:{' '} + {poll.hashedText} + {poll.summary_tx_id && !isTxUploading && ( From 21b1a5826606be5143ad2353fd9fca2ebcb4c502 Mon Sep 17 00:00:00 2001 From: Justin Schreiner Date: Mon, 25 Nov 2024 13:54:06 -0700 Subject: [PATCH 02/24] add fallback background color --- src/providers/themeProvider.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/providers/themeProvider.tsx b/src/providers/themeProvider.tsx index 5840029..e44d460 100644 --- a/src/providers/themeProvider.tsx +++ b/src/providers/themeProvider.tsx @@ -244,6 +244,7 @@ export function ColorModeProvider({ <> Date: Mon, 25 Nov 2024 15:16:11 -0600 Subject: [PATCH 03/24] Change poll page isActiveVoter check to CSR --- src/components/buttons/voteOnPollButtons.tsx | 28 +++++++++++++++----- src/pages/polls/[pollId]/index.tsx | 28 +------------------- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/components/buttons/voteOnPollButtons.tsx b/src/components/buttons/voteOnPollButtons.tsx index 2520f34..cfe109a 100644 --- a/src/components/buttons/voteOnPollButtons.tsx +++ b/src/components/buttons/voteOnPollButtons.tsx @@ -9,12 +9,12 @@ import { useSession } from 'next-auth/react'; import toast from 'react-hot-toast'; import { castVote } from '@/lib/helpers/castVote'; +import { getActiveVoterFromUserId } from '@/lib/helpers/getActiveVoterFromUserId'; import { getPollVote } from '@/lib/helpers/getPollVote'; interface Props { pollName: string; pollId: string; - isActiveVoter: boolean; hashedText: string; link: string; } @@ -23,17 +23,33 @@ interface Props { * Yes, No, Abstain buttons to vote on a poll * @param pollName - The name of the poll * @param pollId - The ID of the poll - * @param isActiveVoter - Whether the user is the active voter + * @param hashedText - The hashed text of the poll + * @param link - The link to the poll * @returns Vote on Poll Buttons */ export function VoteOnPollButtons(props: Props): JSX.Element { - const { pollName, pollId, isActiveVoter, hashedText, link } = props; - const [vote, setVote] = useState(''); - const [disabled, setDisabled] = useState(false); + const { pollName, pollId, hashedText, link } = props; const session = useSession(); const theme = useTheme(); + const [vote, setVote] = useState(''); + const [disabled, setDisabled] = useState(false); + const [isActiveVoter, setIsActiveVoter] = useState(false); + const [fetching, setIsFetching] = useState( + session.status === 'authenticated' ? false : true, + ); + + useEffect(() => { + if (session.data?.user.id) { + setIsFetching(true); + getActiveVoterFromUserId(session.data.user.id).then((result) => { + setIsActiveVoter(result.activeVoterId === session.data.user.id); + setIsFetching(false); + }); + } + }, [session.data?.user.id]); + async function handleVote(vote: string): Promise { setDisabled(true); const result = await castVote( @@ -91,7 +107,7 @@ export function VoteOnPollButtons(props: Props): JSX.Element { )} - {!isActiveVoter && !session.data?.user.isCoordinator && ( + {!isActiveVoter && !session.data?.user.isCoordinator && !fetching && ( You are not the active voter for your workshop. Only the active voter can vote. diff --git a/src/pages/polls/[pollId]/index.tsx b/src/pages/polls/[pollId]/index.tsx index e931e64..6b5b5e3 100644 --- a/src/pages/polls/[pollId]/index.tsx +++ b/src/pages/polls/[pollId]/index.tsx @@ -4,19 +4,16 @@ import type { GetServerSidePropsContext } from 'next'; import Head from 'next/head'; import { useRouter } from 'next/router'; import { pollPhases } from '@/constants/pollPhases'; -import { authOptions } from '@/pages/api/auth/[...nextauth]'; import LaunchRounded from '@mui/icons-material/LaunchRounded'; import { Button, CircularProgress, Modal, useTheme } from '@mui/material'; import Box from '@mui/material/Box'; import Grid from '@mui/material/Grid2'; import Typography from '@mui/material/Typography'; import { useQuery } from '@tanstack/react-query'; -import { getServerSession } from 'next-auth'; import { useSession } from 'next-auth/react'; import toast from 'react-hot-toast'; import type { Poll, User, Workshop } from '@/types'; -import { activeVoterDto } from '@/data/activeVoterDto'; import { pollDto } from '@/data/pollDto'; import { pollResultsDto } from '@/data/pollResultsDto'; import { pollsDto } from '@/data/pollsDto'; @@ -58,17 +55,10 @@ interface Props { }[]; }; polls: Poll[]; - workshopActiveVoterId: string; } export default function ViewPoll(props: Props): JSX.Element { - const { - representatives, - workshops, - pollResultsSSR, - polls, - workshopActiveVoterId, - } = props; + const { representatives, workshops, pollResultsSSR, polls } = props; let { poll } = props; const [isSubmitting, setIsSubmitting] = useState(false); const [pollResults, setPollResults] = useState(pollResultsSSR); @@ -289,9 +279,6 @@ export default function ViewPoll(props: Props): JSX.Element { @@ -384,7 +371,6 @@ export const getServerSideProps = async ( }[]; }; polls: Poll[]; - workshopActiveVoterId: string; }; }> => { if (!context.params) { @@ -395,23 +381,12 @@ export const getServerSideProps = async ( workshops: [], pollResultsSSR: {}, polls: [], - workshopActiveVoterId: '', }, }; } const { pollId } = context.params; - const session = await getServerSession(context.req, context.res, authOptions); - - let workshopActiveVoterId = ''; - if (session) { - const activeVoterId = await activeVoterDto(session.user.id); - if (activeVoterId) { - workshopActiveVoterId = activeVoterId; - } - } - const poll = await pollDto(pollId); const representatives = await representativesDto(); const workshops = await workshopsDto(); @@ -425,7 +400,6 @@ export const getServerSideProps = async ( workshops: workshops, pollResultsSSR: pollResultsSSR, polls: polls, - workshopActiveVoterId: workshopActiveVoterId, }, }; }; From b90a1f71b87385a564ac4d15f30cbc754853e3c5 Mon Sep 17 00:00:00 2001 From: Matthew Laux Date: Mon, 25 Nov 2024 15:51:33 -0600 Subject: [PATCH 04/24] Update voteOnPoll tests --- .../alertsUserWhenPollHasFinishedVoting.test.tsx | 4 +++- .../alertsUserWhenPollIdIsNotProvided.test.tsx | 4 +++- .../voteOnPollButton/alertsUserWhenPollIsArchived.test.tsx | 4 +++- .../voteOnPollButton/alertsUserWhenPollIsNotFound.test.tsx | 4 +++- .../alertsUserWhenPollIsNotOpenForVotingYet.test.tsx | 4 +++- .../voteOnPollButton/alertsUserWhenVoteFailsToCast.test.tsx | 4 +++- .../voteOnPollButton/alertsUserWhenVoteIsInvalid.test.tsx | 4 +++- .../voteOnPollButton/alertsUserWhenVoteIsNotProvided.test.tsx | 4 +++- .../voteOnPollButton/successfullyVotesAbstainOnAPoll.test.tsx | 4 +++- .../voteOnPollButton/successfullyVotesNoAPoll.test.tsx | 4 +++- .../voteOnPollButton/successfullyVotesYesOnAPoll.test.tsx | 4 +++- 11 files changed, 33 insertions(+), 11 deletions(-) diff --git a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollHasFinishedVoting.test.tsx b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollHasFinishedVoting.test.tsx index a8175c7..f2d44d5 100644 --- a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollHasFinishedVoting.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollHasFinishedVoting.test.tsx @@ -19,6 +19,9 @@ test.skip('alerts user when poll has finished voting', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -26,7 +29,6 @@ test.skip('alerts user when poll has finished voting', async () => { diff --git a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIdIsNotProvided.test.tsx b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIdIsNotProvided.test.tsx index cded9a4..f13d46d 100644 --- a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIdIsNotProvided.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIdIsNotProvided.test.tsx @@ -19,6 +19,9 @@ test.skip('alerts user when poll ID is not provided', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -26,7 +29,6 @@ test.skip('alerts user when poll ID is not provided', async () => { diff --git a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsArchived.test.tsx b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsArchived.test.tsx index 55b96b5..beb7b23 100644 --- a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsArchived.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsArchived.test.tsx @@ -19,6 +19,9 @@ test.skip('alerts user when poll is archived', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -26,7 +29,6 @@ test.skip('alerts user when poll is archived', async () => { diff --git a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsNotFound.test.tsx b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsNotFound.test.tsx index 5230585..187930c 100644 --- a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsNotFound.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsNotFound.test.tsx @@ -19,6 +19,9 @@ test.skip('alerts user when poll is not found', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -26,7 +29,6 @@ test.skip('alerts user when poll is not found', async () => { diff --git a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsNotOpenForVotingYet.test.tsx b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsNotOpenForVotingYet.test.tsx index 439d675..aefde1c 100644 --- a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsNotOpenForVotingYet.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenPollIsNotOpenForVotingYet.test.tsx @@ -19,6 +19,9 @@ test.skip('alerts user when poll is not open for voting yet', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -26,7 +29,6 @@ test.skip('alerts user when poll is not open for voting yet', async () => { diff --git a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteFailsToCast.test.tsx b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteFailsToCast.test.tsx index 8f6d9b2..24051da 100644 --- a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteFailsToCast.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteFailsToCast.test.tsx @@ -19,6 +19,9 @@ test.skip('alerts user when poll has finished voting', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -26,7 +29,6 @@ test.skip('alerts user when poll has finished voting', async () => { diff --git a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteIsInvalid.test.tsx b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteIsInvalid.test.tsx index 088d3df..ccefb64 100644 --- a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteIsInvalid.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteIsInvalid.test.tsx @@ -19,6 +19,9 @@ test.skip('alerts user when vote is invalid', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -26,7 +29,6 @@ test.skip('alerts user when vote is invalid', async () => { diff --git a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteIsNotProvided.test.tsx b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteIsNotProvided.test.tsx index b0ace13..3ce9c46 100644 --- a/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteIsNotProvided.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/alertsUserWhenVoteIsNotProvided.test.tsx @@ -19,6 +19,9 @@ test.skip('alerts user when vote is not provided', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -26,7 +29,6 @@ test.skip('alerts user when vote is not provided', async () => { diff --git a/__tests__/components/buttons/voteOnPollButton/successfullyVotesAbstainOnAPoll.test.tsx b/__tests__/components/buttons/voteOnPollButton/successfullyVotesAbstainOnAPoll.test.tsx index 191531a..914e858 100644 --- a/__tests__/components/buttons/voteOnPollButton/successfullyVotesAbstainOnAPoll.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/successfullyVotesAbstainOnAPoll.test.tsx @@ -16,6 +16,9 @@ test.skip('successfully votes abstain on a poll', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -23,7 +26,6 @@ test.skip('successfully votes abstain on a poll', async () => { diff --git a/__tests__/components/buttons/voteOnPollButton/successfullyVotesNoAPoll.test.tsx b/__tests__/components/buttons/voteOnPollButton/successfullyVotesNoAPoll.test.tsx index 0049ae4..94ca4c0 100644 --- a/__tests__/components/buttons/voteOnPollButton/successfullyVotesNoAPoll.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/successfullyVotesNoAPoll.test.tsx @@ -16,6 +16,9 @@ test.skip('successfully votes no on a poll', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -23,7 +26,6 @@ test.skip('successfully votes no on a poll', async () => { diff --git a/__tests__/components/buttons/voteOnPollButton/successfullyVotesYesOnAPoll.test.tsx b/__tests__/components/buttons/voteOnPollButton/successfullyVotesYesOnAPoll.test.tsx index 571bd5a..c83f329 100644 --- a/__tests__/components/buttons/voteOnPollButton/successfullyVotesYesOnAPoll.test.tsx +++ b/__tests__/components/buttons/voteOnPollButton/successfullyVotesYesOnAPoll.test.tsx @@ -16,6 +16,9 @@ test.skip('successfully votes yes on a poll', async () => { id: '1', stakeAddress: 'stakeAddress', walletName: 'walletName', + isCoordinator: false, + isDelegate: true, + isAlternate: false, }, }} > @@ -23,7 +26,6 @@ test.skip('successfully votes yes on a poll', async () => { From 1825055fc5d03269967f271860e725a47ea4e346 Mon Sep 17 00:00:00 2001 From: Matthew Laux Date: Mon, 25 Nov 2024 17:17:43 -0600 Subject: [PATCH 05/24] Add user name 64 byte check --- src/lib/getStringBytes.ts | 9 +++++++++ src/pages/api/updateUser.ts | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 src/lib/getStringBytes.ts diff --git a/src/lib/getStringBytes.ts b/src/lib/getStringBytes.ts new file mode 100644 index 0000000..5446347 --- /dev/null +++ b/src/lib/getStringBytes.ts @@ -0,0 +1,9 @@ +/** + * Gets the byte size of a string + * @param str - The string to get the byte size of + * @returns Number of bytes + */ +export function getStringBytes(str: string): number { + const byteSize = new Blob([str]).size; + return byteSize; +} diff --git a/src/pages/api/updateUser.ts b/src/pages/api/updateUser.ts index b1b9fec..85c52f5 100644 --- a/src/pages/api/updateUser.ts +++ b/src/pages/api/updateUser.ts @@ -7,6 +7,7 @@ import { getServerSession } from 'next-auth'; import { checkIfCO } from '@/lib/checkIfCO'; import { checkIfVoting } from '@/lib/checkIfVoting'; +import { getStringBytes } from '@/lib/getStringBytes'; type Data = { userId: string; @@ -66,10 +67,10 @@ export default async function updateUser( message: 'Name must be provided.', }); } - if (name.length > 100) { + if (getStringBytes(name) > 64) { return res.status(400).json({ userId: BigInt(-1).toString(), - message: 'Name must be less than 100 characters.', + message: 'Name must be less than 64 characters.', }); } // validate email From fddb921d09932c451a5db345e1f2b08de6e6692b Mon Sep 17 00:00:00 2001 From: Matthew Laux Date: Mon, 25 Nov 2024 17:40:55 -0600 Subject: [PATCH 06/24] Update active voter table when delegate info changes --- .../coordinator/manageActivePowerTable.tsx | 22 ++++++++++++++++--- .../manageRepresentativesTable.tsx | 9 +++++++- src/pages/representatives/manage/index.tsx | 15 +++++++++---- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/components/coordinator/manageActivePowerTable.tsx b/src/components/coordinator/manageActivePowerTable.tsx index f49d6b6..cd5f0af 100644 --- a/src/components/coordinator/manageActivePowerTable.tsx +++ b/src/components/coordinator/manageActivePowerTable.tsx @@ -27,11 +27,19 @@ import { getRepresentatives } from '@/lib/helpers/getRepresentatives'; import { getWorkshops } from '@/lib/helpers/getWorkshops'; import { updateActiveVoter } from '@/lib/helpers/updateActiveVoter'; +interface Props { + refresh: boolean; + toggleRefresh: () => void; +} + /** * Allows a workshop coordinator to manage if delegates or alternates have active power from each workhsop + * @param refresh - boolean to refresh the table + * @param toggleRefresh - function to toggle the refresh boolean * @returns Admin Manage Active Power Table */ -export function ManageActivePowerTable(): JSX.Element { +export function ManageActivePowerTable(props: Props): JSX.Element { + const { refresh, toggleRefresh } = props; const [representatives, setRepresentatives] = useState([]); const [workshops, setWorkshops] = useState([]); const [rowModesModel, setRowModesModel] = useState({}); @@ -47,7 +55,7 @@ export function ManageActivePowerTable(): JSX.Element { setRepresentatives(reps); } fetchData(); - }, [reload]); + }, [reload, refresh]); function handleRowEditStop( // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -69,6 +77,7 @@ export function ManageActivePowerTable(): JSX.Element { toast.error(data.message); } setReload(!reload); + toggleRefresh(); return newRow; } @@ -271,7 +280,14 @@ export function ManageActivePowerTable(): JSX.Element { }, }, ]; - }, [representatives, rowModesModel, theme.palette.text.primary, reload]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + representatives, + rowModesModel, + theme.palette.text.primary, + reload, + refresh, // TS is not recognizing refresh as a dependency but it is required to properly refresh the table when info in representatives table changes + ]); return ( diff --git a/src/components/coordinator/manageRepresentativesTable.tsx b/src/components/coordinator/manageRepresentativesTable.tsx index 9cc9862..27cd91a 100644 --- a/src/components/coordinator/manageRepresentativesTable.tsx +++ b/src/components/coordinator/manageRepresentativesTable.tsx @@ -22,11 +22,17 @@ import { User } from '@/types'; import { getRepresentatives } from '@/lib/helpers/getRepresentatives'; import { updateUser } from '@/lib/helpers/updateUser'; +interface Props { + toggleRefresh: () => void; +} + /** * Table for admin to manage representative information + * @param toggleRefresh - function to toggle the refresh boolean * @returns Admin Manage Representatives Table */ -export function ManageRepresentativesTable(): JSX.Element { +export function ManageRepresentativesTable(props: Props): JSX.Element { + const { toggleRefresh } = props; const [representatives, setRepresentatives] = useState([]); const [rowModesModel, setRowModesModel] = useState({}); const [reload, setReload] = useState(false); @@ -64,6 +70,7 @@ export function ManageRepresentativesTable(): JSX.Element { toast.error(data.message); } setReload(!reload); + toggleRefresh(); return newRow; } diff --git a/src/pages/representatives/manage/index.tsx b/src/pages/representatives/manage/index.tsx index df4d9c7..daf9b76 100644 --- a/src/pages/representatives/manage/index.tsx +++ b/src/pages/representatives/manage/index.tsx @@ -1,3 +1,4 @@ +import { useCallback, useState } from 'react'; import type { GetServerSidePropsContext } from 'next'; import Head from 'next/head'; import { authOptions } from '@/pages/api/auth/[...nextauth]'; @@ -12,6 +13,11 @@ import { ManageRepresentativesTable } from '@/components/coordinator/manageRepre export default function ManageRepresentatives(): JSX.Element { useCheckAddressChange(); + const [refresh, setRefresh] = useState(false); + + const toggleRefresh = useCallback((): void => { + setRefresh((prev) => !prev); + }, []); return ( <> @@ -29,10 +35,11 @@ export default function ManageRepresentatives(): JSX.Element { Coordinator Dashboard - - - - + + From 24951ac0945d95799b3a7a1ada916d7aeef29ada Mon Sep 17 00:00:00 2001 From: Matthew Laux Date: Mon, 25 Nov 2024 21:08:47 -0600 Subject: [PATCH 07/24] Add in progress option to voting history table --- src/components/representatives/votingHistoryTable.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/representatives/votingHistoryTable.tsx b/src/components/representatives/votingHistoryTable.tsx index 25ce03a..f1ab3c2 100644 --- a/src/components/representatives/votingHistoryTable.tsx +++ b/src/components/representatives/votingHistoryTable.tsx @@ -1,3 +1,4 @@ +import { pollPhases } from '@/constants/pollPhases'; import DoDisturbRounded from '@mui/icons-material/DoDisturbRounded'; import ThumbDownRounded from '@mui/icons-material/ThumbDownRounded'; import ThumbUpRounded from '@mui/icons-material/ThumbUpRounded'; @@ -42,6 +43,7 @@ export function VotingHistoryTable(props: Props): JSX.Element { const userVoteData = votes.find( (vote) => vote.poll_id === params.row.id, ); + const poll = polls.find((poll) => poll.id === params.row.id); const userVote = userVoteData?.vote; return ( )} - {!userVote && ( + {poll?.status === pollPhases.pending || + (poll?.status === pollPhases.voting && ( + + In Progress + + ))} + {poll?.status === pollPhases.concluded && !userVote && ( None From 0f0e9d4cb716e89942f6ed097b08c720db5c6900 Mon Sep 17 00:00:00 2001 From: Justin Schreiner Date: Mon, 25 Nov 2024 20:09:22 -0700 Subject: [PATCH 08/24] wrap text --- package-lock.json | 1 + src/pages/polls/[pollId]/index.tsx | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 03f548b..d691661 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "cardano-constitution-voting-app", "version": "0.1.0", "hasInstallScript": true, + "license": "AGPL-3.0-or-later", "dependencies": { "@claritydao/clarity-backend": "^0.4.0-dev-512cc333547522c7d53ffe0ecddacd3de3958dd1", "@emotion/react": "^11.13.3", diff --git a/src/pages/polls/[pollId]/index.tsx b/src/pages/polls/[pollId]/index.tsx index 7cfd067..dbc49d5 100644 --- a/src/pages/polls/[pollId]/index.tsx +++ b/src/pages/polls/[pollId]/index.tsx @@ -202,7 +202,13 @@ export default function ViewPoll(props: Props): JSX.Element { - + The linked text document has the Blake2b-256 hash of:{' '} {poll.hashedText} From dfce9e2c5d577f517706debf0797582af3e07292 Mon Sep 17 00:00:00 2001 From: Matthew Laux Date: Tue, 26 Nov 2024 10:09:19 -0600 Subject: [PATCH 09/24] Remove color property from getRepresentativeHandlers --- __mocks__/getRepresentatives/handlers.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/__mocks__/getRepresentatives/handlers.ts b/__mocks__/getRepresentatives/handlers.ts index f5e5c59..7bbf3fb 100644 --- a/__mocks__/getRepresentatives/handlers.ts +++ b/__mocks__/getRepresentatives/handlers.ts @@ -10,7 +10,6 @@ export const getRepresentativesHandlers = [ is_alternate: false, workshop_id: BigInt(1).toString(), email: 'john@johnson.com', - color: '#000000', wallet_address: 'addr1isdufnpoasidjfopmaimdfmopisadj', name: 'John Johnson', }, @@ -21,7 +20,6 @@ export const getRepresentativesHandlers = [ is_alternate: true, workshop_id: BigInt(1).toString(), email: 'mike@mickelson.com', - color: '#000000', wallet_address: 'addr1oqwieuroijfvaondsfipoaapoidjf', name: 'Mike Mickelson', }, @@ -32,7 +30,6 @@ export const getRepresentativesHandlers = [ is_alternate: false, workshop_id: BigInt(2).toString(), email: 'jack@jackson.com', - color: '#000000', wallet_address: 'addr1kidjfsadjfoadspjfoaidsfadsop', name: 'Jack Jackson', }, @@ -43,7 +40,6 @@ export const getRepresentativesHandlers = [ is_alternate: true, workshop_id: BigInt(2).toString(), email: 'rob@robertson.com', - color: '#000000', wallet_address: 'addr1iwoiuwqxpoieujsoidpjfkfjhpqowmfoa', name: 'Robert Robertson', }, @@ -54,7 +50,6 @@ export const getRepresentativesHandlers = [ is_alternate: false, workshop_id: BigInt(3).toString(), email: 'connor@connorson.com', - color: '#000000', wallet_address: 'addr1opijpoiuoimnlkjipoujpoimlkkm', name: 'Connor Connorson', }, @@ -65,7 +60,6 @@ export const getRepresentativesHandlers = [ is_alternate: true, workshop_id: BigInt(3).toString(), email: 'kyle@kyleson.com', - color: '#000000', wallet_address: 'addr1adfasdfwerwqersdvfzxfvsadf', name: 'Kyle Kyleson', }, From 7549f77504f03e9b0ff429797c5d25583276e8af Mon Sep 17 00:00:00 2001 From: Matthew Laux Date: Tue, 26 Nov 2024 10:09:48 -0600 Subject: [PATCH 10/24] Strips trailing and leading spaces from wallet address --- __mocks__/updateUser/errorHandlers.ts | 32 ++++++++++----- ...tsUserIfWhitespaceInWalletAddress.test.tsx | 40 +++++++++++++++++++ ...stripsWhitespaceFromWalletAddress.test.tsx | 33 +++++++++++++++ src/lib/helpers/updateUser.ts | 2 +- src/pages/api/updateUser.ts | 6 +++ 5 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 __tests__/components/coordinator/manageRepresentativesTable/alertsUserIfWhitespaceInWalletAddress.test.tsx create mode 100644 __tests__/components/coordinator/manageRepresentativesTable/stripsWhitespaceFromWalletAddress.test.tsx diff --git a/__mocks__/updateUser/errorHandlers.ts b/__mocks__/updateUser/errorHandlers.ts index 1b1ebce..c2766a3 100644 --- a/__mocks__/updateUser/errorHandlers.ts +++ b/__mocks__/updateUser/errorHandlers.ts @@ -4,7 +4,7 @@ export const updateUserActiveVoteHandler = [ http.post('/api/updateUser', async () => { return HttpResponse.json( { - pollId: BigInt(-1).toString(), + userId: BigInt(-1).toString(), message: 'You cannot update user information while a Poll is actively voting.', }, @@ -17,7 +17,7 @@ export const updateUserNoNameHandler = [ http.post('/api/updateUser', async () => { return HttpResponse.json( { - pollId: BigInt(-1).toString(), + userId: BigInt(-1).toString(), message: 'Name must be provided.', }, { status: 400 }, @@ -29,7 +29,7 @@ export const updateUserNoEmailHandler = [ http.post('/api/updateUser', async () => { return HttpResponse.json( { - pollId: BigInt(-1).toString(), + userId: BigInt(-1).toString(), message: 'Email must be provided.', }, { status: 400 }, @@ -41,7 +41,7 @@ export const updateUserNoWalletAddressHandler = [ http.post('/api/updateUser', async () => { return HttpResponse.json( { - pollId: BigInt(-1).toString(), + userId: BigInt(-1).toString(), message: 'Wallet address must be provided.', }, { status: 400 }, @@ -53,7 +53,7 @@ export const updateUserTooLongNameHandler = [ http.post('/api/updateUser', async () => { return HttpResponse.json( { - pollId: BigInt(-1).toString(), + userId: BigInt(-1).toString(), message: 'Name must be less than 100 characters.', }, { status: 400 }, @@ -65,7 +65,7 @@ export const updateUserTooLongEmailHandler = [ http.post('/api/updateUser', async () => { return HttpResponse.json( { - pollId: BigInt(-1).toString(), + userId: BigInt(-1).toString(), message: 'Email must be less than 100 characters.', }, { status: 400 }, @@ -77,7 +77,7 @@ export const updateUserTooLongWalletAddressHandler = [ http.post('/api/updateUser', async () => { return HttpResponse.json( { - pollId: BigInt(-1).toString(), + userId: BigInt(-1).toString(), message: 'Wallet Address must be less than 100 characters.', }, { status: 400 }, @@ -89,7 +89,7 @@ export const updateUserInternalErrorHandler = [ http.post('/api/updateUser', async () => { return HttpResponse.json( { - pollId: BigInt(-1).toString(), + userId: BigInt(-1).toString(), message: 'Error updating User Information.', }, { status: 500 }, @@ -101,7 +101,7 @@ export const updateUserInvalidSessionHandler = [ http.post('/api/updateUser', async () => { return HttpResponse.json( { - success: false, + userId: BigInt(-1).toString(), message: 'You must be signed in as an Organizer update user information.', }, @@ -114,10 +114,22 @@ export const updateUserNotOrganizerHandler = [ http.post('/api/updateUser', async () => { return HttpResponse.json( { - success: false, + userId: BigInt(-1).toString(), message: 'You must be an Organizer to update user information.', }, { status: 401 }, ); }), ]; + +export const updateUserAddressWhitespaceHandler = [ + http.post('/api/updateUser', async () => { + return HttpResponse.json( + { + userId: BigInt(-1).toString(), + message: 'Wallet address must not have leading or trailing whitespace.', + }, + { status: 401 }, + ); + }), +]; diff --git a/__tests__/components/coordinator/manageRepresentativesTable/alertsUserIfWhitespaceInWalletAddress.test.tsx b/__tests__/components/coordinator/manageRepresentativesTable/alertsUserIfWhitespaceInWalletAddress.test.tsx new file mode 100644 index 0000000..fb36dd1 --- /dev/null +++ b/__tests__/components/coordinator/manageRepresentativesTable/alertsUserIfWhitespaceInWalletAddress.test.tsx @@ -0,0 +1,40 @@ +import { server } from '@/../__mocks__/server'; +import { updateUserAddressWhitespaceHandler } from '@/../__mocks__/updateUser/errorHandlers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { Toaster } from 'react-hot-toast'; +import { expect, test } from 'vitest'; + +import { ManageRepresentativesTable } from '@/components/coordinator/manageRepresentativesTable'; + +test('alerts user if whitespace in wallet address', async () => { + server.use(...updateUserAddressWhitespaceHandler); + const user = userEvent.setup(); + render( + <> + + {}} /> + , + ); + + const editButton = await screen.findByTestId('edit-representative-info-1'); + await user.click(editButton); + + const input = screen.getByDisplayValue('addr1isdufnpoasidjfopmaimdfmopisadj'); + await user.clear(input); + await user.type(input, ' coStakeAddress '); + + const saveButton = await screen.findByTestId('save-representative-info-1'); + await user.click(saveButton); + + // Wait for the error toast to appear + const errorToast = await screen.findByText( + 'Wallet address must not have leading or trailing whitespace.', + ); + expect(errorToast).toBeDefined(); + + const newWalletAddress = await screen.findByText( + 'addr1isdufnpoasidjfopmaimdfmopisadj', + ); + expect(newWalletAddress).toBeDefined(); +}); diff --git a/__tests__/components/coordinator/manageRepresentativesTable/stripsWhitespaceFromWalletAddress.test.tsx b/__tests__/components/coordinator/manageRepresentativesTable/stripsWhitespaceFromWalletAddress.test.tsx new file mode 100644 index 0000000..a0bbb19 --- /dev/null +++ b/__tests__/components/coordinator/manageRepresentativesTable/stripsWhitespaceFromWalletAddress.test.tsx @@ -0,0 +1,33 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { Toaster } from 'react-hot-toast'; +import { expect, test } from 'vitest'; + +import { ManageRepresentativesTable } from '@/components/coordinator/manageRepresentativesTable'; + +test('strips whitespace from wallet address', async () => { + const user = userEvent.setup(); + render( + <> + + {}} /> + , + ); + + const editButton = await screen.findByTestId('edit-representative-info-1'); + await user.click(editButton); + + const input = screen.getByDisplayValue('addr1isdufnpoasidjfopmaimdfmopisadj'); + await user.clear(input); + await user.type(input, ' coStakeAddress '); + + const saveButton = await screen.findByTestId('save-representative-info-1'); + await user.click(saveButton); + + // Wait for the success toast to appear + const successToast = await screen.findByText('User info updated!'); + expect(successToast).toBeDefined(); + + const newWalletAddress = await screen.findByText('coStakeAddress'); + expect(newWalletAddress).toBeDefined(); +}); diff --git a/src/lib/helpers/updateUser.ts b/src/lib/helpers/updateUser.ts index ecbac54..7467727 100644 --- a/src/lib/helpers/updateUser.ts +++ b/src/lib/helpers/updateUser.ts @@ -23,7 +23,7 @@ export async function updateUser( userId: userId, name: name, email: email, - wallet_address: wallet_address, + wallet_address: wallet_address.trim(), }, { headers: { diff --git a/src/pages/api/updateUser.ts b/src/pages/api/updateUser.ts index 85c52f5..e64006a 100644 --- a/src/pages/api/updateUser.ts +++ b/src/pages/api/updateUser.ts @@ -99,6 +99,12 @@ export default async function updateUser( message: 'Wallet address must be less than 100 characters.', }); } + if (wallet_address !== wallet_address.trim()) { + return res.status(400).json({ + userId: BigInt(-1).toString(), + message: 'Wallet address must not have leading or trailing whitespace.', + }); + } const updatedUser = await prisma.user.update({ where: { From 0ddc639c7403d1a4db91c704df2df6849d39bdc2 Mon Sep 17 00:00:00 2001 From: Matthew Laux Date: Tue, 26 Nov 2024 10:27:55 -0600 Subject: [PATCH 11/24] Trim whitespace on BE as well --- __mocks__/updateUser/errorHandlers.ts | 12 ------ ...tsUserIfWhitespaceInWalletAddress.test.tsx | 40 ------------------- src/lib/helpers/updateUser.ts | 2 +- src/pages/api/updateUser.ts | 8 +--- 4 files changed, 2 insertions(+), 60 deletions(-) delete mode 100644 __tests__/components/coordinator/manageRepresentativesTable/alertsUserIfWhitespaceInWalletAddress.test.tsx diff --git a/__mocks__/updateUser/errorHandlers.ts b/__mocks__/updateUser/errorHandlers.ts index c2766a3..10c3d96 100644 --- a/__mocks__/updateUser/errorHandlers.ts +++ b/__mocks__/updateUser/errorHandlers.ts @@ -121,15 +121,3 @@ export const updateUserNotOrganizerHandler = [ ); }), ]; - -export const updateUserAddressWhitespaceHandler = [ - http.post('/api/updateUser', async () => { - return HttpResponse.json( - { - userId: BigInt(-1).toString(), - message: 'Wallet address must not have leading or trailing whitespace.', - }, - { status: 401 }, - ); - }), -]; diff --git a/__tests__/components/coordinator/manageRepresentativesTable/alertsUserIfWhitespaceInWalletAddress.test.tsx b/__tests__/components/coordinator/manageRepresentativesTable/alertsUserIfWhitespaceInWalletAddress.test.tsx deleted file mode 100644 index fb36dd1..0000000 --- a/__tests__/components/coordinator/manageRepresentativesTable/alertsUserIfWhitespaceInWalletAddress.test.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { server } from '@/../__mocks__/server'; -import { updateUserAddressWhitespaceHandler } from '@/../__mocks__/updateUser/errorHandlers'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { Toaster } from 'react-hot-toast'; -import { expect, test } from 'vitest'; - -import { ManageRepresentativesTable } from '@/components/coordinator/manageRepresentativesTable'; - -test('alerts user if whitespace in wallet address', async () => { - server.use(...updateUserAddressWhitespaceHandler); - const user = userEvent.setup(); - render( - <> - - {}} /> - , - ); - - const editButton = await screen.findByTestId('edit-representative-info-1'); - await user.click(editButton); - - const input = screen.getByDisplayValue('addr1isdufnpoasidjfopmaimdfmopisadj'); - await user.clear(input); - await user.type(input, ' coStakeAddress '); - - const saveButton = await screen.findByTestId('save-representative-info-1'); - await user.click(saveButton); - - // Wait for the error toast to appear - const errorToast = await screen.findByText( - 'Wallet address must not have leading or trailing whitespace.', - ); - expect(errorToast).toBeDefined(); - - const newWalletAddress = await screen.findByText( - 'addr1isdufnpoasidjfopmaimdfmopisadj', - ); - expect(newWalletAddress).toBeDefined(); -}); diff --git a/src/lib/helpers/updateUser.ts b/src/lib/helpers/updateUser.ts index 7467727..ecbac54 100644 --- a/src/lib/helpers/updateUser.ts +++ b/src/lib/helpers/updateUser.ts @@ -23,7 +23,7 @@ export async function updateUser( userId: userId, name: name, email: email, - wallet_address: wallet_address.trim(), + wallet_address: wallet_address, }, { headers: { diff --git a/src/pages/api/updateUser.ts b/src/pages/api/updateUser.ts index e64006a..e38456e 100644 --- a/src/pages/api/updateUser.ts +++ b/src/pages/api/updateUser.ts @@ -99,12 +99,6 @@ export default async function updateUser( message: 'Wallet address must be less than 100 characters.', }); } - if (wallet_address !== wallet_address.trim()) { - return res.status(400).json({ - userId: BigInt(-1).toString(), - message: 'Wallet address must not have leading or trailing whitespace.', - }); - } const updatedUser = await prisma.user.update({ where: { @@ -113,7 +107,7 @@ export default async function updateUser( data: { name: name, email: email, - wallet_address: wallet_address, + wallet_address: wallet_address.trim(), }, }); return res.status(200).json({ From 625ec29c6a9d9c2191b0a4f1069f6a9200424243 Mon Sep 17 00:00:00 2001 From: Matthew Laux Date: Tue, 26 Nov 2024 10:37:22 -0600 Subject: [PATCH 12/24] Add custom 404 page --- src/pages/404.tsx | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/pages/404.tsx diff --git a/src/pages/404.tsx b/src/pages/404.tsx new file mode 100644 index 0000000..9a30aac --- /dev/null +++ b/src/pages/404.tsx @@ -0,0 +1,40 @@ +import Head from 'next/head'; +import Link from 'next/link'; +import HomeRounded from '@mui/icons-material/HomeRounded'; +import { Box, Button, Typography } from '@mui/material'; + +export default function Custom404(): JSX.Element { + return ( + <> + + Constitutional Convention Voting App + + + + + + + 404 - Page Not Found + + + + + + + ); +} From fe05fe1856b457a11158177bc8e325a27aadb2e4 Mon Sep 17 00:00:00 2001 From: Justin Schreiner Date: Tue, 26 Nov 2024 10:15:57 -0700 Subject: [PATCH 13/24] add custom header styles to data tables --- src/components/polls/pollCarrousel.tsx | 2 +- .../representatives/representativesTable.tsx | 35 ++++++++++++++++++- .../representatives/votingHistoryTable.tsx | 24 ++++++++++++- src/providers/themeProvider.tsx | 2 +- 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/components/polls/pollCarrousel.tsx b/src/components/polls/pollCarrousel.tsx index 8f41643..51665ba 100644 --- a/src/components/polls/pollCarrousel.tsx +++ b/src/components/polls/pollCarrousel.tsx @@ -93,7 +93,7 @@ export function PollCarrousel(props: Props): JSX.Element { width="100%" alignItems="center" > - + Browse other polls {pollCards} diff --git a/src/components/representatives/representativesTable.tsx b/src/components/representatives/representativesTable.tsx index 8b36a4f..6d836cd 100644 --- a/src/components/representatives/representativesTable.tsx +++ b/src/components/representatives/representativesTable.tsx @@ -30,9 +30,17 @@ export function RepresentativesTable(props: Props): JSX.Element { headerName: 'Name', minWidth: 125, flex: 1, + renderHeader: (): JSX.Element => { + return ( + + Workshop + + ); + }, renderCell: (params): JSX.Element => { return {params.row.name}; }, + disableColumnMenu: true, }, { field: 'Delegate', @@ -41,6 +49,14 @@ export function RepresentativesTable(props: Props): JSX.Element { flex: 1, sortable: false, filterable: false, + disableColumnMenu: true, + renderHeader: (): JSX.Element => { + return ( + + Delegate + + ); + }, renderCell: (params): JSX.Element => { const delegateId = params.row.delegate_id; const delegate = representatives.find((rep) => rep.id === delegateId); @@ -90,6 +106,14 @@ export function RepresentativesTable(props: Props): JSX.Element { flex: 1, sortable: false, filterable: false, + disableColumnMenu: true, + renderHeader: (): JSX.Element => { + return ( + + Alternate + + ); + }, renderCell: (params): JSX.Element => { const alternateId = params.row.alternate_id; const alternate = representatives.find((rep) => rep.id === alternateId); @@ -141,6 +165,14 @@ export function RepresentativesTable(props: Props): JSX.Element { flex: 1, sortable: false, filterable: false, + disableColumnMenu: true, + renderHeader: (): JSX.Element => { + return ( + + Active Voter + + ); + }, renderCell: (params): JSX.Element => { const activeVoterId = params.row.active_voter_id; const activeVoter = representatives.find( @@ -222,7 +254,6 @@ export function RepresentativesTable(props: Props): JSX.Element { display: 'none', }, '.MuiDataGrid-columnHeader': { - backgroundColor: 'rgba(0, 0, 0, 0.1)', fontFamily: 'Montserrat', fontSize: '1.2rem', }, @@ -231,6 +262,8 @@ export function RepresentativesTable(props: Props): JSX.Element { flexDirection: 'column', justifyContent: 'center', }, + border: 'none', + borderRadius: `${theme.shape.borderRadius}px`, }} /> diff --git a/src/components/representatives/votingHistoryTable.tsx b/src/components/representatives/votingHistoryTable.tsx index 25ce03a..78b813c 100644 --- a/src/components/representatives/votingHistoryTable.tsx +++ b/src/components/representatives/votingHistoryTable.tsx @@ -1,6 +1,7 @@ import DoDisturbRounded from '@mui/icons-material/DoDisturbRounded'; import ThumbDownRounded from '@mui/icons-material/ThumbDownRounded'; import ThumbUpRounded from '@mui/icons-material/ThumbUpRounded'; +import { useTheme } from '@mui/material'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import { DataGrid, GridColDef } from '@mui/x-data-grid'; @@ -25,6 +26,8 @@ interface Props { export function VotingHistoryTable(props: Props): JSX.Element { const { userId, votes, polls } = props; + const theme = useTheme(); + const columns: GridColDef[] = [ { field: 'id', headerName: '#' }, { @@ -32,12 +35,26 @@ export function VotingHistoryTable(props: Props): JSX.Element { headerName: 'Name', minWidth: 150, flex: 1, + renderHeader: (): JSX.Element => { + return ( + + Name + + ); + }, }, { field: 'user_vote', headerName: 'User Vote', minWidth: 150, flex: 1, + renderHeader: (): JSX.Element => { + return ( + + User Vote + + ); + }, renderCell: (params): JSX.Element => { const userVoteData = votes.find( (vote) => vote.poll_id === params.row.id, @@ -99,10 +116,15 @@ export function VotingHistoryTable(props: Props): JSX.Element { display: 'none', }, '.MuiDataGrid-columnHeader': { - backgroundColor: 'rgba(0, 0, 0, 0.1)', fontFamily: 'Montserrat', fontSize: '1.2rem', }, + '.MuiDataGrid-cell': { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + }, + borderRadius: `${theme.shape.borderRadius}px`, }} /> diff --git a/src/providers/themeProvider.tsx b/src/providers/themeProvider.tsx index 5840029..2e05fa1 100644 --- a/src/providers/themeProvider.tsx +++ b/src/providers/themeProvider.tsx @@ -17,7 +17,7 @@ export function ColorModeProvider({ }): JSX.Element { const theme = createTheme({ shape: { - borderRadius: 4, + borderRadius: 8, }, palette: { // palette values for light mode From 53404f732ff921f37c2a069a18133de2edde5583 Mon Sep 17 00:00:00 2001 From: Justin Schreiner Date: Tue, 26 Nov 2024 10:57:17 -0700 Subject: [PATCH 14/24] improve voting history table --- .../representatives/votingHistoryTable.tsx | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/representatives/votingHistoryTable.tsx b/src/components/representatives/votingHistoryTable.tsx index 78b813c..9cd64be 100644 --- a/src/components/representatives/votingHistoryTable.tsx +++ b/src/components/representatives/votingHistoryTable.tsx @@ -29,25 +29,44 @@ export function VotingHistoryTable(props: Props): JSX.Element { const theme = useTheme(); const columns: GridColDef[] = [ - { field: 'id', headerName: '#' }, + { + field: 'id', + headerName: '#', + disableColumnMenu: true, + renderHeader: (): JSX.Element => { + return ( + + Poll # + + ); + }, + renderCell: (params): JSX.Element => { + return {params.row.id}; + }, + }, { field: 'name', headerName: 'Name', minWidth: 150, flex: 1, + disableColumnMenu: true, renderHeader: (): JSX.Element => { return ( - Name + Poll Name ); }, + renderCell: (params): JSX.Element => { + return {params.row.name}; + }, }, { field: 'user_vote', headerName: 'User Vote', minWidth: 150, flex: 1, + disableColumnMenu: true, renderHeader: (): JSX.Element => { return ( @@ -118,12 +137,16 @@ export function VotingHistoryTable(props: Props): JSX.Element { '.MuiDataGrid-columnHeader': { fontFamily: 'Montserrat', fontSize: '1.2rem', + backgroundColor: 'rbga(0,0,0,0)', }, '.MuiDataGrid-cell': { display: 'flex', flexDirection: 'column', justifyContent: 'center', }, + '.MuiDataGrid-filler': { + backgroundColor: 'rbga(0,0,0,0)', + }, borderRadius: `${theme.shape.borderRadius}px`, }} /> From 895ea5e34d92f42cf1e4532be8fd1f44f35cc2cf Mon Sep 17 00:00:00 2001 From: Justin Schreiner Date: Tue, 26 Nov 2024 14:52:57 -0700 Subject: [PATCH 15/24] navbar logo --- src/components/layout/navbar.tsx | 67 ++++++++++++++++++++++++++----- src/img/cc-logo.png | Bin 0 -> 2620 bytes 2 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 src/img/cc-logo.png diff --git a/src/components/layout/navbar.tsx b/src/components/layout/navbar.tsx index 32699d7..bd0f135 100644 --- a/src/components/layout/navbar.tsx +++ b/src/components/layout/navbar.tsx @@ -1,4 +1,6 @@ +import Image from 'next/image'; import Link from 'next/link'; +import ccLogo from '@/img/cc-logo.png'; import { Box, Typography } from '@mui/material'; import { paths } from '@/paths'; @@ -20,20 +22,65 @@ export function Navbar(): JSX.Element { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', - p: 2, + py: 2, + px: { + xs: 2, + sm: 4, + md: 10, + lg: 16, + }, backgroundColor: 'rgba(0, 0, 0, 0.1)', height: '75px', }} > - - - Voting Tool - - + + + + Constitutional Convention Logo + + + Constitutional Convention Logo + + + + + + Voting Tool + + + + + ); diff --git a/src/img/cc-logo.png b/src/img/cc-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5ce4afc27267a630fe80658f1ecc7f70732f3eb7 GIT binary patch literal 2620 zcmV-C3d8k@P)m?&^B%>L`S-r?;iU_Y@8tUjuDbc=m{) zZ_B;4d&Xv5wg?yd+cKzQF8}mm|KZLNWjs11%6ybgpCX@mk2J)+;ahg8cKBK`BJwIW zhOXr!(C)~`y(2G$D9NHs(0hn^PldCLTy6)3{EZ0x*BzL#K>5_q-It>_fNNJp#>WLkPH^yt*Taq3?~ zdrIm%M+LNkE-%mq@;?Gy`5qwuD%0pP=AZ?X#r7+04f4K7enapQ)9s~fq1~uk1l~45 z&p9X#IQ)5;Ytug#V8E@RYe(y7P(XuMhPM{_1JDq)!yDzh8tTcZzfKr`&{>d&dKOOp z^@YgfWgT>Cp5#{v`>F$#hOYgAONoxZzLTIAFtQ;hF8lLVF+F`amR0hkAupm%LsiQ4ZAuXy7UvT4a2_-(q}rN zH75HvdA@OqvMq})-Gw~f%afh_S3`R?UXA3la_krfpz)f79TL}nU%T`@m63M;Xl!@DqgOns9OOZGwk&`zc%;wGlFp*xSEL z?|&$oaC@2AmfnC6<+&4|;WFwUFS6w;Q%Q0dm$lMHl=y%5T*_LfNEBiE`@|chqn}+w zuVjt#WcWOcy_p}Rd8pFR2hfJP3qX8-@UxZ6o&f=py z3t2L4LFT*g0$d%G*IiwDt>5Xg8@-dyW%uAs2T$)?aY}X~&VQ11X(5a4DDpTC_SVKK z1z+X)v!Saa{VvZ2^ofR7%eqSoiNWbqZ=Jc4_LPQS9n>2oz8BO!g?X9ZnEV$(+NZKg zldZPgC(3fU4_+Q!Hm}-mQDk!}l$Kf=ya=!pW9P$A1ko@i7lF2&AbSO6w{+P(-$R`% z;MKa1!lOHFMoUhZ$<$S&o|p> z@_F#sK;Fi*8*OPrbFI(|r!TI0(Jc(Pb#zhLmC^es-QjxB9n z?8x?|Ql0{6*zcv`i;gb(LL80WG-fl!B4i-S@wCoU&v}S9M}eHr(*rv)PwAh+Oo(Av z{>cd?As_Ro5f9#xs2bBbVp~ETCzBU*GOg@{jKZ)dc}D8E+Cua>CHS;Gxwc-BuvL_g zFS6xWyD;C=4RCVXD_*Wa*qZuBeK@2)d6k>DAZjBBrWd-5z-9P>AD9W=B=s&9gppBo z1Ysz;j*jcSlHg&Y~lPEE++=R*;wclh>YT_Oh4Xg`If@R&$-jmm7bd( zbG#n3{M_j<7`9)Pw5KGgk-vHiJx$oEkxawC*`qJGrojv&-Ww`c^0w(f~$4N1-F#u9ppBfC<@KDZ4ZD3hC4zO-kUy`YWI#(1B{9 zMbn<6slUd$Xe2xYJdB4*Hqv|7>{-l9wC8|ArZd9`_E9mBmYg&(gQ067ej4A2K(R_=>)4g5HaF zpTvm|i6Tt@HL+_}>Xr+97wB_+E{*btl^o9D< zc7&vSyqLZ>ik+UbtN)Y0^&Ue09;3dDdS@|vwm+lZCd_n(zW{oUz7GCnk1S-C;n|fP z?U(RObaLK{oUg_=xQ{-01H;&TP&PC-2pv?X^&QaWHryLkb!}M_Eb2#eb73ww@JQz^ z18|y0{hNf$*CwT2KDNX75_oIfb<*ueb9&py{m~riNr@kS zfxd_`qi1Q-8u+x1!qIym*Oak!E{$H-$O!eL7w4;uViS!gw~p?0qCI1~_?kw3#zW3* zC$z0;=d9%pU7|Vl7}J`}&w7D=Oq7`usISOJW%vpj2J}ff%8n#w7}_8N19{ltM8hC- zqzr(swdJWF>n)x3w_YBN2kP-=8RCij&{J*X>-~x`2PM(JwaMtJ^sVU7Qa>)5l2d2u zzB=gab9a&>$3^Dm(F|EdxJ>h$c&MNH#|?*I{!l=8N`5ZPkKX9^Cj{P~322`82M4+a zPhlrVlPY7*pB%V4CF+kRn=i9wG2LkO2{L770gQ`cJBxesvcO8w(li{$M! e@e4lk-2VY@zit_d0~=ug0000 Date: Tue, 26 Nov 2024 15:15:24 -0700 Subject: [PATCH 16/24] connect wallet styling --- .../buttons/connectWalletButton.tsx | 71 +++++++++++++++---- src/pages/api/auth/[...nextauth].ts | 5 ++ src/pages/index.tsx | 2 +- src/providers/themeProvider.tsx | 2 +- 4 files changed, 64 insertions(+), 16 deletions(-) diff --git a/src/components/buttons/connectWalletButton.tsx b/src/components/buttons/connectWalletButton.tsx index b7fd30f..0478339 100644 --- a/src/components/buttons/connectWalletButton.tsx +++ b/src/components/buttons/connectWalletButton.tsx @@ -17,11 +17,16 @@ import { paths } from '@/paths'; import { connectWallet } from '@/lib/connectWallet'; import { getUser } from '@/lib/helpers/getUser'; +interface Props { + isHomepage?: boolean; +} + /** * A button to connect a wallet to a variety of cip-30 compatible wallets * @returns Connect Wallet Button */ -export function ConnectWalletButton(): JSX.Element { +export function ConnectWalletButton(props: Props): JSX.Element { + const { isHomepage } = props; const [open, setOpen] = useState(false); const [anchorEl, setAnchorEl] = useState(null); const [connecting, setConnecting] = useState(false); @@ -189,8 +194,19 @@ export function ConnectWalletButton(): JSX.Element { )} - {session.status === 'authenticated' && ( + {isHomepage ? ( + <> + ) : session.status === 'authenticated' ? ( Wallet connected + ) : ( + + Wallet not connected + )} + {wallets} ); diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index 8620890..caffa13 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -51,6 +51,7 @@ export const authOptions: NextAuthOptions = { isCoordinator: user.is_convention_organizer, isDelegate: user.is_delegate, isAlternate: user.is_alternate, + name: user.name, }; } else { return null; @@ -65,6 +66,7 @@ export const authOptions: NextAuthOptions = { async jwt({ token, user }) { if (user) { token.stakeAddress = user.stakeAddress; + token.name = user.name; token.walletName = user.walletName; token.isCoordinator = user.isCoordinator; token.isDelegate = user.isDelegate; @@ -75,6 +77,9 @@ export const authOptions: NextAuthOptions = { async session({ session, token }) { if (typeof token.walletName === 'string') session.user.walletName = token.walletName; + if (typeof token.name === 'string') { + session.user.name = token.name; + } if (typeof token.isCoordinator === 'boolean') session.user.isCoordinator = token.isCoordinator; if (typeof token.isDelegate === 'boolean') diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 698a535..56300fd 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -77,7 +77,7 @@ export default function Home(props: Props): JSX.Element { Connect a wallet to cast your vote: - + diff --git a/src/providers/themeProvider.tsx b/src/providers/themeProvider.tsx index e44d460..22f4cd1 100644 --- a/src/providers/themeProvider.tsx +++ b/src/providers/themeProvider.tsx @@ -89,7 +89,7 @@ export function ColorModeProvider({ backgroundColor: 'rgba(77,166,77, 0.1)', }, outlinedWarning: { - border: '1px solid rgb(245, 148, 77)', + border: '1px solid rgba(255,255,255, .10)', backgroundColor: 'rgba(245, 148, 77, 0.1)', }, outlinedPrimary: { From 85dd3d78e581f00978400bc3bba274d112cdda12 Mon Sep 17 00:00:00 2001 From: Justin Schreiner Date: Tue, 26 Nov 2024 15:23:05 -0700 Subject: [PATCH 17/24] hero content ui --- src/components/buttons/connectWalletButton.tsx | 7 ++++++- src/components/coordinator/manageActivePowerTable.tsx | 4 ++-- .../coordinator/manageRepresentativesTable.tsx | 4 ++-- src/components/polls/coordinatorViewVotes.tsx | 2 +- .../representatives/representativesTable.tsx | 4 ++-- src/components/representatives/votingHistoryTable.tsx | 4 ++-- src/pages/_document.tsx | 6 +----- src/pages/index.tsx | 10 +++++----- src/providers/themeProvider.tsx | 9 ++++----- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/components/buttons/connectWalletButton.tsx b/src/components/buttons/connectWalletButton.tsx index 0478339..36f3051 100644 --- a/src/components/buttons/connectWalletButton.tsx +++ b/src/components/buttons/connectWalletButton.tsx @@ -203,7 +203,12 @@ export function ConnectWalletButton(props: Props): JSX.Element { : 'warning' } sx={{ - px: isHomepage ? 16 : 2, + px: isHomepage + ? { + xs: 12, + sm: 16, + } + : 2, py: isHomepage ? 2 : 1, borderRadius: '40px', }} diff --git a/src/components/coordinator/manageActivePowerTable.tsx b/src/components/coordinator/manageActivePowerTable.tsx index cd5f0af..463a902 100644 --- a/src/components/coordinator/manageActivePowerTable.tsx +++ b/src/components/coordinator/manageActivePowerTable.tsx @@ -296,7 +296,7 @@ export function ManageActivePowerTable(props: Props): JSX.Element { - diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 56300fd..e6876ac 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -40,7 +40,7 @@ export default function Home(props: Props): JSX.Element { />
- + )} - + Welcome to the Constitutional Convention Voting Tool Are you a delegate? - + Connect a wallet to cast your vote: - + Date: Tue, 26 Nov 2024 15:31:43 -0700 Subject: [PATCH 18/24] pollCard ui --- src/components/polls/pollCard.tsx | 4 ++-- src/pages/index.tsx | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/polls/pollCard.tsx b/src/components/polls/pollCard.tsx index 11a4b11..5b2f9ad 100644 --- a/src/components/polls/pollCard.tsx +++ b/src/components/polls/pollCard.tsx @@ -1,6 +1,6 @@ import Link from 'next/link'; import { pollPhases } from '@/constants/pollPhases'; -import LaunchRounded from '@mui/icons-material/LaunchRounded'; +import { ArrowForwardRounded } from '@mui/icons-material'; import { useTheme } from '@mui/material'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; @@ -54,7 +54,7 @@ export function PollCard(props: Props): JSX.Element { mt={2} > View - + diff --git a/src/pages/index.tsx b/src/pages/index.tsx index e6876ac..1fbd416 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -78,7 +78,9 @@ export default function Home(props: Props): JSX.Element { Connect a wallet to cast your vote: - + {session.status === 'unauthenticated' && ( + + )} Date: Tue, 26 Nov 2024 15:49:14 -0700 Subject: [PATCH 19/24] center polls --- src/components/polls/pollCarrousel.tsx | 9 ++++++++- src/components/polls/pollList.tsx | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/polls/pollCarrousel.tsx b/src/components/polls/pollCarrousel.tsx index 8f41643..6d61a29 100644 --- a/src/components/polls/pollCarrousel.tsx +++ b/src/components/polls/pollCarrousel.tsx @@ -63,8 +63,15 @@ export function PollCarrousel(props: Props): JSX.Element { }} flexDirection="column" width="100%" + justifyContent="center" > - + {pollsToDisplay.map((poll) => { return ( )} - + {pollCards} From 0931f0dd4419bc109301f32a9bb11db3eecc8bae Mon Sep 17 00:00:00 2001 From: Justin Schreiner Date: Tue, 26 Nov 2024 15:56:17 -0700 Subject: [PATCH 20/24] hide footer in tables --- .../coordinator/manageActivePowerTable.tsx | 1 + .../coordinator/manageRepresentativesTable.tsx | 1 + src/components/polls/coordinatorViewVotes.tsx | 1 + .../representatives/representativesTable.tsx | 13 +------------ .../representatives/votingHistoryTable.tsx | 1 + 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/components/coordinator/manageActivePowerTable.tsx b/src/components/coordinator/manageActivePowerTable.tsx index 463a902..a40fa77 100644 --- a/src/components/coordinator/manageActivePowerTable.tsx +++ b/src/components/coordinator/manageActivePowerTable.tsx @@ -307,6 +307,7 @@ export function ManageActivePowerTable(props: Props): JSX.Element { onRowModesModelChange={handleRowModesModelChange} onRowEditStop={handleRowEditStop} processRowUpdate={processRowUpdate} + hideFooter slots={{ toolbar: GridToolbar }} slotProps={{ toolbar: { diff --git a/src/components/coordinator/manageRepresentativesTable.tsx b/src/components/coordinator/manageRepresentativesTable.tsx index 1760ae3..4baee61 100644 --- a/src/components/coordinator/manageRepresentativesTable.tsx +++ b/src/components/coordinator/manageRepresentativesTable.tsx @@ -200,6 +200,7 @@ export function ManageRepresentativesTable(props: Props): JSX.Element { rows={representatives} columns={columns} editMode="row" + hideFooter rowModesModel={rowModesModel} onRowModesModelChange={handleRowModesModelChange} onRowEditStop={handleRowEditStop} diff --git a/src/components/polls/coordinatorViewVotes.tsx b/src/components/polls/coordinatorViewVotes.tsx index 17b7dd6..192037e 100644 --- a/src/components/polls/coordinatorViewVotes.tsx +++ b/src/components/polls/coordinatorViewVotes.tsx @@ -123,6 +123,7 @@ export function CoordinatorViewVotes(props: Props): JSX.Element { row.id} sx={{ diff --git a/src/components/representatives/representativesTable.tsx b/src/components/representatives/representativesTable.tsx index a4883fa..861e0d3 100644 --- a/src/components/representatives/representativesTable.tsx +++ b/src/components/representatives/representativesTable.tsx @@ -1,5 +1,4 @@ import Link from 'next/link'; -import LaunchRounded from '@mui/icons-material/LaunchRounded'; import { Box, useTheme } from '@mui/material'; import Typography from '@mui/material/Typography'; import { DataGrid, GridColDef } from '@mui/x-data-grid'; @@ -77,7 +76,6 @@ export function RepresentativesTable(props: Props): JSX.Element { {delegate?.name} - ); @@ -128,7 +126,6 @@ export function RepresentativesTable(props: Props): JSX.Element { > {alternate?.name} - ); @@ -176,7 +173,6 @@ export function RepresentativesTable(props: Props): JSX.Element { }} > {activeVoter?.name} - ); @@ -200,14 +196,7 @@ export function RepresentativesTable(props: Props): JSX.Element { (workshop) => workshop.name !== 'Convention Organizer', )} columns={columns} - initialState={{ - pagination: { - paginationModel: { - pageSize: 100, - }, - }, - }} - pageSizeOptions={[25, 50, 100]} + hideFooter columnVisibilityModel={{ id: false, }} diff --git a/src/components/representatives/votingHistoryTable.tsx b/src/components/representatives/votingHistoryTable.tsx index 8050bb0..2d3a7a3 100644 --- a/src/components/representatives/votingHistoryTable.tsx +++ b/src/components/representatives/votingHistoryTable.tsx @@ -102,6 +102,7 @@ export function VotingHistoryTable(props: Props): JSX.Element { Date: Tue, 26 Nov 2024 16:03:41 -0700 Subject: [PATCH 21/24] abbreviate names in table --- .../representatives/representativesTable.tsx | 10 +++++++--- src/lib/helpers/abbreviateName.ts | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 src/lib/helpers/abbreviateName.ts diff --git a/src/components/representatives/representativesTable.tsx b/src/components/representatives/representativesTable.tsx index 861e0d3..95835c0 100644 --- a/src/components/representatives/representativesTable.tsx +++ b/src/components/representatives/representativesTable.tsx @@ -5,6 +5,7 @@ import { DataGrid, GridColDef } from '@mui/x-data-grid'; import type { User, Workshop } from '@/types'; import { paths } from '@/paths'; +import { abbreviateName } from '@/lib/helpers/abbreviateName'; interface Props { representatives: User[]; @@ -44,6 +45,7 @@ export function RepresentativesTable(props: Props): JSX.Element { const delegateId = params.row.delegate_id; const delegate = representatives.find((rep) => rep.id === delegateId); const activeVoterId = params.row.active_voter_id; + const name = abbreviateName(delegate?.name || ''); return ( - {delegate?.name} + {name} @@ -92,6 +94,7 @@ export function RepresentativesTable(props: Props): JSX.Element { const alternateId = params.row.alternate_id; const alternate = representatives.find((rep) => rep.id === alternateId); const activeVoterId = params.row.active_voter_id; + const name = abbreviateName(alternate?.name || ''); return ( - {alternate?.name} + {name} @@ -143,6 +146,7 @@ export function RepresentativesTable(props: Props): JSX.Element { const activeVoter = representatives.find( (rep) => rep.id === activeVoterId, ); + const name = abbreviateName(activeVoter?.name || ''); return ( - {activeVoter?.name} + {name} ); diff --git a/src/lib/helpers/abbreviateName.ts b/src/lib/helpers/abbreviateName.ts new file mode 100644 index 0000000..1a08712 --- /dev/null +++ b/src/lib/helpers/abbreviateName.ts @@ -0,0 +1,14 @@ +export function abbreviateName(fullName: string) { + const nameParts = fullName.split(' '); + if (nameParts.length < 2) { + return fullName; // If there's no last name, return the original name + } + + const lastName = nameParts[nameParts.length - 1]; + const initials = nameParts + .slice(0, -1) + .map((name) => name[0].toUpperCase() + '.') + .join(''); + + return `${initials} ${lastName}`; +} From 4909c38866dd0c0436857934a32900cc03290180 Mon Sep 17 00:00:00 2001 From: Justin Schreiner Date: Tue, 26 Nov 2024 16:15:33 -0700 Subject: [PATCH 22/24] update browse polls wording --- .../buttons/downloadPollVotesButton.tsx | 5 +++-- src/components/buttons/voteOnPollButtons.tsx | 17 ++++++++++++----- src/components/polls/pollCarrousel.tsx | 5 +++-- src/components/polls/pollResults.tsx | 8 ++++++-- src/pages/polls/[pollId]/index.tsx | 6 +++++- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/components/buttons/downloadPollVotesButton.tsx b/src/components/buttons/downloadPollVotesButton.tsx index 3acf3bc..60acb66 100644 --- a/src/components/buttons/downloadPollVotesButton.tsx +++ b/src/components/buttons/downloadPollVotesButton.tsx @@ -33,11 +33,12 @@ export function DownloadPollVotesButton(props: Props): JSX.Element { return ( ); } diff --git a/src/components/buttons/voteOnPollButtons.tsx b/src/components/buttons/voteOnPollButtons.tsx index cfe109a..c955c35 100644 --- a/src/components/buttons/voteOnPollButtons.tsx +++ b/src/components/buttons/voteOnPollButtons.tsx @@ -132,7 +132,9 @@ export function VoteOnPollButtons(props: Props): JSX.Element { variant="outlined" color="success" sx={{ - width: '150px', + width: '170px', + py: 2, + borderRadius: '60px !important', }} endIcon={} size="large" @@ -140,13 +142,16 @@ export function VoteOnPollButtons(props: Props): JSX.Element { disabled={disabled || !isActiveVoter} data-testid="vote-yes-button" > - Yes + Vote Yes ); } diff --git a/src/components/representatives/votingHistoryTable.tsx b/src/components/representatives/votingHistoryTable.tsx index 2d3a7a3..1c1318d 100644 --- a/src/components/representatives/votingHistoryTable.tsx +++ b/src/components/representatives/votingHistoryTable.tsx @@ -89,8 +89,8 @@ export function VotingHistoryTable(props: Props): JSX.Element { return ( - - Voting History + + History diff --git a/src/pages/representatives/[userId]/index.tsx b/src/pages/representatives/[userId]/index.tsx index 61434fa..1a3651a 100644 --- a/src/pages/representatives/[userId]/index.tsx +++ b/src/pages/representatives/[userId]/index.tsx @@ -128,20 +128,12 @@ export default function Representative(props: Props): JSX.Element { color={theme.palette.text.primary} > - + {userVotes.length} vote - {userVotes.length === 1 ? '' : 's'} + {userVotes.length === 1 ? '' : 's'} cast - + {workshopName || 'Failed to retrieve workshop'} @@ -163,6 +155,12 @@ export default function Representative(props: Props): JSX.Element { xs: 12, md: 6, }} + sx={{ + mt: { + xs: 6, + md: 0, + }, + }} > Date: Tue, 26 Nov 2024 16:29:45 -0700 Subject: [PATCH 24/24] update favicon --- public/cardano.png | Bin 0 -> 7930 bytes src/pages/404.tsx | 5 +---- src/pages/index.tsx | 5 +---- src/pages/polls/[pollId]/index.tsx | 5 +---- src/pages/polls/new.tsx | 5 +---- src/pages/representatives/[userId]/index.tsx | 5 +---- src/pages/representatives/manage/index.tsx | 2 +- 7 files changed, 6 insertions(+), 21 deletions(-) create mode 100644 public/cardano.png diff --git a/public/cardano.png b/public/cardano.png new file mode 100644 index 0000000000000000000000000000000000000000..dbdcf49413eb98eab3ffad225ddd9fb2cbfa7805 GIT binary patch literal 7930 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 WVG&eu>8Hp4PU~qIYF4V>B>opyZ7MVX literal 0 HcmV?d00001 diff --git a/src/pages/404.tsx b/src/pages/404.tsx index 9a30aac..a0b861f 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -10,10 +10,7 @@ export default function Custom404(): JSX.Element { Constitutional Convention Voting App - + - +
diff --git a/src/pages/polls/[pollId]/index.tsx b/src/pages/polls/[pollId]/index.tsx index f44418f..8c10973 100644 --- a/src/pages/polls/[pollId]/index.tsx +++ b/src/pages/polls/[pollId]/index.tsx @@ -142,10 +142,7 @@ export default function ViewPoll(props: Props): JSX.Element { content="Voting app to be used by delegates at the Cardano Constitutional Convention in Buenos Aires to ratify the initial constitution. This voting app was commissioned by Intersect." /> - +
diff --git a/src/pages/polls/new.tsx b/src/pages/polls/new.tsx index 28d32bd..48d9591 100644 --- a/src/pages/polls/new.tsx +++ b/src/pages/polls/new.tsx @@ -38,10 +38,7 @@ export default function NewPoll(props: Props): JSX.Element { content="Voting app to be used by delegates at the Cardano Constitutional Convention in Buenos Aires to ratify the initial constitution. This voting app was commissioned by Intersect." /> - +
diff --git a/src/pages/representatives/[userId]/index.tsx b/src/pages/representatives/[userId]/index.tsx index 1a3651a..813269d 100644 --- a/src/pages/representatives/[userId]/index.tsx +++ b/src/pages/representatives/[userId]/index.tsx @@ -55,10 +55,7 @@ export default function Representative(props: Props): JSX.Element { content="Voting app to be used by delegates at the Cardano Constitutional Convention in Buenos Aires to ratify the initial constitution. This voting app was commissioned by Intersect." /> - +
diff --git a/src/pages/representatives/manage/index.tsx b/src/pages/representatives/manage/index.tsx index daf9b76..7d3aff7 100644 --- a/src/pages/representatives/manage/index.tsx +++ b/src/pages/representatives/manage/index.tsx @@ -28,7 +28,7 @@ export default function ManageRepresentatives(): JSX.Element { content="Voting app to be used by delegates at the Cardano Constitutional Convention in Buenos Aires to ratify the initial constitution. This voting app was commissioned by Intersect." /> - +