@rh-support/troubleshoot 2.4.5-beta.1 → 2.4.5-beta.2

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 (80) hide show
  1. package/lib/esm/components/CaseEditView/CaseDetailsTabs.js +2 -2
  2. package/lib/esm/components/CaseEditView/CaseOverview/CaseType.d.ts.map +1 -1
  3. package/lib/esm/components/CaseEditView/CaseOverview/CaseType.js +4 -18
  4. package/lib/esm/components/CaseEditView/CaseOverview/index.d.ts.map +1 -1
  5. package/lib/esm/components/CaseEditView/CaseOverview/index.js +2 -2
  6. package/lib/esm/components/CaseEditView/CaseSolutions/CaseSolutions.d.ts.map +1 -1
  7. package/lib/esm/components/CaseEditView/CaseSolutions/CaseSolutions.js +2 -4
  8. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseInternalStatus.d.ts.map +1 -1
  9. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseInternalStatus.js +1 -2
  10. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.d.ts.map +1 -1
  11. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.js +4 -6
  12. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CommentSearch.d.ts.map +1 -1
  13. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CommentSearch.js +2 -3
  14. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.d.ts.map +1 -1
  15. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.js +1 -4
  16. package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.css +21 -95
  17. package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.d.ts.map +1 -1
  18. package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.js +137 -269
  19. package/lib/esm/components/CaseInformation/Fts.d.ts.map +1 -1
  20. package/lib/esm/components/CaseInformation/Fts.js +5 -6
  21. package/lib/esm/components/CaseManagement/Cep.d.ts.map +1 -1
  22. package/lib/esm/components/CaseManagement/Cep.js +2 -3
  23. package/lib/esm/components/CaseManagement/RHAssociatesSelector.d.ts.map +1 -1
  24. package/lib/esm/components/CaseManagement/RHAssociatesSelector.js +5 -11
  25. package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.d.ts.map +1 -1
  26. package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.js +5 -11
  27. package/lib/esm/components/Cve/CveItem.d.ts +8 -0
  28. package/lib/esm/components/Cve/CveItem.d.ts.map +1 -0
  29. package/lib/esm/components/Cve/CveItem.js +81 -0
  30. package/lib/esm/components/Cve/CveModal.d.ts +3 -0
  31. package/lib/esm/components/Cve/CveModal.d.ts.map +1 -0
  32. package/lib/esm/components/Cve/CveModal.js +40 -0
  33. package/lib/esm/components/Cve/CvePanel.d.ts +7 -0
  34. package/lib/esm/components/Cve/CvePanel.d.ts.map +1 -0
  35. package/lib/esm/components/Cve/CvePanel.js +23 -0
  36. package/lib/esm/components/Cve/CveSidebar.d.ts +3 -0
  37. package/lib/esm/components/Cve/CveSidebar.d.ts.map +1 -0
  38. package/lib/esm/components/Cve/CveSidebar.js +24 -0
  39. package/lib/esm/components/ProductSelector/ProductSelector.d.ts.map +1 -1
  40. package/lib/esm/components/ProductSelector/ProductSelector.js +4 -0
  41. package/lib/esm/components/Recommendations/AsideResults.d.ts.map +1 -1
  42. package/lib/esm/components/Recommendations/AsideResults.js +2 -4
  43. package/lib/esm/components/Recommendations/EARules/EARule.d.ts.map +1 -1
  44. package/lib/esm/components/Recommendations/EARules/EARule.js +2 -0
  45. package/lib/esm/components/Recommendations/EARules/EARuleInfoInline.d.ts.map +1 -1
  46. package/lib/esm/components/Recommendations/EARules/EARuleInfoInline.js +2 -2
  47. package/lib/esm/components/Recommendations/Recommendations.d.ts.map +1 -1
  48. package/lib/esm/components/Recommendations/Recommendations.js +22 -24
  49. package/lib/esm/components/Recommendations/RecommendationsLoader.d.ts +3 -0
  50. package/lib/esm/components/Recommendations/RecommendationsLoader.d.ts.map +1 -0
  51. package/lib/esm/components/Recommendations/RecommendationsLoader.js +11 -0
  52. package/lib/esm/components/SubmitCase/SubmitCase.js +3 -3
  53. package/lib/esm/components/Suggestions/TopContent.d.ts.map +1 -1
  54. package/lib/esm/components/Suggestions/TopContent.js +4 -1
  55. package/lib/esm/components/shared/fileUpload/fileSelectors/WidgetFileSelector.d.ts +2 -2
  56. package/lib/esm/components/shared/fileUpload/fileSelectors/WidgetFileSelector.d.ts.map +1 -1
  57. package/lib/esm/components/shared/fileUpload/fileSelectors/WidgetFileSelector.js +1 -10
  58. package/lib/esm/components/wizardLayout/WizardAside.d.ts.map +1 -1
  59. package/lib/esm/components/wizardLayout/WizardAside.js +5 -1
  60. package/lib/esm/components/wizardLayout/WizardLayout.d.ts.map +1 -1
  61. package/lib/esm/components/wizardLayout/WizardLayout.js +1 -4
  62. package/lib/esm/components/wizardLayout/WizardMain.d.ts.map +1 -1
  63. package/lib/esm/components/wizardLayout/WizardMain.js +0 -5
  64. package/lib/esm/components/wizardLayout/WizardNavigation.d.ts.map +1 -1
  65. package/lib/esm/components/wizardLayout/WizardNavigation.js +3 -7
  66. package/lib/esm/hooks/useFetchCVEData.d.ts +5 -0
  67. package/lib/esm/hooks/useFetchCVEData.d.ts.map +1 -0
  68. package/lib/esm/hooks/useFetchCVEData.js +101 -0
  69. package/lib/esm/models/caseCreationWorkflows.d.ts +25 -0
  70. package/lib/esm/models/caseCreationWorkflows.d.ts.map +1 -0
  71. package/lib/esm/models/caseCreationWorkflows.js +1 -0
  72. package/lib/esm/reducers/CaseConstNTypes.d.ts +3 -0
  73. package/lib/esm/reducers/CaseConstNTypes.d.ts.map +1 -1
  74. package/lib/esm/reducers/CaseConstNTypes.js +2 -0
  75. package/lib/esm/scss/_main.scss +20 -2
  76. package/lib/esm/scss/_pf-overrides.scss +0 -6
  77. package/lib/esm/utils/caseUtils.d.ts +6 -0
  78. package/lib/esm/utils/caseUtils.d.ts.map +1 -1
  79. package/lib/esm/utils/caseUtils.js +15 -0
  80. package/package.json +11 -11
@@ -9,14 +9,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import './Timeline.css';
11
11
  import { caseHistory } from '@cee-eng/hydrajs';
12
- import { Avatar, Button, DatePicker, EmptyState, EmptyStateBody, EmptyStateHeader, EmptyStateIcon, EmptyStateVariant, MenuToggle, Pagination, SearchInput, Select, SelectOption, Spinner, } from '@patternfly/react-core';
13
- import ExclamationCircleIcon from '@patternfly/react-icons/dist/js/icons/exclamation-circle-icon';
12
+ import { Avatar, Button, DatePicker, EmptyState, EmptyStateBody, EmptyStateHeader, EmptyStateIcon, EmptyStateVariant, MenuToggle, SearchInput, Select, SelectList, SelectOption, Spinner, } from '@patternfly/react-core';
14
13
  import SearchIcon from '@patternfly/react-icons/dist/js/icons/search-icon';
15
- import globalDangerColor100 from '@patternfly/react-tokens/dist/js/global_danger_color_100';
16
- import { format, fromUnixTime, isSameDay, parse } from 'date-fns';
14
+ import { PaginatedList } from '@rh-support/components';
17
15
  import debounce from 'lodash/debounce';
18
16
  import isEmpty from 'lodash/isEmpty';
19
- import React, { useEffect, useRef, useState } from 'react';
17
+ import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
20
18
  import { Trans, useTranslation } from 'react-i18next';
21
19
  const TimelineEvent = ({ date, text, side, user, useAvatar }) => {
22
20
  if (side === 'left') {
@@ -31,7 +29,7 @@ const TimelineEvent = ({ date, text, side, user, useAvatar }) => {
31
29
  React.createElement("div", { className: `contentTimeline content-${side}-history content-${side}-${user}` },
32
30
  React.createElement("p", { className: `content-time-${side}-history` }, text))));
33
31
  }
34
- return (React.createElement("div", { className: `${side}-node ${user}`, role: "complementary", "aria-label": `${user}'s timeline event content` },
32
+ return (React.createElement("div", { className: `${side}-node ${user}`, role: "complementary", "aria-label": `${user}'s timeline event content ` },
35
33
  React.createElement("div", { className: `contentTimeline content-${side}-history content-${side}-${user}` },
36
34
  React.createElement("h2", { className: `content-date-${side}-history` }, date),
37
35
  text && React.createElement("p", { className: `content-time-${side}-history` }, text))));
@@ -39,18 +37,14 @@ const TimelineEvent = ({ date, text, side, user, useAvatar }) => {
39
37
  const TimelineNode = React.forwardRef(({ leftEvent, rightEvent, user, useAvatar }, ref) => {
40
38
  return (React.createElement("div", { className: "timeline-node", role: "region", "aria-label": "Timeline node" },
41
39
  leftEvent && React.createElement(TimelineEvent, Object.assign({}, leftEvent, { side: "left", useAvatar: useAvatar })),
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}` }))),
40
+ React.createElement("div", { ref: ref, "aria-label": `${useAvatar ? 'Open user profile' : 'Marker'}`, tabIndex: 0 }, 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
41
  rightEvent && React.createElement(TimelineEvent, Object.assign({}, rightEvent, { side: "right" }))));
44
42
  });
45
- /**
46
- *
47
- * @param {Object} item - The item object to process.
48
- * @returns {string} The formatted text string.
49
- */
43
+ //Handling changes in case fields to be displayed to the user
50
44
  const applyReplacements = (item) => {
51
- if (!item || typeof item !== 'object')
45
+ if (!item || typeof item !== 'object') {
52
46
  return '';
53
- // API field names to easy to read display names
47
+ }
54
48
  const fieldNameMapping = {
55
49
  ContactId: 'Contact',
56
50
  RH_Product__c: 'Product',
@@ -61,171 +55,94 @@ const applyReplacements = (item) => {
61
55
  BusinessHoursId: 'Business Hours',
62
56
  FolderId__c: 'Case Group',
63
57
  Status: 'Status',
64
- Customer_Escalation__c: 'Customer Escalation',
65
- SlaStartDate: 'SLA Start Date',
66
- Case_Language__c: 'Case Language',
67
- Alternate_Id__c: 'Personal reference number',
68
- SBR_Group__c: 'SBR Group',
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
75
- const languageCodeMapping = {
76
- de: 'German',
77
- en: 'English',
78
- es: 'Spanish',
79
- fr: 'French',
80
- it: 'Italian',
81
- pt: 'Portuguese',
82
- ko: 'Korean',
83
- jp: 'Japanese',
84
- zh: 'Chinese',
85
58
  };
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',
59
+ let fieldName = fieldNameMapping[item.fieldName] || item.fieldLabel || item.fieldName || '';
60
+ const getNameValue = (prefix) => {
61
+ const keyMap = {
62
+ Product: 'product',
63
+ Version: 'version',
64
+ Account: 'account',
65
+ Contact: 'contact',
66
+ Entitlement: 'entitlement',
67
+ 'Business Hours': 'businessHours',
68
+ Owner: 'queueNameOwner',
69
+ };
70
+ const baseKey = keyMap[fieldName] || fieldName.replace(/\s/g, '');
71
+ const key = `${baseKey}${prefix}Name`;
72
+ return item[key] || item[`${prefix}Value`] || '';
100
73
  };
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
74
  const newValue = getNameValue('New');
115
75
  const oldValue = getNameValue('Old');
116
76
  let newText = '';
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;
77
+ if (item.fieldName === 'OwnerId' && item.createdByUserName) {
78
+ newText = `set ${fieldName} to ${item.createdByUserName}`;
79
+ }
80
+ else if (item.fieldName === 'Status') {
81
+ if (item.oldValue && item.newValue) {
82
+ newText = `changed ${fieldName} from ${item.oldValue} to ${item.newValue}`;
127
83
  }
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;
84
+ else if (item.newValue) {
85
+ newText = `set ${fieldName} to ${item.newValue}`;
86
+ }
87
+ }
88
+ else if (oldValue && newValue) {
89
+ newText = `changed ${fieldName} from ${oldValue} to ${newValue}`;
90
+ }
91
+ else if (newValue) {
92
+ newText = `set ${fieldName} to ${newValue}`;
184
93
  }
185
- if (typeof item.newValue === 'boolean')
186
- newText = formatBooleanChange(fieldName, item.newValue);
187
- if (typeof newText === 'string')
94
+ else if (item.newValue !== undefined) {
95
+ newText = `set ${fieldName} to ${item.newValue}`;
96
+ }
97
+ // Handle boolean values
98
+ if (typeof item.newValue === 'boolean') {
99
+ newText = `set ${fieldName} to ${item.newValue}`;
100
+ }
101
+ // Handle date values
102
+ if (item.fieldName === 'SlaStartDate' && item.newValue) {
103
+ newText = `set ${fieldName} to ${item.newValue}`;
104
+ }
105
+ // Replace 'Problem Statement' with 'Title'
106
+ if (typeof newText === 'string') {
188
107
  newText = newText.replace(/Problem Statement/g, 'Title');
108
+ }
189
109
  return newText;
190
110
  };
191
111
  // function to change the apiResponse
112
+ const specialTimelineData = (apiResponse) => {
113
+ return Object.assign(Object.assign({}, apiResponse), { historyItems: apiResponse.historyItems.map((item) => (Object.assign(Object.assign({}, item), { outputText: item.type === 'Change' ? applyReplacements(item) : item.outputText }))) });
114
+ };
192
115
  const transformApiResponseToTimelineData = (apiResponse) => {
193
116
  let previousUser = null;
194
- const timelineEvents = apiResponse.historyItems
195
- // Filter out 'Case Group' changes because we don't have decode of value
196
- .filter((item) => item.fieldName !== 'FolderId__c')
197
- .map((item, index) => {
117
+ const modifiedApiResponse = specialTimelineData(apiResponse);
118
+ const timelineEvents = modifiedApiResponse.historyItems.map((item, index) => {
198
119
  const currentUser = item.createdByUserName;
120
+ // If the current user is the same as the previous one, and it's not the first item, don't use an avatar. use !! cause previousUser start null.
199
121
  const useAvatar = index === 0 || (!!previousUser && currentUser !== previousUser);
122
+ // Update the previousUser for the next iteration
200
123
  previousUser = currentUser;
201
- let eventDate;
202
- if (typeof item.createdDate === 'string' && item.createdDate.includes('T')) {
203
- eventDate = parse(item.createdDate, "yyyy-MM-dd'T'HH:mm:ss'Z'", new Date());
204
- }
205
- else {
206
- eventDate = fromUnixTime(item.createdDate / 1000);
207
- }
124
+ const eventDate = new Date(item.createdDate);
125
+ const date = eventDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
126
+ const time = eventDate.toLocaleTimeString('en-US', {
127
+ hour: 'numeric',
128
+ minute: '2-digit',
129
+ hour12: true,
130
+ timeZoneName: 'short',
131
+ });
208
132
  const user = item.createdByCustomer !== undefined
209
- ? item.createdByCustomer
133
+ ? item.createdByCustomer //using new case history field to determine this
210
134
  ? 'customer'
211
135
  : 'internal'
212
- : currentUser === 'Case Diagnostics' || currentUser === 'GSS Tools'
136
+ : currentUser === 'Case Diagnostics' || currentUser === 'GSS Tools' //for now since it is not on prod yet we have this same fallback
213
137
  ? 'internal'
214
138
  : 'customer';
215
139
  const leftEvent = {
216
- date: format(eventDate, 'MMMM d, yyyy'),
217
- text: format(eventDate, 'h:mm a zzz'),
140
+ date: date,
141
+ text: time,
218
142
  };
219
- let outputText = item.outputText;
220
- if (item.type === 'Change') {
221
- outputText = applyReplacements(item);
222
- }
223
- else {
224
- outputText = stripHTML(outputText);
225
- }
226
143
  const rightEvent = {
227
- date: `${item.createdByUserName} ${outputText}`,
228
- text: '',
144
+ date: `${item.createdByUserName} ${stripHTML(item.outputText)}`, //will change name later from date, for now date is title and text stays text due to initial set up mimicking same ITimelineEvent
145
+ text: '', //future iterations of API make this the 'show more' field
229
146
  user: user,
230
147
  useAvatar: useAvatar,
231
148
  };
@@ -244,45 +161,27 @@ function stripHTML(htmlString) {
244
161
  const Timeline = ({ caseNumber }) => {
245
162
  const { t } = useTranslation();
246
163
  const [timelineEvents, setTimelineEvents] = useState([]);
247
- const [originalTimelineEvents, setOriginalTimelineEvents] = useState([]);
248
164
  const [isFetchingData, setIsFetchingData] = useState();
165
+ const [paginatedListLength, setPaginatedListLength] = useState();
249
166
  const lastNodeRef = useRef(null);
250
- const [dateValue, setDateValue] = useState('');
167
+ const [dateValue, setDateValue] = React.useState('');
251
168
  const [searchValue, setSearchValue] = useState('');
252
169
  const [filteredEvents, setFilteredEvents] = useState([]);
253
170
  const [isSelectOpen, setIsSelectOpen] = useState(false);
254
171
  const [selectedOrder, setSelectedOrder] = useState('Newest to Oldest');
255
- const [fetchError, setFetchError] = useState(null);
256
- const [page, setPage] = useState(1);
257
- const [perPage, setPerPage] = useState(20);
258
- const onSetPage = (_, newPage) => {
259
- setPage(newPage);
260
- };
261
- const onPerPageSelect = (_, newPerPage) => {
262
- setPerPage(newPerPage);
263
- setPage(1);
264
- };
265
- const renderPagination = () => (React.createElement(Pagination, { itemCount: filteredEvents.length, perPage: perPage, page: page, onSetPage: onSetPage, onPerPageSelect: onPerPageSelect, perPageOptions: [
266
- { title: '5', value: 5 },
267
- { title: '10', value: 10 },
268
- { title: '15', value: 15 },
269
- { title: '20', value: 20 },
270
- ], isCompact: true, variant: "top" }));
271
- const handleSelectToggle = (e) => {
272
- e.preventDefault();
172
+ const handleSelectToggle = () => {
273
173
  setIsSelectOpen(!isSelectOpen);
274
174
  };
275
- const handleSelect = (e, value) => {
276
- e.preventDefault();
277
- if (value !== selectedOrder) {
278
- setSelectedOrder(value);
279
- reorderTimelineEvents(value);
280
- }
175
+ const handleSelect = (_event, value) => {
176
+ setSelectedOrder(value);
281
177
  setIsSelectOpen(false);
178
+ reorderTimelineEvents(value);
282
179
  };
283
180
  const reorderTimelineEvents = (order) => {
284
- const events = order === 'Newest to Oldest' ? [...originalTimelineEvents] : [...originalTimelineEvents].reverse();
285
- setTimelineEvents(applyAvatarLogic(events));
181
+ setTimelineEvents((prevEvents) => {
182
+ const reversedEvents = [...prevEvents].reverse();
183
+ return applyAvatarLogic(reversedEvents);
184
+ });
286
185
  };
287
186
  const applyAvatarLogic = (events) => {
288
187
  let previousUser = null;
@@ -297,7 +196,6 @@ const Timeline = ({ caseNumber }) => {
297
196
  function fetchHistory() {
298
197
  return __awaiter(this, void 0, void 0, function* () {
299
198
  setIsFetchingData(true);
300
- setFetchError(null);
301
199
  const options = {
302
200
  sortField: 'createdDate',
303
201
  sortOrder: 'DESC',
@@ -307,11 +205,9 @@ const Timeline = ({ caseNumber }) => {
307
205
  const response = yield caseHistory.getHistoryv1(caseNumber, options);
308
206
  const transformedData = transformApiResponseToTimelineData(response);
309
207
  setTimelineEvents(transformedData);
310
- setOriginalTimelineEvents(transformedData);
311
208
  }
312
209
  catch (error) {
313
210
  console.error('Failed to fetch history:', error);
314
- setFetchError('Unable to connect');
315
211
  }
316
212
  finally {
317
213
  setIsFetchingData(false);
@@ -319,51 +215,27 @@ const Timeline = ({ caseNumber }) => {
319
215
  });
320
216
  }
321
217
  fetchHistory();
218
+ // eslint-disable-next-line react-hooks/exhaustive-deps
322
219
  }, [caseNumber]);
323
- const futureDateValidator = (date) => {
324
- const today = new Date();
325
- today.setHours(0, 0, 0, 0); // Normalize to midnight
326
- if (date > today) {
327
- return 'Date cannot be in the future.';
328
- }
329
- return '';
330
- };
331
- const updateLineStyle = () => {
332
- const timelineElement = document.querySelector('.timeline');
333
- if (timelineElement) {
334
- if (lastNodeRef.current) {
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`);
343
- }
344
- else {
345
- timelineElement.style.setProperty('--timeline-bottom', '0px');
346
- }
347
- }
348
- };
349
220
  //Using useLayoutEffect because we are measuring styles based on DOM, useLayoutEffect runs after DOM Mutations https://react.dev/reference/react/useLayoutEffect
350
- useEffect(() => {
351
- const debouncedUpdateLineStyle = debounce(updateLineStyle, 100);
352
- window.addEventListener('resize', debouncedUpdateLineStyle);
353
- if (!isFetchingData && filteredEvents.length > 0) {
354
- requestAnimationFrame(() => {
355
- updateLineStyle();
356
- });
357
- }
358
- else {
359
- // No events or still fetching, set --timeline-bottom to 0px
360
- const timelineElement = document.querySelector('.timeline');
361
- if (timelineElement) {
362
- timelineElement.style.setProperty('--timeline-bottom', '0px');
221
+ useLayoutEffect(() => {
222
+ const updateLineStyle = debounce(() => {
223
+ var _a;
224
+ if (lastNodeRef.current) {
225
+ const timelineBottom = lastNodeRef.current.getBoundingClientRect().bottom + window.scrollY;
226
+ const timelineElement = document.querySelector('.timeline');
227
+ const avatarHeight = ((_a = lastNodeRef.current.querySelector('.timeline-avatar')) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
228
+ if (timelineElement) {
229
+ // Get the timeline element's position relative to the document
230
+ const timelineTop = timelineElement.getBoundingClientRect().top + window.scrollY;
231
+ // Calculate value relative to the timeline's top must be adjusted for page scroll or we get random bottom value based on where user scrolled before execution
232
+ const calculatedBottomValue = `${timelineTop + timelineElement.offsetHeight - timelineBottom + avatarHeight}px`;
233
+ timelineElement.style.setProperty('--timeline-bottom', calculatedBottomValue);
234
+ }
363
235
  }
364
- }
365
- return () => window.removeEventListener('resize', debouncedUpdateLineStyle);
366
- }, [isFetchingData, filteredEvents, page, perPage]);
236
+ }, 100);
237
+ updateLineStyle();
238
+ }, [timelineEvents, paginatedListLength, dateValue]);
367
239
  const handleSearchChange = (value) => {
368
240
  setSearchValue(value.target.value);
369
241
  };
@@ -379,64 +251,60 @@ const Timeline = ({ caseNumber }) => {
379
251
  const lowercasedFilter = searchValue.toLowerCase();
380
252
  filteredBySearch = filteredBySearch.filter(({ left, right }) => {
381
253
  const dateString = left.date.toLowerCase();
382
- const timeString = left.text.toLowerCase();
383
- const rightDateString = right.date.toLowerCase();
384
254
  return (dateString.includes(lowercasedFilter) ||
385
- timeString.includes(lowercasedFilter) ||
386
- rightDateString.includes(lowercasedFilter));
255
+ left.text.toLocaleLowerCase().includes(lowercasedFilter) ||
256
+ right.date.toLowerCase().includes(lowercasedFilter));
387
257
  });
388
258
  }
389
259
  let finalFilteredEvents = filteredBySearch;
390
260
  if (dateValue) {
391
261
  finalFilteredEvents = filteredBySearch.filter(({ left }) => {
392
- const eventDate = parse(left.date, 'MMMM d, yyyy', new Date());
393
- const selectedDate = parse(dateValue, 'yyyy-MM-dd', new Date());
394
- return isSameDay(eventDate, selectedDate);
262
+ // Format the event date to match the DatePicker format (YYYY-MM-DD) We need to do it this way so we dont have discrepancy (off by one month when selecting) due to the way that JS handles timezones
263
+ const eventDateFormatted = new Date(left.date).toISOString().split('T')[0];
264
+ // Compare the formatted event date with the selected date from DatePicker
265
+ return eventDateFormatted === dateValue;
395
266
  });
396
267
  }
397
268
  setFilteredEvents(finalFilteredEvents);
398
- setPage(1);
399
269
  }, [searchValue, dateValue, timelineEvents]);
400
- const handleReload = (e) => {
401
- e.preventDefault();
402
- window.location.href = window.location.href;
403
- window.location.reload();
404
- };
405
- const renderTimelineContent = () => {
406
- if (fetchError) {
407
- return (React.createElement(EmptyState, { variant: EmptyStateVariant.full },
408
- React.createElement(EmptyStateHeader, { titleText: "Unable to connect", icon: React.createElement(EmptyStateIcon, { icon: ExclamationCircleIcon, color: globalDangerColor100.value }), headingLevel: "h2" }),
409
- React.createElement(EmptyStateBody, null,
410
- React.createElement(Trans, null,
411
- "Try ",
412
- React.createElement("a", { href: "#", onClick: handleReload }, "reloading the page"),
413
- ' ',
414
- "or check back later"))));
415
- }
416
- if (filteredEvents.length < 1) {
417
- return (React.createElement(EmptyState, { variant: EmptyStateVariant.full },
270
+ if (filteredEvents.length < 1 && !isFetchingData) {
271
+ return (React.createElement("div", { key: "no-results" },
272
+ React.createElement("div", { className: "timelineMenu" },
273
+ React.createElement(SearchInput, { placeholder: t('Search for a user, action, or keyword'), value: searchValue, onChange: handleSearchChange, onClear: handleClear, className: "case-history-timeline-search" }),
274
+ React.createElement("div", { className: "case-history-timeline-datepicker" },
275
+ React.createElement(DatePicker, { value: dateValue, onChange: (_event, value) => handleDateChange(value) }),
276
+ React.createElement(Button, { onClick: () => setDateValue(''), isDisabled: isEmpty(dateValue) }, "Reset"))),
277
+ React.createElement("div", { className: "timeline-sort-order-select" },
278
+ React.createElement(Select, { id: "order-select", isOpen: isSelectOpen, selected: selectedOrder, onSelect: handleSelect, onOpenChange: (isOpen) => setIsSelectOpen(isOpen), toggle: (toggleRef) => (React.createElement(MenuToggle, { ref: toggleRef, onClick: handleSelectToggle, isExpanded: isSelectOpen, style: { width: '200px' } }, selectedOrder)), shouldFocusToggleOnSelect: true },
279
+ React.createElement(SelectList, null,
280
+ React.createElement(SelectOption, { value: "Newest to Oldest" }, "Newest to Oldest"),
281
+ React.createElement(SelectOption, { value: "Oldest to Newest" }, "Oldest to Newest")))),
282
+ React.createElement(EmptyState, { variant: EmptyStateVariant.full },
418
283
  React.createElement(EmptyStateHeader, { titleText: React.createElement(Trans, null, "No results found"), icon: React.createElement(EmptyStateIcon, { icon: SearchIcon }), headingLevel: "h2" }),
419
284
  React.createElement(EmptyStateBody, null,
420
- React.createElement(Trans, null, "Try modifying your search query or changing the date range and try again."))));
421
- }
422
- const paginatedEvents = filteredEvents.slice((page - 1) * perPage, page * perPage);
423
- return (React.createElement(React.Fragment, null, paginatedEvents.map((node, index) => (React.createElement(TimelineNode, { key: index, leftEvent: node.left, rightEvent: node.right, user: node.right.user, useAvatar: node.right.useAvatar, ref: index === paginatedEvents.length - 1 ? lastNodeRef : null })))));
424
- };
285
+ React.createElement(Trans, null, "Try modifying your search query or changing the date range and try again.")))));
286
+ }
425
287
  return (React.createElement(React.Fragment, null,
426
288
  React.createElement("div", { className: "timelineMenu" },
427
- React.createElement(SearchInput, { placeholder: t('Search for a user, action, or keyword'), value: searchValue, onChange: handleSearchChange, onClear: handleClear, className: "case-history-timeline-search" }),
289
+ React.createElement(SearchInput, { placeholder: "Search for a user, action, or keyword", value: searchValue, onChange: handleSearchChange, onClear: handleClear, className: "case-history-timeline-search" }),
428
290
  React.createElement("div", { className: "case-history-timeline-datepicker" },
429
- React.createElement(DatePicker, { value: dateValue, onChange: (_event, value) => handleDateChange(value), validators: [futureDateValidator] }),
291
+ React.createElement(DatePicker, { value: dateValue, onChange: (_event, value) => handleDateChange(value) }),
430
292
  React.createElement(Button, { onClick: () => setDateValue(''), isDisabled: isEmpty(dateValue) }, "Reset"))),
431
- React.createElement("div", { className: "timeline-controls" },
432
- React.createElement("div", { className: "timeline-pagination" }, renderPagination()),
433
- React.createElement("div", { className: "timeline-sort-order-select" },
434
- React.createElement(Select, { id: "order-select", isOpen: isSelectOpen, selected: selectedOrder, onSelect: handleSelect, onOpenChange: (isOpen) => setIsSelectOpen(isOpen), shouldFocusToggleOnSelect: false, toggle: (toggleRef) => (React.createElement(MenuToggle, { ref: toggleRef, onClick: handleSelectToggle, isExpanded: isSelectOpen, style: { width: '200px' } }, selectedOrder)) },
293
+ React.createElement("div", { className: "timeline-sort-order-select" },
294
+ React.createElement(Select, { id: "order-select", isOpen: isSelectOpen, selected: selectedOrder, onSelect: handleSelect, onOpenChange: (isOpen) => setIsSelectOpen(isOpen), toggle: (toggleRef) => (React.createElement(MenuToggle, { ref: toggleRef, onClick: handleSelectToggle, isExpanded: isSelectOpen, style: { width: '200px' } }, selectedOrder)), shouldFocusToggleOnSelect: true },
295
+ React.createElement(SelectList, null,
435
296
  React.createElement(SelectOption, { value: "Newest to Oldest" }, "Newest to Oldest"),
436
297
  React.createElement(SelectOption, { value: "Oldest to Newest" }, "Oldest to Newest")))),
437
- isFetchingData ? (React.createElement("div", { className: "timeline-loading-spinner" },
438
- React.createElement(Spinner, { size: "xl" }))) : (React.createElement("div", { className: `timeline ${filteredEvents.length === 0 ? 'timeline-empty' : ''}` }, renderTimelineContent())),
439
- React.createElement("div", { className: "timeline-controls" },
440
- React.createElement("div", { className: "timeline-pagination" }, renderPagination()))));
298
+ React.createElement("div", { className: "timeline" },
299
+ isFetchingData && (React.createElement("div", { className: "timeline-loading-spinner" },
300
+ React.createElement(Spinner, { size: "xl" }))),
301
+ !isFetchingData && (React.createElement(PaginatedList, { id: "case-history-paginated-timeline", listItems: filteredEvents &&
302
+ filteredEvents.map((node, index) => (React.createElement(TimelineNode, { key: index, leftEvent: node.left, rightEvent: node.right, user: node.right.user, useAvatar: node.right.useAvatar, ref: index === (paginatedListLength && paginatedListLength - 1) ? lastNodeRef : null //this ref is needed to perform calculations on the final node height
303
+ }))), perPage: 20, perPageOptions: [
304
+ { title: '5', value: 5 },
305
+ { title: '10', value: 10 },
306
+ { title: '15', value: 15 },
307
+ { title: '20', value: 20 },
308
+ ], setPaginatedListLength: setPaginatedListLength })))));
441
309
  };
442
310
  export default Timeline;
@@ -1 +1 @@
1
- {"version":3,"file":"Fts.d.ts","sourceRoot":"","sources":["../../../../src/components/CaseInformation/Fts.tsx"],"names":[],"mappings":"AAWA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAS/D,UAAU,MAAM;IACZ,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAQD,iBAAS,GAAG,CAAC,KAAK,EAAE,MAAM,qBA6OzB;kBA7OQ,GAAG;;;AAgPZ,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"Fts.d.ts","sourceRoot":"","sources":["../../../../src/components/CaseInformation/Fts.tsx"],"names":[],"mappings":"AAWA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAS/D,UAAU,MAAM;IACZ,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAQD,iBAAS,GAAG,CAAC,KAAK,EAAE,MAAM,qBA4OzB;kBA5OQ,GAAG;;;AA+OZ,eAAe,GAAG,CAAC"}
@@ -100,12 +100,10 @@ function Fts(props) {
100
100
  setCaseState(caseDispatch, { ftsContactSameAsOwner: e.target.checked });
101
101
  if (!e.target.checked) {
102
102
  setFtsContact('');
103
- setContactInfoIntoGlobalState('');
104
- }
105
- else {
106
- setFtsContact(`${phoneCountryCode}-${phoneAreaCodePrefixLineNumber}`);
107
- setContactInfoIntoGlobalState(`${phoneCountryCode}-${phoneAreaCodePrefixLineNumber}`);
103
+ return;
108
104
  }
105
+ setFtsContact(`${phoneCountryCode}-${phoneAreaCodePrefixLineNumber}`);
106
+ setContactInfoIntoGlobalState(`${phoneCountryCode}-${phoneAreaCodePrefixLineNumber}`);
109
107
  };
110
108
  const onSave = (e) => __awaiter(this, void 0, void 0, function* () {
111
109
  setContactInfoIntoGlobalState(contactInfo24X7State);
@@ -150,7 +148,8 @@ function Fts(props) {
150
148
  }
151
149
  // eslint-disable-next-line react-hooks/exhaustive-deps
152
150
  }, [phoneAreaCodePrefixLineNumber, phoneCountryCode]);
153
- const disableFtsNContact = !isEmpty(caseNumber) && (!isFtsEditableOnEditPage(entitlementSla, severity) || isFtsUpdating);
151
+ const disableFtsNContact = !isEmpty(caseNumber) &&
152
+ (!isFtsEditableOnEditPage(entitlementSla, severity) || isFtsContactUpdating || isFtsUpdating);
154
153
  const maxLengthErrorMessage = t('Contact information cannot be more than {{limit}} characters.', {
155
154
  limit: CONTACT_INFO_24X7_LIMIT,
156
155
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Cep.d.ts","sourceRoot":"","sources":["../../../../src/components/CaseManagement/Cep.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAoB/D,UAAU,MAAM;CAAG;AAEnB,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,qBAuThC"}
1
+ {"version":3,"file":"Cep.d.ts","sourceRoot":"","sources":["../../../../src/components/CaseManagement/Cep.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAoB/D,UAAU,MAAM;CAAG;AAEnB,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,qBAsThC"}
@@ -66,9 +66,8 @@ export function Cep(props) {
66
66
  // edit mode - set cep to false
67
67
  yield confirm({
68
68
  catchOnCancel: true,
69
- title: t(`No consultant engaged?`),
70
- description: t("You're confirming that you don't have a consultant engaged on this ticket."),
71
- confirmText: t('Yes, confirm'),
69
+ title: t(`Updating Consultant Engagement in Progress`),
70
+ description: t('Are you sure?'),
72
71
  });
73
72
  cepUpdate({ cep: false });
74
73
  clearCepFormInputs();
@@ -1 +1 @@
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"}
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"}