generated from Luis/nextjs-python-web-template
Merge pull request 'Added Sequence Diagram with Mermaid' (#51) from mermaid into main
Reviewed-on: #51
This commit was merged in pull request #51.
This commit is contained in:
@@ -1,10 +1,19 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
"extends": ["next/core-web-vitals", "plugin:tailwindcss/recommended", "plugin:@typescript-eslint/recommended"],
|
"extends": [
|
||||||
|
"next/core-web-vitals",
|
||||||
|
"plugin:tailwindcss/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"plugins": ["@typescript-eslint"],
|
"plugins": [
|
||||||
"ignorePatterns": ["**/src/api/*"],
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"ignorePatterns": [
|
||||||
|
"**/src/api/*"
|
||||||
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-explicit-any": "off"
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"tailwindcss/no-custom-classname": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1
pkgs/ui/_document.js
Normal file
1
pkgs/ui/_document.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>;
|
||||||
File diff suppressed because it is too large
Load Diff
1067
pkgs/ui/package-lock.json
generated
1067
pkgs/ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,7 @@
|
|||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"hex-rgb": "^5.0.0",
|
"hex-rgb": "^5.0.0",
|
||||||
|
"mermaid": "^10.6.1",
|
||||||
"next": "13.4.12",
|
"next": "13.4.12",
|
||||||
"postcss": "8.4.27",
|
"postcss": "8.4.27",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ export default function Client({
|
|||||||
setSnackbarOpen(false);
|
setSnackbarOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log("entity", entity);
|
||||||
|
|
||||||
if (services_loading) return <Skeleton height={500} />;
|
if (services_loading) return <Skeleton height={500} />;
|
||||||
if (!services) return <Alert severity="error">Client not found</Alert>;
|
if (!services) return <Alert severity="error">Client not found</Alert>;
|
||||||
|
|
||||||
@@ -193,7 +195,7 @@ export default function Client({
|
|||||||
IP: <code>{entity?.ip}</code>
|
IP: <code>{entity?.ip}</code>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography color="text.primary" gutterBottom>
|
<Typography color="text.primary" gutterBottom>
|
||||||
Network: <code>{entity?.other?.network}</code>
|
Network: <code>{entity?.network}</code>
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -3,18 +3,26 @@
|
|||||||
import { DLGResolutionTableConfig, DLGSummaryDetails } from "@/config/dlg";
|
import { DLGResolutionTableConfig, DLGSummaryDetails } from "@/config/dlg";
|
||||||
import CustomTable from "@/components/table";
|
import CustomTable from "@/components/table";
|
||||||
import SummaryDetails from "@/components/summary_card";
|
import SummaryDetails from "@/components/summary_card";
|
||||||
import useFetch from "@/components/hooks/useFetch";
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
import { useGetAllResolutions } from "@/api/resolution/resolution";
|
||||||
|
import { mutate } from "swr";
|
||||||
|
|
||||||
export default function DLG() {
|
export default function DLG() {
|
||||||
const {
|
const {
|
||||||
data: resolutionData,
|
data: resolutionData,
|
||||||
loading: loadingResolutions,
|
isLoading: loadingResolutions,
|
||||||
fetch,
|
swrKey: resolutionsKeyFunc,
|
||||||
} = useFetch("/get_resolutions");
|
} = useGetAllResolutions();
|
||||||
|
|
||||||
const onRefresh = () => {
|
const onRefresh = () => {
|
||||||
fetch();
|
const resolutionsKey =
|
||||||
|
typeof resolutionsKeyFunc === "function"
|
||||||
|
? resolutionsKeyFunc()
|
||||||
|
: resolutionsKeyFunc;
|
||||||
|
|
||||||
|
if (resolutionsKey) {
|
||||||
|
mutate(resolutionsKey);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -41,7 +49,7 @@ export default function DLG() {
|
|||||||
<h4>DID Resolution View</h4>
|
<h4>DID Resolution View</h4>
|
||||||
<CustomTable
|
<CustomTable
|
||||||
loading={loadingResolutions}
|
loading={loadingResolutions}
|
||||||
data={resolutionData}
|
data={resolutionData?.data}
|
||||||
configuration={DLGResolutionTableConfig}
|
configuration={DLGResolutionTableConfig}
|
||||||
tkey="resolution_table"
|
tkey="resolution_table"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useAppState } from "@/components/hooks/useAppContext";
|
import { useAppState } from "@/components/hooks/useAppContext";
|
||||||
import { NoDataOverlay } from "@/components/noDataOverlay";
|
|
||||||
import SummaryDetails from "@/components/summary_card";
|
import SummaryDetails from "@/components/summary_card";
|
||||||
import CustomTable from "@/components/table";
|
import CustomTable from "@/components/table";
|
||||||
import { HomeTableConfig } from "@/config/home";
|
import { HomeTableConfig } from "@/config/home";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
|
|
||||||
|
const NoSSRSequenceDiagram = dynamic(
|
||||||
|
() => import("../../components/sequence_diagram"),
|
||||||
|
{ ssr: false },
|
||||||
|
);
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const { data } = useAppState();
|
const { data } = useAppState();
|
||||||
|
|
||||||
@@ -51,7 +56,7 @@ export default function Home() {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h4>Sequence Diagram</h4>
|
<h4>Sequence Diagram</h4>
|
||||||
<NoDataOverlay label="No Activity yet" />
|
<NoSSRSequenceDiagram />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
CssBaseline,
|
CssBaseline,
|
||||||
IconButton,
|
IconButton,
|
||||||
ThemeProvider,
|
ThemeProvider,
|
||||||
|
Tooltip,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { StyledEngineProvider } from "@mui/material/styles";
|
import { StyledEngineProvider } from "@mui/material/styles";
|
||||||
@@ -50,6 +51,13 @@ export default function RootLayout({
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="Service Aware Networks" />
|
<meta name="description" content="Service Aware Networks" />
|
||||||
<link rel="icon" href="tub-favicon.ico" sizes="any" />
|
<link rel="icon" href="tub-favicon.ico" sizes="any" />
|
||||||
|
{/* <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script> */}
|
||||||
|
<script
|
||||||
|
// eslint-disable-next-line react/no-danger
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `mermaid.initialize({startOnLoad: true});`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</head>
|
</head>
|
||||||
<StyledEngineProvider injectFirst>
|
<StyledEngineProvider injectFirst>
|
||||||
<ThemeProvider theme={userPrefersDarkmode ? darkTheme : lightTheme}>
|
<ThemeProvider theme={userPrefersDarkmode ? darkTheme : lightTheme}>
|
||||||
@@ -77,6 +85,7 @@ export default function RootLayout({
|
|||||||
>
|
>
|
||||||
<div className="grid grid-cols-3">
|
<div className="grid grid-cols-3">
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
|
<Tooltip placement="right" title="Expand Sidebar">
|
||||||
<IconButton
|
<IconButton
|
||||||
style={{ padding: "12px" }}
|
style={{ padding: "12px" }}
|
||||||
hidden={true}
|
hidden={true}
|
||||||
@@ -84,6 +93,7 @@ export default function RootLayout({
|
|||||||
>
|
>
|
||||||
{!showSidebar && <MenuIcon />}
|
{!showSidebar && <MenuIcon />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
136
pkgs/ui/src/components/sequence_diagram/helpers.ts
Normal file
136
pkgs/ui/src/components/sequence_diagram/helpers.ts
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import { Eventmessage } from "@/api/model";
|
||||||
|
|
||||||
|
export const generateMermaidString = (data: Eventmessage[] | undefined) => {
|
||||||
|
if (!data || data.length === 0) return "";
|
||||||
|
|
||||||
|
const participants = Array.from(
|
||||||
|
new Set(data.flatMap((item) => [item.src_did, item.des_did])),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mermaidString = "sequenceDiagram\n";
|
||||||
|
|
||||||
|
participants.forEach((participant, index) => {
|
||||||
|
mermaidString += ` participant ${String.fromCharCode(
|
||||||
|
65 + index,
|
||||||
|
)} as ${participant}\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
let currentGroupId: number | null = null;
|
||||||
|
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
const srcParticipant = String.fromCharCode(
|
||||||
|
65 + participants.indexOf(item.src_did),
|
||||||
|
);
|
||||||
|
const desParticipant = String.fromCharCode(
|
||||||
|
65 + participants.indexOf(item.des_did),
|
||||||
|
);
|
||||||
|
const timestamp = new Date(item.timestamp * 1000).toLocaleString();
|
||||||
|
const message = item.msg.text || `Event message ${index + 1}`;
|
||||||
|
|
||||||
|
if (item.group_id !== currentGroupId) {
|
||||||
|
if (currentGroupId !== null) {
|
||||||
|
mermaidString += ` end\n`;
|
||||||
|
}
|
||||||
|
mermaidString += ` alt Group ${item.group_id}\n`;
|
||||||
|
currentGroupId = item.group_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
mermaidString += ` ${srcParticipant}->>${desParticipant}: [${timestamp}] ${message}\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (currentGroupId !== null) {
|
||||||
|
mermaidString += ` end\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mermaidString;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dummy Data
|
||||||
|
|
||||||
|
export const dataFromBE = [
|
||||||
|
{
|
||||||
|
id: 12,
|
||||||
|
timestamp: 1704892813,
|
||||||
|
group: 0,
|
||||||
|
group_id: 12,
|
||||||
|
// "group_name": "Data",
|
||||||
|
msg_type: 4,
|
||||||
|
src_did: "did:sov:test:121",
|
||||||
|
// "src_name": "Entity A",
|
||||||
|
des_did: "did:sov:test:120",
|
||||||
|
// "des_name": "Entity B",
|
||||||
|
msg: {
|
||||||
|
text: "Hello World",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 60,
|
||||||
|
timestamp: 1704892823,
|
||||||
|
group: 1,
|
||||||
|
group_id: 19,
|
||||||
|
msg_type: 4,
|
||||||
|
src_did: "did:sov:test:122",
|
||||||
|
des_did: "did:sov:test:121",
|
||||||
|
msg: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 30162,
|
||||||
|
timestamp: 1704892817,
|
||||||
|
group: 1,
|
||||||
|
group_id: 53,
|
||||||
|
msg_type: 2,
|
||||||
|
src_did: "did:sov:test:121",
|
||||||
|
des_did: "did:sov:test:122",
|
||||||
|
msg: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 63043,
|
||||||
|
timestamp: 1704892809,
|
||||||
|
group: 0,
|
||||||
|
group_id: 12,
|
||||||
|
msg_type: 3,
|
||||||
|
src_did: "did:sov:test:121",
|
||||||
|
des_did: "did:sov:test:120",
|
||||||
|
msg: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 66251,
|
||||||
|
timestamp: 1704892805,
|
||||||
|
group: 0,
|
||||||
|
group_id: 51,
|
||||||
|
msg_type: 1,
|
||||||
|
src_did: "did:sov:test:120",
|
||||||
|
des_did: "did:sov:test:121",
|
||||||
|
msg: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 85434,
|
||||||
|
timestamp: 1704892807,
|
||||||
|
group: 0,
|
||||||
|
group_id: 51,
|
||||||
|
msg_type: 2,
|
||||||
|
src_did: "did:sov:test:120",
|
||||||
|
des_did: "did:sov:test:121",
|
||||||
|
msg: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 124842,
|
||||||
|
timestamp: 1704892819,
|
||||||
|
group: 1,
|
||||||
|
group_id: 19,
|
||||||
|
msg_type: 3,
|
||||||
|
src_did: "did:sov:test:122",
|
||||||
|
des_did: "did:sov:test:121",
|
||||||
|
msg: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 246326,
|
||||||
|
timestamp: 1704892815,
|
||||||
|
group: 1,
|
||||||
|
group_id: 53,
|
||||||
|
msg_type: 1,
|
||||||
|
src_did: "did:sov:test:121",
|
||||||
|
des_did: "did:sov:test:122",
|
||||||
|
msg: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
181
pkgs/ui/src/components/sequence_diagram/index.tsx
Normal file
181
pkgs/ui/src/components/sequence_diagram/index.tsx
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useRef, useEffect, useState } from "react";
|
||||||
|
import mermaid from "mermaid";
|
||||||
|
import { IconButton } from "@mui/material";
|
||||||
|
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||||
|
import ZoomInIcon from "@mui/icons-material/ZoomIn";
|
||||||
|
import ZoomOutIcon from "@mui/icons-material/ZoomOut";
|
||||||
|
import FullscreenIcon from "@mui/icons-material/Fullscreen";
|
||||||
|
import DownloadIcon from "@mui/icons-material/Download";
|
||||||
|
import ResetIcon from "@mui/icons-material/Autorenew";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import { NoDataOverlay } from "../noDataOverlay";
|
||||||
|
import { useGetAllEventmessages } from "@/api/eventmessages/eventmessages";
|
||||||
|
import { mutate } from "swr";
|
||||||
|
import { LoadingOverlay } from "../join/loadingOverlay";
|
||||||
|
import { generateMermaidString } from "./helpers";
|
||||||
|
|
||||||
|
const SequenceDiagram = () => {
|
||||||
|
const {
|
||||||
|
data: eventMessagesData,
|
||||||
|
isLoading: loadingEventMessages,
|
||||||
|
swrKey: eventMessagesKeyFunc,
|
||||||
|
} = useGetAllEventmessages();
|
||||||
|
|
||||||
|
const mermaidRef: any = useRef(null);
|
||||||
|
const [scale, setScale] = useState(1);
|
||||||
|
const hasData = eventMessagesData?.data && eventMessagesData?.data.length > 0;
|
||||||
|
|
||||||
|
const mermaidString = generateMermaidString(eventMessagesData?.data);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loadingEventMessages && hasData)
|
||||||
|
mermaid.initialize({
|
||||||
|
startOnLoad: false,
|
||||||
|
securityLevel: "loose",
|
||||||
|
sequence: {
|
||||||
|
mirrorActors: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mermaidRef.current) {
|
||||||
|
mermaidRef.current.innerHTML = mermaidString;
|
||||||
|
mermaid.init(undefined, mermaidRef.current);
|
||||||
|
}
|
||||||
|
}, [loadingEventMessages, hasData, mermaidString]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (mermaidRef.current) {
|
||||||
|
const svg = mermaidRef.current.querySelector("svg");
|
||||||
|
if (svg) {
|
||||||
|
svg.style.transform = `scale(${scale})`;
|
||||||
|
svg.style.transformOrigin = "top left";
|
||||||
|
mermaidRef.current.style.width = `${
|
||||||
|
svg.getBoundingClientRect().width * scale
|
||||||
|
}px`;
|
||||||
|
mermaidRef.current.style.height = `${
|
||||||
|
svg.getBoundingClientRect().height * scale
|
||||||
|
}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [scale]);
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
const eventMessagesKey =
|
||||||
|
typeof eventMessagesKeyFunc === "function"
|
||||||
|
? eventMessagesKeyFunc()
|
||||||
|
: eventMessagesKeyFunc;
|
||||||
|
|
||||||
|
if (eventMessagesKey) {
|
||||||
|
mutate(eventMessagesKey);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const zoomIn = () => {
|
||||||
|
setScale((scale) => scale * 1.1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const zoomOut = () => {
|
||||||
|
setScale((scale) => scale / 1.1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetZoom = () => {
|
||||||
|
setScale(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewInFullScreen = () => {
|
||||||
|
if (mermaidRef.current) {
|
||||||
|
const svg = mermaidRef.current.querySelector("svg");
|
||||||
|
const serializer = new XMLSerializer();
|
||||||
|
const svgBlob = new Blob([serializer.serializeToString(svg)], {
|
||||||
|
type: "image/svg+xml",
|
||||||
|
});
|
||||||
|
const url = URL.createObjectURL(svgBlob);
|
||||||
|
window.open(url, "_blank");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadAsPng = () => {
|
||||||
|
if (mermaidRef.current) {
|
||||||
|
const svg = mermaidRef.current.querySelector("svg");
|
||||||
|
const svgData = new XMLSerializer().serializeToString(svg);
|
||||||
|
|
||||||
|
// Create a canvas element to convert SVG to PNG
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const svgSize = svg.getBoundingClientRect();
|
||||||
|
canvas.width = svgSize.width;
|
||||||
|
canvas.height = svgSize.height;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
const img = document.createElement("img");
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
ctx?.drawImage(img, 0, 0);
|
||||||
|
const pngData = canvas.toDataURL("image/png");
|
||||||
|
|
||||||
|
// Trigger download
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.download = "sequence-diagram.png";
|
||||||
|
link.href = pngData;
|
||||||
|
link.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
img.src =
|
||||||
|
"data:image/svg+xml;base64," +
|
||||||
|
btoa(unescape(encodeURIComponent(svgData)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loadingEventMessages)
|
||||||
|
return <LoadingOverlay title="Loading Diagram" subtitle="Please wait..." />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-end">
|
||||||
|
{hasData ? (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Tooltip placement="top" title="Refresh Diagram">
|
||||||
|
<IconButton color="default" onClick={onRefresh}>
|
||||||
|
<RefreshIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Zoom In" placement="top">
|
||||||
|
<IconButton color="primary" onClick={zoomIn}>
|
||||||
|
<ZoomInIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Zoom Out" placement="top">
|
||||||
|
<IconButton color="primary" onClick={zoomOut}>
|
||||||
|
<ZoomOutIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Reset" placement="top">
|
||||||
|
<IconButton color="primary" onClick={resetZoom}>
|
||||||
|
<ResetIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="View in Fullscreen" placement="top">
|
||||||
|
<IconButton color="primary" onClick={viewInFullScreen}>
|
||||||
|
<FullscreenIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Download as PNG" placement="top">
|
||||||
|
<IconButton color="primary" onClick={downloadAsPng}>
|
||||||
|
<DownloadIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<div className="w-full p-2.5">
|
||||||
|
<div className="mermaid" ref={mermaidRef}></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="flex w-full justify-center">
|
||||||
|
<NoDataOverlay label="No Activity yet" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SequenceDiagram;
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
ListItemButton,
|
ListItemButton,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
|
Tooltip,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
@@ -137,9 +138,14 @@ export function Sidebar(props: SidebarProps) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="lg:absolute lg:right-0 lg:top-0">
|
<div className="lg:absolute lg:right-0 lg:top-0">
|
||||||
|
<Tooltip
|
||||||
|
placement="right"
|
||||||
|
title={collapseMenuOpen ? "Close Sidebar" : "Expand Sidebar"}
|
||||||
|
>
|
||||||
<IconButton size="large" className="text-white" onClick={onClose}>
|
<IconButton size="large" className="text-white" onClick={onClose}>
|
||||||
<ChevronLeftIcon fontSize="inherit" />
|
<ChevronLeftIcon fontSize="inherit" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Divider
|
<Divider
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ const CustomTable = ({ configuration, data, loading, tkey }: ICustomTable) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableContainer component={Paper}>
|
<TableContainer component={Paper} style={{ maxHeight: 350 }}>
|
||||||
<Table sx={{ minWidth: 700 }} aria-label="customized table">
|
<Table stickyHeader sx={{ minWidth: 700 }} aria-label="customized table">
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
{configuration.map((header: CustomTableConfiguration) => (
|
{configuration.map((header: CustomTableConfiguration) => (
|
||||||
|
|||||||
@@ -59,22 +59,31 @@ export const APServiceRepositoryTableConfig = [
|
|||||||
{
|
{
|
||||||
key: "status",
|
key: "status",
|
||||||
label: "Status",
|
label: "Status",
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "other",
|
|
||||||
label: "Type",
|
|
||||||
render: (value: any) => {
|
render: (value: any) => {
|
||||||
let renderedValue: any = "";
|
let renderedValue: any = "";
|
||||||
if (typeof value === "object") {
|
if (Array.isArray(value.data)) {
|
||||||
const label = Object.keys(value)[0];
|
renderedValue = value.data.join(", ");
|
||||||
const info = value[label];
|
} else {
|
||||||
renderedValue = (
|
console.error("Status is not an array", value);
|
||||||
<code>
|
|
||||||
{label} {info}
|
|
||||||
</code>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return renderedValue;
|
return renderedValue;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// key: "other",
|
||||||
|
// label: "Type",
|
||||||
|
// render: (value: any) => {
|
||||||
|
// let renderedValue: any = "";
|
||||||
|
// if (typeof value === "object") {
|
||||||
|
// const label = Object.keys(value)[0];
|
||||||
|
// const info = value[label];
|
||||||
|
// renderedValue = (
|
||||||
|
// <code>
|
||||||
|
// {label} {info}
|
||||||
|
// </code>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// return renderedValue;
|
||||||
|
// },
|
||||||
|
// },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { Button } from "@mui/material";
|
import { Button, IconButton, Tooltip } from "@mui/material";
|
||||||
|
import AddCircleIcon from "@mui/icons-material/AddCircle";
|
||||||
|
import RemoveCircleIcon from "@mui/icons-material/RemoveCircle";
|
||||||
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
|
|
||||||
export const ClientTableConfig = [
|
export const ClientTableConfig = [
|
||||||
{
|
{
|
||||||
@@ -51,6 +54,24 @@ export const ServiceTableConfig = [
|
|||||||
key: "entity_did",
|
key: "entity_did",
|
||||||
label: "Entity DID",
|
label: "Entity DID",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "usage",
|
||||||
|
label: "Usage",
|
||||||
|
render: (value: any) => {
|
||||||
|
let renderedValue = "";
|
||||||
|
|
||||||
|
if (value.length > 0) {
|
||||||
|
renderedValue = value.map((item: any, index: number) => {
|
||||||
|
return (
|
||||||
|
<div key={index}>
|
||||||
|
{item.consumer_entity_did} ({item.times_consumed})
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return renderedValue;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "status",
|
key: "status",
|
||||||
label: "Status",
|
label: "Status",
|
||||||
@@ -66,21 +87,40 @@ export const ServiceTableConfig = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "action",
|
key: "action",
|
||||||
label: "Action",
|
label: "Actions",
|
||||||
render: (value: any) => {
|
render: () => {
|
||||||
let renderedValue: any = "";
|
return (
|
||||||
console.log("value", value.data);
|
|
||||||
if (typeof value === "object")
|
|
||||||
renderedValue = (
|
|
||||||
<>
|
<>
|
||||||
{value.data.map((actionType: any) => (
|
<Tooltip title="Register" placement="top">
|
||||||
<>
|
<IconButton disabled size="small">
|
||||||
<Button>{actionType.name}</Button>
|
<AddCircleIcon />
|
||||||
</>
|
</IconButton>
|
||||||
))}
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip title="De-register" placement="top">
|
||||||
|
<IconButton disabled size="small">
|
||||||
|
<RemoveCircleIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Delete" placement="top">
|
||||||
|
<IconButton disabled size="small" color="secondary">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
return renderedValue;
|
// let renderedValue: any = "";
|
||||||
|
// if (typeof value === "object")
|
||||||
|
// renderedValue = (
|
||||||
|
// <>
|
||||||
|
// {[...value.data, { name: 'Delete', endpoint: '' }].map((actionType: any) => (
|
||||||
|
// <>
|
||||||
|
// <Button disabled style={{ marginRight: 8 }} variant="outlined" size="small">{actionType.name}</Button>
|
||||||
|
// </>
|
||||||
|
// ))}
|
||||||
|
// </>
|
||||||
|
// );
|
||||||
|
// return renderedValue;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -10,10 +10,6 @@ export const HomeTableConfig = [
|
|||||||
{
|
{
|
||||||
key: "network",
|
key: "network",
|
||||||
label: "Network",
|
label: "Network",
|
||||||
render: (value: any) => {
|
|
||||||
const renderedValue = typeof value === "object" ? value?.network : "-";
|
|
||||||
return renderedValue;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "ip",
|
key: "ip",
|
||||||
@@ -22,11 +18,6 @@ export const HomeTableConfig = [
|
|||||||
{
|
{
|
||||||
key: "roles",
|
key: "roles",
|
||||||
label: "Roles",
|
label: "Roles",
|
||||||
render: (value: any) => {
|
|
||||||
const renderedValue =
|
|
||||||
typeof value === "object" ? value?.roles?.join(", ") : "-";
|
|
||||||
return renderedValue;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "attached",
|
key: "attached",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ module.exports = {
|
|||||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
],
|
],
|
||||||
|
safelist: ["mermaid"],
|
||||||
important: "#__next",
|
important: "#__next",
|
||||||
theme: {
|
theme: {
|
||||||
colors: {
|
colors: {
|
||||||
|
|||||||
Reference in New Issue
Block a user