@orchestrator-ui/orchestrator-ui-components 7.5.1 → 7.7.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 (58) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +7 -7
  4. package/CHANGELOG.md +26 -0
  5. package/dist/index.d.ts +542 -53
  6. package/dist/index.js +1297 -1101
  7. package/dist/index.js.map +1 -1
  8. package/jest.config.cjs +4 -1
  9. package/package.json +6 -6
  10. package/src/components/WfoAgent/ExportButton/ExportButton.tsx +5 -11
  11. package/src/components/WfoAgent/WfoAgent/WfoAgent.tsx +79 -31
  12. package/src/components/WfoAgent/WfoAgentChart/WfoAgentLineChart.tsx +2 -2
  13. package/src/components/WfoAgent/WfoAgentChart/WfoAgentPieChart.tsx +2 -2
  14. package/src/components/WfoAgent/WfoAgentTable/WfoAgentTable.tsx +9 -9
  15. package/src/components/WfoAgent/WfoAgentVisualization/WfoAgentVisualization.tsx +2 -2
  16. package/src/components/WfoAgent/WfoPlanProgress/WfoPlanProgress.tsx +107 -0
  17. package/src/components/WfoAgent/WfoPlanProgress/index.ts +1 -0
  18. package/src/components/WfoAgent/WfoPlanProgress/styles.ts +62 -0
  19. package/src/components/WfoAgent/WfoQueryArtifact/WfoQueryArtifact.tsx +40 -0
  20. package/src/components/WfoAgent/WfoQueryArtifact/index.ts +1 -0
  21. package/src/components/WfoAgent/index.ts +2 -0
  22. package/src/components/WfoBadges/WfoEngineStatusBadge/WfoEngineStatusBadge.tsx +3 -1
  23. package/src/components/WfoBadges/WfoVersionIncompatibleBadge/WfoVersionIncompatibleBadge.tsx +7 -6
  24. package/src/components/WfoBadges/WfoWebsocketStatusBadge/WfoWebsocketStatusBadge.tsx +9 -3
  25. package/src/components/WfoKeyValueTable/WfoValueCell.tsx +1 -1
  26. package/src/components/WfoPydanticForm/fields/WfoInteger.tsx +22 -3
  27. package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActions.tsx +3 -2
  28. package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/WfoSubscriptionDetailSection.tsx +34 -3
  29. package/src/components/WfoSubscriptionsList/WfoSubscriptionsList.tsx +9 -9
  30. package/src/components/WfoTable/WfoAdvancedTable/WfoAdvancedTable.tsx +1 -1
  31. package/src/components/WfoTable/WfoFirstPartUUID/WfoFirstPartUUID.tsx +1 -1
  32. package/src/components/WfoTitleWithWebsocketBadge/WfoTitleWithWebsocketBadge.tsx +3 -2
  33. package/src/components/WfoWorkflowSteps/WfoStep/WfoStep.tsx +57 -40
  34. package/src/components/WfoWorkflowSteps/WfoStep/useStepDetailOverride.ts +9 -0
  35. package/src/components/WfoWorkflowSteps/WfoWorkflowStepList/WfoStepListHeader.tsx +0 -1
  36. package/src/configuration/constants.ts +3 -0
  37. package/src/configuration/version.ts +1 -1
  38. package/src/hooks/useAgentPlanEvents.ts +188 -0
  39. package/src/messages/en-GB.json +7 -0
  40. package/src/messages/nl-NL.json +2 -0
  41. package/src/pages/metadata/WfoScheduleTaskFormPage.tsx +153 -35
  42. package/src/rtk/endpoints/agentQueryResults.ts +19 -0
  43. package/src/rtk/endpoints/forms.ts +1 -1
  44. package/src/rtk/endpoints/index.ts +1 -0
  45. package/src/rtk/endpoints/metadata/scheduledTasks.ts +9 -17
  46. package/src/rtk/endpoints/streamMessages.ts +10 -23
  47. package/src/rtk/slices/orchestratorComponentOverride.ts +5 -1
  48. package/src/types/search.ts +19 -4
  49. package/src/utils/compareVersions.spec.ts +5 -0
  50. package/src/utils/compareVersions.ts +55 -23
  51. package/src/components/WfoAgent/ToolProgress/DiscoverFilterPathsDisplay.tsx +0 -99
  52. package/src/components/WfoAgent/ToolProgress/RunSearchDisplay.tsx +0 -34
  53. package/src/components/WfoAgent/ToolProgress/SetFilterTreeDisplay.styles.ts +0 -62
  54. package/src/components/WfoAgent/ToolProgress/SetFilterTreeDisplay.tsx +0 -107
  55. package/src/components/WfoAgent/ToolProgress/StartNewSearchDisplay.tsx +0 -60
  56. package/src/components/WfoAgent/ToolProgress/ToolProgress.tsx +0 -98
  57. package/src/components/WfoAgent/ToolProgress/index.ts +0 -1
  58. package/src/components/WfoAgent/ToolProgress/styles.ts +0 -52
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect } from 'react';
2
2
 
3
3
  import _ from 'lodash';
4
4
  import type { PydanticFormControlledElement } from 'pydantic-forms';
@@ -31,14 +31,33 @@ export const WfoInteger: PydanticFormControlledElement = ({ pydanticFormField, o
31
31
  // this is imposed by react-hook-form. We try to detect this and extract the actual value
32
32
  const fieldName = getFormFieldIdWithPath(pydanticFormField.id);
33
33
  const fieldValue = _.isObject(value) && _.has(value, fieldName) ? _.get(value, fieldName) : value;
34
+ const [userInput, setUserInput] = React.useState<string>();
35
+
36
+ useEffect(() => {
37
+ if (fieldValue !== undefined && fieldValue !== null) {
38
+ setUserInput(fieldValue);
39
+ } else {
40
+ setUserInput('');
41
+ }
42
+ }, [fieldValue, value]);
43
+
44
+ const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
45
+ const newValue = event.target.value;
46
+ if (value === null && newValue !== value) {
47
+ onChange(parseInt(newValue));
48
+ }
49
+ setUserInput(newValue);
50
+ };
34
51
 
35
52
  return (
36
53
  <EuiFieldNumber
54
+ placeholder="Enter a number"
37
55
  data-testid={pydanticFormField.id}
38
56
  css={formFieldBaseStyle}
39
57
  name={pydanticFormField.id}
40
- onChange={(event) => onChange(event.target.value ? parseInt(event.target.value) : null)}
41
- value={fieldValue}
58
+ onBlur={(event) => onChange(event.target.value ? parseInt(event.target.value) : null)}
59
+ onChange={handleOnChange}
60
+ value={userInput}
42
61
  disabled={disabled}
43
62
  />
44
63
  );
@@ -9,7 +9,7 @@ import { WfoPopover } from '@/components';
9
9
  import { PATH_START_NEW_TASK, PATH_START_NEW_WORKFLOW, WfoInSyncField } from '@/components';
10
10
  import { WfoSubscriptionActionsMenuItem } from '@/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActionsMenuItem';
11
11
  import { PolicyResource } from '@/configuration/policy-resources';
12
- import { usePolicy } from '@/hooks';
12
+ import { useOrchestratorTheme, usePolicy } from '@/hooks';
13
13
  import { WfoDotsHorizontal } from '@/icons/WfoDotsHorizontal';
14
14
  import { useGetSubscriptionActionsQuery, useGetSubscriptionDetailQuery, useStartProcessMutation } from '@/rtk';
15
15
  import { WorkflowTarget } from '@/types';
@@ -35,6 +35,7 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
35
35
  compactMode = false,
36
36
  }) => {
37
37
  const t = useTranslations('subscriptions.detail.actions');
38
+ const { theme } = useOrchestratorTheme();
38
39
  const [isPopoverOpen, setPopover] = useState<boolean>(false);
39
40
  const router = useRouter();
40
41
  const disableQuery = isLoading || (!isPopoverOpen && compactMode);
@@ -59,7 +60,7 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
59
60
  const button =
60
61
  compactMode ?
61
62
  <EuiButtonIcon
62
- iconType={() => <WfoDotsHorizontal />}
63
+ iconType={() => <WfoDotsHorizontal color={theme.colors.textDisabled} />}
63
64
  onClick={onButtonClick}
64
65
  aria-label="Row context menu"
65
66
  isLoading={isLoading}
@@ -1,15 +1,17 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { useTranslations } from 'next-intl';
4
+ import Link from 'next/link';
4
5
 
5
6
  import {
7
+ PATH_TASKS,
6
8
  SubscriptionKeyValueBlock,
7
9
  WfoCustomerDescriptionsField,
8
10
  WfoInSyncField,
9
11
  WfoSubscriptionDetailNoteEdit,
10
12
  WfoSubscriptionStatusBadge,
11
13
  } from '@/components';
12
- import { SubscriptionDetail } from '@/types';
14
+ import { SubscriptionDetail, WorkflowTarget } from '@/types';
13
15
  import { formatDate } from '@/utils';
14
16
 
15
17
  interface WfoSubscriptionDetailSectionProps {
@@ -19,8 +21,17 @@ interface WfoSubscriptionDetailSectionProps {
19
21
  export const WfoSubscriptionDetailSection = ({ subscriptionDetail }: WfoSubscriptionDetailSectionProps) => {
20
22
  const t = useTranslations('subscriptions.detail');
21
23
 
22
- const { subscriptionId, product, description, startDate, endDate, status, customer, customerDescriptions } =
23
- subscriptionDetail;
24
+ const {
25
+ subscriptionId,
26
+ product,
27
+ description,
28
+ startDate,
29
+ endDate,
30
+ status,
31
+ customer,
32
+ customerDescriptions,
33
+ processes,
34
+ } = subscriptionDetail;
24
35
 
25
36
  const subscriptionDetailBlockData = [
26
37
  {
@@ -52,6 +63,26 @@ export const WfoSubscriptionDetailSection = ({ subscriptionDetail }: WfoSubscrip
52
63
  key: t('insync'),
53
64
  value: <WfoInSyncField subscriptionDetail={subscriptionDetail} />,
54
65
  },
66
+ {
67
+ key: t('lastRunValidation'),
68
+ value: (() => {
69
+ const lastValidate = processes?.page
70
+ ?.filter((process) => process.workflowTarget.toLowerCase() === WorkflowTarget.VALIDATE.toLowerCase())
71
+ .slice(-1)[0];
72
+
73
+ if (!lastValidate) {
74
+ return t('noValidateWorkflows');
75
+ }
76
+
77
+ const processUrl = `${PATH_TASKS}/${lastValidate.processId}`;
78
+
79
+ return (
80
+ <Link href={processUrl}>
81
+ {lastValidate.lastStatus} ({formatDate(lastValidate.startedAt)})
82
+ </Link>
83
+ );
84
+ })(),
85
+ },
55
86
  {
56
87
  key: t('customer'),
57
88
  value: subscriptionDetail && subscriptionDetail.customer ? `${customer?.fullname}` : '-',
@@ -80,6 +80,11 @@ export const WfoSubscriptionsList: FC<WfoSubscriptionsListProps> = ({
80
80
  const subscriptionList = mapGraphQlSubscriptionsResultToSubscriptionListItems(data);
81
81
 
82
82
  const tableColumnConfig: WfoAdvancedTableColumnConfig<SubscriptionListItem> = {
83
+ actions: {
84
+ columnType: ColumnType.CONTROL,
85
+ width: '50px',
86
+ renderControl: (row) => <WfoSubscriptionActions compactMode={true} subscriptionId={row.subscriptionId} />,
87
+ },
83
88
  subscriptionId: {
84
89
  columnType: ColumnType.DATA,
85
90
  label: t('id'),
@@ -104,11 +109,12 @@ export const WfoSubscriptionsList: FC<WfoSubscriptionsListProps> = ({
104
109
  insync: {
105
110
  columnType: ColumnType.DATA,
106
111
  label: t('insync'),
107
- width: '80px',
112
+ width: '75px',
108
113
  renderData: (value) => <WfoInsyncIcon inSync={value} />,
109
114
  },
110
115
  productName: {
111
116
  columnType: ColumnType.DATA,
117
+ width: '260px',
112
118
  label: t('product'),
113
119
  },
114
120
  tag: {
@@ -128,7 +134,7 @@ export const WfoSubscriptionsList: FC<WfoSubscriptionsListProps> = ({
128
134
  startDate: {
129
135
  columnType: ColumnType.DATA,
130
136
  label: t('startDate'),
131
- width: '120px',
137
+ width: '100px',
132
138
  renderData: (value) => <WfoDateTime dateOrIsoString={value} />,
133
139
  renderDetails: parseDateToLocaleDateTimeString,
134
140
  clipboardText: parseDateToLocaleDateTimeString,
@@ -137,7 +143,7 @@ export const WfoSubscriptionsList: FC<WfoSubscriptionsListProps> = ({
137
143
  endDate: {
138
144
  columnType: ColumnType.DATA,
139
145
  label: t('endDate'),
140
- width: '120px',
146
+ width: '100px',
141
147
  renderData: (value) => <WfoDateTime dateOrIsoString={value} />,
142
148
  renderDetails: parseDateToLocaleDateTimeString,
143
149
  clipboardText: parseDateToLocaleDateTimeString,
@@ -159,12 +165,6 @@ export const WfoSubscriptionsList: FC<WfoSubscriptionsListProps> = ({
159
165
  );
160
166
  },
161
167
  },
162
- actions: {
163
- columnType: ColumnType.CONTROL,
164
- label: t('actions'),
165
- width: '80px',
166
- renderControl: (row) => <WfoSubscriptionActions compactMode={true} subscriptionId={row.subscriptionId} />,
167
- },
168
168
  metadata: {
169
169
  columnType: ColumnType.DATA,
170
170
  label: t('metadata'),
@@ -81,7 +81,7 @@ export const WfoAdvancedTable = <T extends object>({
81
81
  width: '36px',
82
82
  renderControl: (row) => (
83
83
  <EuiFlexItem css={{ cursor: 'pointer' }} onClick={() => setSelectedDataForDetailModal(row)}>
84
- <WfoArrowsExpand color={theme.colors.borderBasePlain} />
84
+ <WfoArrowsExpand color={theme.colors.textDisabled} />
85
85
  </EuiFlexItem>
86
86
  ),
87
87
  },
@@ -24,7 +24,7 @@ export const WfoFirstPartUUID: FC<WfoFirstUUIDPartProps> = ({ UUID, showCopyIcon
24
24
  <EuiCopy textToCopy={UUID}>
25
25
  {(copy) => (
26
26
  <div className={COPY_ICON_CLASS} onClick={copy} css={clickable}>
27
- <WfoClipboardCopy width={16} height={16} color={theme.colors.backgroundBaseDisabled} />
27
+ <WfoClipboardCopy width={16} height={16} color={theme.colors.backgroundBaseAccent} />
28
28
  </div>
29
29
  )}
30
30
  </EuiCopy>
@@ -7,15 +7,16 @@ import { useGetOrchestratorConfig } from '@/hooks';
7
7
 
8
8
  interface WfoTitleWithWebsocketBadgeProps {
9
9
  title: string;
10
+ wsUrl?: string;
10
11
  }
11
12
 
12
- export const WfoTitleWithWebsocketBadge = ({ title }: WfoTitleWithWebsocketBadgeProps) => {
13
+ export const WfoTitleWithWebsocketBadge = ({ title, wsUrl = undefined }: WfoTitleWithWebsocketBadgeProps) => {
13
14
  const { useWebSockets } = useGetOrchestratorConfig();
14
15
 
15
16
  const pageTitle =
16
17
  useWebSockets ?
17
18
  <>
18
- {title} <WfoWebsocketStatusBadge />
19
+ {title} <WfoWebsocketStatusBadge wsUrl={wsUrl} />
19
20
  </>
20
21
  : title;
21
22
 
@@ -17,6 +17,7 @@ import { getStepContent } from '../stepListUtils';
17
17
  import { getWorkflowStepsStyles } from '../styles';
18
18
  import { CodeView, WfoCodeViewSelector } from './WfoCodeViewSelector';
19
19
  import { WfoStepForm } from './WfoStepForm';
20
+ import { useStepDetailOverride } from './useStepDetailOverride';
20
21
 
21
22
  export interface WfoStepProps {
22
23
  stepListItem: StepListItem;
@@ -58,6 +59,7 @@ export const WfoStep = React.forwardRef(
58
59
  stepRowStyle,
59
60
  getStepToggleExpandStyle,
60
61
  } = useWithOrchestratorTheme(getWorkflowStepsStyles);
62
+ const { overrideStepDetail } = useStepDetailOverride();
61
63
  const t = useTranslations('processes.steps');
62
64
  const hasHtmlMail = Object.prototype.hasOwnProperty.call(step?.stateDelta || {}, 'confirmation_mail');
63
65
 
@@ -104,55 +106,70 @@ export const WfoStep = React.forwardRef(
104
106
  },
105
107
  [setCodeView],
106
108
  );
107
-
109
+ const shouldExpand: boolean = isExpanded && hasStepContent;
108
110
  return (
109
111
  <div ref={ref}>
110
112
  <EuiPanel>
111
113
  <EuiFlexGroup css={getStepHeaderStyle(hasStepContent)} onClick={() => hasStepContent && onToggleStepDetail()}>
112
114
  <WfoStepStatusIcon stepStatus={step.status} isStartStep={isStartStep} />
113
115
 
114
- <EuiFlexItem grow={0}>
115
- <EuiText css={stepListContentBoldTextStyle}>{step.name}</EuiText>
116
- <EuiText>
117
- {step.status} {step.completed && `- ${formatDate(step.completed)}`}
118
- </EuiText>
119
- </EuiFlexItem>
116
+ <EuiFlexGroup direction="column" gutterSize="none">
117
+ <EuiFlexItem css={{ flexDirection: 'row' }}>
118
+ <EuiFlexItem grow={0}>
119
+ <EuiText css={stepListContentBoldTextStyle}>{step.name}</EuiText>
120
+ <EuiText>
121
+ {step.status} {step.completed && `- ${formatDate(step.completed)}`}
122
+ </EuiText>
123
+ </EuiFlexItem>
120
124
 
121
- <EuiFlexGroup css={stepRowStyle}>
122
- {step.completed && (
123
- <>
124
- {isExpanded && <WfoCodeViewSelector codeView={codeView} handleCodeViewChange={handle} />}
125
- <EuiFlexItem grow={0} css={stepHeaderRightStyle}>
126
- <EuiText css={stepDurationStyle}>{t('duration')}</EuiText>
127
- <EuiText size="m">{calculateTimeDifference(startedAt, completedAt)}</EuiText>
128
- </EuiFlexItem>
129
- <EuiFlexItem grow={0} css={getStepToggleExpandStyle(hasStepContent)}>
130
- {(isExpanded && <WfoChevronUp color={theme.colors.textParagraph} />) || (
131
- <WfoChevronDown color={theme.colors.textParagraph} />
132
- )}
133
- </EuiFlexItem>
134
- </>
135
- )}
125
+ <EuiFlexGroup css={stepRowStyle}>
126
+ {step.completed && (
127
+ <>
128
+ {isExpanded && <WfoCodeViewSelector codeView={codeView} handleCodeViewChange={handle} />}
129
+ <EuiFlexItem grow={0} css={stepHeaderRightStyle}>
130
+ <EuiText css={stepDurationStyle}>{t('duration')}</EuiText>
131
+ <EuiText size="m">{calculateTimeDifference(startedAt, completedAt)}</EuiText>
132
+ </EuiFlexItem>
133
+ <EuiFlexItem grow={0} css={getStepToggleExpandStyle(hasStepContent)}>
134
+ {(isExpanded && <WfoChevronUp color={theme.colors.textParagraph} />) || (
135
+ <WfoChevronDown color={theme.colors.textParagraph} />
136
+ )}
137
+ </EuiFlexItem>
138
+ </>
139
+ )}
140
+ </EuiFlexGroup>
141
+ </EuiFlexItem>
142
+ <EuiFlexItem>
143
+ {overrideStepDetail?.stepHeader && <overrideStepDetail.stepHeader step={step} />}
144
+ </EuiFlexItem>
136
145
  </EuiFlexGroup>
137
146
  </EuiFlexGroup>
138
- {hasStepContent
139
- && !hasHtmlMail
140
- && isExpanded
141
- && (codeView === CodeView.TABLE ? <WfoTableCodeBlock stepState={stepContent} />
142
- : codeView === CodeView.RAW ? <WfoJsonCodeBlock data={stepContent} />
143
- : <WfoMonacoCodeBlock data={stepContent} />)}
144
- {isExpanded && hasHtmlMail && (
145
- <div css={stepEmailContainerStyle}>
146
- {displayMailConfirmation(step.stateDelta.confirmation_mail as EmailState)}
147
- </div>
148
- )}
149
- {step.status === StepStatus.SUSPEND && userInputForm && (
150
- <WfoStepForm
151
- userInputForm={userInputForm}
152
- isTask={isTask}
153
- processId={processId ?? ''}
154
- userPermissions={userPermissions}
155
- />
147
+ {shouldExpand && (
148
+ <EuiFlexGroup direction="column" gutterSize="none">
149
+ <EuiFlexItem grow={1}>
150
+ {overrideStepDetail?.stepBody && <overrideStepDetail.stepBody step={step} />}
151
+ </EuiFlexItem>
152
+ <EuiFlexItem>
153
+ {shouldExpand
154
+ && !hasHtmlMail
155
+ && (codeView === CodeView.TABLE ? <WfoTableCodeBlock stepState={stepContent} />
156
+ : codeView === CodeView.RAW ? <WfoJsonCodeBlock data={stepContent} />
157
+ : <WfoMonacoCodeBlock data={stepContent} />)}
158
+ {isExpanded && hasHtmlMail && (
159
+ <div css={stepEmailContainerStyle}>
160
+ {displayMailConfirmation(step.stateDelta.confirmation_mail as EmailState)}
161
+ </div>
162
+ )}
163
+ {step.status === StepStatus.SUSPEND && userInputForm && (
164
+ <WfoStepForm
165
+ userInputForm={userInputForm}
166
+ isTask={isTask}
167
+ processId={processId ?? ''}
168
+ userPermissions={userPermissions}
169
+ />
170
+ )}
171
+ </EuiFlexItem>
172
+ </EuiFlexGroup>
156
173
  )}
157
174
  </EuiPanel>
158
175
  </div>
@@ -0,0 +1,9 @@
1
+ import { useAppSelector } from '@/rtk/hooks';
2
+
3
+ export const useStepDetailOverride = () => {
4
+ const overrideStepDetail = useAppSelector((state) => state.orchestratorComponentOverride?.stepDetail);
5
+
6
+ return {
7
+ overrideStepDetail,
8
+ };
9
+ };
@@ -69,7 +69,6 @@ export const WfoStepListHeader: FC<WfoStepListHeaderProps> = ({
69
69
  <EuiText css={stepListContentBoldTextStyle}>{t(isTask ? 'taskSteps' : 'workflowSteps')}</EuiText>
70
70
  {!showRaw && <WfoTextAnchor text={allDetailToggleText} onClick={onToggleAllDetailsIsOpen} />}
71
71
  </EuiFlexGroup>
72
-
73
72
  {/* Right side: view options */}
74
73
  <EuiFlexGroup justifyContent="flexEnd" direction="row" css={stepListOptionsContainerStyle} gutterSize="s">
75
74
  {showTracebackButton && (
@@ -35,3 +35,6 @@ export const METADATA_PRODUCT_BLOCK_ENDPOINT = 'product_blocks';
35
35
  export const METADATA_RESOURCE_TYPE_ENDPOINT = 'resource_types';
36
36
  export const METADATA_WORKFLOWS_ENDPOINT = 'workflows';
37
37
  export const METADATA_SCHEDULES_ENDPOINT = 'schedules';
38
+
39
+ //search
40
+ export const SEARCH_QUERY_RESULTS_ENDPOINT = 'search/queries';
@@ -1 +1 @@
1
- export const ORCHESTRATOR_UI_LIBRARY_VERSION = '7.5.1';
1
+ export const ORCHESTRATOR_UI_LIBRARY_VERSION = '7.7.0';
@@ -0,0 +1,188 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import type { AgentSubscriber } from '@ag-ui/client';
4
+ // @ts-expect-error - v2 subpath exists but TypeScript moduleResolution doesn't recognize it
5
+ import { useAgent } from '@copilotkit/react-core/v2';
6
+
7
+ /** AG-UI custom event names emitted by the backend agent. */
8
+ enum AgentEvent {
9
+ PLAN_CREATED = 'PLAN_CREATED',
10
+ STEP_ACTIVE = 'AGENT_STEP_ACTIVE',
11
+ }
12
+
13
+ /** The backend step name used for the planning phase (not a real task). */
14
+ const PLANNER_STEP_NAME = 'Planner';
15
+
16
+ export type ToolCallState = {
17
+ id: string;
18
+ name: string;
19
+ status: 'executing' | 'complete';
20
+ };
21
+
22
+ export type PlanStep = {
23
+ step_name: string;
24
+ reasoning: string | null;
25
+ status: 'pending' | 'active' | 'completed';
26
+ tool_calls: ToolCallState[];
27
+ };
28
+
29
+ export type PlanExecutionState = {
30
+ planning: boolean;
31
+ steps: PlanStep[];
32
+ };
33
+
34
+ const initialState: PlanExecutionState = {
35
+ planning: false,
36
+ steps: [],
37
+ };
38
+
39
+ const updateSteps = (
40
+ steps: PlanStep[],
41
+ predicate: (step: PlanStep) => boolean,
42
+ updater: (step: PlanStep) => PlanStep,
43
+ ): PlanStep[] => steps.map((step) => (predicate(step) ? updater(step) : step));
44
+
45
+ export function useAgentPlanEvents(agentId: string = 'query_agent'): PlanExecutionState {
46
+ const { agent } = useAgent({ agentId });
47
+ const [executionState, setExecutionState] = useState<PlanExecutionState>(initialState);
48
+
49
+ useEffect(() => {
50
+ if (!agent) {
51
+ return;
52
+ }
53
+
54
+ const subscriber: AgentSubscriber = {
55
+ onCustomEvent: (params) => {
56
+ const event = params?.event;
57
+ if (!event) return;
58
+
59
+ if (event.name === AgentEvent.PLAN_CREATED) {
60
+ const tasks = event.value as Array<{
61
+ skillName: string;
62
+ reasoning: string;
63
+ }>;
64
+ if (!Array.isArray(tasks)) return;
65
+
66
+ setExecutionState({
67
+ planning: false,
68
+ steps: tasks.map((task) => ({
69
+ step_name: task.skillName,
70
+ reasoning: task.reasoning,
71
+ status: 'pending' as const,
72
+ tool_calls: [],
73
+ })),
74
+ });
75
+ return;
76
+ }
77
+
78
+ if (event.name === AgentEvent.STEP_ACTIVE) {
79
+ const stepName = event.value?.step;
80
+ if (!stepName) return;
81
+
82
+ if (stepName === PLANNER_STEP_NAME) {
83
+ setExecutionState((prev) => ({
84
+ ...prev,
85
+ planning: true,
86
+ }));
87
+ return;
88
+ }
89
+
90
+ const reasoning = event.value?.reasoning ?? null;
91
+
92
+ setExecutionState((prev) => {
93
+ // Mark previous active step as completed
94
+ const steps = updateSteps(
95
+ prev.steps,
96
+ (step) => step.status === 'active',
97
+ (step) => ({ ...step, status: 'completed' as const }),
98
+ );
99
+
100
+ // If step already exists (from PLAN_CREATED), activate it
101
+ const existingIndex = steps.findIndex((step) => step.step_name === stepName);
102
+ if (existingIndex >= 0) {
103
+ steps[existingIndex] = {
104
+ ...steps[existingIndex],
105
+ status: 'active',
106
+ reasoning: reasoning ?? steps[existingIndex].reasoning,
107
+ };
108
+ } else {
109
+ steps.push({
110
+ step_name: stepName,
111
+ reasoning,
112
+ status: 'active',
113
+ tool_calls: [],
114
+ });
115
+ }
116
+
117
+ return { planning: false, steps };
118
+ });
119
+ }
120
+ },
121
+
122
+ onToolCallStartEvent: ({ event }) => {
123
+ setExecutionState((prev) => {
124
+ const currentStep = prev.steps.find((step) => step.status === 'active');
125
+ if (!currentStep) return prev;
126
+
127
+ return {
128
+ ...prev,
129
+ steps: updateSteps(
130
+ prev.steps,
131
+ (step) => step.step_name === currentStep.step_name,
132
+ (step) => ({
133
+ ...step,
134
+ tool_calls: [
135
+ ...step.tool_calls,
136
+ {
137
+ id: event.toolCallId,
138
+ name: event.toolCallName,
139
+ status: 'executing' as const,
140
+ },
141
+ ],
142
+ }),
143
+ ),
144
+ };
145
+ });
146
+ },
147
+
148
+ onToolCallEndEvent: ({ event }) => {
149
+ setExecutionState((prev) => ({
150
+ ...prev,
151
+ steps: updateSteps(
152
+ prev.steps,
153
+ (step) => step.tool_calls.some((toolCall) => toolCall.id === event.toolCallId),
154
+ (step) => ({
155
+ ...step,
156
+ tool_calls: step.tool_calls.map((toolCall) =>
157
+ toolCall.id === event.toolCallId ? { ...toolCall, status: 'complete' as const } : toolCall,
158
+ ),
159
+ }),
160
+ ),
161
+ }));
162
+ },
163
+
164
+ onRunStartedEvent: () => {
165
+ setExecutionState(initialState);
166
+ },
167
+
168
+ onRunFinishedEvent: () => {
169
+ setExecutionState((prev) => ({
170
+ planning: false,
171
+ steps: updateSteps(
172
+ prev.steps,
173
+ (step) => step.status === 'active',
174
+ (step) => ({ ...step, status: 'completed' as const }),
175
+ ),
176
+ }));
177
+ },
178
+ };
179
+
180
+ const subscription = agent.subscribe(subscriber);
181
+
182
+ return () => {
183
+ subscription.unsubscribe();
184
+ };
185
+ }, [agent]);
186
+
187
+ return executionState;
188
+ }
@@ -390,6 +390,8 @@
390
390
  "subscriptionId": "Subscription ID",
391
391
  "description": "Description",
392
392
  "startDate": "Start date",
393
+ "lastRunValidation": "Most recent validate",
394
+ "noValidateWorkflows": "No validate workflows have been run recently",
393
395
  "insync": "In sync",
394
396
  "customer": "Customer",
395
397
  "customerUuid": "Customer UUID",
@@ -558,6 +560,11 @@
558
560
  "get_valid_operators": "I'm getting valid operators",
559
561
  "set_temporal_grouping": "I'm setting temporal grouping"
560
562
  },
563
+ "planProgress": {
564
+ "planning": "Planning...",
565
+ "completed": "Plan completed",
566
+ "executing": "Executing plan ({completed}/{total})"
567
+ },
561
568
  "visualization": {
562
569
  "noDataAvailable": "No data available for visualization.",
563
570
  "invalidDataStructure": "Invalid data structure."
@@ -389,6 +389,8 @@
389
389
  "subscriptionId": "Subscription ID",
390
390
  "description": "Beschrijving",
391
391
  "startDate": "Startdatum",
392
+ "lastRunValidation": "Meest recente validate",
393
+ "noValidateWorkflows": "Er zijn recentelijk geen validate workflows uitgevoerd",
392
394
  "insync": "In sync",
393
395
  "customer": "Klant",
394
396
  "customerUuid": "Klant UUID",