diff --git a/console/client/src/App.tsx b/console/client/src/App.tsx index 2261e25017..1d05919545 100644 --- a/console/client/src/App.tsx +++ b/console/client/src/App.tsx @@ -2,8 +2,6 @@ import { Navigate, Route, Routes } from 'react-router-dom' import { DeploymentPage } from './features/deployments/DeploymentPage.tsx' import { DeploymentsPage } from './features/deployments/DeploymentsPage.tsx' import { GraphPage } from './features/graph/GraphPage.tsx' -import { ModulePage } from './features/modules/ModulePage.tsx' -import { ModulesPage } from './features/modules/ModulesPage.tsx' import { TimelinePage } from './features/timeline/TimelinePage.tsx' import { VerbPage } from './features/verbs/VerbPage.tsx' import { Layout } from './layout/Layout.tsx' @@ -15,12 +13,9 @@ export const App = () => { } /> } /> - } /> - } /> - } /> - } /> } /> + } /> } /> diff --git a/console/client/src/components/Badge.tsx b/console/client/src/components/Badge.tsx new file mode 100644 index 0000000000..26f641a1aa --- /dev/null +++ b/console/client/src/components/Badge.tsx @@ -0,0 +1,9 @@ +export const Badge = ({ name, className }: { name: string; className?: string }) => { + return ( + + {name} + + ) +} diff --git a/console/client/src/components/ButtonExtraSmall.tsx b/console/client/src/components/ButtonExtraSmall.tsx new file mode 100644 index 0000000000..cbb27d13a7 --- /dev/null +++ b/console/client/src/components/ButtonExtraSmall.tsx @@ -0,0 +1,17 @@ +interface Props { + children: React.ReactNode + onClick?: () => void + className?: string +} + +export const ButtonExtraSmall = ({ children, onClick }: Props) => { + return ( + + ) +} diff --git a/console/client/src/features/deployments/DeploymentCard.tsx b/console/client/src/features/deployments/DeploymentCard.tsx index 63e0b6482f..9c71c21d32 100644 --- a/console/client/src/features/deployments/DeploymentCard.tsx +++ b/console/client/src/features/deployments/DeploymentCard.tsx @@ -1,13 +1,43 @@ +import { useContext, useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' +import { Badge } from '../../components/Badge' import { Card } from '../../components/Card' +import { Module } from '../../protos/xyz/block/ftl/v1/console/console_pb' +import { modulesContext } from '../../providers/modules-provider' import { deploymentTextColor } from './deployment.utils' -export const DeploymentCard = ({ name, className }: { name: string; className?: string }) => { +export const DeploymentCard = ({ deploymentName, className }: { deploymentName: string; className?: string }) => { const navigate = useNavigate() + const { modules } = useContext(modulesContext) + const [module, setModule] = useState() + + useEffect(() => { + if (modules) { + const module = modules.find((module) => module.deploymentName === deploymentName) + setModule(module) + } + }, [modules]) + return ( - navigate(`/deployments/${name}`)}> - {name} -

{name}

+ navigate(`/deployments/${deploymentName}`)} + > +
+
+

{deploymentName}

+ +
+
+ {module?.verbs.map((verb, index) => ( + + {verb.verb?.name} + + ))} +
+
) } diff --git a/console/client/src/features/deployments/DeploymentPage.tsx b/console/client/src/features/deployments/DeploymentPage.tsx index 31aa48528c..39e9168a2d 100644 --- a/console/client/src/features/deployments/DeploymentPage.tsx +++ b/console/client/src/features/deployments/DeploymentPage.tsx @@ -48,6 +48,13 @@ export const DeploymentPage = () => { setCalls(Array.from(verbCalls)) }, [module]) + const handleCallClick = (verb: VerbRef) => { + const module = modules?.modules.find((module) => module.name === verb.module) + if (module) { + navigate(`/deployments/${module.deploymentName}/verbs/${verb.name}`) + } + } + return ( {
{module?.verbs.map((verb) => ( navigate(`/modules/${module.name}/verbs/${verb.verb?.name}`)} + onClick={() => navigate(`/deployments/${module.deploymentName}/verbs/${verb.verb?.name}`)} > {verb.verb?.name}

{verb.verb?.name}

@@ -74,9 +81,7 @@ export const DeploymentPage = () => {
    {calls?.map((verb) => (
  • - navigate(`/modules/${verb.module}/verbs/${verb.name}`)}> - {verbRefString(verb)} - + handleCallClick(verb)}>{verbRefString(verb)}
  • ))}
diff --git a/console/client/src/features/deployments/DeploymentsPage.tsx b/console/client/src/features/deployments/DeploymentsPage.tsx index 1d993d5322..8189090b0a 100644 --- a/console/client/src/features/deployments/DeploymentsPage.tsx +++ b/console/client/src/features/deployments/DeploymentsPage.tsx @@ -13,7 +13,7 @@ export const DeploymentsPage = () => {
{modules.modules.map((module) => ( - + ))}
diff --git a/console/client/src/features/modules/ModulePage.tsx b/console/client/src/features/modules/ModulePage.tsx deleted file mode 100644 index 39377fe0ef..0000000000 --- a/console/client/src/features/modules/ModulePage.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { Square3Stack3DIcon } from '@heroicons/react/24/outline' -import { useContext, useEffect, useState } from 'react' -import { useNavigate, useParams } from 'react-router-dom' -import { Card } from '../../components/Card' -import { Page } from '../../layout' -import { CallEvent, Module } from '../../protos/xyz/block/ftl/v1/console/console_pb' -import { modulesContext } from '../../providers/modules-provider' -import { SidePanelProvider } from '../../providers/side-panel-provider' -import { getCalls } from '../../services/console.service' -import { CallList } from '../calls/CallList' - -export const ModulePage = () => { - const navigate = useNavigate() - const { moduleName } = useParams() - const modules = useContext(modulesContext) - const [module, setModule] = useState() - const [calls, setCalls] = useState() - - useEffect(() => { - if (modules) { - const module = modules.modules.find((module) => module.name === moduleName?.toLocaleLowerCase()) - setModule(module) - } - }, [modules, moduleName]) - - useEffect(() => { - const abortController = new AbortController() - if (!module) return - - const fetchCalls = async () => { - const calls = await getCalls({ abortControllerSignal: abortController.signal, destModule: module.name }) - setCalls(calls) - } - fetchCalls() - - return () => { - abortController.abort() - } - }, [module]) - - return ( - - - } - title={module?.name || ''} - breadcrumbs={[{ label: 'Modules', link: '/modules' }]} - /> - -
-
-
- {module?.verbs.map((verb) => ( - navigate(`/modules/${module.name}/verbs/${verb.verb?.name}`)} - > - {verb.verb?.name} -

{verb.verb?.name}

-
- ))} -
-
-
- -
-
-
-
-
- ) -} diff --git a/console/client/src/features/modules/ModulesGraph.tsx b/console/client/src/features/modules/ModulesGraph.tsx deleted file mode 100644 index 2645dc0677..0000000000 --- a/console/client/src/features/modules/ModulesGraph.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { ArrowPathIcon, MinusCircleIcon, PlusCircleIcon } from '@heroicons/react/24/outline' -import React, { useContext, useEffect, useRef, useState } from 'react' -import { modulesContext } from '../../providers/modules-provider' -import { Panel } from './components' -import { dotToSVG, formatSVG, generateDot, svgZoom } from './graph' -import { ZoomCallbacks } from './modules.constants' - -export const ModulesGraph: React.FC<{ - className?: string - setZoomCallbacks: React.Dispatch> - zoomCallbacks?: ZoomCallbacks -}> = ({ className, setZoomCallbacks, zoomCallbacks }) => { - const modules = useContext(modulesContext) - const canvasRef = useRef(null) - const [resizeCount, setResizeCount] = useState(0) - const previousDimensions = useRef({ width: 0, height: 0 }) // Store previous dimensions - - useEffect(() => { - const canvasCur = canvasRef.current - if (canvasCur) { - const observer = new ResizeObserver((entries) => { - for (const entry of entries) { - const { width, height } = entry.contentRect - // Check if dimensions have changed - if (width !== previousDimensions.current.width || height !== previousDimensions.current.height) { - setResizeCount((n) => n + 1) - // Update previous dimensions - previousDimensions.current = { width, height } - } - } - }) - observer.observe(canvasCur) - return () => { - observer.disconnect() - } - } - }, [canvasRef]) - - useEffect(() => { - const canvas = canvasRef.current - const ready = canvas && Boolean(modules) - let animationFrameId: number - const renderSvg = async () => { - const dot = generateDot(modules) - const data = await dotToSVG(dot) - if (data) { - if (animationFrameId) { - cancelAnimationFrame(animationFrameId) - } - animationFrameId = requestAnimationFrame(() => { - const unformattedSVG = data - const formattedSVG = formatSVG(unformattedSVG) - canvas?.replaceChildren(formattedSVG) - const zoom = svgZoom(formattedSVG, canvas?.clientWidth ?? 0, canvas?.clientHeight ?? 0) - setZoomCallbacks(zoom) - }) - } - } - ready && void renderSvg() - }, [modules, resizeCount]) - - return ( - - -
- - - - - - - - ) -} diff --git a/console/client/src/features/modules/ModulesPage.tsx b/console/client/src/features/modules/ModulesPage.tsx deleted file mode 100644 index b7512be7d0..0000000000 --- a/console/client/src/features/modules/ModulesPage.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { Square3Stack3DIcon } from '@heroicons/react/24/outline' -import { useContext, useState } from 'react' -import { Page } from '../../layout' -import { modulesContext } from '../../providers/modules-provider' -import { SidePanelProvider } from '../../providers/side-panel-provider' -import { classNames } from '../../utils' -import { ModulesGraph } from './ModulesGraph' -import { ModulesRequests } from './ModulesRequests' -import { ModulesSelectedVerbs } from './ModulesSelectedVerbs' -import { ModulesSidebar } from './ModulesSidebar' -import type { ZoomCallbacks } from './modules.constants' -import { VerbId } from './modules.constants' - -export const ModulesPage = () => { - const { modules } = useContext(modulesContext) - const [selectedVerbs, setSelectedVerbs] = useState([]) - const hasVerbs = Boolean(selectedVerbs.length) - const [zoomCallbacks, setZoomCallbacks] = useState() - - return ( - - - } title='Modules' /> - - -
- - - {hasVerbs && ( - - )} -
-
-
-
- ) -} diff --git a/console/client/src/features/modules/ModulesRequests.tsx b/console/client/src/features/modules/ModulesRequests.tsx deleted file mode 100644 index 6cc6a248de..0000000000 --- a/console/client/src/features/modules/ModulesRequests.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { Timestamp } from '@bufbuild/protobuf' -import { useContext, useEffect, useState } from 'react' -import { useSearchParams } from 'react-router-dom' -import { Event, EventsQuery_Filter, Module } from '../../protos/xyz/block/ftl/v1/console/console_pb' -import { SidePanelContext } from '../../providers/side-panel-provider.tsx' -import { modulesFilter, streamEvents } from '../../services/console.service' -import { formatTimestampShort } from '../../utils/date.utils.ts' -import { panelColor } from '../../utils/style.utils.ts' -import { TimelineCall } from '../timeline/TimelineCall.tsx' -import { TimelineDeploymentCreated } from '../timeline/TimelineDeploymentCreated.tsx' -import { TimelineDeploymentUpdated } from '../timeline/TimelineDeploymentUpdated.tsx' -import { TimelineIcon } from '../timeline/TimelineIcon.tsx' -import { TimelineLog } from '../timeline/TimelineLog.tsx' -import { TimelineCallDetails } from '../timeline/details/TimelineCallDetails.tsx' -import { TimelineDeploymentCreatedDetails } from '../timeline/details/TimelineDeploymentCreatedDetails.tsx' -import { TimelineDeploymentUpdatedDetails } from '../timeline/details/TimelineDeploymentUpdatedDetails.tsx' -import { TimelineLogDetails } from '../timeline/details/TimelineLogDetails.tsx' -import { Panel } from './components' - -const maxTimelineEntries = 1000 - -export const ModulesRequests = ({ className, modules }: { className?: string; modules: Module[] }) => { - const [searchParams, setSearchParams] = useSearchParams() - const [entries, setEntries] = useState([]) - const { openPanel, closePanel } = useContext(SidePanelContext) - const [selectedEntry, setSelectedEntry] = useState(null) - const deployments = modules.map(({ deploymentName }) => deploymentName) - - const filters: EventsQuery_Filter[] = [] - if (deployments.length) { - filters.push(modulesFilter(deployments)) - } - - useEffect(() => { - setEntries([]) - if (!filters.length) return - const abortController = new AbortController() - abortController.signal - streamEvents({ - abortControllerSignal: abortController.signal, - filters, - onEventReceived: (event) => { - setEntries((prev) => [event, ...prev].slice(0, maxTimelineEntries)) - }, - }) - return () => { - abortController.abort() - } - }, [modules]) - const handleEntryClicked = (entry: Event) => { - if (selectedEntry === entry) { - setSelectedEntry(null) - closePanel() - const newParams = new URLSearchParams(searchParams.toString()) - newParams.delete('id') - setSearchParams(newParams) - return - } - - switch (entry.entry?.case) { - case 'call': - openPanel() - break - case 'log': - openPanel() - break - case 'deploymentCreated': - openPanel() - break - case 'deploymentUpdated': - openPanel() - break - default: - break - } - setSelectedEntry(entry) - setSearchParams({ ...Object.fromEntries(searchParams.entries()), id: entry.id.toString() }) - } - return ( - - - - Date - Content - - - {entries.map((entry) => ( -
handleEntryClicked(entry)} - > - - - - - {formatTimestampShort(entry.timeStamp)} - - - {(() => { - switch (entry.entry?.case) { - case 'call': - return - case 'log': - return - case 'deploymentCreated': - return - case 'deploymentUpdated': - return - } - })()} - -
- ))} -
-
- ) -} diff --git a/console/client/src/features/modules/ModulesSelectedVerbs.tsx b/console/client/src/features/modules/ModulesSelectedVerbs.tsx deleted file mode 100644 index 61f4698c93..0000000000 --- a/console/client/src/features/modules/ModulesSelectedVerbs.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { Tab } from '@headlessui/react' -import React from 'react' -import { CodeBlock } from '../../components' -import { Data, Module, Verb } from '../../protos/xyz/block/ftl/v1/console/console_pb' -import { classNames } from '../../utils' -import { VerbForm } from '../verbs/VerbForm' -import { Panel } from './components' -import { VerbId } from './modules.constants' -import { buildVerbSchema, getNames } from './modules.utils' - -export const ModulesSelectedVerbs = ({ - className, - modules, - selectedVerbs, -}: { - className?: string - modules: Module[] - selectedVerbs?: VerbId[] -}) => { - if (!selectedVerbs?.length) return <> - const verbs: { module: Module; verb: Verb; callData: Data[] }[] = [] - for (const verbId of selectedVerbs) { - const [moduleName, verbName] = getNames(verbId) - const module = modules.find((module) => module?.name === moduleName) - const verb = module?.verbs.find((v) => v.verb?.name === verbName) - const callData = - module?.data.filter((data) => - [verb?.verb?.request?.name, verb?.verb?.response?.name].includes(data.data?.name), - ) ?? [] - if (verb && module) verbs.push({ module, verb, callData }) - } - return ( - - - - - {verbs.map(({ verb, module }) => { - const name = verb.verb?.name - const id = `${module.name}.${name}` - return ( - - {({ selected }) => ( - - )} - - ) - })} - - - - {verbs.map(({ module, verb, callData }) => ( - - d.schema), - )} - language='graphql' - /> - - - ))} - - - - ) -} diff --git a/console/client/src/features/modules/ModulesSidebar.tsx b/console/client/src/features/modules/ModulesSidebar.tsx deleted file mode 100644 index 1d0f01a144..0000000000 --- a/console/client/src/features/modules/ModulesSidebar.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import { Listbox } from '@headlessui/react' -import { EyeIcon } from '@heroicons/react/20/solid' -import React, { useState } from 'react' -import { Module } from '../../protos/xyz/block/ftl/v1/console/console_pb' -import { classNames } from '../../utils' -import { backgrounds, borders, colors } from './components' -import { VerbId, ZoomCallbacks } from './modules.constants' -import { getNames } from './modules.utils' - -interface DeploymentVerbs { - deploymentName: string - id: string - verbs: Set - queriedVerbs: Set -} - -const ModulesOption = ({ - id, - zoomCallbacks, - verbs, - deploymentName, -}: { - id: string - verbs: VerbId[] - deploymentName: string - zoomCallbacks?: ZoomCallbacks -}) => { - return ( -
  • -
    -
    - {id} - - - {deploymentName} - -
      - {verbs - .sort((a, b) => Intl.Collator('en').compare(a, b)) - .map((verb) => ( - - {({ selected }) => ( -
      - - {getNames(verb)[1]} - -
      - )} -
      - ))} -
    -
    -
  • - ) -} - -export const ModulesSidebar: React.FC<{ - className?: string - modules: Module[] - setSelectedVerbs: React.Dispatch> - selectedVerbs: VerbId[] - zoomCallbacks?: ZoomCallbacks -}> = ({ className, modules, setSelectedVerbs, selectedVerbs, zoomCallbacks }) => { - const [query, setQuery] = useState('') - - const map: Map = new Map() - for (const { name: moduleName, verbs, deploymentName } of modules) { - const value: DeploymentVerbs = { - id: moduleName, - deploymentName, - verbs: new Set(), - queriedVerbs: new Set(), - } - for (const { verb } of verbs) { - verb && value.verbs.add(`${moduleName}.${verb.name}`) - } - map.set(moduleName, value) - } - const options = [...map.values()] - const filteredOptions = - query === '' - ? options - : options.reduce((acc, option) => { - option.queriedVerbs.clear() - let found = option.id.toLowerCase().includes(query.toLowerCase()) - const queriedVerbs: Set = new Set( - [...option.verbs].filter((verb) => verb.toLowerCase().includes(query.toLowerCase())), - ) - if (!found) { - found = Boolean(queriedVerbs.size) - } - if (!found) return acc - option.queriedVerbs = queriedVerbs - acc.push(option) - return acc - }, []) - const handleChange: React.ChangeEventHandler = (event) => { - setSelectedVerbs([]) - setQuery(event.target.value) - } - return ( -
    -
    - -
    - - - {filteredOptions - .sort((a, b) => Intl.Collator('en').compare(a.id, b.id)) - .map(({ deploymentName, id, queriedVerbs, verbs }) => { - const displayedVerbs = query === '' ? verbs : queriedVerbs - return ( - - ) - })} - - -
    - ) -} diff --git a/console/client/src/features/modules/components/Panel.tsx b/console/client/src/features/modules/components/Panel.tsx deleted file mode 100644 index 3b0f762977..0000000000 --- a/console/client/src/features/modules/components/Panel.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react' -import { classNames } from '../../../utils' -import { backgrounds, borders } from './components.constants' - -const Header: React.FC<{ - className?: string - children?: React.ReactNode - style?: React.CSSProperties -}> = ({ className, children, style }) => { - return ( -
    - {children} -
    - ) -} - -const Body: React.FC<{ - className?: string - children?: React.ReactNode - style?: React.CSSProperties -}> = ({ className, children, style }) => { - return ( -
    - {children} -
    - ) -} - -export const Panel: React.FC<{ - className?: string - children?: React.ReactNode - style?: React.CSSProperties -}> & { - Header: typeof Header - Body: typeof Body -} = ({ className, children, style }) => { - return ( -
    - {children} -
    - ) -} - -Panel.Body = Body -Panel.Header = Header diff --git a/console/client/src/features/modules/components/components.constants.ts b/console/client/src/features/modules/components/components.constants.ts deleted file mode 100644 index 7e5d35f375..0000000000 --- a/console/client/src/features/modules/components/components.constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const backgrounds = { - level1: `bg-gray-200 dark:bg-slate-900 bg-opacity-50`, -} - -export const borders = { - level1: `rounded border-gray-300 dark:border-slate-700`, -} - -export const colors = { - deployment: 'green-400', -} diff --git a/console/client/src/features/modules/components/index.ts b/console/client/src/features/modules/components/index.ts deleted file mode 100644 index 8c554565a1..0000000000 --- a/console/client/src/features/modules/components/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './Panel' -export * from './components.constants' diff --git a/console/client/src/features/modules/graph/dot-to-svg.ts b/console/client/src/features/modules/graph/dot-to-svg.ts deleted file mode 100644 index c654632373..0000000000 --- a/console/client/src/features/modules/graph/dot-to-svg.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { instance } from '@viz-js/viz' - -export const dotToSVG = async (dot: string): Promise => { - const viz = await instance() - try { - return viz.renderSVGElement(dot) - } catch (e) { - console.error(e) - } -} diff --git a/console/client/src/features/modules/graph/format-svg.ts b/console/client/src/features/modules/graph/format-svg.ts deleted file mode 100644 index 4d41adfccd..0000000000 --- a/console/client/src/features/modules/graph/format-svg.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { callIcon, moduleVerbCls, callIconID, vizID } from '../modules.constants' - -import styles from './graph.module.css' - -export const formatSVG = (svg: SVGSVGElement): SVGSVGElement => { - svg.insertAdjacentHTML('afterbegin', callIcon) - svg.removeAttribute('width') - svg.removeAttribute('height') - svg.setAttribute('id', vizID) - - for (const $a of svg.querySelectorAll('a')) { - const $g = $a.parentNode! as SVGSVGElement - - const $docFrag = document.createDocumentFragment() - while ($a.firstChild) { - const $child = $a.firstChild - $docFrag.appendChild($child) - } - - $g.replaceChild($docFrag, $a) - - $g.id = $g.id.replace(/^a_/, '') - } - for (const $el of svg.querySelectorAll('title')) { - $el.remove() - } - - for (const $node of svg.querySelectorAll('.node')) { - $node.classList.remove('node') - $node.classList.add(styles.node) - } - - const edgesSources = new Set() - for (const $edge of svg.querySelectorAll('.edge')) { - $edge.classList.remove('edge') - $edge.classList.add(styles.edge) - const [from, to] = $edge.id.split('=>') - $edge.removeAttribute('id') - $edge.setAttribute('data-from', from) - $edge.setAttribute('data-to', to) - edgesSources.add(from) - } - - for (const $el of svg.querySelectorAll('[id*=\\:\\:]')) { - const [tag, id] = $el.id.split('::') - if (moduleVerbCls === tag) { - $el.id = id - } - $el.classList.add(styles[tag as keyof typeof styles]) - } - - for (const $path of svg.querySelectorAll(`g.${styles.edge} path`)) { - const $newPath = $path.cloneNode() as HTMLElement - $newPath.classList.add(styles.hoverPath) - $path.classList.add(styles.edgePath) - $path.parentNode?.appendChild($newPath) - } - for (const $verb of svg.querySelectorAll(`.${styles[moduleVerbCls]}`)) { - const texts = $verb.querySelectorAll('text') - texts[0].classList.add('verb-name') - - // Tag verb as a call source - if (edgesSources.has($verb.id)) $verb.classList.add(styles.callSource) - - // Replace icon - const length = texts.length - for (let i = 1; i < length; ++i) { - const str = texts[i].innerHTML - if (str === '{R}') { - const $iconPlaceholder = texts[i] - const height = 22 - const width = 22 - const $useIcon = document.createElementNS('http://www.w3.org/2000/svg', 'use') - $useIcon.setAttributeNS('http://www.w3.org/1999/xlink', 'href', `#${callIconID}`) - $useIcon.setAttribute('width', `${width}px`) - $useIcon.setAttribute('height', `${height}px`) - $useIcon.classList.add(styles.callLink) - $useIcon.dataset.verbId = $verb.id - - //hardcoded offset - const y = parseInt($iconPlaceholder.getAttribute('y')!) - 15 - $useIcon.setAttribute('x', $iconPlaceholder.getAttribute('x')!) - $useIcon.setAttribute('y', y.toString()) - $verb.replaceChild($useIcon, $iconPlaceholder) - continue - } - } - } - return svg -} diff --git a/console/client/src/features/modules/graph/generate-dot.ts b/console/client/src/features/modules/graph/generate-dot.ts deleted file mode 100644 index edf452adc8..0000000000 --- a/console/client/src/features/modules/graph/generate-dot.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { GetModulesResponse, Module } from '../../../protos/xyz/block/ftl/v1/console/console_pb' -import { moduleTitleCls, moduleVerbCls } from '../modules.constants' -const HtmlId = (id: string) => `HREF="remove_me_url" ID="${id}"` - -const generateRow = ({ - moduleName, - verbName = '', - hasCalls, -}: { - moduleName: string - verbName?: string - hasCalls: boolean -}): string => { - const callsIcon = hasCalls ? '{R}' : '' - return ` - - - - - - -
    ${verbName} ${' '.repeat(4)}${callsIcon}
    - -` -} - -const generateModuleContent = (module: Module): { node: string; edges: string } => { - let edges = '' - const moduleName = module.name - const node = ` - ${moduleName} [ - id=${moduleName} - label=< - - - - - ${module.verbs - .map(({ verb }) => { - let hasCalls = false - verb?.metadata.forEach((metadataEntry) => { - if (metadataEntry?.value?.case === 'calls') { - const calls = metadataEntry.value.value.calls - if (!hasCalls) { - hasCalls = Boolean(calls.length) - } - calls.forEach((call) => { - if (call.module) { - edges += `\n"${moduleName}":"${verb.name}" -> "${call.module}":"${call.name}"[ - id = "${moduleName}.${verb.name}=>${call.module}.${call.name}" - ]` - } - }) - } - }) - return generateRow({ moduleName, verbName: verb?.name, hasCalls }) - }) - .join('\n')} -
    ${' '.repeat( - 4, - )}${moduleName}${' '.repeat(4)}
    - > - ]` - return { edges, node } -} - -export const generateDot = ({ modules }: GetModulesResponse): string => { - let nodes = '' - let allEdges = '' - modules.reverse().forEach((module) => { - const { node, edges } = generateModuleContent(module) - nodes += node - allEdges += edges - }) - return ` - digraph erd { - graph [ - rankdir = "LR" - ]; - node [ - fontsize = "16" - fontname = "helvetica" - shape = "plaintext" - ]; - edge [ - ]; - ranksep = 2.0 - ${nodes} - ${allEdges} - }` -} diff --git a/console/client/src/features/modules/graph/graph.module.css b/console/client/src/features/modules/graph/graph.module.css deleted file mode 100644 index 7df8e7a0cf..0000000000 --- a/console/client/src/features/modules/graph/graph.module.css +++ /dev/null @@ -1,69 +0,0 @@ -:global .graph > polygon { - fill: transparent; -} - -/* Edges Styling */ - -.edge { - cursor: pointer; - & .edge-path { - stroke: currentColor; - stroke-width: 2; - } - - & .hover-path { - stroke: transparent; - stroke-width: 15; - } - - .selected, - &:hover { - & .edge-path { - @apply stroke-pink-400; - stroke-width: 3; - } - - & polygon { - @apply stroke-pink-400 fill-pink-400; - opacity: 1; - } - } - - & polygon { - fill: currentColor; - stroke: currentColor; - } -} - -/** module */ -.module-title { - & > polygon { - @apply fill-green-400 stroke-green-400; - } - & > text { - @apply fill-white; - } -} - -/* verb */ -.module-verb { - & polygon { - @apply stroke-green-400 fill-gray-800; - } - & text { - @apply fill-white; - } -} - -/* Nodes Styling */ -.node { - pointer-events: bounding-box; - cursor: pointer; -} - -.call-link { - @apply text-white; -} - -.call-source { -} diff --git a/console/client/src/features/modules/graph/graph.module.css.d.ts b/console/client/src/features/modules/graph/graph.module.css.d.ts deleted file mode 100644 index 2a6f5af05e..0000000000 --- a/console/client/src/features/modules/graph/graph.module.css.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -declare const styles: { - readonly edge: string - readonly edgePath: string - readonly hoverPath: string - readonly selected: string - readonly moduleTitle: string - readonly moduleVerb: string - readonly node: string - readonly callLink: string - readonly callSource: string -} -export = styles diff --git a/console/client/src/features/modules/graph/index.ts b/console/client/src/features/modules/graph/index.ts deleted file mode 100644 index 361642a90f..0000000000 --- a/console/client/src/features/modules/graph/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './generate-dot' -export * from './dot-to-svg' -export * from './format-svg' -export * from './svg-zoom' diff --git a/console/client/src/features/modules/graph/svg-zoom.ts b/console/client/src/features/modules/graph/svg-zoom.ts deleted file mode 100644 index f77b2ecb9c..0000000000 --- a/console/client/src/features/modules/graph/svg-zoom.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Element, SVG, Svg } from '@svgdotjs/svg.js' -import '@svgdotjs/svg.panzoom.js/dist/svg.panzoom.esm.js' - -const centerGroup = ({ - canvas, - selector, - padding, - width, - height, -}: { - canvas: Svg - selector: `.${string}` | `#${string}` - padding: number - width: number - height: number -}): [number, number, number, number] => { - // Find the modules group with the class the id - const group = canvas.findOne(selector) as Element - - // Get the group's bounding box in the global SVG coordinate system - const BBox = group.rbox(canvas) - - // Calculate the scale factor to fit the group within the desired width and height - const scaleX = width / BBox.width - const scaleY = height / BBox.height - const scale = Math.min(scaleX, scaleY) - - // Calculate dynamic padding factor based on overflow - const overflowX = (BBox.width * scale) / width - const overflowY = (BBox.height * scale) / height - const paddingFactor = Math.max(overflowX, overflowY) + padding // Base padding of 10% + dynamic adjustment - - const newWidth = (width / scale) * paddingFactor - const newHeight = (height / scale) * paddingFactor - - // Calculate the new viewbox coordinates to center the .graph group with padding - const newX = BBox.cx - newWidth / 2 - const newY = BBox.cy - newHeight / 2 - - // new viewbox - return [newX, newY, newWidth, newHeight] -} - -export const svgZoom = (svg: SVGSVGElement, width: number, height: number) => { - // Create an SVG.js instance from the provided SVG element - const canvas = SVG(svg) - - // Center Graph - const viewbox = centerGroup({ - canvas, - height, - width, - selector: '.graph', - padding: 0.3, - }) - - canvas.viewbox(...viewbox) - - //@ts-ignore: lib types bad - canvas.panZoom() - - return { - to(id: string) { - const viewbox = centerGroup({ - canvas, - height, - width, - selector: `#${id}`, - padding: 5.5, - }) - canvas.animate().viewbox(...viewbox) - }, - in() { - const zoomLevel = canvas.zoom() - canvas.animate().zoom(zoomLevel + 0.1) // Increase the zoom level by 0.1 - }, - out() { - const zoomLevel = canvas.zoom() - canvas.animate().zoom(zoomLevel - 0.1) // Decrease the zoom level by 0.1 - }, - reset() { - const viewbox = centerGroup({ - canvas, - height, - width, - selector: '.graph', - padding: 0.3, - }) - canvas.animate().viewbox(...viewbox) - }, - } -} diff --git a/console/client/src/features/modules/modules.constants.ts b/console/client/src/features/modules/modules.constants.ts deleted file mode 100644 index 8fa3ac39d7..0000000000 --- a/console/client/src/features/modules/modules.constants.ts +++ /dev/null @@ -1,19 +0,0 @@ -export const callIconID = 'call-icon' - -export const callIcon = ` - -` - -export const moduleVerbCls = 'moduleVerb' -export const moduleTitleCls = 'moduleTitle' -export const vizID = 'modules-flow-chart' -export const controlsID = 'pan-zoom-controls' - -export type VerbId = `${string}.${string}` - -export interface ZoomCallbacks { - to(id: string): void - in(): void - out(): void - reset(): void -} diff --git a/console/client/src/features/modules/modules.utils.ts b/console/client/src/features/modules/modules.utils.ts deleted file mode 100644 index 4cd1049522..0000000000 --- a/console/client/src/features/modules/modules.utils.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { VerbId } from './modules.constants' -import { VerbRef } from '../../protos/xyz/block/ftl/v1/schema/schema_pb' - -export const buildVerbSchema = (verbSchema: string, dataScemas: string[]): string => { - return dataScemas.join('\n\n') + '\n\n' + verbSchema -} -export const getNames = (verbId: VerbId) => { - const [moduleName, verbName] = verbId.split('.') - return [moduleName, verbName] -} -export const verbRefString = (verb: VerbRef): VerbId => { - return `${verb.module}.${verb.name}` -} diff --git a/console/client/src/features/timeline/details/TimelineCallDetails.tsx b/console/client/src/features/timeline/details/TimelineCallDetails.tsx index 5d0d1fa22b..5337b9d137 100644 --- a/console/client/src/features/timeline/details/TimelineCallDetails.tsx +++ b/console/client/src/features/timeline/details/TimelineCallDetails.tsx @@ -79,7 +79,7 @@ export const TimelineCallDetails = ({ timestamp, call }: { timestamp: Timestamp; )} - +
      {selectedCall.requestName && ( diff --git a/console/client/src/features/timeline/details/TimelineDeploymentCreatedDetails.tsx b/console/client/src/features/timeline/details/TimelineDeploymentCreatedDetails.tsx index 8daa71d8b2..781d89b3c0 100644 --- a/console/client/src/features/timeline/details/TimelineDeploymentCreatedDetails.tsx +++ b/console/client/src/features/timeline/details/TimelineDeploymentCreatedDetails.tsx @@ -34,7 +34,7 @@ export const TimelineDeploymentCreatedDetails = ({
    - +
    • diff --git a/console/client/src/features/timeline/details/TimelineDeploymentUpdatedDetails.tsx b/console/client/src/features/timeline/details/TimelineDeploymentUpdatedDetails.tsx index d7b2418a8c..3584d8d006 100644 --- a/console/client/src/features/timeline/details/TimelineDeploymentUpdatedDetails.tsx +++ b/console/client/src/features/timeline/details/TimelineDeploymentUpdatedDetails.tsx @@ -34,7 +34,7 @@ export const TimelineDeploymentUpdatedDetails = ({
    - +
    • diff --git a/console/client/src/features/timeline/details/TimelineLogDetails.tsx b/console/client/src/features/timeline/details/TimelineLogDetails.tsx index fe8c25ef65..0e489ca77a 100644 --- a/console/client/src/features/timeline/details/TimelineLogDetails.tsx +++ b/console/client/src/features/timeline/details/TimelineLogDetails.tsx @@ -32,7 +32,7 @@ export const TimelineLogDetails = ({ event, log }: { event: Event; log: LogEvent

      Attributes

      - +
        {log.requestName && ( diff --git a/console/client/src/features/verbs/VerbPage.tsx b/console/client/src/features/verbs/VerbPage.tsx index 6c5fb505c0..f77f7f732a 100644 --- a/console/client/src/features/verbs/VerbPage.tsx +++ b/console/client/src/features/verbs/VerbPage.tsx @@ -12,7 +12,7 @@ import { VerbForm } from './VerbForm' import { buildVerbSchema } from './verb.utils' export const VerbPage = () => { - const { moduleName, verbName } = useParams() + const { deploymentName, verbName } = useParams() const modules = useContext(modulesContext) const [module, setModule] = useState() const [verb, setVerb] = useState() @@ -24,12 +24,12 @@ export const VerbPage = () => { useEffect(() => { if (modules) { - const module = modules.modules.find((module) => module.name === moduleName?.toLocaleLowerCase()) + const module = modules.modules.find((module) => module.deploymentName === deploymentName?.toLocaleLowerCase()) setModule(module) const verb = module?.verbs.find((verb) => verb.verb?.name.toLocaleLowerCase() === verbName?.toLocaleLowerCase()) setVerb(verb) } - }, [modules, moduleName]) + }, [modules, deploymentName]) useEffect(() => { const abortController = new AbortController() @@ -59,8 +59,8 @@ export const VerbPage = () => { icon={} title={verb?.verb?.name || ''} breadcrumbs={[ - { label: 'Modules', link: '/modules' }, - { label: module?.name || '', link: `/modules/${module?.name}` }, + { label: 'Deployments', link: '/deployments' }, + { label: module?.deploymentName || '', link: `/deployments/${module?.deploymentName}` }, ]} /> diff --git a/console/client/src/layout/Navigation.tsx b/console/client/src/layout/Navigation.tsx index 6ad88b2f63..08cbb73033 100644 --- a/console/client/src/layout/Navigation.tsx +++ b/console/client/src/layout/Navigation.tsx @@ -1,4 +1,4 @@ -import { CubeTransparentIcon, ListBulletIcon, RocketLaunchIcon, Square3Stack3DIcon } from '@heroicons/react/24/outline' +import { CubeTransparentIcon, ListBulletIcon, Square3Stack3DIcon } from '@heroicons/react/24/outline' import { useContext } from 'react' import { Link, NavLink } from 'react-router-dom' import { DarkModeSwitch } from '../components/DarkModeSwitch' @@ -7,8 +7,7 @@ import { classNames } from '../utils' const navigation = [ { name: 'Events', href: '/events', icon: ListBulletIcon }, - { name: 'Modules', href: '/modules', icon: Square3Stack3DIcon }, - { name: 'Deployments', href: '/deployments', icon: RocketLaunchIcon }, + { name: 'Deployments', href: '/deployments', icon: Square3Stack3DIcon }, { name: 'Graph', href: '/graph', icon: CubeTransparentIcon }, ]