fix(dashboard): render disk chart; show server name as node label
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 7s
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 7s
- Dashboard "ТОП статистика по диску" was stuck on "Загрузка...": the
node name in the dashboard_metrics WS payload is `name`, not `node`, so
the RRD query stayed disabled. Use name fallback and unwrap the
{data:[...]} response shape.
- Instances list + detail page: show the panel-side server name (from the
add-server form) in the "Узел" column/label instead of the Proxmox node
hostname. Real node is still used for all API calls.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -226,16 +226,24 @@ function ClusterNodesSection({ serverList, nodeStats }: { serverList: Rec[]; nod
|
|||||||
.sort((a, b) => (Number(b.disk_pct) || 0) - (Number(a.disk_pct) || 0))
|
.sort((a, b) => (Number(b.disk_pct) || 0) - (Number(a.disk_pct) || 0))
|
||||||
.slice(0, 5)
|
.slice(0, 5)
|
||||||
|
|
||||||
// Fetch RRD for each of those nodes in parallel
|
// Fetch RRD for each of those nodes in parallel.
|
||||||
|
// Node name comes from the `dashboard_metrics` WS payload, where the field is
|
||||||
|
// `name` (not `node`); fall back to `node` for the REST `resources/all` shape.
|
||||||
const rrdResults = useQueries({
|
const rrdResults = useQueries({
|
||||||
queries: chartNodes.map(n => ({
|
queries: chartNodes.map(n => {
|
||||||
queryKey: ['node-rrd', Number(n.server_id) || 0, String(n.node || ''), timeframe],
|
const nodeName = String(n.node || n.name || '')
|
||||||
|
return {
|
||||||
|
queryKey: ['node-rrd', Number(n.server_id) || 0, nodeName, timeframe],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
apiClient.get<Rec[]>(
|
apiClient
|
||||||
`/proxmox/api/${n.server_id}/node/rrddata?node=${n.node}&timeframe=${timeframe}`
|
.get<{ data?: Rec[] } | Rec[]>(
|
||||||
),
|
`/proxmox/api/${n.server_id}/node/rrddata?node=${encodeURIComponent(nodeName)}&timeframe=${timeframe}`
|
||||||
enabled: !!n.server_id && !!n.node,
|
)
|
||||||
})),
|
// Endpoint wraps the series in `{ data: [...] }`; tolerate both shapes.
|
||||||
|
.then(r => (Array.isArray(r) ? r : (r?.data ?? []))),
|
||||||
|
enabled: !!n.server_id && !!nodeName,
|
||||||
|
}
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Align series by index (all PVE nodes produce same-length RRD arrays)
|
// Align series by index (all PVE nodes produce same-length RRD arrays)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import {
|
|||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { StatusDot } from '@/components/shared/status-dot';
|
import { StatusDot } from '@/components/shared/status-dot';
|
||||||
import { useVirtualMachines, usePowerAction, useVMInterfaces } from '@/hooks/use-instances';
|
import { useVirtualMachines, usePowerAction, useVMInterfaces } from '@/hooks/use-instances';
|
||||||
|
import { useServers } from '@/hooks/use-nodes';
|
||||||
import InstanceActionDialogs, { PowerConfirmDialog, type InstanceAction, type PowerAction } from './InstanceActionDialogs';
|
import InstanceActionDialogs, { PowerConfirmDialog, type InstanceAction, type PowerAction } from './InstanceActionDialogs';
|
||||||
import { vmTypeLabel } from '@/lib/format';
|
import { vmTypeLabel } from '@/lib/format';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -57,6 +58,12 @@ export default function InstanceDetailPage() {
|
|||||||
const { data: allVMs, isLoading } = useVirtualMachines();
|
const { data: allVMs, isLoading } = useVirtualMachines();
|
||||||
const vm = allVMs?.find((v) => v.server_id === sid && v.vmid === vid);
|
const vm = allVMs?.find((v) => v.server_id === sid && v.vmid === vid);
|
||||||
|
|
||||||
|
// Show the panel-side server name (from the add-server form) instead of the
|
||||||
|
// Proxmox node hostname, matching the instances list. `node` is still used for
|
||||||
|
// all API calls below — only the displayed label changes.
|
||||||
|
const { data: servers = [] } = useServers();
|
||||||
|
const nodeLabel = servers.find((s) => s.id === sid)?.name || node;
|
||||||
|
|
||||||
const power = usePowerAction(sid, vid, type, node);
|
const power = usePowerAction(sid, vid, type, node);
|
||||||
const isRunning = vm?.status === 'running';
|
const isRunning = vm?.status === 'running';
|
||||||
|
|
||||||
@@ -131,7 +138,7 @@ export default function InstanceDetailPage() {
|
|||||||
<StatusDot status={vm?.status || 'unknown'} pulse />
|
<StatusDot status={vm?.status || 'unknown'} pulse />
|
||||||
{vm?.status || 'unknown'}
|
{vm?.status || 'unknown'}
|
||||||
</span>
|
</span>
|
||||||
<span>Node: {node}</span>
|
<span>{t('common.node', 'Node')}: {nodeLabel}</span>
|
||||||
{displayIP && <span className="font-mono">{displayIP}</span>}
|
{displayIP && <span className="font-mono">{displayIP}</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user