@orchestrator-ui/orchestrator-ui-components 8.1.0 → 8.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchestrator-ui/orchestrator-ui-components",
3
- "version": "8.1.0",
3
+ "version": "8.2.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Library of UI Components used to display the workflow orchestrator frontend",
6
6
  "author": {
@@ -34,10 +34,10 @@
34
34
  "dev": "tsup --watch"
35
35
  },
36
36
  "dependencies": {
37
- "@ag-ui/client": "^0.0.47",
38
- "@copilotkit/react-core": "1.54.0",
39
- "@copilotkit/react-ui": "1.54.0",
40
- "@copilotkit/runtime": "1.54.0",
37
+ "@ag-ui/client": "^0.0.53",
38
+ "@copilotkit/react-core": "^1.57.0",
39
+ "@copilotkit/react-ui": "^1.57.0",
40
+ "@copilotkit/runtime": "^1.57.0",
41
41
  "@elastic/charts": "^64.1.0",
42
42
  "@emotion/css": "^11.11.2",
43
43
  "@emotion/react": "^11.11.4",
@@ -64,7 +64,7 @@
64
64
  "use-query-params": "^2.2.2"
65
65
  },
66
66
  "devDependencies": {
67
- "@elastic/eui": "^113.1.0",
67
+ "@elastic/eui": "^113.3.0",
68
68
  "@orchestrator-ui/eslint-config-custom": "*",
69
69
  "@orchestrator-ui/jest-config": "*",
70
70
  "@reduxjs/toolkit": "^2.0.1",
@@ -14,6 +14,7 @@ import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@e
14
14
 
15
15
  import { getWfoDiffStyles } from '@/components/WfoDiff/styles';
16
16
  import { useWithOrchestratorTheme } from '@/hooks';
17
+ import { ProcessDetailResultRaw } from '@/types';
17
18
 
18
19
  const EMPTY_HUNKS: never[] = [];
19
20
 
@@ -31,6 +32,20 @@ interface WfoDiffProps {
31
32
  syntax?: 'javascript' | 'python';
32
33
  }
33
34
 
35
+ export const stringifyDiffText = (text?: unknown) => (text ? JSON.stringify(text, null, 2) : '');
36
+
37
+ export const getSubscriptionDiffTexts = (data?: ProcessDetailResultRaw) => {
38
+ const subscriptionId = data?.current_state?.subscription?.subscription_id ?? '';
39
+ const newSubscription = data?.current_state?.subscription ?? null;
40
+ const oldSubscriptions = data?.current_state?.__old_subscriptions__ || {};
41
+ const oldSubscription =
42
+ subscriptionId && subscriptionId in oldSubscriptions ? oldSubscriptions[subscriptionId] : null;
43
+ return {
44
+ oldText: stringifyDiffText(oldSubscription),
45
+ newText: stringifyDiffText(newSubscription),
46
+ };
47
+ };
48
+
34
49
  const WfoDiff: FC<WfoDiffProps> = ({ oldText, newText, syntax }) => {
35
50
  const t = useTranslations('processes.delta');
36
51
  const [showSplit, setShowSplit] = useState(true);
@@ -0,0 +1,71 @@
1
+ import React, { FC, useState } from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+
5
+ import { EuiButtonIcon, EuiLoadingSpinner, EuiPanel, EuiPopover } from '@elastic/eui';
6
+
7
+ import { ProcessListItem, WfoToolTip } from '@/components';
8
+ import WfoDiff, { getSubscriptionDiffTexts } from '@/components/WfoDiff/WfoDiff';
9
+ import { useOrchestratorTheme, useWithOrchestratorTheme } from '@/hooks';
10
+ import { WfoCode } from '@/icons';
11
+ import { useGetRawProcessDetailQuery } from '@/rtk/endpoints/processDetail';
12
+ import { WorkflowTarget } from '@/types';
13
+
14
+ import { getWfoProcessListDeltaPopoverStyles } from './styles';
15
+
16
+ interface WfoProcessListDeltaPopoverProps {
17
+ processListItem: ProcessListItem;
18
+ }
19
+
20
+ export const WfoProcessListDeltaPopover: FC<WfoProcessListDeltaPopoverProps> = ({ processListItem }) => {
21
+ const { theme } = useOrchestratorTheme();
22
+ const { popoverPanelStyle, deltaContentPanelStyle, loadingSpinnerStyle } = useWithOrchestratorTheme(
23
+ getWfoProcessListDeltaPopoverStyles,
24
+ );
25
+ const t = useTranslations('processes.steps');
26
+ const [isPopoverOpen, setPopoverOpen] = useState(false);
27
+ const { processId, workflowTarget } = processListItem;
28
+
29
+ const { data, isFetching } = useGetRawProcessDetailQuery({ processId }, { skip: !isPopoverOpen });
30
+
31
+ if (workflowTarget.toUpperCase() !== WorkflowTarget.MODIFY.toUpperCase()) {
32
+ return null;
33
+ }
34
+
35
+ const { oldText, newText } = getSubscriptionDiffTexts(data);
36
+
37
+ const iconButton = (
38
+ <EuiButtonIcon
39
+ iconType={() => <WfoCode color={theme.colors.primary} />}
40
+ onClick={() => setPopoverOpen(!isPopoverOpen)}
41
+ aria-label={t('showDelta')}
42
+ isLoading={isFetching}
43
+ />
44
+ );
45
+
46
+ const button = isPopoverOpen ? iconButton : <WfoToolTip tooltipContent={t('showDelta')}>{iconButton}</WfoToolTip>;
47
+
48
+ const SubscriptionDeltaContent = () => <WfoDiff oldText={oldText} newText={newText} syntax="javascript" />;
49
+
50
+ return (
51
+ <EuiPopover
52
+ id={`delta-${processId}`}
53
+ button={button}
54
+ isOpen={isPopoverOpen}
55
+ closePopover={() => setPopoverOpen((isPopoverOpen) => !isPopoverOpen)}
56
+ panelPaddingSize="s"
57
+ hasArrow
58
+ ownFocus
59
+ repositionOnScroll
60
+ panelStyle={popoverPanelStyle}
61
+ >
62
+ <EuiPanel color="transparent" paddingSize="s" css={deltaContentPanelStyle}>
63
+ {isFetching ?
64
+ <div css={loadingSpinnerStyle}>
65
+ <EuiLoadingSpinner size="xl" />
66
+ </div>
67
+ : <SubscriptionDeltaContent />}
68
+ </EuiPanel>
69
+ </EuiPopover>
70
+ );
71
+ };
@@ -36,6 +36,7 @@ import { parseDateToLocaleDateTimeString } from '@/utils';
36
36
  import { getQueryVariablesForExport } from '@/utils';
37
37
  import { csvDownloadHandler, getCsvFileNameWithDate } from '@/utils/csvDownload';
38
38
 
39
+ import { WfoProcessListDeltaPopover } from './WfoProcessListDeltaPopover';
39
40
  import {
40
41
  graphQlProcessFilterMapper,
41
42
  graphQlProcessSortMapper,
@@ -96,6 +97,11 @@ export const WfoProcessesList = ({
96
97
  const router = useRouter();
97
98
 
98
99
  const defaultTableColumns: WfoAdvancedTableColumnConfig<ProcessListItem> = {
100
+ delta: {
101
+ columnType: ColumnType.CONTROL,
102
+ width: '50px',
103
+ renderControl: (row) => <WfoProcessListDeltaPopover processListItem={row} />,
104
+ },
99
105
  workflowName: {
100
106
  columnType: ColumnType.DATA,
101
107
  label: t('workflowName'),
@@ -0,0 +1,28 @@
1
+ import { css } from '@emotion/react';
2
+
3
+ import { WfoThemeHelpers } from '@/hooks';
4
+
5
+ export const getWfoProcessListDeltaPopoverStyles = ({ theme }: WfoThemeHelpers) => {
6
+ const popoverPanelStyle = {
7
+ backgroundColor: `${theme.colors.backgroundBasePlain}DD`,
8
+ boxShadow: 'none',
9
+ backdropFilter: 'blur(2px)',
10
+ };
11
+
12
+ const deltaContentPanelStyle = css({
13
+ backgroundColor: 'transparent',
14
+ width: '1300px',
15
+ height: '500px',
16
+ overflow: 'auto',
17
+ });
18
+
19
+ const loadingSpinnerStyle = css({
20
+ padding: theme.size.m,
21
+ });
22
+
23
+ return {
24
+ popoverPanelStyle,
25
+ deltaContentPanelStyle,
26
+ loadingSpinnerStyle,
27
+ };
28
+ };
@@ -4,7 +4,8 @@ import { PydanticFormElementProps } from 'pydantic-forms';
4
4
 
5
5
  import { EuiCallOut } from '@elastic/eui';
6
6
 
7
- const CALLOUT_COLORS = ['primary', 'success', 'warning', 'danger', 'accent'];
7
+ export const CALLOUT_COLORS = ['primary', 'success', 'warning', 'danger', 'accent'] as const;
8
+ export type CalloutColor = (typeof CALLOUT_COLORS)[number];
8
9
 
9
10
  export const WfoCallout = ({ pydanticFormField }: PydanticFormElementProps) => {
10
11
  const { header, message, icon_type, message_type } = pydanticFormField.default;
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+
3
+ import { PydanticFormElementProps } from 'pydantic-forms';
4
+
5
+ import { EuiCallOut, EuiMarkdownFormat } from '@elastic/eui';
6
+
7
+ import { CALLOUT_COLORS, CalloutColor } from './WfoCallout';
8
+
9
+ type MarkdownFieldDefault = string | { content: string; color: CalloutColor };
10
+
11
+ export const WfoMarkdownField = ({ pydanticFormField }: PydanticFormElementProps) => {
12
+ const markdownValue: MarkdownFieldDefault = pydanticFormField.default;
13
+ const content = typeof markdownValue === 'string' ? markdownValue : (markdownValue?.content ?? '');
14
+ const color =
15
+ typeof markdownValue === 'object' && CALLOUT_COLORS.includes(markdownValue?.color) ?
16
+ markdownValue?.color
17
+ : undefined;
18
+
19
+ return (
20
+ <div data-testid={pydanticFormField.id} css={{ marginBottom: '2rem' }}>
21
+ <EuiCallOut color={color}>
22
+ <EuiMarkdownFormat>{content}</EuiMarkdownFormat>
23
+ </EuiCallOut>
24
+ </div>
25
+ );
26
+ };
@@ -13,4 +13,5 @@ export * from './WfoReactSelect';
13
13
  export * from './WfoMultiCheckboxField';
14
14
  export * from './wfoPydanticFormUtils';
15
15
  export * from './WfoCallout';
16
+ export * from './WfoMarkdownField';
16
17
  export * from './WfoTimestampField';
@@ -3,11 +3,11 @@ import React, { FC, useState } from 'react';
3
3
  import { useTranslations } from 'next-intl';
4
4
  import { useRouter } from 'next/router';
5
5
 
6
- import { EuiButton, EuiButtonIcon, EuiTitle } from '@elastic/eui';
6
+ import { EuiButton, EuiButtonIcon, EuiLoadingSpinner, EuiTitle } from '@elastic/eui';
7
7
 
8
- import { WfoPopover } from '@/components';
9
- import { PATH_START_NEW_TASK, PATH_START_NEW_WORKFLOW, WfoInSyncField } from '@/components';
8
+ import { PATH_START_NEW_TASK, PATH_START_NEW_WORKFLOW, WfoInSyncField, WfoPopover } from '@/components';
10
9
  import { WfoSubscriptionActionsMenuItem } from '@/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActionsMenuItem';
10
+ import { useActiveProcess } from '@/components/WfoSubscription/WfoSubscriptionActions/utils';
11
11
  import { PolicyResource } from '@/configuration/policy-resources';
12
12
  import { useOrchestratorTheme, usePolicy } from '@/hooks';
13
13
  import { WfoDotsHorizontal } from '@/icons/WfoDotsHorizontal';
@@ -39,20 +39,24 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
39
39
  const [isPopoverOpen, setPopover] = useState<boolean>(false);
40
40
  const router = useRouter();
41
41
  const disableQuery = isLoading || (!isPopoverOpen && compactMode);
42
+ const { isAllowed } = usePolicy();
42
43
  const { data: subscriptionActions, isLoading: subscriptionActionsIsLoading } = useGetSubscriptionActionsQuery(
43
44
  { subscriptionId },
44
45
  { skip: disableQuery },
45
46
  );
46
47
  const [startProcess] = useStartProcessMutation();
47
48
 
48
- const { data: subscriptionDetail } = useGetSubscriptionDetailQuery(
49
+ const { data: subscriptionDetail, isLoading: subscriptionDetailIsLoading } = useGetSubscriptionDetailQuery(
49
50
  {
50
51
  subscriptionId,
51
52
  },
52
- { skip: !isPopoverOpen && compactMode },
53
+ { skip: !isPopoverOpen && compactMode, refetchOnMountOrArgChange: true },
53
54
  );
54
55
 
55
- const { isAllowed } = usePolicy();
56
+ const processes = subscriptionDetail?.subscription?.processes?.page;
57
+ const { hasActiveProcess, isCompleted, setProcessId } = useActiveProcess(processes);
58
+
59
+ const buttonIsLoading = isCompleted ? !isCompleted : hasActiveProcess;
56
60
 
57
61
  const onButtonClick = () => setPopover(!isPopoverOpen);
58
62
  const closePopover = () => setPopover(false);
@@ -60,7 +64,9 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
60
64
  const button =
61
65
  compactMode ?
62
66
  <EuiButtonIcon
63
- iconType={() => <WfoDotsHorizontal color={theme.colors.textDisabled} />}
67
+ iconType={() =>
68
+ buttonIsLoading ? <EuiLoadingSpinner /> : <WfoDotsHorizontal color={theme.colors.textDisabled} />
69
+ }
64
70
  onClick={onButtonClick}
65
71
  aria-label="Row context menu"
66
72
  isLoading={isLoading}
@@ -94,6 +100,11 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
94
100
  ],
95
101
  })
96
102
  .unwrap()
103
+ .then((response) => {
104
+ if (response?.id) {
105
+ setProcessId(response.id);
106
+ }
107
+ })
97
108
  .catch((error) => {
98
109
  console.error(`Failed to start action:`, error);
99
110
  })
@@ -122,6 +133,7 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
122
133
  target={WorkflowTarget.VALIDATE}
123
134
  setPopover={setPopover}
124
135
  onClick={() => handleActionClick(subscriptionAction.name, compactMode, true)}
136
+ isLoading={buttonIsLoading}
125
137
  />
126
138
  ))}
127
139
  </>
@@ -133,10 +145,15 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
133
145
  {subscriptionActions?.reconcile.map((subscriptionAction, index) => (
134
146
  <WfoSubscriptionActionsMenuItem
135
147
  key={`r_${index}`}
136
- subscriptionAction={subscriptionAction}
148
+ subscriptionAction={
149
+ buttonIsLoading && !subscriptionAction.reason ?
150
+ { ...subscriptionAction, reason: 'subscription.running_process' }
151
+ : subscriptionAction
152
+ }
137
153
  target={WorkflowTarget.RECONCILE}
138
154
  setPopover={setPopover}
139
155
  onClick={() => handleActionClick(subscriptionAction.name, compactMode, false)}
156
+ isLoading={buttonIsLoading}
140
157
  />
141
158
  ))}
142
159
  </>
@@ -197,7 +214,7 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
197
214
  return (
198
215
  <WfoPopover
199
216
  id={'subscriptionActionPopover'}
200
- isLoading={subscriptionActionsIsLoading || isLoading || false}
217
+ isLoading={subscriptionActionsIsLoading || (compactMode && subscriptionDetailIsLoading) || isLoading || false}
201
218
  button={button}
202
219
  PopoverContent={MenuItemsList}
203
220
  isPopoverOpen={isPopoverOpen}
@@ -2,7 +2,7 @@ import React, { FC } from 'react';
2
2
 
3
3
  import { useTranslations } from 'next-intl';
4
4
 
5
- import { EuiContextMenuItem, EuiToolTip } from '@elastic/eui';
5
+ import { EuiContextMenuItem, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui';
6
6
 
7
7
  import { flattenArrayProps } from '@/components';
8
8
  import { WfoSubscriptionActionExpandableMenuItem } from '@/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActionExpandableMenuItem';
@@ -18,6 +18,7 @@ interface MenuItemProps {
18
18
  target: WorkflowTarget;
19
19
  setPopover: (isOpen: boolean) => void;
20
20
  onClick: () => void;
21
+ isLoading?: boolean;
21
22
  }
22
23
 
23
24
  export const WfoSubscriptionActionsMenuItem: FC<MenuItemProps> = ({
@@ -25,6 +26,7 @@ export const WfoSubscriptionActionsMenuItem: FC<MenuItemProps> = ({
25
26
  onClick,
26
27
  target,
27
28
  setPopover,
29
+ isLoading = false,
28
30
  }) => {
29
31
  const { linkMenuItemStyle, tooltipMenuItemStyle, disabledIconStyle, iconStyle, secondaryIconStyle } =
30
32
  useWithOrchestratorTheme(getSubscriptionActionStyles);
@@ -32,6 +34,10 @@ export const WfoSubscriptionActionsMenuItem: FC<MenuItemProps> = ({
32
34
  const { isEngineRunningNow } = useCheckEngineStatus();
33
35
  const t = useTranslations('subscriptions.detail.actions');
34
36
  const { theme } = useOrchestratorTheme();
37
+ const subscriptionActionReason =
38
+ subscriptionAction.reason ? subscriptionAction.reason
39
+ : isLoading ? 'subscription.running_process'
40
+ : undefined;
35
41
 
36
42
  const linkIt = (actionItem: React.ReactNode) => {
37
43
  const handleLinkClick = async (e: React.MouseEvent) => {
@@ -51,8 +57,8 @@ export const WfoSubscriptionActionsMenuItem: FC<MenuItemProps> = ({
51
57
  };
52
58
 
53
59
  const tooltipIt = (actionItem: React.ReactNode) => {
54
- if (!subscriptionAction.reason) return actionItem;
55
- const tooltipContent = t(subscriptionAction.reason, flattenArrayProps(subscriptionAction));
60
+ if (!subscriptionActionReason) return actionItem;
61
+ const tooltipContent = t(subscriptionActionReason, flattenArrayProps(subscriptionAction));
56
62
 
57
63
  return (
58
64
  <div css={tooltipMenuItemStyle}>
@@ -68,22 +74,24 @@ export const WfoSubscriptionActionsMenuItem: FC<MenuItemProps> = ({
68
74
  );
69
75
  };
70
76
 
71
- const getIcon = () =>
72
- subscriptionAction.reason ?
73
- <div css={disabledIconStyle}>
74
- <WfoTargetTypeIcon target={target} disabled />
75
- <div css={secondaryIconStyle}>
76
- <WfoXCircleFill width={20} height={20} color={theme.colors.danger} />
77
+ const getIcon = () => {
78
+ if (isLoading) return <EuiLoadingSpinner size="m" />;
79
+ return subscriptionActionReason ?
80
+ <div css={disabledIconStyle}>
81
+ <WfoTargetTypeIcon target={target} disabled />
82
+ <div css={secondaryIconStyle}>
83
+ <WfoXCircleFill width={20} height={20} color={theme.colors.danger} />
84
+ </div>
77
85
  </div>
78
- </div>
79
- : <div css={iconStyle}>
80
- <WfoTargetTypeIcon target={target} />
81
- </div>;
86
+ : <div css={iconStyle}>
87
+ <WfoTargetTypeIcon target={target} />
88
+ </div>;
89
+ };
82
90
 
83
91
  const ActionItem = () => (
84
92
  <EuiContextMenuItem
85
93
  icon={getIcon()}
86
- disabled={!!subscriptionAction.reason}
94
+ disabled={!!subscriptionActionReason}
87
95
  css={{
88
96
  whiteSpace: 'nowrap',
89
97
  }}
@@ -92,5 +100,5 @@ export const WfoSubscriptionActionsMenuItem: FC<MenuItemProps> = ({
92
100
  </EuiContextMenuItem>
93
101
  );
94
102
 
95
- return subscriptionAction?.reason ? tooltipIt(<ActionItem />) : linkIt(<ActionItem />);
103
+ return subscriptionActionReason ? tooltipIt(<ActionItem />) : linkIt(<ActionItem />);
96
104
  };
@@ -0,0 +1,27 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import { useGetProcessDetailQuery } from '@/rtk';
4
+ import { ProcessStatus, StepStatus, SubscriptionDetailProcess } from '@/types';
5
+
6
+ export const useActiveProcess = (processes: SubscriptionDetailProcess[] | undefined) => {
7
+ const lastProcess = processes?.[processes.length - 1];
8
+ const isLastProcessRunning = lastProcess?.lastStatus.toLowerCase() === ProcessStatus.RUNNING;
9
+
10
+ const [processId, setProcessId] = useState<string | null>(null);
11
+
12
+ useEffect(() => {
13
+ if (isLastProcessRunning) {
14
+ setProcessId(lastProcess.processId);
15
+ }
16
+ }, [lastProcess, isLastProcessRunning]);
17
+
18
+ const { data: activeProcessData } = useGetProcessDetailQuery({ processId: processId ?? '' }, { skip: !processId });
19
+
20
+ const processDetail = activeProcessData?.processes[0];
21
+ const lastStatus = processDetail?.lastStatus.toLowerCase() as ProcessStatus;
22
+ const hasActiveProcess = lastStatus === ProcessStatus.RUNNING || isLastProcessRunning;
23
+ const lastState = processDetail?.steps?.[processDetail.steps.length - 1]?.status;
24
+ const isCompleted = lastState !== undefined && [StepStatus.COMPLETE, StepStatus.SUCCESS].includes(lastState);
25
+
26
+ return { hasActiveProcess, isCompleted, setProcessId };
27
+ };
@@ -3,7 +3,7 @@ import React, { Ref, useEffect, useState } from 'react';
3
3
  import { useTranslations } from 'next-intl';
4
4
 
5
5
  import { WfoJsonCodeBlock, WfoLoading, WfoStepList, WfoStepListHeader, WfoStepListRef } from '@/components';
6
- import WfoDiff from '@/components/WfoDiff/WfoDiff';
6
+ import WfoDiff, { getSubscriptionDiffTexts } from '@/components/WfoDiff/WfoDiff';
7
7
  import { WfoTraceback } from '@/components/WfoWorkflowSteps/WfoTraceback/WfoTraceback';
8
8
  import { useGetRawProcessDetailQuery } from '@/rtk/endpoints/processDetail';
9
9
  import { ProcessStatus, Step, StepStatus } from '@/types';
@@ -33,20 +33,9 @@ export const WfoProcessRawData = ({ processId }: { processId: string }) => {
33
33
 
34
34
  export const WfoProcessSubscriptionDelta = ({ processId }: { processId: string }) => {
35
35
  const { data, isFetching } = useGetRawProcessDetailQuery({ processId });
36
+ const { oldText, newText } = getSubscriptionDiffTexts(data);
36
37
 
37
- const subscriptionId = data?.current_state?.subscription?.subscription_id ?? '';
38
- const newText = data?.current_state?.subscription ?? null;
39
- const oldSubscriptions = data?.current_state?.__old_subscriptions__ || {};
40
- const oldSubscription = subscriptionId in oldSubscriptions ? oldSubscriptions[subscriptionId] : null;
41
- const oldText = oldSubscription || null;
42
-
43
- return isFetching ?
44
- <WfoLoading />
45
- : <WfoDiff
46
- oldText={oldText ? JSON.stringify(oldText, null, 2) : ''}
47
- newText={newText ? JSON.stringify(newText, null, 2) : ''}
48
- syntax="javascript"
49
- />;
38
+ return isFetching ? <WfoLoading /> : <WfoDiff oldText={oldText} newText={newText} syntax="javascript" />;
50
39
  };
51
40
 
52
41
  export const WfoWorkflowStepList = React.forwardRef(
@@ -1 +1 @@
1
- export const ORCHESTRATOR_UI_LIBRARY_VERSION = '8.1.0';
1
+ export const ORCHESTRATOR_UI_LIBRARY_VERSION = '8.2.0';
@@ -1,7 +1,6 @@
1
1
  import { useEffect, useState } from 'react';
2
2
 
3
3
  import type { AgentSubscriber } from '@ag-ui/client';
4
- // @ts-expect-error - v2 subpath exists but TypeScript moduleResolution doesn't recognize it
5
4
  import { useAgent } from '@copilotkit/react-core/v2';
6
5
 
7
6
  /** AG-UI custom event names emitted by the backend agent. */
@@ -21,11 +21,13 @@ import { Header } from '@/components/WfoPydanticForm/Header';
21
21
  import { Row } from '@/components/WfoPydanticForm/Row';
22
22
  import {
23
23
  WfoArrayField,
24
+ WfoCallout,
24
25
  WfoCheckbox,
25
26
  WfoDivider,
26
27
  WfoDropdown,
27
28
  WfoInteger,
28
29
  WfoLabel,
30
+ WfoMarkdownField,
29
31
  WfoMultiCheckboxField,
30
32
  WfoObjectField,
31
33
  WfoRadio,
@@ -34,7 +36,6 @@ import {
34
36
  WfoTextArea,
35
37
  WfoTimestampField,
36
38
  } from '@/components/WfoPydanticForm/fields';
37
- import { WfoCallout } from '@/components/WfoPydanticForm/fields';
38
39
  import { useAppSelector } from '@/rtk/hooks';
39
40
 
40
41
  const useGetComponentMatcherExtender = (): ComponentMatcherExtender => {
@@ -180,6 +181,16 @@ const useGetComponentMatcherExtender = (): ComponentMatcherExtender => {
180
181
  return type === PydanticFormFieldType.STRING && format === ('callout' as PydanticFormFieldFormat);
181
182
  },
182
183
  },
184
+ {
185
+ id: 'markdownField',
186
+ ElementMatch: {
187
+ isControlledElement: false,
188
+ Element: WfoMarkdownField,
189
+ },
190
+ matcher: ({ format }) => {
191
+ return format === PydanticFormFieldFormat.MARKDOWN;
192
+ },
193
+ },
183
194
  ...currentMatchers
184
195
  .filter((matcher) => matcher.id !== 'text')
185
196
  .filter((matcher) => matcher.id !== 'array')
@@ -365,7 +365,8 @@
365
365
  "not_in_sync": "This subscription can not be modified because it is not in-sync. This means there is some error in the registration of the subscription or that it is being modified by another workflow.",
366
366
  "relations_not_in_sync": "This subscription can not be modified because some related subscriptions are not in-sync. Locked subscriptions: {locked_relations}",
367
367
  "no_modify_subscription_in_use_by_others": "This subscription can not be modified because it is in use by one or more other subscriptions: {unterminated_parents}",
368
- "insufficient_workflow_permissions": "Insufficient user permissions to run this workflow"
368
+ "insufficient_workflow_permissions": "Insufficient user permissions to run this workflow",
369
+ "running_process": "This action cannot be started because this subscription already has a running process or task."
369
370
  }
370
371
  },
371
372
  "subscriptionInstanceId": "Instance ID",
@@ -364,7 +364,8 @@
364
364
  "not_in_sync": "Deze subscription kan niet worden gewijzigd omdat het niet in-sync is. Dit betekent dat er een fout zit in de registratie van de subscription of dat het wordt gewijzigd door een andere workflow.",
365
365
  "relations_not_in_sync": "Deze subscription kan niet worden gewijzigd omdat sommige gerelateerde subscriptions niet in-sync zijn. Geblokkeerde subscriptions: {locked_relations}",
366
366
  "no_modify_subscription_in_use_by_others": "Deze subscription kan niet worden gewijzigd omdat het in gebruik is door een of meer andere subscriptions: {unterminated_parents}",
367
- "insufficient_workflow_permissions": "Onvoldoende rechten om deze actie uit te kunnen voeren"
367
+ "insufficient_workflow_permissions": "Onvoldoende rechten om deze actie uit te kunnen voeren",
368
+ "running_process": "Deze actie kan niet worden gestart omdat deze subscription al een lopend proces of taak heeft."
368
369
  }
369
370
  },
370
371
  "subscriptionInstanceId": "Instance ID",
@@ -105,4 +105,4 @@ const subscriptionDetailApi = orchestratorApi.injectEndpoints({
105
105
  }),
106
106
  });
107
107
 
108
- export const { useGetSubscriptionDetailQuery } = subscriptionDetailApi;
108
+ export const { useGetSubscriptionDetailQuery, useLazyGetSubscriptionDetailQuery } = subscriptionDetailApi;