@finos/legend-application-marketplace 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/__lib__/LegendMarketplaceAppEvent.d.ts +1 -0
- package/lib/__lib__/LegendMarketplaceAppEvent.d.ts.map +1 -1
- package/lib/__lib__/LegendMarketplaceAppEvent.js +1 -0
- package/lib/__lib__/LegendMarketplaceAppEvent.js.map +1 -1
- package/lib/__lib__/LegendMarketplaceTelemetryHelper.d.ts +1 -0
- package/lib/__lib__/LegendMarketplaceTelemetryHelper.d.ts.map +1 -1
- package/lib/__lib__/LegendMarketplaceTelemetryHelper.js +8 -0
- package/lib/__lib__/LegendMarketplaceTelemetryHelper.js.map +1 -1
- package/lib/components/LegendServiceCard/LegendServiceCard.d.ts +2 -0
- package/lib/components/LegendServiceCard/LegendServiceCard.d.ts.map +1 -1
- package/lib/components/LegendServiceCard/LegendServiceCard.js +12 -8
- package/lib/components/LegendServiceCard/LegendServiceCard.js.map +1 -1
- package/lib/components/LegendServiceCard/LegendServiceGrid.d.ts +24 -0
- package/lib/components/LegendServiceCard/LegendServiceGrid.d.ts.map +1 -0
- package/lib/components/LegendServiceCard/LegendServiceGrid.js +124 -0
- package/lib/components/LegendServiceCard/LegendServiceGrid.js.map +1 -0
- package/lib/components/LegendServiceCard/LegendServiceListRow.d.ts +2 -0
- package/lib/components/LegendServiceCard/LegendServiceListRow.d.ts.map +1 -1
- package/lib/components/LegendServiceCard/LegendServiceListRow.js +8 -4
- package/lib/components/LegendServiceCard/LegendServiceListRow.js.map +1 -1
- package/lib/components/Pagination/PaginationControls.d.ts +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/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/pages/DataAPIs/LegendMarketplaceDataAPIs.d.ts.map +1 -1
- package/lib/pages/DataAPIs/LegendMarketplaceDataAPIs.js +17 -11
- package/lib/pages/DataAPIs/LegendMarketplaceDataAPIs.js.map +1 -1
- package/lib/pages/Lakehouse/MarketplaceLakehouseHome.d.ts.map +1 -1
- package/lib/pages/Lakehouse/MarketplaceLakehouseHome.js +4 -1
- package/lib/pages/Lakehouse/MarketplaceLakehouseHome.js.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.d.ts.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js +6 -2
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js.map +1 -1
- package/lib/stores/dataAPIs/LegendMarketplaceDataAPIsStore.d.ts +8 -1
- package/lib/stores/dataAPIs/LegendMarketplaceDataAPIsStore.d.ts.map +1 -1
- package/lib/stores/dataAPIs/LegendMarketplaceDataAPIsStore.js +37 -3
- package/lib/stores/dataAPIs/LegendMarketplaceDataAPIsStore.js.map +1 -1
- package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.d.ts +2 -0
- package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.d.ts.map +1 -1
- package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.js +8 -4
- package/lib/stores/lakehouse/LegendMarketplaceSearchResultsStore.js.map +1 -1
- package/package.json +13 -13
- package/src/__lib__/LegendMarketplaceAppEvent.ts +1 -0
- package/src/__lib__/LegendMarketplaceTelemetryHelper.ts +15 -0
- package/src/components/LegendServiceCard/LegendServiceCard.tsx +25 -3
- package/src/components/LegendServiceCard/LegendServiceGrid.tsx +206 -0
- package/src/components/LegendServiceCard/LegendServiceListRow.tsx +27 -3
- package/src/components/Pagination/PaginationControls.tsx +7 -4
- package/src/pages/DataAPIs/LegendMarketplaceDataAPIs.tsx +80 -14
- package/src/pages/Lakehouse/MarketplaceLakehouseHome.tsx +7 -1
- package/src/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.tsx +15 -2
- package/src/stores/dataAPIs/LegendMarketplaceDataAPIsStore.ts +58 -2
- package/src/stores/lakehouse/LegendMarketplaceSearchResultsStore.ts +11 -6
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,206 @@
|
|
|
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 { useCallback, useMemo, useRef } from 'react';
|
|
19
|
+
import { Chip, Tooltip } from '@mui/material';
|
|
20
|
+
import { clsx, DownloadIcon, StarIcon } from '@finos/legend-art';
|
|
21
|
+
import type {
|
|
22
|
+
LegendMarketplaceDataAPIsStore,
|
|
23
|
+
LegendServiceCardState,
|
|
24
|
+
} from '../../stores/dataAPIs/LegendMarketplaceDataAPIsStore.js';
|
|
25
|
+
import { ServiceOwnershipType } from '@finos/legend-graph';
|
|
26
|
+
import { isString } from '@finos/legend-shared';
|
|
27
|
+
import {
|
|
28
|
+
DataGrid,
|
|
29
|
+
type DataGridApi,
|
|
30
|
+
type DataGridCellRendererParams,
|
|
31
|
+
type DataGridColumnDefinition,
|
|
32
|
+
} from '@finos/legend-lego/data-grid';
|
|
33
|
+
|
|
34
|
+
const FavoriteCellRenderer = observer(
|
|
35
|
+
(
|
|
36
|
+
params: DataGridCellRendererParams<LegendServiceCardState> & {
|
|
37
|
+
store: LegendMarketplaceDataAPIsStore;
|
|
38
|
+
},
|
|
39
|
+
): React.ReactNode => {
|
|
40
|
+
const { data, store } = params;
|
|
41
|
+
if (!data) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const isFav = store.isFavorite(data.service.pattern);
|
|
45
|
+
return (
|
|
46
|
+
<button
|
|
47
|
+
className={clsx('marketplace-legend-service-grid__star-btn', {
|
|
48
|
+
'marketplace-legend-service-grid__star-btn--active': isFav,
|
|
49
|
+
})}
|
|
50
|
+
onClick={(e) => {
|
|
51
|
+
e.stopPropagation();
|
|
52
|
+
store.toggleFavorite(data.service.pattern);
|
|
53
|
+
}}
|
|
54
|
+
title={isFav ? 'Remove from favorites' : 'Add to favorites'}
|
|
55
|
+
>
|
|
56
|
+
<StarIcon />
|
|
57
|
+
</button>
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const OwnersCellRenderer = observer(
|
|
63
|
+
(
|
|
64
|
+
params: DataGridCellRendererParams<LegendServiceCardState>,
|
|
65
|
+
): React.ReactNode => {
|
|
66
|
+
const data = params.data;
|
|
67
|
+
if (!data) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return (
|
|
71
|
+
<div className="marketplace-legend-service-grid__chips">
|
|
72
|
+
{data.owners.map((owner) => (
|
|
73
|
+
<Chip
|
|
74
|
+
key={owner}
|
|
75
|
+
size="small"
|
|
76
|
+
label={owner}
|
|
77
|
+
className={`marketplace-legend-service-list-row__chip marketplace-legend-service-list-row__chip--${
|
|
78
|
+
data.ownershipType === ServiceOwnershipType.DEPLOYMENT_OWNERSHIP
|
|
79
|
+
? 'did'
|
|
80
|
+
: 'owner'
|
|
81
|
+
}`}
|
|
82
|
+
/>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
export const LegendServiceGridView = observer(
|
|
90
|
+
(props: {
|
|
91
|
+
services: LegendServiceCardState[];
|
|
92
|
+
store: LegendMarketplaceDataAPIsStore;
|
|
93
|
+
onRowClick: (serviceCardState: LegendServiceCardState) => void;
|
|
94
|
+
}): React.ReactNode => {
|
|
95
|
+
const { services, store, onRowClick } = props;
|
|
96
|
+
const gridApiRef = useRef<DataGridApi<LegendServiceCardState> | null>(null);
|
|
97
|
+
|
|
98
|
+
const columnDefs: DataGridColumnDefinition<LegendServiceCardState>[] =
|
|
99
|
+
useMemo(
|
|
100
|
+
() => [
|
|
101
|
+
{
|
|
102
|
+
headerName: '',
|
|
103
|
+
colId: 'favorite',
|
|
104
|
+
cellRenderer: FavoriteCellRenderer,
|
|
105
|
+
cellRendererParams: { store },
|
|
106
|
+
width: 50,
|
|
107
|
+
maxWidth: 50,
|
|
108
|
+
minWidth: 50,
|
|
109
|
+
resizable: false,
|
|
110
|
+
sortable: false,
|
|
111
|
+
filter: false,
|
|
112
|
+
suppressHeaderMenuButton: true,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
headerName: 'Title',
|
|
116
|
+
colId: 'title',
|
|
117
|
+
valueGetter: (p) => p.data?.title,
|
|
118
|
+
minWidth: 150,
|
|
119
|
+
flex: 2,
|
|
120
|
+
filter: true,
|
|
121
|
+
resizable: true,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
headerName: 'URL Path',
|
|
125
|
+
colId: 'urlPath',
|
|
126
|
+
valueGetter: (p) => p.data?.service.pattern,
|
|
127
|
+
minWidth: 200,
|
|
128
|
+
flex: 3,
|
|
129
|
+
filter: true,
|
|
130
|
+
resizable: true,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
headerName: 'Description',
|
|
134
|
+
colId: 'description',
|
|
135
|
+
valueGetter: (p) => p.data?.description,
|
|
136
|
+
minWidth: 200,
|
|
137
|
+
flex: 4,
|
|
138
|
+
filter: true,
|
|
139
|
+
resizable: true,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
headerName: 'Owner / DID',
|
|
143
|
+
colId: 'owners',
|
|
144
|
+
cellRenderer: OwnersCellRenderer,
|
|
145
|
+
valueGetter: (p) => p.data?.owners.join(', '),
|
|
146
|
+
minWidth: 150,
|
|
147
|
+
flex: 2,
|
|
148
|
+
filter: true,
|
|
149
|
+
resizable: true,
|
|
150
|
+
autoHeight: true,
|
|
151
|
+
wrapText: true,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
[store],
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const exportToCSV = useCallback((): void => {
|
|
158
|
+
gridApiRef.current?.exportDataAsCsv({
|
|
159
|
+
fileName: 'legend-services.csv',
|
|
160
|
+
columnKeys: ['title', 'urlPath', 'description', 'owners'],
|
|
161
|
+
processCellCallback: (params) => {
|
|
162
|
+
const value: unknown = params.value;
|
|
163
|
+
if (isString(value)) {
|
|
164
|
+
return value.replaceAll(/[\r\n]+/g, ' ');
|
|
165
|
+
}
|
|
166
|
+
return isString(value) ? value : '';
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}, []);
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div className="marketplace-legend-service-grid">
|
|
173
|
+
<div className="marketplace-legend-service-grid__toolbar">
|
|
174
|
+
<span className="marketplace-legend-service-grid__toolbar__count">
|
|
175
|
+
{`${services.length} result${services.length === 1 ? '' : 's'}`}
|
|
176
|
+
</span>
|
|
177
|
+
<Tooltip title="Export visible rows to CSV" placement="left">
|
|
178
|
+
<button
|
|
179
|
+
className="marketplace-legend-service-grid__toolbar__export-btn"
|
|
180
|
+
onClick={exportToCSV}
|
|
181
|
+
>
|
|
182
|
+
<DownloadIcon />
|
|
183
|
+
Export to CSV
|
|
184
|
+
</button>
|
|
185
|
+
</Tooltip>
|
|
186
|
+
</div>
|
|
187
|
+
<div className="marketplace-legend-service-grid__ag-grid ag-theme-balham">
|
|
188
|
+
<DataGrid
|
|
189
|
+
rowData={services}
|
|
190
|
+
columnDefs={columnDefs}
|
|
191
|
+
onGridReady={(params) => {
|
|
192
|
+
gridApiRef.current = params.api;
|
|
193
|
+
}}
|
|
194
|
+
onCellClicked={(event) => {
|
|
195
|
+
if (event.data && event.column.getColId() !== 'favorite') {
|
|
196
|
+
onRowClick(event.data);
|
|
197
|
+
}
|
|
198
|
+
}}
|
|
199
|
+
suppressCellFocus={true}
|
|
200
|
+
overlayNoRowsTemplate="No services match the filters."
|
|
201
|
+
/>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
);
|
|
205
|
+
},
|
|
206
|
+
);
|
|
@@ -16,8 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
import { observer } from 'mobx-react-lite';
|
|
18
18
|
import { useState } from 'react';
|
|
19
|
-
import { Chip } from '@mui/material';
|
|
20
|
-
import {
|
|
19
|
+
import { Chip, IconButton } from '@mui/material';
|
|
20
|
+
import {
|
|
21
|
+
clsx,
|
|
22
|
+
MarkdownTextViewer,
|
|
23
|
+
StarIcon,
|
|
24
|
+
EmptyStarIcon,
|
|
25
|
+
} from '@finos/legend-art';
|
|
21
26
|
import type { LegendServiceCardState } from '../../stores/dataAPIs/LegendMarketplaceDataAPIsStore.js';
|
|
22
27
|
import { ServiceOwnershipType } from '@finos/legend-graph';
|
|
23
28
|
import { LegendMarketplaceListItem } from '../MarketplaceCard/LegendMarketplaceListItem.js';
|
|
@@ -28,8 +33,10 @@ export const LegendServiceListRow = observer(
|
|
|
28
33
|
(props: {
|
|
29
34
|
serviceCardState: LegendServiceCardState;
|
|
30
35
|
onClick: () => void;
|
|
36
|
+
isFavorite: boolean;
|
|
37
|
+
onToggleFavorite: () => void;
|
|
31
38
|
}): React.ReactNode => {
|
|
32
|
-
const { serviceCardState, onClick } = props;
|
|
39
|
+
const { serviceCardState, onClick, isFavorite, onToggleFavorite } = props;
|
|
33
40
|
const [expanded, setExpanded] = useState(false);
|
|
34
41
|
const description = serviceCardState.description;
|
|
35
42
|
const isTruncatable = description.length > MAX_DESCRIPTION_LENGTH;
|
|
@@ -46,6 +53,23 @@ export const LegendServiceListRow = observer(
|
|
|
46
53
|
content={
|
|
47
54
|
<div className="marketplace-legend-service-list-row__body">
|
|
48
55
|
<div className="marketplace-legend-service-list-row__header">
|
|
56
|
+
<IconButton
|
|
57
|
+
className={clsx(
|
|
58
|
+
'marketplace-legend-service-list-row__favorite-btn',
|
|
59
|
+
isFavorite &&
|
|
60
|
+
'marketplace-legend-service-list-row__favorite-btn--active',
|
|
61
|
+
)}
|
|
62
|
+
onClick={(e) => {
|
|
63
|
+
e.stopPropagation();
|
|
64
|
+
onToggleFavorite();
|
|
65
|
+
}}
|
|
66
|
+
size="small"
|
|
67
|
+
title={
|
|
68
|
+
isFavorite ? 'Remove from favorites' : 'Add to favorites'
|
|
69
|
+
}
|
|
70
|
+
>
|
|
71
|
+
{isFavorite ? <StarIcon /> : <EmptyStarIcon />}
|
|
72
|
+
</IconButton>
|
|
49
73
|
<div className="marketplace-legend-service-list-row__title-block">
|
|
50
74
|
<div className="marketplace-legend-service-list-row__name">
|
|
51
75
|
{serviceCardState.title}
|
|
@@ -33,6 +33,7 @@ export const PaginationControls = observer(
|
|
|
33
33
|
onPageChange: (page: number) => void;
|
|
34
34
|
onItemsPerPageChange: (itemsPerPage: number) => void;
|
|
35
35
|
disabled?: boolean;
|
|
36
|
+
pageSizeOptions?: number[] | undefined;
|
|
36
37
|
}) => {
|
|
37
38
|
const {
|
|
38
39
|
totalItems,
|
|
@@ -41,6 +42,7 @@ export const PaginationControls = observer(
|
|
|
41
42
|
onPageChange,
|
|
42
43
|
onItemsPerPageChange,
|
|
43
44
|
disabled = false,
|
|
45
|
+
pageSizeOptions = [12, 24, 36, 48],
|
|
44
46
|
} = props;
|
|
45
47
|
|
|
46
48
|
const totalPages = Math.ceil(totalItems / itemsPerPage);
|
|
@@ -75,10 +77,11 @@ export const PaginationControls = observer(
|
|
|
75
77
|
size="medium"
|
|
76
78
|
disabled={disabled}
|
|
77
79
|
>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
{pageSizeOptions.map((option) => (
|
|
81
|
+
<MenuItem key={option} value={option}>
|
|
82
|
+
{option}
|
|
83
|
+
</MenuItem>
|
|
84
|
+
))}
|
|
82
85
|
</Select>
|
|
83
86
|
</Box>
|
|
84
87
|
<Box className="legend-marketplace-pagination-info">
|
|
@@ -26,6 +26,8 @@ import {
|
|
|
26
26
|
CheckIcon,
|
|
27
27
|
CubesLoadingIndicator,
|
|
28
28
|
CubesLoadingIndicatorIcon,
|
|
29
|
+
StarIcon,
|
|
30
|
+
TableIcon,
|
|
29
31
|
ViewHeadlineIcon,
|
|
30
32
|
WindowIcon,
|
|
31
33
|
clsx,
|
|
@@ -48,6 +50,7 @@ import {
|
|
|
48
50
|
} from '../../stores/dataAPIs/LegendMarketplaceDataAPIsStore.js';
|
|
49
51
|
import { LegendServiceCard } from '../../components/LegendServiceCard/LegendServiceCard.js';
|
|
50
52
|
import { LegendServiceListRow } from '../../components/LegendServiceCard/LegendServiceListRow.js';
|
|
53
|
+
import { LegendServiceGridView } from '../../components/LegendServiceCard/LegendServiceGrid.js';
|
|
51
54
|
import { PaginationControls } from '../../components/Pagination/PaginationControls.js';
|
|
52
55
|
import { DataAPIsFiltersPanel } from '../../components/DataAPIsFiltersPanel/DataAPIsFiltersPanel.js';
|
|
53
56
|
import { useSearchParams } from '@finos/legend-application/browser';
|
|
@@ -68,7 +71,6 @@ export const LegendMarketplaceDataAPIs = withLegendMarketplaceDataAPIsStore(
|
|
|
68
71
|
);
|
|
69
72
|
|
|
70
73
|
useEffect(() => {
|
|
71
|
-
dataAPIsStore.setItemsPerPage(12);
|
|
72
74
|
if (queryFromUrl) {
|
|
73
75
|
dataAPIsStore.setSearchQuery(queryFromUrl);
|
|
74
76
|
}
|
|
@@ -150,6 +152,14 @@ export const LegendMarketplaceDataAPIs = withLegendMarketplaceDataAPIsStore(
|
|
|
150
152
|
key={serviceCardState.guid}
|
|
151
153
|
serviceCardState={serviceCardState}
|
|
152
154
|
onClick={() => handleServiceClick(serviceCardState)}
|
|
155
|
+
isFavorite={dataAPIsStore.isFavorite(
|
|
156
|
+
serviceCardState.service.pattern,
|
|
157
|
+
)}
|
|
158
|
+
onToggleFavorite={() =>
|
|
159
|
+
dataAPIsStore.toggleFavorite(
|
|
160
|
+
serviceCardState.service.pattern,
|
|
161
|
+
)
|
|
162
|
+
}
|
|
153
163
|
/>
|
|
154
164
|
))}
|
|
155
165
|
</div>
|
|
@@ -163,6 +173,14 @@ export const LegendMarketplaceDataAPIs = withLegendMarketplaceDataAPIsStore(
|
|
|
163
173
|
)}
|
|
164
174
|
</>
|
|
165
175
|
);
|
|
176
|
+
case ServicesViewMode.GRID:
|
|
177
|
+
return (
|
|
178
|
+
<LegendServiceGridView
|
|
179
|
+
services={dataAPIsStore.filteredSortedServices}
|
|
180
|
+
store={dataAPIsStore}
|
|
181
|
+
onRowClick={handleServiceClick}
|
|
182
|
+
/>
|
|
183
|
+
);
|
|
166
184
|
case ServicesViewMode.TILE:
|
|
167
185
|
return (
|
|
168
186
|
<Grid
|
|
@@ -176,6 +194,14 @@ export const LegendMarketplaceDataAPIs = withLegendMarketplaceDataAPIsStore(
|
|
|
176
194
|
<LegendServiceCard
|
|
177
195
|
serviceCardState={serviceCardState}
|
|
178
196
|
onClick={() => handleServiceClick(serviceCardState)}
|
|
197
|
+
isFavorite={dataAPIsStore.isFavorite(
|
|
198
|
+
serviceCardState.service.pattern,
|
|
199
|
+
)}
|
|
200
|
+
onToggleFavorite={() =>
|
|
201
|
+
dataAPIsStore.toggleFavorite(
|
|
202
|
+
serviceCardState.service.pattern,
|
|
203
|
+
)
|
|
204
|
+
}
|
|
179
205
|
/>
|
|
180
206
|
</Grid>
|
|
181
207
|
))}
|
|
@@ -231,11 +257,34 @@ export const LegendMarketplaceDataAPIs = withLegendMarketplaceDataAPIsStore(
|
|
|
231
257
|
label="My Services"
|
|
232
258
|
/>
|
|
233
259
|
<span className="legend-marketplace-search-results__sort-bar__controls-divider" />
|
|
234
|
-
<
|
|
260
|
+
<IconButton
|
|
261
|
+
className={clsx(
|
|
262
|
+
'legend-marketplace-search-results__favorites-toggle',
|
|
263
|
+
dataAPIsStore.showFavoritesOnly &&
|
|
264
|
+
'legend-marketplace-search-results__favorites-toggle--active',
|
|
265
|
+
)}
|
|
266
|
+
onClick={() =>
|
|
267
|
+
dataAPIsStore.setShowFavoritesOnly(
|
|
268
|
+
!dataAPIsStore.showFavoritesOnly,
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
title={
|
|
272
|
+
dataAPIsStore.showFavoritesOnly
|
|
273
|
+
? 'Show all services'
|
|
274
|
+
: 'Show favorites only'
|
|
275
|
+
}
|
|
276
|
+
size="small"
|
|
277
|
+
>
|
|
278
|
+
<StarIcon />
|
|
279
|
+
</IconButton>
|
|
280
|
+
<span className="legend-marketplace-search-results__sort-bar__controls-divider" />
|
|
281
|
+
<div className="legend-marketplace-search-results__view-toggle legend-marketplace-search-results__view-toggle--three">
|
|
235
282
|
<div
|
|
236
283
|
className={clsx(
|
|
237
284
|
'legend-marketplace-search-results__view-toggle__slider',
|
|
238
285
|
viewMode === ServicesViewMode.LIST &&
|
|
286
|
+
'legend-marketplace-search-results__view-toggle__slider--middle',
|
|
287
|
+
viewMode === ServicesViewMode.GRID &&
|
|
239
288
|
'legend-marketplace-search-results__view-toggle__slider--right',
|
|
240
289
|
)}
|
|
241
290
|
/>
|
|
@@ -247,8 +296,6 @@ export const LegendMarketplaceDataAPIs = withLegendMarketplaceDataAPIsStore(
|
|
|
247
296
|
)}
|
|
248
297
|
onClick={() => {
|
|
249
298
|
dataAPIsStore.setViewMode(ServicesViewMode.TILE);
|
|
250
|
-
dataAPIsStore.setItemsPerPage(12);
|
|
251
|
-
dataAPIsStore.setPage(1);
|
|
252
299
|
LegendMarketplaceTelemetryHelper.logEvent_ToggleServicesViewMode(
|
|
253
300
|
applicationStore.telemetryService,
|
|
254
301
|
ServicesViewMode.TILE,
|
|
@@ -267,8 +314,6 @@ export const LegendMarketplaceDataAPIs = withLegendMarketplaceDataAPIsStore(
|
|
|
267
314
|
)}
|
|
268
315
|
onClick={() => {
|
|
269
316
|
dataAPIsStore.setViewMode(ServicesViewMode.LIST);
|
|
270
|
-
dataAPIsStore.setItemsPerPage(12);
|
|
271
|
-
dataAPIsStore.setPage(1);
|
|
272
317
|
LegendMarketplaceTelemetryHelper.logEvent_ToggleServicesViewMode(
|
|
273
318
|
applicationStore.telemetryService,
|
|
274
319
|
ServicesViewMode.LIST,
|
|
@@ -279,6 +324,24 @@ export const LegendMarketplaceDataAPIs = withLegendMarketplaceDataAPIsStore(
|
|
|
279
324
|
>
|
|
280
325
|
<ViewHeadlineIcon />
|
|
281
326
|
</IconButton>
|
|
327
|
+
<IconButton
|
|
328
|
+
className={clsx(
|
|
329
|
+
'legend-marketplace-search-results__view-toggle__btn',
|
|
330
|
+
viewMode === ServicesViewMode.GRID &&
|
|
331
|
+
'legend-marketplace-search-results__view-toggle__btn--active',
|
|
332
|
+
)}
|
|
333
|
+
onClick={() => {
|
|
334
|
+
dataAPIsStore.setViewMode(ServicesViewMode.GRID);
|
|
335
|
+
LegendMarketplaceTelemetryHelper.logEvent_ToggleServicesViewMode(
|
|
336
|
+
applicationStore.telemetryService,
|
|
337
|
+
ServicesViewMode.GRID,
|
|
338
|
+
);
|
|
339
|
+
}}
|
|
340
|
+
title="Grid View"
|
|
341
|
+
size="small"
|
|
342
|
+
>
|
|
343
|
+
<TableIcon />
|
|
344
|
+
</IconButton>
|
|
282
345
|
</div>
|
|
283
346
|
<span className="legend-marketplace-search-results__sort-bar__controls-divider" />
|
|
284
347
|
<FormControl>
|
|
@@ -349,14 +412,17 @@ export const LegendMarketplaceDataAPIs = withLegendMarketplaceDataAPIsStore(
|
|
|
349
412
|
>
|
|
350
413
|
{renderServiceView()}
|
|
351
414
|
</div>
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
415
|
+
{viewMode !== ServicesViewMode.GRID && (
|
|
416
|
+
<PaginationControls
|
|
417
|
+
totalItems={dataAPIsStore.totalFilteredCount}
|
|
418
|
+
itemsPerPage={dataAPIsStore.itemsPerPage}
|
|
419
|
+
page={dataAPIsStore.page}
|
|
420
|
+
onPageChange={handlePageChange}
|
|
421
|
+
onItemsPerPageChange={handleItemsPerPageChange}
|
|
422
|
+
disabled={dataAPIsStore.isLoading}
|
|
423
|
+
pageSizeOptions={[12, 25, 50, 100]}
|
|
424
|
+
/>
|
|
425
|
+
)}
|
|
360
426
|
</>
|
|
361
427
|
)}
|
|
362
428
|
</div>
|
|
@@ -46,6 +46,8 @@ import { logClickingDataProductCard } from '../../utils/LogUtils.js';
|
|
|
46
46
|
import { LakehouseProductCard } from '../../components/LakehouseProductCard/LakehouseProductCard.js';
|
|
47
47
|
import type { HomePageBannerConfig } from '../../application/LegendMarketplaceApplicationPlugin.js';
|
|
48
48
|
|
|
49
|
+
const TRENDING_DATA_PRODUCTS = 4;
|
|
50
|
+
|
|
49
51
|
export const MarketplaceLakehouseHome = observer(() => {
|
|
50
52
|
const legendMarketplaceBaseStore = useLegendMarketplaceBaseStore();
|
|
51
53
|
const applicationStore = legendMarketplaceBaseStore.applicationStore;
|
|
@@ -138,7 +140,11 @@ export const MarketplaceLakehouseHome = observer(() => {
|
|
|
138
140
|
const result: Record<string, ProductCardState[]> = {
|
|
139
141
|
...configDataProducts,
|
|
140
142
|
};
|
|
141
|
-
if (
|
|
143
|
+
if (
|
|
144
|
+
trendingDataProducts &&
|
|
145
|
+
Object.values(trendingDataProducts).flat().length >=
|
|
146
|
+
TRENDING_DATA_PRODUCTS
|
|
147
|
+
) {
|
|
142
148
|
Object.assign(result, trendingDataProducts);
|
|
143
149
|
}
|
|
144
150
|
|
|
@@ -145,7 +145,9 @@ const SearchResultsContent = observer(
|
|
|
145
145
|
</div>
|
|
146
146
|
)}
|
|
147
147
|
{searchResultsStore.isOnLastPage &&
|
|
148
|
-
!searchResultsStore.showAllProducts &&
|
|
148
|
+
!searchResultsStore.showAllProducts &&
|
|
149
|
+
!searchResultsStore.useProducerSearch &&
|
|
150
|
+
searchResultsStore.hasFilteredDataProducts && (
|
|
149
151
|
<div className="marketplace-lakehouse-search-results__show-all-container">
|
|
150
152
|
<div className="marketplace-lakehouse-search-results__show-all-text-row">
|
|
151
153
|
<Typography
|
|
@@ -325,8 +327,19 @@ export const LegendMarketplaceSearchResults =
|
|
|
325
327
|
[applicationStore],
|
|
326
328
|
);
|
|
327
329
|
const handleShowAllProducts = useCallback(() => {
|
|
330
|
+
LegendMarketplaceTelemetryHelper.logEvent_ShowAllDataProducts(
|
|
331
|
+
applicationStore.telemetryService,
|
|
332
|
+
searchResultsStore.searchQuery,
|
|
333
|
+
);
|
|
328
334
|
searchResultsStore.setShowAllProducts(true);
|
|
329
|
-
|
|
335
|
+
flowResult(
|
|
336
|
+
searchResultsStore.executeSearch(
|
|
337
|
+
searchResultsStore.searchQuery ?? '',
|
|
338
|
+
searchResultsStore.useProducerSearch ?? false,
|
|
339
|
+
tokenRef.current,
|
|
340
|
+
),
|
|
341
|
+
).catch(applicationStore.alertUnhandledError);
|
|
342
|
+
}, [searchResultsStore, applicationStore]);
|
|
330
343
|
|
|
331
344
|
return (
|
|
332
345
|
<LegendMarketplacePage className="marketplace-lakehouse-search-results">
|
|
@@ -19,6 +19,7 @@ import type { LegendMarketplaceBaseStore } from '../LegendMarketplaceBaseStore.j
|
|
|
19
19
|
import {
|
|
20
20
|
ActionState,
|
|
21
21
|
assertErrorThrown,
|
|
22
|
+
isString,
|
|
22
23
|
type GeneratorFn,
|
|
23
24
|
} from '@finos/legend-shared';
|
|
24
25
|
import {
|
|
@@ -38,6 +39,7 @@ export enum LegendServiceSort {
|
|
|
38
39
|
export enum ServicesViewMode {
|
|
39
40
|
LIST = 'list',
|
|
40
41
|
TILE = 'tile',
|
|
42
|
+
GRID = 'grid',
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export class LegendServiceCardState {
|
|
@@ -119,6 +121,10 @@ const LEGEND_MARKETPLACE_SETTING_KEY_SERVICES_VIEW_MODE =
|
|
|
119
121
|
'marketplace.data-apis.viewMode';
|
|
120
122
|
const LEGEND_MARKETPLACE_SETTING_KEY_SHOW_OWN_SERVICES =
|
|
121
123
|
'marketplace.data-apis.showOwnServicesOnly';
|
|
124
|
+
const LEGEND_MARKETPLACE_SETTING_KEY_FAVORITES =
|
|
125
|
+
'marketplace.data-apis.favorites';
|
|
126
|
+
const LEGEND_MARKETPLACE_SETTING_KEY_ITEMS_PER_PAGE =
|
|
127
|
+
'marketplace.data-apis.itemsPerPage';
|
|
122
128
|
|
|
123
129
|
export class LegendMarketplaceDataAPIsStore {
|
|
124
130
|
readonly marketplaceBaseStore: LegendMarketplaceBaseStore;
|
|
@@ -127,12 +133,15 @@ export class LegendMarketplaceDataAPIsStore {
|
|
|
127
133
|
sort: LegendServiceSort = LegendServiceSort.DEFAULT;
|
|
128
134
|
viewMode: ServicesViewMode;
|
|
129
135
|
showOwnServicesOnly: boolean;
|
|
136
|
+
showFavoritesOnly = false;
|
|
137
|
+
favoritePatterns: Set<string>;
|
|
130
138
|
serviceCardStates: LegendServiceCardState[] = [];
|
|
131
139
|
page = 1;
|
|
132
|
-
itemsPerPage
|
|
140
|
+
itemsPerPage: number;
|
|
133
141
|
|
|
134
142
|
ownerFilters: string[] = [];
|
|
135
143
|
deploymentIdFilters: string[] = [];
|
|
144
|
+
favorites: Set<string> = new Set();
|
|
136
145
|
|
|
137
146
|
readonly fetchingServicesState = ActionState.create();
|
|
138
147
|
|
|
@@ -164,20 +173,37 @@ export class LegendMarketplaceDataAPIsStore {
|
|
|
164
173
|
);
|
|
165
174
|
this.showOwnServicesOnly = persistedShowOwn ?? false;
|
|
166
175
|
|
|
176
|
+
const persistedFavorites =
|
|
177
|
+
(this.marketplaceBaseStore.applicationStore.settingService.getObjectValue(
|
|
178
|
+
LEGEND_MARKETPLACE_SETTING_KEY_FAVORITES,
|
|
179
|
+
) as string[] | undefined) ?? [];
|
|
180
|
+
this.favoritePatterns = new Set(persistedFavorites.filter(isString));
|
|
181
|
+
|
|
182
|
+
const persistedItemsPerPage =
|
|
183
|
+
this.marketplaceBaseStore.applicationStore.settingService.getNumericValue(
|
|
184
|
+
LEGEND_MARKETPLACE_SETTING_KEY_ITEMS_PER_PAGE,
|
|
185
|
+
);
|
|
186
|
+
this.itemsPerPage = persistedItemsPerPage ?? 12;
|
|
187
|
+
|
|
167
188
|
makeObservable(this, {
|
|
168
189
|
searchQuery: observable,
|
|
169
190
|
sort: observable,
|
|
170
191
|
viewMode: observable,
|
|
171
192
|
showOwnServicesOnly: observable,
|
|
193
|
+
showFavoritesOnly: observable,
|
|
194
|
+
favoritePatterns: observable.shallow,
|
|
172
195
|
serviceCardStates: observable,
|
|
173
196
|
page: observable,
|
|
174
197
|
itemsPerPage: observable,
|
|
175
198
|
ownerFilters: observable,
|
|
176
199
|
deploymentIdFilters: observable,
|
|
200
|
+
favorites: observable,
|
|
177
201
|
setSearchQuery: action,
|
|
178
202
|
setSort: action,
|
|
179
203
|
setViewMode: action,
|
|
180
204
|
setShowOwnServicesOnly: action,
|
|
205
|
+
setShowFavoritesOnly: action,
|
|
206
|
+
toggleFavorite: action,
|
|
181
207
|
setPage: action,
|
|
182
208
|
setItemsPerPage: action,
|
|
183
209
|
addOwnerFilter: action,
|
|
@@ -220,13 +246,37 @@ export class LegendMarketplaceDataAPIsStore {
|
|
|
220
246
|
);
|
|
221
247
|
}
|
|
222
248
|
|
|
249
|
+
setShowFavoritesOnly(value: boolean): void {
|
|
250
|
+
this.showFavoritesOnly = value;
|
|
251
|
+
this.page = 1;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
isFavorite(pattern: string): boolean {
|
|
255
|
+
return this.favoritePatterns.has(pattern);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
toggleFavorite(pattern: string): void {
|
|
259
|
+
if (this.favoritePatterns.has(pattern)) {
|
|
260
|
+
this.favoritePatterns.delete(pattern);
|
|
261
|
+
} else {
|
|
262
|
+
this.favoritePatterns.add(pattern);
|
|
263
|
+
}
|
|
264
|
+
this.marketplaceBaseStore.applicationStore.settingService.persistValue(
|
|
265
|
+
LEGEND_MARKETPLACE_SETTING_KEY_FAVORITES,
|
|
266
|
+
Array.from(this.favoritePatterns),
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
223
270
|
setPage(value: number): void {
|
|
224
271
|
this.page = value;
|
|
225
272
|
}
|
|
226
273
|
|
|
227
274
|
setItemsPerPage(value: number): void {
|
|
228
275
|
this.itemsPerPage = value;
|
|
229
|
-
this.
|
|
276
|
+
this.marketplaceBaseStore.applicationStore.settingService.persistValue(
|
|
277
|
+
LEGEND_MARKETPLACE_SETTING_KEY_ITEMS_PER_PAGE,
|
|
278
|
+
value,
|
|
279
|
+
);
|
|
230
280
|
}
|
|
231
281
|
|
|
232
282
|
private persistOwnerFilters(): void {
|
|
@@ -320,6 +370,12 @@ export class LegendMarketplaceDataAPIsStore {
|
|
|
320
370
|
get filteredSortedServices(): LegendServiceCardState[] {
|
|
321
371
|
let results = this.serviceCardStates;
|
|
322
372
|
|
|
373
|
+
if (this.showFavoritesOnly) {
|
|
374
|
+
results = results.filter((card) =>
|
|
375
|
+
this.favoritePatterns.has(card.service.pattern),
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
323
379
|
if (this.showOwnServicesOnly) {
|
|
324
380
|
const currentUser =
|
|
325
381
|
this.marketplaceBaseStore.applicationStore.identityService.currentUser;
|