From 45ab083f96d9e5c7ea901a1878ca48404459ad0b Mon Sep 17 00:00:00 2001 From: Michal Bajer <michal.bajer@fujitsu.com> Date: Thu, 20 Jun 2024 16:32:40 +0200 Subject: [PATCH] feat(ledger-browser): refactor home page - Remove status apps, its functionality has been moved to the home page. - Add status components for persistence apps. - Add home page that contains cards for each configured app. Clicking on it naviagtes to specific app, clicking on Status button shows status component. - Remove app drawer, replace it with a button that navigates to root path (i.e. the home app, navigation between apps is handled here). - Remove all the remaining dead and legacy code, apply small structure upgrades. - Since this PR removes all old code, and all the current code was written by me and Tomasz, I've also removed previous inactive package contributors. Depends on #3320 Signed-off-by: Michal Bajer <michal.bajer@fujitsu.com> --- packages/cacti-ledger-browser/package.json | 10 -- .../main/typescript/CactiLedgerBrowserApp.tsx | 20 +-- .../src/main/typescript/apps/cacti/index.tsx | 20 --- .../apps/cacti/pages/status-page.tsx | 48 ------ .../src/main/typescript/apps/cacti/queries.ts | 5 - .../src/main/typescript/apps/eth/index.tsx | 17 +- .../CertificateDetailsBox.tsx | 2 +- .../src/main/typescript/apps/fabric/index.tsx | 17 +- .../TransactionDetails/TranactionInfoCard.tsx | 2 +- .../src/main/typescript/common/config.tsx | 7 +- .../common/hook/use-persistence-app-status.ts | 31 ++++ .../src/main/typescript/common/queries.ts | 41 +++++ .../main/typescript/common/supabase-types.ts | 8 + .../main/typescript/common/token-standards.ts | 4 - .../src/main/typescript/common/types/app.ts | 24 ++- .../components/Layout/HeaderBar.tsx | 42 +---- .../PersistencePluginStatus.tsx | 78 +++++++++ .../typescript/components/WelcomePage.tsx | 12 -- .../components/ui/Button.module.css | 77 --------- .../main/typescript/components/ui/Button.tsx | 22 --- .../components/ui/CardWrapper.module.css | 85 ---------- .../typescript/components/ui/CardWrapper.tsx | 150 ----------------- .../typescript/components/ui/CustomTable.css | 53 ------ .../components/ui/CustomTable.module.css | 137 ---------------- .../typescript/components/ui/CustomTable.tsx | 127 --------------- .../EmptyTablePlaceholder.module.css | 7 - .../EmptyTablePlaceholder.tsx | 9 -- .../components/ui/Pagination.module.css | 51 ------ .../typescript/components/ui/Pagination.tsx | 63 -------- .../components/ui/Search.module.css | 43 ----- .../main/typescript/components/ui/Search.tsx | 38 ----- .../components/ui/StackedRowItems.tsx | 0 .../src/main/typescript/main.tsx | 3 + .../main/typescript/pages/home/AppCard.tsx | 151 ++++++++++++++++++ .../main/typescript/pages/home/HomePage.tsx | 31 ++++ .../src/main/typescript/theme.ts | 3 + 36 files changed, 414 insertions(+), 1024 deletions(-) delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/apps/cacti/index.tsx delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/apps/cacti/pages/status-page.tsx delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/apps/cacti/queries.ts create mode 100644 packages/cacti-ledger-browser/src/main/typescript/common/hook/use-persistence-app-status.ts create mode 100644 packages/cacti-ledger-browser/src/main/typescript/common/queries.ts delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/common/token-standards.ts create mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/PersistencePluginStatus/PersistencePluginStatus.tsx delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/WelcomePage.tsx delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/Button.module.css delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/Button.tsx delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/CardWrapper.module.css delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/CardWrapper.tsx delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.css delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.module.css delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.tsx delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/EmptyTablePlaceholder/EmptyTablePlaceholder.module.css delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/EmptyTablePlaceholder/EmptyTablePlaceholder.tsx delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/Pagination.module.css delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/Pagination.tsx delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/Search.module.css delete mode 100644 packages/cacti-ledger-browser/src/main/typescript/components/ui/Search.tsx rename packages/cacti-ledger-browser/src/main/typescript/{apps/fabric => }/components/ui/StackedRowItems.tsx (100%) create mode 100644 packages/cacti-ledger-browser/src/main/typescript/pages/home/AppCard.tsx create mode 100644 packages/cacti-ledger-browser/src/main/typescript/pages/home/HomePage.tsx diff --git a/packages/cacti-ledger-browser/package.json b/packages/cacti-ledger-browser/package.json index d4b857f5ae..cf570032a4 100644 --- a/packages/cacti-ledger-browser/package.json +++ b/packages/cacti-ledger-browser/package.json @@ -38,16 +38,6 @@ "name": "Tomasz Awramski", "email": "tomasz.awramski@fujitsu.com", "url": "https://www.fujitsu.com/global/" - }, - { - "name": "Eryk Baranowski", - "email": "eryk.baranowski@fujitsu.com", - "url": "https://www.fujitsu.com/global/" - }, - { - "name": "Barnaba Pawelczak", - "email": "barnaba.pawelczak@fujitsu.com", - "url": "https://www.fujitsu.com/global/" } ], "scripts": { diff --git a/packages/cacti-ledger-browser/src/main/typescript/CactiLedgerBrowserApp.tsx b/packages/cacti-ledger-browser/src/main/typescript/CactiLedgerBrowserApp.tsx index 05862d55bf..12e43e24dd 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/CactiLedgerBrowserApp.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/CactiLedgerBrowserApp.tsx @@ -7,7 +7,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { themeOptions } from "./theme"; import ContentLayout from "./components/Layout/ContentLayout"; import HeaderBar from "./components/Layout/HeaderBar"; -import WelcomePage from "./components/WelcomePage"; +import HomePage from "./pages/home/HomePage"; import { AppConfig, AppListEntry } from "./common/types/app"; import { patchAppRoutePath } from "./common/utils"; import { NotificationProvider } from "./common/context/NotificationContext"; @@ -22,8 +22,8 @@ type AppConfigProps = { function getAppList(appConfig: AppConfig[]) { const appList: AppListEntry[] = appConfig.map((app) => { return { - path: app.path, - name: app.name, + path: app.options.path, + name: app.appName, }; }); @@ -43,12 +43,12 @@ function getHeaderBarRoutes(appConfig: AppConfig[]) { const headerRoutesConfig = appConfig.map((app) => { return { - key: app.path, - path: `${app.path}/*`, + key: app.options.path, + path: `${app.options.path}/*`, element: ( <HeaderBar appList={appList} - path={app.path} + path={app.options.path} menuEntries={app.menuEntries} /> ), @@ -68,12 +68,12 @@ function getHeaderBarRoutes(appConfig: AppConfig[]) { function getContentRoutes(appConfig: AppConfig[]) { const appRoutes: RouteObject[] = appConfig.map((app) => { return { - key: app.path, - path: app.path, + key: app.options.path, + path: app.options.path, children: app.routes.map((route) => { return { key: route.path, - path: patchAppRoutePath(app.path, route.path), + path: patchAppRoutePath(app.options.path, route.path), element: route.element, children: route.children, }; @@ -84,7 +84,7 @@ function getContentRoutes(appConfig: AppConfig[]) { // Include landing / welcome page appRoutes.push({ index: true, - element: <WelcomePage />, + element: <HomePage />, }); return useRoutes([ diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/cacti/index.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/cacti/index.tsx deleted file mode 100644 index b87f2e2e38..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/apps/cacti/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { AppConfig } from "../../common/types/app"; -import StatusPage from "./pages/status-page"; - -const appConfig: AppConfig = { - name: "Status", - path: "/cacti", - menuEntries: [ - { - title: "Plugin Status", - url: "/", - }, - ], - routes: [ - { - element: <StatusPage />, - }, - ], -}; - -export default appConfig; diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/cacti/pages/status-page.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/cacti/pages/status-page.tsx deleted file mode 100644 index ed71b44257..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/apps/cacti/pages/status-page.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import CardWrapper from "../../../components/ui/CardWrapper"; -import { useQuery } from "@tanstack/react-query"; -import { persistencePluginStatusQuery } from "../queries"; - -function StatusPage() { - const { isSuccess, isError, data, error } = useQuery( - persistencePluginStatusQuery(), - ); - - if (isError) { - console.error("Data fetch error:", error); - } - - return ( - <div> - <CardWrapper - columns={ - { - schema: [ - { display: "Name", objProp: ["name"] }, - { display: "Instance ID", objProp: ["last_instance_id"] }, - { display: "Status", objProp: ["is_schema_initialized"] }, - { display: "Created at", objProp: ["created_at"] }, - { display: "Connected at", objProp: ["last_connected_at"] }, - ], - } as any - } - data={ - isSuccess - ? (data as any).map((p: any) => { - return { - ...p, - is_schema_initialized: p.is_schema_initialized - ? "Setup complete" - : "No schema", - }; - }) - : [] - } - title={"Persistence Plugins"} - display={"All"} - trimmed={false} - ></CardWrapper> - </div> - ); -} - -export default StatusPage; diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/cacti/queries.ts b/packages/cacti-ledger-browser/src/main/typescript/apps/cacti/queries.ts deleted file mode 100644 index 321dce4abf..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/apps/cacti/queries.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { supabaseQueryTable } from "../../common/supabase-client"; - -export function persistencePluginStatusQuery() { - return supabaseQueryTable("plugin_status"); -} diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/eth/index.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/eth/index.tsx index 026aa83122..9ee731b528 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/apps/eth/index.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/eth/index.tsx @@ -1,12 +1,19 @@ -import { AppConfig } from "../../common/types/app"; import Dashboard from "./pages/Dashboard/Dashboard"; import Blocks from "./pages/Blocks/Blocks"; import Transactions from "./pages/Transactions/Transactions"; import Accounts from "./pages/Accounts/Accounts"; +import { AppConfig } from "../../common/types/app"; +import { usePersistenceAppStatus } from "../../common/hook/use-persistence-app-status"; +import PersistencePluginStatus from "../../components/PersistencePluginStatus/PersistencePluginStatus"; const ethConfig: AppConfig = { - name: "Ethereum", - path: "/eth", + appName: "Ethereum Browser", + options: { + instanceName: "Ethereum", + description: + "Applicaion for browsing Ethereum ledger blocks, transactions and tokens. Requires Ethereum persistence plugin to work correctly.", + path: "/eth", + }, menuEntries: [ { title: "Dashboard", @@ -34,6 +41,10 @@ const ethConfig: AppConfig = { element: <Accounts />, }, ], + useAppStatus: () => usePersistenceAppStatus("PluginPersistenceEthereum"), + StatusComponent: ( + <PersistencePluginStatus pluginName="PluginPersistenceEthereum" /> + ), }; export default ethConfig; diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/components/CertificateDetails/CertificateDetailsBox.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/components/CertificateDetails/CertificateDetailsBox.tsx index df79eac865..b7d61dc634 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/components/CertificateDetails/CertificateDetailsBox.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/components/CertificateDetails/CertificateDetailsBox.tsx @@ -4,7 +4,7 @@ import TextField from "@mui/material/TextField"; import { styled } from "@mui/material/styles"; import { FabricCertificate } from "../../fabric-supabase-types"; -import StackedRowItems from "../ui/StackedRowItems"; +import StackedRowItems from "../../../../components/ui/StackedRowItems"; const ListHeaderTypography = styled(Typography)(({ theme }) => ({ color: theme.palette.secondary.main, diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/index.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/index.tsx index e534033c98..437410665c 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/index.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/index.tsx @@ -1,13 +1,20 @@ -import { AppConfig } from "../../common/types/app"; import Dashboard from "./pages/Dashboard/Dashboard"; import Blocks from "./pages/Blocks/Blocks"; import Transactions from "./pages/Transactions/Transactions"; import { Outlet } from "react-router-dom"; import TransactionDetails from "./pages/TransactionDetails/TransactionDetails"; +import { AppConfig } from "../../common/types/app"; +import { usePersistenceAppStatus } from "../../common/hook/use-persistence-app-status"; +import PersistencePluginStatus from "../../components/PersistencePluginStatus/PersistencePluginStatus"; const fabricConfig: AppConfig = { - name: "Fabric", - path: "/fabric", + appName: "Hyperledger Fabric Browser", + options: { + instanceName: "Fabric", + description: + "Applicaion for browsing Hyperledger Fabric ledger blocks and transactions. Requires Fabric persistence plugin to work correctly.", + path: "/fabric", + }, menuEntries: [ { title: "Dashboard", @@ -41,6 +48,10 @@ const fabricConfig: AppConfig = { ], }, ], + useAppStatus: () => usePersistenceAppStatus("PluginPersistenceFabric"), + StatusComponent: ( + <PersistencePluginStatus pluginName="PluginPersistenceFabric" /> + ), }; export default fabricConfig; diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/TransactionDetails/TranactionInfoCard.tsx b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/TransactionDetails/TranactionInfoCard.tsx index 055874a698..c1be9680dd 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/TransactionDetails/TranactionInfoCard.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/pages/TransactionDetails/TranactionInfoCard.tsx @@ -6,7 +6,7 @@ import Skeleton from "@mui/material/Skeleton"; import { FabricTransaction } from "../../fabric-supabase-types"; import ShortenedTypography from "../../../../components/ui/ShortenedTypography"; -import StackedRowItems from "../../components/ui/StackedRowItems"; +import StackedRowItems from "../../../../components/ui/StackedRowItems"; const ListHeaderTypography = styled(Typography)(({ theme }) => ({ color: theme.palette.secondary.main, diff --git a/packages/cacti-ledger-browser/src/main/typescript/common/config.tsx b/packages/cacti-ledger-browser/src/main/typescript/common/config.tsx index a7df619503..102724e64e 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/common/config.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/common/config.tsx @@ -1,10 +1,5 @@ -import cactiGuiConfig from "../apps/cacti/index"; import ethereumGuiConfig from "../apps/eth"; import fabricAppConfig from "../apps/fabric"; import { AppConfig } from "./types/app"; -export const appConfig: AppConfig[] = [ - cactiGuiConfig, - ethereumGuiConfig, - fabricAppConfig, -]; +export const appConfig: AppConfig[] = [ethereumGuiConfig, fabricAppConfig]; diff --git a/packages/cacti-ledger-browser/src/main/typescript/common/hook/use-persistence-app-status.ts b/packages/cacti-ledger-browser/src/main/typescript/common/hook/use-persistence-app-status.ts new file mode 100644 index 0000000000..e2dd346b5a --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/common/hook/use-persistence-app-status.ts @@ -0,0 +1,31 @@ +import React from "react"; +import { useQuery } from "@tanstack/react-query"; +import { GetStatusResponse } from "../types/app"; +import { useNotification } from "../context/NotificationContext"; +import { persistencePluginStatus } from "../queries"; + +/** + * Return status of given persistence plugin from the database. + * + * @param pluginName name of the plugin (as set by the persistence plugin itself) + */ +export function usePersistenceAppStatus(pluginName: string): GetStatusResponse { + const { isError, isPending, data, error } = useQuery( + persistencePluginStatus(pluginName), + ); + const { showNotification } = useNotification(); + + React.useEffect(() => { + isError && + showNotification(`Could get ${pluginName} status: ${error}`, "error"); + }, [isError]); + + return { + isPending, + isInitialized: data?.is_schema_initialized ?? false, + status: { + severity: "info", + message: "Unknown", + }, + }; +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/common/queries.ts b/packages/cacti-ledger-browser/src/main/typescript/common/queries.ts new file mode 100644 index 0000000000..11eff447eb --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/common/queries.ts @@ -0,0 +1,41 @@ +import { createClient } from "@supabase/supabase-js"; +import { queryOptions } from "@tanstack/react-query"; +import { PluginStatus } from "./supabase-types"; + +const supabaseQueryKey = "supabase"; +const supabaseUrl = "http://localhost:8000"; +const supabaseKey = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE"; + +export const supabase = createClient(supabaseUrl, supabaseKey); + +/** + * Get persistence plugin status from the database using it's name. + */ +export function persistencePluginStatus(name: string) { + const tableName = "plugin_status"; + + return queryOptions({ + queryKey: [supabaseQueryKey, tableName, name], + queryFn: async () => { + const { data, error } = await supabase + .from(tableName) + .select() + .match({ name }); + + if (error) { + throw new Error( + `Could not get persistence plugin status with name ${name}: ${error.message}`, + ); + } + + if (data.length !== 1) { + throw new Error( + `Invalid response when persistence plugin status with name ${name}: ${data}`, + ); + } + + return data.pop() as PluginStatus; + }, + }); +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/common/supabase-types.ts b/packages/cacti-ledger-browser/src/main/typescript/common/supabase-types.ts index 373e985e86..465a7701f4 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/common/supabase-types.ts +++ b/packages/cacti-ledger-browser/src/main/typescript/common/supabase-types.ts @@ -108,3 +108,11 @@ export interface TokenERC20 { total_supply: number; token_address: string; } + +export interface PluginStatus { + name: string; + last_instance_id: string; + is_schema_initialized: boolean; + created_at: string; + last_connected_at: string; +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/common/token-standards.ts b/packages/cacti-ledger-browser/src/main/typescript/common/token-standards.ts deleted file mode 100644 index 65ad5a514c..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/common/token-standards.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const STANDARDS = { - erc20: "ERC20", - erc721: "ERC721", -}; diff --git a/packages/cacti-ledger-browser/src/main/typescript/common/types/app.ts b/packages/cacti-ledger-browser/src/main/typescript/common/types/app.ts index fa30b3ed2d..3374b557aa 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/common/types/app.ts +++ b/packages/cacti-ledger-browser/src/main/typescript/common/types/app.ts @@ -1,3 +1,4 @@ +import React from "react"; import { RouteObject } from "react-router-dom"; export interface AppListEntry { @@ -10,9 +11,28 @@ export interface AppConfigMenuEntry { url: string; } -export interface AppConfig { - name: string; +export interface AppStatus { + severity: "success" | "info" | "warning" | "error"; + message: string; +} + +export interface GetStatusResponse { + isPending: boolean; + isInitialized: boolean; + status: AppStatus; +} + +export interface AppConfigOptions { + instanceName: string; + description: string | undefined; path: string; +} + +export interface AppConfig { + appName: string; + options: AppConfigOptions; menuEntries: AppConfigMenuEntry[]; routes: RouteObject[]; + useAppStatus: () => GetStatusResponse; + StatusComponent: React.ReactElement; } diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/Layout/HeaderBar.tsx b/packages/cacti-ledger-browser/src/main/typescript/components/Layout/HeaderBar.tsx index 00482c9cf8..7f23912c14 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/components/Layout/HeaderBar.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/components/Layout/HeaderBar.tsx @@ -4,14 +4,9 @@ import AppBar from "@mui/material/AppBar"; import Box from "@mui/material/Box"; import Toolbar from "@mui/material/Toolbar"; import IconButton from "@mui/material/IconButton"; -import MenuIcon from "@mui/icons-material/Menu"; +import AppsIcon from "@mui/icons-material/Apps"; import Button from "@mui/material/Button"; import Tooltip from "@mui/material/Tooltip"; -import Drawer from "@mui/material/Drawer"; -import List from "@mui/material/List"; -import ListItem from "@mui/material/ListItem"; -import ListItemButton from "@mui/material/ListItemButton"; -import ListItemText from "@mui/material/ListItemText"; import { AppConfigMenuEntry, AppListEntry } from "../../common/types/app"; import { patchAppRoutePath } from "../../common/utils"; @@ -21,31 +16,7 @@ type HeaderBarProps = { menuEntries?: AppConfigMenuEntry[]; }; -const HeaderBar: React.FC<HeaderBarProps> = ({ - appList, - path, - menuEntries, -}) => { - const [isAppSelectOpen, setIsAppSelectOpen] = React.useState(false); - - const AppSelectDrawer = ( - <Box - sx={{ width: 250 }} - role="presentation" - onClick={() => setIsAppSelectOpen(false)} - > - <List> - {appList.map((app) => ( - <ListItem key={app.name} disablePadding> - <ListItemButton component={RouterLink} to={app.path}> - <ListItemText primary={app.name} /> - </ListItemButton> - </ListItem> - ))} - </List> - </Box> - ); - +const HeaderBar: React.FC<HeaderBarProps> = ({ path, menuEntries }) => { return ( <AppBar position="static" sx={{ paddingX: 2 }}> <Toolbar disableGutters> @@ -56,9 +27,10 @@ const HeaderBar: React.FC<HeaderBarProps> = ({ color="inherit" aria-label="select-application-button" sx={{ mr: 2 }} - onClick={() => setIsAppSelectOpen(true)} + component={RouterLink} + to={"/"} > - <MenuIcon /> + <AppsIcon /> </IconButton> </Tooltip> @@ -77,10 +49,6 @@ const HeaderBar: React.FC<HeaderBarProps> = ({ </Box> )} </Toolbar> - - <Drawer open={isAppSelectOpen} onClose={() => setIsAppSelectOpen(false)}> - {AppSelectDrawer} - </Drawer> </AppBar> ); }; diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/PersistencePluginStatus/PersistencePluginStatus.tsx b/packages/cacti-ledger-browser/src/main/typescript/components/PersistencePluginStatus/PersistencePluginStatus.tsx new file mode 100644 index 0000000000..520dc81d5c --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/components/PersistencePluginStatus/PersistencePluginStatus.tsx @@ -0,0 +1,78 @@ +import React from "react"; +import { useQuery } from "@tanstack/react-query"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import CircularProgress from "@mui/material/CircularProgress"; + +import StackedRowItems from "../ui/StackedRowItems"; +import { persistencePluginStatus } from "../../common/queries"; +import { useNotification } from "../../common/context/NotificationContext"; + +type DateTimeStringProps = { + dateString: string | undefined; +}; + +function DateTimeString({ dateString }: DateTimeStringProps) { + const date = dateString ? new Date(dateString) : new Date(); + + return <Typography>{date.toLocaleString()}</Typography>; +} + +type PersistencePluginStatusProps = { + pluginName: string; +}; + +/** + * Box that fetches and displays persistence plugin status from the database. + */ +export default function PersistencePluginStatus({ + pluginName, +}: PersistencePluginStatusProps) { + const { isError, isPending, data, error } = useQuery( + persistencePluginStatus(pluginName), + ); + const { showNotification } = useNotification(); + + React.useEffect(() => { + isError && + showNotification(`Could get ${pluginName} status: ${error}`, "error"); + }, [isError]); + + return ( + <Box> + {isPending && ( + <CircularProgress + style={{ + position: "absolute", + top: "50%", + left: "50%", + zIndex: 9999, + }} + /> + )} + <Typography variant="h5">Persistence Plugin Status</Typography> + <StackedRowItems> + <Typography>Plugin Name:</Typography> + <Typography>{data?.name}</Typography> + </StackedRowItems> + <StackedRowItems> + <Typography>Instance ID:</Typography> + <Typography>{data?.last_instance_id}</Typography> + </StackedRowItems> + <StackedRowItems> + <Typography>Is Schema Initialized:</Typography> + <Typography> + {data?.is_schema_initialized ? "True" : "False"} + </Typography> + </StackedRowItems> + <StackedRowItems> + <Typography>Created At:</Typography> + <DateTimeString dateString={data?.created_at} /> + </StackedRowItems> + <StackedRowItems> + <Typography>Last Connected At:</Typography> + <DateTimeString dateString={data?.last_connected_at} /> + </StackedRowItems> + </Box> + ); +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/WelcomePage.tsx b/packages/cacti-ledger-browser/src/main/typescript/components/WelcomePage.tsx deleted file mode 100644 index 27c5d249d2..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/WelcomePage.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import Card from "@mui/material/Card"; - -const WelcomePage: React.FC = () => { - return ( - <Card elevation={0} sx={{ textAlign: "center" }}> - <h1>Cacti Ledger Browser</h1> - <h3>Select an application to start from top-left menu</h3> - </Card> - ); -}; - -export default WelcomePage; diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Button.module.css b/packages/cacti-ledger-browser/src/main/typescript/components/ui/Button.module.css deleted file mode 100644 index eab9eabd84..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Button.module.css +++ /dev/null @@ -1,77 +0,0 @@ -.button { - color: rgb(14, 48, 23); - background-color: rgb(248, 248, 250); - height: 2.5rem; - display: flex; - align-items: center; - justify-content: center; - width: max-content; - min-width: 100px; - padding: 10px; - border: 1px solid rgb(32, 133, 77); - font-family: 'Roboto'; - border-radius: 10px; -} - -.button:hover { - background-color: rgb(219, 219, 224); - transform: scale(1.01); - cursor: pointer; -} - -.button-primary { - background-color: rgb(244, 247, 245); - color:rgb(14, 44, 14); - border-radius: 5px; - width:150px; -} - -.button-primary:hover { background-color: rgb(226, 253, 219);} - -.button-warn { background-color: rgb(155, 22, 13);} - -.button-warn:hover { background-color: rgb(114, 22, 16);} - -.button-menu{ - border:none; - background: transparent; - height: 100%; - transition: background-color 0.5s ease-out; - position:relative; - border-radius: 0; - -} -.button-menu:hover{ -color:rgb(0, 0, 0); -background-color: rgb(243, 242, 242); -} -.button-menu:hover:after { - content: ''; - display: block; - position: absolute; - left: 0; - right: 0; - bottom: 1px; - width: 100%; - height: 1px; - border-bottom: 2px solid green; - -} - -.button-link { - background: transparent; - border:none; - height: min-content; - color:rgb(64, 64, 228); -} - -.button-link:hover{ - background: transparent; - color:rgb(78, 78, 236); -} - -@media (max-width: 1699px) { - .button-primary { - font-size: 1rem; - } -} \ No newline at end of file diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Button.tsx b/packages/cacti-ledger-browser/src/main/typescript/components/ui/Button.tsx deleted file mode 100644 index 1d7360b057..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Button.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import styles from "./Button.module.css"; - -function Button(props: any) { - type ObjectKey = keyof typeof styles; - const buttonTypeStyle = `button-${props.type}` as ObjectKey; - - const handleClick = (e: { stopPropagation: () => void; }) => { - e.stopPropagation(); - props.onClick(); - }; - - return ( - <button - onClick={handleClick} - className={styles.button + " " + styles[buttonTypeStyle]} - > - {props.children} - </button> - ); -} - -export default Button; diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/CardWrapper.module.css b/packages/cacti-ledger-browser/src/main/typescript/components/ui/CardWrapper.module.css deleted file mode 100644 index d20ba47158..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/CardWrapper.module.css +++ /dev/null @@ -1,85 +0,0 @@ -.wrapper { - background-color: rgb(253, 253, 253); - padding: 1rem; - border-radius: 10px; - border: 1px solid rgb(233, 236, 233); - height: fit-content; -} - -.wrapper-half-width { - width: 50%; -} - -.wrapper-full-width { - width: 100%; -} - -.wrapper-cards { - width: 100%; - display: flex; - justify-content: center; - padding: 1rem; -} - -.wrapper-title { - margin-top: .5rem; - display: flex; - gap:5px; - align-items: center; - font-weight: 700; - font-size: 1.2rem; - color: rgb(9, 75, 9); -} - -.wrapper-btns { - display: flex; - justify-content: flex-end; - padding-right: 1rem; -} - -.wrapper-header { - width: 100%; - display: flex; - justify-content: space-between; - padding: 0 1rem; -} - -.wrapper-columns { - display: flex; - justify-content: space-around; - background-color: rgb(243, 239, 239); - align-items: center; - border-radius: 10px; - border: 1px solid rgb(233, 236, 233); - height: 50px; -} - -.wrapper-columns span { - display: flex; - width: 150px; -} - -.wrapper-search { - display: flex; - gap: 5px; -} - -@media (max-width: 1699px) { - .wrapper { - width: 100%; - } - - .wrapper-header { - padding-left: 0; - padding-right: 0; - } - - .wrapper-cards { - flex-direction: column; - padding: 1rem 0; - } - - .wrapper-title svg { - margin-bottom: -3px; - } -} diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/CardWrapper.tsx b/packages/cacti-ledger-browser/src/main/typescript/components/ui/CardWrapper.tsx deleted file mode 100644 index 5bb19315c5..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/CardWrapper.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import Button from "./Button"; -import Search from "./Search"; - -import CustomTable from "./CustomTable"; - -import Pagination from "./Pagination"; -import EmptyTablePlaceholder from "./EmptyTablePlaceholder/EmptyTablePlaceholder"; -import styles from "./CardWrapper.module.css"; - -import { useLocation, useNavigate } from "react-router-dom"; -import { useEffect, useState } from "react"; - -const pageSize: number = 6; - -function CardWrapper(props: any) { - const location = useLocation(); - const path = location.pathname.split("/"); - const navigate = useNavigate(); - const [searchKey, setSearchKey] = useState(""); - let filteredData = props.data; - const [paginatedData, setPaginatedData] = useState<any[]>([]); - const [currentPage, setCurrentPage] = useState<number>(1); - const [totalPages, setTotalPages] = useState<number>(1); - const [viewport, setViewport] = useState(""); - - const handleGoToPage = (pageNumber: number) => { - if (pageNumber < 1 || pageNumber > totalPages) return; - setCurrentPage(pageNumber); - }; - - const handleNextPage = () => { - if (currentPage === totalPages) return; - setCurrentPage((prev) => prev + 1); - }; - - const handlePrevPage = () => { - if (currentPage === 1) return; - setCurrentPage((prev) => prev - 1); - }; - - const filterData = () => { - const { filters, data } = props; - if (searchKey.length === 0) { - filteredData = data; - return; - } - const newData = data.filter((row: any) => { - let isMatch: boolean = false; - filters?.forEach((property: string | number) => { - if (row[property]?.toString().toLowerCase().includes(searchKey)) { - isMatch = true; - } - }); - return isMatch; - }); - filteredData = newData; - }; - - const handleSearch = () => { - filterData(); - if (props.getSearchValue) { - props.getSearchValue(searchKey); - } - }; - - useEffect(() => { - const screenResized = () => - setViewport(window.innerWidth <= 1699 ? "small" : "wide"); - screenResized(); - window.addEventListener("resize", screenResized, true); - return () => { - window.removeEventListener("resize", screenResized, true); - }; - }, []); - - useEffect(() => { - if (filteredData.length <= pageSize) { - setPaginatedData(filteredData); - } else { - const firstEl = currentPage * pageSize - pageSize; - setPaginatedData(filteredData.slice(firstEl, firstEl + pageSize)); - } - }, [currentPage, filteredData]); - - useEffect(() => { - const pageNum = Math.ceil(filteredData.length / pageSize); - setTotalPages(pageNum); - }, [filteredData]); - - return ( - <section - className={`${styles["wrapper"]} ${ - props.display === "small" - ? styles["wrapper-half-width"] - : styles["wrapper-full-width"] - }`} - > - <header className={styles["wrapper-header"]}> - <span className={styles["wrapper-title"]}>{props.title}</span> - {props.trimmed && viewport === "small" && ( - <Button - type={"primary"} - onClick={() => navigate(`/${path[1]}/${props.title.toLowerCase()}`)} - > - View all - </Button> - )} - {props.filters && ( - <div className={styles["wrapper-search"]}> - <Search - onKeyUp={(e: any) => setSearchKey(e)} - type="text" - placeholder="Type to search" - /> - <Button onClick={handleSearch}>Search</Button> - </div> - )} - </header> - <div className={styles["wrapper-cards"]}> - {props?.columns && props.data?.length > 0 && ( - <CustomTable cols={props.columns} data={paginatedData} /> - )} - {props?.data?.length === 0 && <EmptyTablePlaceholder />} - </div> - <div className={styles["wrapper-btns"]}> - {" "} - {props.trimmed && viewport === "wide" && ( - <Button - type={"primary"} - onClick={() => navigate(`/${path[1]}/${props.title.toLowerCase()}`)} - > - View all - </Button> - )} - </div> - - {!props.trimmed && ( - <Pagination - current={currentPage} - total={totalPages} - goToPage={handleGoToPage} - goNextPage={handleNextPage} - goPrevPage={handlePrevPage} - /> - )} - </section> - ); -} - -export default CardWrapper; diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.css b/packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.css deleted file mode 100644 index d94c8789f0..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.css +++ /dev/null @@ -1,53 +0,0 @@ -table { - border-collapse: separate; - border-spacing: 0; - width: 100%; - } - - tbody tr { - background-color: rgb(248, 248, 248); - border: 1px solid rgb(219, 241, 232); - border-radius: 10px; - } - - tbody tr:hover { - cursor: pointer; - background-color: rgb(235, 240, 237); - } - - th { - background-color: rgb(240, 235, 235); - border-style: none; - border-bottom: solid 1px rgb(223, 218, 218); - padding: 10px; - } - - td { - min-height: 2rem; - border-style: none; - border-bottom: solid 4px rgb(255, 255, 255); - padding: 1.5rem .5rem; - text-align: center; - } - - tr { - min-height: 20rem; - background-color: rgb(90, 103, 116); - padding: 1rem; - } - - tr:first-child th:first-child { - border-top-left-radius: 10px; - } - - tr:first-child th:last-child { - border-top-right-radius: 10px; - } - - tr:last-child td:first-child { - border-bottom-left-radius: 10px; - } - - tr:last-child td:last-child { - border-bottom-right-radius: 10px; - } \ No newline at end of file diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.module.css b/packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.module.css deleted file mode 100644 index 888d6db07b..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.module.css +++ /dev/null @@ -1,137 +0,0 @@ -.custom-table { - border-collapse: separate; - border-spacing: 0; - width: 100%; -} - -.custom-table tbody tr { - background-color: rgb(248, 248, 248); - border: 1px solid rgb(219, 241, 232); - border-radius: 10px; -} - -.custom-table tbody tr:hover { - cursor: pointer; - background-color: rgb(235, 240, 237); -} - -.custom-table th { - background-color: rgb(240, 235, 235); - border-style: none; - border-bottom: solid 1px rgb(155, 153, 153); - padding: 10px; -} - -.custom-table td { - min-height: 2rem; - border-style: none; - border-bottom: solid 2px rgb(255, 255, 255); - padding: 1.5rem 0.5rem; - text-align: center; -} - -.custom-table tr { - min-height: 20rem; - background-color: rgb(90, 103, 116); - padding: 1rem; -} - -.custom-table tr:first-child th:first-child { - border-top-left-radius: 10px; -} - -.custom-table tr:first-child th:last-child { - border-top-right-radius: 10px; -} - -.custom-table tr:last-child td:first-child { - border-bottom-left-radius: 10px; -} - -.custom-table tr:last-child td:last-child { - border-bottom-right-radius: 10px; -} - -@media (max-width: 1699px) { - table { - width: 100%; - margin-bottom: 0.75rem; - table-layout: fixed; - } - - table:hover td:nth-child(even) { - cursor: pointer; - background-color: rgb(235, 240, 237); - } - - td { - position: relative; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - padding: 0.75rem; - } - - .table-rwd { - border: solid 1px rgb(223, 218, 218); - overflow: hidden; - } - - .table-rwd td { - border-bottom: solid 2px rgb(253, 253, 253); - } - - .table-rwd-heading { - background-color: rgb(240, 235, 235); - border-style: none; - padding: 0.5rem; - width: 140px; - font-weight: 700; - font-size: 0.9rem; - border-right: solid 1px rgb(223, 218, 218); - } - - .table-rwd:first-child { - border-top-left-radius: 10px; - } - - .table-rwd:first-child { - border-top-right-radius: 10px; - } - - .table-rwd:last-child { - border-bottom-left-radius: 10px; - } - - .table-rwd:last-child { - border-bottom-right-radius: 10px; - } - - .table-rwd tr:last-child > td:nth-last-of-type(2) { - border-bottom: 0; - } - - .table-rwd tr:last-child > td:last-of-type { - border-bottom: 0; - } - - .table-rwd td:last-child { - text-align: left; - } - - tr:first-child th:first-child { - border-top-left-radius: 0; - } - - tr:first-child th:last-child { - border-top-right-radius: 0; - } - - tr:last-child td:first-child { - border-bottom-left-radius: 0; - } - - tr:last-child td:last-child { - border-bottom-right-radius: 0; - } -} diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.tsx b/packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.tsx deleted file mode 100644 index e3feb54943..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/CustomTable.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import EmptyTablePlaceholder from "./EmptyTablePlaceholder/EmptyTablePlaceholder"; -import styles from "./CustomTable.module.css"; -import { - useState, - useEffect, - ReactElement, - JSXElementConstructor, - ReactNode, - ReactPortal, -} from "react"; -import { TableProperty } from "../../common/supabase-types"; - -function CustomTable(props: any) { - const [viewport, setViewport] = useState(""); - - useEffect(() => { - const screenResized = () => - setViewport(window.innerWidth <= 1699 ? "small" : "wide"); - screenResized(); - window.addEventListener("resize", screenResized, true); - return () => { - window.removeEventListener("resize", screenResized, true); - }; - }, [viewport]); - - const getObjPropVal = (objProp: any[], row: any) => { - if (objProp.length === 1) return row[objProp[0]]; - else { - return objProp.map((prop) => ( - <> - {row[prop]} - <br></br> - </> - )); - } - }; - - const handleRowClick = (row: any) => { - props.cols.onClick.action(row[props.cols.onClick.prop]); - }; - - return ( - <> - {props.data.length === 0 ? ( - <EmptyTablePlaceholder /> - ) : ( - <> - {viewport === "wide" && ( - <table className={styles["custom-table"]}> - <thead> - <tr> - {props.cols.schema.map((col: any) => ( - <th>{col.display}</th> - ))} - </tr> - </thead> - <tbody> - {props.data.map((row: any) => { - return ( - <tr> - {props.cols.schema.map((col: TableProperty) => ( - <td onClick={() => handleRowClick(row)}> - {getObjPropVal(col.objProp, row)} - </td> - ))} - </tr> - ); - })} - </tbody> - </table> - )} - - {viewport === "small" && ( - <> - {props.data.map((row: any) => { - return ( - <table - className={`${styles["custom-table"]} ${styles["table-rwd"]}`} - onClick={() => handleRowClick(row)} - > - <tbody> - {props.cols.schema.map( - ( - heading: { - display: - | string - | number - | boolean - | ReactElement< - any, - string | JSXElementConstructor<any> - > - | Iterable<ReactNode> - | ReactPortal - | null - | undefined; - }, - idx: string | number, - ) => { - return ( - <tr> - <td className={styles["table-rwd-heading"]}> - {heading.display} - </td> - <td> - {getObjPropVal( - props.cols.schema[idx].objProp, - row, - )} - </td> - </tr> - ); - }, - )} - </tbody> - </table> - ); - })} - </> - )} - </> - )} - </> - ); -} - -export default CustomTable; diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/EmptyTablePlaceholder/EmptyTablePlaceholder.module.css b/packages/cacti-ledger-browser/src/main/typescript/components/ui/EmptyTablePlaceholder/EmptyTablePlaceholder.module.css deleted file mode 100644 index 8d9bbfa661..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/EmptyTablePlaceholder/EmptyTablePlaceholder.module.css +++ /dev/null @@ -1,7 +0,0 @@ -.placeholder-container { - display: flex; - justify-content: center; - font-size: 2rem; - font-weight: bold; - color: rgb(9, 75, 9); -} \ No newline at end of file diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/EmptyTablePlaceholder/EmptyTablePlaceholder.tsx b/packages/cacti-ledger-browser/src/main/typescript/components/ui/EmptyTablePlaceholder/EmptyTablePlaceholder.tsx deleted file mode 100644 index f788bcd3ab..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/EmptyTablePlaceholder/EmptyTablePlaceholder.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import styles from "./EmptyTablePlaceholder.module.css"; - -function EmptyTablePlaceholder() { - return ( - <div className={styles["placeholder-container"]}>No data available</div> - ); -} - -export default EmptyTablePlaceholder; diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Pagination.module.css b/packages/cacti-ledger-browser/src/main/typescript/components/ui/Pagination.module.css deleted file mode 100644 index 0632e5e845..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Pagination.module.css +++ /dev/null @@ -1,51 +0,0 @@ -.pagination { - width: 100%; - padding: 1rem; - justify-content: flex-end; - display: flex; - align-items: center; - gap: 10px; -} - -.pagination-counter { - height: 2.5rem; - display: flex; - align-items: center; - padding: 0 1rem; - border-radius: 10px; - border: 1px solid rgb(204, 206, 205); -} - -.pagination-jump { - display: flex; - gap: 10px; - padding: 9px 1rem; - background-color: rgb(233, 229, 229); - border-radius: 10px; -} - -input { - border-radius: 10px; - border: 1px solid rgb(54, 51, 224); - padding: 0 0.5rem; - width: 7rem; - text-align: center; - font-size: 1rem; -} - -@media (max-width: 1699px) { - .pagination { - padding: 0; - justify-content: center; - position: relative; - } - - .pagination button { - min-width: 85px; - } - - .pagination-jump { - position: absolute; - top: 2.75rem; - } -} diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Pagination.tsx b/packages/cacti-ledger-browser/src/main/typescript/components/ui/Pagination.tsx deleted file mode 100644 index 671f847bd5..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Pagination.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { useState } from "react"; -import Button from "./Button"; -import KeyboardArrowLeftIcon from "@mui/icons-material/KeyboardArrowLeft"; -import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight"; -import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft"; -import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; -import styles from "./Pagination.module.css"; - -type pagination = { - current: number; - total: number; - goToPage: (pageNumber: number) => void; - goNextPage: () => void; - goPrevPage: () => void; -}; - -function Pagination(props: any) { - let inputRef: any; - const getInputValue = () => - inputRef?.value ? inputRef.value : props.current; - const [goToPageVisible, setGoToPageVisible] = useState<boolean>(false); - - return ( - <div className={styles.pagination}> - <Button onClick={() => props.goToPage(1)}> - <KeyboardDoubleArrowLeftIcon /> - </Button> - <Button onClick={() => props.goPrevPage()}> - <KeyboardArrowLeftIcon /> - </Button> - <Button onClick={() => setGoToPageVisible((prev) => !prev)}> - {" "} - <span> - {props.current} / {props.total} - </span> - </Button> - {goToPageVisible === true && ( - <div className={styles["pagination-jump"]}> - <input - ref={inputRef} - id="number" - type="number" - min={1} - max={props.total} - placeholder={"Page"} - disabled={inputRef?.value} - /> - <Button onClick={() => props.goToPage(getInputValue())}> - Go to page - </Button> - </div> - )} - <Button onClick={() => props.goNextPage()}> - <KeyboardArrowRightIcon /> - </Button> - <Button onClick={() => props.goToPage(props.total)}> - <KeyboardDoubleArrowRightIcon /> - </Button> - </div> - ); -} - -export default Pagination; diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Search.module.css b/packages/cacti-ledger-browser/src/main/typescript/components/ui/Search.module.css deleted file mode 100644 index 13b6c8e9ac..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Search.module.css +++ /dev/null @@ -1,43 +0,0 @@ -.input { - border: none; - border-radius: 10px; - width: 30rem; - height: 2.5rem; - background-color: rgb(240, 236, 236); - padding: 1rem; - font-size: 16px; -} - -.input-wrapper { - position: relative; -} - -.input-reset { - background: transparent; - position: absolute; - right: 0.5rem; - border: none; - height: 2.5rem; - width: 2.5rem; - font-size: 1.5rem; - cursor: pointer; -} - -.input-reset:hover { - color: green; -} -.input-reset-icon { - pointer-events: none; -} - -@media (max-width: 1699px) { - .input { - width: 12rem; - text-align: left; - } - - .input-reset { - right: 0; - top: 0.1rem; - } -} diff --git a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Search.tsx b/packages/cacti-ledger-browser/src/main/typescript/components/ui/Search.tsx deleted file mode 100644 index a8f8ee77f1..0000000000 --- a/packages/cacti-ledger-browser/src/main/typescript/components/ui/Search.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useState } from "react"; -import styles from "./Search.module.css"; - -function Search(props: any) { - const [val, setValue] = useState<string>(""); - - const handleInput = (e: InputEvent | ClipboardEvent | React.FormEvent<any>) => { - const inputValue = (e.currentTarget as HTMLInputElement).value; - if (inputValue) { - setValue(inputValue); - props.onKeyUp(inputValue); - } - }; - - const handleReset = () => { - setValue(""); - props.onKeyUp(""); - }; - - return ( - <div className={styles["input-wrapper"]}> - <input - className={styles["input"]} - type={props.type} - placeholder={props.placeholder} - maxLength={32} - value={val} - onInput={(e) => handleInput(e)} - onPaste={(e) => handleInput(e)} - /> - <button className={styles["input-reset"]} onClick={handleReset}> - <i className={styles["input-reset-icon"]}>{/* <BiRegularReset /> */}</i> - </button> - </div> - ); -} - -export default Search; diff --git a/packages/cacti-ledger-browser/src/main/typescript/apps/fabric/components/ui/StackedRowItems.tsx b/packages/cacti-ledger-browser/src/main/typescript/components/ui/StackedRowItems.tsx similarity index 100% rename from packages/cacti-ledger-browser/src/main/typescript/apps/fabric/components/ui/StackedRowItems.tsx rename to packages/cacti-ledger-browser/src/main/typescript/components/ui/StackedRowItems.tsx diff --git a/packages/cacti-ledger-browser/src/main/typescript/main.tsx b/packages/cacti-ledger-browser/src/main/typescript/main.tsx index d35d7168b4..610c719003 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/main.tsx +++ b/packages/cacti-ledger-browser/src/main/typescript/main.tsx @@ -1,3 +1,6 @@ +// Needed to fix vite caching error of MUI - see https://github.com/vitejs/vite/issues/12423 +import "@mui/material/styles/styled"; + import * as React from "react"; import * as ReactDOM from "react-dom/client"; import { appConfig } from "./common/config"; diff --git a/packages/cacti-ledger-browser/src/main/typescript/pages/home/AppCard.tsx b/packages/cacti-ledger-browser/src/main/typescript/pages/home/AppCard.tsx new file mode 100644 index 0000000000..5386824c74 --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/pages/home/AppCard.tsx @@ -0,0 +1,151 @@ +import React from "react"; +import { useNavigate } from "react-router-dom"; +import { useTheme } from "@mui/material/styles"; +import Dialog from "@mui/material/Dialog"; +import DialogTitle from "@mui/material/DialogTitle"; +import DialogContent from "@mui/material/DialogContent"; +import Card from "@mui/material/Card"; +import CardActionArea from "@mui/material/CardActionArea"; +import CardActions from "@mui/material/CardActions"; +import CardContent from "@mui/material/CardContent"; +import CircularProgress from "@mui/material/CircularProgress"; +import Button from "@mui/material/Button"; +import Typography from "@mui/material/Typography"; + +import { AppConfig, AppStatus } from "../../common/types/app"; + +type StatusTextProps = { + status: AppStatus; +}; + +/** + * Application status text with color according to it's severity. + */ +function StatusText({ status }: StatusTextProps) { + const theme = useTheme(); + + return ( + <span style={{ color: theme.palette[status.severity].main }}> + {status.message} + </span> + ); +} + +type InitializedTextProps = { + isInitialized: boolean; +}; + +/** + * Application initialization status text - `error` color if not initialized, `success` otherwise. + */ +function InitializedText({ isInitialized }: InitializedTextProps) { + let text = "No"; + let textColor: "error" | "success" = "error"; + + if (isInitialized) { + text = "Yes"; + textColor = "success"; + } + + return <StatusText status={{ severity: textColor, message: text }} />; +} + +type StatusDialogButtonProps = { + statusComponent: React.ReactElement; +}; + +function StatusDialogButton({ statusComponent }: StatusDialogButtonProps) { + const [openDialog, setOpenDialog] = React.useState(false); + + return ( + <> + <Button onClick={() => setOpenDialog(true)}>Status</Button> + <Dialog + fullWidth + maxWidth="sm" + onClose={() => setOpenDialog(false)} + open={openDialog} + > + <DialogTitle color="primary">App Status</DialogTitle> + <DialogContent>{statusComponent}</DialogContent> + </Dialog> + </> + ); +} + +type AppCardProps = { + appConfig: AppConfig; +}; + +/** + * Application card component. Shows basic information and allows navigation to + * specific app on click. Has action for showing app status and configuration + * pop-ups. + */ +export default function AppCard({ appConfig }: AppCardProps) { + const navigate = useNavigate(); + const theme = useTheme(); + const status = appConfig.useAppStatus(); + + return ( + <Card + variant="outlined" + sx={{ + display: "flex", + flexDirection: "column", + width: 400, + }} + > + <CardActionArea + onClick={() => { + navigate(appConfig.options.path); + }} + > + <CardContent + sx={{ + flex: 1, + paddingBottom: 1, + }} + > + <Typography variant="h5" component="div" color="secondary.main"> + {appConfig.options.instanceName} + </Typography> + <Typography sx={{ mb: 1.5 }} color="text.secondary"> + {appConfig.appName} + </Typography> + {appConfig.options.description && ( + <Typography sx={{ mb: 1.5 }}> + {appConfig.options.description} + </Typography> + )} + <Typography> + Initialized:{" "} + {status.isPending ? ( + <CircularProgress size={17} /> + ) : ( + <InitializedText isInitialized={status.isInitialized} /> + )} + </Typography> + <Typography> + Status:{" "} + {status.isPending ? ( + <CircularProgress size={17} /> + ) : ( + <StatusText status={status.status} /> + )} + </Typography> + </CardContent> + </CardActionArea> + <CardActions + sx={{ + marginTop: 0, + justifyContent: "right", + borderTop: 1, + borderColor: theme.palette.primary.main, + }} + > + <StatusDialogButton statusComponent={appConfig.StatusComponent} /> + </CardActions> + </Card> + ); +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/pages/home/HomePage.tsx b/packages/cacti-ledger-browser/src/main/typescript/pages/home/HomePage.tsx new file mode 100644 index 0000000000..2539bcbb67 --- /dev/null +++ b/packages/cacti-ledger-browser/src/main/typescript/pages/home/HomePage.tsx @@ -0,0 +1,31 @@ +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; + +import { appConfig } from "../../common/config"; +import AppCard from "./AppCard"; + +export default function HomePage() { + return ( + <Box> + <Typography variant="h5" color="secondary"> + Applications + </Typography> + <Box + display="flex" + flexWrap="wrap" + justifyContent="space-around" + gap={5} + padding={5} + > + {appConfig.map((a) => { + return ( + <AppCard + key={`${a.appName}_${a.options.instanceName}`} + appConfig={a} + /> + ); + })} + </Box> + </Box> + ); +} diff --git a/packages/cacti-ledger-browser/src/main/typescript/theme.ts b/packages/cacti-ledger-browser/src/main/typescript/theme.ts index 3c8273ac2b..1a8ec83c1b 100644 --- a/packages/cacti-ledger-browser/src/main/typescript/theme.ts +++ b/packages/cacti-ledger-browser/src/main/typescript/theme.ts @@ -12,5 +12,8 @@ export const themeOptions: ThemeOptions = { warning: { main: "#D68C45", }, + info: { + main: "#5D4037", + }, }, };