@terryavg/neptune-ai-chatbot 1.0.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/README +16 -0
- package/dist/chart-component-RCLR3IKU.mjs +887 -0
- package/dist/chat-input-L5LIF4NP.mjs +452 -0
- package/dist/chat-message-FTACCRMO.mjs +1904 -0
- package/dist/chunk-5VL3YPMQ.mjs +406 -0
- package/dist/chunk-C2VGAYER.mjs +295 -0
- package/dist/chunk-FWCSY2DS.mjs +37 -0
- package/dist/index.css +2805 -0
- package/dist/index.d.mts +88 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +6787 -0
- package/dist/index.mjs +2869 -0
- package/dist/styles.css +1 -0
- package/package.json +57 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2869 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chatClient,
|
|
3
|
+
configureChatClient
|
|
4
|
+
} from "./chunk-5VL3YPMQ.mjs";
|
|
5
|
+
import {
|
|
6
|
+
ToolExecutionIndicator,
|
|
7
|
+
ToolExecutionWidget
|
|
8
|
+
} from "./chunk-C2VGAYER.mjs";
|
|
9
|
+
import {
|
|
10
|
+
__spreadProps,
|
|
11
|
+
__spreadValues
|
|
12
|
+
} from "./chunk-FWCSY2DS.mjs";
|
|
13
|
+
|
|
14
|
+
// app/components/chat.tsx
|
|
15
|
+
import {
|
|
16
|
+
useState as useState4,
|
|
17
|
+
useEffect as useEffect4,
|
|
18
|
+
useRef as useRef2,
|
|
19
|
+
useLayoutEffect,
|
|
20
|
+
useCallback as useCallback2
|
|
21
|
+
} from "react";
|
|
22
|
+
import {
|
|
23
|
+
Trash2,
|
|
24
|
+
Edit,
|
|
25
|
+
Moon,
|
|
26
|
+
Sun,
|
|
27
|
+
MessageCirclePlus,
|
|
28
|
+
PanelRightClose,
|
|
29
|
+
PanelRightOpen,
|
|
30
|
+
MoreHorizontal
|
|
31
|
+
} from "lucide-react";
|
|
32
|
+
|
|
33
|
+
// app/components/chat-content.tsx
|
|
34
|
+
import {
|
|
35
|
+
useRef,
|
|
36
|
+
useEffect as useEffect2,
|
|
37
|
+
useState as useState2,
|
|
38
|
+
useMemo as useMemo2,
|
|
39
|
+
useCallback,
|
|
40
|
+
lazy,
|
|
41
|
+
Suspense
|
|
42
|
+
} from "react";
|
|
43
|
+
import { X as X2, ArrowDownCircle } from "lucide-react";
|
|
44
|
+
|
|
45
|
+
// app/components/analytics-panel.tsx
|
|
46
|
+
import { useState, useMemo, useEffect } from "react";
|
|
47
|
+
import {
|
|
48
|
+
ChevronDown,
|
|
49
|
+
ChevronRight,
|
|
50
|
+
Filter,
|
|
51
|
+
Download,
|
|
52
|
+
RotateCcw,
|
|
53
|
+
ArrowUp,
|
|
54
|
+
ArrowDown,
|
|
55
|
+
X,
|
|
56
|
+
ArrowUpDown,
|
|
57
|
+
Folder,
|
|
58
|
+
FolderOpen,
|
|
59
|
+
FileText,
|
|
60
|
+
BarChart3,
|
|
61
|
+
Eye,
|
|
62
|
+
EyeOff
|
|
63
|
+
} from "lucide-react";
|
|
64
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
65
|
+
function AnalyticsPanel({
|
|
66
|
+
name,
|
|
67
|
+
csvData,
|
|
68
|
+
theme
|
|
69
|
+
}) {
|
|
70
|
+
const { headers, data } = useMemo(() => {
|
|
71
|
+
var _a;
|
|
72
|
+
const lines = csvData.trim().split("\n");
|
|
73
|
+
const headers2 = ((_a = lines[0]) == null ? void 0 : _a.split(";").map((h) => h.trim())) || [];
|
|
74
|
+
const rows = lines.slice(1).map((line) => {
|
|
75
|
+
const values = line.split(";").map((v) => v.trim());
|
|
76
|
+
const row = {};
|
|
77
|
+
headers2.forEach((header, index) => {
|
|
78
|
+
row[header] = values[index] || "";
|
|
79
|
+
});
|
|
80
|
+
return row;
|
|
81
|
+
});
|
|
82
|
+
return { headers: headers2, data: rows };
|
|
83
|
+
}, [csvData]);
|
|
84
|
+
const [pivotConfig, setPivotConfig] = useState({
|
|
85
|
+
rows: [],
|
|
86
|
+
columns: [],
|
|
87
|
+
values: [],
|
|
88
|
+
aggregation: "count"
|
|
89
|
+
});
|
|
90
|
+
const [filters, setFilters] = useState({});
|
|
91
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
92
|
+
const [activeFilterDropdown, setActiveFilterDropdown] = useState(null);
|
|
93
|
+
const [sortConfig, setSortConfig] = useState(null);
|
|
94
|
+
const [expandedGroups, setExpandedGroups] = useState(
|
|
95
|
+
/* @__PURE__ */ new Set()
|
|
96
|
+
);
|
|
97
|
+
const [showGroupedView, setShowGroupedView] = useState(false);
|
|
98
|
+
const [setTreeNodes] = useState([]);
|
|
99
|
+
const [animatingNodes, setAnimatingNodes] = useState(
|
|
100
|
+
/* @__PURE__ */ new Set()
|
|
101
|
+
);
|
|
102
|
+
const isNumericValue = (value) => {
|
|
103
|
+
return typeof value === "number" || !isNaN(parseFloat(value)) && isFinite(value);
|
|
104
|
+
};
|
|
105
|
+
const formatNumericValue = (value) => {
|
|
106
|
+
if (typeof value === "number") {
|
|
107
|
+
return value.toLocaleString();
|
|
108
|
+
}
|
|
109
|
+
const parsed = parseFloat(value);
|
|
110
|
+
return !isNaN(parsed) ? parsed.toLocaleString() : value;
|
|
111
|
+
};
|
|
112
|
+
const shouldFormatAsNumber = (value, header) => {
|
|
113
|
+
const isValueColumn = pivotConfig.values.includes(header);
|
|
114
|
+
const isCountColumn = header === "_count";
|
|
115
|
+
return (isValueColumn || isCountColumn) && isNumericValue(value);
|
|
116
|
+
};
|
|
117
|
+
const getCellAlignment = (value, header, isFirstColumn) => {
|
|
118
|
+
if (isFirstColumn) return "text-left";
|
|
119
|
+
const isValueColumn = pivotConfig.values.includes(header);
|
|
120
|
+
const isCountColumn = header === "_count";
|
|
121
|
+
if (isValueColumn || isCountColumn) {
|
|
122
|
+
return "text-right";
|
|
123
|
+
}
|
|
124
|
+
return "text-left";
|
|
125
|
+
};
|
|
126
|
+
const getTreeIcon = (node, hasChildren) => {
|
|
127
|
+
if (!hasChildren) {
|
|
128
|
+
return /* @__PURE__ */ jsx(
|
|
129
|
+
FileText,
|
|
130
|
+
{
|
|
131
|
+
size: 14,
|
|
132
|
+
className: "text-blue-500 dark:text-blue-400"
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (node.isExpanded) {
|
|
137
|
+
return /* @__PURE__ */ jsx(
|
|
138
|
+
FolderOpen,
|
|
139
|
+
{
|
|
140
|
+
size: 14,
|
|
141
|
+
className: "text-emerald-600 dark:text-emerald-400"
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
} else {
|
|
145
|
+
return /* @__PURE__ */ jsx(
|
|
146
|
+
Folder,
|
|
147
|
+
{
|
|
148
|
+
size: 14,
|
|
149
|
+
className: "text-emerald-600 dark:text-emerald-400"
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
const getExpandIcon = (isExpanded, hasChildren) => {
|
|
155
|
+
if (!hasChildren) return null;
|
|
156
|
+
return isExpanded ? /* @__PURE__ */ jsx(
|
|
157
|
+
ChevronDown,
|
|
158
|
+
{
|
|
159
|
+
size: 14,
|
|
160
|
+
className: "text-gray-600 dark:text-gray-400 transition-transform duration-200"
|
|
161
|
+
}
|
|
162
|
+
) : /* @__PURE__ */ jsx(
|
|
163
|
+
ChevronRight,
|
|
164
|
+
{
|
|
165
|
+
size: 14,
|
|
166
|
+
className: "text-gray-600 dark:text-gray-400 transition-transform duration-200"
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
const getLevelColor = (level) => {
|
|
171
|
+
const colors = [
|
|
172
|
+
"border-l-emerald-500 bg-emerald-50 dark:bg-emerald-900/10",
|
|
173
|
+
// Level 0
|
|
174
|
+
"border-l-blue-500 bg-blue-50 dark:bg-blue-900/10",
|
|
175
|
+
// Level 1
|
|
176
|
+
"border-l-purple-500 bg-purple-50 dark:bg-purple-900/10",
|
|
177
|
+
// Level 2
|
|
178
|
+
"border-l-orange-500 bg-orange-50 dark:bg-orange-900/10",
|
|
179
|
+
// Level 3
|
|
180
|
+
"border-l-pink-500 bg-pink-50 dark:bg-pink-900/10"
|
|
181
|
+
// Level 4+
|
|
182
|
+
];
|
|
183
|
+
return colors[Math.min(level, colors.length - 1)];
|
|
184
|
+
};
|
|
185
|
+
const generateTreeId = (path, level) => {
|
|
186
|
+
return `tree-${level}-${path.join("-")}`;
|
|
187
|
+
};
|
|
188
|
+
const calculateAggregations = (rows, valueColumns, aggregation) => {
|
|
189
|
+
const result = {};
|
|
190
|
+
valueColumns.forEach((col) => {
|
|
191
|
+
const numericValues = rows.map((row) => {
|
|
192
|
+
const cleaned = String(row[col] || "").replace(
|
|
193
|
+
/[,\s]/g,
|
|
194
|
+
""
|
|
195
|
+
);
|
|
196
|
+
const parsed = parseFloat(cleaned);
|
|
197
|
+
return !isNaN(parsed) ? parsed : 0;
|
|
198
|
+
}).filter((val) => !isNaN(val));
|
|
199
|
+
switch (aggregation) {
|
|
200
|
+
case "sum":
|
|
201
|
+
result[col] = numericValues.reduce(
|
|
202
|
+
(sum, val) => sum + val,
|
|
203
|
+
0
|
|
204
|
+
);
|
|
205
|
+
break;
|
|
206
|
+
case "avg":
|
|
207
|
+
result[col] = numericValues.length > 0 ? numericValues.reduce((sum, val) => sum + val, 0) / numericValues.length : 0;
|
|
208
|
+
break;
|
|
209
|
+
case "min":
|
|
210
|
+
result[col] = numericValues.length > 0 ? Math.min(...numericValues) : 0;
|
|
211
|
+
break;
|
|
212
|
+
case "max":
|
|
213
|
+
result[col] = numericValues.length > 0 ? Math.max(...numericValues) : 0;
|
|
214
|
+
break;
|
|
215
|
+
default:
|
|
216
|
+
result[col] = rows.length;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
return result;
|
|
220
|
+
};
|
|
221
|
+
const buildTreeStructure = (rows, groupColumns, currentPath = [], level = 0) => {
|
|
222
|
+
if (groupColumns.length === 0) {
|
|
223
|
+
return [
|
|
224
|
+
{
|
|
225
|
+
id: generateTreeId([...currentPath, "aggregated"], level),
|
|
226
|
+
level,
|
|
227
|
+
label: `${rows.length} records`,
|
|
228
|
+
value: rows.length,
|
|
229
|
+
count: rows.length,
|
|
230
|
+
aggregatedData: calculateAggregations(
|
|
231
|
+
rows,
|
|
232
|
+
pivotConfig.values,
|
|
233
|
+
pivotConfig.aggregation
|
|
234
|
+
),
|
|
235
|
+
children: [],
|
|
236
|
+
isExpanded: false,
|
|
237
|
+
originalRows: rows,
|
|
238
|
+
_isTreeNode: true
|
|
239
|
+
}
|
|
240
|
+
];
|
|
241
|
+
}
|
|
242
|
+
const [currentColumn, ...remainingColumns] = groupColumns;
|
|
243
|
+
const grouped = rows.reduce((acc, row) => {
|
|
244
|
+
const value = row[currentColumn] || "Unknown";
|
|
245
|
+
if (!acc[value]) acc[value] = [];
|
|
246
|
+
acc[value].push(row);
|
|
247
|
+
return acc;
|
|
248
|
+
}, {});
|
|
249
|
+
let treeNodes = Object.entries(grouped).map(([value, groupRows]) => {
|
|
250
|
+
const typedGroupRows = groupRows;
|
|
251
|
+
const nodePath = [...currentPath, value];
|
|
252
|
+
const nodeId = generateTreeId(nodePath, level);
|
|
253
|
+
return {
|
|
254
|
+
id: nodeId,
|
|
255
|
+
level,
|
|
256
|
+
label: value,
|
|
257
|
+
value,
|
|
258
|
+
count: typedGroupRows.length,
|
|
259
|
+
aggregatedData: calculateAggregations(
|
|
260
|
+
typedGroupRows,
|
|
261
|
+
pivotConfig.values,
|
|
262
|
+
pivotConfig.aggregation
|
|
263
|
+
),
|
|
264
|
+
children: buildTreeStructure(
|
|
265
|
+
typedGroupRows,
|
|
266
|
+
remainingColumns,
|
|
267
|
+
nodePath,
|
|
268
|
+
level + 1
|
|
269
|
+
),
|
|
270
|
+
isExpanded: expandedGroups.has(nodeId),
|
|
271
|
+
originalRows: typedGroupRows,
|
|
272
|
+
_isTreeNode: true
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
if (sortConfig) {
|
|
276
|
+
treeNodes = treeNodes.sort((a, b) => {
|
|
277
|
+
let valueA;
|
|
278
|
+
let valueB;
|
|
279
|
+
if (sortConfig.key === currentColumn) {
|
|
280
|
+
valueA = a.label;
|
|
281
|
+
valueB = b.label;
|
|
282
|
+
} else if (sortConfig.key === "_count") {
|
|
283
|
+
valueA = a.count;
|
|
284
|
+
valueB = b.count;
|
|
285
|
+
} else if (pivotConfig.values.includes(sortConfig.key)) {
|
|
286
|
+
valueA = a.aggregatedData[sortConfig.key] || 0;
|
|
287
|
+
valueB = b.aggregatedData[sortConfig.key] || 0;
|
|
288
|
+
} else {
|
|
289
|
+
valueA = a.label;
|
|
290
|
+
valueB = b.label;
|
|
291
|
+
}
|
|
292
|
+
return compareValues(valueA, valueB, sortConfig.direction);
|
|
293
|
+
});
|
|
294
|
+
} else {
|
|
295
|
+
treeNodes = treeNodes.sort(
|
|
296
|
+
(a, b) => a.label.localeCompare(b.label)
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
return treeNodes;
|
|
300
|
+
};
|
|
301
|
+
const compareValues = (valueA, valueB, direction) => {
|
|
302
|
+
const numA = parseFloat(String(valueA).replace(/[,\s]/g, ""));
|
|
303
|
+
const numB = parseFloat(String(valueB).replace(/[,\s]/g, ""));
|
|
304
|
+
let comparison = 0;
|
|
305
|
+
if (!isNaN(numA) && !isNaN(numB)) {
|
|
306
|
+
comparison = numA - numB;
|
|
307
|
+
} else {
|
|
308
|
+
comparison = String(valueA).localeCompare(String(valueB));
|
|
309
|
+
}
|
|
310
|
+
return direction === "desc" ? -comparison : comparison;
|
|
311
|
+
};
|
|
312
|
+
const flattenTreeToRows = (nodes) => {
|
|
313
|
+
const result = [];
|
|
314
|
+
nodes.forEach((node) => {
|
|
315
|
+
result.push(__spreadProps(__spreadValues({}, node), {
|
|
316
|
+
_isTreeNode: true,
|
|
317
|
+
_treeLevel: node.level
|
|
318
|
+
}));
|
|
319
|
+
if (node.isExpanded && node.children.length > 0) {
|
|
320
|
+
result.push(...flattenTreeToRows(node.children));
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
return result;
|
|
324
|
+
};
|
|
325
|
+
const pivotData = useMemo(() => {
|
|
326
|
+
if (!data.length || !headers.length)
|
|
327
|
+
return { rows: [], summary: null, filteredData: [], totals: null };
|
|
328
|
+
let filteredData = data.filter((row) => {
|
|
329
|
+
if (searchTerm) {
|
|
330
|
+
const searchLower = searchTerm.toLowerCase();
|
|
331
|
+
const matchesSearch = Object.values(row).some(
|
|
332
|
+
(value) => value.toLowerCase().includes(searchLower)
|
|
333
|
+
);
|
|
334
|
+
if (!matchesSearch) return false;
|
|
335
|
+
}
|
|
336
|
+
return Object.entries(filters).every(([column, allowedValues]) => {
|
|
337
|
+
return allowedValues.length === 0 || allowedValues.includes(row[column]);
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
const totals = pivotConfig.values.length > 0 ? calculateAggregations(
|
|
341
|
+
filteredData,
|
|
342
|
+
pivotConfig.values,
|
|
343
|
+
pivotConfig.aggregation
|
|
344
|
+
) : null;
|
|
345
|
+
if (pivotConfig.rows.length === 0 && pivotConfig.columns.length === 0 && pivotConfig.values.length === 0) {
|
|
346
|
+
return {
|
|
347
|
+
rows: [],
|
|
348
|
+
summary: {
|
|
349
|
+
totalRows: 0,
|
|
350
|
+
originalRows: data.length,
|
|
351
|
+
totalColumns: headers.length
|
|
352
|
+
},
|
|
353
|
+
filteredData: [],
|
|
354
|
+
totals: null
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
const pivotRows = [];
|
|
358
|
+
const groups = filteredData.reduce((acc, row) => {
|
|
359
|
+
const rowKey = pivotConfig.rows.map((col) => row[col]).join(" | ") || "Total";
|
|
360
|
+
if (!acc[rowKey]) {
|
|
361
|
+
acc[rowKey] = [];
|
|
362
|
+
}
|
|
363
|
+
acc[rowKey].push(row);
|
|
364
|
+
return acc;
|
|
365
|
+
}, {});
|
|
366
|
+
const useTreeView = pivotConfig.rows.length > 0 && showGroupedView;
|
|
367
|
+
if (useTreeView) {
|
|
368
|
+
const treeStructure = buildTreeStructure(
|
|
369
|
+
filteredData,
|
|
370
|
+
pivotConfig.rows
|
|
371
|
+
);
|
|
372
|
+
const flattenedRows = flattenTreeToRows(treeStructure);
|
|
373
|
+
pivotRows.push(...flattenedRows);
|
|
374
|
+
} else if (pivotConfig.rows.length > 0) {
|
|
375
|
+
Object.entries(groups).forEach(([rowKey, groupData]) => {
|
|
376
|
+
const pivotRow = {
|
|
377
|
+
_rowKey: rowKey,
|
|
378
|
+
_count: groupData.length
|
|
379
|
+
};
|
|
380
|
+
pivotConfig.rows.forEach((rowCol, index) => {
|
|
381
|
+
pivotRow[rowCol] = rowKey.split(" | ")[index] || "";
|
|
382
|
+
});
|
|
383
|
+
pivotConfig.values.forEach((valueCol) => {
|
|
384
|
+
const rawValues = groupData.map((d) => d[valueCol]);
|
|
385
|
+
const numericValues = rawValues.map((val) => {
|
|
386
|
+
const cleaned = String(val).replace(/[,\s]/g, "");
|
|
387
|
+
const parsed = parseFloat(cleaned);
|
|
388
|
+
return !isNaN(parsed) ? parsed : 0;
|
|
389
|
+
});
|
|
390
|
+
const validNumericValues = numericValues.filter(
|
|
391
|
+
(val) => !isNaN(val) && val !== 0 || rawValues[numericValues.indexOf(val)] === "0"
|
|
392
|
+
);
|
|
393
|
+
switch (pivotConfig.aggregation) {
|
|
394
|
+
case "sum":
|
|
395
|
+
pivotRow[valueCol] = numericValues.reduce(
|
|
396
|
+
(sum, val) => sum + val,
|
|
397
|
+
0
|
|
398
|
+
);
|
|
399
|
+
break;
|
|
400
|
+
case "avg":
|
|
401
|
+
pivotRow[valueCol] = validNumericValues.length > 0 ? validNumericValues.reduce(
|
|
402
|
+
(sum, val) => sum + val,
|
|
403
|
+
0
|
|
404
|
+
) / validNumericValues.length : 0;
|
|
405
|
+
break;
|
|
406
|
+
case "min":
|
|
407
|
+
pivotRow[valueCol] = validNumericValues.length > 0 ? Math.min(...validNumericValues) : 0;
|
|
408
|
+
break;
|
|
409
|
+
case "max":
|
|
410
|
+
pivotRow[valueCol] = validNumericValues.length > 0 ? Math.max(...validNumericValues) : 0;
|
|
411
|
+
break;
|
|
412
|
+
default:
|
|
413
|
+
pivotRow[valueCol] = groupData.length;
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
pivotRows.push(pivotRow);
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
if (sortConfig && useTreeView) {
|
|
420
|
+
} else if (sortConfig && !useTreeView) {
|
|
421
|
+
pivotRows.sort((a, b) => {
|
|
422
|
+
const aValue = a[sortConfig.key];
|
|
423
|
+
const bValue = b[sortConfig.key];
|
|
424
|
+
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
425
|
+
return sortConfig.direction === "asc" ? aValue - bValue : bValue - aValue;
|
|
426
|
+
}
|
|
427
|
+
const aStr = String(aValue).toLowerCase();
|
|
428
|
+
const bStr = String(bValue).toLowerCase();
|
|
429
|
+
if (aStr < bStr) return sortConfig.direction === "asc" ? -1 : 1;
|
|
430
|
+
if (aStr > bStr) return sortConfig.direction === "asc" ? 1 : -1;
|
|
431
|
+
return 0;
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
rows: pivotRows,
|
|
436
|
+
summary: {
|
|
437
|
+
totalRows: pivotRows.length,
|
|
438
|
+
totalGroups: Object.keys(groups).length,
|
|
439
|
+
originalRows: filteredData.length
|
|
440
|
+
},
|
|
441
|
+
filteredData,
|
|
442
|
+
totals
|
|
443
|
+
};
|
|
444
|
+
}, [
|
|
445
|
+
data,
|
|
446
|
+
headers,
|
|
447
|
+
pivotConfig,
|
|
448
|
+
filters,
|
|
449
|
+
searchTerm,
|
|
450
|
+
sortConfig,
|
|
451
|
+
expandedGroups,
|
|
452
|
+
showGroupedView
|
|
453
|
+
]);
|
|
454
|
+
const getUniqueValues = (column) => {
|
|
455
|
+
return [...new Set(data.map((row) => row[column]))].filter(Boolean).sort();
|
|
456
|
+
};
|
|
457
|
+
const updatePivotConfig = (key, value) => {
|
|
458
|
+
setPivotConfig((prev) => __spreadProps(__spreadValues({}, prev), { [key]: value }));
|
|
459
|
+
};
|
|
460
|
+
const addToPivotConfig = (key, value) => {
|
|
461
|
+
setPivotConfig((prev) => __spreadProps(__spreadValues({}, prev), {
|
|
462
|
+
[key]: [...prev[key], value]
|
|
463
|
+
}));
|
|
464
|
+
};
|
|
465
|
+
const removeFromPivotConfig = (key, value) => {
|
|
466
|
+
setPivotConfig((prev) => __spreadProps(__spreadValues({}, prev), {
|
|
467
|
+
[key]: prev[key].filter((item) => item !== value)
|
|
468
|
+
}));
|
|
469
|
+
};
|
|
470
|
+
const moveColumnUp = (key, index) => {
|
|
471
|
+
if (index === 0) return;
|
|
472
|
+
setPivotConfig((prev) => {
|
|
473
|
+
const newArray = [...prev[key]];
|
|
474
|
+
[newArray[index - 1], newArray[index]] = [
|
|
475
|
+
newArray[index],
|
|
476
|
+
newArray[index - 1]
|
|
477
|
+
];
|
|
478
|
+
return __spreadProps(__spreadValues({}, prev), { [key]: newArray });
|
|
479
|
+
});
|
|
480
|
+
};
|
|
481
|
+
const moveColumnDown = (key, index) => {
|
|
482
|
+
setPivotConfig((prev) => {
|
|
483
|
+
if (index >= prev[key].length - 1) return prev;
|
|
484
|
+
const newArray = [...prev[key]];
|
|
485
|
+
[newArray[index], newArray[index + 1]] = [
|
|
486
|
+
newArray[index + 1],
|
|
487
|
+
newArray[index]
|
|
488
|
+
];
|
|
489
|
+
return __spreadProps(__spreadValues({}, prev), { [key]: newArray });
|
|
490
|
+
});
|
|
491
|
+
};
|
|
492
|
+
const toggleColumnFilter = (column, value) => {
|
|
493
|
+
setFilters((prev) => {
|
|
494
|
+
const currentFilters = prev[column] || [];
|
|
495
|
+
const newFilters = currentFilters.includes(value) ? currentFilters.filter((v) => v !== value) : [...currentFilters, value];
|
|
496
|
+
return __spreadProps(__spreadValues({}, prev), { [column]: newFilters });
|
|
497
|
+
});
|
|
498
|
+
};
|
|
499
|
+
const clearColumnFilter = (column) => {
|
|
500
|
+
setFilters((prev) => {
|
|
501
|
+
const newFilters = __spreadValues({}, prev);
|
|
502
|
+
delete newFilters[column];
|
|
503
|
+
return newFilters;
|
|
504
|
+
});
|
|
505
|
+
};
|
|
506
|
+
const handleSort = (columnKey) => {
|
|
507
|
+
setSortConfig((prev) => {
|
|
508
|
+
if (prev && prev.key === columnKey) {
|
|
509
|
+
if (prev.direction === "asc") {
|
|
510
|
+
return { key: columnKey, direction: "desc" };
|
|
511
|
+
} else {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
} else {
|
|
515
|
+
return { key: columnKey, direction: "asc" };
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
};
|
|
519
|
+
const toggleTreeNodeExpansion = (nodeId) => {
|
|
520
|
+
setAnimatingNodes((prev) => new Set(prev).add(nodeId));
|
|
521
|
+
setExpandedGroups((prev) => {
|
|
522
|
+
const newSet = new Set(prev);
|
|
523
|
+
if (newSet.has(nodeId)) {
|
|
524
|
+
newSet.delete(nodeId);
|
|
525
|
+
} else {
|
|
526
|
+
newSet.add(nodeId);
|
|
527
|
+
}
|
|
528
|
+
return newSet;
|
|
529
|
+
});
|
|
530
|
+
setTimeout(() => {
|
|
531
|
+
setAnimatingNodes((prev) => {
|
|
532
|
+
const newSet = new Set(prev);
|
|
533
|
+
newSet.delete(nodeId);
|
|
534
|
+
return newSet;
|
|
535
|
+
});
|
|
536
|
+
}, 200);
|
|
537
|
+
};
|
|
538
|
+
const exportToCsv = () => {
|
|
539
|
+
const csvContent = [
|
|
540
|
+
headers.join(";"),
|
|
541
|
+
...pivotData.rows.map(
|
|
542
|
+
(row) => headers.map((header) => row[header] || "").join(";")
|
|
543
|
+
)
|
|
544
|
+
].join("\n");
|
|
545
|
+
const blob = new Blob([csvContent], { type: "text/csv" });
|
|
546
|
+
const url = URL.createObjectURL(blob);
|
|
547
|
+
const link = document.createElement("a");
|
|
548
|
+
link.href = url;
|
|
549
|
+
link.download = `${name.replace(/[^a-zA-Z0-9]/g, "_")}_export.csv`;
|
|
550
|
+
link.click();
|
|
551
|
+
URL.revokeObjectURL(url);
|
|
552
|
+
};
|
|
553
|
+
const resetConfig = () => {
|
|
554
|
+
setPivotConfig({
|
|
555
|
+
rows: [],
|
|
556
|
+
columns: [],
|
|
557
|
+
values: [],
|
|
558
|
+
aggregation: "count"
|
|
559
|
+
});
|
|
560
|
+
setFilters({});
|
|
561
|
+
setSearchTerm("");
|
|
562
|
+
setActiveFilterDropdown(null);
|
|
563
|
+
setSortConfig(null);
|
|
564
|
+
setExpandedGroups(/* @__PURE__ */ new Set());
|
|
565
|
+
setShowGroupedView(false);
|
|
566
|
+
setAnimatingNodes(/* @__PURE__ */ new Set());
|
|
567
|
+
};
|
|
568
|
+
useEffect(() => {
|
|
569
|
+
const handleClickOutside = (event) => {
|
|
570
|
+
if (activeFilterDropdown && !event.target.closest(".relative")) {
|
|
571
|
+
setActiveFilterDropdown(null);
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
if (activeFilterDropdown) {
|
|
575
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
576
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
577
|
+
}
|
|
578
|
+
}, [activeFilterDropdown]);
|
|
579
|
+
const [isFilterPanelVisible, setIsFilterPanelVisible] = useState(true);
|
|
580
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-[calc(100%-70px)] bg-white dark:bg-gray-900 min-h-0", children: [
|
|
581
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col min-w-0", children: [
|
|
582
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
583
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center space-x-4", children: pivotData.summary && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-4 text-xs text-gray-600 dark:text-gray-400", children: [
|
|
584
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
|
|
585
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Rows:" }),
|
|
586
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-800 dark:text-gray-200", children: pivotData.summary.totalRows.toLocaleString() })
|
|
587
|
+
] }),
|
|
588
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
|
|
589
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Columns:" }),
|
|
590
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-800 dark:text-gray-200", children: (pivotData.summary.totalColumns || headers.length).toLocaleString() })
|
|
591
|
+
] }),
|
|
592
|
+
pivotData.summary.originalRows && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
|
|
593
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Filtered:" }),
|
|
594
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-800 dark:text-gray-200", children: pivotData.summary.originalRows.toLocaleString() })
|
|
595
|
+
] })
|
|
596
|
+
] }) }),
|
|
597
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
598
|
+
/* @__PURE__ */ jsxs(
|
|
599
|
+
"button",
|
|
600
|
+
{
|
|
601
|
+
onClick: exportToCsv,
|
|
602
|
+
className: "flex items-center px-3 py-1.5 bg-emerald-600 text-white rounded text-xs hover:bg-emerald-700 transition-colors",
|
|
603
|
+
title: "Export to CSV",
|
|
604
|
+
children: [
|
|
605
|
+
/* @__PURE__ */ jsx(Download, { size: 14, className: "mr-1" }),
|
|
606
|
+
"Export"
|
|
607
|
+
]
|
|
608
|
+
}
|
|
609
|
+
),
|
|
610
|
+
/* @__PURE__ */ jsxs(
|
|
611
|
+
"button",
|
|
612
|
+
{
|
|
613
|
+
onClick: resetConfig,
|
|
614
|
+
className: "flex items-center px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded text-xs hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors text-gray-700 dark:text-gray-300",
|
|
615
|
+
title: "Reset Configuration",
|
|
616
|
+
children: [
|
|
617
|
+
/* @__PURE__ */ jsx(RotateCcw, { size: 14, className: "mr-1" }),
|
|
618
|
+
"Reset"
|
|
619
|
+
]
|
|
620
|
+
}
|
|
621
|
+
),
|
|
622
|
+
/* @__PURE__ */ jsxs(
|
|
623
|
+
"button",
|
|
624
|
+
{
|
|
625
|
+
onClick: () => setIsFilterPanelVisible(
|
|
626
|
+
!isFilterPanelVisible
|
|
627
|
+
),
|
|
628
|
+
className: "flex items-center px-3 py-1.5 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded text-xs hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors",
|
|
629
|
+
title: isFilterPanelVisible ? "Hide Filters" : "Show Filters",
|
|
630
|
+
children: [
|
|
631
|
+
isFilterPanelVisible ? /* @__PURE__ */ jsx(EyeOff, { size: 14, className: "mr-1" }) : /* @__PURE__ */ jsx(Eye, { size: 14, className: "mr-1" }),
|
|
632
|
+
isFilterPanelVisible ? "Hide" : "Show",
|
|
633
|
+
" Filters"
|
|
634
|
+
]
|
|
635
|
+
}
|
|
636
|
+
)
|
|
637
|
+
] })
|
|
638
|
+
] }) }),
|
|
639
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col overflow-hidden bg-white dark:bg-gray-900", children: /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto min-w-0", children: pivotData.rows.length > 0 ? /* @__PURE__ */ jsx("div", { className: "", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm border-collapse bg-white dark:bg-gray-900 analytics-table-container", children: [
|
|
640
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-gray-100 dark:bg-gray-900 sticky top-0", children: /* @__PURE__ */ jsx("tr", { children: (pivotConfig.rows.length > 0 || pivotConfig.columns.length > 0 || pivotConfig.values.length > 0 ? [
|
|
641
|
+
...pivotConfig.rows,
|
|
642
|
+
...pivotConfig.values,
|
|
643
|
+
...showGroupedView ? [] : ["_count"]
|
|
644
|
+
] : headers).map((header, index) => /* @__PURE__ */ jsx(
|
|
645
|
+
"th",
|
|
646
|
+
{
|
|
647
|
+
className: "border-0 border-gray-300 dark:border-gray-600 px-3 py-2 text-left font-medium text-gray-700 dark:text-gray-200 bg-gray-100 dark:bg-gray-700 relative",
|
|
648
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
649
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: header === "_count" ? "Count" : header }),
|
|
650
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
|
|
651
|
+
/* @__PURE__ */ jsx(
|
|
652
|
+
"button",
|
|
653
|
+
{
|
|
654
|
+
onClick: () => handleSort(
|
|
655
|
+
header
|
|
656
|
+
),
|
|
657
|
+
className: "p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors cursor-pointer",
|
|
658
|
+
title: "Sort column",
|
|
659
|
+
children: (sortConfig == null ? void 0 : sortConfig.key) === header ? sortConfig.direction === "asc" ? /* @__PURE__ */ jsx(
|
|
660
|
+
ArrowUp,
|
|
661
|
+
{
|
|
662
|
+
size: 14
|
|
663
|
+
}
|
|
664
|
+
) : /* @__PURE__ */ jsx(
|
|
665
|
+
ArrowDown,
|
|
666
|
+
{
|
|
667
|
+
size: 14
|
|
668
|
+
}
|
|
669
|
+
) : /* @__PURE__ */ jsx(
|
|
670
|
+
ArrowUpDown,
|
|
671
|
+
{
|
|
672
|
+
size: 14,
|
|
673
|
+
className: "opacity-50"
|
|
674
|
+
}
|
|
675
|
+
)
|
|
676
|
+
}
|
|
677
|
+
),
|
|
678
|
+
header !== "_count" && header !== "_rowKey" && /* @__PURE__ */ jsx(
|
|
679
|
+
"button",
|
|
680
|
+
{
|
|
681
|
+
onClick: () => setActiveFilterDropdown(
|
|
682
|
+
activeFilterDropdown === header ? null : header
|
|
683
|
+
),
|
|
684
|
+
className: `p-1 rounded cursor-pointer ${(filters[header] || []).length > 0 ? "text-emerald-600 bg-emerald-100 dark:bg-emerald-900/30" : "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"}`,
|
|
685
|
+
title: "Filter column",
|
|
686
|
+
children: /* @__PURE__ */ jsx(
|
|
687
|
+
Filter,
|
|
688
|
+
{
|
|
689
|
+
size: 14
|
|
690
|
+
}
|
|
691
|
+
)
|
|
692
|
+
}
|
|
693
|
+
)
|
|
694
|
+
] }),
|
|
695
|
+
activeFilterDropdown === header && header !== "_count" && header !== "_rowKey" && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 top-8 z-50 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg min-w-96 max-w-96 min-h-96 max-h-96 overflow-auto", children: [
|
|
696
|
+
/* @__PURE__ */ jsx("div", { className: "p-2 border-b border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
697
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-gray-700 dark:text-gray-300", children: [
|
|
698
|
+
"Filter",
|
|
699
|
+
" ",
|
|
700
|
+
header
|
|
701
|
+
] }),
|
|
702
|
+
/* @__PURE__ */ jsx(
|
|
703
|
+
"button",
|
|
704
|
+
{
|
|
705
|
+
onClick: () => clearColumnFilter(
|
|
706
|
+
header
|
|
707
|
+
),
|
|
708
|
+
className: "text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300",
|
|
709
|
+
children: "Clear"
|
|
710
|
+
}
|
|
711
|
+
)
|
|
712
|
+
] }) }),
|
|
713
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-y-auto", children: getUniqueValues(
|
|
714
|
+
header
|
|
715
|
+
).map(
|
|
716
|
+
(value) => /* @__PURE__ */ jsxs(
|
|
717
|
+
"label",
|
|
718
|
+
{
|
|
719
|
+
className: "flex items-center px-3 py-1 hover:bg-gray-100 dark:hover:bg-gray-600 cursor-pointer",
|
|
720
|
+
children: [
|
|
721
|
+
/* @__PURE__ */ jsx(
|
|
722
|
+
"input",
|
|
723
|
+
{
|
|
724
|
+
type: "checkbox",
|
|
725
|
+
checked: (filters[header] || []).includes(
|
|
726
|
+
value
|
|
727
|
+
),
|
|
728
|
+
onChange: () => toggleColumnFilter(
|
|
729
|
+
header,
|
|
730
|
+
value
|
|
731
|
+
),
|
|
732
|
+
className: "mr-2 text-emerald-600 focus:ring-emerald-500"
|
|
733
|
+
}
|
|
734
|
+
),
|
|
735
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300 truncate", children: value })
|
|
736
|
+
]
|
|
737
|
+
},
|
|
738
|
+
value
|
|
739
|
+
)
|
|
740
|
+
) })
|
|
741
|
+
] })
|
|
742
|
+
] })
|
|
743
|
+
},
|
|
744
|
+
header
|
|
745
|
+
)) }) }),
|
|
746
|
+
/* @__PURE__ */ jsx("tbody", { className: "bg-white dark:bg-gray-900", children: pivotData.rows.slice(0, 1e6).map((row, index) => {
|
|
747
|
+
if (row._isTreeNode && showGroupedView) {
|
|
748
|
+
const indentLevel = row._treeLevel || row.level || 0;
|
|
749
|
+
const isLeafNode = row.children && row.children.length === 0;
|
|
750
|
+
const hasChildren = row.children && row.children.length > 0;
|
|
751
|
+
const isAnimating = animatingNodes.has(
|
|
752
|
+
row.id
|
|
753
|
+
);
|
|
754
|
+
if (isLeafNode) {
|
|
755
|
+
return /* @__PURE__ */ jsx(
|
|
756
|
+
"tr",
|
|
757
|
+
{
|
|
758
|
+
className: `transition-all duration-200 bg-white dark:bg-gray-900 hover:bg-blue-50 dark:hover:bg-gray-700 border-l-2 border-l-transparent hover:border-l-blue-400 ${isAnimating ? "animate-pulse" : ""}`,
|
|
759
|
+
children: (pivotConfig.rows.length > 0 || pivotConfig.columns.length > 0 || pivotConfig.values.length > 0 ? [
|
|
760
|
+
...pivotConfig.rows,
|
|
761
|
+
...pivotConfig.values,
|
|
762
|
+
...showGroupedView ? [] : [
|
|
763
|
+
"_count"
|
|
764
|
+
]
|
|
765
|
+
] : headers).map(
|
|
766
|
+
(header, headerIndex) => {
|
|
767
|
+
var _a, _b;
|
|
768
|
+
const cellIndent = headerIndex === 0 ? indentLevel * 24 + 32 : 0;
|
|
769
|
+
const isFirstColumn = headerIndex === 0;
|
|
770
|
+
let cellValue;
|
|
771
|
+
if (header === "_count") {
|
|
772
|
+
cellValue = row.count;
|
|
773
|
+
} else if (((_a = row.aggregatedData) == null ? void 0 : _a[header]) !== void 0) {
|
|
774
|
+
cellValue = row.aggregatedData[header];
|
|
775
|
+
} else if (((_b = row.value) == null ? void 0 : _b[header]) !== void 0) {
|
|
776
|
+
cellValue = row.value[header];
|
|
777
|
+
} else {
|
|
778
|
+
cellValue = isFirstColumn ? row.label : "";
|
|
779
|
+
}
|
|
780
|
+
const alignmentClass = getCellAlignment(
|
|
781
|
+
cellValue,
|
|
782
|
+
header,
|
|
783
|
+
isFirstColumn
|
|
784
|
+
);
|
|
785
|
+
return /* @__PURE__ */ jsxs(
|
|
786
|
+
"td",
|
|
787
|
+
{
|
|
788
|
+
className: `border-0 border-b border-gray-200 dark:border-gray-700 px-3 py-2 text-gray-900 dark:text-gray-100 ${alignmentClass}`,
|
|
789
|
+
style: headerIndex === 0 ? {
|
|
790
|
+
paddingLeft: `${cellIndent}px`
|
|
791
|
+
} : {},
|
|
792
|
+
children: [
|
|
793
|
+
headerIndex === 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
794
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
795
|
+
Array.from(
|
|
796
|
+
{
|
|
797
|
+
length: indentLevel
|
|
798
|
+
}
|
|
799
|
+
).map(
|
|
800
|
+
(_, levelIndex) => /* @__PURE__ */ jsx(
|
|
801
|
+
"div",
|
|
802
|
+
{
|
|
803
|
+
className: "w-6 flex justify-center",
|
|
804
|
+
children: /* @__PURE__ */ jsx("div", { className: "w-px h-full bg-gray-300 dark:bg-gray-600" })
|
|
805
|
+
},
|
|
806
|
+
levelIndex
|
|
807
|
+
)
|
|
808
|
+
),
|
|
809
|
+
/* @__PURE__ */ jsx("div", { className: "w-6 flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "w-3 h-px bg-gray-300 dark:bg-gray-600" }) })
|
|
810
|
+
] }),
|
|
811
|
+
getTreeIcon(
|
|
812
|
+
row,
|
|
813
|
+
hasChildren
|
|
814
|
+
),
|
|
815
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-700 dark:text-gray-300", children: shouldFormatAsNumber(
|
|
816
|
+
cellValue,
|
|
817
|
+
header
|
|
818
|
+
) ? formatNumericValue(
|
|
819
|
+
cellValue
|
|
820
|
+
) : cellValue })
|
|
821
|
+
] }),
|
|
822
|
+
headerIndex !== 0 && /* @__PURE__ */ jsx("span", { children: shouldFormatAsNumber(
|
|
823
|
+
cellValue,
|
|
824
|
+
header
|
|
825
|
+
) ? formatNumericValue(
|
|
826
|
+
cellValue
|
|
827
|
+
) : cellValue })
|
|
828
|
+
]
|
|
829
|
+
},
|
|
830
|
+
header
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
)
|
|
834
|
+
},
|
|
835
|
+
row.id || index
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
return /* @__PURE__ */ jsx(
|
|
839
|
+
"tr",
|
|
840
|
+
{
|
|
841
|
+
className: `transition-all duration-200 font-medium ${getLevelColor(
|
|
842
|
+
indentLevel
|
|
843
|
+
)} hover:bg-opacity-75 border-l-4 ${isAnimating ? "animate-pulse" : ""}`,
|
|
844
|
+
children: /* @__PURE__ */ jsx(
|
|
845
|
+
"td",
|
|
846
|
+
{
|
|
847
|
+
colSpan: (pivotConfig.rows.length > 0 || pivotConfig.columns.length > 0 || pivotConfig.values.length > 0 ? [
|
|
848
|
+
...pivotConfig.rows,
|
|
849
|
+
...pivotConfig.values,
|
|
850
|
+
...showGroupedView ? [] : [
|
|
851
|
+
"_count"
|
|
852
|
+
]
|
|
853
|
+
] : headers).length,
|
|
854
|
+
className: "border-0 border-b border-gray-200 dark:border-gray-700 px-3 py-3 text-gray-900 dark:text-gray-100",
|
|
855
|
+
children: /* @__PURE__ */ jsxs(
|
|
856
|
+
"div",
|
|
857
|
+
{
|
|
858
|
+
className: "flex items-center space-x-3 cursor-pointer group",
|
|
859
|
+
style: {
|
|
860
|
+
paddingLeft: `${indentLevel * 24}px`
|
|
861
|
+
},
|
|
862
|
+
onClick: () => toggleTreeNodeExpansion(
|
|
863
|
+
row.id
|
|
864
|
+
),
|
|
865
|
+
children: [
|
|
866
|
+
indentLevel > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center", children: Array.from(
|
|
867
|
+
{
|
|
868
|
+
length: indentLevel
|
|
869
|
+
}
|
|
870
|
+
).map(
|
|
871
|
+
(_, levelIndex) => /* @__PURE__ */ jsx(
|
|
872
|
+
"div",
|
|
873
|
+
{
|
|
874
|
+
className: "w-6 flex justify-center",
|
|
875
|
+
children: /* @__PURE__ */ jsx("div", { className: "w-px h-full bg-gray-300 dark:bg-gray-600" })
|
|
876
|
+
},
|
|
877
|
+
levelIndex
|
|
878
|
+
)
|
|
879
|
+
) }),
|
|
880
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-6 h-6 rounded-full bg-white dark:bg-gray-700 shadow-sm border border-gray-300 dark:border-gray-600 group-hover:bg-gray-50 dark:group-hover:bg-gray-600 transition-colors", children: getExpandIcon(
|
|
881
|
+
row.isExpanded,
|
|
882
|
+
hasChildren
|
|
883
|
+
) }),
|
|
884
|
+
getTreeIcon(
|
|
885
|
+
row,
|
|
886
|
+
hasChildren
|
|
887
|
+
),
|
|
888
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 flex-1", children: [
|
|
889
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-800 dark:text-gray-200", children: row.label }),
|
|
890
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm px-2 py-1 bg-gray-200 dark:bg-gray-700 rounded-full text-gray-600 dark:text-gray-400", children: [
|
|
891
|
+
row.count,
|
|
892
|
+
" ",
|
|
893
|
+
"records"
|
|
894
|
+
] })
|
|
895
|
+
] }),
|
|
896
|
+
pivotConfig.values.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-4 text-sm text-gray-600 dark:text-gray-400", children: [
|
|
897
|
+
pivotConfig.values.slice(
|
|
898
|
+
0,
|
|
899
|
+
3
|
|
900
|
+
).map(
|
|
901
|
+
(col) => /* @__PURE__ */ jsxs(
|
|
902
|
+
"div",
|
|
903
|
+
{
|
|
904
|
+
className: "flex items-center space-x-1",
|
|
905
|
+
children: [
|
|
906
|
+
/* @__PURE__ */ jsx(
|
|
907
|
+
BarChart3,
|
|
908
|
+
{
|
|
909
|
+
size: 12,
|
|
910
|
+
className: "text-gray-500"
|
|
911
|
+
}
|
|
912
|
+
),
|
|
913
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
914
|
+
col,
|
|
915
|
+
":"
|
|
916
|
+
] }),
|
|
917
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-800 dark:text-gray-200 font-mono", children: shouldFormatAsNumber(
|
|
918
|
+
row.aggregatedData[col],
|
|
919
|
+
col
|
|
920
|
+
) ? formatNumericValue(
|
|
921
|
+
row.aggregatedData[col]
|
|
922
|
+
) : row.aggregatedData[col] })
|
|
923
|
+
]
|
|
924
|
+
},
|
|
925
|
+
col
|
|
926
|
+
)
|
|
927
|
+
),
|
|
928
|
+
pivotConfig.values.length > 3 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500", children: [
|
|
929
|
+
"+",
|
|
930
|
+
pivotConfig.values.length - 3,
|
|
931
|
+
" ",
|
|
932
|
+
"more"
|
|
933
|
+
] })
|
|
934
|
+
] })
|
|
935
|
+
]
|
|
936
|
+
}
|
|
937
|
+
)
|
|
938
|
+
}
|
|
939
|
+
)
|
|
940
|
+
},
|
|
941
|
+
row.id
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
return /* @__PURE__ */ jsx(
|
|
945
|
+
"tr",
|
|
946
|
+
{
|
|
947
|
+
className: "bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-gray-700",
|
|
948
|
+
children: (pivotConfig.rows.length > 0 || pivotConfig.columns.length > 0 || pivotConfig.values.length > 0 ? [
|
|
949
|
+
...pivotConfig.rows,
|
|
950
|
+
...pivotConfig.values,
|
|
951
|
+
...showGroupedView ? [] : [
|
|
952
|
+
"_count"
|
|
953
|
+
]
|
|
954
|
+
] : headers).map(
|
|
955
|
+
(header, headerIndex) => {
|
|
956
|
+
const cellValue = header === "_count" ? row._count || "" : row[header] || "";
|
|
957
|
+
const isFirstColumn = headerIndex === 0;
|
|
958
|
+
const alignmentClass = getCellAlignment(
|
|
959
|
+
cellValue,
|
|
960
|
+
header,
|
|
961
|
+
isFirstColumn
|
|
962
|
+
);
|
|
963
|
+
return /* @__PURE__ */ jsx(
|
|
964
|
+
"td",
|
|
965
|
+
{
|
|
966
|
+
className: `border border-gray-300 dark:border-gray-600 px-3 py-2 text-gray-900 dark:text-gray-100 ${alignmentClass}`,
|
|
967
|
+
children: shouldFormatAsNumber(
|
|
968
|
+
cellValue,
|
|
969
|
+
header
|
|
970
|
+
) ? formatNumericValue(
|
|
971
|
+
cellValue
|
|
972
|
+
) : cellValue
|
|
973
|
+
},
|
|
974
|
+
header
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
)
|
|
978
|
+
},
|
|
979
|
+
row._rowKey || index
|
|
980
|
+
);
|
|
981
|
+
}) }),
|
|
982
|
+
pivotData.totals && pivotConfig.values.length > 0 && /* @__PURE__ */ jsx("tfoot", { className: "bg-gray-100 dark:bg-gray-800 border-t-2 border-gray-400 dark:border-gray-600", children: /* @__PURE__ */ jsx("tr", { children: (pivotConfig.rows.length > 0 || pivotConfig.columns.length > 0 || pivotConfig.values.length > 0 ? [
|
|
983
|
+
...pivotConfig.rows,
|
|
984
|
+
...pivotConfig.values,
|
|
985
|
+
...showGroupedView ? [] : ["_count"]
|
|
986
|
+
] : headers).map(
|
|
987
|
+
(header, headerIndex) => {
|
|
988
|
+
const isFirstColumn = headerIndex === 0;
|
|
989
|
+
let cellContent = "";
|
|
990
|
+
let alignmentClass = getCellAlignment(
|
|
991
|
+
"",
|
|
992
|
+
header,
|
|
993
|
+
isFirstColumn
|
|
994
|
+
);
|
|
995
|
+
if (isFirstColumn) {
|
|
996
|
+
cellContent = `Total (${pivotConfig.aggregation})`;
|
|
997
|
+
alignmentClass = "text-left";
|
|
998
|
+
} else if (pivotConfig.values.includes(
|
|
999
|
+
header
|
|
1000
|
+
) && pivotData.totals && pivotData.totals[header] !== void 0) {
|
|
1001
|
+
const totalValue = pivotData.totals[header];
|
|
1002
|
+
cellContent = shouldFormatAsNumber(
|
|
1003
|
+
totalValue,
|
|
1004
|
+
header
|
|
1005
|
+
) ? formatNumericValue(
|
|
1006
|
+
totalValue
|
|
1007
|
+
) : String(
|
|
1008
|
+
totalValue
|
|
1009
|
+
);
|
|
1010
|
+
alignmentClass = "text-right";
|
|
1011
|
+
} else if (header === "_count" && !showGroupedView) {
|
|
1012
|
+
cellContent = pivotData.filteredData.length.toLocaleString();
|
|
1013
|
+
alignmentClass = "text-right";
|
|
1014
|
+
}
|
|
1015
|
+
return /* @__PURE__ */ jsx(
|
|
1016
|
+
"td",
|
|
1017
|
+
{
|
|
1018
|
+
className: `border-0 border-gray-300 dark:border-gray-600 px-3 py-3 font-semibold text-gray-900 dark:text-gray-100 ${alignmentClass}`,
|
|
1019
|
+
children: cellContent
|
|
1020
|
+
},
|
|
1021
|
+
header
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
) }) })
|
|
1025
|
+
] }) }) : /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-64 text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsx("div", { className: "text-center max-w-md", children: pivotConfig.rows.length === 0 && pivotConfig.columns.length === 0 && pivotConfig.values.length === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1026
|
+
/* @__PURE__ */ jsx("div", { className: "w-16 h-16 mx-auto mt-16 mb-4 rounded-full bg-emerald-100 dark:bg-emerald-900/20 flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
1027
|
+
"svg",
|
|
1028
|
+
{
|
|
1029
|
+
className: "w-8 h-8 text-emerald-600 dark:text-emerald-400",
|
|
1030
|
+
fill: "none",
|
|
1031
|
+
stroke: "currentColor",
|
|
1032
|
+
viewBox: "0 0 24 24",
|
|
1033
|
+
children: /* @__PURE__ */ jsx(
|
|
1034
|
+
"path",
|
|
1035
|
+
{
|
|
1036
|
+
strokeLinecap: "round",
|
|
1037
|
+
strokeLinejoin: "round",
|
|
1038
|
+
strokeWidth: 2,
|
|
1039
|
+
d: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
|
1040
|
+
}
|
|
1041
|
+
)
|
|
1042
|
+
}
|
|
1043
|
+
) }),
|
|
1044
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-medium mb-2", children: "Configure Your Analytics" }),
|
|
1045
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm mb-4", children: [
|
|
1046
|
+
"Dataset loaded with",
|
|
1047
|
+
" ",
|
|
1048
|
+
data.length.toLocaleString(),
|
|
1049
|
+
" ",
|
|
1050
|
+
"records and ",
|
|
1051
|
+
headers.length,
|
|
1052
|
+
" ",
|
|
1053
|
+
"columns.",
|
|
1054
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
1055
|
+
"Select columns in the filter panel to start analyzing your data."
|
|
1056
|
+
] }),
|
|
1057
|
+
/* @__PURE__ */ jsxs("div", { className: "text-left bg-gray-50 dark:bg-gray-900 rounded-lg p-4 text-xs", children: [
|
|
1058
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium mb-2", children: "Quick Start:" }),
|
|
1059
|
+
/* @__PURE__ */ jsxs("ol", { className: "list-decimal list-inside space-y-1", children: [
|
|
1060
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
1061
|
+
/* @__PURE__ */ jsx("strong", { children: "Group by Rows:" }),
|
|
1062
|
+
" ",
|
|
1063
|
+
"Select dimensions to group your data"
|
|
1064
|
+
] }),
|
|
1065
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
1066
|
+
/* @__PURE__ */ jsx("strong", { children: "Value Columns:" }),
|
|
1067
|
+
" ",
|
|
1068
|
+
"Choose metrics to analyze"
|
|
1069
|
+
] }),
|
|
1070
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
1071
|
+
/* @__PURE__ */ jsx("strong", { children: "Aggregation:" }),
|
|
1072
|
+
" ",
|
|
1073
|
+
"Pick how to summarize values (sum, average, etc.)"
|
|
1074
|
+
] })
|
|
1075
|
+
] })
|
|
1076
|
+
] })
|
|
1077
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1078
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-medium mb-2", children: "No data to display" }),
|
|
1079
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "Try adjusting your filters or configuration" })
|
|
1080
|
+
] }) }) }) }) })
|
|
1081
|
+
] }),
|
|
1082
|
+
isFilterPanelVisible && /* @__PURE__ */ jsx("div", { className: "w-80 min-w-80 border-l border-gray-200 dark:border-gray-700 bg-white flex flex-col flex-shrink-0", children: /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 space-y-4 bg-gray-50", children: [
|
|
1083
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1084
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300", children: "Search Data" }),
|
|
1085
|
+
/* @__PURE__ */ jsx(
|
|
1086
|
+
"input",
|
|
1087
|
+
{
|
|
1088
|
+
type: "text",
|
|
1089
|
+
value: searchTerm,
|
|
1090
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
1091
|
+
placeholder: "Search across all columns...",
|
|
1092
|
+
className: "w-full text-sm px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 placeholder-gray-500 dark:placeholder-gray-400"
|
|
1093
|
+
}
|
|
1094
|
+
)
|
|
1095
|
+
] }),
|
|
1096
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1097
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1098
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300", children: "Group by Rows" }),
|
|
1099
|
+
/* @__PURE__ */ jsxs(
|
|
1100
|
+
"select",
|
|
1101
|
+
{
|
|
1102
|
+
value: "",
|
|
1103
|
+
onChange: (e) => e.target.value && addToPivotConfig("rows", e.target.value),
|
|
1104
|
+
className: "w-full text-sm px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100",
|
|
1105
|
+
children: [
|
|
1106
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Add column..." }),
|
|
1107
|
+
headers.filter(
|
|
1108
|
+
(h) => !pivotConfig.rows.includes(h)
|
|
1109
|
+
).sort().map((header) => /* @__PURE__ */ jsx("option", { value: header, children: header }, header))
|
|
1110
|
+
]
|
|
1111
|
+
}
|
|
1112
|
+
),
|
|
1113
|
+
pivotConfig.rows.map((row, index) => /* @__PURE__ */ jsxs(
|
|
1114
|
+
"div",
|
|
1115
|
+
{
|
|
1116
|
+
className: "flex items-center justify-between mt-1 px-2 py-1 bg-emerald-100 dark:bg-emerald-900/30 rounded text-sm",
|
|
1117
|
+
children: [
|
|
1118
|
+
/* @__PURE__ */ jsx("span", { className: "text-emerald-800 dark:text-emerald-200 flex-1", children: row }),
|
|
1119
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
|
|
1120
|
+
/* @__PURE__ */ jsx(
|
|
1121
|
+
"button",
|
|
1122
|
+
{
|
|
1123
|
+
onClick: () => moveColumnUp("rows", index),
|
|
1124
|
+
disabled: index === 0,
|
|
1125
|
+
className: "text-emerald-600 hover:text-emerald-800 dark:text-emerald-400 dark:hover:text-emerald-200 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1126
|
+
children: /* @__PURE__ */ jsx(ArrowUp, { size: 14 })
|
|
1127
|
+
}
|
|
1128
|
+
),
|
|
1129
|
+
/* @__PURE__ */ jsx(
|
|
1130
|
+
"button",
|
|
1131
|
+
{
|
|
1132
|
+
onClick: () => moveColumnDown(
|
|
1133
|
+
"rows",
|
|
1134
|
+
index
|
|
1135
|
+
),
|
|
1136
|
+
disabled: index === pivotConfig.rows.length - 1,
|
|
1137
|
+
className: "text-emerald-600 hover:text-emerald-800 dark:text-emerald-400 dark:hover:text-emerald-200 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1138
|
+
children: /* @__PURE__ */ jsx(ArrowDown, { size: 14 })
|
|
1139
|
+
}
|
|
1140
|
+
),
|
|
1141
|
+
/* @__PURE__ */ jsx(
|
|
1142
|
+
"button",
|
|
1143
|
+
{
|
|
1144
|
+
onClick: () => removeFromPivotConfig(
|
|
1145
|
+
"rows",
|
|
1146
|
+
row
|
|
1147
|
+
),
|
|
1148
|
+
className: "text-emerald-600 hover:text-emerald-800 dark:text-emerald-400 dark:hover:text-emerald-200",
|
|
1149
|
+
children: /* @__PURE__ */ jsx(X, { size: 14 })
|
|
1150
|
+
}
|
|
1151
|
+
)
|
|
1152
|
+
] })
|
|
1153
|
+
]
|
|
1154
|
+
},
|
|
1155
|
+
row
|
|
1156
|
+
))
|
|
1157
|
+
] }),
|
|
1158
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1159
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300", children: "Value Columns" }),
|
|
1160
|
+
/* @__PURE__ */ jsxs(
|
|
1161
|
+
"select",
|
|
1162
|
+
{
|
|
1163
|
+
value: "",
|
|
1164
|
+
onChange: (e) => e.target.value && addToPivotConfig(
|
|
1165
|
+
"values",
|
|
1166
|
+
e.target.value
|
|
1167
|
+
),
|
|
1168
|
+
className: "w-full text-sm px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100",
|
|
1169
|
+
children: [
|
|
1170
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Add column..." }),
|
|
1171
|
+
headers.filter(
|
|
1172
|
+
(h) => !pivotConfig.values.includes(h)
|
|
1173
|
+
).sort().map((header) => /* @__PURE__ */ jsx("option", { value: header, children: header }, header))
|
|
1174
|
+
]
|
|
1175
|
+
}
|
|
1176
|
+
),
|
|
1177
|
+
pivotConfig.values.map((value, index) => /* @__PURE__ */ jsxs(
|
|
1178
|
+
"div",
|
|
1179
|
+
{
|
|
1180
|
+
className: "flex items-center justify-between mt-1 px-2 py-1 bg-blue-100 dark:bg-blue-900/30 rounded text-sm",
|
|
1181
|
+
children: [
|
|
1182
|
+
/* @__PURE__ */ jsx("span", { className: "text-blue-800 dark:text-blue-200 flex-1", children: value }),
|
|
1183
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
|
|
1184
|
+
/* @__PURE__ */ jsx(
|
|
1185
|
+
"button",
|
|
1186
|
+
{
|
|
1187
|
+
onClick: () => moveColumnUp(
|
|
1188
|
+
"values",
|
|
1189
|
+
index
|
|
1190
|
+
),
|
|
1191
|
+
disabled: index === 0,
|
|
1192
|
+
className: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1193
|
+
children: /* @__PURE__ */ jsx(ArrowUp, { size: 14 })
|
|
1194
|
+
}
|
|
1195
|
+
),
|
|
1196
|
+
/* @__PURE__ */ jsx(
|
|
1197
|
+
"button",
|
|
1198
|
+
{
|
|
1199
|
+
onClick: () => moveColumnDown(
|
|
1200
|
+
"values",
|
|
1201
|
+
index
|
|
1202
|
+
),
|
|
1203
|
+
disabled: index === pivotConfig.values.length - 1,
|
|
1204
|
+
className: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1205
|
+
children: /* @__PURE__ */ jsx(ArrowDown, { size: 14 })
|
|
1206
|
+
}
|
|
1207
|
+
),
|
|
1208
|
+
/* @__PURE__ */ jsx(
|
|
1209
|
+
"button",
|
|
1210
|
+
{
|
|
1211
|
+
onClick: () => removeFromPivotConfig(
|
|
1212
|
+
"values",
|
|
1213
|
+
value
|
|
1214
|
+
),
|
|
1215
|
+
className: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200",
|
|
1216
|
+
children: /* @__PURE__ */ jsx(X, { size: 14 })
|
|
1217
|
+
}
|
|
1218
|
+
)
|
|
1219
|
+
] })
|
|
1220
|
+
]
|
|
1221
|
+
},
|
|
1222
|
+
value
|
|
1223
|
+
))
|
|
1224
|
+
] }),
|
|
1225
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1226
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300", children: "Aggregation" }),
|
|
1227
|
+
/* @__PURE__ */ jsxs(
|
|
1228
|
+
"select",
|
|
1229
|
+
{
|
|
1230
|
+
value: pivotConfig.aggregation,
|
|
1231
|
+
onChange: (e) => updatePivotConfig(
|
|
1232
|
+
"aggregation",
|
|
1233
|
+
e.target.value
|
|
1234
|
+
),
|
|
1235
|
+
className: "w-full text-sm px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100",
|
|
1236
|
+
children: [
|
|
1237
|
+
/* @__PURE__ */ jsx("option", { value: "count", children: "Count" }),
|
|
1238
|
+
/* @__PURE__ */ jsx("option", { value: "sum", children: "Sum" }),
|
|
1239
|
+
/* @__PURE__ */ jsx("option", { value: "avg", children: "Average" }),
|
|
1240
|
+
/* @__PURE__ */ jsx("option", { value: "min", children: "Minimum" }),
|
|
1241
|
+
/* @__PURE__ */ jsx("option", { value: "max", children: "Maximum" })
|
|
1242
|
+
]
|
|
1243
|
+
}
|
|
1244
|
+
)
|
|
1245
|
+
] }),
|
|
1246
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1247
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300", children: "Display Mode" }),
|
|
1248
|
+
/* @__PURE__ */ jsxs(
|
|
1249
|
+
"select",
|
|
1250
|
+
{
|
|
1251
|
+
value: showGroupedView ? "grouped" : "aggregated",
|
|
1252
|
+
onChange: (e) => setShowGroupedView(
|
|
1253
|
+
e.target.value === "grouped"
|
|
1254
|
+
),
|
|
1255
|
+
className: "w-full text-sm px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100",
|
|
1256
|
+
children: [
|
|
1257
|
+
/* @__PURE__ */ jsx("option", { value: "aggregated", children: "Aggregated View" }),
|
|
1258
|
+
/* @__PURE__ */ jsx("option", { value: "grouped", children: "Grouped View" })
|
|
1259
|
+
]
|
|
1260
|
+
}
|
|
1261
|
+
)
|
|
1262
|
+
] })
|
|
1263
|
+
] })
|
|
1264
|
+
] }) })
|
|
1265
|
+
] });
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// app/components/chat-content.tsx
|
|
1269
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1270
|
+
var ChatInput = lazy(() => import("./chat-input-L5LIF4NP.mjs"));
|
|
1271
|
+
var DynamicChatMessage = lazy(() => import("./chat-message-FTACCRMO.mjs"));
|
|
1272
|
+
var SCROLL_BOTTOM_THRESHOLD = 50;
|
|
1273
|
+
function ChatContent({
|
|
1274
|
+
conversation,
|
|
1275
|
+
agentId,
|
|
1276
|
+
debug = false,
|
|
1277
|
+
onConversationUpdate,
|
|
1278
|
+
theme,
|
|
1279
|
+
onAppStateChange,
|
|
1280
|
+
onSidebarToggle,
|
|
1281
|
+
isReadOnly = false,
|
|
1282
|
+
onThreadCreated
|
|
1283
|
+
}) {
|
|
1284
|
+
const messagesEndRef = useRef(null);
|
|
1285
|
+
const scrollContainerRef = useRef(null);
|
|
1286
|
+
const [messages, setMessages] = useState2(
|
|
1287
|
+
conversation.messages || []
|
|
1288
|
+
);
|
|
1289
|
+
const [isStreaming, setIsStreaming] = useState2(false);
|
|
1290
|
+
const [streamingMessage, setStreamingMessage] = useState2(
|
|
1291
|
+
null
|
|
1292
|
+
);
|
|
1293
|
+
const streamingMessageRef = useRef(null);
|
|
1294
|
+
const [errorDialog, setErrorDialog] = useState2(null);
|
|
1295
|
+
const [showScrollToBottomButton, setShowScrollToBottomButton] = useState2(false);
|
|
1296
|
+
const [userHasScrolledUp, setUserHasScrolledUp] = useState2(false);
|
|
1297
|
+
const [openApp, setOpenApp] = useState2(null);
|
|
1298
|
+
const [openAnalytic, setOpenAnalytic] = useState2(null);
|
|
1299
|
+
const [chatWidth, setChatWidth] = useState2(30);
|
|
1300
|
+
const [isResizingChatApp, setIsResizingChatApp] = useState2(false);
|
|
1301
|
+
const chatAppResizeHandleRef = useRef(null);
|
|
1302
|
+
const chatAppContainerRef = useRef(null);
|
|
1303
|
+
const minChatWidth = 20;
|
|
1304
|
+
const maxChatWidth = 80;
|
|
1305
|
+
const hasMessages = messages.length > 0 || !!streamingMessage;
|
|
1306
|
+
const hasOpenPanel = !!openApp || !!openAnalytic;
|
|
1307
|
+
const isWaitingForInput = useMemo2(() => {
|
|
1308
|
+
return conversation.waiting === true || messages.some((message) => message.waiting === true);
|
|
1309
|
+
}, [conversation.waiting, messages]);
|
|
1310
|
+
const allMessages = useMemo2(() => {
|
|
1311
|
+
const combined = [...messages];
|
|
1312
|
+
if (streamingMessage) {
|
|
1313
|
+
combined.push(streamingMessage);
|
|
1314
|
+
}
|
|
1315
|
+
return combined.sort((a, b) => {
|
|
1316
|
+
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
|
1317
|
+
});
|
|
1318
|
+
}, [messages, streamingMessage]);
|
|
1319
|
+
const scrollToBottom = useCallback(
|
|
1320
|
+
(behavior = "smooth") => {
|
|
1321
|
+
const container = scrollContainerRef.current;
|
|
1322
|
+
const messagesEnd = messagesEndRef.current;
|
|
1323
|
+
if (messagesEnd) {
|
|
1324
|
+
messagesEnd.scrollIntoView({ behavior, block: "end" });
|
|
1325
|
+
}
|
|
1326
|
+
if (container) {
|
|
1327
|
+
container.scrollTop = container.scrollHeight;
|
|
1328
|
+
}
|
|
1329
|
+
setTimeout(() => {
|
|
1330
|
+
const scrollContainer = document.querySelector(
|
|
1331
|
+
'[data-main-chat-scroll="true"]'
|
|
1332
|
+
);
|
|
1333
|
+
if (scrollContainer) {
|
|
1334
|
+
scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
|
1335
|
+
}
|
|
1336
|
+
}, 10);
|
|
1337
|
+
setShowScrollToBottomButton(false);
|
|
1338
|
+
setUserHasScrolledUp(false);
|
|
1339
|
+
},
|
|
1340
|
+
[]
|
|
1341
|
+
);
|
|
1342
|
+
useEffect2(() => {
|
|
1343
|
+
const container = scrollContainerRef.current;
|
|
1344
|
+
if (!container) return;
|
|
1345
|
+
const handleScroll = () => {
|
|
1346
|
+
const isAtBottom = container.scrollHeight - container.scrollTop - container.clientHeight < SCROLL_BOTTOM_THRESHOLD;
|
|
1347
|
+
const isContentScrollable = container.scrollHeight > container.clientHeight;
|
|
1348
|
+
if (isAtBottom) {
|
|
1349
|
+
setShowScrollToBottomButton(false);
|
|
1350
|
+
setUserHasScrolledUp(false);
|
|
1351
|
+
} else {
|
|
1352
|
+
setUserHasScrolledUp(true);
|
|
1353
|
+
if (isContentScrollable) {
|
|
1354
|
+
setShowScrollToBottomButton(true);
|
|
1355
|
+
} else {
|
|
1356
|
+
setShowScrollToBottomButton(false);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
container.addEventListener("scroll", handleScroll);
|
|
1361
|
+
handleScroll();
|
|
1362
|
+
return () => container.removeEventListener("scroll", handleScroll);
|
|
1363
|
+
}, [allMessages]);
|
|
1364
|
+
useEffect2(() => {
|
|
1365
|
+
const container = scrollContainerRef.current;
|
|
1366
|
+
if (!container || !hasMessages) {
|
|
1367
|
+
setShowScrollToBottomButton(false);
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
if (!userHasScrolledUp) {
|
|
1371
|
+
scrollToBottom("smooth");
|
|
1372
|
+
} else {
|
|
1373
|
+
const isContentScrollable = container.scrollHeight > container.clientHeight;
|
|
1374
|
+
if (isContentScrollable) {
|
|
1375
|
+
setShowScrollToBottomButton(true);
|
|
1376
|
+
} else {
|
|
1377
|
+
setShowScrollToBottomButton(false);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}, [allMessages, scrollToBottom, userHasScrolledUp, hasMessages]);
|
|
1381
|
+
useEffect2(() => {
|
|
1382
|
+
setShowScrollToBottomButton(false);
|
|
1383
|
+
setUserHasScrolledUp(false);
|
|
1384
|
+
const timeoutId = setTimeout(() => {
|
|
1385
|
+
scrollToBottom("auto");
|
|
1386
|
+
}, 100);
|
|
1387
|
+
return () => clearTimeout(timeoutId);
|
|
1388
|
+
}, [conversation.id, scrollToBottom]);
|
|
1389
|
+
useEffect2(() => {
|
|
1390
|
+
setMessages(conversation.messages || []);
|
|
1391
|
+
}, [conversation.id, conversation.messages]);
|
|
1392
|
+
useEffect2(() => {
|
|
1393
|
+
if (openApp) {
|
|
1394
|
+
setOpenApp(null);
|
|
1395
|
+
}
|
|
1396
|
+
if (openAnalytic) {
|
|
1397
|
+
setOpenAnalytic(null);
|
|
1398
|
+
}
|
|
1399
|
+
onAppStateChange == null ? void 0 : onAppStateChange(false);
|
|
1400
|
+
}, [conversation.id]);
|
|
1401
|
+
useEffect2(() => {
|
|
1402
|
+
if (messages.length > 0 && !userHasScrolledUp) {
|
|
1403
|
+
const timeoutId = setTimeout(() => {
|
|
1404
|
+
scrollToBottom("smooth");
|
|
1405
|
+
}, 50);
|
|
1406
|
+
return () => clearTimeout(timeoutId);
|
|
1407
|
+
}
|
|
1408
|
+
}, [messages.length, scrollToBottom, userHasScrolledUp]);
|
|
1409
|
+
useEffect2(() => {
|
|
1410
|
+
if (conversation.messages && conversation.messages.length > 0) {
|
|
1411
|
+
const timeouts = [
|
|
1412
|
+
setTimeout(() => scrollToBottom("auto"), 50),
|
|
1413
|
+
setTimeout(() => scrollToBottom("auto"), 200),
|
|
1414
|
+
setTimeout(() => scrollToBottom("auto"), 500)
|
|
1415
|
+
];
|
|
1416
|
+
return () => timeouts.forEach(clearTimeout);
|
|
1417
|
+
}
|
|
1418
|
+
}, [conversation.messages, conversation.id, scrollToBottom]);
|
|
1419
|
+
useEffect2(() => {
|
|
1420
|
+
if (hasMessages) {
|
|
1421
|
+
const timeoutId = setTimeout(() => {
|
|
1422
|
+
scrollToBottom("auto");
|
|
1423
|
+
}, 100);
|
|
1424
|
+
return () => clearTimeout(timeoutId);
|
|
1425
|
+
}
|
|
1426
|
+
}, [hasMessages, scrollToBottom]);
|
|
1427
|
+
useEffect2(() => {
|
|
1428
|
+
return () => {
|
|
1429
|
+
setIsStreaming(false);
|
|
1430
|
+
setStreamingMessage(null);
|
|
1431
|
+
streamingMessageRef.current = null;
|
|
1432
|
+
};
|
|
1433
|
+
}, []);
|
|
1434
|
+
const handleAddUserMessage = useCallback(
|
|
1435
|
+
(message) => {
|
|
1436
|
+
setMessages((prev) => [...prev, message]);
|
|
1437
|
+
setUserHasScrolledUp(false);
|
|
1438
|
+
setShowScrollToBottomButton(false);
|
|
1439
|
+
setTimeout(() => {
|
|
1440
|
+
scrollToBottom("smooth");
|
|
1441
|
+
}, 50);
|
|
1442
|
+
},
|
|
1443
|
+
[scrollToBottom]
|
|
1444
|
+
);
|
|
1445
|
+
const handleStreamStart = useCallback(() => {
|
|
1446
|
+
const newStreamingMessage = {
|
|
1447
|
+
id: `stream-${Date.now()}`,
|
|
1448
|
+
role: "assistant",
|
|
1449
|
+
content: "",
|
|
1450
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1451
|
+
};
|
|
1452
|
+
streamingMessageRef.current = {
|
|
1453
|
+
id: newStreamingMessage.id,
|
|
1454
|
+
role: newStreamingMessage.role,
|
|
1455
|
+
createdAt: newStreamingMessage.createdAt
|
|
1456
|
+
};
|
|
1457
|
+
setStreamingMessage(newStreamingMessage);
|
|
1458
|
+
setIsStreaming(true);
|
|
1459
|
+
}, []);
|
|
1460
|
+
const handleToolExecutionStart = useCallback((toolName) => {
|
|
1461
|
+
setStreamingMessage((prev) => {
|
|
1462
|
+
if (!prev) return null;
|
|
1463
|
+
return __spreadProps(__spreadValues({}, prev), {
|
|
1464
|
+
isToolExecuting: true,
|
|
1465
|
+
executingToolName: toolName
|
|
1466
|
+
});
|
|
1467
|
+
});
|
|
1468
|
+
}, []);
|
|
1469
|
+
const handleToolExecutionEnd = useCallback(() => {
|
|
1470
|
+
setStreamingMessage((prev) => {
|
|
1471
|
+
if (!prev) return null;
|
|
1472
|
+
return __spreadProps(__spreadValues({}, prev), {
|
|
1473
|
+
isToolExecuting: false,
|
|
1474
|
+
executingToolName: void 0
|
|
1475
|
+
});
|
|
1476
|
+
});
|
|
1477
|
+
}, []);
|
|
1478
|
+
const pendingChunksRef = useRef("");
|
|
1479
|
+
const throttleTimerRef = useRef(null);
|
|
1480
|
+
const handleStreamUpdate = useCallback((chunk) => {
|
|
1481
|
+
pendingChunksRef.current += chunk;
|
|
1482
|
+
if (throttleTimerRef.current) {
|
|
1483
|
+
clearTimeout(throttleTimerRef.current);
|
|
1484
|
+
}
|
|
1485
|
+
throttleTimerRef.current = setTimeout(() => {
|
|
1486
|
+
const chunksToAdd = pendingChunksRef.current;
|
|
1487
|
+
pendingChunksRef.current = "";
|
|
1488
|
+
setStreamingMessage((prev) => {
|
|
1489
|
+
if (!prev) return null;
|
|
1490
|
+
const newContent = (prev.content || "") + chunksToAdd;
|
|
1491
|
+
return __spreadProps(__spreadValues({}, prev), { content: newContent });
|
|
1492
|
+
});
|
|
1493
|
+
}, 50);
|
|
1494
|
+
}, []);
|
|
1495
|
+
const handleStreamEnd = useCallback(
|
|
1496
|
+
async (finalContent, metadata) => {
|
|
1497
|
+
if (throttleTimerRef.current) {
|
|
1498
|
+
clearTimeout(throttleTimerRef.current);
|
|
1499
|
+
throttleTimerRef.current = null;
|
|
1500
|
+
}
|
|
1501
|
+
if (pendingChunksRef.current) {
|
|
1502
|
+
const chunksToAdd = pendingChunksRef.current;
|
|
1503
|
+
pendingChunksRef.current = "";
|
|
1504
|
+
setStreamingMessage((prev) => {
|
|
1505
|
+
if (!prev) return null;
|
|
1506
|
+
return __spreadProps(__spreadValues({}, prev), {
|
|
1507
|
+
content: (prev.content || "") + chunksToAdd
|
|
1508
|
+
});
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
const streamDetails = streamingMessageRef.current;
|
|
1512
|
+
let messageToAdd = null;
|
|
1513
|
+
if (streamDetails && (finalContent == null ? void 0 : finalContent.trim())) {
|
|
1514
|
+
const hasFormWidget = /```widget:form\n[\s\S]*?\n```/.test(
|
|
1515
|
+
finalContent
|
|
1516
|
+
);
|
|
1517
|
+
const hasDecisionWidget = /```(?:widget:decision|decision)\n[\s\S]*?\n```/.test(
|
|
1518
|
+
finalContent
|
|
1519
|
+
);
|
|
1520
|
+
messageToAdd = __spreadValues(__spreadValues({
|
|
1521
|
+
id: `assistant-${Date.now()}`,
|
|
1522
|
+
role: streamDetails.role,
|
|
1523
|
+
createdAt: streamDetails.createdAt,
|
|
1524
|
+
content: finalContent.trim()
|
|
1525
|
+
}, (hasFormWidget || hasDecisionWidget) && {
|
|
1526
|
+
waiting: true
|
|
1527
|
+
}), metadata && {
|
|
1528
|
+
metadata: {
|
|
1529
|
+
logId: metadata.logId,
|
|
1530
|
+
steps: metadata.steps
|
|
1531
|
+
}
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
if (messageToAdd) {
|
|
1535
|
+
setMessages((prev) => [...prev, messageToAdd]);
|
|
1536
|
+
}
|
|
1537
|
+
setIsStreaming(false);
|
|
1538
|
+
setStreamingMessage(null);
|
|
1539
|
+
streamingMessageRef.current = null;
|
|
1540
|
+
},
|
|
1541
|
+
[]
|
|
1542
|
+
);
|
|
1543
|
+
const handleError = useCallback((details) => {
|
|
1544
|
+
console.log("handleError called with:", details);
|
|
1545
|
+
setIsStreaming(false);
|
|
1546
|
+
setStreamingMessage(null);
|
|
1547
|
+
streamingMessageRef.current = null;
|
|
1548
|
+
setErrorDialog(details);
|
|
1549
|
+
setShowScrollToBottomButton(false);
|
|
1550
|
+
}, []);
|
|
1551
|
+
const handleStopStreaming = useCallback(() => {
|
|
1552
|
+
console.log("Stop streaming requested");
|
|
1553
|
+
setIsStreaming(false);
|
|
1554
|
+
setStreamingMessage(null);
|
|
1555
|
+
streamingMessageRef.current = null;
|
|
1556
|
+
}, []);
|
|
1557
|
+
const handleAppOpen = useCallback(
|
|
1558
|
+
(name, url) => {
|
|
1559
|
+
setOpenAnalytic(null);
|
|
1560
|
+
setOpenApp({ name, url });
|
|
1561
|
+
onAppStateChange == null ? void 0 : onAppStateChange(true);
|
|
1562
|
+
},
|
|
1563
|
+
[onAppStateChange]
|
|
1564
|
+
);
|
|
1565
|
+
const handleAnalyticOpen = useCallback(
|
|
1566
|
+
(name, data) => {
|
|
1567
|
+
setOpenApp(null);
|
|
1568
|
+
setOpenAnalytic({ name, data });
|
|
1569
|
+
onAppStateChange == null ? void 0 : onAppStateChange(true);
|
|
1570
|
+
onSidebarToggle == null ? void 0 : onSidebarToggle();
|
|
1571
|
+
},
|
|
1572
|
+
[onAppStateChange, onSidebarToggle]
|
|
1573
|
+
);
|
|
1574
|
+
const handlePanelClose = useCallback(() => {
|
|
1575
|
+
setOpenApp(null);
|
|
1576
|
+
setOpenAnalytic(null);
|
|
1577
|
+
onAppStateChange == null ? void 0 : onAppStateChange(false);
|
|
1578
|
+
}, [onAppStateChange]);
|
|
1579
|
+
const handleWidgetSubmit = useCallback(
|
|
1580
|
+
async (data, actionId, messageId, widgetId) => {
|
|
1581
|
+
try {
|
|
1582
|
+
setMessages(
|
|
1583
|
+
(prev) => prev.map(
|
|
1584
|
+
(message) => message.id === messageId ? __spreadProps(__spreadValues({}, message), { waiting: false }) : message
|
|
1585
|
+
)
|
|
1586
|
+
);
|
|
1587
|
+
if (conversation.waiting === true) {
|
|
1588
|
+
}
|
|
1589
|
+
const stepData = {
|
|
1590
|
+
action: actionId,
|
|
1591
|
+
data,
|
|
1592
|
+
id: widgetId,
|
|
1593
|
+
// Use the actual widget ID passed from the form
|
|
1594
|
+
messageId
|
|
1595
|
+
// Backend will use this to update message.waiting = false
|
|
1596
|
+
};
|
|
1597
|
+
if (agentId) {
|
|
1598
|
+
handleStreamStart();
|
|
1599
|
+
const { stream } = await chatClient.messages.sendMessage(
|
|
1600
|
+
agentId,
|
|
1601
|
+
conversation.id,
|
|
1602
|
+
"",
|
|
1603
|
+
//`Processing form submission...`, // Simple processing message
|
|
1604
|
+
messages,
|
|
1605
|
+
new AbortController().signal,
|
|
1606
|
+
debug,
|
|
1607
|
+
stepData,
|
|
1608
|
+
conversation.isTemporary
|
|
1609
|
+
// Pass isTemporary flag
|
|
1610
|
+
);
|
|
1611
|
+
const reader = stream.getReader();
|
|
1612
|
+
const decoder = new TextDecoder();
|
|
1613
|
+
let done = false;
|
|
1614
|
+
let accumulatedResponse = "";
|
|
1615
|
+
while (!done) {
|
|
1616
|
+
const { value, done: readerDone } = await reader.read();
|
|
1617
|
+
done = readerDone;
|
|
1618
|
+
if (value) {
|
|
1619
|
+
const chunk = decoder.decode(value, {
|
|
1620
|
+
stream: !done
|
|
1621
|
+
});
|
|
1622
|
+
if (chunk) {
|
|
1623
|
+
accumulatedResponse += chunk;
|
|
1624
|
+
handleStreamUpdate(chunk);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
handleStreamEnd(accumulatedResponse);
|
|
1629
|
+
if (onConversationUpdate) {
|
|
1630
|
+
try {
|
|
1631
|
+
await onConversationUpdate();
|
|
1632
|
+
} catch (error) {
|
|
1633
|
+
console.error(
|
|
1634
|
+
"Failed to refresh conversation after form submission:",
|
|
1635
|
+
error
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
} catch (error) {
|
|
1641
|
+
console.error("Error submitting form:", error);
|
|
1642
|
+
handleError({
|
|
1643
|
+
title: "Form Submission Error",
|
|
1644
|
+
message: "Failed to submit form data. Please try again."
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
[
|
|
1649
|
+
agentId,
|
|
1650
|
+
conversation.id,
|
|
1651
|
+
messages,
|
|
1652
|
+
debug,
|
|
1653
|
+
handleStreamStart,
|
|
1654
|
+
handleStreamUpdate,
|
|
1655
|
+
handleStreamEnd,
|
|
1656
|
+
handleError
|
|
1657
|
+
]
|
|
1658
|
+
);
|
|
1659
|
+
const handleWidgetCancel = useCallback(
|
|
1660
|
+
async (messageId, widgetId) => {
|
|
1661
|
+
try {
|
|
1662
|
+
setMessages(
|
|
1663
|
+
(prev) => prev.map(
|
|
1664
|
+
(message) => message.id === messageId ? __spreadProps(__spreadValues({}, message), { waiting: false }) : message
|
|
1665
|
+
)
|
|
1666
|
+
);
|
|
1667
|
+
const stepData = {
|
|
1668
|
+
action: "cancel",
|
|
1669
|
+
data: null,
|
|
1670
|
+
// No form data for cancellation
|
|
1671
|
+
id: widgetId,
|
|
1672
|
+
messageId
|
|
1673
|
+
};
|
|
1674
|
+
if (agentId) {
|
|
1675
|
+
handleStreamStart();
|
|
1676
|
+
const { stream } = await chatClient.messages.sendMessage(
|
|
1677
|
+
agentId,
|
|
1678
|
+
conversation.id,
|
|
1679
|
+
"",
|
|
1680
|
+
//cancelMessage.content,
|
|
1681
|
+
messages,
|
|
1682
|
+
new AbortController().signal,
|
|
1683
|
+
debug,
|
|
1684
|
+
stepData,
|
|
1685
|
+
conversation.isTemporary
|
|
1686
|
+
// Pass isTemporary flag
|
|
1687
|
+
);
|
|
1688
|
+
const reader = stream.getReader();
|
|
1689
|
+
const decoder = new TextDecoder();
|
|
1690
|
+
let done = false;
|
|
1691
|
+
let accumulatedResponse = "";
|
|
1692
|
+
while (!done) {
|
|
1693
|
+
const { value, done: readerDone } = await reader.read();
|
|
1694
|
+
done = readerDone;
|
|
1695
|
+
if (value) {
|
|
1696
|
+
const chunk = decoder.decode(value, {
|
|
1697
|
+
stream: !done
|
|
1698
|
+
});
|
|
1699
|
+
if (chunk) {
|
|
1700
|
+
accumulatedResponse += chunk;
|
|
1701
|
+
handleStreamUpdate(chunk);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
handleStreamEnd(accumulatedResponse);
|
|
1706
|
+
if (onConversationUpdate) {
|
|
1707
|
+
try {
|
|
1708
|
+
await onConversationUpdate();
|
|
1709
|
+
} catch (error) {
|
|
1710
|
+
console.error(
|
|
1711
|
+
"Failed to refresh conversation after form cancellation:",
|
|
1712
|
+
error
|
|
1713
|
+
);
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
} catch (error) {
|
|
1718
|
+
console.error("Error cancelling form:", error);
|
|
1719
|
+
handleError({
|
|
1720
|
+
title: "Form Cancellation Error",
|
|
1721
|
+
message: "Failed to cancel form. Please try again."
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
},
|
|
1725
|
+
[
|
|
1726
|
+
agentId,
|
|
1727
|
+
conversation.id,
|
|
1728
|
+
messages,
|
|
1729
|
+
debug,
|
|
1730
|
+
handleStreamStart,
|
|
1731
|
+
handleStreamUpdate,
|
|
1732
|
+
handleStreamEnd,
|
|
1733
|
+
handleError,
|
|
1734
|
+
onConversationUpdate
|
|
1735
|
+
]
|
|
1736
|
+
);
|
|
1737
|
+
const startResizingChatApp = useCallback(
|
|
1738
|
+
(e) => {
|
|
1739
|
+
e.preventDefault();
|
|
1740
|
+
setIsResizingChatApp(true);
|
|
1741
|
+
},
|
|
1742
|
+
[]
|
|
1743
|
+
);
|
|
1744
|
+
useEffect2(() => {
|
|
1745
|
+
const handleMouseMove = (e) => {
|
|
1746
|
+
if (!isResizingChatApp) return;
|
|
1747
|
+
const mainContainer = chatAppContainerRef.current;
|
|
1748
|
+
if (!mainContainer) return;
|
|
1749
|
+
const containerRect = mainContainer.getBoundingClientRect();
|
|
1750
|
+
const containerWidth = containerRect.width;
|
|
1751
|
+
const relativeX = e.clientX - containerRect.left;
|
|
1752
|
+
const newChatWidthPercent = relativeX / containerWidth * 100;
|
|
1753
|
+
const clampedWidth = Math.max(
|
|
1754
|
+
minChatWidth,
|
|
1755
|
+
Math.min(newChatWidthPercent, maxChatWidth)
|
|
1756
|
+
);
|
|
1757
|
+
setChatWidth(clampedWidth);
|
|
1758
|
+
};
|
|
1759
|
+
const handleMouseUp = () => {
|
|
1760
|
+
setIsResizingChatApp(false);
|
|
1761
|
+
};
|
|
1762
|
+
if (isResizingChatApp) {
|
|
1763
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
1764
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
1765
|
+
document.body.classList.add("resize-active");
|
|
1766
|
+
document.body.style.cursor = "col-resize";
|
|
1767
|
+
}
|
|
1768
|
+
return () => {
|
|
1769
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
1770
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
1771
|
+
document.body.classList.remove("resize-active");
|
|
1772
|
+
document.body.style.cursor = "";
|
|
1773
|
+
};
|
|
1774
|
+
}, [isResizingChatApp, minChatWidth, maxChatWidth]);
|
|
1775
|
+
const ErrorDialog = () => {
|
|
1776
|
+
if (!errorDialog) return null;
|
|
1777
|
+
return /* @__PURE__ */ jsx2("div", { className: "fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ jsxs2("div", { className: "bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 max-w-sm w-full", children: [
|
|
1778
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex justify-between items-center mb-4", children: [
|
|
1779
|
+
/* @__PURE__ */ jsx2("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: errorDialog.title }),
|
|
1780
|
+
/* @__PURE__ */ jsx2(
|
|
1781
|
+
"button",
|
|
1782
|
+
{
|
|
1783
|
+
onClick: () => setErrorDialog(null),
|
|
1784
|
+
className: "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300",
|
|
1785
|
+
"aria-label": "Close error dialog",
|
|
1786
|
+
children: /* @__PURE__ */ jsx2(X2, { size: 20 })
|
|
1787
|
+
}
|
|
1788
|
+
)
|
|
1789
|
+
] }),
|
|
1790
|
+
/* @__PURE__ */ jsx2("p", { className: "text-sm text-gray-600 dark:text-gray-300 mb-4", children: errorDialog.message }),
|
|
1791
|
+
/* @__PURE__ */ jsx2("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx2(
|
|
1792
|
+
"button",
|
|
1793
|
+
{
|
|
1794
|
+
onClick: () => setErrorDialog(null),
|
|
1795
|
+
className: "px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800",
|
|
1796
|
+
children: "OK"
|
|
1797
|
+
}
|
|
1798
|
+
) })
|
|
1799
|
+
] }) });
|
|
1800
|
+
};
|
|
1801
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1802
|
+
/* @__PURE__ */ jsx2("style", { children: `
|
|
1803
|
+
.resize-active {
|
|
1804
|
+
user-select: none;
|
|
1805
|
+
}
|
|
1806
|
+
.resize-active * {
|
|
1807
|
+
cursor: col-resize !important;
|
|
1808
|
+
}
|
|
1809
|
+
` }),
|
|
1810
|
+
/* @__PURE__ */ jsxs2(
|
|
1811
|
+
"div",
|
|
1812
|
+
{
|
|
1813
|
+
ref: chatAppContainerRef,
|
|
1814
|
+
className: "flex h-full relative w-full",
|
|
1815
|
+
children: [
|
|
1816
|
+
/* @__PURE__ */ jsx2(ErrorDialog, {}),
|
|
1817
|
+
/* @__PURE__ */ jsxs2(
|
|
1818
|
+
"div",
|
|
1819
|
+
{
|
|
1820
|
+
className: `flex flex-col transition-all duration-300 relative min-w-[500px] ${hasOpenPanel ? "" : "w-full"}`,
|
|
1821
|
+
style: hasOpenPanel ? { width: `${chatWidth}%` } : {},
|
|
1822
|
+
children: [
|
|
1823
|
+
isResizingChatApp && /* @__PURE__ */ jsx2("div", { className: "absolute inset-0 z-40 bg-transparent cursor-col-resize" }),
|
|
1824
|
+
hasMessages ? (
|
|
1825
|
+
// Standard layout with messages and input at bottom
|
|
1826
|
+
/* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1827
|
+
/* @__PURE__ */ jsx2(
|
|
1828
|
+
"div",
|
|
1829
|
+
{
|
|
1830
|
+
ref: scrollContainerRef,
|
|
1831
|
+
className: `flex-1 overflow-y-auto py-6 relative ${hasOpenPanel ? "px-2 sm:px-4" : "px-4 sm:px-8"}`,
|
|
1832
|
+
"data-ref": "scrollContainerRef",
|
|
1833
|
+
"data-main-chat-scroll": "true",
|
|
1834
|
+
children: /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
|
|
1835
|
+
allMessages.map((message, index) => {
|
|
1836
|
+
const isCurrentlyStreaming = isStreaming && (streamingMessage == null ? void 0 : streamingMessage.id) === message.id;
|
|
1837
|
+
return (
|
|
1838
|
+
// @ts-ignore - Typescript doesn't recognize dynamic imports properly
|
|
1839
|
+
/* @__PURE__ */ jsx2(
|
|
1840
|
+
DynamicChatMessage,
|
|
1841
|
+
{
|
|
1842
|
+
message,
|
|
1843
|
+
isStreaming: isCurrentlyStreaming,
|
|
1844
|
+
theme,
|
|
1845
|
+
onAppOpen: handleAppOpen,
|
|
1846
|
+
onAnalyticOpen: handleAnalyticOpen,
|
|
1847
|
+
onWidgetSubmit: handleWidgetSubmit,
|
|
1848
|
+
onWidgetCancel: handleWidgetCancel
|
|
1849
|
+
},
|
|
1850
|
+
message.id || index
|
|
1851
|
+
)
|
|
1852
|
+
);
|
|
1853
|
+
}),
|
|
1854
|
+
/* @__PURE__ */ jsx2(
|
|
1855
|
+
"div",
|
|
1856
|
+
{
|
|
1857
|
+
ref: messagesEndRef,
|
|
1858
|
+
"data-ref": "messagesEndRef"
|
|
1859
|
+
}
|
|
1860
|
+
)
|
|
1861
|
+
] })
|
|
1862
|
+
}
|
|
1863
|
+
),
|
|
1864
|
+
!isReadOnly && /* @__PURE__ */ jsxs2(
|
|
1865
|
+
"div",
|
|
1866
|
+
{
|
|
1867
|
+
className: `border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 ${hasOpenPanel ? "p-2 sm:p-3" : "p-4 sm:p-6"}`,
|
|
1868
|
+
children: [
|
|
1869
|
+
isWaitingForInput && /* @__PURE__ */ jsxs2("div", { className: "mb-4 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg", children: [
|
|
1870
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
1871
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex space-x-1", children: [
|
|
1872
|
+
/* @__PURE__ */ jsx2("div", { className: "w-2 h-2 bg-amber-500 rounded-full animate-pulse" }),
|
|
1873
|
+
/* @__PURE__ */ jsx2(
|
|
1874
|
+
"div",
|
|
1875
|
+
{
|
|
1876
|
+
className: "w-2 h-2 bg-amber-500 rounded-full animate-pulse",
|
|
1877
|
+
style: {
|
|
1878
|
+
animationDelay: "0.2s"
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
),
|
|
1882
|
+
/* @__PURE__ */ jsx2(
|
|
1883
|
+
"div",
|
|
1884
|
+
{
|
|
1885
|
+
className: "w-2 h-2 bg-amber-500 rounded-full animate-pulse",
|
|
1886
|
+
style: {
|
|
1887
|
+
animationDelay: "0.4s"
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
)
|
|
1891
|
+
] }),
|
|
1892
|
+
/* @__PURE__ */ jsx2("p", { className: "text-sm text-amber-800 dark:text-amber-200 font-medium", children: "Waiting for form input..." })
|
|
1893
|
+
] }),
|
|
1894
|
+
/* @__PURE__ */ jsx2("p", { className: "text-xs text-amber-700 dark:text-amber-300 mt-1", children: "Complete the form above or click Cancel to continue without data capture." })
|
|
1895
|
+
] }),
|
|
1896
|
+
showScrollToBottomButton && /* @__PURE__ */ jsx2("div", { className: "absolute bottom-22 left-1/2 transform -translate-x-1/2 z-10", children: /* @__PURE__ */ jsx2(
|
|
1897
|
+
"button",
|
|
1898
|
+
{
|
|
1899
|
+
onClick: () => scrollToBottom("smooth"),
|
|
1900
|
+
className: "bg-indigo-600 hover:bg-indigo-700 text-white rounded-full p-3 shadow-lg transition-opacity duration-200 ease-in-out animate-bounce",
|
|
1901
|
+
title: "Scroll to bottom",
|
|
1902
|
+
children: /* @__PURE__ */ jsx2(ArrowDownCircle, { size: 24 })
|
|
1903
|
+
}
|
|
1904
|
+
) }),
|
|
1905
|
+
/* @__PURE__ */ jsx2(Suspense, { fallback: null, children: /* @__PURE__ */ jsx2(
|
|
1906
|
+
ChatInput,
|
|
1907
|
+
{
|
|
1908
|
+
conversationId: conversation.id,
|
|
1909
|
+
agentId,
|
|
1910
|
+
debug,
|
|
1911
|
+
onAddUserMessage: handleAddUserMessage,
|
|
1912
|
+
onStreamStart: handleStreamStart,
|
|
1913
|
+
onStreamUpdate: handleStreamUpdate,
|
|
1914
|
+
onStreamEnd: handleStreamEnd,
|
|
1915
|
+
onError: handleError,
|
|
1916
|
+
messages,
|
|
1917
|
+
isStreaming,
|
|
1918
|
+
onStopStreaming: handleStopStreaming,
|
|
1919
|
+
disabled: isWaitingForInput,
|
|
1920
|
+
onThreadCreated,
|
|
1921
|
+
onToolExecutionStart: handleToolExecutionStart,
|
|
1922
|
+
onToolExecutionEnd: handleToolExecutionEnd
|
|
1923
|
+
}
|
|
1924
|
+
) })
|
|
1925
|
+
]
|
|
1926
|
+
}
|
|
1927
|
+
)
|
|
1928
|
+
] })
|
|
1929
|
+
) : (
|
|
1930
|
+
// Empty state with centered input - or read-only message for log mode
|
|
1931
|
+
/* @__PURE__ */ jsx2("div", { className: "flex h-full flex-col items-center justify-center p-4", children: isReadOnly ? /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
|
|
1932
|
+
/* @__PURE__ */ jsx2("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: "Log View" }),
|
|
1933
|
+
/* @__PURE__ */ jsx2("p", { className: "text-gray-500 dark:text-gray-400", children: "This log contains no messages." })
|
|
1934
|
+
] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1935
|
+
/* @__PURE__ */ jsxs2("div", { className: "mb-8 flex flex-col items-center", children: [
|
|
1936
|
+
/* @__PURE__ */ jsx2("div", { className: "mb-4 text-gray-500" }),
|
|
1937
|
+
/* @__PURE__ */ jsx2(
|
|
1938
|
+
"h2",
|
|
1939
|
+
{
|
|
1940
|
+
className: `mb-2 font-bold ${hasOpenPanel ? "text-lg" : "text-2xl"}`,
|
|
1941
|
+
children: "How can I help you today?"
|
|
1942
|
+
}
|
|
1943
|
+
),
|
|
1944
|
+
/* @__PURE__ */ jsx2(
|
|
1945
|
+
"p",
|
|
1946
|
+
{
|
|
1947
|
+
className: `text-center text-gray-500 ${hasOpenPanel ? "text-sm max-w-xs" : "max-w-md"}`,
|
|
1948
|
+
children: "Feel free to ask any question you like \u2014 just be precise, as if you're speaking to a real person."
|
|
1949
|
+
}
|
|
1950
|
+
)
|
|
1951
|
+
] }),
|
|
1952
|
+
/* @__PURE__ */ jsx2(
|
|
1953
|
+
"div",
|
|
1954
|
+
{
|
|
1955
|
+
className: `w-full ${hasOpenPanel ? "max-w-sm" : "max-w-2xl"} px-4`,
|
|
1956
|
+
children: /* @__PURE__ */ jsx2(Suspense, { fallback: null, children: /* @__PURE__ */ jsx2(
|
|
1957
|
+
ChatInput,
|
|
1958
|
+
{
|
|
1959
|
+
conversationId: conversation.id,
|
|
1960
|
+
agentId,
|
|
1961
|
+
debug,
|
|
1962
|
+
onAddUserMessage: handleAddUserMessage,
|
|
1963
|
+
onStreamStart: handleStreamStart,
|
|
1964
|
+
onStreamUpdate: handleStreamUpdate,
|
|
1965
|
+
onStreamEnd: handleStreamEnd,
|
|
1966
|
+
onError: handleError,
|
|
1967
|
+
messages: [],
|
|
1968
|
+
isStreaming,
|
|
1969
|
+
onStopStreaming: handleStopStreaming,
|
|
1970
|
+
onThreadCreated,
|
|
1971
|
+
onToolExecutionStart: handleToolExecutionStart,
|
|
1972
|
+
onToolExecutionEnd: handleToolExecutionEnd
|
|
1973
|
+
}
|
|
1974
|
+
) })
|
|
1975
|
+
}
|
|
1976
|
+
)
|
|
1977
|
+
] }) })
|
|
1978
|
+
)
|
|
1979
|
+
]
|
|
1980
|
+
}
|
|
1981
|
+
),
|
|
1982
|
+
hasOpenPanel && /* @__PURE__ */ jsx2(
|
|
1983
|
+
"div",
|
|
1984
|
+
{
|
|
1985
|
+
ref: chatAppResizeHandleRef,
|
|
1986
|
+
onMouseDown: startResizingChatApp,
|
|
1987
|
+
className: `h-full cursor-col-resize transition-all z-50 relative flex items-center justify-center ${isResizingChatApp ? "w-2 bg-indigo-500 dark:bg-indigo-400" : "w-1 hover:w-2 bg-gray-300 dark:bg-gray-700 hover:bg-indigo-500 dark:hover:bg-indigo-400"}`,
|
|
1988
|
+
title: "Drag to resize chat and app areas",
|
|
1989
|
+
children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col space-y-1 opacity-60", children: [
|
|
1990
|
+
/* @__PURE__ */ jsx2("div", { className: "w-0.5 h-0.5 bg-white rounded-full" }),
|
|
1991
|
+
/* @__PURE__ */ jsx2("div", { className: "w-0.5 h-0.5 bg-white rounded-full" }),
|
|
1992
|
+
/* @__PURE__ */ jsx2("div", { className: "w-0.5 h-0.5 bg-white rounded-full" })
|
|
1993
|
+
] })
|
|
1994
|
+
}
|
|
1995
|
+
),
|
|
1996
|
+
hasOpenPanel && /* @__PURE__ */ jsxs2("div", { className: "flex-1 bg-white dark:bg-gray-900 border-l border-gray-200 dark:border-gray-700 flex flex-col w-full", children: [
|
|
1997
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between p-3 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800", children: [
|
|
1998
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center space-x-3", children: [
|
|
1999
|
+
/* @__PURE__ */ jsx2(
|
|
2000
|
+
"div",
|
|
2001
|
+
{
|
|
2002
|
+
className: `w-8 h-8 rounded-lg flex items-center justify-center ${openApp ? "bg-blue-500" : "bg-emerald-500"}`,
|
|
2003
|
+
children: openApp ? /* @__PURE__ */ jsx2(
|
|
2004
|
+
"svg",
|
|
2005
|
+
{
|
|
2006
|
+
className: "w-5 h-5 text-white",
|
|
2007
|
+
fill: "none",
|
|
2008
|
+
stroke: "currentColor",
|
|
2009
|
+
viewBox: "0 0 24 24",
|
|
2010
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2011
|
+
children: /* @__PURE__ */ jsx2(
|
|
2012
|
+
"path",
|
|
2013
|
+
{
|
|
2014
|
+
strokeLinecap: "round",
|
|
2015
|
+
strokeLinejoin: "round",
|
|
2016
|
+
strokeWidth: 2,
|
|
2017
|
+
d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
|
2018
|
+
}
|
|
2019
|
+
)
|
|
2020
|
+
}
|
|
2021
|
+
) : /* @__PURE__ */ jsx2(
|
|
2022
|
+
"svg",
|
|
2023
|
+
{
|
|
2024
|
+
className: "w-5 h-5 text-white",
|
|
2025
|
+
fill: "none",
|
|
2026
|
+
stroke: "currentColor",
|
|
2027
|
+
viewBox: "0 0 24 24",
|
|
2028
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2029
|
+
children: /* @__PURE__ */ jsx2(
|
|
2030
|
+
"path",
|
|
2031
|
+
{
|
|
2032
|
+
strokeLinecap: "round",
|
|
2033
|
+
strokeLinejoin: "round",
|
|
2034
|
+
strokeWidth: 2,
|
|
2035
|
+
d: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
|
2036
|
+
}
|
|
2037
|
+
)
|
|
2038
|
+
}
|
|
2039
|
+
)
|
|
2040
|
+
}
|
|
2041
|
+
),
|
|
2042
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
2043
|
+
/* @__PURE__ */ jsx2("h3", { className: "font-semibold text-gray-800 dark:text-gray-200", children: openApp ? openApp.name : "Analytics Dashboard" }),
|
|
2044
|
+
openAnalytic && /* @__PURE__ */ jsx2("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: openAnalytic.name })
|
|
2045
|
+
] })
|
|
2046
|
+
] }),
|
|
2047
|
+
/* @__PURE__ */ jsx2("div", { className: "flex items-center space-x-2", children: /* @__PURE__ */ jsx2(
|
|
2048
|
+
"button",
|
|
2049
|
+
{
|
|
2050
|
+
onClick: handlePanelClose,
|
|
2051
|
+
className: "p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-lg transition-colors",
|
|
2052
|
+
"aria-label": "Close panel",
|
|
2053
|
+
children: /* @__PURE__ */ jsx2(
|
|
2054
|
+
X2,
|
|
2055
|
+
{
|
|
2056
|
+
size: 20,
|
|
2057
|
+
className: "text-gray-600 dark:text-gray-400"
|
|
2058
|
+
}
|
|
2059
|
+
)
|
|
2060
|
+
}
|
|
2061
|
+
) })
|
|
2062
|
+
] }),
|
|
2063
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 relative h-full", children: [
|
|
2064
|
+
isResizingChatApp && /* @__PURE__ */ jsx2("div", { className: "absolute inset-0 z-50 bg-transparent cursor-col-resize" }),
|
|
2065
|
+
openApp ? /* @__PURE__ */ jsx2(
|
|
2066
|
+
"iframe",
|
|
2067
|
+
{
|
|
2068
|
+
src: openApp.url,
|
|
2069
|
+
className: "w-full h-full border-0",
|
|
2070
|
+
title: openApp.name,
|
|
2071
|
+
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
2072
|
+
sandbox: "allow-scripts allow-same-origin allow-forms allow-popups allow-top-navigation-by-user-activation"
|
|
2073
|
+
}
|
|
2074
|
+
) : openAnalytic ? /* @__PURE__ */ jsx2(
|
|
2075
|
+
AnalyticsPanel,
|
|
2076
|
+
{
|
|
2077
|
+
name: openAnalytic.name,
|
|
2078
|
+
csvData: openAnalytic.data,
|
|
2079
|
+
theme
|
|
2080
|
+
}
|
|
2081
|
+
) : null
|
|
2082
|
+
] })
|
|
2083
|
+
] })
|
|
2084
|
+
]
|
|
2085
|
+
}
|
|
2086
|
+
)
|
|
2087
|
+
] });
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
// app/components/confirmation-dialog.tsx
|
|
2091
|
+
import { createPortal } from "react-dom";
|
|
2092
|
+
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
2093
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2094
|
+
function ConfirmationDialog({
|
|
2095
|
+
isOpen,
|
|
2096
|
+
onClose,
|
|
2097
|
+
onConfirm,
|
|
2098
|
+
title,
|
|
2099
|
+
message,
|
|
2100
|
+
confirmText = "Confirm",
|
|
2101
|
+
cancelText = "Cancel",
|
|
2102
|
+
confirmButtonClass = "bg-red-500 hover:bg-red-600"
|
|
2103
|
+
}) {
|
|
2104
|
+
const [isMounted, setIsMounted] = useState3(false);
|
|
2105
|
+
useEffect3(() => {
|
|
2106
|
+
setIsMounted(true);
|
|
2107
|
+
return () => setIsMounted(false);
|
|
2108
|
+
}, []);
|
|
2109
|
+
if (!isOpen || !isMounted) return null;
|
|
2110
|
+
return createPortal(
|
|
2111
|
+
/* @__PURE__ */ jsxs3("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center", children: [
|
|
2112
|
+
/* @__PURE__ */ jsx3(
|
|
2113
|
+
"div",
|
|
2114
|
+
{
|
|
2115
|
+
className: "fixed inset-0 bg-black/50",
|
|
2116
|
+
onClick: onClose,
|
|
2117
|
+
"aria-hidden": "true"
|
|
2118
|
+
}
|
|
2119
|
+
),
|
|
2120
|
+
/* @__PURE__ */ jsxs3("div", { className: "relative bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 w-80 max-w-[90vw] z-10", children: [
|
|
2121
|
+
/* @__PURE__ */ jsx3("h3", { className: "text-lg font-medium mb-2", children: title }),
|
|
2122
|
+
/* @__PURE__ */ jsx3("p", { className: "text-gray-500 dark:text-gray-400 mb-4", children: message }),
|
|
2123
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex justify-end gap-3", children: [
|
|
2124
|
+
/* @__PURE__ */ jsx3(
|
|
2125
|
+
"button",
|
|
2126
|
+
{
|
|
2127
|
+
onClick: onClose,
|
|
2128
|
+
className: "px-4 py-2 text-sm rounded-md border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer",
|
|
2129
|
+
children: cancelText
|
|
2130
|
+
}
|
|
2131
|
+
),
|
|
2132
|
+
/* @__PURE__ */ jsx3(
|
|
2133
|
+
"button",
|
|
2134
|
+
{
|
|
2135
|
+
onClick: onConfirm,
|
|
2136
|
+
className: `px-4 py-2 text-sm text-white rounded-md cursor-pointer ${confirmButtonClass}`,
|
|
2137
|
+
children: confirmText
|
|
2138
|
+
}
|
|
2139
|
+
)
|
|
2140
|
+
] })
|
|
2141
|
+
] })
|
|
2142
|
+
] }),
|
|
2143
|
+
document.body
|
|
2144
|
+
);
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
// app/components/chat.tsx
|
|
2148
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2149
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect4;
|
|
2150
|
+
function NeptuneChatBot({
|
|
2151
|
+
agentId: propAgentId,
|
|
2152
|
+
debug: propDebug = false,
|
|
2153
|
+
theme: propTheme = "light",
|
|
2154
|
+
localDebug: propLocalDebug
|
|
2155
|
+
}) {
|
|
2156
|
+
const [conversations, setConversations] = useState4([]);
|
|
2157
|
+
const [selectedConversationId, setSelectedConversationId] = useState4(null);
|
|
2158
|
+
const [selectedConversation, setSelectedConversation] = useState4(null);
|
|
2159
|
+
const [isLoading, setIsLoading] = useState4(true);
|
|
2160
|
+
const [isLoadingMessages, setIsLoadingMessages] = useState4(false);
|
|
2161
|
+
const [deleteDialogOpen, setDeleteDialogOpen] = useState4(false);
|
|
2162
|
+
const [pendingDeleteId, setPendingDeleteId] = useState4(null);
|
|
2163
|
+
const [sidebarOpen, setSidebarOpen] = useState4(true);
|
|
2164
|
+
const [titleInput, setTitleInput] = useState4("");
|
|
2165
|
+
const [isSavingTitle, setIsSavingTitle] = useState4(false);
|
|
2166
|
+
const [isRenameDialogOpen, setIsRenameDialogOpen] = useState4(false);
|
|
2167
|
+
const [conversationToRename, setConversationToRename] = useState4(null);
|
|
2168
|
+
const [theme, setTheme] = useState4(propTheme);
|
|
2169
|
+
const [agentId] = useState4(propAgentId);
|
|
2170
|
+
const [debug] = useState4(propDebug);
|
|
2171
|
+
const [initialAssistantIdChecked] = useState4(true);
|
|
2172
|
+
const [initialLoadComplete, setInitialLoadComplete] = useState4(false);
|
|
2173
|
+
const [openMenuId, setOpenMenuId] = useState4(null);
|
|
2174
|
+
const [isCreatingConversation, setIsCreatingConversation] = useState4(false);
|
|
2175
|
+
const [sidebarWidth, setSidebarWidth] = useState4(250);
|
|
2176
|
+
const [isResizing, setIsResizing] = useState4(false);
|
|
2177
|
+
const [hasOpenApp, setHasOpenApp] = useState4(false);
|
|
2178
|
+
const titleInputRef = useRef2(null);
|
|
2179
|
+
const resizeHandleRef = useRef2(null);
|
|
2180
|
+
const hasFetchedRef = useRef2(false);
|
|
2181
|
+
const minSidebarWidth = 200;
|
|
2182
|
+
const maxSidebarWidth = 600;
|
|
2183
|
+
useEffect4(() => {
|
|
2184
|
+
if (propLocalDebug) {
|
|
2185
|
+
configureChatClient(propLocalDebug);
|
|
2186
|
+
}
|
|
2187
|
+
}, [propLocalDebug]);
|
|
2188
|
+
const applyTheme = useCallback2((newTheme) => {
|
|
2189
|
+
document.documentElement.classList.remove("dark");
|
|
2190
|
+
document.documentElement.classList.remove("light");
|
|
2191
|
+
if (newTheme === "dark") {
|
|
2192
|
+
document.documentElement.classList.add("dark");
|
|
2193
|
+
}
|
|
2194
|
+
localStorage.setItem("theme", newTheme);
|
|
2195
|
+
}, []);
|
|
2196
|
+
useEffect4(() => {
|
|
2197
|
+
applyTheme(theme);
|
|
2198
|
+
}, [theme, applyTheme]);
|
|
2199
|
+
const handleAppStateChange = useCallback2((hasApp) => {
|
|
2200
|
+
setHasOpenApp(hasApp);
|
|
2201
|
+
}, []);
|
|
2202
|
+
const toggleTheme = () => {
|
|
2203
|
+
const newTheme = theme === "dark" ? "light" : "dark";
|
|
2204
|
+
setTheme(newTheme);
|
|
2205
|
+
applyTheme(newTheme);
|
|
2206
|
+
document.body.style.opacity = "0.99";
|
|
2207
|
+
setTimeout(() => {
|
|
2208
|
+
document.body.style.opacity = "1";
|
|
2209
|
+
}, 1);
|
|
2210
|
+
};
|
|
2211
|
+
const startResizing = (e) => {
|
|
2212
|
+
e.preventDefault();
|
|
2213
|
+
setIsResizing(true);
|
|
2214
|
+
};
|
|
2215
|
+
useEffect4(() => {
|
|
2216
|
+
const handleMouseMove = (e) => {
|
|
2217
|
+
if (!isResizing) return;
|
|
2218
|
+
const newWidth = e.clientX;
|
|
2219
|
+
const clampedWidth = Math.max(
|
|
2220
|
+
minSidebarWidth,
|
|
2221
|
+
Math.min(newWidth, maxSidebarWidth)
|
|
2222
|
+
);
|
|
2223
|
+
setSidebarWidth(clampedWidth);
|
|
2224
|
+
};
|
|
2225
|
+
const handleMouseUp = () => {
|
|
2226
|
+
setIsResizing(false);
|
|
2227
|
+
};
|
|
2228
|
+
if (isResizing) {
|
|
2229
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
2230
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
2231
|
+
document.body.classList.add("resize-active");
|
|
2232
|
+
}
|
|
2233
|
+
return () => {
|
|
2234
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
2235
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
2236
|
+
document.body.classList.remove("resize-active");
|
|
2237
|
+
};
|
|
2238
|
+
}, [isResizing]);
|
|
2239
|
+
const categorizeConversationsByDate = useCallback2(
|
|
2240
|
+
(conversations2) => {
|
|
2241
|
+
const now = /* @__PURE__ */ new Date();
|
|
2242
|
+
const today = new Date(
|
|
2243
|
+
now.getFullYear(),
|
|
2244
|
+
now.getMonth(),
|
|
2245
|
+
now.getDate()
|
|
2246
|
+
);
|
|
2247
|
+
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1e3);
|
|
2248
|
+
const last7Days = new Date(
|
|
2249
|
+
today.getTime() - 7 * 24 * 60 * 60 * 1e3
|
|
2250
|
+
);
|
|
2251
|
+
const last30Days = new Date(
|
|
2252
|
+
today.getTime() - 30 * 24 * 60 * 60 * 1e3
|
|
2253
|
+
);
|
|
2254
|
+
const groups = {
|
|
2255
|
+
Today: [],
|
|
2256
|
+
Yesterday: [],
|
|
2257
|
+
"Last 7 Days": [],
|
|
2258
|
+
"Last 30 Days": [],
|
|
2259
|
+
Older: []
|
|
2260
|
+
};
|
|
2261
|
+
conversations2.forEach((conversation) => {
|
|
2262
|
+
try {
|
|
2263
|
+
const convDate = new Date(parseInt(conversation.updatedAt));
|
|
2264
|
+
if (convDate >= today) {
|
|
2265
|
+
groups["Today"].push(conversation);
|
|
2266
|
+
} else if (convDate >= yesterday) {
|
|
2267
|
+
groups["Yesterday"].push(conversation);
|
|
2268
|
+
} else if (convDate >= last7Days) {
|
|
2269
|
+
groups["Last 7 Days"].push(conversation);
|
|
2270
|
+
} else if (convDate >= last30Days) {
|
|
2271
|
+
groups["Last 30 Days"].push(conversation);
|
|
2272
|
+
} else {
|
|
2273
|
+
groups["Older"].push(conversation);
|
|
2274
|
+
}
|
|
2275
|
+
} catch (error) {
|
|
2276
|
+
groups["Older"].push(conversation);
|
|
2277
|
+
}
|
|
2278
|
+
});
|
|
2279
|
+
Object.keys(groups).forEach((key) => {
|
|
2280
|
+
groups[key].sort(
|
|
2281
|
+
(a, b) => new Date(parseInt(b.updatedAt)).getTime() - new Date(parseInt(a.updatedAt)).getTime()
|
|
2282
|
+
);
|
|
2283
|
+
});
|
|
2284
|
+
return groups;
|
|
2285
|
+
},
|
|
2286
|
+
[]
|
|
2287
|
+
);
|
|
2288
|
+
const isJson = useCallback2((content) => {
|
|
2289
|
+
if (typeof content === "object" && content !== null) {
|
|
2290
|
+
return Array.isArray(content) || Object.prototype.toString.call(content) === "[object Object]";
|
|
2291
|
+
}
|
|
2292
|
+
if (typeof content === "string") {
|
|
2293
|
+
try {
|
|
2294
|
+
const parsed = JSON.parse(content);
|
|
2295
|
+
return typeof parsed === "object" && parsed !== null;
|
|
2296
|
+
} catch (e) {
|
|
2297
|
+
return false;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
return false;
|
|
2301
|
+
}, []);
|
|
2302
|
+
useEffect4(() => {
|
|
2303
|
+
const fetchConversations = async () => {
|
|
2304
|
+
if (!initialAssistantIdChecked || hasFetchedRef.current) {
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
hasFetchedRef.current = true;
|
|
2308
|
+
setIsLoading(true);
|
|
2309
|
+
try {
|
|
2310
|
+
const data = await chatClient.conversations.getAll(agentId);
|
|
2311
|
+
if (!Array.isArray(data)) {
|
|
2312
|
+
console.error(
|
|
2313
|
+
"Expected array of conversations but got:",
|
|
2314
|
+
data
|
|
2315
|
+
);
|
|
2316
|
+
setConversations([]);
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
setConversations(data);
|
|
2320
|
+
if (data.length > 0) {
|
|
2321
|
+
const validConversations = data.filter(
|
|
2322
|
+
(conv) => conv && conv.updatedAt
|
|
2323
|
+
);
|
|
2324
|
+
if (validConversations.length > 0) {
|
|
2325
|
+
try {
|
|
2326
|
+
const mostRecent = [...validConversations].sort(
|
|
2327
|
+
(a, b) => new Date(parseInt(b.updatedAt)).getTime() - new Date(parseInt(a.updatedAt)).getTime()
|
|
2328
|
+
)[0];
|
|
2329
|
+
selectConversation(mostRecent.id);
|
|
2330
|
+
} catch (sortError) {
|
|
2331
|
+
console.error(
|
|
2332
|
+
"Error sorting conversations:",
|
|
2333
|
+
sortError
|
|
2334
|
+
);
|
|
2335
|
+
selectConversation(validConversations[0].id);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
} else {
|
|
2339
|
+
if (agentId && !isCreatingConversation) {
|
|
2340
|
+
try {
|
|
2341
|
+
setIsCreatingConversation(true);
|
|
2342
|
+
const newConversation = await chatClient.conversations.create(
|
|
2343
|
+
"New Chat",
|
|
2344
|
+
agentId
|
|
2345
|
+
);
|
|
2346
|
+
setConversations([newConversation]);
|
|
2347
|
+
selectConversation(newConversation.id);
|
|
2348
|
+
} catch (createError) {
|
|
2349
|
+
} finally {
|
|
2350
|
+
setIsCreatingConversation(false);
|
|
2351
|
+
}
|
|
2352
|
+
} else {
|
|
2353
|
+
console.warn(
|
|
2354
|
+
"Cannot create conversation: agentId not available or already creating"
|
|
2355
|
+
);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
} catch (error) {
|
|
2359
|
+
console.error("Failed to fetch conversations:", error);
|
|
2360
|
+
} finally {
|
|
2361
|
+
setIsLoading(false);
|
|
2362
|
+
setInitialLoadComplete(true);
|
|
2363
|
+
}
|
|
2364
|
+
};
|
|
2365
|
+
fetchConversations();
|
|
2366
|
+
}, [agentId, initialAssistantIdChecked]);
|
|
2367
|
+
const loadConversationData = async (id) => {
|
|
2368
|
+
setIsLoadingMessages(true);
|
|
2369
|
+
try {
|
|
2370
|
+
const fullConversation = await chatClient.conversations.get(id);
|
|
2371
|
+
setSelectedConversation(fullConversation);
|
|
2372
|
+
} catch (error) {
|
|
2373
|
+
console.error(
|
|
2374
|
+
`Failed to load conversation data for ID ${id}:`,
|
|
2375
|
+
error
|
|
2376
|
+
);
|
|
2377
|
+
} finally {
|
|
2378
|
+
setIsLoadingMessages(false);
|
|
2379
|
+
}
|
|
2380
|
+
};
|
|
2381
|
+
const refreshSelectedConversation = async () => {
|
|
2382
|
+
if (!selectedConversationId) return;
|
|
2383
|
+
try {
|
|
2384
|
+
const updatedConversation = await chatClient.conversations.get(
|
|
2385
|
+
selectedConversationId
|
|
2386
|
+
);
|
|
2387
|
+
setConversations(
|
|
2388
|
+
(prev) => prev.map(
|
|
2389
|
+
(conv) => conv.id === selectedConversationId ? updatedConversation : conv
|
|
2390
|
+
)
|
|
2391
|
+
);
|
|
2392
|
+
setSelectedConversation(updatedConversation);
|
|
2393
|
+
} catch (error) {
|
|
2394
|
+
console.error("Failed to refresh selected conversation:", error);
|
|
2395
|
+
}
|
|
2396
|
+
};
|
|
2397
|
+
const selectConversation = useCallback2((id) => {
|
|
2398
|
+
setSelectedConversationId(id);
|
|
2399
|
+
if (id.startsWith("temp-")) {
|
|
2400
|
+
setConversations((prev) => {
|
|
2401
|
+
const tempConv = prev.find((conv) => conv.id === id);
|
|
2402
|
+
if (tempConv) {
|
|
2403
|
+
setSelectedConversation(tempConv);
|
|
2404
|
+
}
|
|
2405
|
+
return prev;
|
|
2406
|
+
});
|
|
2407
|
+
} else {
|
|
2408
|
+
loadConversationData(id);
|
|
2409
|
+
}
|
|
2410
|
+
setHasOpenApp(false);
|
|
2411
|
+
}, []);
|
|
2412
|
+
const createNewConversation = async () => {
|
|
2413
|
+
try {
|
|
2414
|
+
const existingTempConv = conversations.find(
|
|
2415
|
+
(conv) => conv.isTemporary
|
|
2416
|
+
);
|
|
2417
|
+
if (existingTempConv) {
|
|
2418
|
+
selectConversation(existingTempConv.id);
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2421
|
+
const tempConversation = {
|
|
2422
|
+
id: `temp-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
|
2423
|
+
title: "New Chat",
|
|
2424
|
+
messages: [],
|
|
2425
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2426
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2427
|
+
isTemporary: true
|
|
2428
|
+
// Mark as temporary
|
|
2429
|
+
};
|
|
2430
|
+
setSelectedConversationId(tempConversation.id);
|
|
2431
|
+
setSelectedConversation(tempConversation);
|
|
2432
|
+
setConversations((prev) => [tempConversation, ...prev]);
|
|
2433
|
+
setHasOpenApp(false);
|
|
2434
|
+
} catch (error) {
|
|
2435
|
+
console.error("Failed to create new conversation:", error);
|
|
2436
|
+
}
|
|
2437
|
+
};
|
|
2438
|
+
const handleThreadCreated = useCallback2(
|
|
2439
|
+
async (oldId, newId) => {
|
|
2440
|
+
console.log(`Thread created: ${oldId} -> ${newId}`);
|
|
2441
|
+
setConversations(
|
|
2442
|
+
(prev) => prev.map(
|
|
2443
|
+
(conv) => conv.id === oldId ? __spreadProps(__spreadValues({}, conv), { id: newId, isTemporary: false }) : conv
|
|
2444
|
+
)
|
|
2445
|
+
);
|
|
2446
|
+
if (selectedConversationId === oldId) {
|
|
2447
|
+
setSelectedConversationId(newId);
|
|
2448
|
+
try {
|
|
2449
|
+
const updatedConv = await chatClient.conversations.get(
|
|
2450
|
+
newId
|
|
2451
|
+
);
|
|
2452
|
+
setSelectedConversation(updatedConv);
|
|
2453
|
+
} catch (error) {
|
|
2454
|
+
console.error(
|
|
2455
|
+
"Failed to fetch updated conversation:",
|
|
2456
|
+
error
|
|
2457
|
+
);
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
},
|
|
2461
|
+
[selectedConversationId]
|
|
2462
|
+
);
|
|
2463
|
+
const deleteConversation = async (id) => {
|
|
2464
|
+
try {
|
|
2465
|
+
await chatClient.conversations.delete(id);
|
|
2466
|
+
const updatedConversations = conversations.filter(
|
|
2467
|
+
(conv) => conv.id !== id
|
|
2468
|
+
);
|
|
2469
|
+
setConversations(updatedConversations);
|
|
2470
|
+
if (selectedConversationId === id) {
|
|
2471
|
+
if (updatedConversations.length > 0) {
|
|
2472
|
+
selectConversation(updatedConversations[0].id);
|
|
2473
|
+
} else {
|
|
2474
|
+
setSelectedConversationId(null);
|
|
2475
|
+
setSelectedConversation(null);
|
|
2476
|
+
if (agentId) {
|
|
2477
|
+
try {
|
|
2478
|
+
const newConversation = await chatClient.conversations.create(
|
|
2479
|
+
"New Chat",
|
|
2480
|
+
agentId
|
|
2481
|
+
);
|
|
2482
|
+
setConversations([newConversation]);
|
|
2483
|
+
selectConversation(newConversation.id);
|
|
2484
|
+
} catch (error) {
|
|
2485
|
+
console.error(
|
|
2486
|
+
"Failed to create new conversation after deletion:",
|
|
2487
|
+
error
|
|
2488
|
+
);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
} catch (error) {
|
|
2494
|
+
console.error("Failed to delete conversation:", error);
|
|
2495
|
+
}
|
|
2496
|
+
};
|
|
2497
|
+
const handleDeleteClick = (e, id) => {
|
|
2498
|
+
e.stopPropagation();
|
|
2499
|
+
setPendingDeleteId(id);
|
|
2500
|
+
setDeleteDialogOpen(true);
|
|
2501
|
+
setOpenMenuId(null);
|
|
2502
|
+
};
|
|
2503
|
+
const toggleConversationMenu = (e, id) => {
|
|
2504
|
+
e.stopPropagation();
|
|
2505
|
+
setOpenMenuId(openMenuId === id ? null : id);
|
|
2506
|
+
};
|
|
2507
|
+
const handleRenameClick = (e, conversation) => {
|
|
2508
|
+
e.stopPropagation();
|
|
2509
|
+
setConversationToRename(conversation);
|
|
2510
|
+
setTitleInput(conversation.title || "New Chat");
|
|
2511
|
+
setIsRenameDialogOpen(true);
|
|
2512
|
+
setOpenMenuId(null);
|
|
2513
|
+
};
|
|
2514
|
+
const handleConfirmRename = async () => {
|
|
2515
|
+
if (!conversationToRename || !titleInput.trim()) {
|
|
2516
|
+
setIsRenameDialogOpen(false);
|
|
2517
|
+
setConversationToRename(null);
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2520
|
+
setIsSavingTitle(true);
|
|
2521
|
+
try {
|
|
2522
|
+
await chatClient.conversations.update(conversationToRename.id, {
|
|
2523
|
+
title: titleInput.trim()
|
|
2524
|
+
});
|
|
2525
|
+
const updatedConv = __spreadProps(__spreadValues({}, conversationToRename), {
|
|
2526
|
+
title: titleInput.trim()
|
|
2527
|
+
});
|
|
2528
|
+
setConversations(
|
|
2529
|
+
(prev) => prev.map(
|
|
2530
|
+
(conv) => conv.id === conversationToRename.id ? updatedConv : conv
|
|
2531
|
+
)
|
|
2532
|
+
);
|
|
2533
|
+
if (selectedConversationId === conversationToRename.id) {
|
|
2534
|
+
setSelectedConversation(updatedConv);
|
|
2535
|
+
}
|
|
2536
|
+
} catch (error) {
|
|
2537
|
+
console.error("Failed to update conversation title:", error);
|
|
2538
|
+
} finally {
|
|
2539
|
+
setIsSavingTitle(false);
|
|
2540
|
+
setIsRenameDialogOpen(false);
|
|
2541
|
+
setConversationToRename(null);
|
|
2542
|
+
}
|
|
2543
|
+
};
|
|
2544
|
+
const handleCancelRename = () => {
|
|
2545
|
+
setIsRenameDialogOpen(false);
|
|
2546
|
+
setConversationToRename(null);
|
|
2547
|
+
setTitleInput("");
|
|
2548
|
+
};
|
|
2549
|
+
const confirmDelete = async () => {
|
|
2550
|
+
if (pendingDeleteId) {
|
|
2551
|
+
await deleteConversation(pendingDeleteId);
|
|
2552
|
+
setDeleteDialogOpen(false);
|
|
2553
|
+
setPendingDeleteId(null);
|
|
2554
|
+
}
|
|
2555
|
+
};
|
|
2556
|
+
useIsomorphicLayoutEffect(() => {
|
|
2557
|
+
const handleResize = () => {
|
|
2558
|
+
if (window.innerWidth < 1024) setSidebarOpen(false);
|
|
2559
|
+
else setSidebarOpen(true);
|
|
2560
|
+
};
|
|
2561
|
+
handleResize();
|
|
2562
|
+
window.addEventListener("resize", handleResize);
|
|
2563
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
2564
|
+
}, []);
|
|
2565
|
+
useEffect4(() => {
|
|
2566
|
+
const handleClickOutside = () => setOpenMenuId(null);
|
|
2567
|
+
if (openMenuId) {
|
|
2568
|
+
document.addEventListener("click", handleClickOutside);
|
|
2569
|
+
return () => document.removeEventListener("click", handleClickOutside);
|
|
2570
|
+
}
|
|
2571
|
+
}, [openMenuId]);
|
|
2572
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2573
|
+
!initialLoadComplete && /* @__PURE__ */ jsx4("div", { className: "fixed inset-0 bg-white dark:bg-gray-900 flex items-center justify-center z-50", children: /* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
|
|
2574
|
+
/* @__PURE__ */ jsx4("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-indigo-600 mx-auto mb-4" }),
|
|
2575
|
+
/* @__PURE__ */ jsx4("p", { className: "text-gray-500 dark:text-gray-400", children: "Loading..." })
|
|
2576
|
+
] }) }),
|
|
2577
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex h-screen overflow-hidden bg-white dark:bg-gray-900", children: [
|
|
2578
|
+
/* @__PURE__ */ jsx4("style", { children: `
|
|
2579
|
+
.resize-active {
|
|
2580
|
+
cursor: col-resize;
|
|
2581
|
+
user-select: none;
|
|
2582
|
+
}
|
|
2583
|
+
` }),
|
|
2584
|
+
/* @__PURE__ */ jsx4(
|
|
2585
|
+
ConfirmationDialog,
|
|
2586
|
+
{
|
|
2587
|
+
isOpen: deleteDialogOpen,
|
|
2588
|
+
onClose: () => setDeleteDialogOpen(false),
|
|
2589
|
+
onConfirm: confirmDelete,
|
|
2590
|
+
title: "Delete Conversation",
|
|
2591
|
+
message: "Are you sure you want to delete this conversation? This action cannot be undone.",
|
|
2592
|
+
confirmText: "Delete",
|
|
2593
|
+
cancelText: "Cancel",
|
|
2594
|
+
confirmButtonClass: "bg-red-500 hover:bg-red-600"
|
|
2595
|
+
}
|
|
2596
|
+
),
|
|
2597
|
+
isRenameDialogOpen && /* @__PURE__ */ jsx4("div", { className: "fixed inset-0 bg-black/50 bg-opacity-50 flex items-center justify-center z-50", children: /* @__PURE__ */ jsxs4("div", { className: "bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 w-120 max-w-[90vw]", children: [
|
|
2598
|
+
/* @__PURE__ */ jsx4("h3", { className: "text-lg font-medium mb-4 text-gray-900 dark:text-gray-100", children: "Rename Conversation" }),
|
|
2599
|
+
/* @__PURE__ */ jsx4(
|
|
2600
|
+
"input",
|
|
2601
|
+
{
|
|
2602
|
+
ref: titleInputRef,
|
|
2603
|
+
type: "text",
|
|
2604
|
+
value: titleInput,
|
|
2605
|
+
onChange: (e) => setTitleInput(e.target.value),
|
|
2606
|
+
onKeyDown: (e) => {
|
|
2607
|
+
if (e.key === "Enter")
|
|
2608
|
+
handleConfirmRename();
|
|
2609
|
+
else if (e.key === "Escape")
|
|
2610
|
+
handleCancelRename();
|
|
2611
|
+
},
|
|
2612
|
+
className: "w-full bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md py-2 px-3 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400",
|
|
2613
|
+
placeholder: "Enter conversation title",
|
|
2614
|
+
disabled: isSavingTitle,
|
|
2615
|
+
autoFocus: true
|
|
2616
|
+
}
|
|
2617
|
+
),
|
|
2618
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex justify-end gap-2 mt-4", children: [
|
|
2619
|
+
/* @__PURE__ */ jsx4(
|
|
2620
|
+
"button",
|
|
2621
|
+
{
|
|
2622
|
+
onClick: handleCancelRename,
|
|
2623
|
+
disabled: isSavingTitle,
|
|
2624
|
+
className: "px-4 py-2 text-sm rounded-md border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50",
|
|
2625
|
+
children: "Cancel"
|
|
2626
|
+
}
|
|
2627
|
+
),
|
|
2628
|
+
/* @__PURE__ */ jsx4(
|
|
2629
|
+
"button",
|
|
2630
|
+
{
|
|
2631
|
+
onClick: handleConfirmRename,
|
|
2632
|
+
disabled: isSavingTitle || !titleInput.trim(),
|
|
2633
|
+
className: "px-4 py-2 text-sm text-white rounded-md bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50",
|
|
2634
|
+
children: isSavingTitle ? "Saving..." : "Rename"
|
|
2635
|
+
}
|
|
2636
|
+
)
|
|
2637
|
+
] })
|
|
2638
|
+
] }) }),
|
|
2639
|
+
sidebarOpen && /* @__PURE__ */ jsx4(
|
|
2640
|
+
"div",
|
|
2641
|
+
{
|
|
2642
|
+
className: "lg:hidden fixed inset-0 bg-black bg-opacity-50 z-10",
|
|
2643
|
+
onClick: () => setSidebarOpen(false),
|
|
2644
|
+
"aria-hidden": "true"
|
|
2645
|
+
}
|
|
2646
|
+
),
|
|
2647
|
+
/* @__PURE__ */ jsx4(
|
|
2648
|
+
"div",
|
|
2649
|
+
{
|
|
2650
|
+
className: `bg-[#f9f9f9] dark:bg-gray-900 dark:!bg-gray-900 border-r border-gray-200 dark:border-gray-700 h-full relative z-20 ${sidebarOpen ? "block" : "hidden"}`,
|
|
2651
|
+
style: {
|
|
2652
|
+
width: sidebarOpen ? `${sidebarWidth}px` : "0"
|
|
2653
|
+
},
|
|
2654
|
+
children: /* @__PURE__ */ jsx4("div", { className: "flex flex-col h-full", children: /* @__PURE__ */ jsx4("div", { className: "flex-1 overflow-y-auto p-2 bg-[#f9f9f9] dark:bg-[#171717]", children: /* @__PURE__ */ jsx4("div", { className: "flex-1", children: isLoading ? /* @__PURE__ */ jsx4("div", { className: "space-y-4" }) : conversations.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "space-y-0", children: (() => {
|
|
2655
|
+
const groupedConversations = categorizeConversationsByDate(
|
|
2656
|
+
conversations
|
|
2657
|
+
);
|
|
2658
|
+
return Object.entries(
|
|
2659
|
+
groupedConversations
|
|
2660
|
+
).map(
|
|
2661
|
+
([
|
|
2662
|
+
groupName,
|
|
2663
|
+
groupConversations
|
|
2664
|
+
]) => {
|
|
2665
|
+
if (groupConversations.length === 0)
|
|
2666
|
+
return null;
|
|
2667
|
+
return /* @__PURE__ */ jsxs4(
|
|
2668
|
+
"div",
|
|
2669
|
+
{
|
|
2670
|
+
className: "mb-4",
|
|
2671
|
+
children: [
|
|
2672
|
+
/* @__PURE__ */ jsx4("div", { className: "text-xs font-medium text-gray-500 dark:text-gray-400 px-2 py-2 uppercase tracking-wide", children: groupName }),
|
|
2673
|
+
/* @__PURE__ */ jsx4("div", { className: "space-y-0", children: groupConversations.map(
|
|
2674
|
+
(conversation) => {
|
|
2675
|
+
var _a;
|
|
2676
|
+
const hasWaitingMessages = conversation.waiting === true || ((_a = conversation.messages) == null ? void 0 : _a.some(
|
|
2677
|
+
(message) => message.waiting === true
|
|
2678
|
+
)) || false;
|
|
2679
|
+
return /* @__PURE__ */ jsxs4(
|
|
2680
|
+
"div",
|
|
2681
|
+
{
|
|
2682
|
+
onClick: () => {
|
|
2683
|
+
selectConversation(
|
|
2684
|
+
conversation.id
|
|
2685
|
+
);
|
|
2686
|
+
if (window.innerWidth < 1024) {
|
|
2687
|
+
setSidebarOpen(
|
|
2688
|
+
false
|
|
2689
|
+
);
|
|
2690
|
+
}
|
|
2691
|
+
},
|
|
2692
|
+
className: `flex items-center w-full pl-4 pr-2 py-2 mb-1 rounded-xl text-left group cursor-pointer ${selectedConversationId === conversation.id ? "bg-naia-200 text-gray-800 dark:text-gray-200" : "text-gray-800 dark:text-gray-100 conversation-item-background"}`,
|
|
2693
|
+
children: [
|
|
2694
|
+
/* @__PURE__ */ jsx4("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
2695
|
+
/* @__PURE__ */ jsx4("div", { className: "font-medium overflow-hidden text-ellipsis whitespace-nowrap", children: conversation.title || "New Chat" }),
|
|
2696
|
+
hasWaitingMessages && /* @__PURE__ */ jsx4("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx4(
|
|
2697
|
+
"div",
|
|
2698
|
+
{
|
|
2699
|
+
className: "w-2 h-2 bg-amber-500 dark:bg-amber-400 rounded-full animate-pulse",
|
|
2700
|
+
title: "Waiting for input"
|
|
2701
|
+
}
|
|
2702
|
+
) })
|
|
2703
|
+
] }) }),
|
|
2704
|
+
!conversation.isTemporary && /* @__PURE__ */ jsxs4("div", { className: "relative", children: [
|
|
2705
|
+
/* @__PURE__ */ jsx4(
|
|
2706
|
+
"button",
|
|
2707
|
+
{
|
|
2708
|
+
onClick: (e) => toggleConversationMenu(
|
|
2709
|
+
e,
|
|
2710
|
+
conversation.id
|
|
2711
|
+
),
|
|
2712
|
+
className: "p-1.5 cursor-pointer rounded-full text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 opacity-0 hover:bg-gray-300 dark:hover:bg-gray-700 group-hover:opacity-100 transition-opacity",
|
|
2713
|
+
"aria-label": "More options",
|
|
2714
|
+
children: /* @__PURE__ */ jsx4(
|
|
2715
|
+
MoreHorizontal,
|
|
2716
|
+
{
|
|
2717
|
+
size: 16
|
|
2718
|
+
}
|
|
2719
|
+
)
|
|
2720
|
+
}
|
|
2721
|
+
),
|
|
2722
|
+
openMenuId === conversation.id && /* @__PURE__ */ jsxs4("div", { className: "absolute right-0 top-10 w-40 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl shadow-lg z-50 p-2", children: [
|
|
2723
|
+
/* @__PURE__ */ jsxs4(
|
|
2724
|
+
"button",
|
|
2725
|
+
{
|
|
2726
|
+
onClick: (e) => handleRenameClick(
|
|
2727
|
+
e,
|
|
2728
|
+
conversation
|
|
2729
|
+
),
|
|
2730
|
+
className: "w-full text-left px-3 py-2 text-md flex cursor-pointer items-center gap-2 hover:bg-gray-300 dark:hover:bg-gray-700 rounded-md",
|
|
2731
|
+
children: [
|
|
2732
|
+
/* @__PURE__ */ jsx4(
|
|
2733
|
+
Edit,
|
|
2734
|
+
{
|
|
2735
|
+
size: 14
|
|
2736
|
+
}
|
|
2737
|
+
),
|
|
2738
|
+
"Rename"
|
|
2739
|
+
]
|
|
2740
|
+
}
|
|
2741
|
+
),
|
|
2742
|
+
/* @__PURE__ */ jsxs4(
|
|
2743
|
+
"button",
|
|
2744
|
+
{
|
|
2745
|
+
onClick: (e) => handleDeleteClick(
|
|
2746
|
+
e,
|
|
2747
|
+
conversation.id
|
|
2748
|
+
),
|
|
2749
|
+
className: "w-full text-left px-3 py-2 text-md text-red-600 cursor-pointer flex items-center gap-2 hover:bg-gray-300 dark:hover:bg-gray-700 rounded-md",
|
|
2750
|
+
children: [
|
|
2751
|
+
/* @__PURE__ */ jsx4(
|
|
2752
|
+
Trash2,
|
|
2753
|
+
{
|
|
2754
|
+
size: 14
|
|
2755
|
+
}
|
|
2756
|
+
),
|
|
2757
|
+
"Delete"
|
|
2758
|
+
]
|
|
2759
|
+
}
|
|
2760
|
+
)
|
|
2761
|
+
] })
|
|
2762
|
+
] })
|
|
2763
|
+
]
|
|
2764
|
+
},
|
|
2765
|
+
conversation.id
|
|
2766
|
+
);
|
|
2767
|
+
}
|
|
2768
|
+
) })
|
|
2769
|
+
]
|
|
2770
|
+
},
|
|
2771
|
+
groupName
|
|
2772
|
+
);
|
|
2773
|
+
}
|
|
2774
|
+
).filter(Boolean);
|
|
2775
|
+
})() }) : /* @__PURE__ */ jsx4("div", { className: "text-center py-8 text-gray-500", children: "No conversations yet" }) }) }) })
|
|
2776
|
+
}
|
|
2777
|
+
),
|
|
2778
|
+
sidebarOpen && /* @__PURE__ */ jsx4(
|
|
2779
|
+
"div",
|
|
2780
|
+
{
|
|
2781
|
+
ref: resizeHandleRef,
|
|
2782
|
+
onMouseDown: startResizing,
|
|
2783
|
+
className: "w-1 hover:w-2 bg-gray-300 dark:bg-gray-700 h-full cursor-col-resize transition-all hover:bg-indigo-500 dark:hover:bg-indigo-400 z-30 relative",
|
|
2784
|
+
title: "Drag to resize"
|
|
2785
|
+
}
|
|
2786
|
+
),
|
|
2787
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
|
|
2788
|
+
/* @__PURE__ */ jsxs4("header", { className: "h-14 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between px-4 bg-white dark:bg-gray-900", children: [
|
|
2789
|
+
/* @__PURE__ */ jsx4("div", { className: "flex items-center", children: /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2790
|
+
/* @__PURE__ */ jsx4(
|
|
2791
|
+
"button",
|
|
2792
|
+
{
|
|
2793
|
+
onClick: () => setSidebarOpen(!sidebarOpen),
|
|
2794
|
+
className: "p-2 cursor-pointer rounded-lg text-gray-500 hover:bg-naia-hover disabled:opacity-50 transition-colors",
|
|
2795
|
+
title: "Toggle Sidebar",
|
|
2796
|
+
"aria-label": "Toggle Sidebar",
|
|
2797
|
+
children: sidebarOpen ? /* @__PURE__ */ jsx4(PanelRightOpen, { size: 20 }) : /* @__PURE__ */ jsx4(PanelRightClose, { size: 20 })
|
|
2798
|
+
}
|
|
2799
|
+
),
|
|
2800
|
+
/* @__PURE__ */ jsx4(
|
|
2801
|
+
"button",
|
|
2802
|
+
{
|
|
2803
|
+
onClick: createNewConversation,
|
|
2804
|
+
className: "p-2 cursor-pointer rounded-lg text-gray-500 hover:bg-naia-hover disabled:opacity-50 transition-colors",
|
|
2805
|
+
title: "New Chat",
|
|
2806
|
+
"aria-label": "New Chat",
|
|
2807
|
+
children: /* @__PURE__ */ jsx4(MessageCirclePlus, { size: 20 })
|
|
2808
|
+
}
|
|
2809
|
+
)
|
|
2810
|
+
] }) }),
|
|
2811
|
+
/* @__PURE__ */ jsx4("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx4("div", { className: "relative flex items-center", children: /* @__PURE__ */ jsx4("span", { className: "text-2xl font-bold mr-1", children: "Naia" }) }) }),
|
|
2812
|
+
/* @__PURE__ */ jsx4("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx4(
|
|
2813
|
+
"button",
|
|
2814
|
+
{
|
|
2815
|
+
onClick: toggleTheme,
|
|
2816
|
+
className: "p-2 cursor-pointer rounded-lg text-gray-500 hover:bg-naia-hover transition-colors",
|
|
2817
|
+
title: `Switch to ${theme === "dark" ? "light" : "dark"} mode`,
|
|
2818
|
+
"aria-label": `Switch to ${theme === "dark" ? "light" : "dark"} mode`,
|
|
2819
|
+
children: theme === "dark" ? /* @__PURE__ */ jsx4(Sun, { size: 20 }) : /* @__PURE__ */ jsx4(Moon, { size: 20 })
|
|
2820
|
+
}
|
|
2821
|
+
) })
|
|
2822
|
+
] }),
|
|
2823
|
+
/* @__PURE__ */ jsx4(
|
|
2824
|
+
"div",
|
|
2825
|
+
{
|
|
2826
|
+
className: `flex-1 overflow-hidden bg-white dark:bg-gray-900 text-lg transition-all duration-300 ${hasOpenApp ? "flex" : "flex justify-center"}`,
|
|
2827
|
+
children: /* @__PURE__ */ jsx4(
|
|
2828
|
+
"div",
|
|
2829
|
+
{
|
|
2830
|
+
className: `flex flex-col transition-all duration-300 ${hasOpenApp ? "w-full" : "w-full max-w-6xl"}`,
|
|
2831
|
+
children: isLoading || !initialAssistantIdChecked ? /* @__PURE__ */ jsx4("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx4("div", { className: "animate-pulse text-gray-500", children: isLoading ? "Loading conversations..." : "Initializing Assistant..." }) }) : !agentId ? /* @__PURE__ */ jsxs4("div", { className: "flex h-full flex-col items-center justify-center p-4 text-center", children: [
|
|
2832
|
+
/* @__PURE__ */ jsx4("h2", { className: "text-xl font-semibold text-red-600 dark:text-red-400 mb-2", children: "Agent Not Configured" }),
|
|
2833
|
+
/* @__PURE__ */ jsxs4("p", { className: "text-gray-700 dark:text-gray-300 max-w-md", children: [
|
|
2834
|
+
"The Agent ID is missing or invalid. Please ensure it is provided in the URL parameters (e.g.,",
|
|
2835
|
+
" ",
|
|
2836
|
+
/* @__PURE__ */ jsx4("code", { children: "?agentId=your-id-here" }),
|
|
2837
|
+
")."
|
|
2838
|
+
] }),
|
|
2839
|
+
/* @__PURE__ */ jsx4("p", { className: "text-gray-500 dark:text-gray-400 mt-4 text-sm", children: "If you continue to see this message, please contact support." })
|
|
2840
|
+
] }) : isLoadingMessages && selectedConversationId ? /* @__PURE__ */ jsx4("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx4("div", { className: "animate-pulse text-gray-500", children: "Loading messages..." }) }) : selectedConversation ? /* @__PURE__ */ jsx4(
|
|
2841
|
+
ChatContent,
|
|
2842
|
+
{
|
|
2843
|
+
conversation: selectedConversation,
|
|
2844
|
+
agentId,
|
|
2845
|
+
debug,
|
|
2846
|
+
onConversationUpdate: refreshSelectedConversation,
|
|
2847
|
+
theme,
|
|
2848
|
+
onAppStateChange: handleAppStateChange,
|
|
2849
|
+
onSidebarToggle: () => setSidebarOpen(false),
|
|
2850
|
+
onThreadCreated: handleThreadCreated
|
|
2851
|
+
}
|
|
2852
|
+
) : (
|
|
2853
|
+
// Show loading while the useEffect handles conversation creation
|
|
2854
|
+
/* @__PURE__ */ jsx4("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx4("div", { className: "animate-pulse text-gray-500", children: isCreatingConversation ? "Creating new conversation..." : conversations.length === 0 ? "Creating new conversation..." : "Loading conversation..." }) })
|
|
2855
|
+
)
|
|
2856
|
+
}
|
|
2857
|
+
)
|
|
2858
|
+
}
|
|
2859
|
+
)
|
|
2860
|
+
] })
|
|
2861
|
+
] })
|
|
2862
|
+
] });
|
|
2863
|
+
}
|
|
2864
|
+
export {
|
|
2865
|
+
NeptuneChatBot,
|
|
2866
|
+
ToolExecutionIndicator,
|
|
2867
|
+
ToolExecutionWidget,
|
|
2868
|
+
configureChatClient
|
|
2869
|
+
};
|