@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
@@ -0,0 +1,288 @@
1
+ import type { EthOperationalStatus, EthSeverity } from "@echothink-ui/core";
2
+ import type { ProjectHealthStatus, ProjectBaseProps, ProjectPermissionEntity, ProjectStatusSummaryData, ProjectSummary } from "./types";
3
+ export declare function classNames(...values: Array<string | false | null | undefined>): string;
4
+ export declare function healthToStatus(health?: ProjectHealthStatus): EthOperationalStatus;
5
+ export declare function healthLabel(health?: ProjectHealthStatus): "Critical" | "Warning" | "Healthy";
6
+ export declare function statusToSeverity(status: EthOperationalStatus): EthSeverity;
7
+ export declare function summarizeProjects(projects: ProjectSummary[]): ProjectStatusSummaryData;
8
+ export declare function entityId(entity: ProjectPermissionEntity): string;
9
+ export declare function entityLabel(entity: ProjectPermissionEntity): import("react").ReactNode;
10
+ export declare function permissionKey(subject: ProjectPermissionEntity, resource: ProjectPermissionEntity, action: ProjectPermissionEntity): string;
11
+ export declare function formatNumber(value: number | undefined): string;
12
+ export declare function projectHtmlProps(props: ProjectBaseProps): {
13
+ id?: string | undefined | undefined;
14
+ resource?: string | undefined | undefined;
15
+ role?: import("react").AriaRole | undefined;
16
+ defaultChecked?: boolean | undefined | undefined;
17
+ defaultValue?: string | number | readonly string[] | undefined;
18
+ suppressContentEditableWarning?: boolean | undefined | undefined;
19
+ suppressHydrationWarning?: boolean | undefined | undefined;
20
+ accessKey?: string | undefined | undefined;
21
+ autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters" | undefined | (string & {}) | undefined;
22
+ autoFocus?: boolean | undefined | undefined;
23
+ className?: string | undefined | undefined;
24
+ contentEditable?: (boolean | "true" | "false") | "inherit" | "plaintext-only" | undefined;
25
+ contextMenu?: string | undefined | undefined;
26
+ dir?: string | undefined | undefined;
27
+ draggable?: (boolean | "true" | "false") | undefined;
28
+ enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined | undefined;
29
+ hidden?: boolean | undefined | undefined;
30
+ lang?: string | undefined | undefined;
31
+ nonce?: string | undefined | undefined;
32
+ slot?: string | undefined | undefined;
33
+ spellCheck?: (boolean | "true" | "false") | undefined;
34
+ style?: import("react").CSSProperties | undefined;
35
+ tabIndex?: number | undefined | undefined;
36
+ translate?: "yes" | "no" | undefined | undefined;
37
+ radioGroup?: string | undefined | undefined;
38
+ about?: string | undefined | undefined;
39
+ content?: string | undefined | undefined;
40
+ datatype?: string | undefined | undefined;
41
+ inlist?: any;
42
+ prefix?: string | undefined | undefined;
43
+ property?: string | undefined | undefined;
44
+ rel?: string | undefined | undefined;
45
+ rev?: string | undefined | undefined;
46
+ typeof?: string | undefined | undefined;
47
+ vocab?: string | undefined | undefined;
48
+ autoCorrect?: string | undefined | undefined;
49
+ autoSave?: string | undefined | undefined;
50
+ color?: string | undefined | undefined;
51
+ itemProp?: string | undefined | undefined;
52
+ itemScope?: boolean | undefined | undefined;
53
+ itemType?: string | undefined | undefined;
54
+ itemID?: string | undefined | undefined;
55
+ itemRef?: string | undefined | undefined;
56
+ results?: number | undefined | undefined;
57
+ security?: string | undefined | undefined;
58
+ unselectable?: "on" | "off" | undefined | undefined;
59
+ popover?: "" | "auto" | "manual" | "hint" | undefined | undefined;
60
+ popoverTargetAction?: "toggle" | "show" | "hide" | undefined | undefined;
61
+ popoverTarget?: string | undefined | undefined;
62
+ inert?: boolean | undefined | undefined;
63
+ inputMode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined | undefined;
64
+ is?: string | undefined | undefined;
65
+ exportparts?: string | undefined | undefined;
66
+ part?: string | undefined | undefined;
67
+ "aria-activedescendant"?: string | undefined | undefined;
68
+ "aria-atomic"?: (boolean | "true" | "false") | undefined;
69
+ "aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined | undefined;
70
+ "aria-braillelabel"?: string | undefined | undefined;
71
+ "aria-brailleroledescription"?: string | undefined | undefined;
72
+ "aria-busy"?: (boolean | "true" | "false") | undefined;
73
+ "aria-checked"?: boolean | "false" | "mixed" | "true" | undefined | undefined;
74
+ "aria-colcount"?: number | undefined | undefined;
75
+ "aria-colindex"?: number | undefined | undefined;
76
+ "aria-colindextext"?: string | undefined | undefined;
77
+ "aria-colspan"?: number | undefined | undefined;
78
+ "aria-controls"?: string | undefined | undefined;
79
+ "aria-current"?: boolean | "false" | "true" | "page" | "step" | "location" | "date" | "time" | undefined | undefined;
80
+ "aria-describedby"?: string | undefined | undefined;
81
+ "aria-description"?: string | undefined | undefined;
82
+ "aria-details"?: string | undefined | undefined;
83
+ "aria-disabled"?: (boolean | "true" | "false") | undefined;
84
+ "aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup" | undefined | undefined;
85
+ "aria-errormessage"?: string | undefined | undefined;
86
+ "aria-expanded"?: (boolean | "true" | "false") | undefined;
87
+ "aria-flowto"?: string | undefined | undefined;
88
+ "aria-grabbed"?: (boolean | "true" | "false") | undefined;
89
+ "aria-haspopup"?: boolean | "false" | "true" | "menu" | "listbox" | "tree" | "grid" | "dialog" | undefined | undefined;
90
+ "aria-hidden"?: (boolean | "true" | "false") | undefined;
91
+ "aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling" | undefined | undefined;
92
+ "aria-keyshortcuts"?: string | undefined | undefined;
93
+ "aria-label"?: string | undefined | undefined;
94
+ "aria-labelledby"?: string | undefined | undefined;
95
+ "aria-level"?: number | undefined | undefined;
96
+ "aria-live"?: "off" | "assertive" | "polite" | undefined | undefined;
97
+ "aria-modal"?: (boolean | "true" | "false") | undefined;
98
+ "aria-multiline"?: (boolean | "true" | "false") | undefined;
99
+ "aria-multiselectable"?: (boolean | "true" | "false") | undefined;
100
+ "aria-orientation"?: "horizontal" | "vertical" | undefined | undefined;
101
+ "aria-owns"?: string | undefined | undefined;
102
+ "aria-placeholder"?: string | undefined | undefined;
103
+ "aria-posinset"?: number | undefined | undefined;
104
+ "aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined | undefined;
105
+ "aria-readonly"?: (boolean | "true" | "false") | undefined;
106
+ "aria-relevant"?: "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text" | "text additions" | "text removals" | undefined | undefined;
107
+ "aria-required"?: (boolean | "true" | "false") | undefined;
108
+ "aria-roledescription"?: string | undefined | undefined;
109
+ "aria-rowcount"?: number | undefined | undefined;
110
+ "aria-rowindex"?: number | undefined | undefined;
111
+ "aria-rowindextext"?: string | undefined | undefined;
112
+ "aria-rowspan"?: number | undefined | undefined;
113
+ "aria-selected"?: (boolean | "true" | "false") | undefined;
114
+ "aria-setsize"?: number | undefined | undefined;
115
+ "aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined | undefined;
116
+ "aria-valuemax"?: number | undefined | undefined;
117
+ "aria-valuemin"?: number | undefined | undefined;
118
+ "aria-valuenow"?: number | undefined | undefined;
119
+ "aria-valuetext"?: string | undefined | undefined;
120
+ dangerouslySetInnerHTML?: {
121
+ __html: string | TrustedHTML;
122
+ } | undefined | undefined;
123
+ onCopy?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
124
+ onCopyCapture?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
125
+ onCut?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
126
+ onCutCapture?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
127
+ onPaste?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
128
+ onPasteCapture?: import("react").ClipboardEventHandler<HTMLElement> | undefined;
129
+ onCompositionEnd?: import("react").CompositionEventHandler<HTMLElement> | undefined;
130
+ onCompositionEndCapture?: import("react").CompositionEventHandler<HTMLElement> | undefined;
131
+ onCompositionStart?: import("react").CompositionEventHandler<HTMLElement> | undefined;
132
+ onCompositionStartCapture?: import("react").CompositionEventHandler<HTMLElement> | undefined;
133
+ onCompositionUpdate?: import("react").CompositionEventHandler<HTMLElement> | undefined;
134
+ onCompositionUpdateCapture?: import("react").CompositionEventHandler<HTMLElement> | undefined;
135
+ onFocus?: import("react").FocusEventHandler<HTMLElement> | undefined;
136
+ onFocusCapture?: import("react").FocusEventHandler<HTMLElement> | undefined;
137
+ onBlur?: import("react").FocusEventHandler<HTMLElement> | undefined;
138
+ onBlurCapture?: import("react").FocusEventHandler<HTMLElement> | undefined;
139
+ onChangeCapture?: import("react").ChangeEventHandler<HTMLElement, Element> | undefined;
140
+ onBeforeInput?: import("react").InputEventHandler<HTMLElement> | undefined;
141
+ onBeforeInputCapture?: import("react").InputEventHandler<HTMLElement> | undefined;
142
+ onInput?: import("react").InputEventHandler<HTMLElement> | undefined;
143
+ onInputCapture?: import("react").InputEventHandler<HTMLElement> | undefined;
144
+ onReset?: import("react").ReactEventHandler<HTMLElement> | undefined;
145
+ onResetCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
146
+ onSubmitCapture?: import("react").SubmitEventHandler<HTMLElement> | undefined;
147
+ onInvalid?: import("react").ReactEventHandler<HTMLElement> | undefined;
148
+ onInvalidCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
149
+ onLoad?: import("react").ReactEventHandler<HTMLElement> | undefined;
150
+ onLoadCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
151
+ onError?: import("react").ReactEventHandler<HTMLElement> | undefined;
152
+ onErrorCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
153
+ onKeyDown?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
154
+ onKeyDownCapture?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
155
+ onKeyPress?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
156
+ onKeyPressCapture?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
157
+ onKeyUp?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
158
+ onKeyUpCapture?: import("react").KeyboardEventHandler<HTMLElement> | undefined;
159
+ onAbort?: import("react").ReactEventHandler<HTMLElement> | undefined;
160
+ onAbortCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
161
+ onCanPlay?: import("react").ReactEventHandler<HTMLElement> | undefined;
162
+ onCanPlayCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
163
+ onCanPlayThrough?: import("react").ReactEventHandler<HTMLElement> | undefined;
164
+ onCanPlayThroughCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
165
+ onDurationChange?: import("react").ReactEventHandler<HTMLElement> | undefined;
166
+ onDurationChangeCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
167
+ onEmptied?: import("react").ReactEventHandler<HTMLElement> | undefined;
168
+ onEmptiedCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
169
+ onEncrypted?: import("react").ReactEventHandler<HTMLElement> | undefined;
170
+ onEncryptedCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
171
+ onEnded?: import("react").ReactEventHandler<HTMLElement> | undefined;
172
+ onEndedCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
173
+ onLoadedData?: import("react").ReactEventHandler<HTMLElement> | undefined;
174
+ onLoadedDataCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
175
+ onLoadedMetadata?: import("react").ReactEventHandler<HTMLElement> | undefined;
176
+ onLoadedMetadataCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
177
+ onLoadStart?: import("react").ReactEventHandler<HTMLElement> | undefined;
178
+ onLoadStartCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
179
+ onPause?: import("react").ReactEventHandler<HTMLElement> | undefined;
180
+ onPauseCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
181
+ onPlay?: import("react").ReactEventHandler<HTMLElement> | undefined;
182
+ onPlayCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
183
+ onPlaying?: import("react").ReactEventHandler<HTMLElement> | undefined;
184
+ onPlayingCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
185
+ onProgress?: import("react").ReactEventHandler<HTMLElement> | undefined;
186
+ onProgressCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
187
+ onRateChange?: import("react").ReactEventHandler<HTMLElement> | undefined;
188
+ onRateChangeCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
189
+ onSeeked?: import("react").ReactEventHandler<HTMLElement> | undefined;
190
+ onSeekedCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
191
+ onSeeking?: import("react").ReactEventHandler<HTMLElement> | undefined;
192
+ onSeekingCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
193
+ onStalled?: import("react").ReactEventHandler<HTMLElement> | undefined;
194
+ onStalledCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
195
+ onSuspend?: import("react").ReactEventHandler<HTMLElement> | undefined;
196
+ onSuspendCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
197
+ onTimeUpdate?: import("react").ReactEventHandler<HTMLElement> | undefined;
198
+ onTimeUpdateCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
199
+ onVolumeChange?: import("react").ReactEventHandler<HTMLElement> | undefined;
200
+ onVolumeChangeCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
201
+ onWaiting?: import("react").ReactEventHandler<HTMLElement> | undefined;
202
+ onWaitingCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
203
+ onAuxClick?: import("react").MouseEventHandler<HTMLElement> | undefined;
204
+ onAuxClickCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
205
+ onClick?: import("react").MouseEventHandler<HTMLElement> | undefined;
206
+ onClickCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
207
+ onContextMenu?: import("react").MouseEventHandler<HTMLElement> | undefined;
208
+ onContextMenuCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
209
+ onDoubleClick?: import("react").MouseEventHandler<HTMLElement> | undefined;
210
+ onDoubleClickCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
211
+ onDrag?: import("react").DragEventHandler<HTMLElement> | undefined;
212
+ onDragCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
213
+ onDragEnd?: import("react").DragEventHandler<HTMLElement> | undefined;
214
+ onDragEndCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
215
+ onDragEnter?: import("react").DragEventHandler<HTMLElement> | undefined;
216
+ onDragEnterCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
217
+ onDragExit?: import("react").DragEventHandler<HTMLElement> | undefined;
218
+ onDragExitCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
219
+ onDragLeave?: import("react").DragEventHandler<HTMLElement> | undefined;
220
+ onDragLeaveCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
221
+ onDragOver?: import("react").DragEventHandler<HTMLElement> | undefined;
222
+ onDragOverCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
223
+ onDragStart?: import("react").DragEventHandler<HTMLElement> | undefined;
224
+ onDragStartCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
225
+ onDrop?: import("react").DragEventHandler<HTMLElement> | undefined;
226
+ onDropCapture?: import("react").DragEventHandler<HTMLElement> | undefined;
227
+ onMouseDown?: import("react").MouseEventHandler<HTMLElement> | undefined;
228
+ onMouseDownCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
229
+ onMouseEnter?: import("react").MouseEventHandler<HTMLElement> | undefined;
230
+ onMouseLeave?: import("react").MouseEventHandler<HTMLElement> | undefined;
231
+ onMouseMove?: import("react").MouseEventHandler<HTMLElement> | undefined;
232
+ onMouseMoveCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
233
+ onMouseOut?: import("react").MouseEventHandler<HTMLElement> | undefined;
234
+ onMouseOutCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
235
+ onMouseOver?: import("react").MouseEventHandler<HTMLElement> | undefined;
236
+ onMouseOverCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
237
+ onMouseUp?: import("react").MouseEventHandler<HTMLElement> | undefined;
238
+ onMouseUpCapture?: import("react").MouseEventHandler<HTMLElement> | undefined;
239
+ onSelectCapture?: import("react").ReactEventHandler<HTMLElement> | undefined;
240
+ onTouchCancel?: import("react").TouchEventHandler<HTMLElement> | undefined;
241
+ onTouchCancelCapture?: import("react").TouchEventHandler<HTMLElement> | undefined;
242
+ onTouchEnd?: import("react").TouchEventHandler<HTMLElement> | undefined;
243
+ onTouchEndCapture?: import("react").TouchEventHandler<HTMLElement> | undefined;
244
+ onTouchMove?: import("react").TouchEventHandler<HTMLElement> | undefined;
245
+ onTouchMoveCapture?: import("react").TouchEventHandler<HTMLElement> | undefined;
246
+ onTouchStart?: import("react").TouchEventHandler<HTMLElement> | undefined;
247
+ onTouchStartCapture?: import("react").TouchEventHandler<HTMLElement> | undefined;
248
+ onPointerDown?: import("react").PointerEventHandler<HTMLElement> | undefined;
249
+ onPointerDownCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
250
+ onPointerMove?: import("react").PointerEventHandler<HTMLElement> | undefined;
251
+ onPointerMoveCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
252
+ onPointerUp?: import("react").PointerEventHandler<HTMLElement> | undefined;
253
+ onPointerUpCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
254
+ onPointerCancel?: import("react").PointerEventHandler<HTMLElement> | undefined;
255
+ onPointerCancelCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
256
+ onPointerEnter?: import("react").PointerEventHandler<HTMLElement> | undefined;
257
+ onPointerLeave?: import("react").PointerEventHandler<HTMLElement> | undefined;
258
+ onPointerOver?: import("react").PointerEventHandler<HTMLElement> | undefined;
259
+ onPointerOverCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
260
+ onPointerOut?: import("react").PointerEventHandler<HTMLElement> | undefined;
261
+ onPointerOutCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
262
+ onGotPointerCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
263
+ onGotPointerCaptureCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
264
+ onLostPointerCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
265
+ onLostPointerCaptureCapture?: import("react").PointerEventHandler<HTMLElement> | undefined;
266
+ onScroll?: import("react").UIEventHandler<HTMLElement> | undefined;
267
+ onScrollCapture?: import("react").UIEventHandler<HTMLElement> | undefined;
268
+ onScrollEnd?: import("react").UIEventHandler<HTMLElement> | undefined;
269
+ onScrollEndCapture?: import("react").UIEventHandler<HTMLElement> | undefined;
270
+ onWheel?: import("react").WheelEventHandler<HTMLElement> | undefined;
271
+ onWheelCapture?: import("react").WheelEventHandler<HTMLElement> | undefined;
272
+ onAnimationStart?: import("react").AnimationEventHandler<HTMLElement> | undefined;
273
+ onAnimationStartCapture?: import("react").AnimationEventHandler<HTMLElement> | undefined;
274
+ onAnimationEnd?: import("react").AnimationEventHandler<HTMLElement> | undefined;
275
+ onAnimationEndCapture?: import("react").AnimationEventHandler<HTMLElement> | undefined;
276
+ onAnimationIteration?: import("react").AnimationEventHandler<HTMLElement> | undefined;
277
+ onAnimationIterationCapture?: import("react").AnimationEventHandler<HTMLElement> | undefined;
278
+ onToggle?: import("react").ToggleEventHandler<HTMLElement> | undefined;
279
+ onBeforeToggle?: import("react").ToggleEventHandler<HTMLElement> | undefined;
280
+ onTransitionCancel?: import("react").TransitionEventHandler<HTMLElement> | undefined;
281
+ onTransitionCancelCapture?: import("react").TransitionEventHandler<HTMLElement> | undefined;
282
+ onTransitionEnd?: import("react").TransitionEventHandler<HTMLElement> | undefined;
283
+ onTransitionEndCapture?: import("react").TransitionEventHandler<HTMLElement> | undefined;
284
+ onTransitionRun?: import("react").TransitionEventHandler<HTMLElement> | undefined;
285
+ onTransitionRunCapture?: import("react").TransitionEventHandler<HTMLElement> | undefined;
286
+ onTransitionStart?: import("react").TransitionEventHandler<HTMLElement> | undefined;
287
+ onTransitionStartCapture?: import("react").TransitionEventHandler<HTMLElement> | undefined;
288
+ };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@echothink-ui/project",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "sideEffects": [
7
+ "**/*.css"
8
+ ],
9
+ "main": "./dist/index.cjs",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.cjs"
17
+ },
18
+ "./styles.css": "./src/styles.css"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "src",
23
+ "README.md"
24
+ ],
25
+ "peerDependencies": {
26
+ "react": ">=18.3.0",
27
+ "react-dom": ">=18.3.0"
28
+ },
29
+ "dependencies": {
30
+ "@echothink-ui/core": "0.2.0",
31
+ "@echothink-ui/data": "0.2.0",
32
+ "@echothink-ui/icons": "0.2.0",
33
+ "@echothink-ui/charts": "0.2.0",
34
+ "@echothink-ui/layouts": "0.2.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "scripts": {
40
+ "build": "tsup src/index.tsx --format esm,cjs --sourcemap --clean --external react --external react-dom && tsc -p tsconfig.json --declaration --emitDeclarationOnly --noEmit false --outDir dist",
41
+ "typecheck": "tsc -p tsconfig.json --noEmit",
42
+ "test": "vitest run --config ../../vitest.config.ts --passWithNoTests",
43
+ "lint": "eslint src"
44
+ }
45
+ }
@@ -0,0 +1,43 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { describe, expect, it } from "vitest";
3
+ import { ProjectActivityTimeline } from "./ProjectActivityTimeline";
4
+
5
+ describe("@echothink-ui/project ProjectActivityTimeline", () => {
6
+ it("renders project events with semantic time values and kind tone classes", () => {
7
+ render(
8
+ <ProjectActivityTimeline
9
+ events={[
10
+ {
11
+ id: "approval",
12
+ timestamp: "2026-05-27 10:42",
13
+ actor: "JD",
14
+ kind: "approval",
15
+ summary: "Approved Wave Q3",
16
+ details: "Marked the collection wave ready for release."
17
+ },
18
+ {
19
+ id: "risk",
20
+ timestamp: "09:14",
21
+ actor: "Agent",
22
+ kind: "blocked",
23
+ summary: "Flagged missing transcript evidence"
24
+ }
25
+ ]}
26
+ />
27
+ );
28
+
29
+ expect(screen.getByRole("region", { name: "Activity" })).toBeTruthy();
30
+ expect(screen.getByRole("list")).toBeTruthy();
31
+ expect(screen.getByText("Approved Wave Q3")).toBeTruthy();
32
+ expect(screen.getByText("Marked the collection wave ready for release.")).toBeTruthy();
33
+ expect(screen.getByText("By JD")).toBeTruthy();
34
+ expect(screen.getByText("10:42").getAttribute("datetime")).toBe("2026-05-27T10:42");
35
+ expect(screen.getByText("09:14").getAttribute("datetime")).toBe("09:14");
36
+ expect(screen.getByText("approval").closest("li")?.className).toContain(
37
+ "eth-project-activity-timeline__item--success"
38
+ );
39
+ expect(screen.getByText("blocked").closest("li")?.className).toContain(
40
+ "eth-project-activity-timeline__item--danger"
41
+ );
42
+ });
43
+ });
@@ -0,0 +1,118 @@
1
+ import * as React from "react";
2
+ import { Badge, EmptyState, type EthSeverity } from "@echothink-ui/core";
3
+ import type { ProjectActivityEvent, ProjectBaseProps } from "../types";
4
+ import { classNames, projectHtmlProps } from "../utils";
5
+
6
+ export interface ProjectActivityTimelineProps extends ProjectBaseProps {
7
+ events?: ProjectActivityEvent[];
8
+ }
9
+
10
+ function getActivityKindTone(kind: string): EthSeverity {
11
+ const normalized = kind.toLowerCase();
12
+
13
+ if (/(approve|approved|approval|complete|completed|success|release)/.test(normalized)) {
14
+ return "success";
15
+ }
16
+
17
+ if (/(block|blocked|fail|failed|error|reject|rejected|risk)/.test(normalized)) {
18
+ return "danger";
19
+ }
20
+
21
+ if (/(pending|review|warning|needs|wait)/.test(normalized)) {
22
+ return "warning";
23
+ }
24
+
25
+ if (/(draft|resource|domain|config|sync|agent|update)/.test(normalized)) {
26
+ return "info";
27
+ }
28
+
29
+ return "neutral";
30
+ }
31
+
32
+ function getActivityTime(timestamp: string) {
33
+ const value = timestamp.trim();
34
+ const dateTimeMatch = value.match(
35
+ /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2})(?::\d{2}(?:\.\d{1,3})?)?(?:Z|[+-]\d{2}:?\d{2})?$/
36
+ );
37
+
38
+ if (dateTimeMatch) {
39
+ return {
40
+ dateTime: `${dateTimeMatch[1]}T${dateTimeMatch[2]}`,
41
+ label: dateTimeMatch[2]
42
+ };
43
+ }
44
+
45
+ if (/^\d{2}:\d{2}(?::\d{2})?$/.test(value) || /^\d{4}-\d{2}-\d{2}$/.test(value)) {
46
+ return { dateTime: value, label: value };
47
+ }
48
+
49
+ return { dateTime: undefined, label: value };
50
+ }
51
+
52
+ export function ProjectActivityTimeline({
53
+ events = [],
54
+ className,
55
+ title = "Activity",
56
+ ...props
57
+ }: ProjectActivityTimelineProps) {
58
+ return (
59
+ <section
60
+ {...projectHtmlProps(props)}
61
+ className={classNames("eth-project-activity-timeline", className)}
62
+ data-eth-component="ProjectActivityTimeline"
63
+ role="region"
64
+ aria-label={typeof title === "string" ? title : "Project activity"}
65
+ tabIndex={-1}
66
+ >
67
+ {title ? (
68
+ <header className="eth-project-activity-timeline__header">
69
+ <h3>{title}</h3>
70
+ </header>
71
+ ) : null}
72
+ {events.length ? (
73
+ <ol className="eth-project-activity-timeline__list">
74
+ {events.map((event) => {
75
+ const tone = getActivityKindTone(event.kind);
76
+ const time = getActivityTime(event.timestamp);
77
+
78
+ return (
79
+ <li
80
+ key={event.id}
81
+ className={classNames(
82
+ "eth-project-activity-timeline__item",
83
+ `eth-project-activity-timeline__item--${tone}`
84
+ )}
85
+ data-kind={event.kind}
86
+ >
87
+ <time className="eth-project-activity-timeline__time" dateTime={time.dateTime}>
88
+ {time.label}
89
+ </time>
90
+ <div className="eth-project-activity-timeline__rail" aria-hidden>
91
+ <span className="eth-project-activity-timeline__marker" />
92
+ </div>
93
+ <article className="eth-project-activity-timeline__event">
94
+ <header className="eth-project-activity-timeline__event-header">
95
+ <Badge severity={tone}>{event.kind}</Badge>
96
+ {event.actor ? (
97
+ <span className="eth-project-activity-timeline__actor">
98
+ By {event.actor}
99
+ </span>
100
+ ) : null}
101
+ </header>
102
+ <strong className="eth-project-activity-timeline__summary">
103
+ {event.summary}
104
+ </strong>
105
+ {event.details ? (
106
+ <p className="eth-project-activity-timeline__details">{event.details}</p>
107
+ ) : null}
108
+ </article>
109
+ </li>
110
+ );
111
+ })}
112
+ </ol>
113
+ ) : (
114
+ <EmptyState title="No activity" description="Project changes and events appear here." />
115
+ )}
116
+ </section>
117
+ );
118
+ }
@@ -0,0 +1,147 @@
1
+ import * as React from "react";
2
+ import { EmptyState, StatusDot, statusLabel, type EthAction } from "@echothink-ui/core";
3
+ import { DataTable, type DataColumn } from "@echothink-ui/data";
4
+ import type { ProjectAppDomainInstance, ProjectBaseProps } from "../types";
5
+ import {
6
+ classNames,
7
+ formatNumber,
8
+ healthLabel,
9
+ healthToStatus,
10
+ projectHtmlProps
11
+ } from "../utils";
12
+
13
+ const attentionStatuses = new Set(["blocked", "failed", "stale", "warning", "approval-required"]);
14
+
15
+ export interface ProjectAppDomainPanelProps extends ProjectBaseProps {
16
+ instances?: ProjectAppDomainInstance[];
17
+ onOpen?: (instanceId: string) => void;
18
+ onConfigure?: (instanceId: string) => void;
19
+ onRemove?: (instanceId: string) => void;
20
+ }
21
+
22
+ export function ProjectAppDomainPanel({
23
+ instances = [],
24
+ onOpen,
25
+ onConfigure,
26
+ onRemove,
27
+ className,
28
+ title = "App domains",
29
+ ...props
30
+ }: ProjectAppDomainPanelProps) {
31
+ const runningCount = instances.filter((instance) => instance.status === "running").length;
32
+ const attentionCount = instances.filter(
33
+ (instance) =>
34
+ instance.health === "warning" ||
35
+ instance.health === "critical" ||
36
+ attentionStatuses.has(instance.status)
37
+ ).length;
38
+ const hasRowActions = Boolean(onOpen || onConfigure || onRemove);
39
+
40
+ const columns = React.useMemo<DataColumn<ProjectAppDomainInstance>[]>(
41
+ () => [
42
+ {
43
+ key: "appDomainLabel",
44
+ header: "App domain",
45
+ width: "42%",
46
+ render: (instance) => (
47
+ <div className="eth-project-app-domain-panel__domain">
48
+ <strong>{instance.appDomainLabel}</strong>
49
+ <span>{instance.id}</span>
50
+ </div>
51
+ )
52
+ },
53
+ {
54
+ key: "status",
55
+ header: "Status",
56
+ width: "18%",
57
+ render: (instance) => (
58
+ <StatusDot status={instance.status} label={statusLabel(instance.status)} />
59
+ )
60
+ },
61
+ {
62
+ key: "health",
63
+ header: "Health",
64
+ width: "18%",
65
+ render: (instance) => (
66
+ <StatusDot status={healthToStatus(instance.health)} label={healthLabel(instance.health)} />
67
+ )
68
+ }
69
+ ],
70
+ []
71
+ );
72
+
73
+ const rowActions = React.useCallback(
74
+ (instance: ProjectAppDomainInstance): EthAction[] => {
75
+ const actions: EthAction[] = [];
76
+ if (onOpen) {
77
+ actions.push({
78
+ id: "open",
79
+ label: "Open",
80
+ onSelect: () => onOpen(instance.id)
81
+ });
82
+ }
83
+ if (onConfigure) {
84
+ actions.push({
85
+ id: "configure",
86
+ label: "Configure",
87
+ onSelect: () => onConfigure(instance.id)
88
+ });
89
+ }
90
+ if (onRemove) {
91
+ actions.push({
92
+ id: "remove",
93
+ label: "Remove",
94
+ intent: "danger",
95
+ onSelect: () => onRemove(instance.id)
96
+ });
97
+ }
98
+ return actions;
99
+ },
100
+ [onConfigure, onOpen, onRemove]
101
+ );
102
+
103
+ return (
104
+ <section
105
+ {...projectHtmlProps(props)}
106
+ className={classNames("eth-project-app-domain-panel", className)}
107
+ data-eth-component="ProjectAppDomainPanel"
108
+ role="region"
109
+ aria-label={typeof title === "string" ? title : "Project app domains"}
110
+ tabIndex={-1}
111
+ >
112
+ {title ? (
113
+ <header className="eth-project-app-domain-panel__header">
114
+ <h3>{title}</h3>
115
+ <dl className="eth-project-app-domain-panel__summary" aria-label="App domain summary">
116
+ <div>
117
+ <dt>Installed</dt>
118
+ <dd>{formatNumber(instances.length)}</dd>
119
+ </div>
120
+ <div>
121
+ <dt>Running</dt>
122
+ <dd>{formatNumber(runningCount)}</dd>
123
+ </div>
124
+ <div>
125
+ <dt>Attention</dt>
126
+ <dd>{formatNumber(attentionCount)}</dd>
127
+ </div>
128
+ </dl>
129
+ </header>
130
+ ) : null}
131
+ <DataTable
132
+ rows={instances}
133
+ columns={columns}
134
+ rowKey="id"
135
+ density="compact"
136
+ className="eth-project-app-domain-panel__table"
137
+ rowActions={hasRowActions ? rowActions : undefined}
138
+ emptyState={
139
+ <EmptyState
140
+ title="No app domains"
141
+ description="Installed and running project app domains appear here."
142
+ />
143
+ }
144
+ />
145
+ </section>
146
+ );
147
+ }