diff --git a/src/pages/DashboardPage.jsx b/src/pages/DashboardPage.jsx
index 5a2ea25..2ed81b1 100644
--- a/src/pages/DashboardPage.jsx
+++ b/src/pages/DashboardPage.jsx
@@ -17,6 +17,7 @@ import {
} from '@tabler/icons-react'
export function DashboardPage({ db = {}, settings, ratesData }) {
+ const [dashTab, setDashTab] = useState('overview')
const vps = Array.isArray(db.vps) ? db.vps : []
const providerAccounts = Array.isArray(db.providerAccounts) ? db.providerAccounts : []
const balanceLedger = Array.isArray(db.balanceLedger) ? db.balanceLedger : []
@@ -234,85 +235,37 @@ export function DashboardPage({ db = {}, settings, ratesData }) {
.sort((a, b) => a.paidUntil - b.paidUntil)
.slice(0, 10)
+ const healthIssuesCount = inventoryIssues.reduce((n, i) => n + (i.count || 0), 0)
+
return (
<>
-
- {inventoryIssues.length > 0 ? (
-
-
-
-
Здоровье инвентаря
-
-
-
- {inventoryIssues.map((issue) => (
-
-
-
-
- {issue.title}
- {issue.count}
-
- {issue.hint ?
{issue.hint}
: null}
-
-
-
- ))}
-
-
-
-
- ) : null}
-
-
-
-
-
Последние синхронизации
-
-
- Аккаунты
-
-
-
-
- {recentSyncFeed.map((row) => {
- const acc = providerAccounts.find((a) => a.id === row.accountId)
- const line = formatSyncSummaryLine(row.summary)
- return (
-
-
-
-
{acc?.name || row.accountId}
-
- {row.status === 'error' ? (
- {row.error || line}
- ) : (
- line || 'OK'
- )}
-
-
-
- {row.status === 'ok' ? 'OK' : 'Ошибка'}
-
-
-
- {row.finishedAt
- ? new Date(row.finishedAt).toLocaleString('ru-RU')
- : '—'}
-
-
- )
- })}
- {recentSyncFeed.length === 0 ? (
-
- Запустите синхронизацию на странице аккаунтов — здесь появится краткий итог
-
+
+ -
+
+
+ -
+
-
-
+
+
+
+ {dashTab === 'overview' ? (
+
@@ -372,6 +325,79 @@ export function DashboardPage({ db = {}, settings, ratesData }) {
+
+
+
+
Остатки по аккаунтам хостеров
+
+
+
+
+
+ | Аккаунт |
+ Валюта |
+ Баланс |
+
+
+
+ {accountBalances.map((item) => (
+
+ | {item.name} |
+ {item.currency} |
+
+ provider.id === item.providerId)}
+ settings={settings}
+ ratesData={ratesData}
+ />
+ |
+
+ ))}
+ {accountBalances.length === 0 ? (
+
+ ) : null}
+
+
+
+
+
+
+
+
+
+
Истекающая оплата (ближайшие {UPCOMING_DAYS} дней)
+
+
+ {upcoming.map(({ vps: item, paidUntil }) => {
+ const provider = providers.find((p) => p.id === item.providerId)
+ const account = providerAccounts.find((a) => a.id === item.providerAccountId)
+ return (
+
+
+
+
{item.dns || item.ip}
+
+ {provider?.name || '—'} / {account?.name || '—'}
+
+
+
+ {paidUntil.toLocaleDateString('ru-RU')}
+
+
+
+ )
+ })}
+ {upcoming.length === 0 ? (
+
+ Нет VPS с истекающей оплатой в ближайшие {UPCOMING_DAYS} дней
+
+ ) : null}
+
+
+
+
@@ -463,80 +489,91 @@ export function DashboardPage({ db = {}, settings, ratesData }) {
+
+ ) : null}
-
-
-
-
Остатки по аккаунтам хостеров
-
-
-
-
-
- | Аккаунт |
- Валюта |
- Баланс |
-
-
-
- {accountBalances.map((item) => (
-
- | {item.name} |
- {item.currency} |
-
- provider.id === item.providerId)}
- settings={settings}
- ratesData={ratesData}
- />
- |
-
- ))}
- {accountBalances.length === 0 ? (
-
- ) : null}
-
-
+ {dashTab === 'health' ? (
+
+
+
0 ? 'border-warning' : ''}`}>
+
+
Health Check
+
+
+ {inventoryIssues.length > 0 ? (
+
+ {inventoryIssues.map((issue) => (
+
+
+
+
+ {issue.title}
+ {issue.count}
+
+ {issue.hint ?
{issue.hint}
: null}
+
+
+
+ ))}
+
+ ) : (
+
+ Замечаний по инвентарю нет. При появлении проблем они отобразятся здесь со ссылками на списки.
+
+ )}
+
-
-
-
-
-
Истекающая оплата (ближайшие {UPCOMING_DAYS} дней)
-
-
- {upcoming.map(({ vps: item, paidUntil }) => {
- const provider = providers.find((p) => p.id === item.providerId)
- const account = providerAccounts.find((a) => a.id === item.providerAccountId)
- return (
-
-
-
-
{item.dns || item.ip}
-
- {provider?.name || '—'} / {account?.name || '—'}
+
+
+
+
Последние синхронизации
+
+
+ Аккаунты
+
+
+
+
+ {recentSyncFeed.map((row) => {
+ const acc = providerAccounts.find((a) => a.id === row.accountId)
+ const line = formatSyncSummaryLine(row.summary)
+ return (
+
+
+
+
{acc?.name || row.accountId}
+
+ {row.status === 'error' ? (
+ {row.error || line}
+ ) : (
+ line || 'OK'
+ )}
+
+
+ {row.status === 'ok' ? 'OK' : 'Ошибка'}
+
-
- {paidUntil.toLocaleDateString('ru-RU')}
+
+ {row.finishedAt
+ ? new Date(row.finishedAt).toLocaleString('ru-RU')
+ : '—'}
+ )
+ })}
+ {recentSyncFeed.length === 0 ? (
+
+ Запустите синхронизацию на странице аккаунтов — здесь появится краткий итог
- )
- })}
- {upcoming.length === 0 ? (
-
- Нет VPS с истекающей оплатой в ближайшие {UPCOMING_DAYS} дней
-
- ) : null}
+ ) : null}
+
-
+ ) : null}
>
)
}