@paro.io/expert-shared-components 1.12.42 → 1.12.44
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 +139 -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 +21 -6
- package/lib/components/FileUploader/index.d.ts +9 -5
- package/lib/components/FileUploader/index.js +26 -9
- package/lib/components/Invoices/ClientDisputeProjectCard.d.ts +1 -1
- package/lib/components/Invoices/ClientDisputeProjectCard.js +3 -3
- package/lib/components/Invoices/DisputeSection.js +1 -1
- package/lib/components/shared/UploadClient.d.ts +2 -1
- package/lib/components/shared/UploadClient.js +7 -6
- package/lib/components/shared/utils.d.ts +1 -0
- package/lib/components/shared/utils.js +6 -1
- 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;
|
|
@@ -22,6 +22,15 @@ 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
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
36
|
};
|
|
@@ -32,6 +41,9 @@ const base_ui_1 = require("@paro.io/base-ui");
|
|
|
32
41
|
const core_1 = require("@material-ui/core");
|
|
33
42
|
const LogTimeModalAuthenticated_1 = require("../ProjectCard/LogTimeModalAuthenticated");
|
|
34
43
|
const dayjs_1 = __importDefault(require("dayjs"));
|
|
44
|
+
const EscalationRespondForm_1 = require("./EscalationRespondForm");
|
|
45
|
+
const FileUploader_1 = require("../FileUploader");
|
|
46
|
+
const utils_1 = require("../shared/utils");
|
|
35
47
|
const issueTypeOptions = [
|
|
36
48
|
{ value: 'CommunicationIssues', label: 'Communication Issues' },
|
|
37
49
|
{ value: 'Professionalism', label: 'Professionalism' },
|
|
@@ -41,80 +53,159 @@ const issueTypeOptions = [
|
|
|
41
53
|
{ value: 'ScopeCreep', label: 'Scope Creep/Unauthorized Requests' },
|
|
42
54
|
{ value: 'Other', label: 'Other' },
|
|
43
55
|
];
|
|
44
|
-
const EscalationSubmitForm = ({ goBack, goHome,
|
|
56
|
+
const EscalationSubmitForm = ({ goBack, goHome, expertsOrClients, projects, documentUploadUrl, bucketName, uploadExpertClientFiles, createProjectEscalation, isExpert = false, user }) => {
|
|
57
|
+
var _a;
|
|
45
58
|
const [isChecked, setIsChecked] = (0, react_1.useState)(false);
|
|
46
59
|
const [problemInput, setProblemInput] = (0, react_1.useState)('');
|
|
47
60
|
const [outcomeInput, setOutcomeInput] = (0, react_1.useState)('');
|
|
48
61
|
const [uploadFiles, setUploadFiles] = (0, react_1.useState)([]);
|
|
49
62
|
const [uploadingFile, setUploadingFile] = (0, react_1.useState)(false);
|
|
50
63
|
const [expertOptions, setExpertOptions] = (0, react_1.useState)([]);
|
|
51
|
-
const [
|
|
64
|
+
const [selectedUser, setSelectedUser] = (0, react_1.useState)(null);
|
|
52
65
|
const [projectOptions, setProjectOptions] = (0, react_1.useState)([]);
|
|
53
66
|
const [selectedProjects, setSelectedProjects] = (0, react_1.useState)([]);
|
|
54
67
|
const [issueType, setIssueType] = (0, react_1.useState)('');
|
|
55
68
|
const [severity, setSeverity] = (0, react_1.useState)('');
|
|
56
|
-
const [
|
|
69
|
+
const [issueStartDate, setIssueStartDate] = (0, react_1.useState)(undefined);
|
|
57
70
|
const [isDateInvalid, setIsDateInvalid] = (0, react_1.useState)(false);
|
|
71
|
+
const [submitting, setSubmitting] = (0, react_1.useState)(false);
|
|
58
72
|
const fileInputRef = (0, react_1.useRef)(null);
|
|
73
|
+
const escalationId = (0, utils_1.generateUUID)();
|
|
59
74
|
(0, react_1.useEffect)(() => {
|
|
60
|
-
if (
|
|
61
|
-
const options =
|
|
62
|
-
label:
|
|
63
|
-
value:
|
|
75
|
+
if (expertsOrClients && expertsOrClients.length > 0) {
|
|
76
|
+
const options = expertsOrClients.map((user) => ({
|
|
77
|
+
label: user.name,
|
|
78
|
+
value: { id: user.id, name: user.name },
|
|
64
79
|
}));
|
|
65
80
|
setExpertOptions(options);
|
|
66
81
|
}
|
|
67
|
-
}, [
|
|
82
|
+
}, [expertsOrClients]);
|
|
68
83
|
(0, react_1.useEffect)(() => {
|
|
69
84
|
if (projects && projects.length > 0) {
|
|
70
85
|
const options = projects
|
|
71
86
|
.filter((p) => {
|
|
72
87
|
if (isExpert) {
|
|
73
|
-
return p.client.id === Number(
|
|
88
|
+
return p.client.id === Number(selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id);
|
|
74
89
|
}
|
|
75
90
|
else {
|
|
76
|
-
return p.freelancer.id === Number(
|
|
91
|
+
return p.freelancer.id === Number(selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id);
|
|
77
92
|
}
|
|
78
93
|
})
|
|
79
94
|
.map((p) => ({
|
|
80
|
-
label:
|
|
81
|
-
value:
|
|
95
|
+
label: p.name,
|
|
96
|
+
value: p,
|
|
82
97
|
}));
|
|
83
98
|
setProjectOptions(options);
|
|
84
99
|
}
|
|
85
|
-
}, [projects,
|
|
86
|
-
const handleFileUpload = (event) => {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
100
|
+
}, [projects, selectedUser]);
|
|
101
|
+
const handleFileUpload = (event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
102
|
+
const selectedFiles = event.target.files;
|
|
103
|
+
if (!selectedFiles)
|
|
104
|
+
return;
|
|
105
|
+
setUploadingFile(true);
|
|
106
|
+
const validFileNames = Array.from(selectedFiles)
|
|
107
|
+
.filter(file => (0, EscalationRespondForm_1.validateFileUpload)(file))
|
|
108
|
+
.map(file => file.name);
|
|
109
|
+
if (validFileNames.length === 0)
|
|
110
|
+
return;
|
|
111
|
+
if (validFileNames && validFileNames.length > 0) {
|
|
112
|
+
const uploadPromises = Array.from(selectedFiles).map((selectedFile) => {
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
const reader = new FileReader();
|
|
115
|
+
reader.onloadend = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
116
|
+
var _a;
|
|
117
|
+
try {
|
|
118
|
+
const res = yield (0, FileUploader_1.fileUploader)({
|
|
119
|
+
file: selectedFile,
|
|
120
|
+
documentName: selectedFile.name,
|
|
121
|
+
escalationId: escalationId,
|
|
122
|
+
projectId: selectedProjects.length > 0 ? selectedProjects[0].id : '',
|
|
123
|
+
documentUploadUrl: documentUploadUrl,
|
|
124
|
+
bucketName: bucketName,
|
|
125
|
+
isExpert: false,
|
|
126
|
+
uploadExpertClientFiles: uploadExpertClientFiles,
|
|
127
|
+
createProjectEscalation: createProjectEscalation,
|
|
128
|
+
extraData: {
|
|
129
|
+
clientId: isExpert ? selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id : user === null || user === void 0 ? void 0 : user.userId,
|
|
130
|
+
clientName: isExpert ? selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name : user === null || user === void 0 ? void 0 : user.name,
|
|
131
|
+
email: isExpert ? (_a = selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.primaryContact) === null || _a === void 0 ? void 0 : _a.email : user === null || user === void 0 ? void 0 : user.email,
|
|
132
|
+
freelancerId: isExpert ? user === null || user === void 0 ? void 0 : user.userId : selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id,
|
|
133
|
+
freelancerName: isExpert ? user === null || user === void 0 ? void 0 : user.name : selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name,
|
|
134
|
+
projectName: selectedProjects.length > 0 ? selectedProjects[0].name : '',
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
if (res) {
|
|
138
|
+
setUploadFiles(prev => [...prev, ...res]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
reject(error);
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
resolve();
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
reader.readAsDataURL(selectedFile);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
try {
|
|
152
|
+
const res = yield Promise.all(uploadPromises);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.error('Error uploading files:', error);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
setUploadingFile(false);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
;
|
|
162
|
+
});
|
|
90
163
|
const isFormValid = () => {
|
|
91
164
|
const requiredProjectSelection = isExpert || (!isExpert && selectedProjects.length > 0);
|
|
92
|
-
return (
|
|
165
|
+
return (selectedUser !== null &&
|
|
93
166
|
issueType !== '' &&
|
|
94
167
|
severity !== '' &&
|
|
95
168
|
problemInput.trim() !== '' &&
|
|
96
169
|
outcomeInput.trim() !== '' &&
|
|
97
170
|
isChecked &&
|
|
98
|
-
|
|
171
|
+
issueStartDate !== undefined &&
|
|
99
172
|
requiredProjectSelection);
|
|
100
173
|
};
|
|
101
|
-
const handleSubmit = () => {
|
|
174
|
+
const handleSubmit = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
175
|
+
var _a;
|
|
102
176
|
if (!isFormValid())
|
|
103
177
|
return;
|
|
178
|
+
setSubmitting(true);
|
|
104
179
|
const formData = {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
180
|
+
escalationId: escalationId,
|
|
181
|
+
freelancerId: user.userId,
|
|
182
|
+
clientId: (_a = selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id) !== null && _a !== void 0 ? _a : 0,
|
|
183
|
+
projectDetails: selectedProjects.map((p) => { return { projectId: p.id, projectName: p.name }; }),
|
|
184
|
+
issueStartDate: issueStartDate,
|
|
185
|
+
escalationType: issueType,
|
|
186
|
+
severityLevel: severity,
|
|
110
187
|
problem: problemInput,
|
|
111
|
-
|
|
112
|
-
|
|
188
|
+
outcome: outcomeInput,
|
|
189
|
+
submittedByUserId: user.userId,
|
|
190
|
+
status: 'InProgress',
|
|
191
|
+
[isExpert ? 'expertSupportingDocuments' : 'clientSupportingDocuments']: uploadFiles.join(", "),
|
|
192
|
+
statusChangedBy: user.userId,
|
|
113
193
|
};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
194
|
+
try {
|
|
195
|
+
yield createProjectEscalation({
|
|
196
|
+
variables: {
|
|
197
|
+
input: formData
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
console.error("Failed to create an escalation!", error);
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
setSubmitting(false);
|
|
206
|
+
goHome();
|
|
207
|
+
}
|
|
208
|
+
});
|
|
118
209
|
const validateDate = (date) => {
|
|
119
210
|
const minDate = LogTimeModalAuthenticated_1.constants.MIN_DATE;
|
|
120
211
|
const maxDate = new Date();
|
|
@@ -146,10 +237,19 @@ const EscalationSubmitForm = ({ goBack, goHome, experts, projects, isExpert = fa
|
|
|
146
237
|
react_1.default.createElement("div", { className: "p-4 space-y-4" },
|
|
147
238
|
react_1.default.createElement("div", null,
|
|
148
239
|
react_1.default.createElement("label", { className: "block text-sm font-medium text-gray-700 mb-1" }, `Which ${isExpert ? 'client' : 'expert'} are you experiencing the issue with?`),
|
|
149
|
-
react_1.default.createElement(core_1.Select, { fullWidth: true, value:
|
|
240
|
+
react_1.default.createElement(core_1.Select, { fullWidth: true, value: (_a = selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) !== null && _a !== void 0 ? _a : '', onChange: e => {
|
|
241
|
+
const selectedName = e.target.value;
|
|
242
|
+
if (selectedName === '') {
|
|
243
|
+
setSelectedUser(null);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
const selectedOption = expertOptions.find(opt => opt.value.name === selectedName);
|
|
247
|
+
setSelectedUser(selectedOption ? selectedOption.value : null);
|
|
248
|
+
}
|
|
249
|
+
}, displayEmpty: true, variant: "outlined", placeholder: `Select a ${isExpert ? 'Client' : 'Expert'}` },
|
|
150
250
|
react_1.default.createElement(core_1.MenuItem, { disabled: true, value: "" },
|
|
151
251
|
react_1.default.createElement("em", null, `Select a ${isExpert ? 'Client' : 'Expert'}`)),
|
|
152
|
-
expertOptions.map(option => (react_1.default.createElement(core_1.MenuItem, { key: option.value, value: option.value }, option.label))))),
|
|
252
|
+
expertOptions.map(option => (react_1.default.createElement(core_1.MenuItem, { key: option.value.id, value: option.value.name }, option.label))))),
|
|
153
253
|
react_1.default.createElement("div", null,
|
|
154
254
|
react_1.default.createElement("label", { className: "block text-sm font-medium text-gray-700 mb-1" }, "Which Project(s)?"),
|
|
155
255
|
react_1.default.createElement(core_1.Select, { multiple: true, fullWidth: true, value: selectedProjects, onChange: (e) => {
|
|
@@ -166,7 +266,7 @@ const EscalationSubmitForm = ({ goBack, goHome, experts, projects, isExpert = fa
|
|
|
166
266
|
}, placeholder: `Select a Project(s)` },
|
|
167
267
|
react_1.default.createElement(core_1.MenuItem, { disabled: true, value: "" },
|
|
168
268
|
react_1.default.createElement("em", null, "Select a Project(s)...")),
|
|
169
|
-
projectOptions.map(option => (react_1.default.createElement(core_1.MenuItem, { key: option.value, value: option.value }, option.label))))),
|
|
269
|
+
projectOptions.map(option => (react_1.default.createElement(core_1.MenuItem, { key: option.value.id, value: option.value }, option.label))))),
|
|
170
270
|
react_1.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" },
|
|
171
271
|
react_1.default.createElement("div", null,
|
|
172
272
|
react_1.default.createElement("label", { className: "block text-sm font-medium text-gray-700 mb-1" }, "Issue Type"),
|
|
@@ -194,7 +294,7 @@ const EscalationSubmitForm = ({ goBack, goHome, experts, projects, isExpert = fa
|
|
|
194
294
|
react_1.default.createElement(core_1.MenuItem, { value: "Low" }, "Low")))),
|
|
195
295
|
react_1.default.createElement("div", null,
|
|
196
296
|
react_1.default.createElement("label", { className: "block text-sm font-medium text-gray-700 mb-1" }, "When did this issue start?"),
|
|
197
|
-
react_1.default.createElement(LogTimeModalAuthenticated_1.SelectDate, { timeLogDate:
|
|
297
|
+
react_1.default.createElement(LogTimeModalAuthenticated_1.SelectDate, { timeLogDate: issueStartDate, setTimeLogDate: setIssueStartDate, isInvalid: false, setIsDateInvalid: setIsDateInvalid, validateDate: validateDate, maxDate: (0, dayjs_1.default)(new Date()).format("MM-DD-YYYY") })),
|
|
198
298
|
react_1.default.createElement("div", null,
|
|
199
299
|
react_1.default.createElement("label", { className: "block text-sm font-medium text-gray-700 mb-1" }, "What's the problem?"),
|
|
200
300
|
react_1.default.createElement(base_ui_1.Input, { type: "text", value: problemInput, placeholder: "Describe the issue...", onChange: (e) => setProblemInput(e.target.value) })),
|
|
@@ -206,12 +306,12 @@ const EscalationSubmitForm = ({ goBack, goHome, experts, projects, isExpert = fa
|
|
|
206
306
|
react_1.default.createElement("div", { className: "border border-dashed border-gray-300 rounded-md p-6 text-center" },
|
|
207
307
|
react_1.default.createElement("p", { className: "text-sm text-gray-600 mb-2" }, "Upload screenshots, emails, or documents"),
|
|
208
308
|
react_1.default.createElement("input", { ref: fileInputRef, type: "file", multiple: true, accept: ".pdf,.doc,.docx,.jpeg,.png,.gif,.csv", hidden: true, onChange: handleFileUpload }),
|
|
209
|
-
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", isLoading: uploadingFile, size: "sm" }),
|
|
210
|
-
uploadFiles.length > 0 && (react_1.default.createElement("ul", { className: "mt-2 text-sm text-gray-600 list-disc list-inside" }, uploadFiles.map(f => react_1.default.createElement("li", { key: f.name }, f.
|
|
309
|
+
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", isLoading: uploadingFile, disabled: selectedUser === null, size: "sm" }),
|
|
310
|
+
uploadFiles.length > 0 && (react_1.default.createElement("ul", { className: "mt-2 text-sm text-gray-600 list-disc list-inside" }, uploadFiles.map(f => react_1.default.createElement("li", { key: f.name }, f.split('%2F')[1])))))),
|
|
211
311
|
react_1.default.createElement("div", { className: "bg-[#EFF6FF] p-3 rounded" },
|
|
212
312
|
react_1.default.createElement(base_ui_1.Checkbox, { id: "checkbox-1", label: `I understand this will notify my ${isExpert ? 'client' : 'expert'} and the Paro support team`, name: "acknowledge", isChecked: isChecked, onChange: () => setIsChecked(prev => !prev) })),
|
|
213
313
|
react_1.default.createElement("div", { className: "flex justify-end space-x-3 pt-4" },
|
|
214
|
-
react_1.default.createElement(base_ui_1.Button, { label: "Cancel", onClick: goBack }),
|
|
215
|
-
react_1.default.createElement(base_ui_1.Button, { label: "Submit issue", onClick: handleSubmit, color: "primary", disabled: !isFormValid() }))))));
|
|
314
|
+
react_1.default.createElement(base_ui_1.Button, { label: "Cancel", onClick: goBack, disabled: submitting || uploadingFile }),
|
|
315
|
+
react_1.default.createElement(base_ui_1.Button, { label: "Submit issue", onClick: handleSubmit, color: "primary", isLoading: submitting, disabled: !isFormValid() || uploadingFile }))))));
|
|
216
316
|
};
|
|
217
317
|
exports.default = EscalationSubmitForm;
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
export type EscalationChatDetails = {
|
|
2
|
-
title: string;
|
|
3
|
-
project: string;
|
|
4
|
-
id: string;
|
|
5
|
-
status: string;
|
|
6
|
-
};
|
|
7
1
|
interface EscalationTabProps {
|
|
8
2
|
activeTab: string;
|
|
9
|
-
openEscalationChat: (
|
|
3
|
+
openEscalationChat: (issue: any) => void;
|
|
10
4
|
setSelectedIssueId: (issue: number) => void;
|
|
11
5
|
activeIssues: any[];
|
|
12
6
|
inProgressIssues: any[];
|
|
13
7
|
resolvedIssues: any[];
|
|
14
8
|
isExpert: boolean;
|
|
9
|
+
updateProjectEscalation: any;
|
|
10
|
+
downloadDocumentUrl: string;
|
|
11
|
+
bucketName: string;
|
|
15
12
|
}
|
|
16
|
-
declare const EscalationTabsContent: ({ activeTab, openEscalationChat, setSelectedIssueId, activeIssues, inProgressIssues, resolvedIssues, isExpert, }: EscalationTabProps) => JSX.Element;
|
|
13
|
+
declare const EscalationTabsContent: ({ activeTab, openEscalationChat, setSelectedIssueId, activeIssues, inProgressIssues, resolvedIssues, isExpert, updateProjectEscalation, downloadDocumentUrl, bucketName, }: EscalationTabProps) => JSX.Element;
|
|
17
14
|
export default EscalationTabsContent;
|
|
@@ -30,18 +30,18 @@ const react_1 = __importDefault(require("react"));
|
|
|
30
30
|
const EscalationIssueCard_1 = __importStar(require("./EscalationIssueCard"));
|
|
31
31
|
const base_ui_1 = require("@paro.io/base-ui");
|
|
32
32
|
;
|
|
33
|
-
const EscalationTabsContent = ({ activeTab, openEscalationChat, setSelectedIssueId, activeIssues, inProgressIssues, resolvedIssues, isExpert, }) => {
|
|
33
|
+
const EscalationTabsContent = ({ activeTab, openEscalationChat, setSelectedIssueId, activeIssues, inProgressIssues, resolvedIssues, isExpert, updateProjectEscalation, downloadDocumentUrl, bucketName, }) => {
|
|
34
34
|
if (activeTab === 'action-required') {
|
|
35
35
|
return (react_1.default.createElement("div", null,
|
|
36
36
|
react_1.default.createElement("div", { className: "flex items-center justify-between mb-4" },
|
|
37
37
|
react_1.default.createElement("h3", { className: "text-lg font-semibold text-gray-900" }, "Issues Requiring Your Response")),
|
|
38
|
-
react_1.default.createElement(EscalationIssueCard_1.default, { issues: activeIssues, isExpert: isExpert, openEscalationChat: openEscalationChat, showRespondButton: true, setSelectedIssueId: setSelectedIssueId })));
|
|
38
|
+
react_1.default.createElement(EscalationIssueCard_1.default, { issues: activeIssues, isExpert: isExpert, openEscalationChat: openEscalationChat, showRespondButton: true, setSelectedIssueId: setSelectedIssueId, updateProjectEscalation: updateProjectEscalation, downloadDocumentUrl: downloadDocumentUrl, bucketName: bucketName })));
|
|
39
39
|
}
|
|
40
40
|
if (activeTab === 'in-progress') {
|
|
41
41
|
return (react_1.default.createElement("div", null,
|
|
42
42
|
react_1.default.createElement("div", { className: "flex items-center justify-between mb-4" },
|
|
43
43
|
react_1.default.createElement("h3", { className: "text-lg font-semibold text-gray-900" }, "Issues In Progress")),
|
|
44
|
-
react_1.default.createElement(EscalationIssueCard_1.default, { issues: inProgressIssues, isExpert: isExpert, openEscalationChat: openEscalationChat, showMarkResolvedButton: true })));
|
|
44
|
+
react_1.default.createElement(EscalationIssueCard_1.default, { issues: inProgressIssues, isExpert: isExpert, openEscalationChat: openEscalationChat, showMarkResolvedButton: true, updateProjectEscalation: updateProjectEscalation, downloadDocumentUrl: downloadDocumentUrl, bucketName: bucketName })));
|
|
45
45
|
}
|
|
46
46
|
if (activeTab === 'resolved') {
|
|
47
47
|
return (react_1.default.createElement("div", null,
|
|
@@ -77,12 +77,7 @@ const EscalationTabsContent = ({ activeTab, openEscalationChat, setSelectedIssue
|
|
|
77
77
|
react_1.default.createElement("span", { className: "text-xs text-gray-500" },
|
|
78
78
|
"Resolved By ",
|
|
79
79
|
issue.resolvedBy)))),
|
|
80
|
-
react_1.default.createElement(base_ui_1.Button, { onClick: () => openEscalationChat(
|
|
81
|
-
title: issue.chatTitle,
|
|
82
|
-
project: issue.projectName,
|
|
83
|
-
id: issue.id,
|
|
84
|
-
status: issue.chatStatus
|
|
85
|
-
}), label: "Chat", color: "primary" }))));
|
|
80
|
+
react_1.default.createElement(base_ui_1.Button, { onClick: () => openEscalationChat(issue), label: "Chat", color: "primary" }))));
|
|
86
81
|
}))
|
|
87
82
|
:
|
|
88
83
|
react_1.default.createElement("div", null, "No resolved issues.")));
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
interface EscalationsProps {
|
|
2
|
-
|
|
2
|
+
expertsOrClients: any[];
|
|
3
3
|
projects: any[];
|
|
4
4
|
isExpert: boolean;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
escalations: any[];
|
|
6
|
+
documentUploadUrl: string;
|
|
7
|
+
downloadDocumentUrl: string;
|
|
8
|
+
uploadExpertClientFiles?: any;
|
|
9
|
+
createProjectEscalation: any;
|
|
10
|
+
updateProjectEscalation: any;
|
|
11
|
+
createEscalationChatMessage: any;
|
|
12
|
+
bucketName: string;
|
|
13
|
+
user: any;
|
|
8
14
|
}
|
|
9
|
-
export declare const Escalations: ({
|
|
15
|
+
export declare const Escalations: ({ expertsOrClients, projects, isExpert, escalations, documentUploadUrl, downloadDocumentUrl, uploadExpertClientFiles, createProjectEscalation, updateProjectEscalation, createEscalationChatMessage, bucketName, user, }: EscalationsProps) => JSX.Element;
|
|
10
16
|
export {};
|
|
@@ -39,14 +39,29 @@ const AccountSuspensionBanner_1 = __importDefault(require("./AccountSuspensionBa
|
|
|
39
39
|
const AccountSuspensionModal_1 = __importDefault(require("./AccountSuspensionModal"));
|
|
40
40
|
const base_icons_1 = require("@paro.io/base-icons");
|
|
41
41
|
const EscalationIssueCard_1 = require("./EscalationIssueCard");
|
|
42
|
-
const Escalations = ({
|
|
42
|
+
const Escalations = ({ expertsOrClients, projects, isExpert = false, escalations, documentUploadUrl, downloadDocumentUrl, uploadExpertClientFiles, createProjectEscalation, updateProjectEscalation, createEscalationChatMessage, bucketName, user, }) => {
|
|
43
43
|
const [activeSection, setActiveSection] = (0, react_1.useState)('support');
|
|
44
44
|
const [selectedProject, setSelectedProject] = (0, react_1.useState)(null);
|
|
45
45
|
const [selectedIssueId, setSelectedIssueId] = (0, react_1.useState)(null); // using selectedIssueId 0 for new escalation submission
|
|
46
46
|
const [showEscalationChat, setShowEscalationChat] = (0, react_1.useState)(false);
|
|
47
|
-
const [activeChatIssue, setActiveChatIssue] = (0, react_1.useState)(null);
|
|
47
|
+
const [activeChatIssue, setActiveChatIssue] = (0, react_1.useState)(null);
|
|
48
48
|
const [activeEscalationTab, setActiveEscalationTab] = (0, react_1.useState)('action-required');
|
|
49
49
|
const [showSuspensionModal, setShowSuspensionModal] = (0, react_1.useState)(false);
|
|
50
|
+
const activeIssues = escalations.filter(issue => {
|
|
51
|
+
var _a;
|
|
52
|
+
const userTypeId = (_a = issue === null || issue === void 0 ? void 0 : issue.submittedByUser) === null || _a === void 0 ? void 0 : _a.userTypeId;
|
|
53
|
+
const internalEscalationTo = issue === null || issue === void 0 ? void 0 : issue.internalEscalationTo;
|
|
54
|
+
const status = issue === null || issue === void 0 ? void 0 : issue.status;
|
|
55
|
+
// Determine if this issue should be shown to the current user
|
|
56
|
+
const isRelevantEscalation = (isExpert
|
|
57
|
+
? (internalEscalationTo === 'expert' || internalEscalationTo === 'both')
|
|
58
|
+
: (internalEscalationTo === 'client' || internalEscalationTo === 'both'));
|
|
59
|
+
return ((userTypeId !== user.userTypeId ||
|
|
60
|
+
(userTypeId === 2 && isRelevantEscalation)) &&
|
|
61
|
+
status === 'InProgress');
|
|
62
|
+
});
|
|
63
|
+
const inProgressIssues = escalations.filter(issue => { var _a; return ((_a = issue === null || issue === void 0 ? void 0 : issue.submittedByUser) === null || _a === void 0 ? void 0 : _a.userTypeId) === user.userTypeId && (issue === null || issue === void 0 ? void 0 : issue.status) === 'InProgress'; });
|
|
64
|
+
const resolvedIssues = escalations.filter(issue => (issue === null || issue === void 0 ? void 0 : issue.status) === 'Resolved');
|
|
50
65
|
const goBack = () => {
|
|
51
66
|
if (selectedIssueId !== null) {
|
|
52
67
|
setSelectedIssueId(null);
|
|
@@ -136,10 +151,10 @@ const Escalations = ({ experts, projects, isExpert = false, activeIssues, inProg
|
|
|
136
151
|
!isExpert &&
|
|
137
152
|
react_1.default.createElement(EscalationReportBanner_1.default, { onReport: () => setSelectedIssueId(0) }),
|
|
138
153
|
react_1.default.createElement(EscalationTabs_1.default, { activeTab: activeEscalationTab, setActiveTab: setActiveEscalationTab, activeIssues: activeIssues.length, inProgressIssues: inProgressIssues.length, resolvedIssues: resolvedIssues.length }),
|
|
139
|
-
react_1.default.createElement(EscalationTabsContent_1.default, { activeTab: activeEscalationTab, openEscalationChat: openEscalationChat, setSelectedIssueId: setSelectedIssueId, activeIssues: activeIssues, inProgressIssues: inProgressIssues, resolvedIssues: resolvedIssues, isExpert: isExpert })),
|
|
140
|
-
activeSection === 'support' && activeIssues.filter((issue) => issue.
|
|
141
|
-
activeSection === 'support' && selectedIssueId === 0 && (react_1.default.createElement(EscalationSubmitForm_1.default, { goBack: goBack, goHome: goHome,
|
|
142
|
-
showEscalationChat && activeChatIssue && (react_1.default.createElement(EscalationChat_1.default, { activeChatIssue: activeChatIssue, showEscalationChat: showEscalationChat, setShowEscalationChat: setShowEscalationChat })),
|
|
154
|
+
react_1.default.createElement(EscalationTabsContent_1.default, { activeTab: activeEscalationTab, openEscalationChat: openEscalationChat, setSelectedIssueId: setSelectedIssueId, activeIssues: activeIssues, inProgressIssues: inProgressIssues, resolvedIssues: resolvedIssues, updateProjectEscalation: updateProjectEscalation, isExpert: isExpert, downloadDocumentUrl: downloadDocumentUrl, bucketName: bucketName })),
|
|
155
|
+
activeSection === 'support' && activeIssues.filter((issue) => issue.escalationNumber === selectedIssueId).length > 0 && (react_1.default.createElement(EscalationRespondForm_1.default, { goBack: goBack, selectedIssue: activeIssues.find((issue) => issue.escalationNumber === selectedIssueId), documentUploadUrl: documentUploadUrl, downloadDocumentUrl: downloadDocumentUrl, bucketName: bucketName, uploadExpertClientFiles: uploadExpertClientFiles, updateProjectEscalation: updateProjectEscalation, goHome: goHome, isExpert: isExpert })),
|
|
156
|
+
activeSection === 'support' && selectedIssueId === 0 && (react_1.default.createElement(EscalationSubmitForm_1.default, { goBack: goBack, goHome: goHome, expertsOrClients: expertsOrClients, projects: projects, documentUploadUrl: documentUploadUrl, bucketName: bucketName, uploadExpertClientFiles: uploadExpertClientFiles, createProjectEscalation: createProjectEscalation, isExpert: isExpert, user: user })),
|
|
157
|
+
showEscalationChat && activeChatIssue && (react_1.default.createElement(EscalationChat_1.default, { activeChatIssue: activeChatIssue, showEscalationChat: showEscalationChat, setShowEscalationChat: setShowEscalationChat, user: user, createEscalationChatMessage: createEscalationChatMessage, documentUploadUrl: documentUploadUrl, bucketName: bucketName, uploadExpertClientFiles: uploadExpertClientFiles, createProjectEscalation: createProjectEscalation, isExpert: isExpert })),
|
|
143
158
|
showSuspensionModal &&
|
|
144
159
|
react_1.default.createElement(AccountSuspensionModal_1.default, { showSuspensionModal: showSuspensionModal, onClose: () => setShowSuspensionModal(false) }))));
|
|
145
160
|
};
|
|
@@ -2,14 +2,18 @@ interface UploadFileParams {
|
|
|
2
2
|
file: File;
|
|
3
3
|
documentName: string;
|
|
4
4
|
disputeId?: number;
|
|
5
|
-
|
|
5
|
+
escalationId?: string;
|
|
6
|
+
escalationNumber?: string;
|
|
7
|
+
projectId?: number;
|
|
6
8
|
documentUploadUrl: string;
|
|
7
9
|
bucketName: string;
|
|
8
|
-
updateClientInvoiceDisputeMutation
|
|
10
|
+
updateClientInvoiceDisputeMutation?: any;
|
|
11
|
+
updateProjectEscalation?: any;
|
|
9
12
|
uploadExpertClientFiles?: any;
|
|
10
|
-
|
|
13
|
+
createProjectEscalation?: any;
|
|
14
|
+
previousFiles?: string | string[];
|
|
11
15
|
isExpert: boolean;
|
|
12
|
-
|
|
16
|
+
extraData?: {
|
|
13
17
|
clientId: number;
|
|
14
18
|
clientName: string;
|
|
15
19
|
email: string;
|
|
@@ -18,5 +22,5 @@ interface UploadFileParams {
|
|
|
18
22
|
projectName: string;
|
|
19
23
|
};
|
|
20
24
|
}
|
|
21
|
-
export declare const fileUploader: ({ file, projectId, documentUploadUrl, updateClientInvoiceDisputeMutation, uploadExpertClientFiles, bucketName, disputeId, previousFiles, isExpert,
|
|
25
|
+
export declare const fileUploader: ({ file, projectId, documentUploadUrl, updateClientInvoiceDisputeMutation, updateProjectEscalation, uploadExpertClientFiles, createProjectEscalation, bucketName, disputeId, escalationId, escalationNumber, previousFiles, isExpert, extraData, }: UploadFileParams) => Promise<any>;
|
|
22
26
|
export {};
|
|
@@ -15,7 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.fileUploader = void 0;
|
|
16
16
|
const UploadClient_1 = __importDefault(require("../shared/UploadClient"));
|
|
17
17
|
const utils_1 = require("../shared/utils");
|
|
18
|
-
const fileUploader = (_a) => __awaiter(void 0, [_a], void 0, function* ({ file, projectId, documentUploadUrl, updateClientInvoiceDisputeMutation, uploadExpertClientFiles, bucketName, disputeId, previousFiles, isExpert,
|
|
18
|
+
const fileUploader = (_a) => __awaiter(void 0, [_a], void 0, function* ({ file, projectId, documentUploadUrl, updateClientInvoiceDisputeMutation, updateProjectEscalation, uploadExpertClientFiles, createProjectEscalation, bucketName, disputeId, escalationId, escalationNumber, previousFiles, isExpert, extraData, }) {
|
|
19
19
|
const documentLinks = previousFiles ? (typeof previousFiles === 'string' ? previousFiles.split(',') : [...previousFiles]) : [];
|
|
20
20
|
try {
|
|
21
21
|
(0, utils_1.showToast)('success', 'Starting Document Upload');
|
|
@@ -23,6 +23,7 @@ const fileUploader = (_a) => __awaiter(void 0, [_a], void 0, function* ({ file,
|
|
|
23
23
|
fileSelected: file,
|
|
24
24
|
fileName: file.name,
|
|
25
25
|
projectId,
|
|
26
|
+
escalationId,
|
|
26
27
|
documentUploadUrl,
|
|
27
28
|
bucketName: bucketName,
|
|
28
29
|
});
|
|
@@ -47,20 +48,35 @@ const fileUploader = (_a) => __awaiter(void 0, [_a], void 0, function* ({ file,
|
|
|
47
48
|
},
|
|
48
49
|
}));
|
|
49
50
|
}
|
|
50
|
-
if (
|
|
51
|
+
if (escalationNumber) {
|
|
52
|
+
const escalationPromise = updateProjectEscalation({
|
|
53
|
+
variables: {
|
|
54
|
+
input: Object.assign({ escalationId }, (isExpert ? {
|
|
55
|
+
expertSupportingDocuments: documentLinks.join(',')
|
|
56
|
+
} : {
|
|
57
|
+
clientSupportingDocuments: documentLinks.join(',')
|
|
58
|
+
}))
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
promises.push(escalationPromise);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
console.log("escalationId condition is FALSE");
|
|
65
|
+
}
|
|
66
|
+
if (uploadExpertClientFiles && extraData) {
|
|
51
67
|
promises.push(uploadExpertClientFiles({
|
|
52
68
|
variables: {
|
|
53
69
|
input: {
|
|
54
|
-
clientId:
|
|
55
|
-
clientName:
|
|
56
|
-
email:
|
|
70
|
+
clientId: extraData.clientId,
|
|
71
|
+
clientName: extraData.clientName,
|
|
72
|
+
email: extraData.email,
|
|
57
73
|
fileName: file.name,
|
|
58
74
|
fileSize: file.size,
|
|
59
|
-
fileType: 'DISPUTE_DOCUMENTS',
|
|
60
|
-
freelancerId:
|
|
61
|
-
freelancerName:
|
|
75
|
+
fileType: disputeId ? 'DISPUTE_DOCUMENTS' : 'ESCALATION_DOCUMENTS',
|
|
76
|
+
freelancerId: extraData.freelancerId,
|
|
77
|
+
freelancerName: extraData.freelancerName,
|
|
62
78
|
projectId: projectId,
|
|
63
|
-
projectName:
|
|
79
|
+
projectName: extraData.projectName,
|
|
64
80
|
uploadedBy: isExpert ? 'FREELANCER' : 'CLIENT',
|
|
65
81
|
data: "",
|
|
66
82
|
},
|
|
@@ -78,6 +94,7 @@ const fileUploader = (_a) => __awaiter(void 0, [_a], void 0, function* ({ file,
|
|
|
78
94
|
return documentLinks;
|
|
79
95
|
}
|
|
80
96
|
catch (error) {
|
|
97
|
+
console.error("Error in fileUploader:", error);
|
|
81
98
|
(0, utils_1.showToast)('warning', 'Error uploading file. Please try again.');
|
|
82
99
|
return false;
|
|
83
100
|
}
|
|
@@ -6,6 +6,6 @@ interface ClientDisputeProjectCardProps {
|
|
|
6
6
|
bucketName?: string;
|
|
7
7
|
uploadExpertClientFiles?: any;
|
|
8
8
|
}
|
|
9
|
-
export declare const handleDownloadDocument: (projectId: number, fileName: string, downloadDocumentUrl: string, bucketName: string | undefined) => Promise<void>;
|
|
9
|
+
export declare const handleDownloadDocument: (projectId: number, fileName: string, downloadDocumentUrl: string, bucketName: string | undefined, escalationId?: string | undefined) => Promise<void>;
|
|
10
10
|
export declare const ClientDisputeProjectCard: ({ clientInvoice, updateClientInvoiceDisputeMutation, documentUploadUrl, downloadDocumentUrl, bucketName, uploadExpertClientFiles }: ClientDisputeProjectCardProps) => JSX.Element;
|
|
11
11
|
export {};
|
|
@@ -131,11 +131,11 @@ const handleFileDownload = ({ fileData, downloadFilename, streamData, fileType =
|
|
|
131
131
|
document.body.removeChild(downloadLink);
|
|
132
132
|
URL.revokeObjectURL(blobUrl);
|
|
133
133
|
};
|
|
134
|
-
const handleDownloadDocument = (projectId, fileName, downloadDocumentUrl, bucketName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
134
|
+
const handleDownloadDocument = (projectId, fileName, downloadDocumentUrl, bucketName, escalationId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
135
135
|
var _a;
|
|
136
136
|
// Decode the filename from URL encoding if needed
|
|
137
137
|
const decodedFileName = decodeURIComponent(fileName);
|
|
138
|
-
const downloadFileName = `project-${projectId}/${decodedFileName}`;
|
|
138
|
+
const downloadFileName = escalationId ? `escalation-${escalationId}/${decodedFileName}` : `project-${projectId}/${decodedFileName}`;
|
|
139
139
|
const downloadData = yield (0, FileDownloader_1.fileDownloader)({ downloadDocumentUrl: downloadDocumentUrl, fileKey: downloadFileName, bucketName: bucketName });
|
|
140
140
|
handleFileDownload({
|
|
141
141
|
fileData: downloadData,
|
|
@@ -205,7 +205,7 @@ const ClientDisputeProjectCard = ({ clientInvoice, updateClientInvoiceDisputeMut
|
|
|
205
205
|
previousFiles: (_e = (_d = (_c = clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.disputeProjects) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.clientDocumentLinks) !== null && _e !== void 0 ? _e : [],
|
|
206
206
|
isExpert: false,
|
|
207
207
|
uploadExpertClientFiles: uploadExpertClientFiles,
|
|
208
|
-
|
|
208
|
+
extraData: {
|
|
209
209
|
clientId: (_f = clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.invoice) === null || _f === void 0 ? void 0 : _f.clientId,
|
|
210
210
|
clientName: (_g = clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.client) === null || _g === void 0 ? void 0 : _g.name,
|
|
211
211
|
email: (_h = clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.freelancer) === null || _h === void 0 ? void 0 : _h.email,
|
|
@@ -86,7 +86,7 @@ const DisputeSection = ({ dispute, documentUploadUrl, downloadDocumentUrl, bucke
|
|
|
86
86
|
disputeId: dispute === null || dispute === void 0 ? void 0 : dispute.disputeId,
|
|
87
87
|
previousFiles: isExpert ? expertDocumentLinks : clientDocumentLinks,
|
|
88
88
|
isExpert: isExpert,
|
|
89
|
-
|
|
89
|
+
extraData: {
|
|
90
90
|
clientId: (_c = dispute === null || dispute === void 0 ? void 0 : dispute.invoice) === null || _c === void 0 ? void 0 : _c.clientId,
|
|
91
91
|
clientName: (_d = dispute === null || dispute === void 0 ? void 0 : dispute.client) === null || _d === void 0 ? void 0 : _d.name,
|
|
92
92
|
email: (_e = dispute === null || dispute === void 0 ? void 0 : dispute.freelancer) === null || _e === void 0 ? void 0 : _e.email,
|
|
@@ -6,9 +6,10 @@ export default class UploadClient {
|
|
|
6
6
|
documentUploadUrl: string;
|
|
7
7
|
projectId: number;
|
|
8
8
|
bucketName: string;
|
|
9
|
+
escalationId: string;
|
|
9
10
|
};
|
|
10
11
|
constructor(props: any);
|
|
11
|
-
generateS3Key(projectId: number, fileName: string): string;
|
|
12
|
+
generateS3Key(projectId: number, escalationId: string, fileName: string): string;
|
|
12
13
|
triggerMultipartUpload(): Promise<string | undefined>;
|
|
13
14
|
uploadMultiPartFile(): Promise<string | undefined>;
|
|
14
15
|
completeUpload(partsArray: any): Promise<string | undefined>;
|
|
@@ -18,19 +18,21 @@ class UploadClient {
|
|
|
18
18
|
documentUploadUrl: props.documentUploadUrl,
|
|
19
19
|
projectId: props.projectId,
|
|
20
20
|
bucketName: props.bucketName,
|
|
21
|
+
escalationId: props.escalationId,
|
|
21
22
|
};
|
|
22
23
|
}
|
|
23
|
-
generateS3Key(projectId, fileName) {
|
|
24
|
+
generateS3Key(projectId, escalationId, fileName) {
|
|
24
25
|
const sanitizedFileName = fileName.replace(/[^a-zA-Z0-9.-]/g, '_');
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
if (escalationId) {
|
|
27
|
+
return `escalation-${escalationId}/${sanitizedFileName}`;
|
|
28
|
+
}
|
|
29
|
+
return `project-${projectId}/${sanitizedFileName}`;
|
|
27
30
|
}
|
|
28
31
|
;
|
|
29
32
|
triggerMultipartUpload() {
|
|
30
33
|
return __awaiter(this, void 0, void 0, function* () {
|
|
31
34
|
try {
|
|
32
|
-
const fileName = this.generateS3Key(this.state.projectId, this.state.fileName);
|
|
33
|
-
console.log('File Name', fileName);
|
|
35
|
+
const fileName = this.generateS3Key(this.state.projectId, this.state.escalationId, this.state.fileName);
|
|
34
36
|
this.state = Object.assign(Object.assign({}, this.state), { fileName: fileName });
|
|
35
37
|
const params = {
|
|
36
38
|
fileName: this.state.fileName,
|
|
@@ -120,7 +122,6 @@ class UploadClient {
|
|
|
120
122
|
throw new Error('Failed to complete multipart upload');
|
|
121
123
|
}
|
|
122
124
|
const { responseData } = yield response.json();
|
|
123
|
-
console.log('Upload Complete', responseData);
|
|
124
125
|
return `${JSON.stringify(responseData)}`;
|
|
125
126
|
}
|
|
126
127
|
catch (error) {
|
|
@@ -40,6 +40,7 @@ export declare const features: string[];
|
|
|
40
40
|
export declare const titleFeatures: string[];
|
|
41
41
|
export declare const serviceDescriptions: Record<string, string>;
|
|
42
42
|
export declare const selectedServicesReducer: (selectedServices: any, action: any) => any;
|
|
43
|
+
export declare const generateUUID: () => string;
|
|
43
44
|
export declare const sharedUtils: {
|
|
44
45
|
selectedServicesReducer: (selectedServices: any, action: any) => any;
|
|
45
46
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sharedUtils = exports.selectedServicesReducer = exports.serviceDescriptions = exports.titleFeatures = exports.features = exports.titleMappings = exports.DOCUMENT_TYPE_CONSTANTS = exports.validateFileUpload = exports.showToast = exports.MAX_FILE_SIZE = exports.ACCEPTED_FILE_TYPES = exports.formatDate = exports.CustomPaper = exports.handleDownloadPdf = exports.getFileMimeType = exports.stateAbbreviationMap = exports.getPreviousMonthStartDate = exports.isOlderThan30Days = exports.formatTenure = exports.isOnClickEvent = void 0;
|
|
3
|
+
exports.sharedUtils = exports.generateUUID = exports.selectedServicesReducer = exports.serviceDescriptions = exports.titleFeatures = exports.features = exports.titleMappings = exports.DOCUMENT_TYPE_CONSTANTS = exports.validateFileUpload = exports.showToast = exports.MAX_FILE_SIZE = exports.ACCEPTED_FILE_TYPES = exports.formatDate = exports.CustomPaper = exports.handleDownloadPdf = exports.getFileMimeType = exports.stateAbbreviationMap = exports.getPreviousMonthStartDate = exports.isOlderThan30Days = exports.formatTenure = exports.isOnClickEvent = void 0;
|
|
4
4
|
exports.getComparator = getComparator;
|
|
5
5
|
exports.stableSort = stableSort;
|
|
6
6
|
exports.compareItems = compareItems;
|
|
7
7
|
const core_1 = require("@material-ui/core");
|
|
8
8
|
const ReviewsTab_1 = require("../ReviewsTab/ReviewsTab");
|
|
9
|
+
const uuid_1 = require("uuid");
|
|
9
10
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
11
|
const isOnClickEvent = (potentialEvent) => {
|
|
11
12
|
return (potentialEvent === null || potentialEvent === void 0 ? void 0 : potentialEvent.type) === 'mouseup' || (potentialEvent === null || potentialEvent === void 0 ? void 0 : potentialEvent.type) === 'keydown' || (potentialEvent === null || potentialEvent === void 0 ? void 0 : potentialEvent.type) === 'click';
|
|
@@ -357,4 +358,8 @@ const selectedServicesReducer = (selectedServices, action) => {
|
|
|
357
358
|
}
|
|
358
359
|
};
|
|
359
360
|
exports.selectedServicesReducer = selectedServicesReducer;
|
|
361
|
+
const generateUUID = () => {
|
|
362
|
+
return (0, uuid_1.v4)();
|
|
363
|
+
};
|
|
364
|
+
exports.generateUUID = generateUUID;
|
|
360
365
|
exports.sharedUtils = { selectedServicesReducer: exports.selectedServicesReducer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paro.io/expert-shared-components",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.44",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
"unlink-local": "yarn unlink && cd node_modules/react && yarn unlink && cd ../react-dom && yarn unlink"
|
|
15
15
|
},
|
|
16
16
|
"repository": "https://github.com/paroadmin/expert-shared-components.git",
|
|
17
|
-
"keywords": [
|
|
17
|
+
"keywords": [
|
|
18
|
+
"react",
|
|
19
|
+
"components",
|
|
20
|
+
"shared"
|
|
21
|
+
],
|
|
18
22
|
"author": "apande@paro.io",
|
|
19
23
|
"license": "MIT",
|
|
20
24
|
"dependencies": {
|
|
@@ -40,6 +44,7 @@
|
|
|
40
44
|
"react-hot-toast": "^2.4.1",
|
|
41
45
|
"react-input-mask": "^3.0.0-alpha.2",
|
|
42
46
|
"styled-components": "^5.3.3",
|
|
47
|
+
"uuid": "^11.1.0",
|
|
43
48
|
"yup": "^0.32.11"
|
|
44
49
|
},
|
|
45
50
|
"devDependencies": {
|
|
@@ -49,6 +54,7 @@
|
|
|
49
54
|
"@types/react-datepicker": "^4.19.6",
|
|
50
55
|
"@types/react-dom": "^18.2.22",
|
|
51
56
|
"@types/styled-components": "^5.1.22",
|
|
57
|
+
"@types/uuid": "^10.0.0",
|
|
52
58
|
"@types/yup": "^0.29.13",
|
|
53
59
|
"typescript": "^5.3.3"
|
|
54
60
|
},
|