@echothink-ui/project 0.1.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.
Files changed (55) hide show
  1. package/README.md +5 -0
  2. package/dist/components/ProjectActivityTimeline.d.ts +5 -0
  3. package/dist/components/ProjectAppDomainPanel.d.ts +8 -0
  4. package/dist/components/ProjectCard.d.ts +8 -0
  5. package/dist/components/ProjectCreateForm.d.ts +7 -0
  6. package/dist/components/ProjectDashboardTemplate.d.ts +11 -0
  7. package/dist/components/ProjectManagementPage.d.ts +17 -0
  8. package/dist/components/ProjectMembersPanel.d.ts +9 -0
  9. package/dist/components/ProjectModelConfigPanel.d.ts +9 -0
  10. package/dist/components/ProjectPermissionPanel.d.ts +9 -0
  11. package/dist/components/ProjectResourcePanel.d.ts +6 -0
  12. package/dist/components/ProjectScopeSelector.d.ts +7 -0
  13. package/dist/components/ProjectSettingsPanel.d.ts +6 -0
  14. package/dist/components/ProjectStatusSummary.d.ts +6 -0
  15. package/dist/components/ProjectSummaryPanel.d.ts +5 -0
  16. package/dist/components/ProjectTab.d.ts +3 -0
  17. package/dist/components/ProjectTabGroup.d.ts +12 -0
  18. package/dist/components/ProjectTable.d.ts +9 -0
  19. package/dist/index.cjs +2112 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/index.css +2059 -0
  22. package/dist/index.css.map +1 -0
  23. package/dist/index.d.ts +21 -0
  24. package/dist/index.js +2098 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/types.d.ts +99 -0
  27. package/dist/utils.d.ts +288 -0
  28. package/package.json +45 -0
  29. package/src/components/ProjectActivityTimeline.test.tsx +43 -0
  30. package/src/components/ProjectActivityTimeline.tsx +118 -0
  31. package/src/components/ProjectAppDomainPanel.tsx +147 -0
  32. package/src/components/ProjectCard.tsx +117 -0
  33. package/src/components/ProjectCreateForm.test.tsx +45 -0
  34. package/src/components/ProjectCreateForm.tsx +176 -0
  35. package/src/components/ProjectDashboardTemplate.tsx +107 -0
  36. package/src/components/ProjectManagementPage.tsx +112 -0
  37. package/src/components/ProjectMembersPanel.tsx +181 -0
  38. package/src/components/ProjectModelConfigPanel.tsx +294 -0
  39. package/src/components/ProjectPermissionPanel.tsx +174 -0
  40. package/src/components/ProjectResourcePanel.tsx +154 -0
  41. package/src/components/ProjectScopeSelector.test.tsx +50 -0
  42. package/src/components/ProjectScopeSelector.tsx +92 -0
  43. package/src/components/ProjectSettingsPanel.test.tsx +25 -0
  44. package/src/components/ProjectSettingsPanel.tsx +244 -0
  45. package/src/components/ProjectStatusSummary.tsx +165 -0
  46. package/src/components/ProjectSummaryPanel.test.tsx +37 -0
  47. package/src/components/ProjectSummaryPanel.tsx +85 -0
  48. package/src/components/ProjectTab.tsx +8 -0
  49. package/src/components/ProjectTabGroup.tsx +38 -0
  50. package/src/components/ProjectTable.tsx +138 -0
  51. package/src/index.test.tsx +337 -0
  52. package/src/index.tsx +41 -0
  53. package/src/styles.css +2431 -0
  54. package/src/types.ts +111 -0
  55. package/src/utils.ts +96 -0
package/dist/index.js ADDED
@@ -0,0 +1,2098 @@
1
+ // src/components/ProjectManagementPage.tsx
2
+ import * as React3 from "react";
3
+ import { Button as Button2, EmptyState as EmptyState2 } from "@echothink-ui/core";
4
+ import { PageHeader } from "@echothink-ui/layouts";
5
+
6
+ // src/utils.ts
7
+ function classNames(...values) {
8
+ return values.filter(Boolean).join(" ");
9
+ }
10
+ function healthToStatus(health) {
11
+ if (health === "critical") return "failed";
12
+ if (health === "warning") return "warning";
13
+ return "synced";
14
+ }
15
+ function healthLabel(health) {
16
+ if (health === "critical") return "Critical";
17
+ if (health === "warning") return "Warning";
18
+ return "Healthy";
19
+ }
20
+ function statusToSeverity(status) {
21
+ if (["failed", "blocked", "approval-required"].includes(status)) return "danger";
22
+ if (["warning", "stale", "pending-approval"].includes(status)) return "warning";
23
+ if (["succeeded", "completed", "synced", "active"].includes(status)) return "success";
24
+ if (["inactive", "not-started", "paused"].includes(status)) return "neutral";
25
+ return "info";
26
+ }
27
+ function summarizeProjects(projects) {
28
+ const byStatus = {};
29
+ let healthyCount = 0;
30
+ let blockedCount = 0;
31
+ for (const project of projects) {
32
+ byStatus[project.status] = (byStatus[project.status] ?? 0) + 1;
33
+ if (project.healthStatus === "healthy" || ["completed", "succeeded", "synced", "active"].includes(project.status)) {
34
+ healthyCount += 1;
35
+ }
36
+ if (["blocked", "failed", "approval-required"].includes(project.status)) {
37
+ blockedCount += 1;
38
+ }
39
+ }
40
+ return {
41
+ byStatus,
42
+ healthyPercent: projects.length ? Math.round(healthyCount / projects.length * 100) : 0,
43
+ blockedCount
44
+ };
45
+ }
46
+ function entityId(entity) {
47
+ return typeof entity === "string" ? entity : entity.id;
48
+ }
49
+ function entityLabel(entity) {
50
+ return typeof entity === "string" ? entity : entity.label;
51
+ }
52
+ function permissionKey(subject, resource, action) {
53
+ return `${entityId(subject)}:${entityId(resource)}:${entityId(action)}`;
54
+ }
55
+ function formatNumber(value) {
56
+ return new Intl.NumberFormat("en-US").format(value ?? 0);
57
+ }
58
+ function projectHtmlProps(props) {
59
+ const {
60
+ title,
61
+ subtitle,
62
+ description,
63
+ eyebrow,
64
+ density,
65
+ status,
66
+ severity,
67
+ loading,
68
+ empty,
69
+ error,
70
+ footer,
71
+ children,
72
+ ...htmlProps
73
+ } = props;
74
+ return htmlProps;
75
+ }
76
+
77
+ // src/components/ProjectStatusSummary.tsx
78
+ import * as React from "react";
79
+ import {
80
+ Badge,
81
+ StatusDot,
82
+ statusLabel
83
+ } from "@echothink-ui/core";
84
+ import { KPIBlock } from "@echothink-ui/charts";
85
+ import { jsx, jsxs } from "react/jsx-runtime";
86
+ var priorityStatuses = [
87
+ "running",
88
+ "in-progress",
89
+ "approval-required",
90
+ "blocked",
91
+ "failed",
92
+ "queued",
93
+ "pending-approval",
94
+ "warning",
95
+ "stale",
96
+ "paused",
97
+ "completed",
98
+ "succeeded",
99
+ "synced",
100
+ "active",
101
+ "not-started",
102
+ "inactive"
103
+ ];
104
+ var statusClassNames = {
105
+ "approval-required": "eth-project-status-summary__bar-segment--attention",
106
+ "pending-approval": "eth-project-status-summary__bar-segment--warning",
107
+ "in-progress": "eth-project-status-summary__bar-segment--inflight",
108
+ blocked: "eth-project-status-summary__bar-segment--attention",
109
+ failed: "eth-project-status-summary__bar-segment--attention",
110
+ running: "eth-project-status-summary__bar-segment--inflight",
111
+ queued: "eth-project-status-summary__bar-segment--queued",
112
+ warning: "eth-project-status-summary__bar-segment--warning",
113
+ stale: "eth-project-status-summary__bar-segment--warning",
114
+ paused: "eth-project-status-summary__bar-segment--paused",
115
+ completed: "eth-project-status-summary__bar-segment--complete",
116
+ succeeded: "eth-project-status-summary__bar-segment--complete",
117
+ synced: "eth-project-status-summary__bar-segment--complete",
118
+ active: "eth-project-status-summary__bar-segment--complete",
119
+ "not-started": "eth-project-status-summary__bar-segment--queued",
120
+ inactive: "eth-project-status-summary__bar-segment--queued"
121
+ };
122
+ function ProjectStatusSummary({
123
+ projects = [],
124
+ summary,
125
+ title = "Project status",
126
+ className,
127
+ ...props
128
+ }) {
129
+ const headingId = React.useId();
130
+ const computed = summary ?? summarizeProjects(projects);
131
+ const total = Object.values(computed.byStatus).reduce((sum, value) => sum + (value ?? 0), 0);
132
+ const activeCount = (computed.byStatus.running ?? 0) + (computed.byStatus["in-progress"] ?? 0);
133
+ const prioritizedStatuses = priorityStatuses.filter((status) => computed.byStatus[status]);
134
+ const additionalStatuses = Object.keys(computed.byStatus).filter(
135
+ (status) => !priorityStatuses.includes(status) && Boolean(computed.byStatus[status])
136
+ );
137
+ const displayedStatuses = [...prioritizedStatuses, ...additionalStatuses];
138
+ const statusSummary = total ? `${total} project${total === 1 ? "" : "s"} across ${displayedStatuses.length} status${displayedStatuses.length === 1 ? "" : "es"}` : "No tracked projects";
139
+ const attentionLabel = total ? computed.blockedCount ? `${computed.blockedCount} needs attention` : "Operational" : "No projects";
140
+ const attentionSeverity = total ? computed.blockedCount ? "danger" : "success" : "neutral";
141
+ return /* @__PURE__ */ jsxs(
142
+ "section",
143
+ {
144
+ ...projectHtmlProps(props),
145
+ className: classNames("eth-project-status-summary", className),
146
+ "data-eth-component": "ProjectStatusSummary",
147
+ role: "region",
148
+ "aria-labelledby": headingId,
149
+ tabIndex: -1,
150
+ children: [
151
+ /* @__PURE__ */ jsxs("header", { className: "eth-project-status-summary__header", children: [
152
+ /* @__PURE__ */ jsxs("div", { className: "eth-project-status-summary__heading", children: [
153
+ /* @__PURE__ */ jsx("h3", { id: headingId, children: title }),
154
+ /* @__PURE__ */ jsx("p", { children: statusSummary })
155
+ ] }),
156
+ /* @__PURE__ */ jsx(Badge, { severity: attentionSeverity, children: attentionLabel })
157
+ ] }),
158
+ /* @__PURE__ */ jsxs("div", { className: "eth-project-status-summary__kpis", children: [
159
+ /* @__PURE__ */ jsx(KPIBlock, { title: "Projects", value: total, description: "Total tracked" }),
160
+ /* @__PURE__ */ jsx(KPIBlock, { title: "Healthy", value: `${computed.healthyPercent}%`, description: "Operational" }),
161
+ /* @__PURE__ */ jsx(KPIBlock, { title: "Active", value: activeCount, description: "Running or in progress" }),
162
+ /* @__PURE__ */ jsx(KPIBlock, { title: "Blocked", value: computed.blockedCount, description: "Needs attention" })
163
+ ] }),
164
+ /* @__PURE__ */ jsxs("div", { className: "eth-project-status-summary__distribution", children: [
165
+ /* @__PURE__ */ jsx(
166
+ "div",
167
+ {
168
+ className: "eth-project-status-summary__bar",
169
+ "aria-label": "Project status distribution",
170
+ role: "list",
171
+ children: displayedStatuses.length ? displayedStatuses.map((status) => {
172
+ const count = computed.byStatus[status] ?? 0;
173
+ return /* @__PURE__ */ jsx(
174
+ "span",
175
+ {
176
+ "aria-label": `${statusLabel(status)}: ${count}`,
177
+ className: classNames(
178
+ "eth-project-status-summary__bar-segment",
179
+ statusClassNames[status],
180
+ `eth-project-status-summary__bar-segment--${status}`
181
+ ),
182
+ role: "listitem",
183
+ style: { flexGrow: count },
184
+ title: `${statusLabel(status)}: ${count}`
185
+ },
186
+ status
187
+ );
188
+ }) : /* @__PURE__ */ jsx(
189
+ "span",
190
+ {
191
+ "aria-label": "No project status data",
192
+ className: "eth-project-status-summary__bar-segment",
193
+ role: "listitem",
194
+ style: { flexGrow: 1 }
195
+ }
196
+ )
197
+ }
198
+ ),
199
+ displayedStatuses.length ? /* @__PURE__ */ jsx("div", { className: "eth-project-status-summary__legend", role: "list", children: displayedStatuses.map((status) => /* @__PURE__ */ jsx("span", { role: "listitem", children: /* @__PURE__ */ jsx(
200
+ StatusDot,
201
+ {
202
+ status,
203
+ label: `${statusLabel(status)} ${computed.byStatus[status]}`
204
+ }
205
+ ) }, status)) }) : /* @__PURE__ */ jsx("p", { className: "eth-project-status-summary__empty", children: "No project status data" })
206
+ ] })
207
+ ]
208
+ }
209
+ );
210
+ }
211
+
212
+ // src/components/ProjectTable.tsx
213
+ import * as React2 from "react";
214
+ import {
215
+ Badge as Badge2,
216
+ Button,
217
+ EmptyState,
218
+ statusLabel as statusLabel2
219
+ } from "@echothink-ui/core";
220
+ import { DataTable } from "@echothink-ui/data";
221
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
222
+ function ProjectTable({
223
+ projects = [],
224
+ onRowSelect,
225
+ selectedProjectId,
226
+ rowActions,
227
+ density = "compact",
228
+ className,
229
+ ...props
230
+ }) {
231
+ const columns = React2.useMemo(
232
+ () => [
233
+ {
234
+ key: "name",
235
+ header: "Name",
236
+ width: "34%",
237
+ render: (project) => /* @__PURE__ */ jsxs2("div", { className: "eth-project-table__name", children: [
238
+ onRowSelect ? /* @__PURE__ */ jsx2(
239
+ Button,
240
+ {
241
+ className: "eth-project-table__title eth-project-table__title-button",
242
+ intent: "ghost",
243
+ onClick: () => onRowSelect(project.id),
244
+ children: project.name
245
+ }
246
+ ) : /* @__PURE__ */ jsx2("strong", { className: "eth-project-table__title", children: project.name }),
247
+ project.description ? /* @__PURE__ */ jsx2("small", { className: "eth-project-table__description", children: project.description }) : null
248
+ ] })
249
+ },
250
+ {
251
+ key: "status",
252
+ header: "Status",
253
+ width: "10rem",
254
+ render: (project) => /* @__PURE__ */ jsx2("span", { className: "eth-project-table__status", children: /* @__PURE__ */ jsx2(Badge2, { severity: statusToSeverity(project.status), children: statusLabel2(project.status) }) })
255
+ },
256
+ {
257
+ key: "ownerLabel",
258
+ header: "Owner",
259
+ width: "7rem",
260
+ render: (project) => /* @__PURE__ */ jsx2("span", { className: "eth-project-table__owner", children: project.ownerLabel ?? "Unassigned" })
261
+ },
262
+ {
263
+ key: "appDomainsCount",
264
+ header: "App domains",
265
+ width: "7.5rem",
266
+ align: "end",
267
+ render: (project) => /* @__PURE__ */ jsx2("span", { className: "eth-project-table__metric", children: formatNumber(project.appDomainsCount) })
268
+ },
269
+ {
270
+ key: "openTasksCount",
271
+ header: "Open tasks",
272
+ width: "7.5rem",
273
+ align: "end",
274
+ render: (project) => /* @__PURE__ */ jsx2("span", { className: "eth-project-table__metric", children: formatNumber(project.openTasksCount) })
275
+ },
276
+ {
277
+ key: "updatedAt",
278
+ header: "Updated",
279
+ width: "7.5rem",
280
+ render: (project) => /* @__PURE__ */ jsx2("span", { className: "eth-project-table__updated", children: project.updatedAt ?? "-" })
281
+ }
282
+ ],
283
+ [onRowSelect]
284
+ );
285
+ const htmlProps = projectHtmlProps(props);
286
+ return /* @__PURE__ */ jsx2(
287
+ "section",
288
+ {
289
+ ...htmlProps,
290
+ className: `eth-project-table ${className ?? ""}`,
291
+ "data-eth-component": "ProjectTable",
292
+ role: "region",
293
+ "aria-label": htmlProps["aria-label"] ?? "Project list",
294
+ tabIndex: -1,
295
+ children: /* @__PURE__ */ jsx2(
296
+ DataTable,
297
+ {
298
+ "aria-label": "Project list",
299
+ className: "eth-project-table__table",
300
+ density,
301
+ rows: projects,
302
+ columns,
303
+ rowKey: "id",
304
+ selectedRows: selectedProjectId ? [selectedProjectId] : [],
305
+ rowActions,
306
+ emptyState: /* @__PURE__ */ jsx2(
307
+ EmptyState,
308
+ {
309
+ title: "No projects",
310
+ description: "Create a project to start organizing resources and app domains."
311
+ }
312
+ )
313
+ }
314
+ )
315
+ }
316
+ );
317
+ }
318
+
319
+ // src/components/ProjectManagementPage.tsx
320
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
321
+ function ProjectManagementPage({
322
+ projects = [],
323
+ onCreate,
324
+ onSelect,
325
+ onFilterSelect,
326
+ filters = [],
327
+ activeProjectId,
328
+ title = "Projects",
329
+ subtitle = "Manage project workspaces, resources, and installed app domains.",
330
+ className,
331
+ actions,
332
+ ...props
333
+ }) {
334
+ const headerActions = React3.useMemo(() => {
335
+ const providedActions = actions ?? [];
336
+ const hasCreateAction = providedActions.some((action) => {
337
+ if (action.id === "new-project") return true;
338
+ return typeof action.label === "string" && action.label.trim().toLowerCase() === "new project";
339
+ });
340
+ if (!onCreate || hasCreateAction) return providedActions;
341
+ return [
342
+ ...providedActions,
343
+ {
344
+ id: "new-project",
345
+ label: "New project",
346
+ intent: "primary",
347
+ onSelect: onCreate
348
+ }
349
+ ];
350
+ }, [actions, onCreate]);
351
+ return /* @__PURE__ */ jsxs3(
352
+ "main",
353
+ {
354
+ ...projectHtmlProps(props),
355
+ className: `eth-project-management-page ${className ?? ""}`,
356
+ "data-eth-component": "ProjectManagementPage",
357
+ children: [
358
+ /* @__PURE__ */ jsx3(PageHeader, { title, subtitle, actions: headerActions }),
359
+ filters.length ? /* @__PURE__ */ jsx3(
360
+ "div",
361
+ {
362
+ className: "eth-project-management-page__filters",
363
+ role: "group",
364
+ "aria-label": "Project filters",
365
+ children: filters.map((filter) => /* @__PURE__ */ jsx3(
366
+ "button",
367
+ {
368
+ type: "button",
369
+ className: `eth-project-management-page__filter ${filter.active ? "eth-project-management-page__filter--active" : ""}`,
370
+ "aria-pressed": Boolean(filter.active),
371
+ "aria-disabled": !onFilterSelect ? true : void 0,
372
+ tabIndex: !onFilterSelect ? -1 : void 0,
373
+ onClick: onFilterSelect ? () => onFilterSelect(filter.id) : void 0,
374
+ children: filter.label
375
+ },
376
+ filter.id
377
+ ))
378
+ }
379
+ ) : null,
380
+ projects.length ? /* @__PURE__ */ jsxs3(Fragment, { children: [
381
+ /* @__PURE__ */ jsx3(ProjectStatusSummary, { projects }),
382
+ /* @__PURE__ */ jsx3(
383
+ ProjectTable,
384
+ {
385
+ projects,
386
+ onRowSelect: onSelect,
387
+ selectedProjectId: activeProjectId,
388
+ "aria-label": activeProjectId ? `Projects, active ${activeProjectId}` : "Projects"
389
+ }
390
+ )
391
+ ] }) : /* @__PURE__ */ jsx3(
392
+ EmptyState2,
393
+ {
394
+ title: "No projects yet",
395
+ description: "Create a project to connect resources, members, model settings, and app domains.",
396
+ action: onCreate ? /* @__PURE__ */ jsx3(Button2, { intent: "primary", onClick: onCreate, children: "New project" }) : void 0
397
+ }
398
+ )
399
+ ]
400
+ }
401
+ );
402
+ }
403
+
404
+ // src/components/ProjectCard.tsx
405
+ import {
406
+ ActionGroup,
407
+ Badge as Badge3,
408
+ Button as Button3,
409
+ StatusDot as StatusDot2,
410
+ statusLabel as statusLabel3
411
+ } from "@echothink-ui/core";
412
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
413
+ function ProjectCard({
414
+ project,
415
+ onSelect,
416
+ actions,
417
+ className,
418
+ ...props
419
+ }) {
420
+ const resolvedProject = project ?? {
421
+ id: "project",
422
+ name: "Project",
423
+ status: "not-started",
424
+ description: "Project summary"
425
+ };
426
+ const titleId = `${resolvedProject.id}-title`;
427
+ const descriptionId = resolvedProject.description ? `${resolvedProject.id}-description` : void 0;
428
+ return /* @__PURE__ */ jsxs4(
429
+ "article",
430
+ {
431
+ ...projectHtmlProps(props),
432
+ className: classNames(
433
+ "eth-project-card",
434
+ `eth-project-card--${resolvedProject.status}`,
435
+ className
436
+ ),
437
+ "data-eth-component": "ProjectCard",
438
+ "data-status": resolvedProject.status,
439
+ role: "region",
440
+ "aria-labelledby": titleId,
441
+ "aria-describedby": descriptionId,
442
+ tabIndex: -1,
443
+ children: [
444
+ /* @__PURE__ */ jsxs4("header", { className: "eth-project-card__header", children: [
445
+ /* @__PURE__ */ jsxs4("div", { className: "eth-project-card__heading", children: [
446
+ /* @__PURE__ */ jsx4("h3", { id: titleId, children: resolvedProject.name }),
447
+ resolvedProject.description ? /* @__PURE__ */ jsx4("p", { id: descriptionId, children: resolvedProject.description }) : null
448
+ ] }),
449
+ /* @__PURE__ */ jsx4("div", { className: "eth-project-card__status", children: /* @__PURE__ */ jsx4(Badge3, { severity: statusToSeverity(resolvedProject.status), children: statusLabel3(resolvedProject.status) }) })
450
+ ] }),
451
+ /* @__PURE__ */ jsxs4("dl", { className: "eth-project-card__metadata", children: [
452
+ /* @__PURE__ */ jsxs4("div", { children: [
453
+ /* @__PURE__ */ jsx4("dt", { children: "Owner" }),
454
+ /* @__PURE__ */ jsx4("dd", { children: resolvedProject.ownerLabel ?? "Unassigned" })
455
+ ] }),
456
+ /* @__PURE__ */ jsxs4("div", { children: [
457
+ /* @__PURE__ */ jsx4("dt", { children: "Updated" }),
458
+ /* @__PURE__ */ jsx4("dd", { children: resolvedProject.updatedAt ?? "Not updated" })
459
+ ] }),
460
+ /* @__PURE__ */ jsxs4("div", { children: [
461
+ /* @__PURE__ */ jsx4("dt", { children: "App domains" }),
462
+ /* @__PURE__ */ jsx4("dd", { children: formatNumber(resolvedProject.appDomainsCount) })
463
+ ] }),
464
+ /* @__PURE__ */ jsxs4("div", { children: [
465
+ /* @__PURE__ */ jsx4("dt", { children: "Open tasks" }),
466
+ /* @__PURE__ */ jsx4("dd", { children: formatNumber(resolvedProject.openTasksCount) })
467
+ ] }),
468
+ /* @__PURE__ */ jsxs4("div", { children: [
469
+ /* @__PURE__ */ jsx4("dt", { children: "Health" }),
470
+ /* @__PURE__ */ jsx4("dd", { children: /* @__PURE__ */ jsx4(
471
+ StatusDot2,
472
+ {
473
+ status: healthToStatus(resolvedProject.healthStatus),
474
+ label: healthLabel(resolvedProject.healthStatus)
475
+ }
476
+ ) })
477
+ ] })
478
+ ] }),
479
+ onSelect || actions?.length ? /* @__PURE__ */ jsxs4("footer", { className: "eth-project-card__actions", children: [
480
+ onSelect ? /* @__PURE__ */ jsx4(
481
+ Button3,
482
+ {
483
+ intent: "secondary",
484
+ density: "compact",
485
+ onClick: () => onSelect(resolvedProject.id),
486
+ children: "Open project"
487
+ }
488
+ ) : null,
489
+ /* @__PURE__ */ jsx4(ActionGroup, { actions })
490
+ ] }) : null
491
+ ]
492
+ }
493
+ );
494
+ }
495
+
496
+ // src/components/ProjectTab.tsx
497
+ import { BrowserTab } from "@echothink-ui/layouts";
498
+ import { jsx as jsx5 } from "react/jsx-runtime";
499
+ function ProjectTab({ className, ...props }) {
500
+ return /* @__PURE__ */ jsx5(BrowserTab, { ...props, className: classNames("eth-project-tab", className) });
501
+ }
502
+
503
+ // src/components/ProjectCreateForm.tsx
504
+ import * as React4 from "react";
505
+ import {
506
+ Button as Button4,
507
+ FormField,
508
+ FormSection,
509
+ FormValidationMessage,
510
+ Select,
511
+ TextInput,
512
+ Textarea
513
+ } from "@echothink-ui/core";
514
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
515
+ var templateOptions = [
516
+ { value: "blank", label: "Blank project" },
517
+ { value: "software-delivery", label: "Software delivery" },
518
+ { value: "research", label: "Research workspace" },
519
+ { value: "operations", label: "Operations runbook" }
520
+ ];
521
+ function ProjectCreateForm({
522
+ defaults,
523
+ onSubmit,
524
+ onCancel,
525
+ title = "Create project",
526
+ description = "Start a project workspace with an owner, template, and operating context.",
527
+ className,
528
+ ...props
529
+ }) {
530
+ const [values, setValues] = React4.useState({
531
+ name: defaults?.name ?? "",
532
+ description: defaults?.description ?? "",
533
+ ownerLabel: defaults?.ownerLabel ?? "",
534
+ template: defaults?.template ?? "blank"
535
+ });
536
+ const [nameError, setNameError] = React4.useState();
537
+ const formId = React4.useId().replace(/:/g, "");
538
+ const titleId = `eth-project-create-form-${formId}-title`;
539
+ const descriptionId = `eth-project-create-form-${formId}-description`;
540
+ const htmlProps = projectHtmlProps(props);
541
+ const describedBy = [htmlProps["aria-describedby"], description ? descriptionId : void 0].filter(Boolean).join(" ") || void 0;
542
+ const update = (field, value) => {
543
+ setValues((current) => ({ ...current, [field]: value }));
544
+ if (field === "name" && value.trim()) setNameError(void 0);
545
+ };
546
+ const submit = (event) => {
547
+ event.preventDefault();
548
+ if (!values.name.trim()) {
549
+ setNameError("Project name is required.");
550
+ return;
551
+ }
552
+ onSubmit?.({ ...values, name: values.name.trim() });
553
+ };
554
+ return /* @__PURE__ */ jsxs5(
555
+ "form",
556
+ {
557
+ ...htmlProps,
558
+ className: classNames("eth-project-create-form", className),
559
+ "data-eth-component": "ProjectCreateForm",
560
+ "aria-labelledby": htmlProps["aria-labelledby"] ?? (title ? titleId : void 0),
561
+ "aria-describedby": describedBy,
562
+ onSubmit: submit,
563
+ noValidate: true,
564
+ children: [
565
+ title || description ? /* @__PURE__ */ jsxs5("header", { className: "eth-project-create-form__header", children: [
566
+ title ? /* @__PURE__ */ jsx6("h2", { id: titleId, className: "eth-project-create-form__title", children: title }) : null,
567
+ description ? /* @__PURE__ */ jsx6("p", { id: descriptionId, className: "eth-project-create-form__description", children: description }) : null
568
+ ] }) : null,
569
+ nameError ? /* @__PURE__ */ jsx6(FormValidationMessage, { className: "eth-project-create-form__validation", children: nameError }) : null,
570
+ /* @__PURE__ */ jsx6(
571
+ FormSection,
572
+ {
573
+ className: "eth-project-create-form__section",
574
+ title: "Project details",
575
+ description: "Define the visible identity and initial scope for the workspace.",
576
+ children: /* @__PURE__ */ jsxs5("div", { className: "eth-project-create-form__grid", children: [
577
+ /* @__PURE__ */ jsx6(
578
+ FormField,
579
+ {
580
+ id: "project-name",
581
+ label: "Name",
582
+ helperText: "Shown in project lists, tabs, and approval trails.",
583
+ required: true,
584
+ error: nameError,
585
+ className: "eth-project-create-form__field",
586
+ children: /* @__PURE__ */ jsx6(
587
+ TextInput,
588
+ {
589
+ value: values.name,
590
+ onChange: (event) => update("name", event.currentTarget.value),
591
+ autoFocus: true
592
+ }
593
+ )
594
+ }
595
+ ),
596
+ /* @__PURE__ */ jsx6(
597
+ FormField,
598
+ {
599
+ id: "project-owner",
600
+ label: "Owner",
601
+ helperText: "Person or group accountable for project changes.",
602
+ className: "eth-project-create-form__field",
603
+ children: /* @__PURE__ */ jsx6(
604
+ TextInput,
605
+ {
606
+ value: values.ownerLabel,
607
+ onChange: (event) => update("ownerLabel", event.currentTarget.value)
608
+ }
609
+ )
610
+ }
611
+ ),
612
+ /* @__PURE__ */ jsx6(
613
+ FormField,
614
+ {
615
+ id: "project-description",
616
+ label: "Description",
617
+ helperText: "Summarize the purpose, key outputs, or operating window.",
618
+ className: "eth-project-create-form__field eth-project-create-form__field--wide",
619
+ children: /* @__PURE__ */ jsx6(
620
+ Textarea,
621
+ {
622
+ value: values.description,
623
+ onChange: (event) => update("description", event.currentTarget.value),
624
+ rows: 4
625
+ }
626
+ )
627
+ }
628
+ )
629
+ ] })
630
+ }
631
+ ),
632
+ /* @__PURE__ */ jsx6(
633
+ FormSection,
634
+ {
635
+ className: "eth-project-create-form__section",
636
+ title: "Setup",
637
+ description: "Select the starting structure for tasks, resources, and app-domain links.",
638
+ children: /* @__PURE__ */ jsx6("div", { className: "eth-project-create-form__grid eth-project-create-form__grid--setup", children: /* @__PURE__ */ jsx6(
639
+ FormField,
640
+ {
641
+ id: "project-template",
642
+ label: "Template",
643
+ helperText: "Templates prefill common sections without locking future changes.",
644
+ className: "eth-project-create-form__field",
645
+ children: /* @__PURE__ */ jsx6(
646
+ Select,
647
+ {
648
+ options: templateOptions,
649
+ value: values.template,
650
+ onChange: (event) => update("template", event.currentTarget.value)
651
+ }
652
+ )
653
+ }
654
+ ) })
655
+ }
656
+ ),
657
+ /* @__PURE__ */ jsxs5("footer", { className: "eth-project-create-form__actions", children: [
658
+ onCancel ? /* @__PURE__ */ jsx6(Button4, { intent: "secondary", onClick: onCancel, children: "Cancel" }) : null,
659
+ /* @__PURE__ */ jsx6(Button4, { type: "submit", intent: "primary", children: "Create project" })
660
+ ] })
661
+ ]
662
+ }
663
+ );
664
+ }
665
+
666
+ // src/components/ProjectSummaryPanel.tsx
667
+ import { Badge as Badge4, StatusDot as StatusDot3, statusLabel as statusLabel4 } from "@echothink-ui/core";
668
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
669
+ function ProjectSummaryPanel({ project, className, ...props }) {
670
+ const resolvedProject = project ?? {
671
+ id: "project",
672
+ name: "Project",
673
+ status: "not-started",
674
+ description: "Project summary"
675
+ };
676
+ const titleId = `${resolvedProject.id}-summary-title`;
677
+ const descriptionId = resolvedProject.description ? `${resolvedProject.id}-summary-description` : void 0;
678
+ const metadataItems = [
679
+ { label: "Project ID", value: resolvedProject.id },
680
+ { label: "Owner", value: resolvedProject.ownerLabel ?? "Unassigned" },
681
+ { label: "Updated", value: resolvedProject.updatedAt ?? "Not updated" },
682
+ { label: "App domains", value: formatNumber(resolvedProject.appDomainsCount) },
683
+ { label: "Open tasks", value: formatNumber(resolvedProject.openTasksCount) }
684
+ ];
685
+ return /* @__PURE__ */ jsxs6(
686
+ "section",
687
+ {
688
+ ...projectHtmlProps(props),
689
+ className: classNames(
690
+ "eth-project-summary-panel",
691
+ `eth-project-summary-panel--${resolvedProject.status}`,
692
+ className
693
+ ),
694
+ "data-eth-component": "ProjectSummaryPanel",
695
+ "data-status": resolvedProject.status,
696
+ role: "region",
697
+ "aria-labelledby": titleId,
698
+ "aria-describedby": descriptionId,
699
+ tabIndex: -1,
700
+ children: [
701
+ /* @__PURE__ */ jsxs6("header", { className: "eth-project-summary-panel__header", children: [
702
+ /* @__PURE__ */ jsxs6("div", { className: "eth-project-summary-panel__heading", children: [
703
+ /* @__PURE__ */ jsx7("h3", { id: titleId, children: resolvedProject.name }),
704
+ resolvedProject.description ? /* @__PURE__ */ jsx7("p", { id: descriptionId, children: resolvedProject.description }) : null
705
+ ] }),
706
+ /* @__PURE__ */ jsx7("div", { className: "eth-project-summary-panel__status", children: /* @__PURE__ */ jsx7(Badge4, { severity: statusToSeverity(resolvedProject.status), children: statusLabel4(resolvedProject.status) }) })
707
+ ] }),
708
+ /* @__PURE__ */ jsxs6("dl", { className: "eth-project-summary-panel__metadata", children: [
709
+ metadataItems.map((item) => /* @__PURE__ */ jsxs6("div", { className: "eth-project-summary-panel__metadata-item", children: [
710
+ /* @__PURE__ */ jsx7("dt", { children: item.label }),
711
+ /* @__PURE__ */ jsx7("dd", { children: item.value })
712
+ ] }, item.label)),
713
+ /* @__PURE__ */ jsxs6(
714
+ "div",
715
+ {
716
+ className: "eth-project-summary-panel__metadata-item eth-project-summary-panel__metadata-item--health",
717
+ children: [
718
+ /* @__PURE__ */ jsx7("dt", { children: "Health" }),
719
+ /* @__PURE__ */ jsx7("dd", { children: /* @__PURE__ */ jsx7(
720
+ StatusDot3,
721
+ {
722
+ status: healthToStatus(resolvedProject.healthStatus),
723
+ label: healthLabel(resolvedProject.healthStatus)
724
+ }
725
+ ) })
726
+ ]
727
+ }
728
+ )
729
+ ] })
730
+ ]
731
+ }
732
+ );
733
+ }
734
+
735
+ // src/components/ProjectSettingsPanel.tsx
736
+ import * as React5 from "react";
737
+ import {
738
+ FormField as FormField2,
739
+ FormSection as FormSection2,
740
+ Select as Select2,
741
+ TextInput as TextInput2,
742
+ Textarea as Textarea2
743
+ } from "@echothink-ui/core";
744
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
745
+ var permissionOptions = [
746
+ { value: "private", label: "Private" },
747
+ { value: "team", label: "Team" },
748
+ { value: "organization", label: "Organization" }
749
+ ];
750
+ var roleOptions = [
751
+ { value: "viewer", label: "Viewer" },
752
+ { value: "editor", label: "Editor" },
753
+ { value: "owner", label: "Owner" }
754
+ ];
755
+ var providerOptions = [
756
+ { value: "openai", label: "OpenAI" },
757
+ { value: "azure-openai", label: "Azure OpenAI" },
758
+ { value: "custom", label: "Custom provider" }
759
+ ];
760
+ var modelOptions = [
761
+ { value: "gpt-4o", label: "GPT-4o" },
762
+ { value: "gpt-5.4", label: "GPT-5.4" },
763
+ { value: "gpt-5.4-mini", label: "GPT-5.4 mini" },
764
+ { value: "custom", label: "Custom model" }
765
+ ];
766
+ function optionLabel(options, value) {
767
+ return options.find((option) => option.value === value)?.label ?? value;
768
+ }
769
+ function nodeText(node) {
770
+ if (typeof node === "string" || typeof node === "number") return String(node);
771
+ return "member";
772
+ }
773
+ function memberInitials(label) {
774
+ const text = nodeText(label);
775
+ const words = text.trim().split(/\s+/).filter(Boolean);
776
+ if (words.length === 0 || text === "member") return "M";
777
+ return words.slice(0, 2).map((word) => word.charAt(0).toUpperCase()).join("");
778
+ }
779
+ function countLabel(count, singular, plural = `${singular}s`) {
780
+ return `${count} ${count === 1 ? singular : plural}`;
781
+ }
782
+ function ProjectSettingsPanel({
783
+ settings = {},
784
+ onChange,
785
+ className,
786
+ ...props
787
+ }) {
788
+ const titleId = React5.useId();
789
+ const members = settings.members ?? [];
790
+ const permissionMode = settings.permissionMode ?? "private";
791
+ const defaultRole = settings.defaultRole ?? "viewer";
792
+ const modelProvider = settings.modelProvider ?? "openai";
793
+ const model = settings.model ?? "gpt-5.4";
794
+ const update = (patch) => {
795
+ onChange?.({ ...settings, ...patch });
796
+ };
797
+ const updateMemberRole = (memberId, role) => {
798
+ update({
799
+ members: members.map((member) => member.id === memberId ? { ...member, role } : member)
800
+ });
801
+ };
802
+ return /* @__PURE__ */ jsxs7(
803
+ "section",
804
+ {
805
+ ...projectHtmlProps(props),
806
+ className: classNames("eth-project-settings-panel", className),
807
+ "data-eth-component": "ProjectSettingsPanel",
808
+ role: "region",
809
+ "aria-labelledby": titleId,
810
+ tabIndex: -1,
811
+ children: [
812
+ /* @__PURE__ */ jsxs7("header", { className: "eth-project-settings-panel__header", children: [
813
+ /* @__PURE__ */ jsxs7("div", { className: "eth-project-settings-panel__heading", children: [
814
+ /* @__PURE__ */ jsx8("p", { className: "eth-project-settings-panel__eyebrow", children: "Project management" }),
815
+ /* @__PURE__ */ jsx8("h2", { id: titleId, children: "Project settings" }),
816
+ /* @__PURE__ */ jsx8("p", { children: "Manage identity, access defaults, members, and the project agent model." })
817
+ ] }),
818
+ /* @__PURE__ */ jsxs7("dl", { className: "eth-project-settings-panel__summary", "aria-label": "Project settings summary", children: [
819
+ /* @__PURE__ */ jsxs7("div", { children: [
820
+ /* @__PURE__ */ jsx8("dt", { children: "Visibility" }),
821
+ /* @__PURE__ */ jsx8("dd", { children: optionLabel(permissionOptions, permissionMode) })
822
+ ] }),
823
+ /* @__PURE__ */ jsxs7("div", { children: [
824
+ /* @__PURE__ */ jsx8("dt", { children: "Members" }),
825
+ /* @__PURE__ */ jsx8("dd", { children: countLabel(members.length, "member") })
826
+ ] }),
827
+ /* @__PURE__ */ jsxs7("div", { children: [
828
+ /* @__PURE__ */ jsx8("dt", { children: "Model" }),
829
+ /* @__PURE__ */ jsx8("dd", { children: optionLabel(modelOptions, model) })
830
+ ] })
831
+ ] })
832
+ ] }),
833
+ /* @__PURE__ */ jsxs7("div", { className: "eth-project-settings-panel__content", children: [
834
+ /* @__PURE__ */ jsx8(FormSection2, { title: "General", description: "Core project identity and description.", children: /* @__PURE__ */ jsxs7("div", { className: "eth-project-settings-panel__stack", children: [
835
+ /* @__PURE__ */ jsx8(
836
+ FormField2,
837
+ {
838
+ id: "project-settings-name",
839
+ label: "Project name",
840
+ helperText: "Shown in project lists, audit records, and app-domain routing.",
841
+ children: /* @__PURE__ */ jsx8(
842
+ TextInput2,
843
+ {
844
+ value: settings.name ?? "",
845
+ onChange: (event) => update({ name: event.currentTarget.value })
846
+ }
847
+ )
848
+ }
849
+ ),
850
+ /* @__PURE__ */ jsx8(
851
+ FormField2,
852
+ {
853
+ id: "project-settings-description",
854
+ label: "Description",
855
+ helperText: "Brief context for collaborators and project agents.",
856
+ children: /* @__PURE__ */ jsx8(
857
+ Textarea2,
858
+ {
859
+ value: settings.description ?? "",
860
+ rows: 4,
861
+ onChange: (event) => update({ description: event.currentTarget.value })
862
+ }
863
+ )
864
+ }
865
+ )
866
+ ] }) }),
867
+ /* @__PURE__ */ jsx8(FormSection2, { title: "Members", description: "People who can work in this project.", children: /* @__PURE__ */ jsxs7("div", { className: "eth-project-settings-panel__members", children: [
868
+ /* @__PURE__ */ jsxs7("div", { className: "eth-project-settings-panel__member-meta", children: [
869
+ /* @__PURE__ */ jsx8("span", { children: countLabel(members.length, "active member") }),
870
+ /* @__PURE__ */ jsxs7("span", { children: [
871
+ "Default role: ",
872
+ optionLabel(roleOptions, defaultRole)
873
+ ] })
874
+ ] }),
875
+ members.length ? /* @__PURE__ */ jsx8("div", { className: "eth-project-settings-panel__member-list", role: "list", children: members.map((member) => {
876
+ const label = nodeText(member.label);
877
+ return /* @__PURE__ */ jsxs7(
878
+ "article",
879
+ {
880
+ className: "eth-project-settings-panel__member-row",
881
+ role: "listitem",
882
+ children: [
883
+ /* @__PURE__ */ jsx8(
884
+ "span",
885
+ {
886
+ className: "eth-project-settings-panel__member-avatar",
887
+ "aria-hidden": "true",
888
+ children: memberInitials(member.label)
889
+ }
890
+ ),
891
+ /* @__PURE__ */ jsxs7("div", { className: "eth-project-settings-panel__member-main", children: [
892
+ /* @__PURE__ */ jsx8("strong", { children: member.label }),
893
+ /* @__PURE__ */ jsx8("span", { children: "Project collaborator" })
894
+ ] }),
895
+ /* @__PURE__ */ jsx8(
896
+ Select2,
897
+ {
898
+ "aria-label": `Role for ${label}`,
899
+ options: roleOptions,
900
+ value: member.role ?? defaultRole,
901
+ onChange: (event) => updateMemberRole(member.id, event.currentTarget.value)
902
+ }
903
+ )
904
+ ]
905
+ },
906
+ member.id
907
+ );
908
+ }) }) : /* @__PURE__ */ jsx8("p", { className: "eth-project-settings-panel__empty", children: "Members added to this project will appear here with their assigned role." })
909
+ ] }) }),
910
+ /* @__PURE__ */ jsx8(FormSection2, { title: "Permissions", description: "Default access policy for project resources.", children: /* @__PURE__ */ jsxs7("div", { className: "eth-project-settings-panel__field-grid", children: [
911
+ /* @__PURE__ */ jsx8(
912
+ FormField2,
913
+ {
914
+ id: "project-settings-permission-mode",
915
+ label: "Visibility",
916
+ helperText: "Controls the default project audience.",
917
+ children: /* @__PURE__ */ jsx8(
918
+ Select2,
919
+ {
920
+ options: permissionOptions,
921
+ value: permissionMode,
922
+ onChange: (event) => update({ permissionMode: event.currentTarget.value })
923
+ }
924
+ )
925
+ }
926
+ ),
927
+ /* @__PURE__ */ jsx8(
928
+ FormField2,
929
+ {
930
+ id: "project-settings-default-role",
931
+ label: "Default member role",
932
+ helperText: "Applied when a member has no explicit role.",
933
+ children: /* @__PURE__ */ jsx8(
934
+ Select2,
935
+ {
936
+ options: roleOptions,
937
+ value: defaultRole,
938
+ onChange: (event) => update({ defaultRole: event.currentTarget.value })
939
+ }
940
+ )
941
+ }
942
+ )
943
+ ] }) }),
944
+ /* @__PURE__ */ jsx8(FormSection2, { title: "Model config", description: "Default provider and model for project agents.", children: /* @__PURE__ */ jsxs7("div", { className: "eth-project-settings-panel__field-grid", children: [
945
+ /* @__PURE__ */ jsx8(
946
+ FormField2,
947
+ {
948
+ id: "project-settings-provider",
949
+ label: "Provider",
950
+ helperText: "Runtime provider for project-level agents.",
951
+ children: /* @__PURE__ */ jsx8(
952
+ Select2,
953
+ {
954
+ options: providerOptions,
955
+ value: modelProvider,
956
+ onChange: (event) => update({ modelProvider: event.currentTarget.value })
957
+ }
958
+ )
959
+ }
960
+ ),
961
+ /* @__PURE__ */ jsx8(
962
+ FormField2,
963
+ {
964
+ id: "project-settings-model",
965
+ label: "Model",
966
+ helperText: "Used when an agent has no override.",
967
+ children: /* @__PURE__ */ jsx8(
968
+ Select2,
969
+ {
970
+ options: modelOptions,
971
+ value: model,
972
+ onChange: (event) => update({ model: event.currentTarget.value })
973
+ }
974
+ )
975
+ }
976
+ )
977
+ ] }) })
978
+ ] })
979
+ ]
980
+ }
981
+ );
982
+ }
983
+
984
+ // src/components/ProjectScopeSelector.tsx
985
+ import * as React6 from "react";
986
+ import { EmptyState as EmptyState3, StatusDot as StatusDot4, statusLabel as statusLabel5 } from "@echothink-ui/core";
987
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
988
+ var kindLabels = {
989
+ project: "Project",
990
+ "app-domain": "App domain",
991
+ resource: "Resource"
992
+ };
993
+ function ProjectScopeSelector({
994
+ scopes = [],
995
+ activeScopeId,
996
+ onSelect,
997
+ className,
998
+ ...props
999
+ }) {
1000
+ const groupName = React6.useId();
1001
+ return /* @__PURE__ */ jsx9(
1002
+ "section",
1003
+ {
1004
+ ...projectHtmlProps(props),
1005
+ className: classNames("eth-project-scope-selector", className),
1006
+ "data-eth-component": "ProjectScopeSelector",
1007
+ role: "region",
1008
+ "aria-label": "Project scopes",
1009
+ tabIndex: -1,
1010
+ children: scopes.length ? /* @__PURE__ */ jsx9(
1011
+ "div",
1012
+ {
1013
+ className: "eth-project-scope-selector__group",
1014
+ role: "radiogroup",
1015
+ "aria-label": "Target scope",
1016
+ children: scopes.map((scope, index) => {
1017
+ const active = activeScopeId === scope.id;
1018
+ const inputId = `${groupName}-${index}`;
1019
+ return /* @__PURE__ */ jsxs8(
1020
+ "label",
1021
+ {
1022
+ className: classNames(
1023
+ "eth-project-scope-selector__item",
1024
+ active && "eth-project-scope-selector__item--active",
1025
+ !onSelect && "eth-project-scope-selector__item--readonly"
1026
+ ),
1027
+ htmlFor: inputId,
1028
+ children: [
1029
+ /* @__PURE__ */ jsx9(
1030
+ "input",
1031
+ {
1032
+ id: inputId,
1033
+ className: "eth-project-scope-selector__control",
1034
+ type: "radio",
1035
+ name: groupName,
1036
+ value: scope.id,
1037
+ checked: active,
1038
+ readOnly: !onSelect,
1039
+ onChange: () => onSelect?.(scope.id)
1040
+ }
1041
+ ),
1042
+ /* @__PURE__ */ jsxs8("span", { className: "eth-project-scope-selector__content", children: [
1043
+ /* @__PURE__ */ jsxs8("span", { className: "eth-project-scope-selector__title-row", children: [
1044
+ /* @__PURE__ */ jsx9("span", { className: "eth-project-scope-selector__label", children: scope.label }),
1045
+ scope.status ? /* @__PURE__ */ jsx9("span", { className: "eth-project-scope-selector__status", children: /* @__PURE__ */ jsx9(StatusDot4, { status: scope.status, label: statusLabel5(scope.status) }) }) : null
1046
+ ] }),
1047
+ /* @__PURE__ */ jsx9("span", { className: "eth-project-scope-selector__kind", children: kindLabels[scope.kind] })
1048
+ ] })
1049
+ ]
1050
+ },
1051
+ scope.id
1052
+ );
1053
+ })
1054
+ }
1055
+ ) : /* @__PURE__ */ jsx9(
1056
+ EmptyState3,
1057
+ {
1058
+ className: "eth-project-scope-selector__empty",
1059
+ title: "No scopes",
1060
+ description: "Project, app-domain, and resource targets appear here."
1061
+ }
1062
+ )
1063
+ }
1064
+ );
1065
+ }
1066
+
1067
+ // src/components/ProjectResourcePanel.tsx
1068
+ import * as React7 from "react";
1069
+ import {
1070
+ Badge as Badge5,
1071
+ EmptyState as EmptyState4,
1072
+ StatusDot as StatusDot5,
1073
+ statusLabel as statusLabel6
1074
+ } from "@echothink-ui/core";
1075
+ import { DataTable as DataTable2 } from "@echothink-ui/data";
1076
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1077
+ var syncedStatuses = /* @__PURE__ */ new Set(["completed", "succeeded", "synced", "active"]);
1078
+ var attentionStatuses = /* @__PURE__ */ new Set([
1079
+ "blocked",
1080
+ "failed",
1081
+ "stale",
1082
+ "warning",
1083
+ "pending-approval",
1084
+ "approval-required"
1085
+ ]);
1086
+ function ProjectResourcePanel({
1087
+ resources = [],
1088
+ onSelect,
1089
+ className,
1090
+ title = "Project resources",
1091
+ ...props
1092
+ }) {
1093
+ const syncedCount = resources.filter(
1094
+ (resource) => resource.status && syncedStatuses.has(resource.status)
1095
+ ).length;
1096
+ const attentionCount = resources.filter(
1097
+ (resource) => resource.status && attentionStatuses.has(resource.status)
1098
+ ).length;
1099
+ const hasRowActions = Boolean(onSelect);
1100
+ const columns = React7.useMemo(
1101
+ () => [
1102
+ {
1103
+ key: "label",
1104
+ header: "Resource",
1105
+ width: "40%",
1106
+ render: (resource) => /* @__PURE__ */ jsxs9("div", { className: "eth-project-resource-panel__resource", children: [
1107
+ /* @__PURE__ */ jsx10("span", { className: "eth-project-resource-panel__resource-icon", "aria-hidden": "true", children: resourceKindInitial(resource.kind) }),
1108
+ /* @__PURE__ */ jsxs9("span", { className: "eth-project-resource-panel__resource-copy", children: [
1109
+ /* @__PURE__ */ jsx10("strong", { children: resource.label }),
1110
+ /* @__PURE__ */ jsx10("span", { children: resource.id })
1111
+ ] })
1112
+ ] })
1113
+ },
1114
+ {
1115
+ key: "kind",
1116
+ header: "Kind",
1117
+ width: "14%",
1118
+ render: (resource) => /* @__PURE__ */ jsx10(Badge5, { severity: "neutral", children: resourceKindLabel(resource.kind) })
1119
+ },
1120
+ {
1121
+ key: "status",
1122
+ header: "Status",
1123
+ width: "20%",
1124
+ render: (resource) => resource.status ? /* @__PURE__ */ jsx10(StatusDot5, { status: resource.status, label: statusLabel6(resource.status) }) : /* @__PURE__ */ jsx10("span", { className: "eth-project-resource-panel__muted", children: "Not tracked" })
1125
+ },
1126
+ {
1127
+ key: "updatedAt",
1128
+ header: "Updated",
1129
+ width: "16%",
1130
+ render: (resource) => /* @__PURE__ */ jsx10("span", { className: "eth-project-resource-panel__updated", children: resource.updatedAt ?? "Not updated" })
1131
+ }
1132
+ ],
1133
+ []
1134
+ );
1135
+ const rowActions = React7.useCallback(
1136
+ (resource) => onSelect ? [{ id: "open", label: "Open", onSelect: () => onSelect(resource.id) }] : [],
1137
+ [onSelect]
1138
+ );
1139
+ return /* @__PURE__ */ jsxs9(
1140
+ "section",
1141
+ {
1142
+ ...projectHtmlProps(props),
1143
+ className: classNames("eth-project-resource-panel", className),
1144
+ "data-eth-component": "ProjectResourcePanel",
1145
+ role: "region",
1146
+ "aria-label": typeof title === "string" ? title : "Project resources",
1147
+ tabIndex: -1,
1148
+ children: [
1149
+ title ? /* @__PURE__ */ jsxs9("header", { className: "eth-project-resource-panel__header", children: [
1150
+ /* @__PURE__ */ jsx10("h3", { children: title }),
1151
+ /* @__PURE__ */ jsxs9("dl", { className: "eth-project-resource-panel__summary", "aria-label": "Resource summary", children: [
1152
+ /* @__PURE__ */ jsxs9("div", { children: [
1153
+ /* @__PURE__ */ jsx10("dt", { children: "Resources" }),
1154
+ /* @__PURE__ */ jsx10("dd", { children: formatNumber(resources.length) })
1155
+ ] }),
1156
+ /* @__PURE__ */ jsxs9("div", { children: [
1157
+ /* @__PURE__ */ jsx10("dt", { children: "Synced" }),
1158
+ /* @__PURE__ */ jsx10("dd", { children: formatNumber(syncedCount) })
1159
+ ] }),
1160
+ /* @__PURE__ */ jsxs9("div", { children: [
1161
+ /* @__PURE__ */ jsx10("dt", { children: "Attention" }),
1162
+ /* @__PURE__ */ jsx10("dd", { children: formatNumber(attentionCount) })
1163
+ ] })
1164
+ ] })
1165
+ ] }) : null,
1166
+ /* @__PURE__ */ jsx10(
1167
+ DataTable2,
1168
+ {
1169
+ rows: resources,
1170
+ columns,
1171
+ rowKey: "id",
1172
+ density: "compact",
1173
+ className: "eth-project-resource-panel__table",
1174
+ rowActions: hasRowActions ? rowActions : void 0,
1175
+ emptyState: /* @__PURE__ */ jsx10(
1176
+ EmptyState4,
1177
+ {
1178
+ title: "No resources",
1179
+ description: "Linked files, documents, tables, and services appear here."
1180
+ }
1181
+ )
1182
+ }
1183
+ )
1184
+ ]
1185
+ }
1186
+ );
1187
+ }
1188
+ function resourceKindLabel(kind) {
1189
+ return kind.split(/[-_\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
1190
+ }
1191
+ function resourceKindInitial(kind) {
1192
+ return resourceKindLabel(kind).charAt(0) || "R";
1193
+ }
1194
+
1195
+ // src/components/ProjectActivityTimeline.tsx
1196
+ import { Badge as Badge6, EmptyState as EmptyState5 } from "@echothink-ui/core";
1197
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1198
+ function getActivityKindTone(kind) {
1199
+ const normalized = kind.toLowerCase();
1200
+ if (/(approve|approved|approval|complete|completed|success|release)/.test(normalized)) {
1201
+ return "success";
1202
+ }
1203
+ if (/(block|blocked|fail|failed|error|reject|rejected|risk)/.test(normalized)) {
1204
+ return "danger";
1205
+ }
1206
+ if (/(pending|review|warning|needs|wait)/.test(normalized)) {
1207
+ return "warning";
1208
+ }
1209
+ if (/(draft|resource|domain|config|sync|agent|update)/.test(normalized)) {
1210
+ return "info";
1211
+ }
1212
+ return "neutral";
1213
+ }
1214
+ function getActivityTime(timestamp) {
1215
+ const value = timestamp.trim();
1216
+ const dateTimeMatch = value.match(
1217
+ /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2})(?::\d{2}(?:\.\d{1,3})?)?(?:Z|[+-]\d{2}:?\d{2})?$/
1218
+ );
1219
+ if (dateTimeMatch) {
1220
+ return {
1221
+ dateTime: `${dateTimeMatch[1]}T${dateTimeMatch[2]}`,
1222
+ label: dateTimeMatch[2]
1223
+ };
1224
+ }
1225
+ if (/^\d{2}:\d{2}(?::\d{2})?$/.test(value) || /^\d{4}-\d{2}-\d{2}$/.test(value)) {
1226
+ return { dateTime: value, label: value };
1227
+ }
1228
+ return { dateTime: void 0, label: value };
1229
+ }
1230
+ function ProjectActivityTimeline({
1231
+ events = [],
1232
+ className,
1233
+ title = "Activity",
1234
+ ...props
1235
+ }) {
1236
+ return /* @__PURE__ */ jsxs10(
1237
+ "section",
1238
+ {
1239
+ ...projectHtmlProps(props),
1240
+ className: classNames("eth-project-activity-timeline", className),
1241
+ "data-eth-component": "ProjectActivityTimeline",
1242
+ role: "region",
1243
+ "aria-label": typeof title === "string" ? title : "Project activity",
1244
+ tabIndex: -1,
1245
+ children: [
1246
+ title ? /* @__PURE__ */ jsx11("header", { className: "eth-project-activity-timeline__header", children: /* @__PURE__ */ jsx11("h3", { children: title }) }) : null,
1247
+ events.length ? /* @__PURE__ */ jsx11("ol", { className: "eth-project-activity-timeline__list", children: events.map((event) => {
1248
+ const tone = getActivityKindTone(event.kind);
1249
+ const time = getActivityTime(event.timestamp);
1250
+ return /* @__PURE__ */ jsxs10(
1251
+ "li",
1252
+ {
1253
+ className: classNames(
1254
+ "eth-project-activity-timeline__item",
1255
+ `eth-project-activity-timeline__item--${tone}`
1256
+ ),
1257
+ "data-kind": event.kind,
1258
+ children: [
1259
+ /* @__PURE__ */ jsx11("time", { className: "eth-project-activity-timeline__time", dateTime: time.dateTime, children: time.label }),
1260
+ /* @__PURE__ */ jsx11("div", { className: "eth-project-activity-timeline__rail", "aria-hidden": true, children: /* @__PURE__ */ jsx11("span", { className: "eth-project-activity-timeline__marker" }) }),
1261
+ /* @__PURE__ */ jsxs10("article", { className: "eth-project-activity-timeline__event", children: [
1262
+ /* @__PURE__ */ jsxs10("header", { className: "eth-project-activity-timeline__event-header", children: [
1263
+ /* @__PURE__ */ jsx11(Badge6, { severity: tone, children: event.kind }),
1264
+ event.actor ? /* @__PURE__ */ jsxs10("span", { className: "eth-project-activity-timeline__actor", children: [
1265
+ "By ",
1266
+ event.actor
1267
+ ] }) : null
1268
+ ] }),
1269
+ /* @__PURE__ */ jsx11("strong", { className: "eth-project-activity-timeline__summary", children: event.summary }),
1270
+ event.details ? /* @__PURE__ */ jsx11("p", { className: "eth-project-activity-timeline__details", children: event.details }) : null
1271
+ ] })
1272
+ ]
1273
+ },
1274
+ event.id
1275
+ );
1276
+ }) }) : /* @__PURE__ */ jsx11(EmptyState5, { title: "No activity", description: "Project changes and events appear here." })
1277
+ ]
1278
+ }
1279
+ );
1280
+ }
1281
+
1282
+ // src/components/ProjectTabGroup.tsx
1283
+ import { ProjectTabCollection } from "@echothink-ui/layouts";
1284
+ import { jsx as jsx12 } from "react/jsx-runtime";
1285
+ function ProjectTabGroup({
1286
+ projectId,
1287
+ projectLabel,
1288
+ tabs,
1289
+ activeTabId,
1290
+ onActivateTab,
1291
+ onCloseTab,
1292
+ className,
1293
+ ...props
1294
+ }) {
1295
+ return /* @__PURE__ */ jsx12(
1296
+ ProjectTabCollection,
1297
+ {
1298
+ ...projectHtmlProps(props),
1299
+ className: classNames("eth-project-tab-group", className),
1300
+ "data-eth-component": "ProjectTabGroup",
1301
+ projectId,
1302
+ projectLabel,
1303
+ tabs,
1304
+ activeTabId,
1305
+ onActivateTab,
1306
+ onCloseTab
1307
+ }
1308
+ );
1309
+ }
1310
+
1311
+ // src/components/ProjectMembersPanel.tsx
1312
+ import * as React8 from "react";
1313
+ import {
1314
+ Button as Button5,
1315
+ EmptyState as EmptyState6,
1316
+ Select as Select3,
1317
+ StatusDot as StatusDot6,
1318
+ statusLabel as statusLabel7
1319
+ } from "@echothink-ui/core";
1320
+ import { DataTable as DataTable3 } from "@echothink-ui/data";
1321
+ import { PlusIcon } from "@echothink-ui/icons";
1322
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1323
+ var defaultRoleValues = ["owner", "editor", "viewer"];
1324
+ var defaultMemberStatus = "active";
1325
+ var pendingMemberStatuses = /* @__PURE__ */ new Set([
1326
+ "queued",
1327
+ "pending-approval",
1328
+ "not-started"
1329
+ ]);
1330
+ function roleLabel(role) {
1331
+ return role.replace(/[-_]+/g, " ").replace(/\b\w/g, (character) => character.toUpperCase());
1332
+ }
1333
+ function memberInitials2(label) {
1334
+ if (typeof label !== "string" && typeof label !== "number") return "M";
1335
+ const words = String(label).trim().split(/\s+/).filter(Boolean);
1336
+ const first = words[0]?.charAt(0) ?? "";
1337
+ const last = words.length > 1 ? words[words.length - 1]?.charAt(0) ?? "" : "";
1338
+ return `${first}${last}`.toUpperCase() || "M";
1339
+ }
1340
+ function memberStatus(member) {
1341
+ return member.status ?? defaultMemberStatus;
1342
+ }
1343
+ function memberStatusLabel(status) {
1344
+ if (status === "pending-approval") return "Pending invite";
1345
+ if (status === "not-started" || status === "queued") return "Invited";
1346
+ return statusLabel7(status);
1347
+ }
1348
+ function ProjectMembersPanel({
1349
+ members = [],
1350
+ roles = [],
1351
+ onAdd,
1352
+ onRemove,
1353
+ onChangeRole,
1354
+ className,
1355
+ title = "Members",
1356
+ ...props
1357
+ }) {
1358
+ const roleOptions2 = React8.useMemo(() => {
1359
+ const roleValues = new Set(roles.length ? roles : defaultRoleValues);
1360
+ for (const member of members) {
1361
+ roleValues.add(member.role);
1362
+ }
1363
+ return Array.from(roleValues).map((role) => ({ value: role, label: roleLabel(role) }));
1364
+ }, [members, roles]);
1365
+ const ownerCount = members.filter((member) => member.role.toLowerCase() === "owner").length;
1366
+ const pendingCount = members.filter(
1367
+ (member) => pendingMemberStatuses.has(memberStatus(member))
1368
+ ).length;
1369
+ const columns = React8.useMemo(
1370
+ () => [
1371
+ {
1372
+ key: "label",
1373
+ header: "Member",
1374
+ width: "44%",
1375
+ render: (member) => /* @__PURE__ */ jsxs11("div", { className: "eth-project-members-panel__member", children: [
1376
+ member.avatar ? /* @__PURE__ */ jsx13("img", { className: "eth-project-members-panel__avatar", src: member.avatar, alt: "" }) : /* @__PURE__ */ jsx13("span", { className: "eth-project-members-panel__avatar", "aria-hidden": "true", children: memberInitials2(member.label) }),
1377
+ /* @__PURE__ */ jsx13("strong", { children: member.label })
1378
+ ] })
1379
+ },
1380
+ {
1381
+ key: "role",
1382
+ header: "Role",
1383
+ width: "24%",
1384
+ render: (member) => onChangeRole ? /* @__PURE__ */ jsx13(
1385
+ Select3,
1386
+ {
1387
+ "aria-label": `Role for ${String(member.label)}`,
1388
+ className: "eth-project-members-panel__role-select",
1389
+ density: "compact",
1390
+ options: roleOptions2,
1391
+ value: member.role,
1392
+ onChange: (event) => onChangeRole(member.id, event.currentTarget.value)
1393
+ }
1394
+ ) : /* @__PURE__ */ jsx13("span", { className: "eth-project-members-panel__role-badge", children: roleLabel(member.role) })
1395
+ },
1396
+ {
1397
+ key: "status",
1398
+ header: "Status",
1399
+ width: "20%",
1400
+ render: (member) => {
1401
+ const status = memberStatus(member);
1402
+ return /* @__PURE__ */ jsx13(StatusDot6, { status, label: memberStatusLabel(status) });
1403
+ }
1404
+ }
1405
+ ],
1406
+ [onChangeRole, roleOptions2]
1407
+ );
1408
+ const rowActions = React8.useCallback(
1409
+ (member) => onRemove ? [{ id: "remove", label: "Remove", intent: "danger", onSelect: () => onRemove(member.id) }] : [],
1410
+ [onRemove]
1411
+ );
1412
+ return /* @__PURE__ */ jsxs11(
1413
+ "section",
1414
+ {
1415
+ ...projectHtmlProps(props),
1416
+ className: classNames("eth-project-members-panel", className),
1417
+ "data-eth-component": "ProjectMembersPanel",
1418
+ role: "region",
1419
+ "aria-label": typeof title === "string" ? title : "Project members",
1420
+ tabIndex: -1,
1421
+ children: [
1422
+ /* @__PURE__ */ jsxs11("header", { className: "eth-project-members-panel__header", children: [
1423
+ /* @__PURE__ */ jsxs11("div", { className: "eth-project-members-panel__heading", children: [
1424
+ title ? /* @__PURE__ */ jsx13("h3", { children: title }) : null,
1425
+ /* @__PURE__ */ jsxs11("dl", { className: "eth-project-members-panel__summary", "aria-label": "Member summary", children: [
1426
+ /* @__PURE__ */ jsxs11("div", { children: [
1427
+ /* @__PURE__ */ jsx13("dt", { children: "Members" }),
1428
+ /* @__PURE__ */ jsx13("dd", { children: formatNumber(members.length) })
1429
+ ] }),
1430
+ /* @__PURE__ */ jsxs11("div", { children: [
1431
+ /* @__PURE__ */ jsx13("dt", { children: "Owners" }),
1432
+ /* @__PURE__ */ jsx13("dd", { children: formatNumber(ownerCount) })
1433
+ ] }),
1434
+ /* @__PURE__ */ jsxs11("div", { children: [
1435
+ /* @__PURE__ */ jsx13("dt", { children: "Pending" }),
1436
+ /* @__PURE__ */ jsx13("dd", { children: formatNumber(pendingCount) })
1437
+ ] })
1438
+ ] })
1439
+ ] }),
1440
+ onAdd ? /* @__PURE__ */ jsx13(Button5, { density: "compact", icon: /* @__PURE__ */ jsx13(PlusIcon, {}), intent: "primary", onClick: onAdd, children: "Add member" }) : null
1441
+ ] }),
1442
+ /* @__PURE__ */ jsx13(
1443
+ DataTable3,
1444
+ {
1445
+ rows: members,
1446
+ columns,
1447
+ rowKey: "id",
1448
+ density: "compact",
1449
+ className: "eth-project-members-panel__table",
1450
+ rowActions: onRemove ? rowActions : void 0,
1451
+ emptyState: /* @__PURE__ */ jsx13(EmptyState6, { title: "No members", description: "Project members appear here." })
1452
+ }
1453
+ )
1454
+ ]
1455
+ }
1456
+ );
1457
+ }
1458
+
1459
+ // src/components/ProjectPermissionPanel.tsx
1460
+ import * as React9 from "react";
1461
+ import { Badge as Badge7, Checkbox, EmptyState as EmptyState7 } from "@echothink-ui/core";
1462
+ import { DataTable as DataTable4 } from "@echothink-ui/data";
1463
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1464
+ function ProjectPermissionPanel({
1465
+ subjects = [],
1466
+ resources = [],
1467
+ actions = [],
1468
+ assignments = {},
1469
+ onChange,
1470
+ title = "Project permissions",
1471
+ description = "Review subject access by resource and action.",
1472
+ density = "compact",
1473
+ className,
1474
+ ...props
1475
+ }) {
1476
+ const hasMatrix = subjects.length > 0 && resources.length > 0 && actions.length > 0;
1477
+ const editable = Boolean(onChange);
1478
+ const rows = React9.useMemo(
1479
+ () => subjects.flatMap(
1480
+ (subject) => resources.map((resource) => ({
1481
+ id: `${entityId(subject)}:${entityId(resource)}`,
1482
+ subject,
1483
+ resource
1484
+ }))
1485
+ ),
1486
+ [resources, subjects]
1487
+ );
1488
+ const totalPermissions = subjects.length * resources.length * actions.length;
1489
+ const grantedCount = React9.useMemo(
1490
+ () => subjects.reduce(
1491
+ (subjectTotal, subject) => subjectTotal + resources.reduce(
1492
+ (resourceTotal, resource) => resourceTotal + actions.filter((action) => assignments[permissionKey(subject, resource, action)]).length,
1493
+ 0
1494
+ ),
1495
+ 0
1496
+ ),
1497
+ [actions, assignments, resources, subjects]
1498
+ );
1499
+ const missingCount = Math.max(totalPermissions - grantedCount, 0);
1500
+ const columns = React9.useMemo(
1501
+ () => [
1502
+ {
1503
+ key: "subject",
1504
+ header: "Subject",
1505
+ width: "12rem",
1506
+ render: (row) => /* @__PURE__ */ jsx14("strong", { className: "eth-project-permission-panel__subject", children: entityLabel(row.subject) })
1507
+ },
1508
+ {
1509
+ key: "resource",
1510
+ header: "Resource",
1511
+ width: "12rem",
1512
+ render: (row) => /* @__PURE__ */ jsx14("span", { className: "eth-project-permission-panel__resource", children: entityLabel(row.resource) })
1513
+ },
1514
+ ...actions.map((action) => ({
1515
+ key: `permission-${entityId(action)}`,
1516
+ header: entityLabel(action),
1517
+ width: "9rem",
1518
+ align: "center",
1519
+ render: (row) => {
1520
+ const key = permissionKey(row.subject, row.resource, action);
1521
+ const granted = Boolean(assignments[key]);
1522
+ const label = `${entityText(action)} permission for ${entityText(
1523
+ row.subject
1524
+ )} on ${entityText(row.resource)}`;
1525
+ return editable ? /* @__PURE__ */ jsx14(
1526
+ Checkbox,
1527
+ {
1528
+ "aria-label": label,
1529
+ className: "eth-project-permission-panel__toggle",
1530
+ hideLabel: true,
1531
+ label,
1532
+ checked: granted,
1533
+ onChange: (event) => onChange?.(key, event.currentTarget.checked)
1534
+ }
1535
+ ) : /* @__PURE__ */ jsx14(
1536
+ Badge7,
1537
+ {
1538
+ className: "eth-project-permission-panel__state",
1539
+ severity: granted ? "success" : "neutral",
1540
+ children: granted ? "Granted" : "Not granted"
1541
+ }
1542
+ );
1543
+ }
1544
+ }))
1545
+ ],
1546
+ [actions, assignments, editable, onChange]
1547
+ );
1548
+ return /* @__PURE__ */ jsxs12(
1549
+ "section",
1550
+ {
1551
+ ...projectHtmlProps(props),
1552
+ className: classNames("eth-project-permission-panel", className),
1553
+ "data-eth-component": "ProjectPermissionPanel",
1554
+ role: "region",
1555
+ "aria-label": typeof title === "string" && title ? title : "Project permissions",
1556
+ tabIndex: -1,
1557
+ children: [
1558
+ /* @__PURE__ */ jsxs12("header", { className: "eth-project-permission-panel__header", children: [
1559
+ /* @__PURE__ */ jsxs12("div", { className: "eth-project-permission-panel__heading", children: [
1560
+ title ? /* @__PURE__ */ jsx14("h3", { children: title }) : null,
1561
+ description ? /* @__PURE__ */ jsx14("p", { children: description }) : null
1562
+ ] }),
1563
+ hasMatrix ? /* @__PURE__ */ jsxs12(
1564
+ "dl",
1565
+ {
1566
+ className: "eth-project-permission-panel__summary",
1567
+ "aria-label": "Project permission summary",
1568
+ children: [
1569
+ /* @__PURE__ */ jsxs12("div", { children: [
1570
+ /* @__PURE__ */ jsx14("dt", { children: "Granted" }),
1571
+ /* @__PURE__ */ jsx14("dd", { children: grantedCount })
1572
+ ] }),
1573
+ /* @__PURE__ */ jsxs12("div", { children: [
1574
+ /* @__PURE__ */ jsx14("dt", { children: "Not granted" }),
1575
+ /* @__PURE__ */ jsx14("dd", { children: missingCount })
1576
+ ] }),
1577
+ /* @__PURE__ */ jsxs12("div", { children: [
1578
+ /* @__PURE__ */ jsx14("dt", { children: "Actions" }),
1579
+ /* @__PURE__ */ jsx14("dd", { children: actions.length })
1580
+ ] })
1581
+ ]
1582
+ }
1583
+ ) : null
1584
+ ] }),
1585
+ hasMatrix ? /* @__PURE__ */ jsx14(DataTable4, { rows, columns, rowKey: "id", density }) : /* @__PURE__ */ jsx14("div", { className: "eth-project-permission-panel__empty", children: /* @__PURE__ */ jsx14(
1586
+ EmptyState7,
1587
+ {
1588
+ title: "No permissions",
1589
+ description: "Subjects, resources, and actions are required to render a permission matrix."
1590
+ }
1591
+ ) })
1592
+ ]
1593
+ }
1594
+ );
1595
+ }
1596
+ function entityText(entity) {
1597
+ const label = entityLabel(entity);
1598
+ if (typeof label === "string" || typeof label === "number") return String(label);
1599
+ return entityId(entity);
1600
+ }
1601
+
1602
+ // src/components/ProjectAppDomainPanel.tsx
1603
+ import * as React10 from "react";
1604
+ import { EmptyState as EmptyState8, StatusDot as StatusDot7, statusLabel as statusLabel8 } from "@echothink-ui/core";
1605
+ import { DataTable as DataTable5 } from "@echothink-ui/data";
1606
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1607
+ var attentionStatuses2 = /* @__PURE__ */ new Set(["blocked", "failed", "stale", "warning", "approval-required"]);
1608
+ function ProjectAppDomainPanel({
1609
+ instances = [],
1610
+ onOpen,
1611
+ onConfigure,
1612
+ onRemove,
1613
+ className,
1614
+ title = "App domains",
1615
+ ...props
1616
+ }) {
1617
+ const runningCount = instances.filter((instance) => instance.status === "running").length;
1618
+ const attentionCount = instances.filter(
1619
+ (instance) => instance.health === "warning" || instance.health === "critical" || attentionStatuses2.has(instance.status)
1620
+ ).length;
1621
+ const hasRowActions = Boolean(onOpen || onConfigure || onRemove);
1622
+ const columns = React10.useMemo(
1623
+ () => [
1624
+ {
1625
+ key: "appDomainLabel",
1626
+ header: "App domain",
1627
+ width: "42%",
1628
+ render: (instance) => /* @__PURE__ */ jsxs13("div", { className: "eth-project-app-domain-panel__domain", children: [
1629
+ /* @__PURE__ */ jsx15("strong", { children: instance.appDomainLabel }),
1630
+ /* @__PURE__ */ jsx15("span", { children: instance.id })
1631
+ ] })
1632
+ },
1633
+ {
1634
+ key: "status",
1635
+ header: "Status",
1636
+ width: "18%",
1637
+ render: (instance) => /* @__PURE__ */ jsx15(StatusDot7, { status: instance.status, label: statusLabel8(instance.status) })
1638
+ },
1639
+ {
1640
+ key: "health",
1641
+ header: "Health",
1642
+ width: "18%",
1643
+ render: (instance) => /* @__PURE__ */ jsx15(StatusDot7, { status: healthToStatus(instance.health), label: healthLabel(instance.health) })
1644
+ }
1645
+ ],
1646
+ []
1647
+ );
1648
+ const rowActions = React10.useCallback(
1649
+ (instance) => {
1650
+ const actions = [];
1651
+ if (onOpen) {
1652
+ actions.push({
1653
+ id: "open",
1654
+ label: "Open",
1655
+ onSelect: () => onOpen(instance.id)
1656
+ });
1657
+ }
1658
+ if (onConfigure) {
1659
+ actions.push({
1660
+ id: "configure",
1661
+ label: "Configure",
1662
+ onSelect: () => onConfigure(instance.id)
1663
+ });
1664
+ }
1665
+ if (onRemove) {
1666
+ actions.push({
1667
+ id: "remove",
1668
+ label: "Remove",
1669
+ intent: "danger",
1670
+ onSelect: () => onRemove(instance.id)
1671
+ });
1672
+ }
1673
+ return actions;
1674
+ },
1675
+ [onConfigure, onOpen, onRemove]
1676
+ );
1677
+ return /* @__PURE__ */ jsxs13(
1678
+ "section",
1679
+ {
1680
+ ...projectHtmlProps(props),
1681
+ className: classNames("eth-project-app-domain-panel", className),
1682
+ "data-eth-component": "ProjectAppDomainPanel",
1683
+ role: "region",
1684
+ "aria-label": typeof title === "string" ? title : "Project app domains",
1685
+ tabIndex: -1,
1686
+ children: [
1687
+ title ? /* @__PURE__ */ jsxs13("header", { className: "eth-project-app-domain-panel__header", children: [
1688
+ /* @__PURE__ */ jsx15("h3", { children: title }),
1689
+ /* @__PURE__ */ jsxs13("dl", { className: "eth-project-app-domain-panel__summary", "aria-label": "App domain summary", children: [
1690
+ /* @__PURE__ */ jsxs13("div", { children: [
1691
+ /* @__PURE__ */ jsx15("dt", { children: "Installed" }),
1692
+ /* @__PURE__ */ jsx15("dd", { children: formatNumber(instances.length) })
1693
+ ] }),
1694
+ /* @__PURE__ */ jsxs13("div", { children: [
1695
+ /* @__PURE__ */ jsx15("dt", { children: "Running" }),
1696
+ /* @__PURE__ */ jsx15("dd", { children: formatNumber(runningCount) })
1697
+ ] }),
1698
+ /* @__PURE__ */ jsxs13("div", { children: [
1699
+ /* @__PURE__ */ jsx15("dt", { children: "Attention" }),
1700
+ /* @__PURE__ */ jsx15("dd", { children: formatNumber(attentionCount) })
1701
+ ] })
1702
+ ] })
1703
+ ] }) : null,
1704
+ /* @__PURE__ */ jsx15(
1705
+ DataTable5,
1706
+ {
1707
+ rows: instances,
1708
+ columns,
1709
+ rowKey: "id",
1710
+ density: "compact",
1711
+ className: "eth-project-app-domain-panel__table",
1712
+ rowActions: hasRowActions ? rowActions : void 0,
1713
+ emptyState: /* @__PURE__ */ jsx15(
1714
+ EmptyState8,
1715
+ {
1716
+ title: "No app domains",
1717
+ description: "Installed and running project app domains appear here."
1718
+ }
1719
+ )
1720
+ }
1721
+ )
1722
+ ]
1723
+ }
1724
+ );
1725
+ }
1726
+
1727
+ // src/components/ProjectModelConfigPanel.tsx
1728
+ import * as React11 from "react";
1729
+ import { FormField as FormField3, NumberInput, Select as Select4, Slider } from "@echothink-ui/core";
1730
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1731
+ function normalizeProvider(provider) {
1732
+ return typeof provider === "string" ? { id: provider, label: provider } : provider;
1733
+ }
1734
+ var PARAMETER_DEFINITIONS = [
1735
+ {
1736
+ key: "temperature",
1737
+ label: "Temperature",
1738
+ description: "Controls response randomness.",
1739
+ min: 0,
1740
+ max: 2,
1741
+ step: 0.1,
1742
+ defaultValue: 0.2,
1743
+ control: "slider"
1744
+ },
1745
+ {
1746
+ key: "topP",
1747
+ label: "Top P",
1748
+ description: "Limits nucleus sampling to a cumulative probability threshold.",
1749
+ min: 0,
1750
+ max: 1,
1751
+ step: 0.05,
1752
+ defaultValue: 1,
1753
+ control: "slider"
1754
+ },
1755
+ {
1756
+ key: "maxTokens",
1757
+ label: "Max tokens",
1758
+ description: "Caps the response length for project agent runs.",
1759
+ min: 1,
1760
+ max: 2e5,
1761
+ step: 1,
1762
+ defaultValue: 4096,
1763
+ control: "number"
1764
+ }
1765
+ ];
1766
+ function ProjectModelConfigPanel({
1767
+ providers = [],
1768
+ selectedProvider,
1769
+ model,
1770
+ params = {},
1771
+ onChange,
1772
+ className,
1773
+ title = "Project model configuration",
1774
+ description = "Default provider, model, and generation limits for project agents.",
1775
+ ...props
1776
+ }) {
1777
+ const generatedId = React11.useId();
1778
+ const headingId = `eth-project-model-config-panel-${generatedId}`;
1779
+ const normalizedProviders = providers.map(normalizeProvider);
1780
+ const requestedProvider = selectedProvider ?? normalizedProviders[0]?.id ?? "";
1781
+ const activeProviderData = normalizedProviders.find((provider) => provider.id === requestedProvider) ?? normalizedProviders[0];
1782
+ const activeProvider = activeProviderData?.id ?? requestedProvider;
1783
+ const modelOptions2 = activeProviderData?.models ?? [];
1784
+ const requestedModel = model ?? modelOptions2[0]?.id ?? "";
1785
+ const activeModelData = modelOptions2.find((option) => option.id === requestedModel) ?? modelOptions2[0];
1786
+ const activeModel = activeModelData?.id ?? requestedModel;
1787
+ const providerOptions2 = normalizedProviders.length ? normalizedProviders.map((provider) => ({
1788
+ value: provider.id,
1789
+ label: provider.label
1790
+ })) : [{ value: "", label: "No providers available", disabled: true }];
1791
+ const modelSelectOptions = modelOptions2.length ? modelOptions2.map((option) => ({ value: option.id, label: option.label })) : [
1792
+ {
1793
+ value: "",
1794
+ label: activeProvider ? "No models available" : "Select a provider first",
1795
+ disabled: true
1796
+ }
1797
+ ];
1798
+ const providerHelperText = normalizedProviders.length ? `${normalizedProviders.length} configured ${normalizedProviders.length === 1 ? "provider" : "providers"}.` : "Add a provider before selecting a project model.";
1799
+ const modelHelperText = modelOptions2.length ? `${modelOptions2.length} available ${modelOptions2.length === 1 ? "model" : "models"} for ${activeProviderData?.label ?? "this provider"}.` : "No selectable models are configured for this provider.";
1800
+ const tokenLimit = params.maxTokens ?? PARAMETER_DEFINITIONS[2].defaultValue;
1801
+ const emit = (patch) => {
1802
+ const hasProviderPatch = Object.prototype.hasOwnProperty.call(patch, "provider");
1803
+ const hasModelPatch = Object.prototype.hasOwnProperty.call(patch, "model");
1804
+ const nextParams = { ...params, ...patch.params };
1805
+ onChange?.({
1806
+ provider: hasProviderPatch ? patch.provider : activeProvider,
1807
+ model: hasModelPatch ? patch.model : activeModel,
1808
+ params: nextParams
1809
+ });
1810
+ };
1811
+ const updateParam = (key, value) => {
1812
+ emit({ params: { [key]: value } });
1813
+ };
1814
+ return /* @__PURE__ */ jsxs14(
1815
+ "section",
1816
+ {
1817
+ ...projectHtmlProps(props),
1818
+ className: classNames("eth-project-model-config-panel", className),
1819
+ "data-eth-component": "ProjectModelConfigPanel",
1820
+ role: "region",
1821
+ "aria-labelledby": headingId,
1822
+ tabIndex: -1,
1823
+ children: [
1824
+ /* @__PURE__ */ jsxs14("header", { className: "eth-project-model-config-panel__header", children: [
1825
+ /* @__PURE__ */ jsxs14("div", { className: "eth-project-model-config-panel__heading", children: [
1826
+ /* @__PURE__ */ jsx16("h3", { id: headingId, children: title }),
1827
+ description ? /* @__PURE__ */ jsx16("p", { children: description }) : null
1828
+ ] }),
1829
+ /* @__PURE__ */ jsxs14("dl", { className: "eth-project-model-config-panel__summary", "aria-label": "Selected model summary", children: [
1830
+ /* @__PURE__ */ jsxs14("div", { children: [
1831
+ /* @__PURE__ */ jsx16("dt", { children: "Provider" }),
1832
+ /* @__PURE__ */ jsx16("dd", { children: activeProviderData?.label ?? "Not configured" })
1833
+ ] }),
1834
+ /* @__PURE__ */ jsxs14("div", { children: [
1835
+ /* @__PURE__ */ jsx16("dt", { children: "Model" }),
1836
+ /* @__PURE__ */ jsx16("dd", { children: activeModelData?.label ?? (activeModel || "Default model") })
1837
+ ] }),
1838
+ /* @__PURE__ */ jsxs14("div", { children: [
1839
+ /* @__PURE__ */ jsx16("dt", { children: "Max tokens" }),
1840
+ /* @__PURE__ */ jsx16("dd", { children: formatNumber(tokenLimit) })
1841
+ ] })
1842
+ ] })
1843
+ ] }),
1844
+ /* @__PURE__ */ jsxs14("div", { className: "eth-project-model-config-panel__selectors", children: [
1845
+ /* @__PURE__ */ jsx16(FormField3, { id: "project-model-provider", label: "Provider", helperText: providerHelperText, children: /* @__PURE__ */ jsx16(
1846
+ Select4,
1847
+ {
1848
+ options: providerOptions2,
1849
+ value: activeProvider,
1850
+ disabled: !normalizedProviders.length,
1851
+ onChange: (event) => {
1852
+ const nextProvider = normalizedProviders.find(
1853
+ (provider) => provider.id === event.currentTarget.value
1854
+ );
1855
+ emit({
1856
+ provider: event.currentTarget.value,
1857
+ model: nextProvider?.models?.[0]?.id
1858
+ });
1859
+ }
1860
+ }
1861
+ ) }),
1862
+ /* @__PURE__ */ jsx16(FormField3, { id: "project-model-model", label: "Model", helperText: modelHelperText, children: /* @__PURE__ */ jsx16(
1863
+ Select4,
1864
+ {
1865
+ options: modelSelectOptions,
1866
+ value: activeModel,
1867
+ disabled: !modelOptions2.length,
1868
+ onChange: (event) => emit({ model: event.currentTarget.value })
1869
+ }
1870
+ ) })
1871
+ ] }),
1872
+ /* @__PURE__ */ jsx16(
1873
+ "div",
1874
+ {
1875
+ className: "eth-project-model-config-panel__parameters",
1876
+ role: "group",
1877
+ "aria-label": "Generation parameters",
1878
+ children: PARAMETER_DEFINITIONS.map((definition) => /* @__PURE__ */ jsx16(
1879
+ ProjectModelParamControl,
1880
+ {
1881
+ definition,
1882
+ value: params[definition.key] ?? definition.defaultValue,
1883
+ onChange: (value) => updateParam(definition.key, value)
1884
+ },
1885
+ definition.key
1886
+ ))
1887
+ }
1888
+ )
1889
+ ]
1890
+ }
1891
+ );
1892
+ }
1893
+ function ProjectModelParamControl({
1894
+ definition,
1895
+ value,
1896
+ onChange
1897
+ }) {
1898
+ const id = `project-model-${definition.key}`;
1899
+ const labelId = `${id}-label`;
1900
+ const descriptionId = `${id}-description`;
1901
+ const handleChange = (event) => {
1902
+ const nextValue = Number(event.currentTarget.value);
1903
+ if (Number.isFinite(nextValue)) onChange(nextValue);
1904
+ };
1905
+ const handleNumberChange = (event) => {
1906
+ const nextValue = event.currentTarget.value === "" ? void 0 : Number(event.currentTarget.value);
1907
+ if (nextValue === void 0 || Number.isFinite(nextValue)) onChange(nextValue);
1908
+ };
1909
+ return /* @__PURE__ */ jsxs14("div", { className: "eth-project-model-config-panel__parameter", children: [
1910
+ /* @__PURE__ */ jsxs14("div", { className: "eth-project-model-config-panel__parameter-copy", children: [
1911
+ /* @__PURE__ */ jsx16("h4", { id: labelId, children: definition.label }),
1912
+ /* @__PURE__ */ jsx16("p", { id: descriptionId, children: definition.description }),
1913
+ /* @__PURE__ */ jsxs14("dl", { className: "eth-project-model-config-panel__parameter-meta", children: [
1914
+ /* @__PURE__ */ jsxs14("div", { children: [
1915
+ /* @__PURE__ */ jsx16("dt", { children: "Range" }),
1916
+ /* @__PURE__ */ jsxs14("dd", { children: [
1917
+ formatParamValue(definition.min, definition.step),
1918
+ " -",
1919
+ " ",
1920
+ formatParamValue(definition.max, definition.step)
1921
+ ] })
1922
+ ] }),
1923
+ /* @__PURE__ */ jsxs14("div", { children: [
1924
+ /* @__PURE__ */ jsx16("dt", { children: "Current" }),
1925
+ /* @__PURE__ */ jsx16("dd", { children: formatParamValue(value, definition.step) })
1926
+ ] })
1927
+ ] })
1928
+ ] }),
1929
+ /* @__PURE__ */ jsx16("div", { className: "eth-project-model-config-panel__parameter-control", children: definition.control === "number" ? /* @__PURE__ */ jsx16(
1930
+ NumberInput,
1931
+ {
1932
+ id: `${id}-input`,
1933
+ "aria-describedby": descriptionId,
1934
+ "aria-labelledby": labelId,
1935
+ density: "compact",
1936
+ hideLabel: true,
1937
+ labelText: definition.label,
1938
+ min: definition.min,
1939
+ max: definition.max,
1940
+ step: definition.step,
1941
+ value,
1942
+ onChange: handleNumberChange
1943
+ }
1944
+ ) : /* @__PURE__ */ jsx16(
1945
+ Slider,
1946
+ {
1947
+ id: `${id}-slider`,
1948
+ "aria-describedby": descriptionId,
1949
+ "aria-labelledby": labelId,
1950
+ hideLabel: true,
1951
+ labelText: definition.label,
1952
+ min: definition.min,
1953
+ max: definition.max,
1954
+ step: definition.step,
1955
+ value,
1956
+ onChange: handleChange
1957
+ }
1958
+ ) })
1959
+ ] });
1960
+ }
1961
+ function formatParamValue(value, step) {
1962
+ const maximumFractionDigits = step >= 1 ? 0 : String(step).split(".")[1]?.length ?? 2;
1963
+ return new Intl.NumberFormat("en-US", {
1964
+ maximumFractionDigits
1965
+ }).format(value);
1966
+ }
1967
+
1968
+ // src/components/ProjectDashboardTemplate.tsx
1969
+ import { StatusDot as StatusDot8 } from "@echothink-ui/core";
1970
+ import { KPIBlock as KPIBlock2 } from "@echothink-ui/charts";
1971
+ import { PageHeader as PageHeader2 } from "@echothink-ui/layouts";
1972
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1973
+ var demoProject = {
1974
+ id: "project-demo",
1975
+ name: "Project dashboard",
1976
+ status: "in-progress",
1977
+ description: "Project overview for resources, tasks, activity, and app domains.",
1978
+ ownerLabel: "Operations",
1979
+ updatedAt: "Today",
1980
+ appDomainsCount: 2,
1981
+ openTasksCount: 7,
1982
+ healthStatus: "healthy"
1983
+ };
1984
+ var demoEvents = [
1985
+ {
1986
+ id: "event-1",
1987
+ timestamp: "2026-05-27 09:20",
1988
+ actor: "EchoThink",
1989
+ kind: "resource",
1990
+ summary: "Resource inventory refreshed"
1991
+ },
1992
+ {
1993
+ id: "event-2",
1994
+ timestamp: "2026-05-27 10:05",
1995
+ actor: "Project lead",
1996
+ kind: "app-domain",
1997
+ summary: "Analytics domain configured"
1998
+ }
1999
+ ];
2000
+ var demoResources = [
2001
+ { id: "res-1", label: "Launch brief", kind: "document", status: "synced", updatedAt: "Today" },
2002
+ { id: "res-2", label: "Task backlog", kind: "table", status: "in-progress", updatedAt: "Today" }
2003
+ ];
2004
+ var demoInstances = [
2005
+ { id: "inst-1", appDomainLabel: "Analytics", status: "running", health: "healthy" },
2006
+ { id: "inst-2", appDomainLabel: "Document review", status: "active", health: "warning" }
2007
+ ];
2008
+ function ProjectDashboardTemplate({
2009
+ project = demoProject,
2010
+ projects,
2011
+ events = demoEvents,
2012
+ resources = demoResources,
2013
+ instances = demoInstances,
2014
+ className,
2015
+ actions,
2016
+ ...props
2017
+ }) {
2018
+ const projectSet = projects ?? [project];
2019
+ return /* @__PURE__ */ jsxs15(
2020
+ "main",
2021
+ {
2022
+ ...projectHtmlProps(props),
2023
+ className: `eth-project-dashboard-template ${className ?? ""}`,
2024
+ "data-eth-component": "ProjectDashboardTemplate",
2025
+ children: [
2026
+ /* @__PURE__ */ jsx17(
2027
+ PageHeader2,
2028
+ {
2029
+ title: project.name,
2030
+ subtitle: project.description,
2031
+ status: project.status,
2032
+ actions
2033
+ }
2034
+ ),
2035
+ /* @__PURE__ */ jsxs15("div", { className: "eth-project-dashboard-template__kpis", role: "region", "aria-label": "Project KPIs", children: [
2036
+ /* @__PURE__ */ jsx17(KPIBlock2, { title: "Open tasks", value: project.openTasksCount ?? 0 }),
2037
+ /* @__PURE__ */ jsx17(KPIBlock2, { title: "App domains", value: project.appDomainsCount ?? 0 }),
2038
+ /* @__PURE__ */ jsx17(
2039
+ KPIBlock2,
2040
+ {
2041
+ title: "Health",
2042
+ value: project.healthStatus ?? "healthy",
2043
+ status: /* @__PURE__ */ jsx17(StatusDot8, { status: "synced", label: "Operational" })
2044
+ }
2045
+ )
2046
+ ] }),
2047
+ /* @__PURE__ */ jsx17(ProjectStatusSummary, { projects: projectSet }),
2048
+ /* @__PURE__ */ jsxs15("div", { className: "eth-project-dashboard-template__grid", children: [
2049
+ /* @__PURE__ */ jsx17(ProjectActivityTimeline, { events }),
2050
+ /* @__PURE__ */ jsx17(ProjectResourcePanel, { resources }),
2051
+ /* @__PURE__ */ jsx17(ProjectAppDomainPanel, { instances })
2052
+ ] })
2053
+ ]
2054
+ }
2055
+ );
2056
+ }
2057
+
2058
+ // src/index.tsx
2059
+ var ProjectComponentNames = [
2060
+ "ProjectManagementPage",
2061
+ "ProjectCard",
2062
+ "ProjectTable",
2063
+ "ProjectTab",
2064
+ "ProjectStatusSummary",
2065
+ "ProjectCreateForm",
2066
+ "ProjectSummaryPanel",
2067
+ "ProjectSettingsPanel",
2068
+ "ProjectScopeSelector",
2069
+ "ProjectResourcePanel",
2070
+ "ProjectActivityTimeline",
2071
+ "ProjectTabGroup",
2072
+ "ProjectMembersPanel",
2073
+ "ProjectPermissionPanel",
2074
+ "ProjectAppDomainPanel",
2075
+ "ProjectModelConfigPanel",
2076
+ "ProjectDashboardTemplate"
2077
+ ];
2078
+ export {
2079
+ ProjectActivityTimeline,
2080
+ ProjectAppDomainPanel,
2081
+ ProjectCard,
2082
+ ProjectComponentNames,
2083
+ ProjectCreateForm,
2084
+ ProjectDashboardTemplate,
2085
+ ProjectManagementPage,
2086
+ ProjectMembersPanel,
2087
+ ProjectModelConfigPanel,
2088
+ ProjectPermissionPanel,
2089
+ ProjectResourcePanel,
2090
+ ProjectScopeSelector,
2091
+ ProjectSettingsPanel,
2092
+ ProjectStatusSummary,
2093
+ ProjectSummaryPanel,
2094
+ ProjectTab,
2095
+ ProjectTabGroup,
2096
+ ProjectTable
2097
+ };
2098
+ //# sourceMappingURL=index.js.map