From 3d26371fa90db2d1e5c1047e33e5a10395e0c930 Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Sat, 20 Mar 2021 02:56:56 +0100 Subject: [PATCH] Make web pages pluggable Refactor the page handling of the web interface to allow plugins to add their own pages. --- packages/lib/plugin.js | 22 +++++++ packages/web_ui/src/components/SiteLayout.jsx | 57 ++++++------------- packages/web_ui/src/pages.jsx | 29 ++++++++++ 3 files changed, 67 insertions(+), 41 deletions(-) create mode 100644 packages/web_ui/src/pages.jsx diff --git a/packages/lib/plugin.js b/packages/lib/plugin.js index 960150a0..046190fd 100644 --- a/packages/lib/plugin.js +++ b/packages/lib/plugin.js @@ -439,6 +439,21 @@ class BaseControlPlugin { * form. */ +/** + * Plugin supplied pages + * @typedef {Object} module:Lib/plugin~Page + * @property {string} path - URL path to this page. + * @property {string=} sidebarPath - + * If present and this path matches one of the pages in the sidebar it + * will cause that sidebar entry to be highlighted as active. + * @property {string=} sidebarName - + * If present creates an entry in the sidebar for this page with the + * given text. + * @property {ReactNode} content - + * A react node which is rendered when this page is navigated to. + * Should render a PageLayout. + */ + /** * Base class for web interface plugins * @@ -477,6 +492,13 @@ class BaseWebPlugin { * @type {Array} */ this.loginForms = []; + + /** + * List of pages provided by this plugin + * + * @type {Array} + */ + this.pages = []; } /** diff --git a/packages/web_ui/src/components/SiteLayout.jsx b/packages/web_ui/src/components/SiteLayout.jsx index fda48ca9..eded71c2 100644 --- a/packages/web_ui/src/components/SiteLayout.jsx +++ b/packages/web_ui/src/components/SiteLayout.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { Switch, Route, useHistory } from "react-router-dom"; import { Dropdown, Layout, Menu } from "antd"; import UserOutlined from "@ant-design/icons/UserOutlined"; @@ -7,46 +7,21 @@ import webUiPackage from "../../package.json"; import { useAccount } from "../model/account"; import ErrorBoundary from "./ErrorBoundary"; import ErrorPage from "./ErrorPage"; +import PluginsContext from "./PluginsContext"; +import { pages } from "../pages"; const { Header, Sider } = Layout; -import MasterPage from "./MasterPage"; -import SlavesPage from "./SlavesPage"; -import InstancesPage from "./InstancesPage"; -import InstanceViewPage from "./InstanceViewPage"; -import UsersPage from "./UsersPage"; -import UserViewPage from "./UserViewPage"; -import RolesPage from "./RolesPage"; -import RoleViewPage from "./RoleViewPage"; -import PluginsPage from "./PluginsPage"; -import PluginViewPage from "./PluginViewPage"; - -const pages = [ - { path: "/instances/:id/view", sidebarPath: "/instances", content: }, - { path: "/users/:name/view", sidebarPath: "/users", content: }, - { path: "/roles/:id/view", sidebarPath: "/roles", content: }, - { path: "/plugins/:name/view", sidebarPath: "/plugins", content: }, -]; - -const sidebar = [ - { name: "Master", path: "/master", content: }, - { name: "Slaves", path: "/slaves", content: }, - { name: "Instances", path: "/instances", content: }, - { name: "Users", path: "/users", content: }, - { name: "Roles", path: "/roles", content: }, - { name: "Plugins", path: "/plugins", content: }, -]; - - export default function SiteLayout(props) { let history = useHistory(); - let [sidebarPath, setSidebarPath] = useState(null); + let [currentSidebarPath, setCurrentSidebarPath] = useState(null); let account = useAccount(); + let plugins = useContext(PluginsContext); function SetSidebar(setSidebarProps) { useEffect(() => { - setSidebarPath(setSidebarProps.path); + setCurrentSidebarPath(setSidebarProps.path); }); return null; } @@ -57,6 +32,10 @@ export default function SiteLayout(props) { { account.logOut(); }}>Log out ; + let combinedPages = [...pages]; + for (let plugin of plugins.values()) { + combinedPages.push(...plugin.pages); + } return
@@ -86,27 +65,23 @@ export default function SiteLayout(props) { > history.push(key)} > - {sidebar.map(({name, path}) => {name})} + {combinedPages.map(({sidebarName, path}) => ( + sidebarName ? {sidebarName} : null) + )} - {sidebar.map(({path, content}) => - + {combinedPages.map(({path, sidebarPath, content}) => + {content} )} - {pages.map(page => - - - {page.content} - - )} diff --git a/packages/web_ui/src/pages.jsx b/packages/web_ui/src/pages.jsx new file mode 100644 index 00000000..6ffe389b --- /dev/null +++ b/packages/web_ui/src/pages.jsx @@ -0,0 +1,29 @@ +import React from "react"; + +import MasterPage from "./components/MasterPage"; +import SlavesPage from "./components/SlavesPage"; +import InstancesPage from "./components/InstancesPage"; +import InstanceViewPage from "./components/InstanceViewPage"; +import UsersPage from "./components/UsersPage"; +import UserViewPage from "./components/UserViewPage"; +import RolesPage from "./components/RolesPage"; +import RoleViewPage from "./components/RoleViewPage"; +import PluginsPage from "./components/PluginsPage"; +import PluginViewPage from "./components/PluginViewPage"; + + +export const pages = [ + { path: "/master", sidebarName: "Master", content: }, + { path: "/slaves", sidebarName: "Slaves", content: }, + { path: "/instances", sidebarName: "Instances", content: }, + { path: "/instances/:id/view", sidebarPath: "/instances", content: }, + { path: "/users", sidebarName: "Users", content: }, + { path: "/users/:name/view", sidebarPath: "/users", content: }, + { path: "/roles", sidebarName: "Roles", content: }, + { path: "/roles/:id/view", sidebarPath: "/roles", content: }, + { path: "/plugins", sidebarName: "Plugins", content: }, + { path: "/plugins/:name/view", sidebarPath: "/plugins", content: }, +]; + +export const sidebarPages = [ +];