VersionHistory.jsx (3690B)
1 import { useCallback, useEffect, useState } from 'react'; 2 import { 3 fetchDocumentVersions, 4 friendlyErrorMessage, 5 restoreDocumentVersion, 6 } from '../lib/api.js'; 7 import { STR, formatVersionDate } from '../lib/strings.js'; 8 9 export default function VersionHistory({ 10 editToken, 11 clientId, 12 lockToken, 13 onRestored, 14 }) { 15 const [open, setOpen] = useState(false); 16 const [versions, setVersions] = useState([]); 17 const [loading, setLoading] = useState(false); 18 const [restoringId, setRestoringId] = useState(''); 19 const [error, setError] = useState(''); 20 21 const loadVersions = useCallback(async () => { 22 setLoading(true); 23 setError(''); 24 try { 25 const data = await fetchDocumentVersions(editToken, { clientId, lockToken }); 26 setVersions(data.versions ?? []); 27 } catch (err) { 28 setError(friendlyErrorMessage(err)); 29 } finally { 30 setLoading(false); 31 } 32 }, [editToken, clientId, lockToken]); 33 34 useEffect(() => { 35 if (!open) return; 36 loadVersions(); 37 }, [open, loadVersions]); 38 39 const handleRestore = async (versionId) => { 40 const confirmed = window.confirm(STR.VERSION_RESTORE_CONFIRM); 41 if (!confirmed) return; 42 43 setRestoringId(versionId); 44 setError(''); 45 try { 46 const data = await restoreDocumentVersion(editToken, versionId, { 47 clientId, 48 lockToken, 49 }); 50 onRestored(data); 51 await loadVersions(); 52 } catch (err) { 53 setError(friendlyErrorMessage(err)); 54 } finally { 55 setRestoringId(''); 56 } 57 }; 58 59 return ( 60 <> 61 <button 62 type="button" 63 className="btn btn-ghost" 64 onClick={() => setOpen(true)} 65 > 66 {STR.VERSION_HISTORY} 67 </button> 68 69 {open && ( 70 <div className="modal-backdrop" role="presentation" onClick={() => setOpen(false)}> 71 <div 72 className="modal share-panel version-panel" 73 role="dialog" 74 aria-labelledby="version-history-title" 75 aria-modal="true" 76 onClick={(e) => e.stopPropagation()} 77 > 78 <h2 id="version-history-title" className="modal__title"> 79 {STR.VERSION_HISTORY} 80 </h2> 81 82 {loading && <p className="version-panel__status">{STR.VERSION_LOADING}</p>} 83 84 {!loading && versions.length === 0 && ( 85 <p className="version-panel__status">{STR.VERSION_EMPTY}</p> 86 )} 87 88 {!loading && versions.length > 0 && ( 89 <ul className="version-list"> 90 {versions.map((version) => ( 91 <li key={version.id} className="version-list__item"> 92 <span className="version-list__date"> 93 {formatVersionDate(version.createdAt)} 94 </span> 95 <button 96 type="button" 97 className="btn btn-ghost version-list__restore" 98 disabled={restoringId === version.id} 99 onClick={() => handleRestore(version.id)} 100 > 101 {restoringId === version.id 102 ? STR.VERSION_RESTORING 103 : STR.VERSION_RESTORE} 104 </button> 105 </li> 106 ))} 107 </ul> 108 )} 109 110 {error && ( 111 <p className="share-error" role="alert"> 112 {error} 113 </p> 114 )} 115 116 <div className="modal__actions"> 117 <button type="button" className="btn" onClick={() => setOpen(false)}> 118 {STR.CLOSE} 119 </button> 120 </div> 121 </div> 122 </div> 123 )} 124 </> 125 ); 126 }