@finos/legend-application-marketplace 0.2.20 → 0.2.22

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 (106) hide show
  1. package/lib/__lib__/LegendMarketplaceNavigation.d.ts +1 -0
  2. package/lib/__lib__/LegendMarketplaceNavigation.d.ts.map +1 -1
  3. package/lib/__lib__/LegendMarketplaceNavigation.js +4 -0
  4. package/lib/__lib__/LegendMarketplaceNavigation.js.map +1 -1
  5. package/lib/application/LegendMarketplaceApplicationConfig.d.ts +6 -3
  6. package/lib/application/LegendMarketplaceApplicationConfig.d.ts.map +1 -1
  7. package/lib/application/LegendMarketplaceApplicationConfig.js +13 -4
  8. package/lib/application/LegendMarketplaceApplicationConfig.js.map +1 -1
  9. package/lib/components/OptionSelector/LegendMarketplaceOptionSelector.d.ts +22 -0
  10. package/lib/components/OptionSelector/LegendMarketplaceOptionSelector.d.ts.map +1 -0
  11. package/lib/components/OptionSelector/LegendMarketplaceOptionSelector.js +28 -0
  12. package/lib/components/OptionSelector/LegendMarketplaceOptionSelector.js.map +1 -0
  13. package/lib/components/Pagination/PaginationControls.d.ts.map +1 -1
  14. package/lib/components/Pagination/PaginationControls.js +2 -2
  15. package/lib/components/Pagination/PaginationControls.js.map +1 -1
  16. package/lib/components/ProviderCard/LegendMarketplaceOrderProfileCard.d.ts +23 -0
  17. package/lib/components/ProviderCard/LegendMarketplaceOrderProfileCard.d.ts.map +1 -0
  18. package/lib/components/ProviderCard/LegendMarketplaceOrderProfileCard.js +93 -0
  19. package/lib/components/ProviderCard/LegendMarketplaceOrderProfileCard.js.map +1 -0
  20. package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.d.ts.map +1 -1
  21. package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.js +5 -11
  22. package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.js.map +1 -1
  23. package/lib/components/ProviderCard/OrderProfileDetailModal.d.ts +26 -0
  24. package/lib/components/ProviderCard/OrderProfileDetailModal.d.ts.map +1 -0
  25. package/lib/components/ProviderCard/OrderProfileDetailModal.js +36 -0
  26. package/lib/components/ProviderCard/OrderProfileDetailModal.js.map +1 -0
  27. package/lib/components/ProviderCard/OrderProfileMultiselectModal.d.ts +26 -0
  28. package/lib/components/ProviderCard/OrderProfileMultiselectModal.d.ts.map +1 -0
  29. package/lib/components/ProviderCard/OrderProfileMultiselectModal.js +31 -0
  30. package/lib/components/ProviderCard/OrderProfileMultiselectModal.js.map +1 -0
  31. package/lib/components/ProviderCard/orderProfileUtils.d.ts +71 -0
  32. package/lib/components/ProviderCard/orderProfileUtils.d.ts.map +1 -0
  33. package/lib/components/ProviderCard/orderProfileUtils.js +122 -0
  34. package/lib/components/ProviderCard/orderProfileUtils.js.map +1 -0
  35. package/lib/index.css +2 -2
  36. package/lib/index.css.map +1 -1
  37. package/lib/package.json +1 -1
  38. package/lib/pages/Lakehouse/entitlements/LakehouseDataContract.d.ts.map +1 -1
  39. package/lib/pages/Lakehouse/entitlements/LakehouseDataContract.js +2 -2
  40. package/lib/pages/Lakehouse/entitlements/LakehouseDataContract.js.map +1 -1
  41. package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.d.ts.map +1 -1
  42. package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.js +5 -2
  43. package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.js.map +1 -1
  44. package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.d.ts.map +1 -1
  45. package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.js +2 -2
  46. package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.js.map +1 -1
  47. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.d.ts.map +1 -1
  48. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js +11 -2
  49. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js.map +1 -1
  50. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.d.ts.map +1 -1
  51. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js +9 -3
  52. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js.map +1 -1
  53. package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.d.ts.map +1 -1
  54. package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js +45 -18
  55. package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js.map +1 -1
  56. package/lib/stores/LegendMarketPlaceVendorDataStore.d.ts +6 -2
  57. package/lib/stores/LegendMarketPlaceVendorDataStore.d.ts.map +1 -1
  58. package/lib/stores/LegendMarketPlaceVendorDataStore.js +25 -2
  59. package/lib/stores/LegendMarketPlaceVendorDataStore.js.map +1 -1
  60. package/lib/stores/LegendMarketplaceBaseStore.d.ts +1 -1
  61. package/lib/stores/LegendMarketplaceBaseStore.d.ts.map +1 -1
  62. package/lib/stores/LegendMarketplaceBaseStore.js +10 -5
  63. package/lib/stores/LegendMarketplaceBaseStore.js.map +1 -1
  64. package/lib/stores/ai/LegendMarketplaceAIChatStore.d.ts +10 -0
  65. package/lib/stores/ai/LegendMarketplaceAIChatStore.d.ts.map +1 -1
  66. package/lib/stores/ai/LegendMarketplaceAIChatStore.js +115 -50
  67. package/lib/stores/ai/LegendMarketplaceAIChatStore.js.map +1 -1
  68. package/lib/stores/cart/CartStore.d.ts +14 -2
  69. package/lib/stores/cart/CartStore.d.ts.map +1 -1
  70. package/lib/stores/cart/CartStore.js +68 -5
  71. package/lib/stores/cart/CartStore.js.map +1 -1
  72. package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.d.ts +4 -0
  73. package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.d.ts.map +1 -1
  74. package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.js +5 -0
  75. package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.js.map +1 -1
  76. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts +2 -1
  77. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts.map +1 -1
  78. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js +8 -3
  79. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js.map +1 -1
  80. package/lib/utils/SearchUtils.d.ts.map +1 -1
  81. package/lib/utils/SearchUtils.js +17 -2
  82. package/lib/utils/SearchUtils.js.map +1 -1
  83. package/package.json +11 -11
  84. package/src/__lib__/LegendMarketplaceNavigation.ts +9 -0
  85. package/src/application/LegendMarketplaceApplicationConfig.ts +19 -11
  86. package/src/components/OptionSelector/LegendMarketplaceOptionSelector.tsx +50 -0
  87. package/src/components/Pagination/PaginationControls.tsx +19 -17
  88. package/src/components/ProviderCard/LegendMarketplaceOrderProfileCard.tsx +246 -0
  89. package/src/components/ProviderCard/LegendMarketplaceTerminalCard.tsx +9 -16
  90. package/src/components/ProviderCard/OrderProfileDetailModal.tsx +224 -0
  91. package/src/components/ProviderCard/OrderProfileMultiselectModal.tsx +142 -0
  92. package/src/components/ProviderCard/orderProfileUtils.ts +165 -0
  93. package/src/pages/Lakehouse/entitlements/LakehouseDataContract.tsx +4 -0
  94. package/src/pages/Lakehouse/entitlements/PermitDataAccessRequest.tsx +7 -0
  95. package/src/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.tsx +4 -0
  96. package/src/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.tsx +24 -25
  97. package/src/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.tsx +19 -29
  98. package/src/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.tsx +177 -44
  99. package/src/stores/LegendMarketPlaceVendorDataStore.tsx +33 -1
  100. package/src/stores/LegendMarketplaceBaseStore.ts +13 -9
  101. package/src/stores/ai/LegendMarketplaceAIChatStore.ts +273 -69
  102. package/src/stores/cart/CartStore.ts +90 -4
  103. package/src/stores/lakehouse/LegendMarketplaceSearchResultsStore.ts +5 -0
  104. package/src/stores/lakehouse/entitlements/EntitlementsDashboardState.ts +10 -1
  105. package/src/utils/SearchUtils.tsx +33 -8
  106. package/tsconfig.json +5 -0
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Copyright (c) 2020-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 { Button, ButtonGroup } from '@mui/material';
18
+
19
+ export const LegendMarketplaceOptionSelector = <T extends string>(props: {
20
+ options: readonly T[];
21
+ selectedOption: T;
22
+ onChange: (value: T) => void;
23
+ ariaLabel: string;
24
+ }): React.ReactElement => {
25
+ const { options, selectedOption, onChange, ariaLabel } = props;
26
+
27
+ return (
28
+ <ButtonGroup variant="outlined" role="radiogroup" aria-label={ariaLabel}>
29
+ {options.map((option) => {
30
+ const isSelected = selectedOption === option;
31
+ return (
32
+ <Button
33
+ key={option}
34
+ onClick={() => onChange(option)}
35
+ variant={isSelected ? 'contained' : 'outlined'}
36
+ role="radio"
37
+ aria-checked={isSelected}
38
+ tabIndex={isSelected ? 0 : -1}
39
+ sx={{
40
+ fontSize: '12px',
41
+ backgroundColor: isSelected ? 'primary' : 'white',
42
+ }}
43
+ >
44
+ {option}
45
+ </Button>
46
+ );
47
+ })}
48
+ </ButtonGroup>
49
+ );
50
+ };
@@ -93,23 +93,25 @@ export const PaginationControls = observer(
93
93
  </Box>
94
94
 
95
95
  <Box className="legend-marketplace-pagination-controls">
96
- <Pagination
97
- count={totalPages}
98
- page={page}
99
- onChange={handlePageChange}
100
- color="primary"
101
- showFirstButton={true}
102
- showLastButton={true}
103
- disabled={disabled}
104
- siblingCount={1}
105
- boundaryCount={2}
106
- size="large"
107
- sx={{
108
- '& .MuiPaginationItem-root': {
109
- fontSize: '1.5rem',
110
- },
111
- }}
112
- />
96
+ {totalPages > 1 && (
97
+ <Pagination
98
+ count={totalPages}
99
+ page={page}
100
+ onChange={handlePageChange}
101
+ color="primary"
102
+ showFirstButton={true}
103
+ showLastButton={true}
104
+ disabled={disabled}
105
+ siblingCount={1}
106
+ boundaryCount={2}
107
+ size="large"
108
+ sx={{
109
+ '& .MuiPaginationItem-root': {
110
+ fontSize: '1.5rem',
111
+ },
112
+ }}
113
+ />
114
+ )}
113
115
  </Box>
114
116
  </Box>
115
117
  );
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Copyright (c) 2026-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 JSX, useState } from 'react';
18
+ import {
19
+ Box,
20
+ Button,
21
+ Card,
22
+ CardActionArea,
23
+ CardActions,
24
+ CardContent,
25
+ CardMedia,
26
+ Chip,
27
+ CircularProgress,
28
+ IconButton,
29
+ Typography,
30
+ } from '@mui/material';
31
+ import {
32
+ CheckCircleIcon,
33
+ InfoCircleIcon,
34
+ ShoppingCartIcon,
35
+ } from '@finos/legend-art';
36
+ import type {
37
+ TraderProfile,
38
+ TraderProfileItem,
39
+ } from '@finos/legend-server-marketplace';
40
+ import { useLegendMarketplaceBaseStore } from '../../application/providers/LegendMarketplaceFrameworkProvider.js';
41
+ import { observer } from 'mobx-react-lite';
42
+ import { flowResult } from 'mobx';
43
+ import { toastManager } from '../Toast/CartToast.js';
44
+ import { assertErrorThrown } from '@finos/legend-shared';
45
+ import { OrderProfileDetailModal } from './OrderProfileDetailModal.js';
46
+ import { OrderProfileMultiselectModal } from './OrderProfileMultiselectModal.js';
47
+ import {
48
+ calculateMultiselectTotalPrice,
49
+ formatAddToCartErrorMessage,
50
+ formatAddToCartSuccessMessage,
51
+ formatCardPrice,
52
+ formatProfileSummaryLine,
53
+ getItemSummary,
54
+ getRandomImageUrl,
55
+ OrderProfileLabel,
56
+ } from './orderProfileUtils.js';
57
+
58
+ export const LegendMarketplaceOrderProfileCard = observer(
59
+ (props: { traderProfile: TraderProfile }): JSX.Element => {
60
+ const { traderProfile } = props;
61
+ const [isAddingToCart, setIsAddingToCart] = useState(false);
62
+ const [showDetailModal, setShowDetailModal] = useState(false);
63
+ const [showMultiselectModal, setShowMultiselectModal] = useState(false);
64
+
65
+ const legendMarketplaceBaseStore = useLegendMarketplaceBaseStore();
66
+ const { cartStore, applicationStore } = legendMarketplaceBaseStore;
67
+ const assetUrl = applicationStore.config.assetsBaseUrl;
68
+
69
+ const [imageUrl] = useState(() => getRandomImageUrl(assetUrl));
70
+
71
+ const items = traderProfile.items;
72
+ const { terminalCount, addOnCount } = getItemSummary(items);
73
+
74
+ const multiselectTotalPrice = traderProfile.multiselect
75
+ ? calculateMultiselectTotalPrice(items)
76
+ : undefined;
77
+
78
+ const displayPrice = traderProfile.multiselect
79
+ ? (multiselectTotalPrice ?? traderProfile.price)
80
+ : traderProfile.price;
81
+
82
+ const isInCart = cartStore.isOrderProfileInCart(traderProfile);
83
+
84
+ const executeCartAction = async (
85
+ action: () => Promise<void>,
86
+ ): Promise<void> => {
87
+ setIsAddingToCart(true);
88
+ try {
89
+ await action();
90
+ toastManager.success(
91
+ formatAddToCartSuccessMessage(traderProfile.productName),
92
+ );
93
+ } catch (error) {
94
+ assertErrorThrown(error);
95
+ toastManager.error(
96
+ formatAddToCartErrorMessage(traderProfile.productName, error.message),
97
+ );
98
+ } finally {
99
+ setIsAddingToCart(false);
100
+ }
101
+ };
102
+
103
+ const handleAddToCart = (): void => {
104
+ if (traderProfile.isOwned) {
105
+ return;
106
+ }
107
+ if (traderProfile.multiselect) {
108
+ setShowMultiselectModal(true);
109
+ return;
110
+ }
111
+ const terminals = items.filter((item) => item.isTerminal);
112
+ const addOns = items.filter((item) => !item.isTerminal);
113
+ executeCartAction(async () => {
114
+ await flowResult(cartStore.addOrderProfileItemsToCart(terminals, true));
115
+ await flowResult(cartStore.addOrderProfileItemsToCart(addOns, true));
116
+ }).catch(applicationStore.alertUnhandledError);
117
+ };
118
+
119
+ const handleMultiselectConfirm = (
120
+ selectedTerminals: TraderProfileItem[],
121
+ ): void => {
122
+ setShowMultiselectModal(false);
123
+ const selectedModel = selectedTerminals[0]?.model ?? null;
124
+ const addOnItems = items.filter(
125
+ (item) =>
126
+ !item.isTerminal &&
127
+ !item.isOwned &&
128
+ (selectedModel === null || item.model === selectedModel),
129
+ );
130
+ executeCartAction(async () => {
131
+ await flowResult(
132
+ cartStore.addOrderProfileItemsToCart(selectedTerminals, true),
133
+ );
134
+ await flowResult(
135
+ cartStore.addOrderProfileItemsToCart(addOnItems, true),
136
+ );
137
+ }).catch(applicationStore.alertUnhandledError);
138
+ };
139
+
140
+ return (
141
+ <>
142
+ <Card className="legend-marketplace-terminal-card legend-marketplace-order-profile-card">
143
+ <CardActionArea className="legend-marketplace-terminal-card__action">
144
+ <CardMedia
145
+ component="img"
146
+ className="legend-marketplace-terminal-card__image"
147
+ height="140"
148
+ image={imageUrl}
149
+ alt="order profile"
150
+ />
151
+ <Chip
152
+ label={OrderProfileLabel.CHIP_LABEL}
153
+ className="legend-marketplace-terminal-card__category-chip"
154
+ />
155
+ <CardContent className="legend-marketplace-terminal-card__content">
156
+ <Typography
157
+ variant="subtitle2"
158
+ className="legend-marketplace-terminal-card__provider legend-marketplace-order-profile-card__summary"
159
+ >
160
+ {formatProfileSummaryLine(terminalCount, addOnCount)}
161
+ </Typography>
162
+ <Box className="legend-marketplace-order-profile-card__title-row">
163
+ <Typography
164
+ variant="h6"
165
+ className="legend-marketplace-terminal-card__title"
166
+ >
167
+ {traderProfile.productName.toUpperCase()}
168
+ </Typography>
169
+ <IconButton
170
+ size="small"
171
+ onClick={(e) => {
172
+ e.stopPropagation();
173
+ setShowDetailModal(true);
174
+ }}
175
+ className="legend-marketplace-order-profile-card__info-button"
176
+ aria-label="View profile details"
177
+ >
178
+ <InfoCircleIcon />
179
+ </IconButton>
180
+ </Box>
181
+ </CardContent>
182
+ </CardActionArea>
183
+
184
+ <CardActions className="legend-marketplace-terminal-card__action-buttons">
185
+ {traderProfile.isOwned ? (
186
+ <Box className="legend-marketplace-terminal-card__owned-access">
187
+ {OrderProfileLabel.ALREADY_HAVE_ACCESS} &nbsp;
188
+ <CheckCircleIcon />
189
+ </Box>
190
+ ) : (
191
+ <>
192
+ <Button
193
+ variant="outlined"
194
+ className="legend-marketplace-terminal-card__add-to-cart-button"
195
+ onClick={handleAddToCart}
196
+ disabled={isAddingToCart || isInCart}
197
+ >
198
+ {isAddingToCart && (
199
+ <>
200
+ {OrderProfileLabel.ADDING} &nbsp;
201
+ <CircularProgress size={16} />
202
+ </>
203
+ )}
204
+ {!isAddingToCart && isInCart && (
205
+ <>
206
+ {OrderProfileLabel.IN_CART} &nbsp;
207
+ <Box className="legend-marketplace-terminal-card__in-cart-check">
208
+ <CheckCircleIcon />
209
+ </Box>
210
+ </>
211
+ )}
212
+ {!isAddingToCart && !isInCart && (
213
+ <>
214
+ {OrderProfileLabel.ADD_TO_CART} &nbsp;
215
+ <ShoppingCartIcon />
216
+ </>
217
+ )}
218
+ </Button>
219
+ <Chip
220
+ label={formatCardPrice(displayPrice)}
221
+ className="legend-marketplace-terminal-card__price"
222
+ />
223
+ </>
224
+ )}
225
+ </CardActions>
226
+ </Card>
227
+
228
+ <OrderProfileDetailModal
229
+ profile={traderProfile}
230
+ open={showDetailModal}
231
+ onClose={() => setShowDetailModal(false)}
232
+ {...(multiselectTotalPrice !== undefined
233
+ ? { multiselectTotalPrice }
234
+ : {})}
235
+ />
236
+
237
+ <OrderProfileMultiselectModal
238
+ profile={traderProfile}
239
+ open={showMultiselectModal}
240
+ onClose={() => setShowMultiselectModal(false)}
241
+ onConfirm={handleMultiselectConfirm}
242
+ />
243
+ </>
244
+ );
245
+ },
246
+ );
@@ -144,14 +144,7 @@ export const LegendMarketplaceTerminalCard = observer(
144
144
  </CardActionArea>
145
145
  <CardActions className="legend-marketplace-terminal-card__action-buttons">
146
146
  {terminalResult.isOwned ? (
147
- <Box
148
- sx={{
149
- display: 'flex',
150
- alignItems: 'center',
151
- color: '#077D55',
152
- fontWeight: 'bold',
153
- }}
154
- >
147
+ <Box className="legend-marketplace-terminal-card__owned-access">
155
148
  Already have access &nbsp;
156
149
  <CheckCircleIcon />
157
150
  </Box>
@@ -165,19 +158,21 @@ export const LegendMarketplaceTerminalCard = observer(
165
158
  }}
166
159
  disabled={isAddingToCart || isInCart}
167
160
  >
168
- {isAddingToCart ? (
161
+ {isAddingToCart && (
169
162
  <>
170
163
  Adding... &nbsp;
171
164
  <CircularProgress size={16} />
172
165
  </>
173
- ) : isInCart ? (
166
+ )}
167
+ {!isAddingToCart && isInCart && (
174
168
  <>
175
169
  In Cart &nbsp;
176
- <Box sx={{ color: '#077D55', display: 'inline-flex' }}>
170
+ <Box className="legend-marketplace-terminal-card__in-cart-check">
177
171
  <CheckCircleIcon />
178
172
  </Box>
179
173
  </>
180
- ) : (
174
+ )}
175
+ {!isAddingToCart && !isInCart && (
181
176
  <>
182
177
  Add to cart &nbsp;
183
178
  <ShoppingCartIcon />
@@ -189,12 +184,10 @@ export const LegendMarketplaceTerminalCard = observer(
189
184
  label={`${new Intl.NumberFormat('en-US', {
190
185
  style: 'currency',
191
186
  currency: 'USD',
192
- roundingIncrement: 1,
193
- minimumFractionDigits: 0,
194
- maximumFractionDigits: 0,
187
+ minimumFractionDigits: 2,
188
+ maximumFractionDigits: 2,
195
189
  }).format(terminalResult.price)}/month`}
196
190
  className="legend-marketplace-terminal-card__price"
197
- sx={{ color: 'white', backgroundColor: '#077d55' }}
198
191
  />
199
192
  )}
200
193
  </>
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Copyright (c) 2026-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 { JSX } from 'react';
18
+ import { observer } from 'mobx-react-lite';
19
+ import { useLegendMarketplaceBaseStore } from '../../application/providers/LegendMarketplaceFrameworkProvider.js';
20
+ import {
21
+ Dialog,
22
+ DialogTitle,
23
+ DialogContent,
24
+ DialogActions,
25
+ Button,
26
+ Typography,
27
+ Box,
28
+ IconButton,
29
+ Chip,
30
+ Table,
31
+ TableBody,
32
+ TableCell,
33
+ TableContainer,
34
+ TableHead,
35
+ TableRow,
36
+ } from '@mui/material';
37
+ import { CloseIcon, DocumentIcon } from '@finos/legend-art';
38
+ import type { TraderProfile } from '@finos/legend-server-marketplace';
39
+ import {
40
+ formatItemPrice,
41
+ formatProfileSummaryLine,
42
+ getItemSummary,
43
+ groupOrderProfileItems,
44
+ OrderProfileLabel,
45
+ OrderProfileTableHeader,
46
+ } from './orderProfileUtils.js';
47
+
48
+ const CategoryChip = (props: {
49
+ category: string;
50
+ isTerminal: boolean;
51
+ }): JSX.Element => {
52
+ const { category, isTerminal } = props;
53
+ return (
54
+ <Chip
55
+ label={category}
56
+ size="small"
57
+ className={
58
+ isTerminal
59
+ ? 'order-profile-modal__category-chip--terminal'
60
+ : 'order-profile-modal__category-chip--addon'
61
+ }
62
+ />
63
+ );
64
+ };
65
+
66
+ export const OrderProfileDetailModal = observer(
67
+ (props: {
68
+ profile: TraderProfile;
69
+ open: boolean;
70
+ onClose: () => void;
71
+ multiselectTotalPrice?: number;
72
+ }): JSX.Element => {
73
+ const { profile, open, onClose, multiselectTotalPrice } = props;
74
+ const { cartStore } = useLegendMarketplaceBaseStore();
75
+ const items = profile.items;
76
+ const { terminalCount, addOnCount } = getItemSummary(items);
77
+ const groupedItems = groupOrderProfileItems(items);
78
+ const displayPrice =
79
+ profile.multiselect && multiselectTotalPrice !== undefined
80
+ ? multiselectTotalPrice
81
+ : profile.price;
82
+
83
+ return (
84
+ <Dialog
85
+ open={open}
86
+ onClose={onClose}
87
+ maxWidth="md"
88
+ fullWidth={true}
89
+ className="order-profile-modal"
90
+ aria-labelledby="order-profile-modal-title"
91
+ >
92
+ <DialogTitle
93
+ id="order-profile-modal-title"
94
+ className="order-profile-modal__header"
95
+ >
96
+ <Box className="order-profile-modal__header-content">
97
+ <Box className="order-profile-modal__header-title">
98
+ <DocumentIcon className="order-profile-modal__header-icon" />
99
+ <Typography
100
+ variant="h6"
101
+ className="order-profile-modal__profile-name"
102
+ >
103
+ {profile.productName}
104
+ </Typography>
105
+ </Box>
106
+ <IconButton
107
+ onClick={onClose}
108
+ size="small"
109
+ aria-label="close"
110
+ className="order-profile-modal__close-button"
111
+ >
112
+ <CloseIcon />
113
+ </IconButton>
114
+ </Box>
115
+ <Typography
116
+ variant="body2"
117
+ className="order-profile-modal__header-summary"
118
+ >
119
+ {formatProfileSummaryLine(terminalCount, addOnCount)}
120
+ {OrderProfileLabel.PRICE_TOTAL_SEPARATOR}
121
+ <strong>{formatItemPrice(displayPrice)}</strong>
122
+ </Typography>
123
+ </DialogTitle>
124
+
125
+ <DialogContent className="order-profile-modal__content" dividers={true}>
126
+ <TableContainer>
127
+ <Table size="small" aria-label="order profile items">
128
+ <TableHead>
129
+ <TableRow className="order-profile-modal__table-head">
130
+ <TableCell className="order-profile-modal__table-header-cell">
131
+ {OrderProfileTableHeader.PRODUCT_NAME}
132
+ </TableCell>
133
+ <TableCell className="order-profile-modal__table-header-cell">
134
+ {OrderProfileTableHeader.PROVIDER}
135
+ </TableCell>
136
+ <TableCell className="order-profile-modal__table-header-cell">
137
+ {OrderProfileTableHeader.CATEGORY}
138
+ </TableCell>
139
+ <TableCell
140
+ align="center"
141
+ className="order-profile-modal__table-header-cell"
142
+ >
143
+ {OrderProfileTableHeader.COST_MONTHLY}
144
+ </TableCell>
145
+ </TableRow>
146
+ </TableHead>
147
+ <TableBody>
148
+ {groupedItems.map(({ item, isSubItem }) => {
149
+ const isInCart =
150
+ !item.isOwned && cartStore.isItemInCart(item.id);
151
+ const rowModifierClass = (() => {
152
+ if (item.isOwned) {
153
+ return 'order-profile-modal__table-row--owned';
154
+ }
155
+ if (isInCart) {
156
+ return 'order-profile-modal__table-row--in-cart';
157
+ }
158
+ return '';
159
+ })();
160
+ return (
161
+ <TableRow
162
+ key={item.id}
163
+ className={`order-profile-modal__table-row ${rowModifierClass}`}
164
+ >
165
+ <TableCell className="order-profile-modal__table-cell order-profile-modal__table-cell--name">
166
+ <Box
167
+ className={`order-profile-modal__product-name-wrapper ${isSubItem ? 'order-profile-modal__product-name-wrapper--sub' : ''}`}
168
+ >
169
+ <Box
170
+ className={`order-profile-modal__row-accent ${item.isTerminal ? 'order-profile-modal__row-accent--vendor-profile' : 'order-profile-modal__row-accent--addon'}`}
171
+ />
172
+ <span>
173
+ {item.productName}
174
+ {item.isOwned && (
175
+ <span className="order-profile-modal__owned-label">
176
+ {' '}
177
+ {OrderProfileLabel.OWNED_SUFFIX}
178
+ </span>
179
+ )}
180
+ {isInCart && (
181
+ <span className="order-profile-modal__in-cart-label">
182
+ {' '}
183
+ {OrderProfileLabel.IN_CART_SUFFIX}
184
+ </span>
185
+ )}
186
+ </span>
187
+ </Box>
188
+ </TableCell>
189
+ <TableCell className="order-profile-modal__table-cell">
190
+ {item.providerName}
191
+ </TableCell>
192
+ <TableCell className="order-profile-modal__table-cell">
193
+ <CategoryChip
194
+ category={item.category}
195
+ isTerminal={item.isTerminal}
196
+ />
197
+ </TableCell>
198
+ <TableCell
199
+ align="center"
200
+ className="order-profile-modal__table-cell order-profile-modal__table-cell--price"
201
+ >
202
+ {formatItemPrice(item.price)}
203
+ </TableCell>
204
+ </TableRow>
205
+ );
206
+ })}
207
+ </TableBody>
208
+ </Table>
209
+ </TableContainer>
210
+ </DialogContent>
211
+
212
+ <DialogActions className="order-profile-modal__actions">
213
+ <Button
214
+ variant="contained"
215
+ onClick={onClose}
216
+ className="order-profile-modal__close-btn"
217
+ >
218
+ {OrderProfileLabel.CLOSE}
219
+ </Button>
220
+ </DialogActions>
221
+ </Dialog>
222
+ );
223
+ },
224
+ );