- | {item.ipRange} |
+ {item.ipRange} |
{renderCommunityBadge(item.community)} |
{editingIp === item.ipRange ? (
diff --git a/frontend/src/components/PageHeaderActions.jsx b/frontend/src/components/PageHeaderActions.jsx
index 590d94d..00343b5 100644
--- a/frontend/src/components/PageHeaderActions.jsx
+++ b/frontend/src/components/PageHeaderActions.jsx
@@ -9,6 +9,7 @@ import {
} from '@tabler/icons-react';
import PortalDropdown from './PortalDropdown.jsx';
import { IconClock } from '@tabler/icons-react';
+import { formatDateTimeWithRelative } from '../lib/datetime.js';
function PageHeaderActions({
loading = false,
@@ -149,10 +150,10 @@ function PageHeaderActions({
{lastModified && (
-
diff --git a/frontend/src/components/S3MetaBar.jsx b/frontend/src/components/S3MetaBar.jsx
index 7276b4a..de263b7 100644
--- a/frontend/src/components/S3MetaBar.jsx
+++ b/frontend/src/components/S3MetaBar.jsx
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import api from '../lib/api.js';
import { IconHash, IconClock, IconFileText, IconRefresh } from '@tabler/icons-react';
+import { formatDateTimeWithRelative } from '../lib/datetime.js';
function S3MetaBar({ keys = [], className = '' }) {
const [meta, setMeta] = useState({});
@@ -35,8 +36,8 @@ function S3MetaBar({ keys = [], className = '' }) {
const it = meta[k];
return (
- {it?.etag || '—'}
- {it?.lastModified ? new Date(it.lastModified).toLocaleString() : '—'}
+ {it?.etag || '—'}
+ {it?.lastModified ? formatDateTimeWithRelative(it.lastModified) : '—'}
{typeof it?.contentLength === 'number' ? `${it.contentLength} байт` : '—'}
copyMeta(k)} title="Копировать ETag/Last-Modified">
Копировать
diff --git a/frontend/src/lib/datetime.js b/frontend/src/lib/datetime.js
new file mode 100644
index 0000000..67affa0
--- /dev/null
+++ b/frontend/src/lib/datetime.js
@@ -0,0 +1,42 @@
+function pad(num) {
+ return String(num).padStart(2, '0');
+}
+
+export function formatDateTime(input) {
+ if (!input) return '';
+ const d = input instanceof Date ? input : new Date(input);
+ if (Number.isNaN(d.getTime())) return '';
+ const day = pad(d.getDate());
+ const month = pad(d.getMonth() + 1);
+ const year = d.getFullYear();
+ const h = pad(d.getHours());
+ const m = pad(d.getMinutes());
+ const s = pad(d.getSeconds());
+ return `${day}.${month}.${year} ${h}:${m}:${s}`;
+}
+
+export function formatRelative(input) {
+ if (!input) return '';
+ const d = input instanceof Date ? input : new Date(input);
+ if (Number.isNaN(d.getTime())) return '';
+ const diffMs = Date.now() - d.getTime();
+ const sec = Math.floor(diffMs / 1000);
+ if (sec < 5) return 'только что';
+ if (sec < 60) return `${sec} сек назад`;
+ const min = Math.floor(sec / 60);
+ if (min < 60) return `${min} мин назад`;
+ const hr = Math.floor(min / 60);
+ if (hr < 24) return `${hr} ч назад`;
+ const days = Math.floor(hr / 24);
+ return `${days} дн назад`;
+}
+
+export function formatDateTimeWithRelative(input) {
+ const abs = formatDateTime(input);
+ const rel = formatRelative(input);
+ if (!abs && !rel) return '';
+ if (abs && rel) return `${abs} (${rel})`;
+ return abs || rel;
+}
+
+
|