diff --git a/console/client/index.html b/console/client/index.html index 2406df2c88..a6e97b543d 100644 --- a/console/client/index.html +++ b/console/client/index.html @@ -4,7 +4,7 @@ FTL - + diff --git a/console/client/src/App.tsx b/console/client/src/App.tsx index 078ad058c9..0b9e60ecf9 100644 --- a/console/client/src/App.tsx +++ b/console/client/src/App.tsx @@ -1,4 +1,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 { ModulesPage } from './features/modules/ModulesPage.tsx' import { TimelinePage } from './features/timeline/TimelinePage.tsx' @@ -13,6 +15,8 @@ export const App = () => { } /> } /> } /> + } /> + } /> } /> diff --git a/console/client/src/components/ButtonSmall.tsx b/console/client/src/components/ButtonSmall.tsx new file mode 100644 index 0000000000..22bd3abdf1 --- /dev/null +++ b/console/client/src/components/ButtonSmall.tsx @@ -0,0 +1,17 @@ +interface Props { + children: React.ReactNode + onClick?: () => void + className?: string +} + +export const ButtonSmall = ({ children, onClick }: Props) => { + return ( + + ) +} diff --git a/console/client/src/components/Card.tsx b/console/client/src/components/Card.tsx index e153026120..5b315697d9 100644 --- a/console/client/src/components/Card.tsx +++ b/console/client/src/components/Card.tsx @@ -1,6 +1,19 @@ interface Props { + topBarColor?: string + onClick?: () => void children: React.ReactNode } -export const Card = ({ children }: Props) => { - return
{children}
+export const Card = ({ topBarColor, onClick, children }: Props) => { + return ( +
+ {topBarColor && ( +
+ )} + +
{children}
+
+ ) } diff --git a/console/client/src/features/deployments/DeploymentPage.tsx b/console/client/src/features/deployments/DeploymentPage.tsx new file mode 100644 index 0000000000..15fdc01e05 --- /dev/null +++ b/console/client/src/features/deployments/DeploymentPage.tsx @@ -0,0 +1,74 @@ +import { RocketLaunchIcon } from '@heroicons/react/24/outline' +import React from 'react' +import { useParams } from 'react-router-dom' +import { ButtonSmall } from '../../components/ButtonSmall' +import { Card } from '../../components/Card' +import { PageHeader } from '../../components/PageHeader' +import { Module } from '../../protos/xyz/block/ftl/v1/console/console_pb' +import { MetadataCalls, VerbRef } from '../../protos/xyz/block/ftl/v1/schema/schema_pb' +import { modulesContext } from '../../providers/modules-provider' +import { verbRefString } from '../verbs/verb.utils' + +export const DeploymentPage = () => { + const { deploymentName } = useParams() + const modules = React.useContext(modulesContext) + const [module, setModule] = React.useState() + const [calls, setCalls] = React.useState([]) + + React.useEffect(() => { + if (modules) { + const module = modules.modules.find((module) => module.deploymentName === deploymentName) + setModule(module) + } + }, [modules, deploymentName]) + + React.useEffect(() => { + if (!module) return + + const verbCalls: VerbRef[] = [] + + const metadata = module.verbs + .map((v) => v.verb) + .map((v) => v?.metadata) + .flat() + + const metadataCalls = metadata + .filter((metadata) => metadata?.value.case === 'calls') + .map((metadata) => metadata?.value.value as MetadataCalls) + + const calls = metadataCalls.map((metadata) => metadata?.calls).flat() + + calls.forEach((call) => { + if (!verbCalls.find((v) => v.name === call.name && v.module === call.module)) { + verbCalls.push({ name: call.name, module: call.module } as VerbRef) + } + }) + + setCalls(Array.from(verbCalls)) + }, [modules]) + + return ( + <> + } title={`Deployments - ${deploymentName}`} /> + +
+
+ {module?.verbs.map((verb) => ( + console.log('click')}> + {verb.verb?.name} +

{verb.verb?.name}

+
+ ))} +
+

Calls

+
    + {calls?.map((verb) => ( +
  • + {verbRefString(verb)} +
  • + ))} +
+
+ + ) +} diff --git a/console/client/src/features/deployments/DeploymentsPage.tsx b/console/client/src/features/deployments/DeploymentsPage.tsx new file mode 100644 index 0000000000..cb5b71b16b --- /dev/null +++ b/console/client/src/features/deployments/DeploymentsPage.tsx @@ -0,0 +1,29 @@ +import { RocketLaunchIcon } from '@heroicons/react/24/outline' +import React from 'react' +import { useNavigate } from 'react-router-dom' +import { Card } from '../../components/Card' +import { PageHeader } from '../../components/PageHeader' +import { modulesContext } from '../../providers/modules-provider' + +export const DeploymentsPage = () => { + const modules = React.useContext(modulesContext) + const navigate = useNavigate() + + return ( + <> + } title='Deployments' /> +
+ {modules.modules.map((module) => ( + navigate(`/deployments/${module.deploymentName}`)} + > + {module.name} +

{module.deploymentName}

+
+ ))} +
+ + ) +} diff --git a/console/client/src/layout/Navigation.tsx b/console/client/src/layout/Navigation.tsx index 8559db24a5..6ad88b2f63 100644 --- a/console/client/src/layout/Navigation.tsx +++ b/console/client/src/layout/Navigation.tsx @@ -1,4 +1,4 @@ -import { CubeTransparentIcon, ListBulletIcon, Square3Stack3DIcon } from '@heroicons/react/24/outline' +import { CubeTransparentIcon, ListBulletIcon, RocketLaunchIcon, Square3Stack3DIcon } from '@heroicons/react/24/outline' import { useContext } from 'react' import { Link, NavLink } from 'react-router-dom' import { DarkModeSwitch } from '../components/DarkModeSwitch' @@ -8,6 +8,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: 'Graph', href: '/graph', icon: CubeTransparentIcon }, ] @@ -54,7 +55,7 @@ export const Navigation = () => { aria-hidden='true' /> {item.name} - {item.href === '/modules' && ( + {['/modules', '/deployments'].includes(item.href) && (