From 824408afa93c9e35edc1ce24325fb72106de1dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20FAMIBELLE-PRONZOLA?= Date: Wed, 23 Jul 2025 17:40:45 +0400 Subject: [PATCH] feat: ajoute composant VersionTimeline --- components/versions/version-timeline.js | 317 ++++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 components/versions/version-timeline.js diff --git a/components/versions/version-timeline.js b/components/versions/version-timeline.js new file mode 100644 index 0000000..7d5bac7 --- /dev/null +++ b/components/versions/version-timeline.js @@ -0,0 +1,317 @@ +import {useRef, useState} from 'react' +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 SessionExpired from '../session/session-expired.js' +import VersionDialog from './version-dialog.js' +import {formatDate} from '@/lib/format.js' +import {compareVersion} from '@/lib/directus.js' + +function getVersionStatus(version, index, totalVersions) { + // Logic to determine version status based on position and data + if (index === 0) { + return 'current' // Most recent + } + + if (index === totalVersions - 1) { + return 'initial' // First version + } + + return 'archived' // Intermediate versions +} + +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({ + version, + index, + totalVersions, + accessToken, + userId, + countdownRef, + setError, + setIsErrorAlertOpen, + setIsOpenComparison, + setVersionCompare +}) { + const status = getVersionStatus(version, index, totalVersions) + const statusConfig = getStatusConfig(status) + const userDisplayName = version.user_created?.split('-')[0] || 'Système' + + const handleCompareClick = async () => { + const comparisonData = await compareVersion({ + accessToken, + userId, + versionId: version.id, + countdownRef, + setError, + setIsErrorAlertOpen + }) + + if (comparisonData) { + setVersionCompare({...comparisonData, versionId: version.id}) + setIsOpenComparison(true) + } + } + + // Estimate content preview (first 100 chars) + const contentPreview = version?.delta?.contenu + ? version.delta.contenu.slice(0, 100) + (version.delta.contenu.length > 100 ? '...' : '') + : 'Contenu non disponible' + + return ( + + + + + + {statusConfig.icon} + + + + {version.name} + + + par @{userDisplayName} + + + + + + + + {contentPreview} + + + + + {formatDate(version.date_created, 'PPpp')} + + + {/* Placeholder for vote count - would need API enhancement */} + + + + + + + + + + + + + ) +} + +export default function VersionTimeline({ + collection, + data, + accessToken, + userId, + setError, + setIsErrorAlertOpen +}) { + const countdownRef = useRef() + const [isOpenComparison, setIsOpenComparison] = useState(false) + const [versionCompare, setVersionCompare] = useState(null) + + const versionData = data.find(({id}) => id === versionCompare?.versionId) + + 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 && } + + + + + +
+ ))} +
+
+ + {isOpenComparison && ( + + )} + + + + ) +} + +VersionTimeline.propTypes = { + collection: PropTypes.oneOf(['titres', 'articles']).isRequired, + data: PropTypes.array.isRequired, + accessToken: PropTypes.string.isRequired, + userId: PropTypes.string.isRequired, + setError: PropTypes.func.isRequired, + setIsErrorAlertOpen: PropTypes.func.isRequired +} + +VersionCard.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, + setError: PropTypes.func.isRequired, + setIsErrorAlertOpen: PropTypes.func.isRequired, + setIsOpenComparison: PropTypes.func.isRequired, + setVersionCompare: PropTypes.func.isRequired +}