From 28a5361e9bfe463af7e339189cc689b1abafa5f9 Mon Sep 17 00:00:00 2001 From: Damian Date: Thu, 5 Sep 2024 18:25:39 -0300 Subject: [PATCH 1/5] Enable/Disable voting --- .../nextjs/app/admin/_components/SubmissionCard.tsx | 7 +++++++ .../nextjs/app/admin/_components/Submissions.tsx | 13 ++++++++++++- .../api/submissions/[submissionId]/votes/route.ts | 7 +++++++ packages/nextjs/scaffold.config.ts | 4 ++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/app/admin/_components/SubmissionCard.tsx b/packages/nextjs/app/admin/_components/SubmissionCard.tsx index f154923..2f8eecd 100644 --- a/packages/nextjs/app/admin/_components/SubmissionCard.tsx +++ b/packages/nextjs/app/admin/_components/SubmissionCard.tsx @@ -8,11 +8,13 @@ import { useMutation } from "@tanstack/react-query"; import clsx from "clsx"; import { useAccount } from "wagmi"; import { Address } from "~~/components/scaffold-eth"; +import scaffoldConfig from "~~/scaffold.config"; import { SubmissionWithAvg } from "~~/services/database/repositories/submissions"; import { postMutationFetcher } from "~~/utils/react-query"; import { notification } from "~~/utils/scaffold-eth"; export const SubmissionCard = ({ submission }: { submission: SubmissionWithAvg }) => { + const { votingEnabled } = scaffoldConfig; const { address: connectedAddress } = useAccount(); const { mutateAsync: postNewVote, isPending: isVotePending } = useMutation({ @@ -23,6 +25,11 @@ export const SubmissionCard = ({ submission }: { submission: SubmissionWithAvg } const vote = async (newScore: number) => { try { + if (!votingEnabled) { + notification.error("Voting is disabled"); + return; + } + if (newScore < 0 || newScore > 10) { notification.error("Wrong score"); return; diff --git a/packages/nextjs/app/admin/_components/Submissions.tsx b/packages/nextjs/app/admin/_components/Submissions.tsx index cd63e76..6e887df 100644 --- a/packages/nextjs/app/admin/_components/Submissions.tsx +++ b/packages/nextjs/app/admin/_components/Submissions.tsx @@ -1,7 +1,18 @@ import { SubmissionTabs } from "./SubmissionTabs"; +import scaffoldConfig from "~~/scaffold.config"; import { getAllSubmissions } from "~~/services/database/repositories/submissions"; export const Submissions = async () => { const submissions = await getAllSubmissions(); - return ; + const { votingEnabled } = scaffoldConfig; + return ( + <> + {!votingEnabled && ( +
+
Voting period ended
+
+ )} + + + ); }; diff --git a/packages/nextjs/app/api/submissions/[submissionId]/votes/route.ts b/packages/nextjs/app/api/submissions/[submissionId]/votes/route.ts index 2565ebd..e761dcd 100644 --- a/packages/nextjs/app/api/submissions/[submissionId]/votes/route.ts +++ b/packages/nextjs/app/api/submissions/[submissionId]/votes/route.ts @@ -1,15 +1,22 @@ import { NextRequest, NextResponse } from "next/server"; import { getServerSession } from "next-auth"; +import scaffoldConfig from "~~/scaffold.config"; import { createOrUpdateVote, deleteVote } from "~~/services/database/repositories/votes"; import { authOptions } from "~~/utils/auth"; export async function POST(req: NextRequest, { params }: { params: { submissionId: number } }) { + const { votingEnabled } = scaffoldConfig; try { const session = await getServerSession(authOptions); if (session?.user.role !== "admin") { return NextResponse.json({ error: "Only admins can vote" }, { status: 401 }); } + + if (!votingEnabled) { + return NextResponse.json({ error: "Voting is disabled" }, { status: 400 }); + } + const { submissionId } = params; const { score } = (await req.json()) as { score: string }; diff --git a/packages/nextjs/scaffold.config.ts b/packages/nextjs/scaffold.config.ts index b00a22e..ffe76bf 100644 --- a/packages/nextjs/scaffold.config.ts +++ b/packages/nextjs/scaffold.config.ts @@ -6,6 +6,7 @@ export type ScaffoldConfig = { alchemyApiKey: string; walletConnectProjectId: string; onlyLocalBurnerWallet: boolean; + votingEnabled: boolean; }; const scaffoldConfig = { @@ -30,6 +31,9 @@ const scaffoldConfig = { // Only show the Burner Wallet when running on hardhat network onlyLocalBurnerWallet: true, + + // Enable voting on submissions + votingEnabled: false, } as const satisfies ScaffoldConfig; export default scaffoldConfig; From 40da76ebb299916dfe4dd7c18c306426745dd20b Mon Sep 17 00:00:00 2001 From: Damian Date: Thu, 5 Sep 2024 23:48:08 -0300 Subject: [PATCH 2/5] Winners tag --- .../_components/SubmissionCard.tsx | 9 ++++++-- packages/nextjs/app/submissions/page.tsx | 21 ++++++++++++++++--- packages/nextjs/scaffold.config.ts | 6 ++++++ .../database/repositories/submissions.ts | 3 +++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/nextjs/app/submissions/_components/SubmissionCard.tsx b/packages/nextjs/app/submissions/_components/SubmissionCard.tsx index 44e0f80..efc73f1 100644 --- a/packages/nextjs/app/submissions/_components/SubmissionCard.tsx +++ b/packages/nextjs/app/submissions/_components/SubmissionCard.tsx @@ -1,10 +1,15 @@ import { Address } from "~~/components/scaffold-eth"; -import { Submission } from "~~/services/database/repositories/submissions"; +import { SubmissionWithWinnerTag } from "~~/services/database/repositories/submissions"; -export const SubmissionCard = ({ submission }: { submission: Submission }) => { +export const SubmissionCard = ({ submission }: { submission: SubmissionWithWinnerTag }) => { return (
+ {submission.winnerTag && ( +
+ {submission.winnerTag} +
+ )}

{submission.title}

diff --git a/packages/nextjs/app/submissions/page.tsx b/packages/nextjs/app/submissions/page.tsx index 4b8547d..cbf2f84 100644 --- a/packages/nextjs/app/submissions/page.tsx +++ b/packages/nextjs/app/submissions/page.tsx @@ -1,6 +1,7 @@ import { SubmissionCard } from "./_components/SubmissionCard"; import type { NextPage } from "next"; -import { getAllSubmissions } from "~~/services/database/repositories/submissions"; +import scaffoldConfig from "~~/scaffold.config"; +import { SubmissionWithWinnerTag, getAllSubmissions } from "~~/services/database/repositories/submissions"; import { getMetadata } from "~~/utils/scaffold-eth/getMetadata"; export const metadata = getMetadata({ @@ -10,16 +11,30 @@ export const metadata = getMetadata({ const Submissions: NextPage = async () => { const submissions = await getAllSubmissions(); + const { winnersThreshold, runnersUpThreshold } = scaffoldConfig; + + const submissionsWithAvgScore: SubmissionWithWinnerTag[] = submissions + .map(submission => { + const avgScore = + submission.votes.length > 0 + ? submission.votes.map(vote => vote.score).reduce((a, b) => a + b, 0) / submission.votes.length + : 0; + + const winnerTag = avgScore >= winnersThreshold ? "Winner" : avgScore >= runnersUpThreshold ? "Runner Up" : null; + + return { ...submission, avgScore, winnerTag }; + }) + .sort((a, b) => b.avgScore - a.avgScore); return (
-
+
{submissions.length === 0 && (
No submissions yet.
)} - {submissions.map(submission => { + {submissionsWithAvgScore.map((submission: SubmissionWithWinnerTag) => { return ; })}
diff --git a/packages/nextjs/scaffold.config.ts b/packages/nextjs/scaffold.config.ts index ffe76bf..ec8f25b 100644 --- a/packages/nextjs/scaffold.config.ts +++ b/packages/nextjs/scaffold.config.ts @@ -7,6 +7,8 @@ export type ScaffoldConfig = { walletConnectProjectId: string; onlyLocalBurnerWallet: boolean; votingEnabled: boolean; + winnersThreshold: number; + runnersUpThreshold: number; }; const scaffoldConfig = { @@ -34,6 +36,10 @@ const scaffoldConfig = { // Enable voting on submissions votingEnabled: false, + + // Score threshold for winners and runners up + winnersThreshold: 7, + runnersUpThreshold: 6, } as const satisfies ScaffoldConfig; export default scaffoldConfig; diff --git a/packages/nextjs/services/database/repositories/submissions.ts b/packages/nextjs/services/database/repositories/submissions.ts index 145fd27..117fa26 100644 --- a/packages/nextjs/services/database/repositories/submissions.ts +++ b/packages/nextjs/services/database/repositories/submissions.ts @@ -9,6 +9,9 @@ export type Submission = InferSelectModel & { comments: Comm export type SubmissionWithAvg = Submission & { avgScore: number; }; +export type SubmissionWithWinnerTag = Submission & { + winnerTag: string | null; +}; export async function getAllSubmissions() { return await db.query.submissions.findMany({ From a2319947c7c9331f4fe6bb32a9bf348ecaca5435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Fri, 6 Sep 2024 13:42:57 +0200 Subject: [PATCH 3/5] break words on description. `break-words` TW class doesn't work --- packages/nextjs/app/submissions/_components/SubmissionCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/app/submissions/_components/SubmissionCard.tsx b/packages/nextjs/app/submissions/_components/SubmissionCard.tsx index efc73f1..ccc5e96 100644 --- a/packages/nextjs/app/submissions/_components/SubmissionCard.tsx +++ b/packages/nextjs/app/submissions/_components/SubmissionCard.tsx @@ -27,7 +27,7 @@ export const SubmissionCard = ({ submission }: { submission: SubmissionWithWinne {submission.builder &&
}
-

{submission.description}

+

{submission.description}

); From d17a5adc5bed08a414e67b84f491463c90955d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Fri, 6 Sep 2024 16:03:38 +0200 Subject: [PATCH 4/5] Prize distribution, finalist --- packages/nextjs/app/submissions/page.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/app/submissions/page.tsx b/packages/nextjs/app/submissions/page.tsx index cbf2f84..da66acb 100644 --- a/packages/nextjs/app/submissions/page.tsx +++ b/packages/nextjs/app/submissions/page.tsx @@ -20,14 +20,24 @@ const Submissions: NextPage = async () => { ? submission.votes.map(vote => vote.score).reduce((a, b) => a + b, 0) / submission.votes.length : 0; - const winnerTag = avgScore >= winnersThreshold ? "Winner" : avgScore >= runnersUpThreshold ? "Runner Up" : null; + const winnerTag = avgScore >= winnersThreshold ? "Winner" : avgScore >= runnersUpThreshold ? "Finalist" : null; return { ...submission, avgScore, winnerTag }; }) .sort((a, b) => b.avgScore - a.avgScore); return ( -
+
+

🏆 Prize Distribution

+
    +
  • + Winner: $1000 +
  • +
  • + Finalist: $500 +
  • +
+
{submissions.length === 0 && (
From ac551dd1d6b0d353a16bdbd552e6201bb7e27222 Mon Sep 17 00:00:00 2001 From: pabl0cks Date: Fri, 6 Sep 2024 17:11:43 +0200 Subject: [PATCH 5/5] Add config variable instead of hardcoded --- packages/nextjs/app/api/submissions/route.ts | 5 +++-- packages/nextjs/app/submit/_component/SubmitButton.tsx | 9 +++++---- .../extensions-hackathon/StickySubmissionInfo.tsx | 7 ++++--- packages/nextjs/scaffold.config.ts | 4 ++++ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/nextjs/app/api/submissions/route.ts b/packages/nextjs/app/api/submissions/route.ts index 3ca8811..6b716a8 100644 --- a/packages/nextjs/app/api/submissions/route.ts +++ b/packages/nextjs/app/api/submissions/route.ts @@ -1,6 +1,7 @@ import { NextResponse } from "next/server"; import { getServerSession } from "next-auth"; import { recoverTypedDataAddress } from "viem"; +import scaffoldConfig from "~~/scaffold.config"; import { createBuilder, getBuilderById } from "~~/services/database/repositories/builders"; import { createSubmission, getAllSubmissions } from "~~/services/database/repositories/submissions"; import { SubmissionInsert } from "~~/services/database/repositories/submissions"; @@ -26,8 +27,8 @@ export type CreateNewSubmissionBody = SubmissionInsert & { signature: `0x${strin export async function POST(req: Request) { try { - const isSubmissionClosed = true; - if (isSubmissionClosed) { + const { submissionsEnabled } = scaffoldConfig; + if (!submissionsEnabled) { return NextResponse.json({ error: "Submissions are closed" }, { status: 403 }); } diff --git a/packages/nextjs/app/submit/_component/SubmitButton.tsx b/packages/nextjs/app/submit/_component/SubmitButton.tsx index 81724f3..14dad1b 100644 --- a/packages/nextjs/app/submit/_component/SubmitButton.tsx +++ b/packages/nextjs/app/submit/_component/SubmitButton.tsx @@ -3,18 +3,19 @@ import { useFormStatus } from "react-dom"; import { useAccount } from "wagmi"; import { RainbowKitCustomConnectButton } from "~~/components/scaffold-eth"; +import scaffoldConfig from "~~/scaffold.config"; const SubmitButton = () => { const { pending } = useFormStatus(); const { isConnected } = useAccount(); - const isSubmissionClosed = true; + const { submissionsEnabled } = scaffoldConfig; return (
- {isSubmissionClosed ? ( + {!submissionsEnabled ? ( diff --git a/packages/nextjs/components/extensions-hackathon/StickySubmissionInfo.tsx b/packages/nextjs/components/extensions-hackathon/StickySubmissionInfo.tsx index a9e8cda..2313b16 100644 --- a/packages/nextjs/components/extensions-hackathon/StickySubmissionInfo.tsx +++ b/packages/nextjs/components/extensions-hackathon/StickySubmissionInfo.tsx @@ -1,9 +1,10 @@ import { useState } from "react"; import Link from "next/link"; +import scaffoldConfig from "~~/scaffold.config"; export const StickySubmissionInfo = () => { const [isVisible, setIsVisible] = useState(true); - const isSubmissionClosed = true; + const { submissionsEnabled } = scaffoldConfig; if (!isVisible) { return ( @@ -27,11 +28,11 @@ export const StickySubmissionInfo = () => { ✕

- {isSubmissionClosed ? "Submissions closed" : "Submissions open"} + {submissionsEnabled ? "Submissions open" : "Submissions closed"}

AUG 20 - SEP 2

- {!isSubmissionClosed && ( + {submissionsEnabled && (