@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.
- package/lib/esm/components/CaseEditView/CaseDetailsTabs.js +2 -2
- package/lib/esm/components/CaseEditView/CaseOverview/CaseType.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/CaseOverview/CaseType.js +4 -18
- package/lib/esm/components/CaseEditView/CaseOverview/index.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/CaseOverview/index.js +2 -2
- package/lib/esm/components/CaseEditView/CaseSolutions/CaseSolutions.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/CaseSolutions/CaseSolutions.js +2 -4
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseInternalStatus.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseInternalStatus.js +1 -2
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.js +4 -6
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CommentSearch.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CommentSearch.js +2 -3
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.js +1 -4
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.css +21 -95
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.js +137 -269
- package/lib/esm/components/CaseInformation/Fts.d.ts.map +1 -1
- package/lib/esm/components/CaseInformation/Fts.js +5 -6
- package/lib/esm/components/CaseManagement/Cep.d.ts.map +1 -1
- package/lib/esm/components/CaseManagement/Cep.js +2 -3
- package/lib/esm/components/CaseManagement/RHAssociatesSelector.d.ts.map +1 -1
- package/lib/esm/components/CaseManagement/RHAssociatesSelector.js +5 -11
- package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.d.ts.map +1 -1
- package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.js +5 -11
- package/lib/esm/components/Cve/CveItem.d.ts +8 -0
- package/lib/esm/components/Cve/CveItem.d.ts.map +1 -0
- package/lib/esm/components/Cve/CveItem.js +81 -0
- package/lib/esm/components/Cve/CveModal.d.ts +3 -0
- package/lib/esm/components/Cve/CveModal.d.ts.map +1 -0
- package/lib/esm/components/Cve/CveModal.js +40 -0
- package/lib/esm/components/Cve/CvePanel.d.ts +7 -0
- package/lib/esm/components/Cve/CvePanel.d.ts.map +1 -0
- package/lib/esm/components/Cve/CvePanel.js +23 -0
- package/lib/esm/components/Cve/CveSidebar.d.ts +3 -0
- package/lib/esm/components/Cve/CveSidebar.d.ts.map +1 -0
- package/lib/esm/components/Cve/CveSidebar.js +24 -0
- package/lib/esm/components/ProductSelector/ProductSelector.d.ts.map +1 -1
- package/lib/esm/components/ProductSelector/ProductSelector.js +4 -0
- package/lib/esm/components/Recommendations/AsideResults.d.ts.map +1 -1
- package/lib/esm/components/Recommendations/AsideResults.js +2 -4
- package/lib/esm/components/Recommendations/EARules/EARule.d.ts.map +1 -1
- package/lib/esm/components/Recommendations/EARules/EARule.js +2 -0
- package/lib/esm/components/Recommendations/EARules/EARuleInfoInline.d.ts.map +1 -1
- package/lib/esm/components/Recommendations/EARules/EARuleInfoInline.js +2 -2
- package/lib/esm/components/Recommendations/Recommendations.d.ts.map +1 -1
- package/lib/esm/components/Recommendations/Recommendations.js +22 -24
- package/lib/esm/components/Recommendations/RecommendationsLoader.d.ts +3 -0
- package/lib/esm/components/Recommendations/RecommendationsLoader.d.ts.map +1 -0
- package/lib/esm/components/Recommendations/RecommendationsLoader.js +11 -0
- package/lib/esm/components/SubmitCase/SubmitCase.js +3 -3
- package/lib/esm/components/Suggestions/TopContent.d.ts.map +1 -1
- package/lib/esm/components/Suggestions/TopContent.js +4 -1
- package/lib/esm/components/shared/fileUpload/fileSelectors/WidgetFileSelector.d.ts +2 -2
- package/lib/esm/components/shared/fileUpload/fileSelectors/WidgetFileSelector.d.ts.map +1 -1
- package/lib/esm/components/shared/fileUpload/fileSelectors/WidgetFileSelector.js +1 -10
- package/lib/esm/components/wizardLayout/WizardAside.d.ts.map +1 -1
- package/lib/esm/components/wizardLayout/WizardAside.js +5 -1
- package/lib/esm/components/wizardLayout/WizardLayout.d.ts.map +1 -1
- package/lib/esm/components/wizardLayout/WizardLayout.js +1 -4
- package/lib/esm/components/wizardLayout/WizardMain.d.ts.map +1 -1
- package/lib/esm/components/wizardLayout/WizardMain.js +0 -5
- package/lib/esm/components/wizardLayout/WizardNavigation.d.ts.map +1 -1
- package/lib/esm/components/wizardLayout/WizardNavigation.js +3 -7
- package/lib/esm/hooks/useFetchCVEData.d.ts +5 -0
- package/lib/esm/hooks/useFetchCVEData.d.ts.map +1 -0
- package/lib/esm/hooks/useFetchCVEData.js +101 -0
- package/lib/esm/models/caseCreationWorkflows.d.ts +25 -0
- package/lib/esm/models/caseCreationWorkflows.d.ts.map +1 -0
- package/lib/esm/models/caseCreationWorkflows.js +1 -0
- package/lib/esm/reducers/CaseConstNTypes.d.ts +3 -0
- package/lib/esm/reducers/CaseConstNTypes.d.ts.map +1 -1
- package/lib/esm/reducers/CaseConstNTypes.js +2 -0
- package/lib/esm/scss/_main.scss +20 -2
- package/lib/esm/scss/_pf-overrides.scss +0 -6
- package/lib/esm/utils/caseUtils.d.ts +6 -0
- package/lib/esm/utils/caseUtils.d.ts.map +1 -1
- package/lib/esm/utils/caseUtils.js +15 -0
- 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,
|
|
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
|
|
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 ? '
|
|
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
|
-
|
|
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
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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 (
|
|
186
|
-
newText =
|
|
187
|
-
|
|
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
|
|
195
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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:
|
|
217
|
-
text:
|
|
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
|
|
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 = (
|
|
276
|
-
|
|
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
|
-
|
|
285
|
-
|
|
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
|
-
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
366
|
-
}, [
|
|
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
|
-
|
|
386
|
-
|
|
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
|
-
|
|
393
|
-
const
|
|
394
|
-
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
React.createElement(
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
"
|
|
412
|
-
|
|
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:
|
|
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)
|
|
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-
|
|
432
|
-
React.createElement(
|
|
433
|
-
|
|
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
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
React.createElement(
|
|
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,
|
|
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
|
-
|
|
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) &&
|
|
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,
|
|
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(`
|
|
70
|
-
description: t(
|
|
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":"
|
|
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"}
|