mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-12 22:26:12 -05:00
Banner informing user has voted
This commit is contained in:
@@ -249,3 +249,24 @@ export function usersForVoting(
|
||||
|
||||
return shuffle(result.filter(({ user }) => user.id !== loggedInUser.id));
|
||||
}
|
||||
|
||||
const hasVotedStm = sql.prepare(`
|
||||
SELECT 1
|
||||
FROM "PlusVote"
|
||||
WHERE "authorId" = $userId AND "month" = $month AND "year" = $year
|
||||
`);
|
||||
|
||||
export function hasVoted({
|
||||
month,
|
||||
year,
|
||||
user,
|
||||
}: MonthYear & { user?: Pick<User, "id"> }) {
|
||||
if (!user) return false;
|
||||
return Boolean(
|
||||
hasVotedStm.get({
|
||||
userId: user.id,
|
||||
month,
|
||||
year,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as React from "react";
|
||||
import { z } from "zod";
|
||||
import { Avatar } from "~/components/Avatar";
|
||||
import { Button } from "~/components/Button";
|
||||
import { CheckmarkIcon } from "~/components/icons/Checkmark";
|
||||
import { RelativeTime } from "~/components/RelativeTime";
|
||||
import { PLUS_DOWNVOTE, PLUS_UPVOTE } from "~/constants";
|
||||
import { db } from "~/db";
|
||||
@@ -100,14 +101,13 @@ type PlusVotingLoaderData =
|
||||
relativeTime: string;
|
||||
timestamp: number;
|
||||
timing: "starts" | "ends";
|
||||
voted?: boolean;
|
||||
}
|
||||
// user can vote
|
||||
| {
|
||||
type: "voting";
|
||||
usersForVoting: UsersForVoting;
|
||||
}
|
||||
// user already voted
|
||||
| { type: "votingInfo"; votingInfo: { placeholder: true } };
|
||||
};
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const user = await getUser(request);
|
||||
@@ -124,13 +124,18 @@ export const loader: LoaderFunction = async ({ request }) => {
|
||||
}
|
||||
|
||||
const usersForVoting = db.plusVotes.usersForVoting(user);
|
||||
const hasVoted = db.plusVotes.hasVoted({
|
||||
user,
|
||||
...nextNonCompletedVoting(new Date()),
|
||||
});
|
||||
|
||||
if (!usersForVoting) {
|
||||
if (!usersForVoting || hasVoted) {
|
||||
return json<PlusVotingLoaderData>({
|
||||
type: "timeInfo",
|
||||
relativeTime: formatDistance(endDate, now, { addSuffix: true }),
|
||||
timestamp: endDate.getTime(),
|
||||
timing: "ends",
|
||||
voted: hasVoted,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -150,9 +155,6 @@ export default function PlusVotingPage() {
|
||||
case "voting": {
|
||||
return <Voting {...data} />;
|
||||
}
|
||||
case "votingInfo": {
|
||||
return null;
|
||||
}
|
||||
default: {
|
||||
assertUnreachable(data);
|
||||
}
|
||||
@@ -163,13 +165,20 @@ function VotingTimingInfo(
|
||||
data: Extract<PlusVotingLoaderData, { type: "timeInfo" }>
|
||||
) {
|
||||
return (
|
||||
<div className="text-sm text-center">
|
||||
{data.timing === "starts"
|
||||
? "Next voting starts"
|
||||
: "Voting is currently happening. Ends"}{" "}
|
||||
<RelativeTime timestamp={data.timestamp}>
|
||||
{data.relativeTime}
|
||||
</RelativeTime>
|
||||
<div className="stack md">
|
||||
{data.voted ? (
|
||||
<div className="plus-voting__alert">
|
||||
<CheckmarkIcon /> You have voted
|
||||
</div>
|
||||
) : null}
|
||||
<div className="text-sm text-center">
|
||||
{data.timing === "starts"
|
||||
? "Next voting starts"
|
||||
: "Voting is currently happening. Ends"}{" "}
|
||||
<RelativeTime timestamp={data.timestamp}>
|
||||
{data.relativeTime}
|
||||
</RelativeTime>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
--theme-warning: #f5f587;
|
||||
--theme-info: #a3a6ff;
|
||||
--theme-success: #a3ffae;
|
||||
--theme-success-transparent: #a3ffae52;
|
||||
--theme: hsl(255deg 66.7% 75%);
|
||||
--theme-transparent: hsl(255deg 66.7% 75% / 40%);
|
||||
--theme-transparent-vibrant: hsl(255deg 78% 65% / 54%);
|
||||
@@ -29,7 +30,7 @@
|
||||
--fonts-xl: 1.5rem;
|
||||
--fonts-lg: 1.2rem;
|
||||
--fonts-md: 1rem;
|
||||
--fonts-sm: 0.9rem;
|
||||
--fonts-sm: 0.92rem;
|
||||
--fonts-xs: 0.8rem;
|
||||
--fonts-xxs: 0.7rem;
|
||||
--extra-bold: 700;
|
||||
|
||||
@@ -123,3 +123,25 @@
|
||||
.plus-voting__progress {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.plus-voting__alert {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--theme-success-transparent);
|
||||
border-radius: var(--rounded);
|
||||
color: var(--text);
|
||||
font-size: var(--fonts-sm);
|
||||
font-weight: var(--semi-bold);
|
||||
gap: var(--s-2);
|
||||
line-height: 0.75;
|
||||
margin-inline: auto;
|
||||
padding-block: var(--s-1);
|
||||
padding-inline-end: var(--s-4);
|
||||
padding-inline-start: var(--s-3);
|
||||
}
|
||||
|
||||
.plus-voting__alert > svg {
|
||||
height: 1.75rem;
|
||||
fill: var(--theme-success);
|
||||
}
|
||||
|
||||
@@ -69,4 +69,6 @@ describe("Plus voting results page", () => {
|
||||
cy.contains("Sendou");
|
||||
cy.contains("your score was");
|
||||
});
|
||||
|
||||
// xxx: describe Plus Voting
|
||||
});
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
- /plus
|
||||
-> Shows if you are suggested or not
|
||||
- CRUD suggestions for your own tier and below
|
||||
- CUD disabled when voting is active
|
||||
|
||||
- /plus/voting
|
||||
|
||||
## Voting is active
|
||||
|
||||
-> If not member just show same view as when voting is not active
|
||||
|
||||
- When voting the progress is saved in your browser so even if you leave the page you can continue where you left off.
|
||||
- +1/-1 regardless of region
|
||||
|
||||
## Voting is not active
|
||||
|
||||
- You will see who passed the last voting and who didn't.
|
||||
- If you didn't pass the voting and you were a suggestion then you will see your own percentage
|
||||
- others see who passed and who didn't per tier
|
||||
- at the top show your own results in green/red per tier
|
||||
|
||||
## Tables
|
||||
|
||||
- "User"
|
||||
|
||||
* "plusTier" 1/2/3
|
||||
|
||||
- "PlusSuggestion"
|
||||
|
||||
* "text" text
|
||||
* "authorId" text
|
||||
* "suggestedId" text
|
||||
* "month"
|
||||
* "year"
|
||||
* "tier" 1/2/3
|
||||
* "createdAt"
|
||||
|
||||
- "PlusVote"
|
||||
|
||||
* "month"
|
||||
* "year"
|
||||
* "tier"
|
||||
* "authorId"
|
||||
* "votedId"
|
||||
* "score"
|
||||
|
||||
unique on month, year, authorId, votedId
|
||||
|
||||
# Cypress
|
||||
|
||||
- uses different db file
|
||||
- uses different port
|
||||
- NODE_ENV = test
|
||||
Reference in New Issue
Block a user