Skip to content

Commit

Permalink
Improve VotingCard code readability
Browse files Browse the repository at this point in the history
  • Loading branch information
sembrestels committed Oct 4, 2024
1 parent 5df65c1 commit 9e0fd81
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 105 deletions.
18 changes: 18 additions & 0 deletions apps/web/src/components/VotingCard/SkeletonVote.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Skeleton } from "@repo/ui/components/ui/skeleton";

export function SkeletonVote() {
return (
<div className="flex flex-col space-y-3">
<div className="h-12 flex justify-between items-center">
<Skeleton className="w-3/5 h-5" />
<Skeleton className="w-1/5 h-3" />
</div>

<div className="space-y-2 mt-4">
<Skeleton className="h-10" />
<Skeleton className="h-10" />
<Skeleton className="h-10" />
</div>
</div>
);
}
53 changes: 53 additions & 0 deletions apps/web/src/components/VotingCard/VoteInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Button } from "@repo/ui/components/ui/button";
import { Input } from "@repo/ui/components/ui/input";

export function VoteInput({
value,
onChange,
max,
min = 0,
total,
increment,
disabled,
}: {
value: number;
onChange: (value: number) => void;
max: number;
min?: number;
total: number;
increment: number;
disabled: boolean;
}) {
return (
<div className="flex items-center">
<Button
disabled={disabled || value <= min}
onClick={() => onChange(Math.max(min, value - increment))}
className="bg-gray-700 w-8 py-1 text-white hover:bg-gray-500 rounded-r-none"
>
-
</Button>
<Input
disabled={disabled}
type="number"
value={value}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
onChange(
Math.max(min, Math.min(max, Number.parseInt(e.target.value) || 0)),
)
}
className="w-16 bg-gray-600 text-center text-white rounded-none px-3 py-1 input-number-hide-arrows border-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-default"
/>
<Button
disabled={disabled || value >= max}
onClick={() => onChange(Math.min(max, value + increment))}
className="bg-gray-700 w-8 py-1 text-white hover:bg-gray-500 rounded-l-none"
>
+
</Button>
<span className="w-12 text-right hidden sm:block">
{value > 0 ? Math.round((value / total) * 100) : 0}%
</span>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
"use client";

import { Button } from "@repo/ui/components/ui/button";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "@repo/ui/components/ui/card";
import { Input } from "@repo/ui/components/ui/input";
import { Skeleton } from "@repo/ui/components/ui/skeleton";
import React, { useEffect, useMemo, useState } from "react";
import VotingButton from "./VotingButton";
import VotingButton from "../VotingButton";
import { SkeletonVote } from "./SkeletonVote";
import { VoteInput } from "./VoteInput";

type Project = { account: `0x${string}`; name: string };
type Allocation = { [grantee: `0x${string}`]: number };
Expand All @@ -22,15 +21,15 @@ const VotingCard = ({
projects,
initialAllocation,
votingPower,
maxVotedProjects = 3,
maxVotedProjects,
isLoading = false,
}: {
className: string;
council: `0x${string}` | undefined;
projects: Project[];
initialAllocation: Allocation | undefined;
votingPower: number;
maxVotedProjects?: number;
maxVotedProjects: number;
isLoading: boolean;
}) => {
const randomizedProjects = useMemo(
Expand Down Expand Up @@ -68,13 +67,22 @@ const VotingCard = ({
const handleVote = (grantee: `0x${string}`, value: number) => {
setVotes((prev) => ({
...prev,
[grantee]: Math.max(
0,
Math.min(value || 0, votingPower - totalVotes + (prev[grantee] || 0)),
),
[grantee]: value,
}));
};

const calculateProjectVotingDetails = (project: `0x${string}`) => {
const voteCount = votes[project] || 0;
const totalVotesExcludingCurrentProject = totalVotes - voteCount;
const remainingVotingPowerForThisProject =
votingPower - totalVotesExcludingCurrentProject;
const maxVoteForProject = Math.max(0, remainingVotingPowerForThisProject);
const disabled =
!votingPower ||
(votedProjects.length >= maxVotedProjects && voteCount === 0);
return { voteCount, maxVoteForProject, disabled };
};

return (
<Card className={className}>
<CardHeader>
Expand Down Expand Up @@ -104,6 +112,8 @@ const VotingCard = ({
</div>
</div>
{randomizedProjects.map((project) => {
const { voteCount, maxVoteForProject, disabled } =
calculateProjectVotingDetails(project.account);
return (
<div
key={project.account}
Expand All @@ -112,14 +122,15 @@ const VotingCard = ({
<span className="flex-grow text-ellipsis overflow-hidden">
{project.name}
</span>
<VoteControls
project={project}
votes={votes}
handleVote={handleVote}
votingPower={votingPower}
maxVotedProjects={maxVotedProjects}
votedProjects={votedProjects}
totalVotes={totalVotes}
<VoteInput
value={voteCount}
onChange={(newValue) =>
handleVote(project.account, newValue)
}
max={maxVoteForProject}
total={votingPower}
increment={Math.floor(votingPower / 10)}
disabled={disabled}
/>
</div>
);
Expand All @@ -138,91 +149,4 @@ const VotingCard = ({
);
};

function VoteControls({
project,
votes,
handleVote,
votingPower,
maxVotedProjects,
votedProjects,
totalVotes,
}: {
project: Project;
votes: Allocation;
handleVote: (grantee: `0x${string}`, value: number) => void;
votingPower: number;
maxVotedProjects: number;
votedProjects: `0x${string}`[];
totalVotes: number;
}) {
const voteCount = votes[project.account] || 0;
return (
<>
<div className="flex items-center">
<Button
disabled={voteCount <= 0}
onClick={() =>
handleVote(
project.account,
voteCount - Math.floor(votingPower / 10),
)
}
className="bg-gray-700 w-8 py-1 text-white hover:bg-gray-500 rounded-r-none"
>
-
</Button>
<Input
disabled={
!votingPower ||
(votedProjects.length >= maxVotedProjects &&
!votes[project.account])
}
type="number"
value={voteCount}
onChange={(e) =>
handleVote(project.account, Number.parseInt(e.target.value))
}
className="w-16 bg-gray-600 text-center text-white rounded-none px-3 py-1 input-number-hide-arrows border-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-default"
/>
<Button
disabled={
(votedProjects.length >= maxVotedProjects &&
!votes[project.account]) ||
totalVotes >= votingPower
}
onClick={() =>
handleVote(
project.account,
voteCount + Math.floor(votingPower / 10),
)
}
className="bg-gray-700 w-8 py-1 text-white hover:bg-gray-500 rounded-l-none"
>
+
</Button>
<span className="w-12 text-right hidden sm:block">
{totalVotes > 0 ? Math.round((voteCount / votingPower) * 100) : 0}%
</span>
</div>
</>
);
}

function SkeletonVote() {
return (
<div className="flex flex-col space-y-3">
<div className="h-12 flex justify-between items-center">
<Skeleton className="w-3/5 h-5" />
<Skeleton className="w-1/5 h-3" />
</div>

<div className="space-y-2 mt-4">
<Skeleton className="h-10" />
<Skeleton className="h-10" />
<Skeleton className="h-10" />
</div>
</div>
);
}

export default VotingCard;

0 comments on commit 9e0fd81

Please sign in to comment.