@orchestrator-ui/orchestrator-ui-components 6.4.0 → 6.5.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 (33) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/.turbo/turbo-lint.log +2 -2
  3. package/.turbo/turbo-test.log +8 -8
  4. package/CHANGELOG.md +6 -0
  5. package/dist/index.d.ts +17 -93
  6. package/dist/index.js +603 -332
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/components/WfoAgent/ExportButton/ExportButton.tsx +96 -0
  10. package/src/components/WfoAgent/ExportButton/index.ts +1 -0
  11. package/src/components/WfoAgent/ExportButton/styles.ts +69 -0
  12. package/src/components/WfoAgent/ToolProgress/DiscoverFilterPathsDisplay.tsx +114 -0
  13. package/src/components/WfoAgent/ToolProgress/RunSearchDisplay.tsx +34 -0
  14. package/src/components/WfoAgent/{FilterDisplay/FilterDisplay.tsx → ToolProgress/SetFilterTreeDisplay.tsx} +25 -72
  15. package/src/components/WfoAgent/ToolProgress/StartNewSearchDisplay.tsx +62 -0
  16. package/src/components/WfoAgent/ToolProgress/ToolProgress.tsx +138 -0
  17. package/src/components/WfoAgent/ToolProgress/index.ts +1 -0
  18. package/src/components/WfoAgent/ToolProgress/styles.ts +50 -0
  19. package/src/components/WfoAgent/WfoAgent/WfoAgent.tsx +78 -51
  20. package/src/components/WfoAgent/index.ts +0 -1
  21. package/src/components/WfoSearchPage/WfoSearch/WfoSearch.tsx +6 -10
  22. package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchResultItem.tsx +4 -4
  23. package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchResults.tsx +18 -34
  24. package/src/components/WfoSearchPage/WfoSearchResults/index.ts +0 -1
  25. package/src/components/WfoSearchPage/utils.ts +12 -112
  26. package/src/configuration/version.ts +1 -1
  27. package/src/hooks/useSearchPagination.ts +2 -2
  28. package/src/messages/en-GB.json +1 -3
  29. package/src/rtk/endpoints/agentExport.ts +23 -0
  30. package/src/types/search.ts +8 -70
  31. package/src/components/WfoAgent/FilterDisplay/index.ts +0 -1
  32. package/src/components/WfoSearchPage/WfoSearchResults/WfoSubscriptionDetailModal.tsx +0 -55
  33. /package/src/components/WfoAgent/{FilterDisplay/styles.ts → ToolProgress/SetFilterTreeDisplay.styles.ts} +0 -0
@@ -0,0 +1,50 @@
1
+ import { css } from '@emotion/react';
2
+
3
+ import { WfoTheme } from '@/hooks';
4
+
5
+ export const getToolProgressStyles = ({ theme }: WfoTheme) => {
6
+ const containerStyle = css({
7
+ border: `${theme.border.width.thin} solid ${theme.colors.lightShade}`,
8
+ borderRadius: theme.border.radius.medium,
9
+ backgroundColor: theme.colors.emptyShade,
10
+ transition: `all ${theme.animation.normal} ease`,
11
+ });
12
+
13
+ const containerClickableStyle = css({
14
+ cursor: 'pointer',
15
+ '&:hover': {
16
+ borderColor: theme.colors.primary,
17
+ backgroundColor: theme.colors.lightestShade,
18
+ },
19
+ });
20
+
21
+ const headerStyle = css({
22
+ padding: `${theme.size.base} ${theme.size.l}`,
23
+ });
24
+
25
+ const nameStyle = css({
26
+ fontSize: theme.size.m,
27
+ fontWeight: theme.font.weight.medium,
28
+ });
29
+
30
+ const expandedContentStyle = css({
31
+ borderTop: `${theme.border.width.thin} solid ${theme.colors.lightShade}`,
32
+ padding: `${theme.size.base} ${theme.size.l}`,
33
+ });
34
+
35
+ const iconSize = 18;
36
+
37
+ const iconStyle = css({
38
+ color: theme.colors.subduedText,
39
+ });
40
+
41
+ return {
42
+ containerStyle,
43
+ containerClickableStyle,
44
+ headerStyle,
45
+ nameStyle,
46
+ expandedContentStyle,
47
+ iconSize,
48
+ iconStyle,
49
+ };
50
+ };
@@ -2,28 +2,44 @@ import React from 'react';
2
2
 
3
3
  import { useTranslations } from 'next-intl';
4
4
 
5
- import { useCoAgent } from '@copilotkit/react-core';
5
+ import {
6
+ CatchAllActionRenderProps,
7
+ useCoAgent,
8
+ useCoAgentStateRender,
9
+ useCopilotAction,
10
+ } from '@copilotkit/react-core';
6
11
  import { CopilotSidebar } from '@copilotkit/react-ui';
7
12
  import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui';
8
13
 
9
14
  import { WfoSearchResults } from '@/components/WfoSearchPage/WfoSearchResults';
10
- import { AnySearchParameters, AnySearchResult, PathFilter } from '@/types';
11
-
12
- import { FilterDisplay } from '../FilterDisplay';
15
+ import { AnySearchParameters, SearchResult } from '@/types';
16
+
17
+ import { ExportButton, ExportData } from '../ExportButton';
18
+ import { ToolProgress } from '../ToolProgress';
19
+
20
+ type SearchResultsData = {
21
+ action: string;
22
+ query_id: string;
23
+ results_url: string;
24
+ total_count: number;
25
+ message: string;
26
+ results: SearchResult[];
27
+ };
13
28
 
14
29
  type SearchState = {
15
- parameters: AnySearchParameters;
16
- results: AnySearchResult[];
30
+ run_id: string | null;
31
+ query_id: string | null;
32
+ parameters: AnySearchParameters | null;
33
+ results_data: SearchResultsData | null;
34
+ export_data: ExportData | null;
17
35
  };
18
36
 
19
37
  const initialState: SearchState = {
20
- parameters: {
21
- action: 'select',
22
- entity_type: 'SUBSCRIPTION',
23
- filters: [] as PathFilter[],
24
- query: null,
25
- },
26
- results: [],
38
+ run_id: null,
39
+ query_id: null,
40
+ parameters: null,
41
+ results_data: null,
42
+ export_data: null,
27
43
  };
28
44
 
29
45
  export function WfoAgent() {
@@ -34,23 +50,38 @@ export function WfoAgent() {
34
50
  name: 'query_agent',
35
51
  initialState,
36
52
  });
37
- const { parameters, results } = state;
38
-
39
- const hasStarted = !!(
40
- state.parameters &&
41
- Array.isArray(state.parameters.filters) &&
42
- state.parameters.filters.length > 0
43
- );
44
-
45
- const isLoadingResults =
46
- hasStarted && (!state.results || state.results.length === 0);
53
+ const { results_data } = state;
54
+
55
+ // Automatically render all tool calls
56
+ useCopilotAction({
57
+ name: '*',
58
+ render: ({
59
+ name,
60
+ status,
61
+ args,
62
+ result,
63
+ }: CatchAllActionRenderProps<[]>) => {
64
+ return (
65
+ <ToolProgress
66
+ name={name}
67
+ status={status}
68
+ args={args}
69
+ result={result}
70
+ />
71
+ );
72
+ },
73
+ });
47
74
 
48
- const displayParameters = parameters && {
49
- ...parameters,
50
- filters: Array.isArray(parameters.filters)
51
- ? { op: 'AND' as const, children: parameters.filters }
52
- : parameters.filters,
53
- };
75
+ // Render export button from state
76
+ useCoAgentStateRender<SearchState>({
77
+ name: 'query_agent',
78
+ render: ({ state }) => {
79
+ if (!state?.export_data || state.export_data.action !== 'export') {
80
+ return null;
81
+ }
82
+ return <ExportButton exportData={state.export_data} />;
83
+ },
84
+ });
54
85
 
55
86
  return (
56
87
  <EuiFlexGroup gutterSize="l" alignItems="stretch">
@@ -60,29 +91,25 @@ export function WfoAgent() {
60
91
  </EuiText>
61
92
 
62
93
  <EuiSpacer size="m" />
63
- <EuiText size="s">
64
- <h2>{tPage('filledParameters')}</h2>
65
- </EuiText>
66
- <EuiSpacer size="s" />
67
- {displayParameters && (
68
- <FilterDisplay parameters={displayParameters} />
69
- )}
70
-
71
- <EuiSpacer size="m" />
72
- <EuiText size="s">
73
- <h2>
74
- {tPage('results')}{' '}
75
- {results ? `(${results.length})` : ''}
76
- </h2>
77
- </EuiText>
78
- <EuiSpacer size="s" />
79
94
 
80
- <WfoSearchResults
81
- results={results ?? []}
82
- loading={isLoadingResults}
83
- selectedRecordIndex={-1}
84
- onRecordSelect={() => {}}
85
- />
95
+ {results_data && results_data.action === 'view_results' && (
96
+ <>
97
+ {results_data.message && (
98
+ <>
99
+ <EuiText size="s">
100
+ <p>{results_data.message}</p>
101
+ </EuiText>
102
+ <EuiSpacer size="s" />
103
+ </>
104
+ )}
105
+ <WfoSearchResults
106
+ results={results_data.results}
107
+ loading={false}
108
+ selectedRecordIndex={-1}
109
+ onRecordSelect={() => {}}
110
+ />
111
+ </>
112
+ )}
86
113
  </EuiFlexItem>
87
114
 
88
115
  <EuiFlexItem grow={1}>
@@ -1,2 +1 @@
1
1
  export * from './WfoAgent';
2
- export * from './FilterDisplay';
@@ -19,8 +19,6 @@ import { WfoSubscription } from '@/components';
19
19
  import { WfoBadge } from '@/components/WfoBadges';
20
20
  import {
21
21
  ENTITY_TABS,
22
- findResultIndexById,
23
- getRecordId,
24
22
  isSubscriptionSearchResult,
25
23
  } from '@/components/WfoSearchPage/utils';
26
24
  import { TreeProvider } from '@/contexts';
@@ -172,9 +170,8 @@ export const WfoSearch = () => {
172
170
  useEffect(() => {
173
171
  if (results.data.length > 0) {
174
172
  if (selectedRecordId) {
175
- const foundIndex = findResultIndexById(
176
- results.data,
177
- selectedRecordId,
173
+ const foundIndex = results.data.findIndex(
174
+ (result) => result.entity_id === selectedRecordId,
178
175
  );
179
176
 
180
177
  if (foundIndex !== -1) {
@@ -368,9 +365,9 @@ export const WfoSearch = () => {
368
365
  setSelectedRecordIndex(index);
369
366
  const record = results.data[index];
370
367
  if (record) {
371
- const recordId =
372
- getRecordId(record);
373
- setSelectedRecordId(recordId);
368
+ setSelectedRecordId(
369
+ record.entity_id,
370
+ );
374
371
  }
375
372
  }}
376
373
  />
@@ -395,8 +392,7 @@ export const WfoSearch = () => {
395
392
  subscriptionId={
396
393
  results.data[
397
394
  selectedRecordIndex
398
- ].subscription
399
- .subscription_id
395
+ ].entity_id
400
396
  }
401
397
  />
402
398
  </TreeProvider>
@@ -13,14 +13,14 @@ import {
13
13
 
14
14
  import { WfoBadge } from '@/components/WfoBadges';
15
15
  import { useOrchestratorTheme } from '@/hooks';
16
- import { AnySearchResult } from '@/types';
16
+ import { SearchResult } from '@/types';
17
17
 
18
- import { getDescription, getDetailUrl } from '../utils';
18
+ import { getDetailUrl } from '../utils';
19
19
  import { WfoHighlightedText } from './WfoHighlightedText';
20
20
  import { WfoPathBreadcrumb } from './WfoPathBreadcrumb';
21
21
 
22
22
  interface WfoSearchResultItemProps {
23
- result: AnySearchResult;
23
+ result: SearchResult;
24
24
  index: number;
25
25
  isSelected?: boolean;
26
26
  onSelect?: () => void;
@@ -79,7 +79,7 @@ export const WfoSearchResultItem: FC<WfoSearchResultItemProps> = ({
79
79
  fontWeight: theme.font.weight.semiBold,
80
80
  }}
81
81
  >
82
- {getDescription(result)}
82
+ {result.entity_title}
83
83
  </EuiText>
84
84
  </EuiFlexItem>
85
85
  {matchingField && (
@@ -1,16 +1,15 @@
1
- import React, { useState } from 'react';
1
+ import React from 'react';
2
2
 
3
3
  import { EuiFlexGroup, EuiPanel } from '@elastic/eui';
4
4
 
5
- import { AnySearchResult } from '@/types';
5
+ import { SearchResult } from '@/types';
6
6
 
7
7
  import { WfoSearchEmptyState } from './WfoSearchEmptyState';
8
8
  import { WfoSearchLoadingState } from './WfoSearchLoadingState';
9
9
  import { WfoSearchResultItem } from './WfoSearchResultItem';
10
- import { WfoSubscriptionDetailModal } from './WfoSubscriptionDetailModal';
11
10
 
12
11
  interface WfoSearchResultsProps {
13
- results: AnySearchResult[];
12
+ results: SearchResult[];
14
13
  loading: boolean;
15
14
  selectedRecordIndex?: number;
16
15
  onRecordSelect?: (index: number) => void;
@@ -22,15 +21,6 @@ export const WfoSearchResults = ({
22
21
  selectedRecordIndex = 0,
23
22
  onRecordSelect,
24
23
  }: WfoSearchResultsProps) => {
25
- const [modalData, setModalData] = useState<{
26
- subscription: unknown;
27
- matchingField?: unknown;
28
- } | null>(null);
29
-
30
- const handleCloseModal = () => {
31
- setModalData(null);
32
- };
33
-
34
24
  if (loading) {
35
25
  return <WfoSearchLoadingState />;
36
26
  }
@@ -40,26 +30,20 @@ export const WfoSearchResults = ({
40
30
  }
41
31
 
42
32
  return (
43
- <>
44
- <EuiPanel paddingSize="m" hasShadow={false}>
45
- <EuiFlexGroup direction="column" gutterSize="s">
46
- {results.map((result, idx) => (
47
- <WfoSearchResultItem
48
- key={idx}
49
- result={result}
50
- index={idx}
51
- isSelected={idx === selectedRecordIndex}
52
- onSelect={() => onRecordSelect?.(idx)}
53
- />
54
- ))}
55
- </EuiFlexGroup>
56
- </EuiPanel>
57
- <WfoSubscriptionDetailModal
58
- isVisible={!!modalData}
59
- onClose={handleCloseModal}
60
- subscriptionData={modalData?.subscription}
61
- matchingField={modalData?.matchingField}
62
- />
63
- </>
33
+ <EuiPanel paddingSize="m" hasShadow={false}>
34
+ <EuiFlexGroup direction="column" gutterSize="s">
35
+ {results.map((result, idx) => (
36
+ <WfoSearchResultItem
37
+ key={idx}
38
+ result={result}
39
+ index={idx}
40
+ isSelected={idx === selectedRecordIndex}
41
+ onSelect={() => {
42
+ onRecordSelect?.(idx);
43
+ }}
44
+ />
45
+ ))}
46
+ </EuiFlexGroup>
47
+ </EuiPanel>
64
48
  );
65
49
  };
@@ -7,4 +7,3 @@ export * from './WfoSearchMetadataHeader';
7
7
  export * from './WfoSearchPaginationInfo';
8
8
  export * from './WfoHighlightedText';
9
9
  export * from './WfoPathBreadcrumb';
10
- export * from './WfoSubscriptionDetailModal';
@@ -1,36 +1,19 @@
1
- import {
2
- AnySearchResult,
3
- Condition,
4
- EntityKind,
5
- Group,
6
- ProcessSearchResult,
7
- ProductSearchResult,
8
- SubscriptionSearchResult,
9
- WorkflowSearchResult,
10
- } from '@/types';
1
+ import { Condition, EntityKind, Group, SearchResult } from '@/types';
11
2
 
12
- export function isSubscriptionSearchResult(
13
- item: AnySearchResult,
14
- ): item is SubscriptionSearchResult {
15
- return 'subscription' in item && typeof item.subscription === 'object';
3
+ export function isSubscriptionSearchResult(item: SearchResult): boolean {
4
+ return item.entity_type === 'SUBSCRIPTION';
16
5
  }
17
6
 
18
- export function isProcessSearchResult(
19
- item: AnySearchResult,
20
- ): item is ProcessSearchResult {
21
- return 'process' in item && typeof item.process === 'object';
7
+ export function isProcessSearchResult(item: SearchResult): boolean {
8
+ return item.entity_type === 'PROCESS';
22
9
  }
23
10
 
24
- export function isProductSearchResult(
25
- item: AnySearchResult,
26
- ): item is ProductSearchResult {
27
- return 'product' in item && typeof item.product === 'object';
11
+ export function isProductSearchResult(item: SearchResult): boolean {
12
+ return item.entity_type === 'PRODUCT';
28
13
  }
29
14
 
30
- export function isWorkflowSearchResult(
31
- item: AnySearchResult,
32
- ): item is WorkflowSearchResult {
33
- return 'workflow' in item && typeof item.workflow === 'object';
15
+ export function isWorkflowSearchResult(item: SearchResult): boolean {
16
+ return item.entity_type === 'WORKFLOW';
34
17
  }
35
18
 
36
19
  export const isCondition = (item: Group | Condition): item is Condition => {
@@ -48,92 +31,9 @@ export const getEndpointPath = (entityType: EntityKind): string => {
48
31
  return ENDPOINT_PATHS[entityType] || ENDPOINT_PATHS.SUBSCRIPTION;
49
32
  };
50
33
 
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';
34
+ export const getDetailUrl = (result: SearchResult, baseUrl: string): string => {
35
+ const endpointPath = getEndpointPath(result.entity_type);
36
+ return `${baseUrl}/${endpointPath}/${result.entity_id}`;
137
37
  };
138
38
 
139
39
  export const ENTITY_TABS = [
@@ -1 +1 @@
1
- export const ORCHESTRATOR_UI_LIBRARY_VERSION = '6.4.0';
1
+ export const ORCHESTRATOR_UI_LIBRARY_VERSION = '6.5.0';
@@ -5,15 +5,15 @@ import { Query } from '@elastic/eui';
5
5
  import { buildSearchParams } from '@/components/WfoSearchPage/utils';
6
6
  import { useSearchWithPaginationMutation } from '@/rtk/endpoints';
7
7
  import {
8
- AnySearchResult,
9
8
  EntityKind,
10
9
  Group,
11
10
  PaginatedSearchResults,
11
+ SearchResult,
12
12
  } from '@/types';
13
13
 
14
14
  interface PageHistoryItem {
15
15
  page: number;
16
- results: AnySearchResult[];
16
+ results: SearchResult[];
17
17
  cursor: number | null;
18
18
  }
19
19
 
@@ -475,7 +475,6 @@
475
475
  "title": "Search results",
476
476
  "page": {
477
477
  "filledParameters": "Filled parameters",
478
- "results": "Results",
479
478
  "emptyGroup": "Empty group",
480
479
  "searchQuery": "Search query",
481
480
  "activeFilters": "Active filters",
@@ -484,7 +483,7 @@
484
483
  "action": "Action",
485
484
  "copilot": {
486
485
  "title": "Database assistant",
487
- "initial": "Ask me things such as:\n• *Find active subscriptions for Surf*\n• *Show terminated workflows”*\n\nThe filled template and results will appear on the left."
486
+ "initial": "Ask me things such as:\n• *Find active subscriptions*\n• *Show terminated workflows”*\n\nThe filled template and results will appear on the left."
488
487
  }
489
488
  }
490
489
  },
@@ -521,7 +520,6 @@
521
520
  "resultsOnPage": "{resultCount} result(s) on this page",
522
521
  "searchResultsPagination": "Search results pagination",
523
522
  "viewDetails": "View details",
524
- "closeButton": "Close",
525
523
  "selectOrEnterValue": "Select or type value",
526
524
  "enterValue": "Enter value",
527
525
  "fromNumber": "From",
@@ -0,0 +1,23 @@
1
+ import { BaseQueryTypes, orchestratorApi } from '@/rtk';
2
+ import { GraphQLPageInfo } from '@/types';
3
+
4
+ export type AgentExportResponse = {
5
+ page: object[];
6
+ pageInfo?: GraphQLPageInfo;
7
+ };
8
+
9
+ const agentExportApi = orchestratorApi.injectEndpoints({
10
+ endpoints: (builder) => ({
11
+ getAgentExport: builder.query<AgentExportResponse, string>({
12
+ query: (downloadUrl) => ({
13
+ url: downloadUrl,
14
+ method: 'GET',
15
+ }),
16
+ extraOptions: {
17
+ baseQueryType: BaseQueryTypes.fetch,
18
+ },
19
+ }),
20
+ }),
21
+ });
22
+
23
+ export const { useLazyGetAgentExportQuery } = agentExportApi;