feat(FN-3509): document node mapping availability UI in dashboard guide

Documents the node mapping availability UI in the dashboard guide.

Fusion-Task-Id: FN-3509
This commit is contained in:
Fusion
2026-05-08 18:40:00 -07:00
committed by gsxdsm
parent c0d462a12d
commit ccefdd2c52
13 changed files with 464 additions and 730 deletions

View File

@@ -2,11 +2,13 @@ import { useState, useEffect, useCallback, useRef } from "react";
import type { ProjectInfo } from "../api";
import {
fetchProjectsAcrossNodes,
hasNodeMappingsSupport,
registerProject,
unregisterProject,
updateProject,
type ProjectCreateInput,
type ProjectInfoWithSource,
type ProjectNodeAvailability,
} from "../api";
export interface UseProjectsResult {
@@ -29,6 +31,43 @@ export interface UseProjectsResult {
const POLL_INTERVAL_MS = 5000; // 5 seconds
const VISIBILITY_REFRESH_DEBOUNCE_MS = 1000;
function normalizeNodeMappings(project: ProjectInfoWithSource): ProjectNodeAvailability[] {
const mappingSource = hasNodeMappingsSupport(project)
? (project.nodeMappings ?? project.projectNodeMappings ?? project.pathMappings ?? [])
: [];
const normalizedMappings = mappingSource
.filter((mapping) => Boolean(mapping?.nodeId) && Boolean(mapping?.path))
.map((mapping) => ({
nodeId: mapping.nodeId,
nodeName: mapping.nodeName,
path: mapping.path,
available: mapping.available !== false,
}));
if (normalizedMappings.length > 0) {
return normalizedMappings;
}
if (project.nodeId && project.path) {
return [{
nodeId: project.nodeId,
nodeName: project._sourceNodeName,
path: project.path,
available: true,
}];
}
return [];
}
function normalizeProjects(projects: ProjectInfoWithSource[]): ProjectInfoWithSource[] {
return projects.map((project) => ({
...project,
nodeMappings: normalizeNodeMappings(project),
}));
}
/**
* Hook for fetching and managing projects.
* Automatically polls for updates every 5 seconds.
@@ -46,7 +85,7 @@ export function useProjects(): UseProjectsResult {
try {
setError(null);
const data = await fetchProjectsAcrossNodes();
setProjects(data);
setProjects(normalizeProjects(data));
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to fetch projects");
// Don't clear existing projects on error - keep showing stale data
@@ -62,10 +101,11 @@ export function useProjects(): UseProjectsResult {
const t0 = performance.now();
try {
const data = await fetchProjectsAcrossNodes();
const normalizedData = normalizeProjects(data);
const elapsed = Math.round(performance.now() - t0);
console.log(`[useProjects] initial fetchProjectsAcrossNodes took ${elapsed}ms (${data.length} projects)`);
console.log(`[useProjects] initial fetchProjectsAcrossNodes took ${elapsed}ms (${normalizedData.length} projects)`);
if (!cancelled) {
setProjects(data);
setProjects(normalizedData);
setError(null);
}
} catch (err) {