diff --git a/frontend-modern/src/components/Dashboard/GuestDrawer.test.tsx b/frontend-modern/src/components/Dashboard/GuestDrawer.test.tsx
index cdeae9014..7c2c7d837 100644
--- a/frontend-modern/src/components/Dashboard/GuestDrawer.test.tsx
+++ b/frontend-modern/src/components/Dashboard/GuestDrawer.test.tsx
@@ -2,6 +2,7 @@ import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
import { render, screen, fireEvent, cleanup } from '@solidjs/testing-library';
import type { WorkloadGuest } from '@/types/workloads';
import type { Memory, Disk, GuestNetworkInterface } from '@/types/api';
+import { getCanonicalWorkloadId } from '@/utils/workloads';
// ── Mocks ──────────────────────────────────────────────────────────────
@@ -507,19 +508,23 @@ describe('GuestDrawer', () => {
// ── WebInterfaceUrlField ──
describe('WebInterfaceUrlField', () => {
- it('passes correct metadataId using guest.id', () => {
+ it('passes canonical metadataId using shared workload identity', () => {
render(() => );
- expect(screen.getByTestId('url-id').textContent).toBe('my-guest-id');
+ expect(screen.getByTestId('url-id').textContent).toBe(
+ getCanonicalWorkloadId(makeGuest({ id: 'my-guest-id' })),
+ );
});
- it('builds fallback id from instance:node:vmid when id is empty', () => {
+ it('builds canonical id from instance:node:vmid when id is empty', () => {
render(() => (
));
- expect(screen.getByTestId('url-id').textContent).toBe('pve:n1:200');
+ expect(screen.getByTestId('url-id').textContent).toBe(
+ getCanonicalWorkloadId(makeGuest({ id: '', instance: 'pve', node: 'n1', vmid: 200 })),
+ );
});
it('labels app-container guests as "container"', () => {
diff --git a/frontend-modern/src/components/Dashboard/GuestDrawer.tsx b/frontend-modern/src/components/Dashboard/GuestDrawer.tsx
index 095c65d66..1ed5f7e80 100644
--- a/frontend-modern/src/components/Dashboard/GuestDrawer.tsx
+++ b/frontend-modern/src/components/Dashboard/GuestDrawer.tsx
@@ -6,6 +6,7 @@ import { DiskList } from './DiskList';
import { DiscoveryTab } from '../Discovery/DiscoveryTab';
import {
getDiscoveryResourceTypeForWorkload,
+ getCanonicalWorkloadId,
getWebInterfaceTargetLabelForWorkload,
resolveWorkloadType,
} from '@/utils/workloads';
@@ -28,10 +29,7 @@ interface GuestDrawerProps {
export const GuestDrawer: Component = (props) => {
const navigate = useNavigate();
- const guestId = () => {
- if (props.guest.id) return props.guest.id;
- return `${props.guest.instance}:${props.guest.node}:${props.guest.vmid}`;
- };
+ const guestId = () => getCanonicalWorkloadId(props.guest);
const infrastructureHref = () => buildInfrastructureHrefForWorkload(props.guest);
const isVM = (guest: Guest): boolean => {
diff --git a/frontend-modern/src/components/Dashboard/GuestRow.tsx b/frontend-modern/src/components/Dashboard/GuestRow.tsx
index 13c142060..7d2f39ce8 100644
--- a/frontend-modern/src/components/Dashboard/GuestRow.tsx
+++ b/frontend-modern/src/components/Dashboard/GuestRow.tsx
@@ -110,10 +110,6 @@ const getOutlierEmphasis = (value: number, stats: IODistributionStats): IOEmphas
const GROUPED_FIRST_CELL_INDENT = 'pl-3 sm:pl-5 lg:pl-8';
const DEFAULT_FIRST_CELL_INDENT = 'pl-2 sm:pl-3';
-const buildGuestId = (guest: Guest) => {
- return getCanonicalWorkloadId(guest);
-};
-
// Type guard for VM vs Container
const isVM = (guest: Guest): guest is VM => {
return resolveWorkloadType(guest) === 'vm';
@@ -771,7 +767,7 @@ interface GuestRowProps {
export function GuestRow(props: GuestRowProps) {
const navigate = useNavigate();
- const guestId = createMemo(() => buildGuestId(props.guest));
+ const guestId = createMemo(() => getCanonicalWorkloadId(props.guest));
const infrastructureHref = createMemo(() => buildInfrastructureHrefForWorkload(props.guest));
// Use breakpoint hook directly for responsive behavior
diff --git a/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts b/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts
index 4723b2075..47151cb97 100644
--- a/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts
+++ b/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts
@@ -316,6 +316,10 @@ describe('frontend resource type boundaries', () => {
expect(dashboardSource).toContain('normalizeWorkloadViewModeParam');
expect(dashboardSource).not.toContain('function normalizeViewModeParam');
expect(dashboardSource).not.toContain('workloadSummaryGuestId');
+ expect(guestRowSource).toContain('getCanonicalWorkloadId');
+ expect(guestRowSource).not.toContain('buildGuestId');
+ expect(guestDrawerSource).toContain('getCanonicalWorkloadId');
+ expect(guestDrawerSource).not.toContain('const guestId = () => {');
expect(emptyStateSource).toContain('getEmptyStatePresentation');
expect(emptyStateSource).not.toContain('const iconBgClass: Record =');
expect(emptyStateSource).not.toContain(