diff --git a/src/components/Agents/AgentName.tsx b/src/components/Agents/AgentName.tsx index bea874e9f..7c4ad069a 100644 --- a/src/components/Agents/AgentName.tsx +++ b/src/components/Agents/AgentName.tsx @@ -1,5 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { getAgentByID } from "../../api/services/topology"; +import { Badge } from "../Badge"; type TopologyCardAgentProps = { agentId?: string; @@ -18,5 +19,5 @@ export default function AgentName({ agentId }: TopologyCardAgentProps) { return null; } - return
{agent.name}
; + return ; } diff --git a/src/components/CustomScroll/index.tsx b/src/components/CustomScroll/index.tsx index 57c320e53..fec471a94 100644 --- a/src/components/CustomScroll/index.tsx +++ b/src/components/CustomScroll/index.tsx @@ -1,9 +1,9 @@ import clsx from "clsx"; -import React, { useEffect, useRef, useState } from "react"; +import React, { useEffect, useState } from "react"; export type CustomScrollProps = { maxHeight: string; - children: JSX.Element | JSX.Element[]; + children: React.ReactNode; showMoreClass?: string; minChildCount: number; } & React.HTMLProps; diff --git a/src/components/HealthChecksSummary/index.tsx b/src/components/HealthChecksSummary/index.tsx index 984c014bc..7e6a86503 100644 --- a/src/components/HealthChecksSummary/index.tsx +++ b/src/components/HealthChecksSummary/index.tsx @@ -3,7 +3,7 @@ import { AiFillHeart } from "react-icons/ai"; import { StatusLine, StatusLineProps } from "../StatusLine/StatusLine"; type HealthChecksSummaryProps = React.HTMLProps & { - checks: { + checks?: { health: number; warning: number; unhealthy: number; diff --git a/src/components/HealthSummary/summary.tsx b/src/components/HealthSummary/summary.tsx index 10179c665..49a92b3f9 100644 --- a/src/components/HealthSummary/summary.tsx +++ b/src/components/HealthSummary/summary.tsx @@ -4,22 +4,15 @@ import { StatusLine, StatusLineProps } from "../StatusLine/StatusLine"; - -type TopologyComponentProp = { - id: string; - name: string; - icon: string; - summary: { [key: string]: number }; - components?: TopologyComponentProp[]; -} & { [key: string]: any }; +import { Topology } from "../../context/TopologyPageContext"; type HealthSummaryProps = { - component: TopologyComponentProp; + component: Topology; iconSize?: "2xs" | "2xsi" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl"; viewType?: "individual_level" | "children_level"; } & React.HTMLProps; -function getStatuses(summary: { [key: string]: number }, url?: string) { +function getStatuses(summary?: { [key: string]: number }, url?: string) { if (!summary) { return []; } @@ -88,7 +81,7 @@ export const HealthSummary = ({ data.label = component.name; data.url = `/topology/${component.id}`; data.statuses = getStatuses( - component.summary, + component?.summary, `/topology/${component.id}` ); } else { diff --git a/src/components/TopologyCard/Property.tsx b/src/components/TopologyCard/Property.tsx index 1b68c2b2d..0f29d6945 100644 --- a/src/components/TopologyCard/Property.tsx +++ b/src/components/TopologyCard/Property.tsx @@ -41,12 +41,12 @@ export function FormatProperty({ type PropertyProps = { property: TopologyProperty; -} & React.HTMLAttributes; +} & Omit, "property">; export const Property = ({ property, className = "", - ...rest + ...props }: PropertyProps) => { const { name, icon, color } = property; const label = @@ -65,7 +65,7 @@ export const Property = ({ { [className]: className }, icon ? "flex-row" : "flex-col" )} - {...rest} + {...props} > {!isEmpty(label) && ( diff --git a/src/components/TopologyCard/TopologyCard.stories.tsx b/src/components/TopologyCard/TopologyCard.stories.tsx index ef2848041..0d52ca280 100644 --- a/src/components/TopologyCard/TopologyCard.stories.tsx +++ b/src/components/TopologyCard/TopologyCard.stories.tsx @@ -3,6 +3,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { MemoryRouter } from "react-router-dom"; import { Size } from "../../types"; import { TopologyCard } from "./index"; +import { Topology } from "../../context/TopologyPageContext"; const defaultQueryClient = new QueryClient(); @@ -21,11 +22,11 @@ export default { ] } as ComponentMeta; -const topology = { +const topology: Topology = { name: "abp", id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", text: "ABP Microservices", - topology_type: "component", + type: "component", namespace: "dev", labels: { environment: "dev", @@ -34,17 +35,18 @@ const topology = { }, icon: "dotnet", status: "unknown", - type: "DotnetApplication", summary: {}, properties: [ { icon: "world", type: "url", - text: "https://docs.abp.io/en/abp/latest/Samples/Microservice-Demo" + text: "https://docs.abp.io/en/abp/latest/Samples/Microservice-Demo", + name: "Documentation" }, { icon: "aws", - text: "eu-west-2" + text: "eu-west-2", + name: "Region" }, { name: "Products", @@ -67,7 +69,7 @@ const topology = { { name: "Auth Server", id: "01821200-3e6c-7cf0-ad2a-7ed31a924c25", - topology_type: "component", + type: "component", labels: { environment: "dev", "kustomize.toolkit.fluxcd.io/name": "aws-sandbox", @@ -75,19 +77,16 @@ const topology = { }, icon: "dotnet", path: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - type: "Application", summary: {}, parent_id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.124908Z", updated_at: "2022-07-18T15:49:42.124908Z", - deleted_at: null, external_id: "Auth Server" }, { name: "Website", id: "01821200-3e8d-a787-89cc-49c9bc6841ec", - topology_type: "component", + type: "component", labels: { environment: "dev", "kustomize.toolkit.fluxcd.io/name": "aws-sandbox", @@ -95,19 +94,16 @@ const topology = { }, icon: "dotnet", path: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - type: "Application", summary: {}, parent_id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.157617Z", updated_at: "2022-07-18T15:49:42.157617Z", - deleted_at: null, external_id: "Website" }, { name: "Redis", id: "01821200-3e93-0df7-f6ce-703a52423827", - topology_type: "component", + type: "component", labels: { environment: "dev", "kustomize.toolkit.fluxcd.io/name": "aws-sandbox", @@ -115,19 +111,16 @@ const topology = { }, icon: "redis", path: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - type: "Application", summary: {}, parent_id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.163406Z", updated_at: "2022-07-18T15:49:42.163406Z", - deleted_at: null, external_id: "Redis" }, { name: "RabbitMQ", id: "01821200-3e98-cdd5-f081-bc3f93476705", - topology_type: "component", + type: "component", labels: { environment: "dev", "kustomize.toolkit.fluxcd.io/name": "aws-sandbox", @@ -135,19 +128,16 @@ const topology = { }, icon: "rabbitmq", path: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - type: "Application", summary: {}, parent_id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.169415Z", updated_at: "2022-07-18T15:49:42.169415Z", - deleted_at: null, external_id: "RabbitMQ" }, { name: "Mongo", id: "01821200-3e9f-d054-115a-c818bf42168a", - topology_type: "component", + type: "component", labels: { environment: "dev", "kustomize.toolkit.fluxcd.io/name": "aws-sandbox", @@ -155,19 +145,16 @@ const topology = { }, icon: "mongo", path: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - type: "Application", summary: {}, parent_id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.175625Z", updated_at: "2022-07-18T15:49:42.175625Z", - deleted_at: null, external_id: "Mongo" }, { name: "Backend Admin", id: "01821200-3e73-2612-7bed-623aba12935c", - topology_type: "component", + type: "component", labels: { environment: "dev", "kustomize.toolkit.fluxcd.io/name": "aws-sandbox", @@ -175,19 +162,16 @@ const topology = { }, icon: "dotnet", path: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - type: "Application", summary: {}, parent_id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.131378Z", updated_at: "2022-07-18T15:49:42.131378Z", - deleted_at: null, external_id: "Backend Admin" }, { name: "Blogging Service", id: "01821200-3e7a-91aa-bd3e-b95147d33c9f", - topology_type: "component", + type: "component", labels: { environment: "dev", "kustomize.toolkit.fluxcd.io/name": "aws-sandbox", @@ -195,19 +179,16 @@ const topology = { }, icon: "dotnet", path: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - type: "Application", summary: {}, parent_id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.138489Z", updated_at: "2022-07-18T15:49:42.138489Z", - deleted_at: null, external_id: "Blogging Service" }, { name: "Identity Service", id: "01821200-3e80-1713-425c-2cecf97a8f1f", - topology_type: "component", + type: "component", labels: { environment: "dev", "kustomize.toolkit.fluxcd.io/name": "aws-sandbox", @@ -215,19 +196,15 @@ const topology = { }, icon: "dotnet", path: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - type: "Application", summary: {}, parent_id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.14447Z", updated_at: "2022-07-18T15:49:42.14447Z", - deleted_at: null, external_id: "Identity Service" }, { name: "Product Service", id: "01821200-3e87-9569-3b9a-4e501ba092e9", - topology_type: "component", labels: { environment: "dev", "kustomize.toolkit.fluxcd.io/name": "aws-sandbox", @@ -238,16 +215,13 @@ const topology = { type: "Application", summary: {}, parent_id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.151638Z", updated_at: "2022-07-18T15:49:42.151638Z", - deleted_at: null, external_id: "Product Service" }, { name: "SQL Server", id: "01821200-3ea6-5708-843b-d84222a4d42d", - topology_type: "component", labels: { environment: "dev", "kustomize.toolkit.fluxcd.io/name": "aws-sandbox", @@ -258,17 +232,13 @@ const topology = { type: "Application", summary: {}, parent_id: "01821200-3e60-0dbb-7ef3-58e1635bc8a6", - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.183071Z", updated_at: "2022-07-18T15:49:42.183071Z", - deleted_at: null, external_id: "SQL Server" } ], - system_template_id: "01818abb-1d07-e0c6-cbe0-6b3b702039a5", created_at: "2022-07-18T15:49:42.112918Z", - updated_at: "2022-07-18T15:49:42.112918Z", - deleted_at: null + updated_at: "2022-07-18T15:49:42.112918Z" }; const Template: ComponentStory = (arg: any) => { diff --git a/src/components/TopologyCard/index.tsx b/src/components/TopologyCard/index.tsx index 2f8d10b09..f22f6d101 100644 --- a/src/components/TopologyCard/index.tsx +++ b/src/components/TopologyCard/index.tsx @@ -1,20 +1,21 @@ +import { useQuery } from "@tanstack/react-query"; import clsx from "clsx"; -import { filter } from "lodash"; -import { useEffect, useState, useMemo, MouseEventHandler } from "react"; +import { MouseEventHandler, useMemo } from "react"; import { Link, useParams, useSearchParams } from "react-router-dom"; import { getTopology } from "../../api/services/topology"; +import { Topology } from "../../context/TopologyPageContext"; import { Size } from "../../types"; +import AgentName from "../Agents/AgentName"; import { CustomScroll } from "../CustomScroll"; +import { HealthChecksSummary } from "../HealthChecksSummary"; import { HealthSummary } from "../HealthSummary"; import { Icon } from "../Icon"; -import { HealthChecksSummary } from "../HealthChecksSummary"; -import { CardMetrics } from "./CardMetrics"; -import { Property } from "./Property"; -import { TopologyDropdownMenu } from "./TopologyDropdownMenu"; import IncidentCardSummary from "../IncidentCardSummary"; import TopologyCardSkeletonLoader from "../SkeletonLoader/TopologyCardSkeletonLoader"; +import { CardMetrics } from "./CardMetrics"; +import { Property } from "./Property"; import { TopologyConfigAnalysisLine } from "./TopologyConfigAnalysisLine"; -import AgentName from "../Agents/AgentName"; +import { TopologyDropdownMenu } from "./TopologyDropdownMenu"; export enum ComponentStatus { unhealthy = "unhealthy", @@ -34,9 +35,9 @@ export const StatusStyles: Record = { }; interface IProps { - size: Size | string; + size?: Size | string; topologyId?: string; - topology?: any; + topology?: Topology; selectionMode?: boolean; selected?: boolean; onSelectionChange?: MouseEventHandler; @@ -52,17 +53,18 @@ export function TopologyCard({ onSelectionChange, isTopologyPage = false }: IProps) { - const [topology, setTopology] = useState(topologyData); const [searchParams] = useSearchParams(); const { id: parentId } = useParams(); - useEffect(() => { - if (topologyId != null && topologyData == null) { - getTopology({ id: topologyId }).then(({ components }) => - setTopology(components?.[0]) - ); - } - }, [topologyId, topologyData]); + const { data } = useQuery({ + queryKey: ["topology", topologyId], + queryFn: () => getTopology({ id: topologyId }), + enabled: !!topologyId && topologyData == null + }); + + const topology = useMemo(() => { + return topologyData || data?.components?.[0]; + }, [data, topologyData]); let selectionModeRootProps = null; @@ -85,7 +87,7 @@ export function TopologyCard({ topology.summary.unhealthy = topology.summary.unhealthy || 0; topology.summary.warning = topology.summary.warning || 0; Object.keys(topology.summary).forEach((key) => { - totalCount += topology.summary[key]; + totalCount += topology.summary?.[key]; }); } return ( @@ -94,11 +96,7 @@ export function TopologyCard({ ); }; - const prepareTopologyLink = (topologyItem: { - id: string; - parent_id: string; - path: string; - }) => { + const prepareTopologyLink = (topologyItem: Topology) => { if (topologyItem.id === parentId && parentId) { return ""; } @@ -125,8 +123,8 @@ export function TopologyCard({ } topology.properties = topology.properties || []; - const properties = filter(topology.properties, (i) => !i.headline); - const heading = filter(topology.properties, (i) => i.headline); + const properties = topology.properties.filter((i) => !i.headline); + const heading = topology.properties.filter((i) => i.headline); return (
-
-

+

@@ -157,13 +155,18 @@ export function TopologyCard({ )} {!prepareTopologyLink(topology) && (topology.text || topology.name)} -

- - {topology.description && ( -

- {topology.description} -

- )} +
+
+ + {topology.status_reason && ( +
+ {topology.status_reason} +
+ )} +
@@ -210,7 +213,7 @@ export function TopologyCard({ key={index} property={property} className={ - index === topology.properties.length - 1 + index === topology.properties!.length - 1 ? "mb-0" : "mb-2.5" } diff --git a/src/context/TopologyPageContext.tsx b/src/context/TopologyPageContext.tsx index 513fa3994..e85d6f064 100644 --- a/src/context/TopologyPageContext.tsx +++ b/src/context/TopologyPageContext.tsx @@ -32,6 +32,8 @@ export type Topology = { icon?: string; text?: string; status?: string; + status_reason?: string; + namespace?: string; hidden?: boolean; external_id?: string; agent_id?: string; @@ -47,9 +49,17 @@ export type Topology = { >; [key: string]: any; }; - logs: { + logs?: { name: string; }[]; - parents: string[]; - children: string[]; + parents?: string[]; + children?: string[]; + is_leaf?: boolean; + description?: string; + checks?: { + health: number; + warning: number; + unhealthy: number; + }; + deleted_at?: string; } & CostsData;