@orchestrator-ui/orchestrator-ui-components 4.1.1 → 5.0.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 (32) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +6 -6
  4. package/CHANGELOG.md +21 -0
  5. package/dist/index.d.ts +59 -52
  6. package/dist/index.js +1383 -1186
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/components/WfoBadges/WfoSubscriptionStatusBadge/WfoSubscriptionStatusBadge.tsx +6 -5
  10. package/src/components/WfoBadges/WfoWorkflowTargetBadge/WfoWorkflowTargetBadge.tsx +1 -0
  11. package/src/components/WfoButtonComboBox/WfoButtonComboBox.tsx +79 -0
  12. package/src/components/WfoButtonComboBox/index.ts +1 -0
  13. package/src/components/WfoButtonComboBox/styles.ts +28 -0
  14. package/src/components/WfoForms/formFields/SubscriptionSummaryField.tsx +2 -5
  15. package/src/components/WfoSubscription/WfoInSyncField.tsx +17 -15
  16. package/src/components/WfoSubscription/WfoSubscription.tsx +6 -7
  17. package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActions.tsx +3 -3
  18. package/src/components/WfoSubscription/WfoSubscriptionDetailTree.tsx +64 -14
  19. package/src/components/WfoSubscription/WfoSubscriptionGeneral.tsx +0 -3
  20. package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/WfoSubscriptionDetailSection.tsx +1 -8
  21. package/src/components/WfoSubscription/utils/utils.spec.ts +121 -0
  22. package/src/components/WfoSubscription/utils/utils.ts +42 -6
  23. package/src/components/WfoTable/WfoTable/WfoTableHeaderCell/WfoPopoverContent.tsx +53 -0
  24. package/src/components/WfoTable/WfoTable/WfoTableHeaderCell/WfoTableHeaderCell.tsx +7 -23
  25. package/src/configuration/version.ts +1 -1
  26. package/src/contexts/TreeContext.tsx +16 -0
  27. package/src/messages/en-GB.json +2 -0
  28. package/src/messages/nl-NL.json +2 -0
  29. package/src/rtk/endpoints/metadata/tasks.ts +9 -4
  30. package/src/rtk/endpoints/metadata/workflows.ts +10 -5
  31. package/src/rtk/endpoints/startOptions.ts +9 -3
  32. package/src/types/types.ts +5 -1
@@ -1,16 +1,20 @@
1
1
  import { TranslationValues } from 'next-intl';
2
2
 
3
- import { EuiThemeComputed } from '@elastic/eui';
3
+ import { EuiSelectableOption, EuiThemeComputed } from '@elastic/eui';
4
4
 
5
5
  import {
6
6
  FieldValue,
7
7
  ProcessStatus,
8
+ ProductBlockDefinition,
9
+ ProductBlockInstance,
8
10
  SortOrder,
9
11
  SubscriptionAction,
10
12
  SubscriptionDetailProcess,
11
13
  WorkflowTarget,
12
14
  } from '@/types';
13
15
 
16
+ const PRODUCT_BLOCK_NAME_FIELD: keyof ProductBlockDefinition = 'name';
17
+
14
18
  export enum SubscriptionDetailTab {
15
19
  GENERAL_TAB = 'general',
16
20
  SERVICE_CONFIGURATION_TAB = 'service-configuration',
@@ -68,6 +72,7 @@ export const getWorkflowTargetColor = (
68
72
  case WorkflowTarget.MODIFY:
69
73
  return theme.colors.primaryText;
70
74
  case WorkflowTarget.SYSTEM:
75
+ case WorkflowTarget.VALIDATE:
71
76
  return theme.colors.warning;
72
77
  case WorkflowTarget.TERMINATE:
73
78
  return theme.colors.danger;
@@ -84,6 +89,7 @@ export const getWorkflowTargetIconContent = (
84
89
  case WorkflowTarget.CREATE:
85
90
  return 'C';
86
91
  case WorkflowTarget.SYSTEM:
92
+ case WorkflowTarget.VALIDATE:
87
93
  return 'T';
88
94
  case WorkflowTarget.TERMINATE:
89
95
  return 'X';
@@ -93,9 +99,9 @@ export const getWorkflowTargetIconContent = (
93
99
  };
94
100
 
95
101
  export const getLastUncompletedProcess = (
96
- processes: SubscriptionDetailProcess[],
102
+ processes?: SubscriptionDetailProcess[],
97
103
  ): SubscriptionDetailProcess | undefined => {
98
- if (processes.length === 0) {
104
+ if (!processes || processes.length === 0) {
99
105
  return;
100
106
  }
101
107
 
@@ -107,13 +113,13 @@ export const getLastUncompletedProcess = (
107
113
  return dateB.getTime() - dateA.getTime();
108
114
  });
109
115
 
110
- return uncompletedProcesses.length > 0
116
+ return (uncompletedProcesses && uncompletedProcesses.length) > 0
111
117
  ? uncompletedProcesses[0]
112
118
  : undefined;
113
119
  };
114
120
 
115
- export const getLatestTaskDate = (processes: SubscriptionDetailProcess[]) => {
116
- if (processes.length === 0) {
121
+ export const getLatestTaskDate = (processes?: SubscriptionDetailProcess[]) => {
122
+ if (!processes || processes.length === 0) {
117
123
  return '';
118
124
  }
119
125
 
@@ -143,3 +149,33 @@ export const sortProcessesByDate = (
143
149
  }
144
150
  });
145
151
  };
152
+
153
+ export const mapProductBlockInstancesToEuiSelectableOptions = (
154
+ productBlockInstances: ProductBlockInstance[],
155
+ ): EuiSelectableOption[] => {
156
+ const items2Map = productBlockInstances.reduce((acc, curr) => {
157
+ const name = getFieldFromProductBlockInstanceValues(
158
+ curr.productBlockInstanceValues,
159
+ PRODUCT_BLOCK_NAME_FIELD,
160
+ ).toString();
161
+
162
+ if (!name) {
163
+ console.error('Name field is missing', curr);
164
+ }
165
+
166
+ if (acc.has(name)) {
167
+ acc.get(name)?.push(curr.id);
168
+ } else {
169
+ acc.set(name, [curr.id]);
170
+ }
171
+
172
+ return acc;
173
+ }, new Map<string, number[]>());
174
+
175
+ return Array.from(items2Map).map(([label, ids]) => ({
176
+ label,
177
+ data: {
178
+ ids,
179
+ },
180
+ }));
181
+ };
@@ -0,0 +1,53 @@
1
+ import React, { FC, useRef } from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+
5
+ import { EuiFieldSearch, EuiForm, EuiFormRow } from '@elastic/eui';
6
+
7
+ import { getWfoBasicTableStyles } from '@/components/WfoTable/WfoTable/WfoTableHeaderCell/styles';
8
+ import { useWithOrchestratorTheme } from '@/hooks';
9
+
10
+ interface WfoPopoverContentProps {
11
+ onSearch?: (searchText: string) => void;
12
+ closePopover: () => void;
13
+ fieldName: string;
14
+ }
15
+
16
+ export const WfoPopoverContent: FC<WfoPopoverContentProps> = ({
17
+ onSearch,
18
+ closePopover,
19
+ fieldName,
20
+ }) => {
21
+ const { headerCellPopoverContentStyle } = useWithOrchestratorTheme(
22
+ getWfoBasicTableStyles,
23
+ );
24
+ const t = useTranslations('common');
25
+
26
+ const inputRef = useRef<HTMLInputElement | null>(null);
27
+
28
+ const handleSubmit = (e: React.FormEvent) => {
29
+ e.preventDefault();
30
+ const newValue = inputRef.current?.value || '';
31
+ onSearch?.(newValue);
32
+ if (inputRef.current) inputRef.current.value = '';
33
+ closePopover();
34
+ };
35
+
36
+ return (
37
+ <div css={headerCellPopoverContentStyle}>
38
+ <EuiForm component="form" onSubmit={handleSubmit}>
39
+ <EuiFormRow>
40
+ <EuiFieldSearch
41
+ className={fieldName}
42
+ placeholder={t('search')}
43
+ inputRef={(input) => {
44
+ inputRef.current = input;
45
+ }}
46
+ isClearable={false}
47
+ name={`search-${fieldName}`}
48
+ />
49
+ </EuiFormRow>
50
+ </EuiForm>
51
+ </div>
52
+ );
53
+ };
@@ -1,20 +1,18 @@
1
1
  import React, { FC, useState } from 'react';
2
2
 
3
- import { useTranslations } from 'next-intl';
4
-
5
3
  import {
6
- EuiFieldSearch,
7
4
  EuiHorizontalRule,
8
5
  EuiPopover,
9
6
  EuiText,
10
7
  useGeneratedHtmlId,
11
8
  } from '@elastic/eui';
12
9
 
10
+ import { WfoSortDirectionIcon } from '@/components';
11
+ import { WfoPopoverContent } from '@/components/WfoTable/WfoTable/WfoTableHeaderCell/WfoPopoverContent';
13
12
  import { getUpdatedSortOrder } from '@/components/WfoTable/WfoTable/utils';
14
13
  import { useWithOrchestratorTheme } from '@/hooks';
15
14
  import { SortOrder } from '@/types';
16
15
 
17
- import { WfoSortDirectionIcon } from './WfoSortDirectionIcon';
18
16
  import {
19
17
  HEADER_CELL_SORT_BUTTON_CLASS,
20
18
  HEADER_CELL_TITLE_BUTTON_CLASS,
@@ -41,11 +39,9 @@ export const WfoTableHeaderCell: FC<WfoTableHeaderCellProps> = ({
41
39
  getHeaderCellContentStyle,
42
40
  headerCellPopoverHeaderStyle,
43
41
  headerCellPopoverHeaderTitleStyle,
44
- headerCellPopoverContentStyle,
45
42
  getTitleButtonStyle,
46
43
  sortButtonStyle,
47
44
  } = useWithOrchestratorTheme(getWfoBasicTableStyles);
48
- const t = useTranslations('common');
49
45
 
50
46
  const isSortable = !!onSetSortOrder;
51
47
  const isFilterable = !!onSearch;
@@ -58,11 +54,6 @@ export const WfoTableHeaderCell: FC<WfoTableHeaderCellProps> = ({
58
54
  const handleButtonClick = () => setPopover(!isPopoverOpen);
59
55
  const closePopover = () => setPopover(false);
60
56
 
61
- const handleSearch = (searchText: string) => {
62
- onSearch?.(searchText);
63
- closePopover();
64
- };
65
-
66
57
  const WfoHeaderCellContentButton = () => (
67
58
  <button onClick={handleButtonClick} disabled={!isFilterable}>
68
59
  <div css={getHeaderCellContentStyle(isFilterable)}>{children}</div>
@@ -77,17 +68,6 @@ export const WfoTableHeaderCell: FC<WfoTableHeaderCellProps> = ({
77
68
  </div>
78
69
  );
79
70
 
80
- const WfoPopoverContent = () => (
81
- <div css={headerCellPopoverContentStyle}>
82
- <EuiFieldSearch
83
- className={fieldName}
84
- placeholder={t('search')}
85
- onSearch={handleSearch}
86
- isClearable={false}
87
- />
88
- </div>
89
- );
90
-
91
71
  return (
92
72
  <div css={headerCellStyle}>
93
73
  <EuiPopover
@@ -103,7 +83,11 @@ export const WfoTableHeaderCell: FC<WfoTableHeaderCellProps> = ({
103
83
  >
104
84
  <WfoPopoverHeader />
105
85
  <EuiHorizontalRule margin="none" />
106
- <WfoPopoverContent />
86
+ <WfoPopoverContent
87
+ fieldName={fieldName}
88
+ onSearch={onSearch}
89
+ closePopover={closePopover}
90
+ />
107
91
  </EuiPopover>
108
92
 
109
93
  {isSortable && (
@@ -1 +1 @@
1
- export const ORCHESTRATOR_UI_LIBRARY_VERSION = '4.1.1';
1
+ export const ORCHESTRATOR_UI_LIBRARY_VERSION = '5.0.0';
@@ -12,6 +12,8 @@ export type TreeContextType = {
12
12
  collapseAll: () => void;
13
13
  resetSelection: () => void;
14
14
  selectAll: () => void;
15
+ selectIds: (ids: number[]) => void;
16
+ deselectIds: (ids: number[]) => void;
15
17
  };
16
18
 
17
19
  export const TreeContext = React.createContext<TreeContextType | null>(null);
@@ -39,6 +41,18 @@ export const TreeProvider: React.FC<TreeProviderProps> = ({ children }) => {
39
41
  setSelectedIds(Array.from(Array(depths.length).keys()));
40
42
  };
41
43
 
44
+ const selectIds = (ids: number[]) => {
45
+ setSelectedIds((prevSelectedIds) =>
46
+ Array.from(new Set([...prevSelectedIds, ...ids])),
47
+ );
48
+ };
49
+
50
+ const deselectIds = (ids: number[]) => {
51
+ setSelectedIds((prevSelectedIds) =>
52
+ prevSelectedIds.filter((id) => !ids.includes(id)),
53
+ );
54
+ };
55
+
42
56
  const expandAll = () => {
43
57
  setExpandedIds(Array.from(Array(depths.length).keys()));
44
58
  };
@@ -96,6 +110,8 @@ export const TreeProvider: React.FC<TreeProviderProps> = ({ children }) => {
96
110
  collapseAll,
97
111
  resetSelection,
98
112
  selectAll,
113
+ selectIds,
114
+ deselectIds,
99
115
  }}
100
116
  >
101
117
  {children}
@@ -363,6 +363,8 @@
363
363
  },
364
364
  "showAll": "Show all",
365
365
  "hideAll": "Hide all",
366
+ "selectByNameTitle": "Select product blocks by name",
367
+ "selectByNameButtonText": "Select by name",
366
368
  "see": "See",
367
369
  "setInSync": "Set in Sync",
368
370
  "setInSyncQuestion": "Are you sure you want to do this? You're about to force a subscription in sync. When it's clear why the subscription is out of sync this could enable you to start or finish a change on this subscription. When you're in doubt, please consult the network automators first as running workflows on subscriptions that are not in sync can potentially do great harm to the network.",
@@ -360,6 +360,8 @@
360
360
  },
361
361
  "showAll": "Toon alles",
362
362
  "hideAll": "Verberg alles",
363
+ "selectByNameTitle": "Selecteer product blocks op naam",
364
+ "selectByNameButtonText": "Selecteer op naam",
363
365
  "see": "Bekijk",
364
366
  "setInSync": "Set in Sync",
365
367
  "setInSyncQuestion": "Weet je zeker dat je de subscription in-sync wilt zetten? Je gaat een subscription geforceerd in-sync zetten. Alleen als je zeker weet wat de reden is van out-of-sync kun je de actie uitvoeren, zodat je daarna wijzigingen kunt doorvoeren op deze subscription. Bij twijfel - check eerst bij de network automators of het in sync zetten van de subscription mogelijk schadelijke gevolgen kan hebben op het netwerk.",
@@ -18,13 +18,14 @@ query MetadataWorkflows(
18
18
  after: $after
19
19
  sortBy: $sortBy
20
20
  query: $query
21
- filterBy: { field: "target", value: "SYSTEM" }
21
+ filterBy: { field: "isTask", value: "true" }
22
22
  ) {
23
23
  page {
24
24
  workflowId
25
25
  name
26
26
  description
27
27
  target
28
+ isTask
28
29
  products {
29
30
  tag
30
31
  }
@@ -50,7 +51,7 @@ export type TasksResponse = {
50
51
  const tasksApi = orchestratorApi.injectEndpoints({
51
52
  endpoints: (builder) => ({
52
53
  getTasks: builder.query<
53
- TasksResponse,
54
+ TasksResponse | undefined,
54
55
  GraphqlQueryVariables<TaskDefinition>
55
56
  >({
56
57
  query: (variables) => ({
@@ -58,8 +59,12 @@ const tasksApi = orchestratorApi.injectEndpoints({
58
59
  variables,
59
60
  }),
60
61
  transformResponse: (
61
- response: TaskDefinitionsResult,
62
- ): TasksResponse => {
62
+ response: TaskDefinitionsResult | undefined,
63
+ ): TasksResponse | undefined => {
64
+ if (!response) {
65
+ return undefined;
66
+ }
67
+
63
68
  const tasks = response.workflows.page || [];
64
69
  const pageInfo = response.workflows.pageInfo || {};
65
70
 
@@ -42,6 +42,7 @@ query MetadataWorkflows(
42
42
  name
43
43
  description
44
44
  target
45
+ isTask
45
46
  products {
46
47
  tag
47
48
  }
@@ -76,7 +77,7 @@ export type WorkflowsResponse = {
76
77
  const workflowsApi = orchestratorApi.injectEndpoints({
77
78
  endpoints: (builder) => ({
78
79
  getWorkflows: builder.query<
79
- WorkflowsResponse,
80
+ WorkflowsResponse | undefined,
80
81
  GraphqlQueryVariables<WorkflowDefinition>
81
82
  >({
82
83
  query: (variables) => ({
@@ -84,10 +85,14 @@ const workflowsApi = orchestratorApi.injectEndpoints({
84
85
  variables,
85
86
  }),
86
87
  transformResponse: (
87
- response: WorkflowDefinitionsResult,
88
- ): WorkflowsResponse => {
89
- const workflows = response.workflows.page || [];
90
- const pageInfo = response.workflows.pageInfo || {};
88
+ response: WorkflowDefinitionsResult | undefined,
89
+ ): WorkflowsResponse | undefined => {
90
+ if (!response) {
91
+ return undefined;
92
+ }
93
+
94
+ const workflows = response?.workflows.page || [];
95
+ const pageInfo = response?.workflows.pageInfo || {};
91
96
 
92
97
  return {
93
98
  workflows,
@@ -16,6 +16,7 @@ const workflowOptionsQuery = `
16
16
  productType
17
17
  productId
18
18
  name
19
+ tag
19
20
  }
20
21
  }
21
22
  }
@@ -24,7 +25,7 @@ const workflowOptionsQuery = `
24
25
 
25
26
  const taskOptionsQuery = `
26
27
  query StartOptions {
27
- workflows(first: 1000000, after: 0, filterBy: [{ field: "target", value: "${WorkflowTarget.SYSTEM}"}]) {
28
+ workflows(first: 1000000, after: 0, filterBy: [{ field: "isTask", value: "true"}]) {
28
29
  page {
29
30
  name
30
31
  description
@@ -38,6 +39,7 @@ type WorkflowOption = {
38
39
  productName: ProductDefinition['name'];
39
40
  productId: ProductDefinition['productId'];
40
41
  productType: ProductDefinition['productType'];
42
+ productTag: ProductDefinition['tag'];
41
43
  };
42
44
 
43
45
  type WorkflowOptionsResult = StartOptionsResult<{
@@ -46,6 +48,7 @@ type WorkflowOptionsResult = StartOptionsResult<{
46
48
  name: ProductDefinition['name'];
47
49
  productId: ProductDefinition['productId'];
48
50
  productType: ProductDefinition['productType'];
51
+ tag: ProductDefinition['tag'];
49
52
  }[];
50
53
  }>;
51
54
 
@@ -65,9 +68,11 @@ const startButtonOptionsApi = orchestratorApi.injectEndpoints({
65
68
  query: () => ({
66
69
  document: workflowOptionsQuery,
67
70
  }),
68
- transformResponse: (response: WorkflowOptionsResult) => {
71
+ transformResponse: (
72
+ response: WorkflowOptionsResult | undefined,
73
+ ) => {
69
74
  const startOptions: WorkflowOption[] = [];
70
- const workflows = response.workflows?.page || [];
75
+ const workflows = response?.workflows?.page || [];
71
76
  workflows.forEach((workflow) => {
72
77
  const workflowName = workflow.name;
73
78
  workflow.products.forEach((product) => {
@@ -76,6 +81,7 @@ const startButtonOptionsApi = orchestratorApi.injectEndpoints({
76
81
  productName: product.name,
77
82
  productId: product.productId,
78
83
  productType: product.productType,
84
+ productTag: product.tag,
79
85
  });
80
86
  });
81
87
  });
@@ -144,6 +144,7 @@ export enum WorkflowTarget {
144
144
  MODIFY = 'modify',
145
145
  TERMINATE = 'terminate',
146
146
  SYSTEM = 'system',
147
+ VALIDATE = 'validate',
147
148
  }
148
149
 
149
150
  export type Process = {
@@ -260,6 +261,7 @@ export interface WorkflowDefinition {
260
261
  name: string;
261
262
  description?: string;
262
263
  target: WorkflowTarget;
264
+ isTask: boolean;
263
265
  products: Pick<ProductDefinition, 'tag' | 'productId' | 'name'>[];
264
266
  createdAt: string;
265
267
  }
@@ -269,6 +271,7 @@ export interface TaskDefinition {
269
271
  name: string;
270
272
  description?: string;
271
273
  target: WorkflowTarget;
274
+ isTask: boolean;
272
275
  products: Pick<ProductDefinition, 'tag' | 'productId' | 'name'>[];
273
276
  createdAt: string;
274
277
  }
@@ -514,7 +517,7 @@ export type SubscriptionDetail = {
514
517
 
515
518
  externalServices?: ExternalService[];
516
519
 
517
- processes: GraphQlSinglePage<SubscriptionDetailProcess>;
520
+ processes?: GraphQlSinglePage<SubscriptionDetailProcess>;
518
521
  };
519
522
 
520
523
  export type SubscriptionDetailProcess = Pick<
@@ -600,6 +603,7 @@ export type SubscriptionActions = {
600
603
  modify: SubscriptionAction[];
601
604
  terminate: SubscriptionAction[];
602
605
  system: SubscriptionAction[];
606
+ validate: SubscriptionAction[];
603
607
  };
604
608
 
605
609
  export enum CacheTagType {