@finos/legend-application-marketplace 0.1.51 → 0.1.52

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 (60) hide show
  1. package/lib/__lib__/LegendMarketplaceAppEvent.d.ts +2 -1
  2. package/lib/__lib__/LegendMarketplaceAppEvent.d.ts.map +1 -1
  3. package/lib/__lib__/LegendMarketplaceAppEvent.js +1 -0
  4. package/lib/__lib__/LegendMarketplaceAppEvent.js.map +1 -1
  5. package/lib/components/AddToCart/RecommendedAddOnsModal.d.ts.map +1 -1
  6. package/lib/components/AddToCart/RecommendedAddOnsModal.js +32 -6
  7. package/lib/components/AddToCart/RecommendedAddOnsModal.js.map +1 -1
  8. package/lib/components/AddToCart/RecommendedItemsCard.d.ts +0 -1
  9. package/lib/components/AddToCart/RecommendedItemsCard.d.ts.map +1 -1
  10. package/lib/components/AddToCart/RecommendedItemsCard.js +12 -17
  11. package/lib/components/AddToCart/RecommendedItemsCard.js.map +1 -1
  12. package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.d.ts.map +1 -1
  13. package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.js +2 -2
  14. package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.js.map +1 -1
  15. package/lib/components/orders/CancelOrderDialog.d.ts +27 -0
  16. package/lib/components/orders/CancelOrderDialog.d.ts.map +1 -0
  17. package/lib/components/orders/CancelOrderDialog.js +52 -0
  18. package/lib/components/orders/CancelOrderDialog.js.map +1 -0
  19. package/lib/components/orders/ProgressTracker.d.ts +23 -0
  20. package/lib/components/orders/ProgressTracker.d.ts.map +1 -0
  21. package/lib/components/orders/ProgressTracker.js +116 -0
  22. package/lib/components/orders/ProgressTracker.js.map +1 -0
  23. package/lib/index.css +2 -2
  24. package/lib/index.css.map +1 -1
  25. package/lib/package.json +1 -1
  26. package/lib/pages/Profile/LegendMarketplaceYourOrders.d.ts.map +1 -1
  27. package/lib/pages/Profile/LegendMarketplaceYourOrders.js +43 -36
  28. package/lib/pages/Profile/LegendMarketplaceYourOrders.js.map +1 -1
  29. package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.d.ts.map +1 -1
  30. package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js +42 -18
  31. package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js.map +1 -1
  32. package/lib/stores/LegendMarketPlaceVendorDataStore.d.ts +10 -4
  33. package/lib/stores/LegendMarketPlaceVendorDataStore.d.ts.map +1 -1
  34. package/lib/stores/LegendMarketPlaceVendorDataStore.js +82 -49
  35. package/lib/stores/LegendMarketPlaceVendorDataStore.js.map +1 -1
  36. package/lib/stores/cart/CartStore.d.ts.map +1 -1
  37. package/lib/stores/cart/CartStore.js +2 -1
  38. package/lib/stores/cart/CartStore.js.map +1 -1
  39. package/lib/stores/orders/OrderHelpers.d.ts +49 -0
  40. package/lib/stores/orders/OrderHelpers.d.ts.map +1 -0
  41. package/lib/stores/orders/OrderHelpers.js +134 -0
  42. package/lib/stores/orders/OrderHelpers.js.map +1 -0
  43. package/lib/stores/orders/OrderStore.d.ts +2 -0
  44. package/lib/stores/orders/OrderStore.d.ts.map +1 -1
  45. package/lib/stores/orders/OrderStore.js +31 -0
  46. package/lib/stores/orders/OrderStore.js.map +1 -1
  47. package/package.json +7 -7
  48. package/src/__lib__/LegendMarketplaceAppEvent.ts +1 -0
  49. package/src/components/AddToCart/RecommendedAddOnsModal.tsx +145 -17
  50. package/src/components/AddToCart/RecommendedItemsCard.tsx +32 -62
  51. package/src/components/ProviderCard/LegendMarketplaceTerminalCard.tsx +18 -2
  52. package/src/components/orders/CancelOrderDialog.tsx +157 -0
  53. package/src/components/orders/ProgressTracker.tsx +266 -0
  54. package/src/pages/Profile/LegendMarketplaceYourOrders.tsx +227 -230
  55. package/src/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.tsx +140 -17
  56. package/src/stores/LegendMarketPlaceVendorDataStore.tsx +114 -84
  57. package/src/stores/cart/CartStore.ts +2 -1
  58. package/src/stores/orders/OrderHelpers.ts +167 -0
  59. package/src/stores/orders/OrderStore.ts +51 -0
  60. package/tsconfig.json +3 -0
@@ -27,15 +27,20 @@ import {
27
27
  List,
28
28
  ListItem,
29
29
  CircularProgress,
30
+ Pagination,
31
+ Box,
32
+ Select,
33
+ MenuItem,
34
+ type SelectChangeEvent,
30
35
  } from '@mui/material';
31
- import type { Filter, TerminalResult } from '@finos/legend-server-marketplace';
36
+ import type { TerminalResult } from '@finos/legend-server-marketplace';
32
37
  import { LegendMarketplaceTerminalCard } from '../../components/ProviderCard/LegendMarketplaceTerminalCard.js';
33
38
  import {
34
39
  type LegendMarketPlaceVendorDataStore,
35
40
  VendorDataProviderType,
36
41
  } from '../../stores/LegendMarketPlaceVendorDataStore.js';
37
42
  import { LegendMarketplacePage } from '../LegendMarketplacePage.js';
38
- import { useEffect } from 'react';
43
+ import { useEffect, useCallback } from 'react';
39
44
  import {
40
45
  useLegendMarketPlaceVendorDataStore,
41
46
  withLegendMarketplaceVendorDataStore,
@@ -49,6 +54,7 @@ import {
49
54
  SparkleStarsIcon,
50
55
  } from '@finos/legend-art';
51
56
  import { ComingSoonDisplay } from '../../components/ComingSoon/ComingSoonDisplay.js';
57
+ import { flowResult } from 'mobx';
52
58
 
53
59
  export const RefinedVendorRadioSelector = observer(
54
60
  (props: { vendorDataState: LegendMarketPlaceVendorDataStore }) => {
@@ -59,14 +65,15 @@ export const RefinedVendorRadioSelector = observer(
59
65
  VendorDataProviderType.ADD_ONS,
60
66
  ];
61
67
 
62
- const onRadioChange = (value: VendorDataProviderType) => {
63
- vendorDataState.setProviderDisplayState(value);
64
- if (value === VendorDataProviderType.TERMINAL_LICENSE) {
65
- vendorDataState.setProviders('desktop');
66
- } else {
67
- vendorDataState.setProviders('addon');
68
- }
69
- };
68
+ const onRadioChange = useCallback(
69
+ (value: VendorDataProviderType) => {
70
+ vendorDataState.setProviderDisplayState(value);
71
+ flowResult(vendorDataState.populateProviders()).catch(
72
+ vendorDataState.applicationStore.alertUnhandledError,
73
+ );
74
+ },
75
+ [vendorDataState],
76
+ );
70
77
 
71
78
  return (
72
79
  <ButtonGroup variant="outlined">
@@ -142,6 +149,95 @@ const SearchResultsRenderer = observer(
142
149
  },
143
150
  );
144
151
 
152
+ const PaginationControls = observer(
153
+ (props: { vendorDataState: LegendMarketPlaceVendorDataStore }) => {
154
+ const { vendorDataState } = props;
155
+
156
+ const totalPages = Math.ceil(
157
+ vendorDataState.totalItems / vendorDataState.itemsPerPage,
158
+ );
159
+
160
+ const handlePageChange = useCallback(
161
+ (_event: React.ChangeEvent<unknown>, page: number) => {
162
+ vendorDataState.setPage(page);
163
+ flowResult(vendorDataState.populateProviders()).catch(
164
+ vendorDataState.applicationStore.alertUnhandledError,
165
+ );
166
+ },
167
+ [vendorDataState],
168
+ );
169
+
170
+ const handleItemsPerPageChange = useCallback(
171
+ (event: SelectChangeEvent<number>) => {
172
+ vendorDataState.setItemsPerPage(Number(event.target.value));
173
+ flowResult(vendorDataState.populateProviders()).catch(
174
+ vendorDataState.applicationStore.alertUnhandledError,
175
+ );
176
+ },
177
+ [vendorDataState],
178
+ );
179
+
180
+ if (vendorDataState.providers.length === 0) {
181
+ return null;
182
+ }
183
+
184
+ return (
185
+ <Box className="legend-marketplace-pagination-container">
186
+ <Box className="legend-marketplace-pagination-page-size">
187
+ <Typography variant="body2" sx={{ fontSize: '2rem' }}>
188
+ Items per page:
189
+ </Typography>
190
+ <Select
191
+ value={vendorDataState.itemsPerPage}
192
+ onChange={handleItemsPerPageChange}
193
+ size="medium"
194
+ >
195
+ <MenuItem value={12}>12</MenuItem>
196
+ <MenuItem value={24}>24</MenuItem>
197
+ <MenuItem value={36}>36</MenuItem>
198
+ <MenuItem value={48}>48</MenuItem>
199
+ </Select>
200
+ </Box>
201
+ <Box className="legend-marketplace-pagination-info">
202
+ <Typography variant="body2">
203
+ Showing{' '}
204
+ <strong>
205
+ {(vendorDataState.page - 1) * vendorDataState.itemsPerPage + 1}
206
+ </strong>{' '}
207
+ to{' '}
208
+ <strong>
209
+ {Math.min(
210
+ vendorDataState.page * vendorDataState.itemsPerPage,
211
+ vendorDataState.totalItems,
212
+ )}
213
+ </strong>{' '}
214
+ of <strong>{vendorDataState.totalItems}</strong> results
215
+ </Typography>
216
+ </Box>
217
+
218
+ <Box className="legend-marketplace-pagination-controls">
219
+ <Pagination
220
+ count={totalPages}
221
+ page={vendorDataState.page}
222
+ onChange={handlePageChange}
223
+ color="primary"
224
+ showFirstButton={true}
225
+ showLastButton={true}
226
+ siblingCount={1}
227
+ boundaryCount={2}
228
+ size="large"
229
+ sx={{
230
+ '& .MuiPaginationItem-root': {
231
+ fontSize: '1.5rem',
232
+ },
233
+ }}
234
+ />
235
+ </Box>
236
+ </Box>
237
+ );
238
+ },
239
+ );
240
+
145
241
  export const VendorDataMainContent = observer(
146
242
  (props: { marketPlaceVendorDataState: LegendMarketPlaceVendorDataStore }) => {
147
243
  const { marketPlaceVendorDataState } = props;
@@ -157,7 +253,7 @@ export const VendorDataMainContent = observer(
157
253
  </div>
158
254
  ) : (
159
255
  <>
160
- <div className="legend-marketplace-vendordata-main-sidebar">
256
+ <div className="legend-marketplace-vendordata-main-sidebar legend-marketplace-vendordata-main-sidebar--hidden">
161
257
  <div className="legend-marketplace-vendordata-main-sidebar__title">
162
258
  Filters
163
259
  </div>
@@ -225,6 +321,14 @@ export const VendorDataMainContent = observer(
225
321
  />
226
322
  )}
227
323
  </div>
324
+ {(marketPlaceVendorDataState.providerDisplayState ===
325
+ VendorDataProviderType.TERMINAL_LICENSE ||
326
+ marketPlaceVendorDataState.providerDisplayState ===
327
+ VendorDataProviderType.ADD_ONS) && (
328
+ <PaginationControls
329
+ vendorDataState={marketPlaceVendorDataState}
330
+ />
331
+ )}
228
332
  </>
229
333
  )}
230
334
  </div>
@@ -236,11 +340,27 @@ export const LegendMarketplaceVendorData = withLegendMarketplaceVendorDataStore(
236
340
  observer(() => {
237
341
  const marketPlaceVendorDataStore = useLegendMarketPlaceVendorDataStore();
238
342
 
239
- const onChange = (query: string | undefined) => {
240
- marketPlaceVendorDataStore.setProvidersFilters([
241
- { label: 'query', value: query },
242
- ] as Filter[]);
243
- };
343
+ const handleSearch = useCallback(
344
+ (query: string | undefined) => {
345
+ marketPlaceVendorDataStore.setSearchTerm(query ?? '');
346
+ flowResult(marketPlaceVendorDataStore.populateProviders()).catch(
347
+ marketPlaceVendorDataStore.applicationStore.alertUnhandledError,
348
+ );
349
+ },
350
+ [marketPlaceVendorDataStore],
351
+ );
352
+
353
+ const handleSearchChange = useCallback(
354
+ (query: string) => {
355
+ if (query === '') {
356
+ marketPlaceVendorDataStore.setSearchTerm('');
357
+ flowResult(marketPlaceVendorDataStore.populateProviders()).catch(
358
+ marketPlaceVendorDataStore.applicationStore.alertUnhandledError,
359
+ );
360
+ }
361
+ },
362
+ [marketPlaceVendorDataStore],
363
+ );
244
364
 
245
365
  useEffect(() => {
246
366
  marketPlaceVendorDataStore.init();
@@ -250,7 +370,10 @@ export const LegendMarketplaceVendorData = withLegendMarketplaceVendorDataStore(
250
370
  <LegendMarketplacePage className="legend-marketplace-vendor-data">
251
371
  <div className="legend-marketplace-banner">
252
372
  <div className="legend-marketplace-banner__search-bar">
253
- <LegendMarketplaceSearchBar onSearch={onChange} />
373
+ <LegendMarketplaceSearchBar
374
+ onSearch={handleSearch}
375
+ onChange={handleSearchChange}
376
+ />
254
377
  </div>
255
378
  </div>
256
379
 
@@ -18,14 +18,20 @@ import {
18
18
  TerminalResult,
19
19
  type Filter,
20
20
  type MarketplaceServerClient,
21
+ type TerminalServicesResponse,
22
+ ProductType,
23
+ type FetchProductsParams,
21
24
  } from '@finos/legend-server-marketplace';
22
- import { action, flow, makeObservable, observable } from 'mobx';
25
+ import { action, flow, flowResult, makeObservable, observable } from 'mobx';
23
26
  import type {
24
27
  LegendMarketplaceApplicationStore,
25
28
  LegendMarketplaceBaseStore,
26
29
  } from './LegendMarketplaceBaseStore.js';
27
- import { ActionState, type GeneratorFn } from '@finos/legend-shared';
28
- import { toastManager } from '../components/Toast/CartToast.js';
30
+ import {
31
+ ActionState,
32
+ type GeneratorFn,
33
+ assertErrorThrown,
34
+ } from '@finos/legend-shared';
29
35
 
30
36
  export enum VendorDataProviderType {
31
37
  ALL = 'All',
@@ -38,17 +44,22 @@ export class LegendMarketPlaceVendorDataStore {
38
44
  readonly store: LegendMarketplaceBaseStore;
39
45
  marketplaceServerClient: MarketplaceServerClient;
40
46
 
41
- responseLimit = 6;
42
-
43
47
  currentUser = '';
44
48
 
45
49
  readonly fetchingProvidersState = ActionState.create();
46
50
 
47
- //Vendor Data Page
48
51
  terminalProviders: TerminalResult[] = [];
49
52
  addOnProviders: TerminalResult[] = [];
50
53
  providers: TerminalResult[] = [];
51
54
 
55
+ page = 1;
56
+ itemsPerPage = 24;
57
+ totalTerminalItems = 0;
58
+ totalAddOnItems = 0;
59
+ totalItems = 0;
60
+
61
+ searchTerm = '';
62
+
52
63
  providersFilters: Filter[] = [];
53
64
 
54
65
  providerDisplayState: VendorDataProviderType = VendorDataProviderType.ALL;
@@ -60,14 +71,22 @@ export class LegendMarketPlaceVendorDataStore {
60
71
  makeObservable(this, {
61
72
  terminalProviders: observable,
62
73
  addOnProviders: observable,
63
- populateProviders: action,
64
- providerDisplayState: observable,
65
- setProviderDisplayState: action,
66
74
  providers: observable,
67
- setProviders: action,
68
- init: flow,
75
+ page: observable,
76
+ itemsPerPage: observable,
77
+ totalTerminalItems: observable,
78
+ totalAddOnItems: observable,
79
+ totalItems: observable,
80
+ searchTerm: observable,
81
+ providerDisplayState: observable,
69
82
  providersFilters: observable,
83
+ setProviderDisplayState: action,
70
84
  setProvidersFilters: action,
85
+ setPage: action,
86
+ setItemsPerPage: action,
87
+ setSearchTerm: action,
88
+ init: flow,
89
+ populateProviders: flow,
71
90
  });
72
91
 
73
92
  this.applicationStore = applicationStore;
@@ -85,101 +104,112 @@ export class LegendMarketPlaceVendorDataStore {
85
104
  }
86
105
 
87
106
  try {
88
- yield this.populateProviders();
107
+ yield flowResult(this.populateProviders());
89
108
  } catch (error) {
109
+ assertErrorThrown(error);
90
110
  this.applicationStore.notificationService.notifyError(
91
- `Failed to initialize vendors: ${error}`,
111
+ `Failed to initialize vendors: ${error.message}`,
92
112
  );
93
113
  }
94
114
  }
95
115
 
96
116
  setProviderDisplayState(value: VendorDataProviderType): void {
97
117
  this.providerDisplayState = value;
118
+ this.page = 1;
98
119
  }
99
120
 
100
121
  setProvidersFilters(value: Filter[]): void {
101
122
  this.providersFilters = value;
102
- this.populateData();
123
+ this.page = 1;
103
124
  }
104
125
 
105
- populateData(): void {
106
- this.populateProviders()
107
- .then(() =>
108
- this.applicationStore.notificationService.notifySuccess(
109
- 'Data populated successfully.',
110
- ),
111
- )
112
- .catch((error: Error) =>
113
- this.applicationStore.notificationService.notifyError(
114
- `Failed to populate Data: ${
115
- error instanceof Error ? error.message : String(error)
116
- }`,
117
- ),
118
- );
126
+ setPage(value: number): void {
127
+ this.page = value;
119
128
  }
120
129
 
121
- async populateProviders(): Promise<void> {
122
- try {
123
- const filters: string = this.providersFilters
124
- .map((filter) => `&${filter.label}=${encodeURIComponent(filter.value)}`)
125
- .join('');
130
+ setItemsPerPage(value: number): void {
131
+ this.itemsPerPage = value;
132
+ this.page = 1;
133
+ }
126
134
 
135
+ setSearchTerm(value: string): void {
136
+ this.searchTerm = value;
137
+ this.page = 1;
138
+ }
139
+
140
+ *populateProviders(): GeneratorFn<void> {
141
+ try {
127
142
  this.fetchingProvidersState.inProgress();
128
143
 
129
- this.terminalProviders = (
130
- await this.marketplaceServerClient.getVendorsByCategory(
131
- this.currentUser,
132
- encodeURIComponent('desktop'),
133
- 'landing',
134
- filters,
135
- this.responseLimit,
136
- )
137
- ).map((json) => {
138
- return TerminalResult.serialization.fromJson(json);
139
- });
140
-
141
- this.addOnProviders = (
142
- await this.marketplaceServerClient.getVendorsByCategory(
143
- this.currentUser,
144
- encodeURIComponent('addon'),
145
- 'landing',
146
- filters,
147
- this.responseLimit,
148
- )
149
- ).map((json) => TerminalResult.serialization.fromJson(json));
144
+ if (this.providerDisplayState === VendorDataProviderType.ALL) {
145
+ const params: FetchProductsParams = {
146
+ kerberos: this.currentUser,
147
+ product_type: ProductType.ALL,
148
+ preferred_products: true,
149
+ page_size: this.itemsPerPage,
150
+ search: this.searchTerm,
151
+ };
152
+ const response = (yield this.marketplaceServerClient.fetchProducts(
153
+ params,
154
+ )) as TerminalServicesResponse;
155
+
156
+ this.terminalProviders = (response.vendor_profiles ?? []).map((json) =>
157
+ TerminalResult.serialization.fromJson(json),
158
+ );
159
+
160
+ this.addOnProviders = (response.service_pricing ?? []).map((json) =>
161
+ TerminalResult.serialization.fromJson(json),
162
+ );
163
+
164
+ this.totalTerminalItems = response.vendor_profiles_total_count ?? 0;
165
+ this.totalAddOnItems = response.service_pricing_total_count ?? 0;
166
+ } else if (
167
+ this.providerDisplayState === VendorDataProviderType.TERMINAL_LICENSE
168
+ ) {
169
+ const params: FetchProductsParams = {
170
+ kerberos: this.currentUser,
171
+ product_type: ProductType.VENDOR_PROFILE,
172
+ preferred_products: false,
173
+ page_size: this.itemsPerPage,
174
+ search: this.searchTerm,
175
+ page_number: this.page,
176
+ };
177
+ const response = (yield this.marketplaceServerClient.fetchProducts(
178
+ params,
179
+ )) as TerminalServicesResponse;
180
+
181
+ this.providers = (response.vendor_profiles ?? []).map((json) =>
182
+ TerminalResult.serialization.fromJson(json),
183
+ );
184
+
185
+ this.totalItems = response.total_count ?? 0;
186
+ } else {
187
+ const params: FetchProductsParams = {
188
+ kerberos: this.currentUser,
189
+ product_type: ProductType.SERVICE_PRICING,
190
+ preferred_products: false,
191
+ page_size: this.itemsPerPage,
192
+ search: this.searchTerm,
193
+ page_number: this.page,
194
+ };
195
+ const response = (yield this.marketplaceServerClient.fetchProducts(
196
+ params,
197
+ )) as TerminalServicesResponse;
198
+
199
+ this.providers = (response.service_pricing ?? []).map((json) =>
200
+ TerminalResult.serialization.fromJson(json),
201
+ );
202
+
203
+ this.totalItems = response.total_count ?? 0;
204
+ }
205
+
206
+ this.fetchingProvidersState.complete();
150
207
  } catch (error) {
208
+ assertErrorThrown(error);
151
209
  this.applicationStore.notificationService.notifyError(
152
- `Failed to fetch vendors: ${error}`,
210
+ `Failed to fetch vendors: ${error.message}`,
153
211
  );
154
- } finally {
155
- this.fetchingProvidersState.complete();
212
+ this.fetchingProvidersState.fail();
156
213
  }
157
214
  }
158
-
159
- setProviders(category: string): void {
160
- this.providers = [];
161
- const filters: string = this.providersFilters
162
- .map((filter) => `&${filter.label}=${encodeURIComponent(filter.value)}`)
163
- .join('');
164
-
165
- this.fetchingProvidersState.inProgress();
166
- this.marketplaceServerClient
167
- .getVendorsByCategory(
168
- this.currentUser,
169
- encodeURIComponent(category),
170
- 'list',
171
- filters,
172
- this.responseLimit,
173
- )
174
- .then((response) => {
175
- this.providers = response.map((json) => {
176
- return TerminalResult.serialization.fromJson(json);
177
- });
178
- this.fetchingProvidersState.complete();
179
- })
180
- .catch((error) => {
181
- toastManager.error(`Failed to fetch vendors: ${error.message}`);
182
- this.fetchingProvidersState.fail();
183
- });
184
- }
185
215
  }
@@ -145,7 +145,8 @@ export class CartStore {
145
145
  price: provider.price,
146
146
  description: provider.description,
147
147
  isOwned: provider.isOwned ? 'true' : 'false',
148
- vendorProfileId: provider.vendorProfileId ?? provider.id,
148
+ model: provider.model ?? provider.productName,
149
+ skipWorkflow: provider.skipWorkflow ?? false,
149
150
  };
150
151
  }
151
152
 
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Copyright (c) 2025-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import type { TerminalProductOrder } from '@finos/legend-server-marketplace';
18
+
19
+ export enum WorkflowStage {
20
+ ORDER_PLACED = 'Order Placed',
21
+ MANAGER_APPROVAL = 'Manager Approval',
22
+ BUSINESS_ANALYST_APPROVAL = 'Business Analyst Approval',
23
+ PENDING_FULFILLMENT = 'Pending Fulfillment',
24
+ CANCELLED = 'Cancelled',
25
+ }
26
+
27
+ export enum WorkflowStatus {
28
+ COMPLETED = 'COMPLETED',
29
+ }
30
+
31
+ export enum OrderType {
32
+ CANCELLATION = 'CANCELLATION',
33
+ }
34
+
35
+ export enum WorkflowCurrentStage {
36
+ DIRECT_MANAGER = 'DIRECT MANAGER',
37
+ BUSINESS_ANALYST = 'Business Analyst',
38
+ RPM = 'RPM',
39
+ }
40
+
41
+ export enum RejectedActionStatus {
42
+ REJECTED = 'rejected',
43
+ CANCELLED = 'cancelled',
44
+ AUTO_CANCELLED = 'auto cancelled',
45
+ DENIED = 'denied',
46
+ }
47
+
48
+ export const STAGE_MAP: Record<WorkflowCurrentStage, WorkflowStage> = {
49
+ [WorkflowCurrentStage.DIRECT_MANAGER]: WorkflowStage.MANAGER_APPROVAL,
50
+ [WorkflowCurrentStage.BUSINESS_ANALYST]:
51
+ WorkflowStage.BUSINESS_ANALYST_APPROVAL,
52
+ [WorkflowCurrentStage.RPM]: WorkflowStage.PENDING_FULFILLMENT,
53
+ };
54
+
55
+ export const getWorkflowSteps = (
56
+ order: TerminalProductOrder,
57
+ ): WorkflowStage[] => {
58
+ if (order.order_type.toUpperCase() === OrderType.CANCELLATION) {
59
+ return [
60
+ WorkflowStage.ORDER_PLACED,
61
+ WorkflowStage.MANAGER_APPROVAL,
62
+ WorkflowStage.CANCELLED,
63
+ ];
64
+ }
65
+
66
+ return [
67
+ WorkflowStage.ORDER_PLACED,
68
+ WorkflowStage.MANAGER_APPROVAL,
69
+ WorkflowStage.PENDING_FULFILLMENT,
70
+ ];
71
+ };
72
+
73
+ export const getProcessInstanceId = (
74
+ order: TerminalProductOrder,
75
+ ): string | null => {
76
+ if (!order.workflow_details) {
77
+ return null;
78
+ }
79
+
80
+ if (
81
+ order.workflow_details.current_stage === WorkflowCurrentStage.DIRECT_MANAGER
82
+ ) {
83
+ return order.workflow_details.manager_process_id;
84
+ } else if (
85
+ order.workflow_details.current_stage ===
86
+ WorkflowCurrentStage.BUSINESS_ANALYST
87
+ ) {
88
+ return order.workflow_details.bbg_approval_process_id;
89
+ }
90
+ return null;
91
+ };
92
+
93
+ export const canCancelOrder = (order: TerminalProductOrder): boolean => {
94
+ const currentStage = order.workflow_details?.current_stage;
95
+ return (
96
+ currentStage === WorkflowCurrentStage.DIRECT_MANAGER ||
97
+ currentStage === WorkflowCurrentStage.BUSINESS_ANALYST
98
+ );
99
+ };
100
+
101
+ export const isStageCompleted = (
102
+ order: TerminalProductOrder,
103
+ stageName: string,
104
+ ): boolean => {
105
+ if (!order.workflow_details) {
106
+ return false;
107
+ }
108
+
109
+ if (stageName === WorkflowStage.MANAGER_APPROVAL) {
110
+ return !!order.workflow_details.manager_actioned_by;
111
+ } else if (stageName === WorkflowStage.BUSINESS_ANALYST_APPROVAL) {
112
+ return !!order.workflow_details.bbg_approval_actioned_by;
113
+ }
114
+ return false;
115
+ };
116
+
117
+ export const isStageRejected = (
118
+ order: TerminalProductOrder,
119
+ stageName: string,
120
+ ): boolean => {
121
+ if (!order.workflow_details) {
122
+ return false;
123
+ }
124
+
125
+ const rejectedStatuses = Object.values(RejectedActionStatus);
126
+
127
+ if (stageName === WorkflowStage.MANAGER_APPROVAL) {
128
+ return rejectedStatuses.some((status) =>
129
+ order.workflow_details?.manager_action
130
+ ?.toLowerCase()
131
+ .includes(status.toLowerCase()),
132
+ );
133
+ } else if (stageName === WorkflowStage.BUSINESS_ANALYST_APPROVAL) {
134
+ return rejectedStatuses.some((status) =>
135
+ order.workflow_details?.bbg_approval_action
136
+ ?.toLowerCase()
137
+ .includes(status.toLowerCase()),
138
+ );
139
+ } else if (stageName === WorkflowStage.PENDING_FULFILLMENT) {
140
+ return rejectedStatuses.some((status) =>
141
+ order.workflow_details?.rpm_action
142
+ ?.toLowerCase()
143
+ .includes(status.toLowerCase()),
144
+ );
145
+ }
146
+ return false;
147
+ };
148
+
149
+ export const formatOrderDate = (dateString: string): string => {
150
+ const date = new Date(dateString);
151
+ return date.toLocaleDateString('en-US', {
152
+ year: 'numeric',
153
+ month: 'short',
154
+ day: 'numeric',
155
+ });
156
+ };
157
+
158
+ export const formatTimestamp = (timestamp: string): string => {
159
+ const date = new Date(timestamp);
160
+ return date.toLocaleString('en-US', {
161
+ year: 'numeric',
162
+ month: 'short',
163
+ day: 'numeric',
164
+ hour: '2-digit',
165
+ minute: '2-digit',
166
+ });
167
+ };