@rh-support/troubleshoot 2.2.1851-beta.0 → 2.4.5-beta.1
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 +18 -4
- 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 +4 -2
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseInternalStatus.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDetails/CaseInternalStatus.js +2 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseDiscussion.js +6 -4
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CommentSearch.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CommentSearch.js +3 -2
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/PostComment.js +4 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.css +95 -21
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.d.ts.map +1 -1
- package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.js +269 -137
- package/lib/esm/components/CaseInformation/Fts.d.ts.map +1 -1
- package/lib/esm/components/CaseInformation/Fts.js +6 -5
- package/lib/esm/components/CaseManagement/Cep.d.ts.map +1 -1
- package/lib/esm/components/CaseManagement/Cep.js +3 -2
- package/lib/esm/components/CaseManagement/RHAssociatesSelector.d.ts.map +1 -1
- package/lib/esm/components/CaseManagement/RHAssociatesSelector.js +11 -5
- package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.d.ts.map +1 -1
- package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.js +11 -5
- package/lib/esm/components/Recommendations/AsideResults.d.ts.map +1 -1
- package/lib/esm/components/Recommendations/AsideResults.js +3 -1
- package/lib/esm/components/Recommendations/EARules/EARule.d.ts.map +1 -1
- package/lib/esm/components/Recommendations/EARules/EARule.js +0 -2
- 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 +16 -5
- 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 +1 -4
- 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 +10 -1
- package/lib/esm/components/wizardLayout/WizardLayout.d.ts.map +1 -1
- package/lib/esm/components/wizardLayout/WizardLayout.js +4 -1
- package/lib/esm/components/wizardLayout/WizardMain.d.ts.map +1 -1
- package/lib/esm/components/wizardLayout/WizardMain.js +5 -0
- package/lib/esm/components/wizardLayout/WizardNavigation.d.ts.map +1 -1
- package/lib/esm/components/wizardLayout/WizardNavigation.js +7 -3
- package/lib/esm/scss/_main.scss +2 -1
- package/lib/esm/scss/_pf-overrides.scss +6 -0
- package/package.json +11 -10
|
@@ -9,12 +9,14 @@ 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, SearchInput, Select,
|
|
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';
|
|
13
14
|
import SearchIcon from '@patternfly/react-icons/dist/js/icons/search-icon';
|
|
14
|
-
import
|
|
15
|
+
import globalDangerColor100 from '@patternfly/react-tokens/dist/js/global_danger_color_100';
|
|
16
|
+
import { format, fromUnixTime, isSameDay, parse } from 'date-fns';
|
|
15
17
|
import debounce from 'lodash/debounce';
|
|
16
18
|
import isEmpty from 'lodash/isEmpty';
|
|
17
|
-
import React, { useEffect,
|
|
19
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
18
20
|
import { Trans, useTranslation } from 'react-i18next';
|
|
19
21
|
const TimelineEvent = ({ date, text, side, user, useAvatar }) => {
|
|
20
22
|
if (side === 'left') {
|
|
@@ -29,7 +31,7 @@ const TimelineEvent = ({ date, text, side, user, useAvatar }) => {
|
|
|
29
31
|
React.createElement("div", { className: `contentTimeline content-${side}-history content-${side}-${user}` },
|
|
30
32
|
React.createElement("p", { className: `content-time-${side}-history` }, text))));
|
|
31
33
|
}
|
|
32
|
-
return (React.createElement("div", { className: `${side}-node ${user}`, role: "complementary", "aria-label": `${user}'s timeline event content
|
|
34
|
+
return (React.createElement("div", { className: `${side}-node ${user}`, role: "complementary", "aria-label": `${user}'s timeline event content` },
|
|
33
35
|
React.createElement("div", { className: `contentTimeline content-${side}-history content-${side}-${user}` },
|
|
34
36
|
React.createElement("h2", { className: `content-date-${side}-history` }, date),
|
|
35
37
|
text && React.createElement("p", { className: `content-time-${side}-history` }, text))));
|
|
@@ -37,14 +39,18 @@ const TimelineEvent = ({ date, text, side, user, useAvatar }) => {
|
|
|
37
39
|
const TimelineNode = React.forwardRef(({ leftEvent, rightEvent, user, useAvatar }, ref) => {
|
|
38
40
|
return (React.createElement("div", { className: "timeline-node", role: "region", "aria-label": "Timeline node" },
|
|
39
41
|
leftEvent && React.createElement(TimelineEvent, Object.assign({}, leftEvent, { side: "left", useAvatar: useAvatar })),
|
|
40
|
-
React.createElement("div", { ref: ref, "aria-label": `${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}` }))),
|
|
41
43
|
rightEvent && React.createElement(TimelineEvent, Object.assign({}, rightEvent, { side: "right" }))));
|
|
42
44
|
});
|
|
43
|
-
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @param {Object} item - The item object to process.
|
|
48
|
+
* @returns {string} The formatted text string.
|
|
49
|
+
*/
|
|
44
50
|
const applyReplacements = (item) => {
|
|
45
|
-
if (!item || typeof item !== 'object')
|
|
51
|
+
if (!item || typeof item !== 'object')
|
|
46
52
|
return '';
|
|
47
|
-
|
|
53
|
+
// API field names to easy to read display names
|
|
48
54
|
const fieldNameMapping = {
|
|
49
55
|
ContactId: 'Contact',
|
|
50
56
|
RH_Product__c: 'Product',
|
|
@@ -55,94 +61,171 @@ const applyReplacements = (item) => {
|
|
|
55
61
|
BusinessHoursId: 'Business Hours',
|
|
56
62
|
FolderId__c: 'Case Group',
|
|
57
63
|
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',
|
|
58
69
|
};
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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',
|
|
73
85
|
};
|
|
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',
|
|
100
|
+
};
|
|
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);
|
|
74
114
|
const newValue = getNameValue('New');
|
|
75
115
|
const oldValue = getNameValue('Old');
|
|
76
116
|
let newText = '';
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
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;
|
|
83
127
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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;
|
|
104
184
|
}
|
|
105
|
-
|
|
106
|
-
|
|
185
|
+
if (typeof item.newValue === 'boolean')
|
|
186
|
+
newText = formatBooleanChange(fieldName, item.newValue);
|
|
187
|
+
if (typeof newText === 'string')
|
|
107
188
|
newText = newText.replace(/Problem Statement/g, 'Title');
|
|
108
|
-
}
|
|
109
189
|
return newText;
|
|
110
190
|
};
|
|
111
191
|
// 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
|
-
};
|
|
115
192
|
const transformApiResponseToTimelineData = (apiResponse) => {
|
|
116
193
|
let previousUser = null;
|
|
117
|
-
const
|
|
118
|
-
|
|
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) => {
|
|
119
198
|
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.
|
|
121
199
|
const useAvatar = index === 0 || (!!previousUser && currentUser !== previousUser);
|
|
122
|
-
// Update the previousUser for the next iteration
|
|
123
200
|
previousUser = currentUser;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
});
|
|
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
|
+
}
|
|
132
208
|
const user = item.createdByCustomer !== undefined
|
|
133
|
-
? item.createdByCustomer
|
|
209
|
+
? item.createdByCustomer
|
|
134
210
|
? 'customer'
|
|
135
211
|
: 'internal'
|
|
136
|
-
: currentUser === 'Case Diagnostics' || currentUser === 'GSS Tools'
|
|
212
|
+
: currentUser === 'Case Diagnostics' || currentUser === 'GSS Tools'
|
|
137
213
|
? 'internal'
|
|
138
214
|
: 'customer';
|
|
139
215
|
const leftEvent = {
|
|
140
|
-
date:
|
|
141
|
-
text:
|
|
216
|
+
date: format(eventDate, 'MMMM d, yyyy'),
|
|
217
|
+
text: format(eventDate, 'h:mm a zzz'),
|
|
142
218
|
};
|
|
219
|
+
let outputText = item.outputText;
|
|
220
|
+
if (item.type === 'Change') {
|
|
221
|
+
outputText = applyReplacements(item);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
outputText = stripHTML(outputText);
|
|
225
|
+
}
|
|
143
226
|
const rightEvent = {
|
|
144
|
-
date: `${item.createdByUserName} ${
|
|
145
|
-
text: '',
|
|
227
|
+
date: `${item.createdByUserName} ${outputText}`,
|
|
228
|
+
text: '',
|
|
146
229
|
user: user,
|
|
147
230
|
useAvatar: useAvatar,
|
|
148
231
|
};
|
|
@@ -161,27 +244,45 @@ function stripHTML(htmlString) {
|
|
|
161
244
|
const Timeline = ({ caseNumber }) => {
|
|
162
245
|
const { t } = useTranslation();
|
|
163
246
|
const [timelineEvents, setTimelineEvents] = useState([]);
|
|
247
|
+
const [originalTimelineEvents, setOriginalTimelineEvents] = useState([]);
|
|
164
248
|
const [isFetchingData, setIsFetchingData] = useState();
|
|
165
|
-
const [paginatedListLength, setPaginatedListLength] = useState();
|
|
166
249
|
const lastNodeRef = useRef(null);
|
|
167
|
-
const [dateValue, setDateValue] =
|
|
250
|
+
const [dateValue, setDateValue] = useState('');
|
|
168
251
|
const [searchValue, setSearchValue] = useState('');
|
|
169
252
|
const [filteredEvents, setFilteredEvents] = useState([]);
|
|
170
253
|
const [isSelectOpen, setIsSelectOpen] = useState(false);
|
|
171
254
|
const [selectedOrder, setSelectedOrder] = useState('Newest to Oldest');
|
|
172
|
-
const
|
|
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();
|
|
173
273
|
setIsSelectOpen(!isSelectOpen);
|
|
174
274
|
};
|
|
175
|
-
const handleSelect = (
|
|
176
|
-
|
|
275
|
+
const handleSelect = (e, value) => {
|
|
276
|
+
e.preventDefault();
|
|
277
|
+
if (value !== selectedOrder) {
|
|
278
|
+
setSelectedOrder(value);
|
|
279
|
+
reorderTimelineEvents(value);
|
|
280
|
+
}
|
|
177
281
|
setIsSelectOpen(false);
|
|
178
|
-
reorderTimelineEvents(value);
|
|
179
282
|
};
|
|
180
283
|
const reorderTimelineEvents = (order) => {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return applyAvatarLogic(reversedEvents);
|
|
184
|
-
});
|
|
284
|
+
const events = order === 'Newest to Oldest' ? [...originalTimelineEvents] : [...originalTimelineEvents].reverse();
|
|
285
|
+
setTimelineEvents(applyAvatarLogic(events));
|
|
185
286
|
};
|
|
186
287
|
const applyAvatarLogic = (events) => {
|
|
187
288
|
let previousUser = null;
|
|
@@ -196,6 +297,7 @@ const Timeline = ({ caseNumber }) => {
|
|
|
196
297
|
function fetchHistory() {
|
|
197
298
|
return __awaiter(this, void 0, void 0, function* () {
|
|
198
299
|
setIsFetchingData(true);
|
|
300
|
+
setFetchError(null);
|
|
199
301
|
const options = {
|
|
200
302
|
sortField: 'createdDate',
|
|
201
303
|
sortOrder: 'DESC',
|
|
@@ -205,9 +307,11 @@ const Timeline = ({ caseNumber }) => {
|
|
|
205
307
|
const response = yield caseHistory.getHistoryv1(caseNumber, options);
|
|
206
308
|
const transformedData = transformApiResponseToTimelineData(response);
|
|
207
309
|
setTimelineEvents(transformedData);
|
|
310
|
+
setOriginalTimelineEvents(transformedData);
|
|
208
311
|
}
|
|
209
312
|
catch (error) {
|
|
210
313
|
console.error('Failed to fetch history:', error);
|
|
314
|
+
setFetchError('Unable to connect');
|
|
211
315
|
}
|
|
212
316
|
finally {
|
|
213
317
|
setIsFetchingData(false);
|
|
@@ -215,27 +319,51 @@ const Timeline = ({ caseNumber }) => {
|
|
|
215
319
|
});
|
|
216
320
|
}
|
|
217
321
|
fetchHistory();
|
|
218
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
219
322
|
}, [caseNumber]);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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) {
|
|
224
334
|
if (lastNodeRef.current) {
|
|
225
|
-
const
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
+
//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');
|
|
235
363
|
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
}, [
|
|
364
|
+
}
|
|
365
|
+
return () => window.removeEventListener('resize', debouncedUpdateLineStyle);
|
|
366
|
+
}, [isFetchingData, filteredEvents, page, perPage]);
|
|
239
367
|
const handleSearchChange = (value) => {
|
|
240
368
|
setSearchValue(value.target.value);
|
|
241
369
|
};
|
|
@@ -251,60 +379,64 @@ const Timeline = ({ caseNumber }) => {
|
|
|
251
379
|
const lowercasedFilter = searchValue.toLowerCase();
|
|
252
380
|
filteredBySearch = filteredBySearch.filter(({ left, right }) => {
|
|
253
381
|
const dateString = left.date.toLowerCase();
|
|
382
|
+
const timeString = left.text.toLowerCase();
|
|
383
|
+
const rightDateString = right.date.toLowerCase();
|
|
254
384
|
return (dateString.includes(lowercasedFilter) ||
|
|
255
|
-
|
|
256
|
-
|
|
385
|
+
timeString.includes(lowercasedFilter) ||
|
|
386
|
+
rightDateString.includes(lowercasedFilter));
|
|
257
387
|
});
|
|
258
388
|
}
|
|
259
389
|
let finalFilteredEvents = filteredBySearch;
|
|
260
390
|
if (dateValue) {
|
|
261
391
|
finalFilteredEvents = filteredBySearch.filter(({ left }) => {
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
return eventDateFormatted === dateValue;
|
|
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);
|
|
266
395
|
});
|
|
267
396
|
}
|
|
268
397
|
setFilteredEvents(finalFilteredEvents);
|
|
398
|
+
setPage(1);
|
|
269
399
|
}, [searchValue, dateValue, timelineEvents]);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
React.createElement(
|
|
278
|
-
React.createElement(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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 },
|
|
283
418
|
React.createElement(EmptyStateHeader, { titleText: React.createElement(Trans, null, "No results found"), icon: React.createElement(EmptyStateIcon, { icon: SearchIcon }), headingLevel: "h2" }),
|
|
284
419
|
React.createElement(EmptyStateBody, null,
|
|
285
|
-
React.createElement(Trans, null, "Try modifying your search query or changing the date range and try again."))))
|
|
286
|
-
|
|
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
|
+
};
|
|
287
425
|
return (React.createElement(React.Fragment, null,
|
|
288
426
|
React.createElement("div", { className: "timelineMenu" },
|
|
289
|
-
React.createElement(SearchInput, { placeholder:
|
|
427
|
+
React.createElement(SearchInput, { placeholder: t('Search for a user, action, or keyword'), value: searchValue, onChange: handleSearchChange, onClear: handleClear, className: "case-history-timeline-search" }),
|
|
290
428
|
React.createElement("div", { className: "case-history-timeline-datepicker" },
|
|
291
|
-
React.createElement(DatePicker, { value: dateValue, onChange: (_event, value) => handleDateChange(value) }),
|
|
429
|
+
React.createElement(DatePicker, { value: dateValue, onChange: (_event, value) => handleDateChange(value), validators: [futureDateValidator] }),
|
|
292
430
|
React.createElement(Button, { onClick: () => setDateValue(''), isDisabled: isEmpty(dateValue) }, "Reset"))),
|
|
293
|
-
React.createElement("div", { className: "timeline-
|
|
294
|
-
React.createElement(
|
|
295
|
-
|
|
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)) },
|
|
296
435
|
React.createElement(SelectOption, { value: "Newest to Oldest" }, "Newest to Oldest"),
|
|
297
436
|
React.createElement(SelectOption, { value: "Oldest to Newest" }, "Oldest to Newest")))),
|
|
298
|
-
React.createElement("div", { className: "timeline" },
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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 })))));
|
|
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()))));
|
|
309
441
|
};
|
|
310
442
|
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,qBA6OzB;kBA7OQ,GAAG;;;AAgPZ,eAAe,GAAG,CAAC"}
|
|
@@ -100,10 +100,12 @@ function Fts(props) {
|
|
|
100
100
|
setCaseState(caseDispatch, { ftsContactSameAsOwner: e.target.checked });
|
|
101
101
|
if (!e.target.checked) {
|
|
102
102
|
setFtsContact('');
|
|
103
|
-
|
|
103
|
+
setContactInfoIntoGlobalState('');
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
setFtsContact(`${phoneCountryCode}-${phoneAreaCodePrefixLineNumber}`);
|
|
107
|
+
setContactInfoIntoGlobalState(`${phoneCountryCode}-${phoneAreaCodePrefixLineNumber}`);
|
|
104
108
|
}
|
|
105
|
-
setFtsContact(`${phoneCountryCode}-${phoneAreaCodePrefixLineNumber}`);
|
|
106
|
-
setContactInfoIntoGlobalState(`${phoneCountryCode}-${phoneAreaCodePrefixLineNumber}`);
|
|
107
109
|
};
|
|
108
110
|
const onSave = (e) => __awaiter(this, void 0, void 0, function* () {
|
|
109
111
|
setContactInfoIntoGlobalState(contactInfo24X7State);
|
|
@@ -148,8 +150,7 @@ function Fts(props) {
|
|
|
148
150
|
}
|
|
149
151
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
150
152
|
}, [phoneAreaCodePrefixLineNumber, phoneCountryCode]);
|
|
151
|
-
const disableFtsNContact = !isEmpty(caseNumber) &&
|
|
152
|
-
(!isFtsEditableOnEditPage(entitlementSla, severity) || isFtsContactUpdating || isFtsUpdating);
|
|
153
|
+
const disableFtsNContact = !isEmpty(caseNumber) && (!isFtsEditableOnEditPage(entitlementSla, severity) || isFtsUpdating);
|
|
153
154
|
const maxLengthErrorMessage = t('Contact information cannot be more than {{limit}} characters.', {
|
|
154
155
|
limit: CONTACT_INFO_24X7_LIMIT,
|
|
155
156
|
});
|
|
@@ -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,qBAuThC"}
|
|
@@ -66,8 +66,9 @@ 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('
|
|
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'),
|
|
71
72
|
});
|
|
72
73
|
cepUpdate({ cep: false });
|
|
73
74
|
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":"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"}
|