@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.
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-lint.log +10 -1
- package/.turbo/turbo-test.log +10 -10
- package/CHANGELOG.md +12 -0
- package/__mocks__/@copilotkit/react-core.js +9 -0
- package/__mocks__/@copilotkit/react-ui.js +11 -0
- package/dist/index.d.ts +1432 -2
- package/dist/index.js +3006 -28
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
- package/src/components/WfoAgent/FilterDisplay/FilterDisplay.tsx +182 -0
- package/src/components/WfoAgent/FilterDisplay/index.ts +1 -0
- package/src/components/WfoAgent/FilterDisplay/styles.ts +62 -0
- package/src/components/WfoAgent/WfoAgent/WfoAgent.tsx +100 -0
- package/src/components/WfoAgent/WfoAgent/index.ts +1 -0
- package/src/components/WfoAgent/index.ts +2 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoConditionRow.tsx +388 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoFieldSelector.tsx +43 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoOperatorSelector.tsx +100 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoPathChips.tsx +193 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoPathSelector.tsx +54 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoRenderFunctions.tsx +107 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoSelectedPathDisplay.tsx +75 -0
- package/src/components/WfoSearchPage/WfoConditionRow/index.ts +11 -0
- package/src/components/WfoSearchPage/WfoConditionRow/types.ts +84 -0
- package/src/components/WfoSearchPage/WfoConditionRow/utils.ts +63 -0
- package/src/components/WfoSearchPage/WfoFilterGroup/WfoFilterGroup.tsx +238 -0
- package/src/components/WfoSearchPage/WfoFilterGroup/index.ts +1 -0
- package/src/components/WfoSearchPage/WfoSearch/WfoSearch.tsx +453 -0
- package/src/components/WfoSearchPage/WfoSearch/index.ts +1 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoHighlightedText.tsx +63 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoPathBreadcrumb.tsx +80 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchEmptyState.tsx +24 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchLoadingState.tsx +24 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchMetadataHeader.tsx +24 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchPaginationInfo.tsx +107 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchResultItem.tsx +157 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchResults.tsx +65 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSubscriptionDetailModal.tsx +55 -0
- package/src/components/WfoSearchPage/WfoSearchResults/index.ts +10 -0
- package/src/components/WfoSearchPage/WfoValueControl/WfoValueControl.tsx +247 -0
- package/src/components/WfoSearchPage/WfoValueControl/index.ts +1 -0
- package/src/components/WfoSearchPage/constants.ts +17 -0
- package/src/components/WfoSearchPage/index.ts +6 -0
- package/src/components/WfoSearchPage/utils.ts +271 -0
- package/src/components/index.ts +2 -0
- package/src/configuration/version.ts +1 -1
- package/src/hooks/useDebounce.ts +21 -0
- package/src/hooks/usePathAutoComplete.ts +133 -0
- package/src/hooks/useSearch.ts +83 -0
- package/src/hooks/useSearchPagination.ts +148 -0
- package/src/hooks/useUrlParams.ts +120 -0
- package/src/icons/WfoPencil.tsx +23 -4
- package/src/messages/en-GB.json +79 -1
- package/src/messages/nl-NL.json +2 -1
- package/src/rtk/endpoints/index.ts +1 -0
- package/src/rtk/endpoints/search.ts +90 -0
- package/src/types/index.ts +1 -0
- package/src/types/search.ts +215 -0
- package/src/utils/optionalArray.spec.ts +27 -0
- package/src/utils/optionalArray.ts +5 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import moment from 'moment';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
EuiComboBox,
|
|
8
|
+
EuiDatePicker,
|
|
9
|
+
EuiFieldNumber,
|
|
10
|
+
EuiFieldText,
|
|
11
|
+
EuiFlexGroup,
|
|
12
|
+
EuiFlexItem,
|
|
13
|
+
EuiFormHelpText,
|
|
14
|
+
EuiIcon,
|
|
15
|
+
EuiText,
|
|
16
|
+
} from '@elastic/eui';
|
|
17
|
+
|
|
18
|
+
import { useOrchestratorTheme } from '@/hooks';
|
|
19
|
+
import { PathInfo } from '@/types';
|
|
20
|
+
|
|
21
|
+
interface ValueControlProps {
|
|
22
|
+
pathInfo: PathInfo | null;
|
|
23
|
+
operator: string;
|
|
24
|
+
value: unknown;
|
|
25
|
+
onChange: (value: unknown) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const ValueControl: FC<ValueControlProps> = ({
|
|
29
|
+
pathInfo,
|
|
30
|
+
operator,
|
|
31
|
+
value,
|
|
32
|
+
onChange,
|
|
33
|
+
}) => {
|
|
34
|
+
const t = useTranslations('search.page');
|
|
35
|
+
const { theme } = useOrchestratorTheme();
|
|
36
|
+
|
|
37
|
+
if (!pathInfo || !operator) return null;
|
|
38
|
+
|
|
39
|
+
const schema = pathInfo.value_schema[operator];
|
|
40
|
+
if (!schema || schema.kind === 'none') return null;
|
|
41
|
+
|
|
42
|
+
if (pathInfo.type === 'string') {
|
|
43
|
+
if (operator === 'like') {
|
|
44
|
+
const handleLikeChange = (newValue: string) => {
|
|
45
|
+
onChange(newValue);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const ensureWildcards = (inputValue: string) => {
|
|
49
|
+
if (!inputValue) return inputValue;
|
|
50
|
+
if (!inputValue.includes('%') && !inputValue.includes('_')) {
|
|
51
|
+
return `%${inputValue}%`;
|
|
52
|
+
}
|
|
53
|
+
return inputValue;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const currentValue = String(value || '');
|
|
57
|
+
const hasWildcards =
|
|
58
|
+
currentValue.includes('%') || currentValue.includes('_');
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<>
|
|
62
|
+
<EuiFieldText
|
|
63
|
+
placeholder="Enter pattern (% = any chars, _ = single char)"
|
|
64
|
+
value={currentValue}
|
|
65
|
+
onChange={(event) =>
|
|
66
|
+
handleLikeChange(event.target.value)
|
|
67
|
+
}
|
|
68
|
+
onBlur={(event) => {
|
|
69
|
+
const finalValue = ensureWildcards(
|
|
70
|
+
event.target.value,
|
|
71
|
+
);
|
|
72
|
+
if (finalValue !== event.target.value) {
|
|
73
|
+
handleLikeChange(finalValue);
|
|
74
|
+
}
|
|
75
|
+
}}
|
|
76
|
+
prepend={<EuiIcon type="search" />}
|
|
77
|
+
/>
|
|
78
|
+
<EuiFormHelpText>
|
|
79
|
+
{hasWildcards ? (
|
|
80
|
+
<span>
|
|
81
|
+
<EuiIcon
|
|
82
|
+
type="checkInCircleFilled"
|
|
83
|
+
color="success"
|
|
84
|
+
size="s"
|
|
85
|
+
/>{' '}
|
|
86
|
+
Pattern with wildcards:{' '}
|
|
87
|
+
<strong>{currentValue}</strong>
|
|
88
|
+
</span>
|
|
89
|
+
) : (
|
|
90
|
+
<span>
|
|
91
|
+
Will search for:{' '}
|
|
92
|
+
<strong>%{currentValue || 'your-text'}%</strong>{' '}
|
|
93
|
+
(auto-wrapped with wildcards)
|
|
94
|
+
</span>
|
|
95
|
+
)}
|
|
96
|
+
</EuiFormHelpText>
|
|
97
|
+
</>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (pathInfo.example_values && pathInfo.example_values.length > 0) {
|
|
102
|
+
const options = pathInfo.example_values.map((val) => ({
|
|
103
|
+
label: val,
|
|
104
|
+
value: val,
|
|
105
|
+
}));
|
|
106
|
+
return (
|
|
107
|
+
<EuiComboBox
|
|
108
|
+
placeholder={t('selectOrEnterValue')}
|
|
109
|
+
options={options}
|
|
110
|
+
selectedOptions={
|
|
111
|
+
value
|
|
112
|
+
? [{ label: String(value), value: String(value) }]
|
|
113
|
+
: []
|
|
114
|
+
}
|
|
115
|
+
onChange={(selected) => onChange(selected[0]?.value || '')}
|
|
116
|
+
singleSelection={{ asPlainText: true }}
|
|
117
|
+
isClearable
|
|
118
|
+
/>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return (
|
|
122
|
+
<EuiFieldText
|
|
123
|
+
placeholder={t('enterValue')}
|
|
124
|
+
value={String(value || '')}
|
|
125
|
+
onChange={(event) => onChange(event.target.value)}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (pathInfo.type === 'number') {
|
|
131
|
+
if (operator === 'between') {
|
|
132
|
+
const betweenValue = (value as {
|
|
133
|
+
start: number | string;
|
|
134
|
+
end: number | string;
|
|
135
|
+
}) || { start: '', end: '' };
|
|
136
|
+
return (
|
|
137
|
+
<EuiFlexGroup gutterSize="s" alignItems="center">
|
|
138
|
+
<EuiFlexItem>
|
|
139
|
+
<EuiFieldNumber
|
|
140
|
+
placeholder={t('fromNumber')}
|
|
141
|
+
value={betweenValue.start}
|
|
142
|
+
onChange={(event) =>
|
|
143
|
+
onChange({
|
|
144
|
+
...betweenValue,
|
|
145
|
+
start: parseFloat(event.target.value) || '',
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
/>
|
|
149
|
+
</EuiFlexItem>
|
|
150
|
+
<EuiFlexItem grow={false}>
|
|
151
|
+
<EuiText size="s" color={theme.colors.textSubdued}>
|
|
152
|
+
to
|
|
153
|
+
</EuiText>
|
|
154
|
+
</EuiFlexItem>
|
|
155
|
+
<EuiFlexItem>
|
|
156
|
+
<EuiFieldNumber
|
|
157
|
+
placeholder={t('toNumber')}
|
|
158
|
+
value={betweenValue.end}
|
|
159
|
+
onChange={(event) =>
|
|
160
|
+
onChange({
|
|
161
|
+
...betweenValue,
|
|
162
|
+
end: parseFloat(event.target.value) || '',
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
/>
|
|
166
|
+
</EuiFlexItem>
|
|
167
|
+
</EuiFlexGroup>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return (
|
|
171
|
+
<EuiFieldNumber
|
|
172
|
+
placeholder={t('enterNumber')}
|
|
173
|
+
value={
|
|
174
|
+
value !== undefined && value !== null ? Number(value) : ''
|
|
175
|
+
}
|
|
176
|
+
onChange={(event) =>
|
|
177
|
+
onChange(parseFloat(event.target.value) || '')
|
|
178
|
+
}
|
|
179
|
+
/>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (pathInfo.type === 'datetime') {
|
|
184
|
+
if (operator === 'between') {
|
|
185
|
+
const betweenValue = (value as {
|
|
186
|
+
start: string | null;
|
|
187
|
+
end: string | null;
|
|
188
|
+
}) || { start: null, end: null };
|
|
189
|
+
return (
|
|
190
|
+
<EuiFlexGroup gutterSize="s" alignItems="center">
|
|
191
|
+
<EuiFlexItem>
|
|
192
|
+
<EuiDatePicker
|
|
193
|
+
selected={
|
|
194
|
+
betweenValue.start
|
|
195
|
+
? moment(betweenValue.start)
|
|
196
|
+
: null
|
|
197
|
+
}
|
|
198
|
+
onChange={(date) =>
|
|
199
|
+
onChange({
|
|
200
|
+
...betweenValue,
|
|
201
|
+
start: date?.toISOString(),
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
showTimeSelect
|
|
205
|
+
dateFormat="yyyy-MM-dd HH:mm"
|
|
206
|
+
placeholderText={t('fromDate')}
|
|
207
|
+
/>
|
|
208
|
+
</EuiFlexItem>
|
|
209
|
+
<EuiFlexItem grow={false}>
|
|
210
|
+
<EuiText size="s" color={theme.colors.textSubdued}>
|
|
211
|
+
{t('valueControlTo')}
|
|
212
|
+
</EuiText>
|
|
213
|
+
</EuiFlexItem>
|
|
214
|
+
<EuiFlexItem>
|
|
215
|
+
<EuiDatePicker
|
|
216
|
+
selected={
|
|
217
|
+
betweenValue.end
|
|
218
|
+
? moment(betweenValue.end)
|
|
219
|
+
: null
|
|
220
|
+
}
|
|
221
|
+
onChange={(date) =>
|
|
222
|
+
onChange({
|
|
223
|
+
...betweenValue,
|
|
224
|
+
end: date?.toISOString(),
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
showTimeSelect
|
|
228
|
+
dateFormat="yyyy-MM-dd HH:mm"
|
|
229
|
+
placeholderText={t('toDate')}
|
|
230
|
+
/>
|
|
231
|
+
</EuiFlexItem>
|
|
232
|
+
</EuiFlexGroup>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
return (
|
|
236
|
+
<EuiDatePicker
|
|
237
|
+
selected={value ? moment(String(value)) : null}
|
|
238
|
+
onChange={(date) => onChange(date?.toISOString())}
|
|
239
|
+
showTimeSelect
|
|
240
|
+
dateFormat="yyyy-MM-dd HH:mm"
|
|
241
|
+
placeholderText={t('selectDateAndTime')}
|
|
242
|
+
/>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return null;
|
|
247
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ValueControl } from './WfoValueControl';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const DEFAULT_PAGE_SIZE = 5;
|
|
2
|
+
export const DEFAULT_DEBOUNCE_DELAY = 300;
|
|
3
|
+
export const SMALL_RESULT_THRESHOLD = 10;
|
|
4
|
+
|
|
5
|
+
export const LAYOUT_RATIOS = {
|
|
6
|
+
RESULTS_GROW: 2,
|
|
7
|
+
DETAIL_GROW: 3,
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export const VALID_ENTITY_TYPES = [
|
|
11
|
+
'SUBSCRIPTION',
|
|
12
|
+
'PRODUCT',
|
|
13
|
+
'WORKFLOW',
|
|
14
|
+
'PROCESS',
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
export const DEFAULT_ENTITY_TAB = 'SUBSCRIPTION';
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AnySearchResult,
|
|
3
|
+
Condition,
|
|
4
|
+
EntityKind,
|
|
5
|
+
Group,
|
|
6
|
+
ProcessSearchResult,
|
|
7
|
+
ProductSearchResult,
|
|
8
|
+
SubscriptionSearchResult,
|
|
9
|
+
WorkflowSearchResult,
|
|
10
|
+
} from '@/types';
|
|
11
|
+
|
|
12
|
+
export function isSubscriptionSearchResult(
|
|
13
|
+
item: AnySearchResult,
|
|
14
|
+
): item is SubscriptionSearchResult {
|
|
15
|
+
return 'subscription' in item && typeof item.subscription === 'object';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isProcessSearchResult(
|
|
19
|
+
item: AnySearchResult,
|
|
20
|
+
): item is ProcessSearchResult {
|
|
21
|
+
return 'process' in item && typeof item.process === 'object';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isProductSearchResult(
|
|
25
|
+
item: AnySearchResult,
|
|
26
|
+
): item is ProductSearchResult {
|
|
27
|
+
return 'product' in item && typeof item.product === 'object';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isWorkflowSearchResult(
|
|
31
|
+
item: AnySearchResult,
|
|
32
|
+
): item is WorkflowSearchResult {
|
|
33
|
+
return 'workflow' in item && typeof item.workflow === 'object';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const isCondition = (item: Group | Condition): item is Condition => {
|
|
37
|
+
return 'path' in item && 'condition' in item;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const ENDPOINT_PATHS: Record<EntityKind, string> = {
|
|
41
|
+
PROCESS: 'processes',
|
|
42
|
+
PRODUCT: 'products',
|
|
43
|
+
WORKFLOW: 'workflows',
|
|
44
|
+
SUBSCRIPTION: 'subscriptions',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const getEndpointPath = (entityType: EntityKind): string => {
|
|
48
|
+
return ENDPOINT_PATHS[entityType] || ENDPOINT_PATHS.SUBSCRIPTION;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const getDisplayText = (item: AnySearchResult): string => {
|
|
52
|
+
if (isSubscriptionSearchResult(item)) {
|
|
53
|
+
return item.subscription.description || 'Subscription';
|
|
54
|
+
}
|
|
55
|
+
if (isProcessSearchResult(item)) {
|
|
56
|
+
return item.process.workflowName;
|
|
57
|
+
}
|
|
58
|
+
if (isProductSearchResult(item)) {
|
|
59
|
+
return item.product.name;
|
|
60
|
+
}
|
|
61
|
+
if (isWorkflowSearchResult(item)) {
|
|
62
|
+
return item.workflow.name;
|
|
63
|
+
}
|
|
64
|
+
return 'Unknown result type';
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const getRecordId = (result: AnySearchResult): string => {
|
|
68
|
+
if (isSubscriptionSearchResult(result)) {
|
|
69
|
+
return result.subscription.subscription_id;
|
|
70
|
+
}
|
|
71
|
+
if (isProductSearchResult(result)) {
|
|
72
|
+
return result.product.product_id;
|
|
73
|
+
}
|
|
74
|
+
if (isProcessSearchResult(result)) {
|
|
75
|
+
return result.process.processId;
|
|
76
|
+
}
|
|
77
|
+
if (isWorkflowSearchResult(result)) {
|
|
78
|
+
return result.workflow.name;
|
|
79
|
+
}
|
|
80
|
+
return '';
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const findResultIndexById = (
|
|
84
|
+
results: AnySearchResult[],
|
|
85
|
+
recordId: string,
|
|
86
|
+
): number => {
|
|
87
|
+
return results.findIndex((result) => {
|
|
88
|
+
if (isSubscriptionSearchResult(result)) {
|
|
89
|
+
return result.subscription.subscription_id === recordId;
|
|
90
|
+
}
|
|
91
|
+
if (isProductSearchResult(result)) {
|
|
92
|
+
return result.product.product_id === recordId;
|
|
93
|
+
}
|
|
94
|
+
if (isProcessSearchResult(result)) {
|
|
95
|
+
return result.process.processId === recordId;
|
|
96
|
+
}
|
|
97
|
+
if (isWorkflowSearchResult(result)) {
|
|
98
|
+
return result.workflow.name === recordId;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const getDetailUrl = (
|
|
105
|
+
result: AnySearchResult,
|
|
106
|
+
baseUrl: string,
|
|
107
|
+
): string => {
|
|
108
|
+
if (isSubscriptionSearchResult(result)) {
|
|
109
|
+
return `${baseUrl}/subscriptions/${result.subscription.subscription_id}`;
|
|
110
|
+
}
|
|
111
|
+
if (isProductSearchResult(result)) {
|
|
112
|
+
return `${baseUrl}/products/${result.product.product_id}`;
|
|
113
|
+
}
|
|
114
|
+
if (isProcessSearchResult(result)) {
|
|
115
|
+
return `${baseUrl}/processes/${result.process.processId}`;
|
|
116
|
+
}
|
|
117
|
+
if (isWorkflowSearchResult(result)) {
|
|
118
|
+
return `${baseUrl}/workflows/${result.workflow.name}`;
|
|
119
|
+
}
|
|
120
|
+
return '#';
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const getDescription = (result: AnySearchResult): string => {
|
|
124
|
+
if (isSubscriptionSearchResult(result)) {
|
|
125
|
+
return result.subscription.description;
|
|
126
|
+
}
|
|
127
|
+
if (isProductSearchResult(result)) {
|
|
128
|
+
return result.product.description || result.product.name;
|
|
129
|
+
}
|
|
130
|
+
if (isWorkflowSearchResult(result)) {
|
|
131
|
+
return result.workflow.description || result.workflow.name;
|
|
132
|
+
}
|
|
133
|
+
if (isProcessSearchResult(result)) {
|
|
134
|
+
return result.process.workflowName;
|
|
135
|
+
}
|
|
136
|
+
return 'Unknown';
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const ENTITY_TABS = [
|
|
140
|
+
{ id: 'SUBSCRIPTION' as const, label: 'Subscriptions' },
|
|
141
|
+
{ id: 'PRODUCT' as const, label: 'Products' },
|
|
142
|
+
{ id: 'WORKFLOW' as const, label: 'Workflows' },
|
|
143
|
+
{ id: 'PROCESS' as const, label: 'Processes' },
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
interface ThemeColors {
|
|
147
|
+
success: string;
|
|
148
|
+
primary: string;
|
|
149
|
+
warning: string;
|
|
150
|
+
accent: string;
|
|
151
|
+
textSubdued: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
interface Theme {
|
|
155
|
+
colors: ThemeColors;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const TYPE_COLOR_MAP: Record<string, keyof ThemeColors> = {
|
|
159
|
+
string: 'success',
|
|
160
|
+
number: 'primary',
|
|
161
|
+
boolean: 'warning',
|
|
162
|
+
datetime: 'accent',
|
|
163
|
+
component: 'primary',
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const getTypeColor = (type: string, theme: Theme): string => {
|
|
167
|
+
const colorKey = TYPE_COLOR_MAP[type.toLowerCase()];
|
|
168
|
+
return colorKey ? theme.colors[colorKey] : theme.colors.textSubdued;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
interface PathInfo {
|
|
172
|
+
type?: string;
|
|
173
|
+
[key: string]: unknown;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
interface OperatorDisplay {
|
|
177
|
+
symbol: string;
|
|
178
|
+
description: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const OPERATOR_MAP: Record<string, OperatorDisplay> = {
|
|
182
|
+
eq: { symbol: '=', description: 'equals' },
|
|
183
|
+
neq: { symbol: '≠', description: 'not equals' },
|
|
184
|
+
lt: { symbol: '<', description: 'less than' },
|
|
185
|
+
lte: { symbol: '≤', description: 'less than or equal to' },
|
|
186
|
+
gt: { symbol: '>', description: 'greater than' },
|
|
187
|
+
gte: { symbol: '≥', description: 'greater than or equal to' },
|
|
188
|
+
between: { symbol: '⟷', description: 'between (range)' },
|
|
189
|
+
has_component: { symbol: '✓', description: 'has component' },
|
|
190
|
+
not_has_component: { symbol: '✗', description: 'does not have component' },
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const BOOLEAN_OPERATOR_MAP: Record<string, OperatorDisplay> = {
|
|
194
|
+
eq: { symbol: '✓', description: 'is true' },
|
|
195
|
+
neq: { symbol: '✗', description: 'is false' },
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export const getOperatorDisplay = (
|
|
199
|
+
op: string,
|
|
200
|
+
selectedPathInfo?: PathInfo,
|
|
201
|
+
): OperatorDisplay => {
|
|
202
|
+
if (selectedPathInfo?.type === 'boolean' && BOOLEAN_OPERATOR_MAP[op]) {
|
|
203
|
+
return BOOLEAN_OPERATOR_MAP[op];
|
|
204
|
+
}
|
|
205
|
+
return OPERATOR_MAP[op] || { symbol: op, description: op };
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export const getButtonColor = (
|
|
209
|
+
op: string,
|
|
210
|
+
pathInfo: PathInfo | null,
|
|
211
|
+
condition: Condition,
|
|
212
|
+
): 'primary' | 'text' => {
|
|
213
|
+
if (pathInfo?.type === 'boolean') {
|
|
214
|
+
const isSelected =
|
|
215
|
+
op === 'eq'
|
|
216
|
+
? condition.condition.value === true
|
|
217
|
+
: condition.condition.value === false;
|
|
218
|
+
return isSelected ? 'primary' : 'text';
|
|
219
|
+
}
|
|
220
|
+
return condition.condition.op === op ? 'primary' : 'text';
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const getButtonFill = (
|
|
224
|
+
op: string,
|
|
225
|
+
pathInfo: PathInfo | null,
|
|
226
|
+
condition: Condition,
|
|
227
|
+
): boolean => {
|
|
228
|
+
if (pathInfo?.type === 'boolean') {
|
|
229
|
+
return op === 'eq'
|
|
230
|
+
? condition.condition.value === true
|
|
231
|
+
: condition.condition.value === false;
|
|
232
|
+
}
|
|
233
|
+
return condition.condition.op === op;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export const isFilterValid = (group: Group): boolean => {
|
|
237
|
+
return group.children.every((child) => {
|
|
238
|
+
if (isCondition(child)) {
|
|
239
|
+
return (
|
|
240
|
+
child.path &&
|
|
241
|
+
child.condition.op &&
|
|
242
|
+
child.condition.value !== undefined
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
return isFilterValid(child);
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
interface SearchQuery {
|
|
250
|
+
text?: string;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export const buildSearchParams = (
|
|
254
|
+
debouncedQuery: SearchQuery | string,
|
|
255
|
+
selectedEntityTab: EntityKind,
|
|
256
|
+
filterGroup: Group,
|
|
257
|
+
pageSize: number,
|
|
258
|
+
) => {
|
|
259
|
+
const queryText =
|
|
260
|
+
typeof debouncedQuery === 'string'
|
|
261
|
+
? debouncedQuery
|
|
262
|
+
: debouncedQuery?.text?.trim() || '';
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
action: 'select' as const,
|
|
266
|
+
entity_type: selectedEntityTab,
|
|
267
|
+
query: queryText || '',
|
|
268
|
+
filters: filterGroup?.children.length > 0 ? filterGroup : undefined,
|
|
269
|
+
limit: pageSize,
|
|
270
|
+
};
|
|
271
|
+
};
|
package/src/components/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const ORCHESTRATOR_UI_LIBRARY_VERSION = '
|
|
1
|
+
export const ORCHESTRATOR_UI_LIBRARY_VERSION = '6.1.0';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useDebounce<T>(value: T, delay: number): T {
|
|
4
|
+
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (delay <= 0) {
|
|
8
|
+
setDebouncedValue(value);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const handler = setTimeout(() => {
|
|
12
|
+
setDebouncedValue(value);
|
|
13
|
+
}, delay);
|
|
14
|
+
|
|
15
|
+
return () => {
|
|
16
|
+
clearTimeout(handler);
|
|
17
|
+
};
|
|
18
|
+
}, [value, delay]);
|
|
19
|
+
|
|
20
|
+
return debouncedValue;
|
|
21
|
+
}
|