Skip to content

Add routing map preview for GitHub and Linear#13

Open
5oir5bif wants to merge 2 commits into
arcboxlabs:mainfrom
5oir5bif:5oir5bif/routing-map-preview
Open

Add routing map preview for GitHub and Linear#13
5oir5bif wants to merge 2 commits into
arcboxlabs:mainfrom
5oir5bif:5oir5bif/routing-map-preview

Conversation

@5oir5bif

@5oir5bif 5oir5bif commented Jul 3, 2026

Copy link
Copy Markdown

Summary

  • add React Flow dependency for routing map visualization
  • add read-only RoutingMap component for GitHub and Linear
  • convert routing config into source/event/rule/destination graph nodes
  • keep GitLab unchanged via explicit showRoutingMap flag

Verification

  • pnpm build
  • tsc -b
  • vite build

Copilot AI review requested due to automatic review settings July 3, 2026 13:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a read-only routing map preview to the dashboard’s routing editor (enabled for GitHub and Linear) by introducing an @xyflow/react-based visualization that renders routing rules as a source → event → rule → destination graph.

Changes:

  • Add an optional showRoutingMap flag to RoutingEditor and enable it for GitHub and Linear tabs.
  • Introduce RoutingMap + buildRoutingGraph to convert routing config into React Flow nodes/edges and render a preview.
  • Add React Flow styles and dashboard CSS to style the routing map UI.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
dashboard/src/tabs/Linear.tsx Enables routing map preview for Linear by passing showRoutingMap.
dashboard/src/tabs/GitHub.tsx Enables routing map preview for GitHub by passing showRoutingMap.
dashboard/src/components/RoutingEditor.tsx Adds showRoutingMap prop and renders RoutingMap above the rules section.
dashboard/src/components/RoutingMap.tsx New React Flow-based component for rendering the routing graph preview.
dashboard/src/components/routingGraph.ts New graph builder to map routing config into nodes/edges (events, rules, defaults, destinations).
dashboard/src/main.tsx Imports React Flow base stylesheet globally.
dashboard/src/styles.css Adds styling for routing map container, nodes, edges, minimap, and controls.
dashboard/package.json Adds @xyflow/react dependency.
dashboard/pnpm-lock.yaml Locks @xyflow/react and transitive dependencies.
Files not reviewed (1)
  • dashboard/pnpm-lock.yaml: Generated file

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

defaultViewport={{ x: 80, y: 70, zoom: 0.92 }}
minZoom={0.35}
maxZoom={1.35}
nodesDraggable
Comment on lines +217 to +220
if (routing.rules.length === 0) {
addNode("no-rules", "drop", "No rules", "unmatched events use defaults", 0);
addEdge(sourceId, "no-rules");
}
@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds @xyflow/react-based routing map visualization to the dashboard, converting each app's routing config into a four-column source → event → rule → destination graph. The new RoutingMap component and routingGraph.ts builder are well-structured, but the integration wiring has a significant problem.

  • RoutingEditor removed from all three tabs: GitHub, GitLab, and Linear now render RoutingMapPanel (read-only view) instead of RoutingEditor, silently removing the UI surface for creating/editing routing rules, destinations, the reviewer user map, and alert labels.
  • showRoutingMap flag is dead code: The flag was added to RoutingEditor to optionally embed the map inside the editor, but no tab passes showRoutingMap={true} because RoutingEditor is no longer used — the intended integration point was never wired up.
  • GitLab contradicts the PR description: The PR states GitLab should "keep unchanged via the showRoutingMap flag", but Gitlab.tsx was changed to RoutingMapPanel just like the other tabs.

Confidence Score: 3/5

Not safe to merge as-is — all three integration tabs lose their routing configuration UI.

The RoutingEditor — the only UI surface for configuring routing rules, destinations, user maps, and alert labels — has been replaced by a read-only RoutingMapPanel in every integration tab. Admins on this build can no longer create or modify routing rules from the dashboard. Additionally, showRoutingMap was added to RoutingEditor as the intended embedding hook but is never activated, leaving it as dead code. GitLab is also affected despite the PR stating it should remain unchanged.

dashboard/src/tabs/GitHub.tsx, dashboard/src/tabs/Gitlab.tsx, and dashboard/src/tabs/Linear.tsx — all three replaced the routing editor with a read-only panel.

Important Files Changed

Filename Overview
dashboard/src/tabs/GitHub.tsx RoutingEditor replaced with read-only RoutingMapPanel, silently removing the ability to configure routing rules from the dashboard UI.
dashboard/src/tabs/Gitlab.tsx Same RoutingEditor→RoutingMapPanel swap as GitHub despite PR description explicitly saying GitLab should be kept unchanged.
dashboard/src/tabs/Linear.tsx RoutingEditor (with showUserMap/showAlertLabels=false) replaced by RoutingMapPanel; routing configuration UI removed for Linear.
dashboard/src/components/RoutingMap.tsx New read-only React Flow component; memoizes graph computation and manages node/edge state via useEffect pairs.
dashboard/src/components/RoutingMapPanel.tsx Standalone panel that fetches routing/chats/users via SWR and renders RoutingMap; error/loading states handled correctly.
dashboard/src/components/routingGraph.ts Pure graph-builder function: normalizes rules/events/destinations into React Flow nodes and edges with stable dedup logic.
dashboard/src/components/RoutingEditor.tsx Added showRoutingMap prop that conditionally renders RoutingMap inline, but the prop is never set to true by any caller in this PR.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Tab as GitHub/Linear/GitLab Tab
    participant RMP as RoutingMapPanel
    participant RM as RoutingMap
    participant RG as routingGraph.ts
    participant API as /api/apps/{app}/routing

    Tab->>RMP: render(appName, eventOptions)
    RMP->>API: useSWR(routing)
    RMP->>API: useSWR(chats) [shouldRetryOnError:false]
    RMP->>API: useSWR(users) [shouldRetryOnError:false]
    API-->>RMP: RoutingConfig / chats / users
    RMP->>RM: render(routing, eventOptions, chats, users)
    RM->>RG: buildRoutingGraph(args) [useMemo]
    RG-->>RM: "{ nodes, edges }"
    RM->>RM: map to flowNodes/flowEdges [useMemo]
    RM->>RM: setGraphNodes/setGraphEdges [useEffect]
    RM-->>Tab: ReactFlow canvas (read-only)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Tab as GitHub/Linear/GitLab Tab
    participant RMP as RoutingMapPanel
    participant RM as RoutingMap
    participant RG as routingGraph.ts
    participant API as /api/apps/{app}/routing

    Tab->>RMP: render(appName, eventOptions)
    RMP->>API: useSWR(routing)
    RMP->>API: useSWR(chats) [shouldRetryOnError:false]
    RMP->>API: useSWR(users) [shouldRetryOnError:false]
    API-->>RMP: RoutingConfig / chats / users
    RMP->>RM: render(routing, eventOptions, chats, users)
    RM->>RG: buildRoutingGraph(args) [useMemo]
    RG-->>RM: "{ nodes, edges }"
    RM->>RM: map to flowNodes/flowEdges [useMemo]
    RM->>RM: setGraphNodes/setGraphEdges [useEffect]
    RM-->>Tab: ReactFlow canvas (read-only)
Loading

Reviews (2): Last reviewed commit: "Add routing map panel for GitHub and Lin..." | Re-trigger Greptile

Comment on lines +83 to +94
const [graphNodes, setGraphNodes, onNodesChange] =
useNodesState<RoutingGraphNode>([]);
const [graphEdges, setGraphEdges, onEdgesChange] =
useEdgesState<RoutingGraphEdge>([]);

useEffect(() => {
setGraphNodes(flowNodes);
}, [flowNodes, setGraphNodes]);

useEffect(() => {
setGraphEdges(flowEdges);
}, [flowEdges, setGraphEdges]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Blank-canvas flash on every mount

useNodesState([]) / useEdgesState([]) initialise with empty arrays. Because RoutingMap only renders after the if (!edit) return <Spinner /> guard in RoutingEditor, flowNodes and flowEdges are already non-empty on the very first render — but React Flow still receives the empty initial state on that first pass and is populated only once the two useEffects fire. This produces a visible blank grey canvas between the spinner disappearing and the graph appearing. Initialize with the computed values directly so the first render is already populated. The useEffects are still needed for subsequent updates (user edits, chat/user data arriving), but the initial flash is eliminated.

Comment on lines +72 to +79
className:
edge.data?.kind === "default"
? "routing-map-edge default"
: "routing-map-edge",
style:
edge.data?.kind === "default"
? { strokeDasharray: "6 5" }
: undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The strokeDasharray for default edges is set twice: via the inline style prop here and via the .routing-map-edge.default CSS rule in styles.css. Inline styles take precedence over class rules in SVG, so the CSS rule is dead code. Remove the duplicate inline assignment to keep the styling canonical to CSS.

Suggested change
className:
edge.data?.kind === "default"
? "routing-map-edge default"
: "routing-map-edge",
style:
edge.data?.kind === "default"
? { strokeDasharray: "6 5" }
: undefined,
className:
edge.data?.kind === "default"
? "routing-map-edge default"
: "routing-map-edge",

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@AprilNEA AprilNEA left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The graph-building logic in routingGraph.ts is solid — normalization, dedup, and row allocation all check out. But the wiring contradicts the PR description and removes existing functionality, so this can't merge as-is.

The intended shape per your own description seems to be: keep RoutingEditor in all three tabs, pass showRoutingMap for GitHub/Linear only, and drop RoutingMapPanel. Details inline.

<h2>GitHub</h2>
<LarkBinding appName="github" />
<RoutingEditor appName="github" eventOptions={GITHUB_EVENTS} />
<RoutingMapPanel appName="github" eventOptions={GITHUB_EVENTS} />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: RoutingEditor is replaced wholesale by the read-only RoutingMapPanel (same in Gitlab.tsx and Linear.tsx), so after merge there is no way to add/remove rules, edit default destinations, the reviewer user map, or alert labels — no Save button anywhere. This contradicts the PR description ("add read-only RoutingMap component", "keep GitLab unchanged via explicit showRoutingMap flag"): GitLab is changed too, and nothing is additive. Looks like a bad rebase or mistaken commit — this should be <RoutingEditor appName="github" eventOptions={GITHUB_EVENTS} showRoutingMap />.

<h2>GitLab</h2>
<LarkBinding appName="gitlab" />
<RoutingEditor appName="gitlab" eventOptions={GITLAB_EVENTS} />
<RoutingMapPanel appName="gitlab" eventOptions={GITLAB_EVENTS} />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Per the PR description GitLab should be unchanged — restore RoutingEditor here without showRoutingMap.

showUserMap={false}
showAlertLabels={false}
/>
<RoutingMapPanel appName="linear" eventOptions={LINEAR_EVENTS} />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Restore RoutingEditor with its previous showUserMap={false} showAlertLabels={false} props plus showRoutingMap — the read-only panel drops all editing (rules, defaults, save).

* label-triggered alerts (e.g. Linear). */
showAlertLabels?: boolean;
/** Show the read-only routing map preview (default `false`). */
showRoutingMap?: boolean;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

showRoutingMap is dead code at this head: no file imports RoutingEditor anymore, so this flag and the embedded map section are unreachable. Wiring the tabs back to RoutingEditor (see tab comments) makes this the live path.


export type RoutingGraphDestinationKind = "chat" | "dm";

export interface RoutingGraphDestination {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These duplicate the wire types already defined in RoutingEditor.tsx (Destination/Rule/RoutingConfig/EventOption/Chat/User, all mirroring lark_kit::routing::Config). Two parallel type sets only stay compatible by structural luck and will drift when the backend shape changes — export one shared set (from RoutingEditor.tsx or a small shared module) and import it here.

eventOptions,
}: RoutingMapPanelProps) {
const url = `/api/apps/${appName}/routing`;
const { data: routing, error } = useSWR<RoutingGraphConfig>(url);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This re-implements RoutingEditor's SWR fetching of /routing, /chats, /users and its error/spinner handling. Once the map is embedded in the editor behind showRoutingMap, this component is redundant — delete it.

);

rule.events.forEach((event) => {
addEdge(eventIds.get(event) ?? eventNodeId(event, 0), ruleId);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The ?? eventNodeId(event, 0) fallback would emit an edge pointing at a nonexistent node (React Flow silently drops it). It's currently unreachable — eventValues is built from every normalized rule's events, so eventIds.get can't miss. Remove the fallback rather than keeping a dead branch that hides a future bug.

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.

3 participants