import {useRef, useState} 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 Snackbar from '@mui/material/Snackbar' import Alert from '@mui/material/Alert' 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) { // 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, onVoteResult }) { const theme = useTheme() const status = getVersionStatus(version, index, totalVersions) const statusConfig = getStatusConfig(status) const userDisplayName = version.user_created?.split('-')[0] || 'Système' // Check if voting is disabled (after 3 days) const createdAt = new Date(version.date_created) const threeDaysAgo = new Date(Date.now() - (3 * 24 * 60 * 60 * 1000)) const isVoteDisabled = createdAt < threeDaysAgo const handleCompareClick = async () => { const comparisonData = await compareVersion({ accessToken, userId, versionId: version.id, countdownRef, setError, setIsErrorAlertOpen }) if (comparisonData) { setVersionCompare({...comparisonData, versionId: version.id}) setIsOpenComparison(true) } } // 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 ( {statusConfig.icon} {version.name} par @{userDisplayName} {isVoteDisabled && ( )} div': {fontSize: '0.875rem', fontStyle: 'italic'}}}> ( {children} )} /> {formatDate(version.date_created, 'PPpp')} ) } export default function VersionTimeline({ collection, data, accessToken, userId, setError, setIsErrorAlertOpen }) { const countdownRef = useRef() const [isOpenComparison, setIsOpenComparison] = useState(false) const [versionCompare, setVersionCompare] = useState(null) const [snackbar, setSnackbar] = useState({open: false, message: '', severity: 'success'}) const versionData = data.find(({id}) => id === versionCompare?.versionId) const handleVoteResult = result => { setSnackbar({ open: true, message: result.message, severity: result.success ? 'success' : 'error' }) } 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 && }
))}
{isOpenComparison && ( )} {snackbar.message} ) } 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, onVoteResult: PropTypes.func.isRequired }