mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-07-30 12:49:58 +00:00
Fix: loading and entity count bug fix for Landing page (#955)
* resolved infinite loading bug onClick of active tab for landing page * fix count and loading issue for landing page * move const value to MyData.const file * move MyDataHeader component to its seprate folder and miner bug fix in MyDataPage component * add error handler at page level
This commit is contained in:
parent
328e3da12d
commit
e52dbacfd8
@ -16,16 +16,13 @@
|
||||
*/
|
||||
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Bucket, FormatedTableData, Sterm } from 'Models';
|
||||
import { FormatedTableData } from 'Models';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Ownership } from '../../enums/mydata.enum';
|
||||
import { formatDataResponse } from '../../utils/APIUtils';
|
||||
import { getCurrentUserId } from '../../utils/CommonUtils';
|
||||
import { getEntityCountByService } from '../../utils/ServiceUtils';
|
||||
import ErrorPlaceHolderES from '../common/error-with-placeholder/ErrorPlaceHolderES';
|
||||
import PageContainer from '../containers/PageContainer';
|
||||
import Loader from '../Loader/Loader';
|
||||
import MyDataHeader from '../my-data/MyDataHeader';
|
||||
import MyDataHeader from '../MyDataHeader/MyDataHeader.component';
|
||||
import RecentlyViewed from '../recently-viewed/RecentlyViewed';
|
||||
import SearchedData from '../searched-data/SearchedData';
|
||||
import { MyDataProps } from './MyData.interface';
|
||||
@ -35,21 +32,16 @@ const MyData: React.FC<MyDataProps> = ({
|
||||
userDetails,
|
||||
searchResult,
|
||||
fetchData,
|
||||
error,
|
||||
entityCounts,
|
||||
}: MyDataProps): React.ReactElement => {
|
||||
const [data, setData] = useState<Array<FormatedTableData>>([]);
|
||||
const [currentPage, setCurrentPage] = useState<number>(1);
|
||||
const [totalNumberOfValue, setTotalNumberOfValues] = useState<number>(0);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [isEntityLoading, setIsEntityLoading] = useState<boolean>(true);
|
||||
const [currentTab, setCurrentTab] = useState<number>(1);
|
||||
const [filter, setFilter] = useState<string>('');
|
||||
const [aggregations, setAggregations] = useState<Record<string, Sterm>>();
|
||||
const [searchIndex] = useState<string>(
|
||||
'dashboard_search_index,topic_search_index,table_search_index,pipeline_search_index'
|
||||
);
|
||||
const isMounted = useRef<boolean>(false);
|
||||
const setAssetCount = useRef<boolean>(true);
|
||||
|
||||
const isMounted = useRef(false);
|
||||
|
||||
const getActiveTabClass = (tab: number) => {
|
||||
return tab === currentTab ? 'active' : '';
|
||||
@ -68,21 +60,6 @@ const MyData: React.FC<MyDataProps> = ({
|
||||
return `${filter}:${getCurrentUserId()}`;
|
||||
};
|
||||
|
||||
const fetchTableData = () => {
|
||||
if (!isEntityLoading) {
|
||||
setIsLoading(true);
|
||||
}
|
||||
|
||||
fetchData({
|
||||
queryString: '',
|
||||
from: currentPage,
|
||||
filters: filter ? getFilters() : '',
|
||||
sortField: '',
|
||||
sortOrder: '',
|
||||
searchIndex: searchIndex,
|
||||
});
|
||||
};
|
||||
|
||||
const handleTabChange = (tab: number, filter: string) => {
|
||||
if (currentTab !== tab) {
|
||||
setIsEntityLoading(true);
|
||||
@ -127,8 +104,16 @@ const MyData: React.FC<MyDataProps> = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAssetCount.current = !isMounted.current;
|
||||
fetchTableData();
|
||||
if (isMounted.current) {
|
||||
setIsEntityLoading(true);
|
||||
fetchData({
|
||||
queryString: '',
|
||||
from: currentPage,
|
||||
filters: filter ? getFilters() : '',
|
||||
sortField: '',
|
||||
sortOrder: '',
|
||||
});
|
||||
}
|
||||
}, [currentPage, filter]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -137,22 +122,12 @@ const MyData: React.FC<MyDataProps> = ({
|
||||
if (hits.length > 0) {
|
||||
setTotalNumberOfValues(searchResult.data.hits.total.value);
|
||||
setData(formatDataResponse(hits));
|
||||
if (setAssetCount.current) {
|
||||
setAggregations(searchResult.data.aggregations);
|
||||
setAssetCount.current = false;
|
||||
}
|
||||
setIsLoading(false);
|
||||
setIsEntityLoading(false);
|
||||
} else {
|
||||
setData([]);
|
||||
setTotalNumberOfValues(0);
|
||||
setIsLoading(false);
|
||||
setIsEntityLoading(false);
|
||||
}
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setIsEntityLoading(false);
|
||||
}
|
||||
setIsEntityLoading(false);
|
||||
}, [searchResult]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -160,41 +135,27 @@ const MyData: React.FC<MyDataProps> = ({
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{error ? (
|
||||
<ErrorPlaceHolderES errorMessage={error} type="error" />
|
||||
) : (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<PageContainer>
|
||||
<div className="container-fluid" data-testid="fluid-container">
|
||||
<MyDataHeader
|
||||
countServices={countServices}
|
||||
entityCounts={getEntityCountByService(
|
||||
aggregations?.['sterms#Service']?.buckets as Bucket[]
|
||||
)}
|
||||
/>
|
||||
{getTabs()}
|
||||
<SearchedData
|
||||
showOnboardingTemplate
|
||||
currentPage={currentPage}
|
||||
data={data}
|
||||
isLoading={isEntityLoading}
|
||||
paginate={paginate}
|
||||
searchText="*"
|
||||
showOnlyChildren={currentTab === 1}
|
||||
showResultCount={filter && data.length > 0 ? true : false}
|
||||
totalValue={totalNumberOfValue}>
|
||||
{currentTab === 1 ? <RecentlyViewed /> : null}
|
||||
</SearchedData>
|
||||
</div>
|
||||
</PageContainer>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
<PageContainer>
|
||||
<div className="container-fluid" data-testid="fluid-container">
|
||||
<MyDataHeader
|
||||
countServices={countServices}
|
||||
entityCounts={entityCounts}
|
||||
/>
|
||||
{getTabs()}
|
||||
<SearchedData
|
||||
showOnboardingTemplate
|
||||
currentPage={currentPage}
|
||||
data={data}
|
||||
isLoading={isEntityLoading}
|
||||
paginate={paginate}
|
||||
searchText="*"
|
||||
showOnlyChildren={currentTab === 1}
|
||||
showResultCount={filter && data.length > 0 ? true : false}
|
||||
totalValue={totalNumberOfValue}>
|
||||
{currentTab === 1 ? <RecentlyViewed /> : null}
|
||||
</SearchedData>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { SearchDataFunctionType, SearchResponse } from 'Models';
|
||||
import { EntityCounts, SearchDataFunctionType, SearchResponse } from 'Models';
|
||||
import { User } from '../../generated/entity/teams/user';
|
||||
|
||||
export interface MyDataProps {
|
||||
countServices: number;
|
||||
userDetails: User;
|
||||
error: string;
|
||||
searchResult: SearchResponse | undefined;
|
||||
fetchData: (value: SearchDataFunctionType) => void;
|
||||
entityCounts: EntityCounts;
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ jest.mock('../../components/recently-viewed/RecentlyViewed', () => {
|
||||
return jest.fn().mockReturnValue(<p>RecentlyViewed</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../components/my-data/MyDataHeader', () => {
|
||||
jest.mock('../MyDataHeader/MyDataHeader.component', () => {
|
||||
return jest.fn().mockReturnValue(<p>MyDataHeader</p>);
|
||||
});
|
||||
|
||||
@ -258,7 +258,12 @@ describe('Test MyData page', () => {
|
||||
const { container } = render(
|
||||
<MyDataPage
|
||||
countServices={0}
|
||||
error=""
|
||||
entityCounts={{
|
||||
tableCount: 10,
|
||||
topicCount: 5,
|
||||
dashboardCount: 8,
|
||||
pipelineCount: 1,
|
||||
}}
|
||||
fetchData={fetchData}
|
||||
searchResult={mockData as unknown as SearchResponse}
|
||||
userDetails={mockUserDetails as unknown as User}
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { observer } from 'mobx-react';
|
||||
import { EntityCounts } from 'Models';
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import AppState from '../../AppState';
|
||||
@ -24,12 +25,7 @@ import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||
|
||||
type Props = {
|
||||
countServices: number;
|
||||
entityCounts: {
|
||||
tableCount: number;
|
||||
topicCount: number;
|
||||
dashboardCount: number;
|
||||
pipelineCount: number;
|
||||
};
|
||||
entityCounts: EntityCounts;
|
||||
};
|
||||
type Summary = {
|
||||
icon: string;
|
@ -18,7 +18,7 @@
|
||||
import { getByTestId, getByText, render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
import MyDataHeader from './MyDataHeader';
|
||||
import MyDataHeader from './MyDataHeader.component';
|
||||
|
||||
describe('Test MyDataHeader Component', () => {
|
||||
it('Component should render', () => {
|
@ -19,6 +19,16 @@ import { FilterObject } from 'Models';
|
||||
import { getCurrentUserId } from '../utils/CommonUtils';
|
||||
import { getFilterString } from '../utils/FilterUtils';
|
||||
|
||||
export const myDataSearchIndex =
|
||||
'dashboard_search_index,topic_search_index,table_search_index,pipeline_search_index';
|
||||
|
||||
export const myDataEntityCounts = {
|
||||
tableCount: 0,
|
||||
topicCount: 0,
|
||||
dashboardCount: 0,
|
||||
pipelineCount: 0,
|
||||
};
|
||||
|
||||
export const myDataFilterType = [
|
||||
{ key: 'Owned', value: 'owner' },
|
||||
{ key: 'Following', value: 'followers' },
|
||||
|
@ -331,7 +331,14 @@ declare module 'Models' {
|
||||
filters: string;
|
||||
sortField: string;
|
||||
sortOrder: string;
|
||||
searchIndex: string;
|
||||
searchIndex?: string;
|
||||
};
|
||||
|
||||
export type EntityCounts = {
|
||||
tableCount: number;
|
||||
topicCount: number;
|
||||
dashboardCount: number;
|
||||
pipelineCount: number;
|
||||
};
|
||||
|
||||
// topic interface start
|
||||
|
@ -16,25 +16,35 @@
|
||||
*/
|
||||
|
||||
import { AxiosError } from 'axios';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { observer } from 'mobx-react';
|
||||
import { SearchDataFunctionType, SearchResponse } from 'Models';
|
||||
import { EntityCounts, SearchDataFunctionType, SearchResponse } from 'Models';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import AppState from '../../AppState';
|
||||
import { searchData } from '../../axiosAPIs/miscAPI';
|
||||
import ErrorPlaceHolderES from '../../components/common/error-with-placeholder/ErrorPlaceHolderES';
|
||||
import Loader from '../../components/Loader/Loader';
|
||||
import MyData from '../../components/MyData/MyData.component';
|
||||
import { ERROR500, PAGE_SIZE } from '../../constants/constants';
|
||||
import {
|
||||
myDataEntityCounts,
|
||||
myDataSearchIndex,
|
||||
} from '../../constants/Mydata.constants';
|
||||
import useToastContext from '../../hooks/useToastContext';
|
||||
import { getAllServices } from '../../utils/ServiceUtils';
|
||||
import {
|
||||
getAllServices,
|
||||
getEntityCountByService,
|
||||
} from '../../utils/ServiceUtils';
|
||||
|
||||
const MyDataPage = () => {
|
||||
const showToast = useToastContext();
|
||||
const [error, setError] = useState<string>('');
|
||||
const [countServices, setCountServices] = useState<number>(0);
|
||||
const [countServices, setCountServices] = useState<number>();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [searchResult, setSearchResult] = useState<SearchResponse>();
|
||||
const [entityCounts, setEntityCounts] = useState<EntityCounts>();
|
||||
|
||||
const fetchData = (value: SearchDataFunctionType) => {
|
||||
const fetchData = (value: SearchDataFunctionType, fetchService = false) => {
|
||||
searchData(
|
||||
value.queryString,
|
||||
value.from,
|
||||
@ -42,10 +52,17 @@ const MyDataPage = () => {
|
||||
value.filters,
|
||||
value.sortField,
|
||||
value.sortOrder,
|
||||
value.searchIndex
|
||||
myDataSearchIndex
|
||||
)
|
||||
.then((res: SearchResponse) => {
|
||||
setSearchResult(res);
|
||||
if (isUndefined(entityCounts)) {
|
||||
setEntityCounts(
|
||||
getEntityCountByService(
|
||||
res.data.aggregations?.['sterms#Service']?.buckets
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
setError(err.response?.data?.responseMessage);
|
||||
@ -53,28 +70,45 @@ const MyDataPage = () => {
|
||||
variant: 'error',
|
||||
body: err.response?.data?.responseMessage ?? ERROR500,
|
||||
});
|
||||
setEntityCounts(myDataEntityCounts);
|
||||
});
|
||||
if (fetchService) {
|
||||
getAllServices()
|
||||
.then((res) => setCountServices(res.length))
|
||||
.catch(() => setCountServices(0));
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getAllServices()
|
||||
.then((res) => setCountServices(res.length))
|
||||
.catch(() => setCountServices(0));
|
||||
setIsLoading(false);
|
||||
fetchData(
|
||||
{
|
||||
queryString: '',
|
||||
from: 1,
|
||||
filters: '',
|
||||
sortField: '',
|
||||
sortOrder: '',
|
||||
},
|
||||
isUndefined(countServices)
|
||||
);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div data-testid="my-data-page-conatiner">
|
||||
{isLoading ? (
|
||||
<Loader />
|
||||
{countServices && entityCounts && !isLoading ? (
|
||||
error ? (
|
||||
<ErrorPlaceHolderES errorMessage={error} type="error" />
|
||||
) : (
|
||||
<MyData
|
||||
countServices={countServices}
|
||||
entityCounts={entityCounts}
|
||||
fetchData={fetchData}
|
||||
searchResult={searchResult}
|
||||
userDetails={AppState.userDetails}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<MyData
|
||||
countServices={countServices}
|
||||
error={error}
|
||||
fetchData={fetchData}
|
||||
searchResult={searchResult}
|
||||
userDetails={AppState.userDetails}
|
||||
/>
|
||||
<Loader />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -19,7 +19,7 @@ import { findByTestId, render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import MyDataPageComponent from './MyDataPage.component';
|
||||
|
||||
jest.mock('../../components/LandingPage/MyData.component', () => {
|
||||
jest.mock('../../components/MyData/MyData.component', () => {
|
||||
return jest.fn().mockReturnValue(<p>Mydata component</p>);
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user