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