@finos/legend-application-marketplace 0.2.13 → 0.2.15

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 (120) hide show
  1. package/lib/__lib__/LegendMarketplaceNavigation.d.ts +2 -0
  2. package/lib/__lib__/LegendMarketplaceNavigation.d.ts.map +1 -1
  3. package/lib/__lib__/LegendMarketplaceNavigation.js +4 -0
  4. package/lib/__lib__/LegendMarketplaceNavigation.js.map +1 -1
  5. package/lib/application/LegendMarketplaceApplicationConfig.d.ts +9 -0
  6. package/lib/application/LegendMarketplaceApplicationConfig.d.ts.map +1 -1
  7. package/lib/application/LegendMarketplaceApplicationConfig.js +40 -22
  8. package/lib/application/LegendMarketplaceApplicationConfig.js.map +1 -1
  9. package/lib/application/LegendMarketplaceWebApplication.d.ts.map +1 -1
  10. package/lib/application/LegendMarketplaceWebApplication.js +4 -1
  11. package/lib/application/LegendMarketplaceWebApplication.js.map +1 -1
  12. package/lib/application/__test-utils__/LegendMarketplaceApplicationTestUtils.d.ts.map +1 -1
  13. package/lib/application/__test-utils__/LegendMarketplaceApplicationTestUtils.js +7 -0
  14. package/lib/application/__test-utils__/LegendMarketplaceApplicationTestUtils.js.map +1 -1
  15. package/lib/application/providers/LegendMarketplaceAIChatStoreProvider.d.ts +21 -0
  16. package/lib/application/providers/LegendMarketplaceAIChatStoreProvider.d.ts.map +1 -0
  17. package/lib/application/providers/LegendMarketplaceAIChatStoreProvider.js +29 -0
  18. package/lib/application/providers/LegendMarketplaceAIChatStoreProvider.js.map +1 -0
  19. package/lib/index.css +2 -2
  20. package/lib/index.css.map +1 -1
  21. package/lib/package.json +1 -1
  22. package/lib/pages/Agents/LegendMarketplaceAgents.d.ts.map +1 -1
  23. package/lib/pages/Agents/LegendMarketplaceAgents.js +49 -17
  24. package/lib/pages/Agents/LegendMarketplaceAgents.js.map +1 -1
  25. package/lib/pages/Agents/MarketplaceAIChatView.d.ts +21 -0
  26. package/lib/pages/Agents/MarketplaceAIChatView.d.ts.map +1 -0
  27. package/lib/pages/Agents/MarketplaceAIChatView.js +141 -0
  28. package/lib/pages/Agents/MarketplaceAIChatView.js.map +1 -0
  29. package/lib/pages/Agents/MarketplaceAIInputBar.d.ts +22 -0
  30. package/lib/pages/Agents/MarketplaceAIInputBar.d.ts.map +1 -0
  31. package/lib/pages/Agents/MarketplaceAIInputBar.js +40 -0
  32. package/lib/pages/Agents/MarketplaceAIInputBar.js.map +1 -0
  33. package/lib/pages/Agents/MarketplaceAIProductAutosuggest.d.ts +25 -0
  34. package/lib/pages/Agents/MarketplaceAIProductAutosuggest.d.ts.map +1 -0
  35. package/lib/pages/Agents/MarketplaceAIProductAutosuggest.js +86 -0
  36. package/lib/pages/Agents/MarketplaceAIProductAutosuggest.js.map +1 -0
  37. package/lib/pages/Agents/MarketplaceAIProductCards.d.ts +23 -0
  38. package/lib/pages/Agents/MarketplaceAIProductCards.d.ts.map +1 -0
  39. package/lib/pages/Agents/MarketplaceAIProductCards.js +20 -0
  40. package/lib/pages/Agents/MarketplaceAIProductCards.js.map +1 -0
  41. package/lib/pages/Agents/MarketplaceAIScopeSelector.d.ts +19 -0
  42. package/lib/pages/Agents/MarketplaceAIScopeSelector.d.ts.map +1 -0
  43. package/lib/pages/Agents/MarketplaceAIScopeSelector.js +46 -0
  44. package/lib/pages/Agents/MarketplaceAIScopeSelector.js.map +1 -0
  45. package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.d.ts.map +1 -1
  46. package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.js +74 -63
  47. package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.js.map +1 -1
  48. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.d.ts.map +1 -1
  49. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.js +85 -69
  50. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.js.map +1 -1
  51. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.d.ts.map +1 -1
  52. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.js +7 -11
  53. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.js.map +1 -1
  54. package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.d.ts +17 -0
  55. package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.d.ts.map +1 -0
  56. package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.js +126 -0
  57. package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.js.map +1 -0
  58. package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.d.ts.map +1 -1
  59. package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.js +23 -65
  60. package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.js.map +1 -1
  61. package/lib/pages/Lakehouse/entitlements/showTaskActionAlert.d.ts +29 -0
  62. package/lib/pages/Lakehouse/entitlements/showTaskActionAlert.d.ts.map +1 -0
  63. package/lib/pages/Lakehouse/entitlements/showTaskActionAlert.js +60 -0
  64. package/lib/pages/Lakehouse/entitlements/showTaskActionAlert.js.map +1 -0
  65. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.d.ts.map +1 -1
  66. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js +3 -8
  67. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js.map +1 -1
  68. package/lib/stores/LegendMarketplaceBaseStore.d.ts +2 -1
  69. package/lib/stores/LegendMarketplaceBaseStore.d.ts.map +1 -1
  70. package/lib/stores/LegendMarketplaceBaseStore.js +8 -1
  71. package/lib/stores/LegendMarketplaceBaseStore.js.map +1 -1
  72. package/lib/stores/ai/LegendMarketplaceAIChatStore.d.ts +109 -0
  73. package/lib/stores/ai/LegendMarketplaceAIChatStore.d.ts.map +1 -0
  74. package/lib/stores/ai/LegendMarketplaceAIChatStore.js +1106 -0
  75. package/lib/stores/ai/LegendMarketplaceAIChatStore.js.map +1 -0
  76. package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.d.ts.map +1 -1
  77. package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.js +3 -2
  78. package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.js.map +1 -1
  79. package/lib/stores/lakehouse/dataProducts/ProductCardState.d.ts +1 -1
  80. package/lib/stores/lakehouse/dataProducts/ProductCardState.d.ts.map +1 -1
  81. package/lib/stores/lakehouse/dataProducts/ProductCardState.js +1 -2
  82. package/lib/stores/lakehouse/dataProducts/ProductCardState.js.map +1 -1
  83. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts +30 -3
  84. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts.map +1 -1
  85. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js +94 -16
  86. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js.map +1 -1
  87. package/lib/utils/EntitlementsUtils.d.ts +36 -2
  88. package/lib/utils/EntitlementsUtils.d.ts.map +1 -1
  89. package/lib/utils/EntitlementsUtils.js +225 -46
  90. package/lib/utils/EntitlementsUtils.js.map +1 -1
  91. package/lib/utils/SearchUtils.d.ts.map +1 -1
  92. package/lib/utils/SearchUtils.js +7 -4
  93. package/lib/utils/SearchUtils.js.map +1 -1
  94. package/package.json +10 -10
  95. package/src/__lib__/LegendMarketplaceNavigation.ts +11 -0
  96. package/src/application/LegendMarketplaceApplicationConfig.ts +62 -24
  97. package/src/application/LegendMarketplaceWebApplication.tsx +15 -0
  98. package/src/application/__test-utils__/LegendMarketplaceApplicationTestUtils.ts +7 -0
  99. package/src/application/providers/LegendMarketplaceAIChatStoreProvider.tsx +47 -0
  100. package/src/pages/Agents/LegendMarketplaceAgents.tsx +145 -23
  101. package/src/pages/Agents/MarketplaceAIChatView.tsx +555 -0
  102. package/src/pages/Agents/MarketplaceAIInputBar.tsx +91 -0
  103. package/src/pages/Agents/MarketplaceAIProductAutosuggest.tsx +181 -0
  104. package/src/pages/Agents/MarketplaceAIProductCards.tsx +111 -0
  105. package/src/pages/Agents/MarketplaceAIScopeSelector.tsx +84 -0
  106. package/src/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.tsx +131 -136
  107. package/src/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.tsx +158 -165
  108. package/src/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.tsx +12 -17
  109. package/src/pages/Lakehouse/entitlements/PermitDataAccessRequest.tsx +245 -0
  110. package/src/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.tsx +25 -94
  111. package/src/pages/Lakehouse/entitlements/showTaskActionAlert.tsx +101 -0
  112. package/src/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.tsx +27 -31
  113. package/src/stores/LegendMarketplaceBaseStore.ts +12 -0
  114. package/src/stores/ai/LegendMarketplaceAIChatStore.ts +1720 -0
  115. package/src/stores/lakehouse/LegendMarketplaceProductViewerStore.ts +6 -0
  116. package/src/stores/lakehouse/dataProducts/ProductCardState.ts +3 -4
  117. package/src/stores/lakehouse/entitlements/EntitlementsDashboardState.ts +181 -48
  118. package/src/utils/EntitlementsUtils.tsx +341 -86
  119. package/src/utils/SearchUtils.tsx +7 -4
  120. package/tsconfig.json +9 -0
@@ -0,0 +1,245 @@
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 { ActionAlertType } from '@finos/legend-application';
19
+ import { withLegendMarketplaceProductViewerStore } from '../../../application/providers/LegendMarketplaceProductViewerStoreProvider.js';
20
+ import { useParams } from '@finos/legend-application/browser';
21
+ import {
22
+ generateLakehouseDataProductPath,
23
+ generatePermitDataAccessRequestPagePath,
24
+ type WorkflowDataAccessRequestPathParams,
25
+ LEGEND_MARKETPLACE_ROUTE_PATTERN_TOKEN,
26
+ } from '../../../__lib__/LegendMarketplaceNavigation.js';
27
+ import { assertErrorThrown, guaranteeNonNullable } from '@finos/legend-shared';
28
+ import { useAuth } from 'react-oidc-context';
29
+ import { LegendMarketplacePage } from '../../LegendMarketplacePage.js';
30
+ import { useEffect, useState } from 'react';
31
+ import { useLegendMarketplaceBaseStore } from '../../../application/providers/LegendMarketplaceFrameworkProvider.js';
32
+ import { Box, Button } from '@mui/material';
33
+ import {
34
+ CubesLoadingIndicator,
35
+ CubesLoadingIndicatorIcon,
36
+ } from '@finos/legend-art';
37
+ import {
38
+ DataAccessRequestContent,
39
+ PermitDataAccessRequestState,
40
+ } from '@finos/legend-extension-dsl-data-product';
41
+ import { flowResult } from 'mobx';
42
+ import {
43
+ V1_deserializeDataRequestsWithWorkflowResponse,
44
+ V1_PermitTaskAction,
45
+ } from '@finos/legend-graph';
46
+ import { showTaskActionAlert } from './showTaskActionAlert.js';
47
+
48
+ export const PermitDataAccessRequestTask =
49
+ withLegendMarketplaceProductViewerStore(
50
+ observer(() => {
51
+ const marketplaceBaseStore = useLegendMarketplaceBaseStore();
52
+ const params = useParams<WorkflowDataAccessRequestPathParams>();
53
+ const auth = useAuth();
54
+ const currentUser =
55
+ marketplaceBaseStore.applicationStore.identityService.currentUser;
56
+
57
+ const [permitState, setPermitState] =
58
+ useState<PermitDataAccessRequestState>();
59
+ const [isLoading, setIsLoading] = useState(false);
60
+
61
+ const dataAccessRequestId = guaranteeNonNullable(
62
+ params[LEGEND_MARKETPLACE_ROUTE_PATTERN_TOKEN.DATA_ACCESS_REQUEST_ID],
63
+ );
64
+
65
+ const actionableTask = permitState?.getFirstOpenTask();
66
+ const userCanAction = actionableTask?.assignees.includes(currentUser);
67
+
68
+ useEffect(() => {
69
+ const fetchAndInitialize = async () => {
70
+ try {
71
+ setIsLoading(true);
72
+
73
+ const pluginManager =
74
+ marketplaceBaseStore.applicationStore.pluginManager;
75
+ const permitClient =
76
+ marketplaceBaseStore.permitWorkflowServerClient;
77
+
78
+ const state = new PermitDataAccessRequestState(
79
+ dataAccessRequestId,
80
+ marketplaceBaseStore.applicationStore,
81
+ permitClient,
82
+ marketplaceBaseStore.userSearchService,
83
+ {
84
+ authServerClient:
85
+ marketplaceBaseStore.lakehouseContractServerClient,
86
+ fetchFresh: async (token) => {
87
+ const raw = await permitClient.getDataRequestWithWorkflow(
88
+ dataAccessRequestId,
89
+ token,
90
+ );
91
+ return V1_deserializeDataRequestsWithWorkflowResponse(
92
+ raw,
93
+ pluginManager.getPureProtocolProcessorPlugins(),
94
+ )[0];
95
+ },
96
+ getTaskPageUrl: (id) =>
97
+ marketplaceBaseStore.applicationStore.navigationService.navigator.generateAddress(
98
+ generatePermitDataAccessRequestPagePath(id),
99
+ ),
100
+ },
101
+ );
102
+
103
+ setPermitState(state);
104
+ await flowResult(state.init(auth.user?.access_token));
105
+ } catch (error) {
106
+ assertErrorThrown(error);
107
+ marketplaceBaseStore.applicationStore.notificationService.notifyError(
108
+ `Error fetching data access request: ${error.message}`,
109
+ );
110
+ } finally {
111
+ setIsLoading(false);
112
+ }
113
+ };
114
+ // eslint-disable-next-line no-void
115
+ void fetchAndInitialize();
116
+ }, [dataAccessRequestId, auth.user?.access_token, marketplaceBaseStore]);
117
+
118
+ const handleRefresh = async (): Promise<void> => {
119
+ if (permitState) {
120
+ try {
121
+ setIsLoading(true);
122
+ permitState.initializationState.reset();
123
+ await flowResult(permitState.init(auth.user?.access_token));
124
+ } finally {
125
+ setIsLoading(false);
126
+ }
127
+ }
128
+ };
129
+
130
+ const handleTaskAction = async (
131
+ action: V1_PermitTaskAction,
132
+ justification: string,
133
+ ): Promise<void> => {
134
+ if (!actionableTask || !permitState) {
135
+ return;
136
+ }
137
+ await flowResult(
138
+ permitState.performTaskAction(
139
+ actionableTask.taskId,
140
+ action,
141
+ justification,
142
+ auth.user?.access_token,
143
+ ),
144
+ );
145
+ const label =
146
+ action === V1_PermitTaskAction.APPROVE ? 'approved' : 'denied';
147
+ marketplaceBaseStore.applicationStore.notificationService.notifySuccess(
148
+ `Request has been ${label}`,
149
+ );
150
+ await handleRefresh();
151
+ };
152
+
153
+ const handleApproveClick = (): void => {
154
+ showTaskActionAlert({
155
+ applicationStore: marketplaceBaseStore.applicationStore,
156
+ title: 'Approve Request',
157
+ message:
158
+ 'Please provide a business justification for approving this request.',
159
+ confirmLabel: 'Approve',
160
+ alertType: ActionAlertType.STANDARD,
161
+ requireJustification: true,
162
+ isLoading,
163
+ setIsLoading,
164
+ onConfirm: (justification) =>
165
+ handleTaskAction(V1_PermitTaskAction.APPROVE, justification),
166
+ errorPrefix: 'Error approving request',
167
+ });
168
+ };
169
+
170
+ const handleDenyClick = (): void => {
171
+ showTaskActionAlert({
172
+ applicationStore: marketplaceBaseStore.applicationStore,
173
+ title: 'Deny Request',
174
+ message:
175
+ 'Please provide a business justification for denying this request.',
176
+ confirmLabel: 'Deny',
177
+ alertType: ActionAlertType.CAUTION,
178
+ requireJustification: true,
179
+ isLoading,
180
+ setIsLoading,
181
+ onConfirm: (justification) =>
182
+ handleTaskAction(V1_PermitTaskAction.REJECT, justification),
183
+ errorPrefix: 'Error denying request',
184
+ });
185
+ };
186
+
187
+ return (
188
+ <LegendMarketplacePage className="marketplace-lakehouse-single-contract-viewer">
189
+ <CubesLoadingIndicator isLoading={isLoading}>
190
+ <CubesLoadingIndicatorIcon />
191
+ </CubesLoadingIndicator>
192
+ {permitState?.dataRequestWithWorkflow && (
193
+ <div className="marketplace-lakehouse-single-contract-viewer__container">
194
+ {actionableTask !== undefined && (
195
+ <Box className="marketplace-lakehouse-single-contract-viewer__action-btns">
196
+ <Button
197
+ variant="contained"
198
+ color="success"
199
+ onClick={handleApproveClick}
200
+ title={
201
+ userCanAction
202
+ ? ''
203
+ : 'You are not assigned to review this task'
204
+ }
205
+ disabled={!userCanAction || isLoading}
206
+ >
207
+ Approve Request
208
+ </Button>
209
+ <Button
210
+ variant="contained"
211
+ color="error"
212
+ onClick={handleDenyClick}
213
+ title={
214
+ userCanAction
215
+ ? ''
216
+ : 'You are not assigned to review this task'
217
+ }
218
+ disabled={!userCanAction || isLoading}
219
+ >
220
+ Deny Request
221
+ </Button>
222
+ </Box>
223
+ )}
224
+ <DataAccessRequestContent
225
+ viewerState={permitState}
226
+ getDataProductUrl={(
227
+ dataProductId: string,
228
+ deploymentId: number,
229
+ ) =>
230
+ marketplaceBaseStore.applicationStore.navigationService.navigator.generateAddress(
231
+ generateLakehouseDataProductPath(
232
+ dataProductId,
233
+ deploymentId,
234
+ ),
235
+ )
236
+ }
237
+ onRefresh={handleRefresh}
238
+ isReadOnly={true}
239
+ />
240
+ </div>
241
+ )}
242
+ </LegendMarketplacePage>
243
+ );
244
+ }),
245
+ );
@@ -15,10 +15,7 @@
15
15
  */
16
16
 
17
17
  import { observer } from 'mobx-react-lite';
18
- import {
19
- ActionAlertActionType,
20
- ActionAlertType,
21
- } from '@finos/legend-application';
18
+ import { ActionAlertType } from '@finos/legend-application';
22
19
  import { withLegendMarketplaceProductViewerStore } from '../../../application/providers/LegendMarketplaceProductViewerStoreProvider.js';
23
20
  import { useParams } from '@finos/legend-application/browser';
24
21
  import {
@@ -36,7 +33,7 @@ import {
36
33
  V1_RawWorkflowTask,
37
34
  V1_WorkflowTaskStatus,
38
35
  } from '@finos/legend-graph';
39
- import { Box, Button, TextField } from '@mui/material';
36
+ import { Box, Button } from '@mui/material';
40
37
  import {
41
38
  CubesLoadingIndicator,
42
39
  CubesLoadingIndicatorIcon,
@@ -46,6 +43,7 @@ import {
46
43
  WorkflowDataAccessRequestState,
47
44
  } from '@finos/legend-extension-dsl-data-product';
48
45
  import { flowResult } from 'mobx';
46
+ import { showTaskActionAlert } from './showTaskActionAlert.js';
49
47
 
50
48
  export const WorkflowDataAccessRequestTask =
51
49
  withLegendMarketplaceProductViewerStore(
@@ -71,10 +69,11 @@ export const WorkflowDataAccessRequestTask =
71
69
  return undefined;
72
70
  }
73
71
  // Prefer workflow server tasks
74
- const { privilegeManagerTask, dataOwnerTask } =
72
+ const { privilegeManagerTasks, dataOwnerTasks } =
75
73
  workflowState.workflowTasks;
76
- const workflowServerTask = [privilegeManagerTask, dataOwnerTask].find(
77
- (task) => task !== undefined && task.status === 'OPEN',
74
+ const allTasks = [...privilegeManagerTasks, ...dataOwnerTasks];
75
+ const workflowServerTask = allTasks.find(
76
+ (task) => task.status === 'OPEN',
78
77
  );
79
78
  if (workflowServerTask) {
80
79
  return workflowServerTask;
@@ -85,8 +84,8 @@ export const WorkflowDataAccessRequestTask =
85
84
  .find((task) => task.status === V1_WorkflowTaskStatus.OPEN);
86
85
  if (fallbackTask) {
87
86
  // Find the matching raw workflow task by taskId, or build a minimal one from the fallback
88
- const matchingRaw = [privilegeManagerTask, dataOwnerTask].find(
89
- (t) => t?.taskId === fallbackTask.taskId,
87
+ const matchingRaw = allTasks.find(
88
+ (t) => t.taskId === fallbackTask.taskId,
90
89
  );
91
90
  if (matchingRaw) {
92
91
  return matchingRaw;
@@ -180,100 +179,32 @@ export const WorkflowDataAccessRequestTask =
180
179
  };
181
180
 
182
181
  const handleApproveClick = () => {
183
- let justification = '';
184
- marketplaceBaseStore.applicationStore.alertService.setActionAlertInfo({
182
+ showTaskActionAlert({
183
+ applicationStore: marketplaceBaseStore.applicationStore,
185
184
  title: 'Approve Request',
186
185
  message:
187
186
  'Please provide a business justification for approving this request.',
188
- prompt: (
189
- <TextField
190
- fullWidth={true}
191
- autoFocus={true}
192
- multiline={true}
193
- minRows={3}
194
- placeholder="Business Justification"
195
- onChange={(e) => {
196
- justification = e.target.value;
197
- }}
198
- sx={{ marginTop: 2 }}
199
- />
200
- ),
201
- type: ActionAlertType.STANDARD,
202
- actions: [
203
- {
204
- label: 'Approve',
205
- type: ActionAlertActionType.PROCEED_WITH_CAUTION,
206
- handler: () => {
207
- if (!isLoading) {
208
- setIsLoading(true);
209
- handleApprove(justification)
210
- .catch((error) => {
211
- assertErrorThrown(error);
212
- marketplaceBaseStore.applicationStore.notificationService.notifyError(
213
- `Error approving request: ${error.message}`,
214
- );
215
- })
216
- .finally(() => {
217
- setIsLoading(false);
218
- });
219
- }
220
- },
221
- },
222
- {
223
- label: 'Cancel',
224
- type: ActionAlertActionType.PROCEED,
225
- default: true,
226
- },
227
- ],
187
+ confirmLabel: 'Approve',
188
+ alertType: ActionAlertType.STANDARD,
189
+ isLoading,
190
+ setIsLoading,
191
+ onConfirm: (justification) => handleApprove(justification),
192
+ errorPrefix: 'Error approving request',
228
193
  });
229
194
  };
230
195
 
231
196
  const handleDenyClick = () => {
232
- let justification = '';
233
- marketplaceBaseStore.applicationStore.alertService.setActionAlertInfo({
197
+ showTaskActionAlert({
198
+ applicationStore: marketplaceBaseStore.applicationStore,
234
199
  title: 'Deny Request',
235
200
  message:
236
201
  'Please provide a business justification for denying this request.',
237
- prompt: (
238
- <TextField
239
- fullWidth={true}
240
- autoFocus={true}
241
- multiline={true}
242
- minRows={3}
243
- placeholder="Business Justification"
244
- onChange={(e) => {
245
- justification = e.target.value;
246
- }}
247
- sx={{ marginTop: 2 }}
248
- />
249
- ),
250
- type: ActionAlertType.CAUTION,
251
- actions: [
252
- {
253
- label: 'Deny',
254
- type: ActionAlertActionType.PROCEED_WITH_CAUTION,
255
- handler: () => {
256
- if (!isLoading) {
257
- setIsLoading(true);
258
- handleDeny(justification)
259
- .catch((error) => {
260
- assertErrorThrown(error);
261
- marketplaceBaseStore.applicationStore.notificationService.notifyError(
262
- `Error denying request: ${error.message}`,
263
- );
264
- })
265
- .finally(() => {
266
- setIsLoading(false);
267
- });
268
- }
269
- },
270
- },
271
- {
272
- label: 'Cancel',
273
- type: ActionAlertActionType.PROCEED,
274
- default: true,
275
- },
276
- ],
202
+ confirmLabel: 'Deny',
203
+ alertType: ActionAlertType.CAUTION,
204
+ isLoading,
205
+ setIsLoading,
206
+ onConfirm: (justification) => handleDeny(justification),
207
+ errorPrefix: 'Error denying request',
277
208
  });
278
209
  };
279
210
 
@@ -0,0 +1,101 @@
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 { TextField } from '@mui/material';
18
+ import {
19
+ ActionAlertActionType,
20
+ type ActionAlertType,
21
+ type GenericLegendApplicationStore,
22
+ } from '@finos/legend-application';
23
+ import { assertErrorThrown } from '@finos/legend-shared';
24
+
25
+ export function showTaskActionAlert(options: {
26
+ applicationStore: GenericLegendApplicationStore;
27
+ title: string;
28
+ message: string;
29
+ confirmLabel: string;
30
+ alertType: ActionAlertType;
31
+ requireJustification?: boolean;
32
+ isLoading: boolean;
33
+ setIsLoading: (loading: boolean) => void;
34
+ onConfirm: (justification: string) => Promise<void>;
35
+ errorPrefix: string;
36
+ }): void {
37
+ const {
38
+ applicationStore,
39
+ title,
40
+ message,
41
+ confirmLabel,
42
+ alertType,
43
+ requireJustification = false,
44
+ isLoading,
45
+ setIsLoading,
46
+ onConfirm,
47
+ errorPrefix,
48
+ } = options;
49
+
50
+ let justification = '';
51
+ applicationStore.alertService.setActionAlertInfo({
52
+ title,
53
+ message,
54
+ prompt: (
55
+ <TextField
56
+ fullWidth={true}
57
+ autoFocus={true}
58
+ multiline={true}
59
+ minRows={3}
60
+ placeholder="Business Justification"
61
+ onChange={(e) => {
62
+ justification = e.target.value;
63
+ }}
64
+ className="marketplace-lakehouse-entitlements__data-access-request-viewer__justification-field"
65
+ />
66
+ ),
67
+ type: alertType,
68
+ actions: [
69
+ {
70
+ label: confirmLabel,
71
+ type: ActionAlertActionType.PROCEED_WITH_CAUTION,
72
+ handler: () => {
73
+ if (requireJustification && !justification.trim()) {
74
+ applicationStore.notificationService.notifyError(
75
+ 'Business justification is required',
76
+ );
77
+ return;
78
+ }
79
+ if (!isLoading) {
80
+ setIsLoading(true);
81
+ onConfirm(justification)
82
+ .catch((error) => {
83
+ assertErrorThrown(error);
84
+ applicationStore.notificationService.notifyError(
85
+ `${errorPrefix}: ${error.message}`,
86
+ );
87
+ })
88
+ .finally(() => {
89
+ setIsLoading(false);
90
+ });
91
+ }
92
+ },
93
+ },
94
+ {
95
+ label: 'Cancel',
96
+ type: ActionAlertActionType.PROCEED,
97
+ default: true,
98
+ },
99
+ ],
100
+ });
101
+ }
@@ -382,43 +382,39 @@ export const LegendMarketplaceSearchResults =
382
382
  ? `${searchResultsStore.filterSortProducts?.length ?? 0} Products`
383
383
  : `${searchResultsStore.totalItems} Products`}
384
384
  </Typography>
385
- <div
386
- className="legend-marketplace-search-results__search-type-tabs"
387
- role="tablist"
388
- aria-label="Search result type"
389
- >
390
- <button
391
- type="button"
392
- role="tab"
393
- aria-selected={true}
394
- tabIndex={0}
395
- className="legend-marketplace-search-results__search-type-tab legend-marketplace-search-results__search-type-tab--active"
385
+ {isNonEmptyString(searchResultsStore.searchQuery) && (
386
+ <div
387
+ className="legend-marketplace-search-results__search-type-tabs"
388
+ role="tablist"
389
+ aria-label="Search result type"
396
390
  >
397
- Data Products
398
- </button>
399
- <button
400
- type="button"
401
- role="tab"
402
- aria-selected={false}
403
- tabIndex={-1}
404
- className="legend-marketplace-search-results__search-type-tab"
405
- onClick={() => {
406
- if (isNonEmptyString(searchResultsStore.searchQuery)) {
391
+ <button
392
+ type="button"
393
+ role="tab"
394
+ aria-selected={true}
395
+ tabIndex={0}
396
+ className="legend-marketplace-search-results__search-type-tab legend-marketplace-search-results__search-type-tab--active"
397
+ >
398
+ Data Products
399
+ </button>
400
+ <button
401
+ type="button"
402
+ role="tab"
403
+ aria-selected={false}
404
+ tabIndex={-1}
405
+ className="legend-marketplace-search-results__search-type-tab"
406
+ onClick={() => {
407
407
  applicationStore.navigationService.navigator.goToLocation(
408
408
  generateFieldSearchResultsRoute(
409
409
  searchResultsStore.searchQuery,
410
410
  ),
411
411
  );
412
- } else {
413
- applicationStore.navigationService.navigator.goToLocation(
414
- generateFieldSearchResultsRoute(undefined),
415
- );
416
- }
417
- }}
418
- >
419
- Data Fields
420
- </button>
421
- </div>
412
+ }}
413
+ >
414
+ Data Fields
415
+ </button>
416
+ </div>
417
+ )}
422
418
  <div className="legend-marketplace-search-results__sort-bar__controls">
423
419
  <div className="legend-marketplace-search-results__view-toggle">
424
420
  <div
@@ -53,6 +53,7 @@ import {
53
53
  LakehouseIngestServerClient,
54
54
  LakehousePlatformServerClient,
55
55
  LakehouseWorkflowServerClient,
56
+ PermitWorkflowServerClient,
56
57
  } from '@finos/legend-server-lakehouse';
57
58
  import { CartStore } from './cart/CartStore.js';
58
59
  import { parseGAVCoordinates, type Entity } from '@finos/legend-storage';
@@ -92,6 +93,7 @@ export class LegendMarketplaceBaseStore {
92
93
  readonly remoteEngine: V1_RemoteEngine;
93
94
  readonly userSearchService: UserSearchService | undefined;
94
95
  readonly lakehouseWorkflowServerClient: LakehouseWorkflowServerClient;
96
+ readonly permitWorkflowServerClient: PermitWorkflowServerClient;
95
97
  readonly lakehouseDataProductService: LakehouseDataProductService;
96
98
  readonly cartStore: CartStore;
97
99
  readonly terminalAccessServerClient: TerminalAccessServerClient;
@@ -177,6 +179,16 @@ export class LegendMarketplaceBaseStore {
177
179
  this.applicationStore.tracerService,
178
180
  );
179
181
 
182
+ // permit + eTask workflow
183
+ this.permitWorkflowServerClient = new PermitWorkflowServerClient({
184
+ authBaseUrl: this.applicationStore.config.lakehouseServerUrl,
185
+ workflowBaseUrl:
186
+ this.applicationStore.config.lakehousePermitWorkflowServerUrl,
187
+ });
188
+ this.permitWorkflowServerClient.setTracerService(
189
+ this.applicationStore.tracerService,
190
+ );
191
+
180
192
  // lakehouse ingest
181
193
  this.lakehouseIngestServerClient = new LakehouseIngestServerClient(
182
194
  undefined,