Feat: Added new user profile page ui. (#3229)

* Feat: Added user profile page

* miner fix

* connected mention users to respective page
This commit is contained in:
Shailesh Parmar 2022-03-07 23:51:06 +05:30 committed by GitHub
parent 6ee31eb21f
commit 903e2f6246
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 235 additions and 1 deletions

View File

@ -0,0 +1,167 @@
import classNames from 'classnames';
import React, { useState } from 'react';
import { EntityReference, User } from '../../generated/entity/teams/user';
import UserCard from '../../pages/teams/UserCard';
import SVGIcons, { Icons } from '../../utils/SvgUtils';
import Avatar from '../common/avatar/Avatar';
import TabsPane from '../common/TabsPane/TabsPane';
import PageLayout from '../containers/PageLayout';
type Props = {
userData: User;
};
const Users = ({ userData }: Props) => {
const [activeTab, setActiveTab] = useState(1);
const activeTabHandler = (tab: number) => {
setActiveTab(tab);
};
const tabs = [
{
name: 'Owned Data',
icon: {
alt: 'owned-data',
name: 'owned-data',
title: 'Owned Data',
selectedName: 'owned-data',
},
isProtected: false,
position: 1,
},
{
name: 'Following',
icon: {
alt: 'following',
name: 'following',
title: 'following',
selectedName: 'following',
},
isProtected: false,
position: 2,
},
];
const fetchLeftPanel = () => {
return (
<div className="tw-pt-4">
<div className="tw-pb-4 tw-mb-4 tw-border-b tw-flex tw-flex-col tw-items-center">
{userData.profile?.images?.image ? (
<div className="tw-h-28 tw-w-28">
<img
alt="profile"
className="tw-rounded-full tw-w-full"
src={userData.profile?.images?.image}
/>
</div>
) : (
<Avatar
name={userData?.displayName || userData.name}
textClass="tw-text-5xl"
width="112"
/>
)}
<p className="tw-mt-4">
<span className="tw-text-base tw-font-medium tw-mr-2">
{userData.displayName || userData.name}
</span>
<span
className={classNames(
'tw-text-xs tw-border tw-px-1 tw-py-0.5 tw-rounded',
userData.deleted ? 'tw-border-grey-muted' : 'tw-border-success'
)}>
{userData.deleted ? (
<span className="tw-text-grey-muted">Inactive</span>
) : (
<span className="tw-text-success">Active</span>
)}
</span>
</p>
<p className="tw-mt-2">{userData.email}</p>
</div>
<div className="tw-pb-4 tw-mb-4 tw-border-b">
<h6 className="tw-heading tw-mb-3">Teams</h6>
{userData.teams?.map((team, i) => (
<div className="tw-mb-2 tw-flex tw-items-center tw-gap-2" key={i}>
<SVGIcons alt="icon" className="tw-w-4" icon={Icons.TEAMS_GREY} />
<span>{team?.displayName || team?.name}</span>
</div>
))}
</div>
<div className="tw-pb-4 tw-mb-4 tw-border-b">
<h6 className="tw-heading tw-mb-3">Roles</h6>
{userData.roles?.map((role, i) => (
<div className="tw-mb-2 tw-flex tw-items-center tw-gap-2" key={i}>
<SVGIcons alt="icon" className="tw-w-4" icon={Icons.USERS} />
<span>{role?.displayName || role?.name}</span>
</div>
))}
</div>
</div>
);
};
const getEntityData = (data: EntityReference[], placeholder: string) => {
if ((data?.length as number) <= 0) {
return (
<div className="tw-flex tw-flex-col tw-items-center tw-place-content-center tw-mt-40 tw-gap-1">
<p className="tw-text-base">{placeholder}</p>
</div>
);
}
return (
<>
<div
className="tw-grid xxl:tw-grid-cols-4 md:tw-grid-cols-3 tw-gap-4"
data-testid="dataset-card">
{' '}
{data?.map((dataset, index) => {
const Dataset = {
description: dataset.name || '',
name: dataset.type,
};
return (
<UserCard isDataset isIconVisible item={Dataset} key={index} />
);
})}
</div>
</>
);
};
return (
<PageLayout classes="tw-h-full tw-px-6" leftPanel={fetchLeftPanel()}>
<div className="tw-mb-3">
<TabsPane
activeTab={activeTab}
className="tw-flex-initial"
setActiveTab={activeTabHandler}
tabs={tabs}
/>
</div>
<div>
{activeTab === 1 &&
getEntityData(
userData?.owns || [],
`${
userData?.displayName || userData?.name || 'User'
} does not own anything yet`
)}
{activeTab === 2 &&
getEntityData(
userData?.follows || [],
`${
userData?.displayName || userData?.name || 'User'
} does not follow anything yet`
)}
</div>
</PageLayout>
);
};
export default Users;

View File

@ -50,6 +50,7 @@ const PLACEHOLDER_ROUTE_ENTITY_FQN = ':entityFQN';
const PLACEHOLDER_WEBHOOK_NAME = ':webhookName';
const PLACEHOLDER_GLOSSARY_NAME = ':glossaryName';
const PLACEHOLDER_GLOSSARY_TERMS_FQN = ':glossaryTermsFQN';
const PLACEHOLDER_USER_NAME = ':username';
export const pagingObject = { after: '', before: '' };
@ -154,6 +155,7 @@ export const ROUTES = {
PIPELINE_DETAILS: `/pipeline/${PLACEHOLDER_ROUTE_PIPELINE_FQN}`,
PIPELINE_DETAILS_WITH_TAB: `/pipeline/${PLACEHOLDER_ROUTE_PIPELINE_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
USER_LIST: '/user-list',
USER_PROFILE: `/users/${PLACEHOLDER_USER_NAME}`,
ROLES: '/roles',
WEBHOOKS: '/webhooks',
ADD_WEBHOOK: '/add-webhook',
@ -284,6 +286,13 @@ export const getEditWebhookPath = (webhookName: string) => {
return path;
};
export const getUserPath = (username: string) => {
let path = ROUTES.USER_PROFILE;
path = path.replace(PLACEHOLDER_USER_NAME, username);
return path;
};
export const getGlossaryTermsPath = (
glossaryName: string,
glossaryTerm = ''

View File

@ -23,7 +23,7 @@ export const entityRegex = /<#E\/([^<>]+?)\/([^<>]+?)\|(\[(.+?)?\]\((.+?)?\))>/;
export const entityUrlMap = {
team: 'teams',
user: 'user',
user: 'users',
};
export const EditorPlaceHolder = `Use @mention to tag a user or a team.

View File

@ -179,6 +179,7 @@ declare module 'Models' {
name: string;
profile: UserProfile;
teams: Array<UserTeam>;
follows?: Array<UserTeam>;
timezone: string;
href: string;
};

View File

@ -0,0 +1,55 @@
import { AxiosResponse } from 'axios';
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { getUserByName } from '../../axiosAPIs/userAPI';
import PageContainerV1 from '../../components/containers/PageContainerV1';
import Loader from '../../components/Loader/Loader';
import Users from '../../components/Users/Users.component';
import { User } from '../../generated/entity/teams/user';
const UserPage = () => {
const { username } = useParams<{ [key: string]: string }>();
const [isLoading, setIsLoading] = useState(true);
const [userData, setUserData] = useState<User>({} as User);
const [isError, setIsError] = useState(false);
const fetchUserData = () => {
getUserByName(username, 'profile,roles,teams,follows,owns')
.then((res: AxiosResponse) => {
setUserData(res.data);
})
.catch(() => {
setIsError(true);
})
.finally(() => setIsLoading(false));
};
const errorPlaceholder = () => {
return (
<div className="tw-flex tw-flex-col tw-items-center tw-place-content-center tw-mt-40 tw-gap-1">
<p className="tw-text-base">
No user available with{' '}
<span className="tw-font-medium">{username}</span> username.
</p>
</div>
);
};
useEffect(() => {
fetchUserData();
}, [username]);
return (
<PageContainerV1 className="tw-pt-4">
{isLoading ? (
<Loader />
) : !isError ? (
<Users userData={userData} />
) : (
errorPlaceholder()
)}
</PageContainerV1>
);
};
export default UserPage;

View File

@ -39,6 +39,7 @@ import TeamsPage from '../pages/teams';
import TopicDetailsPage from '../pages/TopicDetails/TopicDetailsPage.component';
import TourPageComponent from '../pages/tour-page/TourPage.component';
import UserListPage from '../pages/UserListPage/UserListPage';
import UserPage from '../pages/UserPage/UserPage.component';
import WebhooksPage from '../pages/WebhooksPage/WebhooksPage.component';
const AuthenticatedAppRouter: FunctionComponent = () => {
const { isAuthDisabled, isAdminUser } = useAuth();
@ -111,6 +112,7 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
path={ROUTES.GLOSSARY_DETAILS}
/> */}
{/* <Route exact component={GlossaryTermPage} path={ROUTES.GLOSSARY_TERMS} /> */}
<Route exact component={UserPage} path={ROUTES.USER_PROFILE} />
{isAuthDisabled || isAdminUser ? (
<>
<Route exact component={AddGlossaryPage} path={ROUTES.ADD_GLOSSARY} />