feat: Implémentation du système de vote sur les versions
- Ajout des fonctions de vote dans directus.js (handleVote, getUserVote) - Intégration des boutons de vote dans version-comparison.js - Support de l'annulation de vote par double-clic - Gestion des retours visuels (succès/erreur) - Passage des props nécessaires dans list-versions.js
This commit is contained in:
@@ -80,7 +80,7 @@ function rowContent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (version) {
|
if (version) {
|
||||||
setVersionData(version)
|
setVersionData({...version, versionId})
|
||||||
setIsOpenComparison(true)
|
setIsOpenComparison(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,63 @@ import PropTypes from 'prop-types'
|
|||||||
import IconButton from '@mui/material/IconButton'
|
import IconButton from '@mui/material/IconButton'
|
||||||
import ThumbUpIcon from '@mui/icons-material/ThumbUp'
|
import ThumbUpIcon from '@mui/icons-material/ThumbUp'
|
||||||
import ThumbDownIcon from '@mui/icons-material/ThumbDown'
|
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}) {
|
export default function VersionComparison({versionData}) {
|
||||||
const {current, main, outdated} = 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 (
|
return (
|
||||||
<Box sx={{padding: 3}}>
|
<Box sx={{padding: 3}}>
|
||||||
@@ -25,12 +79,22 @@ export default function VersionComparison({versionData}) {
|
|||||||
<Typography variant='body1' sx={{color: outdated ? '#D32F2F' : '#1976D2', fontWeight: 'bold'}}>
|
<Typography variant='body1' sx={{color: outdated ? '#D32F2F' : '#1976D2', fontWeight: 'bold'}}>
|
||||||
{current.contenu}
|
{current.contenu}
|
||||||
</Typography>
|
</Typography>
|
||||||
{!outdated && (
|
{!outdated && session?.user && (
|
||||||
<Box sx={{display: 'flex', justifyContent: 'flex-end', mt: 1}}>
|
<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 />
|
<ThumbUpIcon />
|
||||||
</IconButton>
|
</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 />
|
<ThumbDownIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -91,6 +155,16 @@ export default function VersionComparison({versionData}) {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</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>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -101,5 +175,6 @@ VersionComparison.propTypes = {
|
|||||||
mainHash: PropTypes.string.isRequired,
|
mainHash: PropTypes.string.isRequired,
|
||||||
current: PropTypes.object.isRequired,
|
current: PropTypes.object.isRequired,
|
||||||
main: PropTypes.object.isRequired,
|
main: PropTypes.object.isRequired,
|
||||||
|
versionId: PropTypes.string
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
}
|
}
|
||||||
|
|||||||
+98
-1
@@ -2,7 +2,7 @@
|
|||||||
import {
|
import {
|
||||||
createDirectus, rest, authentication, withToken, createItem,
|
createDirectus, rest, authentication, withToken, createItem,
|
||||||
readUser, createContentVersion, readContentVersions, saveToContentVersion,
|
readUser, createContentVersion, readContentVersions, saveToContentVersion,
|
||||||
readContentVersion,
|
readContentVersion, readItems, updateItem, deleteItem,
|
||||||
compareContentVersion
|
compareContentVersion
|
||||||
} from '@directus/sdk'
|
} from '@directus/sdk'
|
||||||
import {signOut} from 'next-auth/react'
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user