@orchestrator-ui/orchestrator-ui-components 5.9.0 → 6.1.0

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.
Files changed (61) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/.turbo/turbo-lint.log +10 -1
  3. package/.turbo/turbo-test.log +10 -10
  4. package/CHANGELOG.md +12 -0
  5. package/__mocks__/@copilotkit/react-core.js +9 -0
  6. package/__mocks__/@copilotkit/react-ui.js +11 -0
  7. package/dist/index.d.ts +1432 -2
  8. package/dist/index.js +3006 -28
  9. package/dist/index.js.map +1 -1
  10. package/package.json +5 -1
  11. package/src/components/WfoAgent/FilterDisplay/FilterDisplay.tsx +182 -0
  12. package/src/components/WfoAgent/FilterDisplay/index.ts +1 -0
  13. package/src/components/WfoAgent/FilterDisplay/styles.ts +62 -0
  14. package/src/components/WfoAgent/WfoAgent/WfoAgent.tsx +100 -0
  15. package/src/components/WfoAgent/WfoAgent/index.ts +1 -0
  16. package/src/components/WfoAgent/index.ts +2 -0
  17. package/src/components/WfoSearchPage/WfoConditionRow/WfoConditionRow.tsx +388 -0
  18. package/src/components/WfoSearchPage/WfoConditionRow/WfoFieldSelector.tsx +43 -0
  19. package/src/components/WfoSearchPage/WfoConditionRow/WfoOperatorSelector.tsx +100 -0
  20. package/src/components/WfoSearchPage/WfoConditionRow/WfoPathChips.tsx +193 -0
  21. package/src/components/WfoSearchPage/WfoConditionRow/WfoPathSelector.tsx +54 -0
  22. package/src/components/WfoSearchPage/WfoConditionRow/WfoRenderFunctions.tsx +107 -0
  23. package/src/components/WfoSearchPage/WfoConditionRow/WfoSelectedPathDisplay.tsx +75 -0
  24. package/src/components/WfoSearchPage/WfoConditionRow/index.ts +11 -0
  25. package/src/components/WfoSearchPage/WfoConditionRow/types.ts +84 -0
  26. package/src/components/WfoSearchPage/WfoConditionRow/utils.ts +63 -0
  27. package/src/components/WfoSearchPage/WfoFilterGroup/WfoFilterGroup.tsx +238 -0
  28. package/src/components/WfoSearchPage/WfoFilterGroup/index.ts +1 -0
  29. package/src/components/WfoSearchPage/WfoSearch/WfoSearch.tsx +453 -0
  30. package/src/components/WfoSearchPage/WfoSearch/index.ts +1 -0
  31. package/src/components/WfoSearchPage/WfoSearchResults/WfoHighlightedText.tsx +63 -0
  32. package/src/components/WfoSearchPage/WfoSearchResults/WfoPathBreadcrumb.tsx +80 -0
  33. package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchEmptyState.tsx +24 -0
  34. package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchLoadingState.tsx +24 -0
  35. package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchMetadataHeader.tsx +24 -0
  36. package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchPaginationInfo.tsx +107 -0
  37. package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchResultItem.tsx +157 -0
  38. package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchResults.tsx +65 -0
  39. package/src/components/WfoSearchPage/WfoSearchResults/WfoSubscriptionDetailModal.tsx +55 -0
  40. package/src/components/WfoSearchPage/WfoSearchResults/index.ts +10 -0
  41. package/src/components/WfoSearchPage/WfoValueControl/WfoValueControl.tsx +247 -0
  42. package/src/components/WfoSearchPage/WfoValueControl/index.ts +1 -0
  43. package/src/components/WfoSearchPage/constants.ts +17 -0
  44. package/src/components/WfoSearchPage/index.ts +6 -0
  45. package/src/components/WfoSearchPage/utils.ts +271 -0
  46. package/src/components/index.ts +2 -0
  47. package/src/configuration/version.ts +1 -1
  48. package/src/hooks/useDebounce.ts +21 -0
  49. package/src/hooks/usePathAutoComplete.ts +133 -0
  50. package/src/hooks/useSearch.ts +83 -0
  51. package/src/hooks/useSearchPagination.ts +148 -0
  52. package/src/hooks/useUrlParams.ts +120 -0
  53. package/src/icons/WfoPencil.tsx +23 -4
  54. package/src/messages/en-GB.json +79 -1
  55. package/src/messages/nl-NL.json +2 -1
  56. package/src/rtk/endpoints/index.ts +1 -0
  57. package/src/rtk/endpoints/search.ts +90 -0
  58. package/src/types/index.ts +1 -0
  59. package/src/types/search.ts +215 -0
  60. package/src/utils/optionalArray.spec.ts +27 -0
  61. package/src/utils/optionalArray.ts +5 -0
@@ -0,0 +1,388 @@
1
+ import React, { FC, useState } from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+
5
+ import {
6
+ EuiButtonIcon,
7
+ EuiFlexGroup,
8
+ EuiFlexItem,
9
+ EuiFormRow,
10
+ EuiPanel,
11
+ } from '@elastic/eui';
12
+
13
+ import { WfoBadge } from '@/components';
14
+ import { useOrchestratorTheme } from '@/hooks';
15
+ import { usePathAutocomplete } from '@/hooks/usePathAutoComplete';
16
+ import { Condition, EntityKind, PathInfo } from '@/types';
17
+
18
+ import { ValueControl } from '../WfoValueControl';
19
+ import { getTypeColor } from '../utils';
20
+ import { WfoFieldSelector } from './WfoFieldSelector';
21
+ import { WfoOperatorSelector } from './WfoOperatorSelector';
22
+ import { WfoPathChips } from './WfoPathChips';
23
+ import { WfoPathSelector } from './WfoPathSelector';
24
+ import { WfoRenderPathOption } from './WfoRenderFunctions';
25
+ import { WfoSelectedPathDisplay } from './WfoSelectedPathDisplay';
26
+ import {
27
+ createOptionsFromPaths,
28
+ getPathSelectionOptions,
29
+ isFullPathSelected,
30
+ shouldHideValueInput,
31
+ } from './utils';
32
+
33
+ interface ConditionRowProps {
34
+ condition: Condition;
35
+ entityType: EntityKind;
36
+ onChange: (condition: Condition) => void;
37
+ onRemove: () => void;
38
+ }
39
+
40
+ export const ConditionRow: FC<ConditionRowProps> = ({
41
+ condition,
42
+ entityType,
43
+ onChange,
44
+ onRemove,
45
+ }) => {
46
+ const t = useTranslations('search.page');
47
+ const { theme } = useOrchestratorTheme();
48
+ const [searchValue, setSearchValue] = useState(condition.path);
49
+ const [showPathSelection, setShowPathSelection] = useState(false);
50
+ const [selectedFieldName, setSelectedFieldName] = useState<string>('');
51
+ const { paths, loading, error } = usePathAutocomplete(
52
+ searchValue,
53
+ entityType,
54
+ );
55
+
56
+ const selectedPathInfo: PathInfo | null = (() => {
57
+ if (!condition.path) return null;
58
+
59
+ const exactMatch = paths.find(({ path, fullPath }) =>
60
+ fullPath ? fullPath === condition.path : path === condition.path,
61
+ );
62
+ if (exactMatch) return exactMatch;
63
+
64
+ if (condition.path.includes('.')) {
65
+ const fieldName = condition.path.split('.').pop();
66
+ if (fieldName) {
67
+ const fieldMatch = paths.find(
68
+ ({ path, availablePaths }) =>
69
+ path === fieldName &&
70
+ availablePaths &&
71
+ availablePaths.includes(condition.path),
72
+ );
73
+ if (fieldMatch) return fieldMatch;
74
+ }
75
+ } else {
76
+ const fieldMatch = paths.find(
77
+ ({ path }) => path === condition.path,
78
+ );
79
+ if (fieldMatch) return fieldMatch;
80
+ }
81
+
82
+ return null;
83
+ })();
84
+
85
+ const handleFieldSelection = (fieldName: string) => {
86
+ const fieldInfo = paths.find(({ path }) => path === fieldName);
87
+
88
+ if (fieldInfo && fieldInfo.group === 'component') {
89
+ onChange({
90
+ path: fieldName,
91
+ value_kind: fieldInfo.type,
92
+ condition: { op: '', value: undefined },
93
+ });
94
+ return;
95
+ }
96
+
97
+ if (
98
+ fieldInfo &&
99
+ fieldInfo.availablePaths &&
100
+ fieldInfo.availablePaths.length === 1
101
+ ) {
102
+ const singlePath = fieldInfo.availablePaths[0];
103
+ onChange({
104
+ path: singlePath,
105
+ value_kind: fieldInfo.type,
106
+ condition: { op: '', value: undefined },
107
+ });
108
+ return;
109
+ }
110
+
111
+ setSelectedFieldName(fieldName);
112
+ setShowPathSelection(true);
113
+ };
114
+
115
+ const handlePathSelection = (selectedOption: {
116
+ label: string;
117
+ value: string;
118
+ fullPath: string;
119
+ isAnyPath?: boolean;
120
+ }) => {
121
+ const fieldInfo = paths.find(({ path }) => path === selectedFieldName);
122
+ if (fieldInfo) {
123
+ if (selectedOption.isAnyPath) {
124
+ onChange({
125
+ path: selectedFieldName,
126
+ value_kind: fieldInfo.type,
127
+ condition: { op: '', value: undefined },
128
+ });
129
+ } else {
130
+ onChange({
131
+ path: selectedOption.value,
132
+ value_kind: fieldInfo.type,
133
+ condition: { op: '', value: undefined },
134
+ });
135
+ }
136
+ }
137
+ setShowPathSelection(false);
138
+ setSelectedFieldName('');
139
+ };
140
+
141
+ const handleOperatorChange = (op: string) => {
142
+ const value: unknown = undefined;
143
+
144
+ if (selectedPathInfo?.type === 'boolean') {
145
+ const actualOp = 'eq';
146
+ const booleanValue = op === 'eq' ? true : false;
147
+
148
+ onChange({
149
+ ...condition,
150
+ value_kind: selectedPathInfo?.type,
151
+ condition: { op: actualOp, value: booleanValue },
152
+ });
153
+ return;
154
+ }
155
+
156
+ onChange({
157
+ ...condition,
158
+ value_kind: selectedPathInfo?.type,
159
+ condition: { op, value },
160
+ });
161
+ };
162
+
163
+ const handleValueChange = (value: unknown) => {
164
+ onChange({
165
+ ...condition,
166
+ value_kind: selectedPathInfo?.type,
167
+ condition: { ...condition.condition, value },
168
+ });
169
+ };
170
+
171
+ const leavesOptions = createOptionsFromPaths(paths, 'leaf');
172
+ const componentsOptions = createOptionsFromPaths(paths, 'component');
173
+
174
+ const pathOptions = [
175
+ ...(leavesOptions.length > 0
176
+ ? [
177
+ {
178
+ label: t('fieldsGroupLabel'),
179
+ options: leavesOptions,
180
+ },
181
+ ]
182
+ : []),
183
+ ...(componentsOptions.length > 0
184
+ ? [
185
+ {
186
+ label: t('componentsGroupLabel'),
187
+ options: componentsOptions,
188
+ },
189
+ ]
190
+ : []),
191
+ ];
192
+
193
+ const hideValueInput = shouldHideValueInput(
194
+ selectedPathInfo,
195
+ !!condition.condition.op,
196
+ );
197
+ const fullPathSelected = isFullPathSelected(
198
+ condition.path,
199
+ selectedPathInfo,
200
+ );
201
+ const pathSelectionOptions = (() => {
202
+ const baseOptions = getPathSelectionOptions(selectedFieldName, paths);
203
+
204
+ // Add "Any path" option at the top if there are multiple paths
205
+ const fieldInfo = paths.find(({ path }) => path === selectedFieldName);
206
+ if (fieldInfo?.availablePaths && fieldInfo.availablePaths.length > 1) {
207
+ return [
208
+ {
209
+ label: t('anyPathOption'),
210
+ value: selectedFieldName,
211
+ fullPath: selectedFieldName,
212
+ isAnyPath: true,
213
+ },
214
+ ...baseOptions,
215
+ ];
216
+ }
217
+
218
+ return baseOptions;
219
+ })();
220
+
221
+ // Create render functions with theme
222
+ const renderPathOption = (
223
+ option: { label: string; value?: string },
224
+ searchValue: string,
225
+ contentClassName?: string,
226
+ ) => (
227
+ <WfoRenderPathOption
228
+ option={option}
229
+ searchValue={searchValue}
230
+ contentClassName={contentClassName}
231
+ paths={paths}
232
+ />
233
+ );
234
+
235
+ const renderPathSelectionOption = (option: {
236
+ label: string;
237
+ value?: string;
238
+ fullPath?: string;
239
+ isAnyPath?: boolean;
240
+ }) => {
241
+ // Get the field type from the selected field info
242
+ const fieldInfo = paths.find(({ path }) => path === selectedFieldName);
243
+ const fieldType = fieldInfo?.type || 'string';
244
+
245
+ return (
246
+ <WfoPathChips
247
+ fullPath={option.fullPath || ''}
248
+ label={option.label}
249
+ fieldType={fieldType}
250
+ isAnyPath={option.isAnyPath}
251
+ />
252
+ );
253
+ };
254
+
255
+ return (
256
+ <EuiPanel paddingSize="m" color="subdued">
257
+ <EuiFlexGroup direction="column" gutterSize="m">
258
+ <EuiFlexItem>
259
+ <EuiFormRow
260
+ label={t('fieldLabel')}
261
+ error={error}
262
+ isInvalid={!!error}
263
+ >
264
+ <EuiFlexGroup gutterSize="s" alignItems="center">
265
+ <EuiFlexItem>
266
+ {showPathSelection ? (
267
+ <WfoPathSelector
268
+ selectedFieldName={selectedFieldName}
269
+ pathOptions={pathSelectionOptions}
270
+ onPathSelection={handlePathSelection}
271
+ onClear={() => {
272
+ setShowPathSelection(false);
273
+ setSelectedFieldName('');
274
+ }}
275
+ renderOption={renderPathSelectionOption}
276
+ />
277
+ ) : condition.path && fullPathSelected ? (
278
+ <WfoSelectedPathDisplay
279
+ condition={condition}
280
+ selectedPathInfo={selectedPathInfo}
281
+ onEdit={() => {
282
+ onChange({
283
+ path: '',
284
+ value_kind: undefined,
285
+ condition: {
286
+ op: '',
287
+ value: undefined,
288
+ },
289
+ });
290
+ }}
291
+ />
292
+ ) : (
293
+ <WfoFieldSelector
294
+ pathOptions={pathOptions}
295
+ loading={loading}
296
+ error={error}
297
+ searchValue={searchValue}
298
+ onFieldSelection={handleFieldSelection}
299
+ onSearchChange={setSearchValue}
300
+ onClear={() => {
301
+ onChange({
302
+ path: '',
303
+ value_kind: undefined,
304
+ condition: {
305
+ op: '',
306
+ value: undefined,
307
+ },
308
+ });
309
+ }}
310
+ renderPathOption={renderPathOption}
311
+ />
312
+ )}
313
+ </EuiFlexItem>
314
+ {condition.path &&
315
+ selectedPathInfo?.ui_types &&
316
+ selectedPathInfo.ui_types.length > 0 && (
317
+ <EuiFlexItem grow={false}>
318
+ <EuiFlexGroup
319
+ gutterSize="xs"
320
+ alignItems="center"
321
+ responsive={false}
322
+ >
323
+ {selectedPathInfo.ui_types.map(
324
+ (type, index) => (
325
+ <EuiFlexItem
326
+ key={index}
327
+ grow={false}
328
+ >
329
+ <WfoBadge
330
+ color={getTypeColor(
331
+ type,
332
+ theme,
333
+ )}
334
+ textColor={
335
+ theme.colors.ink
336
+ }
337
+ size="s"
338
+ >
339
+ {type}
340
+ </WfoBadge>
341
+ </EuiFlexItem>
342
+ ),
343
+ )}
344
+ </EuiFlexGroup>
345
+ </EuiFlexItem>
346
+ )}
347
+ </EuiFlexGroup>
348
+ </EuiFormRow>
349
+ </EuiFlexItem>
350
+
351
+ <EuiFlexItem>
352
+ <EuiFlexGroup gutterSize="s" alignItems="flexEnd">
353
+ <EuiFlexItem>
354
+ <WfoOperatorSelector
355
+ selectedPathInfo={selectedPathInfo}
356
+ condition={condition}
357
+ onOperatorChange={handleOperatorChange}
358
+ />
359
+ </EuiFlexItem>
360
+
361
+ {!hideValueInput && (
362
+ <EuiFlexItem>
363
+ <EuiFormRow label={t('valueLabel')}>
364
+ <ValueControl
365
+ pathInfo={selectedPathInfo}
366
+ operator={condition.condition.op}
367
+ value={condition.condition.value}
368
+ onChange={handleValueChange}
369
+ />
370
+ </EuiFormRow>
371
+ </EuiFlexItem>
372
+ )}
373
+
374
+ <EuiFlexItem grow={false}>
375
+ <EuiButtonIcon
376
+ iconType="trash"
377
+ color="danger"
378
+ onClick={onRemove}
379
+ aria-label={t('removeConditionAriaLabel')}
380
+ size="m"
381
+ />
382
+ </EuiFlexItem>
383
+ </EuiFlexGroup>
384
+ </EuiFlexItem>
385
+ </EuiFlexGroup>
386
+ </EuiPanel>
387
+ );
388
+ };
@@ -0,0 +1,43 @@
1
+ import React, { FC } from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+
5
+ import { EuiComboBox } from '@elastic/eui';
6
+
7
+ import { FieldSelectorProps } from './types';
8
+
9
+ export const WfoFieldSelector: FC<FieldSelectorProps> = ({
10
+ pathOptions,
11
+ loading,
12
+ error,
13
+ onFieldSelection,
14
+ onSearchChange,
15
+ onClear,
16
+ renderPathOption,
17
+ }) => {
18
+ const t = useTranslations('search.page');
19
+
20
+ const handleSelectionChange = (selected: Array<{ value?: string }>) => {
21
+ if (selected[0]?.value) {
22
+ onFieldSelection(selected[0].value);
23
+ } else {
24
+ onClear();
25
+ }
26
+ };
27
+
28
+ return (
29
+ <EuiComboBox
30
+ placeholder={t('searchFieldsPlaceholder')}
31
+ options={pathOptions}
32
+ selectedOptions={[]}
33
+ onChange={handleSelectionChange}
34
+ onSearchChange={onSearchChange}
35
+ singleSelection={{ asPlainText: true }}
36
+ isLoading={loading}
37
+ isClearable
38
+ isInvalid={!!error}
39
+ renderOption={renderPathOption}
40
+ rowHeight={30}
41
+ />
42
+ );
43
+ };
@@ -0,0 +1,100 @@
1
+ import React, { FC } from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+
5
+ import {
6
+ EuiButton,
7
+ EuiFlexGroup,
8
+ EuiFlexItem,
9
+ EuiFormRow,
10
+ EuiText,
11
+ } from '@elastic/eui';
12
+
13
+ import { WfoToolTip } from '@/components';
14
+ import { useOrchestratorTheme } from '@/hooks';
15
+
16
+ import { getButtonColor, getButtonFill, getOperatorDisplay } from '../utils';
17
+ import { OperatorSelectorProps } from './types';
18
+
19
+ export const WfoOperatorSelector: FC<OperatorSelectorProps> = ({
20
+ selectedPathInfo,
21
+ condition,
22
+ onOperatorChange,
23
+ }) => {
24
+ const t = useTranslations('search.page');
25
+ const { theme } = useOrchestratorTheme();
26
+
27
+ return (
28
+ <EuiFormRow label={t('operatorLabel')}>
29
+ <EuiFlexGroup gutterSize="xs" wrap>
30
+ {selectedPathInfo?.operators?.map((operator) => {
31
+ const { symbol, description } = getOperatorDisplay(
32
+ operator,
33
+ selectedPathInfo,
34
+ );
35
+
36
+ const tooltipContent =
37
+ operator === 'like' ? (
38
+ <div>
39
+ <strong>{description}</strong>
40
+ <br />
41
+ <br />
42
+ <strong>Wildcards:</strong>
43
+ <br />• <code>%</code> matches any number of
44
+ characters
45
+ <br />• <code>_</code> matches exactly one
46
+ character
47
+ <br />
48
+ <br />
49
+ <strong>Examples:</strong>
50
+ <br />• <code>%test%</code> finds anything
51
+ containing "test"
52
+ <br />• <code>test%</code> finds anything
53
+ starting with "test"
54
+ <br />• <code>test_</code> finds "test" + one
55
+ character
56
+ </div>
57
+ ) : (
58
+ description
59
+ );
60
+
61
+ return (
62
+ <EuiFlexItem key={operator} grow={false}>
63
+ <WfoToolTip tooltipContent={tooltipContent}>
64
+ <EuiButton
65
+ size="s"
66
+ color={getButtonColor(
67
+ operator,
68
+ selectedPathInfo,
69
+ condition,
70
+ )}
71
+ fill={getButtonFill(
72
+ operator,
73
+ selectedPathInfo,
74
+ condition,
75
+ )}
76
+ onClick={() => onOperatorChange(operator)}
77
+ style={{
78
+ minWidth: theme.size.xxl,
79
+ fontSize: theme.size.base,
80
+ fontWeight: theme.font.weight.bold,
81
+ }}
82
+ >
83
+ {symbol}
84
+ </EuiButton>
85
+ </WfoToolTip>
86
+ </EuiFlexItem>
87
+ );
88
+ })}
89
+ {(!selectedPathInfo ||
90
+ selectedPathInfo.operators.length === 0) && (
91
+ <EuiFlexItem grow={false}>
92
+ <EuiText size="s" color={theme.colors.textSubdued}>
93
+ {t('selectFieldFirst')}
94
+ </EuiText>
95
+ </EuiFlexItem>
96
+ )}
97
+ </EuiFlexGroup>
98
+ </EuiFormRow>
99
+ );
100
+ };