From a184665ed10b7c81983bf0b6eb588e1643e04f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20FAMIBELLE-PRONZOLA?= Date: Sat, 24 Jan 2026 21:34:02 +0400 Subject: [PATCH] feat: simplifie la vue timeline --- components/versions/list-versions.js | 10 +- components/versions/version-comparison.js | 47 ++- components/versions/version-timeline.js | 454 +++++++--------------- 3 files changed, 169 insertions(+), 342 deletions(-) diff --git a/components/versions/list-versions.js b/components/versions/list-versions.js index 004d827..c51ba3e 100644 --- a/components/versions/list-versions.js +++ b/components/versions/list-versions.js @@ -249,8 +249,10 @@ export default function ListVersions({ @@ -271,13 +273,14 @@ export default function ListVersions({ size='small' value={viewMode} onChange={handleViewModeChange} + sx={{alignSelf: {xs: 'center', sm: 'auto'}}} > - + Table - + Timeline @@ -308,7 +311,6 @@ export default function ListVersions({ ) : ( @@ -106,31 +107,29 @@ export default function VersionComparison({versionData, versionCompare, voteRefr @{versionData.user_created?.split('-')[0] || 'Système'} - {!outdated && ( - - - {versionData && ( - - {formatDate(versionData.date_created)} - - )} - - - + + {versionData && ( + + {formatDate(versionData.date_created)} + + )} + - )} + + diff --git a/components/versions/version-timeline.js b/components/versions/version-timeline.js index 61a3db6..e0f1d5c 100644 --- a/components/versions/version-timeline.js +++ b/components/versions/version-timeline.js @@ -1,118 +1,36 @@ import {useRef, useState, useEffect} from 'react' -import {useTheme} from '@mui/material/styles' import PropTypes from 'prop-types' import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' -import Card from '@mui/material/Card' -import CardContent from '@mui/material/CardContent' -import CardActions from '@mui/material/CardActions' -import Button from '@mui/material/Button' -import Chip from '@mui/material/Chip' -import Avatar from '@mui/material/Avatar' -import Divider from '@mui/material/Divider' -import Timeline from '@mui/lab/Timeline' -import TimelineItem from '@mui/lab/TimelineItem' -import TimelineSeparator from '@mui/lab/TimelineSeparator' -import TimelineConnector from '@mui/lab/TimelineConnector' -import TimelineContent from '@mui/lab/TimelineContent' -import TimelineDot from '@mui/lab/TimelineDot' -import TimelineOppositeContent from '@mui/lab/TimelineOppositeContent' -import AccessTimeIcon from '@mui/icons-material/AccessTime' -import CheckCircleIcon from '@mui/icons-material/CheckCircle' -import ErrorIcon from '@mui/icons-material/Error' -import EditIcon from '@mui/icons-material/Edit' +import IconButton from '@mui/material/IconButton' +import Collapse from '@mui/material/Collapse' import Snackbar from '@mui/material/Snackbar' import Alert from '@mui/material/Alert' +import CompareArrowsIcon from '@mui/icons-material/CompareArrows' +import ExpandMoreIcon from '@mui/icons-material/ExpandMore' +import ExpandLessIcon from '@mui/icons-material/ExpandLess' import SessionExpired from '../session/session-expired.js' -import MarkdownRenderer from '../markdown-renderer/index.js' import VersionDialog from './version-dialog.js' import VoteButtons from './vote-buttons.js' import CopyButton from './copy-button.js' -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' -function getVersionStatus(version, index, totalVersions, data) { - // Logic to determine version status based on position and data - // Find which version is the "main" (published) by checking if it matches current content - // This would require the current item content to be passed - // For now, we assume the most recent is current unless it's been promoted - - // Check if this is the initial version - if (index === totalVersions - 1) { - return 'initial' // First version +function getStatusColor(isOutdated, index) { + if (isOutdated) { + return '#D32F2F' } - // If there's a more recent version after this one, this is outdated - // unless this IS the main version (would need item content to determine) - if (index > 0) { - return 'outdated' // Older versions are outdated + if (index === 0) { + return '#1976D2' } - // Most recent version is current (being edited/proposed) - return 'current' + return '#9E9E9E' } -function getStatusConfig(status) { - switch (status) { - case 'current': { - return { - color: '#1976D2', - bgColor: '#E3F2FD', - icon: , - label: 'En cours', - chipColor: 'primary' - } - } - - case 'published': { - return { - color: '#2E7D32', - bgColor: '#E8F5E9', - icon: , - label: 'Publié', - chipColor: 'success' - } - } - - case 'archived': { - return { - color: '#757575', - bgColor: '#F5F5F5', - icon: , - label: 'Archivé', - chipColor: 'default' - } - } - - case 'outdated': { - return { - color: '#D32F2F', - bgColor: '#F9E8E8', - icon: , - label: 'Obsolète', - chipColor: 'error' - } - } - - default: { - return { - color: '#757575', - bgColor: '#F5F5F5', - icon: , - label: 'Archivé', - chipColor: 'default' - } - } - } -} - -function VersionCard({ +function VersionItem({ version, index, - totalVersions, accessToken, userId, countdownRef, @@ -122,13 +40,11 @@ function VersionCard({ setVersionCompare, onVoteResult }) { - const theme = useTheme() - const [versionStatus, setVersionStatus] = useState(null) const [isOutdated, setIsOutdated] = useState(false) + const [expanded, setExpanded] = useState(false) - // Fetch real status from API useEffect(() => { - async function fetchVersionStatus() { + async function fetchStatus() { try { const comparisonData = await compareVersion({ accessToken, @@ -140,41 +56,23 @@ function VersionCard({ }) if (comparisonData) { - // Store outdated flag for vote disabling setIsOutdated(comparisonData.outdated) - - // Determine status based on API response - let status - if (comparisonData.outdated) { - status = 'outdated' - } else if (index === totalVersions - 1) { - status = 'initial' - } else { - status = 'current' - } - setVersionStatus(status) } - } catch (error) { - // Fallback to position-based status on error - setVersionStatus(getVersionStatus(version, index, totalVersions, null)) + } catch { + setIsOutdated(false) } } - fetchVersionStatus() - }, [version.id, index, totalVersions, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen]) + fetchStatus() + }, [version.id, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen]) - const status = versionStatus || getVersionStatus(version, index, totalVersions, null) + const statusColor = getStatusColor(isOutdated, index) - const statusConfig = getStatusConfig(status) - const userDisplayName = version.user_created?.split('-')[0] || 'Système' - - // Check if voting is disabled (after 3 days OR if outdated) const createdAt = new Date(version.date_created) const threeDaysAgo = new Date(Date.now() - (3 * 24 * 60 * 60 * 1000)) - const isExpired = createdAt < threeDaysAgo - const isVoteDisabled = isExpired || isOutdated + const isVoteDisabled = createdAt < threeDaysAgo || isOutdated - const handleCompareClick = async () => { + const handleCompare = async () => { const comparisonData = await compareVersion({ accessToken, userId, @@ -190,162 +88,121 @@ function VersionCard({ } } - // Create content preview preserving markdown structure - const createContentPreview = content => { - if (!content) { - return 'Contenu non disponible' - } - - // If content is short enough, return as is - if (content.length <= 150) { - return content - } - - // Find a good breaking point (end of sentence, paragraph, or word) - const preview = content.slice(0, 150) - const lastSentence = Math.max(preview.lastIndexOf('.'), preview.lastIndexOf('!'), preview.lastIndexOf('?')) - const lastParagraph = preview.lastIndexOf('\n\n') - const lastWord = preview.lastIndexOf(' ') - - // Choose the best breaking point - let breakPoint = 150 - if (lastSentence > 100) { - breakPoint = lastSentence + 1 - } else if (lastParagraph > 80) { - breakPoint = lastParagraph - } else if (lastWord > 100) { - breakPoint = lastWord - } - - return content.slice(0, breakPoint) + (content.length > breakPoint ? '...' : '') - } - - const contentPreview = createContentPreview(version?.delta?.contenu) - return ( - - - + + + + + {/* Content */} + + {/* Header row */} + - - - {statusConfig.icon} - - - - {version.name} - - - par @{userDisplayName} - - + + + {version.name} + + + {formatDate(version.date_created, 'dd/MM/yy HH:mm')} + - - - {isExpired && !isOutdated && ( - - )} - - - div': {fontSize: '0.875rem', fontStyle: 'italic'}}}> - ( - - {children} - - )} - /> - - - - - {formatDate(version.date_created, 'PPpp')} - - - - - - - + {/* Actions */} + + + + + setExpanded(!expanded)}> + {expanded ? : } + - - - - - - + {/* Expanded content */} + + + {/* Preview */} + {version.delta?.contenu && ( + + {version.delta.contenu} + + )} + + {/* Actions row */} + + + + + + + ) } export default function VersionTimeline({ - collection, data, accessToken, userId, @@ -367,58 +224,24 @@ export default function VersionTimeline({ }) } - const handleCloseSnackbar = () => { - setSnackbar(prev => ({...prev, open: false})) - } - return ( <> - - - Historique des versions - {collection} - - - - {data.map((version, index) => ( - - - - {formatDate(version.date_created, 'dd/MM/yyyy')} - -
- - {formatDate(version.date_created, 'HH:mm')} - -
- - - - {index === 0 ? : } - - {index < data.length - 1 && } - - - - - -
- ))} -
+ + {data.map((version, index) => ( + + ))} {isOpenComparison && ( @@ -440,9 +263,14 @@ export default function VersionTimeline({ open={snackbar.open} autoHideDuration={6000} anchorOrigin={{vertical: 'bottom', horizontal: 'center'}} - onClose={handleCloseSnackbar} + onClose={() => setSnackbar(prev => ({...prev, open: false}))} > - + setSnackbar(prev => ({...prev, open: false}))} + > {snackbar.message} @@ -451,7 +279,6 @@ export default function VersionTimeline({ } VersionTimeline.propTypes = { - collection: PropTypes.oneOf(['titres', 'articles']).isRequired, data: PropTypes.array.isRequired, accessToken: PropTypes.string.isRequired, userId: PropTypes.string.isRequired, @@ -459,10 +286,9 @@ VersionTimeline.propTypes = { setIsErrorAlertOpen: PropTypes.func.isRequired } -VersionCard.propTypes = { +VersionItem.propTypes = { version: PropTypes.object.isRequired, index: PropTypes.number.isRequired, - totalVersions: PropTypes.number.isRequired, accessToken: PropTypes.string.isRequired, userId: PropTypes.string.isRequired, countdownRef: PropTypes.object.isRequired,