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