@buerokratt-ria/common-gui-components 0.0.58 → 0.0.60
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/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/templates/history-page/src/History.scss +15 -1
- package/templates/history-page/src/components/ChatMetadataPanel/ChatMetadataPanel.scss +18 -0
- package/templates/history-page/src/components/ChatMetadataPanel/index.tsx +206 -0
- package/templates/history-page/src/components/ChatMetadataPanelItem/index.tsx +17 -0
- package/templates/history-page/src/components/FilterTag/FilterTag.scss +42 -0
- package/templates/history-page/src/components/FilterTag/index.tsx +16 -0
- package/templates/history-page/src/components/HeaderCombobox/index.tsx +67 -0
- package/templates/history-page/src/components/QualitySettings/QualitySettings.scss +19 -0
- package/templates/history-page/src/components/QualitySettings/index.tsx +115 -0
- package/templates/history-page/src/components/SelectedFilterTags/SelectedFilterTags.scss +36 -0
- package/templates/history-page/src/components/SelectedFilterTags/index.tsx +224 -0
- package/templates/history-page/src/components/index.tsx +6 -0
- package/templates/history-page/src/index.tsx +946 -207
- package/templates/history-page/src/types/index.ts +17 -0
- package/translations/en/common.json +22 -2
- package/translations/et/common.json +22 -2
- package/types/chat.ts +3 -0
- package/ui-components/DataTable/index.tsx +0 -1
- package/ui-components/FormElements/FormCombobox/FormCombobox.scss +263 -0
- package/ui-components/FormElements/FormCombobox/index.tsx +393 -0
- package/ui-components/FormElements/index.tsx +1 -0
- package/ui-components/Icon/index.tsx +1 -0
- package/ui-components/index.tsx +2 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {FC, PropsWithChildren, useEffect, useMemo, useRef, useState} from 'react';
|
|
2
|
+
import type {ComponentProps} from 'react';
|
|
2
3
|
import {useTranslation} from 'react-i18next';
|
|
3
|
-
import {useMutation} from '@tanstack/react-query';
|
|
4
|
+
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
|
4
5
|
import {ColumnPinningState, createColumnHelper, PaginationState, SortingState,} from '@tanstack/react-table';
|
|
5
6
|
import {endOfDay, format, formatISO, startOfDay} from "date-fns";
|
|
6
7
|
import {AxiosError} from 'axios';
|
|
7
8
|
import './History.scss';
|
|
8
|
-
import {MdOutlineRemoveRedEye
|
|
9
|
+
import {MdOutlineRemoveRedEye} from 'react-icons/md';
|
|
9
10
|
import {CgSpinner} from 'react-icons/cg';
|
|
10
11
|
|
|
11
12
|
import {
|
|
12
13
|
Button,
|
|
13
14
|
Card,
|
|
14
|
-
ClearFiltersButton,
|
|
15
15
|
DataTable,
|
|
16
16
|
Dialog,
|
|
17
17
|
Drawer,
|
|
@@ -38,6 +38,8 @@ import {ToastContextType} from "../../../context";
|
|
|
38
38
|
import {getDomainsArray} from "../../../utils/multiDomain-utils";
|
|
39
39
|
import {StoreState} from "../../../store";
|
|
40
40
|
import {saveFile} from "../../../services/file";
|
|
41
|
+
import {ChatMetadataPanel, HeaderCombobox, QualitySettings, SelectedFilterTags} from './components';
|
|
42
|
+
import { CharMeasurementType } from './types';
|
|
41
43
|
|
|
42
44
|
type HistoryProps = {
|
|
43
45
|
user: UserInfo | null;
|
|
@@ -62,7 +64,137 @@ type ExportResult = {
|
|
|
62
64
|
chatIds: string[];
|
|
63
65
|
};
|
|
64
66
|
|
|
67
|
+
type QualitySettingsConfig = {
|
|
68
|
+
readonly chatAnalysisEnabled: boolean;
|
|
69
|
+
readonly chatAnalysisTheme: string[];
|
|
70
|
+
readonly chatAnalysisBykResponseQuality: string[];
|
|
71
|
+
readonly chatAnalysisFollowUpAction: string[];
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export type DomainSelection = {
|
|
75
|
+
readonly id: string;
|
|
76
|
+
readonly name: string;
|
|
77
|
+
readonly url: string;
|
|
78
|
+
readonly selected: boolean;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
type FeedbackConfig = {
|
|
82
|
+
readonly isFiveRatingScale?: string | boolean;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const GLOBAL_FEEDBACK_CONFIG_DOMAIN = 'none';
|
|
86
|
+
|
|
87
|
+
export const MEASUREMENT_TYPES = {
|
|
88
|
+
THEME: 'THEME',
|
|
89
|
+
QUALITY: 'QUALITY',
|
|
90
|
+
FOLLOW_UP_ACTION: 'FOLLOW_UP_ACTION',
|
|
91
|
+
} as const;
|
|
92
|
+
|
|
93
|
+
export type MeasurementType =
|
|
94
|
+
(typeof MEASUREMENT_TYPES)[keyof typeof MEASUREMENT_TYPES];
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
export const postQualityMeasurements = (body: {
|
|
98
|
+
readonly chatUuid: string;
|
|
99
|
+
readonly type: MeasurementType;
|
|
100
|
+
readonly value: string | string[];
|
|
101
|
+
}) =>
|
|
102
|
+
apiDev.post('chats/quality/measurements', body);
|
|
103
|
+
|
|
104
|
+
export const getQualityMeasurements = (params: {
|
|
105
|
+
readonly chatUuid: string;
|
|
106
|
+
}) =>
|
|
107
|
+
apiDev.get<{
|
|
108
|
+
readonly response: CharMeasurementType[];
|
|
109
|
+
}>('chats/quality/measurements', { params });
|
|
110
|
+
|
|
111
|
+
export const getWidgetData = async (userId: string) => {
|
|
112
|
+
const { data } = await apiDev.get<DomainSelection[]>('accounts/widget-data', {
|
|
113
|
+
params: {
|
|
114
|
+
user_id: userId,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
return data;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const loadQualitySettingsConfig = async (domain: string): Promise<QualitySettingsConfig> => {
|
|
121
|
+
const response = await apiDev.get<{
|
|
122
|
+
readonly response: {
|
|
123
|
+
readonly chatAnalysisEnabled: boolean;
|
|
124
|
+
readonly chatAnalysisTheme: string; // can be an empty string if no themes are defined
|
|
125
|
+
readonly chatAnalysisBykResponseQuality: string; // can be an empty string if no qualities are defined
|
|
126
|
+
readonly chatAnalysisFollowUpAction: string; // can be an empty string if no follow-up actions are defined
|
|
127
|
+
};
|
|
128
|
+
}>('/configs/chat-analysis', {
|
|
129
|
+
params: { domain },
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
chatAnalysisEnabled: response.data.response.chatAnalysisEnabled,
|
|
133
|
+
chatAnalysisTheme: response.data.response.chatAnalysisTheme.split(',').filter(Boolean),
|
|
134
|
+
chatAnalysisBykResponseQuality: response.data.response.chatAnalysisBykResponseQuality.split(',').filter(Boolean),
|
|
135
|
+
chatAnalysisFollowUpAction: response.data.response.chatAnalysisFollowUpAction.split(',').filter(Boolean),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const loadFeedbackConfig = async (): Promise<FeedbackConfig> => {
|
|
140
|
+
const response = await apiDev.get('/configs/feedback', {
|
|
141
|
+
params: { domain: GLOBAL_FEEDBACK_CONFIG_DOMAIN },
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return response.data.response ?? response.data;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const isFiveRatingScaleEnabled = (value?: string | boolean | null) =>
|
|
148
|
+
value === true || value === 'true';
|
|
149
|
+
|
|
150
|
+
const formatChatAnalysisCell = (
|
|
151
|
+
value: string[] | null | undefined,
|
|
152
|
+
selectionEmptiedLabel: string
|
|
153
|
+
) => {
|
|
154
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
155
|
+
return '';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (value.length === 1 && value[0] === '') {
|
|
159
|
+
return selectionEmptiedLabel;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return value.join(', ');
|
|
163
|
+
};
|
|
164
|
+
|
|
65
165
|
const ALL_COLUMNS_VALUE = '__all__';
|
|
166
|
+
// Boolean -> truthy values before falsy
|
|
167
|
+
// For other columns -> desc before asc - populated before empty
|
|
168
|
+
const NON_EMPTY_FIRST_SORT_COLUMN_IDS = new Set([
|
|
169
|
+
'authenticatedPerson',
|
|
170
|
+
'istest',
|
|
171
|
+
'isPreserve',
|
|
172
|
+
'followUpStatus',
|
|
173
|
+
'responseQuality',
|
|
174
|
+
'theme',
|
|
175
|
+
]);
|
|
176
|
+
const CHAT_STATUSES = [
|
|
177
|
+
CHAT_EVENTS.ACCEPTED,
|
|
178
|
+
CHAT_EVENTS.CLIENT_LEFT_FOR_UNKNOWN_REASONS,
|
|
179
|
+
CHAT_EVENTS.CLIENT_LEFT_WITH_ACCEPTED,
|
|
180
|
+
CHAT_EVENTS.CLIENT_LEFT_WITH_NO_RESOLUTION,
|
|
181
|
+
CHAT_EVENTS.HATE_SPEECH,
|
|
182
|
+
CHAT_EVENTS.OTHER,
|
|
183
|
+
CHAT_EVENTS.RESPONSE_SENT_TO_CLIENT_EMAIL,
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
const getEndedChatsSortBy = (sorting: SortingState) => {
|
|
187
|
+
if (sorting.length === 0) {
|
|
188
|
+
return 'created desc';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const [sortingObject] = sorting;
|
|
192
|
+
const sortType = NON_EMPTY_FIRST_SORT_COLUMN_IDS.has(sortingObject.id)
|
|
193
|
+
? sortingObject.desc ? 'asc' : 'desc'
|
|
194
|
+
: sortingObject.desc ? 'desc' : 'asc';
|
|
195
|
+
|
|
196
|
+
return `${sortingObject.id} ${sortType}`;
|
|
197
|
+
};
|
|
66
198
|
|
|
67
199
|
const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
68
200
|
user,
|
|
@@ -81,6 +213,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
81
213
|
delegatedStartDate = null
|
|
82
214
|
}) => {
|
|
83
215
|
const {t, i18n} = useTranslation();
|
|
216
|
+
const queryClient = useQueryClient();
|
|
84
217
|
const toast = toastContext;
|
|
85
218
|
const userInfo = user;
|
|
86
219
|
const routerLocation = useLocation();
|
|
@@ -89,6 +222,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
89
222
|
const passedStartDate = delegatedStartDate ?? params.get("start");
|
|
90
223
|
const passedEndDate = delegatedEndDate ?? params.get("end");
|
|
91
224
|
const skipNextSelectedColumnsEffect = useRef(false);
|
|
225
|
+
const skipInitialTableHeaderFilterEffect = useRef(true);
|
|
92
226
|
const [selectedChat, setSelectedChat] = useState<ChatType | null>(null);
|
|
93
227
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
94
228
|
const [statusChangeModal, setStatusChangeModal] = useState<string | null>(
|
|
@@ -119,7 +253,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
119
253
|
|
|
120
254
|
const useStore = userDomains;
|
|
121
255
|
const [updateKey, setUpdateKey] = useState<number>(0)
|
|
122
|
-
const currentDomains = useStore.getState().userDomains;
|
|
256
|
+
const currentDomains = useStore.getState().userDomains as string[];
|
|
123
257
|
const multiDomainEnabled = import.meta.env.REACT_APP_ENABLE_MULTI_DOMAIN?.toLowerCase() === 'true';
|
|
124
258
|
const testMessageEnabled = import.meta.env.REACT_APP_SHOW_TEST_MESSAGE?.toLowerCase() === 'true';
|
|
125
259
|
const envVal = import.meta.env.REACT_APP_SHOW_TEST_CONVERSATIONS;
|
|
@@ -129,6 +263,12 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
129
263
|
const loadingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
130
264
|
const timeoutAbortRef = useRef(false);
|
|
131
265
|
|
|
266
|
+
const userWidgetDomains = useQuery<DomainSelection[]>({
|
|
267
|
+
queryKey: ['accounts/widget-data', userInfo?.idCode],
|
|
268
|
+
queryFn: () => getWidgetData(userInfo!.idCode),
|
|
269
|
+
enabled: !!userInfo?.idCode,
|
|
270
|
+
});
|
|
271
|
+
|
|
132
272
|
const parseDateParam = (dateString: string | null) => {
|
|
133
273
|
if (!dateString) return new Date();
|
|
134
274
|
return new Date(dateString.split("+")[0]);
|
|
@@ -285,6 +425,19 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
285
425
|
},
|
|
286
426
|
});
|
|
287
427
|
|
|
428
|
+
const selectedChatDomainUuid = useMemo(() => {
|
|
429
|
+
return userWidgetDomains.data?.find(domain => domain.url === selectedChat?.endUserUrl)?.id ?? null;
|
|
430
|
+
}, [selectedChat]);
|
|
431
|
+
const qualitySettingsConfigQuery = useQuery<QualitySettingsConfig>({
|
|
432
|
+
queryKey: ['configs/chat-analysis'],
|
|
433
|
+
queryFn: () => loadQualitySettingsConfig(selectedChatDomainUuid ? selectedChatDomainUuid : GLOBAL_FEEDBACK_CONFIG_DOMAIN),
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
const isChatAnalysisEnabled = useMemo(() => {
|
|
437
|
+
return qualitySettingsConfigQuery.data?.chatAnalysisEnabled ?? false;
|
|
438
|
+
}, [qualitySettingsConfigQuery.data]);
|
|
439
|
+
console.log("IS CHAT ANALYSIS ENABLED", isChatAnalysisEnabled);
|
|
440
|
+
|
|
288
441
|
const getAllEndedChats = useMutation({
|
|
289
442
|
mutationFn: (data: {
|
|
290
443
|
startDate: string;
|
|
@@ -292,27 +445,25 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
292
445
|
pagination: PaginationState;
|
|
293
446
|
sorting: SortingState;
|
|
294
447
|
search: string;
|
|
448
|
+
urls?: string[];
|
|
295
449
|
}) => {
|
|
296
450
|
abortRef.current?.abort();
|
|
297
451
|
abortRef.current = new AbortController();
|
|
298
452
|
|
|
299
|
-
|
|
300
|
-
if (sorting.length > 0) {
|
|
301
|
-
const sortType = sorting[0].desc ? 'desc' : 'asc';
|
|
302
|
-
sortBy = `${sorting[0].id} ${sortType}`;
|
|
303
|
-
}
|
|
453
|
+
const sortBy = getEndedChatsSortBy(data.sorting);
|
|
304
454
|
|
|
305
455
|
return apiDevEnded.post('agents/chats/ended', {
|
|
306
456
|
startDate: formatISO(startOfDay(new Date(data.startDate))),
|
|
307
457
|
endDate: formatISO(endOfDay(new Date(data.endDate))),
|
|
308
|
-
|
|
309
|
-
|
|
458
|
+
...endedChatsFilterBody,
|
|
459
|
+
...(data.urls?.length ? {urls: getDomainsArray(data.urls)} : {}),
|
|
310
460
|
page: data.pagination.pageIndex + 1,
|
|
311
461
|
page_size: data.pagination.pageSize,
|
|
312
462
|
sorting: sortBy,
|
|
313
463
|
search,
|
|
314
464
|
},
|
|
315
465
|
{
|
|
466
|
+
params: { isChatAnalysisEnabled },
|
|
316
467
|
signal: abortRef.current.signal
|
|
317
468
|
}
|
|
318
469
|
);
|
|
@@ -390,8 +541,16 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
390
541
|
|
|
391
542
|
columns.splice(5, 0, {label: t('global.preserve'), value: 'isPreserve'});
|
|
392
543
|
|
|
544
|
+
if (isChatAnalysisEnabled) {
|
|
545
|
+
columns.push(
|
|
546
|
+
{label: t('chat.history.theme'), value: 'theme'},
|
|
547
|
+
{label: t('chat.history.responseQuality'), value: 'responseQuality'},
|
|
548
|
+
{label: t('chat.history.followUpStatus'), value: 'followUpStatus'}
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
393
552
|
return columns;
|
|
394
|
-
}, [t, showEmail, testMessageEnabled])
|
|
553
|
+
}, [t, showEmail, testMessageEnabled, isChatAnalysisEnabled])
|
|
395
554
|
|
|
396
555
|
const visibleColumnOptions = useMemo(() => [
|
|
397
556
|
{label: t('chat.history.chooseAll'), value: ALL_COLUMNS_VALUE},
|
|
@@ -732,6 +891,335 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
732
891
|
</Button>
|
|
733
892
|
);
|
|
734
893
|
|
|
894
|
+
const customerSupportAgentsQuery = useQuery({
|
|
895
|
+
queryKey: ['customer-support-agents'],
|
|
896
|
+
queryFn: () =>
|
|
897
|
+
apiDev.post<{
|
|
898
|
+
readonly response: {
|
|
899
|
+
readonly login: string;
|
|
900
|
+
readonly firstName: string;
|
|
901
|
+
readonly lastName: string;
|
|
902
|
+
readonly idCode: string;
|
|
903
|
+
readonly displayName: string;
|
|
904
|
+
readonly csaTitle: string;
|
|
905
|
+
readonly csaEmail: string;
|
|
906
|
+
readonly department: string;
|
|
907
|
+
readonly jiraAccountId: string;
|
|
908
|
+
readonly smaxAccountId: string;
|
|
909
|
+
readonly authorities: string[];
|
|
910
|
+
readonly customerSupportStatus: string;
|
|
911
|
+
readonly statusComment: string;
|
|
912
|
+
readonly statusCommentTimeStamp: string;
|
|
913
|
+
readonly domains: (string | null)[] | null;
|
|
914
|
+
readonly totalPages: number;
|
|
915
|
+
}[];
|
|
916
|
+
}>('accounts/customer-support-agents', {
|
|
917
|
+
page: 0,
|
|
918
|
+
page_size: 99999,
|
|
919
|
+
sorting: 'name asc',
|
|
920
|
+
show_active_only: false,
|
|
921
|
+
roles: ['ROLE_CUSTOMER_SUPPORT_AGENT'],
|
|
922
|
+
}),
|
|
923
|
+
select: (result) => {
|
|
924
|
+
return [
|
|
925
|
+
{ label: t('chat.history.chooseAll'), value: ALL_COLUMNS_VALUE },
|
|
926
|
+
{ label: 'Bürokratt', value: 'chatbot' },
|
|
927
|
+
...result.data.response.map((item) => ({
|
|
928
|
+
label: [item.firstName, item.lastName].join(' ').trim(),
|
|
929
|
+
value: item.idCode,
|
|
930
|
+
})),
|
|
931
|
+
]
|
|
932
|
+
},
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
const tableHeaderForm = useForm<{
|
|
936
|
+
readonly csaIdCodesFilter: string[];
|
|
937
|
+
readonly feedbackRatings: string[];
|
|
938
|
+
readonly showAuthenticatedPerson?: boolean;
|
|
939
|
+
readonly isTestFilter?: boolean;
|
|
940
|
+
readonly isPreserveFilter?: boolean;
|
|
941
|
+
readonly domains: string[];
|
|
942
|
+
readonly status: string[];
|
|
943
|
+
readonly theme: string[];
|
|
944
|
+
readonly responseQuality: string[];
|
|
945
|
+
readonly followUpStatus: string[];
|
|
946
|
+
}>({
|
|
947
|
+
defaultValues: {
|
|
948
|
+
csaIdCodesFilter: [],
|
|
949
|
+
feedbackRatings: [],
|
|
950
|
+
showAuthenticatedPerson: undefined,
|
|
951
|
+
isTestFilter: undefined,
|
|
952
|
+
isPreserveFilter: undefined,
|
|
953
|
+
domains: [],
|
|
954
|
+
status: [],
|
|
955
|
+
theme: [],
|
|
956
|
+
responseQuality: [],
|
|
957
|
+
followUpStatus: [],
|
|
958
|
+
},
|
|
959
|
+
});
|
|
960
|
+
const { reset: resetTableHeaderForm, setValue: setTableHeaderValue } = tableHeaderForm;
|
|
961
|
+
const csaIdCodesFilter = tableHeaderForm.watch('csaIdCodesFilter');
|
|
962
|
+
const feedbackRatings = tableHeaderForm.watch('feedbackRatings');
|
|
963
|
+
const showAuthenticatedPerson = tableHeaderForm.watch('showAuthenticatedPerson');
|
|
964
|
+
const isTestFilter = tableHeaderForm.watch('isTestFilter');
|
|
965
|
+
const isPreserveFilter = tableHeaderForm.watch('isPreserveFilter');
|
|
966
|
+
const domains = tableHeaderForm.watch('domains');
|
|
967
|
+
const status = tableHeaderForm.watch('status');
|
|
968
|
+
const theme = tableHeaderForm.watch('theme');
|
|
969
|
+
const responseQuality = tableHeaderForm.watch('responseQuality');
|
|
970
|
+
const followUpStatus = tableHeaderForm.watch('followUpStatus');
|
|
971
|
+
|
|
972
|
+
useEffect(() => {
|
|
973
|
+
if (skipInitialTableHeaderFilterEffect.current) {
|
|
974
|
+
skipInitialTableHeaderFilterEffect.current = false;
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
const resetPagination = { pageIndex: 0, pageSize: pagination.pageSize };
|
|
979
|
+
setPagination(resetPagination);
|
|
980
|
+
setSearchParams((params) => {
|
|
981
|
+
params.set("page", "1");
|
|
982
|
+
return params;
|
|
983
|
+
});
|
|
984
|
+
getAllEndedChats.mutate({
|
|
985
|
+
startDate: formatISO(startOfDay(new Date(startDate))),
|
|
986
|
+
endDate: formatISO(endOfDay(new Date(endDate))),
|
|
987
|
+
pagination: resetPagination,
|
|
988
|
+
sorting,
|
|
989
|
+
search,
|
|
990
|
+
});
|
|
991
|
+
}, [csaIdCodesFilter, feedbackRatings, showAuthenticatedPerson, isTestFilter, isPreserveFilter, domains, status, theme, responseQuality, followUpStatus]);
|
|
992
|
+
|
|
993
|
+
const getBooleanComboboxValue = (value?: boolean) =>
|
|
994
|
+
value === undefined ? '' : String(value);
|
|
995
|
+
const getBooleanFormValue = (value: string) =>
|
|
996
|
+
value ? value === 'true' : undefined;
|
|
997
|
+
const getBooleanApiFilterValue = (value?: boolean) =>
|
|
998
|
+
value === undefined ? [] : [value];
|
|
999
|
+
const getRealStringFilterValues = (values: string[]) =>
|
|
1000
|
+
values.filter((value) => value !== ALL_COLUMNS_VALUE);
|
|
1001
|
+
const getAllStringFilterValues = (options: { readonly value: string }[]) =>
|
|
1002
|
+
getRealStringFilterValues(options.map((option) => option.value));
|
|
1003
|
+
const normalizeAllOptionFilterValues = (
|
|
1004
|
+
values: string[],
|
|
1005
|
+
currentValues: string[],
|
|
1006
|
+
allValues: string[]
|
|
1007
|
+
) => {
|
|
1008
|
+
const currentAllSelected = currentValues.includes(ALL_COLUMNS_VALUE);
|
|
1009
|
+
const nextAllSelected = values.includes(ALL_COLUMNS_VALUE);
|
|
1010
|
+
|
|
1011
|
+
if (nextAllSelected && !currentAllSelected) {
|
|
1012
|
+
return [ALL_COLUMNS_VALUE, ...allValues];
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
if (!nextAllSelected && currentAllSelected) {
|
|
1016
|
+
return [];
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
const realValues = getRealStringFilterValues(values);
|
|
1020
|
+
|
|
1021
|
+
if (allValues.length > 0 && allValues.every((value) => realValues.includes(value))) {
|
|
1022
|
+
return [ALL_COLUMNS_VALUE, ...allValues];
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
return realValues;
|
|
1026
|
+
};
|
|
1027
|
+
const getRealCsaFilterValues = (values: string[]) =>
|
|
1028
|
+
getRealStringFilterValues(values);
|
|
1029
|
+
const getAllCsaFilterValues = () =>
|
|
1030
|
+
getRealCsaFilterValues(customerSupportAgentsQuery.data?.map((option) => option.value) ?? []);
|
|
1031
|
+
const getFeedbackRatingFilterValues = (values: string[]) =>
|
|
1032
|
+
values
|
|
1033
|
+
.map((value) => Number(value))
|
|
1034
|
+
.filter((value) => Number.isInteger(value));
|
|
1035
|
+
const normalizeCsaFilterValues = (values: string[]) => {
|
|
1036
|
+
const currentAllSelected = csaIdCodesFilter.includes(ALL_COLUMNS_VALUE);
|
|
1037
|
+
const nextAllSelected = values.includes(ALL_COLUMNS_VALUE);
|
|
1038
|
+
const allCsaFilterValues = getAllCsaFilterValues();
|
|
1039
|
+
|
|
1040
|
+
if (nextAllSelected && !currentAllSelected) {
|
|
1041
|
+
return [ALL_COLUMNS_VALUE, ...allCsaFilterValues];
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
if (!nextAllSelected && currentAllSelected) {
|
|
1045
|
+
return [];
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const realValues = getRealCsaFilterValues(values);
|
|
1049
|
+
|
|
1050
|
+
if (allCsaFilterValues.length > 0 && allCsaFilterValues.every((value) => realValues.includes(value))) {
|
|
1051
|
+
return [ALL_COLUMNS_VALUE, ...allCsaFilterValues];
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
return realValues;
|
|
1055
|
+
};
|
|
1056
|
+
const csaFilterTagValues = getRealCsaFilterValues(csaIdCodesFilter);
|
|
1057
|
+
const csaFilterTagLabelsByValue = useMemo(() => {
|
|
1058
|
+
return new Map(
|
|
1059
|
+
(customerSupportAgentsQuery.data ?? []).map((option) => [option.value, option.label])
|
|
1060
|
+
);
|
|
1061
|
+
}, [customerSupportAgentsQuery.data]);
|
|
1062
|
+
const getCsaFilterTagLabel = (value: string) => csaFilterTagLabelsByValue.get(value) ?? value;
|
|
1063
|
+
const removeSelectedFilterTag: ComponentProps<typeof SelectedFilterTags>['onRemove'] = (filter, value) => {
|
|
1064
|
+
switch (filter) {
|
|
1065
|
+
case 'csaIdCodesFilter':
|
|
1066
|
+
setTableHeaderValue(
|
|
1067
|
+
filter,
|
|
1068
|
+
getRealCsaFilterValues(csaIdCodesFilter).filter((item) => item !== value)
|
|
1069
|
+
);
|
|
1070
|
+
return;
|
|
1071
|
+
case 'showAuthenticatedPerson':
|
|
1072
|
+
case 'isTestFilter':
|
|
1073
|
+
case 'isPreserveFilter':
|
|
1074
|
+
setTableHeaderValue(filter, undefined);
|
|
1075
|
+
return;
|
|
1076
|
+
case 'domains':
|
|
1077
|
+
setTableHeaderValue(filter, getRealStringFilterValues(domains).filter((item) => item !== value));
|
|
1078
|
+
return;
|
|
1079
|
+
case 'feedbackRatings':
|
|
1080
|
+
setTableHeaderValue(filter, getRealStringFilterValues(feedbackRatings).filter((item) => item !== value));
|
|
1081
|
+
return;
|
|
1082
|
+
case 'status':
|
|
1083
|
+
setTableHeaderValue(filter, getRealStringFilterValues(status).filter((item) => item !== value));
|
|
1084
|
+
return;
|
|
1085
|
+
case 'theme':
|
|
1086
|
+
setTableHeaderValue(filter, getRealStringFilterValues(theme).filter((item) => item !== value));
|
|
1087
|
+
return;
|
|
1088
|
+
case 'responseQuality':
|
|
1089
|
+
setTableHeaderValue(filter, getRealStringFilterValues(responseQuality).filter((item) => item !== value));
|
|
1090
|
+
return;
|
|
1091
|
+
case 'followUpStatus':
|
|
1092
|
+
setTableHeaderValue(filter, getRealStringFilterValues(followUpStatus).filter((item) => item !== value));
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
const globalFeedbackConfigQuery = useQuery<FeedbackConfig>({
|
|
1097
|
+
queryKey: ['configs/feedback', GLOBAL_FEEDBACK_CONFIG_DOMAIN],
|
|
1098
|
+
queryFn: loadFeedbackConfig,
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
const tableHeaderQualitySettingsConfigQuery = useQuery<QualitySettingsConfig>({
|
|
1102
|
+
queryKey: ['configs/chat-analysis', GLOBAL_FEEDBACK_CONFIG_DOMAIN],
|
|
1103
|
+
queryFn: () => loadQualitySettingsConfig(GLOBAL_FEEDBACK_CONFIG_DOMAIN),
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
const realRatingOptions = useMemo(() => {
|
|
1107
|
+
const isFiveRatingScale = isFiveRatingScaleEnabled(globalFeedbackConfigQuery.data?.isFiveRatingScale);
|
|
1108
|
+
const ratingMin = isFiveRatingScale ? 1 : 0;
|
|
1109
|
+
const ratingMax = isFiveRatingScale ? 5 : 10;
|
|
1110
|
+
|
|
1111
|
+
return Array.from({ length: ratingMax - ratingMin + 1 }, (_, index) => {
|
|
1112
|
+
const rating = ratingMin + index;
|
|
1113
|
+
|
|
1114
|
+
return {
|
|
1115
|
+
label: String(rating),
|
|
1116
|
+
value: String(rating),
|
|
1117
|
+
};
|
|
1118
|
+
});
|
|
1119
|
+
}, [globalFeedbackConfigQuery.data?.isFiveRatingScale]);
|
|
1120
|
+
|
|
1121
|
+
const ratingOptions = useMemo(() => [
|
|
1122
|
+
{ label: t('chat.history.chooseAll'), value: ALL_COLUMNS_VALUE },
|
|
1123
|
+
...realRatingOptions,
|
|
1124
|
+
], [t, realRatingOptions]);
|
|
1125
|
+
|
|
1126
|
+
const realStatusOptions = useMemo(() => {
|
|
1127
|
+
return CHAT_STATUSES.map((chatStatus) => ({
|
|
1128
|
+
label: t(`chat.plainEvents.${chatStatus}`),
|
|
1129
|
+
value: chatStatus,
|
|
1130
|
+
}));
|
|
1131
|
+
}, [t]);
|
|
1132
|
+
|
|
1133
|
+
const statusOptions = useMemo(() => [
|
|
1134
|
+
{ label: t('chat.history.chooseAll'), value: ALL_COLUMNS_VALUE },
|
|
1135
|
+
...realStatusOptions,
|
|
1136
|
+
], [t, realStatusOptions]);
|
|
1137
|
+
|
|
1138
|
+
const domainOptions = useMemo(() => [
|
|
1139
|
+
{ label: t('chat.history.chooseAll'), value: ALL_COLUMNS_VALUE },
|
|
1140
|
+
...currentDomains.map((domain) => ({
|
|
1141
|
+
label: domain,
|
|
1142
|
+
value: domain,
|
|
1143
|
+
})),
|
|
1144
|
+
], [t, currentDomains]);
|
|
1145
|
+
|
|
1146
|
+
const realThemeOptions = tableHeaderQualitySettingsConfigQuery.data?.chatAnalysisTheme.map(item => {
|
|
1147
|
+
return { label: item, value: item }
|
|
1148
|
+
}) ?? [];
|
|
1149
|
+
const realResponseQualityOptions = tableHeaderQualitySettingsConfigQuery.data?.chatAnalysisBykResponseQuality.map(item => {
|
|
1150
|
+
return { label: item, value: item }
|
|
1151
|
+
}) ?? [];
|
|
1152
|
+
const realFollowUpStatusOptions = tableHeaderQualitySettingsConfigQuery.data?.chatAnalysisFollowUpAction.map(item => {
|
|
1153
|
+
return { label: item, value: item }
|
|
1154
|
+
}) ?? [];
|
|
1155
|
+
|
|
1156
|
+
const themeOptions = useMemo(() => [
|
|
1157
|
+
{ label: t('chat.history.chooseAll'), value: ALL_COLUMNS_VALUE },
|
|
1158
|
+
...realThemeOptions,
|
|
1159
|
+
], [t, realThemeOptions]);
|
|
1160
|
+
|
|
1161
|
+
const responseQualityOptions = useMemo(() => [
|
|
1162
|
+
{ label: t('chat.history.chooseAll'), value: ALL_COLUMNS_VALUE },
|
|
1163
|
+
...realResponseQualityOptions,
|
|
1164
|
+
], [t, realResponseQualityOptions]);
|
|
1165
|
+
|
|
1166
|
+
const followUpStatusOptions = useMemo(() => [
|
|
1167
|
+
{ label: t('chat.history.chooseAll'), value: ALL_COLUMNS_VALUE },
|
|
1168
|
+
...realFollowUpStatusOptions,
|
|
1169
|
+
], [t, realFollowUpStatusOptions]);
|
|
1170
|
+
|
|
1171
|
+
const feedbackRatingFilterValues = getRealStringFilterValues(feedbackRatings);
|
|
1172
|
+
const statusFilterValues = getRealStringFilterValues(status);
|
|
1173
|
+
const domainFilterValues = getRealStringFilterValues(domains);
|
|
1174
|
+
const themeFilterValues = getRealStringFilterValues(theme);
|
|
1175
|
+
const responseQualityFilterValues = getRealStringFilterValues(responseQuality);
|
|
1176
|
+
const followUpStatusFilterValues = getRealStringFilterValues(followUpStatus);
|
|
1177
|
+
|
|
1178
|
+
const endedChatsFilterBody = useMemo(() => {
|
|
1179
|
+
const currentCustomerSupportIds = getRealCsaFilterValues(csaIdCodesFilter);
|
|
1180
|
+
const currentFeedbackRatings = getFeedbackRatingFilterValues(feedbackRatings);
|
|
1181
|
+
const currentStatusValues = getRealStringFilterValues(status);
|
|
1182
|
+
const currentDomainValues = getRealStringFilterValues(domains);
|
|
1183
|
+
const currentIsTestValues = getBooleanApiFilterValue(isTestFilter);
|
|
1184
|
+
const currentShowAuthenticatedPersonValues = getBooleanApiFilterValue(showAuthenticatedPerson);
|
|
1185
|
+
const currentIsPreserveValues = getBooleanApiFilterValue(isPreserveFilter);
|
|
1186
|
+
const currentThemeValues = getRealStringFilterValues(theme);
|
|
1187
|
+
const currentResponseQualityValues = getRealStringFilterValues(responseQuality);
|
|
1188
|
+
const currentFollowUpStatusValues = getRealStringFilterValues(followUpStatus);
|
|
1189
|
+
|
|
1190
|
+
return {
|
|
1191
|
+
urls: getDomainsArray(currentDomainValues.length > 0 ? currentDomainValues : currentDomains),
|
|
1192
|
+
showTest: showTest,
|
|
1193
|
+
theme: currentThemeValues,
|
|
1194
|
+
responseQuality: currentResponseQualityValues,
|
|
1195
|
+
followUpStatus: currentFollowUpStatusValues,
|
|
1196
|
+
...(currentCustomerSupportIds.length > 0 && {customerSupportIds: currentCustomerSupportIds}),
|
|
1197
|
+
...(currentFeedbackRatings.length > 0 && {feedbackRatings: currentFeedbackRatings}),
|
|
1198
|
+
...(currentIsTestValues.length > 0 && {isTest: currentIsTestValues}),
|
|
1199
|
+
...(currentShowAuthenticatedPersonValues.length > 0 && {authenticatedChats: currentShowAuthenticatedPersonValues}),
|
|
1200
|
+
...(currentIsPreserveValues.length > 0 && {isPreserve: currentIsPreserveValues}),
|
|
1201
|
+
...(currentStatusValues.length > 0 && {status: currentStatusValues}),
|
|
1202
|
+
};
|
|
1203
|
+
}, [
|
|
1204
|
+
csaIdCodesFilter,
|
|
1205
|
+
currentDomains,
|
|
1206
|
+
domains,
|
|
1207
|
+
feedbackRatings,
|
|
1208
|
+
followUpStatus,
|
|
1209
|
+
isPreserveFilter,
|
|
1210
|
+
isTestFilter,
|
|
1211
|
+
responseQuality,
|
|
1212
|
+
showAuthenticatedPerson,
|
|
1213
|
+
showTest,
|
|
1214
|
+
status,
|
|
1215
|
+
theme,
|
|
1216
|
+
]);
|
|
1217
|
+
|
|
1218
|
+
const booleanFilterOptions = useMemo(() => [
|
|
1219
|
+
{ label: t('global.yes') ?? '', value: 'true' },
|
|
1220
|
+
{ label: t('global.no') ?? '', value: 'false' },
|
|
1221
|
+
], [t]);
|
|
1222
|
+
|
|
735
1223
|
const endedChatsColumns = useMemo(() => {
|
|
736
1224
|
const columns = [
|
|
737
1225
|
columnHelper.accessor('created', {
|
|
@@ -774,7 +1262,20 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
774
1262
|
},
|
|
775
1263
|
{
|
|
776
1264
|
id: `customerSupportFullName`,
|
|
777
|
-
header:
|
|
1265
|
+
header: () => {
|
|
1266
|
+
return (
|
|
1267
|
+
<HeaderCombobox
|
|
1268
|
+
label={t('chat.history.csaName')}
|
|
1269
|
+
options={customerSupportAgentsQuery.data ?? []}
|
|
1270
|
+
value={csaIdCodesFilter}
|
|
1271
|
+
onChange={(value) => {
|
|
1272
|
+
const normalizedValue = normalizeCsaFilterValues(value);
|
|
1273
|
+
tableHeaderForm.setValue('csaIdCodesFilter', normalizedValue);
|
|
1274
|
+
}}
|
|
1275
|
+
/>
|
|
1276
|
+
);
|
|
1277
|
+
},
|
|
1278
|
+
sortDescFirst: false,
|
|
778
1279
|
}
|
|
779
1280
|
),
|
|
780
1281
|
columnHelper.accessor(
|
|
@@ -784,30 +1285,134 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
784
1285
|
},
|
|
785
1286
|
{
|
|
786
1287
|
id: 'authenticatedPerson',
|
|
787
|
-
header:
|
|
1288
|
+
header: () => {
|
|
1289
|
+
return (
|
|
1290
|
+
<HeaderCombobox
|
|
1291
|
+
label={t('chat.history.authenticatedPerson') ?? ''}
|
|
1292
|
+
options={booleanFilterOptions}
|
|
1293
|
+
value={getBooleanComboboxValue(showAuthenticatedPerson)}
|
|
1294
|
+
onChange={(value) => {
|
|
1295
|
+
setTableHeaderValue('showAuthenticatedPerson', getBooleanFormValue(value));
|
|
1296
|
+
}}
|
|
1297
|
+
multiple={false}
|
|
1298
|
+
isSearchEnabled={false}
|
|
1299
|
+
/>
|
|
1300
|
+
);
|
|
1301
|
+
},
|
|
1302
|
+
sortDescFirst: false,
|
|
788
1303
|
}
|
|
789
1304
|
),
|
|
1305
|
+
...showEmail ? [
|
|
1306
|
+
columnHelper.accessor('endUserEmail', {
|
|
1307
|
+
id: 'endUserEmail',
|
|
1308
|
+
header: t('global.email'),
|
|
1309
|
+
sortDescFirst: false,
|
|
1310
|
+
})
|
|
1311
|
+
] : [],
|
|
1312
|
+
...testMessageEnabled ? [
|
|
1313
|
+
columnHelper.accessor('istest', {
|
|
1314
|
+
id: 'istest',
|
|
1315
|
+
header: () => {
|
|
1316
|
+
return (
|
|
1317
|
+
<HeaderCombobox
|
|
1318
|
+
label={t('global.test')}
|
|
1319
|
+
options={booleanFilterOptions}
|
|
1320
|
+
value={getBooleanComboboxValue(isTestFilter)}
|
|
1321
|
+
onChange={(value) => {
|
|
1322
|
+
setTableHeaderValue('isTestFilter', getBooleanFormValue(value));
|
|
1323
|
+
}}
|
|
1324
|
+
multiple={false}
|
|
1325
|
+
isSearchEnabled={false}
|
|
1326
|
+
/>
|
|
1327
|
+
);
|
|
1328
|
+
},
|
|
1329
|
+
cell: markConversationAsTest,
|
|
1330
|
+
enableSorting: false,
|
|
1331
|
+
sortDescFirst: false,
|
|
1332
|
+
})
|
|
1333
|
+
] : [],
|
|
1334
|
+
columnHelper.accessor('isPreserve', {
|
|
1335
|
+
id: 'isPreserve',
|
|
1336
|
+
header: () => {
|
|
1337
|
+
return (
|
|
1338
|
+
<HeaderCombobox
|
|
1339
|
+
label={t('global.preserve')}
|
|
1340
|
+
options={booleanFilterOptions}
|
|
1341
|
+
value={getBooleanComboboxValue(isPreserveFilter)}
|
|
1342
|
+
onChange={(value) => {
|
|
1343
|
+
setTableHeaderValue('isPreserveFilter', getBooleanFormValue(value));
|
|
1344
|
+
}}
|
|
1345
|
+
multiple={false}
|
|
1346
|
+
isSearchEnabled={false}
|
|
1347
|
+
/>
|
|
1348
|
+
);
|
|
1349
|
+
},
|
|
1350
|
+
cell: markConversationAsPreserve,
|
|
1351
|
+
sortDescFirst: false,
|
|
1352
|
+
}),
|
|
790
1353
|
columnHelper.accessor('comment', {
|
|
791
1354
|
id: 'comment',
|
|
792
1355
|
header: t('chat.history.comment') ?? '',
|
|
793
1356
|
cell: commentView,
|
|
1357
|
+
enableSorting: false,
|
|
1358
|
+
sortDescFirst: false,
|
|
794
1359
|
}),
|
|
795
1360
|
columnHelper.accessor('feedbackRating', {
|
|
796
1361
|
id: 'feedbackRating',
|
|
797
|
-
header:
|
|
1362
|
+
header: () => {
|
|
1363
|
+
return (
|
|
1364
|
+
<HeaderCombobox
|
|
1365
|
+
label={t('chat.history.rating') ?? ''}
|
|
1366
|
+
options={ratingOptions}
|
|
1367
|
+
value={feedbackRatings}
|
|
1368
|
+
onChange={(value) => {
|
|
1369
|
+
setTableHeaderValue(
|
|
1370
|
+
'feedbackRatings',
|
|
1371
|
+
normalizeAllOptionFilterValues(
|
|
1372
|
+
value,
|
|
1373
|
+
feedbackRatings,
|
|
1374
|
+
getAllStringFilterValues(realRatingOptions)
|
|
1375
|
+
)
|
|
1376
|
+
);
|
|
1377
|
+
}}
|
|
1378
|
+
/>
|
|
1379
|
+
);
|
|
1380
|
+
},
|
|
798
1381
|
cell: (props) => {
|
|
799
1382
|
const value = props.getValue();
|
|
800
1383
|
return value !== null && value !== undefined ? <span>{`${value}/${props.row.original?.isFiveRatingScale === 'true' ? 5 : 10}`}</span> : null;
|
|
801
|
-
}
|
|
1384
|
+
},
|
|
1385
|
+
sortDescFirst: false,
|
|
802
1386
|
}),
|
|
803
1387
|
columnHelper.accessor('feedbackText', {
|
|
804
1388
|
id: 'feedbackText',
|
|
805
|
-
header: t('chat.history.feedback')
|
|
1389
|
+
header: t('chat.history.feedback'),
|
|
806
1390
|
cell: feedbackTextView,
|
|
1391
|
+
enableSorting: false,
|
|
1392
|
+
sortDescFirst: false,
|
|
807
1393
|
}),
|
|
808
1394
|
columnHelper.accessor('status', {
|
|
809
1395
|
id: 'status',
|
|
810
|
-
header:
|
|
1396
|
+
header: () => {
|
|
1397
|
+
return (
|
|
1398
|
+
<HeaderCombobox
|
|
1399
|
+
label={t('global.status') ?? ''}
|
|
1400
|
+
options={statusOptions}
|
|
1401
|
+
value={status}
|
|
1402
|
+
onChange={(value) => {
|
|
1403
|
+
setTableHeaderValue(
|
|
1404
|
+
'status',
|
|
1405
|
+
normalizeAllOptionFilterValues(
|
|
1406
|
+
value,
|
|
1407
|
+
status,
|
|
1408
|
+
getAllStringFilterValues(realStatusOptions)
|
|
1409
|
+
)
|
|
1410
|
+
);
|
|
1411
|
+
}}
|
|
1412
|
+
isSearchEnabled={true}
|
|
1413
|
+
/>
|
|
1414
|
+
);
|
|
1415
|
+
},
|
|
811
1416
|
cell: statusView,
|
|
812
1417
|
sortingFn: (a, b, isAsc) => {
|
|
813
1418
|
const statusA =
|
|
@@ -825,17 +1430,118 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
825
1430
|
}) * (isAsc ? 1 : -1)
|
|
826
1431
|
);
|
|
827
1432
|
},
|
|
1433
|
+
sortDescFirst: false,
|
|
828
1434
|
}),
|
|
829
1435
|
columnHelper.accessor('id', {
|
|
830
1436
|
id: 'id',
|
|
831
1437
|
header: 'ID',
|
|
832
1438
|
cell: idView,
|
|
1439
|
+
enableSorting: false,
|
|
1440
|
+
sortDescFirst: false,
|
|
833
1441
|
}),
|
|
834
1442
|
columnHelper.accessor('endUserUrl', {
|
|
835
1443
|
id: 'www',
|
|
836
|
-
header:
|
|
1444
|
+
header: () => {
|
|
1445
|
+
return (
|
|
1446
|
+
<HeaderCombobox
|
|
1447
|
+
label={t('chat.history.www') ?? ''}
|
|
1448
|
+
value={domains}
|
|
1449
|
+
options={domainOptions}
|
|
1450
|
+
onChange={(value) => {
|
|
1451
|
+
setTableHeaderValue(
|
|
1452
|
+
'domains',
|
|
1453
|
+
normalizeAllOptionFilterValues(value, domains, currentDomains)
|
|
1454
|
+
);
|
|
1455
|
+
}}
|
|
1456
|
+
/>
|
|
1457
|
+
);
|
|
1458
|
+
},
|
|
837
1459
|
cell: wwwView,
|
|
1460
|
+
sortDescFirst: false,
|
|
838
1461
|
}),
|
|
1462
|
+
...isChatAnalysisEnabled ? [
|
|
1463
|
+
columnHelper.accessor(
|
|
1464
|
+
(row) => formatChatAnalysisCell(
|
|
1465
|
+
row.theme,
|
|
1466
|
+
t('chat.quality.selectionEmptied')
|
|
1467
|
+
),
|
|
1468
|
+
{
|
|
1469
|
+
id: 'theme',
|
|
1470
|
+
header: () => (
|
|
1471
|
+
<HeaderCombobox
|
|
1472
|
+
label={t('chat.history.theme')}
|
|
1473
|
+
options={themeOptions}
|
|
1474
|
+
value={theme}
|
|
1475
|
+
onChange={(value) => {
|
|
1476
|
+
setTableHeaderValue(
|
|
1477
|
+
'theme',
|
|
1478
|
+
normalizeAllOptionFilterValues(
|
|
1479
|
+
value,
|
|
1480
|
+
theme,
|
|
1481
|
+
getAllStringFilterValues(realThemeOptions)
|
|
1482
|
+
)
|
|
1483
|
+
);
|
|
1484
|
+
}}
|
|
1485
|
+
/>
|
|
1486
|
+
),
|
|
1487
|
+
enableSorting: true,
|
|
1488
|
+
}
|
|
1489
|
+
),
|
|
1490
|
+
columnHelper.accessor(
|
|
1491
|
+
(row) => formatChatAnalysisCell(
|
|
1492
|
+
row.responseQuality,
|
|
1493
|
+
t('chat.quality.selectionEmptied')
|
|
1494
|
+
),
|
|
1495
|
+
{
|
|
1496
|
+
id: 'responseQuality',
|
|
1497
|
+
header: () => (
|
|
1498
|
+
<HeaderCombobox
|
|
1499
|
+
label={t('chat.history.responseQuality')}
|
|
1500
|
+
options={responseQualityOptions}
|
|
1501
|
+
value={responseQuality}
|
|
1502
|
+
onChange={(value) => {
|
|
1503
|
+
setTableHeaderValue(
|
|
1504
|
+
'responseQuality',
|
|
1505
|
+
normalizeAllOptionFilterValues(
|
|
1506
|
+
value,
|
|
1507
|
+
responseQuality,
|
|
1508
|
+
getAllStringFilterValues(realResponseQualityOptions)
|
|
1509
|
+
)
|
|
1510
|
+
);
|
|
1511
|
+
}}
|
|
1512
|
+
/>
|
|
1513
|
+
),
|
|
1514
|
+
enableSorting: true,
|
|
1515
|
+
}
|
|
1516
|
+
),
|
|
1517
|
+
columnHelper.accessor(
|
|
1518
|
+
(row) => formatChatAnalysisCell(
|
|
1519
|
+
row.followUpStatus,
|
|
1520
|
+
t('chat.quality.selectionEmptied')
|
|
1521
|
+
),
|
|
1522
|
+
{
|
|
1523
|
+
id: 'followUpStatus',
|
|
1524
|
+
header: () => (
|
|
1525
|
+
<HeaderCombobox
|
|
1526
|
+
label={t('chat.history.followUpStatus')}
|
|
1527
|
+
options={followUpStatusOptions}
|
|
1528
|
+
value={followUpStatus}
|
|
1529
|
+
onChange={(value) => {
|
|
1530
|
+
setTableHeaderValue(
|
|
1531
|
+
'followUpStatus',
|
|
1532
|
+
normalizeAllOptionFilterValues(
|
|
1533
|
+
value,
|
|
1534
|
+
followUpStatus,
|
|
1535
|
+
getAllStringFilterValues(realFollowUpStatusOptions)
|
|
1536
|
+
)
|
|
1537
|
+
);
|
|
1538
|
+
}}
|
|
1539
|
+
/>
|
|
1540
|
+
),
|
|
1541
|
+
enableSorting: true,
|
|
1542
|
+
}
|
|
1543
|
+
),
|
|
1544
|
+
] : [],
|
|
839
1545
|
columnHelper.display({
|
|
840
1546
|
id: 'detail',
|
|
841
1547
|
cell: detailsView,
|
|
@@ -846,29 +1552,37 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
846
1552
|
}),
|
|
847
1553
|
];
|
|
848
1554
|
|
|
849
|
-
if (showEmail) {
|
|
850
|
-
columns.splice(4, 0, columnHelper.accessor('endUserEmail', {
|
|
851
|
-
id: 'endUserEmail',
|
|
852
|
-
header: t('global.email') ?? '',
|
|
853
|
-
}));
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
if (testMessageEnabled) {
|
|
857
|
-
columns.splice(4, 0, columnHelper.accessor('istest', {
|
|
858
|
-
id: 'istest',
|
|
859
|
-
header: t('global.test') ?? '',
|
|
860
|
-
cell: markConversationAsTest
|
|
861
|
-
}));
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
columns.splice(4, 0, columnHelper.accessor('isPreserve', {
|
|
865
|
-
id: 'isPreserve',
|
|
866
|
-
header: t('global.preserve') ?? '',
|
|
867
|
-
cell: markConversationAsPreserve
|
|
868
|
-
}));
|
|
869
|
-
|
|
870
1555
|
return columns;
|
|
871
|
-
}, [
|
|
1556
|
+
}, [
|
|
1557
|
+
t,
|
|
1558
|
+
showEmail,
|
|
1559
|
+
testMessageEnabled,
|
|
1560
|
+
customerSupportAgentsQuery.data,
|
|
1561
|
+
csaIdCodesFilter,
|
|
1562
|
+
feedbackRatings,
|
|
1563
|
+
showAuthenticatedPerson,
|
|
1564
|
+
isTestFilter,
|
|
1565
|
+
isPreserveFilter,
|
|
1566
|
+
domains,
|
|
1567
|
+
status,
|
|
1568
|
+
theme,
|
|
1569
|
+
responseQuality,
|
|
1570
|
+
followUpStatus,
|
|
1571
|
+
statusOptions,
|
|
1572
|
+
realStatusOptions,
|
|
1573
|
+
themeOptions,
|
|
1574
|
+
responseQualityOptions,
|
|
1575
|
+
followUpStatusOptions,
|
|
1576
|
+
realThemeOptions,
|
|
1577
|
+
realResponseQualityOptions,
|
|
1578
|
+
realFollowUpStatusOptions,
|
|
1579
|
+
currentDomains,
|
|
1580
|
+
ratingOptions,
|
|
1581
|
+
realRatingOptions,
|
|
1582
|
+
domainOptions,
|
|
1583
|
+
booleanFilterOptions,
|
|
1584
|
+
isChatAnalysisEnabled,
|
|
1585
|
+
])
|
|
872
1586
|
|
|
873
1587
|
const getSortingString = () => {
|
|
874
1588
|
if (sorting && sorting.length > 0) {
|
|
@@ -904,6 +1618,12 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
904
1618
|
return t('chat.history.rating') ?? ''
|
|
905
1619
|
case 'feedbackText':
|
|
906
1620
|
return t('chat.history.feedback') ?? ''
|
|
1621
|
+
case 'theme':
|
|
1622
|
+
return t('chat.history.theme') ?? ''
|
|
1623
|
+
case 'responseQuality':
|
|
1624
|
+
return t('chat.history.responseQuality') ?? ''
|
|
1625
|
+
case 'followUpStatus':
|
|
1626
|
+
return t('chat.history.followUpStatus') ?? ''
|
|
907
1627
|
case 'status':
|
|
908
1628
|
return t('global.status') ?? ''
|
|
909
1629
|
case 'endUserUrl':
|
|
@@ -1032,15 +1752,10 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
1032
1752
|
return {headers, rows, chatIds};
|
|
1033
1753
|
};
|
|
1034
1754
|
|
|
1035
|
-
|
|
1036
1755
|
const downloadChatHistory = async () => {
|
|
1037
1756
|
setLoading(true);
|
|
1038
1757
|
try {
|
|
1039
|
-
|
|
1040
|
-
if (sorting.length > 0) {
|
|
1041
|
-
const sortType = sorting[0].desc ? 'desc' : 'asc';
|
|
1042
|
-
sortBy = `${sorting[0].id} ${sortType}`;
|
|
1043
|
-
}
|
|
1758
|
+
const sortBy = getEndedChatsSortBy(sorting);
|
|
1044
1759
|
|
|
1045
1760
|
const realSelectedColumns = getRealSelectedColumns(selectedColumns);
|
|
1046
1761
|
const { headers } = mapChatsToExportRows([], endedChatsColumns, realSelectedColumns, t);
|
|
@@ -1061,7 +1776,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
1061
1776
|
language: i18n.language,
|
|
1062
1777
|
startDate: formatISO(startOfDay(new Date(startDate))),
|
|
1063
1778
|
endDate: formatISO(endOfDay(new Date(endDate))),
|
|
1064
|
-
|
|
1779
|
+
...endedChatsFilterBody,
|
|
1065
1780
|
sorting: sortBy,
|
|
1066
1781
|
search,
|
|
1067
1782
|
});
|
|
@@ -1079,23 +1794,147 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
1079
1794
|
}
|
|
1080
1795
|
};
|
|
1081
1796
|
|
|
1082
|
-
const endUserFullName = getUserName();
|
|
1083
|
-
|
|
1084
|
-
const isClearFiltersVisible = useMemo(()=> {
|
|
1085
|
-
return search.length > 0 || selectedColumns.length > 0;
|
|
1086
|
-
}, [search, selectedColumns]);
|
|
1087
|
-
|
|
1088
1797
|
const onClearFilersClick = () => {
|
|
1089
|
-
const clearedColumns: string[] = [];
|
|
1090
|
-
setSelectedColumns(clearedColumns);
|
|
1091
1798
|
setCounterKey(0);
|
|
1092
1799
|
setValue('search', '');
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1800
|
+
resetTableHeaderForm({
|
|
1801
|
+
csaIdCodesFilter: [],
|
|
1802
|
+
feedbackRatings: [],
|
|
1803
|
+
showAuthenticatedPerson: undefined,
|
|
1804
|
+
isTestFilter: undefined,
|
|
1805
|
+
isPreserveFilter: undefined,
|
|
1806
|
+
domains: [],
|
|
1807
|
+
status: [],
|
|
1808
|
+
theme: [],
|
|
1809
|
+
responseQuality: [],
|
|
1810
|
+
followUpStatus: [],
|
|
1811
|
+
});
|
|
1812
|
+
};
|
|
1813
|
+
|
|
1814
|
+
const chatQualityMeasurementQuery = useQuery({
|
|
1815
|
+
queryKey: ['chats/quality/measurements', selectedChat?.id],
|
|
1816
|
+
queryFn: () => getQualityMeasurements({ chatUuid: selectedChat!.id }),
|
|
1817
|
+
enabled: !!selectedChat?.id,
|
|
1818
|
+
});
|
|
1819
|
+
|
|
1820
|
+
const selectedQualityMeasurements = useMemo(() => {
|
|
1821
|
+
const measurements = chatQualityMeasurementQuery.data?.data.response ?? [];
|
|
1822
|
+
|
|
1823
|
+
const latest = {
|
|
1824
|
+
theme: {
|
|
1825
|
+
time: Number.NEGATIVE_INFINITY,
|
|
1826
|
+
values: [] as string[],
|
|
1827
|
+
},
|
|
1828
|
+
quality: {
|
|
1829
|
+
time: Number.NEGATIVE_INFINITY,
|
|
1830
|
+
value: '',
|
|
1831
|
+
},
|
|
1832
|
+
followUp: {
|
|
1833
|
+
time: Number.NEGATIVE_INFINITY,
|
|
1834
|
+
value: '',
|
|
1835
|
+
},
|
|
1836
|
+
};
|
|
1837
|
+
|
|
1838
|
+
measurements.forEach(({ type, value, createdAt }) => {
|
|
1839
|
+
const parsedTime = Date.parse(createdAt);
|
|
1840
|
+
const time = Number.isNaN(parsedTime) ? 0 : parsedTime;
|
|
1841
|
+
|
|
1842
|
+
if (type === MEASUREMENT_TYPES.THEME) {
|
|
1843
|
+
if (time > latest.theme.time) {
|
|
1844
|
+
latest.theme.time = time;
|
|
1845
|
+
latest.theme.values = value ? [value] : [];
|
|
1846
|
+
} else if (time === latest.theme.time && value) {
|
|
1847
|
+
latest.theme.values.push(value);
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
|
|
1854
|
+
if (type === MEASUREMENT_TYPES.QUALITY && time >= latest.quality.time) {
|
|
1855
|
+
latest.quality.time = time;
|
|
1856
|
+
latest.quality.value = value;
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
if (type === MEASUREMENT_TYPES.FOLLOW_UP_ACTION && time >= latest.followUp.time) {
|
|
1861
|
+
latest.followUp.time = time;
|
|
1862
|
+
latest.followUp.value = value;
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1096
1865
|
});
|
|
1866
|
+
|
|
1867
|
+
return {
|
|
1868
|
+
theme: latest.theme.values,
|
|
1869
|
+
quality: latest.quality.value,
|
|
1870
|
+
followUp: latest.followUp.value,
|
|
1871
|
+
};
|
|
1872
|
+
}, [chatQualityMeasurementQuery.data]);
|
|
1873
|
+
|
|
1874
|
+
const chatQualityMeasurementMutation = useMutation({
|
|
1875
|
+
mutationFn: postQualityMeasurements,
|
|
1876
|
+
onSuccess: (_data, variables) => {
|
|
1877
|
+
queryClient.invalidateQueries({
|
|
1878
|
+
queryKey: ['chats/quality/measurements', variables.chatUuid],
|
|
1879
|
+
});
|
|
1880
|
+
},
|
|
1881
|
+
onError: (error: AxiosError) => {
|
|
1882
|
+
toast?.open({
|
|
1883
|
+
type: 'error',
|
|
1884
|
+
title: t('global.notificationError'),
|
|
1885
|
+
message: error.message,
|
|
1886
|
+
});
|
|
1887
|
+
},
|
|
1888
|
+
});
|
|
1889
|
+
|
|
1890
|
+
const saveChatQualityMeasurement = async (
|
|
1891
|
+
type: MeasurementType,
|
|
1892
|
+
value: string | string[],
|
|
1893
|
+
successMessage: string
|
|
1894
|
+
) => {
|
|
1895
|
+
if (!selectedChat?.id) return;
|
|
1896
|
+
|
|
1897
|
+
try {
|
|
1898
|
+
await chatQualityMeasurementMutation.mutateAsync({
|
|
1899
|
+
chatUuid: selectedChat.id,
|
|
1900
|
+
type,
|
|
1901
|
+
value,
|
|
1902
|
+
});
|
|
1903
|
+
|
|
1904
|
+
toast?.open({
|
|
1905
|
+
type: 'success',
|
|
1906
|
+
title: t('global.notification'),
|
|
1907
|
+
message: successMessage,
|
|
1908
|
+
});
|
|
1909
|
+
} catch {
|
|
1910
|
+
// Error toast is handled by the mutation's onError callback.
|
|
1911
|
+
}
|
|
1097
1912
|
};
|
|
1098
1913
|
|
|
1914
|
+
const onChatThemeChange = async (value: string[]) => {
|
|
1915
|
+
// TODO: array support in backend
|
|
1916
|
+
saveChatQualityMeasurement(
|
|
1917
|
+
MEASUREMENT_TYPES.THEME,
|
|
1918
|
+
value,
|
|
1919
|
+
t('toast.success.conversationThemeSaved')
|
|
1920
|
+
)
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
const onChatQualityChange = async (value: string) => {
|
|
1924
|
+
await saveChatQualityMeasurement(
|
|
1925
|
+
MEASUREMENT_TYPES.QUALITY,
|
|
1926
|
+
value,
|
|
1927
|
+
t('toast.success.conversationQualitySaved')
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
const onChatFollowUpChange = async (value: string) => {
|
|
1931
|
+
await saveChatQualityMeasurement(
|
|
1932
|
+
MEASUREMENT_TYPES.FOLLOW_UP_ACTION,
|
|
1933
|
+
value,
|
|
1934
|
+
t('toast.success.conversationFollowUpActionSaved')
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1099
1938
|
if (!filteredEndedChatsList) return <>Loading... {{filteredEndedChatsList}} something is wrong </>;
|
|
1100
1939
|
|
|
1101
1940
|
return (
|
|
@@ -1250,12 +2089,23 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
1250
2089
|
</Button>
|
|
1251
2090
|
</div>)
|
|
1252
2091
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
2092
|
+
<SelectedFilterTags
|
|
2093
|
+
csaFilterTagValues={csaFilterTagValues}
|
|
2094
|
+
getCsaFilterTagLabel={getCsaFilterTagLabel}
|
|
2095
|
+
showAuthenticatedPerson={showAuthenticatedPerson}
|
|
2096
|
+
showTestFilter={testMessageEnabled}
|
|
2097
|
+
isTestFilter={isTestFilter}
|
|
2098
|
+
isPreserveFilter={isPreserveFilter}
|
|
2099
|
+
domains={domainFilterValues}
|
|
2100
|
+
feedbackRatings={feedbackRatingFilterValues}
|
|
2101
|
+
status={statusFilterValues}
|
|
2102
|
+
theme={themeFilterValues}
|
|
2103
|
+
responseQuality={responseQualityFilterValues}
|
|
2104
|
+
followUpStatus={followUpStatusFilterValues}
|
|
2105
|
+
onRemove={removeSelectedFilterTag}
|
|
2106
|
+
onClearFiltersClick={onClearFilersClick}
|
|
2107
|
+
/>
|
|
2108
|
+
<div className="card-drawer-container" style={{height: '100%', overflow: 'auto', maxHeight: '60vh'}}>
|
|
1259
2109
|
<div className="card-wrapper">
|
|
1260
2110
|
<Card>
|
|
1261
2111
|
<DataTable
|
|
@@ -1329,141 +2179,37 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
1329
2179
|
/>
|
|
1330
2180
|
</Drawer>
|
|
1331
2181
|
</div>
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
<p>
|
|
1364
|
-
<strong>{t('chat.endUserPhoneNumber')}</strong>
|
|
1365
|
-
</p>
|
|
1366
|
-
<p>{selectedChat.endUserPhone}</p>
|
|
1367
|
-
</div>
|
|
1368
|
-
)}
|
|
1369
|
-
{selectedChat.customerSupportDisplayName && (
|
|
1370
|
-
<div>
|
|
1371
|
-
<p>
|
|
1372
|
-
<strong>{t('chat.csaName')}</strong>
|
|
1373
|
-
</p>
|
|
1374
|
-
<p>{selectedChat.customerSupportDisplayName}</p>
|
|
1375
|
-
</div>
|
|
1376
|
-
)}
|
|
1377
|
-
<div>
|
|
1378
|
-
<p>
|
|
1379
|
-
<strong>{t('chat.startedAt')}</strong>
|
|
1380
|
-
</p>
|
|
1381
|
-
<p>
|
|
1382
|
-
{format(
|
|
1383
|
-
new Date(selectedChat.created),
|
|
1384
|
-
'dd. MMMM Y HH:mm:ss',
|
|
1385
|
-
{
|
|
1386
|
-
locale: et,
|
|
1387
|
-
}
|
|
1388
|
-
).toLowerCase()}
|
|
1389
|
-
</p>
|
|
1390
|
-
</div>
|
|
1391
|
-
<div>
|
|
1392
|
-
<p>
|
|
1393
|
-
<strong>{t('chat.device')}</strong>
|
|
1394
|
-
</p>
|
|
1395
|
-
<p>{selectedChat.endUserOs}</p>
|
|
1396
|
-
</div>
|
|
1397
|
-
<div>
|
|
1398
|
-
<p>
|
|
1399
|
-
<strong>{t('chat.location')}</strong>
|
|
1400
|
-
</p>
|
|
1401
|
-
<p>{selectedChat.endUserUrl}</p>
|
|
1402
|
-
</div>
|
|
1403
|
-
{selectedChat.comment && (
|
|
1404
|
-
<div>
|
|
1405
|
-
<p>
|
|
1406
|
-
<strong>{t('chat.history.comment')}</strong>
|
|
1407
|
-
</p>
|
|
1408
|
-
<p>{selectedChat.comment}</p>
|
|
1409
|
-
</div>
|
|
1410
|
-
)}
|
|
1411
|
-
{selectedChat.commentAuthor && (
|
|
1412
|
-
<div>
|
|
1413
|
-
<p>
|
|
1414
|
-
<strong>{t('chat.history.commentAuthor')}</strong>
|
|
1415
|
-
</p>
|
|
1416
|
-
<p>{selectedChat.commentAuthor}</p>
|
|
1417
|
-
</div>
|
|
1418
|
-
)}
|
|
1419
|
-
{selectedChat.commentAddedDate && (
|
|
1420
|
-
<div>
|
|
1421
|
-
<p>
|
|
1422
|
-
<strong>{t('chat.history.commentAddedDate')}</strong>
|
|
1423
|
-
</p>
|
|
1424
|
-
<p>
|
|
1425
|
-
{format(
|
|
1426
|
-
new Date(selectedChat.commentAddedDate),
|
|
1427
|
-
'dd.MM.yyyy'
|
|
1428
|
-
)}
|
|
1429
|
-
</p>
|
|
1430
|
-
</div>
|
|
1431
|
-
)}
|
|
1432
|
-
{selectedChat.lastMessageEvent && (
|
|
1433
|
-
<div>
|
|
1434
|
-
<p>
|
|
1435
|
-
<strong>{t('global.status')}</strong>
|
|
1436
|
-
</p>
|
|
1437
|
-
<p>
|
|
1438
|
-
{t('chat.plainEvents.' + selectedChat.lastMessageEvent)}
|
|
1439
|
-
</p>
|
|
1440
|
-
</div>
|
|
1441
|
-
)}
|
|
1442
|
-
{selectedChat.userDisplayName && (
|
|
1443
|
-
<div>
|
|
1444
|
-
<p>
|
|
1445
|
-
<strong>{t('chat.history.statusAdder')}</strong>
|
|
1446
|
-
</p>
|
|
1447
|
-
<p>{selectedChat.userDisplayName}</p>
|
|
1448
|
-
</div>
|
|
1449
|
-
)}
|
|
1450
|
-
{selectedChat.lastMessageTimestamp && (
|
|
1451
|
-
<div>
|
|
1452
|
-
<p>
|
|
1453
|
-
<strong>{t('chat.history.statusAddedDate')}</strong>
|
|
1454
|
-
</p>
|
|
1455
|
-
<p>
|
|
1456
|
-
{format(
|
|
1457
|
-
new Date(selectedChat.lastMessageTimestamp),
|
|
1458
|
-
'dd.MM.yyyy'
|
|
1459
|
-
)}
|
|
1460
|
-
</p>
|
|
1461
|
-
</div>
|
|
1462
|
-
)}
|
|
1463
|
-
</div>
|
|
1464
|
-
</>
|
|
1465
|
-
)}
|
|
1466
|
-
</div>
|
|
2182
|
+
<ChatMetadataPanel
|
|
2183
|
+
chat={selectedChat}
|
|
2184
|
+
chatMeasurments={chatQualityMeasurementQuery.data?.data.response ?? []}
|
|
2185
|
+
>
|
|
2186
|
+
<QualitySettings
|
|
2187
|
+
theme={{
|
|
2188
|
+
onChange: (value) => onChatThemeChange(value),
|
|
2189
|
+
options: qualitySettingsConfigQuery.data?.chatAnalysisTheme.map(item => {
|
|
2190
|
+
return { label: item, value: item }
|
|
2191
|
+
}) ?? [],
|
|
2192
|
+
value: selectedQualityMeasurements.theme,
|
|
2193
|
+
}}
|
|
2194
|
+
quality={{
|
|
2195
|
+
onChange: (value) => onChatQualityChange(value),
|
|
2196
|
+
options: qualitySettingsConfigQuery.data?.chatAnalysisBykResponseQuality.map(item => {
|
|
2197
|
+
return { label: item, value: item }
|
|
2198
|
+
}) ?? [],
|
|
2199
|
+
value: selectedQualityMeasurements.quality,
|
|
2200
|
+
}}
|
|
2201
|
+
followUp={{
|
|
2202
|
+
onChange: (value) => onChatFollowUpChange(value),
|
|
2203
|
+
options: qualitySettingsConfigQuery.data?.chatAnalysisFollowUpAction.map(item => {
|
|
2204
|
+
return { label: item, value: item }
|
|
2205
|
+
}) ?? [],
|
|
2206
|
+
value: selectedQualityMeasurements.followUp,
|
|
2207
|
+
}}
|
|
2208
|
+
/>
|
|
2209
|
+
</ChatMetadataPanel>
|
|
2210
|
+
</>
|
|
2211
|
+
)}
|
|
2212
|
+
</div>
|
|
1467
2213
|
</div>
|
|
1468
2214
|
|
|
1469
2215
|
{statusChangeModal && (
|
|
@@ -1498,13 +2244,6 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
1498
2244
|
)}
|
|
1499
2245
|
</div>
|
|
1500
2246
|
);
|
|
1501
|
-
|
|
1502
|
-
function getUserName() {
|
|
1503
|
-
return selectedChat?.endUserFirstName !== '' &&
|
|
1504
|
-
selectedChat?.endUserLastName !== ''
|
|
1505
|
-
? `${selectedChat?.endUserFirstName} ${selectedChat?.endUserLastName}`
|
|
1506
|
-
: t('global.anonymous');
|
|
1507
|
-
}
|
|
1508
2247
|
};
|
|
1509
2248
|
|
|
1510
2249
|
export default ChatHistory;
|