import {forwardRef, useRef, useState, useEffect} from 'react' import PropTypes from 'prop-types' import Table from '@mui/material/Table' import TableBody from '@mui/material/TableBody' import TableCell from '@mui/material/TableCell' import TableContainer from '@mui/material/TableContainer' import TableHead from '@mui/material/TableHead' import TableRow from '@mui/material/TableRow' import Paper from '@mui/material/Paper' import Button from '@mui/material/Button' import ToggleButton from '@mui/material/ToggleButton' import ToggleButtonGroup from '@mui/material/ToggleButtonGroup' import ViewListIcon from '@mui/icons-material/ViewList' import TimelineIcon from '@mui/icons-material/Timeline' import {TableVirtuoso} from 'react-virtuoso' import {Box, Typography} from '@mui/material' import SessionExpired from '../session/session-expired.js' import VersionDialog from './version-dialog.js' import VersionTimeline from './version-timeline.js' import VersionSearch from './version-search.js' import VersionFilters from './version-filters.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, getVoteCounts} from '@/lib/directus.js' import {filterVersions, getFilterStats} from '@/lib/version-utils.js' const columns = [ { width: 180, label: 'Version', dataKey: 'name', }, { width: 140, label: 'Créée le', dataKey: 'date_created', numeric: true, }, { width: 200, label: 'Actions', dataKey: 'actions', } ] const VirtuosoTableComponents = { Scroller: forwardRef((props, ref) => ( )), Table: props => ( ), TableHead: forwardRef((props, ref) => ), TableRow, TableBody: forwardRef((props, ref) => ), } function fixedHeaderContent() { return ( {columns.map(column => ( {column.label} ))} ) } function rowContent({ row, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen, setIsOpenComparison, setVersionCompare, outdatedStatusMap, voteCountsMap }) { const handleButtonClick = async versionId => { const version = await compareVersion({ accessToken, userId, versionId, countdownRef, setError, setIsErrorAlertOpen }) if (version) { setVersionCompare({...version, versionId}) setIsOpenComparison(true) } } const isOutdated = outdatedStatusMap[row.id] || false const voteCounts = voteCountsMap[row.id] || null return ( <> {columns.map(column => ( {column.dataKey === 'date_created' ? formatDate(row[column.dataKey], 'Pp') : (column.dataKey === 'actions' ? ( ) : )} ))} ) } export default function ListVersions({ collection, data, accessToken, userId, setError, setIsErrorAlertOpen }) { const countdownRef = useRef() const [viewMode, setViewMode] = useState('table') const [isOpenComparison, setIsOpenComparison] = useState(false) const [versionCompare, setVersionCompare] = useState(null) const [searchTerm, setSearchTerm] = useState('') const [filters, setFilters] = useState({ author: '', dateFrom: '', dateTo: '', status: '' }) const [outdatedStatusMap, setOutdatedStatusMap] = useState({}) const [voteCountsMap, setVoteCountsMap] = useState({}) // Fetch outdated status and vote counts for all versions useEffect(() => { async function fetchVersionsData() { const statusMap = {} const countsMap = {} await Promise.all( data.map(async version => { try { const comparisonData = await compareVersion({ accessToken, userId, versionId: version.id, countdownRef, setError, setIsErrorAlertOpen }) if (comparisonData) { statusMap[version.id] = comparisonData.outdated || false } // Fetch vote counts const counts = await getVoteCounts({ accessToken, versionId: version.id }) countsMap[version.id] = counts } catch (error) { console.warn(`Failed to fetch data for version ${version.id}:`, error) statusMap[version.id] = false countsMap[version.id] = {positive: 0, negative: 0, total: 0} } }) ) setOutdatedStatusMap(statusMap) setVoteCountsMap(countsMap) } if (data.length > 0) { fetchVersionsData() } }, [data, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen]) // Filter data based on search and filters const filteredData = filterVersions(data, searchTerm, filters) const stats = getFilterStats(data, filteredData) const versionData = data.find(({id}) => id === versionCompare?.versionId) // Function to refresh vote counts for a specific version after voting const refreshVoteCounts = async versionId => { try { const counts = await getVoteCounts({ accessToken, versionId }) setVoteCountsMap(prev => ({...prev, [versionId]: counts})) } catch (error) { console.warn(`Failed to refresh vote counts for version ${versionId}:`, error) } } const handleSearchChange = newSearchTerm => { setSearchTerm(newSearchTerm) } const handleFiltersChange = newFilters => { setFilters(newFilters) } const handleViewModeChange = (event, newViewMode) => { if (newViewMode !== null) { setViewMode(newViewMode) } } return ( <> {collection} {stats.hidden > 0 && ( ({stats.filtered}/{stats.total} versions) )} Table Timeline {filteredData.length === 0 ? ( {searchTerm || Object.values(filters).some(Boolean) ? 'Aucune version ne correspond aux critères de recherche' : 'Aucune version disponible'} ) : ( viewMode === 'table' ? ( rowContent({ index, row, accessToken, userId, countdownRef, setError, setIsErrorAlertOpen, setIsOpenComparison, setVersionCompare, outdatedStatusMap, voteCountsMap })} /> ) : ( ) )} {isOpenComparison && ( )} ) } ListVersions.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 }