@finos/legend-application-marketplace 0.2.3 → 0.2.4

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 (103) hide show
  1. package/lib/__lib__/LegendMarketplaceAppEvent.d.ts +2 -0
  2. package/lib/__lib__/LegendMarketplaceAppEvent.d.ts.map +1 -1
  3. package/lib/__lib__/LegendMarketplaceAppEvent.js +2 -0
  4. package/lib/__lib__/LegendMarketplaceAppEvent.js.map +1 -1
  5. package/lib/__lib__/LegendMarketplaceNavigation.d.ts +7 -1
  6. package/lib/__lib__/LegendMarketplaceNavigation.d.ts.map +1 -1
  7. package/lib/__lib__/LegendMarketplaceNavigation.js +9 -1
  8. package/lib/__lib__/LegendMarketplaceNavigation.js.map +1 -1
  9. package/lib/__lib__/LegendMarketplaceTelemetryHelper.d.ts +2 -1
  10. package/lib/__lib__/LegendMarketplaceTelemetryHelper.d.ts.map +1 -1
  11. package/lib/__lib__/LegendMarketplaceTelemetryHelper.js +10 -2
  12. package/lib/__lib__/LegendMarketplaceTelemetryHelper.js.map +1 -1
  13. package/lib/application/LegendMarketplaceWebApplication.d.ts.map +1 -1
  14. package/lib/application/LegendMarketplaceWebApplication.js +4 -1
  15. package/lib/application/LegendMarketplaceWebApplication.js.map +1 -1
  16. package/lib/application/providers/LegendMarketplaceFieldSearchResultsStoreProvider.d.ts +22 -0
  17. package/lib/application/providers/LegendMarketplaceFieldSearchResultsStoreProvider.d.ts.map +1 -0
  18. package/lib/application/providers/LegendMarketplaceFieldSearchResultsStoreProvider.js +37 -0
  19. package/lib/application/providers/LegendMarketplaceFieldSearchResultsStoreProvider.js.map +1 -0
  20. package/lib/components/AddToCart/CartDrawer.d.ts.map +1 -1
  21. package/lib/components/AddToCart/CartDrawer.js +36 -4
  22. package/lib/components/AddToCart/CartDrawer.js.map +1 -1
  23. package/lib/components/AddToCart/RecommendedAddOnsModal.d.ts +2 -1
  24. package/lib/components/AddToCart/RecommendedAddOnsModal.d.ts.map +1 -1
  25. package/lib/components/AddToCart/RecommendedAddOnsModal.js +23 -13
  26. package/lib/components/AddToCart/RecommendedAddOnsModal.js.map +1 -1
  27. package/lib/components/AddToCart/RecommendedItemsCard.d.ts +3 -1
  28. package/lib/components/AddToCart/RecommendedItemsCard.d.ts.map +1 -1
  29. package/lib/components/AddToCart/RecommendedItemsCard.js +14 -11
  30. package/lib/components/AddToCart/RecommendedItemsCard.js.map +1 -1
  31. package/lib/components/FieldSearchFiltersPanel/FieldSearchFiltersPanel.d.ts +23 -0
  32. package/lib/components/FieldSearchFiltersPanel/FieldSearchFiltersPanel.d.ts.map +1 -0
  33. package/lib/components/FieldSearchFiltersPanel/FieldSearchFiltersPanel.js +22 -0
  34. package/lib/components/FieldSearchFiltersPanel/FieldSearchFiltersPanel.js.map +1 -0
  35. package/lib/components/MarketplaceCard/FieldSearchResultListItem.d.ts +25 -0
  36. package/lib/components/MarketplaceCard/FieldSearchResultListItem.d.ts.map +1 -0
  37. package/lib/components/MarketplaceCard/FieldSearchResultListItem.js +58 -0
  38. package/lib/components/MarketplaceCard/FieldSearchResultListItem.js.map +1 -0
  39. package/lib/components/MarketplaceSearchFiltersPanel/MarketplaceSearchFiltersPanel.d.ts +10 -0
  40. package/lib/components/MarketplaceSearchFiltersPanel/MarketplaceSearchFiltersPanel.d.ts.map +1 -1
  41. package/lib/components/MarketplaceSearchFiltersPanel/MarketplaceSearchFiltersPanel.js +2 -2
  42. package/lib/components/MarketplaceSearchFiltersPanel/MarketplaceSearchFiltersPanel.js.map +1 -1
  43. package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.d.ts.map +1 -1
  44. package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.js +5 -2
  45. package/lib/components/ProviderCard/LegendMarketplaceTerminalCard.js.map +1 -1
  46. package/lib/components/SearchBar/LegendMarketplaceSearchBar.d.ts +2 -1
  47. package/lib/components/SearchBar/LegendMarketplaceSearchBar.d.ts.map +1 -1
  48. package/lib/components/SearchBar/LegendMarketplaceSearchBar.js +20 -7
  49. package/lib/components/SearchBar/LegendMarketplaceSearchBar.js.map +1 -1
  50. package/lib/index.css +2 -2
  51. package/lib/index.css.map +1 -1
  52. package/lib/package.json +1 -1
  53. package/lib/pages/Lakehouse/MarketplaceLakehouseHome.d.ts.map +1 -1
  54. package/lib/pages/Lakehouse/MarketplaceLakehouseHome.js +6 -4
  55. package/lib/pages/Lakehouse/MarketplaceLakehouseHome.js.map +1 -1
  56. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.d.ts +17 -0
  57. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.d.ts.map +1 -0
  58. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js +126 -0
  59. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js.map +1 -0
  60. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.d.ts.map +1 -1
  61. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js +8 -3
  62. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js.map +1 -1
  63. package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.d.ts.map +1 -1
  64. package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js +2 -2
  65. package/lib/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js.map +1 -1
  66. package/lib/stores/cart/CartStore.d.ts +10 -3
  67. package/lib/stores/cart/CartStore.d.ts.map +1 -1
  68. package/lib/stores/cart/CartStore.js +66 -42
  69. package/lib/stores/cart/CartStore.js.map +1 -1
  70. package/lib/stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.d.ts +63 -0
  71. package/lib/stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.d.ts.map +1 -0
  72. package/lib/stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.js +228 -0
  73. package/lib/stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.js.map +1 -0
  74. package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.d.ts.map +1 -1
  75. package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.js +9 -13
  76. package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.js.map +1 -1
  77. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.d.ts +40 -0
  78. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.d.ts.map +1 -0
  79. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.js +84 -0
  80. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.js.map +1 -0
  81. package/package.json +8 -8
  82. package/src/__lib__/LegendMarketplaceAppEvent.ts +2 -0
  83. package/src/__lib__/LegendMarketplaceNavigation.ts +18 -1
  84. package/src/__lib__/LegendMarketplaceTelemetryHelper.ts +17 -1
  85. package/src/application/LegendMarketplaceWebApplication.tsx +13 -0
  86. package/src/application/providers/LegendMarketplaceFieldSearchResultsStoreProvider.tsx +67 -0
  87. package/src/components/AddToCart/CartDrawer.tsx +49 -4
  88. package/src/components/AddToCart/RecommendedAddOnsModal.tsx +86 -24
  89. package/src/components/AddToCart/RecommendedItemsCard.tsx +143 -120
  90. package/src/components/FieldSearchFiltersPanel/FieldSearchFiltersPanel.tsx +65 -0
  91. package/src/components/MarketplaceCard/FieldSearchResultListItem.tsx +163 -0
  92. package/src/components/MarketplaceSearchFiltersPanel/MarketplaceSearchFiltersPanel.tsx +2 -2
  93. package/src/components/ProviderCard/LegendMarketplaceTerminalCard.tsx +7 -0
  94. package/src/components/SearchBar/LegendMarketplaceSearchBar.tsx +44 -3
  95. package/src/pages/Lakehouse/MarketplaceLakehouseHome.tsx +9 -2
  96. package/src/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.tsx +380 -0
  97. package/src/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.tsx +19 -1
  98. package/src/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.tsx +6 -2
  99. package/src/stores/cart/CartStore.ts +86 -51
  100. package/src/stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.ts +309 -0
  101. package/src/stores/lakehouse/LegendMarketplaceProductViewerStore.ts +23 -30
  102. package/src/stores/lakehouse/fieldSearch/FieldSearchResultState.ts +122 -0
  103. package/tsconfig.json +6 -0
@@ -69,7 +69,9 @@ interface RecommendedAddOnsModalProps {
69
69
  selectedTerminal: TerminalResult,
70
70
  recommendations: TerminalResult[],
71
71
  responseMessage: string,
72
+ totalCount?: number | null,
72
73
  ) => void;
74
+ totalCount?: number | null | undefined;
73
75
  }
74
76
 
75
77
  const MAX_DISPLAY_ITEMS_COUNT = 10;
@@ -115,11 +117,12 @@ export const RecommendedAddOnsModal = observer(
115
117
  setShowModal,
116
118
  onViewCart,
117
119
  onTerminalSelected,
120
+ totalCount: initialTotalCount,
118
121
  } = props;
119
122
 
120
123
  const legendMarketplaceBaseStore = useLegendMarketplaceBaseStore();
121
124
  const applicationStore = legendMarketplaceBaseStore.applicationStore;
122
- const currentUser = applicationStore.identityService.currentUser;
125
+ const cartUser = legendMarketplaceBaseStore.cartStore.cartUser;
123
126
 
124
127
  const [searchTerm, setSearchTerm] = useState('');
125
128
  const [sortOrder, setSortOrder] = useState<SortOrder | undefined>();
@@ -128,6 +131,9 @@ export const RecommendedAddOnsModal = observer(
128
131
  const [terminalSearchResults, setTerminalSearchResults] = useState<
129
132
  TerminalResult[] | undefined
130
133
  >(undefined);
134
+ const [searchTotalCount, setSearchTotalCount] = useState<
135
+ number | undefined
136
+ >(undefined);
131
137
  const [isSearching, setIsSearching] = useState(false);
132
138
  const [isAssociating, setIsAssociating] = useState(false);
133
139
  const [associatingItemId, setAssociatingItemId] = useState<
@@ -143,12 +149,16 @@ export const RecommendedAddOnsModal = observer(
143
149
  const hasCartItems = recommendedItems.some(
144
150
  (item) => item.source === RecommendationSource.CART,
145
151
  );
152
+ const hasInventoryItems = recommendedItems.some(
153
+ (item) => item.source === RecommendationSource.INVENTORY,
154
+ );
146
155
  const hasMarketplaceItems = recommendedItems.some(
147
- (item) =>
148
- item.source === RecommendationSource.MARKETPLACE ||
149
- item.source === RecommendationSource.INVENTORY,
156
+ (item) => item.source === RecommendationSource.MARKETPLACE,
157
+ );
158
+ return (
159
+ [hasCartItems, hasInventoryItems, hasMarketplaceItems].filter(Boolean)
160
+ .length >= 2
150
161
  );
151
- return hasCartItems && hasMarketplaceItems;
152
162
  }, [recommendedItems]);
153
163
 
154
164
  const cartSourceItems = useMemo(
@@ -158,12 +168,17 @@ export const RecommendedAddOnsModal = observer(
158
168
  ),
159
169
  [recommendedItems],
160
170
  );
171
+ const inventorySourceItems = useMemo(
172
+ () =>
173
+ recommendedItems.filter(
174
+ (item) => item.source === RecommendationSource.INVENTORY,
175
+ ),
176
+ [recommendedItems],
177
+ );
161
178
  const marketplaceSourceItems = useMemo(
162
179
  () =>
163
180
  recommendedItems.filter(
164
- (item) =>
165
- item.source === RecommendationSource.MARKETPLACE ||
166
- item.source === RecommendationSource.INVENTORY,
181
+ (item) => item.source === RecommendationSource.MARKETPLACE,
167
182
  ),
168
183
  [recommendedItems],
169
184
  );
@@ -181,7 +196,7 @@ export const RecommendedAddOnsModal = observer(
181
196
  try {
182
197
  const response =
183
198
  await legendMarketplaceBaseStore.marketplaceServerClient.searchVendorAddons(
184
- currentUser,
199
+ cartUser,
185
200
  terminal.providerName,
186
201
  {
187
202
  // SERVER_SEARCH_PAGE_SIZE is set high enough to cover all expected results and paginate client-side.
@@ -196,6 +211,7 @@ export const RecommendedAddOnsModal = observer(
196
211
  setTerminalSearchResults(
197
212
  response.marketplace_addons as TerminalResult[],
198
213
  );
214
+ setSearchTotalCount(response.total_count as number | undefined);
199
215
  }
200
216
  } catch (error) {
201
217
  assertErrorThrown(error);
@@ -218,7 +234,7 @@ export const RecommendedAddOnsModal = observer(
218
234
  [
219
235
  terminal,
220
236
  isTerminalAdded,
221
- currentUser,
237
+ cartUser,
222
238
  legendMarketplaceBaseStore.marketplaceServerClient,
223
239
  applicationStore.logService,
224
240
  ],
@@ -232,6 +248,7 @@ export const RecommendedAddOnsModal = observer(
232
248
 
233
249
  if (!isTerminalAdded || !query.trim()) {
234
250
  setTerminalSearchResults(undefined);
251
+ setSearchTotalCount(undefined);
235
252
  setIsSearching(false);
236
253
  return;
237
254
  }
@@ -300,6 +317,7 @@ export const RecommendedAddOnsModal = observer(
300
317
  setSortOrder(undefined);
301
318
  setCurrentPage(1);
302
319
  setTerminalSearchResults(undefined);
320
+ setSearchTotalCount(undefined);
303
321
  setIsSearching(false);
304
322
  setIsAssociating(false);
305
323
  setAssociatingItemId(undefined);
@@ -336,6 +354,7 @@ export const RecommendedAddOnsModal = observer(
336
354
  selectedTerminal,
337
355
  result.recommendations,
338
356
  result.message,
357
+ result.totalCount,
339
358
  );
340
359
  } else {
341
360
  closeModal();
@@ -526,6 +545,45 @@ export const RecommendedAddOnsModal = observer(
526
545
  )}
527
546
 
528
547
  {cartSourceItems.length > 0 &&
548
+ (inventorySourceItems.length > 0 ||
549
+ marketplaceSourceItems.length > 0) && (
550
+ <Divider sx={{ my: 2 }} />
551
+ )}
552
+
553
+ {inventorySourceItems.length > 0 && (
554
+ <Box className="recommended-addons-modal__source-section">
555
+ <Box className="recommended-addons-modal__source-header">
556
+ <Typography
557
+ variant="h6"
558
+ className="recommended-addons-modal__source-title"
559
+ >
560
+ From Your Inventory
561
+ </Typography>
562
+ <Typography
563
+ variant="body2"
564
+ className="recommended-addons-modal__source-description"
565
+ >
566
+ Select a terminal from your existing inventory to
567
+ associate
568
+ </Typography>
569
+ </Box>
570
+ <Box className="recommended-addons-modal__list">
571
+ <ListHeader headerName={headerName} />
572
+ {inventorySourceItems.map((item) => (
573
+ <RecommendedItemsCard
574
+ key={item.id}
575
+ recommendedItem={item}
576
+ onSelect={handleAssociateTerminal}
577
+ isSelecting={isAssociating}
578
+ selectedItemId={associatingItemId}
579
+ />
580
+ ))}
581
+ </Box>
582
+ </Box>
583
+ )}
584
+
585
+ {(cartSourceItems.length > 0 ||
586
+ inventorySourceItems.length > 0) &&
529
587
  marketplaceSourceItems.length > 0 && <Divider sx={{ my: 2 }} />}
530
588
 
531
589
  {marketplaceSourceItems.length > 0 && (
@@ -576,6 +634,7 @@ export const RecommendedAddOnsModal = observer(
576
634
  setCurrentPage(1);
577
635
  if (!e.target.value.trim()) {
578
636
  setTerminalSearchResults(undefined);
637
+ setSearchTotalCount(undefined);
579
638
  setIsSearching(false);
580
639
  abortControllerRef.current?.abort();
581
640
  }
@@ -705,24 +764,27 @@ export const RecommendedAddOnsModal = observer(
705
764
  currentPage * itemsPerPage,
706
765
  filteredAndSortedItems.length,
707
766
  )}{' '}
708
- of {filteredAndSortedItems.length} items
767
+ of{' '}
768
+ {(terminalSearchResults
769
+ ? searchTotalCount
770
+ : initialTotalCount) ??
771
+ filteredAndSortedItems.length}{' '}
772
+ items
709
773
  </Typography>
710
774
  </Box>
711
775
  <Box className="recommended-addons-modal__list">
712
776
  <ListHeader headerName={headerName} />
713
- {paginatedItems
714
- .filter((item) => !item.isMandatory)
715
- .map((item) => (
716
- <RecommendedItemsCard
717
- key={item.id}
718
- recommendedItem={item}
719
- {...(isAddOnAssociation && {
720
- onSelect: handleAssociateTerminal,
721
- isSelecting: isAssociating,
722
- selectedItemId: associatingItemId,
723
- })}
724
- />
725
- ))}
777
+ {paginatedItems.map((item) => (
778
+ <RecommendedItemsCard
779
+ key={item.id}
780
+ recommendedItem={item}
781
+ {...(isAddOnAssociation && {
782
+ onSelect: handleAssociateTerminal,
783
+ isSelecting: isAssociating,
784
+ selectedItemId: associatingItemId,
785
+ })}
786
+ />
787
+ ))}
726
788
  </Box>
727
789
  {totalPages > 1 && (
728
790
  <Box className="recommended-addons-modal__pagination">
@@ -19,8 +19,15 @@ import {
19
19
  RecommendationSource,
20
20
  type TerminalResult,
21
21
  } from '@finos/legend-server-marketplace';
22
- import { Box, Button, CircularProgress, Typography } from '@mui/material';
22
+ import {
23
+ Box,
24
+ Button,
25
+ CircularProgress,
26
+ Tooltip,
27
+ Typography,
28
+ } from '@mui/material';
23
29
  import { flowResult } from 'mobx';
30
+ import { observer } from 'mobx-react-lite';
24
31
  import { useState } from 'react';
25
32
  import { assertErrorThrown } from '@finos/legend-shared';
26
33
  import { toastManager } from '../Toast/CartToast.js';
@@ -33,166 +40,182 @@ interface RecommendedItemsCardProps {
33
40
  selectedItemId?: number | undefined;
34
41
  }
35
42
 
36
- export const RecommendedItemsCard = (props: RecommendedItemsCardProps) => {
37
- const { recommendedItem, onSelect, isSelecting, selectedItemId } = props;
38
- const legendMarketplaceBaseStore = useLegendMarketplaceBaseStore();
39
- const [isAddingToCart, setIsAddingToCart] = useState(false);
40
- const [inCart, setInCart] = useState(() =>
41
- legendMarketplaceBaseStore.cartStore.isItemInCart(recommendedItem.id),
42
- );
43
+ export const RecommendedItemsCard = observer(
44
+ (props: RecommendedItemsCardProps) => {
45
+ const { recommendedItem, onSelect, isSelecting, selectedItemId } = props;
46
+ const legendMarketplaceBaseStore = useLegendMarketplaceBaseStore();
47
+ const [isAddingToCart, setIsAddingToCart] = useState(false);
48
+ const inCart = legendMarketplaceBaseStore.cartStore.isItemInCart(
49
+ recommendedItem.id,
50
+ );
43
51
 
44
- const isAssociationFlow = onSelect !== undefined;
45
- const isCurrentlySelecting =
46
- isAssociationFlow &&
47
- Boolean(isSelecting) &&
48
- selectedItemId === recommendedItem.id;
49
- const isMarketplaceItem =
50
- recommendedItem.source === RecommendationSource.MARKETPLACE;
52
+ const isAssociationFlow = onSelect !== undefined;
53
+ const isCurrentlySelecting =
54
+ isAssociationFlow &&
55
+ Boolean(isSelecting) &&
56
+ selectedItemId === recommendedItem.id;
57
+ const isMarketplaceItem =
58
+ recommendedItem.source === RecommendationSource.MARKETPLACE;
51
59
 
52
- const handleAddAddonToCart = (addon: TerminalResult) => {
53
- setIsAddingToCart(true);
54
- const cartItemRequest =
55
- legendMarketplaceBaseStore.cartStore.providerToCartRequest(addon);
60
+ const handleAddAddonToCart = (addon: TerminalResult) => {
61
+ setIsAddingToCart(true);
62
+ const cartItemRequest =
63
+ legendMarketplaceBaseStore.cartStore.providerToCartRequest(addon);
56
64
 
57
- flowResult(
58
- legendMarketplaceBaseStore.cartStore.addToCartWithAPI(cartItemRequest),
59
- )
60
- .then((result) => {
61
- if (result.success) {
62
- setInCart(true);
63
- }
64
- setIsAddingToCart(false);
65
- })
66
- .catch((error) => {
67
- assertErrorThrown(error);
68
- toastManager.error(
69
- `Failed to add ${addon.productName} to cart: ${error.message}`,
70
- );
71
- setIsAddingToCart(false);
72
- });
73
- };
74
-
75
- const renderAction = () => {
76
- if (isAssociationFlow) {
77
- if (recommendedItem.isOwned) {
78
- return (
79
- <Box className="recommended-addons-modal__owned-badge">
80
- <CheckCircleIcon />
81
- <Typography variant="body2">Owned</Typography>
82
- </Box>
83
- );
84
- }
65
+ flowResult(
66
+ legendMarketplaceBaseStore.cartStore.addToCartWithAPI(cartItemRequest),
67
+ )
68
+ .catch((error) => {
69
+ assertErrorThrown(error);
70
+ toastManager.error(
71
+ `Failed to add ${addon.productName} to cart: ${error.message}`,
72
+ );
73
+ })
74
+ .finally(() => {
75
+ setIsAddingToCart(false);
76
+ });
77
+ };
85
78
 
86
- if (isMarketplaceItem) {
87
- if (inCart) {
79
+ const renderAction = () => {
80
+ if (isAssociationFlow) {
81
+ if (recommendedItem.isOwned) {
88
82
  return (
89
- <Box className="recommended-addons-modal__in-cart-badge">
90
- <Typography variant="body2">In Cart</Typography>
83
+ <Box className="recommended-addons-modal__owned-badge">
91
84
  <CheckCircleIcon />
85
+ <Typography variant="body2">Owned</Typography>
92
86
  </Box>
93
87
  );
94
88
  }
89
+
90
+ if (isMarketplaceItem) {
91
+ if (inCart) {
92
+ return (
93
+ <Box className="recommended-addons-modal__in-cart-badge">
94
+ <Typography variant="body2">In Cart</Typography>
95
+ <CheckCircleIcon />
96
+ </Box>
97
+ );
98
+ }
99
+ return (
100
+ <Button
101
+ variant="outlined"
102
+ onClick={() => onSelect(recommendedItem)}
103
+ disabled={Boolean(isSelecting)}
104
+ size="small"
105
+ className="recommended-addons-modal__add-btn"
106
+ >
107
+ {isCurrentlySelecting ? (
108
+ <>
109
+ Adding... &nbsp;
110
+ <CircularProgress size={14} />
111
+ </>
112
+ ) : (
113
+ <>
114
+ Add to Cart &nbsp;
115
+ <PlusIcon />
116
+ </>
117
+ )}
118
+ </Button>
119
+ );
120
+ }
121
+
95
122
  return (
96
123
  <Button
97
124
  variant="outlined"
98
125
  onClick={() => onSelect(recommendedItem)}
99
126
  disabled={Boolean(isSelecting)}
100
127
  size="small"
101
- className="recommended-addons-modal__add-btn"
128
+ className="recommended-addons-modal__select-btn"
102
129
  >
103
130
  {isCurrentlySelecting ? (
104
131
  <>
105
- Adding... &nbsp;
132
+ Selecting... &nbsp;
106
133
  <CircularProgress size={14} />
107
134
  </>
108
135
  ) : (
109
136
  <>
110
- Add to Cart &nbsp;
111
- <PlusIcon />
137
+ Select &nbsp;
138
+ <CheckIcon />
112
139
  </>
113
140
  )}
114
141
  </Button>
115
142
  );
116
143
  }
117
144
 
118
- return (
145
+ const button = (
119
146
  <Button
120
147
  variant="outlined"
121
- onClick={() => onSelect(recommendedItem)}
122
- disabled={Boolean(isSelecting)}
148
+ onClick={() => handleAddAddonToCart(recommendedItem)}
149
+ disabled={inCart || isAddingToCart}
123
150
  size="small"
124
- className="recommended-addons-modal__select-btn"
151
+ className={clsx('recommended-addons-modal__add-btn', {
152
+ 'recommended-addons-modal__add-btn--added': inCart,
153
+ })}
125
154
  >
126
- {isCurrentlySelecting ? (
155
+ {isAddingToCart ? (
127
156
  <>
128
- Selecting... &nbsp;
157
+ Adding... &nbsp;
129
158
  <CircularProgress size={14} />
130
159
  </>
160
+ ) : inCart ? (
161
+ 'Added to Cart'
131
162
  ) : (
132
163
  <>
133
- Select &nbsp;
134
- <CheckIcon />
164
+ Add to Cart &nbsp;
165
+ <PlusIcon />
135
166
  </>
136
167
  )}
137
168
  </Button>
138
169
  );
139
- }
140
170
 
141
- return (
142
- <Button
143
- variant="outlined"
144
- onClick={() => handleAddAddonToCart(recommendedItem)}
145
- disabled={inCart || isAddingToCart}
146
- size="small"
147
- className={clsx('recommended-addons-modal__add-btn', {
148
- 'recommended-addons-modal__add-btn--added': inCart,
149
- })}
150
- >
151
- {isAddingToCart ? (
152
- <>
153
- Adding... &nbsp;
154
- <CircularProgress size={14} />
155
- </>
156
- ) : inCart ? (
157
- 'Added to Cart'
158
- ) : (
159
- <>
160
- Add to Cart &nbsp;
161
- <PlusIcon />
162
- </>
163
- )}
164
- </Button>
165
- );
166
- };
171
+ if (inCart) {
172
+ return (
173
+ <Tooltip
174
+ title={
175
+ recommendedItem.isMandatory
176
+ ? 'This is a mandatory item which needs to be associated with this order.'
177
+ : 'This item is already in your cart.'
178
+ }
179
+ arrow={true}
180
+ placement="top"
181
+ >
182
+ <span>{button}</span>
183
+ </Tooltip>
184
+ );
185
+ }
167
186
 
168
- return (
169
- <Box className="recommended-addons-modal__list-item">
170
- <Typography
171
- variant="body1"
172
- className="recommended-addons-modal__item-name"
173
- >
174
- {recommendedItem.productName}
175
- </Typography>
176
- <Typography
177
- variant="body2"
178
- className="recommended-addons-modal__item-provider"
179
- >
180
- {recommendedItem.providerName}
181
- </Typography>
182
- <Typography
183
- variant="body2"
184
- className="recommended-addons-modal__item-price"
185
- >
186
- {recommendedItem.price.toLocaleString('en-US', {
187
- style: 'currency',
188
- currency: 'USD',
189
- minimumFractionDigits: 2,
190
- maximumFractionDigits: 2,
191
- })}
192
- </Typography>
193
- <Box className="recommended-addons-modal__item-action">
194
- {renderAction()}
187
+ return button;
188
+ };
189
+
190
+ return (
191
+ <Box className="recommended-addons-modal__list-item">
192
+ <Typography
193
+ variant="body1"
194
+ className="recommended-addons-modal__item-name"
195
+ >
196
+ {recommendedItem.productName}
197
+ </Typography>
198
+ <Typography
199
+ variant="body2"
200
+ className="recommended-addons-modal__item-provider"
201
+ >
202
+ {recommendedItem.providerName}
203
+ </Typography>
204
+ <Typography
205
+ variant="body2"
206
+ className="recommended-addons-modal__item-price"
207
+ >
208
+ {recommendedItem.price.toLocaleString('en-US', {
209
+ style: 'currency',
210
+ currency: 'USD',
211
+ minimumFractionDigits: 2,
212
+ maximumFractionDigits: 2,
213
+ })}
214
+ </Typography>
215
+ <Box className="recommended-addons-modal__item-action">
216
+ {renderAction()}
217
+ </Box>
195
218
  </Box>
196
- </Box>
197
- );
198
- };
219
+ );
220
+ },
221
+ );
@@ -0,0 +1,65 @@
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 { observer } from 'mobx-react-lite';
18
+ import { Typography } from '@mui/material';
19
+ import { DataProductTypeFilter } from '../../stores/lakehouse/LegendMarketplaceSearchResultsStore.js';
20
+ import type { LegendMarketplaceFieldSearchResultsStore } from '../../stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.js';
21
+ import {
22
+ FilterCheckboxOption,
23
+ FilterSection,
24
+ } from '../MarketplaceSearchFiltersPanel/MarketplaceSearchFiltersPanel.js';
25
+
26
+ export const FieldSearchFiltersPanel: React.FC<{
27
+ store: LegendMarketplaceFieldSearchResultsStore;
28
+ onToggleProductType: (productType: DataProductTypeFilter) => void;
29
+ onClearFilters: () => void;
30
+ }> = observer(({ store, onToggleProductType, onClearFilters }) => (
31
+ <div className="marketplace-search-filters-panel">
32
+ <div className="marketplace-search-filters-panel__header">
33
+ <Typography className="marketplace-search-filters-panel__header__title">
34
+ Filters
35
+ </Typography>
36
+ {store.hasActiveFilters && (
37
+ <button
38
+ type="button"
39
+ className="marketplace-search-filters-panel__header__clear"
40
+ onClick={onClearFilters}
41
+ >
42
+ Clear all
43
+ </button>
44
+ )}
45
+ </div>
46
+ <div className="marketplace-search-filters-panel__content">
47
+ <FilterSection title="Data Product Type">
48
+ <FilterCheckboxOption
49
+ label="Lakehouse"
50
+ checked={store.selectedProductTypes.has(
51
+ DataProductTypeFilter.LAKEHOUSE,
52
+ )}
53
+ count={store.lakehouseCount}
54
+ onChange={() => onToggleProductType(DataProductTypeFilter.LAKEHOUSE)}
55
+ />
56
+ <FilterCheckboxOption
57
+ label="Legacy"
58
+ checked={store.selectedProductTypes.has(DataProductTypeFilter.LEGACY)}
59
+ count={store.legacyCount}
60
+ onChange={() => onToggleProductType(DataProductTypeFilter.LEGACY)}
61
+ />
62
+ </FilterSection>
63
+ </div>
64
+ </div>
65
+ ));