@buerokratt-ria/common-gui-components 0.0.24 → 0.0.26
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 +10 -0
- package/buerokratt-ria-common-gui-components-0.0.26.tgz +0 -0
- package/package.json +2 -1
- package/services/file.ts +31 -0
- package/templates/history-page/src/History.scss +34 -0
- package/templates/history-page/src/index.tsx +209 -14
- package/ui-components/FormElements/FormCheckbox/FormCheckbox.scss +16 -1
- package/ui-components/FormElements/FormCheckbox/index.tsx +8 -2
- package/utils/multiDomain-utils.ts +1 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## Template [MajorVersion.MediterraneanVersion.MinorVersion] - DD-MM-YYYY
|
|
6
6
|
|
|
7
|
+
## [0.0.26] - 15-09-2025
|
|
8
|
+
|
|
9
|
+
- Added download button do download chat history with currently selected criteriasa
|
|
10
|
+
- Added new optional param to enable this button(disabled by default)
|
|
11
|
+
|
|
12
|
+
## [0.0.25] - 01-09-2025
|
|
13
|
+
|
|
14
|
+
- Added test column to display chats mark for test
|
|
15
|
+
- FIX: refetch based on updated domains
|
|
16
|
+
|
|
7
17
|
## [0.0.24] - 14-08-2025
|
|
8
18
|
|
|
9
19
|
- Modified Start and End dates to send them in payload in iso format
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buerokratt-ria/common-gui-components",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "Common GUI components and pre defined templates.",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"author": "ExiRai",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"downshift": "^7.0.5",
|
|
30
30
|
"esbuild": "^0.19.5",
|
|
31
31
|
"framer-motion": "^8.5.5",
|
|
32
|
+
"file-saver": "^2.0.5",
|
|
32
33
|
"i18next": "^22.4.5",
|
|
33
34
|
"i18next-browser-languagedetector": "^7.0.1",
|
|
34
35
|
"linkify-react": "^4.1.1",
|
package/services/file.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
|
|
3
|
+
export const saveFile = async (base64String: string, fileName: `${string}.${string}`, type: string) => {
|
|
4
|
+
const blob = new Blob([Buffer.from(base64String, 'base64')], {
|
|
5
|
+
type: type,
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const extension = fileName.split('.').pop();
|
|
9
|
+
|
|
10
|
+
if (window.showSaveFilePicker) {
|
|
11
|
+
const handle = await window.showSaveFilePicker({
|
|
12
|
+
suggestedName: fileName,
|
|
13
|
+
types: [
|
|
14
|
+
{
|
|
15
|
+
description: extension!.toUpperCase() + ' file',
|
|
16
|
+
accept: { [type]: [`.${extension}` as `.${string}`] },
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
});
|
|
20
|
+
const writable = await handle.createWritable();
|
|
21
|
+
await writable.write(blob);
|
|
22
|
+
writable.close();
|
|
23
|
+
} else {
|
|
24
|
+
const url = window.URL.createObjectURL(blob);
|
|
25
|
+
const a = document.createElement('a');
|
|
26
|
+
a.href = url;
|
|
27
|
+
a.download = fileName;
|
|
28
|
+
a.click();
|
|
29
|
+
window.URL.revokeObjectURL(url);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@import 'src/styles/tools/spacing';
|
|
2
2
|
@import 'src/styles/tools/color';
|
|
3
|
+
@import 'src/styles/settings/variables/other';
|
|
3
4
|
@import 'src/styles/settings/variables/typography';
|
|
4
5
|
|
|
5
6
|
.input-wrapper {
|
|
@@ -13,6 +14,39 @@
|
|
|
13
14
|
box-sizing: border-box;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
.checkbox-test {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
|
|
22
|
+
&__item {
|
|
23
|
+
input[type=checkbox] {
|
|
24
|
+
&::before {
|
|
25
|
+
content: '';
|
|
26
|
+
display: block;
|
|
27
|
+
width: 16px;
|
|
28
|
+
height: 16px;
|
|
29
|
+
box-shadow: inset 0 0 0 1px get-color(black-coral-2);
|
|
30
|
+
border-radius: 2px;
|
|
31
|
+
position: absolute;
|
|
32
|
+
left: 4px;
|
|
33
|
+
top: 4px;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&:checked {
|
|
38
|
+
&::before {
|
|
39
|
+
background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTEiIHZpZXdCb3g9IjAgMCAxNCAxMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuNzQ5NzkgOC4xMjkwNkwxLjYyMjI5IDUuMDAxNTZMMC41NjEwMzUgNi4wNjI4MUw0Ljc0OTc5IDEwLjI1MTZMMTMuNzQ5OCAxLjI1MTU2TDEyLjY4ODUgMC4xOTAzMDhMNC43NDk3OSA4LjEyOTA2WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg==');
|
|
40
|
+
background-color: get-color(sapphire-blue-10);
|
|
41
|
+
background-repeat: no-repeat;
|
|
42
|
+
background-position: center;
|
|
43
|
+
background-size: 13px 10px;
|
|
44
|
+
box-shadow: inset 0 0 0 1px get-color(sapphire-blue-10);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
16
50
|
.card-wrapper {
|
|
17
51
|
flex: 1;
|
|
18
52
|
overflow: auto;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import React, {FC, PropsWithChildren, useEffect,
|
|
1
|
+
import React, {FC, PropsWithChildren, useEffect, useMemo, useRef, useState} from 'react';
|
|
2
2
|
import {useTranslation} from 'react-i18next';
|
|
3
3
|
import {useMutation} from '@tanstack/react-query';
|
|
4
4
|
import {ColumnPinningState, createColumnHelper, PaginationState, SortingState,} from '@tanstack/react-table';
|
|
5
|
-
import { format,
|
|
5
|
+
import {endOfDay, format, formatISO, startOfDay} from "date-fns";
|
|
6
6
|
import {AxiosError} from 'axios';
|
|
7
7
|
import './History.scss';
|
|
8
8
|
import {MdOutlineRemoveRedEye} from 'react-icons/md';
|
|
9
|
+
import {CgSpinner} from 'react-icons/cg';
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
Button,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
DataTable,
|
|
14
15
|
Dialog,
|
|
15
16
|
Drawer,
|
|
17
|
+
FormCheckbox,
|
|
16
18
|
FormDatepicker,
|
|
17
19
|
FormInput,
|
|
18
20
|
FormMultiselect,
|
|
@@ -34,6 +36,7 @@ import {ToastContextType} from "../../../context";
|
|
|
34
36
|
|
|
35
37
|
import {getDomainsArray} from "../../../utils/multiDomain-utils";
|
|
36
38
|
import {StoreState} from "../../../store";
|
|
39
|
+
import {saveFile} from "../../../services/file";
|
|
37
40
|
|
|
38
41
|
type HistoryProps = {
|
|
39
42
|
user: UserInfo | null;
|
|
@@ -41,6 +44,7 @@ type HistoryProps = {
|
|
|
41
44
|
toastContext: ToastContextType | null;
|
|
42
45
|
onMessageClick?: (message: any) => void;
|
|
43
46
|
showComment?: boolean;
|
|
47
|
+
showDownload?: boolean;
|
|
44
48
|
showEmail?: boolean;
|
|
45
49
|
showSortingLabel?: boolean;
|
|
46
50
|
showStatus?: boolean;
|
|
@@ -51,12 +55,19 @@ type HistoryProps = {
|
|
|
51
55
|
delegatedEndDate?: string;
|
|
52
56
|
}
|
|
53
57
|
|
|
58
|
+
type ExportResult = {
|
|
59
|
+
headers: string[];
|
|
60
|
+
rows: (string | number | null)[][];
|
|
61
|
+
chatIds: string[];
|
|
62
|
+
};
|
|
63
|
+
|
|
54
64
|
const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
55
65
|
user,
|
|
56
66
|
userDomains,
|
|
57
67
|
toastContext,
|
|
58
68
|
onMessageClick,
|
|
59
69
|
showComment = true,
|
|
70
|
+
showDownload = false,
|
|
60
71
|
showEmail = false,
|
|
61
72
|
showSortingLabel = false,
|
|
62
73
|
showStatus = true,
|
|
@@ -109,6 +120,8 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
109
120
|
const [updateKey, setUpdateKey] = useState<number>(0)
|
|
110
121
|
const currentDomains = useStore.getState().userDomains;
|
|
111
122
|
const multiDomainEnabled = import.meta.env.REACT_APP_ENABLE_MULTI_DOMAIN?.toLowerCase() === 'true';
|
|
123
|
+
const testMessageEnabled = import.meta.env.REACT_APP_SHOW_TEST_MESSAGE?.toLowerCase() === 'true';
|
|
124
|
+
const [loading, setLoading] = useState(false);
|
|
112
125
|
|
|
113
126
|
const parseDateParam = (dateString: string | null) => {
|
|
114
127
|
if (!dateString) return new Date();
|
|
@@ -143,9 +156,9 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
143
156
|
});
|
|
144
157
|
}, 500);
|
|
145
158
|
|
|
146
|
-
if(multiDomainEnabled) {
|
|
159
|
+
if (multiDomainEnabled) {
|
|
147
160
|
useStore.subscribe((state, prevState) => {
|
|
148
|
-
if(JSON.stringify(state.userDomains) !== JSON.stringify(prevState.userDomains)) {
|
|
161
|
+
if (JSON.stringify(state.userDomains) !== JSON.stringify(prevState.userDomains)) {
|
|
149
162
|
setUpdateKey(prevState => prevState + 1);
|
|
150
163
|
}
|
|
151
164
|
});
|
|
@@ -170,7 +183,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
170
183
|
setValue('endDate', unifyDateFromat(delegatedEndDate));
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
if(initialLoad) {
|
|
186
|
+
if (initialLoad) {
|
|
174
187
|
fetchData()
|
|
175
188
|
} else {
|
|
176
189
|
getAllEndedChats.mutate({
|
|
@@ -248,7 +261,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
248
261
|
search,
|
|
249
262
|
});
|
|
250
263
|
}
|
|
251
|
-
}, [selectedColumns]);
|
|
264
|
+
}, [selectedColumns, currentDomains]);
|
|
252
265
|
|
|
253
266
|
useEffect(() => {
|
|
254
267
|
listCustomerSupportAgents.mutate();
|
|
@@ -356,11 +369,15 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
356
369
|
];
|
|
357
370
|
|
|
358
371
|
if (showEmail) {
|
|
359
|
-
columns.splice(4, 0, {label: t('global.email'), value: 'endUserEmail'});
|
|
372
|
+
columns.splice(4, 0, {label: t('global.email'), value: 'endUserEmail'});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (testMessageEnabled) {
|
|
376
|
+
columns.splice(5, 0, {label: t('global.test'), value: 'istest'});
|
|
360
377
|
}
|
|
361
378
|
|
|
362
379
|
return columns;
|
|
363
|
-
}, [t, showEmail])
|
|
380
|
+
}, [t, showEmail, testMessageEnabled])
|
|
364
381
|
|
|
365
382
|
const chatStatusChangeMutation = useMutation({
|
|
366
383
|
mutationFn: async (data: { chatId: string | number; event: string }) => {
|
|
@@ -451,6 +468,31 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
451
468
|
},
|
|
452
469
|
});
|
|
453
470
|
|
|
471
|
+
const chatTestChangeMutation = useMutation({
|
|
472
|
+
mutationFn: (data: {
|
|
473
|
+
chatId: string | number;
|
|
474
|
+
isTest: boolean;
|
|
475
|
+
}) => apiDev.post('chats/mark-test', data),
|
|
476
|
+
onSuccess: (res, {chatId, isTest}) => {
|
|
477
|
+
const updatedChatList = filteredEndedChatsList.map((chat) =>
|
|
478
|
+
chat.id === chatId ? {...chat, isTest} : chat
|
|
479
|
+
);
|
|
480
|
+
filterChatsList(updatedChatList);
|
|
481
|
+
toast?.open({
|
|
482
|
+
type: 'success',
|
|
483
|
+
title: t('global.notification'),
|
|
484
|
+
message: t('toast.success.updateSuccess'),
|
|
485
|
+
});
|
|
486
|
+
},
|
|
487
|
+
onError: (error: AxiosError) => {
|
|
488
|
+
toast?.open({
|
|
489
|
+
type: 'error',
|
|
490
|
+
title: t('global.notificationError'),
|
|
491
|
+
message: error.message,
|
|
492
|
+
});
|
|
493
|
+
},
|
|
494
|
+
});
|
|
495
|
+
|
|
454
496
|
const columnHelper = createColumnHelper<ChatType>();
|
|
455
497
|
|
|
456
498
|
const copyValueToClipboard = async (value: string) => {
|
|
@@ -520,6 +562,26 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
520
562
|
</Tooltip>
|
|
521
563
|
);
|
|
522
564
|
|
|
565
|
+
const markConversationAsTest = (props: any) => {
|
|
566
|
+
return <>
|
|
567
|
+
<FormCheckbox
|
|
568
|
+
label={''}
|
|
569
|
+
hideLabel
|
|
570
|
+
emptyItem={true}
|
|
571
|
+
name="active"
|
|
572
|
+
|
|
573
|
+
item={{
|
|
574
|
+
label: '',
|
|
575
|
+
value: 'active',
|
|
576
|
+
checked: props.getValue()
|
|
577
|
+
}}
|
|
578
|
+
onChange={(e) => {
|
|
579
|
+
return chatTestChangeMutation.mutate({chatId: props.row.original.id, isTest: e.target.checked})
|
|
580
|
+
}}
|
|
581
|
+
/>
|
|
582
|
+
</>
|
|
583
|
+
}
|
|
584
|
+
|
|
523
585
|
const detailsView = (props: any) => (
|
|
524
586
|
<Button
|
|
525
587
|
appearance="text"
|
|
@@ -574,8 +636,8 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
574
636
|
}
|
|
575
637
|
},
|
|
576
638
|
{
|
|
577
|
-
|
|
578
|
-
|
|
639
|
+
id: `customerSupportFullName`,
|
|
640
|
+
header: t('chat.history.csaName') ?? '',
|
|
579
641
|
}
|
|
580
642
|
),
|
|
581
643
|
columnHelper.accessor(
|
|
@@ -660,11 +722,19 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
660
722
|
}));
|
|
661
723
|
}
|
|
662
724
|
|
|
725
|
+
if (testMessageEnabled) {
|
|
726
|
+
columns.splice(4, 0, columnHelper.accessor('istest', {
|
|
727
|
+
id: 'istest',
|
|
728
|
+
header: t('global.test') ?? '',
|
|
729
|
+
cell: markConversationAsTest
|
|
730
|
+
}));
|
|
731
|
+
}
|
|
732
|
+
|
|
663
733
|
return columns;
|
|
664
|
-
}, [t, showEmail])
|
|
734
|
+
}, [t, showEmail, testMessageEnabled])
|
|
665
735
|
|
|
666
736
|
const getSortingString = () => {
|
|
667
|
-
if(sorting && sorting.length > 0) {
|
|
737
|
+
if (sorting && sorting.length > 0) {
|
|
668
738
|
const sortingObject = sorting[0];
|
|
669
739
|
const sortingString = t('sorting.sorting');
|
|
670
740
|
const orderingString = t(`sorting.${sortingObject.desc ? 'desc' : 'asc'}`);
|
|
@@ -675,7 +745,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
675
745
|
}
|
|
676
746
|
}
|
|
677
747
|
|
|
678
|
-
const getColumnTranslation = (column: string)
|
|
748
|
+
const getColumnTranslation = (column: string): string => {
|
|
679
749
|
switch (column) {
|
|
680
750
|
case 'endUserId':
|
|
681
751
|
return t('global.idCode') ?? ''
|
|
@@ -703,6 +773,8 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
703
773
|
return 'www'
|
|
704
774
|
case 'id':
|
|
705
775
|
return 'id';
|
|
776
|
+
case 'istest':
|
|
777
|
+
return t('global.test') ?? ''
|
|
706
778
|
default:
|
|
707
779
|
return '';
|
|
708
780
|
}
|
|
@@ -749,6 +821,120 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
749
821
|
);
|
|
750
822
|
};
|
|
751
823
|
|
|
824
|
+
const mapChatsToExportRows = (
|
|
825
|
+
chats: ChatType[],
|
|
826
|
+
allColumns: any[],
|
|
827
|
+
selectedColumns: string[],
|
|
828
|
+
t: (key: string) => string
|
|
829
|
+
): ExportResult => {
|
|
830
|
+
const activeColumns =
|
|
831
|
+
selectedColumns.length > 0
|
|
832
|
+
? allColumns.filter(
|
|
833
|
+
(col) => col.id && col.id !== 'detail' && selectedColumns.includes(col.id)
|
|
834
|
+
)
|
|
835
|
+
: allColumns.filter((col) => col.id && col.id !== 'detail');
|
|
836
|
+
|
|
837
|
+
const headers = activeColumns.map(
|
|
838
|
+
(col) => getColumnTranslation(col.id) || col.header || col.id
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
const rows = chats.map((chat) =>
|
|
842
|
+
activeColumns.map((col) => {
|
|
843
|
+
let rawValue: any = null;
|
|
844
|
+
|
|
845
|
+
if (typeof col.accessorFn === 'function') {
|
|
846
|
+
rawValue = col.accessorFn(chat, 0);
|
|
847
|
+
} else if (typeof col.accessorKey === 'string') {
|
|
848
|
+
rawValue = (chat as any)[col.accessorKey];
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
let processedValue: any = rawValue;
|
|
852
|
+
switch (col.id) {
|
|
853
|
+
case 'created':
|
|
854
|
+
case 'ended':
|
|
855
|
+
processedValue = rawValue
|
|
856
|
+
? format(
|
|
857
|
+
new Date(rawValue),
|
|
858
|
+
'dd.MM.yyyy HH:mm:ss',
|
|
859
|
+
i18n.language === 'et' ? {locale: et} : undefined
|
|
860
|
+
)
|
|
861
|
+
: '';
|
|
862
|
+
break;
|
|
863
|
+
case 'contactsMessage':
|
|
864
|
+
processedValue = rawValue ? t('global.yes') : t('global.no');
|
|
865
|
+
break;
|
|
866
|
+
case 'feedbackRating':
|
|
867
|
+
processedValue = rawValue != null ? `${rawValue}/10` : '';
|
|
868
|
+
break;
|
|
869
|
+
case 'status':
|
|
870
|
+
processedValue =
|
|
871
|
+
chat.status === CHAT_STATUS.ENDED
|
|
872
|
+
? t('chat.plainEvents.' + (chat.lastMessageEvent ?? ''))
|
|
873
|
+
: '';
|
|
874
|
+
break;
|
|
875
|
+
case 'endUserName':
|
|
876
|
+
processedValue = `${chat.endUserFirstName ?? ''} ${chat.endUserLastName ?? ''}`;
|
|
877
|
+
break;
|
|
878
|
+
default:
|
|
879
|
+
processedValue =
|
|
880
|
+
typeof rawValue === 'object' ? JSON.stringify(rawValue) : rawValue ?? '';
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
return processedValue;
|
|
884
|
+
})
|
|
885
|
+
);
|
|
886
|
+
|
|
887
|
+
const chatIds = chats.map((c) => c.id);
|
|
888
|
+
|
|
889
|
+
return {headers, rows, chatIds};
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
const downloadChatHistory = async () => {
|
|
894
|
+
setLoading(true);
|
|
895
|
+
try {
|
|
896
|
+
let sortBy = 'created desc';
|
|
897
|
+
if (sorting.length > 0) {
|
|
898
|
+
const sortType = sorting[0].desc ? 'desc' : 'asc';
|
|
899
|
+
sortBy = `${sorting[0].id} ${sortType}`;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
const chats = await apiDev.post('agents/chats/ended', {
|
|
903
|
+
customerSupportIds: passedCustomerSupportIds,
|
|
904
|
+
startDate: formatISO(startOfDay(new Date(startDate))),
|
|
905
|
+
endDate: formatISO(endOfDay(new Date(endDate))),
|
|
906
|
+
urls: getDomainsArray(currentDomains),
|
|
907
|
+
page: 1,
|
|
908
|
+
page_size: 1000,
|
|
909
|
+
sorting: sortBy,
|
|
910
|
+
search,
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
const {headers, rows, chatIds} = mapChatsToExportRows(
|
|
914
|
+
chats.data.response,
|
|
915
|
+
endedChatsColumns,
|
|
916
|
+
selectedColumns,
|
|
917
|
+
t
|
|
918
|
+
);
|
|
919
|
+
|
|
920
|
+
const response = await apiDev.post('chats/ended/download', {
|
|
921
|
+
headers, rows, chatIds
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
await saveFile(
|
|
926
|
+
response.data.base64String,
|
|
927
|
+
'history.xlsx',
|
|
928
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
929
|
+
);
|
|
930
|
+
|
|
931
|
+
} catch (error) {
|
|
932
|
+
console.error('Error getting CSV file:', error);
|
|
933
|
+
} finally {
|
|
934
|
+
setLoading(false);
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
|
|
752
938
|
const endUserFullName = getUserName();
|
|
753
939
|
|
|
754
940
|
if (!filteredEndedChatsList) return <>Loading... {{filteredEndedChatsList}} something is wrong </>;
|
|
@@ -759,6 +945,15 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
759
945
|
<h1>{t('chat.history.title')}</h1>
|
|
760
946
|
)}
|
|
761
947
|
|
|
948
|
+
{showDownload && (
|
|
949
|
+
<div>
|
|
950
|
+
<Button appearance={"primary"} onClick={downloadChatHistory}>
|
|
951
|
+
{loading && <CgSpinner className="spinner"/>}
|
|
952
|
+
{!loading && t('files.download_xlsx')}
|
|
953
|
+
</Button>
|
|
954
|
+
</div>
|
|
955
|
+
)}
|
|
956
|
+
|
|
762
957
|
<Card>
|
|
763
958
|
<Track gap={16}>
|
|
764
959
|
{displaySearchBar && (
|
|
@@ -902,7 +1097,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
|
|
|
902
1097
|
</Button>
|
|
903
1098
|
</div>)
|
|
904
1099
|
}
|
|
905
|
-
<div className="card-drawer-container" style={{
|
|
1100
|
+
<div className="card-drawer-container" style={{height: '100%', overflow: 'auto'}}>
|
|
906
1101
|
<div className="card-wrapper">
|
|
907
1102
|
<Card>
|
|
908
1103
|
<DataTable
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
&__item {
|
|
20
|
+
position: relative;
|
|
21
|
+
|
|
20
22
|
input[type=checkbox] {
|
|
21
23
|
display: none;
|
|
22
24
|
|
|
@@ -53,5 +55,18 @@
|
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
}
|
|
58
|
+
|
|
59
|
+
&--nolabel {
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
width: 100%;
|
|
64
|
+
|
|
65
|
+
&::before {
|
|
66
|
+
position: static !important;
|
|
67
|
+
transform: none !important;
|
|
68
|
+
margin: 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
56
71
|
}
|
|
57
|
-
}
|
|
72
|
+
}
|
|
@@ -6,6 +6,7 @@ type FormCheckboxType = InputHTMLAttributes<HTMLInputElement> & {
|
|
|
6
6
|
label: string;
|
|
7
7
|
name: string;
|
|
8
8
|
hideLabel?: boolean;
|
|
9
|
+
emptyItem?: boolean;
|
|
9
10
|
item: {
|
|
10
11
|
label: string;
|
|
11
12
|
value: string;
|
|
@@ -18,6 +19,7 @@ const FormCheckbox = forwardRef<HTMLInputElement, FormCheckboxType>((
|
|
|
18
19
|
label,
|
|
19
20
|
name,
|
|
20
21
|
hideLabel,
|
|
22
|
+
emptyItem,
|
|
21
23
|
item,
|
|
22
24
|
...rest
|
|
23
25
|
},
|
|
@@ -28,9 +30,13 @@ const FormCheckbox = forwardRef<HTMLInputElement, FormCheckboxType>((
|
|
|
28
30
|
return (
|
|
29
31
|
<div className='checkbox'>
|
|
30
32
|
{label && !hideLabel && <label className='checkbox__label'>{label}</label>}
|
|
31
|
-
<div className=
|
|
33
|
+
<div className={`checkbox__item ${!emptyItem ? "checkbox__item--nolabel" : ""}`}>
|
|
32
34
|
<input ref={ref} type='checkbox' name={name} id={uid} value={item.value} defaultChecked={item.checked} {...rest} />
|
|
33
|
-
|
|
35
|
+
{emptyItem ? (
|
|
36
|
+
<label htmlFor={uid} className="checkbox__item--nolabel" />
|
|
37
|
+
) : (
|
|
38
|
+
<label htmlFor={uid}>{item.label}</label>
|
|
39
|
+
)}
|
|
34
40
|
</div>
|
|
35
41
|
</div>
|
|
36
42
|
);
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import useStore from "../store";
|
|
2
|
-
|
|
3
1
|
export const getDomainsArray = (currentDomains) => {
|
|
4
|
-
const multiDomainEnabled = import.meta.env.REACT_APP_ENABLE_MULTI_DOMAIN
|
|
2
|
+
const multiDomainEnabled = import.meta.env.REACT_APP_ENABLE_MULTI_DOMAIN?.toLowerCase() === 'true';
|
|
5
3
|
const userDomains = currentDomains || [];
|
|
6
4
|
|
|
7
5
|
return multiDomainEnabled ? (userDomains?.length > 0 ? userDomains : [null]) : [];
|