@nextlyhq/plugin-form-builder 0.0.2-alpha.34 → 0.0.2-alpha.37

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 (38) hide show
  1. package/README.md +4 -5
  2. package/dist/admin/index.d.ts +122 -315
  3. package/dist/admin/index.js +3836 -3421
  4. package/dist/admin/index.js.map +1 -1
  5. package/dist/chunk-WVI7N3QO.js +559 -0
  6. package/dist/chunk-WVI7N3QO.js.map +1 -0
  7. package/dist/index.d.ts +8 -8
  8. package/dist/index.js +440 -611
  9. package/dist/index.js.map +1 -1
  10. package/dist/{index-BRumOEsE.d.ts → types-r8uBeB0u.d.ts} +97 -96
  11. package/package.json +23 -31
  12. package/dist/CheckboxFieldEditor-K4T6QX52.js +0 -121
  13. package/dist/CheckboxFieldEditor-K4T6QX52.js.map +0 -1
  14. package/dist/DateFieldEditor-JFJFAVOH.js +0 -187
  15. package/dist/DateFieldEditor-JFJFAVOH.js.map +0 -1
  16. package/dist/EmailFieldEditor-ZZ4VSKKB.js +0 -138
  17. package/dist/EmailFieldEditor-ZZ4VSKKB.js.map +0 -1
  18. package/dist/NumberFieldEditor-UISAYR6Z.js +0 -175
  19. package/dist/NumberFieldEditor-UISAYR6Z.js.map +0 -1
  20. package/dist/RadioFieldEditor-6UQFH4WT.js +0 -165
  21. package/dist/RadioFieldEditor-6UQFH4WT.js.map +0 -1
  22. package/dist/SelectFieldEditor-PSO4KHV4.js +0 -213
  23. package/dist/SelectFieldEditor-PSO4KHV4.js.map +0 -1
  24. package/dist/TextFieldEditor-5EU64EA5.js +0 -187
  25. package/dist/TextFieldEditor-5EU64EA5.js.map +0 -1
  26. package/dist/TextareaFieldEditor-773G3LNM.js +0 -190
  27. package/dist/TextareaFieldEditor-773G3LNM.js.map +0 -1
  28. package/dist/admin/index.css +0 -91
  29. package/dist/admin/index.css.map +0 -1
  30. package/dist/chunk-2LLUO3TV.js +0 -547
  31. package/dist/chunk-2LLUO3TV.js.map +0 -1
  32. package/dist/chunk-XTCEPJ7B.js +0 -256
  33. package/dist/chunk-XTCEPJ7B.js.map +0 -1
  34. package/dist/components/index.d.ts +0 -3
  35. package/dist/components/index.js +0 -9
  36. package/dist/components/index.js.map +0 -1
  37. package/dist/styles/form-builder.css +0 -128
  38. package/dist/styles/submissions-filter.css +0 -74
@@ -1,547 +0,0 @@
1
- // src/components/submissions/SubmissionList.tsx
2
- import { DataTableView } from "@nextlyhq/plugin-sdk/admin";
3
- import { useState, useCallback, useMemo } from "react";
4
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
- function formatValue(value) {
6
- if (value === null || value === void 0) return "-";
7
- if (Array.isArray(value)) return value.join(", ");
8
- if (typeof value === "boolean") return value ? "Yes" : "No";
9
- if (value instanceof Date) return value.toLocaleDateString();
10
- let str;
11
- if (typeof value === "object") {
12
- str = JSON.stringify(value);
13
- } else {
14
- str = String(value);
15
- }
16
- return str.length > 50 ? str.slice(0, 50) + "..." : str;
17
- }
18
- function formatDate(date) {
19
- const d = typeof date === "string" ? new Date(date) : date;
20
- return d.toLocaleString();
21
- }
22
- function SubmissionList({
23
- form,
24
- submissions,
25
- isLoading = false,
26
- onStatusChange,
27
- onDelete,
28
- onBulkDelete,
29
- onBulkStatusChange,
30
- onExport,
31
- onViewDetail
32
- }) {
33
- const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
34
- const [filter, setFilter] = useState("all");
35
- const [processingIds, setProcessingIds] = useState(/* @__PURE__ */ new Set());
36
- const displayFields = useMemo(() => {
37
- return form.fields.filter((f) => f.type !== "hidden").slice(0, 3);
38
- }, [form.fields]);
39
- const filteredSubmissions = useMemo(() => {
40
- if (filter === "all") return submissions;
41
- return submissions.filter((sub) => sub.status === filter);
42
- }, [submissions, filter]);
43
- const someSelected = selectedIds.size > 0;
44
- const toggleSelect = useCallback((id) => {
45
- setSelectedIds((prev) => {
46
- const next = new Set(prev);
47
- if (next.has(id)) {
48
- next.delete(id);
49
- } else {
50
- next.add(id);
51
- }
52
- return next;
53
- });
54
- }, []);
55
- const handleStatusChange = useCallback(
56
- async (id, status) => {
57
- if (!onStatusChange) return;
58
- setProcessingIds((prev) => new Set(prev).add(id));
59
- try {
60
- await onStatusChange(id, status);
61
- } finally {
62
- setProcessingIds((prev) => {
63
- const next = new Set(prev);
64
- next.delete(id);
65
- return next;
66
- });
67
- }
68
- },
69
- [onStatusChange]
70
- );
71
- const handleDelete = useCallback(
72
- async (id) => {
73
- if (!onDelete) return;
74
- if (!window.confirm("Are you sure you want to delete this submission?")) {
75
- return;
76
- }
77
- setProcessingIds((prev) => new Set(prev).add(id));
78
- try {
79
- await onDelete(id);
80
- setSelectedIds((prev) => {
81
- const next = new Set(prev);
82
- next.delete(id);
83
- return next;
84
- });
85
- } finally {
86
- setProcessingIds((prev) => {
87
- const next = new Set(prev);
88
- next.delete(id);
89
- return next;
90
- });
91
- }
92
- },
93
- [onDelete]
94
- );
95
- const handleBulkDelete = useCallback(async () => {
96
- if (!onBulkDelete || selectedIds.size === 0) return;
97
- if (!window.confirm(
98
- `Are you sure you want to delete ${selectedIds.size} submission(s)?`
99
- )) {
100
- return;
101
- }
102
- const ids = Array.from(selectedIds);
103
- ids.forEach((id) => setProcessingIds((prev) => new Set(prev).add(id)));
104
- try {
105
- await onBulkDelete(ids);
106
- setSelectedIds(/* @__PURE__ */ new Set());
107
- } finally {
108
- setProcessingIds(/* @__PURE__ */ new Set());
109
- }
110
- }, [onBulkDelete, selectedIds]);
111
- const handleBulkStatusChange = useCallback(
112
- async (status) => {
113
- if (!onBulkStatusChange || selectedIds.size === 0) return;
114
- const ids = Array.from(selectedIds);
115
- ids.forEach((id) => setProcessingIds((prev) => new Set(prev).add(id)));
116
- try {
117
- await onBulkStatusChange(ids, status);
118
- setSelectedIds(/* @__PURE__ */ new Set());
119
- } finally {
120
- setProcessingIds(/* @__PURE__ */ new Set());
121
- }
122
- },
123
- [onBulkStatusChange, selectedIds]
124
- );
125
- const columns = useMemo(() => {
126
- const base = [
127
- {
128
- name: "id",
129
- header: "ID",
130
- cell: ({ row }) => /* @__PURE__ */ jsxs("code", { className: "font-mono text-xs text-muted-foreground", children: [
131
- row.id.slice(0, 8),
132
- "..."
133
- ] })
134
- },
135
- {
136
- name: "status",
137
- header: "Status",
138
- cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-none border border-border bg-muted px-2 py-0.5 text-[11px] font-medium capitalize text-foreground", children: row.status })
139
- },
140
- {
141
- name: "submittedAt",
142
- header: "Submitted",
143
- hideOnMobile: true,
144
- cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap text-sm text-muted-foreground", children: formatDate(row.submittedAt) })
145
- }
146
- ];
147
- const fieldColumns = displayFields.map(
148
- (field) => ({
149
- name: `field:${field.name}`,
150
- header: field.label,
151
- hideOnMobile: true,
152
- cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-sm", children: formatValue(row.data[field.name]) })
153
- })
154
- );
155
- return [...base, ...fieldColumns];
156
- }, [displayFields]);
157
- const selection = useMemo(
158
- () => ({
159
- selectedIds: Array.from(selectedIds),
160
- onToggle: (submission) => toggleSelect(submission.id),
161
- onToggleAll: (rows, allChecked) => {
162
- const ids = rows.map((r) => r.id);
163
- setSelectedIds((prev) => {
164
- const next = new Set(prev);
165
- if (allChecked) ids.forEach((id) => next.delete(id));
166
- else ids.forEach((id) => next.add(id));
167
- return next;
168
- });
169
- }
170
- }),
171
- [selectedIds, toggleSelect]
172
- );
173
- const rowActions = useCallback(() => {
174
- const actions = [];
175
- if (onStatusChange) {
176
- actions.push(
177
- {
178
- id: "mark-read",
179
- label: "Mark Read",
180
- isVisible: (r) => r.status !== "read",
181
- isDisabled: (r) => processingIds.has(r.id),
182
- onSelect: (r) => void handleStatusChange(r.id, "read")
183
- },
184
- {
185
- id: "archive",
186
- label: "Archive",
187
- isVisible: (r) => r.status !== "archived",
188
- isDisabled: (r) => processingIds.has(r.id),
189
- onSelect: (r) => void handleStatusChange(r.id, "archived")
190
- }
191
- );
192
- }
193
- if (onDelete) {
194
- actions.push({
195
- id: "delete",
196
- label: "Delete",
197
- destructive: true,
198
- isDisabled: (r) => processingIds.has(r.id),
199
- onSelect: (r) => void handleDelete(r.id)
200
- });
201
- }
202
- return actions;
203
- }, [
204
- onStatusChange,
205
- onDelete,
206
- processingIds,
207
- handleStatusChange,
208
- handleDelete
209
- ]);
210
- return /* @__PURE__ */ jsxs("div", { className: "submission-list", children: [
211
- /* @__PURE__ */ jsxs("div", { className: "submission-list__toolbar", children: [
212
- /* @__PURE__ */ jsx("div", { className: "submission-list__filters", children: /* @__PURE__ */ jsxs(
213
- "select",
214
- {
215
- value: filter,
216
- onChange: (e) => setFilter(e.target.value),
217
- className: "submission-list__filter-select",
218
- "aria-label": "Filter by status",
219
- children: [
220
- /* @__PURE__ */ jsxs("option", { value: "all", children: [
221
- "All Submissions (",
222
- submissions.length,
223
- ")"
224
- ] }),
225
- /* @__PURE__ */ jsxs("option", { value: "new", children: [
226
- "New (",
227
- submissions.filter((s) => s.status === "new").length,
228
- ")"
229
- ] }),
230
- /* @__PURE__ */ jsxs("option", { value: "read", children: [
231
- "Read (",
232
- submissions.filter((s) => s.status === "read").length,
233
- ")"
234
- ] }),
235
- /* @__PURE__ */ jsxs("option", { value: "archived", children: [
236
- "Archived (",
237
- submissions.filter((s) => s.status === "archived").length,
238
- ")"
239
- ] })
240
- ]
241
- }
242
- ) }),
243
- /* @__PURE__ */ jsxs("div", { className: "submission-list__actions", children: [
244
- someSelected && /* @__PURE__ */ jsxs("div", { className: "submission-list__bulk-actions", children: [
245
- /* @__PURE__ */ jsxs("span", { className: "submission-list__selected-count", children: [
246
- selectedIds.size,
247
- " selected"
248
- ] }),
249
- onBulkStatusChange && /* @__PURE__ */ jsxs(Fragment, { children: [
250
- /* @__PURE__ */ jsx(
251
- "button",
252
- {
253
- type: "button",
254
- onClick: () => void handleBulkStatusChange("read"),
255
- className: "submission-list__action-btn",
256
- children: "Mark Read"
257
- }
258
- ),
259
- /* @__PURE__ */ jsx(
260
- "button",
261
- {
262
- type: "button",
263
- onClick: () => void handleBulkStatusChange("archived"),
264
- className: "submission-list__action-btn",
265
- children: "Archive"
266
- }
267
- )
268
- ] }),
269
- onBulkDelete && /* @__PURE__ */ jsx(
270
- "button",
271
- {
272
- type: "button",
273
- onClick: () => void handleBulkDelete(),
274
- className: "submission-list__action-btn submission-list__action-btn--danger",
275
- children: "Delete"
276
- }
277
- )
278
- ] }),
279
- onExport && /* @__PURE__ */ jsxs("div", { className: "submission-list__export-actions", children: [
280
- /* @__PURE__ */ jsx(
281
- "button",
282
- {
283
- type: "button",
284
- onClick: () => onExport("csv"),
285
- className: "submission-list__export-btn",
286
- children: "Export CSV"
287
- }
288
- ),
289
- /* @__PURE__ */ jsx(
290
- "button",
291
- {
292
- type: "button",
293
- onClick: () => onExport("json"),
294
- className: "submission-list__export-btn",
295
- children: "Export JSON"
296
- }
297
- )
298
- ] })
299
- ] })
300
- ] }),
301
- /* @__PURE__ */ jsx(
302
- DataTableView,
303
- {
304
- columns,
305
- rows: filteredSubmissions,
306
- loading: isLoading,
307
- selection,
308
- rowActions,
309
- onRowClick: onViewDetail ? (submission) => onViewDetail(submission) : void 0,
310
- registryKey: "form-submissions",
311
- ariaLabel: "Form submissions table",
312
- emptyMessage: "No submissions found"
313
- }
314
- ),
315
- /* @__PURE__ */ jsxs("div", { className: "submission-list__summary", children: [
316
- "Showing ",
317
- filteredSubmissions.length,
318
- " of ",
319
- submissions.length,
320
- " submissions"
321
- ] })
322
- ] });
323
- }
324
-
325
- // src/components/submissions/SubmissionDetail.tsx
326
- import { useState as useState2, useCallback as useCallback2 } from "react";
327
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
328
- function safeString(value) {
329
- if (typeof value === "object" && value !== null) {
330
- return JSON.stringify(value);
331
- }
332
- return String(value);
333
- }
334
- function formatFieldValue(value, field) {
335
- if (value === null || value === void 0) return "-";
336
- switch (field.type) {
337
- case "checkbox":
338
- return value ? "Yes" : "No";
339
- case "select":
340
- if (Array.isArray(value)) {
341
- return value.join(", ");
342
- }
343
- return safeString(value);
344
- case "file":
345
- if (Array.isArray(value)) {
346
- return value.map((v) => safeString(v)).join(", ");
347
- }
348
- return safeString(value);
349
- case "date":
350
- if (value instanceof Date) {
351
- return value.toLocaleDateString();
352
- }
353
- if (typeof value === "string") {
354
- return new Date(value).toLocaleDateString();
355
- }
356
- return safeString(value);
357
- case "time":
358
- return safeString(value);
359
- default:
360
- return safeString(value);
361
- }
362
- }
363
- function formatDate2(date) {
364
- const d = typeof date === "string" ? new Date(date) : date;
365
- return d.toLocaleString();
366
- }
367
- var STATUS_OPTIONS = [
368
- { value: "new", label: "New" },
369
- { value: "read", label: "Read" },
370
- { value: "archived", label: "Archived" }
371
- ];
372
- function SubmissionDetail({
373
- form,
374
- submission,
375
- onStatusChange,
376
- onNotesUpdate,
377
- onBack,
378
- onDelete
379
- }) {
380
- const [isProcessing, setIsProcessing] = useState2(false);
381
- const [notes, setNotes] = useState2(
382
- submission.notes || ""
383
- );
384
- const [notesModified, setNotesModified] = useState2(false);
385
- const handleStatusChange = useCallback2(
386
- async (event) => {
387
- if (!onStatusChange) return;
388
- const newStatus = event.target.value;
389
- setIsProcessing(true);
390
- try {
391
- await onStatusChange(newStatus);
392
- } finally {
393
- setIsProcessing(false);
394
- }
395
- },
396
- [onStatusChange]
397
- );
398
- const handleNotesChange = useCallback2(
399
- (event) => {
400
- setNotes(event.target.value);
401
- setNotesModified(true);
402
- },
403
- []
404
- );
405
- const handleNotesSave = useCallback2(async () => {
406
- if (!onNotesUpdate || !notesModified) return;
407
- setIsProcessing(true);
408
- try {
409
- await onNotesUpdate(notes);
410
- setNotesModified(false);
411
- } finally {
412
- setIsProcessing(false);
413
- }
414
- }, [onNotesUpdate, notes, notesModified]);
415
- const handleDelete = useCallback2(async () => {
416
- if (!onDelete) return;
417
- if (!window.confirm("Are you sure you want to delete this submission?")) {
418
- return;
419
- }
420
- setIsProcessing(true);
421
- try {
422
- await onDelete();
423
- } finally {
424
- setIsProcessing(false);
425
- }
426
- }, [onDelete]);
427
- const displayFields = form.fields;
428
- const data = submission.data;
429
- return /* @__PURE__ */ jsxs2("div", { className: "submission-detail", children: [
430
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__header", children: [
431
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__header-left", children: [
432
- onBack && /* @__PURE__ */ jsx2(
433
- "button",
434
- {
435
- type: "button",
436
- onClick: onBack,
437
- className: "submission-detail__back-btn",
438
- disabled: isProcessing,
439
- children: "\u2190 Back to List"
440
- }
441
- ),
442
- /* @__PURE__ */ jsx2("h2", { className: "submission-detail__title", children: "Submission Details" })
443
- ] }),
444
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__header-right", children: [
445
- onStatusChange && /* @__PURE__ */ jsxs2("div", { className: "submission-detail__status-control", children: [
446
- /* @__PURE__ */ jsx2("label", { htmlFor: "submission-status", children: "Status:" }),
447
- /* @__PURE__ */ jsx2(
448
- "select",
449
- {
450
- id: "submission-status",
451
- value: submission.status,
452
- onChange: (e) => void handleStatusChange(e),
453
- disabled: isProcessing,
454
- className: "submission-detail__status-select",
455
- children: STATUS_OPTIONS.map((option) => /* @__PURE__ */ jsx2("option", { value: option.value, children: option.label }, option.value))
456
- }
457
- )
458
- ] }),
459
- onDelete && /* @__PURE__ */ jsx2(
460
- "button",
461
- {
462
- type: "button",
463
- onClick: () => void handleDelete(),
464
- disabled: isProcessing,
465
- className: "submission-detail__delete-btn",
466
- children: "Delete"
467
- }
468
- )
469
- ] })
470
- ] }),
471
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__id", children: [
472
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__id-label", children: "Submission ID:" }),
473
- /* @__PURE__ */ jsx2("code", { className: "submission-detail__id-value", children: submission.id })
474
- ] }),
475
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__section", children: [
476
- /* @__PURE__ */ jsx2("h3", { className: "submission-detail__section-title", children: "Submitted Data" }),
477
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__fields", children: [
478
- displayFields.map((field) => /* @__PURE__ */ jsxs2("div", { className: "submission-detail__field", children: [
479
- /* @__PURE__ */ jsx2("div", { className: "submission-detail__field-label", children: field.label }),
480
- /* @__PURE__ */ jsx2("div", { className: "submission-detail__field-value", children: formatFieldValue(data[field.name], field) })
481
- ] }, field.name)),
482
- displayFields.length === 0 && /* @__PURE__ */ jsx2("div", { className: "submission-detail__empty", children: "No fields to display" })
483
- ] })
484
- ] }),
485
- onNotesUpdate && /* @__PURE__ */ jsxs2("div", { className: "submission-detail__section", children: [
486
- /* @__PURE__ */ jsx2("h3", { className: "submission-detail__section-title", children: "Internal Notes" }),
487
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__notes", children: [
488
- /* @__PURE__ */ jsx2(
489
- "textarea",
490
- {
491
- value: notes,
492
- onChange: handleNotesChange,
493
- placeholder: "Add internal notes about this submission...",
494
- disabled: isProcessing,
495
- className: "submission-detail__notes-textarea",
496
- rows: 4
497
- }
498
- ),
499
- notesModified && /* @__PURE__ */ jsx2(
500
- "button",
501
- {
502
- type: "button",
503
- onClick: () => void handleNotesSave(),
504
- disabled: isProcessing,
505
- className: "submission-detail__notes-save-btn",
506
- children: "Save Notes"
507
- }
508
- )
509
- ] })
510
- ] }),
511
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__section", children: [
512
- /* @__PURE__ */ jsx2("h3", { className: "submission-detail__section-title", children: "Metadata" }),
513
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__metadata", children: [
514
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__metadata-item", children: [
515
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__metadata-label", children: "Submitted At:" }),
516
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__metadata-value", children: formatDate2(submission.submittedAt) })
517
- ] }),
518
- submission.ipAddress && /* @__PURE__ */ jsxs2("div", { className: "submission-detail__metadata-item", children: [
519
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__metadata-label", children: "IP Address:" }),
520
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__metadata-value", children: /* @__PURE__ */ jsx2("code", { children: submission.ipAddress }) })
521
- ] }),
522
- submission.userAgent && /* @__PURE__ */ jsxs2("div", { className: "submission-detail__metadata-item", children: [
523
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__metadata-label", children: "User Agent:" }),
524
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__metadata-value submission-detail__metadata-value--truncate", children: submission.userAgent })
525
- ] }),
526
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__metadata-item", children: [
527
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__metadata-label", children: "Created At:" }),
528
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__metadata-value", children: formatDate2(submission.createdAt) })
529
- ] }),
530
- /* @__PURE__ */ jsxs2("div", { className: "submission-detail__metadata-item", children: [
531
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__metadata-label", children: "Updated At:" }),
532
- /* @__PURE__ */ jsx2("span", { className: "submission-detail__metadata-value", children: formatDate2(submission.updatedAt) })
533
- ] })
534
- ] })
535
- ] }),
536
- /* @__PURE__ */ jsxs2("details", { className: "submission-detail__raw-data", children: [
537
- /* @__PURE__ */ jsx2("summary", { className: "submission-detail__raw-data-summary", children: "View Raw JSON Data" }),
538
- /* @__PURE__ */ jsx2("pre", { className: "submission-detail__raw-data-content", children: JSON.stringify(submission.data, null, 2) })
539
- ] })
540
- ] });
541
- }
542
-
543
- export {
544
- SubmissionList,
545
- SubmissionDetail
546
- };
547
- //# sourceMappingURL=chunk-2LLUO3TV.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/submissions/SubmissionList.tsx","../src/components/submissions/SubmissionDetail.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Submission List Component\n *\n * Admin view for listing and managing form submissions.\n * Displays submissions in a filterable table with bulk actions.\n *\n * @module components/submissions/SubmissionList\n * @since 0.1.0\n */\n\n\"use client\";\n\nimport { DataTableView } from \"@nextlyhq/plugin-sdk/admin\";\nimport type {\n DataTableSelection,\n NextlyColumn,\n RowAction,\n} from \"@nextlyhq/plugin-sdk/admin\";\nimport type React from \"react\";\nimport { useState, useCallback, useMemo } from \"react\";\n\nimport type { SubmissionDocument, FormDocument, FormField } from \"../../types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Props for the SubmissionList component.\n */\nexport interface SubmissionListProps {\n /** Form document containing field definitions */\n form: FormDocument;\n\n /** Array of submission documents to display */\n submissions: SubmissionDocument[];\n\n /** Whether data is currently loading */\n isLoading?: boolean;\n\n /** Callback when submission status is changed */\n onStatusChange?: (\n id: string,\n status: \"new\" | \"read\" | \"archived\"\n ) => Promise<void>;\n\n /** Callback when submission is deleted */\n onDelete?: (id: string) => Promise<void>;\n\n /** Callback when bulk delete is triggered */\n onBulkDelete?: (ids: string[]) => Promise<void>;\n\n /** Callback when bulk status change is triggered */\n onBulkStatusChange?: (\n ids: string[],\n status: \"new\" | \"read\" | \"archived\"\n ) => Promise<void>;\n\n /** Callback when export is triggered */\n onExport?: (format: \"csv\" | \"json\") => void;\n\n /** Callback when a submission is clicked for detail view */\n onViewDetail?: (submission: SubmissionDocument) => void;\n}\n\n/**\n * Status filter options.\n */\ntype StatusFilter = \"all\" | \"new\" | \"read\" | \"archived\";\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Format a value for display in the table.\n */\nfunction formatValue(value: unknown): string {\n if (value === null || value === undefined) return \"-\";\n if (Array.isArray(value)) return value.join(\", \");\n if (typeof value === \"boolean\") return value ? \"Yes\" : \"No\";\n if (value instanceof Date) return value.toLocaleDateString();\n let str: string;\n if (typeof value === \"object\") {\n str = JSON.stringify(value);\n } else {\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- value narrowed to primitive above; rule doesn't follow control flow on unknown\n str = String(value);\n }\n return str.length > 50 ? str.slice(0, 50) + \"...\" : str;\n}\n\n/**\n * Format a date for display.\n */\nfunction formatDate(date: Date | string): string {\n const d = typeof date === \"string\" ? new Date(date) : date;\n return d.toLocaleString();\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Submission List Component\n *\n * Displays form submissions in a table with filtering, selection, and actions.\n *\n * @example\n * ```tsx\n * <SubmissionList\n * form={form}\n * submissions={submissions}\n * onStatusChange={handleStatusChange}\n * onDelete={handleDelete}\n * onExport={handleExport}\n * onViewDetail={handleViewDetail}\n * />\n * ```\n */\nexport function SubmissionList({\n form,\n submissions,\n isLoading = false,\n onStatusChange,\n onDelete,\n onBulkDelete,\n onBulkStatusChange,\n onExport,\n onViewDetail,\n}: SubmissionListProps): React.ReactElement {\n // -------------------------------------------------------------------------\n // State\n // -------------------------------------------------------------------------\n\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n const [filter, setFilter] = useState<StatusFilter>(\"all\");\n const [processingIds, setProcessingIds] = useState<Set<string>>(new Set());\n\n // -------------------------------------------------------------------------\n // Computed Values\n // -------------------------------------------------------------------------\n\n /** Get display columns from form fields (first 3 fields) */\n const displayFields = useMemo((): FormField[] => {\n return form.fields.filter(f => f.type !== \"hidden\").slice(0, 3);\n }, [form.fields]);\n\n /** Filter submissions by status */\n const filteredSubmissions = useMemo((): SubmissionDocument[] => {\n if (filter === \"all\") return submissions;\n return submissions.filter(sub => sub.status === filter);\n }, [submissions, filter]);\n\n /** Check if some submissions are selected */\n const someSelected = selectedIds.size > 0;\n\n // -------------------------------------------------------------------------\n // Handlers\n // -------------------------------------------------------------------------\n\n /** Toggle selection for a single submission */\n const toggleSelect = useCallback((id: string) => {\n setSelectedIds(prev => {\n const next = new Set(prev);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n });\n }, []);\n\n /** Handle status change for a single submission */\n const handleStatusChange = useCallback(\n async (id: string, status: \"new\" | \"read\" | \"archived\") => {\n if (!onStatusChange) return;\n\n setProcessingIds(prev => new Set(prev).add(id));\n try {\n await onStatusChange(id, status);\n } finally {\n setProcessingIds(prev => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }\n },\n [onStatusChange]\n );\n\n /** Handle delete for a single submission */\n const handleDelete = useCallback(\n async (id: string) => {\n if (!onDelete) return;\n if (!window.confirm(\"Are you sure you want to delete this submission?\")) {\n return;\n }\n\n setProcessingIds(prev => new Set(prev).add(id));\n try {\n await onDelete(id);\n setSelectedIds(prev => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n } finally {\n setProcessingIds(prev => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }\n },\n [onDelete]\n );\n\n /** Handle bulk delete */\n const handleBulkDelete = useCallback(async () => {\n if (!onBulkDelete || selectedIds.size === 0) return;\n if (\n !window.confirm(\n `Are you sure you want to delete ${selectedIds.size} submission(s)?`\n )\n ) {\n return;\n }\n\n const ids = Array.from(selectedIds);\n ids.forEach(id => setProcessingIds(prev => new Set(prev).add(id)));\n\n try {\n await onBulkDelete(ids);\n setSelectedIds(new Set());\n } finally {\n setProcessingIds(new Set());\n }\n }, [onBulkDelete, selectedIds]);\n\n /** Handle bulk status change */\n const handleBulkStatusChange = useCallback(\n async (status: \"new\" | \"read\" | \"archived\") => {\n if (!onBulkStatusChange || selectedIds.size === 0) return;\n\n const ids = Array.from(selectedIds);\n ids.forEach(id => setProcessingIds(prev => new Set(prev).add(id)));\n\n try {\n await onBulkStatusChange(ids, status);\n setSelectedIds(new Set());\n } finally {\n setProcessingIds(new Set());\n }\n },\n [onBulkStatusChange, selectedIds]\n );\n\n // -------------------------------------------------------------------------\n // DataTable wiring (columns, selection, row actions)\n // -------------------------------------------------------------------------\n\n const columns = useMemo<NextlyColumn<SubmissionDocument>[]>(() => {\n const base: NextlyColumn<SubmissionDocument>[] = [\n {\n name: \"id\",\n header: \"ID\",\n cell: ({ row }) => (\n <code className=\"font-mono text-xs text-muted-foreground\">\n {row.id.slice(0, 8)}...\n </code>\n ),\n },\n {\n name: \"status\",\n header: \"Status\",\n cell: ({ row }) => (\n <span className=\"inline-flex items-center rounded-none border border-border bg-muted px-2 py-0.5 text-[11px] font-medium capitalize text-foreground\">\n {row.status}\n </span>\n ),\n },\n {\n name: \"submittedAt\",\n header: \"Submitted\",\n hideOnMobile: true,\n cell: ({ row }) => (\n <span className=\"whitespace-nowrap text-sm text-muted-foreground\">\n {formatDate(row.submittedAt)}\n </span>\n ),\n },\n ];\n const fieldColumns: NextlyColumn<SubmissionDocument>[] = displayFields.map(\n field => ({\n name: `field:${field.name}`,\n header: field.label,\n hideOnMobile: true,\n cell: ({ row }) => (\n <span className=\"text-sm\">{formatValue(row.data[field.name])}</span>\n ),\n })\n );\n return [...base, ...fieldColumns];\n }, [displayFields]);\n\n const selection = useMemo<DataTableSelection<SubmissionDocument>>(\n () => ({\n selectedIds: Array.from(selectedIds),\n onToggle: submission => toggleSelect(submission.id),\n onToggleAll: (rows, allChecked) => {\n const ids = rows.map(r => r.id);\n setSelectedIds(prev => {\n const next = new Set(prev);\n if (allChecked) ids.forEach(id => next.delete(id));\n else ids.forEach(id => next.add(id));\n return next;\n });\n },\n }),\n [selectedIds, toggleSelect]\n );\n\n const rowActions = useCallback((): RowAction<SubmissionDocument>[] => {\n const actions: RowAction<SubmissionDocument>[] = [];\n if (onStatusChange) {\n actions.push(\n {\n id: \"mark-read\",\n label: \"Mark Read\",\n isVisible: r => r.status !== \"read\",\n isDisabled: r => processingIds.has(r.id),\n onSelect: r => void handleStatusChange(r.id, \"read\"),\n },\n {\n id: \"archive\",\n label: \"Archive\",\n isVisible: r => r.status !== \"archived\",\n isDisabled: r => processingIds.has(r.id),\n onSelect: r => void handleStatusChange(r.id, \"archived\"),\n }\n );\n }\n if (onDelete) {\n actions.push({\n id: \"delete\",\n label: \"Delete\",\n destructive: true,\n isDisabled: r => processingIds.has(r.id),\n onSelect: r => void handleDelete(r.id),\n });\n }\n return actions;\n }, [\n onStatusChange,\n onDelete,\n processingIds,\n handleStatusChange,\n handleDelete,\n ]);\n\n // -------------------------------------------------------------------------\n // Render\n // -------------------------------------------------------------------------\n\n return (\n <div className=\"submission-list\">\n {/* Toolbar */}\n <div className=\"submission-list__toolbar\">\n <div className=\"submission-list__filters\">\n <select\n value={filter}\n onChange={e => setFilter(e.target.value as StatusFilter)}\n className=\"submission-list__filter-select\"\n aria-label=\"Filter by status\"\n >\n <option value=\"all\">All Submissions ({submissions.length})</option>\n <option value=\"new\">\n New ({submissions.filter(s => s.status === \"new\").length})\n </option>\n <option value=\"read\">\n Read ({submissions.filter(s => s.status === \"read\").length})\n </option>\n <option value=\"archived\">\n Archived (\n {submissions.filter(s => s.status === \"archived\").length})\n </option>\n </select>\n </div>\n\n <div className=\"submission-list__actions\">\n {/* Bulk Actions */}\n {someSelected && (\n <div className=\"submission-list__bulk-actions\">\n <span className=\"submission-list__selected-count\">\n {selectedIds.size} selected\n </span>\n {onBulkStatusChange && (\n <>\n <button\n type=\"button\"\n onClick={() => void handleBulkStatusChange(\"read\")}\n className=\"submission-list__action-btn\"\n >\n Mark Read\n </button>\n <button\n type=\"button\"\n onClick={() => void handleBulkStatusChange(\"archived\")}\n className=\"submission-list__action-btn\"\n >\n Archive\n </button>\n </>\n )}\n {onBulkDelete && (\n <button\n type=\"button\"\n onClick={() => void handleBulkDelete()}\n className=\"submission-list__action-btn submission-list__action-btn--danger\"\n >\n Delete\n </button>\n )}\n </div>\n )}\n\n {/* Export Actions */}\n {onExport && (\n <div className=\"submission-list__export-actions\">\n <button\n type=\"button\"\n onClick={() => onExport(\"csv\")}\n className=\"submission-list__export-btn\"\n >\n Export CSV\n </button>\n <button\n type=\"button\"\n onClick={() => onExport(\"json\")}\n className=\"submission-list__export-btn\"\n >\n Export JSON\n </button>\n </div>\n )}\n </div>\n </div>\n\n {/* Table (unified admin DataTable via the plugin SDK) */}\n <DataTableView<SubmissionDocument>\n columns={columns}\n rows={filteredSubmissions}\n loading={isLoading}\n selection={selection}\n rowActions={rowActions}\n onRowClick={\n onViewDetail ? submission => onViewDetail(submission) : undefined\n }\n registryKey=\"form-submissions\"\n ariaLabel=\"Form submissions table\"\n emptyMessage=\"No submissions found\"\n />\n\n {/* Summary */}\n <div className=\"submission-list__summary\">\n Showing {filteredSubmissions.length} of {submissions.length} submissions\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * Submission Detail Component\n *\n * Admin view for viewing and managing a single form submission.\n * Displays all submission data, metadata, and provides status management.\n *\n * @module components/submissions/SubmissionDetail\n * @since 0.1.0\n */\n\n\"use client\";\n\nimport type React from \"react\";\nimport { useState, useCallback } from \"react\";\n\nimport type { SubmissionDocument, FormDocument, FormField } from \"../../types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Props for the SubmissionDetail component.\n */\nexport interface SubmissionDetailProps {\n /** Form document containing field definitions */\n form: FormDocument;\n\n /** Submission document to display */\n submission: SubmissionDocument;\n\n /** Callback when status is changed */\n onStatusChange?: (status: \"new\" | \"read\" | \"archived\") => Promise<void>;\n\n /** Callback when notes are updated */\n onNotesUpdate?: (notes: string) => Promise<void>;\n\n /** Callback when back button is clicked */\n onBack?: () => void;\n\n /** Callback when delete is triggered */\n onDelete?: () => Promise<void>;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Stringify a value safely, falling back to JSON for plain objects so we\n * never render the default `[object Object]` representation.\n */\nfunction safeString(value: unknown): string {\n if (typeof value === \"object\" && value !== null) {\n return JSON.stringify(value);\n }\n return String(value);\n}\n\n/**\n * Format a value for display based on field type.\n */\nfunction formatFieldValue(value: unknown, field: FormField): string {\n if (value === null || value === undefined) return \"-\";\n\n switch (field.type) {\n case \"checkbox\":\n return value ? \"Yes\" : \"No\";\n\n case \"select\":\n if (Array.isArray(value)) {\n return value.join(\", \");\n }\n return safeString(value);\n\n case \"file\":\n if (Array.isArray(value)) {\n return value.map(v => safeString(v)).join(\", \");\n }\n return safeString(value);\n\n case \"date\":\n if (value instanceof Date) {\n return value.toLocaleDateString();\n }\n if (typeof value === \"string\") {\n return new Date(value).toLocaleDateString();\n }\n return safeString(value);\n\n case \"time\":\n return safeString(value);\n\n default:\n return safeString(value);\n }\n}\n\n/**\n * Format a date for display.\n */\nfunction formatDate(date: Date | string): string {\n const d = typeof date === \"string\" ? new Date(date) : date;\n return d.toLocaleString();\n}\n\n/**\n * Get status options.\n */\nconst STATUS_OPTIONS: Array<{\n value: \"new\" | \"read\" | \"archived\";\n label: string;\n}> = [\n { value: \"new\", label: \"New\" },\n { value: \"read\", label: \"Read\" },\n { value: \"archived\", label: \"Archived\" },\n];\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Submission Detail Component\n *\n * Displays full submission data with status management and notes.\n *\n * @example\n * ```tsx\n * <SubmissionDetail\n * form={form}\n * submission={submission}\n * onStatusChange={handleStatusChange}\n * onNotesUpdate={handleNotesUpdate}\n * onBack={handleBack}\n * />\n * ```\n */\nexport function SubmissionDetail({\n form,\n submission,\n onStatusChange,\n onNotesUpdate,\n onBack,\n onDelete,\n}: SubmissionDetailProps): React.ReactElement {\n // -------------------------------------------------------------------------\n // State\n // -------------------------------------------------------------------------\n\n const [isProcessing, setIsProcessing] = useState(false);\n const [notes, setNotes] = useState(\n (submission as unknown as { notes?: string }).notes || \"\"\n );\n const [notesModified, setNotesModified] = useState(false);\n\n // -------------------------------------------------------------------------\n // Handlers\n // -------------------------------------------------------------------------\n\n /** Handle status change */\n const handleStatusChange = useCallback(\n async (event: React.ChangeEvent<HTMLSelectElement>) => {\n if (!onStatusChange) return;\n\n const newStatus = event.target.value as \"new\" | \"read\" | \"archived\";\n setIsProcessing(true);\n\n try {\n await onStatusChange(newStatus);\n } finally {\n setIsProcessing(false);\n }\n },\n [onStatusChange]\n );\n\n /** Handle notes change */\n const handleNotesChange = useCallback(\n (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n setNotes(event.target.value);\n setNotesModified(true);\n },\n []\n );\n\n /** Handle notes save */\n const handleNotesSave = useCallback(async () => {\n if (!onNotesUpdate || !notesModified) return;\n\n setIsProcessing(true);\n try {\n await onNotesUpdate(notes);\n setNotesModified(false);\n } finally {\n setIsProcessing(false);\n }\n }, [onNotesUpdate, notes, notesModified]);\n\n /** Handle delete */\n const handleDelete = useCallback(async () => {\n if (!onDelete) return;\n if (!window.confirm(\"Are you sure you want to delete this submission?\")) {\n return;\n }\n\n setIsProcessing(true);\n try {\n await onDelete();\n } finally {\n setIsProcessing(false);\n }\n }, [onDelete]);\n\n // -------------------------------------------------------------------------\n // Computed Values\n // -------------------------------------------------------------------------\n\n /** Get displayable fields */\n const displayFields = form.fields;\n\n /** Get submission data */\n const data = submission.data;\n\n // -------------------------------------------------------------------------\n // Render\n // -------------------------------------------------------------------------\n\n return (\n <div className=\"submission-detail\">\n {/* Header */}\n <div className=\"submission-detail__header\">\n <div className=\"submission-detail__header-left\">\n {onBack && (\n <button\n type=\"button\"\n onClick={onBack}\n className=\"submission-detail__back-btn\"\n disabled={isProcessing}\n >\n &larr; Back to List\n </button>\n )}\n <h2 className=\"submission-detail__title\">Submission Details</h2>\n </div>\n\n <div className=\"submission-detail__header-right\">\n {onStatusChange && (\n <div className=\"submission-detail__status-control\">\n <label htmlFor=\"submission-status\">Status:</label>\n <select\n id=\"submission-status\"\n value={submission.status}\n onChange={e => void handleStatusChange(e)}\n disabled={isProcessing}\n className=\"submission-detail__status-select\"\n >\n {STATUS_OPTIONS.map(option => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </div>\n )}\n\n {onDelete && (\n <button\n type=\"button\"\n onClick={() => void handleDelete()}\n disabled={isProcessing}\n className=\"submission-detail__delete-btn\"\n >\n Delete\n </button>\n )}\n </div>\n </div>\n\n {/* Submission ID */}\n <div className=\"submission-detail__id\">\n <span className=\"submission-detail__id-label\">Submission ID:</span>\n <code className=\"submission-detail__id-value\">{submission.id}</code>\n </div>\n\n {/* Form Fields Data */}\n <div className=\"submission-detail__section\">\n <h3 className=\"submission-detail__section-title\">Submitted Data</h3>\n <div className=\"submission-detail__fields\">\n {displayFields.map(field => (\n <div key={field.name} className=\"submission-detail__field\">\n <div className=\"submission-detail__field-label\">\n {field.label}\n </div>\n <div className=\"submission-detail__field-value\">\n {formatFieldValue(data[field.name], field)}\n </div>\n </div>\n ))}\n\n {displayFields.length === 0 && (\n <div className=\"submission-detail__empty\">No fields to display</div>\n )}\n </div>\n </div>\n\n {/* Notes Section */}\n {onNotesUpdate && (\n <div className=\"submission-detail__section\">\n <h3 className=\"submission-detail__section-title\">Internal Notes</h3>\n <div className=\"submission-detail__notes\">\n <textarea\n value={notes}\n onChange={handleNotesChange}\n placeholder=\"Add internal notes about this submission...\"\n disabled={isProcessing}\n className=\"submission-detail__notes-textarea\"\n rows={4}\n />\n {notesModified && (\n <button\n type=\"button\"\n onClick={() => void handleNotesSave()}\n disabled={isProcessing}\n className=\"submission-detail__notes-save-btn\"\n >\n Save Notes\n </button>\n )}\n </div>\n </div>\n )}\n\n {/* Metadata Section */}\n <div className=\"submission-detail__section\">\n <h3 className=\"submission-detail__section-title\">Metadata</h3>\n <div className=\"submission-detail__metadata\">\n <div className=\"submission-detail__metadata-item\">\n <span className=\"submission-detail__metadata-label\">\n Submitted At:\n </span>\n <span className=\"submission-detail__metadata-value\">\n {formatDate(submission.submittedAt)}\n </span>\n </div>\n\n {submission.ipAddress && (\n <div className=\"submission-detail__metadata-item\">\n <span className=\"submission-detail__metadata-label\">\n IP Address:\n </span>\n <span className=\"submission-detail__metadata-value\">\n <code>{submission.ipAddress}</code>\n </span>\n </div>\n )}\n\n {submission.userAgent && (\n <div className=\"submission-detail__metadata-item\">\n <span className=\"submission-detail__metadata-label\">\n User Agent:\n </span>\n <span className=\"submission-detail__metadata-value submission-detail__metadata-value--truncate\">\n {submission.userAgent}\n </span>\n </div>\n )}\n\n <div className=\"submission-detail__metadata-item\">\n <span className=\"submission-detail__metadata-label\">\n Created At:\n </span>\n <span className=\"submission-detail__metadata-value\">\n {formatDate(submission.createdAt)}\n </span>\n </div>\n\n <div className=\"submission-detail__metadata-item\">\n <span className=\"submission-detail__metadata-label\">\n Updated At:\n </span>\n <span className=\"submission-detail__metadata-value\">\n {formatDate(submission.updatedAt)}\n </span>\n </div>\n </div>\n </div>\n\n {/* Raw Data Section (Expandable) */}\n <details className=\"submission-detail__raw-data\">\n <summary className=\"submission-detail__raw-data-summary\">\n View Raw JSON Data\n </summary>\n <pre className=\"submission-detail__raw-data-content\">\n {JSON.stringify(submission.data, null, 2)}\n </pre>\n </details>\n </div>\n );\n}\n"],"mappings":";AAcA,SAAS,qBAAqB;AAO9B,SAAS,UAAU,aAAa,eAAe;AA4PrC,SAkIM,UAzHN,KATA;AAlMV,SAAS,YAAY,OAAwB;AAC3C,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,KAAK,IAAI;AAChD,MAAI,OAAO,UAAU,UAAW,QAAO,QAAQ,QAAQ;AACvD,MAAI,iBAAiB,KAAM,QAAO,MAAM,mBAAmB;AAC3D,MAAI;AACJ,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,KAAK,UAAU,KAAK;AAAA,EAC5B,OAAO;AAEL,UAAM,OAAO,KAAK;AAAA,EACpB;AACA,SAAO,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,QAAQ;AACtD;AAKA,SAAS,WAAW,MAA6B;AAC/C,QAAM,IAAI,OAAO,SAAS,WAAW,IAAI,KAAK,IAAI,IAAI;AACtD,SAAO,EAAE,eAAe;AAC1B;AAuBO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA4C;AAK1C,QAAM,CAAC,aAAa,cAAc,IAAI,SAAsB,oBAAI,IAAI,CAAC;AACrE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAuB,KAAK;AACxD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAsB,oBAAI,IAAI,CAAC;AAOzE,QAAM,gBAAgB,QAAQ,MAAmB;AAC/C,WAAO,KAAK,OAAO,OAAO,OAAK,EAAE,SAAS,QAAQ,EAAE,MAAM,GAAG,CAAC;AAAA,EAChE,GAAG,CAAC,KAAK,MAAM,CAAC;AAGhB,QAAM,sBAAsB,QAAQ,MAA4B;AAC9D,QAAI,WAAW,MAAO,QAAO;AAC7B,WAAO,YAAY,OAAO,SAAO,IAAI,WAAW,MAAM;AAAA,EACxD,GAAG,CAAC,aAAa,MAAM,CAAC;AAGxB,QAAM,eAAe,YAAY,OAAO;AAOxC,QAAM,eAAe,YAAY,CAAC,OAAe;AAC/C,mBAAe,UAAQ;AACrB,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,UAAI,KAAK,IAAI,EAAE,GAAG;AAChB,aAAK,OAAO,EAAE;AAAA,MAChB,OAAO;AACL,aAAK,IAAI,EAAE;AAAA,MACb;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAGL,QAAM,qBAAqB;AAAA,IACzB,OAAO,IAAY,WAAwC;AACzD,UAAI,CAAC,eAAgB;AAErB,uBAAiB,UAAQ,IAAI,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC;AAC9C,UAAI;AACF,cAAM,eAAe,IAAI,MAAM;AAAA,MACjC,UAAE;AACA,yBAAiB,UAAQ;AACvB,gBAAM,OAAO,IAAI,IAAI,IAAI;AACzB,eAAK,OAAO,EAAE;AACd,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,CAAC,cAAc;AAAA,EACjB;AAGA,QAAM,eAAe;AAAA,IACnB,OAAO,OAAe;AACpB,UAAI,CAAC,SAAU;AACf,UAAI,CAAC,OAAO,QAAQ,kDAAkD,GAAG;AACvE;AAAA,MACF;AAEA,uBAAiB,UAAQ,IAAI,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC;AAC9C,UAAI;AACF,cAAM,SAAS,EAAE;AACjB,uBAAe,UAAQ;AACrB,gBAAM,OAAO,IAAI,IAAI,IAAI;AACzB,eAAK,OAAO,EAAE;AACd,iBAAO;AAAA,QACT,CAAC;AAAA,MACH,UAAE;AACA,yBAAiB,UAAQ;AACvB,gBAAM,OAAO,IAAI,IAAI,IAAI;AACzB,eAAK,OAAO,EAAE;AACd,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAGA,QAAM,mBAAmB,YAAY,YAAY;AAC/C,QAAI,CAAC,gBAAgB,YAAY,SAAS,EAAG;AAC7C,QACE,CAAC,OAAO;AAAA,MACN,mCAAmC,YAAY,IAAI;AAAA,IACrD,GACA;AACA;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,KAAK,WAAW;AAClC,QAAI,QAAQ,QAAM,iBAAiB,UAAQ,IAAI,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAEjE,QAAI;AACF,YAAM,aAAa,GAAG;AACtB,qBAAe,oBAAI,IAAI,CAAC;AAAA,IAC1B,UAAE;AACA,uBAAiB,oBAAI,IAAI,CAAC;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,cAAc,WAAW,CAAC;AAG9B,QAAM,yBAAyB;AAAA,IAC7B,OAAO,WAAwC;AAC7C,UAAI,CAAC,sBAAsB,YAAY,SAAS,EAAG;AAEnD,YAAM,MAAM,MAAM,KAAK,WAAW;AAClC,UAAI,QAAQ,QAAM,iBAAiB,UAAQ,IAAI,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAEjE,UAAI;AACF,cAAM,mBAAmB,KAAK,MAAM;AACpC,uBAAe,oBAAI,IAAI,CAAC;AAAA,MAC1B,UAAE;AACA,yBAAiB,oBAAI,IAAI,CAAC;AAAA,MAC5B;AAAA,IACF;AAAA,IACA,CAAC,oBAAoB,WAAW;AAAA,EAClC;AAMA,QAAM,UAAU,QAA4C,MAAM;AAChE,UAAM,OAA2C;AAAA,MAC/C;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,MAAM,CAAC,EAAE,IAAI,MACX,qBAAC,UAAK,WAAU,2CACb;AAAA,cAAI,GAAG,MAAM,GAAG,CAAC;AAAA,UAAE;AAAA,WACtB;AAAA,MAEJ;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,UAAK,WAAU,sIACb,cAAI,QACP;AAAA,MAEJ;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,cAAc;AAAA,QACd,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,UAAK,WAAU,mDACb,qBAAW,IAAI,WAAW,GAC7B;AAAA,MAEJ;AAAA,IACF;AACA,UAAM,eAAmD,cAAc;AAAA,MACrE,YAAU;AAAA,QACR,MAAM,SAAS,MAAM,IAAI;AAAA,QACzB,QAAQ,MAAM;AAAA,QACd,cAAc;AAAA,QACd,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,UAAK,WAAU,WAAW,sBAAY,IAAI,KAAK,MAAM,IAAI,CAAC,GAAE;AAAA,MAEjE;AAAA,IACF;AACA,WAAO,CAAC,GAAG,MAAM,GAAG,YAAY;AAAA,EAClC,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,YAAY;AAAA,IAChB,OAAO;AAAA,MACL,aAAa,MAAM,KAAK,WAAW;AAAA,MACnC,UAAU,gBAAc,aAAa,WAAW,EAAE;AAAA,MAClD,aAAa,CAAC,MAAM,eAAe;AACjC,cAAM,MAAM,KAAK,IAAI,OAAK,EAAE,EAAE;AAC9B,uBAAe,UAAQ;AACrB,gBAAM,OAAO,IAAI,IAAI,IAAI;AACzB,cAAI,WAAY,KAAI,QAAQ,QAAM,KAAK,OAAO,EAAE,CAAC;AAAA,cAC5C,KAAI,QAAQ,QAAM,KAAK,IAAI,EAAE,CAAC;AACnC,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,CAAC,aAAa,YAAY;AAAA,EAC5B;AAEA,QAAM,aAAa,YAAY,MAAuC;AACpE,UAAM,UAA2C,CAAC;AAClD,QAAI,gBAAgB;AAClB,cAAQ;AAAA,QACN;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,WAAW,OAAK,EAAE,WAAW;AAAA,UAC7B,YAAY,OAAK,cAAc,IAAI,EAAE,EAAE;AAAA,UACvC,UAAU,OAAK,KAAK,mBAAmB,EAAE,IAAI,MAAM;AAAA,QACrD;AAAA,QACA;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,WAAW,OAAK,EAAE,WAAW;AAAA,UAC7B,YAAY,OAAK,cAAc,IAAI,EAAE,EAAE;AAAA,UACvC,UAAU,OAAK,KAAK,mBAAmB,EAAE,IAAI,UAAU;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AACA,QAAI,UAAU;AACZ,cAAQ,KAAK;AAAA,QACX,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,aAAa;AAAA,QACb,YAAY,OAAK,cAAc,IAAI,EAAE,EAAE;AAAA,QACvC,UAAU,OAAK,KAAK,aAAa,EAAE,EAAE;AAAA,MACvC,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAMD,SACE,qBAAC,SAAI,WAAU,mBAEb;AAAA,yBAAC,SAAI,WAAU,4BACb;AAAA,0BAAC,SAAI,WAAU,4BACb;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,UAAU,OAAK,UAAU,EAAE,OAAO,KAAqB;AAAA,UACvD,WAAU;AAAA,UACV,cAAW;AAAA,UAEX;AAAA,iCAAC,YAAO,OAAM,OAAM;AAAA;AAAA,cAAkB,YAAY;AAAA,cAAO;AAAA,eAAC;AAAA,YAC1D,qBAAC,YAAO,OAAM,OAAM;AAAA;AAAA,cACZ,YAAY,OAAO,OAAK,EAAE,WAAW,KAAK,EAAE;AAAA,cAAO;AAAA,eAC3D;AAAA,YACA,qBAAC,YAAO,OAAM,QAAO;AAAA;AAAA,cACZ,YAAY,OAAO,OAAK,EAAE,WAAW,MAAM,EAAE;AAAA,cAAO;AAAA,eAC7D;AAAA,YACA,qBAAC,YAAO,OAAM,YAAW;AAAA;AAAA,cAEtB,YAAY,OAAO,OAAK,EAAE,WAAW,UAAU,EAAE;AAAA,cAAO;AAAA,eAC3D;AAAA;AAAA;AAAA,MACF,GACF;AAAA,MAEA,qBAAC,SAAI,WAAU,4BAEZ;AAAA,wBACC,qBAAC,SAAI,WAAU,iCACb;AAAA,+BAAC,UAAK,WAAU,mCACb;AAAA,wBAAY;AAAA,YAAK;AAAA,aACpB;AAAA,UACC,sBACC,iCACE;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,KAAK,uBAAuB,MAAM;AAAA,gBACjD,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,KAAK,uBAAuB,UAAU;AAAA,gBACrD,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,aACF;AAAA,UAED,gBACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,KAAK,iBAAiB;AAAA,cACrC,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,WAEJ;AAAA,QAID,YACC,qBAAC,SAAI,WAAU,mCACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,SAAS,KAAK;AAAA,cAC7B,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,SAAS,MAAM;AAAA,cAC9B,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,WACF;AAAA,SAEJ;AAAA,OACF;AAAA,IAGA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAM;AAAA,QACN,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QACA,YACE,eAAe,gBAAc,aAAa,UAAU,IAAI;AAAA,QAE1D,aAAY;AAAA,QACZ,WAAU;AAAA,QACV,cAAa;AAAA;AAAA,IACf;AAAA,IAGA,qBAAC,SAAI,WAAU,4BAA2B;AAAA;AAAA,MAC/B,oBAAoB;AAAA,MAAO;AAAA,MAAK,YAAY;AAAA,MAAO;AAAA,OAC9D;AAAA,KACF;AAEJ;;;AC5cA,SAAS,YAAAA,WAAU,eAAAC,oBAAmB;AA2N9B,SAEI,OAAAC,MAFJ,QAAAC,aAAA;AApLR,SAAS,WAAW,OAAwB;AAC1C,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B;AACA,SAAO,OAAO,KAAK;AACrB;AAKA,SAAS,iBAAiB,OAAgB,OAA0B;AAClE,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAElD,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aAAO,QAAQ,QAAQ;AAAA,IAEzB,KAAK;AACH,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,eAAO,MAAM,KAAK,IAAI;AAAA,MACxB;AACA,aAAO,WAAW,KAAK;AAAA,IAEzB,KAAK;AACH,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,eAAO,MAAM,IAAI,OAAK,WAAW,CAAC,CAAC,EAAE,KAAK,IAAI;AAAA,MAChD;AACA,aAAO,WAAW,KAAK;AAAA,IAEzB,KAAK;AACH,UAAI,iBAAiB,MAAM;AACzB,eAAO,MAAM,mBAAmB;AAAA,MAClC;AACA,UAAI,OAAO,UAAU,UAAU;AAC7B,eAAO,IAAI,KAAK,KAAK,EAAE,mBAAmB;AAAA,MAC5C;AACA,aAAO,WAAW,KAAK;AAAA,IAEzB,KAAK;AACH,aAAO,WAAW,KAAK;AAAA,IAEzB;AACE,aAAO,WAAW,KAAK;AAAA,EAC3B;AACF;AAKA,SAASC,YAAW,MAA6B;AAC/C,QAAM,IAAI,OAAO,SAAS,WAAW,IAAI,KAAK,IAAI,IAAI;AACtD,SAAO,EAAE,eAAe;AAC1B;AAKA,IAAM,iBAGD;AAAA,EACH,EAAE,OAAO,OAAO,OAAO,MAAM;AAAA,EAC7B,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,EAC/B,EAAE,OAAO,YAAY,OAAO,WAAW;AACzC;AAsBO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8C;AAK5C,QAAM,CAAC,cAAc,eAAe,IAAIJ,UAAS,KAAK;AACtD,QAAM,CAAC,OAAO,QAAQ,IAAIA;AAAA,IACvB,WAA6C,SAAS;AAAA,EACzD;AACA,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,KAAK;AAOxD,QAAM,qBAAqBC;AAAA,IACzB,OAAO,UAAgD;AACrD,UAAI,CAAC,eAAgB;AAErB,YAAM,YAAY,MAAM,OAAO;AAC/B,sBAAgB,IAAI;AAEpB,UAAI;AACF,cAAM,eAAe,SAAS;AAAA,MAChC,UAAE;AACA,wBAAgB,KAAK;AAAA,MACvB;AAAA,IACF;AAAA,IACA,CAAC,cAAc;AAAA,EACjB;AAGA,QAAM,oBAAoBA;AAAA,IACxB,CAAC,UAAkD;AACjD,eAAS,MAAM,OAAO,KAAK;AAC3B,uBAAiB,IAAI;AAAA,IACvB;AAAA,IACA,CAAC;AAAA,EACH;AAGA,QAAM,kBAAkBA,aAAY,YAAY;AAC9C,QAAI,CAAC,iBAAiB,CAAC,cAAe;AAEtC,oBAAgB,IAAI;AACpB,QAAI;AACF,YAAM,cAAc,KAAK;AACzB,uBAAiB,KAAK;AAAA,IACxB,UAAE;AACA,sBAAgB,KAAK;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,eAAe,OAAO,aAAa,CAAC;AAGxC,QAAM,eAAeA,aAAY,YAAY;AAC3C,QAAI,CAAC,SAAU;AACf,QAAI,CAAC,OAAO,QAAQ,kDAAkD,GAAG;AACvE;AAAA,IACF;AAEA,oBAAgB,IAAI;AACpB,QAAI;AACF,YAAM,SAAS;AAAA,IACjB,UAAE;AACA,sBAAgB,KAAK;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAOb,QAAM,gBAAgB,KAAK;AAG3B,QAAM,OAAO,WAAW;AAMxB,SACE,gBAAAE,MAAC,SAAI,WAAU,qBAEb;AAAA,oBAAAA,MAAC,SAAI,WAAU,6BACb;AAAA,sBAAAA,MAAC,SAAI,WAAU,kCACZ;AAAA,kBACC,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS;AAAA,YACT,WAAU;AAAA,YACV,UAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,QAEF,gBAAAA,KAAC,QAAG,WAAU,4BAA2B,gCAAkB;AAAA,SAC7D;AAAA,MAEA,gBAAAC,MAAC,SAAI,WAAU,mCACZ;AAAA,0BACC,gBAAAA,MAAC,SAAI,WAAU,qCACb;AAAA,0BAAAD,KAAC,WAAM,SAAQ,qBAAoB,qBAAO;AAAA,UAC1C,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,OAAO,WAAW;AAAA,cAClB,UAAU,OAAK,KAAK,mBAAmB,CAAC;AAAA,cACxC,UAAU;AAAA,cACV,WAAU;AAAA,cAET,yBAAe,IAAI,YAClB,gBAAAA,KAAC,YAA0B,OAAO,OAAO,OACtC,iBAAO,SADG,OAAO,KAEpB,CACD;AAAA;AAAA,UACH;AAAA,WACF;AAAA,QAGD,YACC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,KAAK,aAAa;AAAA,YACjC,UAAU;AAAA,YACV,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SAEJ;AAAA,OACF;AAAA,IAGA,gBAAAC,MAAC,SAAI,WAAU,yBACb;AAAA,sBAAAD,KAAC,UAAK,WAAU,+BAA8B,4BAAc;AAAA,MAC5D,gBAAAA,KAAC,UAAK,WAAU,+BAA+B,qBAAW,IAAG;AAAA,OAC/D;AAAA,IAGA,gBAAAC,MAAC,SAAI,WAAU,8BACb;AAAA,sBAAAD,KAAC,QAAG,WAAU,oCAAmC,4BAAc;AAAA,MAC/D,gBAAAC,MAAC,SAAI,WAAU,6BACZ;AAAA,sBAAc,IAAI,WACjB,gBAAAA,MAAC,SAAqB,WAAU,4BAC9B;AAAA,0BAAAD,KAAC,SAAI,WAAU,kCACZ,gBAAM,OACT;AAAA,UACA,gBAAAA,KAAC,SAAI,WAAU,kCACZ,2BAAiB,KAAK,MAAM,IAAI,GAAG,KAAK,GAC3C;AAAA,aANQ,MAAM,IAOhB,CACD;AAAA,QAEA,cAAc,WAAW,KACxB,gBAAAA,KAAC,SAAI,WAAU,4BAA2B,kCAAoB;AAAA,SAElE;AAAA,OACF;AAAA,IAGC,iBACC,gBAAAC,MAAC,SAAI,WAAU,8BACb;AAAA,sBAAAD,KAAC,QAAG,WAAU,oCAAmC,4BAAc;AAAA,MAC/D,gBAAAC,MAAC,SAAI,WAAU,4BACb;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU;AAAA,YACV,aAAY;AAAA,YACZ,UAAU;AAAA,YACV,WAAU;AAAA,YACV,MAAM;AAAA;AAAA,QACR;AAAA,QACC,iBACC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,KAAK,gBAAgB;AAAA,YACpC,UAAU;AAAA,YACV,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SAEJ;AAAA,OACF;AAAA,IAIF,gBAAAC,MAAC,SAAI,WAAU,8BACb;AAAA,sBAAAD,KAAC,QAAG,WAAU,oCAAmC,sBAAQ;AAAA,MACzD,gBAAAC,MAAC,SAAI,WAAU,+BACb;AAAA,wBAAAA,MAAC,SAAI,WAAU,oCACb;AAAA,0BAAAD,KAAC,UAAK,WAAU,qCAAoC,2BAEpD;AAAA,UACA,gBAAAA,KAAC,UAAK,WAAU,qCACb,UAAAE,YAAW,WAAW,WAAW,GACpC;AAAA,WACF;AAAA,QAEC,WAAW,aACV,gBAAAD,MAAC,SAAI,WAAU,oCACb;AAAA,0BAAAD,KAAC,UAAK,WAAU,qCAAoC,yBAEpD;AAAA,UACA,gBAAAA,KAAC,UAAK,WAAU,qCACd,0BAAAA,KAAC,UAAM,qBAAW,WAAU,GAC9B;AAAA,WACF;AAAA,QAGD,WAAW,aACV,gBAAAC,MAAC,SAAI,WAAU,oCACb;AAAA,0BAAAD,KAAC,UAAK,WAAU,qCAAoC,yBAEpD;AAAA,UACA,gBAAAA,KAAC,UAAK,WAAU,iFACb,qBAAW,WACd;AAAA,WACF;AAAA,QAGF,gBAAAC,MAAC,SAAI,WAAU,oCACb;AAAA,0BAAAD,KAAC,UAAK,WAAU,qCAAoC,yBAEpD;AAAA,UACA,gBAAAA,KAAC,UAAK,WAAU,qCACb,UAAAE,YAAW,WAAW,SAAS,GAClC;AAAA,WACF;AAAA,QAEA,gBAAAD,MAAC,SAAI,WAAU,oCACb;AAAA,0BAAAD,KAAC,UAAK,WAAU,qCAAoC,yBAEpD;AAAA,UACA,gBAAAA,KAAC,UAAK,WAAU,qCACb,UAAAE,YAAW,WAAW,SAAS,GAClC;AAAA,WACF;AAAA,SACF;AAAA,OACF;AAAA,IAGA,gBAAAD,MAAC,aAAQ,WAAU,+BACjB;AAAA,sBAAAD,KAAC,aAAQ,WAAU,uCAAsC,gCAEzD;AAAA,MACA,gBAAAA,KAAC,SAAI,WAAU,uCACZ,eAAK,UAAU,WAAW,MAAM,MAAM,CAAC,GAC1C;AAAA,OACF;AAAA,KACF;AAEJ;","names":["useState","useCallback","jsx","jsxs","formatDate"]}