diff --git a/src/app/demo/page.tsx b/src/app/demo/page.tsx
index 6868e3c..2709c31 100644
--- a/src/app/demo/page.tsx
+++ b/src/app/demo/page.tsx
@@ -4,7 +4,6 @@ import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
-import { Progress } from "@/components/ui/progress";
import Link from "next/link";
import {
ChevronUp,
@@ -95,30 +94,52 @@ export default function DemoPage() {
const maxWeight = Math.floor(Math.sqrt(credits));
- function incrementVote(proposalId: number) {
+ function handleUpvote(proposalId: number) {
setProposals((prev) =>
prev.map((p) => {
- if (p.id === proposalId) {
- const newPending = p.pendingVote + 1;
- const newCost = newPending * newPending;
- if (newCost <= credits) {
- return { ...p, pendingVote: newPending };
- }
+ if (p.id !== proposalId) return p;
+
+ // If already voted up, remove the vote
+ if (p.userVote > 0) {
+ const refund = p.userVote * p.userVote;
+ setCredits((c) => c + refund);
+ return { ...p, score: p.score - p.userVote, userVote: 0, pendingVote: 0 };
+ }
+
+ // If already voted down, can't upvote
+ if (p.userVote < 0) return p;
+
+ // Increment pending vote
+ const newPending = p.pendingVote + 1;
+ const newCost = newPending * newPending;
+ if (newCost <= credits && newPending <= maxWeight) {
+ return { ...p, pendingVote: newPending };
}
return p;
})
);
}
- function decrementVote(proposalId: number) {
+ function handleDownvote(proposalId: number) {
setProposals((prev) =>
prev.map((p) => {
- if (p.id === proposalId) {
- const newPending = p.pendingVote - 1;
- const newCost = newPending * newPending;
- if (newCost <= credits) {
- return { ...p, pendingVote: newPending };
- }
+ if (p.id !== proposalId) return p;
+
+ // If already voted down, remove the vote
+ if (p.userVote < 0) {
+ const refund = p.userVote * p.userVote;
+ setCredits((c) => c + refund);
+ return { ...p, score: p.score - p.userVote, userVote: 0, pendingVote: 0 };
+ }
+
+ // If already voted up, can't downvote
+ if (p.userVote > 0) return p;
+
+ // Decrement pending vote
+ const newPending = p.pendingVote - 1;
+ const newCost = newPending * newPending;
+ if (newCost <= credits && Math.abs(newPending) <= maxWeight) {
+ return { ...p, pendingVote: newPending };
}
return p;
})
@@ -131,44 +152,26 @@ export default function DemoPage() {
);
}
- function castVote(proposalId: number) {
+ function confirmVote(proposalId: number) {
setProposals((prev) =>
prev.map((p) => {
- if (p.id === proposalId && p.pendingVote !== 0) {
- const cost = p.pendingVote * p.pendingVote;
- const newScore = p.score + p.pendingVote;
- const promoted = newScore >= 100 && p.stage === "ranking";
+ if (p.id !== proposalId || p.pendingVote === 0) return p;
- setCredits((c) => c - cost);
+ const cost = p.pendingVote * p.pendingVote;
+ const newScore = p.score + p.pendingVote;
+ const promoted = newScore >= 100 && p.stage === "ranking";
- return {
- ...p,
- score: newScore,
- userVote: p.pendingVote,
- pendingVote: 0,
- stage: promoted ? "voting" : p.stage,
- yesVotes: promoted ? 8 : p.yesVotes,
- noVotes: promoted ? 3 : p.noVotes,
- };
- }
- return p;
- })
- );
- }
+ setCredits((c) => c - cost);
- function removeVote(proposalId: number) {
- setProposals((prev) =>
- prev.map((p) => {
- if (p.id === proposalId && p.userVote !== 0) {
- const refund = p.userVote * p.userVote;
- setCredits((c) => c + refund);
- return {
- ...p,
- score: p.score - p.userVote,
- userVote: 0,
- };
- }
- return p;
+ return {
+ ...p,
+ score: newScore,
+ userVote: p.pendingVote,
+ pendingVote: 0,
+ stage: promoted ? "voting" : p.stage,
+ yesVotes: promoted ? 8 : p.yesVotes,
+ noVotes: promoted ? 3 : p.noVotes,
+ };
})
);
}
@@ -255,7 +258,7 @@ export default function DemoPage() {
: "bg-muted/50 border-muted text-muted-foreground"
}`}
>
-
{w > 0 ? "+" : ""}{w}
+ +{w}
vote{w > 1 ? "s" : ""}
{w * w}ยข
@@ -279,163 +282,135 @@ export default function DemoPage() {
const hasPending = proposal.pendingVote !== 0;
const hasVoted = proposal.userVote !== 0;
const pendingCost = proposal.pendingVote * proposal.pendingVote;
- const previewScore = proposal.score + proposal.pendingVote;
- const progressPercent = Math.min((proposal.score / 100) * 100, 100);
- const previewPercent = Math.min((previewScore / 100) * 100, 100);
+ const displayScore = hasPending ? proposal.score + proposal.pendingVote : proposal.score;
+ const progressPercent = Math.min((displayScore / 100) * 100, 100);
+
+ const isUpvoted = (hasVoted && proposal.userVote > 0) || proposal.pendingVote > 0;
+ const isDownvoted = (hasVoted && proposal.userVote < 0) || proposal.pendingVote < 0;
return (
- 0
- ? "ring-2 ring-orange-500/50 bg-orange-500/5"
- : "ring-2 ring-blue-500/50 bg-blue-500/5"
- : hasVoted
- ? proposal.userVote > 0
- ? "bg-orange-500/5"
- : "bg-blue-500/5"
- : ""
+ ? "ring-2 ring-orange-500/50"
+ : "ring-2 ring-blue-500/50"
+ : ""
}`}
>
-
- {/* Reddit-style vote column */}
-
- {/* Upvote button */}
-
+ {/* Reddit-style vote column */}
+
+
- {/* Score display */}
-
0
- ? "text-orange-500"
- : "text-blue-500"
- : hasVoted
- ? proposal.userVote > 0
- ? "text-orange-500"
- : "text-blue-500"
- : "text-foreground"
- }`}>
- {hasPending ? previewScore : proposal.score}
-
+
+ {displayScore}
+
- {/* Downvote button */}
-
-
-
- {/* Proposal content */}
-
-
{proposal.title}
-
- {proposal.description}
-
-
- {/* Progress bar */}
-
-
- Progress to voting stage
- 0 ? "text-orange-500" : "text-blue-500") : ""}>
- {hasPending ? previewScore : proposal.score}/100
-
-
-
-
0
- ? "bg-orange-500"
- : "bg-blue-500"
- : "bg-primary"
- }`}
- style={{ width: `${hasPending ? previewPercent : progressPercent}%` }}
- />
-
-
-
- {/* Vote status / pending confirmation */}
- {(hasPending || hasVoted) && (
-
- {hasPending ? (
- <>
- 0
- ? "border-orange-500/50 text-orange-600 bg-orange-500/10"
- : "border-blue-500/50 text-blue-600 bg-blue-500/10"
- }
- >
- {proposal.pendingVote > 0 ? "+" : ""}{proposal.pendingVote} vote = {pendingCost} credits
-
-
-
- >
- ) : hasVoted && (
- 0
- ? "bg-orange-500/20 text-orange-600 border-orange-500/30"
- : "bg-blue-500/20 text-blue-600 border-blue-500/30"
- }
- >
- You voted: {proposal.userVote > 0 ? "+" : ""}{proposal.userVote}
-
- )}
-
- )}
-
+
-
+
+ {/* Proposal content */}
+
+
{proposal.title}
+
+ {proposal.description}
+
+
+ {/* Progress bar */}
+
+
+ Progress to voting stage
+
+ {displayScore}/100
+
+
+
+
+
+ {/* Pending vote confirmation */}
+ {hasPending && (
+
+ 0
+ ? "border-orange-500/50 text-orange-600 bg-orange-500/10"
+ : "border-blue-500/50 text-blue-600 bg-blue-500/10"
+ }
+ >
+ {proposal.pendingVote > 0 ? "+" : ""}{proposal.pendingVote} vote = {pendingCost} credits
+
+
+
+
+ )}
+
+ {/* Existing vote indicator */}
+ {hasVoted && !hasPending && (
+
+ 0
+ ? "bg-orange-500/20 text-orange-600"
+ : "bg-blue-500/20 text-blue-600"
+ }
+ >
+ You voted: {proposal.userVote > 0 ? "+" : ""}{proposal.userVote}
+
+
+ )}
+
+
);
})}
@@ -468,7 +443,6 @@ export default function DemoPage() {
{proposal.description}
- {/* Vote bar */}