@finos/legend-application-marketplace 0.1.50 → 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.
- package/lib/__lib__/LegendMarketplaceAppEvent.d.ts +2 -1
- package/lib/__lib__/LegendMarketplaceAppEvent.d.ts.map +1 -1
- package/lib/__lib__/LegendMarketplaceAppEvent.js +1 -0
- package/lib/__lib__/LegendMarketplaceAppEvent.js.map +1 -1
- package/lib/application/LegendMarketplaceApplicationPlugin.d.ts +5 -1
- package/lib/application/LegendMarketplaceApplicationPlugin.d.ts.map +1 -1
- package/lib/application/LegendMarketplaceApplicationPlugin.js.map +1 -1
- package/lib/components/AddToCart/RecommendedAddOnsModal.d.ts.map +1 -1
- package/lib/components/AddToCart/RecommendedAddOnsModal.js +32 -6
- package/lib/components/AddToCart/RecommendedAddOnsModal.js.map +1 -1
- package/lib/components/AddToCart/RecommendedItemsCard.d.ts +0 -1
- package/lib/components/AddToCart/RecommendedItemsCard.d.ts.map +1 -1
- package/lib/components/AddToCart/RecommendedItemsCard.js +12 -17
- package/lib/components/AddToCart/RecommendedItemsCard.js.map +1 -1
- package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.d.ts.map +1 -1
- package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.js +2 -2
- package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.js.map +1 -1
- package/lib/components/orders/CancelOrderDialog.d.ts +27 -0
- package/lib/components/orders/CancelOrderDialog.d.ts.map +1 -0
- package/lib/components/orders/CancelOrderDialog.js +52 -0
- package/lib/components/orders/CancelOrderDialog.js.map +1 -0
- package/lib/components/orders/ProgressTracker.d.ts +23 -0
- package/lib/components/orders/ProgressTracker.d.ts.map +1 -0
- package/lib/components/orders/ProgressTracker.js +116 -0
- package/lib/components/orders/ProgressTracker.js.map +1 -0
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/pages/Profile/LegendMarketplaceYourOrders.d.ts.map +1 -1
- package/lib/pages/Profile/LegendMarketplaceYourOrders.js +43 -36
- package/lib/pages/Profile/LegendMarketplaceYourOrders.js.map +1 -1
- package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.d.ts.map +1 -1
- package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js +42 -18
- package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js.map +1 -1
- package/lib/stores/LegendMarketPlaceVendorDataStore.d.ts +10 -4
- package/lib/stores/LegendMarketPlaceVendorDataStore.d.ts.map +1 -1
- package/lib/stores/LegendMarketPlaceVendorDataStore.js +82 -49
- package/lib/stores/LegendMarketPlaceVendorDataStore.js.map +1 -1
- package/lib/stores/cart/CartStore.d.ts.map +1 -1
- package/lib/stores/cart/CartStore.js +2 -1
- package/lib/stores/cart/CartStore.js.map +1 -1
- package/lib/stores/orders/OrderHelpers.d.ts +49 -0
- package/lib/stores/orders/OrderHelpers.d.ts.map +1 -0
- package/lib/stores/orders/OrderHelpers.js +134 -0
- package/lib/stores/orders/OrderHelpers.js.map +1 -0
- package/lib/stores/orders/OrderStore.d.ts +2 -0
- package/lib/stores/orders/OrderStore.d.ts.map +1 -1
- package/lib/stores/orders/OrderStore.js +31 -0
- package/lib/stores/orders/OrderStore.js.map +1 -1
- package/package.json +7 -7
- package/src/__lib__/LegendMarketplaceAppEvent.ts +1 -0
- package/src/application/LegendMarketplaceApplicationPlugin.ts +6 -0
- package/src/components/AddToCart/RecommendedAddOnsModal.tsx +145 -17
- package/src/components/AddToCart/RecommendedItemsCard.tsx +32 -62
- package/src/components/ProviderCard/LegendMarketplaceTerminalCard.tsx +18 -2
- package/src/components/orders/CancelOrderDialog.tsx +157 -0
- package/src/components/orders/ProgressTracker.tsx +266 -0
- package/src/pages/Profile/LegendMarketplaceYourOrders.tsx +227 -230
- package/src/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.tsx +140 -17
- package/src/stores/LegendMarketPlaceVendorDataStore.tsx +114 -84
- package/src/stores/cart/CartStore.ts +2 -1
- package/src/stores/orders/OrderHelpers.ts +167 -0
- package/src/stores/orders/OrderStore.ts +51 -0
- 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 {
|
|
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 = (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
vendorDataState.
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
|
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 {
|
|
28
|
-
|
|
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
|
-
|
|
68
|
-
|
|
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.
|
|
123
|
+
this.page = 1;
|
|
103
124
|
}
|
|
104
125
|
|
|
105
|
-
|
|
106
|
-
this.
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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.
|
|
130
|
-
|
|
131
|
-
this.currentUser,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
this.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
};
|