Skip to content

Feature/add routing#18

Closed
ndorin wants to merge 7 commits intomainfrom
feature/add-routing
Closed

Feature/add routing#18
ndorin wants to merge 7 commits intomainfrom
feature/add-routing

Conversation

@ndorin
Copy link
Copy Markdown
Contributor

@ndorin ndorin commented Apr 17, 2026

No description provided.

ndorin added 7 commits April 10, 2026 10:02
- Introduced a new Routing component to visualize device connections using React Flow and Dagre for layout.
- Added RoutingDeviceNode component to represent individual devices with input and output ports.
- Implemented signal type filtering and device selection/deselection functionality.
- Created corresponding SCSS modules for styling the Routing component and device nodes.
- Updated package.json to include necessary dependencies: @xyflow/react and dagre.
- Enhanced apiSlice to fetch routing devices and tie lines from the backend.
- Updated TopNav to include a link to the new Routing feature.
…nce Routing with dark mode and unconnected ports functionality
…bileControl UI, and add TieLineEdge component for routing visualization
- Added LoginForm component for user authentication.
- Introduced RequireAuth component to protect routes.
- Created ApiPaths component to display available API paths.
- Added ApiPathDetailDrawer for detailed view of selected API paths.
- Updated routing to include login and API paths.
- Integrated Redux for authentication state management.
- Enhanced DebugConsole with device filtering and search capabilities.
- Refactored TopNav to dynamically display available apps based on authentication.
- Added selectors and slice for authentication and debug console state management.
Copilot AI review requested due to automatic review settings April 17, 2026 02:59
@ndorin ndorin closed this Apr 17, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds authenticated, app-scoped routing and new diagnostic tooling pages to the React UI (routing graph, API path explorer, initialization exception viewer), while improving Debug Console filtering and connection error visibility.

Changes:

  • Introduces login/auth state, protected routes, and app discovery.
  • Adds new pages/features: Routing graph (xyflow/dagre), API Paths, Initialization Exceptions, plus Mobile Control client management.
  • Refactors Debug Console filters to Redux state and surfaces WebSocket connection failures (certificate hint).

Reviewed changes

Copilot reviewed 30 out of 32 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
tsconfig.tsbuildinfo Updated TS incremental build metadata (shouldn’t be committed).
.gitignore Adds duplicate ignore entry for tsbuildinfo.
package.json Adds xyflow/dagre dependencies.
package-lock.json Locks new dependency tree for xyflow/dagre.
src/App.tsx Reworks routing: login + protected app routes; wraps in ErrorBoundary.
src/features/ErrorBoundary.tsx Adds global error boundary wrapper component.
src/features/RequireAuth.tsx Adds protected-route gate that redirects to login.
src/store/auth/authSlice.ts Adds auth state (isAuthenticated, availableApps).
src/store/auth/authSelectors.ts Adds selectors for auth state.
src/store/store.ts Registers auth + debugConsole reducers in store.
src/features/LoginForm.tsx Adds login form + app probing to populate available apps.
src/features/TopNav.tsx Uses availableApps for app switcher; adds new nav links; feature-gates initialization exceptions.
src/shared/functions/meetsMinimumVersion.ts Adds helper for version gating.
src/store/apiSlice.ts Adds new RTK Query endpoints and DTOs (paths, routing, login, mobile control).
src/features/ApiPaths.tsx Adds API path list view.
src/features/ApiPathDetailDrawer.tsx Adds off-canvas drawer for API path details/links.
src/features/InitializationExceptions.tsx Adds initialization exception list + stack trace expansion.
src/store/websocketSlice.ts Tracks failed WebSocket URL and connection attempt state.
src/store/websocketMiddleware.ts Dispatches connection attempt + failure actions on WebSocket errors.
src/features/DebugConsole/DebugConsole.tsx Shows certificate warning link on WebSocket failure; updates button variants.
src/store/debugConsole/debugConsoleSlice.ts Adds Redux state for debug-console filters.
src/store/debugConsole/debugConsoleSelectors.ts Adds selectors for debug-console filter state.
src/features/DebugConsole/debugConsts.ts Adds log-level ordering map for comparisons.
src/features/DebugConsole/hooks/useFilteredMessages.ts Refactors message filtering to Redux-driven filters + per-device min levels.
src/features/DebugConsole/DeviceFilterDropdown.tsx Adds device + per-device log level filter UI.
src/features/DebugConsole/DebugFilters.tsx Replaces URL-search-param filters with Redux-based filter controls.
src/features/MobileControl.tsx Adds create/delete client flows + action paths table and modals.
src/features/Routing.tsx Adds routing graph visualization with dagre layout + filtering controls.
src/features/Routing.module.scss Styles for routing filter bar + tie-line tooltip.
src/features/RoutingDeviceNode.tsx Adds custom React Flow node for routing devices/ports.
src/features/RoutingDeviceNode.module.scss Styles for routing device node and port tooltips.
src/features/TieLineEdge.tsx Adds custom edge renderer + hover/selection tooltip.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +15 to +16
console.log("Initialization exceptions:", data?.Exceptions);

Comment on lines +36 to +43
/** Dispatched by the middleware when a connection attempt fails */
connectionFailed(state, action: PayloadAction<string>) {
state.failedUrl = action.payload;
},
/** Dispatched by the middleware when a new connection attempt starts */
connectionAttemptStarted(state) {
state.failedUrl = null;
},
Comment on lines 19 to 30
const items = useMemo(() => {
if (!devices) return [{ id: debugConsts.GLOBAL, label: "Global"}];
if (!devices) return [{ id: debugConsts.GLOBAL, label: 'Global' }];

let fullList: IdLabel[] = [
{ id: debugConsts.GLOBAL, label: "Global"}
];
const deviceItems: IdLabel[] = devices
.map((d) => ({ id: d.Key, label: d.Name || d.Key }))
.sort((a, b) => a.label.localeCompare(b.label));

devices.forEach((d) => {
fullList.push({ id: d.Key, label: d.Name});
});

return fullList;
return [{ id: debugConsts.GLOBAL, label: 'Global' }, ...deviceItems];
}, [devices]);

if (!devices) return null;
if (!devices) return null;

Comment on lines +1 to +4
// Compares dot-separated version strings numerically (e.g. "2.29" > "2.9").
// Strips semver pre-release suffixes (e.g. "2.29.0-alpha.1" → "2.29.0").
export function meetsMinVersion(version: string, minimum: string): boolean {
const vParts = version.split("-")[0].split(".").map((s) => parseInt(s, 10));
Comment on lines +25 to +28
const failedUrl = useSelector((state: RootState) => state.websocket.failedUrl);
const certUrl = failedUrl
? new URL(failedUrl).origin.replace(/^wss:/, 'https:').replace(/^ws:/, 'http:')
: null;
>
{opt.label}
</Dropdown.Item>
))} </Dropdown.Menu>
Comment thread tsconfig.tsbuildinfo
],
"version": "6.0.2"
}
{"root":["./src/app.test.tsx","./src/app.tsx","./src/index.tsx","./src/react-app-env.d.ts","./src/reportwebvitals.ts","./src/setuptests.ts","./src/features/apipathdetaildrawer.tsx","./src/features/apipaths.tsx","./src/features/configfile.tsx","./src/features/devicedetail.tsx","./src/features/devicelist.tsx","./src/features/errorboundary.tsx","./src/features/errorbox.tsx","./src/features/initializationexceptions.tsx","./src/features/loginform.tsx","./src/features/mainlayout.tsx","./src/features/mobilecontrol.tsx","./src/features/requireauth.tsx","./src/features/routing.tsx","./src/features/routingdevicenode.tsx","./src/features/tielineedge.tsx","./src/features/topnav.tsx","./src/features/types.tsx","./src/features/versions.tsx","./src/features/debugconsole/consolewindow.tsx","./src/features/debugconsole/debugconsole.tsx","./src/features/debugconsole/debugfilters.tsx","./src/features/debugconsole/devicefilterdropdown.tsx","./src/features/debugconsole/logmessagedetaildrawer.tsx","./src/features/debugconsole/minimumlogleveldropdown.tsx","./src/features/debugconsole/restartconfirmmodal.tsx","./src/features/debugconsole/debugconsts.ts","./src/features/debugconsole/hooks/usefilteredmessages.ts","./src/services/httpservice.ts","./src/shared/filterclearbutton.tsx","./src/shared/filterdropdownsearchparams.tsx","./src/shared/filtersearchtext.tsx","./src/shared/headerscrollerfooter.tsx","./src/shared/listfiltersheader.tsx","./src/shared/tablecellspacer.tsx","./src/shared/functions/meetsminimumversion.ts","./src/shared/hooks/useappparams.ts","./src/shared/icons/objecticons.ts","./src/shared/icons/othericons.ts","./src/shared/icons/index.tsx","./src/shared/types/idlabel.ts","./src/shared/types/logmessage.ts","./src/store/apislice.ts","./src/store/hooks.ts","./src/store/store.ts","./src/store/websocketmiddleware.ts","./src/store/websocketslice.ts","./src/store/auth/authselectors.ts","./src/store/auth/authslice.ts","./src/store/commonui/commonuihooks.ts","./src/store/commonui/commonuiselectors.ts","./src/store/commonui/commonuislice.ts","./src/store/commonui/commonuistate.ts","./src/store/debugconsole/debugconsoleselectors.ts","./src/store/debugconsole/debugconsoleslice.ts","./vite.config.ts"],"version":"6.0.2"} No newline at end of file
Comment thread src/features/Routing.tsx
Comment on lines +84 to +146
// Devices that appear in at least one *visible* tie line endpoint
const connectedKeys = new Set<string>(
data.tieLines
.filter((tl) => !hiddenTypes.has(tl.signalType))
.flatMap((tl) => [tl.sourceDeviceKey, tl.destinationDeviceKey]),
);

// Tie lines that pass all active filters (used for port-level filtering)
const visibleTieLines = data.tieLines.filter(
(tl) =>
!hiddenTypes.has(tl.signalType) &&
!hiddenDevices.has(tl.sourceDeviceKey) &&
!hiddenDevices.has(tl.destinationDeviceKey),
);
const connectedPortKeys = hideUnconnectedPorts
? new Set<string>(
visibleTieLines.flatMap((tl) => [
`${tl.sourceDeviceKey}:${tl.sourcePortKey}`,
`${tl.destinationDeviceKey}:${tl.destinationPortKey}`,
]),
)
: null;

const devices = data.devices.filter((d) => {
if (hiddenDevices.has(d.key)) return false;
if (hideUnconnected && !connectedKeys.has(d.key)) return false;
return true;
});

// Filter each device's ports down to only those with active tie lines
const effectiveDevices = devices.map((d) =>
connectedPortKeys
? {
...d,
inputPorts: (d.inputPorts ?? []).filter((p) =>
connectedPortKeys.has(`${d.key}:${p.key}`),
),
outputPorts: (d.outputPorts ?? []).filter((p) =>
connectedPortKeys.has(`${d.key}:${p.key}`),
),
}
: d,
);

// Collect one unique device-pair edge per source→destination (dagre only
// needs connectivity, not multiplicity, for rank assignment).
const uniquePairs = [
...new Set(
data.tieLines.map(
(tl) => `${tl.sourceDeviceKey}|${tl.destinationDeviceKey}`,
),
),
];

// ── Pass 1: layout with all edges to detect cross-level (backwards) edges ──
const g1 = makeGraph();
for (const device of effectiveDevices) {
g1.setNode(device.key, { width: NODE_WIDTH, height: nodeHeight(device) });
}
for (const pair of uniquePairs) {
const [src, dst] = pair.split("|");
g1.setEdge(src, dst);
}
Comment on lines +50 to +85
return (
<>
<tr key={`ex-${idx}`}>
<td className="text-muted">{idx + 1}</td>
<td>
<span className="text-danger fw-semibold">
{ex.Message}
</span>
</td>
<td>
{ex.StackTrace && (
<button
className="btn btn-sm btn-outline-secondary"
onClick={() =>
setExpandedIndex(isExpanded ? null : idx)
}
>
{isExpanded ? "Hide" : "Show"}
</button>
)}
</td>
</tr>
{isExpanded && ex.StackTrace && (
<tr key={`ex-${idx}-trace`}>
<td colSpan={3} className="p-0">
<pre
className="m-0 p-3 bg-light text-muted"
style={{ fontSize: "0.75rem", whiteSpace: "pre-wrap" }}
>
{ex.StackTrace}
</pre>
</td>
</tr>
)}
</>
);
const probeAppId = isValidAppId ? appId : ALL_APP_IDS[0];

if (isAuthenticated) {
return <Navigate to={from ?? `/${appId}/versions`} replace />;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants