@orchestrator-ui/orchestrator-ui-components 0.11.0 → 0.12.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.
@@ -17,14 +17,17 @@ import {
17
17
  PATH_TASKS,
18
18
  PATH_WORKFLOWS,
19
19
  TimelineItem,
20
+ WfoIsAllowedToRender,
20
21
  WfoLoading,
21
22
  WfoTimeline,
22
23
  } from '@/components';
24
+ import { PolicyResource } from '@/configuration/policy-resources';
23
25
  import { ConfirmationDialogContext } from '@/contexts';
24
26
  import {
25
27
  useCheckEngineStatus,
26
28
  useMutateProcess,
27
29
  useOrchestratorTheme,
30
+ usePolicy,
28
31
  } from '@/hooks';
29
32
  import { WfoRefresh, WfoXCircleFill } from '@/icons';
30
33
  import { ProcessDetail, ProcessStatus } from '@/types';
@@ -101,6 +104,7 @@ export const WfoProcessDetail = ({
101
104
  const { deleteProcess, abortProcess, retryProcess } = useMutateProcess();
102
105
  const router = useRouter();
103
106
  const { isEngineRunningNow } = useCheckEngineStatus();
107
+ const { isAllowed } = usePolicy();
104
108
 
105
109
  const listIncludesStatus = (
106
110
  processStatusesForDisabledState: ProcessStatus[],
@@ -195,55 +199,65 @@ export const WfoProcessDetail = ({
195
199
  css={{ flexGrow: 0 }}
196
200
  gutterSize="s"
197
201
  >
198
- <EuiButton
199
- onClick={handleActionButtonClick(retryAction)}
200
- iconType={() => (
201
- <WfoRefresh
202
- color={
203
- retryButtonIsDisabled
204
- ? theme.colors.subduedText
205
- : theme.colors.link
206
- }
207
- />
208
- )}
209
- isDisabled={retryButtonIsDisabled}
202
+ <WfoIsAllowedToRender
203
+ resource={PolicyResource.PROCESS_RETRY}
210
204
  >
211
- {t('retry')}
212
- </EuiButton>
213
- <EuiButton
214
- onClick={handleActionButtonClick(abortAction)}
215
- iconType={() => (
216
- <WfoXCircleFill
217
- color={
218
- abortButtonIsDisabled
219
- ? theme.colors.subduedText
220
- : theme.colors.danger
221
- }
222
- />
223
- )}
224
- color="danger"
225
- isDisabled={abortButtonIsDisabled}
205
+ <EuiButton
206
+ onClick={handleActionButtonClick(retryAction)}
207
+ iconType={() => (
208
+ <WfoRefresh
209
+ color={
210
+ retryButtonIsDisabled
211
+ ? theme.colors.subduedText
212
+ : theme.colors.link
213
+ }
214
+ />
215
+ )}
216
+ isDisabled={retryButtonIsDisabled}
217
+ >
218
+ {t('retry')}
219
+ </EuiButton>
220
+ </WfoIsAllowedToRender>
221
+ <WfoIsAllowedToRender
222
+ resource={PolicyResource.PROCESS_ABORT}
226
223
  >
227
- {t('abort')}
228
- </EuiButton>
229
- {processDetail && processIsTask && (
230
224
  <EuiButton
231
- onClick={handleActionButtonClick(deleteAction)}
225
+ onClick={handleActionButtonClick(abortAction)}
232
226
  iconType={() => (
233
227
  <WfoXCircleFill
234
228
  color={
235
- deleteButtonIsDisabled
229
+ abortButtonIsDisabled
236
230
  ? theme.colors.subduedText
237
231
  : theme.colors.danger
238
232
  }
239
233
  />
240
234
  )}
241
235
  color="danger"
242
- isDisabled={deleteButtonIsDisabled}
236
+ isDisabled={abortButtonIsDisabled}
243
237
  >
244
- {t('delete')}
238
+ {t('abort')}
245
239
  </EuiButton>
246
- )}
240
+ </WfoIsAllowedToRender>
241
+ {processDetail &&
242
+ processIsTask &&
243
+ isAllowed(PolicyResource.PROCESS_DELETE) && (
244
+ <EuiButton
245
+ onClick={handleActionButtonClick(deleteAction)}
246
+ iconType={() => (
247
+ <WfoXCircleFill
248
+ color={
249
+ deleteButtonIsDisabled
250
+ ? theme.colors.subduedText
251
+ : theme.colors.danger
252
+ }
253
+ />
254
+ )}
255
+ color="danger"
256
+ isDisabled={deleteButtonIsDisabled}
257
+ >
258
+ {t('delete')}
259
+ </EuiButton>
260
+ )}
247
261
  </EuiFlexGroup>
248
262
  </EuiFlexGroup>
249
263
  <EuiSpacer />
@@ -295,47 +309,51 @@ export const WfoProcessDetail = ({
295
309
  : ''
296
310
  }
297
311
  />
298
- {process && processDetail.subscriptions && (
299
- <EuiFlexGroup
300
- gutterSize="xs"
301
- direction="column"
302
- css={{
303
- flex: 1,
304
- overflow: 'hidden',
305
- }}
306
- >
307
- <EuiText size="xs">
308
- {t('relatedSubscriptions')}
309
- </EuiText>
310
- <EuiText
312
+ {process &&
313
+ isAllowed(
314
+ PolicyResource.PROCESS_RELATED_SUBSCRIPTIONS,
315
+ ) &&
316
+ processDetail.subscriptions && (
317
+ <EuiFlexGroup
318
+ gutterSize="xs"
319
+ direction="column"
311
320
  css={{
312
321
  flex: 1,
313
- whiteSpace: 'nowrap',
314
322
  overflow: 'hidden',
315
- textOverflow: 'ellipsis',
316
- fontSize: theme.size.m,
317
323
  }}
318
324
  >
319
- <WfoProcessListSubscriptionsCell
320
- subscriptions={
321
- (process &&
322
- processDetail?.subscriptions?.page.map(
323
- (subscription) => ({
324
- subscriptionId:
325
- subscription.subscriptionId,
326
- description:
327
- subscription.description,
328
- }),
329
- )) ||
330
- []
331
- }
332
- renderDirection={
333
- RenderDirection.VERTICAL
334
- }
335
- />
336
- </EuiText>
337
- </EuiFlexGroup>
338
- )}
325
+ <EuiText size="xs">
326
+ {t('relatedSubscriptions')}
327
+ </EuiText>
328
+ <EuiText
329
+ css={{
330
+ flex: 1,
331
+ whiteSpace: 'nowrap',
332
+ overflow: 'hidden',
333
+ textOverflow: 'ellipsis',
334
+ fontSize: theme.size.m,
335
+ }}
336
+ >
337
+ <WfoProcessListSubscriptionsCell
338
+ subscriptions={
339
+ (process &&
340
+ processDetail?.subscriptions?.page.map(
341
+ (subscription) => ({
342
+ subscriptionId:
343
+ subscription.subscriptionId,
344
+ description:
345
+ subscription.description,
346
+ }),
347
+ )) ||
348
+ []
349
+ }
350
+ renderDirection={
351
+ RenderDirection.VERTICAL
352
+ }
353
+ />
354
+ </EuiText>
355
+ </EuiFlexGroup>
356
+ )}
339
357
  </EuiFlexGroup>
340
358
  ))}
341
359
  </EuiPanel>
@@ -36,6 +36,14 @@ export const WfoStartPage = () => {
36
36
  subscriptionsListSummaryQueryVariables,
37
37
  ['subscriptions', 'startPage'],
38
38
  );
39
+ const {
40
+ data: outOfSyncSubscriptionsSummaryResult,
41
+ isLoading: outOfSyncsubscriptionsSummaryIsFetching,
42
+ } = useQueryWithGraphql(
43
+ getSubscriptionsListSummaryGraphQlQuery(),
44
+ outOfSyncSubscriptionsListSummaryQueryVariables,
45
+ ['subscriptions', 'startPage'],
46
+ );
39
47
  const {
40
48
  data: processesSummaryResult,
41
49
  isLoading: processesSummaryIsFetching,
@@ -82,6 +90,28 @@ export const WfoStartPage = () => {
82
90
  isLoading: subscriptionsSummaryIsFetching,
83
91
  };
84
92
 
93
+ const latestOutOfSyncSubscriptionsSummaryCard: SummaryCard = {
94
+ headerTitle: t('outOfSyncSubscriptions.headerTitle'),
95
+ headerValue:
96
+ outOfSyncSubscriptionsSummaryResult?.subscriptions.pageInfo
97
+ .totalItems ?? 0,
98
+ headerStatus: SummaryCardStatus.Error,
99
+ listTitle: t('outOfSyncSubscriptions.listTitle'),
100
+ listItems:
101
+ outOfSyncSubscriptionsSummaryResult?.subscriptions.page.map(
102
+ (subscription) => ({
103
+ title: subscription.description,
104
+ value: formatDate(subscription.startDate),
105
+ url: `${PATH_SUBSCRIPTIONS}/${subscription.subscriptionId}`,
106
+ }),
107
+ ) ?? [],
108
+ button: {
109
+ name: t('outOfSyncSubscriptions.buttonText'),
110
+ url: `${PATH_SUBSCRIPTIONS}?activeTab=ALL&sortBy=field-startDate_order-ASC&queryString=status%3A%28provisioning%7Cactive%29+insync%3Afalse`,
111
+ },
112
+ isLoading: outOfSyncsubscriptionsSummaryIsFetching,
113
+ };
114
+
85
115
  const latestWorkflowsSummaryCard: SummaryCard = {
86
116
  headerTitle: t('activeWorkflows.headerTitle'),
87
117
  headerValue: processesSummaryResult?.processes.pageInfo.totalItems ?? 0,
@@ -155,6 +185,7 @@ export const WfoStartPage = () => {
155
185
  <WfoSummaryCards
156
186
  summaryCards={[
157
187
  failedTasksSummaryCard,
188
+ latestOutOfSyncSubscriptionsSummaryCard,
158
189
  latestWorkflowsSummaryCard,
159
190
  latestActiveSubscriptionsSummaryCard,
160
191
  productsSummaryCard,
@@ -180,6 +211,23 @@ const subscriptionsListSummaryQueryVariables: GraphqlQueryVariables<Subscription
180
211
  ],
181
212
  };
182
213
 
214
+ const outOfSyncSubscriptionsListSummaryQueryVariables: GraphqlQueryVariables<Subscription> =
215
+ {
216
+ first: 5,
217
+ after: 0,
218
+ sortBy: {
219
+ field: 'startDate',
220
+ order: SortOrder.ASC,
221
+ },
222
+ query: 'insync:false',
223
+ filterBy: [
224
+ {
225
+ field: 'status',
226
+ value: 'Active-Provisioning',
227
+ },
228
+ ],
229
+ };
230
+
183
231
  const processListSummaryQueryVariables: GraphqlQueryVariables<Process> = {
184
232
  first: 5,
185
233
  after: 0,
@@ -19,6 +19,7 @@ import {
19
19
  DEFAULT_PAGE_SIZE,
20
20
  StoredTableConfig,
21
21
  WfoFilterTabs,
22
+ WfoIsAllowedToRender,
22
23
  WfoStartTaskButtonComboBox,
23
24
  WfoTableColumns,
24
25
  } from '@/components';
@@ -27,6 +28,7 @@ import {
27
28
  ProcessListItem,
28
29
  WfoProcessesList,
29
30
  } from '@/components/WfoProcessList/WfoProcessesList';
31
+ import { PolicyResource } from '@/configuration/policy-resources';
30
32
  import { ConfirmationDialogContext } from '@/contexts';
31
33
  import {
32
34
  useCheckEngineStatus,
@@ -150,15 +152,25 @@ export const WfoTasksListPage = () => {
150
152
  <EuiFlexItem>
151
153
  <EuiFlexGroup justifyContent="flexEnd">
152
154
  {' '}
153
- <EuiButton
154
- onClick={handleRerunAllButtonClick}
155
- iconType={() => (
156
- <WfoRefresh color={theme.colors.primaryText} />
157
- )}
155
+ <WfoIsAllowedToRender
156
+ resource={PolicyResource.TASKS_RETRY_ALL}
158
157
  >
159
- {t('rerunAll')}
160
- </EuiButton>
161
- <WfoStartTaskButtonComboBox />
158
+ <EuiButton
159
+ onClick={handleRerunAllButtonClick}
160
+ iconType={() => (
161
+ <WfoRefresh
162
+ color={theme.colors.primaryText}
163
+ />
164
+ )}
165
+ >
166
+ {t('rerunAll')}
167
+ </EuiButton>
168
+ </WfoIsAllowedToRender>
169
+ <WfoIsAllowedToRender
170
+ resource={PolicyResource.TASKS_CREATE}
171
+ >
172
+ <WfoStartTaskButtonComboBox />
173
+ </WfoIsAllowedToRender>
162
174
  </EuiFlexGroup>
163
175
  </EuiFlexItem>
164
176
  </EuiFlexGroup>
@@ -1 +1,2 @@
1
1
  export * from './types';
2
+ export * from './forms';
@@ -1,4 +1,10 @@
1
- import { camelToHuman, removeSuffix, upperCaseFirstChar } from './strings';
1
+ import {
2
+ camelToHuman,
3
+ removeSuffix,
4
+ snakeToHuman,
5
+ snakeToKebab,
6
+ upperCaseFirstChar,
7
+ } from './strings';
2
8
 
3
9
  describe('upperCaseFirstChar()', () => {
4
10
  it("Doesn't crash on an empty string but returns empty string", () => {
@@ -68,3 +74,33 @@ describe('camelToHuman()', () => {
68
74
  expect(result).toEqual('A Quick Brown Fox');
69
75
  });
70
76
  });
77
+
78
+ describe('snakeToHuman()', () => {
79
+ it('Returns an empty string when input is an empty string', () => {
80
+ const result = snakeToHuman('');
81
+ expect(result).toEqual('');
82
+ });
83
+ it('Returns two words from a single underscore snake case word', () => {
84
+ const result = snakeToHuman('hello_world');
85
+ expect(result).toEqual('hello world');
86
+ });
87
+ it('Returns multiple words from a multiple underscore snake case word', () => {
88
+ const result = snakeToHuman('quick_brown_fox');
89
+ expect(result).toEqual('quick brown fox');
90
+ });
91
+ });
92
+
93
+ describe('snakeToKebab()', () => {
94
+ it('Returns an empty string when input is an empty string', () => {
95
+ const result = snakeToKebab('');
96
+ expect(result).toEqual('');
97
+ });
98
+ it('Returns kebab case word from a single underscore snake case word', () => {
99
+ const result = snakeToKebab('hello_world');
100
+ expect(result).toEqual('hello-world');
101
+ });
102
+ it('Returns kebab case word from a multiple underscore snake case word', () => {
103
+ const result = snakeToKebab('quick_brown_fox');
104
+ expect(result).toEqual('quick-brown-fox');
105
+ });
106
+ });
@@ -13,3 +13,12 @@ export const camelToHuman = (value: string): string => {
13
13
  const result = value.replace(/([A-Z])/g, ' $1').trimStart();
14
14
  return result.charAt(0).toUpperCase() + result.slice(1);
15
15
  };
16
+
17
+ export const snakeToHuman = (value: string): string => {
18
+ const result = value.replace(/_/g, ' ');
19
+ return result.charAt(0) + result.slice(1);
20
+ };
21
+
22
+ export const snakeToKebab = (value: string): string => {
23
+ return value.replace(/_/g, '-');
24
+ };