@autobe/ui 0.19.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/AutoBeChatUploadSendButton.d.ts +15 -0
- package/lib/AutoBeChatUploadSendButton.js +38 -0
- package/lib/AutoBeChatUploadSendButton.js.map +1 -0
- package/lib/AutoBeFileUploadBox.d.ts +10 -0
- package/lib/AutoBeFileUploadBox.js +68 -0
- package/lib/AutoBeFileUploadBox.js.map +1 -0
- package/lib/AutoBeVoiceRecoderButton.d.ts +11 -0
- package/lib/AutoBeVoiceRecoderButton.js +58 -0
- package/lib/AutoBeVoiceRecoderButton.js.map +1 -0
- package/lib/events/AutoBeCompleteEventMovie.d.ts +7 -0
- package/lib/events/AutoBeCompleteEventMovie.js +210 -0
- package/lib/events/AutoBeCompleteEventMovie.js.map +1 -0
- package/lib/events/AutoBeProgressEventMovie.js +2 -61
- package/lib/events/AutoBeProgressEventMovie.js.map +1 -1
- package/lib/events/AutoBeScenarioEventMovie.js +2 -44
- package/lib/events/AutoBeScenarioEventMovie.js.map +1 -1
- package/lib/events/AutoBeValidateEventMovie.d.ts +6 -0
- package/lib/events/AutoBeValidateEventMovie.js +115 -0
- package/lib/events/AutoBeValidateEventMovie.js.map +1 -0
- package/lib/events/common/CollapsibleEventGroup.d.ts +28 -0
- package/lib/events/common/CollapsibleEventGroup.js +89 -0
- package/lib/events/common/CollapsibleEventGroup.js.map +1 -0
- package/lib/events/common/EventCard.d.ts +13 -0
- package/lib/events/common/EventCard.js +43 -0
- package/lib/events/common/EventCard.js.map +1 -0
- package/lib/events/common/EventContent.d.ts +11 -0
- package/lib/events/common/EventContent.js +14 -0
- package/lib/events/common/EventContent.js.map +1 -0
- package/lib/events/common/EventHeader.d.ts +15 -0
- package/lib/events/common/EventHeader.js +41 -0
- package/lib/events/common/EventHeader.js.map +1 -0
- package/lib/events/common/EventIcon.d.ts +11 -0
- package/lib/events/common/EventIcon.js +50 -0
- package/lib/events/common/EventIcon.js.map +1 -0
- package/lib/events/common/ProgressBar.d.ts +14 -0
- package/lib/events/common/ProgressBar.js +33 -0
- package/lib/events/common/ProgressBar.js.map +1 -0
- package/lib/events/common/index.d.ts +6 -0
- package/lib/events/common/index.js +16 -0
- package/lib/events/common/index.js.map +1 -0
- package/lib/events/groups/ValidateEventGroup.d.ts +12 -0
- package/lib/events/groups/ValidateEventGroup.js +78 -0
- package/lib/events/groups/ValidateEventGroup.js.map +1 -0
- package/lib/events/groups/index.d.ts +1 -0
- package/lib/events/groups/index.js +6 -0
- package/lib/events/groups/index.js.map +1 -0
- package/lib/events/index.d.ts +5 -0
- package/lib/events/index.js +25 -1
- package/lib/events/index.js.map +1 -1
- package/lib/events/utils/eventGrouper.d.ts +20 -0
- package/lib/events/utils/eventGrouper.js +74 -0
- package/lib/events/utils/eventGrouper.js.map +1 -0
- package/lib/events/utils/index.d.ts +1 -0
- package/lib/events/utils/index.js +6 -0
- package/lib/events/utils/index.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +7 -1
- package/lib/index.js.map +1 -1
- package/lib/utils/AutoBeFileUploader.d.ts +28 -0
- package/lib/utils/AutoBeFileUploader.js +237 -0
- package/lib/utils/AutoBeFileUploader.js.map +1 -0
- package/lib/utils/AutoBeVoiceRecorder.d.ts +7 -0
- package/lib/utils/AutoBeVoiceRecorder.js +94 -0
- package/lib/utils/AutoBeVoiceRecorder.js.map +1 -0
- package/lib/utils/index.d.ts +3 -0
- package/lib/utils/index.js +20 -0
- package/lib/utils/index.js.map +1 -0
- package/package.json +17 -2
- package/src/AutoBeChatUploadSendButton.tsx +66 -0
- package/src/AutoBeFileUploadBox.tsx +124 -0
- package/src/AutoBeVoiceRecoderButton.tsx +100 -0
- package/src/events/AutoBeCompleteEventMovie.tsx +402 -0
- package/src/events/AutoBeProgressEventMovie.tsx +12 -125
- package/src/events/AutoBeScenarioEventMovie.tsx +5 -93
- package/src/events/AutoBeValidateEventMovie.tsx +326 -0
- package/src/events/README.md +169 -0
- package/src/events/common/CollapsibleEventGroup.tsx +220 -0
- package/src/events/common/EventCard.tsx +61 -0
- package/src/events/common/EventContent.tsx +31 -0
- package/src/events/common/EventHeader.tsx +85 -0
- package/src/events/common/EventIcon.tsx +82 -0
- package/src/events/common/ProgressBar.tsx +63 -0
- package/src/events/common/index.ts +13 -0
- package/src/events/groups/ValidateEventGroup.tsx +150 -0
- package/src/events/groups/index.ts +4 -0
- package/src/events/index.ts +14 -0
- package/src/events/utils/eventGrouper.tsx +118 -0
- package/src/events/utils/index.ts +1 -0
- package/src/index.ts +6 -0
- package/src/utils/AutoBeFileUploader.ts +279 -0
- package/src/utils/AutoBeVoiceRecorder.ts +95 -0
- package/src/utils/index.ts +3 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AutoBeInterfaceComplementEvent,
|
|
3
|
+
AutoBeInterfaceOperationsReviewEvent,
|
|
4
|
+
AutoBePrismaInsufficientEvent,
|
|
5
|
+
AutoBePrismaValidateEvent,
|
|
6
|
+
AutoBeRealizeAuthorizationValidateEvent,
|
|
7
|
+
AutoBeRealizeValidateEvent,
|
|
8
|
+
AutoBeTestValidateEvent,
|
|
9
|
+
} from "@autobe/interface";
|
|
10
|
+
import { JSX } from "react";
|
|
11
|
+
|
|
12
|
+
import { EventCard, EventContent, EventHeader } from "./common";
|
|
13
|
+
|
|
14
|
+
export interface IAutoBeValidateEventMovieProps {
|
|
15
|
+
event:
|
|
16
|
+
| AutoBePrismaInsufficientEvent
|
|
17
|
+
| AutoBePrismaValidateEvent
|
|
18
|
+
| AutoBeInterfaceOperationsReviewEvent
|
|
19
|
+
| AutoBeInterfaceComplementEvent
|
|
20
|
+
| AutoBeTestValidateEvent
|
|
21
|
+
| AutoBeRealizeValidateEvent
|
|
22
|
+
| AutoBeRealizeAuthorizationValidateEvent;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const AutoBeValidateEventMovie = (
|
|
26
|
+
props: IAutoBeValidateEventMovieProps,
|
|
27
|
+
) => {
|
|
28
|
+
const { event } = props;
|
|
29
|
+
const { title, description, isError, step, isSuccess } = getState(event);
|
|
30
|
+
|
|
31
|
+
const getCardVariant = () => {
|
|
32
|
+
if (isSuccess) return "success";
|
|
33
|
+
if (isError) return "error";
|
|
34
|
+
return "warning";
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const getIconType = () => {
|
|
38
|
+
if (isSuccess) return "success";
|
|
39
|
+
if (isError) return "error";
|
|
40
|
+
return "warning";
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<EventCard variant={getCardVariant()}>
|
|
45
|
+
<EventHeader
|
|
46
|
+
title={title}
|
|
47
|
+
timestamp={event.created_at}
|
|
48
|
+
iconType={getIconType()}
|
|
49
|
+
step={step}
|
|
50
|
+
/>
|
|
51
|
+
<EventContent>{description}</EventContent>
|
|
52
|
+
</EventCard>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default AutoBeValidateEventMovie;
|
|
57
|
+
|
|
58
|
+
interface IState {
|
|
59
|
+
title: string;
|
|
60
|
+
description: string | JSX.Element;
|
|
61
|
+
isError: boolean;
|
|
62
|
+
isSuccess: boolean;
|
|
63
|
+
step?: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getState(event: IAutoBeValidateEventMovieProps["event"]): IState {
|
|
67
|
+
switch (event.type) {
|
|
68
|
+
case "prismaValidate":
|
|
69
|
+
return {
|
|
70
|
+
title: "Prisma Validation Failed",
|
|
71
|
+
description: (
|
|
72
|
+
<>
|
|
73
|
+
Database schema validation encountered errors that require
|
|
74
|
+
correction.
|
|
75
|
+
<br />
|
|
76
|
+
<br />
|
|
77
|
+
<strong>Error Details:</strong>
|
|
78
|
+
<br />
|
|
79
|
+
{event.result.errors.length > 0 && (
|
|
80
|
+
<>
|
|
81
|
+
{event.result.errors
|
|
82
|
+
.slice(0, 3)
|
|
83
|
+
.map((error: any, idx: number) => (
|
|
84
|
+
<div key={idx} style={{ marginTop: "0.25rem" }}>
|
|
85
|
+
• {error.message}
|
|
86
|
+
</div>
|
|
87
|
+
))}
|
|
88
|
+
</>
|
|
89
|
+
)}
|
|
90
|
+
<br />
|
|
91
|
+
<strong>Failed Schemas:</strong> {Object.keys(event.schemas).length}{" "}
|
|
92
|
+
file(s)
|
|
93
|
+
</>
|
|
94
|
+
),
|
|
95
|
+
isError: true,
|
|
96
|
+
isSuccess: false,
|
|
97
|
+
step: event.step,
|
|
98
|
+
};
|
|
99
|
+
case "testValidate":
|
|
100
|
+
const isTestSuccess = event.result.type === "success";
|
|
101
|
+
return {
|
|
102
|
+
title: isTestSuccess
|
|
103
|
+
? "Test Validation Successful"
|
|
104
|
+
: "Test Validation Failed",
|
|
105
|
+
description: (
|
|
106
|
+
<>
|
|
107
|
+
Test file validation completed.
|
|
108
|
+
<br />
|
|
109
|
+
<br />
|
|
110
|
+
<strong>File:</strong> {event.file.location}
|
|
111
|
+
<br />
|
|
112
|
+
<strong>Status:</strong> {isTestSuccess ? "Success" : "Failed"}
|
|
113
|
+
{!isTestSuccess && (
|
|
114
|
+
<>
|
|
115
|
+
<br />
|
|
116
|
+
<br />
|
|
117
|
+
<strong>Compilation Issues:</strong>
|
|
118
|
+
<br />
|
|
119
|
+
{event.result.type === "failure" &&
|
|
120
|
+
event.result.diagnostics
|
|
121
|
+
.slice(0, 3)
|
|
122
|
+
.map((diagnostic: any, idx: number) => (
|
|
123
|
+
<div key={idx} style={{ marginTop: "0.25rem" }}>
|
|
124
|
+
• {diagnostic.messageText}
|
|
125
|
+
</div>
|
|
126
|
+
))}
|
|
127
|
+
{event.result.type === "exception" && (
|
|
128
|
+
<div style={{ marginTop: "0.25rem" }}>
|
|
129
|
+
• Exception occurred during compilation
|
|
130
|
+
</div>
|
|
131
|
+
)}
|
|
132
|
+
</>
|
|
133
|
+
)}
|
|
134
|
+
</>
|
|
135
|
+
),
|
|
136
|
+
isError: !isTestSuccess,
|
|
137
|
+
isSuccess: isTestSuccess,
|
|
138
|
+
step: event.step,
|
|
139
|
+
};
|
|
140
|
+
case "realizeValidate":
|
|
141
|
+
return {
|
|
142
|
+
title: "Implementation Validation Failed",
|
|
143
|
+
description: (
|
|
144
|
+
<>
|
|
145
|
+
Implementation code compilation encountered errors that need
|
|
146
|
+
correction.
|
|
147
|
+
<br />
|
|
148
|
+
<br />
|
|
149
|
+
<strong>Failed Files:</strong> {Object.keys(event.files).length}{" "}
|
|
150
|
+
file(s)
|
|
151
|
+
<br />
|
|
152
|
+
<br />
|
|
153
|
+
<strong>Error Type:</strong>{" "}
|
|
154
|
+
{event.result.type === "failure"
|
|
155
|
+
? "Compilation Error"
|
|
156
|
+
: "Runtime Exception"}
|
|
157
|
+
<br />
|
|
158
|
+
{event.result.type === "failure" &&
|
|
159
|
+
event.result.diagnostics.length > 0 && (
|
|
160
|
+
<>
|
|
161
|
+
<br />
|
|
162
|
+
<strong>First Error:</strong>
|
|
163
|
+
<br />
|
|
164
|
+
{event.result.diagnostics[0].messageText}
|
|
165
|
+
</>
|
|
166
|
+
)}
|
|
167
|
+
</>
|
|
168
|
+
),
|
|
169
|
+
isError: true,
|
|
170
|
+
isSuccess: false,
|
|
171
|
+
step: event.step,
|
|
172
|
+
};
|
|
173
|
+
case "realizeAuthorizationValidate":
|
|
174
|
+
const isAuthSuccess = event.result.type === "success";
|
|
175
|
+
return {
|
|
176
|
+
title: isAuthSuccess
|
|
177
|
+
? "Authorization Validation Successful"
|
|
178
|
+
: "Authorization Validation Failed",
|
|
179
|
+
description: (
|
|
180
|
+
<>
|
|
181
|
+
Authorization implementation validation completed.
|
|
182
|
+
<br />
|
|
183
|
+
<br />
|
|
184
|
+
<strong>Role:</strong> {event.authorization.role.name}(
|
|
185
|
+
{event.authorization.role.description})
|
|
186
|
+
<br />
|
|
187
|
+
<strong>Status:</strong> {isAuthSuccess ? "Success" : "Failed"}
|
|
188
|
+
{!isAuthSuccess && (
|
|
189
|
+
<>
|
|
190
|
+
<br />
|
|
191
|
+
<br />
|
|
192
|
+
<strong>Validation Issues:</strong>
|
|
193
|
+
<br />
|
|
194
|
+
{event.result.type === "failure" &&
|
|
195
|
+
event.result.diagnostics
|
|
196
|
+
.slice(0, 2)
|
|
197
|
+
.map((diagnostic: any, idx: number) => (
|
|
198
|
+
<div key={idx} style={{ marginTop: "0.25rem" }}>
|
|
199
|
+
• {diagnostic.messageText}
|
|
200
|
+
</div>
|
|
201
|
+
))}
|
|
202
|
+
{event.result.type === "exception" && (
|
|
203
|
+
<div style={{ marginTop: "0.25rem" }}>
|
|
204
|
+
• Exception occurred during validation
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
</>
|
|
208
|
+
)}
|
|
209
|
+
</>
|
|
210
|
+
),
|
|
211
|
+
isError: !isAuthSuccess,
|
|
212
|
+
isSuccess: isAuthSuccess,
|
|
213
|
+
step: event.step,
|
|
214
|
+
};
|
|
215
|
+
case "prismaInsufficient":
|
|
216
|
+
return {
|
|
217
|
+
title: "Prisma Model Generation Insufficient",
|
|
218
|
+
description: (
|
|
219
|
+
<>
|
|
220
|
+
Prisma model generation was incomplete for the assigned component.
|
|
221
|
+
<br />
|
|
222
|
+
<br />
|
|
223
|
+
<strong>Component:</strong> {event.component.namespace}
|
|
224
|
+
<br />
|
|
225
|
+
<strong>Generated Models:</strong> {event.actual.length}
|
|
226
|
+
<br />
|
|
227
|
+
<strong>Missing Models:</strong> {event.missed.length}
|
|
228
|
+
<br />
|
|
229
|
+
<br />
|
|
230
|
+
{event.missed.length > 0 && (
|
|
231
|
+
<>
|
|
232
|
+
<strong>Missed Tables:</strong>
|
|
233
|
+
<br />
|
|
234
|
+
{event.missed.slice(0, 5).map((table: string, idx: number) => (
|
|
235
|
+
<div key={idx} style={{ marginTop: "0.25rem" }}>
|
|
236
|
+
• {table}
|
|
237
|
+
</div>
|
|
238
|
+
))}
|
|
239
|
+
{event.missed.length > 5 && (
|
|
240
|
+
<div style={{ marginTop: "0.25rem" }}>
|
|
241
|
+
... and {event.missed.length - 5} more
|
|
242
|
+
</div>
|
|
243
|
+
)}
|
|
244
|
+
</>
|
|
245
|
+
)}
|
|
246
|
+
</>
|
|
247
|
+
),
|
|
248
|
+
isError: true,
|
|
249
|
+
isSuccess: false,
|
|
250
|
+
step: undefined, // prismaInsufficient doesn't have step
|
|
251
|
+
};
|
|
252
|
+
case "interfaceOperationsReview":
|
|
253
|
+
return {
|
|
254
|
+
title: "Interface Operations Review",
|
|
255
|
+
description: (
|
|
256
|
+
<>
|
|
257
|
+
API operations are being reviewed for quality and consistency.
|
|
258
|
+
<br />
|
|
259
|
+
<br />
|
|
260
|
+
<strong>Operations Count:</strong> {event.operations.length}
|
|
261
|
+
<br />
|
|
262
|
+
<strong>Status:</strong> Review in progress
|
|
263
|
+
<br />
|
|
264
|
+
<br />
|
|
265
|
+
<strong>Review Summary:</strong>
|
|
266
|
+
<br />
|
|
267
|
+
<div
|
|
268
|
+
style={{
|
|
269
|
+
maxHeight: "100px",
|
|
270
|
+
overflow: "hidden",
|
|
271
|
+
fontSize: "0.75rem",
|
|
272
|
+
color: "#64748b",
|
|
273
|
+
marginTop: "0.5rem",
|
|
274
|
+
}}
|
|
275
|
+
>
|
|
276
|
+
{event.review.length > 200
|
|
277
|
+
? `${event.review.substring(0, 200)}...`
|
|
278
|
+
: event.review}
|
|
279
|
+
</div>
|
|
280
|
+
</>
|
|
281
|
+
),
|
|
282
|
+
isError: false,
|
|
283
|
+
isSuccess: true,
|
|
284
|
+
step: event.step,
|
|
285
|
+
};
|
|
286
|
+
case "interfaceComplement":
|
|
287
|
+
return {
|
|
288
|
+
title: "Interface Schema Complement",
|
|
289
|
+
description: (
|
|
290
|
+
<>
|
|
291
|
+
Missing schema definitions are being added to complete the API
|
|
292
|
+
specification.
|
|
293
|
+
<br />
|
|
294
|
+
<br />
|
|
295
|
+
<strong>Missing Schemas:</strong> {event.missed.length}
|
|
296
|
+
<br />
|
|
297
|
+
<strong>Added Schemas:</strong> {Object.keys(event.schemas).length}
|
|
298
|
+
<br />
|
|
299
|
+
<br />
|
|
300
|
+
{event.missed.length > 0 && (
|
|
301
|
+
<>
|
|
302
|
+
<strong>Complemented Types:</strong>
|
|
303
|
+
<br />
|
|
304
|
+
{event.missed.slice(0, 5).map((schema: string, idx: number) => (
|
|
305
|
+
<div key={idx} style={{ marginTop: "0.25rem" }}>
|
|
306
|
+
• {schema}
|
|
307
|
+
</div>
|
|
308
|
+
))}
|
|
309
|
+
{event.missed.length > 5 && (
|
|
310
|
+
<div style={{ marginTop: "0.25rem" }}>
|
|
311
|
+
... and {event.missed.length - 5} more
|
|
312
|
+
</div>
|
|
313
|
+
)}
|
|
314
|
+
</>
|
|
315
|
+
)}
|
|
316
|
+
</>
|
|
317
|
+
),
|
|
318
|
+
isError: false,
|
|
319
|
+
isSuccess: true,
|
|
320
|
+
step: event.step,
|
|
321
|
+
};
|
|
322
|
+
default:
|
|
323
|
+
event satisfies never;
|
|
324
|
+
throw new Error("Unknown validation event type");
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# AutoBe Event Components
|
|
2
|
+
|
|
3
|
+
Event UI components for the AutoBe platform with grouping and collapsible functionality.
|
|
4
|
+
|
|
5
|
+
## Individual Event Components
|
|
6
|
+
|
|
7
|
+
### AutoBeProgressEventMovie
|
|
8
|
+
Shows progress tracking events with progress bars.
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { AutoBeProgressEventMovie } from "@autobe/ui/events";
|
|
12
|
+
|
|
13
|
+
<AutoBeProgressEventMovie event={progressEvent} />
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### AutoBeValidateEventMovie
|
|
17
|
+
Shows validation events with error/success states.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { AutoBeValidateEventMovie } from "@autobe/ui/events";
|
|
21
|
+
|
|
22
|
+
<AutoBeValidateEventMovie event={validateEvent} />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### AutoBeScenarioEventMovie
|
|
26
|
+
Shows scenario analysis and component generation events.
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { AutoBeScenarioEventMovie } from "@autobe/ui/events";
|
|
30
|
+
|
|
31
|
+
<AutoBeScenarioEventMovie event={scenarioEvent} />
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### AutoBeStartEventMovie
|
|
35
|
+
Shows pipeline start events with simple status display.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { AutoBeStartEventMovie } from "@autobe/ui/events";
|
|
39
|
+
|
|
40
|
+
<AutoBeStartEventMovie event={startEvent} />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Grouped Event Components
|
|
44
|
+
|
|
45
|
+
### ValidateEventGroup
|
|
46
|
+
Groups validation events with success rate statistics.
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { ValidateEventGroup } from "@autobe/ui/events";
|
|
50
|
+
|
|
51
|
+
<ValidateEventGroup
|
|
52
|
+
events={validateEvents}
|
|
53
|
+
defaultCollapsed={true}
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Automatic Event Grouping
|
|
58
|
+
|
|
59
|
+
Use the `groupEvents` utility to automatically group events by type:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { groupEvents } from "@autobe/ui/events/utils/eventGrouper";
|
|
63
|
+
|
|
64
|
+
const EventList = ({ events }: { events: AutoBeEvent[] }) => {
|
|
65
|
+
const groupedComponents = groupEvents(events, {
|
|
66
|
+
minGroupSize: 3, // Minimum events to form a group
|
|
67
|
+
defaultCollapsed: true, // Start collapsed
|
|
68
|
+
enableGrouping: true, // Enable grouping
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div>
|
|
73
|
+
{groupedComponents}
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Custom Collapsible Groups
|
|
80
|
+
|
|
81
|
+
Create custom event groups using the `CollapsibleEventGroup` component:
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { CollapsibleEventGroup } from "@autobe/ui/events";
|
|
85
|
+
|
|
86
|
+
<CollapsibleEventGroup
|
|
87
|
+
events={customEvents}
|
|
88
|
+
title="Custom Event Group"
|
|
89
|
+
iconType="info"
|
|
90
|
+
getTimestamp={(event) => event.created_at}
|
|
91
|
+
renderEvent={(event, index) => <CustomEventComponent event={event} />}
|
|
92
|
+
renderSummary={(events) => <CustomSummary events={events} />}
|
|
93
|
+
defaultCollapsed={true}
|
|
94
|
+
description="Custom grouped events"
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Common Components
|
|
99
|
+
|
|
100
|
+
### EventCard
|
|
101
|
+
Basic card container for consistent styling.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { EventCard } from "@autobe/ui/events";
|
|
105
|
+
|
|
106
|
+
<EventCard>
|
|
107
|
+
<div>Your content here</div>
|
|
108
|
+
</EventCard>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### EventHeader
|
|
112
|
+
Standardized header with icon, title, and timestamp.
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { EventHeader } from "@autobe/ui/events";
|
|
116
|
+
|
|
117
|
+
<EventHeader
|
|
118
|
+
title="Event Title"
|
|
119
|
+
timestamp="2024-01-01T12:00:00Z"
|
|
120
|
+
iconType="success"
|
|
121
|
+
step={1}
|
|
122
|
+
/>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### EventContent
|
|
126
|
+
Consistent content area styling.
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { EventContent } from "@autobe/ui/events";
|
|
130
|
+
|
|
131
|
+
<EventContent>
|
|
132
|
+
<div>Your content here</div>
|
|
133
|
+
</EventContent>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### EventIcon
|
|
137
|
+
Consistent icons for different event states.
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
import { EventIcon } from "@autobe/ui/events";
|
|
141
|
+
|
|
142
|
+
<EventIcon type="success" size={16} />
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Available icon types: `success`, `progress`, `warning`, `error`, `info`, `start`
|
|
146
|
+
|
|
147
|
+
### ProgressBar
|
|
148
|
+
Reusable progress bar component.
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { ProgressBar } from "@autobe/ui/events";
|
|
152
|
+
|
|
153
|
+
<ProgressBar
|
|
154
|
+
current={7}
|
|
155
|
+
total={10}
|
|
156
|
+
color="#4caf50"
|
|
157
|
+
showLabel={true}
|
|
158
|
+
/>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Features
|
|
162
|
+
|
|
163
|
+
- 🎯 **Consistent Design**: All components follow the same design system
|
|
164
|
+
- 📱 **Responsive**: Works on all screen sizes
|
|
165
|
+
- 🔄 **Collapsible**: Group events to reduce visual clutter
|
|
166
|
+
- 📊 **Statistics**: Automatic progress and error rate calculations
|
|
167
|
+
- 🎨 **Customizable**: Easy to extend and customize
|
|
168
|
+
- 💎 **TypeScript**: Full type safety and IntelliSense support
|
|
169
|
+
- ⚡ **Performance**: Efficient rendering with minimal re-renders
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { ReactNode, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { EventCard } from "./EventCard";
|
|
4
|
+
import { EventContent } from "./EventContent";
|
|
5
|
+
import { EventIcon, EventIconType } from "./EventIcon";
|
|
6
|
+
|
|
7
|
+
export interface ICollapsibleEventGroupProps<T = any> {
|
|
8
|
+
/** Array of events of the same type */
|
|
9
|
+
events: T[];
|
|
10
|
+
/** Title for the group */
|
|
11
|
+
title: string;
|
|
12
|
+
/** Icon type for the group header */
|
|
13
|
+
iconType: EventIconType;
|
|
14
|
+
/** Function to render individual events */
|
|
15
|
+
renderEvent: (event: T, index: number) => ReactNode;
|
|
16
|
+
/** Function to get timestamp from event (for header) */
|
|
17
|
+
getTimestamp: (event: T) => string;
|
|
18
|
+
/** Optional custom summary content */
|
|
19
|
+
renderSummary?: (events: T[]) => ReactNode;
|
|
20
|
+
/** Initial collapsed state */
|
|
21
|
+
defaultCollapsed?: boolean;
|
|
22
|
+
/** Custom group description */
|
|
23
|
+
description?: string;
|
|
24
|
+
/** Card variant based on event status */
|
|
25
|
+
variant?: "default" | "success" | "error" | "warning";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Collapsible event group component Groups multiple events of the same type
|
|
30
|
+
* with expand/collapse functionality
|
|
31
|
+
*/
|
|
32
|
+
export const CollapsibleEventGroup = <T,>(
|
|
33
|
+
props: ICollapsibleEventGroupProps<T>,
|
|
34
|
+
) => {
|
|
35
|
+
const {
|
|
36
|
+
events,
|
|
37
|
+
title,
|
|
38
|
+
iconType,
|
|
39
|
+
renderEvent,
|
|
40
|
+
getTimestamp,
|
|
41
|
+
renderSummary,
|
|
42
|
+
defaultCollapsed = true,
|
|
43
|
+
description,
|
|
44
|
+
variant = "default",
|
|
45
|
+
} = props;
|
|
46
|
+
|
|
47
|
+
const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
|
|
48
|
+
|
|
49
|
+
if (events.length === 0) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Use the latest event's timestamp for the group header
|
|
54
|
+
const latestTimestamp = getTimestamp(events[events.length - 1]);
|
|
55
|
+
|
|
56
|
+
const toggleCollapse = () => {
|
|
57
|
+
setIsCollapsed(!isCollapsed);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const defaultSummary = (
|
|
61
|
+
<>
|
|
62
|
+
{description && (
|
|
63
|
+
<>
|
|
64
|
+
{description}
|
|
65
|
+
<br />
|
|
66
|
+
<br />
|
|
67
|
+
</>
|
|
68
|
+
)}
|
|
69
|
+
<strong>Total Events:</strong> {events.length}
|
|
70
|
+
<br />
|
|
71
|
+
<strong>Status:</strong> {isCollapsed ? "Collapsed" : "Expanded"}
|
|
72
|
+
</>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<EventCard variant={variant}>
|
|
77
|
+
{/* Header */}
|
|
78
|
+
<div
|
|
79
|
+
style={{
|
|
80
|
+
display: "flex",
|
|
81
|
+
alignItems: "center",
|
|
82
|
+
justifyContent: "space-between",
|
|
83
|
+
marginBottom: "1rem",
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<div
|
|
87
|
+
style={{
|
|
88
|
+
display: "flex",
|
|
89
|
+
alignItems: "center",
|
|
90
|
+
gap: "0.75rem",
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<EventIcon type={iconType} />
|
|
94
|
+
|
|
95
|
+
<div>
|
|
96
|
+
<h3
|
|
97
|
+
style={{
|
|
98
|
+
fontSize: "1.125rem",
|
|
99
|
+
fontWeight: "600",
|
|
100
|
+
color: "#1e293b",
|
|
101
|
+
margin: 0,
|
|
102
|
+
marginBottom: "0.25rem",
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
{title}
|
|
106
|
+
</h3>
|
|
107
|
+
<div
|
|
108
|
+
style={{
|
|
109
|
+
fontSize: "0.75rem",
|
|
110
|
+
color: "#64748b",
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
{events.length} event{events.length !== 1 ? "s" : ""}
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div
|
|
119
|
+
style={{
|
|
120
|
+
fontSize: "0.75rem",
|
|
121
|
+
color: "#64748b",
|
|
122
|
+
textAlign: "right",
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
{latestTimestamp}
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
{/* Summary content when collapsed */}
|
|
130
|
+
{isCollapsed && (
|
|
131
|
+
<EventContent>
|
|
132
|
+
{renderSummary ? renderSummary(events) : defaultSummary}
|
|
133
|
+
</EventContent>
|
|
134
|
+
)}
|
|
135
|
+
|
|
136
|
+
{/* Individual events when expanded */}
|
|
137
|
+
{!isCollapsed && (
|
|
138
|
+
<div
|
|
139
|
+
style={{
|
|
140
|
+
display: "flex",
|
|
141
|
+
flexDirection: "column",
|
|
142
|
+
gap: "0.5rem",
|
|
143
|
+
}}
|
|
144
|
+
>
|
|
145
|
+
{events.map((event, index) => (
|
|
146
|
+
<div
|
|
147
|
+
key={index}
|
|
148
|
+
style={{
|
|
149
|
+
border: "1px solid #e2e8f0",
|
|
150
|
+
borderRadius: "0.5rem",
|
|
151
|
+
overflow: "hidden",
|
|
152
|
+
}}
|
|
153
|
+
>
|
|
154
|
+
{renderEvent(event, index)}
|
|
155
|
+
</div>
|
|
156
|
+
))}
|
|
157
|
+
</div>
|
|
158
|
+
)}
|
|
159
|
+
|
|
160
|
+
{/* Expand/Collapse Button */}
|
|
161
|
+
<div
|
|
162
|
+
style={{
|
|
163
|
+
display: "flex",
|
|
164
|
+
justifyContent: "center",
|
|
165
|
+
marginTop: "1rem",
|
|
166
|
+
}}
|
|
167
|
+
>
|
|
168
|
+
<button
|
|
169
|
+
onClick={toggleCollapse}
|
|
170
|
+
style={{
|
|
171
|
+
display: "flex",
|
|
172
|
+
alignItems: "center",
|
|
173
|
+
gap: "0.5rem",
|
|
174
|
+
padding: "0.5rem 1rem",
|
|
175
|
+
backgroundColor: "#f8fafc",
|
|
176
|
+
border: "1px solid #e2e8f0",
|
|
177
|
+
borderRadius: "0.5rem",
|
|
178
|
+
cursor: "pointer",
|
|
179
|
+
fontSize: "0.875rem",
|
|
180
|
+
color: "#64748b",
|
|
181
|
+
transition: "all 0.2s ease",
|
|
182
|
+
}}
|
|
183
|
+
onMouseEnter={(e) => {
|
|
184
|
+
e.currentTarget.style.backgroundColor = "#f1f5f9";
|
|
185
|
+
e.currentTarget.style.borderColor = "#cbd5e1";
|
|
186
|
+
}}
|
|
187
|
+
onMouseLeave={(e) => {
|
|
188
|
+
e.currentTarget.style.backgroundColor = "#f8fafc";
|
|
189
|
+
e.currentTarget.style.borderColor = "#e2e8f0";
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
192
|
+
<span>{isCollapsed ? "Expand" : "Collapse"}</span>
|
|
193
|
+
<div
|
|
194
|
+
style={{
|
|
195
|
+
width: "1rem",
|
|
196
|
+
height: "1rem",
|
|
197
|
+
display: "flex",
|
|
198
|
+
alignItems: "center",
|
|
199
|
+
justifyContent: "center",
|
|
200
|
+
transition: "transform 0.2s ease",
|
|
201
|
+
transform: isCollapsed ? "rotate(0deg)" : "rotate(180deg)",
|
|
202
|
+
}}
|
|
203
|
+
>
|
|
204
|
+
<svg
|
|
205
|
+
width="12"
|
|
206
|
+
height="12"
|
|
207
|
+
viewBox="0 0 24 24"
|
|
208
|
+
fill="currentColor"
|
|
209
|
+
style={{ color: "#64748b" }}
|
|
210
|
+
>
|
|
211
|
+
<path d="M7 10l5 5 5-5z" />
|
|
212
|
+
</svg>
|
|
213
|
+
</div>
|
|
214
|
+
</button>
|
|
215
|
+
</div>
|
|
216
|
+
</EventCard>
|
|
217
|
+
);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export default CollapsibleEventGroup;
|