@anyshift/backstage-plugin-anyshift 0.1.0-beta.13 → 0.1.0-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/components/entity/AnyshiftEntityContent.esm.js +48 -6
- package/dist/components/entity/AnyshiftEntityContent.esm.js.map +1 -1
- package/dist/components/entity/ArchitectureOperationDetails.esm.js +107 -0
- package/dist/components/entity/ArchitectureOperationDetails.esm.js.map +1 -0
- package/dist/components/entity/MermaidArchitecture.esm.js +202 -43
- package/dist/components/entity/MermaidArchitecture.esm.js.map +1 -1
- package/dist/components/entity/SectionPresenters.esm.js +12 -39
- package/dist/components/entity/SectionPresenters.esm.js.map +1 -1
- package/dist/components/entity/architectureDiagram.esm.js +48 -15
- package/dist/components/entity/architectureDiagram.esm.js.map +1 -1
- package/dist/components/entity/architectureGraph.esm.js +145 -0
- package/dist/components/entity/architectureGraph.esm.js.map +1 -0
- package/dist/components/entity/relatedCatalogEntities.esm.js +60 -0
- package/dist/components/entity/relatedCatalogEntities.esm.js.map +1 -0
- package/dist/package.json.esm.js +10 -10
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.0-beta.15
|
|
6
|
+
|
|
7
|
+
- Resolve Affected footprint entries against one Backstage catalog snapshot and link only entities
|
|
8
|
+
that are proven to exist.
|
|
9
|
+
- Repair stale runtime-namespace links, de-duplicate logical names, and keep unresolved graph
|
|
10
|
+
evidence visible as plain text instead of dead links.
|
|
11
|
+
|
|
12
|
+
## 0.1.0-beta.14
|
|
13
|
+
|
|
14
|
+
- Expose available APM HTTP methods and templated paths from service `CALLS_TO` edges through a compact architecture-diagram affordance and source-grouped evidence inspector.
|
|
15
|
+
- Keep missing operation or observation metadata explicitly unknown without changing legacy edge rendering or adding per-edge Graph API requests.
|
|
16
|
+
|
|
5
17
|
## 0.1.0-beta.13
|
|
6
18
|
|
|
7
19
|
- Keep zero-event cloud-resource pages quiet: identity-completeness codes do not become alerts
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
|
|
3
3
|
import { Typography, Box, Chip, Button, CircularProgress, Grid } from '@material-ui/core';
|
|
4
4
|
import { Progress } from '@backstage/core-components';
|
|
5
|
-
import { useApi } from '@backstage/frontend-plugin-api';
|
|
5
|
+
import { useApi, useApiHolder } from '@backstage/frontend-plugin-api';
|
|
6
6
|
import { ANYSHIFT_KIND_ANNOTATION, ANYSHIFT_CONFIGURATION_ENDPOINTS_ANNOTATION, ANYSHIFT_CONFIGURATION_DEPENDENCIES_ANNOTATION, ANYSHIFT_RUNTIME_RESOURCE_ID_ANNOTATION } from '@anyshift/backstage-plugin-anyshift-common';
|
|
7
|
-
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
7
|
+
import { useEntity, catalogApiRef } from '@backstage/plugin-catalog-react';
|
|
8
8
|
import { anyshiftApiRef } from '../../api/AnyshiftApi.esm.js';
|
|
9
9
|
import { AskAnyshiftCard } from '../ask/AskAnyshiftCard.esm.js';
|
|
10
10
|
import { askScopeForEntity } from '../ask/askScope.esm.js';
|
|
11
11
|
import { CloudAnyshiftEntityContent } from '../cloud/CloudResourceContent.esm.js';
|
|
12
12
|
import { isCloudResourceEntity, entityTarget } from './entityIdentity.esm.js';
|
|
13
|
+
import { resolveRelatedCatalogEntities } from './relatedCatalogEntities.esm.js';
|
|
13
14
|
import { OverviewSection, WhyNowSection, DatadogSignalSection, ArchitectureSection, DependencySection, BriefRail, TimelineSection } from './BriefSections.esm.js';
|
|
14
15
|
|
|
15
16
|
const AnyshiftEntityContent = () => {
|
|
@@ -19,6 +20,7 @@ const AnyshiftEntityContent = () => {
|
|
|
19
20
|
const ServiceAnyshiftEntityContent = () => {
|
|
20
21
|
const { entity } = useEntity();
|
|
21
22
|
const api = useApi(anyshiftApiRef);
|
|
23
|
+
const catalogApi = useApiHolder().get(catalogApiRef);
|
|
22
24
|
const annotations = entity.metadata.annotations ?? {};
|
|
23
25
|
const target = entityTarget(entity);
|
|
24
26
|
const kind = annotations[ANYSHIFT_KIND_ANNOTATION] || "workload";
|
|
@@ -27,6 +29,7 @@ const ServiceAnyshiftEntityContent = () => {
|
|
|
27
29
|
const configurationDependencies = annotations[ANYSHIFT_CONFIGURATION_DEPENDENCIES_ANNOTATION];
|
|
28
30
|
const configurationTarget = annotations[ANYSHIFT_RUNTIME_RESOURCE_ID_ANNOTATION];
|
|
29
31
|
const [brief, setBrief] = useState();
|
|
32
|
+
const [catalogEntities, setCatalogEntities] = useState([]);
|
|
30
33
|
const [capabilities, setCapabilities] = useState({
|
|
31
34
|
askEnabled: false,
|
|
32
35
|
queryEnabled: false
|
|
@@ -115,6 +118,26 @@ const ServiceAnyshiftEntityContent = () => {
|
|
|
115
118
|
active.current = false;
|
|
116
119
|
};
|
|
117
120
|
}, [load]);
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
let cancelled = false;
|
|
123
|
+
if (!catalogApi) {
|
|
124
|
+
setCatalogEntities([]);
|
|
125
|
+
return () => {
|
|
126
|
+
cancelled = true;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catalogApi.getEntities({
|
|
130
|
+
filter: [{ kind: "Component" }, { kind: "Resource" }],
|
|
131
|
+
fields: ["kind", "metadata.name", "metadata.namespace"]
|
|
132
|
+
}).then((response) => {
|
|
133
|
+
if (!cancelled) setCatalogEntities(response.items);
|
|
134
|
+
}).catch(() => {
|
|
135
|
+
if (!cancelled) setCatalogEntities([]);
|
|
136
|
+
});
|
|
137
|
+
return () => {
|
|
138
|
+
cancelled = true;
|
|
139
|
+
};
|
|
140
|
+
}, [catalogApi]);
|
|
118
141
|
useEffect(() => {
|
|
119
142
|
api.capabilities().then((next) => {
|
|
120
143
|
if (active.current) setCapabilities(next);
|
|
@@ -124,12 +147,31 @@ const ServiceAnyshiftEntityContent = () => {
|
|
|
124
147
|
}
|
|
125
148
|
});
|
|
126
149
|
}, [api]);
|
|
127
|
-
|
|
128
|
-
|
|
150
|
+
const resolvedBrief = useMemo(() => {
|
|
151
|
+
if (!brief || brief.impact.state !== "data" || !brief.impact.data)
|
|
152
|
+
return brief;
|
|
153
|
+
const impactData = brief.impact.data;
|
|
154
|
+
return {
|
|
155
|
+
...brief,
|
|
156
|
+
impact: {
|
|
157
|
+
...brief.impact,
|
|
158
|
+
data: {
|
|
159
|
+
...impactData,
|
|
160
|
+
relatedEntities: resolveRelatedCatalogEntities(
|
|
161
|
+
impactData.relatedEntities,
|
|
162
|
+
catalogEntities
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}, [brief, catalogEntities]);
|
|
168
|
+
if (error && !resolvedBrief)
|
|
169
|
+
return /* @__PURE__ */ jsx(Typography, { color: "error", children: error });
|
|
170
|
+
if (!resolvedBrief) return /* @__PURE__ */ jsx(Progress, {});
|
|
129
171
|
return /* @__PURE__ */ jsx(
|
|
130
172
|
ServiceBriefView,
|
|
131
173
|
{
|
|
132
|
-
brief,
|
|
174
|
+
brief: resolvedBrief,
|
|
133
175
|
onRetry: retrySection,
|
|
134
176
|
refreshing,
|
|
135
177
|
onRefresh: () => void load(true),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnyshiftEntityContent.esm.js","sources":["../../../src/components/entity/AnyshiftEntityContent.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n Box,\n Button,\n Chip,\n CircularProgress,\n Grid,\n Typography,\n} from '@material-ui/core';\nimport { Progress } from '@backstage/core-components';\nimport { useApi } from '@backstage/frontend-plugin-api';\nimport {\n ANYSHIFT_CONFIGURATION_DEPENDENCIES_ANNOTATION,\n ANYSHIFT_CONFIGURATION_ENDPOINTS_ANNOTATION,\n ANYSHIFT_KIND_ANNOTATION,\n ANYSHIFT_RUNTIME_RESOURCE_ID_ANNOTATION,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { anyshiftApiRef } from '../../api/AnyshiftApi';\nimport type {\n AnyshiftCapabilities,\n AskScope,\n BriefSectionName,\n ServiceBrief,\n} from '../../api/types';\nimport { AskAnyshiftCard } from '../ask/AskAnyshiftCard';\nimport { askScopeForEntity } from '../ask/askScope';\nimport { CloudAnyshiftEntityContent } from '../cloud/CloudResourceContent';\nimport { entityTarget, isCloudResourceEntity } from './entityIdentity';\nimport {\n ArchitectureSection,\n BriefRail,\n DatadogSignalSection,\n DependencySection,\n OverviewSection,\n TimelineSection,\n WhyNowSection,\n} from './BriefSections';\n\nexport const AnyshiftEntityContent = () => {\n const { entity } = useEntity();\n return isCloudResourceEntity(entity) ? (\n <CloudAnyshiftEntityContent />\n ) : (\n <ServiceAnyshiftEntityContent />\n );\n};\n\nexport const ServiceAnyshiftEntityContent = () => {\n const { entity } = useEntity();\n const api = useApi(anyshiftApiRef);\n const annotations = entity.metadata.annotations ?? {};\n const target = entityTarget(entity);\n const kind = annotations[ANYSHIFT_KIND_ANNOTATION] || 'workload';\n const namespace = entity.metadata.namespace;\n const configurationEndpoints =\n annotations[ANYSHIFT_CONFIGURATION_ENDPOINTS_ANNOTATION];\n const configurationDependencies =\n annotations[ANYSHIFT_CONFIGURATION_DEPENDENCIES_ANNOTATION];\n const configurationTarget =\n annotations[ANYSHIFT_RUNTIME_RESOURCE_ID_ANNOTATION];\n const [brief, setBrief] = useState<ServiceBrief>();\n const [capabilities, setCapabilities] = useState<AnyshiftCapabilities>({\n askEnabled: false,\n queryEnabled: false,\n });\n const [error, setError] = useState<string>();\n const [refreshingAll, setRefreshingAll] = useState(false);\n const [refreshing, setRefreshing] = useState<Set<BriefSectionName>>(\n new Set(),\n );\n const active = useRef(true);\n\n const load = useCallback(\n async (refresh = false) => {\n setError(undefined);\n if (refresh) setRefreshingAll(true);\n try {\n const next = await api.brief(kind, target, {\n namespace,\n refresh,\n configurationEndpoints,\n configurationDependencies,\n configurationTarget,\n });\n if (active.current) setBrief(next);\n } catch (cause) {\n if (active.current)\n setError(cause instanceof Error ? cause.message : String(cause));\n } finally {\n if (active.current) setRefreshingAll(false);\n }\n },\n [\n api,\n configurationDependencies,\n configurationEndpoints,\n configurationTarget,\n kind,\n namespace,\n target,\n ],\n );\n\n const retrySection = useCallback(\n async (section: BriefSectionName) => {\n setRefreshing(current => new Set(current).add(section));\n try {\n const response = await api.briefSection(kind, target, section, {\n namespace,\n refresh: true,\n configurationEndpoints,\n configurationDependencies,\n configurationTarget,\n });\n if (active.current) {\n setBrief(current =>\n current\n ? {\n ...current,\n [section]: response.value,\n refreshedAt: response.refreshedAt,\n ...(response.observedService\n ? { observedService: response.observedService }\n : {}),\n }\n : current,\n );\n }\n } finally {\n if (active.current) {\n setRefreshing(current => {\n const next = new Set(current);\n next.delete(section);\n return next;\n });\n }\n }\n },\n [\n api,\n configurationDependencies,\n configurationEndpoints,\n configurationTarget,\n kind,\n namespace,\n target,\n ],\n );\n\n useEffect(() => {\n active.current = true;\n void load();\n return () => {\n active.current = false;\n };\n }, [load]);\n\n useEffect(() => {\n api\n .capabilities()\n .then(next => {\n if (active.current) setCapabilities(next);\n })\n .catch(() => {\n if (active.current) {\n setCapabilities({ askEnabled: false, queryEnabled: false });\n }\n });\n }, [api]);\n\n if (error && !brief) return <Typography color=\"error\">{error}</Typography>;\n if (!brief) return <Progress />;\n return (\n <ServiceBriefView\n brief={brief}\n onRetry={retrySection}\n refreshing={refreshing}\n onRefresh={() => void load(true)}\n refreshingAll={refreshingAll}\n refreshError={error}\n askEnabled={capabilities.askEnabled}\n askScope={askScopeForEntity(entity)}\n />\n );\n};\n\nexport const ServiceBriefView = ({\n brief,\n onRetry,\n refreshing = new Set(),\n onRefresh = () => {},\n refreshingAll = false,\n refreshError,\n askEnabled = false,\n askScope,\n}: {\n brief: ServiceBrief;\n onRetry: (section: BriefSectionName) => void;\n refreshing?: ReadonlySet<BriefSectionName>;\n onRefresh?: () => void;\n refreshingAll?: boolean;\n refreshError?: string;\n askEnabled?: boolean;\n askScope?: AskScope;\n}) => (\n <>\n <Box\n mb={3}\n px={2.5}\n py={2}\n borderLeft={4}\n borderColor=\"primary.main\"\n bgcolor=\"background.paper\"\n >\n <Box display=\"flex\" justifyContent=\"space-between\" gridGap={16}>\n <Box>\n <Typography variant=\"overline\" color=\"textSecondary\">\n Anyshift evidence trace\n </Typography>\n <Box display=\"flex\" flexWrap=\"wrap\" alignItems=\"center\" gridGap={8}>\n <Chip size=\"small\" label={`Workload · ${brief.target}`} />\n <Typography aria-hidden=\"true\" color=\"primary\">\n →\n </Typography>\n <Chip\n size=\"small\"\n color=\"primary\"\n label={`Observed service · ${\n brief.observedService || 'Not resolved'\n }`}\n />\n </Box>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Runtime namespace: {brief.namespace || 'Not set'} · Refreshed{' '}\n {formatTimestamp(brief.refreshedAt)}\n </Typography>\n </Box>\n <Button\n type=\"button\"\n variant=\"outlined\"\n size=\"small\"\n onClick={onRefresh}\n disabled={refreshingAll}\n startIcon={refreshingAll ? <CircularProgress size={14} /> : undefined}\n >\n {refreshingAll ? 'Refreshing…' : 'Refresh brief'}\n </Button>\n </Box>\n {refreshError && (\n <Typography color=\"error\" variant=\"caption\">\n Refresh failed: {refreshError}. Existing evidence is still shown.\n </Typography>\n )}\n </Box>\n\n <Grid container spacing={3} alignItems=\"stretch\">\n <Grid item xs={12}>\n <OverviewSection\n section={brief.overview}\n onRetry={onRetry}\n refreshing={refreshing.has('overview')}\n />\n </Grid>\n <Grid item xs={12}>\n <WhyNowSection\n section={brief.whyNow}\n onRetry={onRetry}\n refreshing={refreshing.has('whyNow')}\n />\n </Grid>\n {askEnabled && (\n <Grid item xs={12}>\n <AskAnyshiftCard context=\"entity\" scope={askScope} />\n </Grid>\n )}\n <Grid item xs={12}>\n <DatadogSignalSection\n section={brief.observability}\n onRetry={onRetry}\n refreshing={refreshing.has('observability')}\n />\n </Grid>\n <Grid item xs={12}>\n <ArchitectureSection\n section={brief.architecture}\n onRetry={onRetry}\n refreshing={refreshing.has('architecture')}\n />\n </Grid>\n <Grid item xs={12} md={8}>\n <DependencySection\n section={brief.topology}\n onRetry={onRetry}\n refreshing={refreshing.has('topology')}\n />\n </Grid>\n <Grid item xs={12} md={4}>\n <BriefRail brief={brief} onRetry={onRetry} refreshing={refreshing} />\n </Grid>\n <Grid item xs={12}>\n <TimelineSection\n section={brief.timeline}\n onRetry={onRetry}\n refreshing={refreshing.has('timeline')}\n />\n </Grid>\n </Grid>\n </>\n);\n\nfunction formatTimestamp(value: string): string {\n const parsed = Date.parse(value);\n return Number.isNaN(parsed) ? value : new Date(parsed).toLocaleString();\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnyshiftEntityContent.esm.js","sources":["../../../src/components/entity/AnyshiftEntityContent.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n Box,\n Button,\n Chip,\n CircularProgress,\n Grid,\n Typography,\n} from '@material-ui/core';\nimport { Progress } from '@backstage/core-components';\nimport { useApi, useApiHolder } from '@backstage/frontend-plugin-api';\nimport type { Entity } from '@backstage/catalog-model';\nimport {\n ANYSHIFT_CONFIGURATION_DEPENDENCIES_ANNOTATION,\n ANYSHIFT_CONFIGURATION_ENDPOINTS_ANNOTATION,\n ANYSHIFT_KIND_ANNOTATION,\n ANYSHIFT_RUNTIME_RESOURCE_ID_ANNOTATION,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport { catalogApiRef, useEntity } from '@backstage/plugin-catalog-react';\nimport { anyshiftApiRef } from '../../api/AnyshiftApi';\nimport type {\n AnyshiftCapabilities,\n AskScope,\n BriefSectionName,\n ServiceBrief,\n} from '../../api/types';\nimport { AskAnyshiftCard } from '../ask/AskAnyshiftCard';\nimport { askScopeForEntity } from '../ask/askScope';\nimport { CloudAnyshiftEntityContent } from '../cloud/CloudResourceContent';\nimport { entityTarget, isCloudResourceEntity } from './entityIdentity';\nimport { resolveRelatedCatalogEntities } from './relatedCatalogEntities';\nimport {\n ArchitectureSection,\n BriefRail,\n DatadogSignalSection,\n DependencySection,\n OverviewSection,\n TimelineSection,\n WhyNowSection,\n} from './BriefSections';\n\nexport const AnyshiftEntityContent = () => {\n const { entity } = useEntity();\n return isCloudResourceEntity(entity) ? (\n <CloudAnyshiftEntityContent />\n ) : (\n <ServiceAnyshiftEntityContent />\n );\n};\n\nexport const ServiceAnyshiftEntityContent = () => {\n const { entity } = useEntity();\n const api = useApi(anyshiftApiRef);\n const catalogApi = useApiHolder().get(catalogApiRef);\n const annotations = entity.metadata.annotations ?? {};\n const target = entityTarget(entity);\n const kind = annotations[ANYSHIFT_KIND_ANNOTATION] || 'workload';\n const namespace = entity.metadata.namespace;\n const configurationEndpoints =\n annotations[ANYSHIFT_CONFIGURATION_ENDPOINTS_ANNOTATION];\n const configurationDependencies =\n annotations[ANYSHIFT_CONFIGURATION_DEPENDENCIES_ANNOTATION];\n const configurationTarget =\n annotations[ANYSHIFT_RUNTIME_RESOURCE_ID_ANNOTATION];\n const [brief, setBrief] = useState<ServiceBrief>();\n const [catalogEntities, setCatalogEntities] = useState<Entity[]>([]);\n const [capabilities, setCapabilities] = useState<AnyshiftCapabilities>({\n askEnabled: false,\n queryEnabled: false,\n });\n const [error, setError] = useState<string>();\n const [refreshingAll, setRefreshingAll] = useState(false);\n const [refreshing, setRefreshing] = useState<Set<BriefSectionName>>(\n new Set(),\n );\n const active = useRef(true);\n\n const load = useCallback(\n async (refresh = false) => {\n setError(undefined);\n if (refresh) setRefreshingAll(true);\n try {\n const next = await api.brief(kind, target, {\n namespace,\n refresh,\n configurationEndpoints,\n configurationDependencies,\n configurationTarget,\n });\n if (active.current) setBrief(next);\n } catch (cause) {\n if (active.current)\n setError(cause instanceof Error ? cause.message : String(cause));\n } finally {\n if (active.current) setRefreshingAll(false);\n }\n },\n [\n api,\n configurationDependencies,\n configurationEndpoints,\n configurationTarget,\n kind,\n namespace,\n target,\n ],\n );\n\n const retrySection = useCallback(\n async (section: BriefSectionName) => {\n setRefreshing(current => new Set(current).add(section));\n try {\n const response = await api.briefSection(kind, target, section, {\n namespace,\n refresh: true,\n configurationEndpoints,\n configurationDependencies,\n configurationTarget,\n });\n if (active.current) {\n setBrief(current =>\n current\n ? {\n ...current,\n [section]: response.value,\n refreshedAt: response.refreshedAt,\n ...(response.observedService\n ? { observedService: response.observedService }\n : {}),\n }\n : current,\n );\n }\n } finally {\n if (active.current) {\n setRefreshing(current => {\n const next = new Set(current);\n next.delete(section);\n return next;\n });\n }\n }\n },\n [\n api,\n configurationDependencies,\n configurationEndpoints,\n configurationTarget,\n kind,\n namespace,\n target,\n ],\n );\n\n useEffect(() => {\n active.current = true;\n void load();\n return () => {\n active.current = false;\n };\n }, [load]);\n\n useEffect(() => {\n let cancelled = false;\n if (!catalogApi) {\n setCatalogEntities([]);\n return () => {\n cancelled = true;\n };\n }\n catalogApi\n .getEntities({\n filter: [{ kind: 'Component' }, { kind: 'Resource' }],\n fields: ['kind', 'metadata.name', 'metadata.namespace'],\n })\n .then(response => {\n if (!cancelled) setCatalogEntities(response.items);\n })\n .catch(() => {\n if (!cancelled) setCatalogEntities([]);\n });\n return () => {\n cancelled = true;\n };\n }, [catalogApi]);\n\n useEffect(() => {\n api\n .capabilities()\n .then(next => {\n if (active.current) setCapabilities(next);\n })\n .catch(() => {\n if (active.current) {\n setCapabilities({ askEnabled: false, queryEnabled: false });\n }\n });\n }, [api]);\n\n const resolvedBrief = useMemo<ServiceBrief | undefined>(() => {\n if (!brief || brief.impact.state !== 'data' || !brief.impact.data)\n return brief;\n const impactData = brief.impact.data;\n return {\n ...brief,\n impact: {\n ...brief.impact,\n data: {\n ...impactData,\n relatedEntities: resolveRelatedCatalogEntities(\n impactData.relatedEntities,\n catalogEntities,\n ),\n },\n },\n };\n }, [brief, catalogEntities]);\n\n if (error && !resolvedBrief)\n return <Typography color=\"error\">{error}</Typography>;\n if (!resolvedBrief) return <Progress />;\n return (\n <ServiceBriefView\n brief={resolvedBrief}\n onRetry={retrySection}\n refreshing={refreshing}\n onRefresh={() => void load(true)}\n refreshingAll={refreshingAll}\n refreshError={error}\n askEnabled={capabilities.askEnabled}\n askScope={askScopeForEntity(entity)}\n />\n );\n};\n\nexport const ServiceBriefView = ({\n brief,\n onRetry,\n refreshing = new Set(),\n onRefresh = () => {},\n refreshingAll = false,\n refreshError,\n askEnabled = false,\n askScope,\n}: {\n brief: ServiceBrief;\n onRetry: (section: BriefSectionName) => void;\n refreshing?: ReadonlySet<BriefSectionName>;\n onRefresh?: () => void;\n refreshingAll?: boolean;\n refreshError?: string;\n askEnabled?: boolean;\n askScope?: AskScope;\n}) => (\n <>\n <Box\n mb={3}\n px={2.5}\n py={2}\n borderLeft={4}\n borderColor=\"primary.main\"\n bgcolor=\"background.paper\"\n >\n <Box display=\"flex\" justifyContent=\"space-between\" gridGap={16}>\n <Box>\n <Typography variant=\"overline\" color=\"textSecondary\">\n Anyshift evidence trace\n </Typography>\n <Box display=\"flex\" flexWrap=\"wrap\" alignItems=\"center\" gridGap={8}>\n <Chip size=\"small\" label={`Workload · ${brief.target}`} />\n <Typography aria-hidden=\"true\" color=\"primary\">\n →\n </Typography>\n <Chip\n size=\"small\"\n color=\"primary\"\n label={`Observed service · ${\n brief.observedService || 'Not resolved'\n }`}\n />\n </Box>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Runtime namespace: {brief.namespace || 'Not set'} · Refreshed{' '}\n {formatTimestamp(brief.refreshedAt)}\n </Typography>\n </Box>\n <Button\n type=\"button\"\n variant=\"outlined\"\n size=\"small\"\n onClick={onRefresh}\n disabled={refreshingAll}\n startIcon={refreshingAll ? <CircularProgress size={14} /> : undefined}\n >\n {refreshingAll ? 'Refreshing…' : 'Refresh brief'}\n </Button>\n </Box>\n {refreshError && (\n <Typography color=\"error\" variant=\"caption\">\n Refresh failed: {refreshError}. Existing evidence is still shown.\n </Typography>\n )}\n </Box>\n\n <Grid container spacing={3} alignItems=\"stretch\">\n <Grid item xs={12}>\n <OverviewSection\n section={brief.overview}\n onRetry={onRetry}\n refreshing={refreshing.has('overview')}\n />\n </Grid>\n <Grid item xs={12}>\n <WhyNowSection\n section={brief.whyNow}\n onRetry={onRetry}\n refreshing={refreshing.has('whyNow')}\n />\n </Grid>\n {askEnabled && (\n <Grid item xs={12}>\n <AskAnyshiftCard context=\"entity\" scope={askScope} />\n </Grid>\n )}\n <Grid item xs={12}>\n <DatadogSignalSection\n section={brief.observability}\n onRetry={onRetry}\n refreshing={refreshing.has('observability')}\n />\n </Grid>\n <Grid item xs={12}>\n <ArchitectureSection\n section={brief.architecture}\n onRetry={onRetry}\n refreshing={refreshing.has('architecture')}\n />\n </Grid>\n <Grid item xs={12} md={8}>\n <DependencySection\n section={brief.topology}\n onRetry={onRetry}\n refreshing={refreshing.has('topology')}\n />\n </Grid>\n <Grid item xs={12} md={4}>\n <BriefRail brief={brief} onRetry={onRetry} refreshing={refreshing} />\n </Grid>\n <Grid item xs={12}>\n <TimelineSection\n section={brief.timeline}\n onRetry={onRetry}\n refreshing={refreshing.has('timeline')}\n />\n </Grid>\n </Grid>\n </>\n);\n\nfunction formatTimestamp(value: string): string {\n const parsed = Date.parse(value);\n return Number.isNaN(parsed) ? value : new Date(parsed).toLocaleString();\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAyCO,MAAM,wBAAwB,MAAM;AACzC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,OAAO,sBAAsB,MAAM,CAAA,uBAChC,0BAAA,EAAA,EAA2B,CAAA,uBAE3B,4BAAA,EAAA,EAA6B,CAAA;AAElC;AAEO,MAAM,+BAA+B,MAAM;AAChD,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,GAAA,GAAM,OAAO,cAAc,CAAA;AACjC,EAAA,MAAM,UAAA,GAAa,YAAA,EAAa,CAAE,GAAA,CAAI,aAAa,CAAA;AACnD,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAA,CAAS,WAAA,IAAe,EAAC;AACpD,EAAA,MAAM,MAAA,GAAS,aAAa,MAAM,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,wBAAwB,CAAA,IAAK,UAAA;AACtD,EAAA,MAAM,SAAA,GAAY,OAAO,QAAA,CAAS,SAAA;AAClC,EAAA,MAAM,sBAAA,GACJ,YAAY,2CAA2C,CAAA;AACzD,EAAA,MAAM,yBAAA,GACJ,YAAY,8CAA8C,CAAA;AAC5D,EAAA,MAAM,mBAAA,GACJ,YAAY,uCAAuC,CAAA;AACrD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA,EAAuB;AACjD,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,QAAA,CAAmB,EAAE,CAAA;AACnE,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,QAAA,CAA+B;AAAA,IACrE,UAAA,EAAY,KAAA;AAAA,IACZ,YAAA,EAAc;AAAA,GACf,CAAA;AACD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA,EAAiB;AAC3C,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AACxD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,QAAA;AAAA,wBAC9B,GAAA;AAAI,GACV;AACA,EAAA,MAAM,MAAA,GAAS,OAAO,IAAI,CAAA;AAE1B,EAAA,MAAM,IAAA,GAAO,WAAA;AAAA,IACX,OAAO,UAAU,KAAA,KAAU;AACzB,MAAA,QAAA,CAAS,MAAS,CAAA;AAClB,MAAA,IAAI,OAAA,mBAA0B,IAAI,CAAA;AAClC,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,KAAA,CAAM,MAAM,MAAA,EAAQ;AAAA,UACzC,SAAA;AAAA,UACA,OAAA;AAAA,UACA,sBAAA;AAAA,UACA,yBAAA;AAAA,UACA;AAAA,SACD,CAAA;AACD,QAAA,IAAI,MAAA,CAAO,OAAA,EAAS,QAAA,CAAS,IAAI,CAAA;AAAA,MACnC,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,MAAA,CAAO,OAAA;AACT,UAAA,QAAA,CAAS,iBAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,MACnE,CAAA,SAAE;AACA,QAAA,IAAI,MAAA,CAAO,OAAA,EAAS,gBAAA,CAAiB,KAAK,CAAA;AAAA,MAC5C;AAAA,IACF,CAAA;AAAA,IACA;AAAA,MACE,GAAA;AAAA,MACA,yBAAA;AAAA,MACA,sBAAA;AAAA,MACA,mBAAA;AAAA,MACA,IAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA;AACF,GACF;AAEA,EAAA,MAAM,YAAA,GAAe,WAAA;AAAA,IACnB,OAAO,OAAA,KAA8B;AACnC,MAAA,aAAA,CAAc,aAAW,IAAI,GAAA,CAAI,OAAO,CAAA,CAAE,GAAA,CAAI,OAAO,CAAC,CAAA;AACtD,MAAA,IAAI;AACF,QAAA,MAAM,WAAW,MAAM,GAAA,CAAI,YAAA,CAAa,IAAA,EAAM,QAAQ,OAAA,EAAS;AAAA,UAC7D,SAAA;AAAA,UACA,OAAA,EAAS,IAAA;AAAA,UACT,sBAAA;AAAA,UACA,yBAAA;AAAA,UACA;AAAA,SACD,CAAA;AACD,QAAA,IAAI,OAAO,OAAA,EAAS;AAClB,UAAA,QAAA;AAAA,YAAS,aACP,OAAA,GACI;AAAA,cACE,GAAG,OAAA;AAAA,cACH,CAAC,OAAO,GAAG,QAAA,CAAS,KAAA;AAAA,cACpB,aAAa,QAAA,CAAS,WAAA;AAAA,cACtB,GAAI,SAAS,eAAA,GACT,EAAE,iBAAiB,QAAA,CAAS,eAAA,KAC5B;AAAC,aACP,GACA;AAAA,WACN;AAAA,QACF;AAAA,MACF,CAAA,SAAE;AACA,QAAA,IAAI,OAAO,OAAA,EAAS;AAClB,UAAA,aAAA,CAAc,CAAA,OAAA,KAAW;AACvB,YAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,OAAO,CAAA;AAC5B,YAAA,IAAA,CAAK,OAAO,OAAO,CAAA;AACnB,YAAA,OAAO,IAAA;AAAA,UACT,CAAC,CAAA;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAA;AAAA,IACA;AAAA,MACE,GAAA;AAAA,MACA,yBAAA;AAAA,MACA,sBAAA;AAAA,MACA,mBAAA;AAAA,MACA,IAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA;AACF,GACF;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAA,CAAO,OAAA,GAAU,IAAA;AACjB,IAAA,KAAK,IAAA,EAAK;AACV,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,OAAA,GAAU,KAAA;AAAA,IACnB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA,kBAAA,CAAmB,EAAE,CAAA;AACrB,MAAA,OAAO,MAAM;AACX,QAAA,SAAA,GAAY,IAAA;AAAA,MACd,CAAA;AAAA,IACF;AACA,IAAA,UAAA,CACG,WAAA,CAAY;AAAA,MACX,MAAA,EAAQ,CAAC,EAAE,IAAA,EAAM,aAAY,EAAG,EAAE,IAAA,EAAM,UAAA,EAAY,CAAA;AAAA,MACpD,MAAA,EAAQ,CAAC,MAAA,EAAQ,eAAA,EAAiB,oBAAoB;AAAA,KACvD,CAAA,CACA,IAAA,CAAK,CAAA,QAAA,KAAY;AAChB,MAAA,IAAI,CAAC,SAAA,EAAW,kBAAA,CAAmB,QAAA,CAAS,KAAK,CAAA;AAAA,IACnD,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACX,MAAA,IAAI,CAAC,SAAA,EAAW,kBAAA,CAAmB,EAAE,CAAA;AAAA,IACvC,CAAC,CAAA;AACH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,GAAA,CACG,YAAA,EAAa,CACb,IAAA,CAAK,CAAA,IAAA,KAAQ;AACZ,MAAA,IAAI,MAAA,CAAO,OAAA,EAAS,eAAA,CAAgB,IAAI,CAAA;AAAA,IAC1C,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACX,MAAA,IAAI,OAAO,OAAA,EAAS;AAClB,QAAA,eAAA,CAAgB,EAAE,UAAA,EAAY,KAAA,EAAO,YAAA,EAAc,OAAO,CAAA;AAAA,MAC5D;AAAA,IACF,CAAC,CAAA;AAAA,EACL,CAAA,EAAG,CAAC,GAAG,CAAC,CAAA;AAER,EAAA,MAAM,aAAA,GAAgB,QAAkC,MAAM;AAC5D,IAAA,IAAI,CAAC,SAAS,KAAA,CAAM,MAAA,CAAO,UAAU,MAAA,IAAU,CAAC,MAAM,MAAA,CAAO,IAAA;AAC3D,MAAA,OAAO,KAAA;AACT,IAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,IAAA;AAChC,IAAA,OAAO;AAAA,MACL,GAAG,KAAA;AAAA,MACH,MAAA,EAAQ;AAAA,QACN,GAAG,KAAA,CAAM,MAAA;AAAA,QACT,IAAA,EAAM;AAAA,UACJ,GAAG,UAAA;AAAA,UACH,eAAA,EAAiB,6BAAA;AAAA,YACf,UAAA,CAAW,eAAA;AAAA,YACX;AAAA;AACF;AACF;AACF,KACF;AAAA,EACF,CAAA,EAAG,CAAC,KAAA,EAAO,eAAe,CAAC,CAAA;AAE3B,EAAA,IAAI,SAAS,CAAC,aAAA;AACZ,IAAA,uBAAO,GAAA,CAAC,UAAA,EAAA,EAAW,KAAA,EAAM,OAAA,EAAS,QAAA,EAAA,KAAA,EAAM,CAAA;AAC1C,EAAA,IAAI,CAAC,aAAA,EAAe,uBAAO,GAAA,CAAC,QAAA,EAAA,EAAS,CAAA;AACrC,EAAA,uBACE,GAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,aAAA;AAAA,MACP,OAAA,EAAS,YAAA;AAAA,MACT,UAAA;AAAA,MACA,SAAA,EAAW,MAAM,KAAK,IAAA,CAAK,IAAI,CAAA;AAAA,MAC/B,aAAA;AAAA,MACA,YAAA,EAAc,KAAA;AAAA,MACd,YAAY,YAAA,CAAa,UAAA;AAAA,MACzB,QAAA,EAAU,kBAAkB,MAAM;AAAA;AAAA,GACpC;AAEJ;AAEO,MAAM,mBAAmB,CAAC;AAAA,EAC/B,KAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA,uBAAiB,GAAA,EAAI;AAAA,EACrB,YAAY,MAAM;AAAA,EAAC,CAAA;AAAA,EACnB,aAAA,GAAgB,KAAA;AAAA,EAChB,YAAA;AAAA,EACA,UAAA,GAAa,KAAA;AAAA,EACb;AACF,CAAA,qBAUE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,kBAAA,IAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,EAAA,EAAI,CAAA;AAAA,MACJ,EAAA,EAAI,GAAA;AAAA,MACJ,EAAA,EAAI,CAAA;AAAA,MACJ,UAAA,EAAY,CAAA;AAAA,MACZ,WAAA,EAAY,cAAA;AAAA,MACZ,OAAA,EAAQ,kBAAA;AAAA,MAER,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,OAAI,OAAA,EAAQ,MAAA,EAAO,cAAA,EAAe,eAAA,EAAgB,SAAS,EAAA,EAC1D,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,GAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,UAAA,EAAW,KAAA,EAAM,iBAAgB,QAAA,EAAA,yBAAA,EAErD,CAAA;AAAA,4BACA,IAAA,CAAC,OAAI,OAAA,EAAQ,MAAA,EAAO,UAAS,MAAA,EAAO,UAAA,EAAW,QAAA,EAAS,OAAA,EAAS,CAAA,EAC/D,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,QAAK,IAAA,EAAK,OAAA,EAAQ,OAAO,CAAA,cAAA,EAAc,KAAA,CAAM,MAAM,CAAA,CAAA,EAAI,CAAA;AAAA,kCACvD,UAAA,EAAA,EAAW,aAAA,EAAY,MAAA,EAAO,KAAA,EAAM,WAAU,QAAA,EAAA,QAAA,EAE/C,CAAA;AAAA,8BACA,GAAA;AAAA,gBAAC,IAAA;AAAA,gBAAA;AAAA,kBACC,IAAA,EAAK,OAAA;AAAA,kBACL,KAAA,EAAM,SAAA;AAAA,kBACN,KAAA,EAAO,CAAA,sBAAA,EACL,KAAA,CAAM,eAAA,IAAmB,cAC3B,CAAA;AAAA;AAAA;AACF,aAAA,EACF,CAAA;AAAA,4BACA,IAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,SAAA,EAAU,OAAM,eAAA,EAAgB,QAAA,EAAA;AAAA,cAAA,qBAAA;AAAA,cAC9B,MAAM,SAAA,IAAa,SAAA;AAAA,cAAU,iBAAA;AAAA,cAAa,GAAA;AAAA,cAC7D,eAAA,CAAgB,MAAM,WAAW;AAAA,aAAA,EACpC;AAAA,WAAA,EACF,CAAA;AAAA,0BACA,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,OAAA,EAAQ,UAAA;AAAA,cACR,IAAA,EAAK,OAAA;AAAA,cACL,OAAA,EAAS,SAAA;AAAA,cACT,QAAA,EAAU,aAAA;AAAA,cACV,WAAW,aAAA,mBAAgB,GAAA,CAAC,gBAAA,EAAA,EAAiB,IAAA,EAAM,IAAI,CAAA,GAAK,MAAA;AAAA,cAE3D,0BAAgB,kBAAA,GAAgB;AAAA;AAAA;AACnC,SAAA,EACF,CAAA;AAAA,QACC,gCACC,IAAA,CAAC,UAAA,EAAA,EAAW,KAAA,EAAM,OAAA,EAAQ,SAAQ,SAAA,EAAU,QAAA,EAAA;AAAA,UAAA,kBAAA;AAAA,UACzB,YAAA;AAAA,UAAa;AAAA,SAAA,EAChC;AAAA;AAAA;AAAA,GAEJ;AAAA,uBAEC,IAAA,EAAA,EAAK,SAAA,EAAS,MAAC,OAAA,EAAS,CAAA,EAAG,YAAW,SAAA,EACrC,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,EAAA,EAAI,EAAA,EACb,QAAA,kBAAA,GAAA;AAAA,MAAC,eAAA;AAAA,MAAA;AAAA,QACC,SAAS,KAAA,CAAM,QAAA;AAAA,QACf,OAAA;AAAA,QACA,UAAA,EAAY,UAAA,CAAW,GAAA,CAAI,UAAU;AAAA;AAAA,KACvC,EACF,CAAA;AAAA,oBACA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,IAAI,EAAA,EACb,QAAA,kBAAA,GAAA;AAAA,MAAC,aAAA;AAAA,MAAA;AAAA,QACC,SAAS,KAAA,CAAM,MAAA;AAAA,QACf,OAAA;AAAA,QACA,UAAA,EAAY,UAAA,CAAW,GAAA,CAAI,QAAQ;AAAA;AAAA,KACrC,EACF,CAAA;AAAA,IACC,UAAA,oBACC,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,EAAA,EAAI,EAAA,EACb,QAAA,kBAAA,GAAA,CAAC,eAAA,EAAA,EAAgB,OAAA,EAAQ,QAAA,EAAS,KAAA,EAAO,UAAU,CAAA,EACrD,CAAA;AAAA,oBAEF,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,IAAI,EAAA,EACb,QAAA,kBAAA,GAAA;AAAA,MAAC,oBAAA;AAAA,MAAA;AAAA,QACC,SAAS,KAAA,CAAM,aAAA;AAAA,QACf,OAAA;AAAA,QACA,UAAA,EAAY,UAAA,CAAW,GAAA,CAAI,eAAe;AAAA;AAAA,KAC5C,EACF,CAAA;AAAA,oBACA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,IAAI,EAAA,EACb,QAAA,kBAAA,GAAA;AAAA,MAAC,mBAAA;AAAA,MAAA;AAAA,QACC,SAAS,KAAA,CAAM,YAAA;AAAA,QACf,OAAA;AAAA,QACA,UAAA,EAAY,UAAA,CAAW,GAAA,CAAI,cAAc;AAAA;AAAA,KAC3C,EACF,CAAA;AAAA,wBACC,IAAA,EAAA,EAAK,IAAA,EAAI,MAAC,EAAA,EAAI,EAAA,EAAI,IAAI,CAAA,EACrB,QAAA,kBAAA,GAAA;AAAA,MAAC,iBAAA;AAAA,MAAA;AAAA,QACC,SAAS,KAAA,CAAM,QAAA;AAAA,QACf,OAAA;AAAA,QACA,UAAA,EAAY,UAAA,CAAW,GAAA,CAAI,UAAU;AAAA;AAAA,KACvC,EACF,CAAA;AAAA,oBACA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,CAAA,EACrB,QAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAU,KAAA,EAAc,OAAA,EAAkB,YAAwB,CAAA,EACrE,CAAA;AAAA,oBACA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,IAAI,EAAA,EACb,QAAA,kBAAA,GAAA;AAAA,MAAC,eAAA;AAAA,MAAA;AAAA,QACC,SAAS,KAAA,CAAM,QAAA;AAAA,QACf,OAAA;AAAA,QACA,UAAA,EAAY,UAAA,CAAW,GAAA,CAAI,UAAU;AAAA;AAAA,KACvC,EACF;AAAA,GAAA,EACF;AAAA,CAAA,EACF;AAGF,SAAS,gBAAgB,KAAA,EAAuB;AAC9C,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAC/B,EAAA,OAAO,MAAA,CAAO,MAAM,MAAM,CAAA,GAAI,QAAQ,IAAI,IAAA,CAAK,MAAM,CAAA,CAAE,cAAA,EAAe;AACxE;;;;"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Typography, Chip } from '@material-ui/core';
|
|
3
|
+
|
|
4
|
+
const formatObservationTime = (value, now = Date.now()) => {
|
|
5
|
+
if (!value) return { relative: "Observation time unavailable" };
|
|
6
|
+
const timestamp = Date.parse(value);
|
|
7
|
+
if (Number.isNaN(timestamp)) {
|
|
8
|
+
return { relative: "Observation time unavailable" };
|
|
9
|
+
}
|
|
10
|
+
const minutes = Math.round((timestamp - now) / 6e4);
|
|
11
|
+
const [amount, unit] = Math.abs(minutes) < 60 ? [minutes, "minute"] : Math.abs(minutes) < 2880 ? [Math.round(minutes / 60), "hour"] : [Math.round(minutes / 1440), "day"];
|
|
12
|
+
const relative = new Intl.RelativeTimeFormat("en", {
|
|
13
|
+
numeric: "auto"
|
|
14
|
+
}).format(amount, unit);
|
|
15
|
+
return {
|
|
16
|
+
relative: `Observed ${relative}`,
|
|
17
|
+
exact: new Date(timestamp).toISOString()
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const isUsableOperation = (operation) => operation.method !== null || operation.pathTemplate !== null;
|
|
21
|
+
const usableOperations = (relationship) => relationship.operationSources.flatMap((source) => source.operations).filter(isUsableOperation);
|
|
22
|
+
const usableOperationCount = (relationship) => usableOperations(relationship).length;
|
|
23
|
+
const operationPreview = (relationship) => {
|
|
24
|
+
const operations = usableOperations(relationship);
|
|
25
|
+
const operation = operations[0];
|
|
26
|
+
if (relationship.operationCount === 0 || !operation) return "";
|
|
27
|
+
const summary = [
|
|
28
|
+
operation.method ?? "Method unavailable",
|
|
29
|
+
operation.pathTemplate ?? "Path unavailable"
|
|
30
|
+
].join(" ");
|
|
31
|
+
const remaining = relationship.operationCount - 1;
|
|
32
|
+
return remaining > 0 ? `${summary} \xB7 +${remaining} ${remaining === 1 ? "operation" : "operations"}` : summary;
|
|
33
|
+
};
|
|
34
|
+
const ArchitectureOperationDetails = ({
|
|
35
|
+
relationship,
|
|
36
|
+
onClose,
|
|
37
|
+
now
|
|
38
|
+
}) => {
|
|
39
|
+
if (relationship.operationCount === 0 || usableOperationCount(relationship) === 0) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return /* @__PURE__ */ jsxs(Box, { border: 1, borderColor: "divider", borderRadius: 4, p: 2, children: [
|
|
43
|
+
/* @__PURE__ */ jsxs(
|
|
44
|
+
Box,
|
|
45
|
+
{
|
|
46
|
+
display: "flex",
|
|
47
|
+
justifyContent: "space-between",
|
|
48
|
+
alignItems: "flex-start",
|
|
49
|
+
children: [
|
|
50
|
+
/* @__PURE__ */ jsxs(Typography, { component: "h3", variant: "h6", children: [
|
|
51
|
+
relationship.fromLabel,
|
|
52
|
+
" to ",
|
|
53
|
+
relationship.toLabel
|
|
54
|
+
] }),
|
|
55
|
+
/* @__PURE__ */ jsx(
|
|
56
|
+
"button",
|
|
57
|
+
{
|
|
58
|
+
"aria-label": "Close HTTP operation details",
|
|
59
|
+
onClick: onClose,
|
|
60
|
+
type: "button",
|
|
61
|
+
children: "Close"
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
),
|
|
67
|
+
relationship.operationSources.map((source) => /* @__PURE__ */ jsxs(Box, { mt: 2, children: [
|
|
68
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle2", children: source.source }),
|
|
69
|
+
source.operations.filter(isUsableOperation).map((operation, index) => {
|
|
70
|
+
const observed = formatObservationTime(operation.observedAt, now);
|
|
71
|
+
return /* @__PURE__ */ jsxs(
|
|
72
|
+
Box,
|
|
73
|
+
{
|
|
74
|
+
display: "flex",
|
|
75
|
+
flexWrap: "wrap",
|
|
76
|
+
alignItems: "center",
|
|
77
|
+
gridGap: 8,
|
|
78
|
+
mt: 0.5,
|
|
79
|
+
children: [
|
|
80
|
+
operation.method ? /* @__PURE__ */ jsx(
|
|
81
|
+
Chip,
|
|
82
|
+
{
|
|
83
|
+
label: operation.method,
|
|
84
|
+
size: "small",
|
|
85
|
+
variant: "outlined"
|
|
86
|
+
}
|
|
87
|
+
) : /* @__PURE__ */ jsx(Typography, { variant: "body2", children: "Method unavailable" }),
|
|
88
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", children: operation.pathTemplate ?? "Path unavailable" }),
|
|
89
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "textSecondary", children: /* @__PURE__ */ jsxs("time", { dateTime: operation.observedAt ?? void 0, children: [
|
|
90
|
+
/* @__PURE__ */ jsx("span", { children: observed.relative }),
|
|
91
|
+
observed.exact && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
92
|
+
" \xB7 ",
|
|
93
|
+
/* @__PURE__ */ jsx("span", { children: observed.exact })
|
|
94
|
+
] })
|
|
95
|
+
] }) })
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
`${operation.method ?? ""}\0${operation.pathTemplate ?? ""}\0${index}`
|
|
99
|
+
);
|
|
100
|
+
}),
|
|
101
|
+
source.hasIncompleteOperations && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "textSecondary", component: "p", children: "Operation details incomplete" })
|
|
102
|
+
] }, source.source))
|
|
103
|
+
] });
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export { ArchitectureOperationDetails, formatObservationTime, operationPreview };
|
|
107
|
+
//# sourceMappingURL=ArchitectureOperationDetails.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArchitectureOperationDetails.esm.js","sources":["../../../src/components/entity/ArchitectureOperationDetails.tsx"],"sourcesContent":["import { Box, Chip, Typography } from '@material-ui/core';\nimport type { ArchitectureDiagramRelationship } from './architectureDiagram';\n\nexport const formatObservationTime = (\n value: string | null,\n now = Date.now(),\n): { relative: string; exact?: string } => {\n if (!value) return { relative: 'Observation time unavailable' };\n\n const timestamp = Date.parse(value);\n if (Number.isNaN(timestamp)) {\n return { relative: 'Observation time unavailable' };\n }\n\n const minutes = Math.round((timestamp - now) / 60_000);\n const [amount, unit] =\n Math.abs(minutes) < 60\n ? ([minutes, 'minute'] as const)\n : Math.abs(minutes) < 2_880\n ? ([Math.round(minutes / 60), 'hour'] as const)\n : ([Math.round(minutes / 1_440), 'day'] as const);\n const relative = new Intl.RelativeTimeFormat('en', {\n numeric: 'auto',\n }).format(amount, unit);\n\n return {\n relative: `Observed ${relative}`,\n exact: new Date(timestamp).toISOString(),\n };\n};\n\nconst isUsableOperation = (operation: {\n method: string | null;\n pathTemplate: string | null;\n}) => operation.method !== null || operation.pathTemplate !== null;\n\nconst usableOperations = (relationship: ArchitectureDiagramRelationship) =>\n relationship.operationSources\n .flatMap(source => source.operations)\n .filter(isUsableOperation);\n\nconst usableOperationCount = (relationship: ArchitectureDiagramRelationship) =>\n usableOperations(relationship).length;\n\nexport const operationPreview = (\n relationship: ArchitectureDiagramRelationship,\n): string => {\n const operations = usableOperations(relationship);\n const operation = operations[0];\n if (relationship.operationCount === 0 || !operation) return '';\n\n const summary = [\n operation.method ?? 'Method unavailable',\n operation.pathTemplate ?? 'Path unavailable',\n ].join(' ');\n const remaining = relationship.operationCount - 1;\n return remaining > 0\n ? `${summary} · +${remaining} ${\n remaining === 1 ? 'operation' : 'operations'\n }`\n : summary;\n};\n\nexport const ArchitectureOperationDetails = ({\n relationship,\n onClose,\n now,\n}: {\n relationship: ArchitectureDiagramRelationship;\n onClose: () => void;\n now?: number;\n}) => {\n if (\n relationship.operationCount === 0 ||\n usableOperationCount(relationship) === 0\n ) {\n return null;\n }\n\n return (\n <Box border={1} borderColor=\"divider\" borderRadius={4} p={2}>\n <Box\n display=\"flex\"\n justifyContent=\"space-between\"\n alignItems=\"flex-start\"\n >\n <Typography component=\"h3\" variant=\"h6\">\n {relationship.fromLabel} to {relationship.toLabel}\n </Typography>\n <button\n aria-label=\"Close HTTP operation details\"\n onClick={onClose}\n type=\"button\"\n >\n Close\n </button>\n </Box>\n\n {relationship.operationSources.map(source => (\n <Box key={source.source} mt={2}>\n <Typography variant=\"subtitle2\">{source.source}</Typography>\n {source.operations\n .filter(isUsableOperation)\n .map((operation, index) => {\n const observed = formatObservationTime(operation.observedAt, now);\n return (\n <Box\n key={`${operation.method ?? ''}\\u0000${\n operation.pathTemplate ?? ''\n }\\u0000${index}`}\n display=\"flex\"\n flexWrap=\"wrap\"\n alignItems=\"center\"\n gridGap={8}\n mt={0.5}\n >\n {operation.method ? (\n <Chip\n label={operation.method}\n size=\"small\"\n variant=\"outlined\"\n />\n ) : (\n <Typography variant=\"body2\">Method unavailable</Typography>\n )}\n <Typography variant=\"body2\">\n {operation.pathTemplate ?? 'Path unavailable'}\n </Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n <time dateTime={operation.observedAt ?? undefined}>\n <span>{observed.relative}</span>\n {observed.exact && (\n <>\n {' · '}\n <span>{observed.exact}</span>\n </>\n )}\n </time>\n </Typography>\n </Box>\n );\n })}\n {source.hasIncompleteOperations && (\n <Typography variant=\"caption\" color=\"textSecondary\" component=\"p\">\n Operation details incomplete\n </Typography>\n )}\n </Box>\n ))}\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1,47 +1,114 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Box, CircularProgress, Typography } from '@material-ui/core';
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, CircularProgress, Typography, makeStyles } from '@material-ui/core';
|
|
3
3
|
import mermaid from 'mermaid';
|
|
4
4
|
import { useMemo, useRef, useState, useEffect } from 'react';
|
|
5
5
|
import { buildArchitectureDiagram } from './architectureDiagram.esm.js';
|
|
6
|
+
import { operationPreview, ArchitectureOperationDetails } from './ArchitectureOperationDetails.esm.js';
|
|
6
7
|
|
|
7
8
|
let diagramSequence = 0;
|
|
8
9
|
let mermaidInitialized = false;
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
10
|
+
const useStyles = makeStyles((theme) => ({
|
|
11
|
+
diagram: {
|
|
12
|
+
"& .anyshift-http-edge-affordance": {
|
|
13
|
+
cursor: "pointer"
|
|
14
|
+
},
|
|
15
|
+
"& .anyshift-http-edge-affordance:focus": {
|
|
16
|
+
outline: `2px solid ${theme.palette.text.primary}`,
|
|
17
|
+
outlineOffset: 2
|
|
18
|
+
},
|
|
19
|
+
"& .edgeLabel.anyshift-http-edge-affordance rect": {
|
|
20
|
+
rx: 4,
|
|
21
|
+
ry: 4,
|
|
22
|
+
stroke: theme.palette.divider,
|
|
23
|
+
strokeWidth: 1
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
fallbackButton: {
|
|
27
|
+
appearance: "none",
|
|
28
|
+
background: "transparent",
|
|
29
|
+
border: 0,
|
|
30
|
+
color: "inherit",
|
|
31
|
+
cursor: "pointer",
|
|
32
|
+
font: "inherit",
|
|
33
|
+
padding: 0,
|
|
34
|
+
textAlign: "left",
|
|
35
|
+
"&:focus": {
|
|
36
|
+
outline: `2px solid ${theme.palette.text.primary}`,
|
|
37
|
+
outlineOffset: 2
|
|
31
38
|
}
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
const hasUsableOperations = (relationship) => relationship.operationCount > 0 && operationPreview(relationship) !== "";
|
|
42
|
+
const RelationshipFallback = ({
|
|
43
|
+
relationships,
|
|
44
|
+
onSelect,
|
|
45
|
+
selectedId
|
|
46
|
+
}) => {
|
|
47
|
+
const classes = useStyles();
|
|
48
|
+
return /* @__PURE__ */ jsxs(Box, { mt: 2, children: [
|
|
49
|
+
/* @__PURE__ */ jsx(Typography, { color: "error", variant: "body2", role: "alert", children: "The architecture diagram could not be drawn. Showing relationships instead." }),
|
|
50
|
+
/* @__PURE__ */ jsx(
|
|
51
|
+
Box,
|
|
52
|
+
{
|
|
53
|
+
component: "ul",
|
|
54
|
+
m: 0,
|
|
55
|
+
mt: 1,
|
|
56
|
+
p: 0,
|
|
57
|
+
"aria-label": "Architecture relationships",
|
|
58
|
+
children: relationships.map((relationship) => /* @__PURE__ */ jsx(
|
|
59
|
+
Box,
|
|
60
|
+
{
|
|
61
|
+
component: "li",
|
|
62
|
+
py: 0.5,
|
|
63
|
+
style: { listStyle: "none" },
|
|
64
|
+
children: hasUsableOperations(relationship) ? /* @__PURE__ */ jsx(
|
|
65
|
+
"button",
|
|
66
|
+
{
|
|
67
|
+
"aria-label": `Show HTTP operations for ${relationship.fromLabel} to ${relationship.toLabel}`,
|
|
68
|
+
"aria-expanded": selectedId === relationship.id,
|
|
69
|
+
className: classes.fallbackButton,
|
|
70
|
+
onClick: () => onSelect(relationship),
|
|
71
|
+
type: "button",
|
|
72
|
+
children: /* @__PURE__ */ jsx(Typography, { component: "span", variant: "body2", children: relationship.label })
|
|
73
|
+
}
|
|
74
|
+
) : /* @__PURE__ */ jsx(Typography, { variant: "body2", children: relationship.label })
|
|
75
|
+
},
|
|
76
|
+
relationship.id
|
|
77
|
+
))
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
] });
|
|
81
|
+
};
|
|
34
82
|
const ArchitectureDiagram = ({
|
|
35
83
|
graph,
|
|
36
84
|
view = "runtime"
|
|
37
85
|
}) => {
|
|
86
|
+
const classes = useStyles();
|
|
38
87
|
const definition = useMemo(
|
|
39
88
|
() => buildArchitectureDiagram(graph, { view }),
|
|
40
89
|
[graph, view]
|
|
41
90
|
);
|
|
42
91
|
const diagramId = useRef(`anyshift-architecture-${diagramSequence++}`);
|
|
92
|
+
const diagramRef = useRef(null);
|
|
43
93
|
const [svg, setSvg] = useState();
|
|
44
94
|
const [renderFailed, setRenderFailed] = useState(false);
|
|
95
|
+
const [selectedId, setSelectedId] = useState();
|
|
96
|
+
const [previewId, setPreviewId] = useState();
|
|
97
|
+
const operationRelationships = useMemo(
|
|
98
|
+
() => definition.relationships.filter(hasUsableOperations),
|
|
99
|
+
[definition.relationships]
|
|
100
|
+
);
|
|
101
|
+
const relationshipsById = useMemo(
|
|
102
|
+
() => new Map(
|
|
103
|
+
operationRelationships.map((relationship) => [
|
|
104
|
+
relationship.id,
|
|
105
|
+
relationship
|
|
106
|
+
])
|
|
107
|
+
),
|
|
108
|
+
[operationRelationships]
|
|
109
|
+
);
|
|
110
|
+
const selectedRelationship = selectedId ? relationshipsById.get(selectedId) : void 0;
|
|
111
|
+
const previewRelationship = previewId ? relationshipsById.get(previewId) : void 0;
|
|
45
112
|
useEffect(() => {
|
|
46
113
|
let active = true;
|
|
47
114
|
setSvg(void 0);
|
|
@@ -64,25 +131,117 @@ const ArchitectureDiagram = ({
|
|
|
64
131
|
active = false;
|
|
65
132
|
};
|
|
66
133
|
}, [definition.source]);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (selectedId && !relationshipsById.has(selectedId)) {
|
|
136
|
+
setSelectedId(void 0);
|
|
137
|
+
}
|
|
138
|
+
if (previewId && !relationshipsById.has(previewId)) {
|
|
139
|
+
setPreviewId(void 0);
|
|
140
|
+
}
|
|
141
|
+
}, [previewId, relationshipsById, selectedId]);
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (!svg || !diagramRef.current) return;
|
|
144
|
+
const edgePaths = diagramRef.current.querySelectorAll(".edgePath");
|
|
145
|
+
const edgeLabels = diagramRef.current.querySelectorAll(".edgeLabel");
|
|
146
|
+
operationRelationships.forEach((relationship) => {
|
|
147
|
+
const ariaLabel = `HTTP operations for ${relationship.fromLabel} to ${relationship.toLabel}`;
|
|
148
|
+
[
|
|
149
|
+
edgePaths.item(relationship.diagramIndex),
|
|
150
|
+
edgeLabels.item(relationship.diagramIndex)
|
|
151
|
+
].forEach((element) => {
|
|
152
|
+
if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
element.dataset.anyshiftEdgeId = relationship.id;
|
|
156
|
+
element.setAttribute("role", "button");
|
|
157
|
+
element.setAttribute("tabindex", "0");
|
|
158
|
+
element.setAttribute("aria-label", ariaLabel);
|
|
159
|
+
element.setAttribute(
|
|
160
|
+
"aria-expanded",
|
|
161
|
+
String(selectedId === relationship.id)
|
|
162
|
+
);
|
|
163
|
+
element.classList.add("anyshift-http-edge-affordance");
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}, [operationRelationships, selectedId, svg]);
|
|
167
|
+
const relationshipFromTarget = (target) => {
|
|
168
|
+
if (!(target instanceof Element)) return void 0;
|
|
169
|
+
const edge = target.closest("[data-anyshift-edge-id]");
|
|
170
|
+
const id = edge?.dataset.anyshiftEdgeId;
|
|
171
|
+
return id ? relationshipsById.get(id) : void 0;
|
|
172
|
+
};
|
|
173
|
+
const selectRelationship = (target) => {
|
|
174
|
+
const relationship = relationshipFromTarget(target);
|
|
175
|
+
if (relationship) setSelectedId(relationship.id);
|
|
176
|
+
};
|
|
177
|
+
const handleKeyDown = (event) => {
|
|
178
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
179
|
+
const relationship = relationshipFromTarget(event.target);
|
|
180
|
+
if (relationship) {
|
|
181
|
+
event.preventDefault();
|
|
182
|
+
setSelectedId(relationship.id);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (event.key === "Escape") setSelectedId(void 0);
|
|
186
|
+
};
|
|
187
|
+
const handleMouseOver = (event) => {
|
|
188
|
+
const relationship = relationshipFromTarget(event.target);
|
|
189
|
+
if (relationship) setPreviewId(relationship.id);
|
|
190
|
+
};
|
|
191
|
+
const handleMouseOut = (event) => {
|
|
192
|
+
const relationship = relationshipFromTarget(event.target);
|
|
193
|
+
const nextRelationship = relationshipFromTarget(event.relatedTarget);
|
|
194
|
+
if (relationship && relationship.id !== nextRelationship?.id) {
|
|
195
|
+
setPreviewId(void 0);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const handleFocus = (event) => {
|
|
199
|
+
const relationship = relationshipFromTarget(event.target);
|
|
200
|
+
if (relationship) setPreviewId(relationship.id);
|
|
201
|
+
};
|
|
202
|
+
const handleBlur = (event) => {
|
|
203
|
+
const relationship = relationshipFromTarget(event.target);
|
|
204
|
+
const nextRelationship = relationshipFromTarget(event.relatedTarget);
|
|
205
|
+
if (relationship && relationship.id !== nextRelationship?.id) {
|
|
206
|
+
setPreviewId(void 0);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
return /* @__PURE__ */ jsxs(Box, { onKeyDown: handleKeyDown, children: [
|
|
210
|
+
renderFailed ? /* @__PURE__ */ jsx(
|
|
211
|
+
RelationshipFallback,
|
|
212
|
+
{
|
|
213
|
+
relationships: definition.relationships,
|
|
214
|
+
onSelect: (relationship) => setSelectedId(relationship.id),
|
|
215
|
+
selectedId
|
|
216
|
+
}
|
|
217
|
+
) : !svg ? /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", gridGap: 8, py: 3, children: [
|
|
72
218
|
/* @__PURE__ */ jsx(CircularProgress, { size: 18 }),
|
|
73
219
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "textSecondary", children: "Drawing architecture\u2026" })
|
|
74
|
-
] })
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
220
|
+
] }) : /* @__PURE__ */ jsx(
|
|
221
|
+
Box,
|
|
222
|
+
{
|
|
223
|
+
"aria-label": "C4 architecture diagram",
|
|
224
|
+
className: classes.diagram,
|
|
225
|
+
onBlur: handleBlur,
|
|
226
|
+
onClick: (event) => selectRelationship(event.target),
|
|
227
|
+
onFocus: handleFocus,
|
|
228
|
+
onMouseOut: handleMouseOut,
|
|
229
|
+
onMouseOver: handleMouseOver,
|
|
230
|
+
role: "group",
|
|
231
|
+
mt: 1,
|
|
232
|
+
style: { overflowX: "auto" },
|
|
233
|
+
children: /* @__PURE__ */ jsx("div", { ref: diagramRef, dangerouslySetInnerHTML: { __html: svg } })
|
|
234
|
+
}
|
|
235
|
+
),
|
|
236
|
+
previewRelationship && /* @__PURE__ */ jsx(Typography, { role: "tooltip", variant: "caption", color: "textSecondary", children: operationPreview(previewRelationship) }),
|
|
237
|
+
selectedRelationship && /* @__PURE__ */ jsx(Box, { mt: 2, children: /* @__PURE__ */ jsx(
|
|
238
|
+
ArchitectureOperationDetails,
|
|
239
|
+
{
|
|
240
|
+
relationship: selectedRelationship,
|
|
241
|
+
onClose: () => setSelectedId(void 0)
|
|
242
|
+
}
|
|
243
|
+
) })
|
|
244
|
+
] });
|
|
86
245
|
};
|
|
87
246
|
|
|
88
247
|
export { ArchitectureDiagram };
|