UNPKG

@finos/legend-application-marketplace

Version:
381 lines 26.4 kB
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime"; /** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { GraphManagerState, V1_ApprovalType, V1_ResourceType, } from '@finos/legend-graph'; import { DataGrid, } from '@finos/legend-lego/data-grid'; import { Box, Button, Checkbox, Dialog, DialogActions, DialogContent, DialogTitle, Tooltip, } from '@mui/material'; import { useCallback, useMemo, useState } from 'react'; import { useLegendMarketplaceBaseStore } from '../../../application/providers/LegendMarketplaceFrameworkProvider.js'; import { CubesLoadingIndicator, CubesLoadingIndicatorIcon, InfoCircleIcon, } from '@finos/legend-art'; import { flowResult } from 'mobx'; import { useAuth } from 'react-oidc-context'; import { observer } from 'mobx-react-lite'; import { startCase } from '@finos/legend-shared'; import { UserRenderer, getOrganizationalScopeTypeName, getOrganizationalScopeTypeDetails, DataAccessRequestViewer, DataContractViewerState, } from '@finos/legend-extension-dsl-data-product'; import { generateContractPagePath, generateLakehouseDataProductPath, } from '../../../__lib__/LegendMarketplaceNavigation.js'; import { CONTRACT_ACTION, LegendMarketplaceTelemetryHelper, } from '../../../__lib__/LegendMarketplaceTelemetryHelper.js'; import { formatOrderDate } from '../../../stores/orders/OrderHelpers.js'; const EntitlementsDashboardActionModal = (props) => { const { open, selectedTasks, dashboardState, onClose, action, pendingTaskContracts, marketplaceBaseStore, } = props; const auth = useAuth(); const [isLoading, setIsLoading] = useState(false); const [errorMessages, setErrorMessages] = useState([]); const [successCount, setSuccessCount] = useState(0); const handleClose = () => { setIsLoading(false); setErrorMessages([]); setSuccessCount(0); onClose(); }; const actionFunction = action === 'approve' ? dashboardState.approve.bind(dashboardState) : dashboardState.deny.bind(dashboardState); const handleAction = async () => { setIsLoading(true); const currentErrorMessages = []; await Promise.all(Array.from(selectedTasks).map(async (task) => { return flowResult(actionFunction(task, auth.user?.access_token)) .then(() => setSuccessCount((prev) => prev++)) .catch((error) => currentErrorMessages.push([task, error.message])); })); setIsLoading(false); // If everything was successful, show a success message and close the modal. if (currentErrorMessages.length === 0) { dashboardState.lakehouseEntitlementsStore.applicationStore.notificationService.notifySuccess(`${selectedTasks.length} selected contract requests have been ${action === 'approve' ? 'approved' : 'denied'} successfully.`); handleClose(); LegendMarketplaceTelemetryHelper.logEvent_ActionDataContracts(dashboardState.lakehouseEntitlementsStore.applicationStore .telemetryService, selectedTasks, pendingTaskContracts, action === 'approve' ? CONTRACT_ACTION.APPROVED : CONTRACT_ACTION.DENIED, dashboardState.lakehouseEntitlementsStore.applicationStore .identityService.currentUser, undefined); } else { // If there were errors, we won't close the modal and will show the errors in the modal. setErrorMessages(currentErrorMessages); LegendMarketplaceTelemetryHelper.logEvent_ActionDataContracts(dashboardState.lakehouseEntitlementsStore.applicationStore .telemetryService, selectedTasks, pendingTaskContracts, action === 'approve' ? CONTRACT_ACTION.APPROVED : CONTRACT_ACTION.DENIED, dashboardState.lakehouseEntitlementsStore.applicationStore .identityService.currentUser, currentErrorMessages.map((error) => error[1])); } // Refresh pending tasks and contracts after taking action await flowResult(dashboardState.init(auth.user?.access_token)); }; if (action === undefined) { return null; } return (_jsxs(Dialog, { open: open, onClose: handleClose, fullWidth: true, maxWidth: "md", children: [_jsxs(DialogTitle, { children: [action === 'approve' ? 'Approve' : 'Deny', " Contract Requests"] }), _jsxs(DialogContent, { className: "marketplace-lakehouse-entitlements__data-contract-approval__content", children: [_jsx(CubesLoadingIndicator, { isLoading: isLoading, children: _jsx(CubesLoadingIndicatorIcon, {}) }), !isLoading && errorMessages.length === 0 && (_jsxs("div", { children: [action === 'approve' ? 'Approve' : 'Deny', " ", selectedTasks.length, ' ', "selected contract requests"] })), !isLoading && errorMessages.length > 0 && (_jsxs(_Fragment, { children: [successCount > 0 && (_jsxs(Box, { className: "marketplace-lakehouse-entitlements__data-contract-approval__success", children: [successCount, " selected contract requests were", ' ', action === 'approve' ? 'approved' : 'denied', " successfully"] })), errorMessages.map(([task, errorMessage]) => { const contractId = task.dataContractId; const contract = pendingTaskContracts.find((c) => c.guid === contractId); return (_jsxs(Box, { className: "marketplace-lakehouse-entitlements__data-contract-approval__error", children: [_jsxs("div", { className: "marketplace-lakehouse-entitlements__data-contract-approval__error__content", children: ["Encountered an error", ' ', action === 'approve' ? 'approving' : 'denying', " request for", ' ', _jsx("div", { className: "marketplace-lakehouse-entitlements__data-contract-approval__error__user", children: _jsx(UserRenderer, { userId: task.consumer, applicationStore: marketplaceBaseStore.applicationStore, userSearchService: marketplaceBaseStore.userSearchService }) }), ' ', "for ", startCase(contract?.resourceType.toLowerCase()), ' ', _jsx("span", { className: "marketplace-lakehouse-text__emphasis", children: contract?.accessPointGroup }), ' ', "on Data Product", ' ', _jsx("span", { className: "marketplace-lakehouse-text__emphasis", children: contract?.resourceId }), ":"] }), _jsx("div", { children: _jsx("code", { children: errorMessage }) })] }, task.taskId)); })] }))] }), _jsxs(DialogActions, { children: [_jsxs(Button, { onClick: () => { // eslint-disable-next-line no-void void handleAction(); }, variant: "contained", disabled: isLoading || errorMessages.length > 0, color: action === 'approve' ? 'success' : 'error', children: [action === 'approve' ? 'Approve' : 'Deny', " Selected Contracts"] }), _jsx(Button, { onClick: handleClose, variant: "outlined", disabled: isLoading, children: "Cancel" })] })] })); }; export const EntitlementsPendingTasksDashboard = observer((props) => { // State and props const { dashboardState } = props; const pendingTasks = dashboardState.pendingTasks; const pendingTaskContracts = dashboardState.pendingTaskContracts; const privilegeManagerTasks = useMemo(() => pendingTasks?.filter((task) => task.type === V1_ApprovalType.CONSUMER_PRIVILEGE_MANAGER_APPROVAL) ?? [], [pendingTasks]); const dataOwnerTasks = useMemo(() => pendingTasks?.filter((task) => task.type === V1_ApprovalType.DATA_OWNER_APPROVAL) ?? [], [pendingTasks]); const otherTasks = useMemo(() => pendingTasks?.filter((task) => !privilegeManagerTasks.includes(task) && !dataOwnerTasks.includes(task)) ?? [], [dataOwnerTasks, privilegeManagerTasks, pendingTasks]); const loading = dashboardState.fetchingPendingTasksState.isInProgress; const marketplaceBaseStore = useLegendMarketplaceBaseStore(); const [selectedAction, setSelectedAction] = useState(); const selectedTaskIdsSet = dashboardState.selectedTaskIds; const [selectedContract, setSelectedContract] = useState(); const [selectedContractTargetUser, setSelectedContractTargetUser] = useState(); const [contractErrors, setContractErrors] = useState(undefined); const auth = useAuth(); // Callbacks const handleFirstDataRendered = (event) => { const nodesToSelect = []; event.api.forEachNode((node) => { if (node.data && selectedTaskIdsSet.has(node.data.taskId)) { nodesToSelect.push(node); } }); event.api.setNodesSelected({ nodes: nodesToSelect, newValue: true }); }; const handleCellClicked = async (event) => { if (event.colDef.colId !== 'selection') { const contract = pendingTaskContracts.find((c) => c.guid === event.data?.dataContractId); setSelectedContract(contract); setSelectedContractTargetUser(event.data?.consumer); setContractErrors(undefined); if (contract !== undefined) { const result = await dashboardState.getContractErrors(contract.guid, auth.user?.access_token); setContractErrors(result); } } }; const rowSelection = useMemo(() => ({ mode: 'multiRow', checkboxes: false, headerCheckbox: false, }), []); const CustomSelectionRenderer = useCallback((params) => { const handleChange = (_) => { if (params.data) { const newSet = new Set(selectedTaskIdsSet); if (selectedTaskIdsSet.has(params.data.taskId)) { newSet.delete(params.data.taskId); } else { newSet.add(params.data.taskId); } dashboardState.setSelectedTaskIds(newSet); } }; return (_jsx(Checkbox, { size: "large", checked: selectedTaskIdsSet.has(params.data?.taskId ?? ''), onChange: handleChange, sx: { padding: 0 } })); }, [selectedTaskIdsSet, dashboardState]); const CustomSelectionHeaderRenderer = useCallback((_props) => { const { taskSet } = _props; const checked = taskSet.length > 0 && taskSet.every((task) => selectedTaskIdsSet.has(task.taskId)); const indeterminate = taskSet.length > 0 && !checked && taskSet.some((task) => selectedTaskIdsSet.has(task.taskId)); const handleChange = (_e) => { if (!checked || indeterminate) { const newSet = new Set(selectedTaskIdsSet); taskSet.forEach((task) => newSet.add(task.taskId)); dashboardState.setSelectedTaskIds(newSet); } else { const newSet = new Set(selectedTaskIdsSet); taskSet.forEach((task) => newSet.delete(task.taskId)); dashboardState.setSelectedTaskIds(newSet); } }; return (_jsx(Checkbox, { size: "large", checked: checked, indeterminate: indeterminate, onChange: handleChange, disabled: taskSet.length === 0, sx: { padding: 0 } })); }, [selectedTaskIdsSet, dashboardState]); const colDefs = useMemo(() => [ { headerName: 'Date Created', colId: 'dateCreated', valueGetter: (params) => { const contractId = params.data?.dataContractId; const createdAt = pendingTaskContracts.find((c) => c.guid === contractId)?.createdAt; return formatOrderDate(createdAt) ?? 'Unknown'; }, sortable: true, sort: 'desc', comparator: (_, __, val1, val2) => { const contractId1 = val1.data?.dataContractId; const contractId2 = val2.data?.dataContractId; const createdAt1 = pendingTaskContracts.find((c) => c.guid === contractId1)?.createdAt; const createdAt2 = pendingTaskContracts.find((c) => c.guid === contractId2)?.createdAt; const dateA = createdAt1 ? new Date(createdAt1).getTime() : 0; const dateB = createdAt2 ? new Date(createdAt2).getTime() : 0; return dateA - dateB; }, }, { minWidth: 25, sortable: true, resizable: true, colId: 'consumerType', headerName: 'Consumer Type', flex: 1, valueGetter: (params) => { const contractId = params.data?.dataContractId; const consumer = pendingTaskContracts.find((c) => c.guid === contractId)?.consumer; const typeName = consumer ? getOrganizationalScopeTypeName(consumer, dashboardState.lakehouseEntitlementsStore.applicationStore.pluginManager.getApplicationPlugins()) : undefined; return typeName ?? 'Unknown'; }, cellRenderer: (params) => { const contractId = params.data?.dataContractId; const consumer = pendingTaskContracts.find((c) => c.guid === contractId)?.consumer; const typeName = consumer ? getOrganizationalScopeTypeName(consumer, dashboardState.lakehouseEntitlementsStore.applicationStore.pluginManager.getApplicationPlugins()) : undefined; const typeDetails = consumer ? getOrganizationalScopeTypeDetails(consumer, dashboardState.lakehouseEntitlementsStore.applicationStore.pluginManager.getApplicationPlugins()) : undefined; return (_jsxs(_Fragment, { children: [typeName ?? 'Unknown', typeDetails !== undefined && (_jsx(Tooltip, { className: "marketplace-lakehouse-entitlements__grid__consumer-type__tooltip__icon", title: typeDetails, children: _jsx(InfoCircleIcon, {}) }))] })); }, }, { minWidth: 50, sortable: true, resizable: true, colId: 'targetUser', headerName: 'Target User', flex: 1, valueGetter: (params) => { return params.data?.consumer ?? 'Unknown'; }, cellRenderer: (params) => { return (_jsx(UserRenderer, { userId: params.data?.consumer, applicationStore: marketplaceBaseStore.applicationStore, userSearchService: marketplaceBaseStore.userSearchService, disableOnClick: true, className: "marketplace-lakehouse-entitlements__grid__user-display" })); }, }, { minWidth: 50, sortable: true, resizable: true, colId: 'requester', headerName: 'Requester', flex: 1, valueGetter: (params) => { const contractId = params.data?.dataContractId; const requester = pendingTaskContracts.find((c) => c.guid === contractId)?.createdBy; return requester ?? 'Unknown'; }, cellRenderer: (params) => { const contractId = params.data?.dataContractId; const requester = pendingTaskContracts.find((c) => c.guid === contractId)?.createdBy; return requester ? (_jsx(UserRenderer, { userId: requester, applicationStore: marketplaceBaseStore.applicationStore, userSearchService: marketplaceBaseStore.userSearchService, disableOnClick: true, className: "marketplace-lakehouse-entitlements__grid__user-display" })) : (_jsx(_Fragment, { children: "Unknown" })); }, }, { minWidth: 50, sortable: true, resizable: true, headerName: 'Target Data Product', flex: 1, valueGetter: (params) => { const contractId = params.data?.dataContractId; const contract = pendingTaskContracts.find((c) => c.guid === contractId); return contract?.resourceId ?? 'Unknown'; }, }, { minWidth: 50, sortable: true, resizable: true, headerName: 'Target Access Point Group', flex: 1, valueGetter: (params) => { const contractId = params.data?.dataContractId; const contract = pendingTaskContracts.find((c) => c.guid === contractId); const accessPointGroup = contract?.resourceType === V1_ResourceType.ACCESS_POINT_GROUP ? contract.accessPointGroup : `${contract?.accessPointGroup ?? 'Unknown'} (${contract?.resourceType ?? 'Unknown Type'})`; return accessPointGroup ?? 'Unknown'; }, }, { minWidth: 50, sortable: true, resizable: true, headerName: 'Business Justification', flex: 2, valueGetter: (params) => { const contractId = params.data?.dataContractId; const businessJustification = pendingTaskContracts.find((c) => c.guid === contractId)?.description; return businessJustification ?? 'Unknown'; }, }, { minWidth: 50, sortable: true, resizable: true, hide: true, headerName: 'Contract ID', flex: 2, valueGetter: (params) => params.data?.dataContractId ?? 'Unknown', }, ], [ dashboardState.lakehouseEntitlementsStore.applicationStore .pluginManager, marketplaceBaseStore.applicationStore, marketplaceBaseStore.userSearchService, pendingTaskContracts, ]); const privilegeManagerColDefs = useMemo(() => [ { headerName: '', colId: 'selection', width: 50, cellRenderer: CustomSelectionRenderer, headerComponent: (params) => (_jsx(CustomSelectionHeaderRenderer, { params: params, taskSet: privilegeManagerTasks })), pinned: 'left', }, ...colDefs, ], [ CustomSelectionHeaderRenderer, CustomSelectionRenderer, colDefs, privilegeManagerTasks, ]); const dataOwnerColDefs = useMemo(() => [ { headerName: '', colId: 'selection', width: 50, cellRenderer: CustomSelectionRenderer, headerComponent: (params) => (_jsx(CustomSelectionHeaderRenderer, { params: params, taskSet: dataOwnerTasks })), pinned: 'left', }, ...colDefs, ], [ CustomSelectionHeaderRenderer, CustomSelectionRenderer, colDefs, dataOwnerTasks, ]); const otherTasksColDefs = useMemo(() => [ { headerName: '', colId: 'selection', width: 50, cellRenderer: CustomSelectionRenderer, headerComponent: (params) => (_jsx(CustomSelectionHeaderRenderer, { params: params, taskSet: otherTasks })), pinned: 'left', }, ...colDefs, ], [ CustomSelectionHeaderRenderer, CustomSelectionRenderer, colDefs, otherTasks, ]); return (_jsxs(_Fragment, { children: [_jsxs(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks", children: [_jsxs(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__action-btns", children: [_jsxs(Button, { variant: "contained", color: "success", disabled: !selectedTaskIdsSet.size || loading, onClick: () => setSelectedAction('approve'), children: ["Approve ", selectedTaskIdsSet.size, " tasks"] }), _jsxs(Button, { variant: "contained", color: "error", disabled: !selectedTaskIdsSet.size || loading, onClick: () => setSelectedAction('deny'), children: ["Deny ", selectedTaskIdsSet.size, " tasks"] })] }), _jsxs(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__grids", children: [_jsxs(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__grid-container", children: [_jsxs(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__grid__header", children: ["Privilege Manager Approvals", _jsx(Tooltip, { className: "marketplace-lakehouse-entitlements__pending-tasks__tooltip__icon", title: "These are pending requests for which you are listed as a Privilege Manager.", slotProps: { tooltip: { className: 'marketplace-lakehouse-entitlements__pending-tasks__tooltip', }, }, children: _jsx(InfoCircleIcon, {}) })] }), _jsx(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__grid ag-theme-balham", children: _jsx(DataGrid, { rowData: privilegeManagerTasks, onRowDataUpdated: (params) => { params.api.refreshCells({ force: true }); }, suppressFieldDotNotation: true, suppressContextMenu: false, rowHeight: 45, rowSelection: rowSelection, onFirstDataRendered: handleFirstDataRendered, onCellClicked: (event) => // eslint-disable-next-line no-void void handleCellClicked(event), columnDefs: privilegeManagerColDefs, overlayNoRowsTemplate: "You have no contracts to approve as a Privilege Manager", loading: loading, overlayLoadingTemplate: "Loading contracts" }) })] }), _jsxs(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__grid-container", children: [_jsxs(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__grid__header", children: ["Data Owner Approvals", _jsx(Tooltip, { className: "marketplace-lakehouse-entitlements__pending-tasks__tooltip__icon", title: "These are pending requests for which you are listed as a Data Owner.", slotProps: { tooltip: { className: 'marketplace-lakehouse-entitlements__pending-tasks__tooltip', }, }, children: _jsx(InfoCircleIcon, {}) })] }), _jsx(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__grid ag-theme-balham", children: _jsx(DataGrid, { rowData: dataOwnerTasks, onRowDataUpdated: (params) => { params.api.refreshCells({ force: true }); }, suppressFieldDotNotation: true, suppressContextMenu: false, rowHeight: 45, rowSelection: rowSelection, onFirstDataRendered: handleFirstDataRendered, onCellClicked: (event) => // eslint-disable-next-line no-void void handleCellClicked(event), columnDefs: dataOwnerColDefs, overlayNoRowsTemplate: "You have no contracts to approve as a Data Owner", loading: loading, overlayLoadingTemplate: "Loading contracts" }) })] }), otherTasks.length > 0 && (_jsxs(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__grid-container", children: [_jsx(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__grid__header", children: "Other Approvals" }), _jsx(Box, { className: "marketplace-lakehouse-entitlements__pending-tasks__grid ag-theme-balham", children: _jsx(DataGrid, { rowData: otherTasks, onRowDataUpdated: (params) => { params.api.refreshCells({ force: true }); }, suppressFieldDotNotation: true, suppressContextMenu: false, rowHeight: 45, rowSelection: rowSelection, onFirstDataRendered: handleFirstDataRendered, onCellClicked: (event) => // eslint-disable-next-line no-void void handleCellClicked(event), columnDefs: otherTasksColDefs, loading: loading, overlayLoadingTemplate: "Loading contracts" }) })] }))] })] }), _jsx(EntitlementsDashboardActionModal, { open: selectedAction !== undefined, selectedTasks: pendingTasks?.filter((task) => selectedTaskIdsSet.has(task.taskId)) ?? [], dashboardState: dashboardState, onClose: () => setSelectedAction(undefined), action: selectedAction, pendingTaskContracts: pendingTaskContracts, marketplaceBaseStore: marketplaceBaseStore }), selectedContract !== undefined && (_jsx(DataAccessRequestViewer, { open: true, onClose: () => { setSelectedContract(undefined); setContractErrors(undefined); }, contractErrors: contractErrors, viewerState: new DataContractViewerState(selectedContract, (contractId, taskId) => marketplaceBaseStore.applicationStore.navigationService.navigator.generateAddress(generateContractPagePath(contractId, taskId)), undefined, marketplaceBaseStore.applicationStore, marketplaceBaseStore.lakehouseContractServerClient, new GraphManagerState(marketplaceBaseStore.applicationStore.pluginManager, marketplaceBaseStore.applicationStore.logService), marketplaceBaseStore.userSearchService), onRefresh: async () => { await flowResult(dashboardState.updateContract(selectedContract.guid, auth.user?.access_token)); }, getDataProductUrl: (dataProductId, deploymentId) => marketplaceBaseStore.applicationStore.navigationService.navigator.generateAddress(generateLakehouseDataProductPath(dataProductId, deploymentId)), initialSelectedUser: selectedContractTargetUser, //Derives environment from the fact that other environments are filtered out dataProductEnvironment: marketplaceBaseStore.envState.lakehouseEnvironment }))] })); }); //# sourceMappingURL=EntitlementsPendingTasksDashboard.js.map