Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: down vote button and styling fixs #13

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 42 additions & 23 deletions components/card-active.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DateTime } from 'luxon'
import { useContext } from 'react'
import GlobalStoreContext, { Feature } from '@/store/index'
import GlobalStoreContext, { Feature, VoteType } from '@/store/index'
import { useSession } from 'next-auth/react'

export default function CardActive({ item }: { item: Feature }) {
Expand All @@ -12,29 +12,48 @@ export default function CardActive({ item }: { item: Feature }) {
const isAdmin = session?.user['role'] === 'admin'

return (
<article className="flex items-center space-x-4">
<button
className="
flex flex-col items-center border border-zinc-300 py-1 w-10 rounded
hover:bg-zinc-100
dark:border-zinc-700 dark:hover:bg-zinc-700"
onClick={() => onVote(item)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
<article className="p-2 flex items-center space-x-4 border border-zinc-200 dark:border-zinc-700 rounded-md">
<div className="flex flex-col gap-1">
<button
className="flex flex-col items-center border border-zinc-300 py-1 px-2 rounded hover:bg-zinc-100 dark:border-zinc-700 dark:hover:bg-zinc-700"
onClick={() => onVote(item, VoteType.UP)}
>
<polyline points="18 15 12 9 6 15" />
</svg>
<span className="-mt-1 font-bold text-sm">{score}</span>
</button>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<polyline points="18 15 12 9 6 15" />
</svg>
</button>

<div className="text-center">{score}</div>

<button
className="flex flex-col items-center border border-zinc-300 py-1 px-2 rounded hover:bg-zinc-100 dark:border-zinc-700 dark:hover:bg-zinc-700"
onClick={() => onVote(item, VoteType.DOWN)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<polyline points="6 9 12 15 18 9" />
</svg>
</button>
</div>

<div>
<h3 className="text-lg font-bold">{title}</h3>
Expand Down
2 changes: 1 addition & 1 deletion components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function Footer() {

<div className="mt-6">
<select
className="form-select py-2 text-sm capitalize leading-none"
className="form-select py-2 text-sm capitalize"
value={theme}
onChange={(e) => setTheme(e.target.value)}
>
Expand Down
6 changes: 6 additions & 0 deletions components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { useState, useEffect } from 'react'
import { useTheme } from 'next-themes'

export default function Header() {
const [mounted, setMounted] = useState<boolean>(false)
const { resolvedTheme } = useTheme()

useEffect(() => setMounted(true), [])

if (!mounted) return null

return (
<header className="pt-20 mb-12">
<div className="flex justify-center">
Expand Down
24 changes: 22 additions & 2 deletions pages/api/vote.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
import redis, { databaseName } from '@/lib/redis'
import { VoteType } from '@/store/index'
import { unstable_getServerSession } from 'next-auth/next'
import { authOptions } from './auth/[...nextauth]'
import { NextAuthOptions } from 'next-auth'

function defineScore(voteType: VoteType) {
let score: number = 0

switch (voteType) {
case VoteType.UP:
score = 1
break
case VoteType.DOWN:
score = -1
break
default:
throw new Error('Invalid vote type')
}

return score
}

export default async (req, res) => {
const session = (await unstable_getServerSession(
req,
Expand All @@ -11,15 +29,17 @@ export default async (req, res) => {
)) as any

try {
const { title, createdAt, user, status } = req.body
const { title, createdAt, user, status, voteType } = req.body

const FEATURE = JSON.stringify({ title, createdAt, user, status })

const hasUser = await redis.sadd('s:' + FEATURE, session.user.id)

if (!hasUser) throw new Error('You have already voted')

const data = await redis.zincrby(databaseName, 1, FEATURE)
const score = defineScore(voteType)

const data = await redis.zincrby(databaseName, score, FEATURE)

res.status(200).json(data)
} catch (error) {
Expand Down
18 changes: 14 additions & 4 deletions store/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ export enum FeatureStatus {
Released = 'released'
}

export enum VoteType {
UP = 'up',
DOWN = 'down'
}

export type Feature = {
createdAt: number
score: number
Expand All @@ -20,14 +25,14 @@ export interface IGlobalStore {
loadingData: boolean
onPublish: (item: Feature) => void
onRemove: (item: Feature) => void
onVote: (item: Feature) => void
onVote: (item: Feature, voteType: VoteType) => void
onCreate: (title: string, callback?: () => void) => void
}

const GlobalStoreContext = createContext<IGlobalStore>(null)

export function GlobalStoreProvider({ children }) {
const { data, isValidating, mutate, error, } = useSWR<Feature[]>('api/list', {
const { data, isValidating, mutate, error } = useSWR<Feature[]>('api/list', {
fallbackData: []
})

Expand Down Expand Up @@ -73,13 +78,18 @@ export function GlobalStoreProvider({ children }) {
})
}

const onVote = async (item) => {
const onVote = async (item, voteType: VoteType) => {
const reqBody = {
...item,
voteType
}

fetch('api/vote', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(item)
body: JSON.stringify(reqBody)
})
.then((response) => response.json())
.then((data) => {
Expand Down
4 changes: 4 additions & 0 deletions styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
dark:border-zinc-600;
}

.form-select option {
@apply bg-white dark:bg-zinc-800;
}

.form-input {
@apply block w-full bg-transparent border-zinc-300 rounded shadow-sm
dark:bg-zinc-800 dark:border-zinc-600;
Expand Down