@alpaca-editor/core 1.0.4184 → 1.0.4185
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/dist/agents-view/AgentCard.d.ts +12 -0
- package/dist/agents-view/AgentCard.js +30 -0
- package/dist/agents-view/AgentCard.js.map +1 -0
- package/dist/agents-view/AgentsView.d.ts +2 -2
- package/dist/agents-view/AgentsView.js +102 -85
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/agents-view/ProfileAgentsGroup.d.ts +17 -0
- package/dist/agents-view/ProfileAgentsGroup.js +13 -0
- package/dist/agents-view/ProfileAgentsGroup.js.map +1 -0
- package/dist/editor/ai/AgentProfilesOverview.js +1 -1
- package/dist/editor/ai/AgentProfilesOverview.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +19 -1
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +7 -3
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +7 -28
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/reviews/Comment.js +56 -4
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +19 -0
- package/dist/editor/services/agentService.js +39 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/serviceHelper.d.ts +1 -0
- package/dist/editor/services/serviceHelper.js +58 -4
- package/dist/editor/services/serviceHelper.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +4 -6
- package/dist/tour/default-tour.js +24 -9
- package/dist/tour/default-tour.js.map +1 -1
- package/package.json +1 -1
- package/src/agents-view/AgentCard.tsx +162 -0
- package/src/agents-view/AgentsView.tsx +218 -253
- package/src/agents-view/ProfileAgentsGroup.tsx +123 -0
- package/src/editor/ai/AgentProfilesOverview.tsx +1 -2
- package/src/editor/commands/componentCommands.tsx +19 -1
- package/src/editor/page-editor-chrome/FrameMenu.tsx +9 -3
- package/src/editor/page-viewer/PageViewerFrame.tsx +7 -36
- package/src/editor/reviews/Comment.tsx +85 -4
- package/src/editor/services/agentService.ts +77 -0
- package/src/editor/services/serviceHelper.ts +92 -28
- package/src/revision.ts +2 -2
- package/src/tour/default-tour.tsx +63 -48
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Agent,
|
|
4
|
+
ProfileAgentsGroup as ProfileGroup,
|
|
5
|
+
} from "../editor/services/agentService";
|
|
6
|
+
import { ChevronDown, ChevronUp } from "lucide-react";
|
|
7
|
+
import { AgentCard } from "./AgentCard";
|
|
8
|
+
import { SecretAgentIcon } from "../editor/ui/Icons";
|
|
9
|
+
|
|
10
|
+
interface ProfileAgentsGroupProps {
|
|
11
|
+
profileGroup: ProfileGroup;
|
|
12
|
+
isExpanded: boolean;
|
|
13
|
+
isLoadingMore: boolean;
|
|
14
|
+
hasMore: boolean;
|
|
15
|
+
onToggle: () => void;
|
|
16
|
+
onLoadMore: () => void;
|
|
17
|
+
onAgentClick: (agent: Agent) => void;
|
|
18
|
+
onResumeAgent: (agent: Agent) => void;
|
|
19
|
+
onCloseAgent: (agentId: string, agentName: string) => void;
|
|
20
|
+
onDeleteAgent: (
|
|
21
|
+
agentId: string,
|
|
22
|
+
agentName: string,
|
|
23
|
+
event: React.MouseEvent,
|
|
24
|
+
) => void;
|
|
25
|
+
formatDateTime: (dateString: string) => string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function ProfileAgentsGroup({
|
|
29
|
+
profileGroup,
|
|
30
|
+
isExpanded,
|
|
31
|
+
isLoadingMore,
|
|
32
|
+
hasMore,
|
|
33
|
+
onToggle,
|
|
34
|
+
onLoadMore,
|
|
35
|
+
onAgentClick,
|
|
36
|
+
onResumeAgent,
|
|
37
|
+
onCloseAgent,
|
|
38
|
+
onDeleteAgent,
|
|
39
|
+
formatDateTime,
|
|
40
|
+
}: ProfileAgentsGroupProps) {
|
|
41
|
+
return (
|
|
42
|
+
<div className="rounded-lg border border-gray-200 bg-white shadow-sm">
|
|
43
|
+
{/* Group Header */}
|
|
44
|
+
<button
|
|
45
|
+
onClick={onToggle}
|
|
46
|
+
className="flex w-full items-center gap-3 bg-gray-50 p-3 text-left transition-colors hover:bg-gray-100"
|
|
47
|
+
>
|
|
48
|
+
{/* Profile Icon */}
|
|
49
|
+
<div className="flex-shrink-0">
|
|
50
|
+
{profileGroup.profileSvgIcon ? (
|
|
51
|
+
<div
|
|
52
|
+
className="flex h-6 w-6 items-center justify-center text-gray-500 [&>svg]:h-full [&>svg]:w-full"
|
|
53
|
+
dangerouslySetInnerHTML={{
|
|
54
|
+
__html: profileGroup.profileSvgIcon,
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
) : (
|
|
58
|
+
<SecretAgentIcon
|
|
59
|
+
size={24}
|
|
60
|
+
strokeWidth={1}
|
|
61
|
+
className="text-gray-500"
|
|
62
|
+
/>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
{/* Profile Name */}
|
|
67
|
+
<div className="min-w-0 flex-1">
|
|
68
|
+
<h3 className="truncate font-medium text-gray-900">
|
|
69
|
+
{profileGroup.profileName}
|
|
70
|
+
</h3>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
{/* Closed Agents Count Badge */}
|
|
74
|
+
<div className="flex-shrink-0 rounded-full bg-gray-200 px-3 py-1 text-xs font-medium text-gray-700">
|
|
75
|
+
{profileGroup.totalClosedCount} closed agent
|
|
76
|
+
{profileGroup.totalClosedCount !== 1 ? "s" : ""}
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
{/* Chevron Icon */}
|
|
80
|
+
<div className="flex-shrink-0 text-gray-500">
|
|
81
|
+
{isExpanded ? (
|
|
82
|
+
<ChevronUp className="size-5" strokeWidth={1} />
|
|
83
|
+
) : (
|
|
84
|
+
<ChevronDown className="size-5" strokeWidth={1} />
|
|
85
|
+
)}
|
|
86
|
+
</div>
|
|
87
|
+
</button>
|
|
88
|
+
|
|
89
|
+
{/* Expandable Content */}
|
|
90
|
+
{isExpanded && (
|
|
91
|
+
<div className="space-y-2 p-3">
|
|
92
|
+
{profileGroup.agents.map((agent) => (
|
|
93
|
+
<AgentCard
|
|
94
|
+
key={agent.id}
|
|
95
|
+
agent={agent}
|
|
96
|
+
onClick={onAgentClick}
|
|
97
|
+
onResume={onResumeAgent}
|
|
98
|
+
onClose={onCloseAgent}
|
|
99
|
+
onDelete={onDeleteAgent}
|
|
100
|
+
formatDateTime={formatDateTime}
|
|
101
|
+
/>
|
|
102
|
+
))}
|
|
103
|
+
|
|
104
|
+
{/* Load More Button */}
|
|
105
|
+
{hasMore && (
|
|
106
|
+
<div className="flex justify-center pt-2">
|
|
107
|
+
<button
|
|
108
|
+
onClick={(e) => {
|
|
109
|
+
e.stopPropagation();
|
|
110
|
+
onLoadMore();
|
|
111
|
+
}}
|
|
112
|
+
disabled={isLoadingMore}
|
|
113
|
+
className="rounded-lg border border-gray-300 bg-white px-6 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50"
|
|
114
|
+
>
|
|
115
|
+
{isLoadingMore ? "Loading..." : "Load More"}
|
|
116
|
+
</button>
|
|
117
|
+
</div>
|
|
118
|
+
)}
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
@@ -42,7 +42,7 @@ export const AgentProfilesOverview: React.FC<AgentProfilesOverviewProps> = ({
|
|
|
42
42
|
<button
|
|
43
43
|
key={profile.id}
|
|
44
44
|
onClick={() => onSelectProfile(profile.id)}
|
|
45
|
-
className="group flex flex-col gap-3 rounded-lg border border-gray-200 bg-white p-4 text-left transition-all hover:border-blue-300 hover:shadow-md
|
|
45
|
+
className="group flex flex-col gap-3 rounded-lg border border-gray-200 bg-white p-4 text-left transition-all hover:border-blue-300 hover:shadow-md"
|
|
46
46
|
data-testid="agent-profile-card"
|
|
47
47
|
data-profile-id={profile.id}
|
|
48
48
|
>
|
|
@@ -78,4 +78,3 @@ export const AgentProfilesOverview: React.FC<AgentProfilesOverviewProps> = ({
|
|
|
78
78
|
</div>
|
|
79
79
|
);
|
|
80
80
|
};
|
|
81
|
-
|
|
@@ -95,6 +95,9 @@ function getInsertCommand(
|
|
|
95
95
|
if (!item) return null;
|
|
96
96
|
if (!item.placeholders || item.placeholders.length === 0) return null;
|
|
97
97
|
if (editContext.mode === "suggestions") return null;
|
|
98
|
+
// Don't show insert for layout components when showLayoutComponents is disabled
|
|
99
|
+
if (item.layoutId && !editContext.showLayoutComponents) return null;
|
|
100
|
+
|
|
98
101
|
return {
|
|
99
102
|
id: "insert",
|
|
100
103
|
icon: <Plus size={defaultIconSize} />,
|
|
@@ -114,6 +117,10 @@ function getDuplicateCommand(
|
|
|
114
117
|
if (components.length !== 1) return null;
|
|
115
118
|
if (editContext.mode === "suggestions") return null;
|
|
116
119
|
|
|
120
|
+
const component = components[0];
|
|
121
|
+
// Don't show duplicate for layout components when showLayoutComponents is disabled
|
|
122
|
+
if (component.layoutId && !editContext.showLayoutComponents) return null;
|
|
123
|
+
|
|
117
124
|
return {
|
|
118
125
|
id: "duplicate",
|
|
119
126
|
icon: <Copy size={defaultIconSize} />,
|
|
@@ -205,6 +212,9 @@ async function getDesignCommand(
|
|
|
205
212
|
if (components.length !== 1 || isPlaceholder(components[0])) return null;
|
|
206
213
|
const component = components[0];
|
|
207
214
|
if (!component || !component.datasourceItem) return null;
|
|
215
|
+
// Don't show design for layout components when showLayoutComponents is disabled
|
|
216
|
+
if (component.layoutId && !editContext.showLayoutComponents) return null;
|
|
217
|
+
|
|
208
218
|
const item = await editContext.itemsRepository.getItem(
|
|
209
219
|
component.datasourceItem,
|
|
210
220
|
);
|
|
@@ -243,6 +253,9 @@ async function getLinkToMasterCommand(
|
|
|
243
253
|
): Promise<ComponentCommand | null> {
|
|
244
254
|
if (!components.length) return null;
|
|
245
255
|
if (editContext.page?.item?.masterLanguages?.length === 0) return null;
|
|
256
|
+
// Don't show for layout components when showLayoutComponents is disabled
|
|
257
|
+
if (components.some((c) => c.layoutId && !editContext.showLayoutComponents))
|
|
258
|
+
return null;
|
|
246
259
|
|
|
247
260
|
return getCheckboxCommand(
|
|
248
261
|
components,
|
|
@@ -304,6 +317,9 @@ async function getInheritChildrenFromMasterCommand(
|
|
|
304
317
|
if (editContext.page?.item?.masterLanguages?.length === 0) return null;
|
|
305
318
|
if (components.find((x) => !x.placeholders || x.placeholders.length === 0))
|
|
306
319
|
return null;
|
|
320
|
+
// Don't show for layout components when showLayoutComponents is disabled
|
|
321
|
+
if (components.some((c) => c.layoutId && !editContext.showLayoutComponents))
|
|
322
|
+
return null;
|
|
307
323
|
|
|
308
324
|
return getCheckboxCommand(
|
|
309
325
|
components,
|
|
@@ -345,7 +361,9 @@ function getDeleteCommand(
|
|
|
345
361
|
((!isPlaceholder(c) && !c.layoutId) ||
|
|
346
362
|
c.layoutId === editContext.page?.item.id) &&
|
|
347
363
|
!isLocked(c, editContext) &&
|
|
348
|
-
editContext.page?.item.canWriteItem
|
|
364
|
+
editContext.page?.item.canWriteItem &&
|
|
365
|
+
// When showLayoutComponents is disabled, layout components should not be editable
|
|
366
|
+
(editContext.showLayoutComponents || !c.layoutId),
|
|
349
367
|
);
|
|
350
368
|
|
|
351
369
|
if (applicableComponents.length === 0) return null;
|
|
@@ -186,7 +186,10 @@ export function FrameMenu({
|
|
|
186
186
|
loadCommands();
|
|
187
187
|
}, [component]);
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
// Determine if draggable - layout components should not be draggable when showLayoutComponents is disabled
|
|
190
|
+
const isLayoutReadonlyCheck =
|
|
191
|
+
component.layoutId && !editContext.showLayoutComponents;
|
|
192
|
+
const isDraggable = component.canBeMoved && !isLayoutReadonlyCheck;
|
|
190
193
|
|
|
191
194
|
const commandButtons = commands
|
|
192
195
|
.filter(
|
|
@@ -257,11 +260,14 @@ export function FrameMenu({
|
|
|
257
260
|
}
|
|
258
261
|
|
|
259
262
|
const isShared = component.isShared;
|
|
260
|
-
const isReadonly = editContext.mode === "preview" || compareView;
|
|
261
263
|
const isLayout = component.layoutId;
|
|
264
|
+
// Layout components should be read-only when showLayoutComponents is disabled
|
|
265
|
+
const isLayoutReadonly = isLayout && !editContext.showLayoutComponents;
|
|
266
|
+
const isReadonly =
|
|
267
|
+
editContext.mode === "preview" || compareView || isLayoutReadonly;
|
|
262
268
|
|
|
263
269
|
function getColor() {
|
|
264
|
-
if (isReadonly) return "readonly";
|
|
270
|
+
if (isReadonly && !isLayoutReadonly) return "readonly";
|
|
265
271
|
if (editContext?.mode === "suggestions") return "suggestions";
|
|
266
272
|
if (isLayout) return "layout";
|
|
267
273
|
if (isShared) return "shared";
|
|
@@ -487,18 +487,8 @@ export function PageViewerFrame({
|
|
|
487
487
|
|
|
488
488
|
let componentId = findNearestEditableComponentId(target as HTMLElement);
|
|
489
489
|
|
|
490
|
-
//
|
|
491
|
-
|
|
492
|
-
componentId &&
|
|
493
|
-
editContextRef.current &&
|
|
494
|
-
editContextRef.current.page &&
|
|
495
|
-
editContextRef.current.showLayoutComponents === false
|
|
496
|
-
) {
|
|
497
|
-
const comp = getComponentById(componentId, editContextRef.current.page);
|
|
498
|
-
if (comp?.layoutId) {
|
|
499
|
-
componentId = undefined;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
490
|
+
// Layout components can still be selected even when showLayoutComponents is false
|
|
491
|
+
// They will be displayed in read-only mode
|
|
502
492
|
const currentOverlayName = editContextRef.current?.currentOverlay;
|
|
503
493
|
const isGeneratorOverlay = !!(
|
|
504
494
|
currentOverlayName &&
|
|
@@ -530,12 +520,7 @@ export function PageViewerFrame({
|
|
|
530
520
|
for (const el of all) {
|
|
531
521
|
const id = el.getAttribute("data-component-id");
|
|
532
522
|
if (!id) continue;
|
|
533
|
-
//
|
|
534
|
-
if (
|
|
535
|
-
editContextRef.current?.showLayoutComponents === false &&
|
|
536
|
-
getComponentById(id, page)?.layoutId
|
|
537
|
-
)
|
|
538
|
-
continue;
|
|
523
|
+
// Layout components are now selectable even when showLayoutComponents is false
|
|
539
524
|
orderedIds.push(id);
|
|
540
525
|
}
|
|
541
526
|
}
|
|
@@ -688,17 +673,8 @@ export function PageViewerFrame({
|
|
|
688
673
|
|
|
689
674
|
let componentId = findNearestEditableComponentId(target as HTMLElement);
|
|
690
675
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
editContextRef.current &&
|
|
694
|
-
editContextRef.current.page &&
|
|
695
|
-
editContextRef.current.showLayoutComponents === false
|
|
696
|
-
) {
|
|
697
|
-
const comp = getComponentById(componentId, editContextRef.current.page);
|
|
698
|
-
if (comp?.layoutId) {
|
|
699
|
-
componentId = undefined;
|
|
700
|
-
}
|
|
701
|
-
}
|
|
676
|
+
// Layout components can now be right-clicked even when showLayoutComponents is false
|
|
677
|
+
// Context menu will show limited/read-only actions
|
|
702
678
|
|
|
703
679
|
if (componentId) {
|
|
704
680
|
// Only change selection if right-clicking on a component that's not in the current selection
|
|
@@ -721,13 +697,8 @@ export function PageViewerFrame({
|
|
|
721
697
|
.map((id) => getComponentById(id, pageViewContextRef.current!.page!))
|
|
722
698
|
.filter((x) => x) as Component[];
|
|
723
699
|
|
|
724
|
-
//
|
|
725
|
-
|
|
726
|
-
editContextRef.current?.showLayoutComponents === false &&
|
|
727
|
-
selectedComponents.some((c) => c.layoutId)
|
|
728
|
-
) {
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
700
|
+
// Context menu will now show for layout components even when showLayoutComponents is false
|
|
701
|
+
// Commands will be appropriately filtered/disabled
|
|
731
702
|
|
|
732
703
|
const iframeRect = iframe.getBoundingClientRect();
|
|
733
704
|
const adjustedEvent = new MouseEvent("contextmenu", {
|
|
@@ -12,6 +12,7 @@ import { openAiAgentForComment } from "./commentAi";
|
|
|
12
12
|
import { useDebouncedCallback } from "use-debounce";
|
|
13
13
|
import { CommentView } from "./CommentView";
|
|
14
14
|
import { CommentEditor } from "./CommentEditor";
|
|
15
|
+
import { toast } from "sonner";
|
|
15
16
|
|
|
16
17
|
export function Comment({
|
|
17
18
|
comment,
|
|
@@ -71,8 +72,28 @@ export function Comment({
|
|
|
71
72
|
)?.rawValue;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
await createOrUpdateComment(comment);
|
|
75
|
+
const result = await createOrUpdateComment(comment);
|
|
76
|
+
|
|
77
|
+
if (result.type === "error" || result.type === "unauthorized") {
|
|
78
|
+
console.error("Failed to save comment:", result);
|
|
79
|
+
if (result.rawDetails) {
|
|
80
|
+
console.error(
|
|
81
|
+
"Full error details (including stack trace):\n",
|
|
82
|
+
result.rawDetails,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
toast.error(
|
|
86
|
+
result.details || result.summary || "Failed to save comment",
|
|
87
|
+
);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
75
91
|
setIsEditing(false);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error("Failed to save comment:", error);
|
|
94
|
+
toast.error(
|
|
95
|
+
error instanceof Error ? error.message : "Failed to save comment",
|
|
96
|
+
);
|
|
76
97
|
} finally {
|
|
77
98
|
setIsSaving(false);
|
|
78
99
|
}
|
|
@@ -91,15 +112,75 @@ export function Comment({
|
|
|
91
112
|
};
|
|
92
113
|
|
|
93
114
|
const handleDelete = async () => {
|
|
94
|
-
|
|
115
|
+
try {
|
|
116
|
+
const result = await deleteComment(comment);
|
|
117
|
+
|
|
118
|
+
if (result.type === "error" || result.type === "unauthorized") {
|
|
119
|
+
console.error("Failed to delete comment:", result);
|
|
120
|
+
if (result.rawDetails) {
|
|
121
|
+
console.error(
|
|
122
|
+
"Full error details (including stack trace):\n",
|
|
123
|
+
result.rawDetails,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
toast.error(
|
|
127
|
+
result.details || result.summary || "Failed to delete comment",
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error("Failed to delete comment:", error);
|
|
132
|
+
toast.error(
|
|
133
|
+
error instanceof Error ? error.message : "Failed to delete comment",
|
|
134
|
+
);
|
|
135
|
+
}
|
|
95
136
|
};
|
|
96
137
|
|
|
97
138
|
const handleResolve = async () => {
|
|
98
|
-
|
|
139
|
+
try {
|
|
140
|
+
const result = await resolveComment(comment);
|
|
141
|
+
|
|
142
|
+
if (result.type === "error" || result.type === "unauthorized") {
|
|
143
|
+
console.error("Failed to resolve comment:", result);
|
|
144
|
+
if (result.rawDetails) {
|
|
145
|
+
console.error(
|
|
146
|
+
"Full error details (including stack trace):\n",
|
|
147
|
+
result.rawDetails,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
toast.error(
|
|
151
|
+
result.details || result.summary || "Failed to resolve comment",
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error("Failed to resolve comment:", error);
|
|
156
|
+
toast.error(
|
|
157
|
+
error instanceof Error ? error.message : "Failed to resolve comment",
|
|
158
|
+
);
|
|
159
|
+
}
|
|
99
160
|
};
|
|
100
161
|
|
|
101
162
|
const handleUnresolve = async () => {
|
|
102
|
-
|
|
163
|
+
try {
|
|
164
|
+
const result = await unresolveComment(comment);
|
|
165
|
+
|
|
166
|
+
if (result.type === "error" || result.type === "unauthorized") {
|
|
167
|
+
console.error("Failed to unresolve comment:", result);
|
|
168
|
+
if (result.rawDetails) {
|
|
169
|
+
console.error(
|
|
170
|
+
"Full error details (including stack trace):\n",
|
|
171
|
+
result.rawDetails,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
toast.error(
|
|
175
|
+
result.details || result.summary || "Failed to unresolve comment",
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error("Failed to unresolve comment:", error);
|
|
180
|
+
toast.error(
|
|
181
|
+
error instanceof Error ? error.message : "Failed to unresolve comment",
|
|
182
|
+
);
|
|
183
|
+
}
|
|
103
184
|
};
|
|
104
185
|
|
|
105
186
|
const handleAiAction = async () =>
|
|
@@ -517,6 +517,19 @@ export interface GetAgentsResponse {
|
|
|
517
517
|
hasMore: boolean;
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
+
export interface ProfileAgentsGroup {
|
|
521
|
+
profileId: string | null;
|
|
522
|
+
profileName: string;
|
|
523
|
+
profileSvgIcon?: string;
|
|
524
|
+
totalClosedCount: number;
|
|
525
|
+
agents: Agent[];
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
export interface GetAgentsGroupedResponse {
|
|
529
|
+
activeAgents: Agent[];
|
|
530
|
+
closedAgentsByProfile: ProfileAgentsGroup[];
|
|
531
|
+
}
|
|
532
|
+
|
|
520
533
|
/**
|
|
521
534
|
* Gets all agents for the current user with pagination and search support
|
|
522
535
|
*/
|
|
@@ -550,6 +563,70 @@ export async function getActiveAgents(
|
|
|
550
563
|
return result.data || { agents: [], totalCount: 0, hasMore: false };
|
|
551
564
|
}
|
|
552
565
|
|
|
566
|
+
/**
|
|
567
|
+
* Gets all active agents and closed agents grouped by profile for the current user
|
|
568
|
+
*/
|
|
569
|
+
export async function getAgentsGrouped(
|
|
570
|
+
searchTerm?: string,
|
|
571
|
+
includeShared: boolean = true,
|
|
572
|
+
includeOwned: boolean = true,
|
|
573
|
+
): Promise<GetAgentsGroupedResponse> {
|
|
574
|
+
const queryParams = new URLSearchParams();
|
|
575
|
+
|
|
576
|
+
if (searchTerm) queryParams.append("searchTerm", searchTerm);
|
|
577
|
+
queryParams.append("includeShared", includeShared.toString());
|
|
578
|
+
queryParams.append("includeOwned", includeOwned.toString());
|
|
579
|
+
|
|
580
|
+
const queryString = queryParams.toString();
|
|
581
|
+
const url =
|
|
582
|
+
AGENT_BASE_URL +
|
|
583
|
+
"/getAgentsGroupedByProfile" +
|
|
584
|
+
(queryString ? `?${queryString}` : "");
|
|
585
|
+
|
|
586
|
+
const result = await get<GetAgentsGroupedResponse>(url);
|
|
587
|
+
|
|
588
|
+
if (result.type !== "success") {
|
|
589
|
+
throw new Error(
|
|
590
|
+
`Failed to get grouped agents: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return result.data || { activeAgents: [], closedAgentsByProfile: [] };
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Gets more closed agents for a specific profile
|
|
599
|
+
*/
|
|
600
|
+
export async function getClosedAgentsByProfile(
|
|
601
|
+
profileId: string,
|
|
602
|
+
skip: number,
|
|
603
|
+
limit: number,
|
|
604
|
+
searchTerm?: string,
|
|
605
|
+
): Promise<GetAgentsResponse> {
|
|
606
|
+
const queryParams = new URLSearchParams();
|
|
607
|
+
|
|
608
|
+
queryParams.append("profileId", profileId);
|
|
609
|
+
queryParams.append("skip", skip.toString());
|
|
610
|
+
queryParams.append("limit", limit.toString());
|
|
611
|
+
if (searchTerm) queryParams.append("searchTerm", searchTerm);
|
|
612
|
+
|
|
613
|
+
const queryString = queryParams.toString();
|
|
614
|
+
const url =
|
|
615
|
+
AGENT_BASE_URL +
|
|
616
|
+
"/getClosedAgentsByProfile" +
|
|
617
|
+
(queryString ? `?${queryString}` : "");
|
|
618
|
+
|
|
619
|
+
const result = await get<GetAgentsResponse>(url);
|
|
620
|
+
|
|
621
|
+
if (result.type !== "success") {
|
|
622
|
+
throw new Error(
|
|
623
|
+
`Failed to get closed agents by profile: ${result.summary || "Unknown error"} ${result.details || ""}`,
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return result.data || { agents: [], totalCount: 0, hasMore: false };
|
|
628
|
+
}
|
|
629
|
+
|
|
553
630
|
/**
|
|
554
631
|
* Gets all closed agents for the current user
|
|
555
632
|
* @deprecated Use getActiveAgents() instead - the backend now returns all agents (including closed) by default
|
|
@@ -2,47 +2,102 @@ export type ExecutionResult<T> = {
|
|
|
2
2
|
type: "success" | "error" | "unauthorized";
|
|
3
3
|
summary?: string;
|
|
4
4
|
details?: string;
|
|
5
|
+
rawDetails?: string; // Original unprocessed error details (for logging/debugging)
|
|
5
6
|
response: Response;
|
|
6
7
|
data?: T;
|
|
7
8
|
};
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Extracts a user-friendly error message from the response.
|
|
12
|
+
* Handles HTML error pages by extracting the title or exception details.
|
|
13
|
+
* Also extracts source file information if available.
|
|
14
|
+
*/
|
|
15
|
+
function extractErrorMessage(message: string): string {
|
|
16
|
+
// Check if it's an HTML error page
|
|
17
|
+
if (message.includes("<!DOCTYPE html>") || message.includes("<html>")) {
|
|
18
|
+
let errorMessage = "";
|
|
19
|
+
|
|
20
|
+
// Try to extract the title (which usually contains the error message)
|
|
21
|
+
const titleMatch = message.match(/<title>(.*?)<\/title>/i);
|
|
22
|
+
if (titleMatch && titleMatch[1]) {
|
|
23
|
+
// Remove "Server Error" prefix if present
|
|
24
|
+
errorMessage = titleMatch[1].replace(
|
|
25
|
+
/^Server Error in '.*?' Application\.\s*/i,
|
|
26
|
+
"",
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// If no title, try h2 tag (exception message)
|
|
31
|
+
if (!errorMessage) {
|
|
32
|
+
const h2Match = message.match(/<h2>\s*<i>(.*?)<\/i>\s*<\/h2>/i);
|
|
33
|
+
if (h2Match && h2Match[1]) {
|
|
34
|
+
errorMessage = h2Match[1];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If no h2, try Exception Details
|
|
39
|
+
if (!errorMessage) {
|
|
40
|
+
const exceptionMatch = message.match(
|
|
41
|
+
/<b>\s*Exception Details:\s*<\/b>(.*?)(?:<br>|<\/)/i,
|
|
42
|
+
);
|
|
43
|
+
if (exceptionMatch && exceptionMatch[1]) {
|
|
44
|
+
errorMessage = exceptionMatch[1].replace(/<[^>]*>/g, "").trim();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Try to extract source file and line information
|
|
49
|
+
const sourceMatch = message.match(
|
|
50
|
+
/<b>\s*Source File:\s*<\/b>\s*([^<]+)<b>\s* \s*Line:\s*<\/b>\s*(\d+)/i,
|
|
51
|
+
);
|
|
52
|
+
if (sourceMatch && sourceMatch[1] && sourceMatch[2]) {
|
|
53
|
+
const fileName = sourceMatch[1].trim().split("\\").pop();
|
|
54
|
+
errorMessage += ` (${fileName}:${sourceMatch[2]})`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (errorMessage) {
|
|
58
|
+
return errorMessage;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// If we can't parse it, just strip all HTML tags
|
|
62
|
+
const cleaned = message.replace(/<[^>]*>/g, "").trim();
|
|
63
|
+
// Limit length for toast display
|
|
64
|
+
return cleaned.length > 200 ? cleaned.substring(0, 200) + "..." : cleaned;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return message;
|
|
68
|
+
}
|
|
69
|
+
|
|
9
70
|
export async function post<T>(
|
|
10
71
|
url: string,
|
|
11
72
|
body: any,
|
|
12
|
-
session?: string
|
|
73
|
+
session?: string,
|
|
13
74
|
): Promise<ExecutionResult<T>> {
|
|
14
75
|
if (session) url += "?sessionId=" + session;
|
|
15
76
|
|
|
16
|
-
const response = await fetch(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
},
|
|
25
|
-
}
|
|
26
|
-
);
|
|
77
|
+
const response = await fetch(url, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
body: JSON.stringify(body),
|
|
80
|
+
credentials: "include",
|
|
81
|
+
headers: {
|
|
82
|
+
"Content-Type": "application/json",
|
|
83
|
+
},
|
|
84
|
+
});
|
|
27
85
|
|
|
28
86
|
return handleResponse<T>(response);
|
|
29
87
|
}
|
|
30
88
|
|
|
31
89
|
export async function get<T>(
|
|
32
90
|
url: string,
|
|
33
|
-
session?: string
|
|
91
|
+
session?: string,
|
|
34
92
|
): Promise<ExecutionResult<T>> {
|
|
35
93
|
if (session) url += "?sessionId=" + session;
|
|
36
|
-
const response = await fetch(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
},
|
|
44
|
-
}
|
|
45
|
-
);
|
|
94
|
+
const response = await fetch(url, {
|
|
95
|
+
method: "GET",
|
|
96
|
+
credentials: "include",
|
|
97
|
+
headers: {
|
|
98
|
+
"Content-Type": "application/json",
|
|
99
|
+
},
|
|
100
|
+
});
|
|
46
101
|
|
|
47
102
|
return handleResponse<T>(response);
|
|
48
103
|
}
|
|
@@ -54,17 +109,20 @@ async function handleResponse<T>(
|
|
|
54
109
|
const data = await response.json();
|
|
55
110
|
return {
|
|
56
111
|
type: "error",
|
|
57
|
-
summary: data.summary,
|
|
58
|
-
details: data.details,
|
|
112
|
+
summary: data.summary ? extractErrorMessage(data.summary) : data.summary,
|
|
113
|
+
details: data.details ? extractErrorMessage(data.details) : data.details,
|
|
114
|
+
rawDetails: data.details || data.summary, // Preserve original for logging
|
|
59
115
|
response,
|
|
60
116
|
};
|
|
61
117
|
}
|
|
62
118
|
|
|
63
119
|
if (response.status === 500 || response.redirected) {
|
|
120
|
+
const rawText = await response.text();
|
|
64
121
|
return {
|
|
65
122
|
type: "error",
|
|
66
123
|
summary: "Error",
|
|
67
|
-
details:
|
|
124
|
+
details: extractErrorMessage(rawText),
|
|
125
|
+
rawDetails: rawText, // Preserve full HTML error with stack trace
|
|
68
126
|
response,
|
|
69
127
|
};
|
|
70
128
|
}
|
|
@@ -77,9 +135,15 @@ async function handleResponse<T>(
|
|
|
77
135
|
response,
|
|
78
136
|
};
|
|
79
137
|
}
|
|
80
|
-
|
|
138
|
+
|
|
81
139
|
if (response.status !== 200) {
|
|
82
|
-
|
|
140
|
+
const rawText = await response.text();
|
|
141
|
+
return {
|
|
142
|
+
type: "error",
|
|
143
|
+
response,
|
|
144
|
+
details: extractErrorMessage(rawText),
|
|
145
|
+
rawDetails: rawText, // Preserve original for logging
|
|
146
|
+
};
|
|
83
147
|
}
|
|
84
148
|
|
|
85
149
|
if (!response.headers.get("content-type")?.includes("application/json")) {
|
package/src/revision.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = "1.0.
|
|
2
|
-
export const buildDate = "2025-10-23
|
|
1
|
+
export const version = "1.0.4185";
|
|
2
|
+
export const buildDate = "2025-10-23 17:45:06";
|