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:
2024-12-16 09:14:02 +04:00
parent 3105327735
commit aa02a51a1b
3 changed files with 177 additions and 5 deletions
+1 -1
View File
@@ -80,7 +80,7 @@ function rowContent({
}) })
if (version) { if (version) {
setVersionData(version) setVersionData({...version, versionId})
setIsOpenComparison(true) setIsOpenComparison(true)
} }
} }
+78 -3
View File
@@ -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
View File
@@ -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
}
}