diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChart.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChart.tsx index c2fc163834a..7114f4191e7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChart.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChart.tsx @@ -11,6 +11,7 @@ * limitations under the License. */ +import { PlusOutlined } from '@ant-design/icons'; import { Button, Card, @@ -21,6 +22,8 @@ import { Typography, } from 'antd'; import { AxiosError } from 'axios'; +import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder'; +import { ERROR_PLACEHOLDER_TYPE, SIZE } from 'enums/common.enum'; import { isEmpty, isUndefined } from 'lodash'; import React, { FC, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -276,27 +279,27 @@ const KPIChart: FC = ({ chartFilter, kpiList }) => { - - {t('message.no-kpi-available-add-new-one')} - - + - + })} + + + } + className="p-y-lg" + permission={isAdminUser} + size={SIZE.MEDIUM} + type={ERROR_PLACEHOLDER_TYPE.ASSIGN}> + {t('message.no-kpi-available-add-new-one')} + )} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TestSuitePipelineTab/TestSuitePipelineTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TestSuitePipelineTab/TestSuitePipelineTab.component.tsx index b06783f5d06..703214104c7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TestSuitePipelineTab/TestSuitePipelineTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/TestSuitePipelineTab/TestSuitePipelineTab.component.tsx @@ -11,11 +11,14 @@ * limitations under the License. */ -import { CheckOutlined } from '@ant-design/icons'; +import { CheckOutlined, PlusOutlined } from '@ant-design/icons'; import { Button, Col, Popover, Row, Table, Tooltip } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import { AxiosError } from 'axios'; +import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder'; import cronstrue from 'cronstrue'; +import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum'; +import { isEmpty } from 'lodash'; import React, { Fragment, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useHistory, useLocation, useParams } from 'react-router-dom'; @@ -550,13 +553,44 @@ const TestSuitePipelineTab = () => { currTriggerId, ]); + const errorPlaceholder = useMemo( + () => ( + } + type="primary" + onClick={() => { + history.push(getTestSuiteIngestionPath(testSuiteFQN)); + }}> + {t('label.add')} + + } + className="mt-24" + heading={t('label.pipeline')} + permission={createPermission} + type={ERROR_PLACEHOLDER_TYPE.ASSIGN} + /> + ), + [testSuiteFQN] + ); + if (isLoading || isFetchingStatus) { return ; } - return !isAirflowAvailable ? ( - - ) : ( + if (!isAirflowAvailable) { + return ; + } + + if (isEmpty(testSuitePipelines)) { + return errorPlaceholder; + } + + return ( { [] ); - if (isEmpty(alerts)) { + if (isEmpty(alerts) && !loading) { return ( { bordered columns={columns} dataSource={alerts} - loading={{ spinning: loading, indicator: }} + loading={{ spinning: loading, indicator: }} pagination={false} rowKey="id" size="middle" diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DataInsightPage/KPIList.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DataInsightPage/KPIList.tsx index 4356cea5ac1..6a511678b65 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DataInsightPage/KPIList.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DataInsightPage/KPIList.tsx @@ -188,7 +188,7 @@ const KPIList = () => { if (isEmpty(kpiList)) { return ( -
+
); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx index d2775b15147..c9ebcdbdc36 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx @@ -331,6 +331,7 @@ const DatabaseSchemaPage: FunctionComponent = () => { entity: t('label.database-schema'), }) ); + setError(errMsg); showErrorToast(errMsg); }) @@ -638,7 +639,7 @@ const DatabaseSchemaPage: FunctionComponent = () => { const getSchemaTableList = () => { return ( - {isEmpty(tableData) && !showDeletedTables ? ( + {isEmpty(tableData) && !showDeletedTables && !tableDataLoading ? ( { columns={tableColumn} data-testid="databaseSchema-tables" dataSource={tableData} - loading={{ - spinning: tableDataLoading, - indicator: , - }} locale={{ emptyText: , }} @@ -786,162 +783,168 @@ const DatabaseSchemaPage: FunctionComponent = () => { appState.inPageSearchText = ''; }, []); + if (isLoading) { + return ; + } + + if (error) { + return ( + +

{error}

+
+ ); + } + return ( - {isLoading ? ( - - ) : error ? ( - -

{error}

-
- ) : ( - <> - {databaseSchemaPermission.ViewAll || - databaseSchemaPermission.ViewBasic ? ( - - - {IsSchemaDetailsLoading ? ( - + + {IsSchemaDetailsLoading ? ( + + ) : ( + <> + + - ) : ( - <> - - - - - )} - - - - - - - {activeTab === 1 && ( - - - - - - {getSchemaTableList()} - - - )} - {activeTab === 2 && ( - - - - - - - - )} - } - span={24}> - {getLoader()} - - - + + )} + + - {threadLink ? ( - - ) : null} + - - - ) : ( - - )} - + + {activeTab === 1 && ( + + {tableDataLoading ? ( + + ) : ( + + + + + {getSchemaTableList()} + + )} + + )} + {activeTab === 2 && ( + + + + + + + + )} + } + span={24}> + {getLoader()} + + + + + + {threadLink ? ( + + ) : null} + + + + ) : ( + )}
); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TestSuitePage/TestSuitePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TestSuitePage/TestSuitePage.tsx index 7183fcfdad7..9b06a8b0ff8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TestSuitePage/TestSuitePage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TestSuitePage/TestSuitePage.tsx @@ -68,7 +68,7 @@ const TestSuitePage = () => { const { isAdminUser } = useAuth(); const { isAuthDisabled } = useAuthContext(); const [testSuites, setTestSuites] = useState>([]); - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(true); const [testSuitePage, setTestSuitePage] = useState(INITIAL_PAGING_VALUE); const [testSuitePaging, setTestSuitePaging] = useState(pagingObject); const [selectedTestSuite, setSelectedTestSuite] = useState(); @@ -85,6 +85,7 @@ const TestSuitePage = () => { }, [permissions]); const handleShowDeleted = (checked: boolean) => { + setIsLoading(true); setShowDeleted(checked); }; @@ -232,11 +233,7 @@ const TestSuitePage = () => { [createPermission] ); - if (isLoading) { - return ; - } - - if (isEmpty(testSuites) && !showDeleted) { + if (isEmpty(testSuites) && !showDeleted && !isLoading) { return {errorPlaceHolder}; } @@ -284,7 +281,10 @@ const TestSuitePage = () => { columns={columns} data-testid="test-suite-table" dataSource={testSuites} - loading={{ spinning: isLoading, indicator: }} + loading={{ + spinning: isLoading, + indicator: , + }} locale={{ emptyText: , }} @@ -306,6 +306,7 @@ const TestSuitePage = () => { )} { setIsEditable(false); }; + const databaseTable = useMemo(() => { + if (schemaDataLoading) { + return ; + } else if (!isEmpty(schemaData)) { + return ( + + , + }} + pagination={false} + rowKey="id" + size="small" + /> + {Boolean( + !isNil(databaseSchemaPaging.after) || + !isNil(databaseSchemaPaging.before) + ) && ( + + )} + + ); + } else { + return ; + } + }, [ + schemaDataLoading, + schemaData, + tableColumn, + databaseSchemaPaging, + currentPage, + databaseSchemaPagingHandler, + ]); + + if (isLoading) { + return ; + } + + if (error) { + return ( + +

{error}

+
+ ); + } + return ( <> - {isLoading ? ( - - ) : error ? ( - -

{error}

-
- ) : ( - <> - {databasePermission.ViewAll || databasePermission.ViewBasic ? ( - - - {isDatabaseDetailsLoading ? ( - - ) : ( - <> -
- {database && ( - + + {isDatabaseDetailsLoading ? ( + + ) : ( + <> + + {database && ( + - } - icon={ - - } - serviceName={database.service.name ?? ''} /> - )} - - - - - {extraInfo.map((info, index) => ( - - - {extraInfo.length !== 1 && - index < extraInfo.length - 1 ? ( - - {t('label.pipe-symbol')} - - ) : null} - - ))} - - - - { - if (isTagEditable) { - // Fetch tags and terms only once - if (tagList.length === 0) { - fetchTags(); - } - setIsEditable(true); - } - }}> - {!deleted && ( - { - handleTagSelection(); - }} - onSelectionChange={(tags) => { - handleTagSelection(tags); - }} - /> - )} - - - - )} - - - - - - - - {activeTab === 1 && ( - - - - - - -
, - }} - pagination={false} - rowKey="id" - size="small" - /> - {Boolean( - !isNil(databaseSchemaPaging.after) || - !isNil(databaseSchemaPaging.before) - ) && ( - - )} - - - - )} - {activeTab === 2 && ( - - - - - - - - )} - } - span={24}> - {getLoader()} - - - - - - {threadLink ? ( - + } + serviceName={database.service.name ?? ''} /> - ) : null} + )} - - - ) : ( - - )} - + + + + {extraInfo.map((info, index) => ( + + + {extraInfo.length !== 1 && + index < extraInfo.length - 1 ? ( + + {t('label.pipe-symbol')} + + ) : null} + + ))} + + + + { + if (isTagEditable) { + // Fetch tags and terms only once + if (tagList.length === 0) { + fetchTags(); + } + setIsEditable(true); + } + }}> + {!deleted && ( + { + handleTagSelection(); + }} + onSelectionChange={(tags) => { + handleTagSelection(tags); + }} + /> + )} + + + + )} + + + + + + + + {activeTab === 1 && ( + + + + + + + {databaseTable} + + + )} + {activeTab === 2 && ( + + + + + + + + )} + } + span={24}> + {getLoader()} + + + + + + {threadLink ? ( + + ) : null} + + + + ) : ( + )} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx index ac509f97b48..07cae596ee3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx @@ -159,6 +159,7 @@ const ServicePage: FunctionComponent = () => { const [serviceDetails, setServiceDetails] = useState(); const [data, setData] = useState>([]); const [isLoading, setIsLoading] = useState(!isOpenMetadataService); + const [isServiceLoading, setIsServiceLoading] = useState(true); const [dataModel, setDataModel] = useState>([]); const [dataModelPaging, setDataModelPaging] = useState(pagingObject); const [paging, setPaging] = useState(pagingObject); @@ -230,6 +231,23 @@ const ServicePage: FunctionComponent = () => { }, ]; + const isTestingDisabled = useMemo( + () => + !servicePermission.EditAll || + (serviceCategory === ServiceCategory.METADATA_SERVICES && + serviceFQN === OPEN_METADATA) || + isUndefined(connectionDetails), + [servicePermission, serviceCategory, serviceFQN, connectionDetails] + ); + + const goToEditConnection = () => { + history.push(getEditConnectionPath(serviceName || '', serviceFQN || '')); + }; + + const handleDelete = () => { + setDeleteWidgetVisible(true); + }; + const activeTabHandler = (tabValue: number) => { setActiveTab(tabValue); const currentTabIndex = tabValue - 1; @@ -416,7 +434,7 @@ const ServicePage: FunctionComponent = () => { }; const fetchDatabases = async (paging?: PagingWithoutTotal) => { - setIsLoading(true); + setIsServiceLoading(true); try { const { data, paging: resPaging } = await getDatabases( serviceFQN, @@ -431,12 +449,12 @@ const ServicePage: FunctionComponent = () => { } catch (error) { showErrorToast(error as AxiosError); } finally { - setIsLoading(false); + setIsServiceLoading(false); } }; const fetchTopics = async (paging?: PagingWithoutTotal) => { - setIsLoading(true); + setIsServiceLoading(true); try { const { data, paging: resPaging } = await getTopics( serviceFQN, @@ -448,12 +466,12 @@ const ServicePage: FunctionComponent = () => { } catch (error) { showErrorToast(error as AxiosError); } finally { - setIsLoading(false); + setIsServiceLoading(false); } }; const fetchDashboards = async (paging?: PagingWithoutTotal) => { - setIsLoading(true); + setIsServiceLoading(true); try { const { data, paging: resPaging } = await getDashboards( serviceFQN, @@ -465,12 +483,12 @@ const ServicePage: FunctionComponent = () => { } catch (error) { showErrorToast(error as AxiosError); } finally { - setIsLoading(false); + setIsServiceLoading(false); } }; const fetchDashboardsDataModel = async (paging?: PagingWithoutTotal) => { - setIsLoading(true); + setIsServiceLoading(true); try { const { data, paging: resPaging } = await getDataModels( serviceFQN, @@ -482,12 +500,12 @@ const ServicePage: FunctionComponent = () => { } catch (error) { showErrorToast(error as AxiosError); } finally { - setIsLoading(false); + setIsServiceLoading(false); } }; const fetchPipeLines = async (paging?: PagingWithoutTotal) => { - setIsLoading(true); + setIsServiceLoading(true); try { const { data, paging: resPaging } = await getPipelines( serviceFQN, @@ -499,12 +517,12 @@ const ServicePage: FunctionComponent = () => { } catch (error) { showErrorToast(error as AxiosError); } finally { - setIsLoading(false); + setIsServiceLoading(false); } }; const fetchMlModal = async (paging?: PagingWithoutTotal) => { - setIsLoading(true); + setIsServiceLoading(true); try { const { data, paging: resPaging } = await getMlModels( serviceFQN, @@ -516,12 +534,12 @@ const ServicePage: FunctionComponent = () => { } catch (error) { showErrorToast(error as AxiosError); } finally { - setIsLoading(false); + setIsServiceLoading(false); } }; const fetchContainers = async (paging?: PagingWithoutTotal) => { - setIsLoading(true); + setIsServiceLoading(true); try { const response = await getContainers({ service: serviceFQN, @@ -536,7 +554,7 @@ const ServicePage: FunctionComponent = () => { setData([]); setPaging(pagingObject); } finally { - setIsLoading(false); + setIsServiceLoading(false); } }; @@ -836,7 +854,7 @@ const ServicePage: FunctionComponent = () => { setCurrentPage(activePage ?? 1); }; - const getIngestionTab = () => { + const ingestionTab = useMemo(() => { if (!isAirflowAvailable) { return ; } else if (isUndefined(airflowEndpoint) || isUndefined(serviceDetails)) { @@ -863,11 +881,90 @@ const ServicePage: FunctionComponent = () => { ); } - }; + }, [ + isAirflowAvailable, + airflowEndpoint, + serviceDetails, + deleteIngestionById, + deployIngestion, + handleEnableDisableIngestion, + ingestions, + ingestionPaging, + servicePermission, + serviceName, + serviceList, + serviceFQN, + triggerIngestionById, + getAllIngestionWorkflows, + ]); - const getDataModalTab = () => ( - + const dataModalTab = useMemo( + () => , + [dataModel, isLoading] ); + + const testConnectionTab = useMemo(() => { + return ( + <> + + + + + {allowTestConn && isAirflowAvailable && ( + + + + )} + + + + ); + }, [ + servicePermission.EditAll, + allowTestConn, + isAirflowAvailable, + serviceDetails, + connectionDetails, + isTestingDisabled, + serviceCategory, + ]); + useEffect(() => { if ( servicePageTabs(getCountLabel(serviceName))[activeTab - 1].path !== tab @@ -876,18 +973,6 @@ const ServicePage: FunctionComponent = () => { } }, [tab]); - const goToEditConnection = () => { - history.push(getEditConnectionPath(serviceName || '', serviceFQN || '')); - }; - - const handleEditConnection = () => { - goToEditConnection(); - }; - - const handleDelete = () => { - setDeleteWidgetVisible(true); - }; - useEffect(() => { if (!isOpenMetadataService) { fetchServicePermission(); @@ -960,18 +1045,53 @@ const ServicePage: FunctionComponent = () => { ]; }, [serviceName]); + const entityServiceTab = useMemo(() => { + if (isServiceLoading) { + return ; + } else if (!isEmpty(data) && !isServiceLoading) { + return ( +
+
+ {Boolean(!isNil(paging.after) || !isNil(paging.before)) && ( + + )} + + ); + } else { + return ; + } + }, [ + isServiceLoading, + data, + paging, + tableColumn, + tableComponent, + currentPage, + pagingHandler, + ]); + useEffect(() => { if (isAirflowAvailable && !isOpenMetadataService) { getAllIngestionWorkflows(); } }, [isAirflowAvailable]); - const isTestingDisabled = - !servicePermission.EditAll || - (serviceCategory === ServiceCategory.METADATA_SERVICES && - serviceFQN === OPEN_METADATA) || - isUndefined(connectionDetails); - if (isLoading) { return ( @@ -1077,96 +1197,10 @@ const ServicePage: FunctionComponent = () => { tabs={tabs} /> - {activeTab === 1 && - (isEmpty(data) ? ( - - ) : ( -
-
, - }} - pagination={false} - rowKey="id" - size="small" - /> - {Boolean( - !isNil(paging.after) || !isNil(paging.before) - ) && ( - - )} - - ))} - - {activeTab === 4 && getDataModalTab()} - {activeTab === 2 && getIngestionTab()} - - {activeTab === 3 && ( - <> - - - - - {allowTestConn && isAirflowAvailable && ( - - - - )} - - - - )} + {activeTab === 1 && entityServiceTab} + {activeTab === 4 && dataModalTab} + {activeTab === 2 && ingestionTab} + {activeTab === 3 && testConnectionTab}