@paro.io/expert-shared-components 1.13.1 → 1.13.3

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.
@@ -1,7 +1,7 @@
1
- declare const EscalationChat: ({ activeChatIssue, showEscalationChat, setShowEscalationChat, user, createEscalationChatMessage, documentUploadUrl, bucketName, uploadExpertClientFiles, updateProjectEscalation, isExpert, }: {
1
+ declare const EscalationChat: ({ activeChatIssue, showEscalationChat, onClose, user, createEscalationChatMessage, documentUploadUrl, bucketName, uploadExpertClientFiles, updateProjectEscalation, isExpert, }: {
2
2
  activeChatIssue: any;
3
3
  showEscalationChat: boolean;
4
- setShowEscalationChat: (showEscalationChat: boolean) => void;
4
+ onClose: () => void;
5
5
  user: any;
6
6
  createEscalationChatMessage: any;
7
7
  documentUploadUrl: string;
@@ -40,7 +40,8 @@ const DiscussionSection_1 = require("../Invoices/DiscussionSection");
40
40
  const EscalationIssueCard_1 = require("./EscalationIssueCard");
41
41
  const FileUploader_1 = require("../FileUploader");
42
42
  const EscalationRespondForm_1 = require("./EscalationRespondForm");
43
- const EscalationChat = ({ activeChatIssue, showEscalationChat, setShowEscalationChat, user, createEscalationChatMessage, documentUploadUrl, bucketName, uploadExpertClientFiles, updateProjectEscalation, isExpert, }) => {
43
+ const utils_1 = require("../shared/utils");
44
+ const EscalationChat = ({ activeChatIssue, showEscalationChat, onClose, user, createEscalationChatMessage, documentUploadUrl, bucketName, uploadExpertClientFiles, updateProjectEscalation, isExpert, }) => {
44
45
  var _a, _b, _c, _d;
45
46
  const [uploadFiles, setUploadFiles] = (0, react_1.useState)(isExpert
46
47
  ? (((_a = activeChatIssue === null || activeChatIssue === void 0 ? void 0 : activeChatIssue.expertSupportingDocuments) === null || _a === void 0 ? void 0 : _a.split(",")) || [])
@@ -108,15 +109,11 @@ const EscalationChat = ({ activeChatIssue, showEscalationChat, setShowEscalation
108
109
  }
109
110
  });
110
111
  const docs = [activeChatIssue.expertSupportingDocuments, activeChatIssue.clientSupportingDocuments, activeChatIssue.internalSupportingDocuments, ...uploadFiles];
111
- const processedDocs = docs
112
- .filter(doc => doc !== null && doc !== undefined && doc !== '' && doc !== "NULL")
113
- .flatMap(doc => doc.split(','))
114
- .map(doc => doc.trim())
115
- .filter(doc => doc !== '' || doc !== "NULL");
112
+ const processedDocs = (0, utils_1.processDocs)(docs);
116
113
  const project = activeChatIssue.projectDetails && Array.isArray(activeChatIssue.projectDetails) && activeChatIssue.projectDetails.length > 0
117
114
  ? `${(_c = activeChatIssue.projectDetails[0]) === null || _c === void 0 ? void 0 : _c.projectName}${activeChatIssue.projectDetails.length > 1 ? ` +${activeChatIssue.projectDetails.length - 1} more` : ''} `
118
115
  : '';
119
- return (react_1.default.createElement(core_1.Dialog, { open: showEscalationChat, onClose: () => setShowEscalationChat(false), maxWidth: 'sm' },
116
+ return (react_1.default.createElement(core_1.Dialog, { open: showEscalationChat, onClose: onClose, maxWidth: 'sm' },
120
117
  react_1.default.createElement(core_1.DialogTitle, null,
121
118
  react_1.default.createElement("div", { className: "text-black bg-white mb-1 p-2 pl-4 absolute top-0 left-0 w-full flex flex-row justify-between items-center z-50" },
122
119
  react_1.default.createElement("div", { className: "flex flex-col items-start" },
@@ -130,14 +127,14 @@ const EscalationChat = ({ activeChatIssue, showEscalationChat, setShowEscalation
130
127
  " \u2022 ",
131
128
  project)),
132
129
  react_1.default.createElement("div", { className: "flex items-center space-x-4" },
133
- react_1.default.createElement(core_1.IconButton, { onClick: () => setShowEscalationChat(false) },
130
+ react_1.default.createElement(core_1.IconButton, { onClick: onClose },
134
131
  react_1.default.createElement(base_icons_1.IconX, null))))),
135
132
  react_1.default.createElement(core_1.DialogContent, null,
136
133
  react_1.default.createElement("div", { className: "bg-white rounded-lg w-full overflow-hidden flex flex-col p-2 mt-12" },
137
134
  react_1.default.createElement(DiscussionSection_1.DiscussionSection, { escalationNumber: activeChatIssue.escalationNumber, currentUser: user, messages: (_d = activeChatIssue === null || activeChatIssue === void 0 ? void 0 : activeChatIssue.chatMessages) !== null && _d !== void 0 ? _d : [], onCreateMessage: createEscalationChatMessage, isInternal: false }),
138
135
  processedDocs && processedDocs.length > 0 && (react_1.default.createElement("div", { className: "flex flex-wrap gap-2 mt-4 mb-2" },
139
136
  react_1.default.createElement("span", { className: "text-sm font-bold text-gray-500 items-center" }, "Supporting Documents: "),
140
- processedDocs.map((d, idx) => (react_1.default.createElement(EscalationIssueCard_1.CustomTag, { key: idx, label: d.split('%2F')[1], customColor: "success" }))))),
137
+ processedDocs.map((d, idx) => (react_1.default.createElement(EscalationIssueCard_1.CustomTag, { key: idx, label: d.split('%2F')[1], customColor: `bg-success border-success` }))))),
141
138
  react_1.default.createElement("div", { className: "bg-white mt-2" },
142
139
  react_1.default.createElement("div", { className: "space-y-3" },
143
140
  react_1.default.createElement("div", { className: "flex items-center justify-between" },
@@ -145,6 +142,6 @@ const EscalationChat = ({ activeChatIssue, showEscalationChat, setShowEscalation
145
142
  react_1.default.createElement("input", { id: "upload-file", type: "file", multiple: false, accept: ".pdf,.doc,.docx,.jpeg,.png,.gif,.csv,.xslx", style: { display: 'none' }, ref: fileInputRef, onChange: handleFileUpload }),
146
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 })),
147
144
  react_1.default.createElement("div", { className: "flex space-x-2" },
148
- react_1.default.createElement(base_ui_1.Button, { label: "cancel", onClick: () => setShowEscalationChat(false), disabled: uploadingFile })))))))));
145
+ react_1.default.createElement(base_ui_1.Button, { label: "cancel", onClick: onClose, disabled: uploadingFile })))))))));
149
146
  };
150
147
  exports.default = EscalationChat;
@@ -34,6 +34,7 @@ const base_icons_1 = require("@paro.io/base-icons");
34
34
  const dayjs_1 = __importDefault(require("dayjs"));
35
35
  const ViewResponseModal_1 = __importDefault(require("./ViewResponseModal"));
36
36
  const MarkResolvedModal_1 = __importDefault(require("./MarkResolvedModal"));
37
+ const utils_1 = require("../shared/utils");
37
38
  const getBackgroundColor = (type) => {
38
39
  switch (type) {
39
40
  case 'Critical':
@@ -49,8 +50,9 @@ const getBackgroundColor = (type) => {
49
50
  }
50
51
  };
51
52
  const CustomTag = ({ label, iconLeft, onClick, customColor, }) => {
52
- const color = customColor ? customColor : getBackgroundColor(label);
53
- 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-medium ${onClick ? 'cursor-pointer' : ''}` },
53
+ const getColor = getBackgroundColor(label);
54
+ const color = customColor ? customColor : `bg-${getColor} border-${getColor}`;
55
+ 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 ${color} ml-2 font-medium ${onClick ? 'cursor-pointer' : ''}` },
54
56
  iconLeft && react_1.default.createElement("span", null, iconLeft), label === null || label === void 0 ? void 0 :
55
57
  label.toUpperCase()));
56
58
  };
@@ -60,23 +62,44 @@ const EscalationIssueCard = ({ issues, isExpert, openEscalationChat, showRespond
60
62
  const [viewResponseModal, setViewResponseModal] = (0, react_1.useState)(null);
61
63
  const [markAsResolved, setMarkAsResolved] = (0, react_1.useState)(null);
62
64
  const getResponseButtonText = (issue) => {
63
- const hasResponse = isExpert ? issue.clientResponse : issue.expertResponse;
64
- return hasResponse ? "View Response" : "Awaiting Response";
65
+ var _a;
66
+ const userTypeId = (_a = issue === null || issue === void 0 ? void 0 : issue.submittedByUser) === null || _a === void 0 ? void 0 : _a.userTypeId;
67
+ if (userTypeId === 3) { // Submitted by Client
68
+ if (isExpert) {
69
+ return "View Response";
70
+ }
71
+ else {
72
+ return issue.expertResponse ? "View Response" : "Awaiting Response";
73
+ }
74
+ }
75
+ if (userTypeId === 1) { // Submitted by Expert
76
+ if (isExpert) {
77
+ return issue.clientResponse ? "View Response" : "Awaiting Response";
78
+ }
79
+ else {
80
+ return "View Response";
81
+ }
82
+ }
83
+ if (userTypeId === 2) { // Submitted by Paro internal
84
+ if (isExpert) {
85
+ return issue.clientResponse ? "View Response" : "Awaiting Response";
86
+ }
87
+ else {
88
+ return issue.expertResponse ? "View Response" : "Awaiting Response";
89
+ }
90
+ }
91
+ return "Awaiting Response";
65
92
  };
66
93
  if (!issues.length)
67
94
  return react_1.default.createElement("div", null, "No issues.");
68
95
  return (react_1.default.createElement("div", { className: "space-y-4" },
69
96
  issues.map((issue) => {
70
- var _a;
97
+ var _a, _b, _c, _d, _e;
71
98
  const project = issue.projectDetails && Array.isArray(issue.projectDetails) && issue.projectDetails.length > 0
72
99
  ? `${(_a = issue.projectDetails[0]) === null || _a === void 0 ? void 0 : _a.projectName}${issue.projectDetails.length > 1 ? ` +${issue.projectDetails.length - 1} more` : ''} `
73
100
  : '';
74
101
  const docs = [issue.expertSupportingDocuments, issue.clientSupportingDocuments, issue.internalSupportingDocuments];
75
- const processedDocs = docs
76
- .filter(doc => doc !== null && doc !== undefined && doc !== '' && doc !== "NULL")
77
- .flatMap(doc => doc.split(','))
78
- .map(doc => doc.trim())
79
- .filter(doc => doc !== '' || doc !== "NULL");
102
+ const processedDocs = (0, utils_1.processDocs)(docs);
80
103
  return (react_1.default.createElement("div", { key: issue.id, className: "border border-gray-200 rounded-lg bg-white p-4" },
81
104
  react_1.default.createElement("div", { className: "flex items-center justify-between mb-3" },
82
105
  react_1.default.createElement("div", { className: "flex-1 ml-2" },
@@ -84,31 +107,34 @@ const EscalationIssueCard = ({ issues, isExpert, openEscalationChat, showRespond
84
107
  react_1.default.createElement("div", { className: "font-bold" }, issue.problem),
85
108
  react_1.default.createElement(exports.CustomTag, { label: issue.severityLevel })),
86
109
  react_1.default.createElement("div", { className: "text-xs text-gray-500 font-bold" },
110
+ isExpert ? (_b = issue === null || issue === void 0 ? void 0 : issue.client) === null || _b === void 0 ? void 0 : _b.name : (_c = issue === null || issue === void 0 ? void 0 : issue.freelancer) === null || _c === void 0 ? void 0 : _c.name,
111
+ " \u2022 ",
87
112
  project,
88
113
  " \u2022 Case #",
89
114
  issue.escalationNumber)),
90
- react_1.default.createElement(exports.CustomTag, { label: issue.escalationType, customColor: isExpert ? 'neutral' : 'success' })),
115
+ react_1.default.createElement(exports.CustomTag, { label: issue.escalationType, customColor: `bg-[#B0B5D3] border-[#181027]` })),
91
116
  react_1.default.createElement("div", { className: "bg-gray-50 rounded-md p-3 mb-4 border" },
92
117
  react_1.default.createElement("div", { className: "flex items-center justify-between mb-2" },
93
118
  react_1.default.createElement("div", { className: "text-sm text-gray-500" },
94
119
  "Submitted by: ",
95
- react_1.default.createElement("span", { className: "font-bold" }, issue.submittedByUser.firstName + " " + issue.submittedByUser.lastName),
96
- issue.createdAt && react_1.default.createElement(react_1.default.Fragment, null,
120
+ react_1.default.createElement("span", { className: "font-bold" }, ((_d = issue === null || issue === void 0 ? void 0 : issue.submittedByUser) === null || _d === void 0 ? void 0 : _d.firstName) + " " + ((_e = issue === null || issue === void 0 ? void 0 : issue.submittedByUser) === null || _e === void 0 ? void 0 : _e.lastName)),
121
+ (issue === null || issue === void 0 ? void 0 : issue.createdAt) && react_1.default.createElement(react_1.default.Fragment, null,
97
122
  " \u2022 Submitted on: ",
98
- react_1.default.createElement("span", { className: "font-bold" }, (0, dayjs_1.default)(issue.createdAt).format("MM-DD-YYYY"))))),
123
+ react_1.default.createElement("span", { className: "font-bold" }, (0, dayjs_1.default)(issue === null || issue === void 0 ? void 0 : issue.createdAt).format("MM-DD-YYYY"))))),
99
124
  react_1.default.createElement("div", { className: "text-sm text-gray-600 mb-3" }, issue.outcome),
100
125
  processedDocs && processedDocs.length > 0 && (react_1.default.createElement("div", { className: "flex flex-wrap gap-2" },
101
126
  react_1.default.createElement("span", { className: "text-sm font-bold text-gray-500 items-center" }, "Supporting Documents: "),
102
- processedDocs.map((d, idx) => (react_1.default.createElement(exports.CustomTag, { key: idx, customColor: "success", label: d.split('%2F')[1], iconLeft: react_1.default.createElement(base_icons_1.IconDocumentDownload, { size: "xs" }), onClick: () => {
127
+ processedDocs.map((d, idx) => (react_1.default.createElement(exports.CustomTag, { key: idx, customColor: `bg-success border-success`, label: d.split('%2F')[1], iconLeft: react_1.default.createElement(base_icons_1.IconDocumentDownload, { size: "xs" }), onClick: () => {
103
128
  (0, ClientDisputeProjectCard_1.handleDownloadDocument)(issue.escalationId, d.split('%2F')[1], downloadDocumentUrl, bucketName);
104
129
  } })))))),
105
130
  react_1.default.createElement("div", { className: "flex flex-wrap gap-2" },
106
- react_1.default.createElement(base_ui_1.Button, { onClick: () => openEscalationChat(issue), label: "Chat", color: "primary" }),
131
+ react_1.default.createElement(base_ui_1.Button, { onClick: () => openEscalationChat(issue.escalationNumber), label: "Chat", color: "primary" }),
107
132
  showRespondButton && setSelectedIssueId && (react_1.default.createElement(base_ui_1.Button, { label: "Respond to Escalation", onClick: () => setSelectedIssueId(issue.escalationNumber), color: 'primary' })),
108
133
  showMarkResolvedButton && (react_1.default.createElement(base_ui_1.Button, { label: getResponseButtonText(issue), onClick: () => { setViewResponseModal(issue); }, color: 'primary', disabled: getResponseButtonText(issue) === 'Awaiting Response' })),
109
- showMarkResolvedButton && !isExpert && (react_1.default.createElement(base_ui_1.Button, { onClick: () => { setMarkAsResolved(issue); }, label: "Mark as Resolved", color: "primary" })))));
134
+ showMarkResolvedButton && !isExpert && issue.submittedByUser.userTypeId === 3 && ( // only client can resolve issue raised by client
135
+ react_1.default.createElement(base_ui_1.Button, { onClick: () => { setMarkAsResolved(issue); }, label: "Mark as Resolved", color: "primary" })))));
110
136
  }),
111
- !!viewResponseModal && react_1.default.createElement(ViewResponseModal_1.default, { response: isExpert ? viewResponseModal.clientResponse : viewResponseModal.expertResponse, isExpert: isExpert, open: !!viewResponseModal, onClose: () => setViewResponseModal(null) }),
137
+ !!viewResponseModal && react_1.default.createElement(ViewResponseModal_1.default, { selectedIssue: viewResponseModal, isExpert: isExpert, open: !!viewResponseModal, onClose: () => setViewResponseModal(null) }),
112
138
  !!markAsResolved && react_1.default.createElement(MarkResolvedModal_1.default, { escalationId: markAsResolved.escalationId, expertName: (_b = (_a = markAsResolved === null || markAsResolved === void 0 ? void 0 : markAsResolved.freelancer) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'Expert', open: !!markAsResolved, onClose: () => setMarkAsResolved(false), updateProjectEscalation: updateProjectEscalation, userId: userId })));
113
139
  };
114
140
  exports.default = EscalationIssueCard;
@@ -158,7 +158,7 @@ const EscalationRespondForm = ({ goBack, selectedIssue, documentUploadUrl, downl
158
158
  }
159
159
  }
160
160
  });
161
- (0, utils_1.showToast)("success", "Response submitted!");
161
+ (0, utils_1.showToast)("success", "Response submitted! The issue has been moved to in-progress.");
162
162
  }
163
163
  catch (error) {
164
164
  console.error("Failed to send response!", error);
@@ -170,11 +170,7 @@ const EscalationRespondForm = ({ goBack, selectedIssue, documentUploadUrl, downl
170
170
  }
171
171
  });
172
172
  const docs = [selectedIssue.expertSupportingDocuments, selectedIssue.clientSupportingDocuments, selectedIssue.internalSupportingDocuments, ...uploadFiles];
173
- const processedDocs = docs
174
- .filter(doc => doc !== null && doc !== undefined && doc !== '')
175
- .flatMap(doc => doc.split(','))
176
- .map(doc => doc.trim())
177
- .filter(doc => doc !== '');
173
+ const processedDocs = (0, utils_1.processDocs)(docs);
178
174
  return (react_1.default.createElement("div", null,
179
175
  react_1.default.createElement("button", { onClick: goBack, className: "flex items-center text-blue-600 mb-6" },
180
176
  react_1.default.createElement(base_icons_1.IconChevronLeft, { size: "xs" }),
@@ -203,7 +199,7 @@ const EscalationRespondForm = ({ goBack, selectedIssue, documentUploadUrl, downl
203
199
  : "text-gray-500 text-xs mt-1" }, type.description))))))),
204
200
  react_1.default.createElement("div", null,
205
201
  react_1.default.createElement("label", { className: "block text-sm font-medium text-gray-700 mb-1" }, "Your Response"),
206
- react_1.default.createElement(base_ui_1.Input, { type: "text", value: responseInput, placeholder: "Address the concern...", onChange: (e) => setResponseInput(e.target.value) })),
202
+ react_1.default.createElement(base_ui_1.Input, { type: "text", value: responseInput, placeholder: "Address the concern...", isRequired: true, onChange: (e) => setResponseInput(e.target.value) })),
207
203
  react_1.default.createElement("div", null,
208
204
  react_1.default.createElement("label", { className: "block text-sm font-medium text-gray-700 mb-1" }, "Supporting Documents (Optional)"),
209
205
  react_1.default.createElement("div", { className: "border border-dashed border-gray-300 rounded-md p-6 text-center" },
@@ -212,7 +208,7 @@ const EscalationRespondForm = ({ goBack, selectedIssue, documentUploadUrl, downl
212
208
  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" }),
213
209
  processedDocs && processedDocs.length > 0 && (react_1.default.createElement("div", { className: "flex flex-wrap gap-2 mt-4" },
214
210
  react_1.default.createElement("span", { className: "text-sm font-bold text-gray-500 items-center" }, "Supporting Documents: "),
215
- processedDocs.map((d, idx) => (react_1.default.createElement(EscalationIssueCard_1.CustomTag, { key: idx, label: d.split('%2F')[1], customColor: "success" }))))))),
211
+ processedDocs.map((d, idx) => (react_1.default.createElement(EscalationIssueCard_1.CustomTag, { key: idx, label: d.split('%2F')[1], customColor: `bg-success border-success` }))))))),
216
212
  react_1.default.createElement("div", { className: "bg-blue-50 border border-blue-200 rounded-md p-3" },
217
213
  react_1.default.createElement("div", { className: "flex items-center" },
218
214
  react_1.default.createElement(base_icons_1.IconInfoCircle, { size: "md" }),
@@ -221,6 +217,6 @@ const EscalationRespondForm = ({ goBack, selectedIssue, documentUploadUrl, downl
221
217
  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.")))),
222
218
  react_1.default.createElement("div", { className: "flex justify-end space-x-3" },
223
219
  react_1.default.createElement(base_ui_1.Button, { label: "Cancel", onClick: goBack, disabled: uploadingFile || submitting }),
224
- react_1.default.createElement(base_ui_1.Button, { label: "Submit Response", color: "primary", onClick: submitResponse, isLoading: submitting, disabled: uploadingFile })))))));
220
+ react_1.default.createElement(base_ui_1.Button, { label: "Submit Response", color: "primary", onClick: submitResponse, isLoading: submitting, disabled: uploadingFile || responseInput === '' })))))));
225
221
  };
226
222
  exports.default = EscalationRespondForm;
@@ -193,11 +193,11 @@ const EscalationSubmitForm = ({ goBack, goHome, expertsOrClients, projects, docu
193
193
  input: formData
194
194
  }
195
195
  });
196
- (0, utils_1.showToast)("success", "Escalation created successfully!");
196
+ (0, utils_1.showToast)("success", "Issue created successfully!");
197
197
  }
198
198
  catch (error) {
199
- console.error("Failed to create an escalation!", error);
200
- (0, utils_1.showToast)("warning", "Failed to create escalation!");
199
+ console.error("Failed to create an issue!", error);
200
+ (0, utils_1.showToast)("warning", "Failed to create an issue!");
201
201
  }
202
202
  finally {
203
203
  setSubmitting(false);
@@ -77,7 +77,8 @@ 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.statusChangedByUser.firstName + " " + issue.statusChangedByUser.lastName)))),
80
- react_1.default.createElement(base_ui_1.Button, { onClick: () => openEscalationChat(issue), label: "Chat", color: "primary" }))));
80
+ issue.status === 'Resolved' && (0, dayjs_1.default)().diff((0, dayjs_1.default)(issue.updatedAt), 'day') <= 30 &&
81
+ react_1.default.createElement(base_ui_1.Button, { onClick: () => openEscalationChat(issue.escalationNumber), label: "Chat", color: "primary" }))));
81
82
  }))
82
83
  :
83
84
  react_1.default.createElement("div", null, "No resolved issues.")));
@@ -45,7 +45,7 @@ const Escalations = ({ expertsOrClients, projects, isExpert = false, escalations
45
45
  const [selectedProject, setSelectedProject] = (0, react_1.useState)(null);
46
46
  const [selectedIssueId, setSelectedIssueId] = (0, react_1.useState)(null); // using selectedIssueId 0 for new escalation submission
47
47
  const [showEscalationChat, setShowEscalationChat] = (0, react_1.useState)(false);
48
- const [activeChatIssue, setActiveChatIssue] = (0, react_1.useState)(null);
48
+ const [activeChatEscalationNumber, setActiveChatEscalationNumber] = (0, react_1.useState)(null);
49
49
  const [activeEscalationTab, setActiveEscalationTab] = (0, react_1.useState)('action-required');
50
50
  const [showSuspensionModal, setShowSuspensionModal] = (0, react_1.useState)(false);
51
51
  const activeIssues = escalations.filter(issue => {
@@ -79,15 +79,16 @@ const Escalations = ({ expertsOrClients, projects, isExpert = false, escalations
79
79
  setSelectedProject(null);
80
80
  setActiveSection('support');
81
81
  };
82
- const openEscalationChat = (issue) => {
83
- setActiveChatIssue(issue);
82
+ const openEscalationChat = (escalationNumber) => {
83
+ setActiveChatEscalationNumber(escalationNumber);
84
84
  setShowEscalationChat(true);
85
85
  };
86
+ const activeChatIssue = escalations.find((issue) => issue.escalationNumber === activeChatEscalationNumber);
86
87
  return (react_1.default.createElement("div", { className: "bg-white w-full" },
87
88
  react_1.default.createElement("div", { className: isExpert ? "p-6" : "mx-auto p-6 border-solid border-t-2" },
88
89
  activeSection === 'support' && selectedIssueId === null && react_1.default.createElement("div", null,
89
90
  react_1.default.createElement("div", { className: "flex justify-between items-center mb-1 mt-2" },
90
- react_1.default.createElement("h2", { className: "text-xl font-bold" }, "Support & Escalation Management"),
91
+ react_1.default.createElement("h2", { className: "text-xl font-bold" }, "Support & Engagement Management"),
91
92
  activeIssues.length > 0 ? react_1.default.createElement("div", { className: "flex items-center space-x-2" },
92
93
  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-[#A73A43] rounded-full bg-[#F9BABF] border-[#F9BABF] font-medium` },
93
94
  activeIssues.length,
@@ -95,7 +96,7 @@ const Escalations = ({ expertsOrClients, projects, isExpert = false, escalations
95
96
  react_1.default.createElement("div", { className: "border-b pb-4 mb-6" },
96
97
  react_1.default.createElement("p", { className: "text-gray-600" }, `Manage escalations with integrated chat between you, your ${isExpert ? 'client' : 'expert'}, and Paro support. All conversations are organized by priority and status.`)),
97
98
  react_1.default.createElement("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6" },
98
- react_1.default.createElement("h3", { className: "font-medium text-blue-800 mb-3" }, "How Our Escalation System Works"),
99
+ react_1.default.createElement("h3", { className: "font-medium text-blue-800 mb-3" }, "How Our Engagement Support System Works"),
99
100
  react_1.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6" },
100
101
  react_1.default.createElement("div", null,
101
102
  react_1.default.createElement("h4", { className: "font-medium text-gray-900 mb-2" }, "Standard Resolution Process"),
@@ -155,7 +156,10 @@ const Escalations = ({ expertsOrClients, projects, isExpert = false, escalations
155
156
  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, userId: user.userId })),
156
157
  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, userId: user.userId })),
157
158
  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, clientId: clientId })),
158
- 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, updateProjectEscalation: updateProjectEscalation, isExpert: isExpert })),
159
+ showEscalationChat && activeChatIssue && (react_1.default.createElement(EscalationChat_1.default, { activeChatIssue: activeChatIssue, showEscalationChat: showEscalationChat, onClose: () => {
160
+ setActiveChatEscalationNumber(null);
161
+ setShowEscalationChat(false);
162
+ }, user: user, createEscalationChatMessage: createEscalationChatMessage, documentUploadUrl: documentUploadUrl, bucketName: bucketName, uploadExpertClientFiles: uploadExpertClientFiles, updateProjectEscalation: updateProjectEscalation, isExpert: isExpert })),
159
163
  showSuspensionModal &&
160
164
  react_1.default.createElement(AccountSuspensionModal_1.default, { showSuspensionModal: showSuspensionModal, onClose: () => setShowSuspensionModal(false) })),
161
165
  react_1.default.createElement(react_hot_toast_1.Toaster, { position: "top-center", toastOptions: {
@@ -53,11 +53,11 @@ const MarkResolvedModal = ({ escalationId, expertName, open, onClose, updateProj
53
53
  }
54
54
  }
55
55
  });
56
- (0, utils_1.showToast)("success", "Escalation marked as resolved!");
56
+ (0, utils_1.showToast)("success", "Issue marked as resolved!");
57
57
  }
58
58
  catch (error) {
59
59
  console.error("Failed to update escalation!", error);
60
- (0, utils_1.showToast)("warning", "Failed to update escalation!");
60
+ (0, utils_1.showToast)("warning", "Failed to update an issue!");
61
61
  }
62
62
  finally {
63
63
  setSubmitting(false);
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- declare const ViewResponseModal: ({ response, open, onClose, isExpert }: {
3
- response: string | null;
2
+ declare const ViewResponseModal: ({ selectedIssue, open, onClose, isExpert }: {
3
+ selectedIssue: any;
4
4
  open: boolean;
5
5
  onClose: () => void;
6
6
  isExpert: boolean;
@@ -6,7 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
7
  const base_icons_1 = require("@paro.io/base-icons");
8
8
  const core_1 = require("@material-ui/core");
9
- const ViewResponseModal = ({ response, open, onClose, isExpert }) => {
9
+ const utils_1 = require("../shared/utils");
10
+ const EscalationIssueCard_1 = require("./EscalationIssueCard");
11
+ const ViewResponseModal = ({ selectedIssue, open, onClose, isExpert }) => {
12
+ const docs = [selectedIssue.expertSupportingDocuments, selectedIssue.clientSupportingDocuments, selectedIssue.internalSupportingDocuments];
13
+ const processedDocs = (0, utils_1.processDocs)(docs);
10
14
  return (react_1.default.createElement(core_1.Dialog, { open: open, onClose: onClose, maxWidth: 'sm' },
11
15
  react_1.default.createElement(core_1.DialogTitle, null,
12
16
  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" },
@@ -20,8 +24,14 @@ const ViewResponseModal = ({ response, open, onClose, isExpert }) => {
20
24
  react_1.default.createElement("p", { className: "text-sm font-medium" },
21
25
  "Status: ",
22
26
  react_1.default.createElement("span", { className: "font-normal" }, "Responded")),
27
+ react_1.default.createElement("p", { className: "text-sm font-medium" },
28
+ "Response Type: ",
29
+ react_1.default.createElement("span", { className: "font-normal" }, isExpert ? selectedIssue.clientResponseType : selectedIssue.expertResponseType)),
23
30
  react_1.default.createElement("p", { className: "text-sm font-medium" },
24
31
  "Response: ",
25
- react_1.default.createElement("span", { className: "font-normal" }, response))))));
32
+ react_1.default.createElement("span", { className: "font-normal" }, isExpert ? selectedIssue.clientResponse : selectedIssue.expertResponse))),
33
+ processedDocs && processedDocs.length > 0 && (react_1.default.createElement("div", { className: "flex flex-wrap gap-2 mt-4 mb-2" },
34
+ react_1.default.createElement("span", { className: "text-sm font-bold text-gray-500 items-center" }, "Supporting Documents: "),
35
+ processedDocs.map((d, idx) => (react_1.default.createElement(EscalationIssueCard_1.CustomTag, { key: idx, label: d.split('%2F')[1], customColor: `bg-success border-success` }))))))));
26
36
  };
27
37
  exports.default = ViewResponseModal;
@@ -45,6 +45,11 @@ const DiscussionSection = ({ disputeId, escalationNumber, currentUser, messages,
45
45
  const [visibility, setVisibility] = (0, react_1.useState)('ALL');
46
46
  const [isLoading, setIsLoading] = (0, react_1.useState)(false);
47
47
  const threadsEndRef = (0, react_1.useRef)(null);
48
+ (0, react_1.useEffect)(() => {
49
+ if (threadsEndRef.current) {
50
+ threadsEndRef.current.scrollIntoView({ behavior: 'smooth' });
51
+ }
52
+ }, [messages]);
48
53
  // Get the sequence of unique userTypeIds to determine alignment
49
54
  const userSequence = (0, react_1.useMemo)(() => {
50
55
  const uniqueUsers = Array.from(new Set(messages.map(m => m.sender.userTypeId)));
@@ -109,7 +114,7 @@ const DiscussionSection = ({ disputeId, escalationNumber, currentUser, messages,
109
114
  return userTypeId !== currentUser.userTypeId ? 'justify-start' : 'justify-end';
110
115
  };
111
116
  const displayMessageVisibility = (visibility) => {
112
- switch (visibility) {
117
+ switch (visibility.toUpperCase()) {
113
118
  case 'ALL':
114
119
  return 'All';
115
120
  case 'EXPERT_INTERNAL_ONLY':
@@ -141,27 +146,28 @@ const DiscussionSection = ({ disputeId, escalationNumber, currentUser, messages,
141
146
  react_1.default.createElement(base_icons_1.IconChat, { size: "sm", className: "text-blue-600 mr-2" }),
142
147
  react_1.default.createElement("p", null, "This discussion applies to all projects in this dispute."))),
143
148
  react_1.default.createElement("div", { className: "rounded" },
144
- messages.length ? react_1.default.createElement("div", { className: "flex-1 max-h-[40vh] overflow-y-auto border rounded-md p-2 space-y-6" }, messages.map((message) => (react_1.default.createElement("div", { key: message.id, className: `flex ${getMessageAlignment(message.sender.userTypeId)}` },
145
- react_1.default.createElement("div", { className: `flex space-x-4 ${getMessageOrder(message.sender.userTypeId)} ${getMessageAlignment(message.sender.userTypeId)}` },
146
- react_1.default.createElement("div", { style: {
147
- backgroundColor: getSenderBackgroundColor(message.sender.userTypeId),
148
- color: getSenderTextColor(message.sender.userTypeId)
149
- }, className: "w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0" },
150
- react_1.default.createElement("span", { className: "text-sm font-bold" }, getSenderInitial(message.sender))),
151
- react_1.default.createElement("div", { className: "flex-1" },
152
- react_1.default.createElement("div", { className: `flex items-center ${getHeaderAlignment(message.sender.userTypeId)} space-x-2 mr-1` },
153
- react_1.default.createElement("span", { className: "text-sm font-bold text-[#333333]" },
154
- message.sender.firstName,
155
- " ",
156
- message.sender.lastName),
157
- react_1.default.createElement("span", { className: "text-xs text-[#666666]" }, (0, dayjs_1.default)(message.createdAt).format('MMM D, YYYY • h:mm A')),
158
- isInternal && (react_1.default.createElement("span", { className: "text-xs text-[#666666]" },
159
- "(",
160
- displayMessageVisibility(message.visibility),
161
- ")"))),
162
- react_1.default.createElement("div", { style: { backgroundColor: getSenderBackgroundColor(message.sender.userTypeId) }, className: "mt-1 p-3 rounded-lg" },
163
- react_1.default.createElement("p", { className: `text-sm ${getHeaderAlignment(message.sender.userTypeId)}`, style: { color: getSenderTextColor(message.sender.userTypeId) } }, message.messageText)))))))) : react_1.default.createElement(react_1.default.Fragment, null),
164
- react_1.default.createElement("div", { ref: threadsEndRef }),
149
+ messages.length ? react_1.default.createElement("div", { className: "flex-1 max-h-[40vh] overflow-y-auto border rounded-md p-2 space-y-6" },
150
+ messages.map((message) => (react_1.default.createElement("div", { key: message.id, className: `flex ${getMessageAlignment(message.sender.userTypeId)}` },
151
+ react_1.default.createElement("div", { className: `flex space-x-4 ${getMessageOrder(message.sender.userTypeId)} ${getMessageAlignment(message.sender.userTypeId)}` },
152
+ react_1.default.createElement("div", { style: {
153
+ backgroundColor: getSenderBackgroundColor(message.sender.userTypeId),
154
+ color: getSenderTextColor(message.sender.userTypeId)
155
+ }, className: "w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0" },
156
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, getSenderInitial(message.sender))),
157
+ react_1.default.createElement("div", { className: "flex-1" },
158
+ react_1.default.createElement("div", { className: `flex items-center ${getHeaderAlignment(message.sender.userTypeId)} space-x-2 mr-1` },
159
+ react_1.default.createElement("span", { className: "text-sm font-bold text-[#333333]" },
160
+ message.sender.firstName,
161
+ " ",
162
+ message.sender.lastName),
163
+ react_1.default.createElement("span", { className: "text-xs text-[#666666]" }, (0, dayjs_1.default)(message.createdAt).format('MMM D, YYYY • h:mm A')),
164
+ isInternal && (react_1.default.createElement("span", { className: "text-xs text-[#666666]" },
165
+ "(",
166
+ displayMessageVisibility(message.visibility),
167
+ ")"))),
168
+ react_1.default.createElement("div", { style: { backgroundColor: getSenderBackgroundColor(message.sender.userTypeId) }, className: "mt-1 p-3 rounded-lg" },
169
+ react_1.default.createElement("p", { className: `text-sm ${getHeaderAlignment(message.sender.userTypeId)}`, style: { color: getSenderTextColor(message.sender.userTypeId) } }, message.messageText))))))),
170
+ react_1.default.createElement("div", { ref: threadsEndRef })) : react_1.default.createElement(react_1.default.Fragment, null),
165
171
  react_1.default.createElement("div", { className: "space-y-4 p-6 space-y-6 rounded mt-4", style: { backgroundColor: '#F5F7F9' } },
166
172
  react_1.default.createElement("div", null,
167
173
  react_1.default.createElement("div", { className: "font-bold text-[#333333] mb-2" }, "Add Comment"),
@@ -42,6 +42,7 @@ export declare const titleFeatures: string[];
42
42
  export declare const serviceDescriptions: Record<string, string>;
43
43
  export declare const selectedServicesReducer: (selectedServices: any, action: any) => any;
44
44
  export declare const generateUUID: () => string;
45
+ export declare const processDocs: (docs: (string | null | undefined)[]) => string[];
45
46
  export declare const sharedUtils: {
46
47
  selectedServicesReducer: (selectedServices: any, action: any) => any;
47
48
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
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;
3
+ exports.sharedUtils = exports.processDocs = 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;
@@ -363,4 +363,13 @@ const generateUUID = () => {
363
363
  return (0, uuid_1.v4)();
364
364
  };
365
365
  exports.generateUUID = generateUUID;
366
+ const processDocs = (docs) => {
367
+ return docs
368
+ .filter(doc => doc !== null && doc !== undefined && doc !== '' && doc !== "NULL")
369
+ .flatMap(doc => doc.split(','))
370
+ .map(doc => doc.trim())
371
+ //@ts-ignore
372
+ .filter(doc => doc !== '' || doc !== "NULL");
373
+ };
374
+ exports.processDocs = processDocs;
366
375
  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.13.01",
3
+ "version": "1.13.3",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {