feat: récupère le total des votes

This commit is contained in:
2026-01-24 23:35:48 +04:00
parent b838f46b2b
commit 22130529f6
7 changed files with 140 additions and 25 deletions
+28 -2
View File
@@ -46,7 +46,7 @@ const renderMarkdownToHtml = async content => {
}
}
export default function ExportPdfButton({versionData, isOutdated = false, size = 'medium', variant = 'outlined'}) {
export default function ExportPdfButton({versionData, isOutdated = false, voteCounts = null, size = 'medium', variant = 'outlined'}) {
const [isExporting, setIsExporting] = useState(false)
const handleExportPdf = async () => {
@@ -146,6 +146,11 @@ export default function ExportPdfButton({versionData, isOutdated = false, size =
const voteStatus = (isExpired || isOutdated) ? 'fermé' : 'ouvert'
const voteColor = voteStatus === 'ouvert' ? '#2e7d32' : '#d32f2f'
// Vote counts display
const voteTotal = voteCounts ? voteCounts.total : 0
const voteTotalColor = voteTotal > 0 ? '#2e7d32' : (voteTotal < 0 ? '#d32f2f' : '#666')
const voteTotalSign = voteTotal >= 0 ? '+' : ''
// Render markdown content to HTML
const renderedContent = await renderMarkdownToHtml(versionData.delta?.contenu)
@@ -162,9 +167,25 @@ export default function ExportPdfButton({versionData, isOutdated = false, size =
<strong>Date de création :</strong> ${formatDate(versionData.date_created, 'PPpp', {withTimezone: true})}
</p>
<p style="margin: 5px 0; color: #666; font-size: 14px;">
<strong>Statut du vote :</strong>
<strong>Statut du vote :</strong>
<span style="color: ${voteColor}; font-weight: bold;">${voteStatus}</span>
</p>
${voteCounts ? `
<div style="margin-top: 15px; padding: 15px; background-color: #f5f5f5; border-radius: 8px;">
<p style="margin: 0 0 10px 0; font-size: 16px; font-weight: bold; color: #333;">
📊 Résultats des votes
</p>
<p style="margin: 5px 0; font-size: 14px;">
👍 Votes positifs : <strong style="color: #2e7d32;">${voteCounts.positive}</strong>
</p>
<p style="margin: 5px 0; font-size: 14px;">
👎 Votes négatifs : <strong style="color: #d32f2f;">${voteCounts.negative}</strong>
</p>
<p style="margin: 10px 0 0 0; font-size: 16px; font-weight: bold;">
🏆 Total : <span style="color: ${voteTotalColor};">${voteTotalSign}${voteTotal}</span>
</p>
</div>
` : ''}
</div>
</div>
@@ -253,6 +274,11 @@ export default function ExportPdfButton({versionData, isOutdated = false, size =
ExportPdfButton.propTypes = {
versionData: PropTypes.object.isRequired,
isOutdated: PropTypes.bool,
voteCounts: PropTypes.shape({
positive: PropTypes.number,
negative: PropTypes.number,
total: PropTypes.number
}),
size: PropTypes.oneOf(['small', 'medium', 'large']),
variant: PropTypes.oneOf(['text', 'outlined', 'contained'])
}
+43 -8
View File
@@ -24,7 +24,7 @@ import ShareButton from './share-button.js'
import ExportPdfButton from './export-pdf-button.js'
import PrintButton from './print-button.js'
import {formatDate} from '@/lib/format.js'
import {compareVersion} from '@/lib/directus.js'
import {compareVersion, getVoteCounts} from '@/lib/directus.js'
import {filterVersions, getFilterStats} from '@/lib/version-utils.js'
const columns = [
@@ -85,7 +85,8 @@ function rowContent({
setIsErrorAlertOpen,
setIsOpenComparison,
setVersionCompare,
outdatedStatusMap
outdatedStatusMap,
voteCountsMap
}) {
const handleButtonClick = async versionId => {
const version = await compareVersion({
@@ -104,6 +105,7 @@ function rowContent({
}
const isOutdated = outdatedStatusMap[row.id] || false
const voteCounts = voteCountsMap[row.id] || null
return (
<>
@@ -141,12 +143,14 @@ function rowContent({
<ExportPdfButton
versionData={row}
isOutdated={isOutdated}
voteCounts={voteCounts}
size='small'
variant='text'
/>
<PrintButton
versionData={row}
isOutdated={isOutdated}
voteCounts={voteCounts}
size='small'
variant='text'
/>
@@ -188,11 +192,13 @@ export default function ListVersions({
status: ''
})
const [outdatedStatusMap, setOutdatedStatusMap] = useState({})
const [voteCountsMap, setVoteCountsMap] = useState({})
// Fetch outdated status for all versions
// Fetch outdated status and vote counts for all versions
useEffect(() => {
async function fetchOutdatedStatus() {
async function fetchVersionsData() {
const statusMap = {}
const countsMap = {}
await Promise.all(
data.map(async version => {
@@ -209,18 +215,27 @@ export default function ListVersions({
if (comparisonData) {
statusMap[version.id] = comparisonData.outdated || false
}
// Fetch vote counts
const counts = await getVoteCounts({
accessToken,
versionId: version.id
})
countsMap[version.id] = counts
} catch (error) {
console.warn(`Failed to fetch outdated status for version ${version.id}:`, error)
console.warn(`Failed to fetch data for version ${version.id}:`, error)
statusMap[version.id] = false
countsMap[version.id] = {positive: 0, negative: 0, total: 0}
}
})
)
setOutdatedStatusMap(statusMap)
setVoteCountsMap(countsMap)
}
if (data.length > 0) {
fetchOutdatedStatus()
fetchVersionsData()
}
}, [data, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen])
@@ -230,6 +245,19 @@ export default function ListVersions({
const versionData = data.find(({id}) => id === versionCompare?.versionId)
// Function to refresh vote counts for a specific version after voting
const refreshVoteCounts = async versionId => {
try {
const counts = await getVoteCounts({
accessToken,
versionId
})
setVoteCountsMap(prev => ({...prev, [versionId]: counts}))
} catch (error) {
console.warn(`Failed to refresh vote counts for version ${versionId}:`, error)
}
}
const handleSearchChange = newSearchTerm => {
setSearchTerm(newSearchTerm)
}
@@ -305,7 +333,7 @@ export default function ListVersions({
components={VirtuosoTableComponents}
fixedHeaderContent={fixedHeaderContent}
itemContent={(index, row) => rowContent({
index, row, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen, setIsOpenComparison, setVersionCompare, outdatedStatusMap
index, row, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen, setIsOpenComparison, setVersionCompare, outdatedStatusMap, voteCountsMap
})}
/>
</Paper>
@@ -316,13 +344,20 @@ export default function ListVersions({
userId={userId}
setError={setError}
setIsErrorAlertOpen={setIsErrorAlertOpen}
onVoteSuccess={refreshVoteCounts}
/>
)
)}
</Box>
{isOpenComparison && (
<VersionDialog versionData={versionData} versionCompare={versionCompare} isOpen={isOpenComparison} setIsOpen={setIsOpenComparison} />
<VersionDialog
versionData={versionData}
versionCompare={versionCompare}
isOpen={isOpenComparison}
setIsOpen={setIsOpenComparison}
onVoteSuccess={refreshVoteCounts}
/>
)}
<SessionExpired ref={countdownRef} setError={setError} setIsErrorAlertOpen={setIsErrorAlertOpen} />
</>
+28 -2
View File
@@ -49,7 +49,7 @@ const renderMarkdownToHtml = async content => {
}
}
export default function PrintButton({versionData, isOutdated = false, size = 'medium', variant = 'outlined'}) {
export default function PrintButton({versionData, isOutdated = false, voteCounts = null, size = 'medium', variant = 'outlined'}) {
const [isPrinting, setIsPrinting] = useState(false)
const [snackbar, setSnackbar] = useState({open: false, message: '', severity: 'success'})
@@ -65,6 +65,11 @@ export default function PrintButton({versionData, isOutdated = false, size = 'me
const voteStatus = (isExpired || isOutdated) ? 'fermé' : 'ouvert'
const voteColor = voteStatus === 'ouvert' ? '#2e7d32' : '#d32f2f'
// Vote counts display
const voteTotal = voteCounts ? voteCounts.total : 0
const voteTotalColor = voteTotal > 0 ? '#2e7d32' : (voteTotal < 0 ? '#d32f2f' : '#666')
const voteTotalSign = voteTotal >= 0 ? '+' : ''
// Render markdown content to HTML
const renderedContent = await renderMarkdownToHtml(versionData.delta?.contenu)
@@ -306,9 +311,25 @@ export default function PrintButton({versionData, isOutdated = false, size = 'me
<strong>Date de création :</strong> ${formatDate(versionData.date_created, 'PPpp', {withTimezone: true})}
</div>
<div class="metadata">
<strong>Statut du vote :</strong>
<strong>Statut du vote :</strong>
<span class="vote-status">${voteStatus}</span>
</div>
${voteCounts ? `
<div style="margin-top: 15px; padding: 15px; background-color: #f8f9fa; border-radius: 8px; border: 1px solid #e0e0e0;">
<p style="margin: 0 0 10px 0; font-size: 16px; font-weight: bold; color: #333;">
📊 Résultats des votes
</p>
<p style="margin: 5px 0; font-size: 14px;">
👍 Votes positifs : <strong style="color: #2e7d32;">${voteCounts.positive}</strong>
</p>
<p style="margin: 5px 0; font-size: 14px;">
👎 Votes négatifs : <strong style="color: #d32f2f;">${voteCounts.negative}</strong>
</p>
<p style="margin: 10px 0 0 0; font-size: 16px; font-weight: bold;">
🏆 Total : <span style="color: ${voteTotalColor};">${voteTotalSign}${voteTotal}</span>
</p>
</div>
` : ''}
</div>
<div class="content-section">
@@ -392,6 +413,11 @@ export default function PrintButton({versionData, isOutdated = false, size = 'me
PrintButton.propTypes = {
versionData: PropTypes.object.isRequired,
isOutdated: PropTypes.bool,
voteCounts: PropTypes.shape({
positive: PropTypes.number,
negative: PropTypes.number,
total: PropTypes.number
}),
size: PropTypes.oneOf(['small', 'medium', 'large']),
variant: PropTypes.oneOf(['text', 'outlined', 'contained'])
}
+7 -2
View File
@@ -16,7 +16,7 @@ import CopyButton from './copy-button.js'
import {formatDate} from '@/lib/format.js'
import {getVoteCounts} from '@/lib/directus.js'
export default function VersionComparison({versionData, versionCompare, voteRefreshKey = 0, onVoteResult}) {
export default function VersionComparison({versionData, versionCompare, voteRefreshKey = 0, onVoteResult, onVoteSuccess}) {
const {data: session} = useSession()
const {current, main, outdated} = versionCompare
const [snackbar, setSnackbar] = useState({open: false, message: '', severity: 'success'})
@@ -44,6 +44,10 @@ export default function VersionComparison({versionData, versionCompare, voteRefr
})
setVoteCounts(counts)
if (onVoteSuccess) {
onVoteSuccess(versionCompare.versionId)
}
}
if (onVoteResult) {
@@ -265,5 +269,6 @@ VersionComparison.propTypes = {
versionId: PropTypes.string
}).isRequired,
voteRefreshKey: PropTypes.number,
onVoteResult: PropTypes.func
onVoteResult: PropTypes.func,
onVoteSuccess: PropTypes.func
}
+4 -3
View File
@@ -14,7 +14,7 @@ import CompareArrowsIcon from '@mui/icons-material/CompareArrows'
import {useTheme} from '@mui/material/styles'
import VersionComparison from './version-comparison.js'
export default function VersionDialog({versionData, versionCompare, isOpen, setIsOpen}) {
export default function VersionDialog({versionData, versionCompare, isOpen, setIsOpen, onVoteSuccess}) {
const theme = useTheme()
const fullScreen = useMediaQuery(theme.breakpoints.down('md'))
@@ -59,7 +59,7 @@ export default function VersionDialog({versionData, versionCompare, isOpen, setI
</DialogTitle>
<DialogContent sx={{minHeight: '60vh'}}>
<VersionComparison versionData={versionData} versionCompare={versionCompare} />
<VersionComparison versionData={versionData} versionCompare={versionCompare} onVoteSuccess={onVoteSuccess} />
</DialogContent>
<DialogActions sx={{px: 3, py: 2}}>
@@ -84,5 +84,6 @@ VersionDialog.propTypes = {
main: PropTypes.object.isRequired
}).isRequired,
isOpen: PropTypes.bool.isRequired,
setIsOpen: PropTypes.func.isRequired
setIsOpen: PropTypes.func.isRequired,
onVoteSuccess: PropTypes.func
}
+20 -4
View File
@@ -24,7 +24,7 @@ import CopyButton from './copy-button.js'
import ExportPdfButton from './export-pdf-button.js'
import PrintButton from './print-button.js'
import VersionComparison from './version-comparison.js'
import {getVersion, compareVersion} from '@/lib/directus.js'
import {getVersion, compareVersion, getVoteCounts} from '@/lib/directus.js'
import {formatDate} from '@/lib/format.js'
export default function VersionPage({session, versionId, viewMode}) {
@@ -39,6 +39,7 @@ export default function VersionPage({session, versionId, viewMode}) {
const [isErrorAlertOpen, setIsErrorAlertOpen] = useState(false)
const [snackbar, setSnackbar] = useState({open: false, message: '', severity: 'success'})
const [voteRefreshKey, setVoteRefreshKey] = useState(0)
const [voteCounts, setVoteCounts] = useState(null)
useEffect(() => {
async function fetchVersionData() {
@@ -67,6 +68,13 @@ export default function VersionPage({session, versionId, viewMode}) {
if (comparison) {
setVersionCompare({...comparison, versionId})
}
const counts = await getVoteCounts({
accessToken,
versionId
})
setVoteCounts(counts)
} catch (error) {
console.error('Failed to fetch version:', error)
setError('Impossible de charger cette version')
@@ -83,7 +91,7 @@ export default function VersionPage({session, versionId, viewMode}) {
router.push('/dashboard')
}
const handleVoteResult = result => {
const handleVoteResult = async result => {
setSnackbar({
open: true,
message: result.message,
@@ -91,6 +99,14 @@ export default function VersionPage({session, versionId, viewMode}) {
})
// Force refresh of both VoteButtons components by changing the key
setVoteRefreshKey(prev => prev + 1)
if (result.success) {
const counts = await getVoteCounts({
accessToken,
versionId
})
setVoteCounts(counts)
}
}
const handleCloseSnackbar = () => {
@@ -222,8 +238,8 @@ export default function VersionPage({session, versionId, viewMode}) {
</Button>
<Box sx={{display: 'flex', alignItems: 'center', gap: 2}}>
<ExportPdfButton versionData={versionData} size='medium' />
<PrintButton versionData={versionData} size='medium' />
<ExportPdfButton versionData={versionData} isOutdated={versionCompare?.outdated} voteCounts={voteCounts} size='medium' />
<PrintButton versionData={versionData} isOutdated={versionCompare?.outdated} voteCounts={voteCounts} size='medium' />
<Tooltip title='Partager cette version'>
<IconButton color='primary' onClick={handleShare}>
<ShareIcon />
+10 -4
View File
@@ -207,7 +207,8 @@ export default function VersionTimeline({
accessToken,
userId,
setError,
setIsErrorAlertOpen
setIsErrorAlertOpen,
onVoteSuccess
}) {
const countdownRef = useRef()
const [isOpenComparison, setIsOpenComparison] = useState(false)
@@ -216,12 +217,16 @@ export default function VersionTimeline({
const versionData = data.find(({id}) => id === versionCompare?.versionId)
const handleVoteResult = result => {
const handleVoteResult = (result, versionId) => {
setSnackbar({
open: true,
message: result.message,
severity: result.success ? 'success' : 'error'
})
if (result.success && onVoteSuccess && versionId) {
onVoteSuccess(versionId)
}
}
return (
@@ -239,7 +244,7 @@ export default function VersionTimeline({
setIsErrorAlertOpen={setIsErrorAlertOpen}
setIsOpenComparison={setIsOpenComparison}
setVersionCompare={setVersionCompare}
onVoteResult={handleVoteResult}
onVoteResult={result => handleVoteResult(result, version.id)}
/>
))}
</Box>
@@ -283,7 +288,8 @@ VersionTimeline.propTypes = {
accessToken: PropTypes.string.isRequired,
userId: PropTypes.string.isRequired,
setError: PropTypes.func.isRequired,
setIsErrorAlertOpen: PropTypes.func.isRequired
setIsErrorAlertOpen: PropTypes.func.isRequired,
onVoteSuccess: PropTypes.func
}
VersionItem.propTypes = {