From f982ab3222195fe5b45e7b6455fbd2130b82b284 Mon Sep 17 00:00:00 2001 From: hainenber Date: Mon, 5 Feb 2024 16:29:10 +0700 Subject: [PATCH 1/2] feat(ui): PoC for subgraphs of module components Signed-off-by: hainenber --- web/ui/src/Router.tsx | 1 - web/ui/src/hooks/componentInfo.tsx | 62 ++++++++++++++++++++++++ web/ui/src/pages/ComponentDetailPage.tsx | 4 +- web/ui/src/pages/Graph.tsx | 4 +- 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/web/ui/src/Router.tsx b/web/ui/src/Router.tsx index 8b9c0fcb61d0..8ab30d71881d 100644 --- a/web/ui/src/Router.tsx +++ b/web/ui/src/Router.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; import Navbar from './features/layout/Navbar'; diff --git a/web/ui/src/hooks/componentInfo.tsx b/web/ui/src/hooks/componentInfo.tsx index 4dc2015a9a3e..42d368912de6 100644 --- a/web/ui/src/hooks/componentInfo.tsx +++ b/web/ui/src/hooks/componentInfo.tsx @@ -33,3 +33,65 @@ export const useComponentInfo = ( return [components, setComponents]; }; + +/** + * useAllComponentInfo retrieves the list of all components (non-module and ) from the API. + * + * @param fromComponent The component requesting component info. Required for + * determining the proper list of components from the context of a module. + */ +export const useAllComponentInfo = ( + moduleID: string +): [ComponentInfo[], React.Dispatch>] => { + const [components, setComponents] = useState([]); + + useEffect( + function () { + const worker = async () => { + // Request is relative to the tag inside of . + const totalComponents: ComponentInfo[] = []; + + const resp = await fetch('./api/v0/web/components', { + cache: 'no-cache', + credentials: 'same-origin', + }); + + let fetchedComponents: ComponentInfo[] = await resp.json(); + let moduleComponentIds = new Array(); + for (const component of fetchedComponents) { + const { localID: moduleId } = component; + if (moduleId.startsWith('module')) { + moduleComponentIds.push(component); + } + totalComponents.push(component); + } + + // Fetch sub-components in `module.*` components + while (moduleComponentIds.length > 0) { + const newModuleComponentIds: ComponentInfo[] = []; + for (const { localID: moduleId } of moduleComponentIds) { + const resp = await fetch(`./api/v0/web/modules/${moduleId}/components`, { + cache: 'no-cache', + credentials: 'same-origin', + }); + fetchedComponents = (await resp.json()) as ComponentInfo[]; + for (const component of fetchedComponents) { + const { localID: moduleId } = component; + if (moduleId.startsWith('module')) { + newModuleComponentIds.push(component); + } + totalComponents.push(component); + } + } + moduleComponentIds = newModuleComponentIds; + } + + setComponents(totalComponents); + }; + worker().catch(console.error); + }, + [moduleID] + ); + + return [components, setComponents]; +}; diff --git a/web/ui/src/pages/ComponentDetailPage.tsx b/web/ui/src/pages/ComponentDetailPage.tsx index a0f52a9ebd55..1aa325c25072 100644 --- a/web/ui/src/pages/ComponentDetailPage.tsx +++ b/web/ui/src/pages/ComponentDetailPage.tsx @@ -34,9 +34,9 @@ const ComponentDetailPage: FC = () => { cache: 'no-cache', credentials: 'same-origin', }); - const moduleCompoents = (await moduleComponentsResp.json()) as ComponentInfo[]; + const moduleComponents = (await moduleComponentsResp.json()) as ComponentInfo[]; - data.moduleInfo = (data.moduleInfo || []).concat(moduleCompoents); + data.moduleInfo = (data.moduleInfo || []).concat(moduleComponents); } setComponent(data); diff --git a/web/ui/src/pages/Graph.tsx b/web/ui/src/pages/Graph.tsx index 617fa0bb64c7..8be4012681d2 100644 --- a/web/ui/src/pages/Graph.tsx +++ b/web/ui/src/pages/Graph.tsx @@ -2,10 +2,10 @@ import { faDiagramProject } from '@fortawesome/free-solid-svg-icons'; import { ComponentGraph } from '../features/graph/ComponentGraph'; import Page from '../features/layout/Page'; -import { useComponentInfo } from '../hooks/componentInfo'; +import { useAllComponentInfo } from '../hooks/componentInfo'; function Graph() { - const [components] = useComponentInfo(''); + const [components] = useAllComponentInfo(''); return ( From 601374f888cae175f71270f7147f389a85afa2ea Mon Sep 17 00:00:00 2001 From: hainenber Date: Fri, 16 Feb 2024 22:10:52 +0700 Subject: [PATCH 2/2] feat(web/ui): finalize graph for modular components Signed-off-by: hainenber --- CHANGELOG.md | 2 + web/ui/src/Router.tsx | 3 +- .../src/features/component/ComponentView.tsx | 7 +++ web/ui/src/hooks/componentInfo.tsx | 62 +------------------ web/ui/src/pages/Graph.tsx | 21 +++++-- 5 files changed, 29 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2aecab50d25..9bc8c33ff95c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ Main (unreleased) - Introduce the `remotecfg` service that enables loading configuration from a remote endpoint. (@tpaschalis) + +- Flow UI: Display graph for components defined in module. (@hainenber) ### Enhancements diff --git a/web/ui/src/Router.tsx b/web/ui/src/Router.tsx index 8ab30d71881d..0d2ebf3221d6 100644 --- a/web/ui/src/Router.tsx +++ b/web/ui/src/Router.tsx @@ -3,7 +3,7 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom'; import Navbar from './features/layout/Navbar'; import PageClusteringPeers from './pages/Clustering'; import ComponentDetailPage from './pages/ComponentDetailPage'; -import Graph from './pages/Graph'; +import { Graph, ModuleGraph } from './pages/Graph'; import PageComponentList from './pages/PageComponentList'; interface Props { @@ -19,6 +19,7 @@ const Router = ({ basePath }: Props) => { } /> } /> } /> + } /> } /> diff --git a/web/ui/src/features/component/ComponentView.tsx b/web/ui/src/features/component/ComponentView.tsx index bf97e187fea5..02aaa4890ac8 100644 --- a/web/ui/src/features/component/ComponentView.tsx +++ b/web/ui/src/features/component/ComponentView.tsx @@ -79,6 +79,13 @@ export const ComponentView: FC = (props) => { )} + {props.component.localID.startsWith('module') && ( +
  • + + Graph + +
  • + )} diff --git a/web/ui/src/hooks/componentInfo.tsx b/web/ui/src/hooks/componentInfo.tsx index 42d368912de6..a1c38a582fb8 100644 --- a/web/ui/src/hooks/componentInfo.tsx +++ b/web/ui/src/hooks/componentInfo.tsx @@ -34,64 +34,4 @@ export const useComponentInfo = ( return [components, setComponents]; }; -/** - * useAllComponentInfo retrieves the list of all components (non-module and ) from the API. - * - * @param fromComponent The component requesting component info. Required for - * determining the proper list of components from the context of a module. - */ -export const useAllComponentInfo = ( - moduleID: string -): [ComponentInfo[], React.Dispatch>] => { - const [components, setComponents] = useState([]); - - useEffect( - function () { - const worker = async () => { - // Request is relative to the tag inside of . - const totalComponents: ComponentInfo[] = []; - - const resp = await fetch('./api/v0/web/components', { - cache: 'no-cache', - credentials: 'same-origin', - }); - - let fetchedComponents: ComponentInfo[] = await resp.json(); - let moduleComponentIds = new Array(); - for (const component of fetchedComponents) { - const { localID: moduleId } = component; - if (moduleId.startsWith('module')) { - moduleComponentIds.push(component); - } - totalComponents.push(component); - } - - // Fetch sub-components in `module.*` components - while (moduleComponentIds.length > 0) { - const newModuleComponentIds: ComponentInfo[] = []; - for (const { localID: moduleId } of moduleComponentIds) { - const resp = await fetch(`./api/v0/web/modules/${moduleId}/components`, { - cache: 'no-cache', - credentials: 'same-origin', - }); - fetchedComponents = (await resp.json()) as ComponentInfo[]; - for (const component of fetchedComponents) { - const { localID: moduleId } = component; - if (moduleId.startsWith('module')) { - newModuleComponentIds.push(component); - } - totalComponents.push(component); - } - } - moduleComponentIds = newModuleComponentIds; - } - - setComponents(totalComponents); - }; - worker().catch(console.error); - }, - [moduleID] - ); - - return [components, setComponents]; -}; +export default useComponentInfo; \ No newline at end of file diff --git a/web/ui/src/pages/Graph.tsx b/web/ui/src/pages/Graph.tsx index 8be4012681d2..ddea37a0d4ae 100644 --- a/web/ui/src/pages/Graph.tsx +++ b/web/ui/src/pages/Graph.tsx @@ -1,11 +1,13 @@ +import { useParams } from 'react-router-dom'; import { faDiagramProject } from '@fortawesome/free-solid-svg-icons'; import { ComponentGraph } from '../features/graph/ComponentGraph'; import Page from '../features/layout/Page'; -import { useAllComponentInfo } from '../hooks/componentInfo'; +import { useComponentInfo } from '../hooks/componentInfo'; +import { parseID } from '../utils/id'; -function Graph() { - const [components] = useAllComponentInfo(''); +export function Graph() { + const [components] = useComponentInfo(''); return ( @@ -14,4 +16,15 @@ function Graph() { ); } -export default Graph; +export function ModuleGraph() { + const { '*': id } = useParams(); + + const { localID } = parseID(id || ''); + const [components] = useComponentInfo(localID); + + return ( + + {components.length > 0 && } + + ); +}