@elevasis/ui 2.25.5 → 2.26.0
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/dist/api/index.js +2 -2
- package/dist/app/index.css +18 -29
- package/dist/app/index.d.ts +118 -64
- package/dist/app/index.js +6 -5
- package/dist/charts/index.js +6 -5
- package/dist/chunk-3MEXPLWT.js +265 -0
- package/dist/{chunk-IS53MXE4.js → chunk-4KTLOK7K.js} +1 -1
- package/dist/{chunk-KMAXFJPH.js → chunk-CW3UNAF2.js} +5 -409
- package/dist/{chunk-HKBEURCV.js → chunk-G26INIF3.js} +1 -1
- package/dist/{chunk-NHHCUECV.js → chunk-G66QFZXD.js} +11 -214
- package/dist/{chunk-QIW6OCEI.js → chunk-HLFFKKT3.js} +27 -373
- package/dist/chunk-JDNEWB5F.js +10 -0
- package/dist/{chunk-VMAWXEVG.js → chunk-JKBGDFX2.js} +1136 -828
- package/dist/{chunk-MU4VPAMR.js → chunk-JPGX3533.js} +4 -3
- package/dist/chunk-KCGGA36K.js +73 -0
- package/dist/chunk-KEFWANZY.js +155 -0
- package/dist/chunk-LH4GPYDX.js +448 -0
- package/dist/{chunk-QNCVK3ZF.js → chunk-LWKZ3BCC.js} +5 -4
- package/dist/chunk-OGXKOMUT.js +412 -0
- package/dist/chunk-OHXU5WWK.js +3731 -0
- package/dist/chunk-ONFKASZI.js +2004 -0
- package/dist/{chunk-U36X6NZM.js → chunk-RIFTUOPE.js} +2 -14
- package/dist/{chunk-T6INEVX6.js → chunk-SGS4CQ2B.js} +1 -1
- package/dist/{chunk-KINQW4JT.js → chunk-UPMX5GJI.js} +5 -5
- package/dist/{chunk-N2AP4I5N.js → chunk-UY5I2KOZ.js} +223 -3857
- package/dist/{chunk-JMI7L7Y7.js → chunk-W2ZTLH7Y.js} +142 -4
- package/dist/{chunk-3KY2GNPE.js → chunk-WUVR4QY6.js} +9 -9
- package/dist/{chunk-Q5BEODAT.js → chunk-X2SUMO3P.js} +2 -1
- package/dist/{chunk-SNHGSCKH.js → chunk-XBMCDGHA.js} +1 -1
- package/dist/{chunk-N55DVMAG.js → chunk-XQQEKWTL.js} +2 -6
- package/dist/{chunk-F7JDHS2I.js → chunk-XZSEPJZQ.js} +5 -5
- package/dist/{chunk-5BJXMZN4.js → chunk-YHBPR67D.js} +438 -620
- package/dist/{chunk-FVKLHLF4.js → chunk-YO2YORW4.js} +4 -4
- package/dist/{chunk-TAIX4NO3.js → chunk-ZFLM2YVW.js} +2 -2
- package/dist/components/index.css +18 -29
- package/dist/components/index.d.ts +211 -383
- package/dist/components/index.js +43 -427
- package/dist/components/navigation/index.css +28 -39
- package/dist/execution/index.d.ts +0 -73
- package/dist/features/auth/index.css +28 -39
- package/dist/features/crm/index.css +28 -39
- package/dist/features/crm/index.d.ts +49 -49
- package/dist/features/crm/index.js +14 -14
- package/dist/features/dashboard/index.css +28 -39
- package/dist/features/dashboard/index.js +18 -15
- package/dist/features/delivery/index.css +18 -29
- package/dist/features/delivery/index.js +14 -14
- package/dist/features/knowledge/index.css +611 -0
- package/dist/features/knowledge/index.js +700 -1
- package/dist/features/lead-gen/index.css +28 -39
- package/dist/features/lead-gen/index.d.ts +212 -166
- package/dist/features/lead-gen/index.js +16 -15
- package/dist/features/monitoring/index.css +18 -29
- package/dist/features/monitoring/index.js +17 -16
- package/dist/features/monitoring/requests/index.css +28 -39
- package/dist/features/monitoring/requests/index.js +13 -13
- package/dist/features/operations/index.css +28 -39
- package/dist/features/operations/index.d.ts +16 -98
- package/dist/features/operations/index.js +26 -20
- package/dist/features/settings/index.css +28 -39
- package/dist/features/settings/index.d.ts +1 -0
- package/dist/features/settings/index.js +15 -15
- package/dist/hooks/delivery/index.css +28 -39
- package/dist/hooks/delivery/index.js +2 -2
- package/dist/hooks/index.css +18 -29
- package/dist/hooks/index.d.ts +180 -377
- package/dist/hooks/index.js +13 -13
- package/dist/hooks/published.css +18 -29
- package/dist/hooks/published.d.ts +180 -377
- package/dist/hooks/published.js +13 -13
- package/dist/index.css +18 -29
- package/dist/index.d.ts +1549 -946
- package/dist/index.js +15 -14
- package/dist/initialization/index.d.ts +1 -0
- package/dist/knowledge/index.d.ts +991 -41
- package/dist/knowledge/index.js +5469 -413
- package/dist/layout/index.d.ts +2 -0
- package/dist/layout/index.js +3 -2
- package/dist/organization/index.css +28 -39
- package/dist/organization/index.d.ts +1 -0
- package/dist/provider/index.css +28 -39
- package/dist/provider/index.d.ts +1147 -348
- package/dist/provider/index.js +11 -10
- package/dist/provider/published.css +28 -39
- package/dist/provider/published.d.ts +1146 -347
- package/dist/provider/published.js +8 -8
- package/dist/test-utils/index.js +2 -2
- package/dist/test-utils/setup.js +1 -1
- package/dist/theme/index.js +3 -2
- package/dist/theme/presets/index.d.ts +97 -0
- package/dist/theme/presets/index.js +3 -0
- package/dist/types/index.d.ts +71 -126
- package/dist/utils/index.js +1 -1
- package/dist/vite/index.d.ts +7 -0
- package/dist/vite/index.js +10 -0
- package/dist/vite-plugin-knowledge/index.d.ts +1 -33
- package/dist/vite-plugin-knowledge/index.js +1 -66
- package/package.json +46 -33
- package/src/knowledge/README.md +8 -8
- package/src/theme/presets/README.md +19 -0
- /package/dist/{chunk-SGXXJE52.js → chunk-QD4X4H5A.js} +0 -0
|
@@ -0,0 +1,3731 @@
|
|
|
1
|
+
import { SemanticIcon } from './chunk-KEFWANZY.js';
|
|
2
|
+
import { SubshellSidebarLoader } from './chunk-XQQEKWTL.js';
|
|
3
|
+
import { OrganizationModelProspectingSchema } from './chunk-4KTLOK7K.js';
|
|
4
|
+
import { useCyberColors, CyberDonut } from './chunk-CW3UNAF2.js';
|
|
5
|
+
import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
|
|
6
|
+
import { collectResourceFilterFacets, useCommandViewStore, useCommandViewData, useCommandViewStats, usePaginationState, useResourceExecutions, useCheckpointTasks } from './chunk-YHBPR67D.js';
|
|
7
|
+
import { EmptyState, APIErrorAlert } from './chunk-RIFTUOPE.js';
|
|
8
|
+
import { LabelSchema, ColorTokenSchema, IconNameSchema, PathSchema, DescriptionSchema, DisplayMetadataSchema, ModelIdSchema, ReferenceIdsSchema, OrganizationModelSalesSchema } from './chunk-W2ZTLH7Y.js';
|
|
9
|
+
import { useElevasisFeatures } from './chunk-V3HUIZJX.js';
|
|
10
|
+
import { getNodeId } from './chunk-QD4X4H5A.js';
|
|
11
|
+
import { useRef, useEffect, useMemo } from 'react';
|
|
12
|
+
import { Paper, Stack, Group, Text, Button, Badge, SimpleGrid, Divider, Select, NumberInput, MultiSelect, Switch, Card, Checkbox, UnstyledButton, useMantineTheme, Box, Title, Space, Center, Loader, Pagination, TextInput, SegmentedControl } from '@mantine/core';
|
|
13
|
+
import { IconShare2, IconTopologyStar3, IconRefresh, IconCircleX, IconCircleCheck, IconCircleDashed, IconSitemap, IconExternalLink, IconSearch } from '@tabler/icons-react';
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
16
|
+
import { create } from 'zustand';
|
|
17
|
+
import { formatDistanceToNow } from 'date-fns';
|
|
18
|
+
|
|
19
|
+
var NodeIdPathSchema = z.string().trim().min(1).max(100).regex(/^([a-z0-9-]+)(\.[a-z0-9-]+)*$/, "Node IDs must be lowercase dotted paths");
|
|
20
|
+
var NodeIdStringSchema = z.string().trim().min(1).max(200).regex(/^[a-z]+:([a-z0-9-]+)(\.[a-z0-9-]+)*$/, "Node references must use kind:dotted-path");
|
|
21
|
+
var UiPositionSchema = z.enum(["sidebar-primary", "sidebar-bottom"]);
|
|
22
|
+
var FeatureSchema = z.object({
|
|
23
|
+
id: NodeIdPathSchema,
|
|
24
|
+
label: LabelSchema,
|
|
25
|
+
description: DescriptionSchema.optional(),
|
|
26
|
+
enabled: z.boolean().default(true),
|
|
27
|
+
path: PathSchema.optional(),
|
|
28
|
+
icon: IconNameSchema.optional(),
|
|
29
|
+
color: ColorTokenSchema.optional(),
|
|
30
|
+
uiPosition: UiPositionSchema.optional(),
|
|
31
|
+
requiresAdmin: z.boolean().optional(),
|
|
32
|
+
devOnly: z.boolean().optional()
|
|
33
|
+
});
|
|
34
|
+
var OrganizationModelBrandingSchema = z.object({
|
|
35
|
+
organizationName: LabelSchema,
|
|
36
|
+
productName: LabelSchema,
|
|
37
|
+
shortName: z.string().trim().min(1).max(40),
|
|
38
|
+
description: DescriptionSchema.optional(),
|
|
39
|
+
logos: z.object({
|
|
40
|
+
light: z.string().trim().min(1).max(2048).optional(),
|
|
41
|
+
dark: z.string().trim().min(1).max(2048).optional()
|
|
42
|
+
}).default({})
|
|
43
|
+
});
|
|
44
|
+
var DEFAULT_ORGANIZATION_MODEL_BRANDING = {
|
|
45
|
+
organizationName: "Default Organization",
|
|
46
|
+
productName: "Elevasis",
|
|
47
|
+
shortName: "Elevasis",
|
|
48
|
+
logos: {}
|
|
49
|
+
};
|
|
50
|
+
var ProjectsDomainStateSchema = DisplayMetadataSchema.extend({
|
|
51
|
+
id: ModelIdSchema,
|
|
52
|
+
order: z.number().int().min(0)
|
|
53
|
+
});
|
|
54
|
+
var OrganizationModelProjectsSchema = z.object({
|
|
55
|
+
projectEntityId: ModelIdSchema,
|
|
56
|
+
milestoneEntityId: ModelIdSchema,
|
|
57
|
+
taskEntityId: ModelIdSchema,
|
|
58
|
+
projectStatuses: z.array(ProjectsDomainStateSchema).min(1),
|
|
59
|
+
milestoneStatuses: z.array(ProjectsDomainStateSchema).min(1),
|
|
60
|
+
taskStatuses: z.array(ProjectsDomainStateSchema).min(1)
|
|
61
|
+
});
|
|
62
|
+
var DEFAULT_ORGANIZATION_MODEL_PROJECTS = {
|
|
63
|
+
projectEntityId: "delivery.project",
|
|
64
|
+
milestoneEntityId: "delivery.milestone",
|
|
65
|
+
taskEntityId: "delivery.task",
|
|
66
|
+
projectStatuses: [
|
|
67
|
+
{ id: "active", label: "Active", order: 1 },
|
|
68
|
+
{ id: "on_track", label: "On Track", order: 2 },
|
|
69
|
+
{ id: "at_risk", label: "At Risk", order: 3 },
|
|
70
|
+
{ id: "blocked", label: "Blocked", order: 4 },
|
|
71
|
+
{ id: "paused", label: "Paused", order: 5 },
|
|
72
|
+
{ id: "completed", label: "Completed", order: 6 }
|
|
73
|
+
],
|
|
74
|
+
milestoneStatuses: [
|
|
75
|
+
{ id: "upcoming", label: "Upcoming", order: 1 },
|
|
76
|
+
{ id: "in_progress", label: "In Progress", order: 2 },
|
|
77
|
+
{ id: "blocked", label: "Blocked", order: 3 },
|
|
78
|
+
{ id: "overdue", label: "Overdue", order: 4 },
|
|
79
|
+
{ id: "completed", label: "Completed", order: 5 }
|
|
80
|
+
],
|
|
81
|
+
taskStatuses: [
|
|
82
|
+
{ id: "planned", label: "Planned", order: 1 },
|
|
83
|
+
{ id: "in_progress", label: "In Progress", order: 2 },
|
|
84
|
+
{ id: "blocked", label: "Blocked", order: 3 },
|
|
85
|
+
{ id: "submitted", label: "Submitted", order: 4 },
|
|
86
|
+
{ id: "approved", label: "Approved", order: 5 },
|
|
87
|
+
{ id: "revision_requested", label: "Revision Requested", order: 6 },
|
|
88
|
+
{ id: "rejected", label: "Rejected", order: 7 },
|
|
89
|
+
{ id: "cancelled", label: "Cancelled", order: 8 },
|
|
90
|
+
{ id: "completed", label: "Completed", order: 9 }
|
|
91
|
+
]
|
|
92
|
+
};
|
|
93
|
+
var SurfaceTypeSchema = z.enum(["page", "dashboard", "graph", "detail", "list", "settings"]);
|
|
94
|
+
var SurfaceDefinitionSchema = z.object({
|
|
95
|
+
id: ModelIdSchema,
|
|
96
|
+
label: LabelSchema,
|
|
97
|
+
path: PathSchema,
|
|
98
|
+
surfaceType: SurfaceTypeSchema,
|
|
99
|
+
description: DescriptionSchema.optional(),
|
|
100
|
+
enabled: z.boolean().default(true),
|
|
101
|
+
devOnly: z.boolean().optional(),
|
|
102
|
+
icon: IconNameSchema.optional(),
|
|
103
|
+
featureId: ModelIdSchema.optional(),
|
|
104
|
+
featureIds: ReferenceIdsSchema,
|
|
105
|
+
entityIds: ReferenceIdsSchema,
|
|
106
|
+
resourceIds: ReferenceIdsSchema,
|
|
107
|
+
capabilityIds: ReferenceIdsSchema,
|
|
108
|
+
parentId: ModelIdSchema.optional()
|
|
109
|
+
});
|
|
110
|
+
var NavigationGroupSchema = z.object({
|
|
111
|
+
id: ModelIdSchema,
|
|
112
|
+
label: LabelSchema,
|
|
113
|
+
placement: z.string().trim().min(1).max(50),
|
|
114
|
+
surfaceIds: z.array(ModelIdSchema).default([])
|
|
115
|
+
});
|
|
116
|
+
var OrganizationModelNavigationSchema = z.object({
|
|
117
|
+
defaultSurfaceId: ModelIdSchema.optional(),
|
|
118
|
+
surfaces: z.array(SurfaceDefinitionSchema).default([]),
|
|
119
|
+
groups: z.array(NavigationGroupSchema).default([])
|
|
120
|
+
});
|
|
121
|
+
var BusinessHoursDaySchema = z.object({
|
|
122
|
+
open: z.string().trim().regex(/^\d{2}:\d{2}$/, "Expected HH:MM format"),
|
|
123
|
+
close: z.string().trim().regex(/^\d{2}:\d{2}$/, "Expected HH:MM format")
|
|
124
|
+
});
|
|
125
|
+
var BusinessHoursSchema = z.object({
|
|
126
|
+
monday: BusinessHoursDaySchema.optional(),
|
|
127
|
+
tuesday: BusinessHoursDaySchema.optional(),
|
|
128
|
+
wednesday: BusinessHoursDaySchema.optional(),
|
|
129
|
+
thursday: BusinessHoursDaySchema.optional(),
|
|
130
|
+
friday: BusinessHoursDaySchema.optional(),
|
|
131
|
+
saturday: BusinessHoursDaySchema.optional(),
|
|
132
|
+
sunday: BusinessHoursDaySchema.optional()
|
|
133
|
+
}).default({});
|
|
134
|
+
var IdentityDomainSchema = z.object({
|
|
135
|
+
/** Why the organization exists — one or two plain-language sentences. */
|
|
136
|
+
mission: z.string().trim().max(1e3).default(""),
|
|
137
|
+
/** Long-term direction the organization is moving toward. */
|
|
138
|
+
vision: z.string().trim().max(1e3).default(""),
|
|
139
|
+
/** Legal registered name of the entity. */
|
|
140
|
+
legalName: z.string().trim().max(200).default(""),
|
|
141
|
+
/**
|
|
142
|
+
* Type of legal entity (e.g. "LLC", "Corporation", "Sole Proprietor",
|
|
143
|
+
* "Non-profit"). Free-form string so it covers any jurisdiction.
|
|
144
|
+
*/
|
|
145
|
+
entityType: z.string().trim().max(100).default(""),
|
|
146
|
+
/**
|
|
147
|
+
* Primary jurisdiction of registration or operation
|
|
148
|
+
* (e.g. "United States – Delaware", "Canada – Ontario").
|
|
149
|
+
*/
|
|
150
|
+
jurisdiction: z.string().trim().max(200).default(""),
|
|
151
|
+
/**
|
|
152
|
+
* Industry category — broad classification (e.g. "Marketing Agency",
|
|
153
|
+
* "Software / SaaS", "Professional Services").
|
|
154
|
+
*/
|
|
155
|
+
industryCategory: z.string().trim().max(200).default(""),
|
|
156
|
+
/**
|
|
157
|
+
* Geographic focus — where the organization primarily operates or serves
|
|
158
|
+
* (e.g. "North America", "Global", "Southeast Asia").
|
|
159
|
+
*/
|
|
160
|
+
geographicFocus: z.string().trim().max(200).default(""),
|
|
161
|
+
/**
|
|
162
|
+
* IANA timezone identifier for the organization's primary operating timezone
|
|
163
|
+
* (e.g. "America/Los_Angeles", "Europe/London", "UTC").
|
|
164
|
+
*/
|
|
165
|
+
timeZone: z.string().trim().max(100).default("UTC"),
|
|
166
|
+
/** Typical operating hours per day of week. Empty object means not configured. */
|
|
167
|
+
businessHours: BusinessHoursSchema,
|
|
168
|
+
/**
|
|
169
|
+
* Long-form markdown capturing client context, problem narrative, and domain
|
|
170
|
+
* background. Populated by /setup; surfaced to agents as organizational context.
|
|
171
|
+
* Optional — many projects have no external client.
|
|
172
|
+
*/
|
|
173
|
+
clientBrief: z.string().trim().default("")
|
|
174
|
+
});
|
|
175
|
+
var DEFAULT_ORGANIZATION_MODEL_IDENTITY = {
|
|
176
|
+
mission: "",
|
|
177
|
+
vision: "",
|
|
178
|
+
legalName: "",
|
|
179
|
+
entityType: "",
|
|
180
|
+
jurisdiction: "",
|
|
181
|
+
industryCategory: "",
|
|
182
|
+
geographicFocus: "",
|
|
183
|
+
timeZone: "UTC",
|
|
184
|
+
businessHours: {},
|
|
185
|
+
clientBrief: ""
|
|
186
|
+
};
|
|
187
|
+
var FirmographicsSchema = z.object({
|
|
188
|
+
/** Industry vertical (e.g. "Marketing Agency", "Legal", "Real Estate"). */
|
|
189
|
+
industry: z.string().trim().max(200).optional(),
|
|
190
|
+
/**
|
|
191
|
+
* Company headcount band (e.g. "1–10", "11–50", "51–200", "200+").
|
|
192
|
+
* Free-form string to accommodate any band notation.
|
|
193
|
+
*/
|
|
194
|
+
companySize: z.string().trim().max(100).optional(),
|
|
195
|
+
/**
|
|
196
|
+
* Primary geographic region the segment operates in or is targeted from
|
|
197
|
+
* (e.g. "North America", "Europe", "Global").
|
|
198
|
+
*/
|
|
199
|
+
region: z.string().trim().max(200).optional()
|
|
200
|
+
});
|
|
201
|
+
var CustomerSegmentSchema = z.object({
|
|
202
|
+
/** Stable unique identifier for the segment (e.g. "segment-smb-agencies"). */
|
|
203
|
+
id: z.string().trim().min(1).max(100),
|
|
204
|
+
/** Human-readable name shown to agents and in UI (e.g. "SMB Marketing Agencies"). */
|
|
205
|
+
name: z.string().trim().max(200).default(""),
|
|
206
|
+
/** One or two sentences describing who this segment is. */
|
|
207
|
+
description: z.string().trim().max(2e3).default(""),
|
|
208
|
+
/**
|
|
209
|
+
* The primary job(s) this segment is trying to get done — the goal they hire
|
|
210
|
+
* a product/service to accomplish. Plain-language narrative or bullet list.
|
|
211
|
+
*/
|
|
212
|
+
jobsToBeDone: z.string().trim().max(2e3).default(""),
|
|
213
|
+
/**
|
|
214
|
+
* Pains — frustrations, obstacles, and risks the segment experiences
|
|
215
|
+
* when trying to accomplish their jobs-to-be-done.
|
|
216
|
+
*/
|
|
217
|
+
pains: z.array(z.string().trim().max(500)).default([]),
|
|
218
|
+
/**
|
|
219
|
+
* Gains — outcomes and benefits the segment desires; positive motivators
|
|
220
|
+
* beyond merely resolving pains.
|
|
221
|
+
*/
|
|
222
|
+
gains: z.array(z.string().trim().max(500)).default([]),
|
|
223
|
+
/** Firmographic profile for targeting and filtering. */
|
|
224
|
+
firmographics: FirmographicsSchema.default({}),
|
|
225
|
+
/**
|
|
226
|
+
* Value proposition — one or two sentences stating why this organization's
|
|
227
|
+
* offering is uniquely suited for this segment's needs.
|
|
228
|
+
*/
|
|
229
|
+
valueProp: z.string().trim().max(2e3).default("")
|
|
230
|
+
});
|
|
231
|
+
var CustomersDomainSchema = z.object({
|
|
232
|
+
segments: z.array(CustomerSegmentSchema).default([])
|
|
233
|
+
});
|
|
234
|
+
var DEFAULT_ORGANIZATION_MODEL_CUSTOMERS = {
|
|
235
|
+
segments: []
|
|
236
|
+
};
|
|
237
|
+
var PricingModelSchema = z.enum(["one-time", "subscription", "usage-based", "custom"]);
|
|
238
|
+
var ProductSchema = z.object({
|
|
239
|
+
/** Stable unique identifier for the product (e.g. "product-starter-plan"). */
|
|
240
|
+
id: z.string().trim().min(1).max(100),
|
|
241
|
+
/** Human-readable name shown to agents and in UI (e.g. "Starter Plan"). */
|
|
242
|
+
name: z.string().trim().max(200).default(""),
|
|
243
|
+
/** One or two sentences describing what this product/service delivers. */
|
|
244
|
+
description: z.string().trim().max(2e3).default(""),
|
|
245
|
+
/**
|
|
246
|
+
* How this product is priced:
|
|
247
|
+
* - "one-time" — single purchase (setup fee, project fee)
|
|
248
|
+
* - "subscription" — recurring (monthly/annual SaaS, retainer)
|
|
249
|
+
* - "usage-based" — metered by consumption (API calls, seats)
|
|
250
|
+
* - "custom" — negotiated or bespoke pricing
|
|
251
|
+
*/
|
|
252
|
+
pricingModel: PricingModelSchema.default("custom"),
|
|
253
|
+
/** Base price amount (≥ 0). Currency unit defined by `currency`. */
|
|
254
|
+
price: z.number().min(0).default(0),
|
|
255
|
+
/**
|
|
256
|
+
* ISO 4217 currency code (e.g. "USD", "EUR", "GBP").
|
|
257
|
+
* Free-form string to accommodate any currency; defaults to "USD".
|
|
258
|
+
*/
|
|
259
|
+
currency: z.string().trim().max(10).default("USD"),
|
|
260
|
+
/**
|
|
261
|
+
* IDs of customer segments this product targets.
|
|
262
|
+
* Each id must reference a declared `customers.segments[].id`.
|
|
263
|
+
* Cross-reference enforced in `OrganizationModelSchema.superRefine()`.
|
|
264
|
+
*/
|
|
265
|
+
targetSegmentIds: z.array(z.string().trim().min(1)).default([]),
|
|
266
|
+
/**
|
|
267
|
+
* Optional: ID of the platform feature responsible for delivering this product.
|
|
268
|
+
* When present, must reference a declared `features[].id`.
|
|
269
|
+
* Cross-reference enforced in `OrganizationModelSchema.superRefine()`.
|
|
270
|
+
*/
|
|
271
|
+
deliveryFeatureId: z.string().trim().min(1).optional()
|
|
272
|
+
});
|
|
273
|
+
var OfferingsDomainSchema = z.object({
|
|
274
|
+
products: z.array(ProductSchema).default([])
|
|
275
|
+
});
|
|
276
|
+
var DEFAULT_ORGANIZATION_MODEL_OFFERINGS = {
|
|
277
|
+
products: []
|
|
278
|
+
};
|
|
279
|
+
var RoleSchema = z.object({
|
|
280
|
+
/** Stable unique identifier for the role (e.g. "role-ceo", "role-head-of-sales"). */
|
|
281
|
+
id: z.string().trim().min(1).max(100),
|
|
282
|
+
/** Human-readable title shown to agents and in UI (e.g. "CEO", "Head of Sales"). */
|
|
283
|
+
title: z.string().trim().min(1).max(200),
|
|
284
|
+
/**
|
|
285
|
+
* List of responsibilities this role owns — plain-language descriptions of
|
|
286
|
+
* what the person in this role is accountable for delivering.
|
|
287
|
+
* Defaults to empty array so minimal role definitions stay concise.
|
|
288
|
+
*/
|
|
289
|
+
responsibilities: z.array(z.string().trim().max(500)).default([]),
|
|
290
|
+
/**
|
|
291
|
+
* Optional: ID of another role this role reports to.
|
|
292
|
+
* When present, must reference another `roles[].id` in the same organization.
|
|
293
|
+
* Cross-reference enforced in `OrganizationModelSchema.superRefine()`.
|
|
294
|
+
* Absence indicates a top-level role (no reporting line).
|
|
295
|
+
*/
|
|
296
|
+
reportsToId: z.string().trim().min(1).max(100).optional(),
|
|
297
|
+
/**
|
|
298
|
+
* Optional: name or email of the person currently holding this role.
|
|
299
|
+
* Free-form string — supports "Alice Johnson", "alice@example.com", or
|
|
300
|
+
* any human-readable identifier. Not validated against any user registry.
|
|
301
|
+
*/
|
|
302
|
+
heldBy: z.string().trim().max(200).optional()
|
|
303
|
+
});
|
|
304
|
+
var RolesDomainSchema = z.object({
|
|
305
|
+
roles: z.array(RoleSchema).default([])
|
|
306
|
+
});
|
|
307
|
+
var DEFAULT_ORGANIZATION_MODEL_ROLES = {
|
|
308
|
+
roles: []
|
|
309
|
+
};
|
|
310
|
+
var KeyResultSchema = z.object({
|
|
311
|
+
/** Stable unique identifier for the measurable outcome (e.g. "kr-revenue-q1"). */
|
|
312
|
+
id: z.string().trim().min(1).max(100),
|
|
313
|
+
/** Plain-language description of this measurable outcome (e.g. "Increase trial-to-paid conversion"). */
|
|
314
|
+
description: z.string().trim().min(1).max(500),
|
|
315
|
+
/**
|
|
316
|
+
* What is being measured — the metric name (e.g. "monthly revenue", "NPS score",
|
|
317
|
+
* "trial-to-paid conversion rate"). Free-form string.
|
|
318
|
+
*/
|
|
319
|
+
targetMetric: z.string().trim().min(1).max(200),
|
|
320
|
+
/** Current measured value. Defaults to 0 when not yet tracked. */
|
|
321
|
+
currentValue: z.number().default(0),
|
|
322
|
+
/**
|
|
323
|
+
* Target value to reach for this measurable outcome to be considered achieved.
|
|
324
|
+
* Optional — omit if the outcome is directional (e.g. "reduce churn") without
|
|
325
|
+
* a hard numeric target.
|
|
326
|
+
*/
|
|
327
|
+
targetValue: z.number().optional()
|
|
328
|
+
});
|
|
329
|
+
var ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
330
|
+
var ObjectiveSchema = z.object({
|
|
331
|
+
/** Stable unique identifier for the goal (e.g. "goal-grow-arr-q1-2026"). */
|
|
332
|
+
id: z.string().trim().min(1).max(100),
|
|
333
|
+
/** Plain-language description of what the organization wants to achieve. */
|
|
334
|
+
description: z.string().trim().min(1).max(1e3),
|
|
335
|
+
/**
|
|
336
|
+
* Start of the period this goal is active for — ISO 8601 date string (YYYY-MM-DD).
|
|
337
|
+
* Must be strictly before `periodEnd`.
|
|
338
|
+
*/
|
|
339
|
+
periodStart: z.string().regex(ISO_DATE_REGEX, "periodStart must be an ISO date string (YYYY-MM-DD)"),
|
|
340
|
+
/**
|
|
341
|
+
* End of the period this goal is active for — ISO 8601 date string (YYYY-MM-DD).
|
|
342
|
+
* Must be strictly after `periodStart`.
|
|
343
|
+
* Enforced via `OrganizationModelSchema.superRefine()`.
|
|
344
|
+
*/
|
|
345
|
+
periodEnd: z.string().regex(ISO_DATE_REGEX, "periodEnd must be an ISO date string (YYYY-MM-DD)"),
|
|
346
|
+
/**
|
|
347
|
+
* List of measurable outcomes that define success for this goal.
|
|
348
|
+
* Defaults to empty array so goals can be declared before outcomes are defined.
|
|
349
|
+
*/
|
|
350
|
+
keyResults: z.array(KeyResultSchema).default([])
|
|
351
|
+
});
|
|
352
|
+
var GoalsDomainSchema = z.object({
|
|
353
|
+
objectives: z.array(ObjectiveSchema).default([])
|
|
354
|
+
});
|
|
355
|
+
var DEFAULT_ORGANIZATION_MODEL_GOALS = {
|
|
356
|
+
objectives: []
|
|
357
|
+
};
|
|
358
|
+
var OperationSemanticClassSchema = z.enum(["queue", "executions", "sessions", "notifications", "schedules"]);
|
|
359
|
+
var OperationEntrySchema = z.object({
|
|
360
|
+
id: z.string().trim().min(1).max(100),
|
|
361
|
+
label: z.string().trim().min(1).max(120),
|
|
362
|
+
semanticClass: OperationSemanticClassSchema,
|
|
363
|
+
/** Optional reference to the feature that owns this runtime entity. */
|
|
364
|
+
featureId: z.string().trim().min(1).max(100).optional(),
|
|
365
|
+
/**
|
|
366
|
+
* Optional pointer to the status semanticClass values that apply to this
|
|
367
|
+
* entity — ties operations back to the statuses domain for vibe rendering.
|
|
368
|
+
*/
|
|
369
|
+
supportedStatusSemanticClass: z.array(z.string().trim().min(1).max(80)).optional()
|
|
370
|
+
});
|
|
371
|
+
var OperationsDomainSchema = z.object({
|
|
372
|
+
entries: z.array(OperationEntrySchema).default([])
|
|
373
|
+
});
|
|
374
|
+
var DEFAULT_ORGANIZATION_MODEL_OPERATIONS = {
|
|
375
|
+
entries: [
|
|
376
|
+
// --- queue (HITL command queue) ---
|
|
377
|
+
{
|
|
378
|
+
id: "operations.queue",
|
|
379
|
+
label: "HITL Queue",
|
|
380
|
+
semanticClass: "queue",
|
|
381
|
+
featureId: "operations",
|
|
382
|
+
supportedStatusSemanticClass: ["queue"]
|
|
383
|
+
},
|
|
384
|
+
// --- executions (workflow / agent executions) ---
|
|
385
|
+
{
|
|
386
|
+
id: "operations.executions",
|
|
387
|
+
label: "Executions",
|
|
388
|
+
semanticClass: "executions",
|
|
389
|
+
featureId: "operations",
|
|
390
|
+
supportedStatusSemanticClass: ["execution"]
|
|
391
|
+
},
|
|
392
|
+
// --- sessions (agent conversation sessions) ---
|
|
393
|
+
{
|
|
394
|
+
id: "operations.sessions",
|
|
395
|
+
label: "Sessions",
|
|
396
|
+
semanticClass: "sessions",
|
|
397
|
+
featureId: "operations"
|
|
398
|
+
},
|
|
399
|
+
// --- notifications (platform in-app notifications) ---
|
|
400
|
+
{
|
|
401
|
+
id: "operations.notifications",
|
|
402
|
+
label: "Notifications",
|
|
403
|
+
semanticClass: "notifications",
|
|
404
|
+
featureId: "monitoring"
|
|
405
|
+
},
|
|
406
|
+
// --- schedules (task scheduler) ---
|
|
407
|
+
{
|
|
408
|
+
id: "operations.schedules",
|
|
409
|
+
label: "Schedules",
|
|
410
|
+
semanticClass: "schedules",
|
|
411
|
+
featureId: "operations",
|
|
412
|
+
supportedStatusSemanticClass: ["schedule", "schedule.run"]
|
|
413
|
+
}
|
|
414
|
+
]
|
|
415
|
+
};
|
|
416
|
+
var StatusSemanticClassSchema = z.enum([
|
|
417
|
+
"delivery.task",
|
|
418
|
+
"delivery.project",
|
|
419
|
+
"delivery.milestone",
|
|
420
|
+
"queue",
|
|
421
|
+
"execution",
|
|
422
|
+
"schedule",
|
|
423
|
+
"schedule.run",
|
|
424
|
+
"request"
|
|
425
|
+
]);
|
|
426
|
+
var StatusEntrySchema = z.object({
|
|
427
|
+
id: z.string().trim().min(1).max(100),
|
|
428
|
+
label: z.string().trim().min(1).max(120),
|
|
429
|
+
semanticClass: StatusSemanticClassSchema,
|
|
430
|
+
category: z.string().trim().min(1).max(80).optional()
|
|
431
|
+
});
|
|
432
|
+
var StatusesDomainSchema = z.object({
|
|
433
|
+
entries: z.array(StatusEntrySchema).default([])
|
|
434
|
+
});
|
|
435
|
+
var DEFAULT_ORGANIZATION_MODEL_STATUSES = {
|
|
436
|
+
entries: [
|
|
437
|
+
// --- delivery.task (TaskStatus — 9 values) ---
|
|
438
|
+
{ id: "delivery.task.planned", label: "Planned", semanticClass: "delivery.task", category: "delivery" },
|
|
439
|
+
{ id: "delivery.task.in_progress", label: "In Progress", semanticClass: "delivery.task", category: "delivery" },
|
|
440
|
+
{ id: "delivery.task.blocked", label: "Blocked", semanticClass: "delivery.task", category: "delivery" },
|
|
441
|
+
{ id: "delivery.task.submitted", label: "Submitted", semanticClass: "delivery.task", category: "delivery" },
|
|
442
|
+
{ id: "delivery.task.approved", label: "Approved", semanticClass: "delivery.task", category: "delivery" },
|
|
443
|
+
{
|
|
444
|
+
id: "delivery.task.revision_requested",
|
|
445
|
+
label: "Revision Requested",
|
|
446
|
+
semanticClass: "delivery.task",
|
|
447
|
+
category: "delivery"
|
|
448
|
+
},
|
|
449
|
+
{ id: "delivery.task.rejected", label: "Rejected", semanticClass: "delivery.task", category: "delivery" },
|
|
450
|
+
{ id: "delivery.task.cancelled", label: "Cancelled", semanticClass: "delivery.task", category: "delivery" },
|
|
451
|
+
{ id: "delivery.task.completed", label: "Completed", semanticClass: "delivery.task", category: "delivery" },
|
|
452
|
+
// --- delivery.project (ProjectStatus — 6 values) ---
|
|
453
|
+
{ id: "delivery.project.active", label: "Active", semanticClass: "delivery.project", category: "delivery" },
|
|
454
|
+
{ id: "delivery.project.on_track", label: "On Track", semanticClass: "delivery.project", category: "delivery" },
|
|
455
|
+
{ id: "delivery.project.at_risk", label: "At Risk", semanticClass: "delivery.project", category: "delivery" },
|
|
456
|
+
{ id: "delivery.project.blocked", label: "Blocked", semanticClass: "delivery.project", category: "delivery" },
|
|
457
|
+
{ id: "delivery.project.paused", label: "Paused", semanticClass: "delivery.project", category: "delivery" },
|
|
458
|
+
{ id: "delivery.project.completed", label: "Completed", semanticClass: "delivery.project", category: "delivery" },
|
|
459
|
+
// --- delivery.milestone (MilestoneStatus — 5 values) ---
|
|
460
|
+
{
|
|
461
|
+
id: "delivery.milestone.upcoming",
|
|
462
|
+
label: "Upcoming",
|
|
463
|
+
semanticClass: "delivery.milestone",
|
|
464
|
+
category: "delivery"
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
id: "delivery.milestone.in_progress",
|
|
468
|
+
label: "In Progress",
|
|
469
|
+
semanticClass: "delivery.milestone",
|
|
470
|
+
category: "delivery"
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
id: "delivery.milestone.blocked",
|
|
474
|
+
label: "Blocked",
|
|
475
|
+
semanticClass: "delivery.milestone",
|
|
476
|
+
category: "delivery"
|
|
477
|
+
},
|
|
478
|
+
{ id: "delivery.milestone.overdue", label: "Overdue", semanticClass: "delivery.milestone", category: "delivery" },
|
|
479
|
+
{
|
|
480
|
+
id: "delivery.milestone.completed",
|
|
481
|
+
label: "Completed",
|
|
482
|
+
semanticClass: "delivery.milestone",
|
|
483
|
+
category: "delivery"
|
|
484
|
+
},
|
|
485
|
+
// --- queue (QueueTaskStatus — 5 values, maps hitl/command-queue tasks) ---
|
|
486
|
+
{ id: "queue.pending", label: "Pending", semanticClass: "queue", category: "queue" },
|
|
487
|
+
{ id: "queue.processing", label: "Processing", semanticClass: "queue", category: "queue" },
|
|
488
|
+
{ id: "queue.completed", label: "Completed", semanticClass: "queue", category: "queue" },
|
|
489
|
+
{ id: "queue.failed", label: "Failed", semanticClass: "queue", category: "queue" },
|
|
490
|
+
{ id: "queue.expired", label: "Expired", semanticClass: "queue", category: "queue" },
|
|
491
|
+
// --- execution (ExecutionStatus — 5 values) ---
|
|
492
|
+
{ id: "execution.pending", label: "Pending", semanticClass: "execution", category: "execution" },
|
|
493
|
+
{ id: "execution.running", label: "Running", semanticClass: "execution", category: "execution" },
|
|
494
|
+
{ id: "execution.completed", label: "Completed", semanticClass: "execution", category: "execution" },
|
|
495
|
+
{ id: "execution.failed", label: "Failed", semanticClass: "execution", category: "execution" },
|
|
496
|
+
{ id: "execution.warning", label: "Warning", semanticClass: "execution", category: "execution" },
|
|
497
|
+
// --- schedule (schedule status — 4 values) ---
|
|
498
|
+
{ id: "schedule.active", label: "Active", semanticClass: "schedule", category: "schedule" },
|
|
499
|
+
{ id: "schedule.paused", label: "Paused", semanticClass: "schedule", category: "schedule" },
|
|
500
|
+
{ id: "schedule.completed", label: "Completed", semanticClass: "schedule", category: "schedule" },
|
|
501
|
+
{ id: "schedule.cancelled", label: "Cancelled", semanticClass: "schedule", category: "schedule" },
|
|
502
|
+
// --- schedule.run (schedule run status — 4 values) ---
|
|
503
|
+
{ id: "schedule.run.running", label: "Running", semanticClass: "schedule.run", category: "schedule" },
|
|
504
|
+
{ id: "schedule.run.completed", label: "Completed", semanticClass: "schedule.run", category: "schedule" },
|
|
505
|
+
{ id: "schedule.run.failed", label: "Failed", semanticClass: "schedule.run", category: "schedule" },
|
|
506
|
+
{ id: "schedule.run.cancelled", label: "Cancelled", semanticClass: "schedule.run", category: "schedule" },
|
|
507
|
+
// --- request (RequestStatus — 4 values, maps reported_requests) ---
|
|
508
|
+
{ id: "request.open", label: "Open", semanticClass: "request", category: "request" },
|
|
509
|
+
{ id: "request.investigating", label: "Investigating", semanticClass: "request", category: "request" },
|
|
510
|
+
{ id: "request.resolved", label: "Resolved", semanticClass: "request", category: "request" },
|
|
511
|
+
{ id: "request.wont_fix", label: "Won't Fix", semanticClass: "request", category: "request" }
|
|
512
|
+
]
|
|
513
|
+
};
|
|
514
|
+
var KnowledgeLinkSchema = z.object({
|
|
515
|
+
nodeId: NodeIdStringSchema
|
|
516
|
+
});
|
|
517
|
+
var OrgKnowledgeKindSchema = z.enum(["playbook", "strategy", "reference"]);
|
|
518
|
+
var OrgKnowledgeNodeSchema = z.object({
|
|
519
|
+
id: ModelIdSchema,
|
|
520
|
+
kind: OrgKnowledgeKindSchema,
|
|
521
|
+
title: z.string().trim().min(1).max(200),
|
|
522
|
+
summary: z.string().trim().min(1).max(1e3),
|
|
523
|
+
icon: IconNameSchema.optional(),
|
|
524
|
+
/** Raw MDX string. Phase 2 will introduce a structured block format. */
|
|
525
|
+
body: z.string().trim().min(1),
|
|
526
|
+
/**
|
|
527
|
+
* Graph links to other OM nodes this knowledge node governs.
|
|
528
|
+
* Each link emits a `governs` edge: knowledge-node -> target node.
|
|
529
|
+
*/
|
|
530
|
+
links: z.array(KnowledgeLinkSchema).default([]),
|
|
531
|
+
/** Identifiers of the roles or members who own this knowledge node. */
|
|
532
|
+
ownerIds: z.array(ModelIdSchema).default([]),
|
|
533
|
+
/** ISO date string (YYYY-MM-DD or full ISO 8601) of last meaningful update. */
|
|
534
|
+
updatedAt: z.string().trim().min(1).max(50)
|
|
535
|
+
});
|
|
536
|
+
var KnowledgeDomainSchema = z.object({
|
|
537
|
+
nodes: z.array(OrgKnowledgeNodeSchema).default([])
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// ../core/src/organization-model/schema.ts
|
|
541
|
+
var OrganizationModelSchemaBase = z.object({
|
|
542
|
+
version: z.literal(1).default(1),
|
|
543
|
+
features: z.array(FeatureSchema).default([]),
|
|
544
|
+
branding: OrganizationModelBrandingSchema,
|
|
545
|
+
navigation: OrganizationModelNavigationSchema.default({ surfaces: [], groups: [] }),
|
|
546
|
+
sales: OrganizationModelSalesSchema,
|
|
547
|
+
prospecting: OrganizationModelProspectingSchema,
|
|
548
|
+
projects: OrganizationModelProjectsSchema,
|
|
549
|
+
identity: IdentityDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_IDENTITY),
|
|
550
|
+
customers: CustomersDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_CUSTOMERS),
|
|
551
|
+
offerings: OfferingsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_OFFERINGS),
|
|
552
|
+
roles: RolesDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_ROLES),
|
|
553
|
+
goals: GoalsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_GOALS),
|
|
554
|
+
statuses: StatusesDomainSchema.default({ entries: [] }),
|
|
555
|
+
operations: OperationsDomainSchema.default({ entries: [] }),
|
|
556
|
+
knowledge: KnowledgeDomainSchema.default({ nodes: [] })
|
|
557
|
+
});
|
|
558
|
+
function addIssue(ctx, path, message) {
|
|
559
|
+
ctx.addIssue({
|
|
560
|
+
code: z.ZodIssueCode.custom,
|
|
561
|
+
path,
|
|
562
|
+
message
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
function collectIds(items, ctx, collectionPath, label) {
|
|
566
|
+
const itemsById = /* @__PURE__ */ new Map();
|
|
567
|
+
items.forEach((item, index) => {
|
|
568
|
+
if (itemsById.has(item.id)) {
|
|
569
|
+
addIssue(ctx, [...collectionPath, index, "id"], `${label} id "${item.id}" must be unique`);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
itemsById.set(item.id, item);
|
|
573
|
+
});
|
|
574
|
+
return itemsById;
|
|
575
|
+
}
|
|
576
|
+
var LEGACY_FEATURE_ALIASES = /* @__PURE__ */ new Map([
|
|
577
|
+
["crm", "sales.crm"],
|
|
578
|
+
["lead-gen", "sales.lead-gen"],
|
|
579
|
+
["submitted-requests", "monitoring.submitted-requests"]
|
|
580
|
+
]);
|
|
581
|
+
function hasFeature(featuresById, featureId) {
|
|
582
|
+
return featuresById.has(featureId) || featuresById.has(LEGACY_FEATURE_ALIASES.get(featureId) ?? "");
|
|
583
|
+
}
|
|
584
|
+
var OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((model, ctx) => {
|
|
585
|
+
const featuresById = collectIds(model.features, ctx, ["features"], "Feature");
|
|
586
|
+
model.features.forEach((feature, featureIndex) => {
|
|
587
|
+
const segments = feature.id.split(".");
|
|
588
|
+
if (segments.length > 1) {
|
|
589
|
+
const parentId = segments.slice(0, -1).join(".");
|
|
590
|
+
if (!featuresById.has(parentId)) {
|
|
591
|
+
addIssue(
|
|
592
|
+
ctx,
|
|
593
|
+
["features", featureIndex, "id"],
|
|
594
|
+
`Feature "${feature.id}" references unknown parent "${parentId}"`
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const hasChildren = model.features.some(
|
|
599
|
+
(candidate) => candidate.id.startsWith(`${feature.id}.`) && candidate.id !== feature.id
|
|
600
|
+
);
|
|
601
|
+
if (hasChildren && feature.enabled) {
|
|
602
|
+
const hasEnabledDescendant = model.features.some(
|
|
603
|
+
(candidate) => candidate.id.startsWith(`${feature.id}.`) && candidate.enabled
|
|
604
|
+
);
|
|
605
|
+
if (!hasEnabledDescendant) {
|
|
606
|
+
addIssue(
|
|
607
|
+
ctx,
|
|
608
|
+
["features", featureIndex, "enabled"],
|
|
609
|
+
`Feature "${feature.id}" is enabled but has no enabled descendants`
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
const segmentsById = new Map(model.customers.segments.map((seg) => [seg.id, seg]));
|
|
615
|
+
model.offerings.products.forEach((product, productIndex) => {
|
|
616
|
+
product.targetSegmentIds.forEach((segmentId, segmentIndex) => {
|
|
617
|
+
if (!segmentsById.has(segmentId)) {
|
|
618
|
+
addIssue(
|
|
619
|
+
ctx,
|
|
620
|
+
["offerings", "products", productIndex, "targetSegmentIds", segmentIndex],
|
|
621
|
+
`Product "${product.id}" references unknown customer segment "${segmentId}"`
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
if (product.deliveryFeatureId !== void 0 && !hasFeature(featuresById, product.deliveryFeatureId)) {
|
|
626
|
+
addIssue(
|
|
627
|
+
ctx,
|
|
628
|
+
["offerings", "products", productIndex, "deliveryFeatureId"],
|
|
629
|
+
`Product "${product.id}" references unknown delivery feature "${product.deliveryFeatureId}"`
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
model.goals.objectives.forEach((objective, index) => {
|
|
634
|
+
if (objective.periodEnd <= objective.periodStart) {
|
|
635
|
+
addIssue(
|
|
636
|
+
ctx,
|
|
637
|
+
["goals", "objectives", index, "periodEnd"],
|
|
638
|
+
`Goal "${objective.id}" has periodEnd "${objective.periodEnd}" which must be strictly after periodStart "${objective.periodStart}"`
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
const rolesById = new Map(model.roles.roles.map((role) => [role.id, role]));
|
|
643
|
+
model.roles.roles.forEach((role, roleIndex) => {
|
|
644
|
+
if (role.reportsToId !== void 0 && !rolesById.has(role.reportsToId)) {
|
|
645
|
+
addIssue(
|
|
646
|
+
ctx,
|
|
647
|
+
["roles", "roles", roleIndex, "reportsToId"],
|
|
648
|
+
`Role "${role.id}" references unknown reportsToId "${role.reportsToId}"`
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
// ../core/src/organization-model/graph/schema.ts
|
|
655
|
+
var OrganizationGraphNodeKindSchema = z.enum([
|
|
656
|
+
"organization",
|
|
657
|
+
"feature",
|
|
658
|
+
"surface",
|
|
659
|
+
"entity",
|
|
660
|
+
"capability",
|
|
661
|
+
"resource",
|
|
662
|
+
"knowledge"
|
|
663
|
+
]);
|
|
664
|
+
var OrganizationGraphEdgeKindSchema = z.enum([
|
|
665
|
+
"contains",
|
|
666
|
+
"references",
|
|
667
|
+
"exposes",
|
|
668
|
+
"maps_to",
|
|
669
|
+
"operates-on",
|
|
670
|
+
"uses",
|
|
671
|
+
"governs"
|
|
672
|
+
]);
|
|
673
|
+
var OrganizationGraphNodeSchema = z.object({
|
|
674
|
+
id: z.string().trim().min(1).max(200),
|
|
675
|
+
kind: OrganizationGraphNodeKindSchema,
|
|
676
|
+
label: LabelSchema,
|
|
677
|
+
sourceId: z.string().trim().min(1).max(255).optional(),
|
|
678
|
+
description: DescriptionSchema.optional(),
|
|
679
|
+
icon: IconNameSchema.optional(),
|
|
680
|
+
enabled: z.boolean().optional(),
|
|
681
|
+
featureId: z.string().trim().min(1).max(100).optional(),
|
|
682
|
+
resourceType: z.enum(["workflow", "agent", "trigger", "integration", "external", "human_checkpoint"]).optional()
|
|
683
|
+
});
|
|
684
|
+
var OrganizationGraphEdgeSchema = z.object({
|
|
685
|
+
id: z.string().trim().min(1).max(250),
|
|
686
|
+
kind: OrganizationGraphEdgeKindSchema,
|
|
687
|
+
sourceId: z.string().trim().min(1).max(200),
|
|
688
|
+
targetId: z.string().trim().min(1).max(200),
|
|
689
|
+
label: z.string().trim().min(1).max(120).optional(),
|
|
690
|
+
relationshipType: z.enum(["triggers", "uses", "approval"]).optional()
|
|
691
|
+
});
|
|
692
|
+
var OrganizationGraphSchema = z.object({
|
|
693
|
+
version: z.literal(1),
|
|
694
|
+
organizationModelVersion: OrganizationModelSchema.shape.version,
|
|
695
|
+
nodes: z.array(OrganizationGraphNodeSchema),
|
|
696
|
+
edges: z.array(OrganizationGraphEdgeSchema)
|
|
697
|
+
});
|
|
698
|
+
var BuildOrganizationGraphInputSchema = z.object({
|
|
699
|
+
organizationModel: OrganizationModelSchema,
|
|
700
|
+
commandViewData: z.unknown().optional()
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
// ../core/src/organization-model/graph/build.ts
|
|
704
|
+
function nodeId(kind, sourceId) {
|
|
705
|
+
return kind === "organization" ? "organization-model" : `${kind}:${sourceId ?? ""}`;
|
|
706
|
+
}
|
|
707
|
+
function edgeId(kind, sourceId, targetId, variant) {
|
|
708
|
+
return variant ? `edge:${kind}:${variant}:${sourceId}:${targetId}` : `edge:${kind}:${sourceId}:${targetId}`;
|
|
709
|
+
}
|
|
710
|
+
function pushUniqueNode(nodes, seen, node) {
|
|
711
|
+
if (seen.has(node.id)) return;
|
|
712
|
+
seen.add(node.id);
|
|
713
|
+
nodes.push(node);
|
|
714
|
+
}
|
|
715
|
+
function pushUniqueEdge(edges, seen, edge) {
|
|
716
|
+
if (seen.has(edge.id)) return;
|
|
717
|
+
seen.add(edge.id);
|
|
718
|
+
edges.push(edge);
|
|
719
|
+
}
|
|
720
|
+
function upsertResourceNode(nodes, seen, resourceNodesById, node) {
|
|
721
|
+
const existing = resourceNodesById.get(node.id);
|
|
722
|
+
if (existing) {
|
|
723
|
+
if (!existing.label || existing.label === existing.sourceId) {
|
|
724
|
+
existing.label = node.label;
|
|
725
|
+
}
|
|
726
|
+
if (!existing.description && node.description) {
|
|
727
|
+
existing.description = node.description;
|
|
728
|
+
}
|
|
729
|
+
if (!existing.sourceId && node.sourceId) {
|
|
730
|
+
existing.sourceId = node.sourceId;
|
|
731
|
+
}
|
|
732
|
+
if (!existing.resourceType && node.resourceType) {
|
|
733
|
+
existing.resourceType = node.resourceType;
|
|
734
|
+
}
|
|
735
|
+
return existing;
|
|
736
|
+
}
|
|
737
|
+
resourceNodesById.set(node.id, node);
|
|
738
|
+
pushUniqueNode(nodes, seen, node);
|
|
739
|
+
return node;
|
|
740
|
+
}
|
|
741
|
+
function ensureResourceNode(nodes, seen, resourceNodesById, resourceId) {
|
|
742
|
+
const existing = resourceNodesById.get(nodeId("resource", resourceId));
|
|
743
|
+
if (existing) return existing;
|
|
744
|
+
return upsertResourceNode(nodes, seen, resourceNodesById, {
|
|
745
|
+
id: nodeId("resource", resourceId),
|
|
746
|
+
kind: "resource",
|
|
747
|
+
label: resourceId,
|
|
748
|
+
sourceId: resourceId
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
function normalizeCommandViewResourceType(resourceType) {
|
|
752
|
+
return resourceType === "human" ? "human_checkpoint" : resourceType;
|
|
753
|
+
}
|
|
754
|
+
function collectCommandViewResources(commandViewData) {
|
|
755
|
+
return [
|
|
756
|
+
...commandViewData.workflows,
|
|
757
|
+
...commandViewData.agents,
|
|
758
|
+
...commandViewData.triggers,
|
|
759
|
+
...commandViewData.integrations,
|
|
760
|
+
...commandViewData.externalResources,
|
|
761
|
+
...commandViewData.humanCheckpoints
|
|
762
|
+
];
|
|
763
|
+
}
|
|
764
|
+
function pushResourceLinks(edges, edgeIds, resourceNodeId, links) {
|
|
765
|
+
for (const link of links ?? []) {
|
|
766
|
+
pushUniqueEdge(edges, edgeIds, {
|
|
767
|
+
id: edgeId(link.kind, resourceNodeId, link.nodeId),
|
|
768
|
+
kind: link.kind,
|
|
769
|
+
sourceId: resourceNodeId,
|
|
770
|
+
targetId: link.nodeId
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function buildOrganizationGraph(input) {
|
|
775
|
+
const parsed = BuildOrganizationGraphInputSchema.parse(input);
|
|
776
|
+
const organizationModel = parsed.organizationModel;
|
|
777
|
+
const commandViewData = parsed.commandViewData;
|
|
778
|
+
const nodes = [];
|
|
779
|
+
const edges = [];
|
|
780
|
+
const nodeIds = /* @__PURE__ */ new Set();
|
|
781
|
+
const edgeIds = /* @__PURE__ */ new Set();
|
|
782
|
+
const resourceNodesById = /* @__PURE__ */ new Map();
|
|
783
|
+
const organizationNode = {
|
|
784
|
+
id: nodeId("organization"),
|
|
785
|
+
kind: "organization",
|
|
786
|
+
label: "Organization Model"
|
|
787
|
+
};
|
|
788
|
+
pushUniqueNode(nodes, nodeIds, organizationNode);
|
|
789
|
+
for (const feature of [...organizationModel.features].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
790
|
+
const id = nodeId("feature", feature.id);
|
|
791
|
+
pushUniqueNode(nodes, nodeIds, {
|
|
792
|
+
id,
|
|
793
|
+
kind: "feature",
|
|
794
|
+
label: feature.label,
|
|
795
|
+
sourceId: feature.id,
|
|
796
|
+
description: feature.description,
|
|
797
|
+
icon: feature.icon,
|
|
798
|
+
enabled: feature.enabled,
|
|
799
|
+
featureId: feature.id
|
|
800
|
+
});
|
|
801
|
+
pushUniqueEdge(edges, edgeIds, {
|
|
802
|
+
id: edgeId("contains", organizationNode.id, id),
|
|
803
|
+
kind: "contains",
|
|
804
|
+
sourceId: organizationNode.id,
|
|
805
|
+
targetId: id
|
|
806
|
+
});
|
|
807
|
+
const parentId = feature.id.includes(".") ? feature.id.slice(0, feature.id.lastIndexOf(".")) : void 0;
|
|
808
|
+
if (parentId) {
|
|
809
|
+
pushUniqueEdge(edges, edgeIds, {
|
|
810
|
+
id: edgeId("contains", nodeId("feature", parentId), id),
|
|
811
|
+
kind: "contains",
|
|
812
|
+
sourceId: nodeId("feature", parentId),
|
|
813
|
+
targetId: id
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
for (const node of [...organizationModel.knowledge?.nodes ?? []].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
818
|
+
const id = nodeId("knowledge", node.id);
|
|
819
|
+
pushUniqueNode(nodes, nodeIds, {
|
|
820
|
+
id,
|
|
821
|
+
kind: "knowledge",
|
|
822
|
+
label: node.title,
|
|
823
|
+
sourceId: node.id,
|
|
824
|
+
description: node.summary,
|
|
825
|
+
icon: node.icon
|
|
826
|
+
});
|
|
827
|
+
pushUniqueEdge(edges, edgeIds, {
|
|
828
|
+
id: edgeId("contains", organizationNode.id, id),
|
|
829
|
+
kind: "contains",
|
|
830
|
+
sourceId: organizationNode.id,
|
|
831
|
+
targetId: id
|
|
832
|
+
});
|
|
833
|
+
for (const link of node.links) {
|
|
834
|
+
pushUniqueEdge(edges, edgeIds, {
|
|
835
|
+
id: edgeId("governs", id, link.nodeId),
|
|
836
|
+
kind: "governs",
|
|
837
|
+
sourceId: id,
|
|
838
|
+
targetId: link.nodeId
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (commandViewData) {
|
|
843
|
+
const commandViewResources = collectCommandViewResources(commandViewData).sort(
|
|
844
|
+
(a, b) => a.resourceId.localeCompare(b.resourceId)
|
|
845
|
+
);
|
|
846
|
+
for (const resource of commandViewResources) {
|
|
847
|
+
const id = nodeId("resource", resource.resourceId);
|
|
848
|
+
const resourceNode = upsertResourceNode(nodes, nodeIds, resourceNodesById, {
|
|
849
|
+
id,
|
|
850
|
+
kind: "resource",
|
|
851
|
+
label: resource.name,
|
|
852
|
+
sourceId: resource.resourceId,
|
|
853
|
+
description: resource.description,
|
|
854
|
+
resourceType: normalizeCommandViewResourceType(resource.type)
|
|
855
|
+
});
|
|
856
|
+
pushUniqueEdge(edges, edgeIds, {
|
|
857
|
+
id: edgeId("contains", organizationNode.id, resourceNode.id),
|
|
858
|
+
kind: "contains",
|
|
859
|
+
sourceId: organizationNode.id,
|
|
860
|
+
targetId: resourceNode.id
|
|
861
|
+
});
|
|
862
|
+
pushResourceLinks(edges, edgeIds, resourceNode.id, resource.links);
|
|
863
|
+
}
|
|
864
|
+
for (const relationship of [...commandViewData.edges].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
865
|
+
const sourceNode = ensureResourceNode(nodes, nodeIds, resourceNodesById, relationship.source);
|
|
866
|
+
const targetNode = ensureResourceNode(nodes, nodeIds, resourceNodesById, relationship.target);
|
|
867
|
+
pushUniqueEdge(edges, edgeIds, {
|
|
868
|
+
id: edgeId("contains", organizationNode.id, sourceNode.id),
|
|
869
|
+
kind: "contains",
|
|
870
|
+
sourceId: organizationNode.id,
|
|
871
|
+
targetId: sourceNode.id
|
|
872
|
+
});
|
|
873
|
+
pushUniqueEdge(edges, edgeIds, {
|
|
874
|
+
id: edgeId("contains", organizationNode.id, targetNode.id),
|
|
875
|
+
kind: "contains",
|
|
876
|
+
sourceId: organizationNode.id,
|
|
877
|
+
targetId: targetNode.id
|
|
878
|
+
});
|
|
879
|
+
pushUniqueEdge(edges, edgeIds, {
|
|
880
|
+
id: edgeId("references", sourceNode.id, targetNode.id, relationship.relationship),
|
|
881
|
+
kind: "references",
|
|
882
|
+
sourceId: sourceNode.id,
|
|
883
|
+
targetId: targetNode.id,
|
|
884
|
+
label: relationship.relationship,
|
|
885
|
+
relationshipType: relationship.relationship
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
const graph = {
|
|
890
|
+
version: 1,
|
|
891
|
+
organizationModelVersion: organizationModel.version,
|
|
892
|
+
nodes,
|
|
893
|
+
edges
|
|
894
|
+
};
|
|
895
|
+
return OrganizationGraphSchema.parse(graph);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// src/features/operations/organization-graph/organizationGraphDetail.ts
|
|
899
|
+
var NODE_KIND_LABELS = {
|
|
900
|
+
organization: "Organization root",
|
|
901
|
+
feature: "Feature",
|
|
902
|
+
surface: "Surface",
|
|
903
|
+
entity: "Entity",
|
|
904
|
+
capability: "Capability",
|
|
905
|
+
resource: "Resource",
|
|
906
|
+
knowledge: "Knowledge"
|
|
907
|
+
};
|
|
908
|
+
var NODE_KIND_MEANINGS = {
|
|
909
|
+
organization: "The root of the shared organization model and the parent for every derived node.",
|
|
910
|
+
feature: "A feature that enables or disables downstream surfaces and model surfaces.",
|
|
911
|
+
surface: "A user-facing or operational surface that exposes feature behavior in the model.",
|
|
912
|
+
entity: "A shared business concept that can be referenced by surfaces, resources, or capabilities.",
|
|
913
|
+
capability: "A reusable capability that can be attached to a feature or surface.",
|
|
914
|
+
resource: "A concrete command-view or mapped resource that bridges execution topology into the model.",
|
|
915
|
+
knowledge: "An operational knowledge node that documents a process, strategy, or runbook in the org model."
|
|
916
|
+
};
|
|
917
|
+
var EDGE_KIND_LABELS = {
|
|
918
|
+
contains: "Containment",
|
|
919
|
+
references: "Reference",
|
|
920
|
+
exposes: "Exposure",
|
|
921
|
+
maps_to: "Mapping",
|
|
922
|
+
"operates-on": "Operates on",
|
|
923
|
+
uses: "Uses",
|
|
924
|
+
governs: "Governs"
|
|
925
|
+
};
|
|
926
|
+
var EDGE_KIND_MEANINGS = {
|
|
927
|
+
contains: "A hierarchy or ownership link inside the shared graph.",
|
|
928
|
+
references: "A semantic association or dependency between two graph nodes.",
|
|
929
|
+
exposes: "A feature enables a surface to be visible or reachable.",
|
|
930
|
+
maps_to: "A concrete resource is aligned to a domain, entity, capability, or surface.",
|
|
931
|
+
"operates-on": "A concrete resource operates on an Organization Model node.",
|
|
932
|
+
uses: "A concrete resource depends on another graph node or integration.",
|
|
933
|
+
governs: "A knowledge node governs or provides authoritative guidance for another node."
|
|
934
|
+
};
|
|
935
|
+
var RELATIONSHIP_MEANINGS = {
|
|
936
|
+
triggers: "Executable handoff: the source resource starts the target resource.",
|
|
937
|
+
uses: "Operational dependency: the source resource relies on the target integration.",
|
|
938
|
+
approval: "Human gate: the source resource pauses for approval at the target checkpoint."
|
|
939
|
+
};
|
|
940
|
+
function titleCase(value) {
|
|
941
|
+
return value.replace(/[-_.]+/g, " ").replace(/\s+/g, " ").trim().split(" ").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
942
|
+
}
|
|
943
|
+
function getGraphIndex(graph) {
|
|
944
|
+
const nodesById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
945
|
+
const edgesById = new Map(graph.edges.map((edge) => [edge.id, edge]));
|
|
946
|
+
return { nodesById, edgesById };
|
|
947
|
+
}
|
|
948
|
+
function getNodeKindLabel(kind) {
|
|
949
|
+
return NODE_KIND_LABELS[kind];
|
|
950
|
+
}
|
|
951
|
+
function getNodeMeaning(node) {
|
|
952
|
+
const baseMeaning = NODE_KIND_MEANINGS[node.kind];
|
|
953
|
+
if (node.kind === "feature") {
|
|
954
|
+
return node.enabled === false ? `${baseMeaning} This feature is currently disabled in the organization model.` : `${baseMeaning} This feature is currently enabled in the organization model.`;
|
|
955
|
+
}
|
|
956
|
+
if (node.kind === "resource" && node.resourceType) {
|
|
957
|
+
return `${baseMeaning} Resource type: ${titleCase(node.resourceType)}.`;
|
|
958
|
+
}
|
|
959
|
+
if (node.description) {
|
|
960
|
+
return `${baseMeaning} ${node.description}`;
|
|
961
|
+
}
|
|
962
|
+
return baseMeaning;
|
|
963
|
+
}
|
|
964
|
+
function getEdgeKindLabel(kind) {
|
|
965
|
+
return EDGE_KIND_LABELS[kind];
|
|
966
|
+
}
|
|
967
|
+
function getEdgeMeaning(edge, sourceNode, targetNode) {
|
|
968
|
+
const sourceLabel = sourceNode?.label ?? edge.sourceId;
|
|
969
|
+
const targetLabel = targetNode?.label ?? edge.targetId;
|
|
970
|
+
if (edge.relationshipType) {
|
|
971
|
+
const relationshipMeaning = RELATIONSHIP_MEANINGS[edge.relationshipType];
|
|
972
|
+
return `${relationshipMeaning} The graph renders the link from ${sourceLabel} to ${targetLabel} as a ${getEdgeKindLabel(edge.kind).toLowerCase()} edge.`;
|
|
973
|
+
}
|
|
974
|
+
return `${EDGE_KIND_MEANINGS[edge.kind]} This link connects ${sourceLabel} to ${targetLabel}.`;
|
|
975
|
+
}
|
|
976
|
+
function countByKind(nodes) {
|
|
977
|
+
const counts = /* @__PURE__ */ new Map();
|
|
978
|
+
for (const node of nodes) {
|
|
979
|
+
counts.set(node.kind, (counts.get(node.kind) ?? 0) + 1);
|
|
980
|
+
}
|
|
981
|
+
return [...counts.entries()].map(([kind, count]) => ({
|
|
982
|
+
kind,
|
|
983
|
+
label: getNodeKindLabel(kind),
|
|
984
|
+
count
|
|
985
|
+
})).sort((left, right) => left.label.localeCompare(right.label));
|
|
986
|
+
}
|
|
987
|
+
function uniqueNodesById(nodes) {
|
|
988
|
+
const byId = /* @__PURE__ */ new Map();
|
|
989
|
+
for (const node of nodes) {
|
|
990
|
+
byId.set(node.id, node);
|
|
991
|
+
}
|
|
992
|
+
return [...byId.values()];
|
|
993
|
+
}
|
|
994
|
+
function uniqueEdgesById(edges) {
|
|
995
|
+
const byId = /* @__PURE__ */ new Map();
|
|
996
|
+
for (const edge of edges) {
|
|
997
|
+
byId.set(edge.id, edge);
|
|
998
|
+
}
|
|
999
|
+
return [...byId.values()];
|
|
1000
|
+
}
|
|
1001
|
+
function buildNodeState(graph, node, nodesById) {
|
|
1002
|
+
const incidentEdges = graph.edges.filter((edge) => edge.sourceId === node.id || edge.targetId === node.id);
|
|
1003
|
+
const incomingEdges = incidentEdges.filter((edge) => edge.targetId === node.id);
|
|
1004
|
+
const outgoingEdges = incidentEdges.filter((edge) => edge.sourceId === node.id);
|
|
1005
|
+
const adjacentNodes = uniqueNodesById(
|
|
1006
|
+
incidentEdges.map((edge) => edge.sourceId === node.id ? nodesById.get(edge.targetId) : nodesById.get(edge.sourceId)).filter((value) => Boolean(value))
|
|
1007
|
+
);
|
|
1008
|
+
const relatedNodes = adjacentNodes.slice().sort((left, right) => left.label.localeCompare(right.label)).map((adjacentNode) => {
|
|
1009
|
+
const contexts = incidentEdges.filter(
|
|
1010
|
+
(edge) => edge.sourceId === node.id ? edge.targetId === adjacentNode.id : edge.sourceId === adjacentNode.id
|
|
1011
|
+
).map(
|
|
1012
|
+
(edge) => `${edge.sourceId === node.id ? "outgoing" : "incoming"} via ${edge.label ?? getEdgeKindLabel(edge.kind).toLowerCase()}`
|
|
1013
|
+
);
|
|
1014
|
+
return {
|
|
1015
|
+
id: adjacentNode.id,
|
|
1016
|
+
label: adjacentNode.label,
|
|
1017
|
+
kind: adjacentNode.kind,
|
|
1018
|
+
kindLabel: getNodeKindLabel(adjacentNode.kind),
|
|
1019
|
+
context: contexts.slice(0, 2).join(", ")
|
|
1020
|
+
};
|
|
1021
|
+
});
|
|
1022
|
+
const relatedEdges = incidentEdges.slice().sort((left, right) => left.id.localeCompare(right.id)).map((edge) => {
|
|
1023
|
+
const connectedId = edge.sourceId === node.id ? edge.targetId : edge.sourceId;
|
|
1024
|
+
const connectedNode = nodesById.get(connectedId);
|
|
1025
|
+
return {
|
|
1026
|
+
id: edge.id,
|
|
1027
|
+
label: edge.label ?? edge.relationshipType ?? getEdgeKindLabel(edge.kind),
|
|
1028
|
+
kind: edge.kind,
|
|
1029
|
+
kindLabel: getEdgeKindLabel(edge.kind),
|
|
1030
|
+
context: `${edge.sourceId === node.id ? "outgoing" : "incoming"}${connectedNode ? ` to ${connectedNode.label}` : ""}`
|
|
1031
|
+
};
|
|
1032
|
+
});
|
|
1033
|
+
const metadata = [
|
|
1034
|
+
{ label: "Graph ID", value: node.id },
|
|
1035
|
+
{ label: "Source ID", value: node.sourceId ?? node.id },
|
|
1036
|
+
{ label: "Kind", value: getNodeKindLabel(node.kind) }
|
|
1037
|
+
];
|
|
1038
|
+
if (node.enabled !== void 0) {
|
|
1039
|
+
metadata.push({ label: "Enabled", value: node.enabled ? "Yes" : "No" });
|
|
1040
|
+
}
|
|
1041
|
+
if (node.featureId) {
|
|
1042
|
+
metadata.push({ label: "Feature ID", value: node.featureId });
|
|
1043
|
+
}
|
|
1044
|
+
if (node.resourceType) {
|
|
1045
|
+
metadata.push({ label: "Resource Type", value: titleCase(node.resourceType) });
|
|
1046
|
+
}
|
|
1047
|
+
const meaningfulEdges = incidentEdges.filter((edge) => edge.kind !== "contains" || edge.relationshipType);
|
|
1048
|
+
return {
|
|
1049
|
+
state: "node",
|
|
1050
|
+
title: node.label,
|
|
1051
|
+
semanticCategory: getNodeKindLabel(node.kind),
|
|
1052
|
+
operationalMeaning: getNodeMeaning(node),
|
|
1053
|
+
description: node.description,
|
|
1054
|
+
metrics: [
|
|
1055
|
+
{
|
|
1056
|
+
label: "Connected edges",
|
|
1057
|
+
value: String(incidentEdges.length),
|
|
1058
|
+
hint: "All direct relationships attached to this node"
|
|
1059
|
+
},
|
|
1060
|
+
{ label: "Incoming", value: String(incomingEdges.length), hint: "Edges that point into this node" },
|
|
1061
|
+
{ label: "Outgoing", value: String(outgoingEdges.length), hint: "Edges that start at this node" },
|
|
1062
|
+
{
|
|
1063
|
+
label: "Meaningful links",
|
|
1064
|
+
value: String(meaningfulEdges.length),
|
|
1065
|
+
hint: "Edges with semantic or operational meaning"
|
|
1066
|
+
}
|
|
1067
|
+
],
|
|
1068
|
+
metadata,
|
|
1069
|
+
adjacentKindCounts: countByKind(adjacentNodes),
|
|
1070
|
+
node,
|
|
1071
|
+
relatedNodes,
|
|
1072
|
+
relatedEdges
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
function buildEdgeState(graph, edge, nodesById) {
|
|
1076
|
+
const sourceNode = nodesById.get(edge.sourceId);
|
|
1077
|
+
const targetNode = nodesById.get(edge.targetId);
|
|
1078
|
+
const sourceIncidentEdges = graph.edges.filter(
|
|
1079
|
+
(candidate) => candidate.sourceId === edge.sourceId || candidate.targetId === edge.sourceId
|
|
1080
|
+
);
|
|
1081
|
+
const targetIncidentEdges = graph.edges.filter(
|
|
1082
|
+
(candidate) => candidate.sourceId === edge.targetId || candidate.targetId === edge.targetId
|
|
1083
|
+
);
|
|
1084
|
+
const adjacentNodes = uniqueNodesById(
|
|
1085
|
+
[...sourceIncidentEdges, ...targetIncidentEdges].map((candidate) => {
|
|
1086
|
+
if (candidate.sourceId === edge.sourceId) return nodesById.get(candidate.targetId);
|
|
1087
|
+
if (candidate.targetId === edge.sourceId) return nodesById.get(candidate.sourceId);
|
|
1088
|
+
if (candidate.sourceId === edge.targetId) return nodesById.get(candidate.targetId);
|
|
1089
|
+
if (candidate.targetId === edge.targetId) return nodesById.get(candidate.sourceId);
|
|
1090
|
+
return null;
|
|
1091
|
+
}).filter((value) => Boolean(value)).filter((candidate) => candidate.id !== edge.sourceId && candidate.id !== edge.targetId)
|
|
1092
|
+
);
|
|
1093
|
+
const sourceNeighborIds = new Set(
|
|
1094
|
+
sourceIncidentEdges.map((candidate) => {
|
|
1095
|
+
if (candidate.sourceId === edge.sourceId) return candidate.targetId;
|
|
1096
|
+
if (candidate.targetId === edge.sourceId) return candidate.sourceId;
|
|
1097
|
+
return null;
|
|
1098
|
+
}).filter((value) => Boolean(value))
|
|
1099
|
+
);
|
|
1100
|
+
const targetNeighborIds = new Set(
|
|
1101
|
+
targetIncidentEdges.map((candidate) => {
|
|
1102
|
+
if (candidate.sourceId === edge.targetId) return candidate.targetId;
|
|
1103
|
+
if (candidate.targetId === edge.targetId) return candidate.sourceId;
|
|
1104
|
+
return null;
|
|
1105
|
+
}).filter((value) => Boolean(value))
|
|
1106
|
+
);
|
|
1107
|
+
const sharedNeighborCount = [...sourceNeighborIds].filter((neighborId) => targetNeighborIds.has(neighborId)).length;
|
|
1108
|
+
const relatedNodes = adjacentNodes.slice().sort((left, right) => left.label.localeCompare(right.label)).map((adjacentNode) => {
|
|
1109
|
+
const sourceContext = sourceIncidentEdges.some(
|
|
1110
|
+
(candidate) => candidate.sourceId === edge.sourceId && candidate.targetId === adjacentNode.id || candidate.targetId === edge.sourceId && candidate.sourceId === adjacentNode.id
|
|
1111
|
+
);
|
|
1112
|
+
const targetContext = targetIncidentEdges.some(
|
|
1113
|
+
(candidate) => candidate.sourceId === edge.targetId && candidate.targetId === adjacentNode.id || candidate.targetId === edge.targetId && candidate.sourceId === adjacentNode.id
|
|
1114
|
+
);
|
|
1115
|
+
const contextParts = [];
|
|
1116
|
+
if (sourceContext) contextParts.push("source neighborhood");
|
|
1117
|
+
if (targetContext) contextParts.push("target neighborhood");
|
|
1118
|
+
return {
|
|
1119
|
+
id: adjacentNode.id,
|
|
1120
|
+
label: adjacentNode.label,
|
|
1121
|
+
kind: adjacentNode.kind,
|
|
1122
|
+
kindLabel: getNodeKindLabel(adjacentNode.kind),
|
|
1123
|
+
context: contextParts.join(" + ")
|
|
1124
|
+
};
|
|
1125
|
+
});
|
|
1126
|
+
const surroundingEdges = uniqueEdgesById([...sourceIncidentEdges, ...targetIncidentEdges]).filter((candidate) => candidate.id !== edge.id).sort((left, right) => left.id.localeCompare(right.id)).map((candidate) => {
|
|
1127
|
+
const leftNode = nodesById.get(candidate.sourceId);
|
|
1128
|
+
const rightNode = nodesById.get(candidate.targetId);
|
|
1129
|
+
return {
|
|
1130
|
+
id: candidate.id,
|
|
1131
|
+
label: candidate.label ?? candidate.relationshipType ?? getEdgeKindLabel(candidate.kind),
|
|
1132
|
+
kind: candidate.kind,
|
|
1133
|
+
kindLabel: getEdgeKindLabel(candidate.kind),
|
|
1134
|
+
context: `${leftNode?.label ?? candidate.sourceId} -> ${rightNode?.label ?? candidate.targetId}`
|
|
1135
|
+
};
|
|
1136
|
+
});
|
|
1137
|
+
const metadata = [
|
|
1138
|
+
{ label: "Graph ID", value: edge.id },
|
|
1139
|
+
{ label: "Source ID", value: edge.sourceId },
|
|
1140
|
+
{ label: "Target ID", value: edge.targetId },
|
|
1141
|
+
{ label: "Edge kind", value: getEdgeKindLabel(edge.kind) }
|
|
1142
|
+
];
|
|
1143
|
+
if (edge.label) {
|
|
1144
|
+
metadata.push({ label: "Label", value: edge.label });
|
|
1145
|
+
}
|
|
1146
|
+
if (edge.relationshipType) {
|
|
1147
|
+
metadata.push({ label: "Relationship", value: titleCase(edge.relationshipType) });
|
|
1148
|
+
}
|
|
1149
|
+
return {
|
|
1150
|
+
state: "edge",
|
|
1151
|
+
title: edge.label ?? `${sourceNode?.label ?? edge.sourceId} \u2192 ${targetNode?.label ?? edge.targetId}`,
|
|
1152
|
+
semanticCategory: edge.relationshipType ? titleCase(edge.relationshipType) : getEdgeKindLabel(edge.kind),
|
|
1153
|
+
operationalMeaning: getEdgeMeaning(edge, sourceNode, targetNode),
|
|
1154
|
+
description: edge.label,
|
|
1155
|
+
metrics: [
|
|
1156
|
+
{
|
|
1157
|
+
label: "Source degree",
|
|
1158
|
+
value: String(sourceIncidentEdges.length),
|
|
1159
|
+
hint: "Relationships attached to the source node"
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
label: "Target degree",
|
|
1163
|
+
value: String(targetIncidentEdges.length),
|
|
1164
|
+
hint: "Relationships attached to the target node"
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
label: "Nearby nodes",
|
|
1168
|
+
value: String(adjacentNodes.length),
|
|
1169
|
+
hint: "Unique nodes in the source and target neighborhoods"
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
label: "Shared peers",
|
|
1173
|
+
value: String(sharedNeighborCount),
|
|
1174
|
+
hint: "Neighbors that appear in both node neighborhoods"
|
|
1175
|
+
}
|
|
1176
|
+
],
|
|
1177
|
+
metadata,
|
|
1178
|
+
adjacentKindCounts: countByKind(adjacentNodes),
|
|
1179
|
+
edge,
|
|
1180
|
+
sourceNode,
|
|
1181
|
+
targetNode,
|
|
1182
|
+
relatedNodes,
|
|
1183
|
+
relatedEdges: surroundingEdges
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
function getOrganizationGraphDetailState(graph, selectedElement) {
|
|
1187
|
+
if (!selectedElement) {
|
|
1188
|
+
return {
|
|
1189
|
+
state: "empty",
|
|
1190
|
+
title: "No element selected",
|
|
1191
|
+
semanticCategory: "Selection",
|
|
1192
|
+
operationalMeaning: "Select a node or edge to inspect its semantic and operational context.",
|
|
1193
|
+
metrics: [],
|
|
1194
|
+
metadata: [],
|
|
1195
|
+
adjacentKindCounts: []
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
if (!graph) {
|
|
1199
|
+
return {
|
|
1200
|
+
state: "missing",
|
|
1201
|
+
title: "Graph unavailable",
|
|
1202
|
+
semanticCategory: selectedElement.type === "node" ? "Selected node" : "Selected edge",
|
|
1203
|
+
operationalMeaning: "The graph data is not available yet, so this selection cannot be resolved.",
|
|
1204
|
+
metrics: [],
|
|
1205
|
+
metadata: [{ label: "Selected ID", value: selectedElement.id }],
|
|
1206
|
+
adjacentKindCounts: [],
|
|
1207
|
+
missingId: selectedElement.id
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
const { nodesById, edgesById } = getGraphIndex(graph);
|
|
1211
|
+
if (selectedElement.type === "node") {
|
|
1212
|
+
const node = nodesById.get(selectedElement.id);
|
|
1213
|
+
if (!node) {
|
|
1214
|
+
return {
|
|
1215
|
+
state: "missing",
|
|
1216
|
+
title: "Selected node unavailable",
|
|
1217
|
+
semanticCategory: "Node",
|
|
1218
|
+
operationalMeaning: "The selected node is no longer present in the current graph build.",
|
|
1219
|
+
metrics: [],
|
|
1220
|
+
metadata: [{ label: "Selected ID", value: selectedElement.id }],
|
|
1221
|
+
adjacentKindCounts: [],
|
|
1222
|
+
missingId: selectedElement.id
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
return buildNodeState(graph, node, nodesById);
|
|
1226
|
+
}
|
|
1227
|
+
const edge = edgesById.get(selectedElement.id);
|
|
1228
|
+
if (!edge) {
|
|
1229
|
+
return {
|
|
1230
|
+
state: "missing",
|
|
1231
|
+
title: "Selected edge unavailable",
|
|
1232
|
+
semanticCategory: "Edge",
|
|
1233
|
+
operationalMeaning: "The selected edge is no longer present in the current graph build.",
|
|
1234
|
+
metrics: [],
|
|
1235
|
+
metadata: [{ label: "Selected ID", value: selectedElement.id }],
|
|
1236
|
+
adjacentKindCounts: [],
|
|
1237
|
+
missingId: selectedElement.id
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
return buildEdgeState(graph, edge, nodesById);
|
|
1241
|
+
}
|
|
1242
|
+
function getNodeKindColor(kind) {
|
|
1243
|
+
switch (kind) {
|
|
1244
|
+
case "organization":
|
|
1245
|
+
return "gray";
|
|
1246
|
+
case "feature":
|
|
1247
|
+
return "blue";
|
|
1248
|
+
case "domain":
|
|
1249
|
+
return "violet";
|
|
1250
|
+
case "surface":
|
|
1251
|
+
return "cyan";
|
|
1252
|
+
case "entity":
|
|
1253
|
+
return "pink";
|
|
1254
|
+
case "capability":
|
|
1255
|
+
return "green";
|
|
1256
|
+
case "resource":
|
|
1257
|
+
return "orange";
|
|
1258
|
+
default:
|
|
1259
|
+
return "gray";
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
function getEdgeKindColor(kind) {
|
|
1263
|
+
switch (kind) {
|
|
1264
|
+
case "contains":
|
|
1265
|
+
return "gray";
|
|
1266
|
+
case "references":
|
|
1267
|
+
return "cyan";
|
|
1268
|
+
case "exposes":
|
|
1269
|
+
return "blue";
|
|
1270
|
+
case "maps_to":
|
|
1271
|
+
return "violet";
|
|
1272
|
+
default:
|
|
1273
|
+
return "gray";
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
function getNodeKindLabel2(kind) {
|
|
1277
|
+
switch (kind) {
|
|
1278
|
+
case "organization":
|
|
1279
|
+
return "Organization root";
|
|
1280
|
+
case "feature":
|
|
1281
|
+
return "Feature gate";
|
|
1282
|
+
case "domain":
|
|
1283
|
+
return "Domain boundary";
|
|
1284
|
+
case "surface":
|
|
1285
|
+
return "Surface";
|
|
1286
|
+
case "entity":
|
|
1287
|
+
return "Entity";
|
|
1288
|
+
case "capability":
|
|
1289
|
+
return "Capability";
|
|
1290
|
+
case "resource":
|
|
1291
|
+
return "Resource";
|
|
1292
|
+
default:
|
|
1293
|
+
return kind;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
function getNodeIconFallbackKind(kind) {
|
|
1297
|
+
if (kind === "knowledge") return "knowledge";
|
|
1298
|
+
if (kind === "feature") return "feature";
|
|
1299
|
+
if (kind === "resource") return "resource";
|
|
1300
|
+
return "unknown";
|
|
1301
|
+
}
|
|
1302
|
+
function titleCase2(value) {
|
|
1303
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
1304
|
+
}
|
|
1305
|
+
function MetricCard({ metric }) {
|
|
1306
|
+
return /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
1307
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, c: "dimmed", children: metric.label }),
|
|
1308
|
+
/* @__PURE__ */ jsx(Text, { size: "lg", fw: 800, children: metric.value }),
|
|
1309
|
+
metric.hint ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: metric.hint }) : null
|
|
1310
|
+
] }) });
|
|
1311
|
+
}
|
|
1312
|
+
function RelatedItemCard({ item, itemType }) {
|
|
1313
|
+
const badgeColor = itemType === "node" ? getNodeKindColor(item.kind) : getEdgeKindColor(item.kind);
|
|
1314
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1315
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", align: "flex-start", children: [
|
|
1316
|
+
/* @__PURE__ */ jsx(Text, { fw: 700, size: "sm", style: { lineHeight: 1.2 }, children: item.label }),
|
|
1317
|
+
/* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: badgeColor, children: item.kindLabel })
|
|
1318
|
+
] }),
|
|
1319
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: item.context }),
|
|
1320
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", ff: "monospace", c: "dimmed", children: item.id })
|
|
1321
|
+
] }) });
|
|
1322
|
+
}
|
|
1323
|
+
function getStatusColor(value) {
|
|
1324
|
+
switch (value) {
|
|
1325
|
+
case "failed":
|
|
1326
|
+
return "red";
|
|
1327
|
+
case "warning":
|
|
1328
|
+
return "yellow";
|
|
1329
|
+
case "running":
|
|
1330
|
+
case "processing":
|
|
1331
|
+
return "blue";
|
|
1332
|
+
case "completed":
|
|
1333
|
+
return "green";
|
|
1334
|
+
case "pending":
|
|
1335
|
+
return "orange";
|
|
1336
|
+
case "expired":
|
|
1337
|
+
return "gray";
|
|
1338
|
+
default:
|
|
1339
|
+
return "gray";
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
function FollowUpSectionCard({ section }) {
|
|
1343
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
1344
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
|
|
1345
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
1346
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: section.title }),
|
|
1347
|
+
section.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: section.description }) : null
|
|
1348
|
+
] }),
|
|
1349
|
+
section.primaryAction ? /* @__PURE__ */ jsx(
|
|
1350
|
+
Button,
|
|
1351
|
+
{
|
|
1352
|
+
size: "xs",
|
|
1353
|
+
variant: "light",
|
|
1354
|
+
component: "a",
|
|
1355
|
+
href: section.primaryAction.href,
|
|
1356
|
+
target: "_blank",
|
|
1357
|
+
rel: "noreferrer",
|
|
1358
|
+
children: section.primaryAction.label
|
|
1359
|
+
}
|
|
1360
|
+
) : null
|
|
1361
|
+
] }),
|
|
1362
|
+
section.items.length > 0 ? /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: section.items.map((item) => /* @__PURE__ */ jsx(
|
|
1363
|
+
Paper,
|
|
1364
|
+
{
|
|
1365
|
+
withBorder: true,
|
|
1366
|
+
radius: "md",
|
|
1367
|
+
p: "sm",
|
|
1368
|
+
component: "a",
|
|
1369
|
+
href: item.href,
|
|
1370
|
+
target: "_blank",
|
|
1371
|
+
rel: "noreferrer",
|
|
1372
|
+
style: { display: "block", textDecoration: "none", color: "inherit" },
|
|
1373
|
+
children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1374
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", align: "flex-start", children: [
|
|
1375
|
+
/* @__PURE__ */ jsx(Text, { fw: 700, size: "sm", style: { lineHeight: 1.2 }, children: item.label }),
|
|
1376
|
+
item.badgeLabel ? /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: item.badgeColor ?? getStatusColor(item.badgeLabel), children: item.badgeLabel }) : null
|
|
1377
|
+
] }),
|
|
1378
|
+
item.description ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: item.description }) : null,
|
|
1379
|
+
item.meta ? /* @__PURE__ */ jsx(Text, { size: "xs", ff: "monospace", c: "dimmed", children: item.meta }) : null
|
|
1380
|
+
] })
|
|
1381
|
+
},
|
|
1382
|
+
item.id
|
|
1383
|
+
)) }) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: section.emptyMessage })
|
|
1384
|
+
] }) });
|
|
1385
|
+
}
|
|
1386
|
+
function OrganizationGraphDetailPanel({
|
|
1387
|
+
graph,
|
|
1388
|
+
selectedElement,
|
|
1389
|
+
supplementalSummary,
|
|
1390
|
+
followUpSections,
|
|
1391
|
+
expandAroundPanel,
|
|
1392
|
+
onClearSelection,
|
|
1393
|
+
className
|
|
1394
|
+
}) {
|
|
1395
|
+
const state = getOrganizationGraphDetailState(graph, selectedElement);
|
|
1396
|
+
if (state.state === "empty") {
|
|
1397
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "lg", p: "md", className, children: /* @__PURE__ */ jsx(EmptyState, { icon: IconShare2, title: state.title, description: state.operationalMeaning, py: "md" }) });
|
|
1398
|
+
}
|
|
1399
|
+
if (state.state === "missing") {
|
|
1400
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "lg", p: "md", className, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1401
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
|
|
1402
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1403
|
+
/* @__PURE__ */ jsx(Text, { fw: 800, size: "lg", children: state.title }),
|
|
1404
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: state.operationalMeaning })
|
|
1405
|
+
] }),
|
|
1406
|
+
onClearSelection ? /* @__PURE__ */ jsx(Button, { size: "xs", variant: "subtle", onClick: onClearSelection, children: "Clear" }) : null
|
|
1407
|
+
] }),
|
|
1408
|
+
/* @__PURE__ */ jsx(Badge, { variant: "light", color: "gray", children: state.semanticCategory }),
|
|
1409
|
+
/* @__PURE__ */ jsxs(Paper, { withBorder: true, radius: "md", p: "sm", children: [
|
|
1410
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, c: "dimmed", children: "Selected ID" }),
|
|
1411
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", ff: "monospace", children: state.missingId })
|
|
1412
|
+
] })
|
|
1413
|
+
] }) });
|
|
1414
|
+
}
|
|
1415
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "lg", p: "md", className, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
|
|
1416
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
|
|
1417
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1418
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1419
|
+
/* @__PURE__ */ jsx(Badge, { variant: "light", color: "violet", children: state.semanticCategory }),
|
|
1420
|
+
state.state === "node" ? /* @__PURE__ */ jsx(Badge, { variant: "light", color: state.node.enabled === false ? "gray" : "green", children: state.node.enabled === false ? "Disabled" : "Enabled" }) : null,
|
|
1421
|
+
state.state === "edge" && state.edge.relationshipType ? /* @__PURE__ */ jsx(Badge, { variant: "light", color: "orange", children: titleCase2(state.edge.relationshipType) }) : null
|
|
1422
|
+
] }),
|
|
1423
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", align: "center", children: [
|
|
1424
|
+
state.state === "node" ? /* @__PURE__ */ jsx(
|
|
1425
|
+
SemanticIcon,
|
|
1426
|
+
{
|
|
1427
|
+
token: state.node.icon,
|
|
1428
|
+
fallbackKind: getNodeIconFallbackKind(state.node.kind),
|
|
1429
|
+
size: 18,
|
|
1430
|
+
style: { color: "var(--color-text-subtle)" }
|
|
1431
|
+
}
|
|
1432
|
+
) : null,
|
|
1433
|
+
/* @__PURE__ */ jsx(Text, { fw: 800, size: "lg", style: { minWidth: 0 }, children: state.title })
|
|
1434
|
+
] }),
|
|
1435
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: state.operationalMeaning })
|
|
1436
|
+
] }),
|
|
1437
|
+
onClearSelection ? /* @__PURE__ */ jsx(Button, { size: "xs", variant: "subtle", onClick: onClearSelection, children: "Clear" }) : null
|
|
1438
|
+
] }),
|
|
1439
|
+
/* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 2, md: 4 }, spacing: "sm", children: state.metrics.map((metric) => /* @__PURE__ */ jsx(MetricCard, { metric }, metric.label)) }),
|
|
1440
|
+
expandAroundPanel,
|
|
1441
|
+
supplementalSummary && (supplementalSummary.metrics.length > 0 || supplementalSummary.metadata.length > 0) ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1442
|
+
/* @__PURE__ */ jsx(
|
|
1443
|
+
Divider,
|
|
1444
|
+
{
|
|
1445
|
+
label: /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
|
|
1446
|
+
/* @__PURE__ */ jsx(IconTopologyStar3, { size: 14 }),
|
|
1447
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, children: supplementalSummary.title })
|
|
1448
|
+
] }),
|
|
1449
|
+
labelPosition: "center"
|
|
1450
|
+
}
|
|
1451
|
+
),
|
|
1452
|
+
supplementalSummary.description ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: supplementalSummary.description }) : null,
|
|
1453
|
+
supplementalSummary.metrics.length > 0 ? /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 2, md: 4 }, spacing: "sm", children: supplementalSummary.metrics.map((metric) => /* @__PURE__ */ jsx(MetricCard, { metric }, `supplemental-${metric.label}`)) }) : null,
|
|
1454
|
+
supplementalSummary.metadata.length > 0 ? /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: supplementalSummary.metadata.map((item) => /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
1455
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, c: "dimmed", children: item.label }),
|
|
1456
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: item.value })
|
|
1457
|
+
] }) }, `supplemental-${item.label}`)) }) : null
|
|
1458
|
+
] }) : null,
|
|
1459
|
+
followUpSections && followUpSections.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1460
|
+
/* @__PURE__ */ jsx(
|
|
1461
|
+
Divider,
|
|
1462
|
+
{
|
|
1463
|
+
label: /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
|
|
1464
|
+
/* @__PURE__ */ jsx(IconShare2, { size: 14 }),
|
|
1465
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, children: "Follow-up actions" })
|
|
1466
|
+
] }),
|
|
1467
|
+
labelPosition: "center"
|
|
1468
|
+
}
|
|
1469
|
+
),
|
|
1470
|
+
/* @__PURE__ */ jsx(Stack, { gap: "sm", children: followUpSections.map((section) => /* @__PURE__ */ jsx(FollowUpSectionCard, { section }, section.title)) })
|
|
1471
|
+
] }) : null,
|
|
1472
|
+
/* @__PURE__ */ jsx(
|
|
1473
|
+
Divider,
|
|
1474
|
+
{
|
|
1475
|
+
label: /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
|
|
1476
|
+
/* @__PURE__ */ jsx(IconTopologyStar3, { size: 14 }),
|
|
1477
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, children: "Linked metadata" })
|
|
1478
|
+
] }),
|
|
1479
|
+
labelPosition: "center"
|
|
1480
|
+
}
|
|
1481
|
+
),
|
|
1482
|
+
/* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: state.metadata.map((item) => /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
1483
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, c: "dimmed", children: item.label }),
|
|
1484
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: item.value })
|
|
1485
|
+
] }) }, item.label)) }),
|
|
1486
|
+
state.adjacentKindCounts.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1487
|
+
/* @__PURE__ */ jsx(
|
|
1488
|
+
Divider,
|
|
1489
|
+
{
|
|
1490
|
+
label: /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
|
|
1491
|
+
/* @__PURE__ */ jsx(IconShare2, { size: 14 }),
|
|
1492
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, children: "Nearby kinds" })
|
|
1493
|
+
] }),
|
|
1494
|
+
labelPosition: "center"
|
|
1495
|
+
}
|
|
1496
|
+
),
|
|
1497
|
+
/* @__PURE__ */ jsx(Group, { gap: "xs", children: state.adjacentKindCounts.map((item) => /* @__PURE__ */ jsxs(Badge, { variant: "light", color: getNodeKindColor(item.kind), children: [
|
|
1498
|
+
item.count,
|
|
1499
|
+
" ",
|
|
1500
|
+
item.label
|
|
1501
|
+
] }, item.kind)) })
|
|
1502
|
+
] }) : null,
|
|
1503
|
+
state.state === "edge" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1504
|
+
/* @__PURE__ */ jsx(
|
|
1505
|
+
Divider,
|
|
1506
|
+
{
|
|
1507
|
+
label: /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
|
|
1508
|
+
/* @__PURE__ */ jsx(IconShare2, { size: 14 }),
|
|
1509
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, children: "Endpoints" })
|
|
1510
|
+
] }),
|
|
1511
|
+
labelPosition: "center"
|
|
1512
|
+
}
|
|
1513
|
+
),
|
|
1514
|
+
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
|
|
1515
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
1516
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, c: "dimmed", children: "Source" }),
|
|
1517
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1518
|
+
/* @__PURE__ */ jsx(Text, { fw: 700, children: state.sourceNode?.label ?? state.edge.sourceId }),
|
|
1519
|
+
/* @__PURE__ */ jsx(
|
|
1520
|
+
Badge,
|
|
1521
|
+
{
|
|
1522
|
+
size: "xs",
|
|
1523
|
+
variant: "light",
|
|
1524
|
+
color: state.sourceNode ? getNodeKindColor(state.sourceNode.kind) : "gray",
|
|
1525
|
+
children: state.sourceNode ? getNodeKindLabel2(state.sourceNode.kind) : "unresolved"
|
|
1526
|
+
}
|
|
1527
|
+
)
|
|
1528
|
+
] })
|
|
1529
|
+
] }) }),
|
|
1530
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
1531
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, c: "dimmed", children: "Target" }),
|
|
1532
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
1533
|
+
/* @__PURE__ */ jsx(Text, { fw: 700, children: state.targetNode?.label ?? state.edge.targetId }),
|
|
1534
|
+
/* @__PURE__ */ jsx(
|
|
1535
|
+
Badge,
|
|
1536
|
+
{
|
|
1537
|
+
size: "xs",
|
|
1538
|
+
variant: "light",
|
|
1539
|
+
color: state.targetNode ? getNodeKindColor(state.targetNode.kind) : "gray",
|
|
1540
|
+
children: state.targetNode ? getNodeKindLabel2(state.targetNode.kind) : "unresolved"
|
|
1541
|
+
}
|
|
1542
|
+
)
|
|
1543
|
+
] })
|
|
1544
|
+
] }) })
|
|
1545
|
+
] })
|
|
1546
|
+
] }) : null,
|
|
1547
|
+
/* @__PURE__ */ jsx(
|
|
1548
|
+
Divider,
|
|
1549
|
+
{
|
|
1550
|
+
label: /* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, children: state.state === "edge" ? "Surrounding nodes" : "Direct nodes" }),
|
|
1551
|
+
labelPosition: "center"
|
|
1552
|
+
}
|
|
1553
|
+
),
|
|
1554
|
+
state.relatedNodes.length > 0 ? /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: state.relatedNodes.map((item) => /* @__PURE__ */ jsx(RelatedItemCard, { item, itemType: "node" }, item.id)) }) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No nearby nodes were found in the current graph selection." }),
|
|
1555
|
+
/* @__PURE__ */ jsx(
|
|
1556
|
+
Divider,
|
|
1557
|
+
{
|
|
1558
|
+
label: /* @__PURE__ */ jsx(Text, { size: "xs", tt: "uppercase", fw: 700, children: state.state === "edge" ? "Surrounding edges" : "Direct edges" }),
|
|
1559
|
+
labelPosition: "center"
|
|
1560
|
+
}
|
|
1561
|
+
),
|
|
1562
|
+
state.relatedEdges.length > 0 ? /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: state.relatedEdges.map((item) => /* @__PURE__ */ jsx(RelatedItemCard, { item, itemType: "edge" }, item.id)) }) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No adjacent edges were found for the current selection." })
|
|
1563
|
+
] }) });
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
// src/features/operations/organization-graph/types.ts
|
|
1567
|
+
var ORGANIZATION_GRAPH_NODE_KIND_ORDER = [
|
|
1568
|
+
"organization",
|
|
1569
|
+
"feature",
|
|
1570
|
+
"surface",
|
|
1571
|
+
"entity",
|
|
1572
|
+
"capability",
|
|
1573
|
+
"resource",
|
|
1574
|
+
"knowledge"
|
|
1575
|
+
];
|
|
1576
|
+
var ORGANIZATION_GRAPH_NODE_KIND_LABELS = {
|
|
1577
|
+
organization: "Organization",
|
|
1578
|
+
feature: "Feature",
|
|
1579
|
+
surface: "Surface",
|
|
1580
|
+
entity: "Entity",
|
|
1581
|
+
capability: "Capability",
|
|
1582
|
+
resource: "Resource",
|
|
1583
|
+
knowledge: "Knowledge"
|
|
1584
|
+
};
|
|
1585
|
+
var DEFAULT_ORGANIZATION_GRAPH_FILTERS = {
|
|
1586
|
+
search: "",
|
|
1587
|
+
nodeKinds: [],
|
|
1588
|
+
topologyPresence: "all",
|
|
1589
|
+
environmentStatus: "all",
|
|
1590
|
+
resourceTypes: [],
|
|
1591
|
+
showIntegrations: true,
|
|
1592
|
+
domainFilters: {}
|
|
1593
|
+
};
|
|
1594
|
+
|
|
1595
|
+
// src/features/operations/organization-graph/helpers.ts
|
|
1596
|
+
var TOPLOGY_EDGE_TYPES = /* @__PURE__ */ new Set(["maps_to", "triggers", "uses", "approval"]);
|
|
1597
|
+
function getCommandViewNodes(data) {
|
|
1598
|
+
return [
|
|
1599
|
+
...data.agents,
|
|
1600
|
+
...data.workflows,
|
|
1601
|
+
...data.triggers,
|
|
1602
|
+
...data.integrations,
|
|
1603
|
+
...data.externalResources,
|
|
1604
|
+
...data.humanCheckpoints
|
|
1605
|
+
];
|
|
1606
|
+
}
|
|
1607
|
+
function getCommandViewNodeForGraphNode(node, commandViewData) {
|
|
1608
|
+
if (!commandViewData || node.kind !== "resource" || !node.sourceId) {
|
|
1609
|
+
return null;
|
|
1610
|
+
}
|
|
1611
|
+
return getCommandViewNodes(commandViewData).find((item) => item.resourceId === node.sourceId) ?? null;
|
|
1612
|
+
}
|
|
1613
|
+
function normalizeText(value) {
|
|
1614
|
+
return value.trim().toLowerCase();
|
|
1615
|
+
}
|
|
1616
|
+
function includesQuery(value, query) {
|
|
1617
|
+
if (!value) return false;
|
|
1618
|
+
return normalizeText(value).includes(query);
|
|
1619
|
+
}
|
|
1620
|
+
function titleCase3(value) {
|
|
1621
|
+
return value.replace(/_/g, " ").split(" ").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
1622
|
+
}
|
|
1623
|
+
function isTopologyEdge(edge) {
|
|
1624
|
+
return TOPLOGY_EDGE_TYPES.has(edge.kind) || Boolean(edge.relationshipType);
|
|
1625
|
+
}
|
|
1626
|
+
function isSemanticEdge(edge) {
|
|
1627
|
+
return !isTopologyEdge(edge);
|
|
1628
|
+
}
|
|
1629
|
+
function getIncidentEdges(graph, nodeId2) {
|
|
1630
|
+
return graph.edges.filter((edge) => edge.sourceId === nodeId2 || edge.targetId === nodeId2);
|
|
1631
|
+
}
|
|
1632
|
+
function createOrganizationGraphFilters(partial) {
|
|
1633
|
+
return {
|
|
1634
|
+
search: partial?.search ?? DEFAULT_ORGANIZATION_GRAPH_FILTERS.search,
|
|
1635
|
+
nodeKinds: partial?.nodeKinds ? [...partial.nodeKinds] : [...DEFAULT_ORGANIZATION_GRAPH_FILTERS.nodeKinds],
|
|
1636
|
+
topologyPresence: partial?.topologyPresence ?? DEFAULT_ORGANIZATION_GRAPH_FILTERS.topologyPresence,
|
|
1637
|
+
environmentStatus: partial?.environmentStatus ?? DEFAULT_ORGANIZATION_GRAPH_FILTERS.environmentStatus,
|
|
1638
|
+
resourceTypes: partial?.resourceTypes ? [...partial.resourceTypes] : [...DEFAULT_ORGANIZATION_GRAPH_FILTERS.resourceTypes],
|
|
1639
|
+
showIntegrations: partial?.showIntegrations ?? DEFAULT_ORGANIZATION_GRAPH_FILTERS.showIntegrations,
|
|
1640
|
+
domainFilters: partial?.domainFilters ? { ...partial.domainFilters } : { ...DEFAULT_ORGANIZATION_GRAPH_FILTERS.domainFilters }
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
function normalizeOrganizationGraphSearch(search) {
|
|
1644
|
+
return search.trim().toLowerCase();
|
|
1645
|
+
}
|
|
1646
|
+
function getOrganizationGraphNodePresence(node, incidentEdges) {
|
|
1647
|
+
if (node.kind === "resource") {
|
|
1648
|
+
const relevantEdges = incidentEdges.filter((edge) => edge.kind !== "contains");
|
|
1649
|
+
const hasTopologyEdges2 = relevantEdges.some(isTopologyEdge);
|
|
1650
|
+
const hasSemanticEdges2 = relevantEdges.some(isSemanticEdge);
|
|
1651
|
+
if (hasTopologyEdges2 && hasSemanticEdges2) {
|
|
1652
|
+
return "bridge";
|
|
1653
|
+
}
|
|
1654
|
+
if (hasTopologyEdges2) {
|
|
1655
|
+
return "topology-only";
|
|
1656
|
+
}
|
|
1657
|
+
return "semantic-only";
|
|
1658
|
+
}
|
|
1659
|
+
const hasTopologyEdges = incidentEdges.some(isTopologyEdge);
|
|
1660
|
+
const hasSemanticEdges = incidentEdges.some(isSemanticEdge);
|
|
1661
|
+
if (hasTopologyEdges && hasSemanticEdges) {
|
|
1662
|
+
return "bridge";
|
|
1663
|
+
}
|
|
1664
|
+
if (hasTopologyEdges) {
|
|
1665
|
+
return "topology-only";
|
|
1666
|
+
}
|
|
1667
|
+
return "semantic-only";
|
|
1668
|
+
}
|
|
1669
|
+
function getOrganizationGraphNodePresenceMap(graph) {
|
|
1670
|
+
const presenceByNodeId = /* @__PURE__ */ new Map();
|
|
1671
|
+
for (const node of graph.nodes) {
|
|
1672
|
+
presenceByNodeId.set(node.id, getOrganizationGraphNodePresence(node, getIncidentEdges(graph, node.id)));
|
|
1673
|
+
}
|
|
1674
|
+
return presenceByNodeId;
|
|
1675
|
+
}
|
|
1676
|
+
function getOrganizationGraphNodeKindOptions(kinds = ORGANIZATION_GRAPH_NODE_KIND_ORDER) {
|
|
1677
|
+
return kinds.map((kind) => ({
|
|
1678
|
+
kind,
|
|
1679
|
+
label: ORGANIZATION_GRAPH_NODE_KIND_LABELS[kind]
|
|
1680
|
+
}));
|
|
1681
|
+
}
|
|
1682
|
+
function getOrganizationGraphResourceTypeOptions(resourceTypes = [
|
|
1683
|
+
"workflow",
|
|
1684
|
+
"agent",
|
|
1685
|
+
"trigger",
|
|
1686
|
+
"integration",
|
|
1687
|
+
"external",
|
|
1688
|
+
"human_checkpoint"
|
|
1689
|
+
]) {
|
|
1690
|
+
return resourceTypes.map((resourceType) => ({
|
|
1691
|
+
resourceType,
|
|
1692
|
+
label: titleCase3(resourceType)
|
|
1693
|
+
}));
|
|
1694
|
+
}
|
|
1695
|
+
function getNodeStatus(node, commandViewData) {
|
|
1696
|
+
const commandViewNode = getCommandViewNodeForGraphNode(node, commandViewData);
|
|
1697
|
+
if (commandViewNode) {
|
|
1698
|
+
return commandViewNode.status;
|
|
1699
|
+
}
|
|
1700
|
+
return "status" in node && typeof node.status === "string" ? node.status : void 0;
|
|
1701
|
+
}
|
|
1702
|
+
function getExplicitNodeFacets(node, commandViewData) {
|
|
1703
|
+
const commandViewNode = getCommandViewNodeForGraphNode(node, commandViewData);
|
|
1704
|
+
if (commandViewNode) {
|
|
1705
|
+
return [
|
|
1706
|
+
...commandViewNode.category ? [`category:${commandViewNode.category}`] : [],
|
|
1707
|
+
...commandViewNode.links?.map((link) => link.nodeId) ?? []
|
|
1708
|
+
];
|
|
1709
|
+
}
|
|
1710
|
+
return [];
|
|
1711
|
+
}
|
|
1712
|
+
function getNodeFacets(graph, node, commandViewData) {
|
|
1713
|
+
const explicitFacets = getExplicitNodeFacets(node, commandViewData);
|
|
1714
|
+
if (explicitFacets.length > 0) {
|
|
1715
|
+
return explicitFacets;
|
|
1716
|
+
}
|
|
1717
|
+
if (node.kind === "feature") {
|
|
1718
|
+
return node.sourceId ? [node.sourceId] : [];
|
|
1719
|
+
}
|
|
1720
|
+
if (node.kind !== "resource") {
|
|
1721
|
+
return [];
|
|
1722
|
+
}
|
|
1723
|
+
const facetIds = /* @__PURE__ */ new Set();
|
|
1724
|
+
for (const edge of graph.edges) {
|
|
1725
|
+
if (edge.sourceId !== node.id) {
|
|
1726
|
+
continue;
|
|
1727
|
+
}
|
|
1728
|
+
const targetNode = graph.nodes.find((candidate) => candidate.id === edge.targetId);
|
|
1729
|
+
if (targetNode?.kind === "feature" && targetNode.sourceId) {
|
|
1730
|
+
facetIds.add(targetNode.id);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
return [...facetIds];
|
|
1734
|
+
}
|
|
1735
|
+
function matchesOrganizationGraphNodeSearch(node, search) {
|
|
1736
|
+
const query = normalizeOrganizationGraphSearch(search);
|
|
1737
|
+
if (!query) return true;
|
|
1738
|
+
const nodeStatus = getNodeStatus(node);
|
|
1739
|
+
const nodeFacets = getExplicitNodeFacets(node);
|
|
1740
|
+
return includesQuery(node.id, query) || includesQuery(node.label, query) || includesQuery(node.description, query) || includesQuery(node.sourceId, query) || includesQuery(node.featureId, query) || includesQuery(node.resourceType, query) || includesQuery(nodeStatus, query) || nodeFacets.some((facet) => includesQuery(facet, query)) || includesQuery(node.kind, query);
|
|
1741
|
+
}
|
|
1742
|
+
function matchesOrganizationGraphEdgeSearch(edge, graph, search) {
|
|
1743
|
+
const query = normalizeOrganizationGraphSearch(search);
|
|
1744
|
+
if (!query) return true;
|
|
1745
|
+
const sourceNode = graph.nodes.find((node) => node.id === edge.sourceId);
|
|
1746
|
+
const targetNode = graph.nodes.find((node) => node.id === edge.targetId);
|
|
1747
|
+
return includesQuery(edge.id, query) || includesQuery(edge.label, query) || includesQuery(edge.kind, query) || includesQuery(edge.relationshipType, query) || includesQuery(sourceNode?.label, query) || includesQuery(sourceNode?.sourceId, query) || includesQuery(targetNode?.label, query) || includesQuery(targetNode?.sourceId, query);
|
|
1748
|
+
}
|
|
1749
|
+
function matchesTopologyPresence(nodePresence, topologyPresence) {
|
|
1750
|
+
if (topologyPresence === "all") return true;
|
|
1751
|
+
return nodePresence === topologyPresence;
|
|
1752
|
+
}
|
|
1753
|
+
function hasActiveDomainFilters(domainFilters) {
|
|
1754
|
+
return Object.values(domainFilters).some((filterState) => filterState !== "neutral");
|
|
1755
|
+
}
|
|
1756
|
+
function matchesDomainFilters(graph, node, domainFilters, commandViewData) {
|
|
1757
|
+
if (!hasActiveDomainFilters(domainFilters)) {
|
|
1758
|
+
return true;
|
|
1759
|
+
}
|
|
1760
|
+
const includedDomains = Object.entries(domainFilters).filter(([, filterState]) => filterState === "include").map(([domainId]) => domainId);
|
|
1761
|
+
const excludedDomains = Object.entries(domainFilters).filter(([, filterState]) => filterState === "exclude").map(([domainId]) => domainId);
|
|
1762
|
+
const nodeFacets = getNodeFacets(graph, node, commandViewData);
|
|
1763
|
+
if (excludedDomains.length > 0 && nodeFacets.some((domainId) => excludedDomains.includes(domainId))) {
|
|
1764
|
+
return false;
|
|
1765
|
+
}
|
|
1766
|
+
if (includedDomains.length > 0 && !nodeFacets.some((domainId) => includedDomains.includes(domainId))) {
|
|
1767
|
+
return false;
|
|
1768
|
+
}
|
|
1769
|
+
return true;
|
|
1770
|
+
}
|
|
1771
|
+
function matchesEnvironmentStatus(node, environmentStatus, commandViewData) {
|
|
1772
|
+
if (environmentStatus === "all") {
|
|
1773
|
+
return true;
|
|
1774
|
+
}
|
|
1775
|
+
const nodeStatus = getNodeStatus(node, commandViewData);
|
|
1776
|
+
if (!nodeStatus) {
|
|
1777
|
+
return true;
|
|
1778
|
+
}
|
|
1779
|
+
return nodeStatus === environmentStatus;
|
|
1780
|
+
}
|
|
1781
|
+
function matchesResourceTypeFilters(node, resourceTypes) {
|
|
1782
|
+
if (node.kind !== "resource" || resourceTypes.length === 0) {
|
|
1783
|
+
return true;
|
|
1784
|
+
}
|
|
1785
|
+
return Boolean(node.resourceType && resourceTypes.includes(node.resourceType));
|
|
1786
|
+
}
|
|
1787
|
+
function matchesIntegrationVisibility(node, showIntegrations) {
|
|
1788
|
+
if (showIntegrations) {
|
|
1789
|
+
return true;
|
|
1790
|
+
}
|
|
1791
|
+
return !(node.kind === "resource" && node.resourceType === "integration");
|
|
1792
|
+
}
|
|
1793
|
+
function filterOrganizationGraph(graph, filters, options) {
|
|
1794
|
+
const normalizedFilters = createOrganizationGraphFilters(filters);
|
|
1795
|
+
const normalizedSearch = normalizeOrganizationGraphSearch(normalizedFilters.search);
|
|
1796
|
+
const nodePresenceMap = getOrganizationGraphNodePresenceMap(graph);
|
|
1797
|
+
const selectedKinds = new Set(normalizedFilters.nodeKinds);
|
|
1798
|
+
const commandViewData = options?.commandViewData;
|
|
1799
|
+
const visibleNodeIds = /* @__PURE__ */ new Set();
|
|
1800
|
+
for (const node of graph.nodes) {
|
|
1801
|
+
if (selectedKinds.size > 0 && !selectedKinds.has(node.kind)) {
|
|
1802
|
+
continue;
|
|
1803
|
+
}
|
|
1804
|
+
if (!matchesIntegrationVisibility(node, normalizedFilters.showIntegrations)) {
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1807
|
+
if (!matchesResourceTypeFilters(node, normalizedFilters.resourceTypes)) {
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1810
|
+
if (!matchesEnvironmentStatus(node, normalizedFilters.environmentStatus, commandViewData)) {
|
|
1811
|
+
continue;
|
|
1812
|
+
}
|
|
1813
|
+
if (!matchesDomainFilters(graph, node, normalizedFilters.domainFilters, commandViewData)) {
|
|
1814
|
+
continue;
|
|
1815
|
+
}
|
|
1816
|
+
const presence = nodePresenceMap.get(node.id) ?? "semantic-only";
|
|
1817
|
+
if (!matchesTopologyPresence(presence, normalizedFilters.topologyPresence)) {
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1820
|
+
if (normalizedSearch && !matchesOrganizationGraphNodeSearch(node, normalizedSearch)) {
|
|
1821
|
+
const incidentEdges = getIncidentEdges(graph, node.id);
|
|
1822
|
+
const edgeMatch = incidentEdges.some((edge) => matchesOrganizationGraphEdgeSearch(edge, graph, normalizedSearch));
|
|
1823
|
+
if (!edgeMatch) {
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
visibleNodeIds.add(node.id);
|
|
1828
|
+
}
|
|
1829
|
+
const visibleEdges = graph.edges.filter((edge) => {
|
|
1830
|
+
const sourceVisible = visibleNodeIds.has(edge.sourceId);
|
|
1831
|
+
const targetVisible = visibleNodeIds.has(edge.targetId);
|
|
1832
|
+
if (!sourceVisible || !targetVisible) {
|
|
1833
|
+
return false;
|
|
1834
|
+
}
|
|
1835
|
+
if (normalizedSearch && !matchesOrganizationGraphEdgeSearch(edge, graph, normalizedSearch)) {
|
|
1836
|
+
const sourceNode = graph.nodes.find((node) => node.id === edge.sourceId);
|
|
1837
|
+
const targetNode = graph.nodes.find((node) => node.id === edge.targetId);
|
|
1838
|
+
const nodeSearchMatch = Boolean(sourceNode && matchesOrganizationGraphNodeSearch(sourceNode, normalizedSearch)) || Boolean(targetNode && matchesOrganizationGraphNodeSearch(targetNode, normalizedSearch));
|
|
1839
|
+
if (!nodeSearchMatch) {
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
return true;
|
|
1844
|
+
});
|
|
1845
|
+
return {
|
|
1846
|
+
...graph,
|
|
1847
|
+
nodes: graph.nodes.filter((node) => visibleNodeIds.has(node.id)),
|
|
1848
|
+
edges: visibleEdges
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
function getCommandViewResourceCategory(node, commandViewData) {
|
|
1852
|
+
return getCommandViewNodeForGraphNode(node, commandViewData)?.category ?? null;
|
|
1853
|
+
}
|
|
1854
|
+
function getConnectedHiddenResourceIds(graph, nodeId2, hiddenIds) {
|
|
1855
|
+
if (!nodeId2) {
|
|
1856
|
+
return /* @__PURE__ */ new Set();
|
|
1857
|
+
}
|
|
1858
|
+
const nodesById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
1859
|
+
const connectedIds = /* @__PURE__ */ new Set();
|
|
1860
|
+
for (const edge of graph.edges) {
|
|
1861
|
+
const neighborId = edge.sourceId === nodeId2 ? edge.targetId : edge.targetId === nodeId2 ? edge.sourceId : null;
|
|
1862
|
+
if (!neighborId || !hiddenIds.has(neighborId)) {
|
|
1863
|
+
continue;
|
|
1864
|
+
}
|
|
1865
|
+
if (nodesById.get(neighborId)?.kind === "resource") {
|
|
1866
|
+
connectedIds.add(neighborId);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
return connectedIds;
|
|
1870
|
+
}
|
|
1871
|
+
function getCommandViewVisibilityProjection({
|
|
1872
|
+
graph,
|
|
1873
|
+
commandViewData,
|
|
1874
|
+
resourcesHidden,
|
|
1875
|
+
diagnosticsHidden,
|
|
1876
|
+
diagnosticCategories,
|
|
1877
|
+
revealedIds = /* @__PURE__ */ new Set(),
|
|
1878
|
+
mode
|
|
1879
|
+
}) {
|
|
1880
|
+
const hiddenIds = /* @__PURE__ */ new Set();
|
|
1881
|
+
const hiddenEdgeIds = /* @__PURE__ */ new Set();
|
|
1882
|
+
const diagnosticCategorySet = new Set(diagnosticCategories);
|
|
1883
|
+
let totalResourceCount = 0;
|
|
1884
|
+
let hiddenDiagnosticResourceCount = 0;
|
|
1885
|
+
if (mode === "trace" || mode === "impact") {
|
|
1886
|
+
const resourceCount = graph.nodes.filter((node) => node.kind === "resource").length;
|
|
1887
|
+
return {
|
|
1888
|
+
hiddenIds,
|
|
1889
|
+
hiddenEdgeIds,
|
|
1890
|
+
totalResourceCount: resourceCount,
|
|
1891
|
+
visibleResourceCount: resourceCount,
|
|
1892
|
+
hiddenResourceCount: 0,
|
|
1893
|
+
hiddenDiagnosticResourceCount: 0
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
for (const node of graph.nodes) {
|
|
1897
|
+
if (node.kind !== "resource") {
|
|
1898
|
+
continue;
|
|
1899
|
+
}
|
|
1900
|
+
totalResourceCount += 1;
|
|
1901
|
+
const category = getCommandViewResourceCategory(node, commandViewData);
|
|
1902
|
+
const isDiagnostic = Boolean(category && diagnosticCategorySet.has(category));
|
|
1903
|
+
if (isDiagnostic && diagnosticsHidden) {
|
|
1904
|
+
hiddenDiagnosticResourceCount += 1;
|
|
1905
|
+
}
|
|
1906
|
+
if (revealedIds.has(node.id)) {
|
|
1907
|
+
continue;
|
|
1908
|
+
}
|
|
1909
|
+
if (resourcesHidden || diagnosticsHidden && isDiagnostic) {
|
|
1910
|
+
hiddenIds.add(node.id);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
for (const edge of graph.edges) {
|
|
1914
|
+
if (hiddenIds.has(edge.sourceId) || hiddenIds.has(edge.targetId)) {
|
|
1915
|
+
hiddenEdgeIds.add(edge.id);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
const hiddenResourceCount = hiddenIds.size;
|
|
1919
|
+
return {
|
|
1920
|
+
hiddenIds,
|
|
1921
|
+
hiddenEdgeIds,
|
|
1922
|
+
totalResourceCount,
|
|
1923
|
+
visibleResourceCount: totalResourceCount - hiddenResourceCount,
|
|
1924
|
+
hiddenResourceCount,
|
|
1925
|
+
hiddenDiagnosticResourceCount
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
function isOrganizationGraphFilterPristine(filters) {
|
|
1929
|
+
return normalizeOrganizationGraphSearch(filters.search) === "" && filters.nodeKinds.length === 0 && filters.topologyPresence === DEFAULT_ORGANIZATION_GRAPH_FILTERS.topologyPresence && filters.environmentStatus === DEFAULT_ORGANIZATION_GRAPH_FILTERS.environmentStatus && filters.resourceTypes.length === 0 && filters.showIntegrations === DEFAULT_ORGANIZATION_GRAPH_FILTERS.showIntegrations && !hasActiveDomainFilters(filters.domainFilters);
|
|
1930
|
+
}
|
|
1931
|
+
var TOPOLOGY_PRESENCE_OPTIONS = [
|
|
1932
|
+
{ label: "All", value: "all" },
|
|
1933
|
+
{ label: "Semantic only", value: "semantic-only" },
|
|
1934
|
+
{ label: "Topology only", value: "topology-only" }
|
|
1935
|
+
];
|
|
1936
|
+
var ENVIRONMENT_STATUS_OPTIONS = [
|
|
1937
|
+
{ label: "All", value: "all" },
|
|
1938
|
+
{ label: "Prod", value: "prod" },
|
|
1939
|
+
{ label: "Dev", value: "dev" }
|
|
1940
|
+
];
|
|
1941
|
+
function OrganizationGraphFilterToolbar({
|
|
1942
|
+
value,
|
|
1943
|
+
onChange,
|
|
1944
|
+
availableKinds,
|
|
1945
|
+
disabled = false,
|
|
1946
|
+
searchPlaceholder = "Search nodes, relationships, or IDs",
|
|
1947
|
+
kindLabel = "Node kinds",
|
|
1948
|
+
resourceTypeLabel = "Resource types",
|
|
1949
|
+
topologyLabel = "Relationship presence",
|
|
1950
|
+
environmentLabel = "Environment",
|
|
1951
|
+
showIntegrationsLabel = "Show integrations",
|
|
1952
|
+
resetLabel = "Reset filters",
|
|
1953
|
+
resetValue = createOrganizationGraphFilters()
|
|
1954
|
+
}) {
|
|
1955
|
+
const kindOptions = getOrganizationGraphNodeKindOptions(availableKinds);
|
|
1956
|
+
const resourceTypeOptions = getOrganizationGraphResourceTypeOptions();
|
|
1957
|
+
const canReset = !isOrganizationGraphFilterPristine(value);
|
|
1958
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
1959
|
+
/* @__PURE__ */ jsx(
|
|
1960
|
+
TextInput,
|
|
1961
|
+
{
|
|
1962
|
+
label: "Search",
|
|
1963
|
+
placeholder: searchPlaceholder,
|
|
1964
|
+
value: value.search,
|
|
1965
|
+
onChange: (event) => onChange({
|
|
1966
|
+
...value,
|
|
1967
|
+
search: event.currentTarget.value
|
|
1968
|
+
}),
|
|
1969
|
+
leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 14 }),
|
|
1970
|
+
disabled
|
|
1971
|
+
}
|
|
1972
|
+
),
|
|
1973
|
+
/* @__PURE__ */ jsx(
|
|
1974
|
+
MultiSelect,
|
|
1975
|
+
{
|
|
1976
|
+
label: kindLabel,
|
|
1977
|
+
placeholder: "All kinds",
|
|
1978
|
+
data: kindOptions.map((option) => ({ value: option.kind, label: option.label })),
|
|
1979
|
+
value: value.nodeKinds,
|
|
1980
|
+
onChange: (nodeKinds) => onChange({
|
|
1981
|
+
...value,
|
|
1982
|
+
nodeKinds
|
|
1983
|
+
}),
|
|
1984
|
+
searchable: true,
|
|
1985
|
+
clearable: true,
|
|
1986
|
+
disabled
|
|
1987
|
+
}
|
|
1988
|
+
),
|
|
1989
|
+
/* @__PURE__ */ jsx(
|
|
1990
|
+
MultiSelect,
|
|
1991
|
+
{
|
|
1992
|
+
label: resourceTypeLabel,
|
|
1993
|
+
placeholder: "All resource types",
|
|
1994
|
+
data: resourceTypeOptions.map((option) => ({ value: option.resourceType, label: option.label })),
|
|
1995
|
+
value: value.resourceTypes,
|
|
1996
|
+
onChange: (resourceTypes) => onChange({
|
|
1997
|
+
...value,
|
|
1998
|
+
resourceTypes
|
|
1999
|
+
}),
|
|
2000
|
+
searchable: true,
|
|
2001
|
+
clearable: true,
|
|
2002
|
+
disabled
|
|
2003
|
+
}
|
|
2004
|
+
),
|
|
2005
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
|
|
2006
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: topologyLabel }),
|
|
2007
|
+
/* @__PURE__ */ jsx(
|
|
2008
|
+
SegmentedControl,
|
|
2009
|
+
{
|
|
2010
|
+
value: value.topologyPresence,
|
|
2011
|
+
onChange: (topologyPresence) => onChange({
|
|
2012
|
+
...value,
|
|
2013
|
+
topologyPresence
|
|
2014
|
+
}),
|
|
2015
|
+
data: TOPOLOGY_PRESENCE_OPTIONS,
|
|
2016
|
+
size: "sm",
|
|
2017
|
+
fullWidth: true,
|
|
2018
|
+
disabled
|
|
2019
|
+
}
|
|
2020
|
+
)
|
|
2021
|
+
] }),
|
|
2022
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
|
|
2023
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: environmentLabel }),
|
|
2024
|
+
/* @__PURE__ */ jsx(
|
|
2025
|
+
SegmentedControl,
|
|
2026
|
+
{
|
|
2027
|
+
value: value.environmentStatus,
|
|
2028
|
+
onChange: (environmentStatus) => onChange({
|
|
2029
|
+
...value,
|
|
2030
|
+
environmentStatus
|
|
2031
|
+
}),
|
|
2032
|
+
data: ENVIRONMENT_STATUS_OPTIONS,
|
|
2033
|
+
size: "sm",
|
|
2034
|
+
fullWidth: true,
|
|
2035
|
+
disabled
|
|
2036
|
+
}
|
|
2037
|
+
)
|
|
2038
|
+
] }),
|
|
2039
|
+
/* @__PURE__ */ jsx(
|
|
2040
|
+
Switch,
|
|
2041
|
+
{
|
|
2042
|
+
label: showIntegrationsLabel,
|
|
2043
|
+
description: "Hide integration resources and their relationship edges.",
|
|
2044
|
+
checked: value.showIntegrations,
|
|
2045
|
+
onChange: (event) => onChange({
|
|
2046
|
+
...value,
|
|
2047
|
+
showIntegrations: event.currentTarget.checked
|
|
2048
|
+
}),
|
|
2049
|
+
size: "sm",
|
|
2050
|
+
disabled
|
|
2051
|
+
}
|
|
2052
|
+
),
|
|
2053
|
+
/* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(
|
|
2054
|
+
Button,
|
|
2055
|
+
{
|
|
2056
|
+
variant: "subtle",
|
|
2057
|
+
leftSection: /* @__PURE__ */ jsx(IconRefresh, { size: 14 }),
|
|
2058
|
+
onClick: () => onChange(resetValue),
|
|
2059
|
+
disabled: disabled || !canReset,
|
|
2060
|
+
children: resetLabel
|
|
2061
|
+
}
|
|
2062
|
+
) })
|
|
2063
|
+
] });
|
|
2064
|
+
}
|
|
2065
|
+
var useOrganizationGraphFiltersStore = create((set) => ({
|
|
2066
|
+
filtersByScope: {},
|
|
2067
|
+
initializeScope: (scope, initialFilters) => set((state) => {
|
|
2068
|
+
if (state.filtersByScope[scope]) {
|
|
2069
|
+
return state;
|
|
2070
|
+
}
|
|
2071
|
+
return {
|
|
2072
|
+
filtersByScope: {
|
|
2073
|
+
...state.filtersByScope,
|
|
2074
|
+
[scope]: createOrganizationGraphFilters(initialFilters)
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
2077
|
+
}),
|
|
2078
|
+
updateScope: (scope, next) => set((state) => ({
|
|
2079
|
+
filtersByScope: {
|
|
2080
|
+
...state.filtersByScope,
|
|
2081
|
+
[scope]: createOrganizationGraphFilters({
|
|
2082
|
+
...state.filtersByScope[scope],
|
|
2083
|
+
...next
|
|
2084
|
+
})
|
|
2085
|
+
}
|
|
2086
|
+
})),
|
|
2087
|
+
resetScope: (scope, initialFilters) => set((state) => ({
|
|
2088
|
+
filtersByScope: {
|
|
2089
|
+
...state.filtersByScope,
|
|
2090
|
+
[scope]: createOrganizationGraphFilters(initialFilters)
|
|
2091
|
+
}
|
|
2092
|
+
}))
|
|
2093
|
+
}));
|
|
2094
|
+
function getOrganizationGraphFilterScope(initialFilters) {
|
|
2095
|
+
return JSON.stringify(createOrganizationGraphFilters(initialFilters));
|
|
2096
|
+
}
|
|
2097
|
+
function useOrganizationGraphFilters(initialFilters) {
|
|
2098
|
+
const scopeRef = useRef(getOrganizationGraphFilterScope(initialFilters));
|
|
2099
|
+
const fallbackFiltersRef = useRef(createOrganizationGraphFilters(initialFilters));
|
|
2100
|
+
const filters = useOrganizationGraphFiltersStore(
|
|
2101
|
+
(state) => state.filtersByScope[scopeRef.current] ?? fallbackFiltersRef.current
|
|
2102
|
+
);
|
|
2103
|
+
const initializeScope = useOrganizationGraphFiltersStore((state) => state.initializeScope);
|
|
2104
|
+
const updateScope = useOrganizationGraphFiltersStore((state) => state.updateScope);
|
|
2105
|
+
const resetScope = useOrganizationGraphFiltersStore((state) => state.resetScope);
|
|
2106
|
+
useEffect(() => {
|
|
2107
|
+
initializeScope(scopeRef.current, initialFilters);
|
|
2108
|
+
}, [initialFilters, initializeScope]);
|
|
2109
|
+
const updateFilters = (next) => {
|
|
2110
|
+
updateScope(scopeRef.current, next);
|
|
2111
|
+
};
|
|
2112
|
+
const setSearch = (search) => {
|
|
2113
|
+
updateFilters({ search });
|
|
2114
|
+
};
|
|
2115
|
+
const setNodeKinds = (nodeKinds) => {
|
|
2116
|
+
updateFilters({ nodeKinds });
|
|
2117
|
+
};
|
|
2118
|
+
const setTopologyPresence = (topologyPresence) => {
|
|
2119
|
+
updateFilters({ topologyPresence });
|
|
2120
|
+
};
|
|
2121
|
+
const resetFilters = () => {
|
|
2122
|
+
resetScope(scopeRef.current, initialFilters);
|
|
2123
|
+
};
|
|
2124
|
+
return {
|
|
2125
|
+
filters,
|
|
2126
|
+
setSearch,
|
|
2127
|
+
setNodeKinds,
|
|
2128
|
+
setTopologyPresence,
|
|
2129
|
+
resetFilters,
|
|
2130
|
+
updateFilters
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
// src/features/operations/organization-graph/lenses.ts
|
|
2135
|
+
var COMMAND_VIEW_NODE_KINDS = [
|
|
2136
|
+
"organization",
|
|
2137
|
+
"feature",
|
|
2138
|
+
"surface",
|
|
2139
|
+
"entity",
|
|
2140
|
+
"capability",
|
|
2141
|
+
"resource"
|
|
2142
|
+
];
|
|
2143
|
+
function getOrganizationGraphLensConfig(lens) {
|
|
2144
|
+
if (lens === "command-view") {
|
|
2145
|
+
return {
|
|
2146
|
+
title: "Command View",
|
|
2147
|
+
caption: "Operations lens backed by the shared organization graph. This preset focuses the graph on bridged runtime resources and their operational relationships.",
|
|
2148
|
+
initialMode: "map",
|
|
2149
|
+
initialFilters: {
|
|
2150
|
+
nodeKinds: COMMAND_VIEW_NODE_KINDS,
|
|
2151
|
+
topologyPresence: "all"
|
|
2152
|
+
},
|
|
2153
|
+
filterSummary: "Command View lens keeps the organization structure visible while resource visibility is controlled progressively."
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
return {
|
|
2157
|
+
title: "Organization Graph",
|
|
2158
|
+
caption: "Cytoscape-based shared graph surface built from the organization model and bridged Command View topology.",
|
|
2159
|
+
initialMode: "map",
|
|
2160
|
+
filterSummary: "Filter by node kind, semantic-vs-topology presence, and free-text search across nodes and relationships."
|
|
2161
|
+
};
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
// src/features/operations/organization-graph/commandViewOperationalSummary.ts
|
|
2165
|
+
function toPercentage(numerator, denominator) {
|
|
2166
|
+
if (denominator <= 0) return null;
|
|
2167
|
+
return numerator / denominator * 100;
|
|
2168
|
+
}
|
|
2169
|
+
function formatPercent(value) {
|
|
2170
|
+
return value == null ? "N/A" : `${Math.round(value)}%`;
|
|
2171
|
+
}
|
|
2172
|
+
function formatTimestamp(value) {
|
|
2173
|
+
if (!value) return "N/A";
|
|
2174
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
2175
|
+
month: "short",
|
|
2176
|
+
day: "numeric",
|
|
2177
|
+
hour: "numeric",
|
|
2178
|
+
minute: "2-digit"
|
|
2179
|
+
}).format(new Date(value));
|
|
2180
|
+
}
|
|
2181
|
+
function getCommandViewNodes2(data) {
|
|
2182
|
+
return [
|
|
2183
|
+
...data.agents,
|
|
2184
|
+
...data.workflows,
|
|
2185
|
+
...data.triggers,
|
|
2186
|
+
...data.integrations,
|
|
2187
|
+
...data.externalResources,
|
|
2188
|
+
...data.humanCheckpoints
|
|
2189
|
+
];
|
|
2190
|
+
}
|
|
2191
|
+
function getNodeByResourceId(data, resourceId) {
|
|
2192
|
+
return getCommandViewNodes2(data).find((node) => node.resourceId === resourceId) ?? null;
|
|
2193
|
+
}
|
|
2194
|
+
function getCommandViewOperationalOverview(data, stats) {
|
|
2195
|
+
if (!stats) {
|
|
2196
|
+
return null;
|
|
2197
|
+
}
|
|
2198
|
+
const resourceEntries = Object.entries(stats.resources);
|
|
2199
|
+
const humanEntries = Object.entries(stats.humanCheckpoints);
|
|
2200
|
+
const totalRuns = resourceEntries.reduce((sum, [, item]) => sum + item.totalRuns, 0);
|
|
2201
|
+
const successCount = resourceEntries.reduce((sum, [, item]) => sum + item.successCount, 0);
|
|
2202
|
+
const failureCount = resourceEntries.reduce((sum, [, item]) => sum + item.failureCount, 0);
|
|
2203
|
+
const warningCount = resourceEntries.reduce((sum, [, item]) => sum + item.warningCount, 0);
|
|
2204
|
+
const pendingApprovals = humanEntries.reduce((sum, [, item]) => sum + item.pendingCount, 0);
|
|
2205
|
+
const activeHumanCheckpoints = humanEntries.filter(([, item]) => item.pendingCount > 0).length;
|
|
2206
|
+
const topFailingResources = resourceEntries.map(([resourceId, item]) => ({
|
|
2207
|
+
id: resourceId,
|
|
2208
|
+
label: data ? getNodeByResourceId(data, resourceId)?.name ?? resourceId : resourceId,
|
|
2209
|
+
failureCount: item.failureCount,
|
|
2210
|
+
warningCount: item.warningCount
|
|
2211
|
+
})).filter((item) => item.failureCount > 0 || item.warningCount > 0).sort((left, right) => {
|
|
2212
|
+
if (right.failureCount !== left.failureCount) {
|
|
2213
|
+
return right.failureCount - left.failureCount;
|
|
2214
|
+
}
|
|
2215
|
+
if (right.warningCount !== left.warningCount) {
|
|
2216
|
+
return right.warningCount - left.warningCount;
|
|
2217
|
+
}
|
|
2218
|
+
return left.label.localeCompare(right.label);
|
|
2219
|
+
}).slice(0, 3);
|
|
2220
|
+
return {
|
|
2221
|
+
totalRuns,
|
|
2222
|
+
successCount,
|
|
2223
|
+
failureCount,
|
|
2224
|
+
warningCount,
|
|
2225
|
+
successRate: toPercentage(successCount, totalRuns),
|
|
2226
|
+
trackedResources: resourceEntries.length,
|
|
2227
|
+
pendingApprovals,
|
|
2228
|
+
activeHumanCheckpoints,
|
|
2229
|
+
topFailingResources,
|
|
2230
|
+
timeRange: stats.timeRange,
|
|
2231
|
+
generatedAt: stats.generatedAt
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
function buildResourceSelectionSummary(node, stats) {
|
|
2235
|
+
const successRate = toPercentage(stats.successCount, stats.totalRuns);
|
|
2236
|
+
const metadata = [
|
|
2237
|
+
{ label: "Operational Type", value: node.type },
|
|
2238
|
+
{ label: "Environment", value: node.status.toUpperCase() },
|
|
2239
|
+
{ label: "Version", value: node.version },
|
|
2240
|
+
{ label: "Origin", value: node.origin ? node.origin : "local" }
|
|
2241
|
+
];
|
|
2242
|
+
if (node.category) {
|
|
2243
|
+
metadata.push({ label: "Category", value: node.category });
|
|
2244
|
+
}
|
|
2245
|
+
if (node.links?.length) {
|
|
2246
|
+
metadata.push({ label: "Links", value: node.links.map((link) => `${link.kind} ${link.nodeId}`).join(", ") });
|
|
2247
|
+
}
|
|
2248
|
+
if (node.type === "agent") {
|
|
2249
|
+
metadata.push({ label: "Model", value: `${node.modelProvider}/${node.modelId}` });
|
|
2250
|
+
metadata.push({ label: "Tools", value: String(node.toolCount) });
|
|
2251
|
+
metadata.push({ label: "Memory", value: node.hasMemory ? "Enabled" : "Disabled" });
|
|
2252
|
+
}
|
|
2253
|
+
if (node.type === "workflow") {
|
|
2254
|
+
metadata.push({ label: "Entry Point", value: node.entryPoint });
|
|
2255
|
+
metadata.push({ label: "Steps", value: String(node.stepCount) });
|
|
2256
|
+
}
|
|
2257
|
+
if (node.type === "integration") {
|
|
2258
|
+
metadata.push({ label: "Provider", value: node.provider });
|
|
2259
|
+
metadata.push({ label: "Credential", value: node.credentialName });
|
|
2260
|
+
}
|
|
2261
|
+
if (node.type === "trigger") {
|
|
2262
|
+
metadata.push({ label: "Trigger Type", value: node.triggerType });
|
|
2263
|
+
}
|
|
2264
|
+
if (node.type === "external") {
|
|
2265
|
+
metadata.push({ label: "Platform", value: node.platform });
|
|
2266
|
+
}
|
|
2267
|
+
return {
|
|
2268
|
+
title: "Operational Summary",
|
|
2269
|
+
description: "Execution telemetry for this resource inside the Command View lens.",
|
|
2270
|
+
metrics: [
|
|
2271
|
+
{ label: "Runs", value: String(stats.totalRuns), hint: "Total tracked executions in the selected time range" },
|
|
2272
|
+
{ label: "Success rate", value: formatPercent(successRate), hint: "Completed and warning runs as a percentage of all runs" },
|
|
2273
|
+
{ label: "Failures", value: String(stats.failureCount), hint: "Runs that ended in a failed state" },
|
|
2274
|
+
{ label: "Warnings", value: String(stats.warningCount), hint: "Completed runs that surfaced warnings" }
|
|
2275
|
+
],
|
|
2276
|
+
metadata: [...metadata, { label: "Last Run", value: formatTimestamp(stats.lastRunAt) }]
|
|
2277
|
+
};
|
|
2278
|
+
}
|
|
2279
|
+
function buildHumanSelectionSummary(node, stats) {
|
|
2280
|
+
const metadata = [
|
|
2281
|
+
{ label: "Operational Type", value: "human checkpoint" },
|
|
2282
|
+
{ label: "Environment", value: node.status.toUpperCase() },
|
|
2283
|
+
{ label: "Version", value: node.version },
|
|
2284
|
+
{ label: "Origin", value: node.origin ? node.origin : "local" }
|
|
2285
|
+
];
|
|
2286
|
+
if (node.category) {
|
|
2287
|
+
metadata.push({ label: "Category", value: node.category });
|
|
2288
|
+
}
|
|
2289
|
+
if (node.links?.length) {
|
|
2290
|
+
metadata.push({ label: "Links", value: node.links.map((link) => `${link.kind} ${link.nodeId}`).join(", ") });
|
|
2291
|
+
}
|
|
2292
|
+
return {
|
|
2293
|
+
title: "Approval Queue Summary",
|
|
2294
|
+
description: "Human checkpoint activity for the current Command View lens.",
|
|
2295
|
+
metrics: [
|
|
2296
|
+
{ label: "Pending", value: String(stats.pendingCount), hint: "Tasks currently waiting on a decision" },
|
|
2297
|
+
{ label: "Completed", value: String(stats.completedCount), hint: "Tasks completed in the selected time range" },
|
|
2298
|
+
{ label: "Expired", value: String(stats.expiredCount), hint: "Tasks that expired before resolution" }
|
|
2299
|
+
],
|
|
2300
|
+
metadata: [...metadata, { label: "Last Decision", value: formatTimestamp(stats.lastDecisionAt) }]
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
function getCommandViewSelectionOperationalSummary(data, stats, selectedElement) {
|
|
2304
|
+
if (!data || !stats || !selectedElement || selectedElement.type !== "node") {
|
|
2305
|
+
return null;
|
|
2306
|
+
}
|
|
2307
|
+
if (!selectedElement.id.startsWith("resource:")) {
|
|
2308
|
+
return null;
|
|
2309
|
+
}
|
|
2310
|
+
const resourceId = selectedElement.id.slice("resource:".length);
|
|
2311
|
+
const node = getNodeByResourceId(data, resourceId);
|
|
2312
|
+
if (!node) {
|
|
2313
|
+
return null;
|
|
2314
|
+
}
|
|
2315
|
+
if (node.type === "human") {
|
|
2316
|
+
const checkpointStats = stats.humanCheckpoints[resourceId];
|
|
2317
|
+
return checkpointStats ? buildHumanSelectionSummary(node, checkpointStats) : null;
|
|
2318
|
+
}
|
|
2319
|
+
const resourceStats = stats.resources[resourceId];
|
|
2320
|
+
return resourceStats ? buildResourceSelectionSummary(node, resourceStats) : null;
|
|
2321
|
+
}
|
|
2322
|
+
function formatRelativeTime(date) {
|
|
2323
|
+
if (!date) return "N/A";
|
|
2324
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
2325
|
+
return formatDistanceToNow(dateObj, { addSuffix: true });
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
// src/hooks/operations/command-view/utils/mergeStatsWithTopology.ts
|
|
2329
|
+
function mergeStatsWithTopology(topology, stats) {
|
|
2330
|
+
return {
|
|
2331
|
+
...topology,
|
|
2332
|
+
agents: topology.agents.map((agent) => ({
|
|
2333
|
+
...agent,
|
|
2334
|
+
stats: stats.resources[agent.resourceId] || null
|
|
2335
|
+
})),
|
|
2336
|
+
workflows: topology.workflows.map((workflow) => ({
|
|
2337
|
+
...workflow,
|
|
2338
|
+
stats: stats.resources[workflow.resourceId] || null
|
|
2339
|
+
})),
|
|
2340
|
+
humanCheckpoints: topology.humanCheckpoints.map((checkpoint) => ({
|
|
2341
|
+
...checkpoint,
|
|
2342
|
+
stats: stats.humanCheckpoints[checkpoint.resourceId] || null
|
|
2343
|
+
}))
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
// src/features/operations/organization-graph/commandViewDrillDown.ts
|
|
2348
|
+
function titleCase4(value) {
|
|
2349
|
+
return value.replace(/[-_.]+/g, " ").replace(/\s+/g, " ").trim().split(" ").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
2350
|
+
}
|
|
2351
|
+
function getResourceHref(resourceType, resourceId) {
|
|
2352
|
+
if (resourceType === "agent") {
|
|
2353
|
+
return `/operations/resources/agent/${resourceId}`;
|
|
2354
|
+
}
|
|
2355
|
+
if (resourceType === "workflow") {
|
|
2356
|
+
return `/operations/resources/workflow/${resourceId}`;
|
|
2357
|
+
}
|
|
2358
|
+
if (resourceType === "human_checkpoint") {
|
|
2359
|
+
return `/operations/command-queue?checkpoint=${resourceId}`;
|
|
2360
|
+
}
|
|
2361
|
+
return null;
|
|
2362
|
+
}
|
|
2363
|
+
function getExecutionBadgeColor(status) {
|
|
2364
|
+
switch (status) {
|
|
2365
|
+
case "failed":
|
|
2366
|
+
return "red";
|
|
2367
|
+
case "warning":
|
|
2368
|
+
return "yellow";
|
|
2369
|
+
case "running":
|
|
2370
|
+
return "blue";
|
|
2371
|
+
case "completed":
|
|
2372
|
+
return "green";
|
|
2373
|
+
case "pending":
|
|
2374
|
+
default:
|
|
2375
|
+
return "gray";
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
function getTaskBadgeColor(status) {
|
|
2379
|
+
switch (status) {
|
|
2380
|
+
case "pending":
|
|
2381
|
+
return "orange";
|
|
2382
|
+
case "processing":
|
|
2383
|
+
return "blue";
|
|
2384
|
+
case "completed":
|
|
2385
|
+
return "green";
|
|
2386
|
+
case "failed":
|
|
2387
|
+
return "red";
|
|
2388
|
+
case "expired":
|
|
2389
|
+
default:
|
|
2390
|
+
return "gray";
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
function getExecutionDescription(execution) {
|
|
2394
|
+
if (execution.errorMessage) {
|
|
2395
|
+
return execution.errorMessage;
|
|
2396
|
+
}
|
|
2397
|
+
if (execution.status === "running") {
|
|
2398
|
+
return "Currently running";
|
|
2399
|
+
}
|
|
2400
|
+
if (execution.status === "warning") {
|
|
2401
|
+
return "Completed with warnings";
|
|
2402
|
+
}
|
|
2403
|
+
if (execution.status === "completed") {
|
|
2404
|
+
return "Completed successfully";
|
|
2405
|
+
}
|
|
2406
|
+
return titleCase4(execution.status);
|
|
2407
|
+
}
|
|
2408
|
+
function getTaskDescription(task) {
|
|
2409
|
+
if (task.description) {
|
|
2410
|
+
return task.description;
|
|
2411
|
+
}
|
|
2412
|
+
if (task.humanCheckpoint) {
|
|
2413
|
+
return `Checkpoint: ${task.humanCheckpoint}`;
|
|
2414
|
+
}
|
|
2415
|
+
return "Pending approval task";
|
|
2416
|
+
}
|
|
2417
|
+
function buildCommandViewDrillDownSections({
|
|
2418
|
+
node,
|
|
2419
|
+
timeRange,
|
|
2420
|
+
executions,
|
|
2421
|
+
checkpointTasks
|
|
2422
|
+
}) {
|
|
2423
|
+
if (!node || node.kind !== "resource" || !node.sourceId || !node.resourceType) {
|
|
2424
|
+
return [];
|
|
2425
|
+
}
|
|
2426
|
+
const sections = [];
|
|
2427
|
+
const resourceHref = getResourceHref(node.resourceType, node.sourceId);
|
|
2428
|
+
if ((node.resourceType === "agent" || node.resourceType === "workflow") && executions) {
|
|
2429
|
+
sections.push({
|
|
2430
|
+
title: "Recent executions",
|
|
2431
|
+
description: `Latest runs for this ${titleCase4(node.resourceType)} in the ${timeRange} window.`,
|
|
2432
|
+
emptyMessage: `No executions were recorded for this ${titleCase4(node.resourceType).toLowerCase()} in the current window.`,
|
|
2433
|
+
primaryAction: resourceHref ? {
|
|
2434
|
+
label: "Open resource",
|
|
2435
|
+
href: resourceHref
|
|
2436
|
+
} : void 0,
|
|
2437
|
+
items: executions.map((execution) => ({
|
|
2438
|
+
id: execution.executionId,
|
|
2439
|
+
label: execution.executionId,
|
|
2440
|
+
href: `${resourceHref ?? "#"}?exec=${execution.executionId}`,
|
|
2441
|
+
description: getExecutionDescription(execution),
|
|
2442
|
+
badgeLabel: execution.status,
|
|
2443
|
+
badgeColor: getExecutionBadgeColor(execution.status),
|
|
2444
|
+
meta: `Started ${formatRelativeTime(execution.startedAt)}`
|
|
2445
|
+
}))
|
|
2446
|
+
});
|
|
2447
|
+
}
|
|
2448
|
+
if (node.resourceType === "human_checkpoint" && checkpointTasks) {
|
|
2449
|
+
sections.push({
|
|
2450
|
+
title: "Pending tasks",
|
|
2451
|
+
description: "Human approval tasks waiting on this checkpoint.",
|
|
2452
|
+
emptyMessage: "No pending tasks are currently assigned to this checkpoint.",
|
|
2453
|
+
primaryAction: resourceHref ? {
|
|
2454
|
+
label: "Open queue",
|
|
2455
|
+
href: resourceHref
|
|
2456
|
+
} : void 0,
|
|
2457
|
+
items: checkpointTasks.map((task) => ({
|
|
2458
|
+
id: task.id,
|
|
2459
|
+
label: task.description ?? task.id,
|
|
2460
|
+
href: `/operations/command-queue?task=${task.id}`,
|
|
2461
|
+
description: getTaskDescription(task),
|
|
2462
|
+
badgeLabel: task.status,
|
|
2463
|
+
badgeColor: getTaskBadgeColor(task.status),
|
|
2464
|
+
meta: `Created ${formatRelativeTime(task.createdAt)}`
|
|
2465
|
+
}))
|
|
2466
|
+
});
|
|
2467
|
+
}
|
|
2468
|
+
return sections;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
// src/features/operations/organization-graph/path-tracing/trace.ts
|
|
2472
|
+
var NODE_KIND_ORDER = {
|
|
2473
|
+
organization: 0,
|
|
2474
|
+
feature: 1,
|
|
2475
|
+
surface: 2,
|
|
2476
|
+
capability: 3,
|
|
2477
|
+
entity: 4,
|
|
2478
|
+
resource: 5,
|
|
2479
|
+
knowledge: 6
|
|
2480
|
+
};
|
|
2481
|
+
var NODE_KIND_LABEL = {
|
|
2482
|
+
organization: "Organization",
|
|
2483
|
+
feature: "Feature",
|
|
2484
|
+
surface: "Surface",
|
|
2485
|
+
capability: "Capability",
|
|
2486
|
+
entity: "Entity",
|
|
2487
|
+
resource: "Resource",
|
|
2488
|
+
knowledge: "Knowledge"
|
|
2489
|
+
};
|
|
2490
|
+
function getNodeLabel(node) {
|
|
2491
|
+
return node.label || node.sourceId || node.id;
|
|
2492
|
+
}
|
|
2493
|
+
function compareTraceNodes(a, b) {
|
|
2494
|
+
const kindDelta = NODE_KIND_ORDER[a.kind] - NODE_KIND_ORDER[b.kind];
|
|
2495
|
+
if (kindDelta !== 0) {
|
|
2496
|
+
return kindDelta;
|
|
2497
|
+
}
|
|
2498
|
+
const labelDelta = getNodeLabel(a).localeCompare(getNodeLabel(b));
|
|
2499
|
+
if (labelDelta !== 0) {
|
|
2500
|
+
return labelDelta;
|
|
2501
|
+
}
|
|
2502
|
+
return a.id.localeCompare(b.id);
|
|
2503
|
+
}
|
|
2504
|
+
function buildMissingNodeMessage(selection, missingNodeIds) {
|
|
2505
|
+
if (missingNodeIds.length === 0) {
|
|
2506
|
+
return "The selected trace is not available.";
|
|
2507
|
+
}
|
|
2508
|
+
if (missingNodeIds.length === 1) {
|
|
2509
|
+
const missingId = missingNodeIds[0];
|
|
2510
|
+
return `Graph node "${missingId}" is not available.`;
|
|
2511
|
+
}
|
|
2512
|
+
return `Graph nodes "${selection.sourceId}" and "${selection.targetId}" are not available.`;
|
|
2513
|
+
}
|
|
2514
|
+
function buildPathNotFoundMessage(source, target) {
|
|
2515
|
+
return `No directed path found from "${source.label}" to "${target.label}".`;
|
|
2516
|
+
}
|
|
2517
|
+
function buildOrganizationGraphTraceIndex(graph) {
|
|
2518
|
+
const nodesById = /* @__PURE__ */ new Map();
|
|
2519
|
+
const edgesById = /* @__PURE__ */ new Map();
|
|
2520
|
+
const outgoingEdgesByNodeId = /* @__PURE__ */ new Map();
|
|
2521
|
+
const incomingEdgesByNodeId = /* @__PURE__ */ new Map();
|
|
2522
|
+
for (const node of graph.nodes) {
|
|
2523
|
+
nodesById.set(node.id, node);
|
|
2524
|
+
outgoingEdgesByNodeId.set(node.id, []);
|
|
2525
|
+
incomingEdgesByNodeId.set(node.id, []);
|
|
2526
|
+
}
|
|
2527
|
+
for (const edge of graph.edges) {
|
|
2528
|
+
edgesById.set(edge.id, edge);
|
|
2529
|
+
const outgoingEdges = outgoingEdgesByNodeId.get(edge.sourceId);
|
|
2530
|
+
if (outgoingEdges) {
|
|
2531
|
+
outgoingEdges.push(edge);
|
|
2532
|
+
}
|
|
2533
|
+
const incomingEdges = incomingEdgesByNodeId.get(edge.targetId);
|
|
2534
|
+
if (incomingEdges) {
|
|
2535
|
+
incomingEdges.push(edge);
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
return {
|
|
2539
|
+
nodesById,
|
|
2540
|
+
edgesById,
|
|
2541
|
+
outgoingEdgesByNodeId,
|
|
2542
|
+
incomingEdgesByNodeId
|
|
2543
|
+
};
|
|
2544
|
+
}
|
|
2545
|
+
function getOrganizationGraphTraceNodeKindLabel(kind) {
|
|
2546
|
+
return NODE_KIND_LABEL[kind];
|
|
2547
|
+
}
|
|
2548
|
+
function formatOrganizationGraphTraceNodeOptionLabel(option) {
|
|
2549
|
+
return `${option.label} \xB7 ${getOrganizationGraphTraceNodeKindLabel(option.kind)}`;
|
|
2550
|
+
}
|
|
2551
|
+
function getOrganizationGraphTraceNodeOptions(graph) {
|
|
2552
|
+
return [...graph.nodes].sort(compareTraceNodes).map((node) => ({
|
|
2553
|
+
id: node.id,
|
|
2554
|
+
label: getNodeLabel(node),
|
|
2555
|
+
kind: node.kind,
|
|
2556
|
+
sourceId: node.sourceId,
|
|
2557
|
+
description: node.description,
|
|
2558
|
+
enabled: node.enabled
|
|
2559
|
+
}));
|
|
2560
|
+
}
|
|
2561
|
+
function resolveOrganizationGraphPathTrace(graph, selection) {
|
|
2562
|
+
const index = buildOrganizationGraphTraceIndex(graph);
|
|
2563
|
+
const source = selection.sourceId ? index.nodesById.get(selection.sourceId) ?? null : null;
|
|
2564
|
+
const target = selection.targetId ? index.nodesById.get(selection.targetId) ?? null : null;
|
|
2565
|
+
const missingNodeIds = [
|
|
2566
|
+
selection.sourceId && !source ? selection.sourceId : null,
|
|
2567
|
+
selection.targetId && !target ? selection.targetId : null
|
|
2568
|
+
].filter((value) => Boolean(value));
|
|
2569
|
+
if (!selection.sourceId && !selection.targetId) {
|
|
2570
|
+
return {
|
|
2571
|
+
status: "idle",
|
|
2572
|
+
selection,
|
|
2573
|
+
source,
|
|
2574
|
+
target,
|
|
2575
|
+
missingNodeIds: [],
|
|
2576
|
+
pathNodes: [],
|
|
2577
|
+
pathEdges: [],
|
|
2578
|
+
highlightNodeIds: [],
|
|
2579
|
+
highlightEdgeIds: [],
|
|
2580
|
+
distance: 0,
|
|
2581
|
+
message: "Select a source and target to trace a path."
|
|
2582
|
+
};
|
|
2583
|
+
}
|
|
2584
|
+
if (!selection.sourceId || !selection.targetId) {
|
|
2585
|
+
return {
|
|
2586
|
+
status: "incomplete",
|
|
2587
|
+
selection,
|
|
2588
|
+
source,
|
|
2589
|
+
target,
|
|
2590
|
+
missingNodeIds,
|
|
2591
|
+
pathNodes: [],
|
|
2592
|
+
pathEdges: [],
|
|
2593
|
+
highlightNodeIds: [],
|
|
2594
|
+
highlightEdgeIds: [],
|
|
2595
|
+
distance: 0,
|
|
2596
|
+
message: "Select both a source and a target to resolve a path."
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
if (missingNodeIds.length > 0 || !source || !target) {
|
|
2600
|
+
return {
|
|
2601
|
+
status: "missing-node",
|
|
2602
|
+
selection,
|
|
2603
|
+
source,
|
|
2604
|
+
target,
|
|
2605
|
+
missingNodeIds,
|
|
2606
|
+
pathNodes: [],
|
|
2607
|
+
pathEdges: [],
|
|
2608
|
+
highlightNodeIds: [],
|
|
2609
|
+
highlightEdgeIds: [],
|
|
2610
|
+
distance: 0,
|
|
2611
|
+
message: buildMissingNodeMessage(selection, missingNodeIds)
|
|
2612
|
+
};
|
|
2613
|
+
}
|
|
2614
|
+
if (source.id === target.id) {
|
|
2615
|
+
return {
|
|
2616
|
+
status: "found",
|
|
2617
|
+
selection,
|
|
2618
|
+
source,
|
|
2619
|
+
target,
|
|
2620
|
+
missingNodeIds: [],
|
|
2621
|
+
pathNodes: [source],
|
|
2622
|
+
pathEdges: [],
|
|
2623
|
+
highlightNodeIds: [source.id],
|
|
2624
|
+
highlightEdgeIds: [],
|
|
2625
|
+
distance: 0,
|
|
2626
|
+
message: `Source and target already point to "${source.label}".`
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
const visitedNodeIds = /* @__PURE__ */ new Set([source.id]);
|
|
2630
|
+
const predecessorByNodeId = /* @__PURE__ */ new Map();
|
|
2631
|
+
const queue = [source.id];
|
|
2632
|
+
while (queue.length > 0) {
|
|
2633
|
+
const currentNodeId = queue.shift();
|
|
2634
|
+
if (!currentNodeId) {
|
|
2635
|
+
continue;
|
|
2636
|
+
}
|
|
2637
|
+
const outgoingEdges = index.outgoingEdgesByNodeId.get(currentNodeId) ?? [];
|
|
2638
|
+
for (const edge of outgoingEdges) {
|
|
2639
|
+
if (visitedNodeIds.has(edge.targetId)) {
|
|
2640
|
+
continue;
|
|
2641
|
+
}
|
|
2642
|
+
visitedNodeIds.add(edge.targetId);
|
|
2643
|
+
predecessorByNodeId.set(edge.targetId, {
|
|
2644
|
+
nodeId: currentNodeId,
|
|
2645
|
+
edgeId: edge.id
|
|
2646
|
+
});
|
|
2647
|
+
if (edge.targetId === target.id) {
|
|
2648
|
+
queue.length = 0;
|
|
2649
|
+
break;
|
|
2650
|
+
}
|
|
2651
|
+
queue.push(edge.targetId);
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
if (!predecessorByNodeId.has(target.id)) {
|
|
2655
|
+
return {
|
|
2656
|
+
status: "not-found",
|
|
2657
|
+
selection,
|
|
2658
|
+
source,
|
|
2659
|
+
target,
|
|
2660
|
+
missingNodeIds: [],
|
|
2661
|
+
pathNodes: [],
|
|
2662
|
+
pathEdges: [],
|
|
2663
|
+
highlightNodeIds: [],
|
|
2664
|
+
highlightEdgeIds: [],
|
|
2665
|
+
distance: 0,
|
|
2666
|
+
message: buildPathNotFoundMessage(source, target)
|
|
2667
|
+
};
|
|
2668
|
+
}
|
|
2669
|
+
const pathNodes = [target];
|
|
2670
|
+
const pathEdges = [];
|
|
2671
|
+
let cursorNodeId = target.id;
|
|
2672
|
+
while (cursorNodeId !== source.id) {
|
|
2673
|
+
const predecessor = predecessorByNodeId.get(cursorNodeId);
|
|
2674
|
+
if (!predecessor) {
|
|
2675
|
+
break;
|
|
2676
|
+
}
|
|
2677
|
+
const edge = index.edgesById.get(predecessor.edgeId);
|
|
2678
|
+
const previousNode = index.nodesById.get(predecessor.nodeId);
|
|
2679
|
+
if (edge) {
|
|
2680
|
+
pathEdges.push(edge);
|
|
2681
|
+
}
|
|
2682
|
+
if (previousNode) {
|
|
2683
|
+
pathNodes.push(previousNode);
|
|
2684
|
+
}
|
|
2685
|
+
cursorNodeId = predecessor.nodeId;
|
|
2686
|
+
}
|
|
2687
|
+
pathNodes.reverse();
|
|
2688
|
+
pathEdges.reverse();
|
|
2689
|
+
return {
|
|
2690
|
+
status: "found",
|
|
2691
|
+
selection,
|
|
2692
|
+
source,
|
|
2693
|
+
target,
|
|
2694
|
+
missingNodeIds: [],
|
|
2695
|
+
pathNodes,
|
|
2696
|
+
pathEdges,
|
|
2697
|
+
highlightNodeIds: pathNodes.map((node) => node.id),
|
|
2698
|
+
highlightEdgeIds: pathEdges.map((edge) => edge.id),
|
|
2699
|
+
distance: pathEdges.length,
|
|
2700
|
+
message: `Path found from "${source.label}" to "${target.label}" across ${pathEdges.length} edge${pathEdges.length === 1 ? "" : "s"}.`
|
|
2701
|
+
};
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
// src/features/operations/organization-graph/expand-around/expandAroundGraph.ts
|
|
2705
|
+
var DEFAULT_MAX_DEPTH = 1;
|
|
2706
|
+
var DEFAULT_MAX_RESULTS = 25;
|
|
2707
|
+
var ORG_MODEL_ROOT_KINDS = /* @__PURE__ */ new Set([
|
|
2708
|
+
"feature",
|
|
2709
|
+
"surface",
|
|
2710
|
+
"entity",
|
|
2711
|
+
"capability"
|
|
2712
|
+
]);
|
|
2713
|
+
var PRESET_EDGE_KINDS = {
|
|
2714
|
+
coverage: ["contains", "exposes", "operates-on", "maps_to", "references"],
|
|
2715
|
+
"operational-dependencies": ["references"],
|
|
2716
|
+
"org-context": ["contains", "exposes", "operates-on", "maps_to"],
|
|
2717
|
+
"impact-path": ["references"]
|
|
2718
|
+
};
|
|
2719
|
+
var PRESET_RELATIONSHIP_TYPES = {
|
|
2720
|
+
"operational-dependencies": ["triggers", "uses", "approval"],
|
|
2721
|
+
"impact-path": ["triggers", "uses", "approval"]
|
|
2722
|
+
};
|
|
2723
|
+
var PRESET_DIRECTIONS = {
|
|
2724
|
+
coverage: "both",
|
|
2725
|
+
"operational-dependencies": "both",
|
|
2726
|
+
"org-context": "both",
|
|
2727
|
+
"impact-path": "both"
|
|
2728
|
+
};
|
|
2729
|
+
function toSet(values) {
|
|
2730
|
+
if (!values || values.length === 0) {
|
|
2731
|
+
return null;
|
|
2732
|
+
}
|
|
2733
|
+
return new Set(values);
|
|
2734
|
+
}
|
|
2735
|
+
function clampMaxDepth(maxDepth) {
|
|
2736
|
+
if (maxDepth === 2 || maxDepth === 3) {
|
|
2737
|
+
return maxDepth;
|
|
2738
|
+
}
|
|
2739
|
+
return 1;
|
|
2740
|
+
}
|
|
2741
|
+
function resolvePreset(rootNode, preset) {
|
|
2742
|
+
if (preset) {
|
|
2743
|
+
return preset;
|
|
2744
|
+
}
|
|
2745
|
+
if (!rootNode) {
|
|
2746
|
+
return null;
|
|
2747
|
+
}
|
|
2748
|
+
if (ORG_MODEL_ROOT_KINDS.has(rootNode.kind)) {
|
|
2749
|
+
return "coverage";
|
|
2750
|
+
}
|
|
2751
|
+
if (rootNode.kind === "resource") {
|
|
2752
|
+
return "operational-dependencies";
|
|
2753
|
+
}
|
|
2754
|
+
return null;
|
|
2755
|
+
}
|
|
2756
|
+
function resolveRequest(rootNode, request) {
|
|
2757
|
+
const preset = resolvePreset(rootNode, request.preset);
|
|
2758
|
+
const maxResults = request.maxResults === void 0 || !Number.isFinite(request.maxResults) ? DEFAULT_MAX_RESULTS : Math.max(0, request.maxResults);
|
|
2759
|
+
return {
|
|
2760
|
+
rootNodeId: request.rootNodeId,
|
|
2761
|
+
direction: request.direction ?? (preset ? PRESET_DIRECTIONS[preset] : "both"),
|
|
2762
|
+
maxDepth: clampMaxDepth(request.maxDepth ?? DEFAULT_MAX_DEPTH),
|
|
2763
|
+
maxResults,
|
|
2764
|
+
edgeKinds: toSet(request.edgeKinds ?? (preset ? PRESET_EDGE_KINDS[preset] : void 0)),
|
|
2765
|
+
relationshipTypes: toSet(
|
|
2766
|
+
request.relationshipTypes ?? (preset ? PRESET_RELATIONSHIP_TYPES[preset] : void 0)
|
|
2767
|
+
),
|
|
2768
|
+
preset,
|
|
2769
|
+
nodeKinds: toSet(request.nodeKinds),
|
|
2770
|
+
resourceTypes: toSet(request.resourceTypes),
|
|
2771
|
+
includeHiddenResources: request.includeHiddenResources ?? false
|
|
2772
|
+
};
|
|
2773
|
+
}
|
|
2774
|
+
function getOppositeNodeId(edge, currentNodeId) {
|
|
2775
|
+
if (edge.sourceId === currentNodeId) {
|
|
2776
|
+
return edge.targetId;
|
|
2777
|
+
}
|
|
2778
|
+
if (edge.targetId === currentNodeId) {
|
|
2779
|
+
return edge.sourceId;
|
|
2780
|
+
}
|
|
2781
|
+
return null;
|
|
2782
|
+
}
|
|
2783
|
+
function getCandidateEdges(index, nodeId2, direction) {
|
|
2784
|
+
if (direction === "outgoing") {
|
|
2785
|
+
return index.outgoingEdgesByNodeId.get(nodeId2) ?? [];
|
|
2786
|
+
}
|
|
2787
|
+
if (direction === "incoming") {
|
|
2788
|
+
return index.incomingEdgesByNodeId.get(nodeId2) ?? [];
|
|
2789
|
+
}
|
|
2790
|
+
const seenEdgeIds = /* @__PURE__ */ new Set();
|
|
2791
|
+
const edges = [];
|
|
2792
|
+
for (const edge of [
|
|
2793
|
+
...index.outgoingEdgesByNodeId.get(nodeId2) ?? [],
|
|
2794
|
+
...index.incomingEdgesByNodeId.get(nodeId2) ?? []
|
|
2795
|
+
]) {
|
|
2796
|
+
if (seenEdgeIds.has(edge.id)) {
|
|
2797
|
+
continue;
|
|
2798
|
+
}
|
|
2799
|
+
seenEdgeIds.add(edge.id);
|
|
2800
|
+
edges.push(edge);
|
|
2801
|
+
}
|
|
2802
|
+
return edges;
|
|
2803
|
+
}
|
|
2804
|
+
function matchesPresetTraversalDirection(edge, currentNodeId, request) {
|
|
2805
|
+
if (request.preset === "coverage" && (edge.kind === "contains" || edge.kind === "exposes")) {
|
|
2806
|
+
return edge.sourceId === currentNodeId;
|
|
2807
|
+
}
|
|
2808
|
+
return true;
|
|
2809
|
+
}
|
|
2810
|
+
function matchesEdgeFilters(edge, currentNodeId, request) {
|
|
2811
|
+
if (request.edgeKinds && !request.edgeKinds.has(edge.kind)) {
|
|
2812
|
+
return false;
|
|
2813
|
+
}
|
|
2814
|
+
if (request.relationshipTypes && (!edge.relationshipType || !request.relationshipTypes.has(edge.relationshipType))) {
|
|
2815
|
+
return false;
|
|
2816
|
+
}
|
|
2817
|
+
return matchesPresetTraversalDirection(edge, currentNodeId, request);
|
|
2818
|
+
}
|
|
2819
|
+
function matchesNodeFilters(node, request) {
|
|
2820
|
+
if (request.nodeKinds && !request.nodeKinds.has(node.kind)) {
|
|
2821
|
+
return false;
|
|
2822
|
+
}
|
|
2823
|
+
if (request.resourceTypes) {
|
|
2824
|
+
if (node.kind !== "resource") {
|
|
2825
|
+
return false;
|
|
2826
|
+
}
|
|
2827
|
+
if (!node.resourceType || !request.resourceTypes.has(node.resourceType)) {
|
|
2828
|
+
return false;
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
return true;
|
|
2832
|
+
}
|
|
2833
|
+
function pluralize(count, singular, plural = `${singular}s`) {
|
|
2834
|
+
return `${count} ${count === 1 ? singular : plural}`;
|
|
2835
|
+
}
|
|
2836
|
+
function getNodeDisplayName(node) {
|
|
2837
|
+
return node.label || node.sourceId || node.id;
|
|
2838
|
+
}
|
|
2839
|
+
function buildSummaryMessages(input) {
|
|
2840
|
+
const { rootNode, preset, expandedNodes, counts, truncated } = input;
|
|
2841
|
+
if (!rootNode) {
|
|
2842
|
+
return ["Select an available node to expand around."];
|
|
2843
|
+
}
|
|
2844
|
+
if (!preset && rootNode.kind === "organization") {
|
|
2845
|
+
return ["Choose a feature, surface, capability, entity, or resource before expanding."];
|
|
2846
|
+
}
|
|
2847
|
+
if (expandedNodes.length === 0) {
|
|
2848
|
+
return [`No matching graph context found around "${getNodeDisplayName(rootNode)}".`];
|
|
2849
|
+
}
|
|
2850
|
+
const resourceCount = expandedNodes.filter((node) => node.kind === "resource").length;
|
|
2851
|
+
const semanticContextCount = expandedNodes.length - resourceCount;
|
|
2852
|
+
const rootLabel = getNodeDisplayName(rootNode);
|
|
2853
|
+
const messages = [];
|
|
2854
|
+
if (preset === "coverage" && ORG_MODEL_ROOT_KINDS.has(rootNode.kind)) {
|
|
2855
|
+
if (resourceCount > 0 && semanticContextCount > 0) {
|
|
2856
|
+
messages.push(
|
|
2857
|
+
`${pluralize(resourceCount, "resource")} and ${pluralize(
|
|
2858
|
+
semanticContextCount,
|
|
2859
|
+
"semantic context node"
|
|
2860
|
+
)} support "${rootLabel}".`
|
|
2861
|
+
);
|
|
2862
|
+
} else if (resourceCount > 0) {
|
|
2863
|
+
messages.push(`${pluralize(resourceCount, "resource")} support "${rootLabel}".`);
|
|
2864
|
+
} else {
|
|
2865
|
+
messages.push(
|
|
2866
|
+
`${pluralize(semanticContextCount, "semantic context node")} found around "${rootLabel}"; no mapped resources matched this coverage preset.`
|
|
2867
|
+
);
|
|
2868
|
+
}
|
|
2869
|
+
} else if (preset === "operational-dependencies") {
|
|
2870
|
+
messages.push(`${pluralize(resourceCount, "operational dependency resource")} found around "${rootLabel}".`);
|
|
2871
|
+
} else if (preset === "org-context") {
|
|
2872
|
+
messages.push(`${pluralize(expandedNodes.length, "semantic context node")} found around "${rootLabel}".`);
|
|
2873
|
+
} else if (preset === "impact-path") {
|
|
2874
|
+
messages.push(`${pluralize(resourceCount, "resource")} can call or be affected by "${rootLabel}".`);
|
|
2875
|
+
} else {
|
|
2876
|
+
messages.push(`${pluralize(expandedNodes.length, "node")} found around "${rootLabel}".`);
|
|
2877
|
+
}
|
|
2878
|
+
if (counts.hiddenResourceNodes > 0) {
|
|
2879
|
+
messages.push(`${pluralize(counts.hiddenResourceNodes, "hidden resource")} matched current visibility settings.`);
|
|
2880
|
+
}
|
|
2881
|
+
if (counts.alreadyVisibleNodes > 0) {
|
|
2882
|
+
messages.push(`${pluralize(counts.alreadyVisibleNodes, "node")} already visible in the graph.`);
|
|
2883
|
+
}
|
|
2884
|
+
if (truncated) {
|
|
2885
|
+
messages.push(`Results were limited to ${pluralize(expandedNodes.length, "node")}.`);
|
|
2886
|
+
}
|
|
2887
|
+
return messages;
|
|
2888
|
+
}
|
|
2889
|
+
function expandAroundGraph(graph, request, options = {}) {
|
|
2890
|
+
const index = buildOrganizationGraphTraceIndex(graph);
|
|
2891
|
+
const rootNode = index.nodesById.get(request.rootNodeId) ?? null;
|
|
2892
|
+
const resolved = resolveRequest(rootNode, request);
|
|
2893
|
+
const alreadyVisibleNodeIds = new Set(options.alreadyVisibleNodeIds ?? []);
|
|
2894
|
+
const hiddenResourceNodeIds = new Set(options.hiddenResourceNodeIds ?? []);
|
|
2895
|
+
const expandedNodeIds = /* @__PURE__ */ new Set();
|
|
2896
|
+
const expandedEdgeIds = /* @__PURE__ */ new Set();
|
|
2897
|
+
const frontierNodeIds = /* @__PURE__ */ new Set();
|
|
2898
|
+
const visitedDepthByNodeId = /* @__PURE__ */ new Map([[resolved.rootNodeId, 0]]);
|
|
2899
|
+
const hasExplicitTraversalFilter = Boolean(
|
|
2900
|
+
request.preset || request.edgeKinds?.length || request.relationshipTypes?.length || request.nodeKinds?.length || request.resourceTypes?.length
|
|
2901
|
+
);
|
|
2902
|
+
const queue = rootNode && (resolved.preset || hasExplicitTraversalFilter) ? [{ nodeId: rootNode.id, depth: 0 }] : [];
|
|
2903
|
+
let truncated = false;
|
|
2904
|
+
for (let queueIndex = 0; queueIndex < queue.length; queueIndex += 1) {
|
|
2905
|
+
const current = queue[queueIndex];
|
|
2906
|
+
if (current.depth >= resolved.maxDepth) {
|
|
2907
|
+
if (current.nodeId !== resolved.rootNodeId) {
|
|
2908
|
+
frontierNodeIds.add(current.nodeId);
|
|
2909
|
+
}
|
|
2910
|
+
continue;
|
|
2911
|
+
}
|
|
2912
|
+
for (const edge of getCandidateEdges(index, current.nodeId, resolved.direction)) {
|
|
2913
|
+
if (!matchesEdgeFilters(edge, current.nodeId, resolved)) {
|
|
2914
|
+
continue;
|
|
2915
|
+
}
|
|
2916
|
+
const nextNodeId = getOppositeNodeId(edge, current.nodeId);
|
|
2917
|
+
if (!nextNodeId) {
|
|
2918
|
+
continue;
|
|
2919
|
+
}
|
|
2920
|
+
const nextNode = index.nodesById.get(nextNodeId);
|
|
2921
|
+
if (!nextNode || !matchesNodeFilters(nextNode, resolved)) {
|
|
2922
|
+
continue;
|
|
2923
|
+
}
|
|
2924
|
+
if (!resolved.includeHiddenResources && hiddenResourceNodeIds.has(nextNode.id)) {
|
|
2925
|
+
continue;
|
|
2926
|
+
}
|
|
2927
|
+
const nextDepth = current.depth + 1;
|
|
2928
|
+
const previousDepth = visitedDepthByNodeId.get(nextNode.id);
|
|
2929
|
+
if (previousDepth !== void 0) {
|
|
2930
|
+
if (nextNode.id === resolved.rootNodeId || expandedNodeIds.has(nextNode.id)) {
|
|
2931
|
+
expandedEdgeIds.add(edge.id);
|
|
2932
|
+
}
|
|
2933
|
+
continue;
|
|
2934
|
+
}
|
|
2935
|
+
if (expandedNodeIds.size >= resolved.maxResults) {
|
|
2936
|
+
truncated = true;
|
|
2937
|
+
frontierNodeIds.add(nextNode.id);
|
|
2938
|
+
continue;
|
|
2939
|
+
}
|
|
2940
|
+
visitedDepthByNodeId.set(nextNode.id, nextDepth);
|
|
2941
|
+
expandedNodeIds.add(nextNode.id);
|
|
2942
|
+
expandedEdgeIds.add(edge.id);
|
|
2943
|
+
if (nextDepth >= resolved.maxDepth) {
|
|
2944
|
+
frontierNodeIds.add(nextNode.id);
|
|
2945
|
+
continue;
|
|
2946
|
+
}
|
|
2947
|
+
queue.push({ nodeId: nextNode.id, depth: nextDepth });
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
const expandedNodes = [...expandedNodeIds].map((nodeId2) => index.nodesById.get(nodeId2)).filter((node) => Boolean(node));
|
|
2951
|
+
const hiddenResourceNodes = expandedNodes.filter((node) => hiddenResourceNodeIds.has(node.id)).length;
|
|
2952
|
+
const alreadyVisibleNodes = expandedNodes.filter((node) => alreadyVisibleNodeIds.has(node.id)).length;
|
|
2953
|
+
const newNodes = expandedNodes.filter(
|
|
2954
|
+
(node) => !hiddenResourceNodeIds.has(node.id) && !alreadyVisibleNodeIds.has(node.id)
|
|
2955
|
+
).length;
|
|
2956
|
+
const counts = {
|
|
2957
|
+
newNodes,
|
|
2958
|
+
newEdges: expandedEdgeIds.size,
|
|
2959
|
+
alreadyVisibleNodes,
|
|
2960
|
+
hiddenResourceNodes
|
|
2961
|
+
};
|
|
2962
|
+
const summaryMessages = buildSummaryMessages({
|
|
2963
|
+
rootNode,
|
|
2964
|
+
preset: resolved.preset,
|
|
2965
|
+
expandedNodes,
|
|
2966
|
+
counts,
|
|
2967
|
+
truncated
|
|
2968
|
+
});
|
|
2969
|
+
return {
|
|
2970
|
+
rootNodeId: request.rootNodeId,
|
|
2971
|
+
rootNode,
|
|
2972
|
+
preset: resolved.preset,
|
|
2973
|
+
direction: resolved.direction,
|
|
2974
|
+
maxDepth: resolved.maxDepth,
|
|
2975
|
+
maxResults: resolved.maxResults,
|
|
2976
|
+
expandedNodeIds: [...expandedNodeIds],
|
|
2977
|
+
expandedEdgeIds: [...expandedEdgeIds],
|
|
2978
|
+
frontierNodeIds: [...frontierNodeIds].filter((nodeId2) => nodeId2 !== resolved.rootNodeId),
|
|
2979
|
+
truncated,
|
|
2980
|
+
counts,
|
|
2981
|
+
summaryMessages,
|
|
2982
|
+
message: summaryMessages[0] ?? ""
|
|
2983
|
+
};
|
|
2984
|
+
}
|
|
2985
|
+
var PRESET_OPTIONS = [
|
|
2986
|
+
{ value: "coverage", label: "Coverage" },
|
|
2987
|
+
{ value: "operational-dependencies", label: "Operational dependencies" },
|
|
2988
|
+
{ value: "org-context", label: "Org context" },
|
|
2989
|
+
{ value: "impact-path", label: "Impact path" }
|
|
2990
|
+
];
|
|
2991
|
+
var DIRECTION_OPTIONS = [
|
|
2992
|
+
{ value: "both", label: "Both" },
|
|
2993
|
+
{ value: "outgoing", label: "Outgoing" },
|
|
2994
|
+
{ value: "incoming", label: "Incoming" }
|
|
2995
|
+
];
|
|
2996
|
+
var EDGE_KIND_OPTIONS = [
|
|
2997
|
+
{ value: "contains", label: "Containment" },
|
|
2998
|
+
{ value: "references", label: "References" },
|
|
2999
|
+
{ value: "exposes", label: "Exposes" },
|
|
3000
|
+
{ value: "maps_to", label: "Maps to" },
|
|
3001
|
+
{ value: "operates-on", label: "Operates on" },
|
|
3002
|
+
{ value: "uses", label: "Uses" }
|
|
3003
|
+
];
|
|
3004
|
+
var RELATIONSHIP_OPTIONS = [
|
|
3005
|
+
{ value: "triggers", label: "Triggers" },
|
|
3006
|
+
{ value: "uses", label: "Uses" },
|
|
3007
|
+
{ value: "approval", label: "Approval" }
|
|
3008
|
+
];
|
|
3009
|
+
var RESOURCE_TYPE_OPTIONS = [
|
|
3010
|
+
{ value: "workflow", label: "Workflow" },
|
|
3011
|
+
{ value: "agent", label: "Agent" },
|
|
3012
|
+
{ value: "trigger", label: "Trigger" },
|
|
3013
|
+
{ value: "integration", label: "Integration" },
|
|
3014
|
+
{ value: "external", label: "External" },
|
|
3015
|
+
{ value: "human_checkpoint", label: "Human checkpoint" }
|
|
3016
|
+
];
|
|
3017
|
+
function updateValue(value, key, nextValue, onChange) {
|
|
3018
|
+
onChange({
|
|
3019
|
+
...value,
|
|
3020
|
+
[key]: nextValue
|
|
3021
|
+
});
|
|
3022
|
+
}
|
|
3023
|
+
function toOptionalArray(values) {
|
|
3024
|
+
return values.length > 0 ? values : void 0;
|
|
3025
|
+
}
|
|
3026
|
+
function toMaxDepth(value) {
|
|
3027
|
+
const numeric = typeof value === "number" ? value : Number.parseInt(value, 10);
|
|
3028
|
+
if (numeric === 3) return 3;
|
|
3029
|
+
if (numeric === 2) return 2;
|
|
3030
|
+
return 1;
|
|
3031
|
+
}
|
|
3032
|
+
function ExpandAroundPanel({
|
|
3033
|
+
selectedNode,
|
|
3034
|
+
value,
|
|
3035
|
+
result,
|
|
3036
|
+
appliedNodeCount,
|
|
3037
|
+
appliedEdgeCount,
|
|
3038
|
+
onChange,
|
|
3039
|
+
onPreview,
|
|
3040
|
+
onApply,
|
|
3041
|
+
onClear
|
|
3042
|
+
}) {
|
|
3043
|
+
const disabled = !selectedNode;
|
|
3044
|
+
const canApply = Boolean(result && result.expandedNodeIds.length > 0);
|
|
3045
|
+
const rootIsTooBroad = selectedNode?.kind === "organization" && !value.preset;
|
|
3046
|
+
return /* @__PURE__ */ jsx(Paper, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
3047
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", wrap: "wrap", children: [
|
|
3048
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
3049
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 800, children: "Expand Around" }),
|
|
3050
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedNode ? `Preview typed graph context around ${selectedNode.label}.` : "Select a graph node to preview nearby operational context." })
|
|
3051
|
+
] }),
|
|
3052
|
+
appliedNodeCount > 0 || appliedEdgeCount > 0 ? /* @__PURE__ */ jsxs(Badge, { variant: "light", color: "blue", children: [
|
|
3053
|
+
appliedNodeCount,
|
|
3054
|
+
" nodes / ",
|
|
3055
|
+
appliedEdgeCount,
|
|
3056
|
+
" edges applied"
|
|
3057
|
+
] }) : null
|
|
3058
|
+
] }),
|
|
3059
|
+
/* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
|
|
3060
|
+
/* @__PURE__ */ jsx(
|
|
3061
|
+
Select,
|
|
3062
|
+
{
|
|
3063
|
+
label: "Preset",
|
|
3064
|
+
placeholder: "Auto",
|
|
3065
|
+
data: PRESET_OPTIONS,
|
|
3066
|
+
value: value.preset ?? null,
|
|
3067
|
+
onChange: (preset) => updateValue(value, "preset", preset ?? void 0, onChange),
|
|
3068
|
+
disabled,
|
|
3069
|
+
clearable: true
|
|
3070
|
+
}
|
|
3071
|
+
),
|
|
3072
|
+
/* @__PURE__ */ jsx(
|
|
3073
|
+
Select,
|
|
3074
|
+
{
|
|
3075
|
+
label: "Direction",
|
|
3076
|
+
data: DIRECTION_OPTIONS,
|
|
3077
|
+
value: value.direction ?? "both",
|
|
3078
|
+
onChange: (direction) => updateValue(value, "direction", direction ?? "both", onChange),
|
|
3079
|
+
disabled
|
|
3080
|
+
}
|
|
3081
|
+
),
|
|
3082
|
+
/* @__PURE__ */ jsx(
|
|
3083
|
+
NumberInput,
|
|
3084
|
+
{
|
|
3085
|
+
label: "Depth",
|
|
3086
|
+
min: 1,
|
|
3087
|
+
max: 3,
|
|
3088
|
+
value: value.maxDepth ?? 1,
|
|
3089
|
+
onChange: (maxDepth) => updateValue(value, "maxDepth", toMaxDepth(maxDepth), onChange),
|
|
3090
|
+
disabled
|
|
3091
|
+
}
|
|
3092
|
+
),
|
|
3093
|
+
/* @__PURE__ */ jsx(
|
|
3094
|
+
NumberInput,
|
|
3095
|
+
{
|
|
3096
|
+
label: "Max results",
|
|
3097
|
+
min: 1,
|
|
3098
|
+
max: 100,
|
|
3099
|
+
value: value.maxResults ?? 25,
|
|
3100
|
+
onChange: (maxResults) => updateValue(
|
|
3101
|
+
value,
|
|
3102
|
+
"maxResults",
|
|
3103
|
+
Math.max(1, typeof maxResults === "number" ? maxResults : Number.parseInt(maxResults, 10) || 25),
|
|
3104
|
+
onChange
|
|
3105
|
+
),
|
|
3106
|
+
disabled
|
|
3107
|
+
}
|
|
3108
|
+
)
|
|
3109
|
+
] }),
|
|
3110
|
+
/* @__PURE__ */ jsx(
|
|
3111
|
+
MultiSelect,
|
|
3112
|
+
{
|
|
3113
|
+
label: "Relationship filters",
|
|
3114
|
+
placeholder: "Preset defaults",
|
|
3115
|
+
data: RELATIONSHIP_OPTIONS,
|
|
3116
|
+
value: value.relationshipTypes ?? [],
|
|
3117
|
+
onChange: (relationshipTypes) => updateValue(value, "relationshipTypes", toOptionalArray(relationshipTypes), onChange),
|
|
3118
|
+
disabled,
|
|
3119
|
+
clearable: true
|
|
3120
|
+
}
|
|
3121
|
+
),
|
|
3122
|
+
/* @__PURE__ */ jsx(
|
|
3123
|
+
MultiSelect,
|
|
3124
|
+
{
|
|
3125
|
+
label: "Edge kind filters",
|
|
3126
|
+
placeholder: "Preset defaults",
|
|
3127
|
+
data: EDGE_KIND_OPTIONS,
|
|
3128
|
+
value: value.edgeKinds ?? [],
|
|
3129
|
+
onChange: (edgeKinds) => updateValue(value, "edgeKinds", toOptionalArray(edgeKinds), onChange),
|
|
3130
|
+
disabled,
|
|
3131
|
+
clearable: true
|
|
3132
|
+
}
|
|
3133
|
+
),
|
|
3134
|
+
/* @__PURE__ */ jsx(
|
|
3135
|
+
MultiSelect,
|
|
3136
|
+
{
|
|
3137
|
+
label: "Resource type filters",
|
|
3138
|
+
placeholder: "All resource types",
|
|
3139
|
+
data: RESOURCE_TYPE_OPTIONS,
|
|
3140
|
+
value: value.resourceTypes ?? [],
|
|
3141
|
+
onChange: (resourceTypes) => updateValue(value, "resourceTypes", toOptionalArray(resourceTypes), onChange),
|
|
3142
|
+
disabled,
|
|
3143
|
+
clearable: true
|
|
3144
|
+
}
|
|
3145
|
+
),
|
|
3146
|
+
/* @__PURE__ */ jsx(
|
|
3147
|
+
Switch,
|
|
3148
|
+
{
|
|
3149
|
+
label: "Include hidden resources",
|
|
3150
|
+
checked: value.includeHiddenResources ?? true,
|
|
3151
|
+
onChange: (event) => updateValue(value, "includeHiddenResources", event.currentTarget.checked, onChange),
|
|
3152
|
+
disabled
|
|
3153
|
+
}
|
|
3154
|
+
),
|
|
3155
|
+
rootIsTooBroad ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "orange", children: "The organization root is broad. Choose a narrower node or a semantic preset before previewing." }) : null,
|
|
3156
|
+
result ? /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
|
|
3157
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
|
|
3158
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "light", children: [
|
|
3159
|
+
result.counts.newNodes,
|
|
3160
|
+
" new"
|
|
3161
|
+
] }),
|
|
3162
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "light", children: [
|
|
3163
|
+
result.counts.alreadyVisibleNodes,
|
|
3164
|
+
" visible"
|
|
3165
|
+
] }),
|
|
3166
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "light", color: result.counts.hiddenResourceNodes > 0 ? "orange" : "gray", children: [
|
|
3167
|
+
result.counts.hiddenResourceNodes,
|
|
3168
|
+
" hidden"
|
|
3169
|
+
] }),
|
|
3170
|
+
result.truncated ? /* @__PURE__ */ jsx(Badge, { variant: "light", color: "yellow", children: "Truncated" }) : null
|
|
3171
|
+
] }),
|
|
3172
|
+
result.summaryMessages.map((message) => /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: message }, message))
|
|
3173
|
+
] }) }) : null,
|
|
3174
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", wrap: "wrap", children: [
|
|
3175
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
3176
|
+
/* @__PURE__ */ jsx(Button, { size: "xs", variant: "light", onClick: onPreview, disabled: disabled || rootIsTooBroad, children: "Preview" }),
|
|
3177
|
+
/* @__PURE__ */ jsx(Button, { size: "xs", onClick: onApply, disabled: !canApply, children: "Apply" })
|
|
3178
|
+
] }),
|
|
3179
|
+
/* @__PURE__ */ jsx(Button, { size: "xs", variant: "subtle", onClick: onClear, disabled: !result && appliedNodeCount === 0, children: "Clear expansion" })
|
|
3180
|
+
] })
|
|
3181
|
+
] }) });
|
|
3182
|
+
}
|
|
3183
|
+
var FILTER_STATE_ICONS = {
|
|
3184
|
+
neutral: IconCircleDashed,
|
|
3185
|
+
include: IconCircleCheck,
|
|
3186
|
+
exclude: IconCircleX
|
|
3187
|
+
};
|
|
3188
|
+
var FILTER_STATE_COLORS = {
|
|
3189
|
+
neutral: "var(--color-text-dimmed)",
|
|
3190
|
+
include: "var(--mantine-color-green-6)",
|
|
3191
|
+
exclude: "var(--mantine-color-red-6)"
|
|
3192
|
+
};
|
|
3193
|
+
var FILTER_STATE_LABELS = {
|
|
3194
|
+
neutral: "Not filtered",
|
|
3195
|
+
include: "Include only",
|
|
3196
|
+
exclude: "Exclude"
|
|
3197
|
+
};
|
|
3198
|
+
function getCommandViewResources(commandViewData) {
|
|
3199
|
+
if (!commandViewData) {
|
|
3200
|
+
return [];
|
|
3201
|
+
}
|
|
3202
|
+
return [
|
|
3203
|
+
...commandViewData.agents,
|
|
3204
|
+
...commandViewData.workflows,
|
|
3205
|
+
...commandViewData.triggers,
|
|
3206
|
+
...commandViewData.integrations,
|
|
3207
|
+
...commandViewData.externalResources,
|
|
3208
|
+
...commandViewData.humanCheckpoints
|
|
3209
|
+
];
|
|
3210
|
+
}
|
|
3211
|
+
function nextDomainFilterState(current) {
|
|
3212
|
+
if (current === "neutral") return "include";
|
|
3213
|
+
if (current === "include") return "exclude";
|
|
3214
|
+
return "neutral";
|
|
3215
|
+
}
|
|
3216
|
+
function FilterPanel({
|
|
3217
|
+
filters,
|
|
3218
|
+
onChangeFilters,
|
|
3219
|
+
resetValue,
|
|
3220
|
+
disabled = false,
|
|
3221
|
+
commandViewData,
|
|
3222
|
+
lens,
|
|
3223
|
+
resourcesHidden,
|
|
3224
|
+
diagnosticsHidden,
|
|
3225
|
+
onResourcesHiddenChange,
|
|
3226
|
+
onDiagnosticsHiddenChange,
|
|
3227
|
+
onRevealResources,
|
|
3228
|
+
onResetFilters,
|
|
3229
|
+
visibilityCounts,
|
|
3230
|
+
graph,
|
|
3231
|
+
baseGraph,
|
|
3232
|
+
layout = "grid"
|
|
3233
|
+
}) {
|
|
3234
|
+
const resourceFacets = collectResourceFilterFacets(getCommandViewResources(commandViewData));
|
|
3235
|
+
const Section = ({ children }) => layout === "stack" ? /* @__PURE__ */ jsx(
|
|
3236
|
+
Stack,
|
|
3237
|
+
{
|
|
3238
|
+
gap: "sm",
|
|
3239
|
+
pb: "sm",
|
|
3240
|
+
style: {
|
|
3241
|
+
borderBottom: "1px solid var(--color-border)"
|
|
3242
|
+
},
|
|
3243
|
+
children
|
|
3244
|
+
}
|
|
3245
|
+
) : /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", radius: "md", style: { background: "var(--color-surface)" }, children });
|
|
3246
|
+
const updateDomainFilter = (domainId) => {
|
|
3247
|
+
const current = filters.domainFilters[domainId] ?? "neutral";
|
|
3248
|
+
onChangeFilters({
|
|
3249
|
+
...filters,
|
|
3250
|
+
domainFilters: {
|
|
3251
|
+
...filters.domainFilters,
|
|
3252
|
+
[domainId]: nextDomainFilterState(current)
|
|
3253
|
+
}
|
|
3254
|
+
});
|
|
3255
|
+
};
|
|
3256
|
+
const handleResourceVisibilityAction = () => {
|
|
3257
|
+
if (resourcesHidden) {
|
|
3258
|
+
onRevealResources();
|
|
3259
|
+
return;
|
|
3260
|
+
}
|
|
3261
|
+
onResourcesHiddenChange(true);
|
|
3262
|
+
};
|
|
3263
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3264
|
+
/* @__PURE__ */ jsx(Section, { children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
3265
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: "Filters" }),
|
|
3266
|
+
/* @__PURE__ */ jsx(
|
|
3267
|
+
OrganizationGraphFilterToolbar,
|
|
3268
|
+
{
|
|
3269
|
+
value: filters,
|
|
3270
|
+
onChange: onChangeFilters,
|
|
3271
|
+
disabled,
|
|
3272
|
+
resetValue
|
|
3273
|
+
}
|
|
3274
|
+
)
|
|
3275
|
+
] }) }),
|
|
3276
|
+
/* @__PURE__ */ jsx(Section, { children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
3277
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", children: [
|
|
3278
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: "Visibility" }),
|
|
3279
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "light", children: [
|
|
3280
|
+
visibilityCounts.visibleResourceCount,
|
|
3281
|
+
"/",
|
|
3282
|
+
visibilityCounts.totalResourceCount,
|
|
3283
|
+
" resources"
|
|
3284
|
+
] })
|
|
3285
|
+
] }),
|
|
3286
|
+
lens === "command-view" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3287
|
+
/* @__PURE__ */ jsx(
|
|
3288
|
+
Checkbox,
|
|
3289
|
+
{
|
|
3290
|
+
label: "Hide resource nodes",
|
|
3291
|
+
description: "Keep the organization structure readable and reveal resource nodes as needed.",
|
|
3292
|
+
checked: resourcesHidden,
|
|
3293
|
+
onChange: (event) => onResourcesHiddenChange(event.currentTarget.checked),
|
|
3294
|
+
disabled
|
|
3295
|
+
}
|
|
3296
|
+
),
|
|
3297
|
+
/* @__PURE__ */ jsx(
|
|
3298
|
+
Checkbox,
|
|
3299
|
+
{
|
|
3300
|
+
label: "Hide diagnostic and testing resources",
|
|
3301
|
+
description: "Diagnostics stay available through filters, trace, impact, and contextual reveal.",
|
|
3302
|
+
checked: diagnosticsHidden,
|
|
3303
|
+
onChange: (event) => onDiagnosticsHiddenChange(event.currentTarget.checked),
|
|
3304
|
+
disabled
|
|
3305
|
+
}
|
|
3306
|
+
),
|
|
3307
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
|
|
3308
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "light", color: visibilityCounts.hiddenResourceCount > 0 ? "yellow" : "green", children: [
|
|
3309
|
+
visibilityCounts.hiddenResourceCount,
|
|
3310
|
+
" hidden"
|
|
3311
|
+
] }),
|
|
3312
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "light", color: visibilityCounts.hiddenDiagnosticResourceCount > 0 ? "yellow" : "gray", children: [
|
|
3313
|
+
visibilityCounts.hiddenDiagnosticResourceCount,
|
|
3314
|
+
" diagnostic/testing"
|
|
3315
|
+
] })
|
|
3316
|
+
] }),
|
|
3317
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
|
|
3318
|
+
/* @__PURE__ */ jsx(Button, { size: "xs", variant: "light", onClick: handleResourceVisibilityAction, disabled, children: resourcesHidden ? "Reveal resources" : "Hide resources" }),
|
|
3319
|
+
/* @__PURE__ */ jsx(
|
|
3320
|
+
Button,
|
|
3321
|
+
{
|
|
3322
|
+
size: "xs",
|
|
3323
|
+
variant: "subtle",
|
|
3324
|
+
leftSection: /* @__PURE__ */ jsx(IconRefresh, { size: 14 }),
|
|
3325
|
+
onClick: onResetFilters,
|
|
3326
|
+
disabled,
|
|
3327
|
+
children: "Reset filters"
|
|
3328
|
+
}
|
|
3329
|
+
)
|
|
3330
|
+
] })
|
|
3331
|
+
] }) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "Visibility controls are available in the Command View lens." })
|
|
3332
|
+
] }) }),
|
|
3333
|
+
/* @__PURE__ */ jsx(Section, { children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
3334
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: "Resource Facets" }),
|
|
3335
|
+
resourceFacets.length > 0 ? /* @__PURE__ */ jsx(Stack, { gap: 4, children: resourceFacets.map((facet) => {
|
|
3336
|
+
const filterState = filters.domainFilters[facet.id] ?? "neutral";
|
|
3337
|
+
const StateIcon = FILTER_STATE_ICONS[filterState];
|
|
3338
|
+
return /* @__PURE__ */ jsx(
|
|
3339
|
+
UnstyledButton,
|
|
3340
|
+
{
|
|
3341
|
+
title: FILTER_STATE_LABELS[filterState],
|
|
3342
|
+
onClick: () => updateDomainFilter(facet.id),
|
|
3343
|
+
disabled,
|
|
3344
|
+
style: {
|
|
3345
|
+
border: "1px solid var(--color-border)",
|
|
3346
|
+
borderRadius: "var(--mantine-radius-sm)",
|
|
3347
|
+
padding: "6px 8px"
|
|
3348
|
+
},
|
|
3349
|
+
children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
3350
|
+
/* @__PURE__ */ jsx(StateIcon, { size: 16, style: { color: FILTER_STATE_COLORS[filterState], flexShrink: 0 } }),
|
|
3351
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", truncate: true, c: filterState === "neutral" ? "dimmed" : void 0, children: facet.label })
|
|
3352
|
+
] })
|
|
3353
|
+
},
|
|
3354
|
+
facet.id
|
|
3355
|
+
);
|
|
3356
|
+
}) }) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No resource facets are available for the current topology." }),
|
|
3357
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
3358
|
+
"Showing ",
|
|
3359
|
+
graph?.nodes.length ?? 0,
|
|
3360
|
+
" nodes and ",
|
|
3361
|
+
graph?.edges.length ?? 0,
|
|
3362
|
+
" edges from",
|
|
3363
|
+
" ",
|
|
3364
|
+
baseGraph?.nodes.length ?? 0,
|
|
3365
|
+
" total nodes."
|
|
3366
|
+
] })
|
|
3367
|
+
] }) })
|
|
3368
|
+
] });
|
|
3369
|
+
if (layout === "stack") {
|
|
3370
|
+
return /* @__PURE__ */ jsx(Stack, { gap: "sm", children: content });
|
|
3371
|
+
}
|
|
3372
|
+
return /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, lg: 3 }, spacing: "md", children: content });
|
|
3373
|
+
}
|
|
3374
|
+
var EXECUTION_SECTIONS = [
|
|
3375
|
+
{ status: "failed", title: "Failed Executions", badgeColor: "red" },
|
|
3376
|
+
{ status: "warning", title: "Warning Executions", badgeColor: "yellow" },
|
|
3377
|
+
{ status: "completed", title: "Successful Executions", badgeColor: null }
|
|
3378
|
+
];
|
|
3379
|
+
var HOVER_CARD_STYLE = {
|
|
3380
|
+
cursor: "pointer",
|
|
3381
|
+
transition: "box-shadow var(--duration-fast) var(--easing)",
|
|
3382
|
+
textDecoration: "none",
|
|
3383
|
+
color: "inherit"
|
|
3384
|
+
};
|
|
3385
|
+
function handleHoverEnter(e) {
|
|
3386
|
+
e.currentTarget.style.boxShadow = "var(--standard-box-shadow)";
|
|
3387
|
+
}
|
|
3388
|
+
function handleHoverLeave(e) {
|
|
3389
|
+
e.currentTarget.style.boxShadow = "";
|
|
3390
|
+
}
|
|
3391
|
+
function ExecutionStatusSection({ executions, status, title, badgeColor, resourceUrl }) {
|
|
3392
|
+
const filtered = executions.filter((e) => e.status === status);
|
|
3393
|
+
if (filtered.length === 0) return null;
|
|
3394
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
3395
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", mb: 8, children: [
|
|
3396
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 600, tt: "uppercase", children: title }),
|
|
3397
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: filtered.length })
|
|
3398
|
+
] }),
|
|
3399
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: filtered.map((execution) => /* @__PURE__ */ jsxs(
|
|
3400
|
+
Card,
|
|
3401
|
+
{
|
|
3402
|
+
padding: "xs",
|
|
3403
|
+
withBorder: true,
|
|
3404
|
+
component: "a",
|
|
3405
|
+
href: `${resourceUrl}?exec=${execution.executionId}`,
|
|
3406
|
+
target: "_blank",
|
|
3407
|
+
rel: "noopener noreferrer",
|
|
3408
|
+
style: HOVER_CARD_STYLE,
|
|
3409
|
+
onMouseEnter: handleHoverEnter,
|
|
3410
|
+
onMouseLeave: handleHoverLeave,
|
|
3411
|
+
children: [
|
|
3412
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", mb: status === "failed" && execution.errorMessage ? 4 : 0, children: [
|
|
3413
|
+
badgeColor ? /* @__PURE__ */ jsx(Badge, { size: "xs", color: badgeColor, variant: "light", children: status }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "green", children: "\u2713 Completed" }),
|
|
3414
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatRelativeTime(execution.startedAt) })
|
|
3415
|
+
] }),
|
|
3416
|
+
status === "failed" && execution.errorMessage && /* @__PURE__ */ jsx(Text, { size: "xs", c: "red", lineClamp: 2, children: execution.errorMessage })
|
|
3417
|
+
]
|
|
3418
|
+
},
|
|
3419
|
+
execution.executionId
|
|
3420
|
+
)) }),
|
|
3421
|
+
/* @__PURE__ */ jsx(Space, { h: "sm" })
|
|
3422
|
+
] });
|
|
3423
|
+
}
|
|
3424
|
+
function CommandViewSidebarContent({ timeRange }) {
|
|
3425
|
+
const theme = useMantineTheme();
|
|
3426
|
+
const colors = useCyberColors();
|
|
3427
|
+
const { organizationModel } = useElevasisFeatures();
|
|
3428
|
+
const lensConfig = useMemo(() => getOrganizationGraphLensConfig("command-view"), []);
|
|
3429
|
+
const { filters, resetFilters, updateFilters } = useOrganizationGraphFilters(lensConfig.initialFilters);
|
|
3430
|
+
const toolbarResetValue = useMemo(
|
|
3431
|
+
() => createOrganizationGraphFilters(lensConfig.initialFilters),
|
|
3432
|
+
[lensConfig.initialFilters]
|
|
3433
|
+
);
|
|
3434
|
+
const selectedNodeId = useCommandViewStore((s) => s.selectedNodeId);
|
|
3435
|
+
const resourcesHidden = useCommandViewStore((s) => s.resourcesHidden);
|
|
3436
|
+
const setResourcesHidden = useCommandViewStore((s) => s.setResourcesHidden);
|
|
3437
|
+
const diagnosticsHidden = useCommandViewStore((s) => s.diagnosticsHidden);
|
|
3438
|
+
const setDiagnosticsHidden = useCommandViewStore((s) => s.setDiagnosticsHidden);
|
|
3439
|
+
const diagnosticCategories = useCommandViewStore((s) => s.diagnosticCategories);
|
|
3440
|
+
const revealedIds = useCommandViewStore((s) => s.revealedIds);
|
|
3441
|
+
const clearRevealedIds = useCommandViewStore((s) => s.clearRevealedIds);
|
|
3442
|
+
const { data, isLoading } = useCommandViewData();
|
|
3443
|
+
const { data: statsData } = useCommandViewStats(timeRange);
|
|
3444
|
+
const cleanData = data ?? null;
|
|
3445
|
+
const dataWithStats = useMemo(() => {
|
|
3446
|
+
if (!cleanData) return null;
|
|
3447
|
+
if (!statsData) return cleanData;
|
|
3448
|
+
return mergeStatsWithTopology(cleanData, statsData);
|
|
3449
|
+
}, [cleanData, statsData]);
|
|
3450
|
+
const baseGraph = useMemo(() => {
|
|
3451
|
+
if (!organizationModel) {
|
|
3452
|
+
return null;
|
|
3453
|
+
}
|
|
3454
|
+
return buildOrganizationGraph({
|
|
3455
|
+
organizationModel,
|
|
3456
|
+
commandViewData: cleanData ?? void 0
|
|
3457
|
+
});
|
|
3458
|
+
}, [cleanData, organizationModel]);
|
|
3459
|
+
const graph = useMemo(() => {
|
|
3460
|
+
if (!baseGraph) {
|
|
3461
|
+
return null;
|
|
3462
|
+
}
|
|
3463
|
+
return filterOrganizationGraph(baseGraph, filters, {
|
|
3464
|
+
commandViewData: cleanData
|
|
3465
|
+
});
|
|
3466
|
+
}, [baseGraph, cleanData, filters]);
|
|
3467
|
+
const visibilityProjection = useMemo(() => {
|
|
3468
|
+
if (!graph) {
|
|
3469
|
+
return {
|
|
3470
|
+
hiddenIds: /* @__PURE__ */ new Set(),
|
|
3471
|
+
hiddenEdgeIds: /* @__PURE__ */ new Set(),
|
|
3472
|
+
totalResourceCount: 0,
|
|
3473
|
+
visibleResourceCount: 0,
|
|
3474
|
+
hiddenResourceCount: 0,
|
|
3475
|
+
hiddenDiagnosticResourceCount: 0
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
return getCommandViewVisibilityProjection({
|
|
3479
|
+
graph,
|
|
3480
|
+
commandViewData: cleanData,
|
|
3481
|
+
resourcesHidden,
|
|
3482
|
+
diagnosticsHidden,
|
|
3483
|
+
diagnosticCategories,
|
|
3484
|
+
revealedIds,
|
|
3485
|
+
mode: "map"
|
|
3486
|
+
});
|
|
3487
|
+
}, [cleanData, diagnosticCategories, diagnosticsHidden, graph, resourcesHidden, revealedIds]);
|
|
3488
|
+
const visibleGraph = useMemo(() => {
|
|
3489
|
+
if (!graph || visibilityProjection.hiddenIds.size === 0) {
|
|
3490
|
+
return graph;
|
|
3491
|
+
}
|
|
3492
|
+
return {
|
|
3493
|
+
...graph,
|
|
3494
|
+
nodes: graph.nodes.filter((node) => !visibilityProjection.hiddenIds.has(node.id)),
|
|
3495
|
+
edges: graph.edges.filter((edge) => !visibilityProjection.hiddenEdgeIds.has(edge.id))
|
|
3496
|
+
};
|
|
3497
|
+
}, [graph, visibilityProjection]);
|
|
3498
|
+
const handleResetFilters = () => {
|
|
3499
|
+
resetFilters();
|
|
3500
|
+
clearRevealedIds();
|
|
3501
|
+
};
|
|
3502
|
+
const handleResourcesHiddenChange = (value) => {
|
|
3503
|
+
setResourcesHidden(value);
|
|
3504
|
+
if (value) {
|
|
3505
|
+
clearRevealedIds();
|
|
3506
|
+
}
|
|
3507
|
+
};
|
|
3508
|
+
const handleDiagnosticsHiddenChange = (value) => {
|
|
3509
|
+
setDiagnosticsHidden(value);
|
|
3510
|
+
if (value) {
|
|
3511
|
+
clearRevealedIds();
|
|
3512
|
+
}
|
|
3513
|
+
};
|
|
3514
|
+
const revealAllResources = () => {
|
|
3515
|
+
setResourcesHidden(false);
|
|
3516
|
+
setDiagnosticsHidden(false);
|
|
3517
|
+
clearRevealedIds();
|
|
3518
|
+
};
|
|
3519
|
+
const { donutSuccessCount, donutFailedCount } = useMemo(() => {
|
|
3520
|
+
if (!cleanData || !statsData) return { donutSuccessCount: 0, donutFailedCount: 0 };
|
|
3521
|
+
const allResources = [
|
|
3522
|
+
...cleanData.agents,
|
|
3523
|
+
...cleanData.workflows,
|
|
3524
|
+
...cleanData.triggers,
|
|
3525
|
+
...cleanData.integrations,
|
|
3526
|
+
...cleanData.externalResources ?? [],
|
|
3527
|
+
...cleanData.humanCheckpoints ?? []
|
|
3528
|
+
];
|
|
3529
|
+
const filteredIds = new Set(allResources.map((resource) => resource.resourceId));
|
|
3530
|
+
let success = 0;
|
|
3531
|
+
let failed = 0;
|
|
3532
|
+
for (const [id, stats] of Object.entries(statsData.resources)) {
|
|
3533
|
+
if (filteredIds.has(id)) {
|
|
3534
|
+
success += stats.successCount;
|
|
3535
|
+
failed += stats.failureCount;
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
return { donutSuccessCount: success, donutFailedCount: failed };
|
|
3539
|
+
}, [cleanData, statsData]);
|
|
3540
|
+
const selectedNode = useMemo(() => {
|
|
3541
|
+
if (!selectedNodeId || !dataWithStats) return null;
|
|
3542
|
+
const allNodes = [
|
|
3543
|
+
...dataWithStats.agents || [],
|
|
3544
|
+
...dataWithStats.workflows || [],
|
|
3545
|
+
...dataWithStats.triggers || [],
|
|
3546
|
+
...dataWithStats.integrations || [],
|
|
3547
|
+
...dataWithStats.externalResources || [],
|
|
3548
|
+
...dataWithStats.humanCheckpoints || []
|
|
3549
|
+
];
|
|
3550
|
+
return allNodes.find((node) => {
|
|
3551
|
+
const nodeId2 = getNodeId(node);
|
|
3552
|
+
return nodeId2 === selectedNodeId || node.name === selectedNodeId;
|
|
3553
|
+
}) || null;
|
|
3554
|
+
}, [selectedNodeId, dataWithStats]);
|
|
3555
|
+
const isNavigable = selectedNode?.type === "agent" || selectedNode?.type === "workflow";
|
|
3556
|
+
const isHumanCheckpoint = selectedNode?.type === "human";
|
|
3557
|
+
const selectedResourceId = useMemo(() => {
|
|
3558
|
+
if (!selectedNode) return null;
|
|
3559
|
+
return getNodeId(selectedNode);
|
|
3560
|
+
}, [selectedNode]);
|
|
3561
|
+
const {
|
|
3562
|
+
page: executionPage,
|
|
3563
|
+
setPage: setExecutionPage,
|
|
3564
|
+
totalPages: totalExecutionPages
|
|
3565
|
+
} = usePaginationState(10, [selectedResourceId, timeRange]);
|
|
3566
|
+
const getNavigationUrl = () => {
|
|
3567
|
+
if (!selectedNode || !selectedResourceId) return null;
|
|
3568
|
+
if (selectedNode.type === "agent") return `/operations/resources/agent/${selectedResourceId}`;
|
|
3569
|
+
if (selectedNode.type === "workflow") return `/operations/resources/workflow/${selectedResourceId}`;
|
|
3570
|
+
if (selectedNode.type === "human") return `/operations/command-queue?checkpoint=${selectedResourceId}`;
|
|
3571
|
+
return null;
|
|
3572
|
+
};
|
|
3573
|
+
const resourceUrl = selectedNode?.type === "agent" ? `/operations/resources/agent/${selectedResourceId}` : `/operations/resources/workflow/${selectedResourceId}`;
|
|
3574
|
+
const {
|
|
3575
|
+
data: executionsData,
|
|
3576
|
+
isLoading: executionsLoading,
|
|
3577
|
+
error: executionsError
|
|
3578
|
+
} = useResourceExecutions({
|
|
3579
|
+
resourceId: selectedResourceId,
|
|
3580
|
+
timeRange,
|
|
3581
|
+
page: executionPage,
|
|
3582
|
+
limit: 10,
|
|
3583
|
+
enabled: isNavigable
|
|
3584
|
+
});
|
|
3585
|
+
const {
|
|
3586
|
+
data: checkpointTasksData,
|
|
3587
|
+
isLoading: checkpointTasksLoading,
|
|
3588
|
+
error: checkpointTasksError
|
|
3589
|
+
} = useCheckpointTasks({
|
|
3590
|
+
checkpointId: selectedResourceId,
|
|
3591
|
+
enabled: isHumanCheckpoint
|
|
3592
|
+
});
|
|
3593
|
+
const totalExecutions = donutSuccessCount + donutFailedCount;
|
|
3594
|
+
const successRate = totalExecutions > 0 ? donutSuccessCount / totalExecutions * 100 : 0;
|
|
3595
|
+
const healthSegments = [
|
|
3596
|
+
{ name: "Completed", value: donutSuccessCount, color: colors.green },
|
|
3597
|
+
{ name: "Failed", value: donutFailedCount, color: colors.red }
|
|
3598
|
+
];
|
|
3599
|
+
const centerValueColor = totalExecutions === 0 ? "var(--mantine-color-dimmed)" : successRate >= 95 ? colors.green : successRate >= 80 ? colors.yellow : colors.red;
|
|
3600
|
+
if (isLoading && !data) {
|
|
3601
|
+
return /* @__PURE__ */ jsx(SubshellSidebarLoader, {});
|
|
3602
|
+
}
|
|
3603
|
+
return /* @__PURE__ */ jsx(
|
|
3604
|
+
Box,
|
|
3605
|
+
{
|
|
3606
|
+
style: {
|
|
3607
|
+
flex: 1,
|
|
3608
|
+
height: "100%",
|
|
3609
|
+
minHeight: 0,
|
|
3610
|
+
display: "flex",
|
|
3611
|
+
flexDirection: "column",
|
|
3612
|
+
overflow: "hidden"
|
|
3613
|
+
},
|
|
3614
|
+
children: /* @__PURE__ */ jsxs("div", { style: { flex: 1, minHeight: 0, overflowY: "auto", overflowX: "hidden" }, children: [
|
|
3615
|
+
/* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconSitemap, label: "Command View" }),
|
|
3616
|
+
/* @__PURE__ */ jsx(Box, { style: { padding: theme.spacing.sm, paddingBottom: 0 }, children: /* @__PURE__ */ jsx(Box, { pb: "xs", mb: 4, children: /* @__PURE__ */ jsx(
|
|
3617
|
+
CyberDonut,
|
|
3618
|
+
{
|
|
3619
|
+
title: `Execution Health (${timeRange})`,
|
|
3620
|
+
segments: healthSegments,
|
|
3621
|
+
centerValue: totalExecutions === 0 ? "\u2014" : `${Math.round(successRate)}%`,
|
|
3622
|
+
centerLabel: `${totalExecutions} runs`,
|
|
3623
|
+
centerValueColor,
|
|
3624
|
+
glowId: "cvHealthGlow",
|
|
3625
|
+
colors
|
|
3626
|
+
}
|
|
3627
|
+
) }) }),
|
|
3628
|
+
/* @__PURE__ */ jsx(Box, { p: "sm", children: /* @__PURE__ */ jsx(
|
|
3629
|
+
FilterPanel,
|
|
3630
|
+
{
|
|
3631
|
+
filters,
|
|
3632
|
+
onChangeFilters: updateFilters,
|
|
3633
|
+
resetValue: toolbarResetValue,
|
|
3634
|
+
disabled: !baseGraph,
|
|
3635
|
+
commandViewData: cleanData,
|
|
3636
|
+
lens: "command-view",
|
|
3637
|
+
resourcesHidden,
|
|
3638
|
+
diagnosticsHidden,
|
|
3639
|
+
onResourcesHiddenChange: handleResourcesHiddenChange,
|
|
3640
|
+
onDiagnosticsHiddenChange: handleDiagnosticsHiddenChange,
|
|
3641
|
+
onRevealResources: revealAllResources,
|
|
3642
|
+
onResetFilters: handleResetFilters,
|
|
3643
|
+
visibilityCounts: visibilityProjection,
|
|
3644
|
+
graph: visibleGraph,
|
|
3645
|
+
baseGraph,
|
|
3646
|
+
layout: "stack"
|
|
3647
|
+
}
|
|
3648
|
+
) }),
|
|
3649
|
+
selectedNode && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3650
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
3651
|
+
/* @__PURE__ */ jsxs(Stack, { gap: "xs", p: "sm", mt: 8, children: [
|
|
3652
|
+
/* @__PURE__ */ jsx(Title, { order: 4, children: selectedNode.name }),
|
|
3653
|
+
selectedNode.description && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: selectedNode.description }),
|
|
3654
|
+
(isNavigable || isHumanCheckpoint) && /* @__PURE__ */ jsxs(
|
|
3655
|
+
Button,
|
|
3656
|
+
{
|
|
3657
|
+
component: "a",
|
|
3658
|
+
href: getNavigationUrl() || "#",
|
|
3659
|
+
target: "_blank",
|
|
3660
|
+
rel: "noopener noreferrer",
|
|
3661
|
+
variant: "light",
|
|
3662
|
+
size: "xs",
|
|
3663
|
+
leftSection: /* @__PURE__ */ jsx(IconExternalLink, { size: 14 }),
|
|
3664
|
+
mt: 4,
|
|
3665
|
+
children: [
|
|
3666
|
+
"Go to",
|
|
3667
|
+
" ",
|
|
3668
|
+
selectedNode.type === "agent" ? "Agent" : selectedNode.type === "workflow" ? "Workflow" : "Queue"
|
|
3669
|
+
]
|
|
3670
|
+
}
|
|
3671
|
+
),
|
|
3672
|
+
/* @__PURE__ */ jsx(Space, { h: "sm" })
|
|
3673
|
+
] })
|
|
3674
|
+
] }),
|
|
3675
|
+
selectedNode && isNavigable && /* @__PURE__ */ jsx(Stack, { p: "sm", children: executionsLoading ? /* @__PURE__ */ jsx(Center, { py: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : executionsError ? /* @__PURE__ */ jsx(APIErrorAlert, { error: executionsError, title: "Failed to load executions" }) : executionsData && executionsData.executions.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3676
|
+
EXECUTION_SECTIONS.map((section) => /* @__PURE__ */ jsx(
|
|
3677
|
+
ExecutionStatusSection,
|
|
3678
|
+
{
|
|
3679
|
+
executions: executionsData.executions,
|
|
3680
|
+
status: section.status,
|
|
3681
|
+
title: section.title,
|
|
3682
|
+
badgeColor: section.badgeColor,
|
|
3683
|
+
resourceUrl
|
|
3684
|
+
},
|
|
3685
|
+
section.status
|
|
3686
|
+
)),
|
|
3687
|
+
totalExecutionPages(executionsData.totalExecutions) > 1 && /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
|
|
3688
|
+
Pagination,
|
|
3689
|
+
{
|
|
3690
|
+
size: "sm",
|
|
3691
|
+
total: totalExecutionPages(executionsData.totalExecutions),
|
|
3692
|
+
value: executionPage,
|
|
3693
|
+
onChange: setExecutionPage,
|
|
3694
|
+
boundaries: 1
|
|
3695
|
+
}
|
|
3696
|
+
) })
|
|
3697
|
+
] }) : executionsData ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No executions in the selected time range" }) : null }),
|
|
3698
|
+
selectedNode && isHumanCheckpoint && /* @__PURE__ */ jsx(Stack, { p: "sm", children: checkpointTasksLoading ? /* @__PURE__ */ jsx(Center, { py: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : checkpointTasksError ? /* @__PURE__ */ jsx(APIErrorAlert, { error: checkpointTasksError, title: "Failed to load pending tasks" }) : checkpointTasksData && checkpointTasksData.tasks.length > 0 ? /* @__PURE__ */ jsxs("div", { children: [
|
|
3699
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", mb: 8, children: [
|
|
3700
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 600, tt: "uppercase", children: "Pending Tasks" }),
|
|
3701
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: checkpointTasksData.tasks.length })
|
|
3702
|
+
] }),
|
|
3703
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: checkpointTasksData.tasks.map((task) => /* @__PURE__ */ jsxs(
|
|
3704
|
+
Card,
|
|
3705
|
+
{
|
|
3706
|
+
padding: "xs",
|
|
3707
|
+
withBorder: true,
|
|
3708
|
+
component: "a",
|
|
3709
|
+
href: `/operations/command-queue?task=${task.id}`,
|
|
3710
|
+
target: "_blank",
|
|
3711
|
+
rel: "noopener noreferrer",
|
|
3712
|
+
style: HOVER_CARD_STYLE,
|
|
3713
|
+
onMouseEnter: handleHoverEnter,
|
|
3714
|
+
onMouseLeave: handleHoverLeave,
|
|
3715
|
+
children: [
|
|
3716
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", mb: task.description ? 4 : 0, children: [
|
|
3717
|
+
/* @__PURE__ */ jsx(Badge, { size: "xs", color: "orange", variant: "light", children: "pending" }),
|
|
3718
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatRelativeTime(task.createdAt) })
|
|
3719
|
+
] }),
|
|
3720
|
+
task.description && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 2, children: task.description })
|
|
3721
|
+
]
|
|
3722
|
+
},
|
|
3723
|
+
task.id
|
|
3724
|
+
)) })
|
|
3725
|
+
] }) : checkpointTasksData ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No pending tasks" }) : null })
|
|
3726
|
+
] })
|
|
3727
|
+
}
|
|
3728
|
+
);
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3731
|
+
export { CommandViewSidebarContent, DEFAULT_ORGANIZATION_MODEL_BRANDING, DEFAULT_ORGANIZATION_MODEL_CUSTOMERS, DEFAULT_ORGANIZATION_MODEL_GOALS, DEFAULT_ORGANIZATION_MODEL_IDENTITY, DEFAULT_ORGANIZATION_MODEL_OFFERINGS, DEFAULT_ORGANIZATION_MODEL_OPERATIONS, DEFAULT_ORGANIZATION_MODEL_PROJECTS, DEFAULT_ORGANIZATION_MODEL_ROLES, DEFAULT_ORGANIZATION_MODEL_STATUSES, ExpandAroundPanel, FilterPanel, OrganizationGraphDetailPanel, OrganizationModelSchema, buildCommandViewDrillDownSections, buildOrganizationGraph, createOrganizationGraphFilters, expandAroundGraph, filterOrganizationGraph, formatOrganizationGraphTraceNodeOptionLabel, getCommandViewOperationalOverview, getCommandViewSelectionOperationalSummary, getCommandViewVisibilityProjection, getConnectedHiddenResourceIds, getOrganizationGraphLensConfig, getOrganizationGraphTraceNodeOptions, resolveOrganizationGraphPathTrace, useOrganizationGraphFilters };
|