feat: simplifie la vue timeline
This commit is contained in:
@@ -249,8 +249,10 @@ export default function ListVersions({
|
|||||||
<Box>
|
<Box>
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
flexDirection: {xs: 'column', sm: 'row'},
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: {xs: 'stretch', sm: 'center'},
|
||||||
|
gap: 1,
|
||||||
mb: 2
|
mb: 2
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -271,13 +273,14 @@ export default function ListVersions({
|
|||||||
size='small'
|
size='small'
|
||||||
value={viewMode}
|
value={viewMode}
|
||||||
onChange={handleViewModeChange}
|
onChange={handleViewModeChange}
|
||||||
|
sx={{alignSelf: {xs: 'center', sm: 'auto'}}}
|
||||||
>
|
>
|
||||||
<ToggleButton value='table' aria-label='vue tableau'>
|
<ToggleButton value='table' aria-label='vue tableau'>
|
||||||
<ViewListIcon sx={{mr: 1}} />
|
<ViewListIcon fontSize='small' sx={{mr: 0.5}} />
|
||||||
Table
|
Table
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<ToggleButton value='timeline' aria-label='vue chronologique'>
|
<ToggleButton value='timeline' aria-label='vue chronologique'>
|
||||||
<TimelineIcon sx={{mr: 1}} />
|
<TimelineIcon fontSize='small' sx={{mr: 0.5}} />
|
||||||
Timeline
|
Timeline
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</ToggleButtonGroup>
|
</ToggleButtonGroup>
|
||||||
@@ -308,7 +311,6 @@ export default function ListVersions({
|
|||||||
</Paper>
|
</Paper>
|
||||||
) : (
|
) : (
|
||||||
<VersionTimeline
|
<VersionTimeline
|
||||||
collection={collection}
|
|
||||||
data={filteredData}
|
data={filteredData}
|
||||||
accessToken={accessToken}
|
accessToken={accessToken}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ export default function VersionComparison({versionData, versionCompare, voteRefr
|
|||||||
|
|
||||||
const createdAt = new Date(versionData.date_created)
|
const createdAt = new Date(versionData.date_created)
|
||||||
const threeDaysAgo = new Date(Date.now() - (3 * 24 * 60 * 60 * 1000))
|
const threeDaysAgo = new Date(Date.now() - (3 * 24 * 60 * 60 * 1000))
|
||||||
const isVoteDisabled = createdAt < threeDaysAgo
|
const isExpired = createdAt < threeDaysAgo
|
||||||
|
const isVoteDisabled = isExpired || outdated
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{padding: 3}}>
|
<Box sx={{padding: 3}}>
|
||||||
@@ -106,7 +107,6 @@ export default function VersionComparison({versionData, versionCompare, voteRefr
|
|||||||
@{versionData.user_created?.split('-')[0] || 'Système'}
|
@{versionData.user_created?.split('-')[0] || 'Système'}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
{!outdated && (
|
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between', mt: 1
|
display: 'flex', alignItems: 'center', justifyContent: 'space-between', mt: 1
|
||||||
}}
|
}}
|
||||||
@@ -130,7 +130,6 @@ export default function VersionComparison({versionData, versionCompare, voteRefr
|
|||||||
onVoteResult={handleVoteResult}
|
onVoteResult={handleVoteResult}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -1,118 +1,36 @@
|
|||||||
import {useRef, useState, useEffect} from 'react'
|
import {useRef, useState, useEffect} from 'react'
|
||||||
import {useTheme} from '@mui/material/styles'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import Box from '@mui/material/Box'
|
import Box from '@mui/material/Box'
|
||||||
import Typography from '@mui/material/Typography'
|
import Typography from '@mui/material/Typography'
|
||||||
import Card from '@mui/material/Card'
|
import IconButton from '@mui/material/IconButton'
|
||||||
import CardContent from '@mui/material/CardContent'
|
import Collapse from '@mui/material/Collapse'
|
||||||
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 Snackbar from '@mui/material/Snackbar'
|
||||||
import Alert from '@mui/material/Alert'
|
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 SessionExpired from '../session/session-expired.js'
|
||||||
import MarkdownRenderer from '../markdown-renderer/index.js'
|
|
||||||
import VersionDialog from './version-dialog.js'
|
import VersionDialog from './version-dialog.js'
|
||||||
import VoteButtons from './vote-buttons.js'
|
import VoteButtons from './vote-buttons.js'
|
||||||
import CopyButton from './copy-button.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 {formatDate} from '@/lib/format.js'
|
||||||
import {compareVersion} from '@/lib/directus.js'
|
import {compareVersion} from '@/lib/directus.js'
|
||||||
|
|
||||||
function getVersionStatus(version, index, totalVersions, data) {
|
function getStatusColor(isOutdated, index) {
|
||||||
// Logic to determine version status based on position and data
|
if (isOutdated) {
|
||||||
// Find which version is the "main" (published) by checking if it matches current content
|
return '#D32F2F'
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's a more recent version after this one, this is outdated
|
if (index === 0) {
|
||||||
// unless this IS the main version (would need item content to determine)
|
return '#1976D2'
|
||||||
if (index > 0) {
|
|
||||||
return 'outdated' // Older versions are outdated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Most recent version is current (being edited/proposed)
|
return '#9E9E9E'
|
||||||
return 'current'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatusConfig(status) {
|
function VersionItem({
|
||||||
switch (status) {
|
|
||||||
case 'current': {
|
|
||||||
return {
|
|
||||||
color: '#1976D2',
|
|
||||||
bgColor: '#E3F2FD',
|
|
||||||
icon: <EditIcon />,
|
|
||||||
label: 'En cours',
|
|
||||||
chipColor: 'primary'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'published': {
|
|
||||||
return {
|
|
||||||
color: '#2E7D32',
|
|
||||||
bgColor: '#E8F5E9',
|
|
||||||
icon: <CheckCircleIcon />,
|
|
||||||
label: 'Publié',
|
|
||||||
chipColor: 'success'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'archived': {
|
|
||||||
return {
|
|
||||||
color: '#757575',
|
|
||||||
bgColor: '#F5F5F5',
|
|
||||||
icon: <AccessTimeIcon />,
|
|
||||||
label: 'Archivé',
|
|
||||||
chipColor: 'default'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'outdated': {
|
|
||||||
return {
|
|
||||||
color: '#D32F2F',
|
|
||||||
bgColor: '#F9E8E8',
|
|
||||||
icon: <ErrorIcon />,
|
|
||||||
label: 'Obsolète',
|
|
||||||
chipColor: 'error'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
return {
|
|
||||||
color: '#757575',
|
|
||||||
bgColor: '#F5F5F5',
|
|
||||||
icon: <AccessTimeIcon />,
|
|
||||||
label: 'Archivé',
|
|
||||||
chipColor: 'default'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function VersionCard({
|
|
||||||
version,
|
version,
|
||||||
index,
|
index,
|
||||||
totalVersions,
|
|
||||||
accessToken,
|
accessToken,
|
||||||
userId,
|
userId,
|
||||||
countdownRef,
|
countdownRef,
|
||||||
@@ -122,13 +40,11 @@ function VersionCard({
|
|||||||
setVersionCompare,
|
setVersionCompare,
|
||||||
onVoteResult
|
onVoteResult
|
||||||
}) {
|
}) {
|
||||||
const theme = useTheme()
|
|
||||||
const [versionStatus, setVersionStatus] = useState(null)
|
|
||||||
const [isOutdated, setIsOutdated] = useState(false)
|
const [isOutdated, setIsOutdated] = useState(false)
|
||||||
|
const [expanded, setExpanded] = useState(false)
|
||||||
|
|
||||||
// Fetch real status from API
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchVersionStatus() {
|
async function fetchStatus() {
|
||||||
try {
|
try {
|
||||||
const comparisonData = await compareVersion({
|
const comparisonData = await compareVersion({
|
||||||
accessToken,
|
accessToken,
|
||||||
@@ -140,41 +56,23 @@ function VersionCard({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (comparisonData) {
|
if (comparisonData) {
|
||||||
// Store outdated flag for vote disabling
|
|
||||||
setIsOutdated(comparisonData.outdated)
|
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 {
|
||||||
}
|
setIsOutdated(false)
|
||||||
} catch (error) {
|
|
||||||
// Fallback to position-based status on error
|
|
||||||
setVersionStatus(getVersionStatus(version, index, totalVersions, null))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchVersionStatus()
|
fetchStatus()
|
||||||
}, [version.id, index, totalVersions, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen])
|
}, [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 createdAt = new Date(version.date_created)
|
||||||
const threeDaysAgo = new Date(Date.now() - (3 * 24 * 60 * 60 * 1000))
|
const threeDaysAgo = new Date(Date.now() - (3 * 24 * 60 * 60 * 1000))
|
||||||
const isExpired = createdAt < threeDaysAgo
|
const isVoteDisabled = createdAt < threeDaysAgo || isOutdated
|
||||||
const isVoteDisabled = isExpired || isOutdated
|
|
||||||
|
|
||||||
const handleCompareClick = async () => {
|
const handleCompare = async () => {
|
||||||
const comparisonData = await compareVersion({
|
const comparisonData = await compareVersion({
|
||||||
accessToken,
|
accessToken,
|
||||||
userId,
|
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 (
|
return (
|
||||||
<Card
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
borderLeft: `4px solid ${statusConfig.color}`,
|
|
||||||
backgroundColor: statusConfig.bgColor,
|
|
||||||
mb: 2,
|
|
||||||
transition: 'all 0.2s ease-in-out',
|
|
||||||
'&:hover': {
|
|
||||||
transform: 'translateY(-2px)',
|
|
||||||
boxShadow: 3
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CardContent>
|
|
||||||
<Box sx={{
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
gap: 1.5,
|
||||||
alignItems: 'flex-start',
|
py: 1.5,
|
||||||
mb: 2
|
borderBottom: '1px solid',
|
||||||
|
borderColor: 'divider',
|
||||||
|
'&:last-child': {borderBottom: 'none'}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Status indicator */}
|
||||||
|
<Box sx={{display: 'flex', flexDirection: 'column', alignItems: 'center', pt: 0.5}}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
borderRadius: '50%',
|
||||||
|
bgcolor: statusColor,
|
||||||
|
flexShrink: 0
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: 2,
|
||||||
|
flex: 1,
|
||||||
|
bgcolor: 'divider',
|
||||||
|
mt: 0.5,
|
||||||
|
display: index === 0 ? 'none' : 'block'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<Box sx={{flex: 1, minWidth: 0}}>
|
||||||
|
{/* Header row */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
gap: 1
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{minWidth: 0, flex: 1}}>
|
||||||
|
<Typography
|
||||||
|
variant='body2'
|
||||||
|
sx={{
|
||||||
|
fontWeight: 600,
|
||||||
|
color: statusColor,
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{display: 'flex', alignItems: 'center', gap: 1}}>
|
|
||||||
<Avatar sx={{bgcolor: statusConfig.color, width: 32, height: 32}}>
|
|
||||||
{statusConfig.icon}
|
|
||||||
</Avatar>
|
|
||||||
<Box>
|
|
||||||
<Typography variant='h6' sx={{fontWeight: 'bold', color: statusConfig.color}}>
|
|
||||||
{version.name}
|
{version.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant='caption' color='text.secondary'>
|
<Typography variant='caption' color='text.secondary'>
|
||||||
par @{userDisplayName}
|
{formatDate(version.date_created, 'dd/MM/yy HH:mm')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
|
||||||
<Box sx={{display: 'flex', gap: 1}}>
|
|
||||||
<Chip
|
|
||||||
label={statusConfig.label}
|
|
||||||
color={statusConfig.chipColor}
|
|
||||||
size='small'
|
|
||||||
variant='outlined'
|
|
||||||
/>
|
|
||||||
{isExpired && !isOutdated && (
|
|
||||||
<Chip
|
|
||||||
label='Vote fermé'
|
|
||||||
color='error'
|
|
||||||
size='small'
|
|
||||||
variant='outlined'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box sx={{mb: 2, '& > div': {fontSize: '0.875rem', fontStyle: 'italic'}}}>
|
{/* Actions */}
|
||||||
<MarkdownRenderer
|
<Box sx={{display: 'flex', alignItems: 'center', gap: 0.5, flexShrink: 0}}>
|
||||||
content={contentPreview}
|
|
||||||
color={theme.palette.text.secondary}
|
|
||||||
fallbackComponent={({children, ...props}) => (
|
|
||||||
<Typography
|
|
||||||
variant='body2'
|
|
||||||
color='text.secondary'
|
|
||||||
sx={{fontStyle: 'italic'}}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
|
|
||||||
<Typography variant='caption' color='text.secondary'>
|
|
||||||
{formatDate(version.date_created, 'PPpp')}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Box sx={{display: 'flex', alignItems: 'center', gap: 1}}>
|
|
||||||
<CopyButton
|
|
||||||
content={version.delta?.contenu || version.name || ''}
|
|
||||||
label='Copier le contenu de cette version'
|
|
||||||
hasSnackbarVisible={false}
|
|
||||||
/>
|
|
||||||
<ShareButton
|
|
||||||
versionId={version.id}
|
|
||||||
versionName={version.name}
|
|
||||||
hasSnackbarVisible={false}
|
|
||||||
/>
|
|
||||||
<ExportPdfButton
|
|
||||||
versionData={version}
|
|
||||||
isOutdated={isOutdated}
|
|
||||||
size='small'
|
|
||||||
variant='text'
|
|
||||||
/>
|
|
||||||
<PrintButton
|
|
||||||
versionData={version}
|
|
||||||
isOutdated={isOutdated}
|
|
||||||
size='small'
|
|
||||||
variant='text'
|
|
||||||
/>
|
|
||||||
<VoteButtons
|
<VoteButtons
|
||||||
hasCountsVisible
|
hasCountsVisible
|
||||||
versionId={version.id}
|
versionId={version.id}
|
||||||
isDisabled={isVoteDisabled}
|
isDisabled={isVoteDisabled}
|
||||||
onVoteResult={onVoteResult}
|
onVoteResult={onVoteResult}
|
||||||
/>
|
/>
|
||||||
|
<IconButton size='small' onClick={handleCompare} title='Comparer'>
|
||||||
|
<CompareArrowsIcon fontSize='small' />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton size='small' onClick={() => setExpanded(!expanded)}>
|
||||||
|
{expanded ? <ExpandLessIcon fontSize='small' /> : <ExpandMoreIcon fontSize='small' />}
|
||||||
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</CardContent>
|
|
||||||
|
|
||||||
<Divider />
|
{/* Expanded content */}
|
||||||
<CardActions sx={{justifyContent: 'flex-end'}}>
|
<Collapse in={expanded}>
|
||||||
<Button
|
<Box sx={{mt: 1.5, display: 'flex', flexDirection: 'column', gap: 1}}>
|
||||||
size='small'
|
{/* Preview */}
|
||||||
variant='outlined'
|
{version.delta?.contenu && (
|
||||||
color='primary'
|
<Typography
|
||||||
onClick={handleCompareClick}
|
variant='caption'
|
||||||
|
color='text.secondary'
|
||||||
|
sx={{
|
||||||
|
display: '-webkit-box',
|
||||||
|
WebkitLineClamp: 3,
|
||||||
|
WebkitBoxOrient: 'vertical',
|
||||||
|
overflow: 'hidden',
|
||||||
|
fontStyle: 'italic'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Comparer
|
{version.delta.contenu}
|
||||||
</Button>
|
</Typography>
|
||||||
</CardActions>
|
)}
|
||||||
</Card>
|
|
||||||
|
{/* Actions row */}
|
||||||
|
<Box sx={{display: 'flex', alignItems: 'center', gap: 1, flexWrap: 'wrap'}}>
|
||||||
|
<CopyButton
|
||||||
|
content={version.delta?.contenu || version.name || ''}
|
||||||
|
label='Copier'
|
||||||
|
hasSnackbarVisible={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Collapse>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function VersionTimeline({
|
export default function VersionTimeline({
|
||||||
collection,
|
|
||||||
data,
|
data,
|
||||||
accessToken,
|
accessToken,
|
||||||
userId,
|
userId,
|
||||||
@@ -367,45 +224,14 @@ export default function VersionTimeline({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCloseSnackbar = () => {
|
|
||||||
setSnackbar(prev => ({...prev, open: false}))
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box>
|
<Box sx={{maxWidth: 500, mx: 'auto'}}>
|
||||||
<Typography variant='h5' textAlign='center' sx={{mb: 3, fontWeight: 'bold'}}>
|
|
||||||
Historique des versions - {collection}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Timeline position='right'>
|
|
||||||
{data.map((version, index) => (
|
{data.map((version, index) => (
|
||||||
<TimelineItem key={version.id}>
|
<VersionItem
|
||||||
<TimelineOppositeContent sx={{flex: 0.3, pr: 2}}>
|
key={version.id}
|
||||||
<Typography variant='caption' color='text.secondary'>
|
|
||||||
{formatDate(version.date_created, 'dd/MM/yyyy')}
|
|
||||||
</Typography>
|
|
||||||
<br />
|
|
||||||
<Typography variant='caption' color='text.secondary'>
|
|
||||||
{formatDate(version.date_created, 'HH:mm')}
|
|
||||||
</Typography>
|
|
||||||
</TimelineOppositeContent>
|
|
||||||
|
|
||||||
<TimelineSeparator>
|
|
||||||
<TimelineDot
|
|
||||||
color={index === 0 ? 'primary' : 'grey'}
|
|
||||||
variant={index === 0 ? 'filled' : 'outlined'}
|
|
||||||
>
|
|
||||||
{index === 0 ? <EditIcon /> : <AccessTimeIcon />}
|
|
||||||
</TimelineDot>
|
|
||||||
{index < data.length - 1 && <TimelineConnector />}
|
|
||||||
</TimelineSeparator>
|
|
||||||
|
|
||||||
<TimelineContent sx={{flex: 1}}>
|
|
||||||
<VersionCard
|
|
||||||
version={version}
|
version={version}
|
||||||
index={index}
|
index={index}
|
||||||
totalVersions={data.length}
|
|
||||||
accessToken={accessToken}
|
accessToken={accessToken}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
countdownRef={countdownRef}
|
countdownRef={countdownRef}
|
||||||
@@ -415,10 +241,7 @@ export default function VersionTimeline({
|
|||||||
setVersionCompare={setVersionCompare}
|
setVersionCompare={setVersionCompare}
|
||||||
onVoteResult={handleVoteResult}
|
onVoteResult={handleVoteResult}
|
||||||
/>
|
/>
|
||||||
</TimelineContent>
|
|
||||||
</TimelineItem>
|
|
||||||
))}
|
))}
|
||||||
</Timeline>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{isOpenComparison && (
|
{isOpenComparison && (
|
||||||
@@ -440,9 +263,14 @@ export default function VersionTimeline({
|
|||||||
open={snackbar.open}
|
open={snackbar.open}
|
||||||
autoHideDuration={6000}
|
autoHideDuration={6000}
|
||||||
anchorOrigin={{vertical: 'bottom', horizontal: 'center'}}
|
anchorOrigin={{vertical: 'bottom', horizontal: 'center'}}
|
||||||
onClose={handleCloseSnackbar}
|
onClose={() => setSnackbar(prev => ({...prev, open: false}))}
|
||||||
|
>
|
||||||
|
<Alert
|
||||||
|
variant='filled'
|
||||||
|
severity={snackbar.severity}
|
||||||
|
sx={{width: '100%'}}
|
||||||
|
onClose={() => setSnackbar(prev => ({...prev, open: false}))}
|
||||||
>
|
>
|
||||||
<Alert variant='filled' severity={snackbar.severity} sx={{width: '100%'}} onClose={handleCloseSnackbar}>
|
|
||||||
{snackbar.message}
|
{snackbar.message}
|
||||||
</Alert>
|
</Alert>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
@@ -451,7 +279,6 @@ export default function VersionTimeline({
|
|||||||
}
|
}
|
||||||
|
|
||||||
VersionTimeline.propTypes = {
|
VersionTimeline.propTypes = {
|
||||||
collection: PropTypes.oneOf(['titres', 'articles']).isRequired,
|
|
||||||
data: PropTypes.array.isRequired,
|
data: PropTypes.array.isRequired,
|
||||||
accessToken: PropTypes.string.isRequired,
|
accessToken: PropTypes.string.isRequired,
|
||||||
userId: PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
@@ -459,10 +286,9 @@ VersionTimeline.propTypes = {
|
|||||||
setIsErrorAlertOpen: PropTypes.func.isRequired
|
setIsErrorAlertOpen: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
VersionCard.propTypes = {
|
VersionItem.propTypes = {
|
||||||
version: PropTypes.object.isRequired,
|
version: PropTypes.object.isRequired,
|
||||||
index: PropTypes.number.isRequired,
|
index: PropTypes.number.isRequired,
|
||||||
totalVersions: PropTypes.number.isRequired,
|
|
||||||
accessToken: PropTypes.string.isRequired,
|
accessToken: PropTypes.string.isRequired,
|
||||||
userId: PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
countdownRef: PropTypes.object.isRequired,
|
countdownRef: PropTypes.object.isRequired,
|
||||||
|
|||||||
Reference in New Issue
Block a user