Merge pull request 'Système de vote sur les versions de contenu' (#2) from feat-votes into master

Reviewed-on: https://codeberg.org/OKI/konstitisyon.la/pulls/2
This commit is contained in:
Cédric Famibelle-Pronzola
2024-12-16 05:20:10 +00:00
3 changed files with 177 additions and 5 deletions
+1 -1
View File
@@ -80,7 +80,7 @@ function rowContent({
})
if (version) {
setVersionData(version)
setVersionData({...version, versionId})
setIsOpenComparison(true)
}
}
+78 -3
View File
@@ -6,9 +6,63 @@ import PropTypes from 'prop-types'
import IconButton from '@mui/material/IconButton'
import ThumbUpIcon from '@mui/icons-material/ThumbUp'
import ThumbDownIcon from '@mui/icons-material/ThumbDown'
import {useState, useEffect} from 'react'
import {useSession} from 'next-auth/react'
import Snackbar from '@mui/material/Snackbar'
import Alert from '@mui/material/Alert'
import {handleVote, getUserVote} from '../../lib/directus.js'
export default function VersionComparison({versionData}) {
const {current, main, outdated} = versionData
const {data: session} = useSession()
const [snackbar, setSnackbar] = useState({open: false, message: '', severity: 'success'})
const [currentVote, setCurrentVote] = useState(null)
useEffect(() => {
const fetchVote = async () => {
if (session?.user && versionData.versionId) {
try {
const vote = await getUserVote({
accessToken: session.user.accessToken,
userId: session.user.userId,
versionId: versionData.versionId
})
setCurrentVote(vote)
} catch (error) {
console.error('Error fetching vote:', error)
}
}
}
fetchVote()
}, [session, versionData.versionId])
const handleVoteClick = async voteValue => {
try {
const newVoteValue = await handleVote({
accessToken: session.user.accessToken,
userId: session.user.userId,
versionId: versionData.versionId,
voteValue
})
setCurrentVote(newVoteValue)
setSnackbar({
open: true,
message: newVoteValue === null ? 'Vote annulé' : 'Vote enregistré avec succès',
severity: 'success'
})
} catch {
setSnackbar({
open: true,
message: 'Erreur lors du vote',
severity: 'error'
})
}
}
const handleCloseSnackbar = () => {
setSnackbar(prev => ({...prev, open: false}))
}
return (
<Box sx={{padding: 3}}>
@@ -25,12 +79,22 @@ export default function VersionComparison({versionData}) {
<Typography variant='body1' sx={{color: outdated ? '#D32F2F' : '#1976D2', fontWeight: 'bold'}}>
{current.contenu}
</Typography>
{!outdated && (
{!outdated && session?.user && (
<Box sx={{display: 'flex', justifyContent: 'flex-end', mt: 1}}>
<IconButton size='small' color='success' aria-label='vote positif'>
<IconButton
size='small'
color={currentVote === 1 ? 'success' : 'primary'}
aria-label='vote positif'
onClick={() => handleVoteClick(1)}
>
<ThumbUpIcon />
</IconButton>
<IconButton size='small' color='error' aria-label='vote négatif'>
<IconButton
size='small'
color={currentVote === -1 ? 'error' : 'primary'}
aria-label='vote négatif'
onClick={() => handleVoteClick(-1)}
>
<ThumbDownIcon />
</IconButton>
</Box>
@@ -91,6 +155,16 @@ export default function VersionComparison({versionData}) {
</Grid>
</Grid>
</Box>
<Snackbar
open={snackbar.open}
autoHideDuration={6000}
anchorOrigin={{vertical: 'bottom', horizontal: 'center'}}
onClose={handleCloseSnackbar}
>
<Alert variant='filled' severity={snackbar.severity} sx={{width: '100%'}} onClose={handleCloseSnackbar}>
{snackbar.message}
</Alert>
</Snackbar>
</Box>
)
}
@@ -101,5 +175,6 @@ VersionComparison.propTypes = {
mainHash: PropTypes.string.isRequired,
current: PropTypes.object.isRequired,
main: PropTypes.object.isRequired,
versionId: PropTypes.string
}).isRequired,
}
+98 -1
View File
@@ -2,7 +2,7 @@
import {
createDirectus, rest, authentication, withToken, createItem,
readUser, createContentVersion, readContentVersions, saveToContentVersion,
readContentVersion,
readContentVersion, readItems, updateItem, deleteItem,
compareContentVersion
} from '@directus/sdk'
import {signOut} from 'next-auth/react'
@@ -241,3 +241,100 @@ export async function createVersion({
}
}
}
export async function handleVote({
accessToken,
userId,
versionId,
voteValue
}) {
try {
await handleUserStatus(accessToken, userId)
const existingVotes = await directusClient.request(
withToken(
accessToken,
readItems('votes', {
filter: {
_and: [
{content_version_id: {_eq: versionId}},
{user_created: {_eq: userId}}
]
}
})
)
)
if (existingVotes && existingVotes.length > 0) {
const existingVote = existingVotes[0]
if (existingVote.vote === voteValue) {
await directusClient.request(
withToken(
accessToken,
deleteItem('votes', existingVote.id)
)
)
return null
}
const vote = {
content_version_id: versionId,
vote: voteValue
}
await directusClient.request(
withToken(
accessToken,
updateItem('votes', existingVote.id, vote)
)
)
return voteValue
}
const vote = {
content_version_id: versionId,
vote: voteValue
}
await directusClient.request(
withToken(
accessToken,
createItem('votes', vote)
)
)
return voteValue
} catch (error) {
console.error('Error voting:', error)
throw error
}
}
export async function getUserVote({
accessToken,
userId,
versionId
}) {
try {
await handleUserStatus(accessToken, userId)
const existingVotes = await directusClient.request(
withToken(
accessToken,
readItems('votes', {
filter: {
_and: [
{content_version_id: {_eq: versionId}},
{user_created: {_eq: userId}}
]
}
})
)
)
return existingVotes && existingVotes.length > 0 ? existingVotes[0].vote : null
} catch (error) {
console.error('Error fetching vote:', error)
throw error
}
}