diff --git a/packages/webapp/src/pages/election/round-video-upload.tsx b/packages/webapp/src/pages/election/round-video-upload.tsx
index 48d7c8cc1..70701d7e2 100644
--- a/packages/webapp/src/pages/election/round-video-upload.tsx
+++ b/packages/webapp/src/pages/election/round-video-upload.tsx
@@ -3,301 +3,279 @@ import { RiVideoUploadLine } from "react-icons/ri";
import dayjs from "dayjs";
import {
- SideNavLayout,
- isValidDelegate,
- onError,
- uploadIpfsFileWithTransaction,
- uploadToIpfs,
- useCurrentElection,
- useUALAccount,
- useElectionState,
- useCurrentMemberElectionVotingData,
+ SideNavLayout,
+ isValidDelegate,
+ onError,
+ uploadIpfsFileWithTransaction,
+ uploadToIpfs,
+ useCurrentElection,
+ useUALAccount,
+ useElectionState,
+ useCurrentMemberElectionVotingData
} from "_app";
import {
- Container,
- Text,
- Expander,
- Heading,
- Loader,
- VideoSubmissionFormAndPreview,
- VideoSubmissionPhase,
+ Container,
+ Text,
+ Expander,
+ Heading,
+ Loader,
+ VideoSubmissionFormAndPreview,
+ VideoSubmissionPhase
} from "_app/ui";
import {
- ElectionStatus,
- ErrorLoadingElection,
- setElectionRoundVideo,
+ ElectionStatus,
+ ErrorLoadingElection,
+ setElectionRoundVideo
} from "elections";
import { RoundHeader } from "elections/components/ongoing-election-components";
import { MemberGateContainer } from "members";
import { MemberNFT } from "nfts/interfaces";
export const RoundVideoUploadPage = () => {
- const {
- data: currentElection,
- isLoading: isLoadingElection,
- isError: isErrorElection,
- } = useCurrentElection();
- const {
- data: electionState,
- isLoading: isLoadingElectionState,
- isError: isErrorElectionState,
- } = useElectionState();
+ const {
+ data: currentElection,
+ isLoading: isLoadingElection,
+ isError: isErrorElection
+ } = useCurrentElection();
+ const {
+ data: electionState,
+ isLoading: isLoadingElectionState,
+ isError: isErrorElectionState
+ } = useElectionState();
- const isLoading = isLoadingElection || isLoadingElectionState;
- const isError = isErrorElection || isErrorElectionState;
+ const isLoading = isLoadingElection || isLoadingElectionState;
+ const isError = isErrorElection || isErrorElectionState;
- if (isLoading) return ;
- if (isError || !currentElection) return ;
+ if (isLoading) return ;
+ if (isError || !currentElection) return ;
- const uploadLimitTime = electionState
- ? dayjs(electionState.last_election_time + "Z").add(2, "weeks")
- : dayjs().add(1, "day");
+ const uploadLimitTime = electionState
+ ? dayjs(electionState.last_election_time + "Z").add(2, "weeks")
+ : dayjs().add(1, "day");
- const isUploadExpired =
- dayjs().isAfter(uploadLimitTime) &&
- currentElection?.electionState === ElectionStatus.Registration;
+ const isUploadExpired =
+ dayjs().isAfter(uploadLimitTime) &&
+ currentElection?.electionState === ElectionStatus.Registration;
- return (
-
-
-
- Election video upload service
-
-
- {isUploadExpired ? (
-
- Election is complete and upload videos time is
- expired.
-
- ) : (
- <>
-
- Election video files are typically large and
- require many minutes to upload completely. Do
- not close this tab during the upload. A
- confirmation will be displayed when your upload
- is complete.
-
-
- Be patient, and remember you have 2 weeks from
- the beginning of the election to complete your
- election video uploads.
-
- >
- )}
-
-
- {!isUploadExpired && (
-
- )}
-
-
-
- );
+ return (
+
+
+
+ Election video upload service
+
+
+ {isUploadExpired ? (
+ Election is complete and upload videos time is expired.
+ ) : (
+ <>
+
+ Election video files are typically large and require many
+ minutes to upload completely. Do not close this tab during the
+ upload. A confirmation will be displayed when your upload is
+ complete.
+
+
+ Be patient, and remember you have 2 weeks from the beginning of
+ the election to complete your election video uploads.
+
+ >
+ )}
+
+
+ {!isUploadExpired && (
+
+ )}
+
+
+
+ );
};
export default RoundVideoUploadPage;
const RoundVideoUploadList = ({ electionState }: { electionState: string }) => {
- const [ualAccount] = useUALAccount();
- const {
- data: currentMemberElectionVotingData,
- isLoading: isLoadingCurrentMemberElectionVotingData,
- isError: isErrorCurrentMemberElectionVotingData,
- } = useCurrentMemberElectionVotingData(ualAccount?.accountName);
- const [videoSubmissionPhase, setVideoSubmissionPhase] = useState<
- VideoSubmissionPhase | undefined
- >(undefined);
- const [uploadCompleteMessage, setUploadCompleteMessage] = useState({
- roundIndex: 0,
- message: "",
- });
- const [uploadErrorMessage, setUploadErrorMessage] = useState({
- roundIndex: 0,
- message: "",
- });
+ const [ualAccount] = useUALAccount();
+ const {
+ data: currentMemberElectionVotingData,
+ isLoading: isLoadingCurrentMemberElectionVotingData,
+ isError: isErrorCurrentMemberElectionVotingData
+ } = useCurrentMemberElectionVotingData(ualAccount?.accountName);
+ const [videoSubmissionPhase, setVideoSubmissionPhase] = useState<
+ VideoSubmissionPhase | undefined
+ >(undefined);
+ const [uploadCompleteMessage, setUploadCompleteMessage] = useState({
+ roundIndex: 0,
+ message: ""
+ });
+ const [uploadErrorMessage, setUploadErrorMessage] = useState({
+ roundIndex: 0,
+ message: ""
+ });
- if (isLoadingCurrentMemberElectionVotingData) {
- return ;
- } else if (isErrorCurrentMemberElectionVotingData) {
- return ;
- }
+ if (isLoadingCurrentMemberElectionVotingData) {
+ return ;
+ } else if (isErrorCurrentMemberElectionVotingData) {
+ return ;
+ }
- if (!currentMemberElectionVotingData?.votes.length) {
- return (
-
-
- It appears you haven't participated in any election round.
- Nothing to upload.
-
-
- );
- }
+ if (!currentMemberElectionVotingData?.votes.length) {
+ return (
+
+
+ It appears you haven't participated in any election round. Nothing to
+ upload.
+
+
+ );
+ }
- const resetErrorMessage = () => {
- setUploadErrorMessage({
- roundIndex: 0,
- message: "",
- });
- };
+ const resetErrorMessage = () => {
+ setUploadErrorMessage({
+ roundIndex: 0,
+ message: ""
+ });
+ };
- const submitElectionRoundVideo = (roundIndex: number) => async (
- videoFile: File
- ) => {
- resetErrorMessage();
- try {
- setVideoSubmissionPhase("uploading");
- const videoHash = await uploadToIpfs(videoFile);
+ const submitElectionRoundVideo = (roundIndex: number) => async (
+ videoFile: File
+ ) => {
+ resetErrorMessage();
+ try {
+ setVideoSubmissionPhase("uploading");
+ const videoHash = await uploadToIpfs(videoFile);
- const authorizerAccount = ualAccount.accountName;
- const transaction = setElectionRoundVideo(
- authorizerAccount,
- roundIndex,
- videoHash
- );
- console.info(transaction);
- setVideoSubmissionPhase("signing");
- const signedTrx = await ualAccount.signTransaction(transaction, {
- broadcast: false,
- expireSeconds: 1 * 60 * 60, // 1 hour (max expiration)
- });
- console.info("electvideo trx", signedTrx);
+ const authorizerAccount = ualAccount.accountName;
+ const transaction = setElectionRoundVideo(
+ authorizerAccount,
+ roundIndex,
+ videoHash
+ );
+ console.info(transaction);
+ setVideoSubmissionPhase("signing");
+ const signedTrx = await ualAccount.signTransaction(transaction, {
+ broadcast: false,
+ expireSeconds: 1 * 60 * 60 // 1 hour (max expiration)
+ });
+ console.info("electvideo trx", signedTrx);
- setVideoSubmissionPhase("finishing");
- await uploadIpfsFileWithTransaction(
- signedTrx,
- videoHash,
- videoFile
- );
+ setVideoSubmissionPhase("finishing");
+ await uploadIpfsFileWithTransaction(signedTrx, videoHash, videoFile);
- setVideoSubmissionPhase(undefined);
- setUploadCompleteMessage({
- roundIndex,
- message: "Election video uploaded successfully!",
- });
- } catch (error) {
- onError(error as Error, "Error uploading election round video");
- setVideoSubmissionPhase(undefined);
- setUploadErrorMessage({
- roundIndex,
- message:
- "There was an error uploading your video. Please try again.",
- });
- }
- };
+ setVideoSubmissionPhase(undefined);
+ setUploadCompleteMessage({
+ roundIndex,
+ message: "Election video uploaded successfully!"
+ });
+ } catch (error) {
+ onError(error as Error, "Error uploading election round video");
+ setVideoSubmissionPhase(undefined);
+ setUploadErrorMessage({
+ roundIndex,
+ message: "There was an error uploading your video. Please try again."
+ });
+ }
+ };
- return (
- <>
- {currentMemberElectionVotingData.votes.map((vote) => {
- return (
-
-
- }
- startExpanded
- type="inactive"
- >
-
-
- }
- submitButtonText="Upload meeting video"
- title="Upload your election video recording."
- subtitle=""
- action="electvideo"
- uploadErrorMessage={
- uploadErrorMessage.roundIndex ===
- vote.roundIndex
- ? uploadErrorMessage.message
- : undefined
- }
- uploadCompleteMessage={
- uploadCompleteMessage.roundIndex ===
- vote.roundIndex
- ? uploadCompleteMessage.message
- : undefined
- }
- />
-
-
-
- );
- })}
- >
- );
+ return (
+ <>
+ {currentMemberElectionVotingData.votes.map((vote) => {
+ return (
+
+
+ }
+ startExpanded
+ type="inactive"
+ >
+
+
+ }
+ submitButtonText="Upload meeting video"
+ title="Upload your election video recording."
+ subtitle=""
+ action="electvideo"
+ uploadErrorMessage={
+ uploadErrorMessage.roundIndex === vote.roundIndex
+ ? uploadErrorMessage.message
+ : undefined
+ }
+ uploadCompleteMessage={
+ uploadCompleteMessage.roundIndex === vote.roundIndex
+ ? uploadCompleteMessage.message
+ : undefined
+ }
+ />
+
+
+
+ );
+ })}
+ >
+ );
};
const LoaderSection = () => (
-
-
-
+
+
+
);
interface HeaderProps {
- isOngoing: boolean;
- roundIndex: number;
- winner?: MemberNFT;
- roundStartTime?: dayjs.Dayjs;
- roundEndTime?: dayjs.Dayjs;
+ isOngoing: boolean;
+ roundIndex: number;
+ winner?: MemberNFT;
+ roundStartTime?: dayjs.Dayjs;
+ roundEndTime?: dayjs.Dayjs;
}
const Header = ({
- isOngoing,
- roundIndex,
- winner,
- roundStartTime,
- roundEndTime,
+ isOngoing,
+ roundIndex,
+ winner,
+ roundStartTime,
+ roundEndTime
}: HeaderProps) => {
- const subText =
- winner && isValidDelegate(winner.account)
- ? `Delegate: ${winner.name}`
- : roundStartTime && roundEndTime
- ? `${roundStartTime
- .utc()
- .format("LT")} - ${roundEndTime.utc().format("LT")} UTC`
- : "Consensus not achieved";
+ const subText =
+ winner && isValidDelegate(winner.account)
+ ? `Delegate: ${winner.name}`
+ : roundStartTime && roundEndTime
+ ? `${roundStartTime.utc().format("LT")} - ${roundEndTime
+ .utc()
+ .format("LT")} UTC`
+ : "Consensus not achieved";
- return (
-
- Round {roundIndex + 1} {!isOngoing && "completed"}
-
- }
- sublineComponent={
-
- {subText}
-
- }
- />
- );
+ return (
+
+ Round {roundIndex + 1} {!isOngoing && "completed"}
+
+ }
+ sublineComponent={
+
+ {subText}
+
+ }
+ />
+ );
};
diff --git a/packages/webapp/src/pages/election/stats.tsx b/packages/webapp/src/pages/election/stats.tsx
index cb0359fc6..c7bd07b91 100644
--- a/packages/webapp/src/pages/election/stats.tsx
+++ b/packages/webapp/src/pages/election/stats.tsx
@@ -1,22 +1,22 @@
import React from "react";
import {
- RoundBasicQueryData,
- RoundGroupQueryData,
- RoundWithGroupQueryData,
- SideNavLayout,
- useCountdown,
- useCurrentGlobalElectionData,
+ RoundBasicQueryData,
+ RoundGroupQueryData,
+ RoundWithGroupQueryData,
+ SideNavLayout,
+ useCountdown,
+ useCurrentGlobalElectionData
} from "_app";
import { Container, Heading, Loader, Expander, Text } from "_app/ui";
import {
- Avatars,
- DelegateChip,
- ErrorLoadingElection,
- RoundStage,
- useRoundStageTimes,
- VoteData,
- VotingMemberChip,
+ Avatars,
+ DelegateChip,
+ ErrorLoadingElection,
+ RoundStage,
+ useRoundStageTimes,
+ VoteData,
+ VotingMemberChip
} from "elections";
import { MembersGrid } from "members";
import { MemberNFT } from "nfts/interfaces";
@@ -24,287 +24,272 @@ import { RoundHeader } from "elections/components/ongoing-election-components";
import { ConsensometerBlocks } from "elections/components/ongoing-election-components/ongoing-round/round-info/consensometer";
export const ElectionStatsPage = () => {
- const {
- data: globalElectionData,
- isLoading: isLoading,
- isError: isError,
- } = useCurrentGlobalElectionData();
+ const {
+ data: globalElectionData,
+ isLoading: isLoading,
+ isError: isError
+ } = useCurrentGlobalElectionData();
- return (
-
-
-
- Election
-
- {isLoading ? (
-
-
-
- ) : isError || !globalElectionData ? (
-
- ) : (
- <>
-
-
- All results
-
- {globalElectionData.time.format("LL")}
-
-
-
-
- {globalElectionData.rounds.map((round) => (
-
- ))}
- >
- )}
-
-
- );
+ return (
+
+
+
+ Election
+
+ {isLoading ? (
+
+
+
+ ) : isError || !globalElectionData ? (
+
+ ) : (
+ <>
+
+
+ All results
+ {globalElectionData.time.format("LL")}
+
+
+
+ {globalElectionData.rounds.map((round) => (
+
+ ))}
+ >
+ )}
+
+
+ );
};
export default ElectionStatsPage;
interface RoundSegmentProps {
- round: RoundWithGroupQueryData;
+ round: RoundWithGroupQueryData;
}
const RoundSegment = ({ round }: RoundSegmentProps) => {
- return (
- }
- showContentDivider
- >
-
- {round.groups.map((group, i) => (
-
- ))}
-
-
- );
+ return (
+ } showContentDivider>
+
+ {round.groups.map((group, i) => (
+
+ ))}
+
+
+ );
};
interface GlobalRoundHeaderProps {
- round: RoundBasicQueryData;
+ round: RoundBasicQueryData;
}
const GlobalRoundHeader = ({ round }: GlobalRoundHeaderProps) => {
- const { stage, currentStageEndTime } = useRoundStageTimes(
- round.votingBegin,
- round.votingEnd
- );
+ const { stage, currentStageEndTime } = useRoundStageTimes(
+ round.votingBegin,
+ round.votingEnd
+ );
- const { hmmss } = useCountdown({ endTime: currentStageEndTime.toDate() });
- const roundNum = round.roundIndex + 1;
+ const { hmmss } = useCountdown({ endTime: currentStageEndTime.toDate() });
+ const roundNum = round.roundIndex + 1;
- const isChiefDelegateRound = round.numGroups === 1;
- const roundTitle = isChiefDelegateRound
- ? "Chief Delegates"
- : `Round ${roundNum}`;
+ const isChiefDelegateRound = round.numGroups === 1;
+ const roundTitle = isChiefDelegateRound
+ ? "Chief Delegates"
+ : `Round ${roundNum}`;
- const roundStatusLabel = () => {
- if (isChiefDelegateRound) {
- return round.resultsAvailable ? "" : "elected";
- } else if (round.resultsAvailable) {
- return <>completed>;
- }
- switch (stage) {
- case RoundStage.PreMeeting:
- return (
- <>
- starts in: {hmmss}
- >
- );
- case RoundStage.PostMeeting:
- return (
- <>
- finalizes in:{" "}
- {hmmss}
- >
- );
- case RoundStage.Complete:
- return <>completed>;
- default:
- return <>in progress>;
- }
- };
+ const roundStatusLabel = () => {
+ if (isChiefDelegateRound) {
+ return round.resultsAvailable ? "" : "elected";
+ } else if (round.resultsAvailable) {
+ return <>completed>;
+ }
+ switch (stage) {
+ case RoundStage.PreMeeting:
+ return (
+ <>
+ starts in: {hmmss}
+ >
+ );
+ case RoundStage.PostMeeting:
+ return (
+ <>
+ finalizes in: {hmmss}
+ >
+ );
+ case RoundStage.Complete:
+ return <>completed>;
+ default:
+ return <>in progress>;
+ }
+ };
- const subText = `${round.votingBegin.utc().format("LT")} - ${round.votingEnd.utc().format("LT")} UTC`;
+ const subText = `${round.votingBegin
+ .utc()
+ .format("LT")} - ${round.votingEnd.utc().format("LT")} UTC`;
- return (
-
- {roundTitle} {roundStatusLabel()}
-
- }
- sublineComponent={
-
- {subText}
-
- }
- />
- );
+ return (
+
+ {roundTitle} {roundStatusLabel()}
+
+ }
+ sublineComponent={
+
+ {subText}
+
+ }
+ />
+ );
};
interface GroupSegmentProps {
- group: RoundGroupQueryData;
- groupIndex: number;
- isFinished: boolean;
- isChiefDelegateGroup?: boolean;
+ group: RoundGroupQueryData;
+ groupIndex: number;
+ isFinished: boolean;
+ isChiefDelegateGroup?: boolean;
}
interface GroupMembersStats {
- [key: string]: {
- receivedVotes: number;
- votedFor?: string;
- video?: string;
- isDelegate: boolean;
- };
+ [key: string]: {
+ receivedVotes: number;
+ votedFor?: string;
+ video?: string;
+ isDelegate: boolean;
+ };
}
const GroupSegment = ({
- group,
- groupIndex,
- isFinished,
- isChiefDelegateGroup,
+ group,
+ groupIndex,
+ isFinished,
+ isChiefDelegateGroup
}: GroupSegmentProps) => {
- // TODO: revisit this, unfortunately the MembersGrid only accepts MemberData,
- // even though we don't need it to display the required summarized member
- // chip data
- const members: MemberNFT[] = group.votes.map((vote) => vote.voter);
+ // TODO: revisit this, unfortunately the MembersGrid only accepts MemberData,
+ // even though we don't need it to display the required summarized member
+ // chip data
+ const members: MemberNFT[] = group.votes.map((vote) => vote.voter);
- const membersStats = group.votes.reduce((membersVotingMap, vote) => {
- membersVotingMap[vote.voter.account] = {
- receivedVotes: 0,
- votedFor: vote.candidate?.name,
- video: vote.video,
- isDelegate: group.winner?.account === vote.voter.account,
- };
- return membersVotingMap;
- }, {} as GroupMembersStats);
+ const membersStats = group.votes.reduce((membersVotingMap, vote) => {
+ membersVotingMap[vote.voter.account] = {
+ receivedVotes: 0,
+ votedFor: vote.candidate?.name,
+ video: vote.video,
+ isDelegate: group.winner?.account === vote.voter.account
+ };
+ return membersVotingMap;
+ }, {} as GroupMembersStats);
- group.votes
- .filter((vote) => vote.candidate)
- .forEach(
- (vote) => (membersStats[vote.candidate!.account].receivedVotes += 1)
- );
+ group.votes
+ .filter((vote) => vote.candidate)
+ .forEach(
+ (vote) => (membersStats[vote.candidate!.account].receivedVotes += 1)
+ );
- return isChiefDelegateGroup ? (
-
+ ) : (
+
- ) : (
-
- }
- members={members}
- groupMembersStats={membersStats}
- isFinished={isFinished}
- />
- );
+ }
+ members={members}
+ groupMembersStats={membersStats}
+ isFinished={isFinished}
+ />
+ );
};
interface GroupProps {
- members: MemberNFT[];
- isFinished: boolean;
- groupMembersStats: GroupMembersStats;
- header?: React.ReactNode;
+ members: MemberNFT[];
+ isFinished: boolean;
+ groupMembersStats: GroupMembersStats;
+ header?: React.ReactNode;
}
const RegularGroup = ({ members, groupMembersStats, header }: GroupProps) => {
- return (
-
-
- {(member: MemberNFT) => {
- return (
-
- );
- }}
-
-
- );
+ return (
+
+
+ {(member: MemberNFT) => {
+ return (
+
+ );
+ }}
+
+
+ );
};
const ChiefDelegateGroup = ({
- members,
- groupMembersStats,
- isFinished,
+ members,
+ groupMembersStats,
+ isFinished
}: GroupProps) => {
- return (
-
- {(member: MemberNFT) => {
- const delegateTitle =
- isFinished && groupMembersStats[member.account].isDelegate
- ? "Head Chief"
- : "Chief Delegate";
- return (
-
- );
- }}
-
- );
+ return (
+
+ {(member: MemberNFT) => {
+ const delegateTitle =
+ isFinished && groupMembersStats[member.account].isDelegate
+ ? "Head Chief"
+ : "Chief Delegate";
+ return (
+
+ );
+ }}
+
+ );
};
const GroupHeader = ({ group, groupIndex, isFinished }: GroupSegmentProps) => {
- const consensusData = group.votes.map((groupVotes) => ({
- member: groupVotes.voter.account,
- round: groupIndex,
- index: 0,
- candidate: groupVotes.candidate?.account,
- })) as VoteData[];
+ const consensusData = group.votes.map((groupVotes) => ({
+ member: groupVotes.voter.account,
+ round: groupIndex,
+ index: 0,
+ candidate: groupVotes.candidate?.account
+ })) as VoteData[];
- const groupLabel =
- group.winner?.name || (isFinished ? "[no consensus]" : "");
+ const groupLabel = group.winner?.name || (isFinished ? "[no consensus]" : "");
- return (
-
-
- Group {groupIndex + 1}
-
-
- {groupLabel}
-
-
-
- );
+ return (
+
+
+ Group {groupIndex + 1}
+
+
+ {groupLabel}
+
+
+
+ );
};