@rh-support/troubleshoot 2.6.358 → 2.6.361
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/esm/components/AccountInfo/OwnerSelector.js +1 -1
- package/lib/esm/components/CaseEditView/CaseDetailsErrorMessage.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/CaseDetailsErrorMessage.js +14 -4
- package/lib/esm/components/CaseEditView/CaseDetailsTabs.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/CaseDetailsTabs.js +11 -7
- package/lib/esm/components/CaseEditView/CaseOverview/CaseStatus.d.ts +2 -0
- package/lib/esm/components/CaseEditView/CaseOverview/CaseStatus.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/CaseOverview/CaseStatus.js +19 -8
- package/lib/esm/components/CaseEditView/CaseOverview/index.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/CaseOverview/index.js +16 -13
- package/lib/esm/components/CaseEditView/CaseSolutions/CaseSolutions.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/CaseSolutions/CaseSolutions.js +1 -1
- package/lib/esm/components/CaseEditView/ConfirmationModals/ReopenCaseModal.js +2 -2
- package/lib/esm/components/CaseEditView/RequestRemoteSession/NewEssTermsModal.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/RequestRemoteSession/NewEssTermsModal.js +7 -5
- package/lib/esm/components/CaseEditView/RequestRemoteSession/RemoteSessionAgreementModal.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/RequestRemoteSession/RemoteSessionAgreementModal.js +7 -5
- package/lib/esm/components/CaseEditView/RequestRemoteSession/remoteSessionUtils.d.ts +6 -0
- package/lib/esm/components/CaseEditView/RequestRemoteSession/remoteSessionUtils.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/RequestRemoteSession/remoteSessionUtils.js +8 -0
- package/lib/esm/components/CaseEditView/Tabs/CaseActionPlan/CaseActionPlan.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseActionPlan/CaseActionPlan.js +6 -3
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseContactPhoneNumber.js +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseHostname.js +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseInternalStatus.js +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseOpenshiftClusterId/CaseOpenshiftClusterId.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseOpenshiftClusterId/CaseOpenshiftClusterId.js +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CasePhoneNumberConfirmAlert.js +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseReferenceNumber.js +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseSbrGroups.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseSbrGroups.js +3 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/ProductVersion.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/ProductVersion.js +1 -6
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/Summary.js +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/useCustomEmails.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/useCustomEmails.js +4 -2
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseAttachment.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseAttachment.js +5 -3
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.js +33 -3
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.d.ts +6 -0
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.js +210 -35
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment/useCommentDrafts.d.ts +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment/useCommentDrafts.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment/useCommentDrafts.js +3 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.js +11 -17
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/VerifyCaseStatusModal/VerifyCaseStatusModal.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/VerifyCaseStatusModal/VerifyCaseStatusModal.js +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/CaseHistory.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/CaseHistory.js +5 -2
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.d.ts +2 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.js +4 -11
- package/lib/esm/components/CaseEditView/Tabs/CasePrivateNotes/CasePrivateNotes.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CasePrivateNotes/CasePrivateNotes.js +6 -3
- package/lib/esm/components/CaseEditView/Tabs/CaseSummary/CaseSummary.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseSummary/CaseSummary.js +1 -0
- package/lib/esm/components/CaseInformation/CaseGroup.d.ts.map +1 -1
- package/lib/esm/components/CaseInformation/CaseGroup.js +6 -2
- package/lib/esm/components/CaseInformation/CaseLabels.d.ts.map +1 -1
- package/lib/esm/components/CaseInformation/CaseLabels.js +6 -4
- package/lib/esm/components/CaseInformation/Fts.js +2 -2
- package/lib/esm/components/CaseManagement/Cep.js +1 -1
- package/lib/esm/components/CaseManagement/RHAssociatesSelector.d.ts.map +1 -1
- package/lib/esm/components/CaseManagement/RHAssociatesSelector.js +4 -2
- package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.d.ts.map +1 -1
- package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.js +4 -2
- package/lib/esm/components/EditDescription/EditDescription.d.ts.map +1 -1
- package/lib/esm/components/EditDescription/EditDescription.js +3 -1
- package/lib/esm/components/UpdateSeverityModal/UpdateSeverityModal.js +4 -4
- package/lib/esm/css/case.css +154 -8
- package/lib/esm/graphql/queries/getEscalationDetails.d.ts.map +1 -1
- package/lib/esm/graphql/queries/getEscalationDetails.js +5 -0
- package/lib/esm/hooks/useCaseComments/caseCommentsQuery.d.ts.map +1 -1
- package/lib/esm/hooks/useCaseComments/caseCommentsQuery.js +6 -1
- package/lib/esm/hooks/useCaseComments/index.d.ts +2 -1
- package/lib/esm/hooks/useCaseComments/index.d.ts.map +1 -1
- package/lib/esm/hooks/useCaseComments/types.d.ts +8 -0
- package/lib/esm/hooks/useCaseComments/types.d.ts.map +1 -1
- package/lib/esm/hooks/useCaseComments/useCaseComments.d.ts +12 -3
- package/lib/esm/hooks/useCaseComments/useCaseComments.d.ts.map +1 -1
- package/lib/esm/hooks/useCaseComments/useCaseComments.js +84 -9
- package/lib/esm/hooks/useCaseHistory.d.ts +2 -2
- package/lib/esm/hooks/useCaseHistory.d.ts.map +1 -1
- package/lib/esm/hooks/useCaseHistory.js +16 -7
- package/lib/esm/hooks/useCaseUpdateErrorMessage.d.ts +9 -1
- package/lib/esm/hooks/useCaseUpdateErrorMessage.d.ts.map +1 -1
- package/lib/esm/hooks/useCaseUpdateErrorMessage.js +103 -16
- package/lib/esm/models/caseHistory.d.ts +6 -0
- package/lib/esm/models/caseHistory.d.ts.map +1 -1
- package/lib/esm/reducers/CaseDiscussionTabReducer.d.ts +2 -1
- package/lib/esm/reducers/CaseDiscussionTabReducer.d.ts.map +1 -1
- package/lib/esm/reducers/CaseDiscussionTabReducer.js +26 -4
- package/lib/esm/utils/caseHistoryUtils.d.ts +19 -0
- package/lib/esm/utils/caseHistoryUtils.d.ts.map +1 -1
- package/lib/esm/utils/caseHistoryUtils.js +31 -0
- package/lib/esm/utils/linkifyHtmlTextNodes.d.ts +8 -0
- package/lib/esm/utils/linkifyHtmlTextNodes.d.ts.map +1 -0
- package/lib/esm/utils/linkifyHtmlTextNodes.js +37 -0
- package/package.json +7 -6
|
@@ -17,6 +17,7 @@ import { deleteCaseAttachment, formatDateTime, humanizeSize, updateCaseAttachmen
|
|
|
17
17
|
import isEmpty from 'lodash/isEmpty';
|
|
18
18
|
import React, { useContext, useRef, useState } from 'react';
|
|
19
19
|
import { Trans, useTranslation } from 'react-i18next';
|
|
20
|
+
import { useCaseUpdateErrorMessage } from '../../../../hooks/useCaseUpdateErrorMessage';
|
|
20
21
|
import { ATTACHMENT_DESCRIPTION_LENGTH_LIMIT } from '../../../../reducers/CaseConstNTypes';
|
|
21
22
|
import { DiscussionType } from '../../../../reducers/CaseDiscussionTabReducer';
|
|
22
23
|
import { PDFContext } from '../../PDFContainer';
|
|
@@ -25,6 +26,7 @@ import { JumpAndCopyLink } from './JumpAndCopyLink';
|
|
|
25
26
|
const CaseAttachment = React.forwardRef((props, ref) => {
|
|
26
27
|
var _a;
|
|
27
28
|
const { t } = useTranslation();
|
|
29
|
+
const caseUpdateError = useCaseUpdateErrorMessage();
|
|
28
30
|
const clipboardRef = useRef(null);
|
|
29
31
|
const { clipboardText, copyToClipboard } = useCopyToClipboard(clipboardRef);
|
|
30
32
|
const { request, isFetching: isDeleting } = useFetch(deleteCaseAttachment, { propgateErrors: true });
|
|
@@ -39,7 +41,7 @@ const CaseAttachment = React.forwardRef((props, ref) => {
|
|
|
39
41
|
const canEditCase = useCanEditCase();
|
|
40
42
|
const onDescriptionSave = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
41
43
|
if (!apolloClient.mutate) {
|
|
42
|
-
|
|
44
|
+
caseUpdateError.showFieldError({}, t('Attachment description'));
|
|
43
45
|
return;
|
|
44
46
|
}
|
|
45
47
|
setIsUpdating(true);
|
|
@@ -47,8 +49,8 @@ const CaseAttachment = React.forwardRef((props, ref) => {
|
|
|
47
49
|
const updatedAttachment = yield updateCaseAttachmentDescriptionByGraphQL(apolloClient, props.attachment, attachmentDescription);
|
|
48
50
|
props.onMetadataUpdate && props.onMetadataUpdate(props.attachment.uuid, updatedAttachment);
|
|
49
51
|
}
|
|
50
|
-
catch (
|
|
51
|
-
|
|
52
|
+
catch (e) {
|
|
53
|
+
caseUpdateError.showFieldError(e, t('Attachment description'), onDescriptionSave);
|
|
52
54
|
}
|
|
53
55
|
finally {
|
|
54
56
|
setIsUpdating(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CaseComments.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AA2BvE,OAAO,KAAwC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"CaseComments.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AA2BvE,OAAO,KAAwC,MAAM,OAAO,CAAC;AAW7D,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,kBAAkB,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAChH;AAED,QAAA,MAAM,YAAY,4EAgXhB,CAAC;AAEH,eAAe,YAAY,CAAC"}
|
|
@@ -25,6 +25,7 @@ import { Trans } from 'react-i18next';
|
|
|
25
25
|
// TODO: Re-enable after GraphQL migration of feedback
|
|
26
26
|
// import { useTranslation } from 'react-i18next';
|
|
27
27
|
import { DiscussionType } from '../../../../reducers/CaseDiscussionTabReducer';
|
|
28
|
+
import { linkifyHtmlTextNodes } from '../../../../utils/linkifyHtmlTextNodes';
|
|
28
29
|
import { PDFContext } from '../../PDFContainer';
|
|
29
30
|
import { DiscussionItemLink } from './DiscussionItemLink';
|
|
30
31
|
import { JumpAndCopyLink } from './JumpAndCopyLink';
|
|
@@ -149,6 +150,34 @@ const CaseComments = React.forwardRef((props, ref) => {
|
|
|
149
150
|
return React.createElement(Trans, null, "Automated comment");
|
|
150
151
|
}
|
|
151
152
|
};
|
|
153
|
+
// Tags the Quill RTE legitimately produces — everything else was typed
|
|
154
|
+
// literally by the user or injected from an external source (SFDC) and
|
|
155
|
+
// should render as visible text, not interpreted as HTML formatting.
|
|
156
|
+
const QUILL_TAGS = new Set([
|
|
157
|
+
'p',
|
|
158
|
+
'br',
|
|
159
|
+
'strong',
|
|
160
|
+
'em',
|
|
161
|
+
'u',
|
|
162
|
+
's',
|
|
163
|
+
'ul',
|
|
164
|
+
'ol',
|
|
165
|
+
'li',
|
|
166
|
+
'a',
|
|
167
|
+
'img',
|
|
168
|
+
'h1',
|
|
169
|
+
'h2',
|
|
170
|
+
'h3',
|
|
171
|
+
'h4',
|
|
172
|
+
'h5',
|
|
173
|
+
'h6',
|
|
174
|
+
'span',
|
|
175
|
+
'pre',
|
|
176
|
+
'blockquote',
|
|
177
|
+
'sub',
|
|
178
|
+
'sup',
|
|
179
|
+
]);
|
|
180
|
+
const escapeNonQuillTags = (html) => html.replace(/<(\/?)([\w-]+)([^>]*)>/g, (match, slash, tag, attrs) => QUILL_TAGS.has(tag.toLowerCase()) ? match : `<${slash}${tag}${attrs}>`);
|
|
152
181
|
const sanitizeHtml = (html) => {
|
|
153
182
|
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
|
|
154
183
|
if (node.tagName === 'A') {
|
|
@@ -156,7 +185,7 @@ const CaseComments = React.forwardRef((props, ref) => {
|
|
|
156
185
|
node.setAttribute('rel', 'noopener noreferrer');
|
|
157
186
|
}
|
|
158
187
|
});
|
|
159
|
-
const clean = DOMPurify.sanitize(html, { ADD_ATTR: ['target', 'rel'] });
|
|
188
|
+
const clean = DOMPurify.sanitize(escapeNonQuillTags(html), { ADD_ATTR: ['target', 'rel'] });
|
|
160
189
|
DOMPurify.removeHook('afterSanitizeAttributes');
|
|
161
190
|
return clean;
|
|
162
191
|
};
|
|
@@ -216,6 +245,7 @@ const CaseComments = React.forwardRef((props, ref) => {
|
|
|
216
245
|
}
|
|
217
246
|
// HTML comments (new rich text or legacy markdown — both stored as HTML)
|
|
218
247
|
let htmlString = comment.commentBody;
|
|
248
|
+
htmlString = linkifyHtmlTextNodes(htmlString);
|
|
219
249
|
if (isExportingPDF) {
|
|
220
250
|
htmlString = formatMarkdownHtmlForPDF(htmlString);
|
|
221
251
|
}
|
|
@@ -251,8 +281,8 @@ const CaseComments = React.forwardRef((props, ref) => {
|
|
|
251
281
|
React.createElement("span", { className: "pull-right pf-v6-l-flex pf-m-align-items-center" },
|
|
252
282
|
props.showJumpToComment && (React.createElement(DiscussionItemLink, { onClick: onJumpToComment(props.comment.id), discussionType: DiscussionType.COMMENT, itemId: props.comment.id, caseNumber: caseNumber, isCopyLink: false },
|
|
253
283
|
React.createElement(Trans, null, "Jump to comment"))),
|
|
254
|
-
React.createElement(JumpAndCopyLink, { caseNumber: caseNumber, cardId: props.comment.id, discussionType: DiscussionType.COMMENT }),
|
|
255
284
|
React.createElement("button", { onClick: () => props.onReply(props.comment.lastModifiedById, props.comment.id, props.comment.lastModifiedDate), className: `btn btn-app btn-link case-reply-button ${isExportingPDF ? 'hide-in-pdf' : ''}`, "data-tracking-id": "case-comment-reply", type: "button" },
|
|
256
|
-
React.createElement(Trans, null, "Reply"))
|
|
285
|
+
React.createElement(Trans, null, "Reply")),
|
|
286
|
+
React.createElement(JumpAndCopyLink, { caseNumber: caseNumber, cardId: props.comment.id, discussionType: DiscussionType.COMMENT })))));
|
|
257
287
|
});
|
|
258
288
|
export default CaseComments;
|
|
@@ -9,6 +9,12 @@ interface IProps {
|
|
|
9
9
|
tabRef: React.MutableRefObject<HTMLDivElement>;
|
|
10
10
|
isDiscussionTab: boolean;
|
|
11
11
|
refetchComments: () => Promise<unknown> | void;
|
|
12
|
+
fetchMore?: () => Promise<unknown> | void;
|
|
13
|
+
hasNextPage?: boolean;
|
|
14
|
+
isLoadingMore?: boolean;
|
|
15
|
+
loadMoreError?: boolean;
|
|
16
|
+
loadMoreRetryCount?: number;
|
|
17
|
+
retryLoadMore?: () => Promise<unknown> | void;
|
|
12
18
|
}
|
|
13
19
|
export default function CaseDiscussion(props: IProps): React.JSX.Element;
|
|
14
20
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CaseDiscussion.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"CaseDiscussion.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAoBvE,OAAO,KAA6E,MAAM,OAAO,CAAC;AAwClG,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IAGxB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC/C,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/C,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,KAAK,EAAE,MAAM,qBAyzBnD"}
|
|
@@ -8,25 +8,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { useApolloClient } from '@apollo/client/react';
|
|
11
|
-
import { MenuToggle, Select, SelectList, SelectOption } from '@patternfly/react-core';
|
|
11
|
+
import { Button, MenuToggle, Select, SelectList, SelectOption } from '@patternfly/react-core';
|
|
12
12
|
import { AlertMessage, AlertType, ErrorBoundary, LoadingIndicator, ToastNotification } from '@rh-support/components';
|
|
13
13
|
import { useGlobalStateContext } from '@rh-support/react-context';
|
|
14
14
|
import { AbilityContext, CaseDetailsFields, resourceActions, resources } from '@rh-support/user-permissions';
|
|
15
|
-
import { downloadCaseAttachment, dtmTrackEventCaseStepEncountered, getUrlParsedParams, scrollIntoView, } from '@rh-support/utils';
|
|
15
|
+
import { downloadCaseAttachment, dtmTrackEventCaseStepEncountered, getDropdownBtnPlaceholder, getUrlParsedParams, scrollIntoView, } from '@rh-support/utils';
|
|
16
16
|
import cloneDeep from 'lodash/cloneDeep';
|
|
17
17
|
import filter from 'lodash/filter';
|
|
18
18
|
import findIndex from 'lodash/findIndex';
|
|
19
19
|
import isEmpty from 'lodash/isEmpty';
|
|
20
20
|
import isEqual from 'lodash/isEqual';
|
|
21
|
+
import orderBy from 'lodash/orderBy';
|
|
21
22
|
import Mark from 'mark.js';
|
|
22
|
-
import React, { useContext, useEffect, useRef, useState } from 'react';
|
|
23
|
+
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
23
24
|
import { Trans, useTranslation } from 'react-i18next';
|
|
25
|
+
import { useInView } from 'react-intersection-observer';
|
|
24
26
|
import { useLocation } from 'react-router-dom';
|
|
25
27
|
import { AutoSizer, CellMeasurer, CellMeasurerCache, List, WindowScroller } from 'react-virtualized';
|
|
26
28
|
import { useCaseDispatch, useCaseSelector } from '../../../../context/CaseContext';
|
|
27
29
|
import { useCaseDetailsPageDispatchContext, useCaseDetailsPageStateContext, } from '../../../../context/CaseDetailsPageContext';
|
|
28
30
|
import { useCaseDiscussionTabDispatchContext, useCaseDiscussionTabStateContext, } from '../../../../context/CaseDiscussionTabContext';
|
|
29
|
-
import { clearReplyingTo, discussionSortOrderConst, DiscussionType, setDiscussions, setReplyingTo, setSort, updateAttachment, updateAttacmentsAfterRemoval, updateDiscussionStateAttachments, } from '../../../../reducers/CaseDiscussionTabReducer';
|
|
31
|
+
import { clearReplyingTo, discussionSortOrderConst, DiscussionType, setDiscussions, setFilter, setReplyingTo, setSort, updateAttachment, updateAttacmentsAfterRemoval, updateDiscussionStateAttachments, } from '../../../../reducers/CaseDiscussionTabReducer';
|
|
30
32
|
import { setCaseState } from '../../../../reducers/CaseReducer';
|
|
31
33
|
import { AttachmentContextProvider } from '../../../shared/fileUpload/reducer/AttachmentReducerContext';
|
|
32
34
|
import { PDFContext } from '../../PDFContainer';
|
|
@@ -37,11 +39,8 @@ import { CommentSearch } from './CommentSearch';
|
|
|
37
39
|
import { PostComment } from './PostComment';
|
|
38
40
|
/** Threshold above which comments render in a virtualized (windowed) list for performance. */
|
|
39
41
|
const VC_MINIMUM_COMMENT = 150;
|
|
40
|
-
let cellMeasureCache = new CellMeasurerCache({
|
|
41
|
-
fixedWidth: true,
|
|
42
|
-
defaultHeight: 100,
|
|
43
|
-
});
|
|
44
42
|
export default function CaseDiscussion(props) {
|
|
43
|
+
var _a;
|
|
45
44
|
const { t } = useTranslation();
|
|
46
45
|
const { isPostingRemoteRequestCommentCompleted, product, version, caseSalesforceId } = useCaseSelector((state) => ({
|
|
47
46
|
isPostingRemoteRequestCommentCompleted: state.isPostingRemoteRequestCommentCompleted,
|
|
@@ -52,7 +51,7 @@ export default function CaseDiscussion(props) {
|
|
|
52
51
|
const urlSearchParams = getUrlParsedParams(useLocation().search);
|
|
53
52
|
const discussionIdFromUrl = ((urlSearchParams === null || urlSearchParams === void 0 ? void 0 : urlSearchParams.commentId) || (urlSearchParams === null || urlSearchParams === void 0 ? void 0 : urlSearchParams.attachmentId));
|
|
54
53
|
const activeDiscussionElementRef = useRef(null);
|
|
55
|
-
const { sort, discussionFiltersListState,
|
|
54
|
+
const { sort, discussionFiltersListState, replyingToAuthor, replyingToTimestamp } = useCaseDiscussionTabStateContext();
|
|
56
55
|
const { globalMetadataState: { navBarRef, loggedInUsersAccount }, } = useGlobalStateContext();
|
|
57
56
|
const dispatchDiscussion = useCaseDiscussionTabDispatchContext();
|
|
58
57
|
const { caseDetailsPageState: { caseAttachmentHydra }, } = useCaseDetailsPageStateContext();
|
|
@@ -66,7 +65,9 @@ export default function CaseDiscussion(props) {
|
|
|
66
65
|
const listRef = React.useRef(null);
|
|
67
66
|
const windowScrollerRef = React.useRef(null);
|
|
68
67
|
const commentRef = React.useRef(null);
|
|
68
|
+
const cellMeasureCacheRef = useRef(new CellMeasurerCache({ fixedWidth: true, defaultHeight: 100 }));
|
|
69
69
|
const { isExportingPDF } = useContext(PDFContext);
|
|
70
|
+
const { ref: sentinelRef, inView } = useInView({ threshold: 0, rootMargin: '200px' });
|
|
70
71
|
const [searchResult, setSearchResult] = useState([]);
|
|
71
72
|
const [searchCommentText, setSearchCommentText] = useState('');
|
|
72
73
|
const [commentIndex, setCommentIndex] = useState(-1);
|
|
@@ -74,19 +75,105 @@ export default function CaseDiscussion(props) {
|
|
|
74
75
|
const [isJumpToCommentClicked, setJumpToCommentClicked] = useState(false);
|
|
75
76
|
const [canUpdateScrollTop, setCanUpdateScrollTop] = useState(true);
|
|
76
77
|
const [discussionSectionWidth, setDiscussionSectionWidth] = useState(null);
|
|
78
|
+
const [debouncedShowSkeleton, setDebouncedShowSkeleton] = useState(false);
|
|
79
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
80
|
+
const [selectedFilterItems, setSelectedFilterItems] = useState([]);
|
|
77
81
|
const [isSortOpen, setIsSortOpen] = useState(false);
|
|
78
82
|
const [sortSelected, setSortSelected] = useState('Select a value');
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
const { hasNextPage, isLoadingMore, loadMoreError, fetchMore } = props;
|
|
84
|
+
const isCommentFilterActive = discussionFiltersListState.some((f) => (f.value === DiscussionType.COMMENT || f.value === DiscussionType.ALL) && f.isSelected);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
console.log('[CaseDiscussion] fetchMore useEffect', {
|
|
87
|
+
inView,
|
|
88
|
+
hasNextPage,
|
|
89
|
+
isLoadingMore,
|
|
90
|
+
loadMoreError,
|
|
91
|
+
hasFetchMore: !!fetchMore,
|
|
92
|
+
isCommentFilterActive,
|
|
93
|
+
willFetch: inView && hasNextPage && !isLoadingMore && !loadMoreError && !!fetchMore && isCommentFilterActive,
|
|
83
94
|
});
|
|
95
|
+
if (inView && hasNextPage && !isLoadingMore && !loadMoreError && fetchMore && isCommentFilterActive) {
|
|
96
|
+
fetchMore();
|
|
97
|
+
}
|
|
98
|
+
}, [inView, hasNextPage, isLoadingMore, loadMoreError, fetchMore, isCommentFilterActive]);
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (props.isLoadingMore && props.hasNextPage) {
|
|
101
|
+
const timer = setTimeout(() => setDebouncedShowSkeleton(true), 300);
|
|
102
|
+
return () => clearTimeout(timer);
|
|
103
|
+
}
|
|
104
|
+
setDebouncedShowSkeleton(false);
|
|
105
|
+
}, [props.isLoadingMore, props.hasNextPage]);
|
|
106
|
+
const cellMeasureCache = cellMeasureCacheRef.current;
|
|
107
|
+
const allDiscussions = useMemo(() => {
|
|
108
|
+
var _a, _b, _c;
|
|
109
|
+
const allComments = (props.caseComments || [])
|
|
110
|
+
.filter((comment) => comment.isDraft === false)
|
|
111
|
+
.map((comment) => ({
|
|
112
|
+
item: Object.assign(Object.assign({}, comment), { isPublic: comment.isPublic === undefined ? true : comment.isPublic, last_modified_date: comment.lastModifiedDate || comment.createdDate }),
|
|
113
|
+
discussionType: DiscussionType.COMMENT,
|
|
114
|
+
last_modified_date: comment.lastModifiedDate || comment.createdDate,
|
|
115
|
+
}));
|
|
116
|
+
let allAttachments = canSeeAttachments
|
|
117
|
+
? (caseAttachmentHydra.data || [])
|
|
118
|
+
.filter((attachment) => (attachment === null || attachment === void 0 ? void 0 : attachment.lastModifiedDate) || (attachment === null || attachment === void 0 ? void 0 : attachment.createdDate))
|
|
119
|
+
.map((attachment) => ({
|
|
120
|
+
item: Object.assign(Object.assign({}, attachment), { lastModifiedDate: attachment.lastModifiedDate || attachment.createdDate }),
|
|
121
|
+
last_modified_date: (attachment.lastModifiedDate || attachment.createdDate),
|
|
122
|
+
discussionType: DiscussionType.ATTACHMENT,
|
|
123
|
+
}))
|
|
124
|
+
: [];
|
|
125
|
+
if (props.hasNextPage && allComments.length > 0) {
|
|
126
|
+
const sortOrder = (_a = sort === null || sort === void 0 ? void 0 : sort.sortOrder) !== null && _a !== void 0 ? _a : 'desc';
|
|
127
|
+
const commentDates = allComments.map((c) => c.last_modified_date).filter(Boolean);
|
|
128
|
+
if (commentDates.length > 0) {
|
|
129
|
+
if (sortOrder === 'desc') {
|
|
130
|
+
const oldestLoaded = commentDates.reduce((min, d) => (d < min ? d : min));
|
|
131
|
+
allAttachments = allAttachments.filter((a) => a.last_modified_date >= oldestLoaded);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const newestLoaded = commentDates.reduce((max, d) => (d > max ? d : max));
|
|
135
|
+
allAttachments = allAttachments.filter((a) => a.last_modified_date <= newestLoaded);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const merged = [...allComments, ...allAttachments];
|
|
140
|
+
return orderBy(merged, ['last_modified_date', (d) => { var _a; return (_a = d.item.id) !== null && _a !== void 0 ? _a : ''; }], [(_b = sort === null || sort === void 0 ? void 0 : sort.sortOrder) !== null && _b !== void 0 ? _b : 'desc', (_c = sort === null || sort === void 0 ? void 0 : sort.sortOrder) !== null && _c !== void 0 ? _c : 'desc']);
|
|
141
|
+
}, [props.caseComments, caseAttachmentHydra.data, canSeeAttachments, sort, props.hasNextPage]);
|
|
142
|
+
const usedPaginationRef = useRef(false);
|
|
143
|
+
if (props.hasNextPage || props.isLoadingMore) {
|
|
144
|
+
usedPaginationRef.current = true;
|
|
84
145
|
}
|
|
85
|
-
const shouldRenderRVComponent = !isExportingPDF && allDiscussions.length > VC_MINIMUM_COMMENT;
|
|
146
|
+
const shouldRenderRVComponent = !isExportingPDF && allDiscussions.length > VC_MINIMUM_COMMENT && !usedPaginationRef.current;
|
|
147
|
+
const list = discussionFiltersListState.filter((f) => f.canSee);
|
|
148
|
+
const computeFilterComment = () => {
|
|
149
|
+
return filter(allDiscussions, (discussion) => {
|
|
150
|
+
const activeFilter = discussionFiltersListState.filter((f) => f.value === discussion.discussionType && f.isSelected);
|
|
151
|
+
return activeFilter.length === 1 ? discussion : null;
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
const computeFilteredComments = computeFilterComment();
|
|
86
155
|
const comuteSearchedComments = searchResult.length === 0
|
|
87
|
-
? cloneDeep(
|
|
88
|
-
: filter([...
|
|
89
|
-
const
|
|
156
|
+
? cloneDeep(computeFilteredComments)
|
|
157
|
+
: filter([...computeFilteredComments], (o) => { var _a; return searchResult.indexOf((_a = o === null || o === void 0 ? void 0 : o.item) === null || _a === void 0 ? void 0 : _a.id) !== -1; });
|
|
158
|
+
const isRefetching = props.commentsLoading && !props.isLoadingMore;
|
|
159
|
+
const isSearchResultEmpty = !isRefetching &&
|
|
160
|
+
!isEmpty(searchCommentText) &&
|
|
161
|
+
(searchResult.length === 0 || comuteSearchedComments.length === 0);
|
|
162
|
+
// DEBUG: track every render of CaseDiscussion
|
|
163
|
+
console.log('[CaseDiscussion] render', {
|
|
164
|
+
commentsLoading: props.commentsLoading,
|
|
165
|
+
isLoadingMore: props.isLoadingMore,
|
|
166
|
+
hasNextPage: props.hasNextPage,
|
|
167
|
+
loadMoreError: props.loadMoreError,
|
|
168
|
+
isRefetching,
|
|
169
|
+
isSearchResultEmpty,
|
|
170
|
+
allDiscussionsLen: allDiscussions.length,
|
|
171
|
+
filteredLen: computeFilteredComments.length,
|
|
172
|
+
searchedLen: comuteSearchedComments.length,
|
|
173
|
+
caseCommentsLen: (_a = props.caseComments) === null || _a === void 0 ? void 0 : _a.length,
|
|
174
|
+
inView,
|
|
175
|
+
isCommentFilterActive,
|
|
176
|
+
});
|
|
90
177
|
const setVRCommentIndex = (cId) => {
|
|
91
178
|
setCommentIndex(cId < 0 ? 0 : cId);
|
|
92
179
|
setJumpToCommentClicked(true);
|
|
@@ -100,11 +187,15 @@ export default function CaseDiscussion(props) {
|
|
|
100
187
|
(allDiscussions || []).length === 0) {
|
|
101
188
|
return;
|
|
102
189
|
}
|
|
190
|
+
const targetIndex = findIndex([...comuteSearchedComments], (discussion) => discussion.item.id === discussionIdFromUrl);
|
|
191
|
+
// Comment is on a later page — wait until keep-fetching brings it in.
|
|
192
|
+
if (targetIndex < 0) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
103
195
|
activeElementId.current = discussionIdFromUrl;
|
|
104
196
|
if (allDiscussions.length > VC_MINIMUM_COMMENT) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (0 <= commentIndex && commentIndex <= 3) {
|
|
197
|
+
setVRCommentIndex(targetIndex);
|
|
198
|
+
if (targetIndex <= 3) {
|
|
108
199
|
const offset = 80;
|
|
109
200
|
const scrollTopValue = ((_a = windowScrollerRef === null || windowScrollerRef === void 0 ? void 0 : windowScrollerRef.current) === null || _a === void 0 ? void 0 : _a._positionFromTop) - offset;
|
|
110
201
|
const scrollLeftValue = (_b = windowScrollerRef === null || windowScrollerRef === void 0 ? void 0 : windowScrollerRef.current) === null || _b === void 0 ? void 0 : _b._positionFromLeft;
|
|
@@ -126,13 +217,13 @@ export default function CaseDiscussion(props) {
|
|
|
126
217
|
});
|
|
127
218
|
}
|
|
128
219
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
129
|
-
}, [discussionIdFromUrl
|
|
220
|
+
}, [discussionIdFromUrl]);
|
|
130
221
|
useEffect(() => {
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
222
|
+
if (isExportingPDF)
|
|
223
|
+
return;
|
|
224
|
+
setDiscussions(dispatchDiscussion, props.caseComments, [], caseAttachmentHydra.data, [], [], discussionFiltersListState, false, false, canSeeAttachments, sort);
|
|
134
225
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
135
|
-
}, [props.caseComments, caseAttachmentHydra, canSeeAttachments]);
|
|
226
|
+
}, [props.caseComments, caseAttachmentHydra, canSeeAttachments, sort]);
|
|
136
227
|
useEffect(() => {
|
|
137
228
|
if (!caseAttachmentHydra.isFetching && caseAttachmentHydra.isError) {
|
|
138
229
|
ToastNotification.addDangerMessage(t('There was an error loading attachments.'));
|
|
@@ -238,6 +329,15 @@ export default function CaseDiscussion(props) {
|
|
|
238
329
|
}
|
|
239
330
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
240
331
|
}, [discussionSectionWidth, shouldRenderRVComponent]);
|
|
332
|
+
useEffect(() => {
|
|
333
|
+
var _a;
|
|
334
|
+
if (shouldRenderRVComponent) {
|
|
335
|
+
cellMeasureCache.clearAll();
|
|
336
|
+
(_a = listRef.current) === null || _a === void 0 ? void 0 : _a.recomputeRowHeights();
|
|
337
|
+
}
|
|
338
|
+
usedPaginationRef.current = false;
|
|
339
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
340
|
+
}, [sort]);
|
|
241
341
|
const renderVRCommentElement = () => {
|
|
242
342
|
return (React.createElement(WindowScroller, { ref: windowScrollerRef, onScroll: handleScroll }, ({ height, registerChild, onChildScroll, isScrolling, scrollTop }) => {
|
|
243
343
|
return (React.createElement("div", { ref: commentRef, "data-tracking-id": "vr-case-comments" },
|
|
@@ -313,6 +413,22 @@ export default function CaseDiscussion(props) {
|
|
|
313
413
|
}
|
|
314
414
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
315
415
|
}, [isPostingRemoteRequestCommentCompleted]);
|
|
416
|
+
const onToggleClick = (e) => {
|
|
417
|
+
e.stopPropagation();
|
|
418
|
+
setIsOpen(!isOpen);
|
|
419
|
+
};
|
|
420
|
+
const onFilterSelect = (_event, prevOption) => {
|
|
421
|
+
const isFilterSelected = !prevOption.isSelected;
|
|
422
|
+
const prevSelectedFilters = list.filter((option) => option.isSelected && option.value !== prevOption.value);
|
|
423
|
+
const prevSelectedFiltersValue = prevSelectedFilters.map((option) => option.value);
|
|
424
|
+
const newSelectedFilters = isFilterSelected
|
|
425
|
+
? [...prevSelectedFiltersValue, prevOption.value]
|
|
426
|
+
: prevSelectedFiltersValue;
|
|
427
|
+
setFilter(dispatchDiscussion, newSelectedFilters, discussionFiltersListState);
|
|
428
|
+
shouldRenderRVComponent && setCanUpdateScrollTop(false);
|
|
429
|
+
setSelectedFilterItems(prevSelectedFiltersValue);
|
|
430
|
+
};
|
|
431
|
+
const filterToggle = (toggleRef) => (React.createElement(MenuToggle, { ref: toggleRef, onClick: onToggleClick, isExpanded: isOpen, isFullWidth: true }, getDropdownBtnPlaceholder(t('Select a filter'), list.filter((i) => i.isSelected).map((i) => i.filterLabel), ' ', list.length, t('All comments'))));
|
|
316
432
|
const onSortToggleClick = (e) => {
|
|
317
433
|
e.stopPropagation();
|
|
318
434
|
setIsSortOpen(!isSortOpen);
|
|
@@ -327,24 +443,83 @@ export default function CaseDiscussion(props) {
|
|
|
327
443
|
};
|
|
328
444
|
const onSortToggle = (toggleRef) => (React.createElement(MenuToggle, { ref: toggleRef, onClick: onSortToggleClick, isExpanded: isSortOpen, isFullWidth: true }, sort.name));
|
|
329
445
|
return (React.createElement(React.Fragment, null,
|
|
446
|
+
(() => {
|
|
447
|
+
const showInitialLoading = props.commentsLoading && allDiscussions.length === 0;
|
|
448
|
+
const showNoComments = !props.commentsLoading && !props.commentsError && allDiscussions.length === 0;
|
|
449
|
+
const showDiscussions = allDiscussions.length !== 0;
|
|
450
|
+
const showSkeleton = debouncedShowSkeleton;
|
|
451
|
+
const showNoMore = !props.hasNextPage &&
|
|
452
|
+
!props.isLoadingMore &&
|
|
453
|
+
!props.commentsLoading &&
|
|
454
|
+
!props.loadMoreError &&
|
|
455
|
+
comuteSearchedComments.length > 0;
|
|
456
|
+
console.log('[CaseDiscussion] RENDER DECISION', {
|
|
457
|
+
showInitialLoading,
|
|
458
|
+
showNoComments,
|
|
459
|
+
showDiscussions,
|
|
460
|
+
isRefetching,
|
|
461
|
+
showSkeleton,
|
|
462
|
+
showNoMore,
|
|
463
|
+
isSearchResultEmpty,
|
|
464
|
+
shouldRenderRVComponent,
|
|
465
|
+
});
|
|
466
|
+
return null;
|
|
467
|
+
})(),
|
|
330
468
|
React.createElement(ErrorBoundary, { errorMsgInfo: { message: t('There was an error loading post comment section') } },
|
|
331
469
|
React.createElement(AttachmentContextProvider, null,
|
|
332
470
|
React.createElement(PostComment, { caseNumber: caseNumber, allDiscussions: allDiscussions, onCommentAdded: onCommentAdded, onAttachmentAdded: onAttachmentAdded, onAttachmentRemove: onAttachmentRemove, replyingToAuthor: replyingToAuthor, replyingToTimestamp: replyingToTimestamp, onClearReply: onClearReply }))),
|
|
333
471
|
props.commentsLoading && allDiscussions.length === 0 && React.createElement(LoadingIndicator, { size: "sm" }),
|
|
334
472
|
props.commentsError && allDiscussions.length === 0 && (React.createElement(AlertMessage, { variant: AlertType.DANGER, show: true, title: t('Failed to load comments. Try refreshing.') })),
|
|
335
|
-
!props.commentsLoading && !props.commentsError && allDiscussions.length === 0 && (React.createElement(
|
|
473
|
+
!props.commentsLoading && !props.commentsError && allDiscussions.length === 0 && (React.createElement("p", null,
|
|
474
|
+
React.createElement(Trans, null, "No comments"))),
|
|
336
475
|
allDiscussions.length !== 0 && (React.createElement(ErrorBoundary, { errorMsgInfo: { message: t('There was an error loading comments. Try refreshing.') } },
|
|
337
476
|
React.createElement("div", { className: `pfe-l-grid pfe-m-all-6-col pfe-m-gutters push-bottom ${isExportingPDF ? 'hide-in-pdf' : ''}` },
|
|
338
|
-
React.createElement("div", { className: "pfe-m-12-col" }, allDiscussions.length > 20 && (React.createElement(CommentSearch, { caseDiscussions:
|
|
477
|
+
React.createElement("div", { className: "pfe-m-12-col" }, allDiscussions.length > 20 && (React.createElement(CommentSearch, { caseDiscussions: computeFilteredComments || [], onCommentSearch: onCaseCommentSearch, isCommentSearchCleared: isCommentSearchCleared }))),
|
|
478
|
+
React.createElement("span", { className: "push-top-narrow" },
|
|
479
|
+
React.createElement("label", { htmlFor: "case-list-comments-dropdown", className: "pf-v6-u-mb-sm" }, t('Filter by')),
|
|
480
|
+
React.createElement(Select, { "data-tracking-id": "discussions-filter-comments-dropdown", id: "case-list-comments-dropdown", role: "menu", isOpen: isOpen, selected: selectedFilterItems, onSelect: onFilterSelect, onOpenChange: (isOpen) => setIsOpen(isOpen), toggle: filterToggle, popperProps: { direction: 'down', enableFlip: false }, zIndex: 9 },
|
|
481
|
+
React.createElement(SelectList, null, list.map((option) => (React.createElement(SelectOption, { key: option.value, hasCheckbox: true, value: option, isSelected: option.isSelected, isDisabled: option.isDisabled }, option.filterLabel)))))),
|
|
339
482
|
React.createElement("span", { className: "push-top-narrow" },
|
|
340
483
|
React.createElement("label", { htmlFor: "se-discussion-sort", className: "pf-v6-u-mb-sm" }, t('Sort by')),
|
|
341
484
|
React.createElement(Select, { id: "se-discussion-sort", "data-tracking-id": "discussions-sort-dropdown", isOpen: isSortOpen, selected: sortSelected, onSelect: onSortSelect, onOpenChange: (isSortOpen) => setIsSortOpen(isSortOpen), toggle: onSortToggle, shouldFocusToggleOnSelect: true, popperProps: { direction: 'down', enableFlip: false }, zIndex: 9 },
|
|
342
|
-
React.createElement(SelectList, null, discussionSortOrderConst.map((option) => (React.createElement(SelectOption, { value: option.name, isSelected: isEqual(sort.name, option.name) }, option.name))))))),
|
|
343
|
-
React.createElement("div", { className: "support-comment-section" },
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
485
|
+
React.createElement(SelectList, null, discussionSortOrderConst.map((option) => (React.createElement(SelectOption, { key: option.name, value: option.name, isSelected: isEqual(sort.name, option.name) }, option.name))))))),
|
|
486
|
+
React.createElement("div", { className: "support-comment-section" },
|
|
487
|
+
isRefetching && React.createElement(LoadingIndicator, { size: "sm" }),
|
|
488
|
+
!isRefetching &&
|
|
489
|
+
!shouldRenderRVComponent &&
|
|
490
|
+
!isSearchResultEmpty &&
|
|
491
|
+
comuteSearchedComments.map((discussion, index) => {
|
|
492
|
+
return renderCaseCommentElement(discussion);
|
|
493
|
+
})),
|
|
494
|
+
isSearchResultEmpty && (React.createElement("p", { className: "infinite-scroll-end-status" },
|
|
495
|
+
React.createElement(Trans, null, "No results found"))),
|
|
496
|
+
!isRefetching && shouldRenderRVComponent && !isSearchResultEmpty && renderVRCommentElement(),
|
|
497
|
+
React.createElement("div", { ref: sentinelRef, "aria-hidden": "true", style: { height: 10, marginBottom: -10 } }),
|
|
498
|
+
debouncedShowSkeleton && (React.createElement("div", { className: "load-more-skeleton", role: "status", "aria-label": t('Loading more comments') },
|
|
499
|
+
React.createElement("div", { className: "load-more-skeleton__header" },
|
|
500
|
+
React.createElement("div", { className: "load-more-skeleton__avatar" }),
|
|
501
|
+
React.createElement("div", { className: "load-more-skeleton__name" }),
|
|
502
|
+
React.createElement("div", { className: "load-more-skeleton__timestamp" })),
|
|
503
|
+
React.createElement("div", { className: "load-more-skeleton__body" },
|
|
504
|
+
React.createElement("div", { className: "load-more-skeleton__line", style: { width: '100%' } }),
|
|
505
|
+
React.createElement("div", { className: "load-more-skeleton__line", style: { width: '85%' } }),
|
|
506
|
+
React.createElement("div", { className: "load-more-skeleton__line", style: { width: '60%' } })))),
|
|
507
|
+
props.loadMoreError && props.loadMoreRetryCount <= 1 && (React.createElement("div", { className: "infinite-scroll-footer-message" },
|
|
508
|
+
React.createElement(Button, { variant: "secondary", onClick: props.retryLoadMore, icon: React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: "14", height: "14", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
509
|
+
React.createElement("polyline", { points: "23 4 23 10 17 10" }),
|
|
510
|
+
React.createElement("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })) },
|
|
511
|
+
React.createElement(Trans, null, "try loading again")))),
|
|
512
|
+
props.loadMoreError && props.loadMoreRetryCount > 1 && (React.createElement("p", { className: "infinite-scroll-end-status" },
|
|
513
|
+
React.createElement(Trans, null, "We're having trouble loading your comments. Please refresh or"),
|
|
514
|
+
' ',
|
|
515
|
+
React.createElement(Button, { variant: "link", isInline: true, onClick: props.retryLoadMore },
|
|
516
|
+
React.createElement(Trans, null, "try again")),
|
|
517
|
+
".")),
|
|
518
|
+
!props.hasNextPage &&
|
|
519
|
+
!props.isLoadingMore &&
|
|
520
|
+
!props.commentsLoading &&
|
|
521
|
+
!props.loadMoreError &&
|
|
522
|
+
comuteSearchedComments.length > 0 &&
|
|
523
|
+
props.retryLoadMore !== undefined && (React.createElement("p", { className: "infinite-scroll-end-status" },
|
|
524
|
+
React.createElement(Trans, null, "No more comments to load")))))));
|
|
350
525
|
}
|
package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment/useCommentDrafts.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ interface UseCommentDraftsParams {
|
|
|
10
10
|
applySignature: (text: string) => void;
|
|
11
11
|
}
|
|
12
12
|
export declare function useCommentDrafts({ caseNumber, loggedInUserSSO, isInternal, commentSignature, canPostPrivateComments, setCommentText, setIsPublic, applySignature, }: UseCommentDraftsParams): {
|
|
13
|
-
saveDraft: (
|
|
13
|
+
saveDraft: import("lodash").DebouncedFunc<(value: string, publicFlag: boolean) => Promise<void>>;
|
|
14
14
|
clearDraft: () => Promise<void>;
|
|
15
15
|
};
|
|
16
16
|
export {};
|
package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment/useCommentDrafts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCommentDrafts.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/PostComment/useCommentDrafts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAsB,MAAM,OAAO,CAAC;AAMrE,UAAU,sBAAsB;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,OAAO,CAAC;IAChC,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACjD,WAAW,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,UAAU,EACV,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,WAAW,EACX,cAAc,GACjB,EAAE,sBAAsB
|
|
1
|
+
{"version":3,"file":"useCommentDrafts.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/PostComment/useCommentDrafts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAsB,MAAM,OAAO,CAAC;AAMrE,UAAU,sBAAsB;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,OAAO,CAAC;IAChC,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACjD,WAAW,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,UAAU,EACV,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,WAAW,EACX,cAAc,GACjB,EAAE,sBAAsB;sDAGmB,MAAM,cAAc,OAAO;;EA2DtE"}
|
|
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import
|
|
10
|
+
import debounce from 'lodash/debounce';
|
|
11
11
|
import { useEffect, useMemo } from 'react';
|
|
12
12
|
import { CaseDetailsCacheNamespaces } from '../../../../../enums/cache';
|
|
13
13
|
import { CaseDetailsCacheUtils } from '../../../../../utils/caseDetailsCacheUtils';
|
|
@@ -28,6 +28,8 @@ export function useCommentDrafts({ caseNumber, loggedInUserSSO, isInternal, comm
|
|
|
28
28
|
});
|
|
29
29
|
}, 500), [caseNumber, commentSignature, loggedInUserSSO]);
|
|
30
30
|
const clearDraft = () => __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
// RTE onChange keeps scheduling saves; cancel so submit/reset cannot resurrect the draft.
|
|
32
|
+
saveDraft.cancel();
|
|
31
33
|
const { value: prevDraftComments = {} } = (yield CaseDetailsCacheUtils.get(CaseDetailsCacheNamespaces.COMMENTS_DRAFT.name, loggedInUserSSO)) || {};
|
|
32
34
|
delete prevDraftComments[caseNumber];
|
|
33
35
|
CaseDetailsCacheUtils.set(CaseDetailsCacheNamespaces.COMMENTS_DRAFT.name, loggedInUserSSO, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostComment.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/PostComment.tsx"],"names":[],"mappings":"AAUA,OAAO,KAA+D,MAAM,OAAO,CAAC;AAOpF,OAAO,EAAE,WAAW,EAAE,MAAM,+CAA+C,CAAC;AAmB5E,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C;;;;;OAKG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,WAAW,EAAE,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"PostComment.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/PostComment.tsx"],"names":[],"mappings":"AAUA,OAAO,KAA+D,MAAM,OAAO,CAAC;AAOpF,OAAO,EAAE,WAAW,EAAE,MAAM,+CAA+C,CAAC;AAmB5E,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C;;;;;OAKG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,WAAW,EAAE,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,qBAibxC"}
|
|
@@ -36,7 +36,6 @@ export function PostComment(props) {
|
|
|
36
36
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
37
37
|
const { t } = useTranslation();
|
|
38
38
|
let { caseNumber } = props;
|
|
39
|
-
const [isTextAreaFocused, setIsTextAreaFocused] = useState(false);
|
|
40
39
|
const canPostPrivateComments = ability.can(resourceActions.PATCH, resources.CASE_COMMENTS, CaseDiscussionFields.POST_PRIVATE_COMMENT);
|
|
41
40
|
const { createComment, loading: isPostingComment } = useCreateCaseComment();
|
|
42
41
|
const dispatchToAttachmentReducer = useContext(AttachmentDispatchContext);
|
|
@@ -133,11 +132,10 @@ export function PostComment(props) {
|
|
|
133
132
|
applySignature,
|
|
134
133
|
});
|
|
135
134
|
const resetCommentSection = () => __awaiter(this, void 0, void 0, function* () {
|
|
136
|
-
setCommentText('');
|
|
137
|
-
setIsTextAreaFocused(false);
|
|
138
|
-
resetIsPublic();
|
|
139
135
|
yield clearDraft();
|
|
140
|
-
|
|
136
|
+
const resetText = commentSignature ? `<p><br></p><p>${commentSignature}</p>` : '';
|
|
137
|
+
setCommentText(resetText);
|
|
138
|
+
resetIsPublic();
|
|
141
139
|
});
|
|
142
140
|
const resetAttachmentSection = () => {
|
|
143
141
|
resetAttachment(dispatchToAttachmentReducer);
|
|
@@ -259,18 +257,18 @@ export function PostComment(props) {
|
|
|
259
257
|
const onFileUploadClick = () => {
|
|
260
258
|
ToastNotification.addInfoMessage(t('Uploading Attachment(s)'));
|
|
261
259
|
};
|
|
262
|
-
const onTextAreaBlur = () => {
|
|
263
|
-
if (!commentText)
|
|
264
|
-
setIsTextAreaFocused(false);
|
|
265
|
-
};
|
|
266
260
|
const onCommentExceedCharsLimit = (lengthExceededNew) => {
|
|
267
261
|
if (isCommentTooLong !== lengthExceededNew)
|
|
268
262
|
setIsCommentTooLong(lengthExceededNew);
|
|
269
263
|
};
|
|
270
264
|
// Submit stays independent of attachment upload so comments can be posted mid-upload (PCM-15915).
|
|
271
|
-
const isSubmitDisabled = isCommentEmpty ||
|
|
265
|
+
const isSubmitDisabled = isCommentEmpty ||
|
|
266
|
+
isPostCommentDisabled ||
|
|
267
|
+
isCommentTooLong ||
|
|
268
|
+
(isConfirmedStateSideSupport && !cssCommentAgreed) ||
|
|
269
|
+
isCaseReadOnly;
|
|
272
270
|
return (React.createElement("div", { className: `pf-v6-u-mb-md ${isExportingPDF ? 'hide-in-pdf' : ''}`, ref: componentRef },
|
|
273
|
-
props.replyingToAuthor && (React.createElement("
|
|
271
|
+
props.replyingToAuthor && (React.createElement("output", { className: "post-comment-reply-banner pf-v6-u-mb-sm", "aria-label": `Replying to ${props.replyingToAuthor}`, "data-tracking-id": "reply-banner" },
|
|
274
272
|
React.createElement("span", null,
|
|
275
273
|
React.createElement(Trans, null, "Replying to"),
|
|
276
274
|
" ",
|
|
@@ -281,11 +279,7 @@ export function PostComment(props) {
|
|
|
281
279
|
")"))),
|
|
282
280
|
React.createElement(Button, { variant: ButtonVariant.plain, "aria-label": t('Cancel reply'), onClick: props.onClearReply, "data-tracking-id": "cancel-reply-button" },
|
|
283
281
|
React.createElement(TimesIcon, null)))),
|
|
284
|
-
React.createElement(RichTextEditor, { key: `${caseNumber}-${isCaseReadOnly}`, className: `${!isPublic && canPostPrivateComments ? 'is-private' : ''}`, disabled: isProcessing || isCaseReadOnly, hideToolbar: isCaseReadOnly, "aria-label": "comment text area",
|
|
285
|
-
if (isCaseReadOnly)
|
|
286
|
-
return;
|
|
287
|
-
setIsTextAreaFocused(true);
|
|
288
|
-
}, onBlur: onTextAreaBlur, onChange: onCommentTextChange, value: commentText, height: isTextAreaFocused ? 313 : 120, onCharLimitExceeded: onCommentExceedCharsLimit, placeholder: !isPublic && canPostPrivateComments
|
|
282
|
+
React.createElement(RichTextEditor, { key: `${caseNumber}-${isCaseReadOnly}`, className: `${!isPublic && canPostPrivateComments ? 'is-private' : ''}`, disabled: isProcessing || isCaseReadOnly, hideToolbar: isCaseReadOnly, "aria-label": "comment text area", onChange: onCommentTextChange, value: commentText, height: 313, onCharLimitExceeded: onCommentExceedCharsLimit, placeholder: !isPublic && canPostPrivateComments
|
|
289
283
|
? 'Add a comment or attach a file privately'
|
|
290
284
|
: 'Add a comment to this case', charLimit: 32000, onImageUpload: onImageUpload, bindEditor: (editor) => {
|
|
291
285
|
quillEditorRef.current = editor;
|
|
@@ -303,7 +297,7 @@ export function PostComment(props) {
|
|
|
303
297
|
React.createElement(PlusIcon, null),
|
|
304
298
|
" ",
|
|
305
299
|
React.createElement(Trans, null, "Attach files")) }),
|
|
306
|
-
React.createElement(Button, { className: "secondary-button-pill", "data-tracking-id": "postcomment-cancel-button", type: "button", variant: ButtonVariant.link, onClick: onCancelClick, isDisabled:
|
|
300
|
+
React.createElement(Button, { className: "secondary-button-pill", "data-tracking-id": "postcomment-cancel-button", type: "button", variant: ButtonVariant.link, onClick: onCancelClick, isDisabled: isCommentSignatureMatch() || isProcessing || isCaseReadOnly },
|
|
307
301
|
React.createElement(Trans, null, "Cancel")),
|
|
308
302
|
canPostPrivateComments && (React.createElement("span", { className: "post-comment-checkbox" },
|
|
309
303
|
React.createElement(Checkbox, { label: t('Private'), isChecked: !isPublic && canPostPrivateComments, isDisabled: isCaseReadOnly, onChange: handleCheckboxToggle, "aria-label": t('Private'), id: "private-comment", name: "private-comment", "data-tracking-id": "private-comment-checkbox" }))))),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VerifyCaseStatusModal.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/VerifyCaseStatusModal/VerifyCaseStatusModal.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAa3E,UAAU,MAAM;IACZ,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"VerifyCaseStatusModal.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/VerifyCaseStatusModal/VerifyCaseStatusModal.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAa3E,UAAU,MAAM;IACZ,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,qBA8IlD"}
|
|
@@ -71,7 +71,7 @@ export function VerifyCaseStatusModal(props) {
|
|
|
71
71
|
}
|
|
72
72
|
catch (e) {
|
|
73
73
|
setStatusUpdating(caseOverviewDispatch, false);
|
|
74
|
-
caseUpdateError.
|
|
74
|
+
caseUpdateError.showFieldError(e, t('Status'), () => updateStatusState(statusVal, onStatusUpdate));
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
else {
|