Files
konstitisyon.nu/components/versions/version-comparison.js
T

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
}