@k-4u/resource-mapper-core 0.0.1
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/.aws-icons-last-updated +1 -0
- package/.svelte-kit/ambient.d.ts +263 -0
- package/.svelte-kit/generated/client/app.js +31 -0
- package/.svelte-kit/generated/client/matchers.js +1 -0
- package/.svelte-kit/generated/client/nodes/0.js +1 -0
- package/.svelte-kit/generated/client/nodes/1.js +1 -0
- package/.svelte-kit/generated/client/nodes/2.js +3 -0
- package/.svelte-kit/generated/client/nodes/3.js +3 -0
- package/.svelte-kit/generated/client-optimized/app.js +31 -0
- package/.svelte-kit/generated/client-optimized/matchers.js +1 -0
- package/.svelte-kit/generated/client-optimized/nodes/0.js +1 -0
- package/.svelte-kit/generated/client-optimized/nodes/1.js +1 -0
- package/.svelte-kit/generated/client-optimized/nodes/2.js +3 -0
- package/.svelte-kit/generated/client-optimized/nodes/3.js +3 -0
- package/.svelte-kit/generated/root.js +3 -0
- package/.svelte-kit/generated/root.svelte +68 -0
- package/.svelte-kit/generated/server/internal.js +53 -0
- package/.svelte-kit/non-ambient.d.ts +43 -0
- package/.svelte-kit/output/client/.vite/manifest.json +175 -0
- package/.svelte-kit/output/client/_app/immutable/assets/0.Czt_67iE.css +1 -0
- package/.svelte-kit/output/client/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css +1 -0
- package/.svelte-kit/output/client/_app/immutable/assets/helpers.ysDrpaDf.css +1 -0
- package/.svelte-kit/output/client/_app/immutable/assets/libavoid.DQJapW5w.wasm +0 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/BlLuv0eP.js +46 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/CSBHmwYv.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/CTCi5ueQ.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/CfOzjaik.js +2 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/D4PdvFNs.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/DXgP-QUS.js +2 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/DlbDC5An.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/chunks/wRWe7aK9.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/entry/app.ConrMuHl.js +2 -0
- package/.svelte-kit/output/client/_app/immutable/entry/start.Bm6FyGme.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/nodes/0.d3cL-ETU.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/nodes/1.D6z9rPGv.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/nodes/2.CLD-8chl.js +1 -0
- package/.svelte-kit/output/client/_app/immutable/nodes/3.DXYeBoel.js +1 -0
- package/.svelte-kit/output/client/_app/version.json +1 -0
- package/.svelte-kit/output/client/libavoid.wasm +0 -0
- package/.svelte-kit/output/client/static/robots.txt +3 -0
- package/.svelte-kit/output/prerendered/dependencies/_app/env.js +1 -0
- package/.svelte-kit/output/server/.vite/manifest.json +224 -0
- package/.svelte-kit/output/server/_app/immutable/assets/LoadingOverlay.DBbe6V8W.css +1 -0
- package/.svelte-kit/output/server/_app/immutable/assets/_layout.Czt_67iE.css +1 -0
- package/.svelte-kit/output/server/_app/immutable/assets/_page.D9P41uDZ.css +1 -0
- package/.svelte-kit/output/server/chunks/ErrorDisplay.js +59 -0
- package/.svelte-kit/output/server/chunks/LoadingOverlay.js +12 -0
- package/.svelte-kit/output/server/chunks/LoadingOverlay.svelte_svelte_type_style_lang.js +1671 -0
- package/.svelte-kit/output/server/chunks/connections.js +33 -0
- package/.svelte-kit/output/server/chunks/diagram.js +7 -0
- package/.svelte-kit/output/server/chunks/environment.js +34 -0
- package/.svelte-kit/output/server/chunks/equality.js +57 -0
- package/.svelte-kit/output/server/chunks/exports.js +174 -0
- package/.svelte-kit/output/server/chunks/false.js +4 -0
- package/.svelte-kit/output/server/chunks/index.js +59 -0
- package/.svelte-kit/output/server/chunks/index2.js +2939 -0
- package/.svelte-kit/output/server/chunks/index3.js +20 -0
- package/.svelte-kit/output/server/chunks/internal.js +1017 -0
- package/.svelte-kit/output/server/chunks/shared.js +770 -0
- package/.svelte-kit/output/server/chunks/utils.js +43 -0
- package/.svelte-kit/output/server/entries/pages/_error.svelte.js +64 -0
- package/.svelte-kit/output/server/entries/pages/_layout.svelte.js +65 -0
- package/.svelte-kit/output/server/entries/pages/_page.svelte.js +3991 -0
- package/.svelte-kit/output/server/entries/pages/_page.ts.js +30 -0
- package/.svelte-kit/output/server/entries/pages/group/_groupId_/_page.svelte.js +67 -0
- package/.svelte-kit/output/server/entries/pages/group/_groupId_/_page.ts.js +47 -0
- package/.svelte-kit/output/server/index.js +3747 -0
- package/.svelte-kit/output/server/internal.js +13 -0
- package/.svelte-kit/output/server/manifest-full.js +47 -0
- package/.svelte-kit/output/server/manifest.js +47 -0
- package/.svelte-kit/output/server/nodes/0.js +8 -0
- package/.svelte-kit/output/server/nodes/1.js +8 -0
- package/.svelte-kit/output/server/nodes/2.js +10 -0
- package/.svelte-kit/output/server/nodes/3.js +10 -0
- package/.svelte-kit/output/server/remote-entry.js +557 -0
- package/.svelte-kit/tsconfig.json +61 -0
- package/.svelte-kit/types/route_meta_data.json +8 -0
- package/.svelte-kit/types/src/routes/$types.d.ts +26 -0
- package/.svelte-kit/types/src/routes/group/[groupId]/$types.d.ts +21 -0
- package/.svelte-kit/types/src/routes/group/[groupId]/proxy+page.ts +49 -0
- package/.svelte-kit/types/src/routes/proxy+page.ts +33 -0
- package/build/_app/env.js +1 -0
- package/build/_app/env.js.br +1 -0
- package/build/_app/env.js.gz +0 -0
- package/build/_app/immutable/assets/0.Czt_67iE.css +1 -0
- package/build/_app/immutable/assets/0.Czt_67iE.css.br +0 -0
- package/build/_app/immutable/assets/0.Czt_67iE.css.gz +0 -0
- package/build/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css +1 -0
- package/build/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css.br +0 -0
- package/build/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css.gz +0 -0
- package/build/_app/immutable/assets/helpers.ysDrpaDf.css +1 -0
- package/build/_app/immutable/assets/helpers.ysDrpaDf.css.br +0 -0
- package/build/_app/immutable/assets/helpers.ysDrpaDf.css.gz +0 -0
- package/build/_app/immutable/assets/libavoid.DQJapW5w.wasm +0 -0
- package/build/_app/immutable/assets/libavoid.DQJapW5w.wasm.br +0 -0
- package/build/_app/immutable/assets/libavoid.DQJapW5w.wasm.gz +0 -0
- package/build/_app/immutable/chunks/BlLuv0eP.js +46 -0
- package/build/_app/immutable/chunks/BlLuv0eP.js.br +0 -0
- package/build/_app/immutable/chunks/BlLuv0eP.js.gz +0 -0
- package/build/_app/immutable/chunks/CSBHmwYv.js +1 -0
- package/build/_app/immutable/chunks/CSBHmwYv.js.br +0 -0
- package/build/_app/immutable/chunks/CSBHmwYv.js.gz +0 -0
- package/build/_app/immutable/chunks/CTCi5ueQ.js +1 -0
- package/build/_app/immutable/chunks/CTCi5ueQ.js.br +0 -0
- package/build/_app/immutable/chunks/CTCi5ueQ.js.gz +0 -0
- package/build/_app/immutable/chunks/CfOzjaik.js +2 -0
- package/build/_app/immutable/chunks/CfOzjaik.js.br +0 -0
- package/build/_app/immutable/chunks/CfOzjaik.js.gz +0 -0
- package/build/_app/immutable/chunks/D4PdvFNs.js +1 -0
- package/build/_app/immutable/chunks/D4PdvFNs.js.br +0 -0
- package/build/_app/immutable/chunks/D4PdvFNs.js.gz +0 -0
- package/build/_app/immutable/chunks/DXgP-QUS.js +2 -0
- package/build/_app/immutable/chunks/DXgP-QUS.js.br +0 -0
- package/build/_app/immutable/chunks/DXgP-QUS.js.gz +0 -0
- package/build/_app/immutable/chunks/DlbDC5An.js +1 -0
- package/build/_app/immutable/chunks/DlbDC5An.js.br +0 -0
- package/build/_app/immutable/chunks/DlbDC5An.js.gz +0 -0
- package/build/_app/immutable/chunks/wRWe7aK9.js +1 -0
- package/build/_app/immutable/chunks/wRWe7aK9.js.br +0 -0
- package/build/_app/immutable/chunks/wRWe7aK9.js.gz +0 -0
- package/build/_app/immutable/entry/app.ConrMuHl.js +2 -0
- package/build/_app/immutable/entry/app.ConrMuHl.js.br +0 -0
- package/build/_app/immutable/entry/app.ConrMuHl.js.gz +0 -0
- package/build/_app/immutable/entry/start.Bm6FyGme.js +1 -0
- package/build/_app/immutable/entry/start.Bm6FyGme.js.br +2 -0
- package/build/_app/immutable/entry/start.Bm6FyGme.js.gz +0 -0
- package/build/_app/immutable/nodes/0.d3cL-ETU.js +1 -0
- package/build/_app/immutable/nodes/0.d3cL-ETU.js.br +0 -0
- package/build/_app/immutable/nodes/0.d3cL-ETU.js.gz +0 -0
- package/build/_app/immutable/nodes/1.D6z9rPGv.js +1 -0
- package/build/_app/immutable/nodes/1.D6z9rPGv.js.br +0 -0
- package/build/_app/immutable/nodes/1.D6z9rPGv.js.gz +0 -0
- package/build/_app/immutable/nodes/2.CLD-8chl.js +1 -0
- package/build/_app/immutable/nodes/2.CLD-8chl.js.br +0 -0
- package/build/_app/immutable/nodes/2.CLD-8chl.js.gz +0 -0
- package/build/_app/immutable/nodes/3.DXYeBoel.js +1 -0
- package/build/_app/immutable/nodes/3.DXYeBoel.js.br +0 -0
- package/build/_app/immutable/nodes/3.DXYeBoel.js.gz +0 -0
- package/build/_app/version.json +1 -0
- package/build/_app/version.json.br +0 -0
- package/build/_app/version.json.gz +0 -0
- package/build/index.html +34 -0
- package/build/index.html.br +0 -0
- package/build/index.html.gz +0 -0
- package/build/libavoid.wasm +0 -0
- package/build/libavoid.wasm.br +0 -0
- package/build/libavoid.wasm.gz +0 -0
- package/build/static/robots.txt +3 -0
- package/coverage/coverage-final.json +6 -0
- package/coverage/coverage-summary.json +7 -0
- package/coverage/junit.xml +57 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +131 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/stores/index.html +116 -0
- package/coverage/lcov-report/stores/routingStore.ts.html +781 -0
- package/coverage/lcov-report/utils/flow/helpers.ts.html +127 -0
- package/coverage/lcov-report/utils/flow/index.html +161 -0
- package/coverage/lcov-report/utils/flow/layout.ts.html +805 -0
- package/coverage/lcov-report/utils/flow/serviceIds.ts.html +97 -0
- package/coverage/lcov-report/utils/flow/servicesGraph.ts.html +859 -0
- package/coverage/lcov.info +646 -0
- package/coverage/test-results.json +1 -0
- package/data/services/api/api-gateway.yaml +18 -0
- package/data/services/api/group-info.yaml +7 -0
- package/data/services/api/lambda-orders.yaml +21 -0
- package/data/services/api/lambda-products.yaml +15 -0
- package/data/services/api/lambda-users.yaml +15 -0
- package/data/services/compute/alb.yaml +15 -0
- package/data/services/compute/ecs-inventory.yaml +16 -0
- package/data/services/compute/ecs-notification.yaml +15 -0
- package/data/services/compute/group-info.yaml +7 -0
- package/data/services/data/dynamodb-notifications.yaml +12 -0
- package/data/services/data/dynamodb-orders.yaml +9 -0
- package/data/services/data/dynamodb-products.yaml +9 -0
- package/data/services/data/dynamodb-users.yaml +9 -0
- package/data/services/data/group-info.yaml +7 -0
- package/data/services/data/rds-postgres.yaml +9 -0
- package/data/services/data/redis.yaml +10 -0
- package/data/services/frontend/cloudfront.yaml +12 -0
- package/data/services/frontend/group-info.yaml +7 -0
- package/data/services/frontend/route53.yaml +15 -0
- package/data/services/frontend/s3-website.yaml +9 -0
- package/data/teams/cloud-shepherds.yaml +15 -0
- package/data/teams/data-wizards.yaml +15 -0
- package/data/teams/interface-architects.yaml +19 -0
- package/e2e/demo.test.ts +54 -0
- package/e2e/header-toolbar.simple.spec.ts +0 -0
- package/e2e/header-toolbar.spec.ts +53 -0
- package/e2e/layout.spec.ts +30 -0
- package/package.json +69 -0
- package/playwright.config.ts +10 -0
- package/plugins/mapper-data-plugin.ts +32 -0
- package/project.json +23 -0
- package/src/app.css +125 -0
- package/src/app.d.ts +31 -0
- package/src/app.html +11 -0
- package/src/lib/assets/favicon.svg +19 -0
- package/src/lib/components/EmptyState.svelte +37 -0
- package/src/lib/components/ErrorDisplay.svelte +82 -0
- package/src/lib/components/FlowCanvas.svelte +223 -0
- package/src/lib/components/GenericSidebarCard.svelte +44 -0
- package/src/lib/components/GroupDetailSidebar.svelte +31 -0
- package/src/lib/components/Header.svelte +57 -0
- package/src/lib/components/Legend.svelte +25 -0
- package/src/lib/components/LoadingOverlay.svelte +42 -0
- package/src/lib/components/LoadingSpinner.svelte +10 -0
- package/src/lib/components/ServiceDetailSidebar.svelte +90 -0
- package/src/lib/components/TeamContactCard.svelte +166 -0
- package/src/lib/components/flow/ExternalNode.svelte +45 -0
- package/src/lib/components/flow/MainGroupNode.svelte +24 -0
- package/src/lib/components/flow/ServiceGroupNode.svelte +17 -0
- package/src/lib/components/flow/ServiceNode.svelte +40 -0
- package/src/lib/components/flow/SnakeEdge.svelte +206 -0
- package/src/lib/components/flow/index.ts +6 -0
- package/src/lib/components/index.ts +12 -0
- package/src/lib/data/connections.ts +26 -0
- package/src/lib/data/groups.ts +11 -0
- package/src/lib/data/services.ts +12 -0
- package/src/lib/data/teams.ts +11 -0
- package/src/lib/index.ts +1 -0
- package/src/lib/state/theme.svelte.ts +21 -0
- package/src/lib/stores/diagram.ts +6 -0
- package/src/lib/stores/routingStore.test.ts +133 -0
- package/src/lib/stores/routingStore.ts +232 -0
- package/src/lib/utils/awsIcons.ts +117 -0
- package/src/lib/utils/flow/groupOverviewGraph.ts +73 -0
- package/src/lib/utils/flow/helpers.ts +14 -0
- package/src/lib/utils/flow/layout.test.ts +271 -0
- package/src/lib/utils/flow/layout.ts +240 -0
- package/src/lib/utils/flow/serviceIds.ts +5 -0
- package/src/lib/utils/flow/servicesGraph.test.ts +119 -0
- package/src/lib/utils/flow/servicesGraph.ts +258 -0
- package/src/routes/+error.svelte +36 -0
- package/src/routes/+layout.svelte +27 -0
- package/src/routes/+page.svelte +81 -0
- package/src/routes/+page.ts +31 -0
- package/src/routes/group/[groupId]/+page.svelte +102 -0
- package/src/routes/group/[groupId]/+page.ts +48 -0
- package/src/routes/layout.css +0 -0
- package/static/static/robots.txt +3 -0
- package/svelte.config.js +28 -0
- package/tailwind.config.js +12 -0
- package/tsconfig.json +22 -0
- package/vite.config.ts +81 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import TeamContactCard from '$lib/components/TeamContactCard.svelte'
|
|
3
|
+
import {useNodes, useOnSelectionChange} from "@xyflow/svelte";
|
|
4
|
+
import type {GroupInfo, ServiceDefinition} from "$shared/types";
|
|
5
|
+
import {getAwsIconPath} from "$lib/utils/awsIcons";
|
|
6
|
+
import {GenericSidebarCard} from "$lib/components";
|
|
7
|
+
import {getGroup} from "$lib/data/groups";
|
|
8
|
+
|
|
9
|
+
const nodes = useNodes();
|
|
10
|
+
let selectedNode = $derived(nodes.current.find((node) => node.selected));
|
|
11
|
+
|
|
12
|
+
useOnSelectionChange(({nodes: n, edges}) => {
|
|
13
|
+
selectedNodes = n.map((node) => node.id);
|
|
14
|
+
selectedEdges = edges.map((edge) => edge.id);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const {group, serviceNodeLookup, externalServiceLookup} = $props<{
|
|
18
|
+
group: GroupInfo | null;
|
|
19
|
+
serviceNodeLookup: Record<string, ServiceDefinition>;
|
|
20
|
+
externalServiceLookup: Record<string, { service: ServiceDefinition; group: GroupInfo }>;
|
|
21
|
+
}>();
|
|
22
|
+
|
|
23
|
+
let isExternal = $derived(!!selectedNode && selectedNode.id in externalServiceLookup);
|
|
24
|
+
|
|
25
|
+
let serviceInfo = $derived.by<ServiceDefinition | null>(() => {
|
|
26
|
+
if (!selectedNode) return null;
|
|
27
|
+
|
|
28
|
+
// Use the boolean to pick the source
|
|
29
|
+
return isExternal
|
|
30
|
+
? externalServiceLookup[selectedNode.id]?.service
|
|
31
|
+
: serviceNodeLookup[selectedNode.id];
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
let activeGroup = $state<GroupInfo | null>(group);
|
|
35
|
+
|
|
36
|
+
$effect(() => {
|
|
37
|
+
// If it's external and we have a groupId, go fetch it
|
|
38
|
+
if (isExternal && serviceInfo?.groupId) {
|
|
39
|
+
getGroup(serviceInfo.groupId).then(res => {
|
|
40
|
+
activeGroup = res;
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
// Otherwise, reset back to the group passed in via props
|
|
44
|
+
activeGroup = group;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
let displayGroup = $derived(activeGroup);
|
|
49
|
+
let displayTeamId = $derived(displayGroup?.teamId);
|
|
50
|
+
|
|
51
|
+
let iconPath = $derived(serviceInfo ? getAwsIconPath(serviceInfo.serviceType) : null);
|
|
52
|
+
let initials = $derived(
|
|
53
|
+
serviceInfo?.friendlyName
|
|
54
|
+
? serviceInfo.friendlyName.split(' ').map(n => n[0]).join('').slice(0,3).toUpperCase()
|
|
55
|
+
: 'Svc'
|
|
56
|
+
);
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<aside class="flex h-full w-96 flex-col border-l border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 text-gray-900 dark:text-gray-100">
|
|
60
|
+
<div class="flex-1 overflow-y-auto p-4 space-y-4">
|
|
61
|
+
|
|
62
|
+
{#if !displayGroup}
|
|
63
|
+
<div class="rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-slate-700 p-4 text-sm text-gray-500 dark:text-gray-300">
|
|
64
|
+
Select a group to view details.
|
|
65
|
+
</div>
|
|
66
|
+
{:else}
|
|
67
|
+
<GenericSidebarCard
|
|
68
|
+
title={isExternal ? "Group" : "Current Group"}
|
|
69
|
+
subtitle={displayGroup.name}
|
|
70
|
+
description={displayGroup.description}
|
|
71
|
+
classes={isExternal ? "external" : ""}
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
{#if displayTeamId}
|
|
75
|
+
<TeamContactCard teamId={displayTeamId} classes={isExternal ? "external" : ""} />
|
|
76
|
+
{/if}
|
|
77
|
+
|
|
78
|
+
{#if serviceInfo}
|
|
79
|
+
<GenericSidebarCard
|
|
80
|
+
title="Service"
|
|
81
|
+
subtitle={serviceInfo.friendlyName}
|
|
82
|
+
description={serviceInfo.description}
|
|
83
|
+
iconPath={iconPath ?? undefined}
|
|
84
|
+
iconAlt={initials}
|
|
85
|
+
classes={isExternal ? "external" : ""}
|
|
86
|
+
/>
|
|
87
|
+
{/if}
|
|
88
|
+
{/if}
|
|
89
|
+
</div>
|
|
90
|
+
</aside>
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type {Team} from '$shared/types'
|
|
3
|
+
import Icon from '@iconify/svelte'
|
|
4
|
+
import {getTeam} from "$lib/data/teams";
|
|
5
|
+
import {LoadingSpinner, GenericSidebarCard} from "$lib/components";
|
|
6
|
+
import { slide } from 'svelte/transition';
|
|
7
|
+
import { quintOut } from 'svelte/easing';
|
|
8
|
+
|
|
9
|
+
const {teamId, classes} = $props<{ teamId: string, classes?: string }>();
|
|
10
|
+
|
|
11
|
+
const iconMap: Record<string, string> = {
|
|
12
|
+
email: 'mailto:',
|
|
13
|
+
phone: 'tel:',
|
|
14
|
+
sms: 'sms:',
|
|
15
|
+
web: '',
|
|
16
|
+
slack: '',
|
|
17
|
+
teams: '',
|
|
18
|
+
pigeon: '',
|
|
19
|
+
pagerduty: ''
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const channelIcons: Record<string, string> = {
|
|
23
|
+
email: 'mdi:email-outline',
|
|
24
|
+
phone: 'mdi:phone-outline',
|
|
25
|
+
sms: 'mdi:message-text-outline',
|
|
26
|
+
web: 'mdi:web',
|
|
27
|
+
slack: 'mdi:slack',
|
|
28
|
+
teams: 'mdi:microsoft-teams',
|
|
29
|
+
pigeon: 'mdi:owl',
|
|
30
|
+
pagerduty: 'mdi:bell-ring-outline'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let team = $state<Team | null>(null);
|
|
34
|
+
let isLoading = $state(false);
|
|
35
|
+
|
|
36
|
+
$effect(() => {
|
|
37
|
+
if (!teamId) {
|
|
38
|
+
team = null
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
isLoading = true
|
|
42
|
+
getTeam(teamId)
|
|
43
|
+
.then((data) => {
|
|
44
|
+
team = data
|
|
45
|
+
})
|
|
46
|
+
.catch((error) => {
|
|
47
|
+
console.error('Error loading team data:', error)
|
|
48
|
+
team = null
|
|
49
|
+
})
|
|
50
|
+
.finally(() => {
|
|
51
|
+
isLoading = false
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
const defaultChannelIcon = 'mdi:link-variant'
|
|
57
|
+
|
|
58
|
+
function getChannelIcon(channel: string): string {
|
|
59
|
+
return channelIcons[channel.toLowerCase()] ?? defaultChannelIcon
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function formatChannel(channel: string) {
|
|
63
|
+
return channel.replace(/[-_]/g, ' ').replace(/^\w|\s\w/g, (c) => c.toUpperCase())
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function buildLink(channel: string, detail: string) {
|
|
67
|
+
const prefix = iconMap[channel.toLowerCase()]
|
|
68
|
+
if (prefix === undefined) {
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
if (!prefix) {
|
|
72
|
+
return detail.startsWith('http') ? detail : detail.startsWith('//') ? detail : `https://${detail}`
|
|
73
|
+
}
|
|
74
|
+
return `${prefix}${detail}`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let activeChannel = $state<string | null>(null);
|
|
78
|
+
let copied = $state(false);
|
|
79
|
+
|
|
80
|
+
function toggleChannel(channelId: string) {
|
|
81
|
+
activeChannel = activeChannel === channelId ? null : channelId;
|
|
82
|
+
copied = false; // Reset feedback when switching
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function copyToClipboard(text: string) {
|
|
86
|
+
try {
|
|
87
|
+
await navigator.clipboard.writeText(text);
|
|
88
|
+
copied = true;
|
|
89
|
+
setTimeout(() => (copied = false), 2000);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error('Failed to copy: ', err);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
{#if isLoading}
|
|
97
|
+
<LoadingSpinner message="Loading team information..."/>
|
|
98
|
+
{/if}
|
|
99
|
+
|
|
100
|
+
{#if team}
|
|
101
|
+
<GenericSidebarCard title="Team" subtitle={team.name} description={team.description} classes={classes}>
|
|
102
|
+
<div class="mt-4 w-full">
|
|
103
|
+
{#if team.reachability?.length}
|
|
104
|
+
<div class="flex flex-col overflow-hidden rounded-xl border border-gray-300 bg-white shadow-sm dark:border-slate-700 dark:bg-slate-800/50">
|
|
105
|
+
|
|
106
|
+
<div class="flex items-center divide-x divide-gray-200 dark:divide-slate-700">
|
|
107
|
+
{#each team.reachability as entry}
|
|
108
|
+
{@const isExpanded = activeChannel === entry.channel}
|
|
109
|
+
<button
|
|
110
|
+
onclick={() => toggleChannel(entry.channel)}
|
|
111
|
+
class="flex h-12 flex-1 items-center justify-center transition-all
|
|
112
|
+
{isExpanded ? 'bg-blue-50 text-blue-600 dark:bg-blue-500/20 dark:text-blue-400' : 'text-gray-500 hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-slate-700/50'}"
|
|
113
|
+
>
|
|
114
|
+
<Icon icon={getChannelIcon(entry.channel)} class="h-5 w-5" />
|
|
115
|
+
</button>
|
|
116
|
+
{/each}
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
{#each team.reachability as entry}
|
|
120
|
+
{#if activeChannel === entry.channel}
|
|
121
|
+
<div
|
|
122
|
+
transition:slide={{ duration: 300, easing: quintOut }}
|
|
123
|
+
class="relative border-t border-gray-200 bg-gray-50/50 px-4 py-3 dark:border-slate-700 dark:bg-slate-900/30"
|
|
124
|
+
>
|
|
125
|
+
<button
|
|
126
|
+
onclick={() => copyToClipboard(entry.detail)}
|
|
127
|
+
class="absolute right-2 top-2 rounded-md p-1.5 transition-colors hover:bg-gray-200 dark:hover:bg-slate-700"
|
|
128
|
+
aria-label="Copy to clipboard"
|
|
129
|
+
>
|
|
130
|
+
{#if copied}
|
|
131
|
+
<Icon icon="mdi:check-bold" class="h-4 w-4 text-green-500" />
|
|
132
|
+
{:else}
|
|
133
|
+
<Icon icon="mdi:content-copy" class="h-4 w-4 text-gray-400 hover:text-gray-600 dark:text-slate-500 dark:hover:text-slate-300" />
|
|
134
|
+
{/if}
|
|
135
|
+
</button>
|
|
136
|
+
|
|
137
|
+
<div class="flex flex-col gap-1 pr-8">
|
|
138
|
+
<span class="text-[10px] font-black uppercase tracking-widest text-gray-400 dark:text-slate-500">
|
|
139
|
+
{formatChannel(entry.channel)}
|
|
140
|
+
</span>
|
|
141
|
+
|
|
142
|
+
{#if buildLink(entry.channel, entry.detail)}
|
|
143
|
+
<a
|
|
144
|
+
href={buildLink(entry.channel, entry.detail)}
|
|
145
|
+
target={entry.channel === 'web' ? '_blank' : undefined}
|
|
146
|
+
rel="noreferrer"
|
|
147
|
+
class="text-sm font-semibold text-blue-600 hover:underline dark:text-blue-400 break-all"
|
|
148
|
+
>
|
|
149
|
+
{entry.detail}
|
|
150
|
+
</a>
|
|
151
|
+
{:else}
|
|
152
|
+
<span class="text-sm font-medium text-gray-700 dark:text-gray-200 leading-relaxed">
|
|
153
|
+
{entry.detail}
|
|
154
|
+
</span>
|
|
155
|
+
{/if}
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
{/if}
|
|
159
|
+
{/each}
|
|
160
|
+
</div>
|
|
161
|
+
{:else}
|
|
162
|
+
<p class="text-sm italic text-gray-500 dark:text-gray-400">No contact methods provided.</p>
|
|
163
|
+
{/if}
|
|
164
|
+
</div>
|
|
165
|
+
</GenericSidebarCard>
|
|
166
|
+
{/if}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type {FlowNodeData} from '$shared/flow-types'
|
|
3
|
+
import {Handle, type NodeProps, Position, type Node} from '@xyflow/svelte'
|
|
4
|
+
import {getAwsIconPath} from '$lib/utils/awsIcons'
|
|
5
|
+
import {goto} from '$app/navigation'
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
data,
|
|
9
|
+
}: NodeProps<Node<FlowNodeData>> = $props()
|
|
10
|
+
|
|
11
|
+
let initials = $derived(data.label
|
|
12
|
+
? data.label
|
|
13
|
+
.split(' ')
|
|
14
|
+
.map(part => part[0])
|
|
15
|
+
.join('')
|
|
16
|
+
.slice(0, 3)
|
|
17
|
+
.toUpperCase()
|
|
18
|
+
: 'Svc')
|
|
19
|
+
|
|
20
|
+
//Todo: Figure out if we just want to hard-link the data format to the nodes
|
|
21
|
+
//@ts-ignore
|
|
22
|
+
let iconPath = $derived(getAwsIconPath(data.serviceType))
|
|
23
|
+
|
|
24
|
+
function navigateToExternalGroupPage() {
|
|
25
|
+
goto(`/group/${data.groupId}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<Handle type="target" position={Position.Left} id="input" />
|
|
31
|
+
<Handle type="source" position={Position.Right} id="output"/>
|
|
32
|
+
|
|
33
|
+
<div class="flex items-center gap-3" ondblclick={() => navigateToExternalGroupPage()} role="navigation">
|
|
34
|
+
{#if iconPath}
|
|
35
|
+
<img src={iconPath} alt={data.label} class="h-10 w-10 rounded-lg bg-white/5 object-contain"
|
|
36
|
+
loading="lazy"/>
|
|
37
|
+
{:else}
|
|
38
|
+
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-gray-800 text-xs font-semibold">
|
|
39
|
+
{initials}
|
|
40
|
+
</div>
|
|
41
|
+
{/if}
|
|
42
|
+
<div class="flex-1">
|
|
43
|
+
<div class="text-sm font-semibold">{data.label}</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type {FlowNodeData} from '$shared/flow-types'
|
|
3
|
+
import {Handle, type Node, type NodeProps, Position} from '@xyflow/svelte'
|
|
4
|
+
import {goto} from '$app/navigation'
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
data,
|
|
8
|
+
selected = false,
|
|
9
|
+
}: NodeProps<Node<FlowNodeData>> = $props()
|
|
10
|
+
|
|
11
|
+
//On double click, navigate to the group page.
|
|
12
|
+
const navigateToGroupPage = () => {
|
|
13
|
+
goto(`/group/${data.groupId}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
<Handle type="target" position={Position.Left} id="input" />
|
|
20
|
+
<Handle type="source" position={Position.Right} id="output" />
|
|
21
|
+
<div class="relative" ondblclick={() => navigateToGroupPage()} aria-label="Group node" aria-roledescription="Group node">
|
|
22
|
+
{data.label}
|
|
23
|
+
</div>
|
|
24
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
|
|
3
|
+
import type {FlowNodeData} from '$shared/flow-types'
|
|
4
|
+
import {type Node, type NodeProps} from '@xyflow/svelte'
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
data,
|
|
8
|
+
width,
|
|
9
|
+
height,
|
|
10
|
+
selected = false,
|
|
11
|
+
}: NodeProps<Node<FlowNodeData>> = $props()
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<!--Todo: Add custom styling depending on what kind of group it is -->
|
|
15
|
+
<div class="custom-group-node" style:width="{width}px" style:height="{height}px">
|
|
16
|
+
<div class="big-title">{data.label}</div>
|
|
17
|
+
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type {FlowNodeData} from '$shared/flow-types'
|
|
3
|
+
import {Handle, type NodeProps, Position, type Node} from '@xyflow/svelte'
|
|
4
|
+
import {getAwsIconPath} from '$lib/utils/awsIcons'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
data,
|
|
9
|
+
}: NodeProps<Node<FlowNodeData>> = $props()
|
|
10
|
+
|
|
11
|
+
let initials = $derived(data.label
|
|
12
|
+
? data.label
|
|
13
|
+
.split(' ')
|
|
14
|
+
.map(part => part[0])
|
|
15
|
+
.join('')
|
|
16
|
+
.slice(0, 3)
|
|
17
|
+
.toUpperCase()
|
|
18
|
+
: 'Svc')
|
|
19
|
+
|
|
20
|
+
//Todo: Figure out if we just want to hard-link the data format to the nodes
|
|
21
|
+
//@ts-ignore
|
|
22
|
+
let iconPath = $derived(getAwsIconPath(data.serviceType))
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<Handle type="target" position={Position.Left} id="input" />
|
|
26
|
+
<Handle type="source" position={Position.Right} id="output"/>
|
|
27
|
+
|
|
28
|
+
<div class="flex items-center gap-3">
|
|
29
|
+
{#if iconPath}
|
|
30
|
+
<img src={iconPath} alt={data.label} class="h-10 w-10 rounded-lg bg-white/5 object-contain"
|
|
31
|
+
loading="lazy"/>
|
|
32
|
+
{:else}
|
|
33
|
+
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-gray-800 text-xs font-semibold">
|
|
34
|
+
{initials}
|
|
35
|
+
</div>
|
|
36
|
+
{/if}
|
|
37
|
+
<div class="flex-1">
|
|
38
|
+
<div class="text-sm font-semibold">{data.label}</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {BaseEdge, EdgeLabel, type EdgeProps, Position, useEdges, useNodes} from '@xyflow/svelte';
|
|
3
|
+
import {calculateEdgeOffset, getAbsolutePosition, getNodeDimensions} from '$lib/utils/flow/layout';
|
|
4
|
+
import {page} from '$app/state';
|
|
5
|
+
import {routingStore} from '$lib/stores/routingStore';
|
|
6
|
+
import {onDestroy} from 'svelte';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
id,
|
|
10
|
+
data,
|
|
11
|
+
source,
|
|
12
|
+
target,
|
|
13
|
+
sourceHandleId,
|
|
14
|
+
targetHandleId,
|
|
15
|
+
sourceX,
|
|
16
|
+
sourceY,
|
|
17
|
+
sourcePosition,
|
|
18
|
+
targetX,
|
|
19
|
+
targetY,
|
|
20
|
+
targetPosition,
|
|
21
|
+
label,
|
|
22
|
+
style,
|
|
23
|
+
selected
|
|
24
|
+
}: EdgeProps = $props();
|
|
25
|
+
|
|
26
|
+
const nodes = useNodes();
|
|
27
|
+
const edges = useEdges();
|
|
28
|
+
|
|
29
|
+
let isConnectedNodeSelected = $derived.by(() => {
|
|
30
|
+
const sourceNode = nodes.current.find((n) => n.id === source);
|
|
31
|
+
const targetNode = nodes.current.find((n) => n.id === target);
|
|
32
|
+
return sourceNode?.selected || targetNode?.selected;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
let isEffectivelySelected = $derived(selected || isConnectedNodeSelected);
|
|
36
|
+
|
|
37
|
+
let sourceOffset = $derived(calculateEdgeOffset(id, nodes.current, edges.current, true));
|
|
38
|
+
let targetOffset = $derived(calculateEdgeOffset(id, nodes.current, edges.current, false));
|
|
39
|
+
|
|
40
|
+
// Register nodes and edges with the routing store
|
|
41
|
+
$effect(() => {
|
|
42
|
+
const sourceNode = nodes.current.find(n => n.id === source);
|
|
43
|
+
const targetNode = nodes.current.find(n => n.id === target);
|
|
44
|
+
|
|
45
|
+
if (sourceNode) {
|
|
46
|
+
const absPos = getAbsolutePosition(sourceNode.id, nodes.current);
|
|
47
|
+
const { w, h } = getNodeDimensions(sourceNode);
|
|
48
|
+
routingStore.registerNode({
|
|
49
|
+
nodeId: sourceNode.id,
|
|
50
|
+
absPos,
|
|
51
|
+
width: w,
|
|
52
|
+
height: h,
|
|
53
|
+
isGroup: sourceNode.type === 'serviceGroup'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (targetNode) {
|
|
58
|
+
const absPos = getAbsolutePosition(targetNode.id, nodes.current);
|
|
59
|
+
const { w, h } = getNodeDimensions(targetNode);
|
|
60
|
+
routingStore.registerNode({
|
|
61
|
+
nodeId: targetNode.id,
|
|
62
|
+
absPos,
|
|
63
|
+
width: w,
|
|
64
|
+
height: h,
|
|
65
|
+
isGroup: targetNode.type === 'serviceGroup'
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
$effect(() => {
|
|
71
|
+
// We rely on XYFlow props for positions, but we apply our calculated offsets
|
|
72
|
+
let sX = sourceX;
|
|
73
|
+
let sY = sourceY;
|
|
74
|
+
let tX = targetX;
|
|
75
|
+
let tY = targetY;
|
|
76
|
+
|
|
77
|
+
if (sourcePosition === Position.Left || sourcePosition === Position.Right) {
|
|
78
|
+
sY += sourceOffset;
|
|
79
|
+
} else {
|
|
80
|
+
sX += sourceOffset;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (targetPosition === Position.Left || targetPosition === Position.Right) {
|
|
84
|
+
tY += targetOffset;
|
|
85
|
+
} else {
|
|
86
|
+
tX += targetOffset;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
routingStore.registerEdge({
|
|
90
|
+
edgeId: id,
|
|
91
|
+
sourceNodeId: source,
|
|
92
|
+
targetNodeId: target,
|
|
93
|
+
sourcePos: { x: sX, y: sY },
|
|
94
|
+
sourceSide: sourcePosition ?? 'right',
|
|
95
|
+
targetPos: { x: tX, y: tY },
|
|
96
|
+
targetSide: targetPosition ?? 'left'
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
onDestroy(() => {
|
|
101
|
+
routingStore.unregisterEdge(id);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// 1. Derive the path and label position
|
|
105
|
+
let pts = $derived($routingStore[id] || []);
|
|
106
|
+
|
|
107
|
+
let edgePathData = $derived.by(() => {
|
|
108
|
+
if (!pts || pts.length === 0) {
|
|
109
|
+
// Fallback if not routed yet
|
|
110
|
+
return { path: `M ${sourceX} ${sourceY} L ${targetX} ${targetY}`, labelX: (sourceX + targetX) / 2, labelY: (sourceY + targetY) / 2 };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Generate rounded SVG path from points
|
|
114
|
+
let d = `M ${pts[0].x} ${pts[0].y}`;
|
|
115
|
+
const radius = 8;
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
118
|
+
const p = pts[i];
|
|
119
|
+
const next = pts[i + 1];
|
|
120
|
+
const prev = i > 0 ? pts[i - 1] : null;
|
|
121
|
+
|
|
122
|
+
if (prev && next) {
|
|
123
|
+
// Round this corner
|
|
124
|
+
const dPrev = { x: p.x - prev.x, y: p.y - prev.y };
|
|
125
|
+
const dNext = { x: next.x - p.x, y: next.y - p.y };
|
|
126
|
+
const lenPrev = Math.hypot(dPrev.x, dPrev.y);
|
|
127
|
+
const lenNext = Math.hypot(dNext.x, dNext.y);
|
|
128
|
+
const actualRadius = Math.min(radius, lenPrev / 2, lenNext / 2);
|
|
129
|
+
|
|
130
|
+
const startPoint = {
|
|
131
|
+
x: p.x - (dPrev.x / lenPrev) * actualRadius,
|
|
132
|
+
y: p.y - (dPrev.y / lenPrev) * actualRadius
|
|
133
|
+
};
|
|
134
|
+
const endPoint = {
|
|
135
|
+
x: p.x + (dNext.x / lenNext) * actualRadius,
|
|
136
|
+
y: p.y + (dNext.y / lenNext) * actualRadius
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
d += ` L ${startPoint.x} ${startPoint.y} Q ${p.x} ${p.y} ${endPoint.x} ${endPoint.y}`;
|
|
140
|
+
} else if (i > 0) {
|
|
141
|
+
d += ` L ${p.x} ${p.y}`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
d += ` L ${pts[pts.length - 1].x} ${pts[pts.length - 1].y}`;
|
|
145
|
+
|
|
146
|
+
// Label position: midpoint of the longest segment
|
|
147
|
+
let longestLen = -1;
|
|
148
|
+
let labelPos = { x: (sourceX + targetX) / 2, y: (sourceY + targetY) / 2 };
|
|
149
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
150
|
+
const dx = pts[i + 1].x - pts[i].x;
|
|
151
|
+
const dy = pts[i + 1].y - pts[i].y;
|
|
152
|
+
const len = Math.hypot(dx, dy);
|
|
153
|
+
if (len > longestLen) {
|
|
154
|
+
longestLen = len;
|
|
155
|
+
labelPos = { x: pts[i].x + dx / 2, y: pts[i].y + dy / 2 };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { path: d, labelX: labelPos.x, labelY: labelPos.y };
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
let path = $derived(edgePathData.path);
|
|
162
|
+
let labelPos = $derived({ x: edgePathData.labelX, y: edgePathData.labelY });
|
|
163
|
+
|
|
164
|
+
let incomingOrOutgoingOrInternal = $derived.by(() => {
|
|
165
|
+
const urlGroupId = page.params.groupId;
|
|
166
|
+
|
|
167
|
+
if (!urlGroupId) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Determine if the edge is incoming, outgoing, or internal based on the handle ids, and if the group is the same.
|
|
172
|
+
//Assume that a node id is in the format of "svc::<group>::<service>".
|
|
173
|
+
const sourceGroup = source.split('::')[1];
|
|
174
|
+
const targetGroup = target.split('::')[1];
|
|
175
|
+
|
|
176
|
+
if (sourceGroup === targetGroup) {
|
|
177
|
+
return 'internal';
|
|
178
|
+
} else if (sourceHandleId === 'output' && sourceGroup === urlGroupId) {
|
|
179
|
+
return 'outgoing';
|
|
180
|
+
} else if (targetHandleId === 'input' && targetGroup === urlGroupId) {
|
|
181
|
+
return 'incoming';
|
|
182
|
+
} else {
|
|
183
|
+
return 'internal';
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
</script>
|
|
188
|
+
|
|
189
|
+
<BaseEdge
|
|
190
|
+
class={`${isEffectivelySelected ? 'snake-edge-path selected' : 'snake-edge-path'} ${incomingOrOutgoingOrInternal ?? ''}`}
|
|
191
|
+
{id}
|
|
192
|
+
{path}
|
|
193
|
+
{style}
|
|
194
|
+
/>
|
|
195
|
+
|
|
196
|
+
{#if label}
|
|
197
|
+
<EdgeLabel>
|
|
198
|
+
<div
|
|
199
|
+
style:transform="translate(-50%, -50%) translate({labelPos.x}px, {labelPos.y}px)"
|
|
200
|
+
class="edge-label"
|
|
201
|
+
class:selected={isEffectivelySelected}
|
|
202
|
+
>
|
|
203
|
+
{label}
|
|
204
|
+
</div>
|
|
205
|
+
</EdgeLabel>
|
|
206
|
+
{/if}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//Export all
|
|
2
|
+
export {default as ExternalNode} from './ExternalNode.svelte';
|
|
3
|
+
export {default as MainGroupNode} from './MainGroupNode.svelte';
|
|
4
|
+
export {default as ServiceNode} from './ServiceNode.svelte';
|
|
5
|
+
export {default as ServiceGroupNode} from './ServiceGroupNode.svelte';
|
|
6
|
+
export {default as SnakeEdge} from './SnakeEdge.svelte';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './flow';
|
|
2
|
+
export {default as EmptyState} from './EmptyState.svelte';
|
|
3
|
+
export {default as ErrorDisplay} from './ErrorDisplay.svelte';
|
|
4
|
+
export {default as FlowCanvas} from './FlowCanvas.svelte';
|
|
5
|
+
export {default as GenericSidebarCard} from './GenericSidebarCard.svelte';
|
|
6
|
+
export {default as GroupDetailSidebar} from './GroupDetailSidebar.svelte';
|
|
7
|
+
export {default as Header} from './Header.svelte';
|
|
8
|
+
export {default as Legend} from './Legend.svelte';
|
|
9
|
+
export {default as LoadingOverlay} from './LoadingOverlay.svelte';
|
|
10
|
+
export {default as LoadingSpinner} from './LoadingSpinner.svelte';
|
|
11
|
+
export {default as ServiceDetailSidebar} from './ServiceDetailSidebar.svelte';
|
|
12
|
+
export {default as TeamContactCard} from './TeamContactCard.svelte';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { GroupConnection } from '$shared/types'
|
|
2
|
+
import bakedData from 'virtual:mapper-data'
|
|
3
|
+
|
|
4
|
+
export async function getConnectionsFromGroup(groupId: string | null | undefined): Promise<GroupConnection[]> {
|
|
5
|
+
if (!groupId) return []
|
|
6
|
+
// Not pre-baked, so filter from all groupConnections
|
|
7
|
+
return (bakedData.groupConnections as GroupConnection[]).filter(c => c.sourceGroup === groupId)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function getConnectionsToGroup(groupId: string | null | undefined): Promise<GroupConnection[]> {
|
|
11
|
+
if (!groupId) return []
|
|
12
|
+
// Not pre-baked, so filter from all groupConnections
|
|
13
|
+
return (bakedData.groupConnections as GroupConnection[]).filter(c => c.targetGroup === groupId)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function getAllGroupConnections(): Promise<GroupConnection[]> {
|
|
17
|
+
// Returns all group connections except self connections
|
|
18
|
+
return (bakedData.groupConnections as GroupConnection[]).filter(
|
|
19
|
+
c => c.sourceGroup !== c.targetGroup
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function getAllGroupConnectionsWithSelf(): Promise<GroupConnection[]> {
|
|
24
|
+
// Returns all group connections including self connections
|
|
25
|
+
return bakedData.groupConnections as GroupConnection[]
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { GroupInfo } from '$shared/types'
|
|
2
|
+
import bakedData from 'virtual:mapper-data'
|
|
3
|
+
|
|
4
|
+
export async function getAllGroups(): Promise<Record<string, GroupInfo>> {
|
|
5
|
+
return bakedData.groups as Record<string, GroupInfo>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function getGroup(groupId: string | null | undefined): Promise<GroupInfo | null> {
|
|
9
|
+
if (!groupId) return null;
|
|
10
|
+
return ((bakedData.groups as Record<string, GroupInfo>)[groupId]) ?? null;
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ExternalGroupServices, ServiceDefinition } from '$shared/types'
|
|
2
|
+
import bakedData from 'virtual:mapper-data'
|
|
3
|
+
|
|
4
|
+
export async function getServicesByGroup(groupId: string | null | undefined): Promise<ServiceDefinition[]> {
|
|
5
|
+
if (!groupId) return []
|
|
6
|
+
return (bakedData.servicesByGroup[groupId] as ServiceDefinition[]) || []
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function getExternalServicesForGroup(groupId: string | null | undefined): Promise<ExternalGroupServices[]> {
|
|
10
|
+
if (!groupId) return []
|
|
11
|
+
return (bakedData.externalServices[groupId] as ExternalGroupServices[]) || []
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Team } from '$shared/types'
|
|
2
|
+
import bakedData from 'virtual:mapper-data'
|
|
3
|
+
|
|
4
|
+
export async function getAllTeams(): Promise<Record<string, Team>> {
|
|
5
|
+
return bakedData.teams as Record<string, Team>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function getTeam(teamId: string | null | undefined): Promise<Team | null> {
|
|
9
|
+
if (!teamId) return null;
|
|
10
|
+
return (bakedData.teams[teamId] as Team) ?? null;
|
|
11
|
+
}
|