feat: Enhance error handling in server responses by including request IDs and detailed error codes; improve frontend modal components for better accessibility and user experience
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 4m59s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 4m59s
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
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';
|
||||
|
||||
@@ -22,30 +23,49 @@ export default function HistoryModal({ resource, show, onClose, onRolledBack })
|
||||
|
||||
useEffect(() => { if (show) refetch().catch(() => notify.error('Не удалось загрузить историю версий')); }, [show, resource]);
|
||||
|
||||
const [confirmState, setConfirmState] = useState({ open: false, onConfirm: null });
|
||||
const rollback = async (versionId) => {
|
||||
if (!versionId) return;
|
||||
if (!confirm('Откатить к выбранной версии? Текущее содержимое будет перезаписано.')) return;
|
||||
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);
|
||||
}
|
||||
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 (
|
||||
<div className="modal show d-block" tabIndex="-1" style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}>
|
||||
<div className="modal show d-block" role="dialog" aria-modal="true" aria-labelledby="history-title" style={{ backgroundColor: 'rgba(0,0,0,0.5)' }} onKeyDown={(e) => { if (e.key === 'Escape') onClose?.() }}>
|
||||
<div className="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-content" tabIndex={-1} onKeyDown={(e) => {
|
||||
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(); }
|
||||
}
|
||||
}}>
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title d-flex align-items-center">
|
||||
<h5 id="history-title" className="modal-title d-flex align-items-center">
|
||||
<IconHistory className="me-2" /> История версий
|
||||
</h5>
|
||||
<button type="button" className="btn-close" onClick={onClose}></button>
|
||||
@@ -96,6 +116,14 @@ export default function HistoryModal({ resource, show, onClose, onRolledBack })
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ConfirmDialog
|
||||
open={confirmState.open}
|
||||
title="Подтверждение"
|
||||
message="Откатить к выбранной версии? Текущее содержимое будет перезаписано."
|
||||
confirmText="Откатить"
|
||||
onCancel={() => setConfirmState({ open: false, onConfirm: null })}
|
||||
onConfirm={confirmState.onConfirm}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user