270 lines
9.0 KiB
JavaScript
270 lines
9.0 KiB
JavaScript
import Box from '@mui/material/Box'
|
|
import Typography from '@mui/material/Typography'
|
|
import Paper from '@mui/material/Paper'
|
|
import Grid from '@mui/material/Grid'
|
|
import Chip from '@mui/material/Chip'
|
|
import PropTypes from 'prop-types'
|
|
import Snackbar from '@mui/material/Snackbar'
|
|
import Alert from '@mui/material/Alert'
|
|
import ThumbUpIcon from '@mui/icons-material/ThumbUp'
|
|
import ThumbDownIcon from '@mui/icons-material/ThumbDown'
|
|
import {useState, useEffect} from 'react'
|
|
import {useSession} from 'next-auth/react'
|
|
import MarkdownRenderer from '../markdown-renderer/index.js'
|
|
import VoteButtons from './vote-buttons.js'
|
|
import CopyButton from './copy-button.js'
|
|
import {formatDate} from '@/lib/format.js'
|
|
import {getVoteCounts} from '@/lib/directus.js'
|
|
|
|
export default function VersionComparison({versionData, versionCompare, voteRefreshKey = 0, onVoteResult}) {
|
|
const {data: session} = useSession()
|
|
const {current, main, outdated} = versionCompare
|
|
const [snackbar, setSnackbar] = useState({open: false, message: '', severity: 'success'})
|
|
const [voteCounts, setVoteCounts] = useState({positive: 0, negative: 0, total: 0})
|
|
|
|
useEffect(() => {
|
|
async function fetchVoteCounts() {
|
|
if (session?.user?.accessToken && versionCompare?.versionId) {
|
|
const counts = await getVoteCounts({
|
|
accessToken: session.user.accessToken,
|
|
versionId: versionCompare.versionId
|
|
})
|
|
setVoteCounts(counts)
|
|
}
|
|
}
|
|
|
|
fetchVoteCounts()
|
|
}, [session?.user?.accessToken, versionCompare?.versionId, voteRefreshKey])
|
|
|
|
const handleVoteResult = async result => {
|
|
if (result.success && session?.user?.accessToken && versionCompare?.versionId) {
|
|
const counts = await getVoteCounts({
|
|
accessToken: session.user.accessToken,
|
|
versionId: versionCompare.versionId
|
|
})
|
|
|
|
setVoteCounts(counts)
|
|
}
|
|
|
|
if (onVoteResult) {
|
|
// Use the parent's vote result handler if provided
|
|
onVoteResult(result)
|
|
} else {
|
|
// Fallback to local snackbar if no parent handler
|
|
setSnackbar({
|
|
open: true,
|
|
message: result.message,
|
|
severity: result.success ? 'success' : 'error'
|
|
})
|
|
}
|
|
}
|
|
|
|
const handleCloseSnackbar = () => {
|
|
setSnackbar(prev => ({...prev, open: false}))
|
|
}
|
|
|
|
const createdAt = new Date(versionData.date_created)
|
|
const threeDaysAgo = new Date(Date.now() - (3 * 24 * 60 * 60 * 1000))
|
|
const isExpired = createdAt < threeDaysAgo
|
|
const isVoteDisabled = isExpired || outdated
|
|
|
|
return (
|
|
<Box sx={{padding: 3}}>
|
|
<Grid container spacing={2} sx={{width: '100%'}}>
|
|
<Grid xs={6} sx={{width: '100%'}}>
|
|
<Paper sx={{
|
|
padding: 2,
|
|
backgroundColor: '#E8F5E9',
|
|
width: '100%',
|
|
minHeight: '200px'
|
|
}}
|
|
>
|
|
<Box sx={{
|
|
color: '#2E7D32',
|
|
fontWeight: 'bold'
|
|
}}
|
|
>
|
|
<MarkdownRenderer
|
|
content={main.contenu}
|
|
color='#2E7D32'
|
|
fallbackComponent={({children, ...props}) => (
|
|
<Typography variant='body1' sx={{color: '#2E7D32', fontWeight: 'bold'}} {...props}>
|
|
{children}
|
|
</Typography>
|
|
)}
|
|
/>
|
|
</Box>
|
|
<Box sx={{
|
|
mt: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'center'
|
|
}}
|
|
>
|
|
<Typography sx={{textDecoration: 'underline'}} variant='caption' color='success'>
|
|
@{main.user_created?.split('-')[0] || 'Système'}
|
|
</Typography>
|
|
<CopyButton
|
|
content={main.contenu || ''}
|
|
label='Copier la version de référence'
|
|
hasSnackbarVisible={false}
|
|
/>
|
|
</Box>
|
|
</Paper>
|
|
</Grid>
|
|
<Grid xs={6} sx={{width: '100%'}}>
|
|
<Paper sx={{
|
|
padding: 2,
|
|
backgroundColor: outdated ? '#F9E8E8' : '#E3F2FD',
|
|
width: '100%',
|
|
minHeight: '200px'
|
|
}}
|
|
>
|
|
<Box sx={{
|
|
color: outdated ? '#D32F2F' : '#1976D2',
|
|
fontWeight: 'bold'
|
|
}}
|
|
>
|
|
<MarkdownRenderer
|
|
content={current.contenu}
|
|
color={outdated ? '#D32F2F' : '#1976D2'}
|
|
fallbackComponent={({children, ...props}) => (
|
|
<Typography variant='body1' sx={{color: outdated ? '#D32F2F' : '#1976D2', fontWeight: 'bold'}} {...props}>
|
|
{children}
|
|
</Typography>
|
|
)}
|
|
/>
|
|
</Box>
|
|
<Box sx={{mt: 1}}>
|
|
<Typography sx={{textDecoration: 'underline'}} variant='caption' color='primary'>
|
|
@{versionData.user_created?.split('-')[0] || 'Système'}
|
|
</Typography>
|
|
</Box>
|
|
<Box sx={{
|
|
display: 'flex', alignItems: 'center', justifyContent: 'space-between', mt: 1, flexWrap: 'wrap', gap: 1
|
|
}}
|
|
>
|
|
<Box sx={{display: 'flex', alignItems: 'center', gap: 1}}>
|
|
{versionData && (
|
|
<Typography sx={{fontWeight: 'bold'}} color={isVoteDisabled ? 'error' : 'primary'}>
|
|
{formatDate(versionData.date_created)}
|
|
</Typography>
|
|
)}
|
|
<CopyButton
|
|
content={current.contenu || ''}
|
|
label='Copier cette version'
|
|
hasSnackbarVisible={false}
|
|
/>
|
|
</Box>
|
|
<Box sx={{display: 'flex', alignItems: 'center', gap: 1}}>
|
|
<VoteButtons
|
|
key={`vote-comparison-${voteRefreshKey}`}
|
|
versionId={versionCompare.versionId}
|
|
isDisabled={isVoteDisabled}
|
|
onVoteResult={handleVoteResult}
|
|
/>
|
|
<Chip
|
|
icon={<ThumbUpIcon />}
|
|
label={voteCounts.positive}
|
|
size='small'
|
|
color='success'
|
|
variant='outlined'
|
|
/>
|
|
<Chip
|
|
icon={<ThumbDownIcon />}
|
|
label={voteCounts.negative}
|
|
size='small'
|
|
color='error'
|
|
variant='outlined'
|
|
/>
|
|
<Chip
|
|
label={`Total: ${voteCounts.total >= 0 ? '+' : ''}${voteCounts.total}`}
|
|
size='small'
|
|
color={voteCounts.total > 0 ? 'success' : (voteCounts.total < 0 ? 'error' : 'primary')}
|
|
variant='outlined'
|
|
/>
|
|
</Box>
|
|
</Box>
|
|
</Paper>
|
|
</Grid>
|
|
</Grid>
|
|
|
|
<Box sx={{marginTop: 4}}>
|
|
<Typography variant='button' sx={{
|
|
fontWeight: 'bold', marginBottom: 1, textAlign: 'center', display: 'block'
|
|
}}
|
|
>
|
|
LÉGENDE
|
|
</Typography>
|
|
<Grid container textAlign='center' spacing={1}>
|
|
<Grid size={{xs: 6, sm: 6}}>
|
|
<Typography
|
|
variant='body2'
|
|
sx={{
|
|
backgroundColor: '#E8F5E9',
|
|
padding: 1,
|
|
borderRadius: 1,
|
|
color: '#2E7D32',
|
|
fontWeight: 'bold'
|
|
}}
|
|
>
|
|
Version de référence
|
|
</Typography>
|
|
</Grid>
|
|
<Grid size={{xs: 6, sm: 6}}>
|
|
<Typography
|
|
variant='body2'
|
|
sx={{
|
|
backgroundColor: '#E3F2FD',
|
|
padding: 1,
|
|
borderRadius: 1,
|
|
color: '#1976D2',
|
|
fontWeight: 'bold'
|
|
}}
|
|
>
|
|
En cours de révision
|
|
</Typography>
|
|
</Grid>
|
|
<Grid size={{xs: 12, sm: 4}}>
|
|
<Typography
|
|
variant='body2'
|
|
sx={{
|
|
backgroundColor: '#F9E8E8',
|
|
padding: 1,
|
|
borderRadius: 1,
|
|
color: '#D32F2F',
|
|
fontWeight: 'bold'
|
|
}}
|
|
>
|
|
Remplacée / Publiée
|
|
</Typography>
|
|
</Grid>
|
|
</Grid>
|
|
</Box>
|
|
|
|
{!onVoteResult && (
|
|
<Snackbar
|
|
open={snackbar.open}
|
|
autoHideDuration={6000}
|
|
anchorOrigin={{vertical: 'bottom', horizontal: 'center'}}
|
|
onClose={handleCloseSnackbar}
|
|
>
|
|
<Alert variant='filled' severity={snackbar.severity} sx={{width: '100%'}} onClose={handleCloseSnackbar}>
|
|
{snackbar.message}
|
|
</Alert>
|
|
</Snackbar>
|
|
)}
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
VersionComparison.propTypes = {
|
|
versionData: PropTypes.object,
|
|
versionCompare: PropTypes.shape({
|
|
outdated: PropTypes.bool.isRequired,
|
|
mainHash: PropTypes.string.isRequired,
|
|
current: PropTypes.object.isRequired,
|
|
main: PropTypes.object.isRequired,
|
|
versionId: PropTypes.string
|
|
}).isRequired,
|
|
voteRefreshKey: PropTypes.number,
|
|
onVoteResult: PropTypes.func
|
|
}
|