Skip to content

Commit

Permalink
feat: add v2 frontend UI design
Browse files Browse the repository at this point in the history
  • Loading branch information
kittybest committed Oct 14, 2024
1 parent f87f2b0 commit 5c4c908
Show file tree
Hide file tree
Showing 71 changed files with 826 additions and 665 deletions.
12 changes: 2 additions & 10 deletions packages/interface/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,12 @@ NEXT_PUBLIC_WALLETCONNECT_ID="21fef48091f12692cad574a6f7753643"
# Event title for the round, just for display
NEXT_PUBLIC_EVENT_NAME="Devcon"

# Unique identifier for your applications and lists - your app will group attestations by this id
NEXT_PUBLIC_ROUND_ID="open-rpgf-1"
# Event title for the round, just for display
NEXT_PUBLIC_ROUND_ORGANIZER="PSE"
# Event description, just for display
NEXT_PUBLIC_EVENT_DESCRIPTION="Write a descripion about your community"

# Name of the token you want to allocate (only updates UI)
NEXT_PUBLIC_TOKEN_NAME="Votes"

# Voting periods
# Determine when users can register applications, admins review them, voters vote, and results are published
NEXT_PUBLIC_START_DATE=2024-01-01T00:00:00.000Z
NEXT_PUBLIC_REGISTRATION_END_DATE=2024-01-01T00:00:00.000Z
NEXT_PUBLIC_RESULTS_DATE=2024-01-01T00:00:00.000Z

# Collect user feedback. Is shown as a link when user has voted
NEXT_PUBLIC_FEEDBACK_URL=https://github.com/privacy-scaling-explorations/maci-platform/issues/new?title=Feedback

Expand Down
8 changes: 6 additions & 2 deletions packages/interface/src/components/AddedProjects.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useBallot } from "~/contexts/Ballot";
import { useProjectCount } from "~/features/projects/hooks/useProjects";

export const AddedProjects = (): JSX.Element => {
interface IAddedProjectsProps {
roundId: string;
}

export const AddedProjects = ({ roundId }: IAddedProjectsProps): JSX.Element => {
const { ballot } = useBallot();
const allocations = ballot.votes;
const { data: projectCount } = useProjectCount();
const { data: projectCount } = useProjectCount(roundId);

return (
<div className="border-b border-gray-200 py-2">
Expand Down
45 changes: 24 additions & 21 deletions packages/interface/src/components/BallotOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,43 @@ import Link from "next/link";

import { Heading } from "~/components/ui/Heading";
import { useBallot } from "~/contexts/Ballot";
import { useAppState } from "~/utils/state";
import { EAppState } from "~/utils/types";
import { useRoundState } from "~/utils/state";
import { ERoundState } from "~/utils/types";

import { AddedProjects } from "./AddedProjects";
import { VotingUsage } from "./VotingUsage";

interface IBallotOverviewProps {
roundId: string;
title?: string;
}

export const BallotOverview = ({ title = undefined }: IBallotOverviewProps): JSX.Element => {
export const BallotOverview = ({ title = undefined, roundId }: IBallotOverviewProps): JSX.Element => {
const { ballot } = useBallot();

const appState = useAppState();
const roundState = useRoundState(roundId);

return (
<div className="w-full">
<Link
href={
ballot.published && (appState === EAppState.TALLYING || appState === EAppState.RESULTS)
? "/ballot/confirmation"
: "/ballot"
}
>
{title && (
<Heading as="h3" size="3xl">
{title}
</Heading>
)}

<AddedProjects />
<Link
href={
ballot.published && (roundState === ERoundState.TALLYING || roundState === ERoundState.RESULTS)
? `/rounds/${roundId}/ballot/confirmation`
: `/rounds/${roundId}/ballot`
}
>
<div className="dark:bg-lightBlack my-8 flex-col items-center gap-2 rounded-lg bg-white p-5 uppercase shadow-lg dark:text-white">
<Heading as="h3" size="3xl">
{title && (
<Heading as="h3" size="3xl">
{title}
</Heading>
)}
</Heading>

<AddedProjects roundId={roundId} />

<VotingUsage />
</Link>
</div>
</div>
</Link>
);
};
41 changes: 20 additions & 21 deletions packages/interface/src/components/EligibilityDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ import { useAccount, useDisconnect } from "wagmi";
import { zupass, config } from "~/config";
import { useMaci } from "~/contexts/Maci";
import { useEthersSigner } from "~/hooks/useEthersSigner";
import { useAppState } from "~/utils/state";
import { EAppState, jsonPCD } from "~/utils/types";
import { useRoundState } from "~/utils/state";
import { ERoundState, jsonPCD } from "~/utils/types";

import type { EdDSAPublicKey } from "@pcd/eddsa-pcd";

import { Dialog } from "./ui/Dialog";

export const EligibilityDialog = (): JSX.Element | null => {
const signer = useEthersSigner();
interface IEligibilityDialogProps {
roundId?: string;
}

export const EligibilityDialog = ({ roundId = "" }: IEligibilityDialogProps): JSX.Element | null => {
const { address } = useAccount();
const { disconnect } = useDisconnect();

Expand All @@ -35,10 +38,12 @@ export const EligibilityDialog = (): JSX.Element | null => {
} = useMaci();
const router = useRouter();

const appState = useAppState();
const roundState = useRoundState(roundId);

const onError = useCallback(() => toast.error("Signup error"), []);

const signer = useEthersSigner();

const handleSignup = useCallback(async () => {
await onSignup(onError);
setOpenDialog(false);
Expand Down Expand Up @@ -87,15 +92,11 @@ export const EligibilityDialog = (): JSX.Element | null => {
disconnect();
}, [disconnect]);

const handleGoToProjects = useCallback(() => {
router.push("/projects");
}, [router]);

const handleGoToCreateApp = useCallback(() => {
router.push("/applications/new");
}, [router]);

if (appState === EAppState.APPLICATION) {
if (roundState === ERoundState.APPLICATION) {
return (
<Dialog
button="secondary"
Expand All @@ -114,12 +115,10 @@ export const EligibilityDialog = (): JSX.Element | null => {
);
}

if (appState === EAppState.VOTING && isRegistered) {
if (roundState === ERoundState.VOTING && isRegistered) {
return (
<Dialog
button="secondary"
buttonAction={handleGoToProjects}
buttonName="See all projects"
description={
<div className="flex flex-col gap-4">
<p>You have {initialVoiceCredits} voice credits to vote with.</p>
Expand All @@ -134,21 +133,21 @@ export const EligibilityDialog = (): JSX.Element | null => {
}
isOpen={openDialog}
size="sm"
title="You're all set to vote"
title="You're all set to vote for rounds!"
onOpenChange={handleCloseDialog}
/>
);
}

if (appState === EAppState.VOTING && !isRegistered && isEligibleToVote) {
if (roundState === ERoundState.VOTING && !isRegistered && isEligibleToVote) {
return (
<Dialog
button="secondary"
buttonAction={handleSignup}
buttonName="Join voting round"
buttonName="Join voting rounds"
description={
<div className="flex flex-col gap-6">
<p>Next, you will need to join the voting round.</p>
<p>Next, you will need to register to the event to join the voting rounds.</p>

<i>
<span>Learn more about this process </span>
Expand All @@ -169,7 +168,7 @@ export const EligibilityDialog = (): JSX.Element | null => {
);
}

if (appState === EAppState.VOTING && !isEligibleToVote && gatekeeperTrait === GatekeeperTrait.Zupass) {
if (roundState === ERoundState.VOTING && !isEligibleToVote && gatekeeperTrait === GatekeeperTrait.Zupass) {
return (
<Dialog
button="secondary"
Expand All @@ -184,13 +183,13 @@ export const EligibilityDialog = (): JSX.Element | null => {
);
}

if (appState === EAppState.VOTING && !isEligibleToVote) {
if (roundState === ERoundState.VOTING && !isEligibleToVote) {
return (
<Dialog
button="secondary"
buttonAction={handleDisconnect}
buttonName="Disconnect"
description="To participate in this round, you must be in the voter's registry. Contact the round organizers to get access as a voter."
description="To participate in the event, you must be in the voter's registry. Contact the round organizers to get access as a voter."
isOpen={openDialog}
size="sm"
title="Sorry, this account does not have the credentials to be verified."
Expand All @@ -199,7 +198,7 @@ export const EligibilityDialog = (): JSX.Element | null => {
);
}

if (appState === EAppState.TALLYING) {
if (roundState === ERoundState.TALLYING) {
return (
<Dialog
description="The result is under tallying, please come back to check the result later."
Expand Down
22 changes: 14 additions & 8 deletions packages/interface/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import dynamic from "next/dynamic";
import Link from "next/link";
import { useRouter } from "next/router";
import { useTheme } from "next-themes";
import { type ComponentPropsWithRef, useState, useCallback } from "react";
import { type ComponentPropsWithRef, useState, useCallback, useMemo } from "react";

import { useBallot } from "~/contexts/Ballot";
import { useAppState } from "~/utils/state";
import { EAppState } from "~/utils/types";
import { useRoundState } from "~/utils/state";
import { ERoundState } from "~/utils/types";

import { ConnectButton } from "./ConnectButton";
import { IconButton } from "./ui/Button";
Expand Down Expand Up @@ -52,19 +52,23 @@ interface INavLink {

interface IHeaderProps {
navLinks: INavLink[];
roundId?: string;
}

const Header = ({ navLinks }: IHeaderProps) => {
const Header = ({ navLinks, roundId = "" }: IHeaderProps) => {
const { asPath } = useRouter();
const [isOpen, setOpen] = useState(false);
const { ballot } = useBallot();
const appState = useAppState();
const roundState = useRoundState(roundId);
const { theme, setTheme } = useTheme();

const handleChangeTheme = useCallback(() => {
setTheme(theme === "light" ? "dark" : "light");
}, [theme, setTheme]);

// the URI of round index page looks like: /rounds/:roundId, without anything else, which is the reason why the length is 3
const isRoundIndexPage = useMemo(() => asPath.includes("rounds") && asPath.split("/").length === 3, [asPath]);

return (
<header className="dark:border-lighterBlack dark:bg-lightBlack relative z-[100] border-b border-gray-200 bg-white dark:text-white">
<div className="container mx-auto flex h-[72px] max-w-screen-2xl items-center px-2">
Expand All @@ -85,12 +89,14 @@ const Header = ({ navLinks }: IHeaderProps) => {

<div className="hidden h-full items-center gap-4 overflow-x-auto uppercase md:flex">
{navLinks.map((link) => {
const pageName = `/${link.href.split("/")[1]}`;
const isActive =
asPath.includes(link.children.toLowerCase()) || (link.children === "Projects" && isRoundIndexPage);

return (
<NavLink key={link.href} href={link.href} isActive={asPath.startsWith(pageName)}>
<NavLink key={link.href} href={link.href} isActive={isActive}>
{link.children}

{appState === EAppState.VOTING && pageName === "/ballot" && ballot.votes.length > 0 && (
{roundState === ERoundState.VOTING && link.href.includes("/ballot") && ballot.votes.length > 0 && (
<div className="ml-2 h-5 w-5 rounded-full border-2 border-blue-400 bg-blue-50 text-center text-sm leading-4 text-blue-400">
{ballot.votes.length}
</div>
Expand Down
Loading

0 comments on commit 5c4c908

Please sign in to comment.