@parhelia/core 0.1.12393 → 0.1.12404
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/components/ui/input.d.ts +1 -1
- package/dist/components/ui/input.js +5 -3
- package/dist/components/ui/input.js.map +1 -1
- package/dist/editor/FieldHistory.js +47 -17
- package/dist/editor/FieldHistory.js.map +1 -1
- package/dist/editor/PictureCropper.js +9 -4
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/PictureEditor.js +12 -13
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +136 -23
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AgentTerminalStatusBar.d.ts +5 -1
- package/dist/editor/ai/AgentTerminalStatusBar.js +182 -22
- package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
- package/dist/editor/ai/ContentInspectorPopover.js +105 -24
- package/dist/editor/ai/ContentInspectorPopover.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.d.ts +2 -1
- package/dist/editor/ai/ContextInfoBar.js +9 -6
- package/dist/editor/ai/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/agentDiagnostics.d.ts +36 -0
- package/dist/editor/ai/agentDiagnostics.js +120 -0
- package/dist/editor/ai/agentDiagnostics.js.map +1 -0
- package/dist/editor/ai/dialogs/QuestionnaireInline.d.ts +2 -1
- package/dist/editor/ai/dialogs/QuestionnaireInline.js +452 -63
- package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
- package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +27 -5
- package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
- package/dist/editor/client/EditorShell.js +8 -0
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +6 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/hooks/useEditorWebSocket.d.ts +3 -0
- package/dist/editor/client/hooks/useEditorWebSocket.js +66 -1
- package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
- package/dist/editor/client/socketDiagnostics.d.ts +19 -0
- package/dist/editor/client/socketDiagnostics.js +32 -0
- package/dist/editor/client/socketDiagnostics.js.map +1 -0
- package/dist/editor/pictureRawValue.d.ts +3 -0
- package/dist/editor/pictureRawValue.js +30 -0
- package/dist/editor/pictureRawValue.js.map +1 -0
- package/dist/editor/reviews/CreateReviewDetailsStep.d.ts +1 -1
- package/dist/editor/reviews/CreateReviewDialog.js +3 -2
- package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +57 -0
- package/dist/editor/services/agentService.js +30 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +5 -0
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contentService.js.map +1 -1
- package/dist/editor/settings/panels/AgentProfileConfigPanel.js +1 -0
- package/dist/editor/settings/panels/AgentProfileConfigPanel.js.map +1 -1
- package/dist/editor/settings/panels/AgentProfileEditorPanel.d.ts +14 -0
- package/dist/editor/settings/panels/AgentProfileEditorPanel.js +7 -0
- package/dist/editor/settings/panels/AgentProfileEditorPanel.js.map +1 -0
- package/dist/editor/settings/panels/AgentsPanel.js +2 -2
- package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js +132 -82
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
- package/dist/editor/ui/ItemCollectionEditor.d.ts +2 -1
- package/dist/editor/ui/ItemCollectionEditor.js +70 -27
- package/dist/editor/ui/ItemCollectionEditor.js.map +1 -1
- package/dist/editor/ui/TreeListSelector.d.ts +4 -1
- package/dist/editor/ui/TreeListSelector.js +2 -2
- package/dist/editor/ui/TreeListSelector.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/task-board/TaskBoardWorkspace.js +11 -18
- package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
- package/dist/task-board/components/ItemCollectionEditorDialog.js +28 -17
- package/dist/task-board/components/ItemCollectionEditorDialog.js.map +1 -1
- package/dist/task-board/components/ProjectDashboard.d.ts +4 -0
- package/dist/task-board/components/ProjectDashboard.js +1 -1
- package/dist/task-board/components/ProjectDashboard.js.map +1 -1
- package/dist/task-board/components/ProjectListContent.d.ts +1 -1
- package/dist/task-board/components/ProjectListContent.js +4 -1
- package/dist/task-board/components/ProjectListContent.js.map +1 -1
- package/dist/task-board/components/ProjectOverviewContent.d.ts +17 -0
- package/dist/task-board/components/ProjectOverviewContent.js +134 -0
- package/dist/task-board/components/ProjectOverviewContent.js.map +1 -0
- package/dist/task-board/components/ProjectSelector.d.ts +1 -1
- package/dist/task-board/components/ProjectSelector.js +1 -1
- package/dist/task-board/components/ProjectSelector.js.map +1 -1
- package/dist/task-board/components/TaskAssigneePicker.d.ts +3 -2
- package/dist/task-board/components/TaskAssigneePicker.js +10 -6
- package/dist/task-board/components/TaskAssigneePicker.js.map +1 -1
- package/dist/task-board/components/TaskDetailPanel.js +59 -9
- package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
- package/dist/task-board/services/taskService.d.ts +5 -1
- package/dist/task-board/services/taskService.js +11 -0
- package/dist/task-board/services/taskService.js.map +1 -1
- package/dist/task-board/taskBoardNavStore.d.ts +3 -1
- package/dist/task-board/taskBoardNavStore.js.map +1 -1
- package/dist/task-board/types.d.ts +30 -0
- package/dist/task-board/utils/taskDependencyOrdering.d.ts +3 -0
- package/dist/task-board/utils/taskDependencyOrdering.js +64 -0
- package/dist/task-board/utils/taskDependencyOrdering.js.map +1 -0
- package/dist/task-board/views/WizardView.js +5 -15
- package/dist/task-board/views/WizardView.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,43 +1,327 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useCallback, useMemo, useEffect, useRef } from "react";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useCallback, useMemo, useEffect, useRef, } from "react";
|
|
3
3
|
import { Button } from "../../../components/ui/button";
|
|
4
4
|
import { Checkbox } from "../../../components/ui/checkbox";
|
|
5
5
|
import { Textarea } from "../../../components/ui/textarea";
|
|
6
|
-
import { X, ClipboardList, MessageSquare, AlertCircle } from "lucide-react";
|
|
6
|
+
import { X, ClipboardList, MessageSquare, AlertCircle, FileUp, Loader2, Paperclip, Trash2, } from "lucide-react";
|
|
7
|
+
import { ItemCollectionEditor, } from "../../ui/ItemCollectionEditor";
|
|
7
8
|
import { TreeListSelector } from "../../ui/TreeListSelector";
|
|
8
9
|
import { useEditContext } from "../../client/editContext";
|
|
9
10
|
import { cn } from "../../../lib/utils";
|
|
11
|
+
import { uploadDocument } from "../../services/agentService";
|
|
12
|
+
import { uploadAttachmentFile } from "../../../task-board/services/taskService";
|
|
10
13
|
const questionnaireDrafts = new Map();
|
|
14
|
+
const questionnairePendingFileDrafts = new Map();
|
|
15
|
+
const QUESTIONNAIRE_MAX_FILE_SIZE_MB = 10;
|
|
16
|
+
const QUESTIONNAIRE_MAX_FILE_SIZE_BYTES = QUESTIONNAIRE_MAX_FILE_SIZE_MB * 1024 * 1024;
|
|
17
|
+
const QUESTIONNAIRE_FILE_ACCEPT = ".txt,.md,.pdf,.docx,.xlsx,.pptx,.csv,.json,.xml,.cs,.js,.ts,.tsx,.jsx,.py,.java,.cpp,.c,.h,.hpp,.cshtml,.razor,.vb,.php,.rb,.go,.rs,.swift,.kt,.scala,.sql,.sh,.ps1,.bat,.yaml,.yml,.png,.jpg,.jpeg,.gif,.webp";
|
|
18
|
+
function isItemSelectionType(type) {
|
|
19
|
+
return type === "item-picker" || type === "item-collection";
|
|
20
|
+
}
|
|
21
|
+
function isFileUploadType(type) {
|
|
22
|
+
return type === "file-upload";
|
|
23
|
+
}
|
|
24
|
+
function createEmptyAnswer(questionId) {
|
|
25
|
+
return {
|
|
26
|
+
questionId,
|
|
27
|
+
selectedOptions: [],
|
|
28
|
+
selectedItems: [],
|
|
29
|
+
uploadedDocuments: [],
|
|
30
|
+
freetext: undefined,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function toUploadedDocumentReference(document) {
|
|
34
|
+
if (!document) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
id: document.id,
|
|
39
|
+
agentId: document.agentId,
|
|
40
|
+
fileName: document.fileName,
|
|
41
|
+
fileSize: document.fileSize,
|
|
42
|
+
contentType: document.contentType,
|
|
43
|
+
uploadedBy: document.uploadedBy,
|
|
44
|
+
uploadedDate: document.uploadedDate,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function toItemCollectionItems(selectedItems, language) {
|
|
48
|
+
return (selectedItems ?? []).map((item) => ({
|
|
49
|
+
descriptor: {
|
|
50
|
+
id: item.id,
|
|
51
|
+
language,
|
|
52
|
+
version: 1,
|
|
53
|
+
name: item.name || "",
|
|
54
|
+
displayName: item.name || "",
|
|
55
|
+
path: item.path || "",
|
|
56
|
+
},
|
|
57
|
+
includeSubitems: Boolean(item.includeSubitems),
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
function toSelectedItems(items) {
|
|
61
|
+
return items.map((item) => ({
|
|
62
|
+
id: item.descriptor.id,
|
|
63
|
+
name: item.descriptor.displayName ||
|
|
64
|
+
item.descriptor.name ||
|
|
65
|
+
item.descriptor.path ||
|
|
66
|
+
item.descriptor.id,
|
|
67
|
+
path: item.descriptor.path,
|
|
68
|
+
includeSubitems: item.includeSubitems,
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
11
71
|
function createInitialAnswers(parameters, savedAnswers) {
|
|
12
72
|
const initial = new Map();
|
|
13
73
|
parameters.questions.forEach((q) => {
|
|
14
|
-
const preselectedItems = q.type
|
|
74
|
+
const preselectedItems = isItemSelectionType(q.type)
|
|
15
75
|
? (q.preselectedItemIds ?? [])
|
|
16
|
-
.slice(q.allowMultiple ?
|
|
76
|
+
.slice(q.type === "item-picker" && !q.allowMultiple ? -1 : 0)
|
|
17
77
|
.filter((id) => Boolean(id && id.trim()))
|
|
18
|
-
.map((id) => ({
|
|
78
|
+
.map((id) => ({
|
|
79
|
+
id,
|
|
80
|
+
includeSubitems: false,
|
|
81
|
+
}))
|
|
19
82
|
: [];
|
|
20
83
|
const savedAnswer = savedAnswers?.get(q.id);
|
|
21
|
-
const isTextQuestion = q.type
|
|
84
|
+
const isTextQuestion = !isItemSelectionType(q.type) &&
|
|
85
|
+
!isFileUploadType(q.type) &&
|
|
86
|
+
(q.options?.length ?? 0) === 0;
|
|
22
87
|
initial.set(q.id, {
|
|
23
88
|
questionId: q.id,
|
|
24
89
|
selectedOptions: savedAnswer?.selectedOptions ?? [],
|
|
25
90
|
selectedItems: savedAnswer?.selectedItems ?? preselectedItems,
|
|
91
|
+
uploadedDocuments: savedAnswer?.uploadedDocuments ?? [],
|
|
26
92
|
freetext: savedAnswer?.freetext ?? (isTextQuestion ? q.default : undefined),
|
|
27
93
|
});
|
|
28
94
|
});
|
|
29
95
|
return initial;
|
|
30
96
|
}
|
|
97
|
+
function createInitialPendingFiles(parameters, savedFiles) {
|
|
98
|
+
const initial = new Map();
|
|
99
|
+
parameters.questions.forEach((question) => {
|
|
100
|
+
if (!isFileUploadType(question.type)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
initial.set(question.id, [...(savedFiles?.get(question.id) ?? [])]);
|
|
104
|
+
});
|
|
105
|
+
return initial;
|
|
106
|
+
}
|
|
107
|
+
function QuestionnaireFileUploadField({ questionId, agentId, allowMultiple, selectedFiles, submissionError, disabled, attachToTaskIfAvailable, taskId, onChange, }) {
|
|
108
|
+
const [isDragOver, setIsDragOver] = useState(false);
|
|
109
|
+
const [selectionError, setSelectionError] = useState(null);
|
|
110
|
+
const fileInputRef = useRef(null);
|
|
111
|
+
const handleFiles = useCallback((files) => {
|
|
112
|
+
if (!files || files.length === 0) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (!agentId) {
|
|
116
|
+
setSelectionError("File upload is unavailable because no agent is associated with this questionnaire.");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const nextValidFiles = [];
|
|
120
|
+
let nextError = null;
|
|
121
|
+
for (const file of Array.from(files)) {
|
|
122
|
+
if (file.size > QUESTIONNAIRE_MAX_FILE_SIZE_BYTES) {
|
|
123
|
+
nextError = `File "${file.name}" exceeds the maximum size of ${QUESTIONNAIRE_MAX_FILE_SIZE_MB} MB.`;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
nextValidFiles.push(file);
|
|
127
|
+
}
|
|
128
|
+
if (nextValidFiles.length === 0) {
|
|
129
|
+
setSelectionError(nextError);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
setSelectionError(nextError);
|
|
133
|
+
onChange(allowMultiple
|
|
134
|
+
? [...selectedFiles, ...nextValidFiles]
|
|
135
|
+
: nextValidFiles.slice(0, 1));
|
|
136
|
+
}, [agentId, allowMultiple, onChange, selectedFiles]);
|
|
137
|
+
const removeSelectedFile = useCallback((fileIndex) => {
|
|
138
|
+
onChange(selectedFiles.filter((_, index) => index !== fileIndex));
|
|
139
|
+
}, [onChange, selectedFiles]);
|
|
140
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { "data-testid": "questionnaire-file-upload", "data-question-id": questionId, className: cn("rounded-lg border-2 border-dashed p-3 transition-colors", isDragOver
|
|
141
|
+
? "border-indigo-400 bg-indigo-50"
|
|
142
|
+
: "border-gray-200 bg-gray-50/70"), onDragOver: (event) => {
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
event.stopPropagation();
|
|
145
|
+
setIsDragOver(true);
|
|
146
|
+
}, onDragEnter: (event) => {
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
event.stopPropagation();
|
|
149
|
+
setIsDragOver(true);
|
|
150
|
+
}, onDragLeave: (event) => {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
event.stopPropagation();
|
|
153
|
+
setIsDragOver(false);
|
|
154
|
+
}, onDrop: (event) => {
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
event.stopPropagation();
|
|
157
|
+
setIsDragOver(false);
|
|
158
|
+
handleFiles(event.dataTransfer.files);
|
|
159
|
+
}, children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(FileUp, { className: "mt-0.5 h-4 w-4 shrink-0 text-indigo-500" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-xs font-medium text-gray-700", children: allowMultiple
|
|
160
|
+
? "Drop files here or browse to attach"
|
|
161
|
+
: "Drop a file here or browse to attach" }), _jsxs("p", { className: "mt-0.5 text-[11px] text-gray-500", children: ["Files stay local until you submit the questionnaire. Max", " ", QUESTIONNAIRE_MAX_FILE_SIZE_MB, " MB per file.", attachToTaskIfAvailable && taskId
|
|
162
|
+
? " Submitted files will also be attached to the current task."
|
|
163
|
+
: ""] }), _jsxs("div", { className: "mt-2", children: [_jsx("input", { ref: fileInputRef, "data-testid": "questionnaire-file-input", "data-question-id": questionId, type: "file", className: "hidden", multiple: allowMultiple, accept: QUESTIONNAIRE_FILE_ACCEPT, onChange: (event) => {
|
|
164
|
+
handleFiles(event.target.files);
|
|
165
|
+
event.target.value = "";
|
|
166
|
+
} }), _jsx(Button, { type: "button", variant: "outline", size: "sm", disabled: disabled || !agentId, onClick: () => fileInputRef.current?.click(), className: "h-8 text-xs", children: allowMultiple ? "Browse Files" : "Browse File" })] })] })] }) }), (selectionError || submissionError) && (_jsx("div", { "data-testid": "questionnaire-file-upload-error", "data-question-id": questionId, className: "rounded border border-red-200 bg-red-50 px-2 py-1.5 text-[11px] text-red-700", children: selectionError || submissionError })), selectedFiles.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2", children: selectedFiles.map((file, index) => (_jsxs("span", { "data-testid": "questionnaire-selected-file", "data-question-id": questionId, className: "inline-flex items-center gap-1.5 rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-700", title: file.name, children: [_jsx(Paperclip, { className: "h-3 w-3 text-gray-500" }), _jsx("span", { className: "max-w-[220px] truncate", children: file.name }), _jsx("button", { type: "button", "data-testid": "questionnaire-remove-selected-file", "data-file-index": index, onClick: () => removeSelectedFile(index), className: "rounded p-0.5 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600", "aria-label": `Remove ${file.name}`, children: _jsx(Trash2, { className: "h-3 w-3" }) })] }, `${file.name}-${file.size}-${index}`))) }))] }));
|
|
167
|
+
}
|
|
168
|
+
function QuestionnaireItemCollectionField({ questionId, rootItemId, answer, language, onChange, }) {
|
|
169
|
+
const editContext = useEditContext();
|
|
170
|
+
const items = useMemo(() => toItemCollectionItems(answer?.selectedItems, language), [answer?.selectedItems, language]);
|
|
171
|
+
const [selectedInTree, setSelectedInTree] = useState([]);
|
|
172
|
+
const [selectedFromList, setSelectedFromList] = useState([]);
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
setSelectedInTree([]);
|
|
175
|
+
setSelectedFromList([]);
|
|
176
|
+
}, [answer?.selectedItems]);
|
|
177
|
+
const commitItems = useCallback((nextItems) => {
|
|
178
|
+
onChange(toSelectedItems(nextItems));
|
|
179
|
+
}, [onChange]);
|
|
180
|
+
const addToList = useCallback(async (itemsToAdd) => {
|
|
181
|
+
const nextItems = [...items];
|
|
182
|
+
const sourceItems = itemsToAdd ?? selectedInTree;
|
|
183
|
+
const nodesToAdd = sourceItems.filter((node) => !nextItems.some((item) => item.descriptor.id === node.id));
|
|
184
|
+
const nodesNeedingFetch = nodesToAdd.filter((node) => !node.path || !node.name || !node.displayName);
|
|
185
|
+
if (nodesNeedingFetch.length > 0 && editContext?.itemsRepository) {
|
|
186
|
+
try {
|
|
187
|
+
const stubs = await editContext.itemsRepository.getItemsStubs(nodesNeedingFetch.map((node) => ({
|
|
188
|
+
id: node.id,
|
|
189
|
+
language: node.language || language,
|
|
190
|
+
version: node.version || 1,
|
|
191
|
+
})));
|
|
192
|
+
const stubMap = new Map(stubs.map((stub) => [stub.id, stub]));
|
|
193
|
+
nodesToAdd.forEach((node) => {
|
|
194
|
+
const stub = stubMap.get(node.id);
|
|
195
|
+
nextItems.push({
|
|
196
|
+
descriptor: {
|
|
197
|
+
id: node.id,
|
|
198
|
+
language: node.language || language,
|
|
199
|
+
version: node.version || 1,
|
|
200
|
+
name: stub?.name || node.name || "",
|
|
201
|
+
displayName: stub?.displayName || node.displayName || "",
|
|
202
|
+
path: stub?.path || node.path || node.idPath || "",
|
|
203
|
+
},
|
|
204
|
+
includeSubitems: false,
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
console.error("Failed to fetch item stubs:", error);
|
|
210
|
+
nodesToAdd.forEach((node) => {
|
|
211
|
+
nextItems.push({
|
|
212
|
+
descriptor: {
|
|
213
|
+
id: node.id,
|
|
214
|
+
language: node.language || language,
|
|
215
|
+
version: node.version || 1,
|
|
216
|
+
name: node.name || "",
|
|
217
|
+
displayName: node.displayName || "",
|
|
218
|
+
path: node.path || node.idPath || "",
|
|
219
|
+
},
|
|
220
|
+
includeSubitems: false,
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
nodesToAdd.forEach((node) => {
|
|
227
|
+
nextItems.push({
|
|
228
|
+
descriptor: {
|
|
229
|
+
id: node.id,
|
|
230
|
+
language: node.language || language,
|
|
231
|
+
version: node.version || 1,
|
|
232
|
+
name: node.name || "",
|
|
233
|
+
displayName: node.displayName || "",
|
|
234
|
+
path: node.path || node.idPath || "",
|
|
235
|
+
},
|
|
236
|
+
includeSubitems: false,
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
commitItems(nextItems);
|
|
241
|
+
setSelectedInTree([]);
|
|
242
|
+
setSelectedFromList([]);
|
|
243
|
+
}, [
|
|
244
|
+
items,
|
|
245
|
+
selectedInTree,
|
|
246
|
+
editContext?.itemsRepository,
|
|
247
|
+
language,
|
|
248
|
+
commitItems,
|
|
249
|
+
]);
|
|
250
|
+
const handleAddItem = useCallback(async (item) => {
|
|
251
|
+
const itemId = item.id;
|
|
252
|
+
if (items.some((existing) => existing.descriptor.id === itemId)) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
let itemDescriptor = {
|
|
256
|
+
id: itemId,
|
|
257
|
+
language: item.language || language,
|
|
258
|
+
version: item.version ?? 1,
|
|
259
|
+
name: item.name || "",
|
|
260
|
+
displayName: item.displayName || "",
|
|
261
|
+
path: item.path || "",
|
|
262
|
+
};
|
|
263
|
+
if ((!itemDescriptor.path ||
|
|
264
|
+
!itemDescriptor.name ||
|
|
265
|
+
!itemDescriptor.displayName) &&
|
|
266
|
+
editContext?.itemsRepository) {
|
|
267
|
+
try {
|
|
268
|
+
const stubs = await editContext.itemsRepository.getItemsStubs([
|
|
269
|
+
{
|
|
270
|
+
id: itemId,
|
|
271
|
+
language: itemDescriptor.language,
|
|
272
|
+
version: itemDescriptor.version,
|
|
273
|
+
},
|
|
274
|
+
]);
|
|
275
|
+
if (stubs[0]) {
|
|
276
|
+
itemDescriptor = {
|
|
277
|
+
...itemDescriptor,
|
|
278
|
+
name: stubs[0].name || itemDescriptor.name,
|
|
279
|
+
displayName: stubs[0].displayName || itemDescriptor.displayName,
|
|
280
|
+
path: stubs[0].path || itemDescriptor.path,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
console.error("Failed to fetch item stub:", error);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
commitItems([
|
|
289
|
+
...items,
|
|
290
|
+
{
|
|
291
|
+
descriptor: itemDescriptor,
|
|
292
|
+
includeSubitems: false,
|
|
293
|
+
},
|
|
294
|
+
]);
|
|
295
|
+
setSelectedFromList([]);
|
|
296
|
+
}, [items, editContext?.itemsRepository, language, commitItems]);
|
|
297
|
+
const removeFromList = useCallback(() => {
|
|
298
|
+
const idsToRemove = new Set(selectedFromList.map((item) => item.descriptor.id));
|
|
299
|
+
commitItems(items.filter((item) => !idsToRemove.has(item.descriptor.id)));
|
|
300
|
+
setSelectedFromList([]);
|
|
301
|
+
}, [items, selectedFromList, commitItems]);
|
|
302
|
+
const removeItem = useCallback((index) => {
|
|
303
|
+
commitItems(items.filter((_, itemIndex) => itemIndex !== index));
|
|
304
|
+
setSelectedFromList([]);
|
|
305
|
+
}, [items, commitItems]);
|
|
306
|
+
const toggleSubitems = useCallback((index) => {
|
|
307
|
+
commitItems(items.map((item, itemIndex) => itemIndex === index
|
|
308
|
+
? { ...item, includeSubitems: !item.includeSubitems }
|
|
309
|
+
: item));
|
|
310
|
+
setSelectedFromList([]);
|
|
311
|
+
}, [items, commitItems]);
|
|
312
|
+
return (_jsx(ItemCollectionEditor, { items: items, selectedInTree: selectedInTree, onSelectedInTreeChange: setSelectedInTree, selectedFromList: selectedFromList, onSelectedFromListChange: setSelectedFromList, onAddToList: addToList, onRemoveFromList: removeFromList, onAddItem: handleAddItem, onRemoveItem: removeItem, onToggleSubitems: toggleSubitems, language: language, rootItemIds: rootItemId ? [rootItemId] : undefined, selectedItemsLabel: "Selected Items", emptyMessage: "No items selected", emptyHint: "Browse or search to add items", height: 320, localStorageKey: `questionnaire-item-collection-${questionId}` }));
|
|
313
|
+
}
|
|
31
314
|
/**
|
|
32
315
|
* QuestionnaireInline - Inline component for displaying a questionnaire
|
|
33
316
|
*
|
|
34
317
|
* This is rendered directly inside the AgentTerminal rather than as a modal dialog.
|
|
35
318
|
* Displays questions with multiple-choice options and optional freetext input.
|
|
36
319
|
*/
|
|
37
|
-
export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Questionnaire", description, parameters, footerActions, }) {
|
|
320
|
+
export function QuestionnaireInline({ requestId, agentId, onClose, onCancel, title = "Questionnaire", description, parameters, footerActions, }) {
|
|
38
321
|
const editContext = useEditContext();
|
|
39
322
|
const language = editContext?.currentItemDescriptor?.language || "en";
|
|
40
323
|
const questionsScrollAreaRef = useRef(null);
|
|
324
|
+
const taskId = parameters.taskId?.trim() || undefined;
|
|
41
325
|
const [isHighlighted, setIsHighlighted] = useState(false);
|
|
42
326
|
useEffect(() => {
|
|
43
327
|
// Flash highlight on mount to grab user's attention
|
|
@@ -51,8 +335,14 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
51
335
|
}, [parameters.questions.length, requestId, title]);
|
|
52
336
|
// State for each question's answers: Map<questionId, { selectedOptions, freetext }>
|
|
53
337
|
const [answers, setAnswers] = useState(() => createInitialAnswers(parameters, requestId ? questionnaireDrafts.get(requestId) : undefined));
|
|
338
|
+
const [pendingFiles, setPendingFiles] = useState(() => createInitialPendingFiles(parameters, requestId ? questionnairePendingFileDrafts.get(requestId) : undefined));
|
|
339
|
+
const [fileUploadErrors, setFileUploadErrors] = useState(() => new Map());
|
|
340
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
54
341
|
useEffect(() => {
|
|
55
342
|
setAnswers(createInitialAnswers(parameters, requestId ? questionnaireDrafts.get(requestId) : undefined));
|
|
343
|
+
setPendingFiles(createInitialPendingFiles(parameters, requestId ? questionnairePendingFileDrafts.get(requestId) : undefined));
|
|
344
|
+
setFileUploadErrors(new Map());
|
|
345
|
+
setIsSubmitting(false);
|
|
56
346
|
}, [parameters, requestId]);
|
|
57
347
|
useEffect(() => {
|
|
58
348
|
const scrollArea = questionsScrollAreaRef.current;
|
|
@@ -67,15 +357,16 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
67
357
|
}
|
|
68
358
|
questionnaireDrafts.set(requestId, new Map(answers));
|
|
69
359
|
}, [answers, requestId]);
|
|
360
|
+
useEffect(() => {
|
|
361
|
+
if (!requestId) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
questionnairePendingFileDrafts.set(requestId, new Map(pendingFiles));
|
|
365
|
+
}, [pendingFiles, requestId]);
|
|
70
366
|
const updateSelectedItems = useCallback((questionId, items) => {
|
|
71
367
|
setAnswers((prev) => {
|
|
72
368
|
const newMap = new Map(prev);
|
|
73
|
-
const currentAnswer = newMap.get(questionId) ||
|
|
74
|
-
questionId,
|
|
75
|
-
selectedOptions: [],
|
|
76
|
-
selectedItems: [],
|
|
77
|
-
freetext: undefined,
|
|
78
|
-
};
|
|
369
|
+
const currentAnswer = newMap.get(questionId) || createEmptyAnswer(questionId);
|
|
79
370
|
newMap.set(questionId, {
|
|
80
371
|
...currentAnswer,
|
|
81
372
|
selectedItems: items,
|
|
@@ -86,12 +377,7 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
86
377
|
const upsertSelectedItem = useCallback((questionId, item, allowMultiple) => {
|
|
87
378
|
setAnswers((prev) => {
|
|
88
379
|
const newMap = new Map(prev);
|
|
89
|
-
const currentAnswer = newMap.get(questionId) ||
|
|
90
|
-
questionId,
|
|
91
|
-
selectedOptions: [],
|
|
92
|
-
selectedItems: [],
|
|
93
|
-
freetext: undefined,
|
|
94
|
-
};
|
|
380
|
+
const currentAnswer = newMap.get(questionId) || createEmptyAnswer(questionId);
|
|
95
381
|
const existing = currentAnswer.selectedItems ?? [];
|
|
96
382
|
const next = allowMultiple
|
|
97
383
|
? existing.some((i) => i.id === item.id)
|
|
@@ -105,16 +391,26 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
105
391
|
return newMap;
|
|
106
392
|
});
|
|
107
393
|
}, []);
|
|
394
|
+
const updatePendingFiles = useCallback((questionId, files) => {
|
|
395
|
+
setPendingFiles((prev) => {
|
|
396
|
+
const newMap = new Map(prev);
|
|
397
|
+
newMap.set(questionId, files);
|
|
398
|
+
return newMap;
|
|
399
|
+
});
|
|
400
|
+
setFileUploadErrors((prev) => {
|
|
401
|
+
if (!prev.has(questionId)) {
|
|
402
|
+
return prev;
|
|
403
|
+
}
|
|
404
|
+
const newMap = new Map(prev);
|
|
405
|
+
newMap.delete(questionId);
|
|
406
|
+
return newMap;
|
|
407
|
+
});
|
|
408
|
+
}, []);
|
|
108
409
|
// Toggle option selection for a question
|
|
109
410
|
const toggleOption = useCallback((questionId, optionId, allowMultiple) => {
|
|
110
411
|
setAnswers((prev) => {
|
|
111
412
|
const newMap = new Map(prev);
|
|
112
|
-
const currentAnswer = newMap.get(questionId) ||
|
|
113
|
-
questionId,
|
|
114
|
-
selectedOptions: [],
|
|
115
|
-
selectedItems: [],
|
|
116
|
-
freetext: undefined,
|
|
117
|
-
};
|
|
413
|
+
const currentAnswer = newMap.get(questionId) || createEmptyAnswer(questionId);
|
|
118
414
|
let newSelectedOptions;
|
|
119
415
|
if (allowMultiple) {
|
|
120
416
|
// Multi-select: toggle the option
|
|
@@ -145,12 +441,7 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
145
441
|
const updateFreetext = useCallback((questionId, text) => {
|
|
146
442
|
setAnswers((prev) => {
|
|
147
443
|
const newMap = new Map(prev);
|
|
148
|
-
const currentAnswer = newMap.get(questionId) ||
|
|
149
|
-
questionId,
|
|
150
|
-
selectedOptions: [],
|
|
151
|
-
selectedItems: [],
|
|
152
|
-
freetext: undefined,
|
|
153
|
-
};
|
|
444
|
+
const currentAnswer = newMap.get(questionId) || createEmptyAnswer(questionId);
|
|
154
445
|
newMap.set(questionId, {
|
|
155
446
|
...currentAnswer,
|
|
156
447
|
freetext: text || undefined,
|
|
@@ -165,12 +456,16 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
165
456
|
const answer = answers.get(question.id);
|
|
166
457
|
if (!answer)
|
|
167
458
|
return false;
|
|
168
|
-
if (question.type
|
|
459
|
+
if (isItemSelectionType(question.type)) {
|
|
169
460
|
return (answer.selectedItems?.length ?? 0) > 0;
|
|
170
461
|
}
|
|
462
|
+
if (isFileUploadType(question.type)) {
|
|
463
|
+
return ((pendingFiles.get(question.id)?.length ?? 0) > 0 ||
|
|
464
|
+
(answer.uploadedDocuments?.length ?? 0) > 0);
|
|
465
|
+
}
|
|
171
466
|
return (answer.selectedOptions.length > 0 ||
|
|
172
467
|
(answer.freetext !== undefined && answer.freetext.trim() !== ""));
|
|
173
|
-
}, [answers]);
|
|
468
|
+
}, [answers, pendingFiles]);
|
|
174
469
|
// Check if all required questions have been answered
|
|
175
470
|
const allRequiredQuestionsAnswered = useMemo(() => {
|
|
176
471
|
return parameters.questions.every((q) => q.optional || isQuestionAnswered(q));
|
|
@@ -181,13 +476,17 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
181
476
|
const answer = answers.get(q.id);
|
|
182
477
|
if (!answer)
|
|
183
478
|
return false;
|
|
184
|
-
if (q.type
|
|
479
|
+
if (isItemSelectionType(q.type)) {
|
|
185
480
|
return (answer.selectedItems?.length ?? 0) > 0;
|
|
186
481
|
}
|
|
482
|
+
if (isFileUploadType(q.type)) {
|
|
483
|
+
return ((pendingFiles.get(q.id)?.length ?? 0) > 0 ||
|
|
484
|
+
(answer.uploadedDocuments?.length ?? 0) > 0);
|
|
485
|
+
}
|
|
187
486
|
return (answer.selectedOptions.length > 0 ||
|
|
188
487
|
(answer.freetext !== undefined && answer.freetext.trim() !== ""));
|
|
189
488
|
}).length;
|
|
190
|
-
}, [parameters.questions, answers]);
|
|
489
|
+
}, [parameters.questions, answers, pendingFiles]);
|
|
191
490
|
const requiredQuestionCount = useMemo(() => {
|
|
192
491
|
return parameters.questions.filter((q) => !q.optional).length;
|
|
193
492
|
}, [parameters.questions]);
|
|
@@ -195,20 +494,97 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
195
494
|
return parameters.questions.filter((q) => !q.optional && isQuestionAnswered(q)).length;
|
|
196
495
|
}, [parameters.questions, isQuestionAnswered]);
|
|
197
496
|
// Handle submit
|
|
198
|
-
const handleSubmit = useCallback(() => {
|
|
199
|
-
if (
|
|
200
|
-
|
|
497
|
+
const handleSubmit = useCallback(async () => {
|
|
498
|
+
if (isSubmitting) {
|
|
499
|
+
return;
|
|
201
500
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
501
|
+
setIsSubmitting(true);
|
|
502
|
+
setFileUploadErrors(new Map());
|
|
503
|
+
const nextAnswers = [];
|
|
504
|
+
const nextErrors = new Map();
|
|
505
|
+
try {
|
|
506
|
+
for (const question of parameters.questions) {
|
|
507
|
+
const currentAnswer = answers.get(question.id) || createEmptyAnswer(question.id);
|
|
508
|
+
if (!isFileUploadType(question.type)) {
|
|
509
|
+
nextAnswers.push(currentAnswer);
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
const files = pendingFiles.get(question.id) ?? [];
|
|
513
|
+
if (files.length === 0) {
|
|
514
|
+
nextAnswers.push({
|
|
515
|
+
...currentAnswer,
|
|
516
|
+
uploadedDocuments: currentAnswer.uploadedDocuments ?? [],
|
|
517
|
+
});
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
if (!agentId) {
|
|
521
|
+
nextErrors.set(question.id, "File upload is unavailable because no agent is associated with this questionnaire.");
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
const uploadedDocuments = [];
|
|
525
|
+
for (const file of files) {
|
|
526
|
+
const result = await uploadDocument(agentId, file);
|
|
527
|
+
if (!result.success || !result.document) {
|
|
528
|
+
nextErrors.set(question.id, result.error || `Failed to upload "${file.name}".`);
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
const uploadedDocument = toUploadedDocumentReference(result.document);
|
|
532
|
+
if (!uploadedDocument) {
|
|
533
|
+
nextErrors.set(question.id, `Failed to capture upload metadata for "${file.name}".`);
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
if (question.attachToTaskIfAvailable && taskId) {
|
|
537
|
+
const attachmentResult = await uploadAttachmentFile(taskId, file);
|
|
538
|
+
if (attachmentResult.type !== "success") {
|
|
539
|
+
nextErrors.set(question.id, attachmentResult.details ||
|
|
540
|
+
attachmentResult.summary ||
|
|
541
|
+
`Failed to attach "${file.name}" to the current task.`);
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
uploadedDocuments.push(uploadedDocument);
|
|
546
|
+
}
|
|
547
|
+
if (nextErrors.has(question.id)) {
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
nextAnswers.push({
|
|
551
|
+
...currentAnswer,
|
|
552
|
+
uploadedDocuments,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
if (nextErrors.size > 0) {
|
|
556
|
+
setFileUploadErrors(nextErrors);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (requestId) {
|
|
560
|
+
questionnaireDrafts.delete(requestId);
|
|
561
|
+
questionnairePendingFileDrafts.delete(requestId);
|
|
562
|
+
}
|
|
563
|
+
const result = {
|
|
564
|
+
answers: nextAnswers.filter((answer) => answer.selectedOptions.length > 0 ||
|
|
565
|
+
(answer.selectedItems?.length ?? 0) > 0 ||
|
|
566
|
+
(answer.uploadedDocuments?.length ?? 0) > 0 ||
|
|
567
|
+
(answer.freetext !== undefined && answer.freetext.trim() !== "")),
|
|
568
|
+
};
|
|
569
|
+
onClose(result);
|
|
570
|
+
}
|
|
571
|
+
finally {
|
|
572
|
+
setIsSubmitting(false);
|
|
573
|
+
}
|
|
574
|
+
}, [
|
|
575
|
+
agentId,
|
|
576
|
+
answers,
|
|
577
|
+
isSubmitting,
|
|
578
|
+
onClose,
|
|
579
|
+
parameters.questions,
|
|
580
|
+
pendingFiles,
|
|
581
|
+
requestId,
|
|
582
|
+
taskId,
|
|
583
|
+
]);
|
|
209
584
|
const handleCancel = useCallback(() => {
|
|
210
585
|
if (requestId) {
|
|
211
586
|
questionnaireDrafts.delete(requestId);
|
|
587
|
+
questionnairePendingFileDrafts.delete(requestId);
|
|
212
588
|
}
|
|
213
589
|
onCancel();
|
|
214
590
|
}, [onCancel, requestId]);
|
|
@@ -226,9 +602,13 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
226
602
|
const answer = answers.get(question.id);
|
|
227
603
|
const isAnswered = isQuestionAnswered(question);
|
|
228
604
|
const isItemPicker = question.type === "item-picker";
|
|
229
|
-
|
|
605
|
+
const isItemCollection = question.type === "item-collection";
|
|
606
|
+
const isFileUpload = isFileUploadType(question.type);
|
|
607
|
+
const isItemSelection = isItemPicker || isItemCollection;
|
|
608
|
+
const isStructuredQuestion = isItemSelection || isFileUpload;
|
|
609
|
+
// Auto-enable freetext if options array is empty, but not for structured question types.
|
|
230
610
|
const hasOptions = question.options && question.options.length > 0;
|
|
231
|
-
const effectiveAllowFreetext =
|
|
611
|
+
const effectiveAllowFreetext = isStructuredQuestion
|
|
232
612
|
? (question.allowFreetext ?? false)
|
|
233
613
|
: question.allowFreetext || !hasOptions;
|
|
234
614
|
const effectiveRootItemId = question.rootItemId || question.root;
|
|
@@ -236,17 +616,26 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
236
616
|
? "border-green-200 bg-green-50/30"
|
|
237
617
|
: "border-gray-200 bg-white"}`, children: [_jsxs("div", { className: "mb-2 flex items-start gap-2", children: [_jsx("span", { className: `flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-[10px] font-semibold ${isAnswered
|
|
238
618
|
? "bg-green-100 text-green-700"
|
|
239
|
-
: "bg-gray-100 text-gray-500"}`, children: index + 1 }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium text-gray-800", children: question.prompt }), _jsxs("p", { className: "mt-0.5 text-[10px] text-gray-400", children: [
|
|
240
|
-
?
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
:
|
|
246
|
-
?
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
619
|
+
: "bg-gray-100 text-gray-500"}`, children: index + 1 }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium text-gray-800", children: question.prompt }), _jsxs("p", { className: "mt-0.5 text-[10px] text-gray-400", children: [isItemCollection
|
|
620
|
+
? "Select items and choose whether to include subitems"
|
|
621
|
+
: isItemPicker
|
|
622
|
+
? question.allowMultiple
|
|
623
|
+
? "Select one or more items"
|
|
624
|
+
: "Select one item"
|
|
625
|
+
: isFileUpload
|
|
626
|
+
? question.allowMultiple
|
|
627
|
+
? "Upload one or more files"
|
|
628
|
+
: "Upload a file"
|
|
629
|
+
: !hasOptions
|
|
630
|
+
? "Open-ended question"
|
|
631
|
+
: question.allowMultiple
|
|
632
|
+
? "Select one or more options"
|
|
633
|
+
: "Select one option", isFileUpload &&
|
|
634
|
+
question.attachToTaskIfAvailable &&
|
|
635
|
+
taskId &&
|
|
636
|
+
" • Also attaches to task", question.optional && " (optional)", effectiveAllowFreetext &&
|
|
637
|
+
(isItemSelection || hasOptions) &&
|
|
638
|
+
" • Freetext allowed"] })] })] }), isItemCollection && (_jsx("div", { className: "mt-2 ml-7", children: _jsx(QuestionnaireItemCollectionField, { questionId: question.id, rootItemId: effectiveRootItemId, answer: answer, language: language, onChange: (items) => updateSelectedItems(question.id, items) }) })), isItemPicker && (_jsxs("div", { className: "mt-2 ml-7", children: [_jsx("div", { className: "h-[250px] overflow-hidden rounded border border-gray-200", children: _jsx(TreeListSelector, { language: language, rootItemIds: effectiveRootItemId
|
|
250
639
|
? [effectiveRootItemId]
|
|
251
640
|
: undefined, selectedItemIds: (answer?.selectedItems ?? []).map((i) => i.id), selectionMode: question.allowMultiple ? "multiple" : "single", multiSelectRequiresModifier: !question.allowMultiple, onSelectionChange: (items) => {
|
|
252
641
|
const effectiveItems = question.allowMultiple
|
|
@@ -263,7 +652,7 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
263
652
|
upsertSelectedItem(question.id, { id, name, path: item.path }, question.allowMultiple ?? false);
|
|
264
653
|
}, className: "h-full" }) }), (answer?.selectedItems?.length ?? 0) > 0 && (_jsxs("div", { className: "mt-2 flex flex-wrap gap-1", children: [(answer?.selectedItems ?? [])
|
|
265
654
|
.slice(0, 8)
|
|
266
|
-
.map((i) => (_jsx("span", { className: "rounded border border-gray-200 bg-gray-50 px-1.5 py-0.5 text-[10px] text-gray-600", title: i.path || i.id, children: i.name || i.path || i.id }, i.id))), (answer?.selectedItems?.length ?? 0) > 8 && (_jsxs("span", { className: "px-1.5 py-0.5 text-[10px] text-gray-400", children: ["+", (answer?.selectedItems?.length ?? 0) - 8, " more"] }))] }))] })), !
|
|
655
|
+
.map((i) => (_jsx("span", { className: "rounded border border-gray-200 bg-gray-50 px-1.5 py-0.5 text-[10px] text-gray-600", title: i.path || i.id, children: i.name || i.path || i.id }, i.id))), (answer?.selectedItems?.length ?? 0) > 8 && (_jsxs("span", { className: "px-1.5 py-0.5 text-[10px] text-gray-400", children: ["+", (answer?.selectedItems?.length ?? 0) - 8, " more"] }))] }))] })), isFileUpload && (_jsx("div", { className: "mt-2 ml-7", children: _jsx(QuestionnaireFileUploadField, { questionId: question.id, agentId: agentId, allowMultiple: question.allowMultiple ?? false, selectedFiles: pendingFiles.get(question.id) ?? [], submissionError: fileUploadErrors.get(question.id), disabled: isSubmitting, attachToTaskIfAvailable: question.attachToTaskIfAvailable, taskId: taskId, onChange: (files) => updatePendingFiles(question.id, files) }) })), !isStructuredQuestion && hasOptions && (_jsx("div", { className: "ml-7 space-y-1", children: question.options.map((option) => {
|
|
267
656
|
const isSelected = answer?.selectedOptions.includes(option.id) ?? false;
|
|
268
657
|
return (_jsxs("div", { "data-testid": "questionnaire-option", "data-option-id": option.id, "data-selected": isSelected, className: `flex cursor-pointer items-center gap-2 rounded border px-2 py-1.5 transition-colors ${isSelected
|
|
269
658
|
? "border-indigo-200 bg-indigo-50"
|
|
@@ -275,9 +664,9 @@ export function QuestionnaireInline({ requestId, onClose, onCancel, title = "Que
|
|
|
275
664
|
: "Your response" })] }), _jsx(Textarea, { "data-testid": "questionnaire-freetext", value: answer?.freetext ?? "", onChange: (e) => updateFreetext(question.id, e.target.value), placeholder: "Enter your response...", className: "mt-1 min-h-[60px] text-xs" })] }))] }, question.id));
|
|
276
665
|
}) })) }), _jsxs("div", { className: "flex items-center justify-end gap-2 border-t border-gray-100 bg-gray-50/50 px-4 py-3", children: [footerActions, _jsx(Button, { variant: "outline", size: "sm", onClick: () => {
|
|
277
666
|
handleCancel();
|
|
278
|
-
}, "data-testid": "questionnaire-cancel", className: "h-8 text-xs", children: "Cancel" }), _jsx(Button, { size: "sm", onClick:
|
|
279
|
-
|
|
280
|
-
|
|
667
|
+
}, "data-testid": "questionnaire-cancel", className: "h-8 text-xs", children: "Cancel" }), _jsx(Button, { size: "sm", onClick: () => {
|
|
668
|
+
void handleSubmit();
|
|
669
|
+
}, disabled: !allRequiredQuestionsAnswered || isSubmitting, "data-testid": "questionnaire-submit", className: "h-8 text-xs", children: isSubmitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-1 h-3 w-3 animate-spin" }), "Submitting..."] })) : answeredCount === 0 ? ("Submit Answers") : (`Submit ${answeredCount} Answer${answeredCount !== 1 ? "s" : ""}`) })] })] }));
|
|
281
670
|
}
|
|
282
671
|
export default QuestionnaireInline;
|
|
283
672
|
//# sourceMappingURL=QuestionnaireInline.js.map
|