From 22130529f604d5630031558c9d02a0980cc8b706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20FAMIBELLE-PRONZOLA?= Date: Sat, 24 Jan 2026 23:35:48 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20r=C3=A9cup=C3=A8re=20le=20total=20des?= =?UTF-8?q?=20votes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/versions/export-pdf-button.js | 30 ++++++++++++- components/versions/list-versions.js | 51 +++++++++++++++++++---- components/versions/print-button.js | 30 ++++++++++++- components/versions/version-comparison.js | 9 +++- components/versions/version-dialog.js | 7 ++-- components/versions/version-page.js | 24 +++++++++-- components/versions/version-timeline.js | 14 +++++-- 7 files changed, 140 insertions(+), 25 deletions(-) diff --git a/components/versions/export-pdf-button.js b/components/versions/export-pdf-button.js index 06ae409..b7d509d 100644 --- a/components/versions/export-pdf-button.js +++ b/components/versions/export-pdf-button.js @@ -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 = Date de création : ${formatDate(versionData.date_created, 'PPpp', {withTimezone: true})}

- Statut du vote : + Statut du vote : ${voteStatus}

+ ${voteCounts ? ` +
+

+ 📊 Résultats des votes +

+

+ 👍 Votes positifs : ${voteCounts.positive} +

+

+ 👎 Votes négatifs : ${voteCounts.negative} +

+

+ 🏆 Total : ${voteTotalSign}${voteTotal} +

+
+ ` : ''} @@ -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']) } diff --git a/components/versions/list-versions.js b/components/versions/list-versions.js index c51ba3e..d598a83 100644 --- a/components/versions/list-versions.js +++ b/components/versions/list-versions.js @@ -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({ @@ -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 })} /> @@ -316,13 +344,20 @@ export default function ListVersions({ userId={userId} setError={setError} setIsErrorAlertOpen={setIsErrorAlertOpen} + onVoteSuccess={refreshVoteCounts} /> ) )} {isOpenComparison && ( - + )} diff --git a/components/versions/print-button.js b/components/versions/print-button.js index 0b57342..0db53f5 100644 --- a/components/versions/print-button.js +++ b/components/versions/print-button.js @@ -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 Date de création : ${formatDate(versionData.date_created, 'PPpp', {withTimezone: true})} + ${voteCounts ? ` +
+

+ 📊 Résultats des votes +

+

+ 👍 Votes positifs : ${voteCounts.positive} +

+

+ 👎 Votes négatifs : ${voteCounts.negative} +

+

+ 🏆 Total : ${voteTotalSign}${voteTotal} +

+
+ ` : ''}
@@ -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']) } diff --git a/components/versions/version-comparison.js b/components/versions/version-comparison.js index f586e4a..56aad88 100644 --- a/components/versions/version-comparison.js +++ b/components/versions/version-comparison.js @@ -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 } diff --git a/components/versions/version-dialog.js b/components/versions/version-dialog.js index ff8f208..665fea7 100644 --- a/components/versions/version-dialog.js +++ b/components/versions/version-dialog.js @@ -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 - + @@ -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 } diff --git a/components/versions/version-page.js b/components/versions/version-page.js index 1fc108c..c42524c 100644 --- a/components/versions/version-page.js +++ b/components/versions/version-page.js @@ -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}) { - - + + diff --git a/components/versions/version-timeline.js b/components/versions/version-timeline.js index e0f1d5c..9cb16b3 100644 --- a/components/versions/version-timeline.js +++ b/components/versions/version-timeline.js @@ -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)} /> ))} @@ -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 = {