@rh-support/troubleshoot 2.2.190 → 2.2.192

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 (20) hide show
  1. package/lib/esm/components/CaseEditView/CaseOverview/index.d.ts.map +1 -1
  2. package/lib/esm/components/CaseEditView/CaseOverview/index.js +2 -2
  3. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.d.ts.map +1 -1
  4. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.js +2 -2
  5. package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.css +15 -0
  6. package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.d.ts.map +1 -1
  7. package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.js +119 -73
  8. package/lib/esm/components/CaseManagement/RHAssociatesSelector.d.ts.map +1 -1
  9. package/lib/esm/components/CaseManagement/RHAssociatesSelector.js +11 -5
  10. package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.d.ts.map +1 -1
  11. package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.js +11 -5
  12. package/lib/esm/components/Recommendations/AsideResults.d.ts.map +1 -1
  13. package/lib/esm/components/Recommendations/AsideResults.js +3 -1
  14. package/lib/esm/components/Recommendations/Recommendations.d.ts.map +1 -1
  15. package/lib/esm/components/Recommendations/Recommendations.js +1 -1
  16. package/lib/esm/components/shared/fileUpload/fileSelectors/WidgetFileSelector.d.ts +2 -2
  17. package/lib/esm/components/shared/fileUpload/fileSelectors/WidgetFileSelector.d.ts.map +1 -1
  18. package/lib/esm/components/shared/fileUpload/fileSelectors/WidgetFileSelector.js +10 -1
  19. package/lib/esm/scss/_pf-overrides.scss +6 -0
  20. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/CaseEditView/CaseOverview/index.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAkD,MAAM,OAAO,CAAC;AA4BvE,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;CACnD;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,KAAK,EAAE,MAAM,qBAigBjD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/CaseEditView/CaseOverview/index.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAkD,MAAM,OAAO,CAAC;AA4BvE,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;CACnD;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,KAAK,EAAE,MAAM,qBA4fjD"}
@@ -12,7 +12,7 @@ import LockIcon from '@patternfly/react-icons/dist/js/icons/lock-icon';
12
12
  import { ToastNotification, useConfirmation } from '@rh-support/components';
13
13
  import { CloseCaseModal, fetchCaseSeverities, fetchCaseTypes, GlobalMetadataDispatchContext, GlobalMetadataStateContext, useCanEditCase, } from '@rh-support/react-context';
14
14
  import { AbilityContext, CaseDetailsFields, resourceActions, resources } from '@rh-support/user-permissions';
15
- import { dtmTrackEvent, haventLoadedMetadata, isOpenShiftOnlineProduct, PreviousCaseTypes, toNewCaseTypeMapper, toNewCaseTypeSwitcher, } from '@rh-support/utils';
15
+ import { dtmTrackEventCaseStepEncountered, haventLoadedMetadata, isOpenShiftOnlineProduct, PreviousCaseTypes, toNewCaseTypeMapper, toNewCaseTypeSwitcher, } from '@rh-support/utils';
16
16
  import isEqual from 'lodash/isEqual';
17
17
  import React, { useContext, useEffect, useRef, useState } from 'react';
18
18
  import { Trans, useTranslation } from 'react-i18next';
@@ -301,7 +301,7 @@ export default function CaseOverview(props) {
301
301
  yield updateStatusState(CaseStatusEnum.CLOSED);
302
302
  yield postCommentAfterStatusUpdate(commentBody);
303
303
  setShowCaseCloseModal(false);
304
- dtmTrackEvent('case closed', 'case closed - case details page', caseNumber, `${product}|${version}`);
304
+ dtmTrackEventCaseStepEncountered('close', caseNumber, product, version);
305
305
  }), isUpdating: (caseOverviewState.caseStatusUpdating && isCaseUpdating) || isPostingComment, onClose: () => {
306
306
  setShowCaseCloseModal(false);
307
307
  }, caseNumber: caseNumber })),
@@ -1 +1 @@
1
- {"version":3,"file":"CaseDiscussion.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,YAAY,EAA0B,MAAM,yCAAyC,CAAC;AAa1G,OAAO,KAAoE,MAAM,OAAO,CAAC;AAsCzF,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC/C,eAAe,EAAE,OAAO,CAAC;CAC5B;AAOD,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,KAAK,EAAE,MAAM,qBA8mBnD"}
1
+ {"version":3,"file":"CaseDiscussion.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,YAAY,EAA0B,MAAM,yCAAyC,CAAC;AAkB1G,OAAO,KAAoE,MAAM,OAAO,CAAC;AAsCzF,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC/C,eAAe,EAAE,OAAO,CAAC;CAC5B;AAOD,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,KAAK,EAAE,MAAM,qBA8mBnD"}
@@ -12,7 +12,7 @@ import { MenuToggle, Select, SelectList, SelectOption } from '@patternfly/react-
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 { dtmTrackEvent, getDropdownBtnPlaceholder, getUrlParsedParams, scrollIntoView } from '@rh-support/utils';
15
+ import { 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';
@@ -171,7 +171,7 @@ export default function CaseDiscussion(props) {
171
171
  };
172
172
  const onCommentAdded = () => __awaiter(this, void 0, void 0, function* () {
173
173
  yield updateDiscussionStateComments(dispatchDiscussion, caseDispatch, caseNumber, allDiscussions, sort, caseFeedbacksHydra.data, discussionFiltersListState);
174
- dtmTrackEvent('case details page comment', 'comment', caseNumber, `${product}|${version}`);
174
+ dtmTrackEventCaseStepEncountered('comment', caseNumber, product, version);
175
175
  });
176
176
  const onAttachmentAdded = () => __awaiter(this, void 0, void 0, function* () {
177
177
  yield updateDiscussionStateAttachments(dispatchDiscussion, caseNumber, allDiscussions, sort, loggedInUsersAccount.data.secureSupport, discussionFiltersListState);
@@ -17,9 +17,24 @@
17
17
  }
18
18
 
19
19
  .right-node {
20
+ white-space: nowrap;
20
21
  overflow: hidden;
21
22
  text-overflow: ellipsis;
23
+ width: 100%;
24
+ }
25
+ .content-date-right-history {
22
26
  white-space: nowrap;
27
+ overflow: hidden;
28
+ text-overflow: ellipsis;
29
+ }
30
+
31
+ .timelineMenu button.pf-v5-c-button.pf-m-primary {
32
+ margin-bottom: 31px;
33
+ }
34
+
35
+ /* fixes a bug with datepicker month input being bigger than the day */
36
+ .timelineMenu div.pf-v5-c-calendar-month__header-year span.pf-v5-c-form-control input {
37
+ margin-bottom: -3px;
23
38
  }
24
39
 
25
40
  .timeline::after {
@@ -1 +1 @@
1
- {"version":3,"file":"Timeline.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseHistory/Timeline.tsx"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,CAAC;AA0BxB,OAAO,KAAsC,MAAM,OAAO,CAAC;AA8P3D,QAAA,MAAM,QAAQ;;uBAgUb,CAAC;AAEF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"Timeline.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseHistory/Timeline.tsx"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,CAAC;AA0BxB,OAAO,KAAsC,MAAM,OAAO,CAAC;AAgT3D,QAAA,MAAM,QAAQ;;uBAkUb,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -42,11 +42,15 @@ const TimelineNode = React.forwardRef(({ leftEvent, rightEvent, user, useAvatar
42
42
  React.createElement("div", { ref: ref, "aria-label": `${useAvatar ? 'User profile picture' : 'Timeline marker'}`, tabIndex: -1 }, useAvatar ? (React.createElement(Avatar, { src: 'https://www.patternfly.org/images/668560cd.svg', className: `timeline-avatar timeline-avatar-${user}`, alt: `${user}'s avatar` })) : (React.createElement("div", { className: `timeline-marker timeline-${user}` }))),
43
43
  rightEvent && React.createElement(TimelineEvent, Object.assign({}, rightEvent, { side: "right" }))));
44
44
  });
45
- //Handling changes in case fields to be displayed to the user
45
+ /**
46
+ *
47
+ * @param {Object} item - The item object to process.
48
+ * @returns {string} The formatted text string.
49
+ */
46
50
  const applyReplacements = (item) => {
47
- if (!item || typeof item !== 'object') {
51
+ if (!item || typeof item !== 'object')
48
52
  return '';
49
- }
53
+ // API field names to easy to read display names
50
54
  const fieldNameMapping = {
51
55
  ContactId: 'Contact',
52
56
  RH_Product__c: 'Product',
@@ -61,7 +65,13 @@ const applyReplacements = (item) => {
61
65
  SlaStartDate: 'SLA Start Date',
62
66
  Case_Language__c: 'Case Language',
63
67
  Alternate_Id__c: 'Personal reference number',
68
+ SBR_Group__c: 'SBR Group',
64
69
  };
70
+ // https://help.salesforce.com/s/articleView?id=000384484&type=1
71
+ // adding in option for multiselect fields provided by salesforce.
72
+ // in future for more multiselect fields just add to below array.
73
+ const multiSelectFields = ['SBR_Group__c'];
74
+ // language codes to language names
65
75
  const languageCodeMapping = {
66
76
  de: 'German',
67
77
  en: 'English',
@@ -73,75 +83,109 @@ const applyReplacements = (item) => {
73
83
  jp: 'Japanese',
74
84
  zh: 'Chinese',
75
85
  };
76
- let fieldName = fieldNameMapping[item.fieldName] || item.fieldLabel || item.fieldName || '';
77
- const getNameValue = (prefix) => {
78
- const keyMap = {
79
- Product: 'product',
80
- Version: 'version',
81
- Account: 'account',
82
- Contact: 'contact',
83
- Entitlement: 'entitlement',
84
- 'Business Hours': 'businessHours',
85
- Owner: 'queueNameOwner',
86
- 'Customer Escalation': 'customerEscalation',
87
- 'Case Group': 'caseGroup',
88
- 'SLA Start Date': 'slaStartDate',
89
- 'Case Language': 'caseLanguage',
90
- Alternate_Id__c: 'Personal reference number',
91
- };
92
- const baseKey = keyMap[fieldName] || fieldName.replace(/\s/g, '');
93
- const key = `${baseKey}${prefix}Name`;
94
- return item[key] || item[`${prefix}Value`] || '';
86
+ // field display names to prefix keys used in item properties
87
+ const keyMap = {
88
+ Product: 'product',
89
+ Version: 'version',
90
+ Account: 'account',
91
+ Contact: 'contact',
92
+ Entitlement: 'entitlement',
93
+ 'Business Hours': 'businessHours',
94
+ Owner: 'queueNameOwner',
95
+ 'Customer Escalation': 'customerEscalation',
96
+ 'Case Group': 'caseGroup',
97
+ 'SLA Start Date': 'slaStartDate',
98
+ 'Case Language': 'caseLanguage',
99
+ 'Personal reference number': 'alternateId',
95
100
  };
96
- let newValue = getNameValue('New');
97
- let oldValue = getNameValue('Old');
101
+ // get the mapped display name for a field
102
+ const getMappedFieldName = (fieldName) => fieldNameMapping[fieldName] || item.fieldLabel || fieldName || '';
103
+ // get the new or old value using the prefix
104
+ const getNameValue = (prefix) => item[`${keyMap[getMappedFieldName(item.fieldName)]}${prefix}Name`] ||
105
+ item[`${prefix}Value`] ||
106
+ item[`${prefix.toLowerCase()}Value`] ||
107
+ '';
108
+ // formatting functions
109
+ const formatLanguageChange = (fieldName, oldLanguage, newLanguage) => `changed ${fieldName} from ${oldLanguage} to ${newLanguage}`;
110
+ const formatSimpleChange = (fieldName, value) => `set ${fieldName} to ${value}`;
111
+ const formatBooleanChange = (fieldName, value) => `set ${fieldName} to ${value}`;
112
+ // get field name and values
113
+ const fieldName = getMappedFieldName(item.fieldName);
114
+ const newValue = getNameValue('New');
115
+ const oldValue = getNameValue('Old');
98
116
  let newText = '';
99
- if (item.fieldName === 'Case_Language__c') {
100
- // Map language codes to language names
101
- const newLanguage = languageCodeMapping[item.newValue] || item.newValue;
102
- const oldLanguage = languageCodeMapping[item.oldValue] || item.oldValue;
103
- if (item.oldValue && item.newValue) {
104
- newText = `changed ${fieldName} from ${oldLanguage} to ${newLanguage}`;
105
- }
106
- else if (item.newValue) {
107
- newText = `set ${fieldName} to ${newLanguage}`;
108
- }
109
- }
110
- else if (item.fieldName === 'OwnerId' && item.createdByUserName) {
111
- newText = `set ${fieldName} to ${item.createdByUserName}`;
112
- }
113
- else if (item.fieldName === 'Status') {
114
- if (item.oldValue && item.newValue) {
115
- newText = `changed ${fieldName} from ${item.oldValue} to ${item.newValue}`;
116
- }
117
- else if (item.newValue) {
118
- newText = `set ${fieldName} to ${item.newValue}`;
117
+ // special field handling below, useful for future exposed case history fields etc / api changes
118
+ switch (item.fieldName) {
119
+ case 'Case_Language__c': {
120
+ const newLanguage = languageCodeMapping[item.newValue] || item.newValue;
121
+ const oldLanguage = languageCodeMapping[item.oldValue] || item.oldValue;
122
+ newText =
123
+ item.oldValue && item.newValue
124
+ ? formatLanguageChange(fieldName, oldLanguage, newLanguage)
125
+ : formatSimpleChange(fieldName, newLanguage || 'empty');
126
+ break;
119
127
  }
128
+ case 'OwnerId':
129
+ if (item.createdByUserName)
130
+ newText = formatSimpleChange(fieldName, item.createdByUserName);
131
+ break;
132
+ case 'Status':
133
+ newText =
134
+ item.oldValue && item.newValue
135
+ ? `changed ${fieldName} from ${item.oldValue} to ${item.newValue}`
136
+ : formatSimpleChange(fieldName, item.newValue || 'empty');
137
+ break;
138
+ case 'Customer_Escalation__c':
139
+ if (item.createdByUserName)
140
+ newText = 'started Escalation for this ticket';
141
+ break;
142
+ case 'SlaStartDate':
143
+ if (item.newValue)
144
+ newText = formatSimpleChange(fieldName, item.newValue);
145
+ break;
146
+ case 'RemoteSessionTermsAckedBy__c':
147
+ newText = 'Acked Remote Rider terms';
148
+ break;
149
+ default:
150
+ if (oldValue && newValue) {
151
+ if (multiSelectFields.includes(item.fieldName)) {
152
+ // in salesforce multi-select picklist values are stored as a single string, with each value separated by a semicolon
153
+ const oldValues = oldValue.split(';').map((s) => s.trim());
154
+ const newValues = newValue.split(';').map((s) => s.trim());
155
+ const added = newValues.filter((nv) => !oldValues.includes(nv));
156
+ const removed = oldValues.filter((ov) => !newValues.includes(ov));
157
+ if (added.length > 0 && removed.length === 0) {
158
+ newText = `added ${added.join(', ')} to ${fieldName}`;
159
+ }
160
+ else if (removed.length > 0 && added.length === 0) {
161
+ newText = `removed ${removed.join(', ')} from ${fieldName}`;
162
+ }
163
+ else if (added.length > 0 && removed.length > 0) {
164
+ newText = `changed ${fieldName}, added ${added.join(', ')} and removed ${removed.join(', ')}`;
165
+ }
166
+ else {
167
+ newText = `changed ${fieldName} from ${oldValue} to ${newValue}`;
168
+ }
169
+ }
170
+ else {
171
+ newText = `changed ${fieldName} from ${oldValue} to ${newValue}`;
172
+ }
173
+ }
174
+ else if (newValue) {
175
+ newText = formatSimpleChange(fieldName, newValue);
176
+ }
177
+ else if (oldValue && (newValue === undefined || newValue === '')) {
178
+ newText = `removed ${fieldName}, was ${oldValue}`;
179
+ }
180
+ else if (item.newValue !== undefined) {
181
+ newText = formatSimpleChange(fieldName, item.newValue);
182
+ }
183
+ break;
120
184
  }
121
- else if (item.fieldName === 'Customer_Escalation__c' && item.createdByUserName) {
122
- newText = `started Escalation for this ticket`;
123
- }
124
- else if (oldValue && newValue) {
125
- newText = `changed ${fieldName} from ${oldValue} to ${newValue}`;
126
- }
127
- else if (newValue) {
128
- newText = `set ${fieldName} to ${newValue}`;
129
- }
130
- else if (item.newValue !== undefined) {
131
- newText = `set ${fieldName} to ${item.newValue}`;
132
- }
133
- // Handle boolean values
134
- if (typeof item.newValue === 'boolean') {
135
- newText = `set ${fieldName} to ${item.newValue}`;
136
- }
137
- // Handle date values
138
- if (item.fieldName === 'SlaStartDate' && item.newValue) {
139
- newText = `set ${fieldName} to ${item.newValue}`;
140
- }
141
- // Replace 'Problem Statement' with 'Title'
142
- if (typeof newText === 'string') {
185
+ if (typeof item.newValue === 'boolean')
186
+ newText = formatBooleanChange(fieldName, item.newValue);
187
+ if (typeof newText === 'string')
143
188
  newText = newText.replace(/Problem Statement/g, 'Title');
144
- }
145
189
  return newText;
146
190
  };
147
191
  // function to change the apiResponse
@@ -285,15 +329,17 @@ const Timeline = ({ caseNumber }) => {
285
329
  return '';
286
330
  };
287
331
  const updateLineStyle = () => {
288
- var _a;
289
332
  const timelineElement = document.querySelector('.timeline');
290
333
  if (timelineElement) {
291
334
  if (lastNodeRef.current) {
292
- const timelineBottom = lastNodeRef.current.getBoundingClientRect().bottom + window.scrollY;
293
- const timelineTop = timelineElement.getBoundingClientRect().top + window.scrollY;
294
- const avatarHeight = ((_a = lastNodeRef.current.querySelector('.timeline-avatar')) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
295
- const calculatedBottomValue = `${timelineTop + timelineElement.offsetHeight - timelineBottom + avatarHeight}px`;
296
- timelineElement.style.setProperty('--timeline-bottom', calculatedBottomValue);
335
+ const timelineRect = timelineElement.getBoundingClientRect();
336
+ const lastNodeRect = lastNodeRef.current.getBoundingClientRect();
337
+ const timelineBottomPosition = timelineRect.bottom + window.scrollY;
338
+ const lastNodeBottomPosition = lastNodeRect.bottom + window.scrollY;
339
+ // Calculate the difference between the bottom of the timeline and the bottom of the last node
340
+ const bottomOffset = timelineBottomPosition - lastNodeBottomPosition;
341
+ // Set the --timeline-bottom CSS variable to adjust the line height
342
+ timelineElement.style.setProperty('--timeline-bottom', `${bottomOffset}px`);
297
343
  }
298
344
  else {
299
345
  timelineElement.style.setProperty('--timeline-bottom', '0px');
@@ -1 +1 @@
1
- {"version":3,"file":"RHAssociatesSelector.d.ts","sourceRoot":"","sources":["../../../../src/components/CaseManagement/RHAssociatesSelector.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAS5D,MAAM,WAAW,MAAM;CAAG;AAG1B,iBAAS,oBAAoB,CAAC,KAAK,EAAE,MAAM,qBAqM1C;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
1
+ {"version":3,"file":"RHAssociatesSelector.d.ts","sourceRoot":"","sources":["../../../../src/components/CaseManagement/RHAssociatesSelector.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAS5D,MAAM,WAAW,MAAM;CAAG;AAG1B,iBAAS,oBAAoB,CAAC,KAAK,EAAE,MAAM,qBA+M1C;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
@@ -11,6 +11,7 @@ import { Button, Tooltip, TooltipPosition } from '@patternfly/react-core';
11
11
  import { ToastNotification } from '@rh-support/components';
12
12
  import { GlobalMetadataStateContext } from '@rh-support/react-context';
13
13
  import { AbilityContext, CaseListFields, resourceActions, resources } from '@rh-support/user-permissions';
14
+ import { dtmTrackEventCaseStepEncountered } from '@rh-support/utils';
14
15
  import differenceBy from 'lodash/differenceBy';
15
16
  import filter from 'lodash/filter';
16
17
  import find from 'lodash/find';
@@ -31,11 +32,13 @@ function RHAssociatesSelector(props) {
31
32
  const [isRHAssociatesUpdating, setIsRHAssociatesUpdating] = useState(false);
32
33
  const ability = useContext(AbilityContext);
33
34
  const { isExportingPDF } = useContext(PDFContext);
34
- const { caseNumber, contactSsoUsername, selectedOwner, selectedNotificationContacts } = useCaseSelector((state) => ({
35
+ const { caseNumber, contactSsoUsername, product, selectedOwner, selectedNotificationContacts, version } = useCaseSelector((state) => ({
35
36
  caseNumber: state.caseDetails.caseNumber,
36
37
  contactSsoUsername: state.caseDetails.contactSSOName,
37
38
  selectedOwner: state.selectedOwner,
38
39
  selectedNotificationContacts: state.selectedNotificationContacts,
40
+ product: state.caseDetails.product,
41
+ version: state.caseDetails.version,
39
42
  }), isEqual);
40
43
  const caseDispatch = useCaseDispatch();
41
44
  const onRhAssociateAdded = (addedUsers) => __awaiter(this, void 0, void 0, function* () {
@@ -64,7 +67,7 @@ function RHAssociatesSelector(props) {
64
67
  ToastNotification.addDangerMessage(t(`Red Hat associate failed to get removed`));
65
68
  }
66
69
  });
67
- const onNotifiedUserChange = (selectedContacts) => __awaiter(this, void 0, void 0, function* () {
70
+ const onNotifiedUserChange = (selectedContacts_1, ...args_1) => __awaiter(this, [selectedContacts_1, ...args_1], void 0, function* (selectedContacts, throwTrackEventOnAdd = false) {
68
71
  const toAdd = filter(differenceBy(selectedContacts, selectedNotificationContacts, 'ssoUsername'), (item) => item.ssoUsername !== contactSsoUsername);
69
72
  const toRemove = filter(differenceBy(selectedNotificationContacts, selectedContacts, 'ssoUsername'), (item) => item.isInternal && item.ssoUsername !== contactSsoUsername);
70
73
  let newContacts = [];
@@ -72,6 +75,9 @@ function RHAssociatesSelector(props) {
72
75
  newContacts = [...selectedNotificationContacts, ...toAdd];
73
76
  if (!isEmpty(caseNumber)) {
74
77
  yield onRhAssociateAdded(toAdd);
78
+ if (throwTrackEventOnAdd) {
79
+ dtmTrackEventCaseStepEncountered('follow', caseNumber, product, version);
80
+ }
75
81
  }
76
82
  }
77
83
  else if ((toRemove || []).length > 0) {
@@ -90,9 +96,9 @@ function RHAssociatesSelector(props) {
90
96
  !isCurrentUserSelectedInternalNotifiedUser();
91
97
  const showRemoveWatchButton = () => (loggedInUserRights.data.isInternal() && isCurrentUserSelectedInternalNotifiedUser()) ||
92
98
  contactSsoUsername === loggedInUser.data.ssoUsername;
93
- const addCurrentUser = () => {
94
- onNotifiedUserChange([Object.assign(Object.assign({}, loggedInUser.data), { accountNumber: loggedInUserRights.data.getAccountNumber() })]);
95
- };
99
+ const addCurrentUser = () => __awaiter(this, void 0, void 0, function* () {
100
+ onNotifiedUserChange([Object.assign(Object.assign({}, loggedInUser.data), { accountNumber: loggedInUserRights.data.getAccountNumber() })], true);
101
+ });
96
102
  const removeCurrentUser = () => {
97
103
  if (isCurrentUserSelectedInternalNotifiedUser()) {
98
104
  onNotifiedUserChange(filter(selectedNotificationContacts, (contact) => contact.ssoUsername !== loggedInUser.data.ssoUsername));
@@ -1 +1 @@
1
- {"version":3,"file":"CaseContactSelector.d.ts","sourceRoot":"","sources":["../../../../../src/components/CaseManagement/SendNotifications/CaseContactSelector.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAgBvE,MAAM,WAAW,MAAM;CAAG;AAG1B,iBAAS,mBAAmB,CAAC,KAAK,EAAE,MAAM,qBAmbzC;AAED,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"CaseContactSelector.d.ts","sourceRoot":"","sources":["../../../../../src/components/CaseManagement/SendNotifications/CaseContactSelector.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAgBvE,MAAM,WAAW,MAAM;CAAG;AAG1B,iBAAS,mBAAmB,CAAC,KAAK,EAAE,MAAM,qBA8bzC;AAED,eAAe,mBAAmB,CAAC"}
@@ -11,6 +11,7 @@ import { Button, Tooltip, TooltipPosition } from '@patternfly/react-core';
11
11
  import { CaseContactsSelectorExternal, getHydraContactLabel, PromisifyModal, ToastNotification, usePrevious, } from '@rh-support/components';
12
12
  import { GlobalMetadataStateContext, useCanEditCase } from '@rh-support/react-context';
13
13
  import { AbilityContext, CaseDetailsFields, resourceActions, resources } from '@rh-support/user-permissions';
14
+ import { dtmTrackEventCaseStepEncountered } from '@rh-support/utils';
14
15
  import differenceBy from 'lodash/differenceBy';
15
16
  import filter from 'lodash/filter';
16
17
  import find from 'lodash/find';
@@ -34,7 +35,7 @@ function CaseContactSelector(props) {
34
35
  const canSeeEmailNotifications = ability.can(resourceActions.PATCH, resources.CASE_DETAILS, CaseDetailsFields.CASE_DETAILS_SEND_NOTIFICATIONS);
35
36
  const [isAssociatesUpdating, setIsAssociatesUpdating] = useState(false);
36
37
  const { t } = useTranslation();
37
- const { accountNumber, caseNumber, contactSsoUsername, selectedNotificationContacts, selectedOwner, customEmailsList, selectedCaseGroupUsers, ownersCaseGroups, groupNumber, createdById, } = useCaseSelector((state) => ({
38
+ const { accountNumber, caseNumber, contactSsoUsername, selectedNotificationContacts, selectedOwner, customEmailsList, product, selectedCaseGroupUsers, ownersCaseGroups, groupNumber, createdById, version, } = useCaseSelector((state) => ({
38
39
  accountNumber: state.caseDetails.accountNumberRef,
39
40
  caseNumber: state.caseDetails.caseNumber,
40
41
  contactSsoUsername: state.caseDetails.contactSSOName,
@@ -45,6 +46,8 @@ function CaseContactSelector(props) {
45
46
  groupNumber: state.caseDetails.groupNumber,
46
47
  ownersCaseGroups: state.ownersCaseGroups,
47
48
  createdById: state.caseDetails.createdById,
49
+ product: state.caseDetails.product,
50
+ version: state.caseDetails.version,
48
51
  }), isEqual);
49
52
  const caseDispatch = useCaseDispatch();
50
53
  const { addCustomEmail, canAddCustomEmail, hideCustomEmails, deleteCustomEmail, isUpdatingCustomEmails, isEmailValid, showAddEmailToAccountModal, } = useCustomEmails({
@@ -185,7 +188,7 @@ function CaseContactSelector(props) {
185
188
  }
186
189
  return hasGroupAccess;
187
190
  });
188
- const onNotifiedUserChange = (...args_1) => __awaiter(this, [...args_1], void 0, function* (selectedContacts = []) {
191
+ const onNotifiedUserChange = (...args_1) => __awaiter(this, [...args_1], void 0, function* (selectedContacts = [], throwTrackEvent = false) {
189
192
  // check if selection changed
190
193
  const alreadySelected = [
191
194
  ...customEmailsList.data,
@@ -199,6 +202,9 @@ function CaseContactSelector(props) {
199
202
  const selectedCustomEmails = remove(allSelectedContacts, (item) => item.customOption || item.emailAddress).map((item) => item.label || item.emailAddress || item.ssoUsername);
200
203
  yield processCustomEmails(selectedCustomEmails);
201
204
  yield processCaseContacts(allSelectedContacts);
205
+ if (throwTrackEvent) {
206
+ dtmTrackEventCaseStepEncountered('follow', caseNumber, product, version);
207
+ }
202
208
  });
203
209
  /** Reset all non-internal selectedNotificationContacts when selected account number changes */
204
210
  useEffect(() => {
@@ -214,9 +220,9 @@ function CaseContactSelector(props) {
214
220
  !isCurrentUserSelectedExternalNotifiedUser();
215
221
  const showRemoveWatchButton = () => loggedInUserRights.data.isExternal() &&
216
222
  (isCurrentUserSelectedExternalNotifiedUser() || isCurrentUserCaseContact);
217
- const addCurrentUser = () => {
218
- onNotifiedUserChange([...selectedNotificationContacts, ...customEmailsList.data, loggedInUser.data]);
219
- };
223
+ const addCurrentUser = () => __awaiter(this, void 0, void 0, function* () {
224
+ onNotifiedUserChange([...selectedNotificationContacts, ...customEmailsList.data, loggedInUser.data], true);
225
+ });
220
226
  const removeCurrentUser = () => {
221
227
  if (isCurrentUserSelectedExternalNotifiedUser()) {
222
228
  onNotifiedUserChange([
@@ -1 +1 @@
1
- {"version":3,"file":"AsideResults.d.ts","sourceRoot":"","sources":["../../../../src/components/Recommendations/AsideResults.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAE7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AASvD,OAAO,EAAE,eAAe,EAAsC,MAAM,iCAAiC,CAAC;AAUtG,UAAU,MAAM;IACZ,UAAU,EAAE,mBAAmB,CAAC,eAAe,CAAC,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,qBA6LzC"}
1
+ {"version":3,"file":"AsideResults.d.ts","sourceRoot":"","sources":["../../../../src/components/Recommendations/AsideResults.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAE7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AASvD,OAAO,EAAE,eAAe,EAAsC,MAAM,iCAAiC,CAAC;AAUtG,UAAU,MAAM;IACZ,UAAU,EAAE,mBAAmB,CAAC,eAAe,CAAC,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,qBA0MzC"}
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { pcm, search } from '@cee-eng/hydrajs';
11
11
  import { useDebounce } from '@rh-support/components';
12
- import { computeRecommendationAbstract, computeRecommendationTitle, convertObjToEncodedQueryParams, getSolrParams, getTrimmedCharacters, PreviousCaseTypes, replaceHighlightingData, } from '@rh-support/utils';
12
+ import { computeRecommendationAbstract, computeRecommendationTitle, convertObjToEncodedQueryParams, dtmTrackEventRecommendationListingItemClicked, getSolrParams, getTrimmedCharacters, PreviousCaseTypes, replaceHighlightingData, } from '@rh-support/utils';
13
13
  import isEmpty from 'lodash/isEmpty';
14
14
  import isEqual from 'lodash/isEqual';
15
15
  import React, { useContext, useEffect, useRef } from 'react';
@@ -117,7 +117,9 @@ export function AsideResults(props) {
117
117
  const onResourceClick = (doc, index) => (event) => {
118
118
  if (isSelectedAccounntSecureSupport)
119
119
  return;
120
+ const rank = index + 1 + PAGE_SIZE * ((recommendationState.currentPage || 1) - 1);
120
121
  createOrUpdateSessionResources(sessionRestoreDispatch, activeSessionId, sessionResourceTracking, SessionResourceSource.RECOMMENDATIONS_ASIDE, [getSessResFromRec(doc, SessionResourceVisibility.VISITED, index + 1)], payload);
122
+ dtmTrackEventRecommendationListingItemClicked(window.location.href, activeSection, 'middle', doc.id, rank, doc.allTitle, doc.view_uri, 'Recommendation Aside', 'Live troubleshooting powered by OpenShift AI');
121
123
  };
122
124
  useEffect(() => {
123
125
  if (isSelectedAccounntSecureSupport)
@@ -1 +1 @@
1
- {"version":3,"file":"Recommendations.d.ts","sourceRoot":"","sources":["../../../../src/components/Recommendations/Recommendations.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAEvE,OAAO,EAAE,mBAAmB,EAAa,MAAM,kBAAkB,CAAC;AAelE,OAAO,EAAoB,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAWpF,UAAU,MAAM;IACZ,UAAU,EAAE,mBAAmB,CAAC,eAAe,CAAC,CAAC;IACjD,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IACzE,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,aAAa,EAAE,GAAG,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,KAAK,EAAE,MAAM,qBA6SpD"}
1
+ {"version":3,"file":"Recommendations.d.ts","sourceRoot":"","sources":["../../../../src/components/Recommendations/Recommendations.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAEvE,OAAO,EAAE,mBAAmB,EAAa,MAAM,kBAAkB,CAAC;AAelE,OAAO,EAAoB,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAWpF,UAAU,MAAM;IACZ,UAAU,EAAE,mBAAmB,CAAC,eAAe,CAAC,CAAC;IACjD,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IACzE,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,aAAa,EAAE,GAAG,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,KAAK,EAAE,MAAM,qBA+SpD"}
@@ -159,7 +159,7 @@ export default function Recommendations(props) {
159
159
  const onResourceClick = (doc, index) => (event) => {
160
160
  const rank = index + 1 + (pageSize || DEFAULTPAGESIZE) * ((recommendationState.currentPage || 1) - 1);
161
161
  createOrUpdateSessionResources(sessionRestoreDispatch, activeSessionId, sessionResourceTracking, SessionResourceSource.RECOMMENDATIONS, [getSessResFromRec(doc, SessionResourceVisibility.VISITED, rank)], JSON.stringify(payload));
162
- dtmTrackEventRecommendationListingItemClicked(window.location.href, activeSection, 'middle', doc.id, rank, doc.allTitle, doc.view_uri);
162
+ dtmTrackEventRecommendationListingItemClicked(window.location.href, activeSection, 'middle', doc.id, rank, doc.allTitle, doc.view_uri, 'Main Recommendation', 'Live search results powered by OpenShift AI');
163
163
  };
164
164
  return (React.createElement(React.Fragment, null,
165
165
  React.createElement(LoadingDots, { className: "recommendation-loading-dots", show: recommendationState.isLoadingRecommendations && !isEmpty(summary) && !isEmpty(issue) }),
@@ -1,6 +1,6 @@
1
1
  import { IDClassNameProps } from '@rh-support/types/shared';
2
2
  import React from 'react';
3
- interface IProps extends IDClassNameProps {
3
+ export interface IWidgetFileSelectorProps extends IDClassNameProps {
4
4
  isSessionId: boolean;
5
5
  isPrivate: boolean;
6
6
  isIdea?: boolean;
@@ -9,6 +9,6 @@ interface IProps extends IDClassNameProps {
9
9
  fileName: string[];
10
10
  };
11
11
  }
12
- declare function WidgetFileSelector({ id, className, isSessionId, isPrivate, isIdea, showFileExceed, }: IProps): React.JSX.Element;
12
+ declare function WidgetFileSelector({ id, className, isSessionId, isPrivate, isIdea, showFileExceed, }: IWidgetFileSelectorProps): React.JSX.Element;
13
13
  export default WidgetFileSelector;
14
14
  //# sourceMappingURL=WidgetFileSelector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetFileSelector.d.ts","sourceRoot":"","sources":["../../../../../../src/components/shared/fileUpload/fileSelectors/WidgetFileSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,OAAO,KAAqB,MAAM,OAAO,CAAC;AAO1C,UAAU,MAAO,SAAQ,gBAAgB;IACrC,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE;QACb,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;CACL;AAED,iBAAS,kBAAkB,CAAC,EACxB,EAAO,EACP,SAAc,EACd,WAAW,EACX,SAAiB,EACjB,MAAc,EACd,cAA8C,GACjD,EAAE,MAAM,qBAmER;AAED,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"WidgetFileSelector.d.ts","sourceRoot":"","sources":["../../../../../../src/components/shared/fileUpload/fileSelectors/WidgetFileSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAG5D,OAAO,KAAqB,MAAM,OAAO,CAAC;AAS1C,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAC9D,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE;QACb,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;CACL;AAED,iBAAS,kBAAkB,CAAC,EACxB,EAAO,EACP,SAAc,EACd,WAAW,EACX,SAAiB,EACjB,MAAc,EACd,cAA8C,GACjD,EAAE,wBAAwB,qBAsF1B;AAED,eAAe,kBAAkB,CAAC"}
@@ -1,11 +1,18 @@
1
- import { humanizeSize } from '@rh-support/utils';
1
+ import { dtmTrackEventUploadFileToAnalyze, humanizeSize } from '@rh-support/utils';
2
+ import isEqual from 'lodash/isEqual';
2
3
  import React, { useContext } from 'react';
3
4
  import { useDropzone } from 'react-dropzone';
4
5
  import { Trans, useTranslation } from 'react-i18next';
6
+ import { useCaseSelector } from '../../../../context/CaseContext';
7
+ import { RouteContext } from '../../../../context/RouteContext';
5
8
  import { onDrop } from '../reducer/AttachmentHelper';
6
9
  import { AttachmentDispatchContext } from '../reducer/AttachmentReducerContext';
7
10
  function WidgetFileSelector({ id = '', className = '', isSessionId, isPrivate = false, isIdea = false, showFileExceed = { show: false, fileName: [] }, }) {
8
11
  const { t } = useTranslation();
12
+ const { caseDetails } = useCaseSelector((state) => ({
13
+ caseDetails: state.caseDetails,
14
+ }), isEqual);
15
+ const { routeState: { activeSection, isCaseCreate }, } = useContext(RouteContext);
9
16
  const MAX_SIZE = isIdea ? 5242880 : undefined;
10
17
  // Attachment Reducer Related
11
18
  const dispatchToAttachmentReducer = useContext(AttachmentDispatchContext);
@@ -23,6 +30,8 @@ function WidgetFileSelector({ id = '', className = '', isSessionId, isPrivate =
23
30
  // react-dropzone doesn't get triggered. So we need to manually clear the input value
24
31
  // so that user can attach the same file twice
25
32
  inputRef.current.value = null;
33
+ // Call a dtm track even whenever the file uploader is clicked
34
+ dtmTrackEventUploadFileToAnalyze(isCaseCreate, activeSection, caseDetails.caseType, caseDetails.product, caseDetails.version);
26
35
  };
27
36
  return (React.createElement(React.Fragment, null,
28
37
  React.createElement("label", Object.assign({ className: "file-diag-dragndrop", htmlFor: "file-diag-selector" }, getRootProps({ onClick: onLabelClick }), { "data-tracking-id": "file-selector-widget-main" }),
@@ -784,3 +784,9 @@ div.case-details-tabs pre {
784
784
  .get-support-summary-input {
785
785
  margin-bottom: 1rem;
786
786
  }
787
+
788
+ #external-ssousername-filter {
789
+ @media (min-width: 1300px) {
790
+ min-width: calc(100% + 269px) !important;
791
+ }
792
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rh-support/troubleshoot",
3
- "version": "2.2.190",
3
+ "version": "2.2.192",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org"
@@ -60,11 +60,11 @@
60
60
  "@progress/kendo-licensing": "1.3.5",
61
61
  "@progress/kendo-react-pdf": "^5.16.0",
62
62
  "@redux-devtools/extension": "^3.3.0",
63
- "@rh-support/components": "2.1.91",
64
- "@rh-support/react-context": "2.1.100",
63
+ "@rh-support/components": "2.1.92",
64
+ "@rh-support/react-context": "2.1.101",
65
65
  "@rh-support/types": "2.0.5",
66
- "@rh-support/user-permissions": "2.1.56",
67
- "@rh-support/utils": "2.1.45",
66
+ "@rh-support/user-permissions": "2.1.57",
67
+ "@rh-support/utils": "2.1.46",
68
68
  "date-fns": "^4.1.0",
69
69
  "dompurify": "^2.2.6",
70
70
  "dot": "^1.1.3",
@@ -134,5 +134,5 @@
134
134
  "defaults and supports es6-module",
135
135
  "maintained node versions"
136
136
  ],
137
- "gitHead": "d867a78014ee4a08aa8d7bf9e0dc9bd08ce6ac5a"
137
+ "gitHead": "8402b82ce0dac6a288aa9361ebb61f4a209f90e0"
138
138
  }