mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-29 09:42:23 +00:00
fix(ui): user page scroll issue on tabs and pagination (#12424)
* fix user page scroll on tabs and pagination too * changes as per comments * fix unit test
This commit is contained in:
parent
fc29eba285
commit
00934279c6
@ -139,8 +139,7 @@ const mockProp = {
|
||||
updateThreadHandler: jest.fn(),
|
||||
setFeedFilter: jest.fn(),
|
||||
threadType: 'Task' as ThreadType.Task,
|
||||
onFollowingEntityPaginate: jest.fn(),
|
||||
onOwnedEntityPaginate: jest.fn(),
|
||||
handlePaginate: jest.fn(),
|
||||
onSwitchChange: jest.fn(),
|
||||
};
|
||||
|
||||
|
||||
@ -11,7 +11,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Card, Image, Input, Select, Space, Tabs, Typography } from 'antd';
|
||||
import {
|
||||
Card,
|
||||
Col,
|
||||
Image,
|
||||
Input,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Tabs,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { ReactComponent as EditIcon } from 'assets/svg/edit-new.svg';
|
||||
import { ReactComponent as IconTeamsGrey } from 'assets/svg/teams-grey.svg';
|
||||
import { AxiosError } from 'axios';
|
||||
@ -80,8 +90,7 @@ const Users = ({
|
||||
isLoggedinUser,
|
||||
isAuthDisabled,
|
||||
username,
|
||||
onFollowingEntityPaginate,
|
||||
onOwnedEntityPaginate,
|
||||
handlePaginate,
|
||||
}: Props) => {
|
||||
const { tab = UserPageTabs.ACTIVITY } = useParams<{ tab: UserPageTabs }>();
|
||||
const [displayName, setDisplayName] = useState(userData.displayName);
|
||||
@ -692,48 +701,42 @@ const Users = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<PageLayoutV1
|
||||
className="user-page-layout"
|
||||
pageTitle={t('label.user')}
|
||||
rightPanel={
|
||||
showSummaryPanel &&
|
||||
entityDetails && (
|
||||
<Row className="user-page-layout" wrap={false}>
|
||||
<Col className="user-layout-scroll" flex="auto">
|
||||
{entityData.data.length ? (
|
||||
<SearchedData
|
||||
data={entityData.data ?? []}
|
||||
handleSummaryPanelDisplay={handleSummaryPanelDisplay}
|
||||
isFilterSelected={false}
|
||||
isSummaryPanelVisible={showSummaryPanel}
|
||||
selectedEntityId={entityDetails?.id || ''}
|
||||
totalValue={entityData.total ?? 0}
|
||||
onPaginationChange={handlePaginate}
|
||||
/>
|
||||
) : (
|
||||
<ErrorPlaceHolder className="m-0">
|
||||
<Typography.Paragraph>
|
||||
{tab === UserPageTabs.MY_DATA
|
||||
? t('server.you-have-not-action-anything-yet', {
|
||||
action: t('label.owned-lowercase'),
|
||||
})
|
||||
: t('server.you-have-not-action-anything-yet', {
|
||||
action: t('label.followed-lowercase'),
|
||||
})}
|
||||
</Typography.Paragraph>
|
||||
</ErrorPlaceHolder>
|
||||
)}
|
||||
</Col>
|
||||
|
||||
{showSummaryPanel && entityDetails && (
|
||||
<Col className="user-page-layout-right-panel " flex="400px">
|
||||
<EntitySummaryPanel
|
||||
entityDetails={{ details: entityDetails }}
|
||||
handleClosePanel={handleClosePanel}
|
||||
/>
|
||||
)
|
||||
}
|
||||
rightPanelWidth={400}>
|
||||
{entityData.data.length ? (
|
||||
<SearchedData
|
||||
currentPage={entityData.currPage}
|
||||
data={entityData.data ?? []}
|
||||
handleSummaryPanelDisplay={handleSummaryPanelDisplay}
|
||||
isFilterSelected={false}
|
||||
isSummaryPanelVisible={showSummaryPanel}
|
||||
selectedEntityId={entityDetails?.id || ''}
|
||||
totalValue={entityData.total ?? 0}
|
||||
onPaginationChange={
|
||||
tab === UserPageTabs.MY_DATA
|
||||
? onOwnedEntityPaginate
|
||||
: onFollowingEntityPaginate
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<ErrorPlaceHolder className="m-0">
|
||||
<Typography.Paragraph>
|
||||
{tab === UserPageTabs.MY_DATA
|
||||
? t('server.you-have-not-action-anything-yet', {
|
||||
action: t('label.owned-lowercase'),
|
||||
})
|
||||
: t('server.you-have-not-action-anything-yet', {
|
||||
action: t('label.followed-lowercase'),
|
||||
})}
|
||||
</Typography.Paragraph>
|
||||
</ErrorPlaceHolder>
|
||||
</Col>
|
||||
)}
|
||||
</PageLayoutV1>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
case UserPageTabs.ACTIVITY:
|
||||
|
||||
@ -19,21 +19,18 @@ export interface Props {
|
||||
followingEntities: {
|
||||
data: SearchedDataProps['data'];
|
||||
total: number;
|
||||
currPage: number;
|
||||
};
|
||||
ownedEntities: {
|
||||
data: SearchedDataProps['data'];
|
||||
total: number;
|
||||
currPage: number;
|
||||
};
|
||||
username: string;
|
||||
isUserEntitiesLoading: boolean;
|
||||
isAdminUser: boolean;
|
||||
isLoggedinUser: boolean;
|
||||
isAuthDisabled: boolean;
|
||||
handlePaginate: (page: string | number) => void;
|
||||
updateUserDetails: (data: Partial<User>) => Promise<void>;
|
||||
onFollowingEntityPaginate: (page: string | number) => void;
|
||||
onOwnedEntityPaginate: (page: string | number) => void;
|
||||
}
|
||||
|
||||
export enum UserPageTabs {
|
||||
|
||||
@ -29,8 +29,12 @@
|
||||
}
|
||||
|
||||
.user-page-layout {
|
||||
height: @users-page-tabs-height;
|
||||
.page-layout-rightpanel {
|
||||
.user-layout-scroll {
|
||||
height: @users-page-tabs-height;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.user-page-layout-right-panel {
|
||||
padding-right: 0 !important;
|
||||
background-color: @white;
|
||||
border: 1px solid @border-color;
|
||||
|
||||
@ -91,5 +91,4 @@ export interface SearchedDataProps {
|
||||
entityType: string
|
||||
) => void;
|
||||
filter?: Qs.ParsedQs;
|
||||
currentPage?: number;
|
||||
}
|
||||
|
||||
@ -48,7 +48,6 @@ const SearchedData: React.FC<SearchedDataProps> = ({
|
||||
selectedEntityId,
|
||||
handleSummaryPanelDisplay,
|
||||
filter,
|
||||
currentPage,
|
||||
}) => {
|
||||
const searchResultCards = useMemo(() => {
|
||||
return data.map(({ _source: table, highlight }, index) => {
|
||||
@ -159,11 +158,7 @@ const SearchedData: React.FC<SearchedDataProps> = ({
|
||||
<Pagination
|
||||
hideOnSinglePage
|
||||
className="text-center"
|
||||
current={
|
||||
isNumber(Number(page ?? currentPage))
|
||||
? Number(page ?? currentPage)
|
||||
: 1
|
||||
}
|
||||
current={isNumber(Number(page)) ? Number(page) : 1}
|
||||
pageSize={
|
||||
size && isNumber(Number(size))
|
||||
? Number(size)
|
||||
|
||||
@ -18,10 +18,16 @@ import Users from 'components/Users/Users.component';
|
||||
import { compare } from 'fast-json-patch';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { observer } from 'mobx-react';
|
||||
import { AssetsDataType } from 'Models';
|
||||
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
||||
import Qs from 'qs';
|
||||
import React, {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { searchData } from 'rest/miscAPI';
|
||||
import { getUserByName, updateUserDetail } from 'rest/userAPI';
|
||||
import AppState from '../../AppState';
|
||||
@ -32,8 +38,10 @@ import { User } from '../../generated/entity/teams/user';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import { SearchEntityHits } from '../../utils/APIUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import { UserAssetsDataType } from './UserPage.interface';
|
||||
|
||||
const UserPage = () => {
|
||||
const history = useHistory();
|
||||
const { t } = useTranslation();
|
||||
const { username, tab = UserProfileTab.ACTIVITY } =
|
||||
useParams<{ [key: string]: string }>();
|
||||
@ -46,17 +54,26 @@ const UserPage = () => {
|
||||
const [isUserEntitiesLoading, setIsUserEntitiesLoading] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [followingEntities, setFollowingEntities] = useState<AssetsDataType>({
|
||||
const [followingEntities, setFollowingEntities] =
|
||||
useState<UserAssetsDataType>({
|
||||
data: [],
|
||||
total: 0,
|
||||
});
|
||||
const [ownedEntities, setOwnedEntities] = useState<UserAssetsDataType>({
|
||||
data: [],
|
||||
total: 0,
|
||||
currPage: 1,
|
||||
});
|
||||
const [ownedEntities, setOwnedEntities] = useState<AssetsDataType>({
|
||||
data: [],
|
||||
total: 0,
|
||||
currPage: 1,
|
||||
});
|
||||
|
||||
const { page = 1 } = useMemo(
|
||||
() =>
|
||||
Qs.parse(
|
||||
location.search.startsWith('?')
|
||||
? location.search.substr(1)
|
||||
: location.search
|
||||
),
|
||||
[location.search]
|
||||
);
|
||||
|
||||
const fetchUserData = () => {
|
||||
setUserData({} as User);
|
||||
getUserByName(username, 'profile,roles,teams')
|
||||
@ -95,16 +112,14 @@ const UserPage = () => {
|
||||
|
||||
const fetchEntities = async (
|
||||
fetchOwnedEntities = false,
|
||||
handleEntity: Dispatch<SetStateAction<AssetsDataType>>
|
||||
handleEntity: Dispatch<SetStateAction<UserAssetsDataType>>
|
||||
) => {
|
||||
const entity = fetchOwnedEntities ? ownedEntities : followingEntities;
|
||||
|
||||
if (userData.id) {
|
||||
setIsUserEntitiesLoading(true);
|
||||
try {
|
||||
const response = await searchData(
|
||||
'',
|
||||
entity.currPage,
|
||||
Number(page),
|
||||
PAGE_SIZE,
|
||||
getQueryFilters(fetchOwnedEntities),
|
||||
'',
|
||||
@ -118,14 +133,12 @@ const UserPage = () => {
|
||||
handleEntity({
|
||||
data: hits,
|
||||
total,
|
||||
currPage: entity.currPage,
|
||||
});
|
||||
} else {
|
||||
const total = 0;
|
||||
handleEntity({
|
||||
data: [],
|
||||
total,
|
||||
currPage: entity.currPage,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@ -141,12 +154,10 @@ const UserPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleFollowingEntityPaginate = (page: string | number) => {
|
||||
setFollowingEntities((pre) => ({ ...pre, currPage: page as number }));
|
||||
};
|
||||
|
||||
const handleOwnedEntityPaginate = (page: string | number) => {
|
||||
setOwnedEntities((pre) => ({ ...pre, currPage: page as number }));
|
||||
const handleEntityPaginate = (page: string | number) => {
|
||||
history.push({
|
||||
search: Qs.stringify({ page }),
|
||||
});
|
||||
};
|
||||
|
||||
const ErrorPlaceholder = () => {
|
||||
@ -189,6 +200,7 @@ const UserPage = () => {
|
||||
return (
|
||||
<Users
|
||||
followingEntities={followingEntities}
|
||||
handlePaginate={handleEntityPaginate}
|
||||
isAdminUser={Boolean(isAdminUser)}
|
||||
isAuthDisabled={Boolean(isAuthDisabled)}
|
||||
isLoggedinUser={isLoggedinUser(username)}
|
||||
@ -197,8 +209,6 @@ const UserPage = () => {
|
||||
updateUserDetails={updateUserDetails}
|
||||
userData={userData}
|
||||
username={username}
|
||||
onFollowingEntityPaginate={handleFollowingEntityPaginate}
|
||||
onOwnedEntityPaginate={handleOwnedEntityPaginate}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@ -214,13 +224,13 @@ const UserPage = () => {
|
||||
if (tab === UserProfileTab.FOLLOWING) {
|
||||
fetchEntities(false, setFollowingEntities);
|
||||
}
|
||||
}, [followingEntities.currPage, tab, userData]);
|
||||
}, [page, tab, userData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tab === UserProfileTab.MY_DATA) {
|
||||
fetchEntities(true, setOwnedEntities);
|
||||
}
|
||||
}, [ownedEntities.currPage, tab, userData]);
|
||||
}, [page, tab, userData]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentLoggedInUser(AppState.getCurrentUserDetails());
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* 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 { SearchEntityHits } from 'utils/APIUtils';
|
||||
|
||||
export interface UserAssetsDataType {
|
||||
data: SearchEntityHits;
|
||||
total: number;
|
||||
}
|
||||
@ -104,6 +104,7 @@ jest.mock('components/authentication/auth-provider/AuthProvider', () => {
|
||||
});
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useHistory: jest.fn(),
|
||||
useParams: jest.fn().mockImplementation(() => ({ username: 'xyz' })),
|
||||
useLocation: jest.fn().mockImplementation(() => new URLSearchParams()),
|
||||
}));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user