@buerokratt-ria/common-gui-components 0.0.57 → 0.0.59
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/package.json +1 -1
- package/templates/history-page/src/History.scss +33 -3
- 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 +66 -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 +943 -209
- 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/FormElements/FormCombobox/FormCombobox.scss +252 -0
- package/ui-components/FormElements/FormCombobox/index.tsx +334 -0
- package/ui-components/FormElements/index.tsx +1 -0
- package/ui-components/HistoricalChat/ChatMessage.tsx +24 -9
- package/ui-components/HistoricalChat/index.tsx +16 -6
- package/ui-components/Icon/index.tsx +1 -0
- package/ui-components/index.tsx +2 -0
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.59] - 05.06.2026
|
|
8
|
+
|
|
9
|
+
- Quality measurements in chat history view
|
|
10
|
+
- Column sorting and filtering
|
|
11
|
+
- Analytics: theme, follow up action, response quality
|
|
12
|
+
|
|
13
|
+
## [0.0.58] - 25.05.2026
|
|
14
|
+
|
|
15
|
+
- Properly render mcq selected button
|
|
16
|
+
|
|
7
17
|
## [0.0.57] - 14.05.2026
|
|
8
18
|
|
|
9
19
|
- Added authenticated person to chosen columns list
|
package/package.json
CHANGED
|
@@ -9,10 +9,29 @@
|
|
|
9
9
|
gap: 24px;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
.history-page-wrapper {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
height: 100%;
|
|
16
|
+
min-height: 0;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.history-header-combobox {
|
|
21
|
+
display: inline-flex;
|
|
22
|
+
vertical-align: middle;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.data-table th:has(.history-header-combobox .select--open) {
|
|
26
|
+
z-index: 10001 !important;
|
|
27
|
+
}
|
|
28
|
+
|
|
12
29
|
.card-drawer-container {
|
|
13
30
|
display: flex;
|
|
14
31
|
width: 100%;
|
|
15
|
-
|
|
32
|
+
flex: 1;
|
|
33
|
+
min-height: 0;
|
|
34
|
+
overflow: hidden;
|
|
16
35
|
box-sizing: border-box;
|
|
17
36
|
}
|
|
18
37
|
|
|
@@ -57,7 +76,7 @@
|
|
|
57
76
|
|
|
58
77
|
.drawer-container {
|
|
59
78
|
flex: 1;
|
|
60
|
-
height:
|
|
79
|
+
min-height: 0;
|
|
61
80
|
overflow-y: auto;
|
|
62
81
|
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
|
|
63
82
|
border: 1px solid get-color(black-coral-2);
|
|
@@ -79,7 +98,13 @@
|
|
|
79
98
|
font-size: $veera-font-size-80;
|
|
80
99
|
gap: 4px;
|
|
81
100
|
overflow-y: auto;
|
|
82
|
-
|
|
101
|
+
position: relative;
|
|
102
|
+
|
|
103
|
+
.side-meta__content {
|
|
104
|
+
display: grid;
|
|
105
|
+
gap: 4px;
|
|
106
|
+
padding: get-spacing(haapsalu);
|
|
107
|
+
}
|
|
83
108
|
}
|
|
84
109
|
|
|
85
110
|
.spinner {
|
|
@@ -87,6 +112,11 @@
|
|
|
87
112
|
}
|
|
88
113
|
|
|
89
114
|
.history-content {
|
|
115
|
+
display: flex;
|
|
116
|
+
flex-direction: column;
|
|
117
|
+
flex: 1;
|
|
118
|
+
min-height: 0;
|
|
119
|
+
|
|
90
120
|
&--loading {
|
|
91
121
|
opacity: 0.5;
|
|
92
122
|
pointer-events: none;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
@import 'src/styles/settings/variables/typography';
|
|
2
|
+
|
|
3
|
+
.metadata-item {
|
|
4
|
+
&__value {
|
|
5
|
+
font-size: $veera-font-size-80;
|
|
6
|
+
}
|
|
7
|
+
&__meta {
|
|
8
|
+
font-size: $veera-font-size-70;
|
|
9
|
+
color: #686B78;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.divider {
|
|
14
|
+
height: 1px;
|
|
15
|
+
background-color: #686B78;
|
|
16
|
+
width: 100%;
|
|
17
|
+
margin: 16px 0;
|
|
18
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { format } from "date-fns";
|
|
2
|
+
import { et } from "date-fns/locale";
|
|
3
|
+
import { ComponentProps, FC, PropsWithChildren, useMemo } from "react";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { Chat as ChatType } from "../../../../../types/chat";
|
|
6
|
+
|
|
7
|
+
import './ChatMetadataPanel.scss';
|
|
8
|
+
import { CharMeasurementType } from "../../types";
|
|
9
|
+
|
|
10
|
+
type ChatMetadataPanelProps = PropsWithChildren<{
|
|
11
|
+
readonly chat: ChatType
|
|
12
|
+
readonly chatMeasurments: ComponentProps<typeof Measurements>['measurments'];
|
|
13
|
+
}>;
|
|
14
|
+
|
|
15
|
+
const formatMetaDate = (date: string) => format(
|
|
16
|
+
new Date(date),
|
|
17
|
+
'd.MMMM yyyy HH:mm:ss',
|
|
18
|
+
{
|
|
19
|
+
locale: et,
|
|
20
|
+
}
|
|
21
|
+
).toLowerCase();
|
|
22
|
+
|
|
23
|
+
const formatAuthor = (value?: string) => value ? `(${value})` : '';
|
|
24
|
+
|
|
25
|
+
const formatMeta = (date?: string, author?: string) => {
|
|
26
|
+
const formattedDate = date ? formatMetaDate(date) : '';
|
|
27
|
+
const formattedAuthor = formatAuthor(author);
|
|
28
|
+
|
|
29
|
+
return [formattedDate, formattedAuthor].filter(Boolean).join(' ');
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const ChatMetadataPanel: FC<ChatMetadataPanelProps> = ({ chat, chatMeasurments, children }) => {
|
|
33
|
+
const { t } = useTranslation();
|
|
34
|
+
|
|
35
|
+
const endUserFullName = useMemo(() => {
|
|
36
|
+
return chat.endUserFirstName !== '' &&
|
|
37
|
+
chat?.endUserLastName !== ''
|
|
38
|
+
? `${chat.endUserFirstName} ${chat.endUserLastName}`
|
|
39
|
+
: t('global.anonymous');
|
|
40
|
+
}, [chat, t]);
|
|
41
|
+
|
|
42
|
+
const commentMeta = useMemo(
|
|
43
|
+
() => formatMeta(chat.commentAddedDate, chat.commentAuthor),
|
|
44
|
+
[chat.commentAddedDate, chat.commentAuthor]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const statusMeta = useMemo(
|
|
48
|
+
() => formatMeta(chat.lastMessageTimestamp, chat.userDisplayName),
|
|
49
|
+
[chat.lastMessageTimestamp, chat.userDisplayName]
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return <>
|
|
53
|
+
<div className="side-meta">
|
|
54
|
+
<div className="side-meta__content">
|
|
55
|
+
<MetadataItem label="ID" value={chat.id} />
|
|
56
|
+
<MetadataItem label={t('chat.endUser')} value={endUserFullName} />
|
|
57
|
+
{chat.endUserId && (
|
|
58
|
+
<MetadataItem label={t('chat.endUserId')} value={chat.endUserId ?? ''} />
|
|
59
|
+
)}
|
|
60
|
+
{chat.endUserEmail && (
|
|
61
|
+
<MetadataItem label={t('chat.endUserEmail')} value={chat.endUserEmail} />
|
|
62
|
+
)}
|
|
63
|
+
{chat.endUserPhone && (
|
|
64
|
+
<MetadataItem label={t('chat.endUserPhoneNumber')} value={chat.endUserPhone} />
|
|
65
|
+
)}
|
|
66
|
+
{chat.customerSupportDisplayName && (
|
|
67
|
+
<MetadataItem label={t('chat.csaName')} value={chat.customerSupportDisplayName} />
|
|
68
|
+
)}
|
|
69
|
+
<MetadataItem
|
|
70
|
+
label={t('chat.startedAt')}
|
|
71
|
+
value={format(
|
|
72
|
+
new Date(chat.created),
|
|
73
|
+
'dd. MMMM Y HH:mm:ss',
|
|
74
|
+
{
|
|
75
|
+
locale: et,
|
|
76
|
+
}
|
|
77
|
+
).toLowerCase()}
|
|
78
|
+
/>
|
|
79
|
+
<MetadataItem label={t('chat.device')} value={chat.endUserOs ?? ''} />
|
|
80
|
+
<MetadataItem label={t('chat.location')} value={chat.endUserUrl ?? ''} />
|
|
81
|
+
{chat.comment && (
|
|
82
|
+
<MetadataItem
|
|
83
|
+
label={t('chat.history.comment')}
|
|
84
|
+
value={chat.comment}
|
|
85
|
+
meta={commentMeta}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
{chat.lastMessageEvent && (
|
|
89
|
+
<MetadataItem
|
|
90
|
+
label={t('global.status')}
|
|
91
|
+
value={t('chat.plainEvents.' + chat.lastMessageEvent)}
|
|
92
|
+
meta={statusMeta}
|
|
93
|
+
/>
|
|
94
|
+
)}
|
|
95
|
+
<Measurements measurments={chatMeasurments} />
|
|
96
|
+
</div>
|
|
97
|
+
{children}
|
|
98
|
+
</div>
|
|
99
|
+
</>;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const Measurements: FC<{
|
|
103
|
+
readonly measurments: CharMeasurementType[];
|
|
104
|
+
}> = ({ measurments }) => {
|
|
105
|
+
const { t } = useTranslation();
|
|
106
|
+
|
|
107
|
+
const groupedMeasurements = useMemo(() => {
|
|
108
|
+
const typeOrder: CharMeasurementType['type'][] = [
|
|
109
|
+
'THEME',
|
|
110
|
+
'QUALITY',
|
|
111
|
+
'FOLLOW_UP_ACTION',
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
const grouped = measurments.reduce<
|
|
115
|
+
Partial<
|
|
116
|
+
Record<
|
|
117
|
+
CharMeasurementType['type'],
|
|
118
|
+
Record<
|
|
119
|
+
string,
|
|
120
|
+
{
|
|
121
|
+
createdAt: string;
|
|
122
|
+
authorDisplayName: string;
|
|
123
|
+
values: string[];
|
|
124
|
+
}
|
|
125
|
+
>
|
|
126
|
+
>
|
|
127
|
+
>
|
|
128
|
+
>((acc, item) => {
|
|
129
|
+
acc[item.type] ??= {};
|
|
130
|
+
|
|
131
|
+
acc[item.type]![item.createdAt] ??= {
|
|
132
|
+
createdAt: item.createdAt,
|
|
133
|
+
authorDisplayName: item.authorDisplayName,
|
|
134
|
+
values: [],
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
if (item.value) {
|
|
138
|
+
acc[item.type]![item.createdAt].values.push(item.value);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return acc;
|
|
142
|
+
}, {});
|
|
143
|
+
|
|
144
|
+
return typeOrder
|
|
145
|
+
.map((type) => ({
|
|
146
|
+
type,
|
|
147
|
+
items: Object.values(grouped[type] ?? {})
|
|
148
|
+
.map((item) => ({
|
|
149
|
+
createdAt: item.createdAt,
|
|
150
|
+
authorDisplayName: item.authorDisplayName,
|
|
151
|
+
value: item.values.join(', '),
|
|
152
|
+
}))
|
|
153
|
+
.sort(
|
|
154
|
+
(a, b) =>
|
|
155
|
+
new Date(b.createdAt).getTime() -
|
|
156
|
+
new Date(a.createdAt).getTime()
|
|
157
|
+
),
|
|
158
|
+
}))
|
|
159
|
+
.filter(({ items }) => items.length > 0);
|
|
160
|
+
}, [measurments]);
|
|
161
|
+
|
|
162
|
+
const getLabel = (type: CharMeasurementType['type']) =>
|
|
163
|
+
({
|
|
164
|
+
THEME: t('chat.quality.theme'),
|
|
165
|
+
QUALITY: t('chat.quality.responseQuality'),
|
|
166
|
+
FOLLOW_UP_ACTION: t('chat.quality.followUpAction'),
|
|
167
|
+
})[type];
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<>
|
|
171
|
+
<div className="divider"></div>
|
|
172
|
+
|
|
173
|
+
{groupedMeasurements.map(({ type, items }) =>
|
|
174
|
+
items.map((item, index) => (
|
|
175
|
+
<MetadataItem
|
|
176
|
+
labelStyle={{ marginTop: '12px' }}
|
|
177
|
+
key={`${type}-${item.createdAt}`}
|
|
178
|
+
{...((index === 0) ? { label: getLabel(type) } : {})}
|
|
179
|
+
value={item.value || t('chat.quality.selectionEmptied')}
|
|
180
|
+
meta={formatMeta(item.createdAt, item.authorDisplayName)}
|
|
181
|
+
/>
|
|
182
|
+
))
|
|
183
|
+
)}
|
|
184
|
+
</>
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
type MetadataItemProps = {
|
|
190
|
+
readonly label?: string;
|
|
191
|
+
readonly value: string;
|
|
192
|
+
readonly meta?: string;
|
|
193
|
+
readonly labelStyle?: React.CSSProperties;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const MetadataItem: FC<MetadataItemProps> = ({ label, value, meta, labelStyle }) => {
|
|
197
|
+
return <>
|
|
198
|
+
{label && <p style={labelStyle}>
|
|
199
|
+
<strong>{label}</strong>
|
|
200
|
+
</p>}
|
|
201
|
+
<p className="metadata-item__value">{value}</p>
|
|
202
|
+
{meta && <p className="metadata-item__meta">{meta}</p>}
|
|
203
|
+
</>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export { ChatMetadataPanel };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
|
|
3
|
+
type MetadataItemProps = {
|
|
4
|
+
readonly label: string,
|
|
5
|
+
readonly value: string
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const ChatMetadataPanelItem: FC<MetadataItemProps> = ({ label, value }) => {
|
|
9
|
+
return <>
|
|
10
|
+
<p>
|
|
11
|
+
<strong>{label}</strong>
|
|
12
|
+
</p>
|
|
13
|
+
<p>{value}</p>
|
|
14
|
+
</>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { ChatMetadataPanelItem };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
@import 'src/styles/settings/variables/typography';
|
|
2
|
+
|
|
3
|
+
.filter-tag {
|
|
4
|
+
align-items: center;
|
|
5
|
+
appearance: none;
|
|
6
|
+
background-color: #EAF6FF;
|
|
7
|
+
border-radius: 4px;
|
|
8
|
+
border: 1px solid #CCDEED;
|
|
9
|
+
color: #131317;
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
display: flex;
|
|
12
|
+
font-family: inherit;
|
|
13
|
+
font-size: $veera-font-size-80;
|
|
14
|
+
line-height: $veera-font-size-100;
|
|
15
|
+
padding: 4px 4px 4px 8px;
|
|
16
|
+
|
|
17
|
+
&:hover {
|
|
18
|
+
background-color: #CCDEED;
|
|
19
|
+
border-color: #99BDDA;
|
|
20
|
+
|
|
21
|
+
.icon {
|
|
22
|
+
color: #004882;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&:focus {
|
|
27
|
+
background-color: #003662;
|
|
28
|
+
border-color: #003662;
|
|
29
|
+
color: #FFFFFF;
|
|
30
|
+
|
|
31
|
+
.icon {
|
|
32
|
+
color: #FFFFFF;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
.icon {
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
margin-left: 4px;
|
|
40
|
+
color: #686B78;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FC } from "react"
|
|
2
|
+
import { MdClose } from "react-icons/md";
|
|
3
|
+
import './FilterTag.scss';
|
|
4
|
+
|
|
5
|
+
type FilterTagProps = {
|
|
6
|
+
readonly text: string;
|
|
7
|
+
readonly onClick: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const FilterTag: FC<FilterTagProps> = ({ text, onClick }) => {
|
|
11
|
+
return <>
|
|
12
|
+
<button className="filter-tag" type="button" onClick={onClick}>
|
|
13
|
+
{text} <MdClose size="16" className="icon" />
|
|
14
|
+
</button>
|
|
15
|
+
</>
|
|
16
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ComponentProps, FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { FormCombobox } from '../../../../../ui-components';
|
|
4
|
+
|
|
5
|
+
type HeaderComboboxBaseProps = {
|
|
6
|
+
readonly label: string;
|
|
7
|
+
readonly options?: ComponentProps<typeof FormCombobox>['options'];
|
|
8
|
+
readonly isSearchEnabled?: ComponentProps<typeof FormCombobox>['isSearchEnabled'];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type HeaderComboboxSingleProps = HeaderComboboxBaseProps & {
|
|
12
|
+
readonly multiple: false;
|
|
13
|
+
readonly value?: string;
|
|
14
|
+
readonly onChange: (value: string) => void;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type HeaderComboboxMultipleProps = HeaderComboboxBaseProps & {
|
|
18
|
+
readonly multiple?: true;
|
|
19
|
+
readonly value?: string[];
|
|
20
|
+
readonly onChange: (value: string[]) => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type HeaderComboboxProps = HeaderComboboxSingleProps | HeaderComboboxMultipleProps;
|
|
24
|
+
|
|
25
|
+
const HeaderCombobox: FC<HeaderComboboxProps> = (props) => {
|
|
26
|
+
const {
|
|
27
|
+
label,
|
|
28
|
+
options = [],
|
|
29
|
+
isSearchEnabled = true,
|
|
30
|
+
} = props;
|
|
31
|
+
const sharedProps = {
|
|
32
|
+
hideInputStyle: true,
|
|
33
|
+
hideLabel: true,
|
|
34
|
+
label,
|
|
35
|
+
placeholder: label,
|
|
36
|
+
searchPlaceholder: label,
|
|
37
|
+
options,
|
|
38
|
+
isSearchEnabled,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<span
|
|
43
|
+
className="history-header-combobox"
|
|
44
|
+
onMouseDown={(event) => event.stopPropagation()}
|
|
45
|
+
onClick={(event) => event.stopPropagation()}
|
|
46
|
+
>
|
|
47
|
+
{props.multiple === false ? (
|
|
48
|
+
<FormCombobox
|
|
49
|
+
{...sharedProps}
|
|
50
|
+
multiple={false}
|
|
51
|
+
value={props.value}
|
|
52
|
+
onChange={props.onChange}
|
|
53
|
+
/>
|
|
54
|
+
) : (
|
|
55
|
+
<FormCombobox
|
|
56
|
+
{...sharedProps}
|
|
57
|
+
multiple={true}
|
|
58
|
+
value={props.value}
|
|
59
|
+
onChange={props.onChange}
|
|
60
|
+
/>
|
|
61
|
+
)}
|
|
62
|
+
</span>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export { HeaderCombobox };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
@import 'src/styles/tools/spacing';
|
|
2
|
+
|
|
3
|
+
.title {
|
|
4
|
+
font-size: 16px;
|
|
5
|
+
font-weight: 400;
|
|
6
|
+
line-height: 20px;
|
|
7
|
+
color: #131317;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.quality-settings {
|
|
11
|
+
background-color: #ffffff;
|
|
12
|
+
border-top: 1px solid #e0e0e0;
|
|
13
|
+
bottom: 0;
|
|
14
|
+
gap: 8px;
|
|
15
|
+
margin-top: auto;
|
|
16
|
+
padding: get-spacing(haapsalu);
|
|
17
|
+
position: sticky;
|
|
18
|
+
width: 100%;
|
|
19
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { ComponentProps, FC } from 'react';
|
|
2
|
+
import { Controller, useForm } from 'react-hook-form';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
|
|
5
|
+
import { FormCombobox, Track } from '../../../../../ui-components';
|
|
6
|
+
|
|
7
|
+
import './QualitySettings.scss';
|
|
8
|
+
|
|
9
|
+
type QualitySettingsProps = {
|
|
10
|
+
readonly theme: {
|
|
11
|
+
readonly onChange: (value: string[]) => void;
|
|
12
|
+
readonly options: Pick<ComponentProps<typeof FormCombobox>, 'options'>['options'];
|
|
13
|
+
readonly value: string[];
|
|
14
|
+
};
|
|
15
|
+
readonly quality: {
|
|
16
|
+
readonly onChange: (value: string) => void;
|
|
17
|
+
readonly options: Pick<ComponentProps<typeof FormCombobox>, 'options'>['options'];
|
|
18
|
+
readonly value?: string;
|
|
19
|
+
};
|
|
20
|
+
readonly followUp: {
|
|
21
|
+
readonly onChange: (value: string) => void;
|
|
22
|
+
readonly options: Pick<ComponentProps<typeof FormCombobox>, 'options'>['options'];
|
|
23
|
+
readonly value?: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const QualitySettings: FC<QualitySettingsProps> = ({ theme, quality, followUp }) => {
|
|
28
|
+
const { t } = useTranslation();
|
|
29
|
+
const form = useForm<{
|
|
30
|
+
readonly theme: string[];
|
|
31
|
+
readonly quality: string;
|
|
32
|
+
readonly followUp: string;
|
|
33
|
+
}>({
|
|
34
|
+
defaultValues: {
|
|
35
|
+
theme: [],
|
|
36
|
+
quality: '',
|
|
37
|
+
followUp: '',
|
|
38
|
+
},
|
|
39
|
+
values: {
|
|
40
|
+
theme: theme.value,
|
|
41
|
+
quality: quality.value ?? '',
|
|
42
|
+
followUp: followUp.value ?? '',
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const handleThemeChange = (value: string[], onChange: (value: string[]) => void) => {
|
|
47
|
+
onChange(value);
|
|
48
|
+
theme.onChange(value);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleQualityChange = (value: string, onChange: (value: string) => void) => {
|
|
52
|
+
onChange(value);
|
|
53
|
+
quality.onChange(value);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handleFollowUpChange = (value: string, onChange: (value: string) => void) => {
|
|
57
|
+
onChange(value);
|
|
58
|
+
followUp.onChange(value);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Track
|
|
63
|
+
gap={8}
|
|
64
|
+
direction="vertical"
|
|
65
|
+
align="left"
|
|
66
|
+
className="quality-settings"
|
|
67
|
+
>
|
|
68
|
+
<p className="title">{t('chat.history.analysis')}</p>
|
|
69
|
+
|
|
70
|
+
<Controller
|
|
71
|
+
name="theme"
|
|
72
|
+
control={form.control}
|
|
73
|
+
render={({ field }) => (
|
|
74
|
+
<FormCombobox
|
|
75
|
+
multiple={true}
|
|
76
|
+
placeholder={t('chat.history.chooseConversationTheme')}
|
|
77
|
+
searchPlaceholder={t('chat.history.conversationTheme')}
|
|
78
|
+
options={theme.options}
|
|
79
|
+
value={field.value}
|
|
80
|
+
onChange={(value) => handleThemeChange(value, field.onChange)}
|
|
81
|
+
isSearchEnabled={true}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
/>
|
|
85
|
+
<Controller
|
|
86
|
+
name="quality"
|
|
87
|
+
control={form.control}
|
|
88
|
+
render={({ field }) => (
|
|
89
|
+
<FormCombobox
|
|
90
|
+
placeholder={t('chat.history.chooseConversationResponseQuality')}
|
|
91
|
+
searchPlaceholder={t('chat.history.conversationResponseQuality')}
|
|
92
|
+
options={quality.options}
|
|
93
|
+
value={field.value}
|
|
94
|
+
onChange={(value) => handleQualityChange(value, field.onChange)}
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
/>
|
|
98
|
+
<Controller
|
|
99
|
+
name="followUp"
|
|
100
|
+
control={form.control}
|
|
101
|
+
render={({ field }) => (
|
|
102
|
+
<FormCombobox
|
|
103
|
+
placeholder={t('chat.history.chooseFollowUpAction')}
|
|
104
|
+
searchPlaceholder={t('chat.history.followUpAction')}
|
|
105
|
+
options={followUp.options}
|
|
106
|
+
value={field.value}
|
|
107
|
+
onChange={(value) => handleFollowUpChange(value, field.onChange)}
|
|
108
|
+
/>
|
|
109
|
+
)}
|
|
110
|
+
/>
|
|
111
|
+
</Track>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export { QualitySettings };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
@import 'src/styles/settings/variables/typography';
|
|
2
|
+
|
|
3
|
+
.selected-filters__container {
|
|
4
|
+
display: grid;
|
|
5
|
+
grid-template-columns: 1fr;
|
|
6
|
+
grid-template-columns: 24px 1fr auto;
|
|
7
|
+
margin-bottom: 16px;
|
|
8
|
+
|
|
9
|
+
.selected-filters {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-wrap: wrap;
|
|
12
|
+
gap: 4px;
|
|
13
|
+
|
|
14
|
+
.selected-filters__label {
|
|
15
|
+
color: #686B78;
|
|
16
|
+
font-size: $veera-font-size-80;
|
|
17
|
+
line-height: $veera-font-size-250;
|
|
18
|
+
// max-height -> border-left is with correct height!
|
|
19
|
+
max-height: 24px;
|
|
20
|
+
padding-left: 4px;
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
|
|
23
|
+
&:not(:first-child):not(:last-child) {
|
|
24
|
+
border-left: 1px solid #878A97;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.selected-filters__items {
|
|
29
|
+
display: contents;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
button {
|
|
34
|
+
align-self: start;
|
|
35
|
+
}
|
|
36
|
+
}
|