mirror of
https://github.com/Egonex-AI/Understand-Anything.git
synced 2026-06-22 10:58:03 +08:00
feat(dashboard): add node type category filter controls
Add nodeTypeFilters state and toggleNodeTypeFilter action to the Zustand store, apply category-based filtering in GraphView's useLayerDetailTopology, and render filter toggle buttons in the App header for Code, Config, Docs, Infra, and Data categories with colored indicator dots. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
78c8ce2602
commit
642626653c
@@ -72,6 +72,8 @@ function Dashboard({ accessToken }: { accessToken: string }) {
|
||||
const codeViewerOpen = useDashboardStore((s) => s.codeViewerOpen);
|
||||
const closeCodeViewer = useDashboardStore((s) => s.closeCodeViewer);
|
||||
const setDiffOverlay = useDashboardStore((s) => s.setDiffOverlay);
|
||||
const nodeTypeFilters = useDashboardStore((s) => s.nodeTypeFilters);
|
||||
const toggleNodeTypeFilter = useDashboardStore((s) => s.toggleNodeTypeFilter);
|
||||
const [loadError, setLoadError] = useState<string | null>(null);
|
||||
const [graphIssues, setGraphIssues] = useState<GraphIssue[]>([]);
|
||||
const [showKeyboardHelp, setShowKeyboardHelp] = useState(false);
|
||||
@@ -250,6 +252,35 @@ function Dashboard({ accessToken }: { accessToken: string }) {
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<DiffToggle />
|
||||
<div className="flex items-center gap-1">
|
||||
{([
|
||||
{ key: "code", label: "Code", color: "var(--color-node-file)" },
|
||||
{ key: "config", label: "Config", color: "var(--color-node-config)" },
|
||||
{ key: "docs", label: "Docs", color: "var(--color-node-document)" },
|
||||
{ key: "infra", label: "Infra", color: "var(--color-node-service)" },
|
||||
{ key: "data", label: "Data", color: "var(--color-node-table)" },
|
||||
] as const).map((cat) => (
|
||||
<button
|
||||
key={cat.key}
|
||||
onClick={() => toggleNodeTypeFilter(cat.key)}
|
||||
className={`text-[10px] font-semibold uppercase tracking-wider px-2 py-1 rounded border transition-colors flex items-center gap-1.5 ${
|
||||
nodeTypeFilters[cat.key] !== false
|
||||
? "border-border-medium bg-elevated text-text-secondary hover:text-text-primary"
|
||||
: "border-transparent bg-transparent text-text-muted/40 line-through hover:text-text-muted"
|
||||
}`}
|
||||
title={`${nodeTypeFilters[cat.key] !== false ? "Hide" : "Show"} ${cat.label} nodes`}
|
||||
>
|
||||
<span
|
||||
className="w-2 h-2 rounded-full shrink-0"
|
||||
style={{
|
||||
backgroundColor: cat.color,
|
||||
opacity: nodeTypeFilters[cat.key] !== false ? 1 : 0.3,
|
||||
}}
|
||||
/>
|
||||
{cat.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<LayerLegend />
|
||||
<ThemePicker />
|
||||
<button
|
||||
|
||||
@@ -200,6 +200,7 @@ function useLayerDetailTopology() {
|
||||
const changedNodeIds = useDashboardStore((s) => s.changedNodeIds);
|
||||
const affectedNodeIds = useDashboardStore((s) => s.affectedNodeIds);
|
||||
const focusNodeId = useDashboardStore((s) => s.focusNodeId);
|
||||
const nodeTypeFilters = useDashboardStore((s) => s.nodeTypeFilters);
|
||||
const drillIntoLayer = useDashboardStore((s) => s.drillIntoLayer);
|
||||
|
||||
const handleNodeSelect = useCallback(
|
||||
@@ -218,6 +219,15 @@ function useLayerDetailTopology() {
|
||||
|
||||
const layerNodeIds = new Set(activeLayer.nodeIds);
|
||||
|
||||
// Map node types to filter categories
|
||||
const nodeTypeToCategory: Record<string, string> = {
|
||||
file: "code", function: "code", class: "code", module: "code", concept: "code",
|
||||
config: "config",
|
||||
document: "docs",
|
||||
service: "infra", resource: "infra", pipeline: "infra",
|
||||
table: "data", endpoint: "data", schema: "data",
|
||||
};
|
||||
|
||||
// Non-technical persona only sees concept/module/file nodes
|
||||
let filteredGraphNodes = persona === "non-technical"
|
||||
? graph.nodes.filter(
|
||||
@@ -225,6 +235,12 @@ function useLayerDetailTopology() {
|
||||
)
|
||||
: graph.nodes.filter((n) => layerNodeIds.has(n.id) && n.type === "file");
|
||||
|
||||
// Apply node type category filters
|
||||
filteredGraphNodes = filteredGraphNodes.filter((n) => {
|
||||
const category = nodeTypeToCategory[n.type] ?? "code";
|
||||
return nodeTypeFilters[category] !== false;
|
||||
});
|
||||
|
||||
let filteredNodeIds = new Set(filteredGraphNodes.map((n) => n.id));
|
||||
|
||||
let filteredGraphEdges = graph.edges.filter(
|
||||
@@ -360,7 +376,7 @@ function useLayerDetailTopology() {
|
||||
|
||||
const laid = applyDagreLayout(allFlowNodes, allFlowEdges, "TB", dims);
|
||||
return { nodes: laid.nodes, edges: laid.edges, portalNodes, portalEdges, filteredEdges: filteredGraphEdges };
|
||||
}, [graph, activeLayerId, persona, handleNodeSelect, diffMode, changedNodeIds, affectedNodeIds, focusNodeId, drillIntoLayer]);
|
||||
}, [graph, activeLayerId, persona, handleNodeSelect, diffMode, changedNodeIds, affectedNodeIds, focusNodeId, nodeTypeFilters, drillIntoLayer]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,6 +53,10 @@ interface DashboardStore {
|
||||
// Sidebar navigation history (stack of visited node IDs)
|
||||
nodeHistory: string[];
|
||||
|
||||
// Node type category filters
|
||||
nodeTypeFilters: Record<string, boolean>;
|
||||
toggleNodeTypeFilter: (category: string) => void;
|
||||
|
||||
setGraph: (graph: KnowledgeGraph) => void;
|
||||
selectNode: (nodeId: string | null) => void;
|
||||
navigateToNode: (nodeId: string) => void;
|
||||
@@ -125,6 +129,16 @@ export const useDashboardStore = create<DashboardStore>()((set, get) => ({
|
||||
focusNodeId: null,
|
||||
nodeHistory: [],
|
||||
|
||||
nodeTypeFilters: { code: true, config: true, docs: true, infra: true, data: true },
|
||||
|
||||
toggleNodeTypeFilter: (category) =>
|
||||
set((state) => ({
|
||||
nodeTypeFilters: {
|
||||
...state.nodeTypeFilters,
|
||||
[category]: !state.nodeTypeFilters[category],
|
||||
},
|
||||
})),
|
||||
|
||||
setGraph: (graph) => {
|
||||
const searchEngine = new SearchEngine(graph.nodes);
|
||||
const query = get().searchQuery;
|
||||
|
||||
Reference in New Issue
Block a user