@orchestrator-ui/orchestrator-ui-components 0.3.1 → 0.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 (84) hide show
  1. package/.turbo/turbo-build.log +5 -5
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +11 -9
  4. package/CHANGELOG.md +19 -0
  5. package/dist/index.d.ts +332 -58
  6. package/dist/index.js +8487 -2415
  7. package/package.json +8 -9
  8. package/src/components/WfoBadges/WfoEngineStatusBadge/WfoEngineStatusBadge.tsx +8 -5
  9. package/src/components/WfoBadges/WfoEnvironmentBadge/WfoEnvironmentBadge.tsx +2 -1
  10. package/src/components/WfoForms/formFields/OrganisationField.tsx +11 -11
  11. package/src/components/WfoForms/formFields/SplitPrefix.tsx +11 -4
  12. package/src/components/WfoForms/formFields/SubscriptionField.tsx +1 -1
  13. package/src/components/WfoPageTemplate/WfoSidebar/WfoCopyright.tsx +22 -0
  14. package/src/components/WfoPageTemplate/WfoSidebar/WfoSidebar.tsx +10 -6
  15. package/src/components/WfoPageTemplate/WfoSidebar/styles.ts +17 -0
  16. package/src/components/WfoProcessList/WfoProcessesList.tsx +59 -25
  17. package/src/components/WfoProcessList/processListObjectMappers.ts +45 -1
  18. package/src/components/WfoSettings/WfoEngineStatusButton.tsx +11 -13
  19. package/src/components/WfoSettings/WfoFlushSettings.tsx +23 -35
  20. package/src/components/WfoSettings/WfoModifySettings.tsx +3 -16
  21. package/src/components/WfoSettings/WfoStatus.tsx +4 -9
  22. package/src/components/WfoSettings/index.ts +4 -1
  23. package/src/components/WfoSubscription/WfoSubscription.tsx +5 -2
  24. package/src/components/WfoSubscription/WfoSubscriptionDetailTree.tsx +1 -0
  25. package/src/components/WfoSubscription/WfoSubscriptionGeneral.tsx +1 -1
  26. package/src/components/WfoSubscription/subscriptionDetailTabs.tsx +36 -6
  27. package/src/components/WfoSubscriptionsList/WfoSubscriptionsList.tsx +42 -11
  28. package/src/components/WfoSubscriptionsList/index.ts +1 -1
  29. package/src/components/WfoSubscriptionsList/subscriptionListTabs.ts +1 -1
  30. package/src/components/WfoSubscriptionsList/{mapGraphQlSubscriptionsResultToSubscriptionListItems.ts → subscriptionResultMappers.ts} +6 -0
  31. package/src/components/WfoTable/WfoTableWithFilter/WfoTableWithFilter.tsx +12 -0
  32. package/src/components/WfoToastsList/WfoToastsList.tsx +11 -11
  33. package/src/components/WfoTree/WfoTreeNode.tsx +2 -0
  34. package/src/configuration/constants.ts +1 -0
  35. package/src/contexts/OrchestratorConfigContext.tsx +2 -4
  36. package/src/contexts/index.ts +0 -1
  37. package/src/graphqlQueries/index.ts +1 -1
  38. package/src/graphqlQueries/processListQuery.ts +29 -29
  39. package/src/graphqlQueries/subscriptionDetailQuery.ts +1 -1
  40. package/src/graphqlQueries/subscriptionsDropdownOptionsQuery.ts +1 -1
  41. package/src/hooks/index.ts +1 -2
  42. package/src/hooks/useCheckEngineStatus.ts +10 -9
  43. package/src/hooks/useOrchestratorConfig.ts +1 -16
  44. package/src/hooks/useQueryWithGraphql.ts +22 -0
  45. package/src/hooks/useShowToastMessage.ts +43 -0
  46. package/src/hooks/useStoredTableConfig.ts +5 -4
  47. package/src/icons/WfoCogFill.tsx +23 -25
  48. package/src/icons/WfoCubeSolid.tsx +33 -0
  49. package/src/icons/WfoPlayCircle.tsx +35 -0
  50. package/src/icons/WfoShare.tsx +35 -0
  51. package/src/icons/index.ts +3 -0
  52. package/src/index.ts +1 -0
  53. package/src/messages/en-GB.json +9 -5
  54. package/src/messages/nl-NL.json +8 -4
  55. package/src/pages/metadata/WfoProductBlocksPage.tsx +40 -6
  56. package/src/pages/metadata/WfoProductsPage.tsx +38 -9
  57. package/src/pages/metadata/WfoResourceTypesPage.tsx +36 -5
  58. package/src/pages/metadata/WfoWorkflowsPage.tsx +40 -9
  59. package/src/pages/settings/WfoSettingsPage.tsx +14 -23
  60. package/src/rtk/api.ts +40 -0
  61. package/src/rtk/endpoints/customers.ts +27 -0
  62. package/src/rtk/endpoints/index.ts +3 -0
  63. package/src/rtk/endpoints/processList.ts +89 -0
  64. package/src/rtk/endpoints/settings.ts +72 -0
  65. package/src/rtk/hooks.ts +7 -0
  66. package/src/rtk/index.ts +5 -0
  67. package/src/rtk/slices/index.ts +1 -0
  68. package/src/rtk/slices/orchestratorConfig.ts +16 -0
  69. package/src/rtk/slices/toastMessages.ts +56 -0
  70. package/src/rtk/store.ts +40 -0
  71. package/src/rtk/storeProvider.tsx +26 -0
  72. package/src/types/types.ts +33 -2
  73. package/src/utils/csvDownload.ts +83 -0
  74. package/src/utils/getDefaultTableConfig.ts +1 -3
  75. package/src/utils/getQueryVariablesForExport.spec.ts +19 -0
  76. package/src/utils/getQueryVariablesForExport.ts +11 -0
  77. package/src/utils/index.ts +1 -0
  78. package/src/utils/sortObjectKeys.spec.ts +34 -0
  79. package/src/utils/sortObjectKeys.ts +33 -0
  80. package/src/components/WfoSettings/WfoSettings.tsx +0 -40
  81. package/src/contexts/ToastContext.tsx +0 -136
  82. package/src/graphqlQueries/customersQuery.ts +0 -20
  83. package/src/hooks/useEngineStatusQuery.ts +0 -64
  84. package/src/hooks/useToastMessage.ts +0 -5
@@ -1,30 +1,21 @@
1
1
  import React from 'react';
2
2
 
3
- import { WfoSettings } from '@/components';
4
- import { useEngineStatusMutation, useEngineStatusQuery } from '@/hooks';
5
- import { EngineStatus } from '@/types';
3
+ import { EuiHorizontalRule, EuiPageHeader, EuiSpacer } from '@elastic/eui';
6
4
 
7
- export const WfoSettingsPage = () => {
8
- const { data: engineStatus } = useEngineStatusQuery();
9
- const { mutate, data: newEngineStatus } = useEngineStatusMutation();
10
-
11
- const isRunning =
12
- newEngineStatus?.global_status === EngineStatus.RUNNING ||
13
- engineStatus?.global_status === EngineStatus.RUNNING;
14
- const currentEngineStatus =
15
- newEngineStatus?.global_status || engineStatus?.global_status;
16
- const currentRunningProcesses =
17
- newEngineStatus?.running_processes || engineStatus?.running_processes;
18
-
19
- const changeEngineStatus = () => {
20
- mutate({ global_lock: isRunning });
21
- };
5
+ import { WfoFlushSettings, WfoModifySettings, WfoStatus } from '@/components';
22
6
 
7
+ export const WfoSettingsPage = () => {
23
8
  return (
24
- <WfoSettings
25
- currentEngineStatus={currentEngineStatus}
26
- currentRunningProcesses={currentRunningProcesses}
27
- changeEngineStatus={changeEngineStatus}
28
- />
9
+ <>
10
+ <EuiSpacer />
11
+
12
+ <EuiPageHeader pageTitle="Settings" />
13
+ <EuiHorizontalRule />
14
+ <WfoFlushSettings />
15
+ <EuiSpacer />
16
+ <WfoModifySettings />
17
+ <EuiSpacer />
18
+ <WfoStatus />
19
+ </>
29
20
  );
30
21
  };
package/src/rtk/api.ts ADDED
@@ -0,0 +1,40 @@
1
+ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
2
+ import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query';
3
+
4
+ import type { RootState } from './store';
5
+
6
+ export enum BaseQueryTypes {
7
+ fetch = 'fetch',
8
+ graphql = 'graphql',
9
+ custom = 'custom',
10
+ }
11
+
12
+ type ExtraOptions = {
13
+ baseQueryType?: BaseQueryTypes;
14
+ };
15
+
16
+ export const orchestratorApi = createApi({
17
+ reducerPath: 'orchestratorApi',
18
+ baseQuery: (args, api, extraOptions: ExtraOptions) => {
19
+ const { baseQueryType } = extraOptions || {};
20
+
21
+ const state = api.getState() as RootState;
22
+ const { orchestratorApiBaseUrl, graphqlEndpointCore } =
23
+ state.orchestratorConfig;
24
+
25
+ switch (baseQueryType) {
26
+ case BaseQueryTypes.fetch:
27
+ const fetchFn = fetchBaseQuery({
28
+ baseUrl: orchestratorApiBaseUrl,
29
+ });
30
+ return fetchFn(args, api, {});
31
+ default:
32
+ const graphqlFn = graphqlRequestBaseQuery({
33
+ url: graphqlEndpointCore,
34
+ });
35
+ return graphqlFn(args, api, {});
36
+ }
37
+ },
38
+ endpoints: () => ({}),
39
+ tagTypes: ['engineStatus'],
40
+ });
@@ -0,0 +1,27 @@
1
+ import { Customer, CustomersResult } from '@/types';
2
+
3
+ import { orchestratorApi } from '../api';
4
+
5
+ const customersQuery = `query Customers {
6
+ customers(first: 1000000, after: 0) {
7
+ page {
8
+ fullname
9
+ identifier
10
+ shortcode
11
+ }:
12
+ }
13
+ }`;
14
+
15
+ const customersApi = orchestratorApi.injectEndpoints({
16
+ endpoints: (build) => ({
17
+ getCustomers: build.query<Customer[], void>({
18
+ query: () => ({ document: customersQuery }),
19
+ transformResponse: (response: CustomersResult): Customer[] => {
20
+ const customers = response?.customers?.page || [];
21
+ return customers;
22
+ },
23
+ }),
24
+ }),
25
+ });
26
+
27
+ export const { useGetCustomersQuery } = customersApi;
@@ -0,0 +1,3 @@
1
+ export * from './customers';
2
+ export * from './processList';
3
+ export * from './settings';
@@ -0,0 +1,89 @@
1
+ import {
2
+ BaseGraphQlResult,
3
+ GraphqlQueryVariables,
4
+ Process,
5
+ ProcessListResult,
6
+ } from '@/types';
7
+
8
+ import { orchestratorApi } from '../api';
9
+
10
+ export const processListQuery = `
11
+ query ProcessList(
12
+ $first: Int!
13
+ $after: Int!
14
+ $sortBy: [GraphqlSort!]
15
+ $filterBy: [GraphqlFilter!]
16
+ $query: String
17
+ ) {
18
+ processes(
19
+ first: $first
20
+ after: $after
21
+ sortBy: $sortBy
22
+ filterBy: $filterBy
23
+ query: $query
24
+ ) {
25
+ page {
26
+ workflowName
27
+ lastStep
28
+ lastStatus
29
+ workflowTarget
30
+ product {
31
+ name
32
+ tag
33
+ }
34
+ customer {
35
+ fullname
36
+ shortcode
37
+ }
38
+ createdBy
39
+ assignee
40
+ processId
41
+ startedAt
42
+ lastModifiedAt
43
+ isTask
44
+ subscriptions {
45
+ page {
46
+ subscriptionId
47
+ description
48
+ }
49
+ }
50
+ }
51
+ pageInfo {
52
+ hasNextPage
53
+ hasPreviousPage
54
+ startCursor
55
+ totalItems
56
+ endCursor
57
+ sortFields
58
+ filterFields
59
+ }
60
+ }
61
+ }
62
+ `;
63
+
64
+ export type ProcessListResponse = {
65
+ processes: Process[];
66
+ } & BaseGraphQlResult;
67
+
68
+ const processApi = orchestratorApi.injectEndpoints({
69
+ endpoints: (build) => ({
70
+ getProcessList: build.query<
71
+ ProcessListResponse,
72
+ GraphqlQueryVariables<Process>
73
+ >({
74
+ query: (variables) => ({ document: processListQuery, variables }),
75
+ transformResponse: (
76
+ response: ProcessListResult,
77
+ ): ProcessListResponse => {
78
+ const processes = response?.processes?.page || [];
79
+
80
+ return {
81
+ processes,
82
+ pageInfo: response.processes?.pageInfo || {},
83
+ };
84
+ },
85
+ }),
86
+ }),
87
+ });
88
+
89
+ export const { useGetProcessListQuery } = processApi;
@@ -0,0 +1,72 @@
1
+ import { EngineStatus } from '@/types';
2
+
3
+ import { BaseQueryTypes, orchestratorApi } from '../api';
4
+
5
+ interface EngineStatusReturnValue {
6
+ engineStatus: EngineStatus;
7
+ runningProcesses: number;
8
+ }
9
+
10
+ const statusApi = orchestratorApi.injectEndpoints({
11
+ endpoints: (build) => ({
12
+ getEngineStatus: build.query<EngineStatusReturnValue, void>({
13
+ query: () => '/settings/status',
14
+ extraOptions: {
15
+ baseQueryType: BaseQueryTypes.fetch,
16
+ },
17
+ transformResponse(data: {
18
+ global_status: string;
19
+ running_processes: number;
20
+ }) {
21
+ const engineStatus = (() => {
22
+ switch (data?.global_status) {
23
+ case 'RUNNING':
24
+ return EngineStatus.RUNNING;
25
+ case 'PAUSING':
26
+ return EngineStatus.PAUSING;
27
+ case 'PAUSED':
28
+ return EngineStatus.PAUSED;
29
+ default:
30
+ return EngineStatus.UNKNOWN;
31
+ }
32
+ })();
33
+ return {
34
+ engineStatus,
35
+ runningProcesses: data?.running_processes || 0,
36
+ };
37
+ },
38
+ providesTags: ['engineStatus'],
39
+ }),
40
+ clearCache: build.mutation<void, string>({
41
+ query: (settingName) => ({
42
+ url: `/settings/cache/${settingName}`,
43
+ method: 'DELETE',
44
+ }),
45
+ extraOptions: {
46
+ baseQueryType: BaseQueryTypes.fetch,
47
+ },
48
+ }),
49
+ setEngineStatus: build.mutation<EngineStatusReturnValue, boolean>({
50
+ query: (globalStatus) => ({
51
+ url: `/settings/status`,
52
+ method: 'PUT',
53
+ body: JSON.stringify({
54
+ global_lock: globalStatus,
55
+ }),
56
+ headers: {
57
+ 'Content-Type': 'application/json',
58
+ },
59
+ }),
60
+ extraOptions: {
61
+ baseQueryType: BaseQueryTypes.fetch,
62
+ },
63
+ invalidatesTags: ['engineStatus'],
64
+ }),
65
+ }),
66
+ });
67
+
68
+ export const {
69
+ useGetEngineStatusQuery,
70
+ useClearCacheMutation,
71
+ useSetEngineStatusMutation,
72
+ } = statusApi;
@@ -0,0 +1,7 @@
1
+ import { useDispatch, useSelector } from 'react-redux';
2
+ import type { TypedUseSelectorHook } from 'react-redux';
3
+
4
+ import type { AppDispatch, RootState } from './store';
5
+
6
+ export const useAppDispatch: () => AppDispatch = useDispatch;
7
+ export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
@@ -0,0 +1,5 @@
1
+ export * from './api';
2
+ export * from './endpoints';
3
+ export * from './store';
4
+ export * from './slices';
5
+ export * from './storeProvider';
@@ -0,0 +1 @@
1
+ export * from './toastMessages';
@@ -0,0 +1,16 @@
1
+ import { createSlice } from '@reduxjs/toolkit';
2
+ import type { Slice } from '@reduxjs/toolkit';
3
+
4
+ import type { OrchestratorConfig } from '@/types';
5
+
6
+ type OrchestratorConfigSlice = Slice<OrchestratorConfig>;
7
+
8
+ export const getOrchestratorConfigSlice = (
9
+ config: OrchestratorConfig,
10
+ ): OrchestratorConfigSlice => {
11
+ return createSlice({
12
+ name: 'orchestrator',
13
+ initialState: config,
14
+ reducers: {},
15
+ });
16
+ };
@@ -0,0 +1,56 @@
1
+ import { createSlice } from '@reduxjs/toolkit';
2
+ import type { PayloadAction, Reducer, Slice } from '@reduxjs/toolkit';
3
+
4
+ import { Toast } from '@/types';
5
+
6
+ export type ToastState = {
7
+ messages: Toast[];
8
+ };
9
+
10
+ const initialState: ToastState = {
11
+ messages: [],
12
+ };
13
+
14
+ type ToastMessagesSlice = Slice<
15
+ ToastState,
16
+ {
17
+ addToastMessage: (
18
+ state: ToastState,
19
+ action: PayloadAction<Toast>,
20
+ ) => ToastState;
21
+ removeToastMessage: (
22
+ state: ToastState,
23
+ action: PayloadAction<Toast['id']>,
24
+ ) => ToastState;
25
+ },
26
+ 'toastMessages',
27
+ 'toastMessages'
28
+ >;
29
+
30
+ export const toastMessagesSlice: ToastMessagesSlice = createSlice({
31
+ name: 'toastMessages',
32
+ initialState,
33
+ reducers: {
34
+ addToastMessage: (state, action: PayloadAction<Toast>) => {
35
+ return {
36
+ ...state,
37
+ messages: [...state.messages, action.payload],
38
+ };
39
+ },
40
+ removeToastMessage: (state, action: PayloadAction<Toast['id']>) => {
41
+ return {
42
+ ...state,
43
+ messages: state.messages.filter(
44
+ (message) => message.id !== action.payload,
45
+ ),
46
+ };
47
+ },
48
+ },
49
+ });
50
+
51
+ // Action creators are generated for each case reducer function
52
+ export const { addToastMessage, removeToastMessage } =
53
+ toastMessagesSlice.actions;
54
+
55
+ export const toastMessagesReducer: Reducer<ToastState> =
56
+ toastMessagesSlice.reducer;
@@ -0,0 +1,40 @@
1
+ import { configureStore } from '@reduxjs/toolkit';
2
+ import type { EnhancedStore } from '@reduxjs/toolkit';
3
+ import type { Dispatch, UnknownAction } from '@reduxjs/toolkit';
4
+ import { CombinedState } from '@reduxjs/toolkit/query';
5
+
6
+ import type { OrchestratorConfig } from '@/types';
7
+
8
+ import { orchestratorApi } from './api';
9
+ import { getOrchestratorConfigSlice } from './slices/orchestratorConfig';
10
+ import { toastMessagesReducer } from './slices/toastMessages';
11
+
12
+ export type RootState = {
13
+ orchestratorApi: CombinedState<
14
+ Record<string, never>,
15
+ 'engineStatus',
16
+ 'orchestratorApi'
17
+ >;
18
+ toastMessages: ReturnType<typeof toastMessagesReducer>;
19
+ orchestratorConfig: OrchestratorConfig;
20
+ };
21
+
22
+ export const getOrchestratorStore = (
23
+ orchestratorConfig: OrchestratorConfig,
24
+ ): EnhancedStore<RootState> => {
25
+ const configSlice = getOrchestratorConfigSlice(orchestratorConfig);
26
+
27
+ const orchestratorStore = configureStore({
28
+ reducer: {
29
+ [orchestratorApi.reducerPath]: orchestratorApi.reducer,
30
+ toastMessages: toastMessagesReducer,
31
+ orchestratorConfig: configSlice.reducer,
32
+ },
33
+ middleware: (getDefaultMiddleware) =>
34
+ getDefaultMiddleware().concat(orchestratorApi.middleware),
35
+ });
36
+
37
+ return orchestratorStore;
38
+ };
39
+
40
+ export type AppDispatch = Dispatch<UnknownAction>;
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import type { ReactNode } from 'react';
3
+ import { Provider } from 'react-redux';
4
+
5
+ import { useOrchestratorConfig } from '@/hooks';
6
+ import type { OrchestratorConfig } from '@/types';
7
+
8
+ import { getOrchestratorStore } from './store';
9
+
10
+ export type StoreProviderProps = {
11
+ initialOrchestratorConfig: OrchestratorConfig;
12
+ children: ReactNode;
13
+ };
14
+
15
+ export const StoreProvider = ({
16
+ initialOrchestratorConfig,
17
+ children,
18
+ }: StoreProviderProps) => {
19
+ const { orchestratorConfig } = useOrchestratorConfig(
20
+ initialOrchestratorConfig,
21
+ );
22
+
23
+ const store = getOrchestratorStore(orchestratorConfig);
24
+
25
+ return <Provider store={store}>{children}</Provider>;
26
+ };
@@ -1,3 +1,5 @@
1
+ import { Toast } from '@elastic/eui/src/components/toast/global_toast_list';
2
+
1
3
  import { InputForm } from './forms';
2
4
 
3
5
  export type Nullable<T> = T | null;
@@ -18,11 +20,12 @@ export enum EngineStatus {
18
20
  RUNNING = 'RUNNING',
19
21
  PAUSING = 'PAUSING',
20
22
  PAUSED = 'PAUSED',
23
+ UNKNOWN = 'UNKNOWN',
21
24
  }
22
25
 
23
26
  export type Customer = {
24
27
  fullname: string;
25
- identifier: string;
28
+ customerId: string;
26
29
  shortcode: string;
27
30
  };
28
31
 
@@ -266,6 +269,10 @@ export type GraphQLPageInfo = {
266
269
  filterFields: string[];
267
270
  };
268
271
 
272
+ export type BaseGraphQlResult = {
273
+ pageInfo: GraphQLPageInfo;
274
+ };
275
+
269
276
  export interface SubscriptionsResult<T = Subscription> {
270
277
  subscriptions: GraphQlResultPage<T>;
271
278
  }
@@ -377,7 +384,7 @@ export type SubscriptionDropdownOption = {
377
384
  description: Subscription['description'];
378
385
  subscriptionId: Subscription['subscriptionId'];
379
386
  product: Pick<ProductDefinition, 'tag' | 'productId'>;
380
- customer: Pick<Customer, 'fullname' | 'identifier'>;
387
+ customer: Pick<Customer, 'fullname' | 'customerId'>;
381
388
  productBlockInstances: ProductBlockInstance[];
382
389
  fixedInputs: FieldValue[];
383
390
  tag: string;
@@ -440,3 +447,27 @@ export type ExternalService = {
440
447
  };
441
448
 
442
449
  export type WfoTreeNodeMap = { [key: number]: TreeBlock };
450
+
451
+ export type { Toast };
452
+
453
+ export enum ToastTypes {
454
+ ERROR = 'ERROR',
455
+ SUCCESS = 'SUCCESS',
456
+ }
457
+
458
+ export enum Environment {
459
+ DEVELOPMENT = 'Development',
460
+ PRODUCTION = 'Production',
461
+ }
462
+
463
+ export type OrchestratorConfig = {
464
+ environmentName: Environment | string;
465
+ orchestratorApiBaseUrl: string;
466
+ graphqlEndpointCore: string;
467
+ engineStatusEndpoint: string;
468
+ processStatusCountsEndpoint: string;
469
+ processesEndpoint: string;
470
+ subscriptionActionsEndpoint: string;
471
+ subscriptionProcessesEndpoint: string;
472
+ authActive: boolean;
473
+ };
@@ -0,0 +1,83 @@
1
+ import { TranslationValues } from 'next-intl';
2
+
3
+ import { MAXIMUM_ITEMS_FOR_BULK_FETCHING } from '@/configuration/constants';
4
+ import { ToastTypes } from '@/types';
5
+ import { GraphQLPageInfo } from '@/types';
6
+ import { sortObjectKeys } from '@/utils/sortObjectKeys';
7
+
8
+ function toCsvFileContent<T extends object>(data: T[]): string {
9
+ const headers = Object.keys(data[0]).join(';');
10
+ const rows = data.map((row) =>
11
+ Object.values(row)
12
+ .map((value) => (value === null ? '' : `"${value}"`))
13
+ .join(';')
14
+ .split('\n')
15
+ .join(' '),
16
+ );
17
+ return [headers, ...rows].join('\n');
18
+ }
19
+
20
+ function startCsvDownload(csvFileContent: string, fileName: string) {
21
+ const blob = new Blob([csvFileContent], { type: 'text/csv' });
22
+ const url = URL.createObjectURL(blob);
23
+ const link = document.createElement('a');
24
+ link.href = url;
25
+ link.download = fileName.endsWith('.csv') ? fileName : `${fileName}.csv`;
26
+ link.click();
27
+ URL.revokeObjectURL(url);
28
+ }
29
+
30
+ export function initiateCsvFileDownload<T extends object>(
31
+ data: T[],
32
+ fileName: string,
33
+ ) {
34
+ const csvFileContent = toCsvFileContent(data);
35
+ startCsvDownload(csvFileContent, fileName);
36
+ }
37
+
38
+ export const getCsvFileNameWithDate = (fileNameWithoutExtension: string) => {
39
+ const date = new Date();
40
+ const year = date.getFullYear();
41
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
42
+ const day = date.getDate();
43
+
44
+ const hour = date.getHours();
45
+ const minute = date.getMinutes();
46
+ const second = date.getSeconds();
47
+
48
+ return `${fileNameWithoutExtension}_${year}-${month}-${day}-${hour}${minute}${second}.csv`;
49
+ };
50
+
51
+ export const csvDownloadHandler = <T extends object, U extends object>(
52
+ dataFetchFunction: () => Promise<T | undefined>,
53
+ dataMapper: (data: T) => U[],
54
+ pageInfoMapper: (data: T) => GraphQLPageInfo,
55
+ keyOrder: string[],
56
+ filename: string,
57
+ addToastFunction: (type: ToastTypes, text: string, title: string) => void,
58
+ translationFunction: (
59
+ translationKey: string,
60
+ variables?: TranslationValues,
61
+ ) => string,
62
+ ) => {
63
+ return async () => {
64
+ const data: T | undefined = await dataFetchFunction();
65
+
66
+ if (data) {
67
+ const dataForExport = dataMapper(data).map((d) =>
68
+ sortObjectKeys(d, keyOrder),
69
+ );
70
+ const pageInfo = pageInfoMapper(data);
71
+ (pageInfo.totalItems ?? 0) > MAXIMUM_ITEMS_FOR_BULK_FETCHING &&
72
+ addToastFunction(
73
+ ToastTypes.ERROR,
74
+ translationFunction('notAllResultsExported', {
75
+ totalResults: pageInfo.totalItems,
76
+ maximumExportedResults: MAXIMUM_ITEMS_FOR_BULK_FETCHING,
77
+ }),
78
+ translationFunction('notAllResultsExportedTitle'),
79
+ );
80
+ initiateCsvFileDownload(dataForExport, filename);
81
+ }
82
+ };
83
+ };
@@ -64,19 +64,17 @@ export const getDefaultTableConfig = <T>(storageKey: string) => {
64
64
  const activeProcessColumns: (keyof ProcessListItem)[] = [
65
65
  'productName',
66
66
  'customer',
67
- 'createdBy',
68
67
  'assignee',
69
68
  'processId',
69
+ 'startedAt',
70
70
  ];
71
71
  return getTableConfig<T>(activeProcessColumns as (keyof T)[]);
72
72
 
73
73
  case COMPLETED_PROCESSES_LIST_TABLE_LOCAL_STORAGE_KEY:
74
74
  const completedProcessColumns: (keyof ProcessListItem)[] = [
75
75
  'lastStep',
76
- 'lastStatus',
77
76
  'productName',
78
77
  'customer',
79
- 'createdBy',
80
78
  'assignee',
81
79
  'processId',
82
80
  'startedAt',
@@ -0,0 +1,19 @@
1
+ import { getQueryVariablesForExport } from './getQueryVariablesForExport';
2
+
3
+ describe('getQueryVariablesForExport', () => {
4
+ it('returns queryVariables with first and after property set to 1000 and 0', () => {
5
+ const queryVariables = {
6
+ first: 10,
7
+ after: 20,
8
+ otherField: 'test',
9
+ };
10
+
11
+ const result = getQueryVariablesForExport(queryVariables);
12
+
13
+ expect(result).toEqual({
14
+ first: 1000,
15
+ after: 0,
16
+ otherField: 'test',
17
+ });
18
+ });
19
+ });
@@ -0,0 +1,11 @@
1
+ import type { GraphqlQueryVariables } from '@/types';
2
+
3
+ import { MAXIMUM_ITEMS_FOR_BULK_FETCHING } from '../configuration/constants';
4
+
5
+ export const getQueryVariablesForExport = <T extends object>(
6
+ queryVariables: GraphqlQueryVariables<T>,
7
+ ) => ({
8
+ ...queryVariables,
9
+ first: MAXIMUM_ITEMS_FOR_BULK_FETCHING,
10
+ after: 0,
11
+ });
@@ -5,3 +5,4 @@ export * from './getTypedFieldFromObject';
5
5
  export * from './uuid';
6
6
  export * from './strings';
7
7
  export * from './getProductNamesFromProcess';
8
+ export * from './getQueryVariablesForExport';