import { createFileRoute, useNavigate } from '@tanstack/react-router' import { useQuery } from '@tanstack/react-query' import { ServerIcon, AlertTriangleIcon, WalletIcon, TrendingUpIcon } from 'lucide-react' import { snapshotQueryOptions, ratesQueryOptions } from '@/queries/snapshot' import { PageShell } from '@/components/page-shell' import { PageHeader } from '@/components/page-header' import { SectionCards } from '@/components/section-cards' import { QueryState } from '@/components/query-state' import { TableCard } from '@/components/table-card' import { SectionCardsSkeleton } from '@/components/skeletons' import { EmptyState } from '@/components/empty-state' import { Button } from '@cfdm/ui/components/button' import { Badge } from '@cfdm/ui/components/badge' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@cfdm/ui/components/table' import { computeInventoryHealth } from '@/lib/inventory-health' import { formatInBaseCurrency, normalizeRatesPayload } from '@/lib/format' import { vpsStatusLabel } from '@/lib/format' export const Route = createFileRoute('/_auth/dashboard')({ loader: ({ context: { queryClient } }) => queryClient.ensureQueryData(snapshotQueryOptions()), component: DashboardPage, }) function DashboardPage() { const navigate = useNavigate() const { data: snapshot, isLoading, isError, error, refetch } = useQuery(snapshotQueryOptions()) const settings = snapshot?.settings?.[0] const { data: rawRates } = useQuery(ratesQueryOptions(settings?.ratesUrl)) const ratesData = normalizeRatesPayload(rawRates) ?? rawRates ?? null return ( refetch()} skeleton={} > {(snap) => { const activeVps = snap.vps.filter((v) => v.status === 'active') const monthlyTotal = activeVps.reduce((acc, v) => { const monthly = Number(v.monthlyRate || 0) const daily = Number(v.dailyRate || 0) const burn = v.tariffType === 'daily' ? daily * 30 : monthly return acc + (Number.isFinite(burn) ? burn : 0) }, 0) const issues = computeInventoryHealth(snap) const totalBalance = snap.providerAccounts.reduce( (acc, a) => acc + (Number(a.balance_api ?? 0) || 0), 0, ) return ( <> , hint: `всего ${snap.vps.length}`, }, { label: 'Хостеры', value: snap.providers.length, icon: , hint: `${snap.providerAccounts.length} аккаунтов`, }, { label: 'Расход/мес (оценка)', value: formatInBaseCurrency(monthlyTotal, snap.vps[0]?.currency ?? 'USD', snap.settings, ratesData), icon: , }, { label: 'Баланс аккаунтов (API)', value: formatInBaseCurrency(totalBalance, snap.settings[0]?.baseCurrency ?? 'RUB', snap.settings, ratesData), icon: , }, ]} /> 0 ? ( {issues.length} ) : ( всё ок ) } > {issues.length === 0 ? (
} title="Проблем не найдено" description="Все активные VPS имеют проект, ставку и актуальный синк" />
) : ( Проблема Кол-во Действие {issues.map((issue) => (
{issue.title} {issue.hint ? ( {issue.hint} ) : null}
{issue.count}
))}
)}
IP / DNS Проект Статус Ставка/мес {activeVps.slice(0, 8).map((v) => ( {v.ip || v.dns} {v.project || '—'} {vpsStatusLabel(v.status)} {formatInBaseCurrency( v.tariffType === 'daily' ? Number(v.dailyRate || 0) * 30 : Number(v.monthlyRate || 0), v.currency, snap.settings, ratesData, )} ))}
) }}
) }