@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.
- package/lib/__lib__/LegendMarketplaceNavigation.d.ts +1 -0
- package/lib/__lib__/LegendMarketplaceNavigation.d.ts.map +1 -1
- package/lib/__lib__/LegendMarketplaceNavigation.js +4 -0
- package/lib/__lib__/LegendMarketplaceNavigation.js.map +1 -1
- package/lib/application/LegendMarketplaceApplicationConfig.d.ts +6 -3
- package/lib/application/LegendMarketplaceApplicationConfig.d.ts.map +1 -1
- package/lib/application/LegendMarketplaceApplicationConfig.js +13 -4
- package/lib/application/LegendMarketplaceApplicationConfig.js.map +1 -1
- package/lib/components/OptionSelector/LegendMarketplaceOptionSelector.d.ts +22 -0
- package/lib/components/OptionSelector/LegendMarketplaceOptionSelector.d.ts.map +1 -0
- package/lib/components/OptionSelector/LegendMarketplaceOptionSelector.js +28 -0
- package/lib/components/OptionSelector/LegendMarketplaceOptionSelector.js.map +1 -0
- package/lib/components/Pagination/PaginationControls.d.ts.map +1 -1
- package/lib/components/Pagination/PaginationControls.js +2 -2
- package/lib/components/Pagination/PaginationControls.js.map +1 -1
- package/lib/components/ProviderCard/LegendMarketplaceOrderProfileCard.d.ts +23 -0
- package/lib/components/ProviderCard/LegendMarketplaceOrderProfileCard.d.ts.map +1 -0
- package/lib/components/ProviderCard/LegendMarketplaceOrderProfileCard.js +93 -0
- package/lib/components/ProviderCard/LegendMarketplaceOrderProfileCard.js.map +1 -0
- package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.d.ts.map +1 -1
- package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.js +5 -11
- package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.js.map +1 -1
- package/lib/components/ProviderCard/OrderProfileDetailModal.d.ts +26 -0
- package/lib/components/ProviderCard/OrderProfileDetailModal.d.ts.map +1 -0
- package/lib/components/ProviderCard/OrderProfileDetailModal.js +36 -0
- package/lib/components/ProviderCard/OrderProfileDetailModal.js.map +1 -0
- package/lib/components/ProviderCard/OrderProfileMultiselectModal.d.ts +26 -0
- package/lib/components/ProviderCard/OrderProfileMultiselectModal.d.ts.map +1 -0
- package/lib/components/ProviderCard/OrderProfileMultiselectModal.js +31 -0
- package/lib/components/ProviderCard/OrderProfileMultiselectModal.js.map +1 -0
- package/lib/components/ProviderCard/orderProfileUtils.d.ts +71 -0
- package/lib/components/ProviderCard/orderProfileUtils.d.ts.map +1 -0
- package/lib/components/ProviderCard/orderProfileUtils.js +122 -0
- package/lib/components/ProviderCard/orderProfileUtils.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/Lakehouse/entitlements/LakehouseDataContract.d.ts.map +1 -1
- package/lib/pages/Lakehouse/entitlements/LakehouseDataContract.js +2 -2
- package/lib/pages/Lakehouse/entitlements/LakehouseDataContract.js.map +1 -1
- package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.d.ts.map +1 -1
- package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.js +5 -2
- package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.js.map +1 -1
- package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.d.ts.map +1 -1
- package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.js +2 -2
- package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.js.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.d.ts.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js +11 -2
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.d.ts.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js +9 -3
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js.map +1 -1
- package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.d.ts.map +1 -1
- package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js +45 -18
- package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js.map +1 -1
- package/lib/stores/LegendMarketPlaceVendorDataStore.d.ts +6 -2
- package/lib/stores/LegendMarketPlaceVendorDataStore.d.ts.map +1 -1
- package/lib/stores/LegendMarketPlaceVendorDataStore.js +25 -2
- package/lib/stores/LegendMarketPlaceVendorDataStore.js.map +1 -1
- package/lib/stores/LegendMarketplaceBaseStore.d.ts +1 -1
- package/lib/stores/LegendMarketplaceBaseStore.d.ts.map +1 -1
- package/lib/stores/LegendMarketplaceBaseStore.js +10 -5
- package/lib/stores/LegendMarketplaceBaseStore.js.map +1 -1
- package/lib/stores/ai/LegendMarketplaceAIChatStore.d.ts +10 -0
- package/lib/stores/ai/LegendMarketplaceAIChatStore.d.ts.map +1 -1
- package/lib/stores/ai/LegendMarketplaceAIChatStore.js +115 -50
- package/lib/stores/ai/LegendMarketplaceAIChatStore.js.map +1 -1
- package/lib/stores/cart/CartStore.d.ts +14 -2
- package/lib/stores/cart/CartStore.d.ts.map +1 -1
- package/lib/stores/cart/CartStore.js +68 -5
- package/lib/stores/cart/CartStore.js.map +1 -1
- package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.d.ts +4 -0
- package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.d.ts.map +1 -1
- package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.js +5 -0
- package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.js.map +1 -1
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts +2 -1
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts.map +1 -1
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js +8 -3
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js.map +1 -1
- package/lib/utils/SearchUtils.d.ts.map +1 -1
- package/lib/utils/SearchUtils.js +17 -2
- package/lib/utils/SearchUtils.js.map +1 -1
- package/package.json +11 -11
- package/src/__lib__/LegendMarketplaceNavigation.ts +9 -0
- package/src/application/LegendMarketplaceApplicationConfig.ts +19 -11
- package/src/components/OptionSelector/LegendMarketplaceOptionSelector.tsx +50 -0
- package/src/components/Pagination/PaginationControls.tsx +19 -17
- package/src/components/ProviderCard/LegendMarketplaceOrderProfileCard.tsx +246 -0
- package/src/components/ProviderCard/LegendMarketplaceTerminalCard.tsx +9 -16
- package/src/components/ProviderCard/OrderProfileDetailModal.tsx +224 -0
- package/src/components/ProviderCard/OrderProfileMultiselectModal.tsx +142 -0
- package/src/components/ProviderCard/orderProfileUtils.ts +165 -0
- package/src/pages/Lakehouse/entitlements/LakehouseDataContract.tsx +4 -0
- package/src/pages/Lakehouse/entitlements/PermitDataAccessRequest.tsx +7 -0
- package/src/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.tsx +4 -0
- package/src/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.tsx +24 -25
- package/src/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.tsx +19 -29
- package/src/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.tsx +177 -44
- package/src/stores/LegendMarketPlaceVendorDataStore.tsx +33 -1
- package/src/stores/LegendMarketplaceBaseStore.ts +13 -9
- package/src/stores/ai/LegendMarketplaceAIChatStore.ts +273 -69
- package/src/stores/cart/CartStore.ts +90 -4
- package/src/stores/lakehouse/LegendMarketplaceSearchResultsStore.ts +5 -0
- package/src/stores/lakehouse/entitlements/EntitlementsDashboardState.ts +10 -1
- package/src/utils/SearchUtils.tsx +33 -8
- 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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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}
|
|
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}
|
|
201
|
+
<CircularProgress size={16} />
|
|
202
|
+
</>
|
|
203
|
+
)}
|
|
204
|
+
{!isAddingToCart && isInCart && (
|
|
205
|
+
<>
|
|
206
|
+
{OrderProfileLabel.IN_CART}
|
|
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}
|
|
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
|
|
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...
|
|
171
164
|
<CircularProgress size={16} />
|
|
172
165
|
</>
|
|
173
|
-
)
|
|
166
|
+
)}
|
|
167
|
+
{!isAddingToCart && isInCart && (
|
|
174
168
|
<>
|
|
175
169
|
In Cart
|
|
176
|
-
<Box
|
|
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
|
|
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
|
-
|
|
193
|
-
|
|
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
|
+
);
|