@finos/legend-application-marketplace 0.2.3 → 0.2.5

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__/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/pages/__test-utils__/TEST_DATA__LakehouseDataProducts.d.ts.map +1 -1
  67. package/lib/pages/__test-utils__/TEST_DATA__LakehouseDataProducts.js.map +1 -1
  68. package/lib/stores/cart/CartStore.d.ts +10 -3
  69. package/lib/stores/cart/CartStore.d.ts.map +1 -1
  70. package/lib/stores/cart/CartStore.js +66 -42
  71. package/lib/stores/cart/CartStore.js.map +1 -1
  72. package/lib/stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.d.ts +63 -0
  73. package/lib/stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.d.ts.map +1 -0
  74. package/lib/stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.js +228 -0
  75. package/lib/stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.js.map +1 -0
  76. package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.d.ts.map +1 -1
  77. package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.js +9 -13
  78. package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.js.map +1 -1
  79. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.d.ts +40 -0
  80. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.d.ts.map +1 -0
  81. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.js +84 -0
  82. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.js.map +1 -0
  83. package/package.json +8 -8
  84. package/src/__lib__/LegendMarketplaceAppEvent.ts +2 -0
  85. package/src/__lib__/LegendMarketplaceNavigation.ts +18 -1
  86. package/src/__lib__/LegendMarketplaceTelemetryHelper.ts +17 -1
  87. package/src/application/LegendMarketplaceWebApplication.tsx +13 -0
  88. package/src/application/providers/LegendMarketplaceFieldSearchResultsStoreProvider.tsx +67 -0
  89. package/src/components/AddToCart/CartDrawer.tsx +49 -4
  90. package/src/components/AddToCart/RecommendedAddOnsModal.tsx +86 -24
  91. package/src/components/AddToCart/RecommendedItemsCard.tsx +143 -120
  92. package/src/components/FieldSearchFiltersPanel/FieldSearchFiltersPanel.tsx +65 -0
  93. package/src/components/MarketplaceCard/FieldSearchResultListItem.tsx +163 -0
  94. package/src/components/MarketplaceSearchFiltersPanel/MarketplaceSearchFiltersPanel.tsx +2 -2
  95. package/src/components/ProviderCard/LegendMarketplaceTerminalCard.tsx +7 -0
  96. package/src/components/SearchBar/LegendMarketplaceSearchBar.tsx +44 -3
  97. package/src/pages/Lakehouse/MarketplaceLakehouseHome.tsx +9 -2
  98. package/src/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.tsx +380 -0
  99. package/src/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.tsx +19 -1
  100. package/src/pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.tsx +6 -2
  101. package/src/pages/__test-utils__/TEST_DATA__LakehouseDataProducts.ts +3 -2
  102. package/src/stores/cart/CartStore.ts +86 -51
  103. package/src/stores/lakehouse/LegendMarketplaceFieldSearchResultsStore.ts +309 -0
  104. package/src/stores/lakehouse/LegendMarketplaceProductViewerStore.ts +23 -30
  105. package/src/stores/lakehouse/fieldSearch/FieldSearchResultState.ts +122 -0
  106. package/tsconfig.json +6 -0
@@ -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
+ ));
@@ -0,0 +1,163 @@
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 { observer } from 'mobx-react-lite';
18
+ import { useState } from 'react';
19
+ import { Chip, Typography } from '@mui/material';
20
+ import { DatasetIcon, PackageIcon } from '@finos/legend-art';
21
+ import type {
22
+ FieldSearchDataProductEntry,
23
+ FieldSearchResultState,
24
+ } from '../../stores/lakehouse/fieldSearch/FieldSearchResultState.js';
25
+ import { LegendMarketplaceListItem } from '../MarketplaceCard/LegendMarketplaceListItem.js';
26
+
27
+ enum FieldSearchResultListItemLabel {
28
+ SHOW_LESS = 'Show Less',
29
+ MORE_SUFFIX = 'More',
30
+ DATASET_SEPARATOR = '|',
31
+ EMPTY_VALUE = '-',
32
+ }
33
+
34
+ enum FieldSearchResultListItemValue {
35
+ COLLAPSED_VISIBLE_DATA_PRODUCTS = 2,
36
+ MAX_DESCRIPTION_LENGTH = 150,
37
+ }
38
+
39
+ export const FieldSearchResultListRow = observer(
40
+ (props: {
41
+ fieldSearchResultState: FieldSearchResultState;
42
+ expanded: boolean;
43
+ onToggleExpanded: (rowId: string) => void;
44
+ onOpenDataProduct: (dataProduct: FieldSearchDataProductEntry) => void;
45
+ }): React.ReactNode => {
46
+ const {
47
+ fieldSearchResultState,
48
+ expanded,
49
+ onToggleExpanded,
50
+ onOpenDataProduct,
51
+ } = props;
52
+
53
+ const [descriptionExpanded, setDescriptionExpanded] = useState(false);
54
+ const description = fieldSearchResultState.fieldDescription;
55
+ const isDescriptionTruncatable =
56
+ description.length >
57
+ FieldSearchResultListItemValue.MAX_DESCRIPTION_LENGTH;
58
+ const displayDescription =
59
+ !descriptionExpanded && isDescriptionTruncatable
60
+ ? `${description.substring(0, FieldSearchResultListItemValue.MAX_DESCRIPTION_LENGTH)}...`
61
+ : description;
62
+
63
+ const visibleDataProducts = expanded
64
+ ? fieldSearchResultState.dataProducts
65
+ : fieldSearchResultState.dataProducts.slice(
66
+ 0,
67
+ FieldSearchResultListItemValue.COLLAPSED_VISIBLE_DATA_PRODUCTS,
68
+ );
69
+ const hiddenDataProductCount = Math.max(
70
+ 0,
71
+ fieldSearchResultState.dataProducts.length -
72
+ FieldSearchResultListItemValue.COLLAPSED_VISIBLE_DATA_PRODUCTS,
73
+ );
74
+ const toggleLabel = expanded
75
+ ? FieldSearchResultListItemLabel.SHOW_LESS
76
+ : `+${hiddenDataProductCount} ${FieldSearchResultListItemLabel.MORE_SUFFIX}`;
77
+
78
+ const content = (
79
+ <div className="marketplace-lakehouse-field-search-results__list-item-grid">
80
+ <div className="marketplace-lakehouse-field-search-results__field-name-cell">
81
+ <Typography className="marketplace-lakehouse-field-search-results__list-item-text marketplace-lakehouse-field-search-results__list-item-text--primary">
82
+ {fieldSearchResultState.fieldName}
83
+ </Typography>
84
+ </div>
85
+ <div className="marketplace-lakehouse-field-search-results__type-cell">
86
+ <Typography className="marketplace-lakehouse-field-search-results__list-item-text">
87
+ {fieldSearchResultState.fieldType}
88
+ </Typography>
89
+ </div>
90
+ <div className="marketplace-lakehouse-field-search-results__description-cell">
91
+ <Typography className="marketplace-lakehouse-field-search-results__list-item-text">
92
+ {displayDescription}
93
+ </Typography>
94
+ {isDescriptionTruncatable && (
95
+ <button
96
+ className="marketplace-lakehouse-field-search-results__description-toggle"
97
+ onClick={(e) => {
98
+ e.stopPropagation();
99
+ setDescriptionExpanded(!descriptionExpanded);
100
+ }}
101
+ >
102
+ {descriptionExpanded ? 'Show less' : 'Show more'}
103
+ </button>
104
+ )}
105
+ </div>
106
+ <div className="marketplace-lakehouse-field-search-results__data-products-cell">
107
+ {fieldSearchResultState.dataProducts.length > 0 ? (
108
+ <>
109
+ {visibleDataProducts.map((dataProduct) => (
110
+ <Chip
111
+ key={`${fieldSearchResultState.id}-${dataProduct.path}-${dataProduct.datasetName ?? ''}`}
112
+ clickable={true}
113
+ label={
114
+ <span className="marketplace-lakehouse-field-search-results__chip-label">
115
+ <PackageIcon className="marketplace-lakehouse-field-search-results__chip-icon" />
116
+ <span>{dataProduct.name}</span>
117
+ {dataProduct.datasetName && (
118
+ <span className="marketplace-lakehouse-field-search-results__chip-secondary">
119
+ <span className="marketplace-lakehouse-field-search-results__chip-separator">
120
+ {FieldSearchResultListItemLabel.DATASET_SEPARATOR}
121
+ </span>
122
+ <DatasetIcon className="marketplace-lakehouse-field-search-results__chip-icon" />
123
+ <span className="marketplace-lakehouse-field-search-results__chip-secondary-text">
124
+ {dataProduct.datasetName}
125
+ </span>
126
+ </span>
127
+ )}
128
+ </span>
129
+ }
130
+ onClick={() => onOpenDataProduct(dataProduct)}
131
+ className="marketplace-lakehouse-field-search-results__data-product-link"
132
+ size="small"
133
+ />
134
+ ))}
135
+ {fieldSearchResultState.dataProducts.length >
136
+ FieldSearchResultListItemValue.COLLAPSED_VISIBLE_DATA_PRODUCTS && (
137
+ <Chip
138
+ key={`${fieldSearchResultState.id}-toggle`}
139
+ label={toggleLabel}
140
+ onClick={() => onToggleExpanded(fieldSearchResultState.id)}
141
+ size="small"
142
+ variant="outlined"
143
+ className="marketplace-lakehouse-field-search-results__data-product-toggle"
144
+ />
145
+ )}
146
+ </>
147
+ ) : (
148
+ <Typography className="marketplace-lakehouse-field-search-results__list-item-text marketplace-lakehouse-field-search-results__list-item-text--empty">
149
+ {FieldSearchResultListItemLabel.EMPTY_VALUE}
150
+ </Typography>
151
+ )}
152
+ </div>
153
+ </div>
154
+ );
155
+
156
+ return (
157
+ <LegendMarketplaceListItem
158
+ className="marketplace-lakehouse-field-search-results__list-item"
159
+ content={content}
160
+ />
161
+ );
162
+ },
163
+ );
@@ -181,7 +181,7 @@ const TaxonomyTreeNode: React.FC<{
181
181
  );
182
182
  });
183
183
 
184
- const FilterSection: React.FC<{
184
+ export const FilterSection: React.FC<{
185
185
  title: string;
186
186
  children: React.ReactNode;
187
187
  }> = ({ title, children }) => (
@@ -195,7 +195,7 @@ const FilterSection: React.FC<{
195
195
  </div>
196
196
  );
197
197
 
198
- const FilterCheckboxOption: React.FC<{
198
+ export const FilterCheckboxOption: React.FC<{
199
199
  label: string;
200
200
  checked: boolean;
201
201
  onChange: () => void;
@@ -48,6 +48,9 @@ export const LegendMarketplaceTerminalCard = observer(
48
48
  );
49
49
 
50
50
  const [modalMessage, setModalMessage] = useState<string>('');
51
+ const [modalTotalCount, setModalTotalCount] = useState<
52
+ number | null | undefined
53
+ >(undefined);
51
54
 
52
55
  const legendMarketplaceBaseStore = useLegendMarketplaceBaseStore();
53
56
  const applicationStore = legendMarketplaceBaseStore.applicationStore;
@@ -72,6 +75,7 @@ export const LegendMarketplaceTerminalCard = observer(
72
75
  if (result.recommendations && result.recommendations.length > 0) {
73
76
  setRecommendedItems(result.recommendations);
74
77
  setModalMessage(result.message);
78
+ setModalTotalCount(result.totalCount);
75
79
  setShowRecommendationsModal(true);
76
80
  }
77
81
  } catch (error) {
@@ -97,9 +101,11 @@ export const LegendMarketplaceTerminalCard = observer(
97
101
  _selectedTerminal: TerminalResult,
98
102
  recommendations: TerminalResult[],
99
103
  responseMessage: string,
104
+ totalCount?: number | null,
100
105
  ) => {
101
106
  setRecommendedItems(recommendations);
102
107
  setModalMessage(responseMessage);
108
+ setModalTotalCount(totalCount);
103
109
  setShowRecommendationsModal(true);
104
110
  },
105
111
  [],
@@ -203,6 +209,7 @@ export const LegendMarketplaceTerminalCard = observer(
203
209
  setShowModal={setShowRecommendationsModal}
204
210
  onViewCart={handleViewCart}
205
211
  onTerminalSelected={handleTerminalSelected}
212
+ totalCount={modalTotalCount}
206
213
  />
207
214
  </Card>
208
215
  );