@rh-support/troubleshoot 2.6.334 → 2.6.336

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.
Files changed (76) hide show
  1. package/lib/esm/components/AccountInfo/OwnerSelector.d.ts.map +1 -1
  2. package/lib/esm/components/AccountInfo/OwnerSelector.js +5 -2
  3. package/lib/esm/components/CaseEditView/ActiveCustomerEscalation/ActiveCustomerEscalation.d.ts.map +1 -1
  4. package/lib/esm/components/CaseEditView/ActiveCustomerEscalation/ActiveCustomerEscalation.js +1 -1
  5. package/lib/esm/components/CaseEditView/ActiveCustomerEscalation/RequestEscalationModal.js +1 -1
  6. package/lib/esm/components/CaseEditView/ArchivedCaseBanner.d.ts +7 -0
  7. package/lib/esm/components/CaseEditView/ArchivedCaseBanner.d.ts.map +1 -0
  8. package/lib/esm/components/CaseEditView/ArchivedCaseBanner.js +17 -0
  9. package/lib/esm/components/CaseEditView/Case.d.ts.map +1 -1
  10. package/lib/esm/components/CaseEditView/Case.js +23 -19
  11. package/lib/esm/components/CaseEditView/CaseOverview/CaseStatus.js +2 -2
  12. package/lib/esm/components/CaseEditView/CaseOverview/index.d.ts.map +1 -1
  13. package/lib/esm/components/CaseEditView/CaseOverview/index.js +25 -10
  14. package/lib/esm/components/CaseEditView/CaseSolutions/CaseSolutions.d.ts.map +1 -1
  15. package/lib/esm/components/CaseEditView/CaseSolutions/CaseSolutions.js +1 -1
  16. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseContactPhoneNumber.js +1 -1
  17. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseHostname.js +1 -1
  18. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseInternalStatus.d.ts.map +1 -1
  19. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseInternalStatus.js +3 -2
  20. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseOpenshiftClusterId/CaseOpenshiftClusterId.d.ts.map +1 -1
  21. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseOpenshiftClusterId/CaseOpenshiftClusterId.js +109 -72
  22. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseReferenceNumber.js +1 -1
  23. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/ProductVersion.d.ts.map +1 -1
  24. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/ProductVersion.js +5 -7
  25. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/Summary.js +1 -1
  26. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseAttachment.d.ts.map +1 -1
  27. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseAttachment.js +2 -2
  28. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.d.ts +1 -0
  29. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.d.ts.map +1 -1
  30. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.js +3 -1
  31. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.d.ts.map +1 -1
  32. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.js +11 -4
  33. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment/useCommentDrafts.d.ts +4 -2
  34. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment/useCommentDrafts.d.ts.map +1 -1
  35. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment/useCommentDrafts.js +11 -4
  36. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.d.ts +3 -0
  37. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.d.ts.map +1 -1
  38. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.js +47 -15
  39. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/VerifyCaseStatusModal/VerifyCaseStatusModal.d.ts.map +1 -1
  40. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/VerifyCaseStatusModal/VerifyCaseStatusModal.js +1 -1
  41. package/lib/esm/components/CaseInformation/CaseGroup.d.ts.map +1 -1
  42. package/lib/esm/components/CaseInformation/CaseGroup.js +8 -5
  43. package/lib/esm/components/CaseInformation/CaseLabels.d.ts.map +1 -1
  44. package/lib/esm/components/CaseInformation/CaseLabels.js +5 -1
  45. package/lib/esm/components/CaseInformation/Fts.d.ts.map +1 -1
  46. package/lib/esm/components/CaseInformation/Fts.js +2 -1
  47. package/lib/esm/components/CaseManagement/NoClusterIDReasonSelector.d.ts +2 -1
  48. package/lib/esm/components/CaseManagement/NoClusterIDReasonSelector.d.ts.map +1 -1
  49. package/lib/esm/components/CaseManagement/NoClusterIDReasonSelector.js +19 -3
  50. package/lib/esm/components/CaseManagement/OpenshiftDropdownV4.d.ts.map +1 -1
  51. package/lib/esm/components/CaseManagement/OpenshiftDropdownV4.js +13 -14
  52. package/lib/esm/components/UpdateSeverityModal/UpdateSeverityModal.d.ts.map +1 -1
  53. package/lib/esm/components/UpdateSeverityModal/UpdateSeverityModal.js +7 -3
  54. package/lib/esm/constants/caseDetailsConstants.d.ts +5 -0
  55. package/lib/esm/constants/caseDetailsConstants.d.ts.map +1 -1
  56. package/lib/esm/constants/caseDetailsConstants.js +5 -0
  57. package/lib/esm/hooks/useCaseComments/index.d.ts +1 -1
  58. package/lib/esm/hooks/useCaseComments/index.d.ts.map +1 -1
  59. package/lib/esm/hooks/useCaseComments/index.js +1 -1
  60. package/lib/esm/hooks/useCaseComments/useCreateCaseComment.d.ts.map +1 -1
  61. package/lib/esm/hooks/useCaseComments/useCreateCaseComment.js +1 -2
  62. package/lib/esm/hooks/useCaseUpdateErrorMessage.d.ts +1 -1
  63. package/lib/esm/hooks/useCaseUpdateErrorMessage.d.ts.map +1 -1
  64. package/lib/esm/hooks/useCaseUpdateErrorMessage.js +21 -11
  65. package/lib/esm/reducers/CaseConstNTypes.d.ts +1 -0
  66. package/lib/esm/reducers/CaseConstNTypes.d.ts.map +1 -1
  67. package/lib/esm/reducers/CaseDiscussionTabReducer.d.ts +5 -0
  68. package/lib/esm/reducers/CaseDiscussionTabReducer.d.ts.map +1 -1
  69. package/lib/esm/reducers/CaseDiscussionTabReducer.js +15 -0
  70. package/lib/esm/reducers/CaseReducer.d.ts.map +1 -1
  71. package/lib/esm/reducers/CaseReducer.js +11 -12
  72. package/lib/esm/scss/_main.scss +9 -0
  73. package/package.json +6 -6
  74. package/lib/esm/hooks/useCaseComments/caseCommentMutation.d.ts +0 -4
  75. package/lib/esm/hooks/useCaseComments/caseCommentMutation.d.ts.map +0 -1
  76. package/lib/esm/hooks/useCaseComments/caseCommentMutation.js +0 -12
@@ -9,12 +9,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { useApolloClient } from '@apollo/client/react';
11
11
  import { LoadingIndicator, ToastNotification, usePrevious } from '@rh-support/components';
12
- import { GlobalMetadataStateContext } from '@rh-support/react-context';
13
- import { extractNoClusterIdFieldsFromDescription, getVersion } from '@rh-support/utils';
14
- import debounce from 'lodash/debounce';
12
+ import { GlobalMetadataStateContext, useCanEditCase } from '@rh-support/react-context';
13
+ import { extractNoClusterIdFieldsFromAdditionalCaseInformation, getVersion, hasNoClusterIdFieldsInAdditionalCaseInformation, } from '@rh-support/utils';
15
14
  import isEmpty from 'lodash/isEmpty';
16
15
  import isEqual from 'lodash/isEqual';
17
- import React, { useContext, useEffect, useState } from 'react';
16
+ import React, { useContext, useEffect, useMemo, useState } from 'react';
18
17
  import { useTranslation } from 'react-i18next';
19
18
  import { useCaseDispatch, useCaseSelector } from '../../../../../context/CaseContext';
20
19
  import { useCaseUpdateErrorMessage } from '../../../../../hooks/useCaseUpdateErrorMessage';
@@ -27,7 +26,7 @@ import { PDFContext } from '../../../PDFContainer';
27
26
  export function CaseOpenshiftClusterId() {
28
27
  const caseUpdateError = useCaseUpdateErrorMessage();
29
28
  const apolloClient = useApolloClient();
30
- const { openshiftClusterID, product, version, caseNumber, caseId, openshiftClusterVersion, noClusterIdReason, noClusterIdReasonExplanation, selectedAccountDetails, description, isFetchingCaseDetails, } = useCaseSelector((state) => ({
29
+ const { openshiftClusterID, product, version, caseNumber, caseId, openshiftClusterVersion, noClusterIdReason, noClusterIdReasonExplanation, selectedAccountDetails, description, additionalCaseInformation, isFetchingCaseDetails, } = useCaseSelector((state) => ({
31
30
  openshiftClusterID: state.caseDetails.openshiftClusterID,
32
31
  product: state.caseDetails.product,
33
32
  version: state.caseDetails.version,
@@ -38,9 +37,12 @@ export function CaseOpenshiftClusterId() {
38
37
  noClusterIdReasonExplanation: state.caseDetails.noClusterIdReasonExplanation,
39
38
  selectedAccountDetails: state.selectedAccountDetails,
40
39
  description: state.caseDetails.description,
40
+ additionalCaseInformation: state.caseDetails.additionalCaseInformation,
41
41
  isFetchingCaseDetails: state.isFetchingCaseDetails,
42
42
  }), isEqual);
43
43
  const caseDispatch = useCaseDispatch();
44
+ const canEditCase = useCanEditCase();
45
+ const isCaseReadOnly = canEditCase.isCaseNotEditable;
44
46
  const { globalMetadataState: { loggedInUserRights }, } = useContext(GlobalMetadataStateContext);
45
47
  const majorVersion = (version && getVersion(version).major) || '';
46
48
  const isOpenShiftV3 = product === 'OpenShift Container Platform' && majorVersion === '3';
@@ -58,6 +60,47 @@ export function CaseOpenshiftClusterId() {
58
60
  const previousProduct = usePrevious(product);
59
61
  const { t } = useTranslation();
60
62
  const { isExportingPDF } = useContext(PDFContext);
63
+ const additionalCaseNoClusterFields = useMemo(() => extractNoClusterIdFieldsFromAdditionalCaseInformation(additionalCaseInformation), [additionalCaseInformation]);
64
+ const resolvedNoClusterFields = useMemo(() => ({
65
+ reason: noClusterIdReason || additionalCaseNoClusterFields.noClusterIdReason,
66
+ explanation: noClusterIdReasonExplanation || additionalCaseNoClusterFields.noClusterIdReasonExplanation,
67
+ }), [noClusterIdReason, noClusterIdReasonExplanation, additionalCaseNoClusterFields]);
68
+ const hasNoClusterIdSelection = useMemo(() => {
69
+ const { reason, explanation } = resolvedNoClusterFields;
70
+ if (reason) {
71
+ return true;
72
+ }
73
+ return explanation !== '' && explanation !== 'v3-cluster';
74
+ }, [resolvedNoClusterFields]);
75
+ const clusterDropdownId = useMemo(() => {
76
+ if (!isEmpty(localOpenshiftClusterIDState)) {
77
+ return localOpenshiftClusterIDState;
78
+ }
79
+ if (!isEmpty(selectedReason)) {
80
+ return selectedReason;
81
+ }
82
+ if (!isEmpty(openshiftClusterID)) {
83
+ return openshiftClusterID;
84
+ }
85
+ const { reason, explanation } = resolvedNoClusterFields;
86
+ if (reason === 'Other' && explanation === 'v3-cluster') {
87
+ return 'v3-cluster';
88
+ }
89
+ if (hasNoClusterIdSelection) {
90
+ return 'dont-have-id';
91
+ }
92
+ return '';
93
+ }, [
94
+ localOpenshiftClusterIDState,
95
+ selectedReason,
96
+ openshiftClusterID,
97
+ resolvedNoClusterFields,
98
+ hasNoClusterIdSelection,
99
+ ]);
100
+ const resolvedNoClusterIdReason = resolvedNoClusterFields.reason;
101
+ const resolvedNoClusterIdReasonExplanation = resolvedNoClusterFields.explanation;
102
+ const shouldShowNoClusterReasonSelector = resolvedNoClusterIdReasonExplanation !== 'v3-cluster' &&
103
+ (clusterDropdownId === 'dont-have-id' || dontKnowSelected);
61
104
  const clusterStateReset = () => {
62
105
  setSelectedReason('');
63
106
  setDontKnowSelected(false);
@@ -70,8 +113,15 @@ export function CaseOpenshiftClusterId() {
70
113
  openshiftClusterVersion: '',
71
114
  noClusterIdReasonExplanation: '',
72
115
  noClusterIdReason: '',
116
+ additionalCaseInformation: null,
73
117
  });
74
118
  };
119
+ const clearedClusterProductFields = {
120
+ openshiftClusterID: '',
121
+ openshiftClusterVersion: '',
122
+ noClusterIdReasonExplanation: '',
123
+ noClusterIdReason: '',
124
+ };
75
125
  const onDisplayNameChange = (val) => {
76
126
  setDisplayName(val);
77
127
  };
@@ -79,43 +129,24 @@ export function CaseOpenshiftClusterId() {
79
129
  const isClusterVisible = () => __awaiter(this, void 0, void 0, function* () {
80
130
  const hasCluster = yield isClusterIdEnabledForProduct(product);
81
131
  setShowClusterId(hasCluster);
82
- // when product changes from a product with cluster to a product without cluster, reset the clusterId
83
132
  const hasPreviousProduct = previousProduct !== undefined;
84
133
  const hasCurrentProduct = product !== undefined;
85
134
  //need to check if they are undefined as the data loads in and can cause eval against product version === undefined causing edge case to reset our cluster with logic as if product changed.
86
135
  const IsProductHasChanged = hasPreviousProduct && hasCurrentProduct && previousProduct !== product;
87
- const previousHadCluster = !isEmpty(previousProduct) && isClusterIdEnabledForProduct(previousProduct);
88
- const isResetClusterID = previousHadCluster && (!hasCluster || isOpenShiftV3) && IsProductHasChanged;
89
- if (isResetClusterID) {
90
- const caseDetails = {
91
- openshiftClusterID: '',
92
- openshiftClusterVersion: '',
93
- noClusterIdReasonExplanation: '',
94
- noClusterIdReason: '',
95
- };
96
- setCaseDetails(caseDispatch, caseDetails);
136
+ const currentIsClusterEnabled = hasCluster && !isOpenShiftV3;
137
+ const previousIsClusterEnabled = !isEmpty(previousProduct) && isClusterIdEnabledForProduct(previousProduct);
138
+ const shouldResetOnClusterProductBoundaryChange = IsProductHasChanged && previousIsClusterEnabled !== currentIsClusterEnabled;
139
+ if (shouldResetOnClusterProductBoundaryChange) {
140
+ setCaseDetails(caseDispatch, Object.assign(Object.assign({}, clearedClusterProductFields), { additionalCaseInformation: null }));
97
141
  setLocalOpenshiftClusterIDState('');
98
142
  clusterStateReset();
99
- if (!isEmpty(openshiftClusterID) || !isEmpty(openshiftClusterVersion)) {
100
- yield clusterIdUpdate({ openshiftClusterID: '', openshiftClusterVersion: '' }, false);
101
- }
102
- return;
103
- }
104
- // Switching onto a cluster product: restore reason fields from description and auto-select
105
- if (hasCluster && IsProductHasChanged && isEmpty(openshiftClusterID)) {
106
- const fromDescription = extractNoClusterIdFieldsFromDescription(description || '');
107
- const reason = noClusterIdReason || fromDescription.noClusterIdReason;
108
- const explanation = noClusterIdReasonExplanation || fromDescription.noClusterIdReasonExplanation;
109
- if (!isEmpty(reason) || (!isEmpty(explanation) && explanation !== 'v3-cluster')) {
110
- if (reason !== noClusterIdReason || explanation !== noClusterIdReasonExplanation) {
111
- setCaseDetails(caseDispatch, {
112
- noClusterIdReason: reason,
113
- noClusterIdReasonExplanation: explanation,
114
- });
115
- }
116
- setDontKnowSelected(true);
117
- setSelectedReason('dont-have-id');
118
- setLocalOpenshiftClusterIDState('');
143
+ const hasClusterRelatedData = !isEmpty(openshiftClusterID) ||
144
+ !isEmpty(openshiftClusterVersion) ||
145
+ !isEmpty(noClusterIdReason) ||
146
+ !isEmpty(noClusterIdReasonExplanation) ||
147
+ hasNoClusterIdFieldsInAdditionalCaseInformation(additionalCaseInformation);
148
+ if (hasClusterRelatedData) {
149
+ yield clusterIdUpdate(clearedClusterProductFields, false);
119
150
  }
120
151
  }
121
152
  });
@@ -123,25 +154,16 @@ export function CaseOpenshiftClusterId() {
123
154
  // eslint-disable-next-line react-hooks/exhaustive-deps
124
155
  }, [product]);
125
156
  /**
126
- * Hydrate cluster dropdown / reason UI from loaded case details
157
+ * Keep local invalid state in sync when a real cluster ID is present on the case.
127
158
  */
128
159
  useEffect(() => {
129
- const isClusterIdInvalid = getIsClusterIdInvalid(openshiftClusterID);
130
- setIsClusterIdInValid(isClusterIdInvalid);
160
+ setIsClusterIdInValid(getIsClusterIdInvalid(openshiftClusterID));
131
161
  if (!isEmpty(openshiftClusterID)) {
132
162
  setLocalOpenshiftClusterIDState(openshiftClusterID);
133
163
  setSelectedReason('');
134
164
  setDontKnowSelected(false);
135
- return;
136
165
  }
137
- if (!isEmpty(noClusterIdReason) ||
138
- (!isEmpty(noClusterIdReasonExplanation) && noClusterIdReasonExplanation !== 'v3-cluster')) {
139
- setDontKnowSelected(true);
140
- setSelectedReason('dont-have-id');
141
- setLocalOpenshiftClusterIDState('');
142
- }
143
- // eslint-disable-next-line react-hooks/exhaustive-deps
144
- }, [noClusterIdReason, noClusterIdReasonExplanation, openshiftClusterID, openshiftClusterVersion]);
166
+ }, [openshiftClusterID, openshiftClusterVersion]);
145
167
  const clusterIdUpdate = (caseDetails_1, ...args_1) => __awaiter(this, [caseDetails_1, ...args_1], void 0, function* (caseDetails, showToast = true) {
146
168
  if (showToast)
147
169
  setIsClusterIdUpdating(true);
@@ -153,9 +175,21 @@ export function CaseOpenshiftClusterId() {
153
175
  if (caseDetails.openshiftClusterVersion !== undefined) {
154
176
  caseUpdate.openshiftClusterVersion = caseDetails.openshiftClusterVersion;
155
177
  }
178
+ if (caseDetails.noClusterIdReason !== undefined) {
179
+ caseUpdate.noClusterIdReason = caseDetails.noClusterIdReason;
180
+ }
181
+ if (caseDetails.noClusterIdReasonExplanation !== undefined) {
182
+ caseUpdate.noClusterIdReasonExplanation = caseDetails.noClusterIdReasonExplanation;
183
+ }
156
184
  yield updateCaseDetailsGraphQL(caseDispatch, apolloClient, caseNumber, caseUpdate, false, {
157
185
  salesforceCaseId: caseId,
158
- existingCaseDetails: { product, version },
186
+ existingCaseDetails: {
187
+ product,
188
+ version,
189
+ noClusterIdReason,
190
+ noClusterIdReasonExplanation,
191
+ additionalCaseInformation,
192
+ },
159
193
  });
160
194
  if (showToast) {
161
195
  setIsClusterIdUpdating(false);
@@ -223,38 +257,37 @@ export function CaseOpenshiftClusterId() {
223
257
  yield clusterIdUpdate(caseDetails);
224
258
  }
225
259
  });
226
- const isDedicatedV3 = () => noClusterIdReason === 'Other' && noClusterIdReasonExplanation === 'v3-cluster';
260
+ const isDedicatedV3 = () => resolvedNoClusterIdReason === 'Other' && resolvedNoClusterIdReasonExplanation === 'v3-cluster';
227
261
  const v3Message = () => description.match(/Cluster Name: /g) ? (React.createElement(React.Fragment, null,
228
262
  "Your Cluster Name has been added to the description below. ",
229
263
  v3Tov4TransitionLink())) : (React.createElement(React.Fragment, null, v3Tov4TransitionLink()));
230
264
  const clusterIdDropdownFormInstructions = () => {
231
265
  const errorMessage = isClusterIdInvalid
232
266
  ? isInvalidErrorMessage()
233
- : isEmpty(openshiftClusterID) && isEmpty(selectedReason)
234
- ? discoverV4ClusterIdLinkError()
235
- : null;
267
+ : isEmpty(clusterDropdownId) || clusterDropdownId === 'dont-have-id' || clusterDropdownId === 'v3-cluster'
268
+ ? null
269
+ : discoverV4ClusterIdLinkError();
236
270
  const infoMessage = isDedicatedV3() ? v3Message() : discoverV4ClusterIdLink();
237
271
  return errorMessage ? errorMessage : infoMessage;
238
272
  };
239
273
  const onReasonExplanationInputBoxChanged = (explanationInput) => __awaiter(this, void 0, void 0, function* () {
240
274
  explanationInput = (explanationInput || '').trim();
241
- if (explanationInput) {
242
- const caseDetails = {
243
- noClusterIdReason: 'Other',
244
- openshiftClusterID: '',
245
- noClusterIdReasonExplanation: explanationInput,
246
- };
247
- try {
248
- setCaseDetails(caseDispatch, { noClusterIdReasonExplanation: explanationInput });
249
- yield clusterIdUpdate(caseDetails);
250
- }
251
- catch (e) {
252
- setCaseDetails(caseDispatch, { noClusterIdReasonExplanation });
253
- }
275
+ if (!explanationInput || explanationInput === (noClusterIdReasonExplanation || '').trim()) {
276
+ return;
277
+ }
278
+ const caseDetails = {
279
+ noClusterIdReason: 'Other',
280
+ openshiftClusterID: '',
281
+ noClusterIdReasonExplanation: explanationInput,
282
+ };
283
+ try {
284
+ setCaseDetails(caseDispatch, { noClusterIdReasonExplanation: explanationInput });
285
+ yield clusterIdUpdate(caseDetails);
286
+ }
287
+ catch (e) {
288
+ setCaseDetails(caseDispatch, { noClusterIdReasonExplanation });
254
289
  }
255
290
  });
256
- // eslint-disable-next-line react-hooks/exhaustive-deps
257
- const debounceFn = debounce(onReasonExplanationInputBoxChanged, 1000);
258
291
  const onReasonChange = (reason) => __awaiter(this, void 0, void 0, function* () {
259
292
  if (!reason || reason === noClusterIdReason)
260
293
  return;
@@ -274,7 +307,8 @@ export function CaseOpenshiftClusterId() {
274
307
  }
275
308
  setIsReasonUpdating(false);
276
309
  });
277
- const isInVaidNoClusterIdReason = isEmpty(noClusterIdReason) || (noClusterIdReason === 'Other' && isEmpty(noClusterIdReasonExplanation.trim()));
310
+ const isInVaidNoClusterIdReason = isEmpty(resolvedNoClusterIdReason) ||
311
+ (resolvedNoClusterIdReason === 'Other' && isEmpty((resolvedNoClusterIdReasonExplanation || '').trim()));
278
312
  if (!showClusterId || isFetchingCaseDetails) {
279
313
  return null;
280
314
  }
@@ -283,9 +317,12 @@ export function CaseOpenshiftClusterId() {
283
317
  }
284
318
  else {
285
319
  return (React.createElement(React.Fragment, null,
286
- React.createElement(OpenshiftDropdownV4, { openshiftDisplayName: displayName, setOpenshiftDisplayName: onDisplayNameChange, openshiftClusterIDState: localOpenshiftClusterIDState || selectedReason, onClusterIdStateUpdate: onClusterIdSave, isClusterIdInvalid: (isEmpty(localOpenshiftClusterIDState) && isEmpty(selectedReason)) || isClusterIdInvalid, clusterIdDropdownFormInstructions: !isExportingPDF ? clusterIdDropdownFormInstructions() : undefined, onClusterClear: resetClusterData, isV3: product !== 'OpenShift Container Platform', isDisabled: isClusterIdUpdating, noClusterIdReasonExplanation: noClusterIdReasonExplanation }),
287
- noClusterIdReasonExplanation !== 'v3-cluster' &&
288
- (dontKnowSelected || noClusterIdReasonExplanation || noClusterIdReason) && (React.createElement(NoClusterIDReasonSelector, { noClusterIdReasonExplanation: noClusterIdReasonExplanation, noClusterIdReason: noClusterIdReason, onReasonInputBoxChanged: debounceFn, onReasonChange: onReasonChange, isInValid: isInVaidNoClusterIdReason, isCustomer: isCustomer, isDisabled: isReasonUpdating })),
320
+ React.createElement(OpenshiftDropdownV4, { openshiftDisplayName: displayName, setOpenshiftDisplayName: onDisplayNameChange, openshiftClusterIDState: clusterDropdownId || '', onClusterIdStateUpdate: onClusterIdSave, isClusterIdInvalid: isEmpty(clusterDropdownId) ||
321
+ clusterDropdownId === 'dont-have-id' ||
322
+ clusterDropdownId === 'v3-cluster'
323
+ ? false
324
+ : isClusterIdInvalid, clusterIdDropdownFormInstructions: !isExportingPDF ? clusterIdDropdownFormInstructions() : undefined, onClusterClear: resetClusterData, isV3: product !== 'OpenShift Container Platform', isDisabled: isClusterIdUpdating || isCaseReadOnly, noClusterIdReasonExplanation: resolvedNoClusterIdReasonExplanation || '' }),
325
+ shouldShowNoClusterReasonSelector && (React.createElement(NoClusterIDReasonSelector, { noClusterIdReasonExplanation: resolvedNoClusterIdReasonExplanation || '', noClusterIdReason: resolvedNoClusterIdReason || '', onReasonInputBoxBlur: onReasonExplanationInputBoxChanged, onReasonChange: onReasonChange, isInValid: isInVaidNoClusterIdReason, isCustomer: isCustomer, isDisabled: isReasonUpdating || isCaseReadOnly })),
289
326
  React.createElement(LoadingIndicator, { show: isClusterIdUpdating, size: "xs" })));
290
327
  }
291
328
  }
@@ -111,7 +111,7 @@ export default function CaseReferenceNumber(props) {
111
111
  React.createElement(ValueChangedIcon, { afterLocalChange: afterLocalChange, isLocalChange: localRefNumberChange, value: referenceNumber, getTooltipContent: getChangedValueTooltip(() => CaseValuesToWatch.altID) }),
112
112
  ' ',
113
113
  !isExportingPDF ? (React.createElement(Tooltip, { trigger: 'mouseenter focus', position: TooltipPosition.top, content: React.createElement(Trans, null, "Add your internal tracking ID to better identify and organize support issues.") },
114
- React.createElement(InfoIcon, { className: "pf-v6-u-ml-sm cursor-pointer", "aria-label": "Case Alternate ID", color: "#5e40be" }))) : ('')), allowInlineEdit: inlineEditable, content: referenceNumber || t('No personal reference number to display.'), hideSaveCancel: hideSaveCancel, saveDisabled: saveDisabled, charCount: (referenceNumberState === null || referenceNumberState === void 0 ? void 0 : referenceNumberState.length) || 0, charTotal: CASE_REFERENCE_NUMBER_LIMIT, cancelToggleState: isCancelClicked, saveToggleState: isSaveClicked, isExportingPDF: isExportingPDF },
114
+ React.createElement(InfoIcon, { className: "pf-v6-u-ml-sm cursor-pointer", "aria-label": "Case Alternate ID", color: "#5e40be" }))) : ('')), allowInlineEdit: inlineEditable && !canEditCase.isCaseNotEditable, content: referenceNumber || t('No personal reference number to display.'), hideSaveCancel: hideSaveCancel, saveDisabled: saveDisabled, charCount: (referenceNumberState === null || referenceNumberState === void 0 ? void 0 : referenceNumberState.length) || 0, charTotal: CASE_REFERENCE_NUMBER_LIMIT, cancelToggleState: isCancelClicked, saveToggleState: isSaveClicked, isExportingPDF: isExportingPDF },
115
115
  React.createElement(TextInputGroup, null,
116
116
  React.createElement(TextInput, { value: referenceNumberState, type: "text", id: "case-details-reference-number", placeholder: t(`Enter the reference number used personally or within your company`), onChange: (_event, value) => onReferenceNumberChange(value), "data-tracking-id": "case-details-reference-number", isDisabled: isUpdating, onKeyDown: handleKeyDown, validated: (referenceNumberState === null || referenceNumberState === void 0 ? void 0 : referenceNumberState.length) > CASE_REFERENCE_NUMBER_LIMIT
117
117
  ? ValidatedOptions.error
@@ -1 +1 @@
1
- {"version":3,"file":"ProductVersion.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDetails/ProductVersion.tsx"],"names":[],"mappings":"AA0CA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAevE,UAAU,MAAM;IACZ,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,iBAAS,eAAe,CAAC,KAAK,EAAE,MAAM,qBAkbrC;AAED,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"ProductVersion.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDetails/ProductVersion.tsx"],"names":[],"mappings":"AA0CA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAevE,UAAU,MAAM;IACZ,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,iBAAS,eAAe,CAAC,KAAK,EAAE,MAAM,qBA8arC;AAED,eAAe,eAAe,CAAC"}
@@ -77,6 +77,7 @@ function ProductNVersion(props) {
77
77
  (_a = textInputRef === null || textInputRef === void 0 ? void 0 : textInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
78
78
  };
79
79
  const canEditCase = useCanEditCase();
80
+ const isCaseReadOnly = canEditCase.isCaseNotEditable;
80
81
  const { isExportingPDF } = useContext(PDFContext);
81
82
  const onProductChange = (option) => __awaiter(this, void 0, void 0, function* () {
82
83
  if (canEditCase.alert())
@@ -135,11 +136,7 @@ function ProductNVersion(props) {
135
136
  const serviceLevels = !onlyVersionChanged
136
137
  ? resolveProductServiceLevelsForAccount(selectedAccountDetails.data, allProducts.data.productsResult, selectedProduct)
137
138
  : undefined;
138
- yield updateCaseDetailsGraphQL(caseDispatch, apolloClient, caseNumber, Object.assign(Object.assign(Object.assign({ product: selectedProduct, version: selectedVersion }, (selectedVersionId ? { productId: selectedVersionId } : {})), (!onlyVersionChanged && (serviceLevels === null || serviceLevels === void 0 ? void 0 : serviceLevels.defaultEntitlementId)
139
- ? { entitlementId: serviceLevels.defaultEntitlementId }
140
- : {})), (!onlyVersionChanged && (serviceLevels === null || serviceLevels === void 0 ? void 0 : serviceLevels.entitlementSla)
141
- ? { entitlementSla: serviceLevels.entitlementSla }
142
- : {})), false, { existingCaseDetails });
139
+ yield updateCaseDetailsGraphQL(caseDispatch, apolloClient, caseNumber, Object.assign({ product: selectedProduct, version: selectedVersion }, (selectedVersionId ? { productId: selectedVersionId } : {})), false, { existingCaseDetails });
143
140
  if (!onlyVersionChanged) {
144
141
  const catalogProduct = getProductObj(allProducts.data.productsResult, selectedProduct);
145
142
  if (catalogProduct.productId) {
@@ -253,7 +250,7 @@ function ProductNVersion(props) {
253
250
  setIsOpen,
254
251
  onSelect: onProductChange,
255
252
  });
256
- const toggle = (toggleRef) => (React.createElement(MenuToggle, { ref: toggleRef, variant: "typeahead", onClick: onToggleClick, isExpanded: isOpen, isFullWidth: true, isDisabled: isProductUpdating || (allProducts.isFetching && !allProducts.isError), status: isProductInvalid ? 'danger' : undefined },
253
+ const toggle = (toggleRef) => (React.createElement(MenuToggle, { ref: toggleRef, variant: "typeahead", onClick: onToggleClick, isExpanded: isOpen, isFullWidth: true, isDisabled: isProductUpdating || (allProducts.isFetching && !allProducts.isError) || isCaseReadOnly, status: isProductInvalid ? 'danger' : undefined },
257
254
  React.createElement(TextInputGroup, { isPlain: true },
258
255
  React.createElement(TextInputGroupMain, { value: inputValue, onClick: onToggleClick, onChange: onTextInputChange, onKeyDown: onInputKeyDown, autoComplete: "off", innerRef: textInputRef, placeholder: t('Select a product'), role: "combobox", isExpanded: isOpen, "aria-controls": "case-details-product-selector-listbox" }),
259
256
  isProductUpdating ? (React.createElement(TextInputGroupUtilities, null,
@@ -290,6 +287,7 @@ function ProductNVersion(props) {
290
287
  React.createElement(SingleSelectDropdown, { className: "open-case-version", id: "version-dropdown", placeholder: t(`Select a version`), selected: getVersionSelectedDropdownOption, options: getVersionDropdownOptions, isInvalid: checkIfVersionIsInvalid, isDisabled: isVersionUpdating ||
291
288
  (allProducts.isFetching && !allProducts.isError) ||
292
289
  isEmpty(selectedProductLocal) ||
293
- isCaseUpdating, onSelect: onVersionChange, isLoading: isVersionUpdating || versionsDetails.isFetching, dataTrackingId: "case-details-version-selector", isScrollable: true }))))));
290
+ isCaseUpdating ||
291
+ isCaseReadOnly, onSelect: onVersionChange, isLoading: isVersionUpdating || versionsDetails.isFetching, dataTrackingId: "case-details-version-selector", isScrollable: true }))))));
294
292
  }
295
293
  export default ProductNVersion;
@@ -133,7 +133,7 @@ function Summary(props) {
133
133
  React.createElement(NewInlineEdit, { labelProps: { htmlFor: 'case-details-summary' }, labelContent: React.createElement(React.Fragment, null,
134
134
  React.createElement(Trans, null, "Title"),
135
135
  React.createElement(ValueChangedIcon, { afterLocalChange: afterLocalChange, isLocalChange: localSummaryChange, value: summary, getTooltipContent: getSummaryChangedTT }),
136
- React.createElement("span", { className: "form-required", "aria-hidden": true }, "*")), allowInlineEdit: props.inlineEditable, content: summary ? summary : t('No summary to display.'), saveDisabled: saveDisabled, hideLabel: props.hideLabel, hideSaveCancel: props.hideSaveCancel, charCount: (summaryState === null || summaryState === void 0 ? void 0 : summaryState.length) || 0, charTotal: SUMMARY_LENGTH_LIMIT, cancelToggleState: cancelToggleKey % 2 === 1, saveToggleState: saveToggleKey % 2 === 1, isExportingPDF: isExportingPDF },
136
+ React.createElement("span", { className: "form-required", "aria-hidden": true }, "*")), allowInlineEdit: props.inlineEditable && !canEditCase.isCaseNotEditable, content: summary ? summary : t('No summary to display.'), saveDisabled: saveDisabled, hideLabel: props.hideLabel, hideSaveCancel: props.hideSaveCancel, charCount: (summaryState === null || summaryState === void 0 ? void 0 : summaryState.length) || 0, charTotal: SUMMARY_LENGTH_LIMIT, cancelToggleState: cancelToggleKey % 2 === 1, saveToggleState: saveToggleKey % 2 === 1, isExportingPDF: isExportingPDF },
137
137
  React.createElement("div", { className: "pf-v6-u-display-flex pf-v6-u-flex-direction-row pf-v6-u-align-items-center" },
138
138
  React.createElement(TextInput, { type: "text", id: "case-details-summary", "aria-required": true, required: true, name: "case-details-summary", value: summaryState, onChange: (_event, value) => onSummaryChange(value), isDisabled: isSummaryUpdating, "data-tracking-id": "case-details-summary", onKeyDown: handleKeyDown, validated: (summaryState === null || summaryState === void 0 ? void 0 : summaryState.length) > SUMMARY_LENGTH_LIMIT || isSummaryEmpty
139
139
  ? ValidatedOptions.error
@@ -1 +1 @@
1
- {"version":3,"file":"CaseAttachment.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/CaseAttachment.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AAsBxE,OAAO,KAAuC,MAAM,OAAO,CAAC;AAS5D,UAAU,MAAM;IACZ,UAAU,EAAE,WAAW,CAAC;IACxB,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,WAAW,KAAK,IAAI,CAAC;IACzE,eAAe,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,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,cAAc,4EAkSlB,CAAC;AAEH,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"CaseAttachment.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/CaseAttachment.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AAsBxE,OAAO,KAAuC,MAAM,OAAO,CAAC;AAS5D,UAAU,MAAM;IACZ,UAAU,EAAE,WAAW,CAAC;IACxB,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,WAAW,KAAK,IAAI,CAAC;IACzE,eAAe,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,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,cAAc,4EAmSlB,CAAC;AAEH,eAAe,cAAc,CAAC"}
@@ -186,7 +186,7 @@ const CaseAttachment = React.forwardRef((props, ref) => {
186
186
  React.createElement("code", { ref: clipboardRef }, props.attachment.checksum)),
187
187
  React.createElement(Tooltip, { position: TooltipPosition.top, content: clipboardText, trigger: "click" },
188
188
  React.createElement("button", { "aria-label": "Copy to clipboard", className: "nimbus-icon-clipboard clip-code-raw-btn xs-icon", onClick: copyToClipboard, type: "button" }))))),
189
- React.createElement(InlineEdit, { labelProps: { htmlFor: `case-details-file-description-${props.attachment.id}` }, allowInlineEdit: true, labelContent: React.createElement(Trans, null, "Description"), content: React.createElement("div", { className: "clip-content-wrapper code-block" }, props.attachment.description), hideSaveCancel: false, onSave: onDescriptionSave, onCancel: onDescriptionCancel, dataTrackingId: "case-details-file-description", initialIsEditing: props.attachment.description ? false : true, saveDisabled: isCaseAttachmentDescriptionLengthy ||
189
+ React.createElement(InlineEdit, { labelProps: { htmlFor: `case-details-file-description-${props.attachment.id}` }, allowInlineEdit: !canEditCase.isCaseNotEditable, labelContent: React.createElement(Trans, null, "Description"), content: React.createElement("div", { className: "clip-content-wrapper code-block" }, props.attachment.description), hideSaveCancel: false, onSave: onDescriptionSave, onCancel: onDescriptionCancel, dataTrackingId: "case-details-file-description", initialIsEditing: props.attachment.description ? false : true, saveDisabled: isCaseAttachmentDescriptionLengthy ||
190
190
  isCaseAttachmentDescription ||
191
191
  attachmentDescription === props.attachment.description ||
192
192
  isUpdating, loadingIndicator: isUpdating ? React.createElement(LoadingIndicator, { isInline: true }) : undefined, isExportingPDF: isExportingPDF },
@@ -198,7 +198,7 @@ const CaseAttachment = React.forwardRef((props, ref) => {
198
198
  React.createElement("span", { className: "pull-right pf-v6-l-flex pf-m-align-items-center" },
199
199
  props.showJumpToComment && (React.createElement(DiscussionItemLink, { onClick: onJumpToComment(props.attachment.id), discussionType: DiscussionType.ATTACHMENT, itemId: props.attachment.id, caseNumber: props.attachment.caseNumber, isCopyLink: false },
200
200
  React.createElement(Trans, null, "Jump to attachment"))),
201
- React.createElement("button", { className: `btn btn-app btn-link text-danger ${isExportingPDF ? 'hide-in-pdf' : ''}`, title: "Delete attachment", onClick: deleteAttachment(props.attachment), "data-tracking-id": "delete-attachmentid-button", type: "button" },
201
+ React.createElement("button", { className: `btn btn-app btn-link text-danger ${isExportingPDF ? 'hide-in-pdf' : ''}`, title: "Delete attachment", onClick: deleteAttachment(props.attachment), "data-tracking-id": "delete-attachmentid-button", type: "button", disabled: canEditCase.isCaseNotEditable },
202
202
  React.createElement(Trans, null, "Delete")),
203
203
  React.createElement(JumpAndCopyLink, { caseNumber: props.attachment.caseNumber, cardId: props.attachment.id, discussionType: DiscussionType.ATTACHMENT })))));
204
204
  });
@@ -6,6 +6,7 @@ interface IProps {
6
6
  feedback: number;
7
7
  onFeedBackUpdate: (id: string, feedback: number) => void;
8
8
  accountNumber: string;
9
+ onReply: (author: string, commentId: string, timestamp: string) => void;
9
10
  showJumpToComment?: boolean;
10
11
  onJumpToComment?: (commentId: string) => void;
11
12
  downloadAttachment: ({ caseNumber, uuid, name }: {
@@ -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;AAU7D,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,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,4EAgUhB,CAAC;AAEH,eAAe,YAAY,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;AAU7D,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,4EA8UhB,CAAC;AAEH,eAAe,YAAY,CAAC"}
@@ -251,6 +251,8 @@ const CaseComments = React.forwardRef((props, ref) => {
251
251
  React.createElement("span", { className: "pull-right pf-v6-l-flex pf-m-align-items-center" },
252
252
  props.showJumpToComment && (React.createElement(DiscussionItemLink, { onClick: onJumpToComment(props.comment.id), discussionType: DiscussionType.COMMENT, itemId: props.comment.id, caseNumber: caseNumber, isCopyLink: false },
253
253
  React.createElement(Trans, null, "Jump to comment"))),
254
- React.createElement(JumpAndCopyLink, { caseNumber: caseNumber, cardId: props.comment.id, discussionType: DiscussionType.COMMENT })))));
254
+ React.createElement(JumpAndCopyLink, { caseNumber: caseNumber, cardId: props.comment.id, discussionType: DiscussionType.COMMENT }),
255
+ 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"))))));
255
257
  });
256
258
  export default CaseComments;
@@ -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;AAkBvE,OAAO,KAAoE,MAAM,OAAO,CAAC;AAoCzF,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;CAClD;AAOD,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,KAAK,EAAE,MAAM,qBAugBnD"}
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;AAkBvE,OAAO,KAAoE,MAAM,OAAO,CAAC;AAsCzF,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;CAClD;AAOD,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,KAAK,EAAE,MAAM,qBAqhBnD"}
@@ -26,7 +26,7 @@ import { AutoSizer, CellMeasurer, CellMeasurerCache, List, WindowScroller } from
26
26
  import { useCaseDispatch, useCaseSelector } from '../../../../context/CaseContext';
27
27
  import { useCaseDetailsPageDispatchContext, useCaseDetailsPageStateContext, } from '../../../../context/CaseDetailsPageContext';
28
28
  import { useCaseDiscussionTabDispatchContext, useCaseDiscussionTabStateContext, } from '../../../../context/CaseDiscussionTabContext';
29
- import { discussionSortOrderConst, DiscussionType, setDiscussions, setSort, updateAttachment, updateAttacmentsAfterRemoval, updateDiscussionStateAttachments, } from '../../../../reducers/CaseDiscussionTabReducer';
29
+ import { clearReplyingTo, discussionSortOrderConst, DiscussionType, setDiscussions, setReplyingTo, setSort, updateAttachment, updateAttacmentsAfterRemoval, updateDiscussionStateAttachments, } from '../../../../reducers/CaseDiscussionTabReducer';
30
30
  import { setCaseState } from '../../../../reducers/CaseReducer';
31
31
  import { AttachmentContextProvider } from '../../../shared/fileUpload/reducer/AttachmentReducerContext';
32
32
  import { PDFContext } from '../../PDFContainer';
@@ -52,7 +52,7 @@ export default function CaseDiscussion(props) {
52
52
  const urlSearchParams = getUrlParsedParams(useLocation().search);
53
53
  const discussionIdFromUrl = ((urlSearchParams === null || urlSearchParams === void 0 ? void 0 : urlSearchParams.commentId) || (urlSearchParams === null || urlSearchParams === void 0 ? void 0 : urlSearchParams.attachmentId));
54
54
  const activeDiscussionElementRef = useRef(null);
55
- const { sort, discussionFiltersListState, allDiscussions } = useCaseDiscussionTabStateContext();
55
+ const { sort, discussionFiltersListState, allDiscussions, replyingToAuthor, replyingToTimestamp } = useCaseDiscussionTabStateContext();
56
56
  const { globalMetadataState: { navBarRef }, } = useGlobalStateContext();
57
57
  const dispatchDiscussion = useCaseDiscussionTabDispatchContext();
58
58
  const { caseDetailsPageState: { caseAttachmentHydra }, } = useCaseDetailsPageStateContext();
@@ -147,6 +147,13 @@ export default function CaseDiscussion(props) {
147
147
  }
148
148
  dtmTrackEventCaseStepEncountered('comment', caseNumber, product, version);
149
149
  });
150
+ const onReply = (author, commentId, timestamp) => {
151
+ setReplyingTo(dispatchDiscussion, author, commentId, timestamp);
152
+ scrollIntoView(props.tabRef, { navBarRef, timeout: 0, onlyIfNotInViewport: true, offset: 30 });
153
+ };
154
+ const onClearReply = () => {
155
+ clearReplyingTo(dispatchDiscussion);
156
+ };
150
157
  /** Strips special chars to prevent regex injection, then HTML-escapes via DOM round-trip to prevent XSS. */
151
158
  const sanitizedSearchInput = (value) => {
152
159
  if (!value)
@@ -247,7 +254,7 @@ export default function CaseDiscussion(props) {
247
254
  if (isEmpty(discussion) || isEmpty(discussion.item))
248
255
  return null;
249
256
  return (React.createElement("div", { "data-tracking-id": "discussion-elements", key: discussion.item.id },
250
- discussion.discussionType === DiscussionType.COMMENT && (React.createElement(CaseComments, { caseNumber: caseNumber, ref: discussionIdFromUrl === discussion.item.id ? activeDiscussionElementRef : null, comment: discussion.item, onFeedBackUpdate: () => { }, feedback: discussion.feedback, accountNumber: props.accountNumber, showJumpToComment: !isEmpty(searchCommentText), onJumpToComment: onJumpToComment, downloadAttachment: downloadAttachment })),
257
+ discussion.discussionType === DiscussionType.COMMENT && (React.createElement(CaseComments, { caseNumber: caseNumber, ref: discussionIdFromUrl === discussion.item.id ? activeDiscussionElementRef : null, comment: discussion.item, onFeedBackUpdate: () => { }, feedback: discussion.feedback, accountNumber: props.accountNumber, onReply: onReply, showJumpToComment: !isEmpty(searchCommentText), onJumpToComment: onJumpToComment, downloadAttachment: downloadAttachment })),
251
258
  discussion.discussionType === DiscussionType.ATTACHMENT && (React.createElement(CaseAttachment, { attachment: discussion.item, onAttachmentRemove: onAttachmentRemove, onMetadataUpdate: onMetadataUpdate, showJumpToComment: !isEmpty(searchCommentText), onJumpToComment: onJumpToComment, downloadAttachment: downloadAttachment, ref: discussionIdFromUrl === discussion.item.id ? activeDiscussionElementRef : null }))));
252
259
  };
253
260
  const rowRenderer = ({ index, parent, key, style, }) => {
@@ -321,7 +328,7 @@ export default function CaseDiscussion(props) {
321
328
  return (React.createElement(React.Fragment, null,
322
329
  React.createElement(ErrorBoundary, { errorMsgInfo: { message: t('There was an error loading post comment section') } },
323
330
  React.createElement(AttachmentContextProvider, null,
324
- React.createElement(PostComment, { caseNumber: caseNumber, allDiscussions: allDiscussions, onCommentAdded: onCommentAdded, onAttachmentAdded: onAttachmentAdded, onAttachmentRemove: onAttachmentRemove }))),
331
+ React.createElement(PostComment, { caseNumber: caseNumber, allDiscussions: allDiscussions, onCommentAdded: onCommentAdded, onAttachmentAdded: onAttachmentAdded, onAttachmentRemove: onAttachmentRemove, replyingToAuthor: replyingToAuthor, replyingToTimestamp: replyingToTimestamp, onClearReply: onClearReply }))),
325
332
  props.commentsLoading && allDiscussions.length === 0 && React.createElement(LoadingIndicator, { size: "sm" }),
326
333
  props.commentsError && allDiscussions.length === 0 && (React.createElement(AlertMessage, { variant: AlertType.DANGER, show: true, title: t('Failed to load comments. Try refreshing.') })),
327
334
  !props.commentsLoading && !props.commentsError && allDiscussions.length === 0 && (React.createElement(Trans, null, "No comments")),
@@ -2,12 +2,14 @@ import { Dispatch, SetStateAction } from 'react';
2
2
  interface UseCommentDraftsParams {
3
3
  caseNumber: string;
4
4
  loggedInUserSSO: string;
5
+ isInternal: boolean;
5
6
  commentSignature: string;
7
+ canPostPrivateComments: boolean;
6
8
  setCommentText: Dispatch<SetStateAction<string>>;
7
- setIsPublic: Dispatch<SetStateAction<boolean | undefined>>;
9
+ setIsPublic: Dispatch<SetStateAction<boolean>>;
8
10
  applySignature: (text: string) => void;
9
11
  }
10
- export declare function useCommentDrafts({ caseNumber, loggedInUserSSO, commentSignature, setCommentText, setIsPublic, applySignature, }: UseCommentDraftsParams): {
12
+ export declare function useCommentDrafts({ caseNumber, loggedInUserSSO, isInternal, commentSignature, canPostPrivateComments, setCommentText, setIsPublic, applySignature, }: UseCommentDraftsParams): {
11
13
  saveDraft: (...args: any[]) => void;
12
14
  clearDraft: () => Promise<void>;
13
15
  };
@@ -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,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACjD,WAAW,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC;IAC3D,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,cAAc,GACjB,EAAE,sBAAsB;;;EAqDxB"}
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;;;EA4DxB"}
@@ -11,16 +11,22 @@ import { debounce } from '@patternfly/react-core';
11
11
  import { useEffect, useMemo } from 'react';
12
12
  import { CaseDetailsCacheNamespaces } from '../../../../../enums/cache';
13
13
  import { CaseDetailsCacheUtils } from '../../../../../utils/caseDetailsCacheUtils';
14
- export function useCommentDrafts({ caseNumber, loggedInUserSSO, commentSignature, setCommentText, setIsPublic, applySignature, }) {
14
+ export function useCommentDrafts({ caseNumber, loggedInUserSSO, isInternal, commentSignature, canPostPrivateComments, setCommentText, setIsPublic, applySignature, }) {
15
15
  const saveDraft = useMemo(() => debounce(function (value, publicFlag) {
16
16
  return __awaiter(this, void 0, void 0, function* () {
17
+ const div = document.createElement('div');
18
+ div.innerHTML = value;
19
+ const plainText = (div.textContent || '').trim();
20
+ if (!plainText || plainText === commentSignature) {
21
+ return;
22
+ }
17
23
  const { value: prevDraftComments = {} } = (yield CaseDetailsCacheUtils.get(CaseDetailsCacheNamespaces.COMMENTS_DRAFT.name, loggedInUserSSO)) || {};
18
- prevDraftComments[caseNumber] = { text: value, privateFlag: !publicFlag };
24
+ prevDraftComments[caseNumber] = { text: value, privateFlag: !(publicFlag !== null && publicFlag !== void 0 ? publicFlag : true) };
19
25
  CaseDetailsCacheUtils.set(CaseDetailsCacheNamespaces.COMMENTS_DRAFT.name, loggedInUserSSO, {
20
26
  value: prevDraftComments,
21
27
  });
22
28
  });
23
- }, 500), [caseNumber, loggedInUserSSO]);
29
+ }, 500), [caseNumber, commentSignature, loggedInUserSSO]);
24
30
  const clearDraft = () => __awaiter(this, void 0, void 0, function* () {
25
31
  const { value: prevDraftComments = {} } = (yield CaseDetailsCacheUtils.get(CaseDetailsCacheNamespaces.COMMENTS_DRAFT.name, loggedInUserSSO)) || {};
26
32
  delete prevDraftComments[caseNumber];
@@ -41,8 +47,9 @@ export function useCommentDrafts({ caseNumber, loggedInUserSSO, commentSignature
41
47
  if (!/<[a-z][\s\S]*>/i.test(draftText)) {
42
48
  draftText = `<p>${draftText}</p>`;
43
49
  }
50
+ setCommentText(draftText);
44
51
  applySignature(draftText);
45
- if (draft.privateFlag)
52
+ if (draft.privateFlag && canPostPrivateComments)
46
53
  setIsPublic(false);
47
54
  });
48
55
  }
@@ -13,6 +13,9 @@ interface IProps {
13
13
  */
14
14
  defaultIsPublic?: boolean;
15
15
  allDiscussions: IDiscussion[];
16
+ replyingToAuthor?: string | null;
17
+ replyingToTimestamp?: string | null;
18
+ onClearReply?: () => void;
16
19
  }
17
20
  export declare function PostComment(props: IProps): React.JSX.Element;
18
21
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"PostComment.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/PostComment.tsx"],"names":[],"mappings":"AASA,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;CACjC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,qBA0XxC"}
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,qBA+ZxC"}