diff --git a/packages/cacti-ledger-browser/package.json b/packages/cacti-ledger-browser/package.json index cc5e78453b..5c1a6999ae 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: ( ), @@ -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: , + element: , }); 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: , - }, - ], -}; - -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 ( -
- { - return { - ...p, - is_schema_initialized: p.is_schema_initialized - ? "Setup complete" - : "No schema", - }; - }) - : [] - } - title={"Persistence Plugins"} - display={"All"} - trimmed={false} - > -
- ); -} - -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: , }, ], + useAppStatus: () => usePersistenceAppStatus("PluginPersistenceEthereum"), + StatusComponent: ( + + ), }; 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: ( + + ), }; 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 = ({ - appList, - path, - menuEntries, -}) => { - const [isAppSelectOpen, setIsAppSelectOpen] = React.useState(false); - - const AppSelectDrawer = ( - setIsAppSelectOpen(false)} - > - - {appList.map((app) => ( - - - - - - ))} - - - ); - +const HeaderBar: React.FC = ({ path, menuEntries }) => { return ( @@ -56,9 +27,10 @@ const HeaderBar: React.FC = ({ color="inherit" aria-label="select-application-button" sx={{ mr: 2 }} - onClick={() => setIsAppSelectOpen(true)} + component={RouterLink} + to={"/"} > - + @@ -77,10 +49,6 @@ const HeaderBar: React.FC = ({ )} - - setIsAppSelectOpen(false)}> - {AppSelectDrawer} - ); }; 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 {date.toLocaleString()}; +} + +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 ( + + {isPending && ( + + )} + Persistence Plugin Status + + Plugin Name: + {data?.name} + + + Instance ID: + {data?.last_instance_id} + + + Is Schema Initialized: + + {data?.is_schema_initialized ? "True" : "False"} + + + + Created At: + + + + Last Connected At: + + + + ); +} 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 ( - -

Cacti Ledger Browser

-

Select an application to start from top-left menu

-
- ); -}; - -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 ( - - ); -} - -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([]); - const [currentPage, setCurrentPage] = useState(1); - const [totalPages, setTotalPages] = useState(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 ( -
-
- {props.title} - {props.trimmed && viewport === "small" && ( - - )} - {props.filters && ( -
- setSearchKey(e)} - type="text" - placeholder="Type to search" - /> - -
- )} -
-
- {props?.columns && props.data?.length > 0 && ( - - )} - {props?.data?.length === 0 && } -
-
- {" "} - {props.trimmed && viewport === "wide" && ( - - )} -
- - {!props.trimmed && ( - - )} -
- ); -} - -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]} -

- - )); - } - }; - - const handleRowClick = (row: any) => { - props.cols.onClick.action(row[props.cols.onClick.prop]); - }; - - return ( - <> - {props.data.length === 0 ? ( - - ) : ( - <> - {viewport === "wide" && ( - - - - {props.cols.schema.map((col: any) => ( - - ))} - - - - {props.data.map((row: any) => { - return ( - - {props.cols.schema.map((col: TableProperty) => ( - - ))} - - ); - })} - -
{col.display}
handleRowClick(row)}> - {getObjPropVal(col.objProp, row)} -
- )} - - {viewport === "small" && ( - <> - {props.data.map((row: any) => { - return ( - handleRowClick(row)} - > - - {props.cols.schema.map( - ( - heading: { - display: - | string - | number - | boolean - | ReactElement< - any, - string | JSXElementConstructor - > - | Iterable - | ReactPortal - | null - | undefined; - }, - idx: string | number, - ) => { - return ( - - - - - ); - }, - )} - -
- {heading.display} - - {getObjPropVal( - props.cols.schema[idx].objProp, - row, - )} -
- ); - })} - - )} - - )} - - ); -} - -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 ( -
No data available
- ); -} - -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(false); - - return ( -
- - - - {goToPageVisible === true && ( -
- - -
- )} - - -
- ); -} - -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(""); - - const handleInput = (e: InputEvent | ClipboardEvent | React.FormEvent) => { - const inputValue = (e.currentTarget as HTMLInputElement).value; - if (inputValue) { - setValue(inputValue); - props.onKeyUp(inputValue); - } - }; - - const handleReset = () => { - setValue(""); - props.onKeyUp(""); - }; - - return ( -
- handleInput(e)} - onPaste={(e) => handleInput(e)} - /> - -
- ); -} - -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 ( + + {status.message} + + ); +} + +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 ; +} + +type StatusDialogButtonProps = { + statusComponent: React.ReactElement; +}; + +function StatusDialogButton({ statusComponent }: StatusDialogButtonProps) { + const [openDialog, setOpenDialog] = React.useState(false); + + return ( + <> + + setOpenDialog(false)} + open={openDialog} + > + App Status + {statusComponent} + + + ); +} + +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 ( + + { + navigate(appConfig.options.path); + }} + > + + + {appConfig.options.instanceName} + + + {appConfig.appName} + + {appConfig.options.description && ( + + {appConfig.options.description} + + )} + + Initialized:{" "} + {status.isPending ? ( + + ) : ( + + )} + + + Status:{" "} + {status.isPending ? ( + + ) : ( + + )} + + + + + + + + ); +} 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 ( + + + Applications + + + {appConfig.map((a) => { + return ( + + ); + })} + + + ); +} 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", + }, }, };