diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-kpi.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-kpi.svg new file mode 100644 index 00000000000..d329b681a80 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-kpi.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-open-task.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-open-task.svg new file mode 100644 index 00000000000..a6dca502f7e --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-open-task.svg @@ -0,0 +1,3 @@ + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/ActivityFeedCardV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/ActivityFeedCardV1.tsx index ffb6cf2ec74..8a62dfb5a9b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/ActivityFeedCardV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/ActivityFeedCardV1.tsx @@ -14,9 +14,11 @@ import { Col, Row } from 'antd'; import classNames from 'classnames'; import UserPopOverCard from 'components/common/PopOverCard/UserPopOverCard'; import ProfilePicture from 'components/common/ProfilePicture/ProfilePicture'; +import Reactions from 'components/Reactions/Reactions'; import { ReactionOperation } from 'enums/reactions.enum'; import { compare } from 'fast-json-patch'; import { Post, ReactionType, Thread } from 'generated/entity/feed/thread'; +import { noop } from 'lodash'; import React, { useState } from 'react'; import { useActivityFeedProvider } from '../ActivityFeedProvider/ActivityFeedProvider'; import ActivityFeedActions from '../Shared/ActivityFeedActions'; @@ -81,6 +83,7 @@ const ActivityFeedCardV1 = ({ @@ -91,9 +94,7 @@ const ActivityFeedCardV1 = ({ announcement={!isPost ? feed.announcement : undefined} isEditPost={isEditPost} message={post.message} - reactions={post.reactions} onEditCancel={() => setIsEditPost(false)} - onReactionUpdate={onReactionUpdate} onUpdate={onUpdate} /> @@ -101,15 +102,9 @@ const ActivityFeedCardV1 = ({ {!showThread && !isPost && postLength > 0 && ( - -
-
- {' '} - {postLength} -
-
+ +
+
{repliedUniqueUsersList.map((user) => ( ))}
+
+ {' '} + {postLength} +
+ + {Boolean(post.reactions?.length) && ( + + )}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.interface.ts index 28949879ce1..99c94bc2845 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.interface.ts @@ -10,25 +10,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ReactionOperation } from 'enums/reactions.enum'; -import { - AnnouncementDetails, - Reaction, - ReactionType, -} from 'generated/entity/feed/thread'; +import { AnnouncementDetails } from 'generated/entity/feed/thread'; export interface FeedCardBodyV1Props { isEditPost: boolean; className?: string; showSchedule?: boolean; - showReactions?: boolean; announcement?: AnnouncementDetails; message: string; - reactions?: Reaction[]; + isOpenInDrawer?: boolean; onUpdate?: (message: string) => void; onEditCancel?: () => void; - onReactionUpdate?: ( - reaction: ReactionType, - operation: ReactionOperation - ) => void; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.tsx index a3ef354f7ab..2bd4eb830f4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyV1.tsx @@ -14,8 +14,7 @@ import { Button, Col, Row, Typography } from 'antd'; import classNames from 'classnames'; import ActivityFeedEditor from 'components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditor'; import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer'; -import Reactions from 'components/Reactions/Reactions'; -import { isUndefined, noop } from 'lodash'; +import { isUndefined } from 'lodash'; import React, { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { getFrontEndFormat, MarkdownToHTMLConverter } from 'utils/FeedUtils'; @@ -26,13 +25,10 @@ const FeedCardBodyV1 = ({ isEditPost, className, showSchedule = true, - showReactions = true, message, announcement, - reactions = [], onUpdate, onEditCancel, - onReactionUpdate, }: FeedCardBodyV1Props) => { const { t } = useTranslation(); const [postMessage, setPostMessage] = useState(message); @@ -122,12 +118,6 @@ const FeedCardBodyV1 = ({ feedBody )}
- {showReactions && Boolean(reactions?.length) && ( - - )}
); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardHeader/feed-card-header-v1.style.less b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardHeader/feed-card-header-v1.style.less index f7611ab15a4..b2403d03228 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardHeader/feed-card-header-v1.style.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardHeader/feed-card-header-v1.style.less @@ -19,15 +19,6 @@ a { color: @link-color !important; } - .feed-header-timestamp { - font-size: 12px; - color: @grey-2; - } - .feed-header-timestamp::before { - content: '\2022'; - margin-right: 8px; - margin-left: 4px; - } .thread-author { font-weight: 600; } @@ -46,3 +37,13 @@ flex-wrap: wrap; } } + +.feed-header-timestamp { + font-size: 12px; + color: @grey-2; +} +.feed-header-timestamp::before { + content: '\2022'; + margin-right: 8px; + margin-left: 4px; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/activity-feed-card.style.less b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/activity-feed-card.style.less index 40473e9c723..746d6f636e3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/activity-feed-card.style.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/activity-feed-card.style.less @@ -58,15 +58,6 @@ border-right: none; } -.feed-actions:hover { - width: 130px; -} - -.feed-actions:hover .expand-button { - opacity: 0; - pointer-events: none; -} - .action-buttons { position: absolute; top: 0; @@ -78,11 +69,6 @@ transition: opacity 0.3s, transform 0.3s; } -.feed-actions:hover .action-buttons { - opacity: 1; - transform: translateX(0); -} - .expand-button { background-color: transparent; border: none; @@ -112,3 +98,17 @@ margin-left: -12px; } } + +.activity-feed-card-v1:hover { + .feed-actions { + width: 130px; + .expand-button { + opacity: 0; + pointer-events: none; + } + .action-buttons { + opacity: 1; + transform: translateX(0); + } + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedDrawer/ActivityFeedDrawer.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedDrawer/ActivityFeedDrawer.tsx index 102a0f3824f..c6fb9431c2c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedDrawer/ActivityFeedDrawer.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedDrawer/ActivityFeedDrawer.tsx @@ -77,7 +77,11 @@ const ActivityFeedDrawer: FC = ({ ) : (
- + = ({ feed, className, showThread = true, + isOpenInDrawer = false, }) => { const { t } = useTranslation(); const mainFeed = { @@ -45,13 +48,24 @@ const FeedPanelBodyV1: FC = ({ 'has-replies': showThread && postLength > 0, })} data-testid="message-container"> - + {feed.type === ThreadType.Task ? ( + + ) : ( + + )} + {showThread && postLength > 0 ? (
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Shared/ActivityFeedActions.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Shared/ActivityFeedActions.tsx index 96cb4aca6c6..1754603037f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Shared/ActivityFeedActions.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Shared/ActivityFeedActions.tsx @@ -119,6 +119,8 @@ const ActivityFeedActions = ({ const editCheck = useMemo(() => { if (feed.type === ThreadType.Announcement && !isPost) { return false; + } else if (feed.type === ThreadType.Task && !isPost) { + return false; } else if (isAuthor || currentUser?.isAdmin) { return true; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/TaskFeedCard/TaskFeedCard.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/TaskFeedCard/TaskFeedCard.component.tsx new file mode 100644 index 00000000000..4c1d3b7e4ea --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/TaskFeedCard/TaskFeedCard.component.tsx @@ -0,0 +1,218 @@ +/* + * 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 { Col, Row, Tooltip, Typography } from 'antd'; +import classNames from 'classnames'; +import AssigneeList from 'components/common/AssigneeList/AssigneeList'; +import EntityPopOverCard from 'components/common/PopOverCard/EntityPopOverCard'; +import UserPopOverCard from 'components/common/PopOverCard/UserPopOverCard'; +import ProfilePicture from 'components/common/ProfilePicture/ProfilePicture'; +import Reactions from 'components/Reactions/Reactions'; +import { ReactionOperation } from 'enums/reactions.enum'; +import { Post, ReactionType, Thread } from 'generated/entity/feed/thread'; +import { isUndefined, toString } from 'lodash'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; +import { + entityDisplayName, + getEntityFieldDisplay, + getEntityFQN, + getEntityType, + prepareFeedLink, +} from 'utils/FeedUtils'; +import { getTaskDetailPath } from 'utils/TasksUtils'; +import { + getDateTimeFromMilliSeconds, + getDayTimeByTimeStamp, +} from 'utils/TimeUtils'; +import { useActivityFeedProvider } from '../ActivityFeedProvider/ActivityFeedProvider'; +import ActivityFeedActions from '../Shared/ActivityFeedActions'; +import './task-feed-card.less'; +import { ReactComponent as TaskOpenIcon } from '/assets/svg/ic-open-task.svg'; +import { ReactComponent as ThreadIcon } from '/assets/svg/thread.svg'; + +interface TaskFeedCardProps { + post: Post; + feed: Thread; + className?: string; + showThread?: boolean; + isEntityFeed?: boolean; + isOpenInDrawer?: boolean; +} + +const TaskFeedCard = ({ + post, + feed, + className = '', + isEntityFeed = false, + showThread = true, + isOpenInDrawer = false, +}: TaskFeedCardProps) => { + const { t } = useTranslation(); + const timeStamp = feed.threadTs; + const taskDetails = feed.task; + const postLength = feed?.postsCount ?? 0; + const entityType = getEntityType(feed.about) ?? ''; + const entityFQN = getEntityFQN(feed.about) ?? ''; + const entityCheck = !isUndefined(entityFQN) && !isUndefined(entityType); + const [isEditPost, setIsEditPost] = useState(false); + const repliedUsers = [...new Set((feed?.posts ?? []).map((f) => f.from))]; + const repliedUniqueUsersList = repliedUsers.slice(0, postLength >= 3 ? 2 : 1); + + const { showDrawer, updateReactions } = useActivityFeedProvider(); + + const showReplies = () => { + showDrawer?.(feed); + }; + + const onEditPost = () => { + setIsEditPost(!isEditPost); + }; + + const onReactionUpdate = ( + reaction: ReactionType, + operation: ReactionOperation + ) => { + updateReactions(post, feed.id, true, reaction, operation); + }; + + const getTaskLinkElement = entityCheck && ( + + e.stopPropagation()}> + {`#${taskDetails?.id} `} + + + {taskDetails?.type} + {t('label.for-lowercase')} + {isEntityFeed ? ( + + {getEntityFieldDisplay(feed.about)} + + ) : ( + <> + {entityType} + + e.stopPropagation()}> + {entityDisplayName(entityType, entityFQN)} + + + + )} + + ); + + return ( + <> +
+ + + + + + {getTaskLinkElement} + + + + + {feed.createdBy} + + {t('message.created-this-task-lowercase')} + {timeStamp && ( + + + {getDayTimeByTimeStamp(timeStamp)} + + + )} + + + + + + {t('label.assignee-plural')} + + + + + {!showThread && postLength > 0 && ( + + +
+
+ {repliedUniqueUsersList.map((user) => ( + + + + + + ))} +
+
+ {' '} + {postLength} +
+ {Boolean(feed.reactions?.length) && ( + + )} +
+ +
+ )} + + +
+ + ); +}; + +export default TaskFeedCard; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/TaskFeedCard/task-feed-card.less b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/TaskFeedCard/task-feed-card.less new file mode 100644 index 00000000000..935a2420646 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/TaskFeedCard/task-feed-card.less @@ -0,0 +1,22 @@ +/* + * 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. + */ + +.task-feed-card-v1 { + .task-feed-body { + padding-left: 1.5rem; + } + .assignee-item { + margin: 0; + margin-right: 4px; + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChartV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChartV1.tsx index 90c51141dde..d59b8b5f045 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChartV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChartV1.tsx @@ -12,7 +12,9 @@ */ import { Card, Col, Row, Typography } from 'antd'; +import { ReactComponent as KPIIcon } from 'assets/svg/ic-kpi.svg'; import { AxiosError } from 'axios'; +import { DATA_INSIGHT_DOCS } from 'constants/docs.constants'; import { isEmpty, isUndefined } from 'lodash'; import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -26,6 +28,7 @@ import { YAxis, } from 'recharts'; import { getLatestKpiResult, getListKpiResult } from 'rest/KpiAPI'; +import { Transi18next } from 'utils/CommonUtils'; import { getCurrentDateTimeMillis, getPastDaysDateTimeMillis, @@ -36,7 +39,6 @@ import { Kpi, KpiResult } from '../../generated/dataInsight/kpi/kpi'; import { UIKpiResult } from '../../interface/data-insight.interface'; import { CustomTooltip, getKpiGraphData } from '../../utils/DataInsightUtils'; import { showErrorToast } from '../../utils/ToastUtils'; -import { EmptyGraphPlaceholder } from './EmptyGraphPlaceholder'; import KPILatestResultsV1 from './KPILatestResultsV1'; interface Props { @@ -44,6 +46,39 @@ interface Props { selectedDays: number; } +const EmptyPlaceholder = () => { + const { t } = useTranslation(); + + return ( +
+ +
+ + {t('message.adding-new-entity-is-easy-just-give-it-a-spin', { + entity: t('label.data-insight'), + })} + + + + } + values={{ + doc: t('label.doc-plural-lowercase'), + }} + /> + +
+
+ ); +}; + const KPIChartV1: FC = ({ kpiList, selectedDays }) => { const { t } = useTranslation(); @@ -204,12 +239,12 @@ const KPIChartV1: FC = ({ kpiList, selectedDays }) => { ) : ( - + )} ) : ( - + )} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeader/EntityHeader.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeader/EntityHeader.component.tsx index 86e097b0c44..d583c55a5de 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeader/EntityHeader.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeader/EntityHeader.component.tsx @@ -50,7 +50,7 @@ export const EntityHeader = ({
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/RightSidebar/RightSidebar.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/RightSidebar/RightSidebar.component.tsx index 8de0520661b..dfd5e520a80 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/RightSidebar/RightSidebar.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/RightSidebar/RightSidebar.component.tsx @@ -54,52 +54,56 @@ const RightSidebar = ({ return ( <> -
- - {t('label.recent-announcement-plural')} - -
- {announcements.map((item) => { - return ( - - - - - } - key={item.id} - message={ -
- - - {t('label.announcement')} - -
- } - type="info" - /> - ); - })} -
-
+ {announcements.length > 0 && ( + <> +
+ + {t('label.recent-announcement-plural')} + +
+ {announcements.map((item) => { + return ( + + + + + } + key={item.id} + message={ +
+ + + {t('label.announcement')} + +
+ } + type="info" + /> + ); + })} +
+
+ + + + )} -
= ({ reactions, onReactionSelect }) => { }); return ( -
-
- {emojis} - - - -
+
+ {emojis} + + +
); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Widgets/FeedsWidget/FeedsWidget.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Widgets/FeedsWidget/FeedsWidget.component.tsx index d8f828fcb84..7f3dc46525e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Widgets/FeedsWidget/FeedsWidget.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Widgets/FeedsWidget/FeedsWidget.component.tsx @@ -11,25 +11,35 @@ * limitations under the License. */ import { Tabs } from 'antd'; +import AppState from 'AppState'; import ActivityFeedListV1 from 'components/ActivityFeed/ActivityFeedList/ActivityFeedListV1.component'; import { useActivityFeedProvider } from 'components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider'; import { FeedFilter } from 'enums/mydata.enum'; import { ThreadType } from 'generated/entity/feed/thread'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { getFeedsWithFilter } from 'rest/feedsAPI'; +import { showErrorToast } from 'utils/ToastUtils'; import './feeds-widget.less'; const FeedsWidget = () => { const { t } = useTranslation(); const [activeTab, setActiveTab] = useState('all'); const { loading, entityThread, getFeedData } = useActivityFeedProvider(); + const [taskCount, setTaskCount] = useState(0); + const currentUser = useMemo( + () => AppState.getCurrentUserDetails(), + [AppState.userDetails, AppState.nonSecureUserDetails] + ); useEffect(() => { if (activeTab === 'all') { - getFeedData(FeedFilter.OWNER).catch(() => { - // ignore since error is displayed in toast in the parent promise. - // Added block for sonar code smell - }); + getFeedData(FeedFilter.OWNER, undefined, ThreadType.Conversation).catch( + () => { + // ignore since error is displayed in toast in the parent promise. + // Added block for sonar code smell + } + ); } else if (activeTab === 'mentions') { getFeedData(FeedFilter.MENTIONS).catch(() => { // ignore since error is displayed in toast in the parent promise. @@ -43,6 +53,21 @@ const FeedsWidget = () => { } }, [activeTab]); + useEffect(() => { + getFeedsWithFilter( + currentUser?.id, + FeedFilter.OWNER, + undefined, + ThreadType.Task + ) + .then((res) => { + setTaskCount(res.data.length); + }) + .catch((err) => { + showErrorToast(err); + }); + }, [currentUser]); + return (
{ ), }, { - label: t('label.task-plural'), + label: `${t('label.task-plural')} (${taskCount})`, key: 'tasks', children: ( { assignees: EntityReference[]; + profilePicType?: ImageShape; + showUserName?: boolean; + profileWidth?: string; } -const AssigneeList: FC = ({ assignees, className }) => { +const AssigneeList: FC = ({ + assignees, + className, + profilePicType = 'square', + showUserName = true, + profileWidth = '20', +}) => { const history = useHistory(); const handleClick = (e: React.MouseEvent, assignee: EntityReference) => { @@ -40,11 +50,18 @@ const AssigneeList: FC = ({ assignees, className }) => { type={assignee.type} userName={assignee.name || ''}> handleClick(e, assignee)}> - - {assignee.name || ''} + + {showUserName && ( + {assignee.name ?? ''} + )} ))} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/EntityPopOverCard.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/EntityPopOverCard.tsx index 97362015c5d..464310b3fb8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/EntityPopOverCard.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/EntityPopOverCard.tsx @@ -11,64 +11,66 @@ * limitations under the License. */ -import { Button, Divider, Popover, Space, Typography } from 'antd'; -import { AxiosError } from 'axios'; +import { Popover } from 'antd'; import { EntityUnion } from 'components/Explore/explore.interface'; -import { get, uniqueId } from 'lodash'; -import { EntityTags } from 'Models'; -import React, { FC, HTMLAttributes, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Link } from 'react-router-dom'; +import ExploreSearchCard from 'components/ExploreV1/ExploreSearchCard/ExploreSearchCard'; +import React, { + FC, + HTMLAttributes, + useCallback, + useEffect, + useState, +} from 'react'; import { getDashboardByFqn } from 'rest/dashboardAPI'; import { getDatabaseDetailsByFQN, getDatabaseSchemaDetailsByFQN, } from 'rest/databaseAPI'; +import { getGlossaryTermByFQN } from 'rest/glossaryAPI'; import { getMlModelByFQN } from 'rest/mlModelAPI'; import { getPipelineByFqn } from 'rest/pipelineAPI'; import { getTableDetailsByFQN } from 'rest/tableAPI'; import { getTopicByFqn } from 'rest/topicsAPI'; import { getEntityName } from 'utils/EntityUtils'; import AppState from '../../../AppState'; -import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants'; import { EntityType } from '../../../enums/entity.enum'; import { Table } from '../../../generated/entity/data/table'; -import { TagSource } from '../../../generated/type/tagLabel'; -import SVGIcons from '../../../utils/SvgUtils'; -import { - getEntityLink, - getTagsWithoutTier, - getTierTags, -} from '../../../utils/TableUtils'; -import { showErrorToast } from '../../../utils/ToastUtils'; -import ProfilePicture from '../ProfilePicture/ProfilePicture'; -import RichTextEditorPreviewer from '../rich-text-editor/RichTextEditorPreviewer'; +import './popover-card.less'; interface Props extends HTMLAttributes { entityType: string; entityFQN: string; } +const PopoverContent: React.FC<{ + entityData: EntityUnion; + entityFQN: string; + entityType: string; +}> = ({ entityData, entityFQN, entityType }) => { + const name = entityData.name; + const displayName = getEntityName(entityData); + + return ( + + ); +}; + const EntityPopOverCard: FC = ({ children, entityType, entityFQN }) => { - const { t } = useTranslation(); const [entityData, setEntityData] = useState({} as EntityUnion); - const entityTier = useMemo(() => { - const tierFQN = getTierTags((entityData as Table).tags || [])?.tagFQN; - - return tierFQN?.split(FQN_SEPARATOR_CHAR)[1]; - }, [(entityData as Table).tags]); - - const entityTags = useMemo(() => { - const tags: EntityTags[] = - getTagsWithoutTier((entityData as Table).tags || []) || []; - - return tags.map((tag) => - tag.source === TagSource.Glossary ? tag.tagFQN : `#${tag.tagFQN}` - ); - }, [(entityData as Table).tags]); - - const getData = () => { + const getData = useCallback(() => { const setEntityDetails = (entityDetail: EntityUnion) => { AppState.entityData[entityFQN] = entityDetail; }; @@ -105,6 +107,10 @@ const EntityPopOverCard: FC = ({ children, entityType, entityFQN }) => { case EntityType.DATABASE_SCHEMA: promise = getDatabaseSchemaDetailsByFQN(entityFQN, 'owner'); + break; + case EntityType.GLOSSARY_TERM: + promise = getGlossaryTermByFQN(entityFQN, 'owner'); + break; default: @@ -118,23 +124,11 @@ const EntityPopOverCard: FC = ({ children, entityType, entityFQN }) => { setEntityData(res); }) - .catch((err: AxiosError) => showErrorToast(err)); + .catch(() => { + // do nothing + }); } - }; - - const PopoverTitle = () => { - return ( - - - - ); - }; + }, [entityType, entityFQN]); const onMouseOver = () => { const entitydetails = AppState.entityData[entityFQN]; @@ -145,95 +139,22 @@ const EntityPopOverCard: FC = ({ children, entityType, entityFQN }) => { } }; - const ownerName = useMemo(() => { - return get(entityData, 'owner'); - }, [entityData]); - - const PopoverContent = () => { - useEffect(() => { - onMouseOver(); - }, []); - - return ( -
- -
- {ownerName ? ( - - - - {getEntityName(ownerName)} - - - ) : ( - - {t('label.no-entity', { - entity: t('label.owner'), - })} - - )} -
- | - - {entityTier - ? entityTier - : t('label.no-entity', { - entity: t('label.tier'), - })} - -
- -
- {entityData.description ? ( - - ) : ( - - {t('label.no-entity', { - entity: t('label.description'), - })} - - )} -
- - {entityTags.length ? ( - <> - -
- - - - - - {entityTags.map((tag) => ( - - {tag} - - ))} - -
- - ) : null} -
- ); - }; + useEffect(() => { + onMouseOver(); + }, [getData, entityFQN]); return ( } - overlayClassName="ant-popover-card" - title={} + content={ + + } + overlayClassName="entity-popover-card" trigger="hover" zIndex={9999}> {children} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/UserPopOverCard.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/UserPopOverCard.tsx index 5db0d721a9a..db64222532a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/UserPopOverCard.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/UserPopOverCard.tsx @@ -68,15 +68,15 @@ const UserPopOverCard: FC = ({ children, userName, type = 'user' }) => { const teams = getNonDeletedTeams(userData.teams ?? []); return teams?.length ? ( -

- - +

+ + {t('label.team-plural')} - + {teams.map((team, i) => ( {team?.displayName ?? team?.name} @@ -91,21 +91,19 @@ const UserPopOverCard: FC = ({ children, userName, type = 'user' }) => { const isAdmin = userData?.isAdmin; return roles?.length ? ( -

- - +

+ + {t('label.role-plural')} - + {isAdmin && ( - + {TERM_ADMIN} )} {roles.map((role, i) => ( - + {role?.displayName ?? role?.name} ))} @@ -120,17 +118,17 @@ const UserPopOverCard: FC = ({ children, userName, type = 'user' }) => { return (

-
+
-
+
{displayName !== name ? ( {name} @@ -151,7 +149,7 @@ const UserPopOverCard: FC = ({ children, userName, type = 'user' }) => { {isLoading ? ( ) : ( -
+
{isEmpty(userData) ? ( {t('message.no-data-available')} ) : ( @@ -168,7 +166,6 @@ const UserPopOverCard: FC = ({ children, userName, type = 'user' }) => { return ( } overlayClassName="ant-popover-card" diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/popover-card.less b/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/popover-card.less new file mode 100644 index 00000000000..4221aee6a3d --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/PopOverCard/popover-card.less @@ -0,0 +1,22 @@ +/* + * 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. + */ +.entity-popover-card { + max-width: 500px; + min-width: 300px; + .explore-search-card { + padding: 0; + } + .entity-breadcrumb { + display: none; + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less b/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less index 6aa110af991..cdbbd7e9157 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less @@ -24,6 +24,9 @@ .m-0 { margin: 0 !important; } +.m-xss { + margin: @margin-xss; +} .m-xs { margin: @margin-xs; } @@ -321,6 +324,10 @@ padding-left: 0px; } +.pl-8 { + padding-left: 2rem; +} + .pt-8 { padding-top: 2rem; } @@ -413,6 +420,9 @@ .p-r-0 { padding-right: 0 !important; } +.p-r-xss { + padding-right: @padding-xss; +} .p-r-xs { padding-right: @padding-xs; } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx index 88205086e58..c58e38b38b5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx @@ -984,6 +984,9 @@ export const getEntityBreadcrumbs = ( case EntityType.GLOSSARY_TERM: // eslint-disable-next-line no-case-declarations const glossary = (entity as GlossaryTerm).glossary; + if (!glossary) { + return []; + } // eslint-disable-next-line no-case-declarations const fqnList = Fqn.split((entity as GlossaryTerm).fullyQualifiedName); // eslint-disable-next-line no-case-declarations