const VersionHistoryPanel = ({ article, historyVersions, versions: versionsProp, onRestore, onClose, versioningMode, isOpen }) => {
const [selectedVersion, setSelectedVersion] = React.useState(null);
const [showRestoreConfirm, setShowRestoreConfirm] = React.useState(false);
const versions = Array.isArray(versionsProp) ? versionsProp : (Array.isArray(historyVersions) ? historyVersions : []);
const isHistoryMode = versioningMode === 'history';
const isParallelMode = versioningMode === 'parallel';
React.useEffect(() => {
if (versions.length > 0 && !selectedVersion) {
setSelectedVersion(versions[0]);
}
}, [versions.length]);
const formatFileSize = (bytes) => {
if (!bytes) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return (bytes / Math.pow(k, i)).toFixed(1) + ' ' + sizes[i];
};
const getAttachments = (version) => {
if (version.attachments_snapshot) {
try {
return typeof version.attachments_snapshot === 'string'
? JSON.parse(version.attachments_snapshot)
: version.attachments_snapshot;
} catch (e) {
return version.attachments || [];
}
}
return version.attachments || [];
};
const handleRestore = async () => {
if (!selectedVersion || selectedVersion.is_current) return;
try {
await onRestore(selectedVersion.id);
setShowRestoreConfirm(false);
onClose();
} catch (error) {
console.error('Restore error:', error);
window.otoNotify?.toast('Failed to restore version', 'error');
}
};
if (!isOpen) return null;
return (
{/* Header with gradient */}
{isParallelMode ? 'Parallel Versions' : 'Version History'}
{isParallelMode ? 'All versions work together' : 'Track changes and restore previous versions'}
{/* Content Area */}
{/* Left Sidebar - Version List */}
{isParallelMode ? 'All Versions' : 'Timeline'}
{versions.length} version{versions.length !== 1 ? 's' : ''}
{versions.length === 0 ? (
) : (
versions.map((version, index) => {
const isSelected = selectedVersion?.id === version.id || selectedVersion?.version === version.version;
const isCurrent = version.is_current || index === 0;
return (
setSelectedVersion(version)}
className={`px-6 py-4 border-b border-gray-100 cursor-pointer transition-colors ${isSelected
? 'bg-primary/5 border-l-4 border-l-primary'
: 'hover:bg-white border-l-4 border-l-transparent'
}`}
>
Version {version.version}
{version.restored_from_version != null && (
Restored from v{version.restored_from_version}
)}
{isCurrent && (
CURRENT
)}
{version.changed_by_name || version.changed_by}
{new Date(version.changed_at || version.modified_at).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit'
})}
);
})
)}
{/* Right Panel - Version Details */}
{selectedVersion ? (
<>
Version {selectedVersion.version}
{selectedVersion.restored_from_version != null && (
Restored from v{selectedVersion.restored_from_version}
)}
{selectedVersion.is_current && (
CURRENT
)}
{selectedVersion.changed_by_name || selectedVersion.changed_by} • {' '}
{new Date(selectedVersion.changed_at || selectedVersion.modified_at).toLocaleString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit'
})}
{isHistoryMode && !selectedVersion.is_current && (
)}
{selectedVersion.title}
{selectedVersion.content && (
{selectedVersion.content}
)}
{selectedVersion.category && (
{selectedVersion.category}
)}
{selectedVersion.status && (
{selectedVersion.status}
)}
{selectedVersion.tags && selectedVersion.tags.length > 0 && (
{selectedVersion.tags.map((tag, i) => (
{tag}
))}
)}
{(() => {
const attachments = getAttachments(selectedVersion);
if (!attachments || attachments.length === 0) return null;
return (
{attachments.map((att, i) => (
))}
);
})()}
>
) : (
📄
Select a version
Click on any version in the timeline
)}
{/* Restore Confirmation Modal */}
{showRestoreConfirm && selectedVersion && !selectedVersion.is_current && (
Restore Version {selectedVersion.version}?
This will restore the content from Version {selectedVersion.version} and make it the new current version.
What will be restored:
- • Title: {selectedVersion.title}
- • Content ({(selectedVersion.content || '').length} characters)
- • Tags ({(selectedVersion.tags || []).length})
{selectedVersion.category && - • Category: {selectedVersion.category}
}
{(() => {
const attachments = getAttachments(selectedVersion);
if (attachments.length > 0) {
return - • Attachments ({attachments.length} file{attachments.length !== 1 ? 's' : ''})
;
}
})()}
)}
);
};
if (typeof window !== 'undefined') window.VersionHistoryPanel = VersionHistoryPanel;