import {useRef, useState, useEffect} from 'react' import PropTypes from 'prop-types' import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' 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 VersionDialog from './version-dialog.js' import VoteButtons from './vote-buttons.js' import CopyButton from './copy-button.js' import {formatDate} from '@/lib/format.js' import {compareVersion} from '@/lib/directus.js' function getStatusColor(isOutdated, index) { if (isOutdated) { return '#D32F2F' } if (index === 0) { return '#1976D2' } return '#9E9E9E' } function VersionItem({ version, index, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen, setIsOpenComparison, setVersionCompare, onVoteResult }) { const [isOutdated, setIsOutdated] = useState(false) const [expanded, setExpanded] = useState(false) useEffect(() => { async function fetchStatus() { try { const comparisonData = await compareVersion({ accessToken, userId, versionId: version.id, countdownRef, setError, setIsErrorAlertOpen }) if (comparisonData) { setIsOutdated(comparisonData.outdated) } } catch { setIsOutdated(false) } } fetchStatus() }, [version.id, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen]) const statusColor = getStatusColor(isOutdated, index) const createdAt = new Date(version.date_created) const threeDaysAgo = new Date(Date.now() - (3 * 24 * 60 * 60 * 1000)) const isVoteDisabled = createdAt < threeDaysAgo || isOutdated const handleCompare = async () => { const comparisonData = await compareVersion({ accessToken, userId, versionId: version.id, countdownRef, setError, setIsErrorAlertOpen }) if (comparisonData) { setVersionCompare({...comparisonData, versionId: version.id}) setIsOpenComparison(true) } } return ( {/* Status indicator */} {/* Content */} {/* Header row */} {version.name} {formatDate(version.date_created, 'dd/MM/yy HH:mm')} {/* Actions */} setExpanded(!expanded)}> {expanded ? : } {/* Expanded content */} {/* Preview */} {version.delta?.contenu && ( {version.delta.contenu} )} {/* Actions row */} ) } export default function VersionTimeline({ data, accessToken, userId, setError, setIsErrorAlertOpen, onVoteSuccess }) { 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, versionId) => { setSnackbar({ open: true, message: result.message, severity: result.success ? 'success' : 'error' }) if (result.success && onVoteSuccess && versionId) { onVoteSuccess(versionId) } } return ( <> {data.map((version, index) => ( handleVoteResult(result, version.id)} /> ))} {isOpenComparison && ( )} setSnackbar(prev => ({...prev, open: false}))} > setSnackbar(prev => ({...prev, open: false}))} > {snackbar.message} ) } VersionTimeline.propTypes = { data: PropTypes.array.isRequired, accessToken: PropTypes.string.isRequired, userId: PropTypes.string.isRequired, setError: PropTypes.func.isRequired, setIsErrorAlertOpen: PropTypes.func.isRequired, onVoteSuccess: PropTypes.func } VersionItem.propTypes = { version: PropTypes.object.isRequired, index: 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 }