diff --git a/platforms/eVoting/src/app/(app)/[id]/page.tsx b/platforms/eVoting/src/app/(app)/[id]/page.tsx
index 0219be44..920a7bb1 100644
--- a/platforms/eVoting/src/app/(app)/[id]/page.tsx
+++ b/platforms/eVoting/src/app/(app)/[id]/page.tsx
@@ -421,12 +421,12 @@ export default function Vote({ params }: { params: Promise<{ id: string }> }) {
{result.optionText || `Option ${index + 1}`}
- {result.isTied && (
+ {result.isTied && blindVoteResults.totalVotes > 0 && (
🏆 Tied
)}
- {isWinner && !result.isTied && (
+ {isWinner && !result.isTied && blindVoteResults.totalVotes > 0 && (
🏆 Winner
@@ -480,6 +480,9 @@ export default function Vote({ params }: { params: Promise<{ id: string }> }) {
{resultsData.totalVotes || 0} of {resultsData.totalEligibleVoters} eligible voters
+ {(resultsData.mode === "ereputation" || selectedPoll?.votingWeight === "ereputation") && resultsData.pointsVoted !== undefined && resultsData.totalEligiblePoints !== undefined && (
+ <> ({resultsData.pointsVoted} points of {resultsData.totalEligiblePoints} total eligible points)>
+ )}
@@ -521,7 +524,8 @@ export default function Vote({ params }: { params: Promise<{ id: string }> }) {
} else {
// Normal voting (including eReputation weighted normal voting): show votes and percentage
const voteCount = result.votes || 0;
- displayValue = `${voteCount} votes`;
+ // For eReputation mode, show "Points" instead of "votes"
+ displayValue = resultsData.mode === "ereputation" ? `${voteCount} Points` : `${voteCount} votes`;
// Calculate total from results array for percentage (handles both weighted and non-weighted)
const totalVotesForPercentage = resultsData.results.reduce((sum, r) => sum + (r.votes || 0), 0);
isWinner = voteCount === Math.max(...resultsData.results.map(r => r.votes || 0));
@@ -541,12 +545,12 @@ export default function Vote({ params }: { params: Promise<{ id: string }> }) {
{result.option}
- {result.isTied && (
+ {result.isTied && resultsData.totalVotes > 0 && (
🏆 Tied
)}
- {isWinner && !result.isTied && (
+ {isWinner && !result.isTied && resultsData.totalVotes > 0 && (
🏆 Winner
diff --git a/platforms/eVoting/src/app/(app)/page.tsx b/platforms/eVoting/src/app/(app)/page.tsx
index fae0b3cd..eecccce3 100644
--- a/platforms/eVoting/src/app/(app)/page.tsx
+++ b/platforms/eVoting/src/app/(app)/page.tsx
@@ -17,8 +17,18 @@ export default function Home() {
const [currentPage, setCurrentPage] = useState(1);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
- const [sortField, setSortField] = useState("deadline");
- const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc");
+ const [sortField, setSortField] = useState(() => {
+ if (typeof window !== "undefined") {
+ return localStorage.getItem("evoting_sortField") || "deadline";
+ }
+ return "deadline";
+ });
+ const [sortDirection, setSortDirection] = useState<"asc" | "desc">(() => {
+ if (typeof window !== "undefined") {
+ return (localStorage.getItem("evoting_sortDirection") as "asc" | "desc") || "asc";
+ }
+ return "asc";
+ });
const itemsPerPage = 15;
useEffect(() => {
@@ -75,10 +85,18 @@ export default function Home() {
const handleSort = (field: string) => {
if (sortField === field) {
- setSortDirection(sortDirection === "asc" ? "desc" : "asc");
+ const newDirection = sortDirection === "asc" ? "desc" : "asc";
+ setSortDirection(newDirection);
+ if (typeof window !== "undefined") {
+ localStorage.setItem("evoting_sortDirection", newDirection);
+ }
} else {
setSortField(field);
setSortDirection("asc");
+ if (typeof window !== "undefined") {
+ localStorage.setItem("evoting_sortField", field);
+ localStorage.setItem("evoting_sortDirection", "asc");
+ }
}
};
diff --git a/platforms/eVoting/src/lib/pollApi.ts b/platforms/eVoting/src/lib/pollApi.ts
index c0dadbb9..a0b33014 100644
--- a/platforms/eVoting/src/lib/pollApi.ts
+++ b/platforms/eVoting/src/lib/pollApi.ts
@@ -121,6 +121,8 @@ export interface PollResults {
results: PollResultOption[];
irvDetails?: IRVDetails;
voterDetails?: VoterDetail[];
+ pointsVoted?: number;
+ totalEligiblePoints?: number;
}
export interface BlindVoteOptionResult {
diff --git a/platforms/evoting-api/src/services/PollService.ts b/platforms/evoting-api/src/services/PollService.ts
index 8b61fe15..1d9d3829 100644
--- a/platforms/evoting-api/src/services/PollService.ts
+++ b/platforms/evoting-api/src/services/PollService.ts
@@ -89,13 +89,7 @@ export class PollService {
const aIsActive = !a.deadline || new Date(a.deadline) > now;
const bIsActive = !b.deadline || new Date(b.deadline) > now;
- // ALWAYS show active polls first, UNLESS sorting by status
- if (sortField !== "status") {
- if (aIsActive && !bIsActive) return -1;
- if (!aIsActive && bIsActive) return 1;
- }
-
- // If both are active or both are ended, apply the user's chosen sorting
+ // Apply the user's chosen sorting
let comparison = 0;
switch (sortField) {
diff --git a/platforms/evoting-api/src/services/VoteService.ts b/platforms/evoting-api/src/services/VoteService.ts
index 8b2fe225..afbe59e4 100644
--- a/platforms/evoting-api/src/services/VoteService.ts
+++ b/platforms/evoting-api/src/services/VoteService.ts
@@ -275,6 +275,15 @@ export class VoteService {
? Object.values(finalResults).reduce((sum, r) => sum + r.votes, 0)
: Object.values(optionCounts).reduce((sum, count) => sum + count, 0);
+ // Calculate totalEligiblePoints and pointsVoted for eReputation
+ let totalEligiblePoints: number | undefined;
+ let pointsVoted: number | undefined;
+ if (isWeighted && reputationResults) {
+ // eReputation weighted normal voting: sum of all scores
+ totalEligiblePoints = reputationResults.results.reduce((sum, r) => sum + r.score, 0);
+ pointsVoted = totalWeightedVotes; // Sum of weighted votes
+ }
+
return {
pollId,
totalVotes: votes.length, // Actual number of votes cast
@@ -282,7 +291,9 @@ export class VoteService {
totalEligibleVoters,
turnout: totalEligibleVoters > 0 ? (votes.length / totalEligibleVoters) * 100 : 0,
mode: isWeighted ? "ereputation" : "normal",
- results: finalResults
+ results: finalResults,
+ ...(totalEligiblePoints !== undefined && { totalEligiblePoints }),
+ ...(pointsVoted !== undefined && { pointsVoted })
};
} else if (poll.mode === "point") {
// STEP 1: Calculate point-based results normally (without eReputation weighting)
@@ -341,6 +352,15 @@ export class VoteService {
// Sort by total points (highest first)
finalResults.sort((a, b) => b.totalPoints - a.totalPoints);
+ // Calculate totalEligiblePoints and pointsVoted for eReputation weighted points-based voting
+ let totalEligiblePoints: number | undefined;
+ let pointsVoted: number | undefined;
+ if (reputationResults) {
+ // eReputation weighted points-based voting: sum of all scores * 100 (each user has 100 points)
+ totalEligiblePoints = reputationResults.results.reduce((sum, r) => sum + r.score, 0) * 100;
+ pointsVoted = totalWeightedPoints; // Sum of weighted points
+ }
+
return {
pollId,
totalVotes,
@@ -348,7 +368,9 @@ export class VoteService {
totalEligibleVoters,
turnout: totalEligibleVoters > 0 ? (totalVotes / totalEligibleVoters) * 100 : 0,
mode: "ereputation",
- results: finalResults
+ results: finalResults,
+ ...(totalEligiblePoints !== undefined && { totalEligiblePoints }),
+ ...(pointsVoted !== undefined && { pointsVoted })
};
} else {
// No weighting - use normal points
@@ -369,6 +391,11 @@ export class VoteService {
// Sort by total points (highest first)
finalResults.sort((a, b) => b.totalPoints - a.totalPoints);
+ // Calculate totalEligiblePoints for simple points-based voting (not eReputation)
+ // Each user has 100 points by default
+ const totalEligiblePoints = totalEligibleVoters * 100;
+ const pointsVoted = totalPoints; // Sum of all points distributed
+
return {
pollId,
totalVotes,
@@ -376,7 +403,9 @@ export class VoteService {
totalEligibleVoters,
turnout: totalEligibleVoters > 0 ? (totalVotes / totalEligibleVoters) * 100 : 0,
mode: "point",
- results: finalResults
+ results: finalResults,
+ totalEligiblePoints,
+ pointsVoted
};
}
} else if (poll.mode === "rank") {