diff --git a/pkgs/ui/src/app/access-point/page.tsx b/pkgs/ui/src/app/access-point/page.tsx index c4783f0..5a00802 100644 --- a/pkgs/ui/src/app/access-point/page.tsx +++ b/pkgs/ui/src/app/access-point/page.tsx @@ -6,13 +6,15 @@ import { useGetAllRepositories } from "@/api/repositories/repositories"; import SummaryDetails from "@/components/summary_card"; import CustomTable from "@/components/table"; import { - APSummaryDetails, APAttachmentsTableConfig, APServiceRepositoryTableConfig, } from "@/config/access_point"; import { useEffect } from "react"; +import useGetEntityByNameOrDid from "@/components/hooks/useGetEntityByNameOrDid"; +import { projectConfig } from "@/config/config"; export default function AccessPoint() { + const { entity } = useGetEntityByNameOrDid("AP"); const { data: APAttachementData, isLoading: loadingAttachements, @@ -45,7 +47,7 @@ export default function AccessPoint() { useEffect(() => { const interval = setInterval(() => { onRefresh(); - }, 5000); + }, projectConfig.REFRESH_FREQUENCY); return () => clearInterval(interval); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -54,10 +56,25 @@ export default function AccessPoint() { return (

Attachment View

diff --git a/pkgs/ui/src/app/client/client.tsx b/pkgs/ui/src/app/client/client.tsx index 2be8284..155ed18 100644 --- a/pkgs/ui/src/app/client/client.tsx +++ b/pkgs/ui/src/app/client/client.tsx @@ -1,19 +1,14 @@ "use client"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { ClientTableConfig, ServiceTableConfig } from "@/config/client_1"; import CustomTable from "@/components/table"; import { Alert, Button, - Card, - CardContent, - CardHeader, Snackbar, - Typography, CircularProgress, IconButton, } from "@mui/material"; -import CopyToClipboard from "@/components/copy_to_clipboard"; import { attachEntity, detachEntity, @@ -27,6 +22,8 @@ import { useGetAllServices } from "@/api/services/services"; import axios from "axios"; import CloseIcon from "@mui/icons-material/Close"; import { useSearchParams } from "next/navigation"; +import SummaryDetails from "@/components/summary_card"; +import { projectConfig } from "@/config/config"; interface SnackMessage { message: string; @@ -136,13 +133,12 @@ export default function Client() { useEffect(() => { const interval = setInterval(() => { onRefresh(); - }, 5000); + }, projectConfig.REFRESH_FREQUENCY); return () => clearInterval(interval); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const cardContentRef = useRef(null); const [snackbarOpen, setSnackbarOpen] = useState(false); const [snackbarMessage, setSnackbarMessage] = useState< SnackMessage | undefined @@ -153,8 +149,6 @@ export default function Client() { setSnackbarOpen(false); }; - console.log("entity", entity); - if (services_loading) return ; if (!services) return Client not found; @@ -181,23 +175,16 @@ export default function Client() {
- - } - /> - - - DID: {entity?.did} - - - IP: {entity?.ip} - - - Network: {entity?.network} - - - +

Client View

{ const interval = setInterval(() => { onRefresh(); - }, 5000); + }, projectConfig.REFRESH_FREQUENCY); return () => clearInterval(interval); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -37,12 +40,24 @@ export default function DLG() { return (
diff --git a/pkgs/ui/src/app/home/page.tsx b/pkgs/ui/src/app/home/page.tsx index 7b31e65..bd22485 100644 --- a/pkgs/ui/src/app/home/page.tsx +++ b/pkgs/ui/src/app/home/page.tsx @@ -8,6 +8,7 @@ import dynamic from "next/dynamic"; import { useEffect } from "react"; import { mutate } from "swr"; import ErrorBoundary from "@/components/error_boundary"; +import { projectConfig } from "@/config/config"; const NoSSRSequenceDiagram = dynamic( () => import("../../components/sequence_diagram"), @@ -30,7 +31,7 @@ export default function Home() { useEffect(() => { const interval = setInterval(() => { onRefresh(); - }, 5000); + }, projectConfig.REFRESH_FREQUENCY); return () => clearInterval(interval); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/pkgs/ui/src/components/entity_actions/index.tsx b/pkgs/ui/src/components/entity_actions/index.tsx new file mode 100644 index 0000000..7a3ba8d --- /dev/null +++ b/pkgs/ui/src/components/entity_actions/index.tsx @@ -0,0 +1,111 @@ +import { IEntityActions } from "@/types"; +import { Button, Snackbar, Alert, AlertColor } from "@mui/material"; +import { useState } from "react"; +import useAxios from "../hooks/useAxios"; +import { deleteEntity } from "@/api/entities/entities"; + +interface Props { + endpointData: IEntityActions[]; + rowData?: any; +} + +const SNACKBAR_DEFAULT = { + open: false, + message: "", + severity: "info" as AlertColor, +}; + +const EntityActions = ({ endpointData, rowData }: Props) => { + const [currentEndpoint, setCurrentEndpoint] = useState(""); + const [shouldFetch, setShouldFetch] = useState(false); + const { error } = useAxios(currentEndpoint, "GET", null, true, shouldFetch); + + const [snackbar, setSnackbar] = useState<{ + open: boolean; + message: string; + severity: AlertColor; + }>(SNACKBAR_DEFAULT); + + console.error("Error registering/deregistering:", error); + + const onDeleteEntity = async () => { + if (rowData) + try { + const response = await deleteEntity({ + entity_did: rowData?.entity_did, + }); + console.log("On Delete:", response.data.message); + setSnackbar({ + open: true, + message: response.data.message, + severity: "success", + }); + } catch (error) { + console.error("Error deleting entity: ", error); + setSnackbar({ + open: true, + message: "Failed to delete entity.", + severity: "error", + }); + } + }; + + const onRegisterEntity = (endpoint: string) => { + setCurrentEndpoint(endpoint); + setShouldFetch(true); + }; + + const onDeregisterEntity = (endpoint: string) => { + setCurrentEndpoint(endpoint); + setShouldFetch(true); + }; + + const handleCloseSnackbar = () => { + setSnackbar(SNACKBAR_DEFAULT); + }; + + return ( + <> +
+ {endpointData.map( + ({ name, endpoint }: IEntityActions, index: number) => { + const isRegister = name && name.toLocaleLowerCase() === "register"; + return ( + + ); + }, + )} + +
+ + + {snackbar.message} + + + + ); +}; + +export default EntityActions; diff --git a/pkgs/ui/src/components/hooks/useAxios.tsx b/pkgs/ui/src/components/hooks/useAxios.tsx new file mode 100644 index 0000000..48a0493 --- /dev/null +++ b/pkgs/ui/src/components/hooks/useAxios.tsx @@ -0,0 +1,49 @@ +import { useState, useEffect } from "react"; +import axios from "axios"; +import { projectConfig } from "@/config/config"; + +const useAxios = ( + url: string, + method = "GET", + payload = null, + isFullUrl = false, + shouldFetch = false, +) => { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const fetch = () => { + setLoading(true); + setError(null); + const finalUrl = isFullUrl ? url : projectConfig.BASE_URL + url; + + const axiosConfig = { + url: finalUrl, + method, + data: payload, + }; + + axios(axiosConfig) + .then((response) => { + setData(response.data); + }) + .catch((error) => { + setError(error); + }) + .finally(() => { + setLoading(false); + }); + }; + + useEffect(() => { + if (shouldFetch) { + fetch(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [url, method, JSON.stringify(payload), shouldFetch]); + + return { data, loading, error, refetch: fetch }; +}; + +export default useAxios; diff --git a/pkgs/ui/src/components/hooks/useFetch.tsx b/pkgs/ui/src/components/hooks/useFetch.tsx deleted file mode 100644 index 8eae13c..0000000 --- a/pkgs/ui/src/components/hooks/useFetch.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useState, useEffect } from "react"; -import axios from "axios"; -import { projectConfig } from "@/config/config"; - -const useFetch = (url: string) => { - const [data, setData] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const fetch = () => { - setLoading(true); - axios - .get(projectConfig.BASE_URL + url) - .then((response) => { - setData(response.data); - }) - .catch((error) => { - setError(error); - }) - .finally(() => { - setLoading(false); - }); - }; - - useEffect(() => { - fetch(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [url]); - - return { data, loading, error, fetch }; -}; - -export default useFetch; diff --git a/pkgs/ui/src/components/sequence_diagram/helpers.ts b/pkgs/ui/src/components/sequence_diagram/helpers.ts index 8e1b4a3..2c83cce 100644 --- a/pkgs/ui/src/components/sequence_diagram/helpers.ts +++ b/pkgs/ui/src/components/sequence_diagram/helpers.ts @@ -1,7 +1,7 @@ import { getGroupColor, sanitizeDID } from "@/utils/helpers"; export const generateMermaidString = (data: any) => { - if (!data) return ""; + if (!data || !data.length) return ""; let mermaidString = "sequenceDiagram\n"; const participantDetails = new Map(); diff --git a/pkgs/ui/src/components/sequence_diagram/index.tsx b/pkgs/ui/src/components/sequence_diagram/index.tsx index 6d9e7b8..4c5754b 100644 --- a/pkgs/ui/src/components/sequence_diagram/index.tsx +++ b/pkgs/ui/src/components/sequence_diagram/index.tsx @@ -43,32 +43,48 @@ const SequenceDiagram = () => { swrKey: eventMessagesKeyFunc, } = useGetAllEventmessages(); - const mermaidRef: any = useRef(null); const [scale, setScale] = useState(1); const [openFilters, setOpenFilters] = useState(false); - const [sequenceNr, setSequenceNr] = useState(""); - const hasData = eventMessagesData?.data; + const mermaidRef: any = useRef(null); + + const hasData = eventMessagesData?.data && eventMessagesData?.data.length > 0; const mermaidString = generateMermaidString(eventMessagesData?.data); const allEventMessages = extractAllEventMessages(eventMessagesData?.data); + const dataDependency = JSON.stringify(hasData ? eventMessagesData?.data : ""); useEffect(() => { - if (!loadingEventMessages && hasData) - mermaid.initialize({ - startOnLoad: false, - securityLevel: "loose", - sequence: { - mirrorActors: true, - showSequenceNumbers: true, - }, - }); + const currentMermaidRef = mermaidRef?.current; - if (mermaidRef.current) { - mermaidRef.current.innerHTML = mermaidString; - mermaid.init(undefined, mermaidRef.current); + if (!loadingEventMessages && hasData) { + if ( + currentMermaidRef && + !currentMermaidRef.getAttribute("data-processed") + ) { + mermaid.initialize({ + startOnLoad: false, + securityLevel: "loose", + sequence: { + mirrorActors: true, + showSequenceNumbers: true, + }, + }); + } + + if (currentMermaidRef) { + currentMermaidRef.innerHTML = mermaidString; + mermaid.init(undefined, currentMermaidRef); + } } - }, [loadingEventMessages, hasData, mermaidString]); + return () => { + if (currentMermaidRef) { + currentMermaidRef.removeAttribute("data-processed"); + currentMermaidRef.innerHTML = ""; + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [dataDependency]); useEffect(() => { if (mermaidRef.current) { diff --git a/pkgs/ui/src/components/sidebar/index.tsx b/pkgs/ui/src/components/sidebar/index.tsx index 69ac216..79de3da 100644 --- a/pkgs/ui/src/components/sidebar/index.tsx +++ b/pkgs/ui/src/components/sidebar/index.tsx @@ -93,7 +93,7 @@ export function Sidebar(props: SidebarProps) { const menuEntityEntries: MenuEntry[] = React.useMemo(() => { if (entityData) { return Array.isArray(entityData.data) - ? entityData.data.map((entity) => ({ + ? entityData.data.map((entity: any) => ({ icon: , label: entity.name, to: entity.name, diff --git a/pkgs/ui/src/components/table/index.tsx b/pkgs/ui/src/components/table/index.tsx index 635fbe4..d4d35bf 100644 --- a/pkgs/ui/src/components/table/index.tsx +++ b/pkgs/ui/src/components/table/index.tsx @@ -23,7 +23,8 @@ const CustomTable = ({ configuration, data, loading, tkey }: ICustomTable) => { const renderTableCell = ( value: any, cellKey: string, - render?: (param: any) => void | undefined, + render?: (param: any, data?: any) => void | undefined, + rowData?: any, ) => { let renderedValue = value; @@ -35,7 +36,7 @@ const CustomTable = ({ configuration, data, loading, tkey }: ICustomTable) => { renderedValue = ; // cover use case if we want to render a component - if (render) renderedValue = render(value); + if (render) renderedValue = render(value, rowData); // catch use case where the value is an object but the render function is not provided in the table config if ( @@ -66,17 +67,18 @@ const CustomTable = ({ configuration, data, loading, tkey }: ICustomTable) => { - {data.map((data: any, rowIndex: number) => ( + {data.map((rowData: any, rowIndex: number) => ( {configuration.map( (column: CustomTableConfiguration, columnIndex: number) => { - const cellValue: any = data[column.key]; + const cellValue: any = rowData[column.key]; const cellKey = tkey + ":" + column.key + ":" + rowIndex; const renderComponent = column?.render; return renderTableCell( cellValue, cellKey + ":" + columnIndex, renderComponent, + rowData, ); }, )} diff --git a/pkgs/ui/src/config/access_point/index.tsx b/pkgs/ui/src/config/access_point/index.tsx index 8fae746..7b3d58b 100644 --- a/pkgs/ui/src/config/access_point/index.tsx +++ b/pkgs/ui/src/config/access_point/index.tsx @@ -1,20 +1,3 @@ -// AP - Summary - -export const APSummaryDetails = [ - { - label: "DID", - value: "did:sov:test:1274", - }, - { - label: "IP", - value: "127.0.0.2", - }, - { - label: "Network", - value: "Carlo's Home Network", - }, -]; - // AP - 2 Tables Configurations to display labels export const APAttachmentsTableConfig = [ diff --git a/pkgs/ui/src/config/client_1/index.tsx b/pkgs/ui/src/config/client_1/index.tsx index e1ad33e..d8190f2 100644 --- a/pkgs/ui/src/config/client_1/index.tsx +++ b/pkgs/ui/src/config/client_1/index.tsx @@ -1,7 +1,5 @@ -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"; +import { Button } from "@mui/material"; +import EntityActions from "@/components/entity_actions"; export const ClientTableConfig = [ { @@ -88,39 +86,10 @@ export const ServiceTableConfig = [ { key: "action", label: "Actions", - render: () => { - return ( - <> - - - - - - - - - - - - - - - - - - ); - // let renderedValue: any = ""; - // if (typeof value === "object") - // renderedValue = ( - // <> - // {[...value.data, { name: 'Delete', endpoint: '' }].map((actionType: any) => ( - // <> - // - // - // ))} - // - // ); - // return renderedValue; + render: (value: any, rowData?: any) => { + if (value && value?.data.length > 0) + return ; + else return "N/A"; }, }, ]; diff --git a/pkgs/ui/src/config/config.tsx b/pkgs/ui/src/config/config.tsx index a42175e..7f4a73a 100644 --- a/pkgs/ui/src/config/config.tsx +++ b/pkgs/ui/src/config/config.tsx @@ -9,6 +9,7 @@ import BuildIcon from "@mui/icons-material/Build"; export const projectConfig: any = { BASE_URL: "http://localhost:2979/api/v1", + REFRESH_FREQUENCY: 5000, GROUPS: [ { groupName: "Attachement", diff --git a/pkgs/ui/src/config/dlg/index.ts b/pkgs/ui/src/config/dlg/index.ts index 1fdafde..6afdd7c 100644 --- a/pkgs/ui/src/config/dlg/index.ts +++ b/pkgs/ui/src/config/dlg/index.ts @@ -1,18 +1,5 @@ -// DLG Summary Details - import { formatDateTime } from "@/utils/helpers"; -export const DLGSummaryDetails = [ - { - label: "DID", - value: "did:sov:test:1274", - }, - { - label: "URL", - value: "dlg.tu-berlin.de", - }, -]; - // DLG - 2 Tables Configurations to display labels export const DLGResolutionDummyData = [ diff --git a/pkgs/ui/src/types/index.ts b/pkgs/ui/src/types/index.ts index d1be251..28fe7f0 100644 --- a/pkgs/ui/src/types/index.ts +++ b/pkgs/ui/src/types/index.ts @@ -13,17 +13,22 @@ export interface ICustomTable { export interface EntityDetails { label: string; - value: string; + value: string | undefined; } export interface Entity { - name: string; + name?: string; details: EntityDetails[]; } export interface ISummaryDetails { - entity: any; + entity: Entity; fake?: boolean; hasRefreshButton?: boolean; onRefresh?: () => void; } + +export interface IEntityActions { + name: string; + endpoint: string; +}