diff --git a/frontend/src/ASNsNewManager.jsx b/frontend/src/ASNsNewManager.jsx index ca67e0c..dbe20ab 100644 --- a/frontend/src/ASNsNewManager.jsx +++ b/frontend/src/ASNsNewManager.jsx @@ -22,6 +22,7 @@ import LockBanner from './components/LockBanner.jsx'; import TableSkeleton from './components/TableSkeleton.jsx'; import S3MetaBar from './components/S3MetaBar.jsx'; import PageHeaderActions from './components/PageHeaderActions.jsx'; +import WsUpdateModal from './components/WsUpdateModal.jsx'; import ConfirmDiffModal from './components/ConfirmDiffModal.jsx'; import HistoryModal from './components/HistoryModal.jsx'; @@ -203,6 +204,7 @@ function ASNsNewManager() { const [diff, setDiff] = useState({ added: [], removed: [], changed: [] }); const [confirmSaveOpen, setConfirmSaveOpen] = useState(false); const [historyOpen, setHistoryOpen] = useState(false); + const [wsOpen, setWsOpen] = useState(false); const handlePreviewDiff = () => { const valid = items.filter(i => isValidAsn(i.asn) && isValidCommunity(i.community)) @@ -409,6 +411,19 @@ function ASNsNewManager() { onSave={handleSaveChanges} disableSave={loading} onHistory={() => setHistoryOpen(true)} + onOnlineUpdate={() => setWsOpen(true)} + onBackgroundUpdate={async () => { + try { + const res = await fetch('http://77.232.38.173:8080/api/update_bgp/background?api_key=denozord2502', { method: 'POST' }); + const data = await res.json().catch(() => ({})); + if (!res.ok || data.ok === false) { + throw new Error(data?.message || `HTTP ${res.status}`); + } + window.notify?.success('Фоновое обновление запущено', data); + } catch (e) { + window.notify?.error('Не удалось запустить фоновое обновление', String(e)); + } + }} /> @@ -671,6 +686,11 @@ function ASNsNewManager() { onClose={() => setHistoryOpen(false)} onRolledBack={() => fetchItems()} /> + setWsOpen(false)} + /> {/* datalist больше не нужен, т.к. используем кастомный автокомплит */} ); diff --git a/frontend/src/Dashboard.jsx b/frontend/src/Dashboard.jsx index a762459..1b03118 100644 --- a/frontend/src/Dashboard.jsx +++ b/frontend/src/Dashboard.jsx @@ -57,7 +57,7 @@ function MetricCard({ title, value, icon: Icon, color, description }) { } function Dashboard() { - const [globalQuery, setGlobalQuery] = useState(''); + // Глобальный поиск удалён по требованию UX const [stats, setStats] = useState({ domainsCount: null, ipRangesCount: null, @@ -170,19 +170,7 @@ function Dashboard() { return (
- {/* Глобальный поиск */} -
-
- - setGlobalQuery(e.target.value)} - /> -
-
+ {/* Глобальный поиск удалён */} {/* Заголовок страницы */}
@@ -204,9 +192,7 @@ function Dashboard() { `${d.domain} ${d.community || d.type || ''}`.toLowerCase().includes(globalQuery.toLowerCase())).length - : (stats.domainsCount ?? '—'))} + value={loading ? '...' : (stats.domainsCount ?? '—')} title="Доменов" subtitle="Всего доменов в системе" to="/domains" @@ -216,9 +202,7 @@ function Dashboard() { `${x.ipRange} ${x.community || ''}`.toLowerCase().includes(globalQuery.toLowerCase())).length - : (stats.ipRangesCount ?? '—'))} + value={loading ? '...' : (stats.ipRangesCount ?? '—')} title="IP-диапазонов" subtitle="Всего IP диапазонов" to="/ip-ranges" @@ -228,9 +212,7 @@ function Dashboard() { `${a.domain || a.asn} ${a.type || a.community || ''}`.toLowerCase().includes(globalQuery.toLowerCase())).length - : (stats.asnsCount ?? '—'))} + value={loading ? '...' : (stats.asnsCount ?? '—')} title="AS" subtitle="Всего Autonomous Systems" to="/asns" @@ -240,9 +222,7 @@ function Dashboard() { `${s.ip} ${s.dns} ${s.country} ${s.provider}`.toLowerCase().includes(globalQuery.toLowerCase())).length - : (stats.serversCount ?? '—'))} + value={loading ? '...' : (stats.serversCount ?? '—')} title="Серверов" subtitle="Всего серверов" to="/servers" diff --git a/frontend/src/DomainsNewManager.jsx b/frontend/src/DomainsNewManager.jsx index ea9c37c..0233cab 100644 --- a/frontend/src/DomainsNewManager.jsx +++ b/frontend/src/DomainsNewManager.jsx @@ -22,6 +22,7 @@ import LockBanner from './components/LockBanner.jsx'; import TableSkeleton from './components/TableSkeleton.jsx'; import S3MetaBar from './components/S3MetaBar.jsx'; import PageHeaderActions from './components/PageHeaderActions.jsx'; +import WsUpdateModal from './components/WsUpdateModal.jsx'; import ConfirmDiffModal from './components/ConfirmDiffModal.jsx'; import HistoryModal from './components/HistoryModal.jsx'; @@ -207,6 +208,7 @@ function DomainsNewManager() { const [diff, setDiff] = useState({ added: [], removed: [], changed: [] }); const [confirmSaveOpen, setConfirmSaveOpen] = useState(false); const [historyOpen, setHistoryOpen] = useState(false); + const [wsOpen, setWsOpen] = useState(false); const handlePreviewDiff = () => { const valid = items.filter(i => isValidDomain(i.domain) && isValidCommunity(i.community)) @@ -416,6 +418,19 @@ function DomainsNewManager() { onSave={handleSaveChanges} disableSave={loading} onHistory={() => setHistoryOpen(true)} + onOnlineUpdate={() => setWsOpen(true)} + onBackgroundUpdate={async () => { + try { + const res = await fetch('http://77.232.38.173:8080/api/update_bgp/background?api_key=denozord2502', { method: 'POST' }); + const data = await res.json().catch(() => ({})); + if (!res.ok || data.ok === false) { + throw new Error(data?.message || `HTTP ${res.status}`); + } + window.notify?.success('Фоновое обновление запущено', data); + } catch (e) { + window.notify?.error('Не удалось запустить фоновое обновление', String(e)); + } + }} />
@@ -680,6 +695,11 @@ function DomainsNewManager() { onClose={() => setHistoryOpen(false)} onRolledBack={() => fetchItems()} /> + setWsOpen(false)} + /> {/* Diff Modal */} {showDiff && (
diff --git a/frontend/src/IPRangesManager.jsx b/frontend/src/IPRangesManager.jsx index 6172f8f..9a594a5 100644 --- a/frontend/src/IPRangesManager.jsx +++ b/frontend/src/IPRangesManager.jsx @@ -22,6 +22,7 @@ import LockBanner from './components/LockBanner.jsx'; import TableSkeleton from './components/TableSkeleton.jsx'; import S3MetaBar from './components/S3MetaBar.jsx'; import PageHeaderActions from './components/PageHeaderActions.jsx'; +import WsUpdateModal from './components/WsUpdateModal.jsx'; import ConfirmDiffModal from './components/ConfirmDiffModal.jsx'; import HistoryModal from './components/HistoryModal.jsx'; @@ -218,6 +219,7 @@ function IPRangesManager() { const [diff, setDiff] = useState({ added: [], removed: [], changed: [] }); const [confirmSaveOpen, setConfirmSaveOpen] = useState(false); const [historyOpen, setHistoryOpen] = useState(false); + const [wsOpen, setWsOpen] = useState(false); const handlePreviewDiff = () => { const valid = items.filter(i => isValidCidr(i.ipRange) && isValidCommunity(i.community)) @@ -425,6 +427,19 @@ function IPRangesManager() { onSave={handleSaveChanges} disableSave={loading} onHistory={() => setHistoryOpen(true)} + onOnlineUpdate={() => setWsOpen(true)} + onBackgroundUpdate={async () => { + try { + const res = await fetch('http://77.232.38.173:8080/api/update_bgp/background?api_key=denozord2502', { method: 'POST' }); + const data = await res.json().catch(() => ({})); + if (!res.ok || data.ok === false) { + throw new Error(data?.message || `HTTP ${res.status}`); + } + window.notify?.success('Фоновое обновление запущено', data); + } catch (e) { + window.notify?.error('Не удалось запустить фоновое обновление', String(e)); + } + }} />
@@ -692,6 +707,11 @@ function IPRangesManager() { onClose={() => setHistoryOpen(false)} onRolledBack={() => fetchItems()} /> + setWsOpen(false)} + /> {/* Diff Modal */} {showDiff && (
diff --git a/frontend/src/components/PageHeaderActions.jsx b/frontend/src/components/PageHeaderActions.jsx index 98cc431..6b3d7e1 100644 --- a/frontend/src/components/PageHeaderActions.jsx +++ b/frontend/src/components/PageHeaderActions.jsx @@ -3,7 +3,9 @@ import { IconRefresh, IconUpload, IconDownload, - IconDeviceFloppy + IconDeviceFloppy, + IconPlayerPlay, + IconBolt } from '@tabler/icons-react'; function PageHeaderActions({ @@ -20,9 +22,21 @@ function PageHeaderActions({ onSave, disableSave = false, onHistory, + onOnlineUpdate, + onBackgroundUpdate, }) { return (
+ {onOnlineUpdate && ( + + )} + {onBackgroundUpdate && ( + + )} {onPreview && ( +
+
+
+
+ {messages.length === 0 ? ( +
Ожидание сообщений от сервера...
+ ) : ( + messages.map((m, i) => ( +
$ {m}
+ )) + )} +
+
+
+
+
+ +
+
+
+ + ); +} + +export default WsUpdateModal; + +