feat: Добавить кнопку для копирования метаданных и отображение времени последнего изменения в компонентах PageHeaderActions и S3MetaBar
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m52s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m52s
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
|||||||
IconEraser
|
IconEraser
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import PortalDropdown from './PortalDropdown.jsx';
|
import PortalDropdown from './PortalDropdown.jsx';
|
||||||
|
import { IconClock } from '@tabler/icons-react';
|
||||||
|
|
||||||
function PageHeaderActions({
|
function PageHeaderActions({
|
||||||
loading = false,
|
loading = false,
|
||||||
@@ -25,6 +26,7 @@ function PageHeaderActions({
|
|||||||
onSave,
|
onSave,
|
||||||
disableSave = false,
|
disableSave = false,
|
||||||
onHistory,
|
onHistory,
|
||||||
|
lastModified,
|
||||||
onOnlineUpdate,
|
onOnlineUpdate,
|
||||||
onBackgroundUpdate,
|
onBackgroundUpdate,
|
||||||
}) {
|
}) {
|
||||||
@@ -144,7 +146,16 @@ function PageHeaderActions({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{onHistory && (
|
{onHistory && (
|
||||||
<button className="btn btn-outline-secondary" type="button" onClick={onHistory} title="История версий">История</button>
|
<div className="btn-group">
|
||||||
|
<button className="btn btn-outline-secondary" type="button" onClick={onHistory} title="История версий">История</button>
|
||||||
|
{lastModified && (
|
||||||
|
<button className="btn btn-outline-secondary" type="button" onClick={onHistory} title={`Последние изменения: ${new Date(lastModified).toLocaleString()}`}>
|
||||||
|
<IconClock className="me-1" />
|
||||||
|
<span className="d-none d-sm-inline">{new Date(lastModified).toLocaleTimeString()}</span>
|
||||||
|
<span className="d-inline d-sm-none">{new Date(lastModified).toLocaleTimeString()}</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,6 +20,17 @@ function S3MetaBar({ keys = [], className = '' }) {
|
|||||||
|
|
||||||
useEffect(() => { fetchMeta(); }, []);
|
useEffect(() => { fetchMeta(); }, []);
|
||||||
|
|
||||||
|
const copyMeta = async (k) => {
|
||||||
|
try {
|
||||||
|
const it = meta[k] || {};
|
||||||
|
const text = `etag: ${it?.etag || ''}\nlastModified: ${it?.lastModified || ''}`;
|
||||||
|
await navigator.clipboard?.writeText(text);
|
||||||
|
window.notify?.success?.('Скопировано в буфер');
|
||||||
|
} catch (e) {
|
||||||
|
window.notify?.error?.('Не удалось скопировать', String(e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const renderKey = (k) => {
|
const renderKey = (k) => {
|
||||||
const it = meta[k];
|
const it = meta[k];
|
||||||
return (
|
return (
|
||||||
@@ -27,12 +38,15 @@ function S3MetaBar({ keys = [], className = '' }) {
|
|||||||
<span className="badge bg-blue-lt text-blue d-inline-flex align-items-center"><IconHash size={14} className="me-1" />{it?.etag || '—'}</span>
|
<span className="badge bg-blue-lt text-blue d-inline-flex align-items-center"><IconHash size={14} className="me-1" />{it?.etag || '—'}</span>
|
||||||
<span className="badge bg-blue-lt text-blue d-inline-flex align-items-center"><IconClock size={14} className="me-1" />{it?.lastModified ? new Date(it.lastModified).toLocaleString() : '—'}</span>
|
<span className="badge bg-blue-lt text-blue d-inline-flex align-items-center"><IconClock size={14} className="me-1" />{it?.lastModified ? new Date(it.lastModified).toLocaleString() : '—'}</span>
|
||||||
<span className="badge bg-blue-lt text-blue d-inline-flex align-items-center"><IconFileText size={14} className="me-1" />{typeof it?.contentLength === 'number' ? `${it.contentLength} байт` : '—'}</span>
|
<span className="badge bg-blue-lt text-blue d-inline-flex align-items-center"><IconFileText size={14} className="me-1" />{typeof it?.contentLength === 'number' ? `${it.contentLength} байт` : '—'}</span>
|
||||||
|
<button className="btn btn-outline-secondary btn-sm" type="button" onClick={() => copyMeta(k)} title="Копировать ETag/Last-Modified">
|
||||||
|
<IconFileText size={14} className="me-1" />Копировать
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`card-footer ${className}`}>
|
<div className={`card-footer ${className}`} style={{ position: 'sticky', bottom: 0, zIndex: 5, background: 'var(--tblr-card-bg)', borderTop: '1px solid rgba(0,0,0,.05)' }}>
|
||||||
<div className="d-flex align-items-center justify-content-between">
|
<div className="d-flex align-items-center justify-content-between">
|
||||||
<div className="d-flex flex-column gap-1">
|
<div className="d-flex flex-column gap-1">
|
||||||
{keys.length === 0 ? renderKey('domainsNew') : keys.map(renderKey)}
|
{keys.length === 0 ? renderKey('domainsNew') : keys.map(renderKey)}
|
||||||
|
|||||||
Reference in New Issue
Block a user