From a60bce57092b1cda53a6f4c7ca76514c5c1780f0 Mon Sep 17 00:00:00 2001 From: Wes Date: Fri, 23 Aug 2024 12:01:55 -0700 Subject: [PATCH] feat: restructure navigation for console (#2484) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Restructure console navigation to better support future updates - Add new "modules" and "infrastructure" sections - Add sample tree to "modules" ![Screenshot 2024-08-23 at 11 43 20 AM](https://github.com/user-attachments/assets/9ccfded3-c5d5-4236-822b-2a270daf3514) ![Screenshot 2024-08-23 at 11 43 22 AM](https://github.com/user-attachments/assets/8aaaef0f-9429-4ccd-b651-8dd846cf22b9) ![Screenshot 2024-08-23 at 11 43 28 AM](https://github.com/user-attachments/assets/2b5f472e-ce92-4dc5-bf86-afbd9548ef8e) ![Screenshot 2024-08-23 at 11 43 31 AM](https://github.com/user-attachments/assets/d3141a84-8185-4007-bac1-bfbf828a8c8a) ![Screenshot 2024-08-23 at 11 43 33 AM](https://github.com/user-attachments/assets/0139032d-c99b-4d02-bb1d-865d9b76098d) ![Screenshot 2024-08-23 at 11 43 37 AM](https://github.com/user-attachments/assets/be3460e5-b9e7-4df3-8b89-29c608614ae5) ![Screenshot 2024-08-23 at 11 43 41 AM](https://github.com/user-attachments/assets/dd17ad69-4003-489e-b445-4338b22f113e) --- frontend/e2e/events.spec.ts | 3 +- frontend/e2e/ftl-test.ts | 11 +- frontend/index.html | 6 +- .../infrastructure/InfrastructurePage.tsx | 3 + frontend/src/features/modules/ModulesPage.tsx | 21 +++ frontend/src/features/modules/ModulesTree.tsx | 65 +++++++++ frontend/src/features/modules/module.utils.ts | 52 +++++++ frontend/src/layout/Layout.tsx | 43 +----- frontend/src/layout/index.ts | 1 - .../layout/navigation/MobileNavigation.tsx | 59 -------- frontend/src/layout/navigation/Navigation.tsx | 135 ++++++------------ frontend/src/layout/navigation/Version.tsx | 3 + .../src/layout/navigation/navigation-items.ts | 7 - frontend/src/providers/routing-provider.tsx | 12 +- 14 files changed, 212 insertions(+), 209 deletions(-) create mode 100644 frontend/src/features/infrastructure/InfrastructurePage.tsx create mode 100644 frontend/src/features/modules/ModulesPage.tsx create mode 100644 frontend/src/features/modules/ModulesTree.tsx delete mode 100644 frontend/src/layout/navigation/MobileNavigation.tsx create mode 100644 frontend/src/layout/navigation/Version.tsx delete mode 100644 frontend/src/layout/navigation/navigation-items.ts diff --git a/frontend/e2e/events.spec.ts b/frontend/e2e/events.spec.ts index 4dfdfc5134..dd2c8b6fbc 100644 --- a/frontend/e2e/events.spec.ts +++ b/frontend/e2e/events.spec.ts @@ -1,7 +1,8 @@ import { expect, ftlTest } from './ftl-test' ftlTest('defaults to the events page', async ({ page }) => { + await page.goto('http://localhost:8892') const eventsNavItem = page.getByRole('link', { name: 'Events' }) - await expect(eventsNavItem).toHaveClass(/bg-indigo-600 text-white/) + await expect(eventsNavItem).toHaveClass(/bg-indigo-700 text-white/) }) diff --git a/frontend/e2e/ftl-test.ts b/frontend/e2e/ftl-test.ts index 23847ae037..8ebed3a76d 100644 --- a/frontend/e2e/ftl-test.ts +++ b/frontend/e2e/ftl-test.ts @@ -5,13 +5,16 @@ const ftlTest = base.extend<{ page: any }>({ page: async ({ page }, use) => { - await page.goto('http://localhost:8892') + await page.goto('http://localhost:8892/modules') await page.waitForFunction(() => { - const element = document.querySelector('#deployments-count') - return element && element.textContent?.trim() === '2' - }) + const timeItem = document.querySelector('li#module-tree-item-time'); + const echoItem = document.querySelector('li#module-tree-item-echo'); + return timeItem !== null && echoItem !== null; + }); + await use(page) }, }) export { ftlTest, expect } + diff --git a/frontend/index.html b/frontend/index.html index 82085d444a..240190cc68 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,5 +1,5 @@ - + @@ -8,8 +8,8 @@ - -
+ +
diff --git a/frontend/src/features/infrastructure/InfrastructurePage.tsx b/frontend/src/features/infrastructure/InfrastructurePage.tsx new file mode 100644 index 0000000000..3a71d5d211 --- /dev/null +++ b/frontend/src/features/infrastructure/InfrastructurePage.tsx @@ -0,0 +1,3 @@ +export const InfrastructurePage = () => { + return <>Infrastructure +} diff --git a/frontend/src/features/modules/ModulesPage.tsx b/frontend/src/features/modules/ModulesPage.tsx new file mode 100644 index 0000000000..3114e56b2e --- /dev/null +++ b/frontend/src/features/modules/ModulesPage.tsx @@ -0,0 +1,21 @@ +import { useMemo } from 'react' +import { useSchema } from '../../api/schema/use-schema' +import { ModulesTree } from './ModulesTree' +import { moduleTreeFromSchema } from './module.utils' + +export const ModulesPage = () => { + const schema = useSchema() + const tree = useMemo(() => moduleTreeFromSchema(schema?.data || []), [schema?.data]) + + return ( +
+
+ +
+ +
+

Content

+
+
+ ) +} diff --git a/frontend/src/features/modules/ModulesTree.tsx b/frontend/src/features/modules/ModulesTree.tsx new file mode 100644 index 0000000000..be26511bf9 --- /dev/null +++ b/frontend/src/features/modules/ModulesTree.tsx @@ -0,0 +1,65 @@ +import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react' +import { ChevronRightIcon } from '@heroicons/react/24/outline' +import { classNames } from '../../utils' +import type { ModuleTreeItem } from './module.utils' + +export const ModulesTree = ({ modules }: { modules: ModuleTreeItem[] }) => { + return ( +
+ +
+ ) +} diff --git a/frontend/src/features/modules/module.utils.ts b/frontend/src/features/modules/module.utils.ts index aa7454ea57..118f7dfde2 100644 --- a/frontend/src/features/modules/module.utils.ts +++ b/frontend/src/features/modules/module.utils.ts @@ -1,4 +1,16 @@ +import { + ArrowDownOnSquareStackIcon, + BellAlertIcon, + BoltIcon, + CircleStackIcon, + CodeBracketIcon, + CogIcon, + LockClosedIcon, + RectangleGroupIcon, +} from '@heroicons/react/24/outline' +import type { ForwardRefExoticComponent, SVGProps } from 'react' import type { Module } from '../../protos/xyz/block/ftl/v1/console/console_pb' +import type { PullSchemaResponse } from '../../protos/xyz/block/ftl/v1/ftl_pb' import type { MetadataCalls, Ref } from '../../protos/xyz/block/ftl/v1/schema/schema_pb' import { verbCalls } from '../verbs/verb.utils' @@ -55,3 +67,43 @@ export const deploymentKeyModuleName = (deploymentKey: string) => { } return null } + +interface ModuleTreeChild { + name: string + href: string + icon: ForwardRefExoticComponent & { title?: string; titleId?: string }> + current?: boolean +} + +export interface ModuleTreeItem { + name: string + href?: string + icon: ForwardRefExoticComponent & { title?: string; titleId?: string }> + current: boolean + expanded?: boolean + children?: ModuleTreeChild[] +} + +export const moduleTreeFromSchema = (schema: PullSchemaResponse[]) => { + const tree: ModuleTreeItem[] = [] + + for (const module of schema) { + tree.push({ + name: module.moduleName, + icon: RectangleGroupIcon, + current: false, + expanded: module.moduleName === 'echo', + children: [ + { name: 'Data', href: '#', icon: CodeBracketIcon }, + { name: 'Verb', href: '#', icon: BoltIcon }, + { name: 'Database', href: '#', icon: CircleStackIcon }, + { name: 'Config', href: '#', icon: CogIcon }, + { name: 'Secret', href: '#', icon: LockClosedIcon }, + { name: 'FSM', href: '#', icon: ArrowDownOnSquareStackIcon }, + { name: 'Pubsub', href: '#', icon: BellAlertIcon }, + ], + }) + } + + return tree +} diff --git a/frontend/src/layout/Layout.tsx b/frontend/src/layout/Layout.tsx index a8ed4e7653..787429395b 100644 --- a/frontend/src/layout/Layout.tsx +++ b/frontend/src/layout/Layout.tsx @@ -1,44 +1,13 @@ -import { Bars3Icon } from '@heroicons/react/24/outline' -import { useState } from 'react' import { Outlet } from 'react-router-dom' -import useLocalStorage from '../hooks/use-local-storage' -import { bgColor, textColor } from '../utils' -import { Notification } from './Notification' -import { SidePanel } from './SidePanel' -import MobileNavigation from './navigation/MobileNavigation' import { Navigation } from './navigation/Navigation' export const Layout = () => { - const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) - const [isCollapsed, setIsCollapsed] = useLocalStorage('isNavCollapsed', false) - return ( - <> -
-
- setIsMobileMenuOpen(false)} /> -
- -
-
- FTL - -
- -
- -
- -
- -
-
-
- - - - +
+ +
+ +
+
) } diff --git a/frontend/src/layout/index.ts b/frontend/src/layout/index.ts index fee2a4e15d..504cdc12a3 100644 --- a/frontend/src/layout/index.ts +++ b/frontend/src/layout/index.ts @@ -1,2 +1 @@ -export * from './Layout' export * from './Page' diff --git a/frontend/src/layout/navigation/MobileNavigation.tsx b/frontend/src/layout/navigation/MobileNavigation.tsx deleted file mode 100644 index 169789f617..0000000000 --- a/frontend/src/layout/navigation/MobileNavigation.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { XMarkIcon } from '@heroicons/react/24/outline' -import { NavLink } from 'react-router-dom' -import { useModules } from '../../api/modules/use-modules' -import { DarkModeSwitch } from '../../components' -import { classNames } from '../../utils' -import { navigation } from './navigation-items' - -const MobileNavigation = ({ onClose }: { onClose: () => void }) => { - const modules = useModules() - - return ( -
-
-
- FTL - -
- -
- -
- -
-
- ) -} - -export default MobileNavigation diff --git a/frontend/src/layout/navigation/Navigation.tsx b/frontend/src/layout/navigation/Navigation.tsx index ea2bb82b2c..0096eed3fb 100644 --- a/frontend/src/layout/navigation/Navigation.tsx +++ b/frontend/src/layout/navigation/Navigation.tsx @@ -1,102 +1,51 @@ -import { ChevronDoubleLeftIcon, ChevronDoubleRightIcon } from '@heroicons/react/24/outline' -import { Link, NavLink } from 'react-router-dom' -import { useModules } from '../../api/modules/use-modules' -import { DarkModeSwitch } from '../../components/DarkModeSwitch' +import { CubeTransparentIcon, ListBulletIcon, ServerStackIcon, Square3Stack3DIcon } from '@heroicons/react/24/outline' +import { NavLink } from 'react-router-dom' +import { DarkModeSwitch } from '../../components' import { classNames } from '../../utils' -import { navigation } from './navigation-items' +import { Version } from './Version' -export const Navigation = ({ - isCollapsed, - setIsCollapsed, -}: { - isCollapsed: boolean - setIsCollapsed: React.Dispatch> -}) => { - const modules = useModules() +const navigation = [ + { name: 'Events', href: '/events', icon: ListBulletIcon }, + { name: 'Deployments', href: '/deployments', icon: Square3Stack3DIcon }, + { name: 'Modules', href: '/modules', icon: Square3Stack3DIcon }, + { name: 'Graph', href: '/graph', icon: CubeTransparentIcon }, + { name: 'Infrastructure', href: '/infrastructure', icon: ServerStackIcon }, +] +export const Navigation = () => { return ( -
- -
+ + ) } diff --git a/frontend/src/layout/navigation/Version.tsx b/frontend/src/layout/navigation/Version.tsx new file mode 100644 index 0000000000..440c4a19db --- /dev/null +++ b/frontend/src/layout/navigation/Version.tsx @@ -0,0 +1,3 @@ +export const Version = ({ version }: { version: string }) => { + return {version} +} diff --git a/frontend/src/layout/navigation/navigation-items.ts b/frontend/src/layout/navigation/navigation-items.ts deleted file mode 100644 index 9c9281aaf0..0000000000 --- a/frontend/src/layout/navigation/navigation-items.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { CubeTransparentIcon, ListBulletIcon, Square3Stack3DIcon } from '@heroicons/react/24/outline' - -export const navigation = [ - { name: 'Events', href: '/events', icon: ListBulletIcon }, - { name: 'Console', href: '/console', icon: CubeTransparentIcon }, - { name: 'Deployments', href: '/deployments', icon: Square3Stack3DIcon }, -] diff --git a/frontend/src/providers/routing-provider.tsx b/frontend/src/providers/routing-provider.tsx index b73019ddba..84b6ba21aa 100644 --- a/frontend/src/providers/routing-provider.tsx +++ b/frontend/src/providers/routing-provider.tsx @@ -2,23 +2,27 @@ import { Navigate, Route, RouterProvider, createBrowserRouter, createRoutesFromE import { ConsolePage } from '../features/console/ConsolePage' import { DeploymentPage } from '../features/deployments/DeploymentPage' import { DeploymentsPage } from '../features/deployments/DeploymentsPage' +import { InfrastructurePage } from '../features/infrastructure/InfrastructurePage' +import { ModulesPage } from '../features/modules/ModulesPage' import { TimelinePage } from '../features/timeline/TimelinePage' import { VerbPage } from '../features/verbs/VerbPage' -import { Layout } from '../layout' +import { Layout } from '../layout/Layout' import { NotFoundPage } from '../layout/NotFoundPage' const router = createBrowserRouter( createRoutesFromElements( <> }> - } /> + } /> } /> - + } /> } /> } /> } /> - } /> + } /> + } /> + } /> , ),