feat: Добавить отображение версии UI в нижнем колонтитуле и улучшить отображение ASN и CIDR с помощью моноширинного шрифта в менеджерах ASNs и IPRanges
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m51s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m51s
This commit is contained in:
@@ -578,7 +578,7 @@ function ASNsNewManager() {
|
||||
<tbody>
|
||||
{paginatedItems.map((item) => (
|
||||
<tr key={item.asn} className={editingAsn === item.asn ? 'table-info' : ''}>
|
||||
<td>{item.asn}</td>
|
||||
<td className="font-monospace" title="ASN">{item.asn}</td>
|
||||
<td>{renderCommunityBadge(item.community)}</td>
|
||||
<td className="text-end">
|
||||
{editingAsn === item.asn ? (
|
||||
|
||||
@@ -336,6 +336,11 @@ function MainLayout() {
|
||||
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
||||
</Routes>
|
||||
</div>
|
||||
<footer className="footer mt-auto py-3">
|
||||
<div className="container-xl text-muted small">
|
||||
Версия UI: {import.meta?.env?.VITE_APP_VERSION || 'dev'}
|
||||
</div>
|
||||
</footer>
|
||||
<SettingsModal open={settingsOpen} onClose={() => setSettingsOpen(false)} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -841,7 +841,7 @@ function IPRangesManager() {
|
||||
<tbody>
|
||||
{paginatedItems.map((item) => (
|
||||
<tr key={item.ipRange} className={editingIp === item.ipRange ? 'table-info' : ''}>
|
||||
<td>{item.ipRange}</td>
|
||||
<td className="font-monospace" title="CIDR">{item.ipRange}</td>
|
||||
<td>{renderCommunityBadge(item.community)}</td>
|
||||
<td className="text-end">
|
||||
{editingIp === item.ipRange ? (
|
||||
|
||||
@@ -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({
|
||||
<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()}`}>
|
||||
<button className="btn btn-outline-secondary" type="button" onClick={onHistory} title={`Последние изменения: ${formatDateTimeWithRelative(lastModified)}`}>
|
||||
<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>
|
||||
<span className="d-none d-sm-inline">{formatDateTimeWithRelative(lastModified)}</span>
|
||||
<span className="d-inline d-sm-none">{formatDateTimeWithRelative(lastModified)}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -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 (
|
||||
<div key={k} className="d-flex align-items-center gap-2 flex-wrap">
|
||||
<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" title="ETag"><IconHash size={14} className="me-1" /><span className="font-monospace">{it?.etag || '—'}</span></span>
|
||||
<span className="badge bg-blue-lt text-blue d-inline-flex align-items-center" title="Время последнего изменения"><IconClock size={14} className="me-1" />{it?.lastModified ? formatDateTimeWithRelative(it.lastModified) : '—'}</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" />Копировать
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user