diff --git a/packages/cacti-ledger-browser/README.md b/packages/cacti-ledger-browser/README.md
index 8dd06fd879..6030f200ef 100644
--- a/packages/cacti-ledger-browser/README.md
+++ b/packages/cacti-ledger-browser/README.md
@@ -46,7 +46,9 @@ npm install
- Run one or more persistence plugins:
- [Ethereum](../cacti-plugin-persistence-ethereum)
- [Fabric] (../cacti-plugin-persistence-fabric)
-- Edit [Supabase configuration file](./src/supabase-client.tsx), set correct supabase API URL and service_role key.
+- Edit Supabase configuration files, set correct supabase API URL and service_role key.
+ - ./src/main/typescript/common/supabase-client.tsx
+ - ./src/main/typescript/common/queries.ts
- Execute `yarn run start` or `npm start` in this package directory.
- The running application address: http://localhost:3001/ (can be changed in [Vite configuration](./vite.config.ts))
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..786bc979bd
--- /dev/null
+++ b/packages/cacti-ledger-browser/src/main/typescript/common/queries.ts
@@ -0,0 +1,40 @@
+import { createClient } from "@supabase/supabase-js";
+import { queryOptions } from "@tanstack/react-query";
+import { PluginStatus } from "./supabase-types";
+
+const supabaseQueryKey = "supabase";
+const supabaseUrl = "__SUPABASE_URL__";
+const supabaseKey = "__SUPABASE_KEY__";
+
+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-client.tsx b/packages/cacti-ledger-browser/src/main/typescript/common/supabase-client.tsx
index 6a609c7e1f..f8f8863a66 100644
--- a/packages/cacti-ledger-browser/src/main/typescript/common/supabase-client.tsx
+++ b/packages/cacti-ledger-browser/src/main/typescript/common/supabase-client.tsx
@@ -2,9 +2,9 @@ import { createClient } from "@supabase/supabase-js";
import { queryOptions } from "@tanstack/react-query";
export const supabaseQueryKey = "supabase";
-const supabaseUrl = "http://localhost:8000";
-const supabaseKey =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE";
+const supabaseUrl = "__SUPABASE_URL__";
+const supabaseKey = "__SUPABASE_KEY__";
+
export const supabase = createClient(supabaseUrl, supabaseKey);
/**
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?.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) => (
- {col.display} |
- ))}
-
-
-
- {props.data.map((row: any) => {
- return (
-
- {props.cols.schema.map((col: TableProperty) => (
- 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 (
+ <>
+
+
+ >
+ );
+}
+
+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",
+ },
},
};