@paro.io/expert-shared-components 1.12.41 → 1.12.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Escalations/EscalationChat.d.ts +8 -1
- package/lib/components/Escalations/EscalationChat.js +80 -23
- package/lib/components/Escalations/EscalationIssueCard.d.ts +7 -3
- package/lib/components/Escalations/EscalationIssueCard.js +38 -20
- package/lib/components/Escalations/EscalationRespondForm.d.ts +9 -1
- package/lib/components/Escalations/EscalationRespondForm.js +128 -20
- package/lib/components/Escalations/EscalationSubmitForm.d.ts +7 -2
- package/lib/components/Escalations/EscalationSubmitForm.js +138 -39
- package/lib/components/Escalations/EscalationTabsContent.d.ts +5 -8
- package/lib/components/Escalations/EscalationTabsContent.js +4 -9
- package/lib/components/Escalations/Escalations.d.ts +11 -5
- package/lib/components/Escalations/Escalations.js +8 -5
- package/lib/components/FileUploader/index.d.ts +9 -5
- package/lib/components/FileUploader/index.js +55 -9
- package/lib/components/Invoices/ClientDisputeProjectCard.d.ts +1 -1
- package/lib/components/Invoices/ClientDisputeProjectCard.js +3 -3
- package/lib/components/Invoices/DiscussionSection.d.ts +2 -2
- package/lib/components/Invoices/DiscussionSection.js +2 -2
- package/lib/components/Invoices/DisputeSection.js +1 -1
- package/lib/components/shared/UploadClient.d.ts +2 -1
- package/lib/components/shared/UploadClient.js +7 -4
- package/lib/components/shared/utils.d.ts +1 -0
- package/lib/components/shared/utils.js +6 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +1 -3
- package/package.json +8 -2
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
declare const EscalationChat: ({ activeChatIssue, showEscalationChat, setShowEscalationChat }: {
|
|
1
|
+
declare const EscalationChat: ({ activeChatIssue, showEscalationChat, setShowEscalationChat, user, createEscalationChatMessage, documentUploadUrl, bucketName, uploadExpertClientFiles, createProjectEscalation, isExpert, }: {
|
|
2
2
|
activeChatIssue: any;
|
|
3
3
|
showEscalationChat: boolean;
|
|
4
4
|
setShowEscalationChat: (showEscalationChat: boolean) => void;
|
|
5
|
+
user: any;
|
|
6
|
+
createEscalationChatMessage: any;
|
|
7
|
+
documentUploadUrl: string;
|
|
8
|
+
bucketName: string;
|
|
9
|
+
uploadExpertClientFiles: string;
|
|
10
|
+
createProjectEscalation: string;
|
|
11
|
+
isExpert: boolean;
|
|
5
12
|
}) => JSX.Element;
|
|
6
13
|
export default EscalationChat;
|
|
@@ -38,17 +38,81 @@ const base_ui_1 = require("@paro.io/base-ui");
|
|
|
38
38
|
const core_1 = require("@material-ui/core");
|
|
39
39
|
const DiscussionSection_1 = require("../Invoices/DiscussionSection");
|
|
40
40
|
const EscalationIssueCard_1 = require("./EscalationIssueCard");
|
|
41
|
-
const
|
|
41
|
+
const FileUploader_1 = require("../FileUploader");
|
|
42
|
+
const EscalationRespondForm_1 = require("./EscalationRespondForm");
|
|
43
|
+
const EscalationChat = ({ activeChatIssue, showEscalationChat, setShowEscalationChat, user, createEscalationChatMessage, documentUploadUrl, bucketName, uploadExpertClientFiles, createProjectEscalation, isExpert, }) => {
|
|
44
|
+
var _a;
|
|
42
45
|
const [uploadFiles, setUploadFiles] = (0, react_1.useState)([]);
|
|
43
46
|
const [uploadingFile, setUploadingFile] = (0, react_1.useState)(false);
|
|
44
47
|
const fileInputRef = (0, react_1.useRef)(null);
|
|
45
|
-
const handleFileUpload = (event) => {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
const handleFileUpload = (event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
+
const selectedFiles = event.target.files;
|
|
50
|
+
if (!selectedFiles)
|
|
51
|
+
return;
|
|
52
|
+
setUploadingFile(true);
|
|
53
|
+
const validFileNames = Array.from(selectedFiles)
|
|
54
|
+
.filter(file => (0, EscalationRespondForm_1.validateFileUpload)(file))
|
|
55
|
+
.map(file => file.name);
|
|
56
|
+
if (validFileNames.length === 0)
|
|
57
|
+
return;
|
|
58
|
+
if (validFileNames && validFileNames.length > 0) {
|
|
59
|
+
const uploadPromises = Array.from(selectedFiles).map((selectedFile) => {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const reader = new FileReader();
|
|
62
|
+
reader.onloadend = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
|
+
var _a;
|
|
64
|
+
try {
|
|
65
|
+
const res = yield (0, FileUploader_1.fileUploader)({
|
|
66
|
+
file: selectedFile,
|
|
67
|
+
documentName: selectedFile.name,
|
|
68
|
+
escalationId: activeChatIssue.escalationId,
|
|
69
|
+
projectId: activeChatIssue.projectDetails.length > 0 ? activeChatIssue.projectDetails[0].projectId : '',
|
|
70
|
+
documentUploadUrl: documentUploadUrl,
|
|
71
|
+
bucketName: bucketName,
|
|
72
|
+
isExpert: false,
|
|
73
|
+
uploadExpertClientFiles: uploadExpertClientFiles,
|
|
74
|
+
createProjectEscalation: createProjectEscalation,
|
|
75
|
+
extraData: {
|
|
76
|
+
clientId: activeChatIssue.client.id,
|
|
77
|
+
clientName: activeChatIssue.client.name,
|
|
78
|
+
email: isExpert ? "client@gmail.com" : user === null || user === void 0 ? void 0 : user.email,
|
|
79
|
+
freelancerId: activeChatIssue.freelancer.id,
|
|
80
|
+
freelancerName: (_a = activeChatIssue.freelancer.name) !== null && _a !== void 0 ? _a : '',
|
|
81
|
+
projectName: activeChatIssue.projectDetails.length > 0 ? activeChatIssue.projectDetails[0].projectName : '',
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
if (res) {
|
|
85
|
+
setUploadFiles(prev => [...prev, ...res]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
reject(error);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
resolve();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
reader.readAsDataURL(selectedFile);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
try {
|
|
99
|
+
const res = yield Promise.all(uploadPromises);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error('Error uploading files:', error);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
setUploadingFile(false);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
;
|
|
109
|
+
});
|
|
110
|
+
const docs = [activeChatIssue.expertSupportingDocuments, activeChatIssue.clientSupportingDocuments, activeChatIssue.internalSupportingDocuments, ...uploadFiles];
|
|
111
|
+
const processedDocs = docs
|
|
112
|
+
.filter(doc => doc !== null && doc !== undefined && doc !== '')
|
|
113
|
+
.flatMap(doc => doc.split(','))
|
|
114
|
+
.map(doc => doc.trim())
|
|
115
|
+
.filter(doc => doc !== '');
|
|
52
116
|
return (react_1.default.createElement(core_1.Dialog, { open: showEscalationChat, onClose: () => setShowEscalationChat(false), maxWidth: 'sm' },
|
|
53
117
|
react_1.default.createElement(core_1.DialogTitle, null,
|
|
54
118
|
react_1.default.createElement("div", { className: "text-black mb-1 p-2 pl-4 absolute top-0 left-0 w-full flex flex-row justify-between items-center z-50" },
|
|
@@ -56,10 +120,10 @@ const EscalationChat = ({ activeChatIssue, showEscalationChat, setShowEscalation
|
|
|
56
120
|
react_1.default.createElement("div", { className: "flex flex-row items-center" },
|
|
57
121
|
react_1.default.createElement("h1", { className: "text-md font-bold mr-2" },
|
|
58
122
|
"Escalation Chat #",
|
|
59
|
-
activeChatIssue.
|
|
123
|
+
activeChatIssue.escalationNumber),
|
|
60
124
|
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: activeChatIssue.status })),
|
|
61
125
|
react_1.default.createElement("p", { className: "text-xs" },
|
|
62
|
-
activeChatIssue.
|
|
126
|
+
activeChatIssue.probelm,
|
|
63
127
|
" - ",
|
|
64
128
|
activeChatIssue.project)),
|
|
65
129
|
react_1.default.createElement("div", { className: "flex items-center space-x-4" },
|
|
@@ -67,24 +131,17 @@ const EscalationChat = ({ activeChatIssue, showEscalationChat, setShowEscalation
|
|
|
67
131
|
react_1.default.createElement(base_icons_1.IconX, null))))),
|
|
68
132
|
react_1.default.createElement(core_1.DialogContent, null,
|
|
69
133
|
react_1.default.createElement("div", { className: "bg-white rounded-lg w-full overflow-hidden flex flex-col p-2 mt-12" },
|
|
70
|
-
react_1.default.createElement(DiscussionSection_1.DiscussionSection
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
134
|
+
react_1.default.createElement(DiscussionSection_1.DiscussionSection, { escalationNumber: activeChatIssue.escalationNumber, currentUser: user, messages: (_a = activeChatIssue === null || activeChatIssue === void 0 ? void 0 : activeChatIssue.chatMessages) !== null && _a !== void 0 ? _a : [], onCreateMessage: createEscalationChatMessage, isInternal: false }),
|
|
135
|
+
processedDocs && processedDocs.length > 0 && (react_1.default.createElement("div", { className: "flex flex-wrap gap-2" },
|
|
136
|
+
react_1.default.createElement("span", { className: "text-sm font-bold text-gray-500 items-center" }, "Supporting Documents: "),
|
|
137
|
+
processedDocs.map((d, idx) => (react_1.default.createElement(EscalationIssueCard_1.CustomTag, { key: idx, label: d.split('%2F')[1] }))))),
|
|
74
138
|
react_1.default.createElement("div", { className: "bg-white mt-2" },
|
|
75
139
|
react_1.default.createElement("div", { className: "space-y-3" },
|
|
76
140
|
react_1.default.createElement("div", { className: "flex items-center justify-between" },
|
|
77
141
|
react_1.default.createElement("div", { className: "flex items-center space-x-2" },
|
|
78
142
|
react_1.default.createElement("input", { id: "upload-file", type: "file", multiple: true, accept: ".pdf,.doc,.docx,.jpeg,.png,.gif,.csv", style: { display: 'none' }, ref: fileInputRef, onChange: handleFileUpload }),
|
|
79
|
-
react_1.default.createElement(base_ui_1.Button, { label: "Attach Files", iconLeft: react_1.default.createElement(base_icons_1.IconPlus, { size: "sm" }), onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, color: "info", className: "mx-2", isLoading: uploadingFile }),
|
|
80
|
-
uploadFiles.length > 0 && (react_1.default.createElement("div", { className: "text-sm text-gray-600" },
|
|
81
|
-
uploadFiles.length,
|
|
82
|
-
" file(s) selected"))),
|
|
143
|
+
react_1.default.createElement(base_ui_1.Button, { label: "Attach Files", iconLeft: react_1.default.createElement(base_icons_1.IconPlus, { size: "sm" }), onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, color: "info", className: "mx-2", isLoading: uploadingFile })),
|
|
83
144
|
react_1.default.createElement("div", { className: "flex space-x-2" },
|
|
84
|
-
react_1.default.createElement(base_ui_1.Button, { label: "cancel", onClick: () => setShowEscalationChat(false) })))
|
|
85
|
-
uploadFiles.length > 0 && (react_1.default.createElement("div", { className: "space-y-1" }, uploadFiles.map((file, index) => (react_1.default.createElement("div", { key: index, className: "flex items-center justify-between bg-gray-50 px-2 py-1 rounded text-sm" },
|
|
86
|
-
react_1.default.createElement("span", null, file.name),
|
|
87
|
-
react_1.default.createElement(core_1.IconButton, { onClick: () => removeFile(index) },
|
|
88
|
-
react_1.default.createElement(base_icons_1.IconX, { size: "sm" })))))))))))));
|
|
145
|
+
react_1.default.createElement(base_ui_1.Button, { label: "cancel", onClick: () => setShowEscalationChat(false), disabled: uploadingFile })))))))));
|
|
89
146
|
};
|
|
90
147
|
exports.default = EscalationChat;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
export declare const CustomTag: ({ label }: {
|
|
2
|
+
export declare const CustomTag: ({ label, iconLeft, onClick, }: {
|
|
4
3
|
label: any;
|
|
4
|
+
iconLeft?: React.ReactNode;
|
|
5
|
+
onClick?: () => void;
|
|
5
6
|
}) => React.JSX.Element;
|
|
6
7
|
type EscalationIssueCardProps = {
|
|
7
8
|
issues: any[];
|
|
8
9
|
isExpert: boolean;
|
|
9
|
-
openEscalationChat: (
|
|
10
|
+
openEscalationChat: (issue: any) => void;
|
|
10
11
|
showRespondButton?: boolean;
|
|
11
12
|
setSelectedIssueId?: (id: number) => void;
|
|
12
13
|
showMarkResolvedButton?: boolean;
|
|
14
|
+
updateProjectEscalation: any;
|
|
15
|
+
downloadDocumentUrl: string;
|
|
16
|
+
bucketName: string;
|
|
13
17
|
};
|
|
14
18
|
declare const EscalationIssueCard: React.FC<EscalationIssueCardProps>;
|
|
15
19
|
export default EscalationIssueCard;
|
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.CustomTag = void 0;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
8
|
const base_ui_1 = require("@paro.io/base-ui");
|
|
9
|
+
const ClientDisputeProjectCard_1 = require("../Invoices/ClientDisputeProjectCard");
|
|
10
|
+
const base_icons_1 = require("@paro.io/base-icons");
|
|
9
11
|
const getBackgroundColor = (type) => {
|
|
10
12
|
switch (type) {
|
|
11
13
|
case 'Critical':
|
|
@@ -20,19 +22,27 @@ const getBackgroundColor = (type) => {
|
|
|
20
22
|
return 'success';
|
|
21
23
|
}
|
|
22
24
|
};
|
|
23
|
-
const CustomTag = ({ label }) => {
|
|
25
|
+
const CustomTag = ({ label, iconLeft, onClick, }) => {
|
|
24
26
|
const color = getBackgroundColor(label);
|
|
25
|
-
return (react_1.default.createElement("div", { className: `border box-border text-center px-4 pb-0.5 pt-1 text-sm inline-block break-words text-white rounded-full bg-${color} border-${color} ml-2 font-bold` },
|
|
27
|
+
return (react_1.default.createElement("div", { onClick: onClick, className: `flex flex-row gap-1 border box-border text-center px-4 pb-0.5 pt-1 text-sm inline-block break-words text-white rounded-full bg-${color} border-${color} ml-2 font-bold ${onClick ? 'cursor-pointer' : ''}` },
|
|
28
|
+
iconLeft && react_1.default.createElement("span", null, iconLeft), label === null || label === void 0 ? void 0 :
|
|
29
|
+
label.toUpperCase()));
|
|
26
30
|
};
|
|
27
31
|
exports.CustomTag = CustomTag;
|
|
28
|
-
const EscalationIssueCard = ({ issues, isExpert, openEscalationChat, showRespondButton = false, setSelectedIssueId, showMarkResolvedButton = false, }) => {
|
|
32
|
+
const EscalationIssueCard = ({ issues, isExpert, openEscalationChat, showRespondButton = false, setSelectedIssueId, showMarkResolvedButton = false, updateProjectEscalation, downloadDocumentUrl, bucketName, }) => {
|
|
29
33
|
if (!issues.length)
|
|
30
34
|
return react_1.default.createElement("div", null, "No issues.");
|
|
31
35
|
return (react_1.default.createElement("div", { className: "space-y-4" }, issues.map((issue) => {
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
var _a;
|
|
37
|
+
const project = issue.projectDetails && Array.isArray(issue.projectDetails) && issue.projectDetails.length > 0
|
|
38
|
+
? `${(_a = issue.projectDetails[0]) === null || _a === void 0 ? void 0 : _a.projectName}${issue.projectDetails.length > 1 ? ` +${issue.projectDetails.length - 1} more` : ''} `
|
|
34
39
|
: '';
|
|
35
|
-
const docs = [
|
|
40
|
+
const docs = [issue.expertSupportingDocuments, issue.clientSupportingDocuments, issue.internalSupportingDocuments];
|
|
41
|
+
const processedDocs = docs
|
|
42
|
+
.filter(doc => doc !== null && doc !== undefined && doc !== '')
|
|
43
|
+
.flatMap(doc => doc.split(','))
|
|
44
|
+
.map(doc => doc.trim())
|
|
45
|
+
.filter(doc => doc !== '');
|
|
36
46
|
return (react_1.default.createElement("div", { key: issue.id, className: "border border-gray-200 rounded-lg bg-white p-4" },
|
|
37
47
|
react_1.default.createElement("div", { className: "flex items-center justify-between mb-3" },
|
|
38
48
|
react_1.default.createElement("div", { className: "flex-1 ml-2" },
|
|
@@ -42,29 +52,37 @@ const EscalationIssueCard = ({ issues, isExpert, openEscalationChat, showRespond
|
|
|
42
52
|
react_1.default.createElement("div", { className: "text-xs text-gray-500 font-bold" },
|
|
43
53
|
project,
|
|
44
54
|
" \u2022 Case #",
|
|
45
|
-
issue.
|
|
46
|
-
react_1.default.createElement(exports.CustomTag, { label: issue.
|
|
55
|
+
issue.escalationNumber)),
|
|
56
|
+
react_1.default.createElement(exports.CustomTag, { label: issue.escalationType })),
|
|
47
57
|
react_1.default.createElement("div", { className: "bg-gray-50 rounded-md p-3 mb-4 border" },
|
|
48
58
|
react_1.default.createElement("div", { className: "flex items-center justify-between mb-2" },
|
|
49
59
|
react_1.default.createElement("div", { className: "text-sm text-gray-500" },
|
|
50
60
|
"Submitted by: ",
|
|
51
|
-
react_1.default.createElement("span", { className: "font-bold" }, issue.
|
|
61
|
+
react_1.default.createElement("span", { className: "font-bold" }, issue.submittedByUser.firstName + " " + issue.submittedByUser.lastName),
|
|
52
62
|
issue.createdAt && react_1.default.createElement(react_1.default.Fragment, null,
|
|
53
63
|
" \u2022 Submitted on: ",
|
|
54
64
|
react_1.default.createElement("span", { className: "font-bold" }, issue.createdAt)))),
|
|
55
|
-
react_1.default.createElement("div", { className: "text-sm text-gray-600 mb-3" }, issue.
|
|
56
|
-
|
|
65
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600 mb-3" }, issue.outcome),
|
|
66
|
+
processedDocs && processedDocs.length > 0 && (react_1.default.createElement("div", { className: "flex flex-wrap gap-2" },
|
|
57
67
|
react_1.default.createElement("span", { className: "text-sm font-bold text-gray-500 items-center" }, "Supporting Documents: "),
|
|
58
|
-
|
|
68
|
+
processedDocs.map((d, idx) => (react_1.default.createElement(exports.CustomTag, { key: idx, label: d.split('%2F')[1], iconLeft: react_1.default.createElement(base_icons_1.IconDocumentDownload, { size: "xs" }), onClick: () => {
|
|
69
|
+
(0, ClientDisputeProjectCard_1.handleDownloadDocument)(issue.escalationId, d.split('%2F')[1], downloadDocumentUrl, bucketName);
|
|
70
|
+
} })))))),
|
|
59
71
|
react_1.default.createElement("div", { className: "flex flex-wrap gap-2" },
|
|
60
|
-
react_1.default.createElement(base_ui_1.Button, { onClick: () => openEscalationChat(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
react_1.default.createElement(base_ui_1.Button, { onClick: () => openEscalationChat(issue), label: "Chat", color: "primary" }),
|
|
73
|
+
showRespondButton && setSelectedIssueId && (react_1.default.createElement(base_ui_1.Button, { label: "Respond to Escalation", onClick: () => setSelectedIssueId(issue.escalationNumber), color: 'primary' })),
|
|
74
|
+
showMarkResolvedButton && !isExpert && (react_1.default.createElement(base_ui_1.Button, { onClick: () => {
|
|
75
|
+
updateProjectEscalation({
|
|
76
|
+
variables: {
|
|
77
|
+
input: {
|
|
78
|
+
escalationNumber: issue.escalationNumber,
|
|
79
|
+
status: 'Resolved'
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}).then(() => { console.log("its marked as resolved"); }).catch((error) => {
|
|
83
|
+
console.error("Error updating escalation status:", error);
|
|
84
|
+
});
|
|
85
|
+
}, label: "Mark as Resolved", color: "primary" })))));
|
|
68
86
|
})));
|
|
69
87
|
};
|
|
70
88
|
exports.default = EscalationIssueCard;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
declare const
|
|
1
|
+
export declare const ACCEPTED_FILE_TYPES: string[];
|
|
2
|
+
export declare const validateFileUpload: (file: File | null) => boolean;
|
|
3
|
+
declare const EscalationRespondForm: ({ goBack, selectedIssue, documentUploadUrl, downloadDocumentUrl, uploadExpertClientFiles, updateProjectEscalation, bucketName, goHome, isExpert, }: {
|
|
2
4
|
goBack: () => void;
|
|
3
5
|
selectedIssue: any;
|
|
6
|
+
documentUploadUrl: string;
|
|
7
|
+
downloadDocumentUrl: string;
|
|
8
|
+
uploadExpertClientFiles: any;
|
|
9
|
+
updateProjectEscalation: any;
|
|
10
|
+
bucketName: string;
|
|
4
11
|
goHome: () => void;
|
|
12
|
+
isExpert: boolean;
|
|
5
13
|
}) => JSX.Element;
|
|
6
14
|
export default EscalationRespondForm;
|
|
@@ -22,10 +22,40 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
25
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.validateFileUpload = exports.ACCEPTED_FILE_TYPES = void 0;
|
|
26
36
|
const react_1 = __importStar(require("react"));
|
|
27
37
|
const base_icons_1 = require("@paro.io/base-icons");
|
|
28
38
|
const base_ui_1 = require("@paro.io/base-ui");
|
|
39
|
+
const FileUploader_1 = require("../FileUploader");
|
|
40
|
+
const EscalationIssueCard_1 = require("./EscalationIssueCard");
|
|
41
|
+
exports.ACCEPTED_FILE_TYPES = [
|
|
42
|
+
'application/pdf',
|
|
43
|
+
'application/msword',
|
|
44
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
45
|
+
'image/jpeg',
|
|
46
|
+
'image/png',
|
|
47
|
+
'text/csv',
|
|
48
|
+
];
|
|
49
|
+
const validateFileUpload = (file) => {
|
|
50
|
+
if (!file) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
if (!exports.ACCEPTED_FILE_TYPES.includes(file.type)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
};
|
|
58
|
+
exports.validateFileUpload = validateFileUpload;
|
|
29
59
|
const responseTypes = [
|
|
30
60
|
{
|
|
31
61
|
label: "Acknowledge issue and provide resolution plan",
|
|
@@ -40,29 +70,106 @@ const responseTypes = [
|
|
|
40
70
|
{
|
|
41
71
|
label: "Request clarification",
|
|
42
72
|
description: "Ask for more details about the issue",
|
|
43
|
-
value: "
|
|
73
|
+
value: "NoResponse",
|
|
44
74
|
},
|
|
45
75
|
];
|
|
46
|
-
const EscalationRespondForm = ({ goBack, selectedIssue, goHome }) => {
|
|
76
|
+
const EscalationRespondForm = ({ goBack, selectedIssue, documentUploadUrl, downloadDocumentUrl, uploadExpertClientFiles, updateProjectEscalation, bucketName, goHome, isExpert, }) => {
|
|
47
77
|
const [selectedType, setSelectedType] = (0, react_1.useState)(responseTypes[0].value);
|
|
48
78
|
const [responseInput, setResponseInput] = (0, react_1.useState)('');
|
|
49
79
|
const [uploadFiles, setUploadFiles] = (0, react_1.useState)([]);
|
|
50
80
|
const [uploadingFile, setUploadingFile] = (0, react_1.useState)(false);
|
|
81
|
+
const [submitting, setSubmitting] = (0, react_1.useState)(false);
|
|
51
82
|
const fileInputRef = (0, react_1.useRef)(null);
|
|
52
|
-
const handleFileUpload = (event) => {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
const handleFileUpload = (event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
84
|
+
const selectedFiles = event.target.files;
|
|
85
|
+
if (!selectedFiles)
|
|
86
|
+
return;
|
|
87
|
+
setUploadingFile(true);
|
|
88
|
+
const validFileNames = Array.from(selectedFiles)
|
|
89
|
+
.filter(file => (0, exports.validateFileUpload)(file))
|
|
90
|
+
.map(file => file.name);
|
|
91
|
+
if (validFileNames.length === 0)
|
|
92
|
+
return;
|
|
93
|
+
if (validFileNames && validFileNames.length > 0) {
|
|
94
|
+
const uploadPromises = Array.from(selectedFiles).map((selectedFile) => {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
const reader = new FileReader();
|
|
97
|
+
reader.onloadend = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
98
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
99
|
+
try {
|
|
100
|
+
const res = yield (0, FileUploader_1.fileUploader)({
|
|
101
|
+
file: selectedFile,
|
|
102
|
+
documentName: selectedFile.name,
|
|
103
|
+
escalationId: selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.escalationId,
|
|
104
|
+
projectId: selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.projectDetails[0].projectId,
|
|
105
|
+
documentUploadUrl: documentUploadUrl,
|
|
106
|
+
bucketName: bucketName,
|
|
107
|
+
previousFiles: (selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.isExpert) ? (_a = selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.clientDocumentLinks) !== null && _a !== void 0 ? _a : [] : selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.expertDocumentlinks,
|
|
108
|
+
isExpert: false,
|
|
109
|
+
uploadExpertClientFiles: uploadExpertClientFiles,
|
|
110
|
+
updateProjectEscalation: updateProjectEscalation,
|
|
111
|
+
extraData: {
|
|
112
|
+
clientId: selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.client.id,
|
|
113
|
+
clientName: (_b = selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.client.name) !== null && _b !== void 0 ? _b : '',
|
|
114
|
+
email: isExpert ? (_d = (_c = selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.freelancer) === null || _c === void 0 ? void 0 : _c.email) !== null && _d !== void 0 ? _d : '' : (_g = (_f = (_e = selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.client) === null || _e === void 0 ? void 0 : _e.primaryContact) === null || _f === void 0 ? void 0 : _f.email) !== null && _g !== void 0 ? _g : '',
|
|
115
|
+
freelancerId: selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.freelancer.id,
|
|
116
|
+
freelancerName: (_h = selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.freelancer.name) !== null && _h !== void 0 ? _h : '',
|
|
117
|
+
projectName: selectedIssue === null || selectedIssue === void 0 ? void 0 : selectedIssue.projectDetails[0].projectName,
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
if (res) {
|
|
121
|
+
setUploadFiles(prev => [...prev, ...res]);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
reject(error);
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
resolve();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
reader.readAsDataURL(selectedFile);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
try {
|
|
135
|
+
yield Promise.all(uploadPromises);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
console.error('Error uploading files:', error);
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
setUploadingFile(false);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
;
|
|
145
|
+
});
|
|
146
|
+
const submitResponse = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
147
|
+
setSubmitting(true);
|
|
148
|
+
try {
|
|
149
|
+
yield updateProjectEscalation({
|
|
150
|
+
variables: {
|
|
151
|
+
input: {
|
|
152
|
+
escalationId: selectedIssue.escalationId,
|
|
153
|
+
[isExpert ? 'expertResponse' : 'clientResponse']: responseInput,
|
|
154
|
+
[isExpert ? 'expertSupportingDocuments' : 'clientSupportingDocuments']: uploadFiles.join(", "),
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.error("Failed to send response!", error);
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
setSubmitting(false);
|
|
164
|
+
goHome();
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
const docs = [selectedIssue.expertSupportingDocuments, selectedIssue.clientSupportingDocuments, selectedIssue.internalSupportingDocuments, ...uploadFiles];
|
|
168
|
+
const processedDocs = docs
|
|
169
|
+
.filter(doc => doc !== null && doc !== undefined && doc !== '')
|
|
170
|
+
.flatMap(doc => doc.split(','))
|
|
171
|
+
.map(doc => doc.trim())
|
|
172
|
+
.filter(doc => doc !== '');
|
|
66
173
|
return (react_1.default.createElement("div", null,
|
|
67
174
|
react_1.default.createElement("button", { onClick: goBack, className: "flex items-center text-blue-600 mb-6" },
|
|
68
175
|
react_1.default.createElement(base_icons_1.IconChevronLeft, { size: "xs" }),
|
|
@@ -96,8 +203,9 @@ const EscalationRespondForm = ({ goBack, selectedIssue, goHome }) => {
|
|
|
96
203
|
react_1.default.createElement("p", { className: "text-sm text-gray-600 mb-2" }, "Upload any supporting documentation"),
|
|
97
204
|
react_1.default.createElement("input", { id: "upload-file", type: "file", multiple: true, accept: ".pdf,.doc,.docx,.jpeg,.png,.gif,.csv", style: { display: 'none' }, ref: fileInputRef, onChange: handleFileUpload }),
|
|
98
205
|
react_1.default.createElement(base_ui_1.Button, { label: "Attach Files", iconLeft: react_1.default.createElement(base_icons_1.IconPlus, { size: "sm" }), onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, color: "info", className: "mx-2", isLoading: uploadingFile, size: "sm" }),
|
|
99
|
-
|
|
100
|
-
react_1.default.createElement("
|
|
206
|
+
processedDocs && processedDocs.length > 0 && (react_1.default.createElement("div", { className: "flex flex-wrap gap-2" },
|
|
207
|
+
react_1.default.createElement("span", { className: "text-sm font-bold text-gray-500 items-center" }, "Supporting Documents: "),
|
|
208
|
+
processedDocs.map((d, idx) => (react_1.default.createElement(EscalationIssueCard_1.CustomTag, { key: idx, label: d.split('%2F')[1] }))))))),
|
|
101
209
|
react_1.default.createElement("div", { className: "bg-blue-50 border border-blue-200 rounded-md p-3" },
|
|
102
210
|
react_1.default.createElement("div", { className: "flex items-center" },
|
|
103
211
|
react_1.default.createElement(base_icons_1.IconInfoCircle, { size: "md" }),
|
|
@@ -105,7 +213,7 @@ const EscalationRespondForm = ({ goBack, selectedIssue, goHome }) => {
|
|
|
105
213
|
react_1.default.createElement("p", { className: "font-medium" }, "Response Guidelines"),
|
|
106
214
|
react_1.default.createElement("p", { className: "mt-1" }, "Your response will be visible to the client and Paro support team. Be professional and focus on resolution.")))),
|
|
107
215
|
react_1.default.createElement("div", { className: "flex justify-end space-x-3" },
|
|
108
|
-
react_1.default.createElement(base_ui_1.Button, { label: "Cancel", onClick: goBack }),
|
|
109
|
-
react_1.default.createElement(base_ui_1.Button, { label: "Submit Response", color: "primary", onClick: submitResponse })))))));
|
|
216
|
+
react_1.default.createElement(base_ui_1.Button, { label: "Cancel", onClick: goBack, disabled: uploadingFile || submitting }),
|
|
217
|
+
react_1.default.createElement(base_ui_1.Button, { label: "Submit Response", color: "primary", onClick: submitResponse, isLoading: submitting, disabled: uploadingFile })))))));
|
|
110
218
|
};
|
|
111
219
|
exports.default = EscalationRespondForm;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
interface EscalationSubmitProps {
|
|
2
2
|
goBack: () => void;
|
|
3
3
|
goHome: () => void;
|
|
4
|
-
|
|
4
|
+
expertsOrClients: any[];
|
|
5
5
|
projects: any[];
|
|
6
6
|
isExpert: boolean;
|
|
7
|
+
documentUploadUrl: string;
|
|
8
|
+
bucketName: string;
|
|
9
|
+
uploadExpertClientFiles: any;
|
|
10
|
+
createProjectEscalation: any;
|
|
11
|
+
user: any;
|
|
7
12
|
}
|
|
8
|
-
declare const EscalationSubmitForm: ({ goBack, goHome,
|
|
13
|
+
declare const EscalationSubmitForm: ({ goBack, goHome, expertsOrClients, projects, documentUploadUrl, bucketName, uploadExpertClientFiles, createProjectEscalation, isExpert, user }: EscalationSubmitProps) => JSX.Element;
|
|
9
14
|
export default EscalationSubmitForm;
|