import { useEffect, useState } from 'react'; import api from '../lib/api.js'; import { useQuery } from '@tanstack/react-query'; import ConfirmDialog from './ConfirmDialog.jsx'; import { useNotify } from './NotifyProvider.jsx'; import { IconHistory, IconRefresh, IconDeviceFloppy } from '@tabler/icons-react'; export default function HistoryModal({ resource, show, onClose, onRolledBack }) { const [loading, setLoading] = useState(false); const [items, setItems] = useState([]); const notify = useNotify(); const { refetch } = useQuery({ queryKey: ['history', resource], enabled: false, queryFn: async () => { const res = await api.get(`/history/${resource}`) const data = Array.isArray(res.data?.items) ? res.data.items : [] setItems(data) return data }, }) useEffect(() => { if (show) refetch().catch(() => notify.error('Не удалось загрузить историю версий')); }, [show, resource]); const [confirmState, setConfirmState] = useState({ open: false, onConfirm: null }); const rollback = async (versionId) => { if (!versionId) return; await new Promise((resolve) => { setConfirmState({ open: true, onConfirm: async () => { setConfirmState({ open: false, onConfirm: null }); setLoading(true); try { const res = await api.post(`/history/${resource}/rollback`, { versionId }); notify.success('Откат выполнен'); onRolledBack && onRolledBack(res.data || {}); onClose && onClose(); } catch (e) { notify.error('Не удалось выполнить откат'); } finally { setLoading(false); } resolve(); } }); }); }; if (!show) return null; return (
{ if (e.key === 'Escape') onClose?.() }}>
{ if (e.key === 'Tab') { const c = e.currentTarget const focusable = c.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])') if (!focusable || focusable.length === 0) return const first = focusable[0] const last = focusable[focusable.length - 1] if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } } }}>
История версий
Ресурс: {resource}
{items.length === 0 ? ( ) : items.map((v) => ( ))}
Версия Дата Размер ETag Действия
Нет данных (возможно, версионирование бакета отключено)
{v.versionId} {v.lastModified ? new Date(v.lastModified).toLocaleString() : '—'} {typeof v.size === 'number' ? `${v.size} байт` : '—'} {v.etag || '—'} {!v.isLatest && ( )}
setConfirmState({ open: false, onConfirm: null })} onConfirm={confirmState.onConfirm} />
); }