diff --git a/openmetadata-ui/src/main/resources/ui/package.json b/openmetadata-ui/src/main/resources/ui/package.json index 72341062440e..b86e341af2dc 100644 --- a/openmetadata-ui/src/main/resources/ui/package.json +++ b/openmetadata-ui/src/main/resources/ui/package.json @@ -46,6 +46,7 @@ "@azure/msal-react": "^2.1.1", "@dagrejs/dagre": "^1.1.2", "@deuex-solutions/react-tour": "^1.2.6", + "@fontsource/inter": "^5.1.1", "@fontsource/poppins": "^5.0.0", "@fontsource/source-code-pro": "^5.0.0", "@github/g-emoji-element": "^1.1.5", diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/Domain.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/Domain.svg new file mode 100644 index 000000000000..b9cef752d353 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/Domain.svg @@ -0,0 +1,3 @@ + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/InheritedRoles.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/InheritedRoles.svg new file mode 100644 index 000000000000..a76e17867b41 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/InheritedRoles.svg @@ -0,0 +1,3 @@ + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/Persona.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/Persona.svg new file mode 100644 index 000000000000..fcf9aad89a6d --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/Persona.svg @@ -0,0 +1,3 @@ + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/add-circle.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/add-circle.svg new file mode 100644 index 000000000000..e6b7a948de0f --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/add-circle.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/add-emoji.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/add-emoji.svg new file mode 100644 index 000000000000..868d9636f87d --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/add-emoji.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/assignees.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/assignees.svg new file mode 100644 index 000000000000..a2c39c328a8c --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/assignees.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/change-pw.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/change-pw.svg new file mode 100644 index 000000000000..01735484cebd --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/change-pw.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/chat-grey.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/chat-grey.svg new file mode 100644 index 000000000000..18cad269239d --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/chat-grey.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/chat-icon-grey.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/chat-icon-grey.svg new file mode 100644 index 000000000000..769b2b768b85 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/chat-icon-grey.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/check-circle.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/check-circle.svg new file mode 100644 index 000000000000..05e63bb1b8f7 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/check-circle.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/close-circle.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/close-circle.svg new file mode 100644 index 000000000000..42d8f005729c --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/close-circle.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/close-tab.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/close-tab.svg new file mode 100644 index 000000000000..4f2bacb9e1f7 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/close-tab.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/dot (1).svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/dot (1).svg new file mode 100644 index 000000000000..c87c8f27bbba --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/dot (1).svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/edit-profile.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/edit-profile.svg new file mode 100644 index 000000000000..5ec216290518 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/edit-profile.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/edit-suggestion.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/edit-suggestion.svg new file mode 100644 index 000000000000..0dec4ee1301e --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/edit-suggestion.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/mention.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/mention.svg new file mode 100644 index 000000000000..f23dad75ed55 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/mention.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/menu-dots.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/menu-dots.svg new file mode 100644 index 000000000000..707fbedc88ba --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/menu-dots.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/persona (2).svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/persona (2).svg new file mode 100644 index 000000000000..72dd6938abc8 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/persona (2).svg @@ -0,0 +1,5 @@ + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/popover-close.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/popover-close.svg new file mode 100644 index 000000000000..119c1a00e73e --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/popover-close.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/popover-save.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/popover-save.svg new file mode 100644 index 000000000000..8f5bfd303963 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/popover-save.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/reply-2.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/reply-2.svg new file mode 100644 index 000000000000..c1bf853c4236 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/reply-2.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/reply-icon.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/reply-icon.svg new file mode 100644 index 000000000000..a29a5c88a773 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/reply-icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/reply.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/reply.svg new file mode 100644 index 000000000000..39cdc5e6fab1 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/reply.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/roles.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/roles.svg new file mode 100644 index 000000000000..6561971cc0be --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/roles.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/status.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/status.svg new file mode 100644 index 000000000000..caed8cd4ac71 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/status.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/task-filter-button.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/task-filter-button.svg new file mode 100644 index 000000000000..b5f0398c08a6 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/task-filter-button.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/task.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/task.svg new file mode 100644 index 000000000000..0abd1c5091b9 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/task.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/teams.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/teams.svg index 8b982bb8da78..5ec70af3cfdc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/teams.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/teams.svg @@ -1,5 +1,5 @@ - + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/tick-circle.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/tick-circle.svg new file mode 100644 index 000000000000..362763242b35 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/tick-circle.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/trash.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/trash.svg new file mode 100644 index 000000000000..9f0713bb16fc --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/trash.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/user-profile-edit.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/user-profile-edit.svg new file mode 100644 index 000000000000..4e063561d888 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/user-profile-edit.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/user-profile.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/user-profile.svg new file mode 100644 index 000000000000..fd618c64f17f --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/user-profile.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyNew.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyNew.tsx new file mode 100644 index 000000000000..77f35f613609 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/FeedCardBody/FeedCardBodyNew.tsx @@ -0,0 +1,234 @@ +/* + * 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 { Button, Card, Typography } from 'antd'; +import React, { useCallback, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ASSET_CARD_STYLES } from '../../../../constants/Feeds.constants'; +import { CardStyle } from '../../../../generated/entity/feed/thread'; +import { + getEntityFQN, + getEntityType, + getFrontEndFormat, + MarkdownToHTMLConverter, +} from '../../../../utils/FeedUtils'; +import RichTextEditorPreviewerV1 from '../../../common/RichTextEditor/RichTextEditorPreviewerV1'; +import DescriptionFeed from '../../ActivityFeedCardV2/FeedCardBody/DescriptionFeed/DescriptionFeed'; +import TagsFeed from '../../ActivityFeedCardV2/FeedCardBody/TagsFeed/TagsFeed'; +import ActivityFeedEditor from '../../ActivityFeedEditor/ActivityFeedEditor'; +import './feed-card-body-v1.less'; +import { FeedCardBodyV1Props } from './FeedCardBodyV1.interface'; + +const FeedCardBodyNew = ({ + isPost = false, + feed, + isEditPost, + className, + showSchedule = true, + message, + announcement, + onUpdate, + onEditCancel, + showThread, +}: FeedCardBodyV1Props) => { + const { t } = useTranslation(); + const [postMessage, setPostMessage] = useState(message); + + const { entityFQN, entityType, cardStyle } = useMemo(() => { + return { + entityFQN: getEntityFQN(feed.about) ?? '', + entityType: getEntityType(feed.about) ?? '', + cardStyle: feed.cardStyle ?? '', + }; + }, [feed]); + + const handleSave = useCallback(() => { + onUpdate?.(postMessage ?? ''); + }, [onUpdate, postMessage]); + + const getDefaultValue = (defaultMessage: string) => { + return MarkdownToHTMLConverter.makeHtml(getFrontEndFormat(defaultMessage)); + }; + + const feedBodyStyleCardsRender = useMemo(() => { + if (!isPost) { + if (cardStyle === CardStyle.Description) { + return ; + } + + if (cardStyle === CardStyle.Tags) { + return ; + } + + if (ASSET_CARD_STYLES.includes(cardStyle as CardStyle)) { + + + {message} + + ; + // const entityInfo = feed.feedInfo?.entitySpecificInfo?.entity; + // const isExecutableTestSuite = + // entityType === EntityType.TEST_SUITE && entityInfo.basic; + // const isObservabilityAlert = + // entityType === EntityType.EVENT_SUBSCRIPTION && + // (entityInfo as EventSubscription).alertType === + // AlertType.Observability; + + // const entityCard = ( + // + // ); + + // return cardStyle === CardStyle.EntityDeleted ? ( + //
{entityCard}
+ // ) : ( + // + // {entityCard} + // + // ); + } + } + + return ( + + ); + }, [isPost, message, postMessage, cardStyle, feed, entityType, entityFQN]); + + const feedBodyRender = useMemo(() => { + if (isEditPost) { + return ( + + + + + } + editorClass="is_edit_post" + onSave={handleSave} + onTextChange={(message) => setPostMessage(message)} + /> + ); + } + + return feedBodyStyleCardsRender; + }, [isEditPost, message, feedBodyStyleCardsRender]); + + function stripHtml(html: any) { + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = html; + + return tempDiv.innerText || tempDiv.textContent; + } + + // return !isPost ? ( + // feed.feedInfo?.entitySpecificInfo?.entity?.description ? ( + // + // + // {/* {feed.feedInfo.entitySpecificInfo.entity.description} */} + // {stripHtml(feed.feedInfo.entitySpecificInfo.entity.description)} + // + // + // ) : null + // ) : isEditPost ? ( + // feedBodyRender + // ) : ( + // + // + // {message} + // + // + // ); + return ( +
+ {feedBodyRender} +
+ ); +}; + +export default FeedCardBodyNew; 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 435ceaadf3ee..1bb9d19142a8 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 @@ -26,4 +26,5 @@ export interface FeedCardBodyV1Props { isOpenInDrawer?: boolean; onUpdate?: (message: string) => void; onEditCancel?: () => void; + showThread?: boolean; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/ActivityFeedcardNew.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/ActivityFeedcardNew.component.tsx new file mode 100644 index 000000000000..3630466376ed --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/ActivityFeedcardNew.component.tsx @@ -0,0 +1,272 @@ +/* + * Copyright 2025 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 { Card, Col, Input, Space, Tooltip, Typography } from 'antd'; +import classNames from 'classnames'; +import { compare } from 'fast-json-patch'; +import React, { useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; +import { ReactComponent as CloseTabIcon } from '../../../assets/svg/close-tab.svg'; +import { Thread } from '../../../generated/entity/feed/thread'; +import { useUserProfile } from '../../../hooks/user-profile/useUserProfile'; +import { + formatDateTime, + getRelativeTime, +} from '../../../utils/date-time/DateTimeUtils'; +import entityUtilClassBase from '../../../utils/EntityUtilClassBase'; +import { getEntityName } from '../../../utils/EntityUtils'; +import { + entityDisplayName, + getEntityFQN, + getEntityType, + getFeedHeaderTextFromCardStyle, +} from '../../../utils/FeedUtils'; +import ProfilePicture from '../../common/ProfilePicture/ProfilePicture'; +import FeedCardBodyNew from '../ActivityFeedCard/FeedCardBody/FeedCardBodyNew'; +import FeedCardFooterNew from '../ActivityFeedCardV2/FeedCardFooter/FeedCardFooterNew'; +import ActivityFeedEditorNew from '../ActivityFeedEditor/ActivityFeedEditorNew'; +import { useActivityFeedProvider } from '../ActivityFeedProvider/ActivityFeedProvider'; +import '../ActivityFeedTab/activity-feed-tab-new.less'; +import CommentCard from './ReplyCard.component'; +const { Text } = Typography; + +interface ActivityFeedCardNewProps { + componentsVisibility: any; + feed: Thread; + isPost?: boolean; + isActive?: boolean; + post?: any; + showActivityFeedEditor?: boolean; + showThread?: boolean; + handlePanelResize?: () => void; +} + +const ActivityFeedCardNew = ({ + feed, + isPost = false, + post, + showActivityFeedEditor, + showThread, + isActive, + handlePanelResize, +}: ActivityFeedCardNewProps) => { + const { entityFQN, entityType } = useMemo(() => { + const entityFQN = getEntityFQN(feed.about) ?? ''; + const entityType = getEntityType(feed.about) ?? ''; + + return { entityFQN, entityType }; + }, [feed.about]); + const [, , user] = useUserProfile({ + permission: true, + name: post.from ?? '', + }); + const { t } = useTranslation(); + const { selectedThread, postFeed } = useActivityFeedProvider(); + const [showFeedEditor, setShowFeedEditor] = useState(false); + const [isEditPost, setIsEditPost] = useState(false); + const [postMessage, setPostMessage] = useState(''); + const { updateFeed } = useActivityFeedProvider(); + // const [isHovered, setIsHovered] = useState(false); + + const onSave = (message: string) => { + postFeed(message, selectedThread?.id ?? '').catch(() => { + // ignore since error is displayed in toast in the parent promise. + // Added block for sonar code smell + }); + setShowFeedEditor(false); + }; + const onUpdate = (message: string) => { + const updatedPost = { ...feed, message }; + const patch = compare(feed, updatedPost); + updateFeed(feed.id, post.id, !isPost, patch); + setIsEditPost(!isEditPost); + }; + + return ( + + + + + + + + + {feed.updatedBy} + + {post.postTs && ( + + + {getRelativeTime(post.postTs)} + + + )} + + {!isPost && ( + + + {getFeedHeaderTextFromCardStyle( + feed.fieldOperation, + feed.cardStyle, + feed.feedInfo?.fieldName, + entityType + )} + + + + + {feed?.entityRef + ? getEntityName(feed.entityRef) + : entityDisplayName(entityType, entityFQN)} + + + {showThread && ( + + )} + + )} + + + + setIsEditPost(false)} + onUpdate={onUpdate} + /> + + {(isPost || (!showThread && !isPost)) && ( + + )} + + {/* {(feed.generatedBy !== GeneratedBy.System || + (isPost && isHovered)) && ( */} + {/* */} + {/* )} */} + + + {showThread && ( +
+ {showActivityFeedEditor && ( + + {t('label.comment-plural')} + + )} + {showFeedEditor ? ( + + ) : ( +
+
+ +
+ + setShowFeedEditor(true)} + /> +
+ )} + + {showThread && feed?.posts && feed?.posts?.length > 0 && ( + + {feed?.posts + ?.slice() + .sort((a, b) => (b.postTs as number) - (a.postTs as number)) + .map((reply) => ( + + ))} + + )} +
+ )} +
+ ); +}; + +export default ActivityFeedCardNew; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/ReplyCard.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/ReplyCard.component.tsx new file mode 100644 index 000000000000..52923732e7eb --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/ReplyCard.component.tsx @@ -0,0 +1,217 @@ +/* + * Copyright 2025 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 { Tooltip, Typography } from 'antd'; +import { compare } from 'fast-json-patch'; +import React, { useCallback, useMemo, useState } from 'react'; +import { Thread } from '../../../generated/entity/feed/thread'; +import { useUserProfile } from '../../../hooks/user-profile/useUserProfile'; +import { + formatDateTime, + getRelativeTime, +} from '../../../utils/date-time/DateTimeUtils'; +import { + getFrontEndFormat, + MarkdownToHTMLConverter, +} from '../../../utils/FeedUtils'; +import ProfilePicture from '../../common/ProfilePicture/ProfilePicture'; +import FeedCardFooterNew from '../ActivityFeedCardV2/FeedCardFooter/FeedCardFooterNew'; +import { useActivityFeedProvider } from '../ActivityFeedProvider/ActivityFeedProvider'; +import ActivityFeedActions from '../Shared/ActivityFeedActions'; +// import ActivityFeedEditor from '../ActivityFeedEditor/ActivityFeedEditor'; +import { useTranslation } from 'react-i18next'; +import ActivityFeedEditor from '../ActivityFeedEditor/ActivityFeedEditorNew'; + +const { Text } = Typography; + +interface CommentCardInterface { + feed: Thread; + post: any; +} + +const CommentCard = ({ feed, post }: CommentCardInterface) => { + const [isHovered, setIsHovered] = useState(false); + // const { entityFQN, entityType } = useMemo(() => { + // const entityFQN = getEntityFQN(feed.about) ?? ''; + // const entityType = getEntityType(feed.about) ?? ''; + + // return { entityFQN, entityType }; + // }, [feed.about]); + const [, , user] = useUserProfile({ + permission: true, + name: post.from ?? '', + }); + const { t } = useTranslation(); + const { selectedThread, postFeed } = useActivityFeedProvider(); + const [showFeedEditor, setShowFeedEditor] = useState(false); + const [isEditPost, setIsEditPost] = useState(false); + const [postMessage, setPostMessage] = useState(''); + const { updateFeed } = useActivityFeedProvider(); + const onSave = (message: string) => { + postFeed(message, selectedThread?.id ?? '').catch(() => { + // ignore since error is displayed in toast in the parent promise. + // Added block for sonar code smell + }); + setShowFeedEditor(false); + }; + const onEditPost = () => { + setIsEditPost(!isEditPost); + }; + const onUpdate = (message: string) => { + const updatedPost = { ...feed, message }; + const patch = compare(feed, updatedPost); + updateFeed(feed.id, post.id, false, patch); + setIsEditPost(!isEditPost); + }; + const handleSave = useCallback(() => { + onUpdate?.(postMessage ?? ''); + }, [onUpdate, postMessage]); + const getDefaultValue = (defaultMessage: string) => { + return MarkdownToHTMLConverter.makeHtml(getFrontEndFormat(defaultMessage)); + }; + const feedBodyRender = useMemo(() => { + if (isEditPost) { + return ( + + // + // + // + // } + editorClass="is_edit_post" + onSave={handleSave} + onTextChange={(message) => setPostMessage(message)} + /> + ); + } + + return null; + }, [isEditPost, postMessage, handleSave]); + + function stripHtml(html: any) { + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = html; + + return tempDiv.innerText || tempDiv.textContent; + } + + return ( +
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)}> +
+ +
+
+
+ + {feed.updatedBy} + + + {t('label.dot')} + + + + + {getRelativeTime(post.postTs)} + + + +
+ {isEditPost ? ( + feedBodyRender + ) : ( + + {stripHtml(post.message)} + + )} + +
+ +
+
+ + {isHovered && ( + + )} +
+ ); +}; + +export default CommentCard; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardFooter/FeedCardFooterNew.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardFooter/FeedCardFooterNew.tsx new file mode 100644 index 000000000000..e730098a4fda --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardV2/FeedCardFooter/FeedCardFooterNew.tsx @@ -0,0 +1,142 @@ +/* + * Copyright 2024 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 { Avatar, Button, Col, Row, Space } from 'antd'; +import { min, noop, sortBy } from 'lodash'; +import React, { useCallback, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactComponent as ThreadIcon } from '../../../../assets/svg/reply-2.svg'; +import { ReactionOperation } from '../../../../enums/reactions.enum'; +import { ReactionType } from '../../../../generated/type/reaction'; +import ProfilePicture from '../../../common/ProfilePicture/ProfilePicture'; +import ActivityFeedEditor from '../../ActivityFeedEditor/ActivityFeedEditor'; +import { useActivityFeedProvider } from '../../ActivityFeedProvider/ActivityFeedProvider'; +import Reactions from '../../Reactions/Reactions'; +import { FeedCardFooterProps } from './FeedCardFooter.interface'; + +function FeedCardFooterNew({ + feed, + post, + isPost = false, + componentsVisibility = { + showThreadIcon: true, + showRepliesContainer: true, + }, +}: Readonly) { + const { t } = useTranslation(); + const { showDrawer, updateReactions, fetchUpdatedThread } = + useActivityFeedProvider(); + const [showReplyEditor, setShowReplyEditor] = useState(false); + + // The number of posts in the thread + const postLength = useMemo(() => feed?.postsCount ?? 0, [feed?.postsCount]); + + // The latest reply timestamp and the list of unique users who replied + const { latestReplyTimeStamp, repliedUniqueUsersList } = useMemo(() => { + const posts = sortBy(feed?.posts, 'postTs').reverse(); + const latestReplyTimeStamp = posts[0]?.postTs; + + const repliedUsers = [...new Set((feed?.posts ?? []).map((f) => f.from))]; + + const repliedUniqueUsersList = repliedUsers.slice( + 0, + min([3, repliedUsers.length]) + ); + + return { latestReplyTimeStamp, repliedUniqueUsersList }; + }, [feed?.posts]); + + const onReactionUpdate = useCallback( + async (reaction: ReactionType, operation: ReactionOperation) => { + await updateReactions(post, feed.id, !isPost, reaction, operation); + await fetchUpdatedThread(feed.id); + }, + [updateReactions, post, feed.id, isPost, fetchUpdatedThread] + ); + + const showReplies = useCallback(() => { + showDrawer?.(feed); + }, [showDrawer, feed]); + + // const onSave = () => { + // console.log('save'); + // }; + + return ( + + + +
+ {postLength > 0 && !isPost && ( + + {repliedUniqueUsersList.slice(0, 2).map((user) => ( + + ))} + {/* {repliedUniqueUsersList.length > 2 && ( +
+ +{repliedUniqueUsersList.length - 2} +
+ )} */} +
+ )} + + {!isPost && ( + + )} */} +
+ + {isPost && showReplyEditor && ( + + )} +
+ + +
+ ); +} + +export default FeedCardFooterNew; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditorNew.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditorNew.tsx new file mode 100644 index 000000000000..69703bf3b2e7 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditorNew.tsx @@ -0,0 +1,113 @@ +/* + * Copyright 2022 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 classNames from 'classnames'; +import React, { + forwardRef, + HTMLAttributes, + useImperativeHandle, + useRef, + useState, +} from 'react'; +import { getBackendFormat, HTMLToMarkdown } from '../../../utils/FeedUtils'; +import { editorRef } from '../../common/RichTextEditor/RichTextEditor.interface'; +import { FeedEditor } from '../FeedEditor/FeedEditor'; +import { KeyHelp } from './KeyHelp'; +import { SendButton } from './SendButton'; + +interface ActivityFeedEditorProp extends HTMLAttributes { + placeHolder?: string; + defaultValue?: string; + editorClass?: string; + editAction?: React.ReactNode; + onSave?: (value: string) => void; + onTextChange?: (message: string) => void; + focused?: boolean; +} + +export type EditorContentRef = { + getEditorValue: () => string; + clearEditorValue: () => string; +}; + +const ActivityFeedEditor = forwardRef( + ( + { + className, + editorClass, + onSave, + placeHolder, + defaultValue, + onTextChange, + editAction, + focused = false, + }, + ref + ) => { + const editorRef = useRef(); + const [editorValue, setEditorValue] = useState(''); + + const onChangeHandler = (value: string) => { + const markdown = HTMLToMarkdown.turndown(value); + const backendFormat = getBackendFormat(markdown); + setEditorValue(markdown); + onTextChange && onTextChange(backendFormat); + }; + + const onSaveHandler = () => { + if (editorRef.current) { + if (editorRef.current?.getEditorValue()) { + setEditorValue(''); + editorRef.current?.clearEditorValue(); + const message = getBackendFormat(editorRef.current?.getEditorValue()); + onSave && onSave(message); + } + } + }; + + /** + * Handle forward ref logic and provide method access to parent component + */ + useImperativeHandle(ref, () => ({ + ...editorRef.current, + })); + + return ( +
e.stopPropagation()}> + + {editAction ?? ( + <> + + + + )} +
+ ); + } +); + +export default ActivityFeedEditor; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedList/ActivityFeedListV1New.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedList/ActivityFeedListV1New.component.tsx new file mode 100644 index 000000000000..2c3f1e19b1a1 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedList/ActivityFeedListV1New.component.tsx @@ -0,0 +1,127 @@ +/* + * 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 { Typography } from 'antd'; +import { isEmpty } from 'lodash'; +import React, { ReactNode, useEffect, useMemo, useState } from 'react'; +import { ReactComponent as FeedEmptyIcon } from '../../../assets/svg/activity-feed-no-data-placeholder.svg'; +import ErrorPlaceHolder from '../../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; +import { ERROR_PLACEHOLDER_TYPE, SIZE } from '../../../enums/common.enum'; +import { Thread } from '../../../generated/entity/feed/thread'; +import { getFeedListWithRelativeDays } from '../../../utils/FeedUtils'; +import Loader from '../../common/Loader/Loader'; +import FeedPanelBodyV1New from '../ActivityFeedPanel/FeedPanelBodyV1New'; + +interface ActivityFeedListV1Props { + feedList: Thread[]; + isLoading: boolean; + showThread?: boolean; + onFeedClick?: (feed: Thread) => void; + activeFeedId?: string; + hidePopover: boolean; + isForFeedTab?: boolean; + emptyPlaceholderText: ReactNode; + componentsVisibility?: { + showThreadIcon?: boolean; + showRepliesContainer?: boolean; + }; + selectedThread?: Thread; + onAfterClose?: () => void; + onUpdateEntityDetails?: () => void; +} + +const ActivityFeedListV1New = ({ + feedList, + isLoading, + showThread = true, + componentsVisibility = { + showThreadIcon: true, + showRepliesContainer: true, + }, + onFeedClick, + activeFeedId, + hidePopover = false, + isForFeedTab = false, + emptyPlaceholderText, + selectedThread, + onAfterClose, + onUpdateEntityDetails, +}: ActivityFeedListV1Props) => { + const [entityThread, setEntityThread] = useState([]); + + useEffect(() => { + const { updatedFeedList } = getFeedListWithRelativeDays(feedList); + setEntityThread(updatedFeedList); + }, [feedList]); + + useEffect(() => { + if (onFeedClick) { + onFeedClick( + entityThread.find((feed) => feed.id === selectedThread?.id) ?? + entityThread[0] + ); + } + }, [entityThread, selectedThread, onFeedClick]); + + const feeds = useMemo( + () => + entityThread.map((feed) => ( + + )), + [ + entityThread, + activeFeedId, + componentsVisibility, + hidePopover, + isForFeedTab, + showThread, + ] + ); + + if (isLoading) { + return ; + } + + if (isEmpty(entityThread)) { + return ( +
+ } + type={ERROR_PLACEHOLDER_TYPE.CUSTOM}> + + {emptyPlaceholderText} + + +
+ ); + } + + return
{feeds}
; +}; + +export default ActivityFeedListV1New; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/FeedPanelBodyV1.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/FeedPanelBodyV1.interface.ts index 233697221817..a4bf5c32d916 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/FeedPanelBodyV1.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/FeedPanelBodyV1.interface.ts @@ -27,4 +27,8 @@ export interface FeedPanelBodyPropV1 { showRepliesContainer?: boolean; }; hidePopover: boolean; + showActivityFeedEditor?: boolean; + onAfterClose: any; + onUpdateEntityDetails: any; + handlePanelResize: () => void; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/FeedPanelBodyV1New.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/FeedPanelBodyV1New.tsx new file mode 100644 index 000000000000..ac88cc17fc01 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/FeedPanelBodyV1New.tsx @@ -0,0 +1,92 @@ +/* + * Copyright 2022 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 { Button } from 'antd'; +import classNames from 'classnames'; +import React, { FC, useCallback, useMemo } from 'react'; +import { Post, ThreadType } from '../../../generated/entity/feed/thread'; +import ActivityFeedCardNew from '../ActivityFeedCardNew/ActivityFeedcardNew.component'; +import TaskFeedCardNew from '../TaskFeedCard/TaskFeedCardNew.component'; +import './feed-panel-body-v1.less'; +import { FeedPanelBodyPropV1 } from './FeedPanelBodyV1.interface'; + +const FeedPanelBodyV1: FC = ({ + feed, + showThread, + componentsVisibility = { + showThreadIcon: true, + showRepliesContainer: true, + }, + isOpenInDrawer = false, + onFeedClick, + isActive, + hidePopover = false, + isForFeedTab = false, + showActivityFeedEditor = false, + onAfterClose, + onUpdateEntityDetails, + handlePanelResize, +}) => { + const mainFeed = useMemo( + () => + ({ + message: feed.message, + postTs: feed.threadTs, + from: feed.createdBy, + id: feed.id, + reactions: feed.reactions, + } as Post), + [feed] + ); + + const handleFeedClick = useCallback(() => { + onFeedClick?.(feed); + }, [onFeedClick, feed]); + + return ( + + ); +}; + +export default FeedPanelBodyV1; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface.ts index 5f7fc59aa4fb..ca0225979d2f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface.ts @@ -33,6 +33,7 @@ export interface ActivityFeedTabBasicProps { onFeedUpdate: () => void; onUpdateEntityDetails?: () => void; owners?: EntityReference[]; + subTab?: ActivityFeedTabs; } export type ActivityFeedTabProps = ActivityFeedTabBasicProps & diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/ActivityFeedTabNew.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/ActivityFeedTabNew.component.tsx new file mode 100644 index 000000000000..b52787262cbd --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/ActivityFeedTabNew.component.tsx @@ -0,0 +1,556 @@ +/* + * 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 { Dropdown, Segmented, Skeleton } from 'antd'; +import { AxiosError } from 'axios'; +import classNames from 'classnames'; +import { + default as React, + ReactNode, + RefObject, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { useTranslation } from 'react-i18next'; +import { Link, useHistory, useParams } from 'react-router-dom'; +import { ReactComponent as TaskCloseIcon } from '../../../assets/svg/check-circle.svg'; +import { ReactComponent as TaskCloseIconBlue } from '../../../assets/svg/ic-close-task.svg'; +import { ReactComponent as TaskOpenIcon } from '../../../assets/svg/ic-open-task.svg'; +import { ReactComponent as TaskIcon } from '../../../assets/svg/ic-task.svg'; +import { ReactComponent as MentionIcon } from '../../../assets/svg/mention.svg'; +import { ReactComponent as TaskFilterIcon } from '../../../assets/svg/task-filter-button.svg'; +import { ReactComponent as MyTaskIcon } from '../../../assets/svg/task.svg'; + +import { ICON_DIMENSION_USER_PAGE, ROUTES } from '../../../constants/constants'; +import { FEED_COUNT_INITIAL_DATA } from '../../../constants/entity.constants'; +import { observerOptions } from '../../../constants/Mydata.constants'; +import { EntityTabs, EntityType } from '../../../enums/entity.enum'; +import { FeedFilter } from '../../../enums/mydata.enum'; +import { + Thread, + ThreadTaskStatus, + ThreadType, +} from '../../../generated/entity/feed/thread'; +import { useAuth } from '../../../hooks/authHooks'; +import { useApplicationStore } from '../../../hooks/useApplicationStore'; +import { useElementInView } from '../../../hooks/useElementInView'; +import { FeedCounts } from '../../../interface/feed.interface'; +import { getFeedCount } from '../../../rest/feedsAPI'; +import { getFeedCounts, Transi18next } from '../../../utils/CommonUtils'; +import entityUtilClassBase from '../../../utils/EntityUtilClassBase'; +import { + ENTITY_LINK_SEPARATOR, + getEntityUserLink, +} from '../../../utils/EntityUtils'; +import { showErrorToast } from '../../../utils/ToastUtils'; +import Loader from '../../common/Loader/Loader'; +import { TaskTabNew } from '../../Entity/Task/TaskTab/TaskTabNew.component'; +import '../../MyData/Widgets/FeedsWidget/feeds-widget.less'; +import ActivityFeedListV1New from '../ActivityFeedList/ActivityFeedListV1New.component'; +import FeedPanelBodyV1New from '../ActivityFeedPanel/FeedPanelBodyV1New'; +import { useActivityFeedProvider } from '../ActivityFeedProvider/ActivityFeedProvider'; +import './activity-feed-tab.less'; +import { + ActivityFeedTabProps, + ActivityFeedTabs, +} from './ActivityFeedTab.interface'; + +const componentsVisibility = { + showThreadIcon: false, + showRepliesContainer: true, +}; + +export const ActivityFeedTabNew = ({ + fqn, + owners = [], + columns, + entityType, + refetchFeed, + hasGlossaryReviewer, + entityFeedTotalCount, + isForFeedTab = true, + onUpdateFeedCount, + onUpdateEntityDetails, + subTab, +}: ActivityFeedTabProps) => { + const history = useHistory(); + const { t } = useTranslation(); + const { currentUser } = useApplicationStore(); + const { isAdminUser } = useAuth(); + const initialRender = useRef(true); + const [elementRef, isInView] = useElementInView({ + ...observerOptions, + root: document.querySelector('#center-container'), + rootMargin: '0px 0px 2px 0px', + }); + const { tab = EntityTabs.ACTIVITY_FEED, subTab: activeTab = subTab } = + useParams<{ tab: EntityTabs; subTab: ActivityFeedTabs }>(); + const [taskFilter, setTaskFilter] = useState( + ThreadTaskStatus.Open + ); + const [isFullWidth, setIsFullWidth] = useState(false); + const [countData, setCountData] = useState<{ + loading: boolean; + data: FeedCounts; + }>({ + loading: false, + data: FEED_COUNT_INITIAL_DATA, + }); + + const { + postFeed, + selectedThread, + setActiveThread, + entityThread, + getFeedData, + loading, + entityPaging, + } = useActivityFeedProvider(); + + const isUserEntity = useMemo( + () => entityType === EntityType.USER, + [entityType] + ); + + const entityTypeTask = useMemo( + () => + selectedThread?.about?.split(ENTITY_LINK_SEPARATOR)?.[1] as Exclude< + EntityType, + EntityType.TABLE + >, + [selectedThread] + ); + + const isTaskActiveTab = useMemo( + () => activeTab === ActivityFeedTabs.TASKS, + [activeTab] + ); + const isMentionTabSelected = useMemo( + () => activeTab === ActivityFeedTabs.MENTIONS, + [activeTab] + ); + + const getElementWithCountLoader = useCallback( + (element: ReactNode) => { + if (countData.loading) { + return ; + } + + return element; + }, + [countData.loading] + ); + + const handleTabChange = (subTab: string) => { + history.push( + entityUtilClassBase.getEntityLink( + entityType, + fqn, + EntityTabs.ACTIVITY_FEED, + subTab + ) + ); + setActiveThread(); + setIsFullWidth(false); + }; + + const placeholderText = useMemo(() => { + if (activeTab === ActivityFeedTabs.ALL) { + return ( + } + values={{ + explored: t('message.have-not-explored-yet'), + }} + /> + ); + } else if (activeTab === ActivityFeedTabs.MENTIONS) { + return t('message.no-mentions'); + } else { + return t('message.no-open-tasks'); + } + }, [activeTab]); + + const handleFeedCount = useCallback( + (data: FeedCounts) => { + setCountData((prev) => ({ ...prev, data })); + onUpdateFeedCount?.(data); + }, + [setCountData] + ); + + const fetchFeedsCount = async () => { + setCountData((prev) => ({ ...prev, loading: true })); + if (isUserEntity) { + try { + const res = await getFeedCount(getEntityUserLink(fqn)); + setCountData((prev) => ({ + ...prev, + data: { + conversationCount: res[0].conversationCount ?? 0, + totalTasksCount: res[0].totalTaskCount, + openTaskCount: res[0].openTaskCount ?? 0, + closedTaskCount: res[0].closedTaskCount ?? 0, + totalCount: res[0].conversationCount ?? 0 + res[0].totalTaskCount, + mentionCount: res[0].mentionCount ?? 0, + }, + })); + } catch (err) { + showErrorToast(err as AxiosError, t('server.entity-feed-fetch-error')); + } + } else { + await getFeedCounts(entityType, fqn, handleFeedCount); + } + setCountData((prev) => ({ ...prev, loading: false })); + }; + + const getThreadType = useCallback((activeTab) => { + if (activeTab === ActivityFeedTabs.TASKS) { + return ThreadType.Task; + } else if (activeTab === ActivityFeedTabs.ALL) { + return ThreadType.Conversation; + } else { + return; + } + }, []); + + const isActivityFeedTab = useMemo( + () => tab === EntityTabs.ACTIVITY_FEED, + [tab] + ); + + useEffect(() => { + if (fqn && isActivityFeedTab) { + fetchFeedsCount(); + } + }, [fqn, isActivityFeedTab]); + + const { feedFilter, threadType } = useMemo(() => { + const currentFilter = + isAdminUser && + currentUser?.name === fqn && + activeTab !== ActivityFeedTabs.TASKS + ? FeedFilter.ALL + : FeedFilter.OWNER_OR_FOLLOWS; + const filter = isUserEntity ? currentFilter : undefined; + + return { + threadType: getThreadType(activeTab), + feedFilter: activeTab === 'mentions' ? FeedFilter.MENTIONS : filter, + }; + }, [activeTab, isUserEntity, currentUser]); + + const handleFeedFetchFromFeedList = useCallback( + (after?: string) => { + getFeedData(feedFilter, after, threadType, entityType, fqn, taskFilter); + }, + [threadType, feedFilter, entityType, fqn, taskFilter, getFeedData] + ); + + const refetchFeedData = useCallback(() => { + if ( + entityFeedTotalCount !== countData.data.totalCount && + isActivityFeedTab && + refetchFeed + ) { + getFeedData( + feedFilter, + undefined, + threadType, + entityType, + fqn, + taskFilter + ); + } + }, [ + fqn, + taskFilter, + feedFilter, + threadType, + entityType, + refetchFeed, + countData.data.totalCount, + entityFeedTotalCount, + isActivityFeedTab, + ]); + + useEffect(() => { + if (initialRender.current) { + initialRender.current = false; + + return; + } + refetchFeedData(); + }, [refetchFeedData]); + + useEffect(() => { + if (fqn) { + getFeedData( + feedFilter, + undefined, + threadType, + entityType, + fqn, + taskFilter + ); + } + }, [feedFilter, threadType, fqn]); + + const handleFeedClick = useCallback( + (feed: Thread) => { + setActiveThread(feed); + }, + [setActiveThread] + ); + + useEffect(() => { + if (fqn && isInView && entityPaging.after && !loading) { + handleFeedFetchFromFeedList(entityPaging.after); + } + }, [entityPaging, loading, isInView, fqn]); + + const loader = useMemo(() => (loading ? : null), [loading]); + + const onSave = (message: string) => { + postFeed(message, selectedThread?.id ?? '').catch(() => { + // ignore since error is displayed in toast in the parent promise. + // Added block for sonar code smell + }); + }; + + const handleUpdateTaskFilter = (filter: ThreadTaskStatus) => { + setTaskFilter(filter); + getFeedData(feedFilter, undefined, threadType, entityType, fqn, filter); + }; + + const handleAfterTaskClose = () => { + handleFeedFetchFromFeedList(); + handleUpdateTaskFilter(ThreadTaskStatus.Closed); + }; + const taskFilterOptions = [ + { + key: ThreadTaskStatus.Open, + label: ( +
+
+ {taskFilter === ThreadTaskStatus.Open ? ( + + ) : ( + + )} + + {t('label.open')} + +
+ + + {countData.data.openTaskCount} + + +
+ ), + onClick: () => { + handleUpdateTaskFilter(ThreadTaskStatus.Open); + setActiveThread(); + }, + }, + { + key: ThreadTaskStatus.Closed, + label: ( +
+
+ {taskFilter === ThreadTaskStatus.Closed ? ( + + ) : ( + + )} + + + {t('label.closed')} + +
+ + + {countData.data.closedTaskCount} + + +
+ ), + onClick: () => { + handleUpdateTaskFilter(ThreadTaskStatus.Closed); + setActiveThread(); + }, + }, + ]; + const TaskToggle = () => { + return ( + + + {t('label.my-task-plural')} + + ), + value: ActivityFeedTabs.TASKS, + }, + { + label: ( + + + {t('label.mention-plural')} + + ), + value: ActivityFeedTabs.MENTIONS, + }, + ]} + onChange={(value) => handleTabChange(value as ActivityFeedTabs)} + /> + ); + }; + const handlePanelResize = () => { + setIsFullWidth(true); + }; + + return ( +
+
+ {(isTaskActiveTab || isMentionTabSelected) && ( +
+ + + + {TaskToggle()} +
+ )} + + {loader} +
} + style={{ height: '2px' }} + /> +
+ +
+ {loader} + {selectedThread && + !loading && + (activeTab !== ActivityFeedTabs.TASKS ? ( +
+
+ + {/* */} +
+
+ ) : ( +
+ {entityType === EntityType.TABLE ? ( + + ) : ( + + )} +
+ ))} +
+
+ ); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/activity-feed-tab-new.less b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/activity-feed-tab-new.less new file mode 100644 index 000000000000..639a1e1f487b --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedTab/activity-feed-tab-new.less @@ -0,0 +1,396 @@ +/* + * 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 url('../../../styles/variables.less'); + +.feed-explore-heading { + background-color: @grey-1; +} + +.activity-feed-tab { + display: flex; + gap: 16px; + width: 100%; + width: 100%; + overflow-x: hidden; + + .custom-menu.ant-menu-root.ant-menu-inline { + .ant-menu-item { + height: 40px; + } + } + + .center-container { + flex: 1; + min-width: 0; + height: @user-profile-page-panel-height; + overflow-y: scroll; + border-radius: 12px; + background-color: @white; + padding: 14px 20px 20px 20px; + .full-width { + flex: 10; + } + } + .right-container { + flex: 1; + min-width: 0; + border: none; + height: @user-profile-page-panel-height; + overflow-y: scroll; + background-color: @white; + border-radius: 12px; + .full-width { + flex: 0; + } + // padding: 4px; + .activity-feed-card-container { + margin: 0px; + } + + // .activity-feed-card-container.ant-btn:hover { + // color: inherit; + // background-color: red; + // } + .activity-feed-comments-container { + border-radius: 12px; + padding: 16px; + margin: 20px 0px; + border: 0.8px solid #dfdfdf; + background: rgba(239, 244, 250, 0.25); + } + + .activity-feed-reply-button { + border-radius: 24px; + border: 1px solid #d4d4d8; + color: #52525b; + font-size: 12px; + } + .comments-input-field { + background-color: #f4f5f7; + border-radius: 20px; + border: 1px solid #d4d4d8; + height: 36px; + font-size: 12px; + line-height: 14.52px; + } + } + + .left-container { + flex: 0 0 @left-side-panel-width; + border-right: @global-border !important; + } + .activity-feed-card-new { + border-radius: 8px; + margin: 10px 0px; + background: #f5f5f5; + display: flex; + flex-direction: column; + flex-wrap: wrap; + width: inherit; + border: none; + + .ant-card-body { + width: inherit; + padding: 20px; + // margin-top: 2px; + + .ant-space-item { + width: inherit; + } + } + + .activity-feed-card-message { + padding: 12px; + background-color: @white; + width: inherit; + margin-top: 2px; + .ant-card-body { + padding: 0px; + border: none; + border-radius: 12px; + } + } + .activity-feed-card-message-right-panel { + background: rgba(239, 244, 250, 0.25); + margin-top: 10px; + } + } + .activity-feed-reply-card { + border-radius: 8px; + + display: flex; + flex-direction: column; + flex-wrap: wrap; + width: inherit; + + .ant-card-body { + width: inherit; + padding: 0px; + // margin: 10px 0px 20px 0px; + padding-top: 20px; + border-bottom: 1.5px solid #d4d4d8; + background: rgba(239, 244, 250, 0.25); + + padding-bottom: 16px; + .ant-space-item { + width: inherit; + } + } + .activity-feed-reply-card-message { + padding-left: 50px; + border: none; + background: rgba(239, 244, 250, 0.25); + .ant-card-body { + margin: 0px; + margin-top: -8px; + margin-bottom: -6px; + border: none; + padding: 0px; + background: rgba(239, 244, 250, 0.25); + } + .activity-feed-comment-text { + color: #52525b; + } + } + } + .activity-feed-card-new-right-panel { + background-color: @white; + border: none; + } +} + +.activity-feed-task { + background-color: @grey-1; +} + +.count-loader.ant-skeleton-element .ant-skeleton-button-sm { + width: 72px; +} + +// .activity-feed-comments-container { +// border-radius: 12px; +// padding: 16px; +// margin: 16px 0px; +// border: 0.8px solid #dfdfdf; +// background: rgba(239, 244, 250, 0.25); /* Background with 25% opacity */ +// } + +// .activity-feed-reply-button { +// border-radius: 24px; +// border: 1px solid #d4d4d8; +// color: #52525b; +// font-size: 12px; +// } +// .comments-input-field { +// background-color: #f4f5f7; +// border-radius: 20px; +// border: 1px solid #d4d4d8; +// height: 36px; +// font-size: 12px; +// line-height: 14.52px; +// } +.activity-feed-editor-container-new { + background-color: #f5f5f5; + border: none; + border-radius: 10px; + .ql-container { + background-color: #f5f5f5; + border: none; + } + .ql-toolbar { + background-color: #f5f5f5; + border: none; + border-bottom: 1px solid #d9d9d9; + } + .ql-toolbar button { + color: #292929; + } +} + +.activity-feed-editor-send-btn { + // background-color: #292929; + color: @white; + .ant-btn.send-button { + // background: #292929; + position: absolute; + bottom: 8px; + right: 8px; + width: 34px; + height: 34px; + padding: 0; + border-radius: 50%; + } +} +.activity-feed-user-name { + font-size: 14px; + font-weight: 600; + line-height: 20px; + letter-spacing: 0.03em; + color: #181d27; +} +.reply-card-user-name { + font-size: 14px; + font-weight: 500; + line-height: 24px; + color: #373e44; +} +.activity-feed-reply-card-footer { + margin-left: 45px; +} +.active-card { + background-color: #e6f1fe; +} +// .activity-feed-reply-card-container{ +// .ant-card-body{ +// padding: 0 !important; +// } + +// } +.task-tab-custom-dropdown { + border-radius: 6px; + .task-tab-filter-item { + color: #535862; + font-size: 14px; + font-weight: 400; + line-height: 20px; + &.selected { + color: #2e90fa; + } + } + + .task-filter-icon:hover path { + stroke: #2e90fa; + stroke-width: 2; + } + .task-count-container { + background: #e9eaeb; + border-radius: 16px; + width: 30px; + height: 22px; + display: flex; + padding: 2px 8px; + justify-content: center; + align-items: center; + &.active { + background-color: #2e90fa !important; + } + + .task-count-text { + color: #414651; + text-align: center; + font-family: Inter; + font-size: 12px; + font-weight: 500; + line-height: 18px; + } + + &.active .task-count-text { + // color: #039855; + color: @white; + } + } + .ant-dropdown-menu .ant-dropdown-menu-item { + box-shadow: 0px 1px 2px 0px rgba(10, 13, 18, 0.05); + padding: 0px; + margin: 8px; + .active { + box-shadow: 0px 1px 2px 0px rgba(10, 13, 18, 0.05); + background-color: #f4f4f5; + border-radius: 8px; + svg path { + // stroke: #2e90fa; + fill: #2e90fa; + } + } + } +} + +.task-toggle { + background: white; + border-radius: 8px; + padding: 4px; + border: 1px solid #dce3ec; + + .ant-segmented-item { + color: #71717a; + transition: all 0.3s ease; + + &-selected { + background: #e6f1fe; + color: #005bc4; + text-align: center; + + .ant-segmented-item-label { + color: #005bc4; + } + svg path { + // stroke: #005bc4; + fill: #005bc4; + } + } + } + &:hover { + background: none !important; + } + + &:focus { + background: none !important; + } + + .ant-segmented:not(.ant-segmented-disabled):hover, + .ant-segmented:not(.ant-segmented-disabled):focus { + background: none !important; + } +} + +.toggle-item { + display: flex; + align-items: center; + justify-content: center; + gap: 5px; + font-size: 14px; + height: 40px; + width: 100px; + + .ant-segmented-item-label { + color: #71717a; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 20px; + } +} + +.ant-btn:hover, +.ant-btn:focus { + background-color: inherit !important; + color: inherit !important; +} +.card-style-feed-header { + color: #535862; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; +} +.feed-card-header-v2-timestamp { + color: #717680; +} +.close-tab-icon { + position: absolute; + top: 20px; + right: 20px; +} +.full-width { + flex: auto !important; // Applies when .full-width is added to .center-container +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Reactions/Emoji.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Reactions/Emoji.tsx index 13fff400eced..8446ba96da44 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Reactions/Emoji.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Reactions/Emoji.tsx @@ -120,10 +120,18 @@ const Emoji: FC = ({ key={reaction} shape="round" size="small" + style={{ + background: 'transparent', + border: '1px solid #75757533', + padding: '4px 6px', + }} onClick={handleEmojiOnClick} onMouseOver={() => setVisible(true)}> {element} - + {reactionList.length.toLocaleString('en-US', { useGrouping: false, })} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Reactions/Reactions.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Reactions/Reactions.tsx index bbb1afbc5016..6d515c9aad70 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Reactions/Reactions.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Reactions/Reactions.tsx @@ -16,7 +16,7 @@ import { Button, Popover } from 'antd'; import { groupBy, uniqueId } from 'lodash'; import React, { FC, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { ReactComponent as AddReactionIcon } from '../../../assets/svg/ic-reaction.svg'; +import { ReactComponent as AddReactionIcon } from '../../../assets/svg/add-emoji.svg'; import { REACTION_LIST, REACTION_TYPE_LIST, @@ -113,7 +113,7 @@ const Reactions: FC = ({ reactions, onReactionSelect }) => { + + ) : null, + [isEntityDetailsAvailable, entityFQN, entityType, taskDetails] + ); + + const handleMouseEnter = () => { + setShowActions(true); + }; + + const handleMouseLeave = () => { + setShowActions(false); + }; + const { currentUser } = useApplicationStore(); + const isTaskTestCaseResult = + taskDetails?.type === TaskType.RequestTestCaseFailureResolution; + const isTaskGlossaryApproval = taskDetails?.type === TaskType.RequestApproval; + const isAssignee = taskDetails?.assignees?.some((assignee) => + isEqual(assignee.id, currentUser?.id) + ); + const isPartOfAssigneeTeam = taskDetails?.assignees?.some((assignee) => + assignee.type === 'team' ? checkIfUserPartOfTeam(assignee.id) : false + ); + + const checkIfUserPartOfTeam = useCallback( + (teamId: string): boolean => { + return Boolean(currentUser?.teams?.find((team) => teamId === team.id)); + }, + [currentUser] + ); + + const updateTaskData = (data: TaskDetails | ResolveTask) => { + if (!taskDetails?.id) { + return; + } + updateTask(TaskOperation.RESOLVE, taskDetails?.id + '', data) + .then(() => { + showSuccessToast(t('server.task-resolved-successfully')); + onAfterClose?.(); + onUpdateEntityDetails?.(); + }) + .catch((err: AxiosError) => + showErrorToast(getErrorText(err, t('server.unexpected-error'))) + ); + }; + const onTaskResolve = () => { + if (isEmpty(taskDetails?.suggestion)) { + showErrorToast( + t('message.field-text-is-required', { + fieldText: isTaskTags + ? t('label.tag-plural') + : t('label.description'), + }) + ); + + return; + } + if (isTaskTags) { + const tagsData = { + newValue: taskDetails?.suggestion || '[]', + }; + + updateTaskData(tagsData as TaskDetails); + } else { + const newValue = taskDetails?.suggestion; + const data = { newValue: newValue }; + updateTaskData(data as TaskDetails); + } + }; + const onTaskReject = () => { + // if (!isTaskGlossaryApproval && isEmpty(comment)) { + // showErrorToast(t('server.task-closed-without-comment')); + + // return; + // } + + // const updatedComment = isTaskGlossaryApproval ? 'Rejected' : comment; + const updatedComment = isTaskGlossaryApproval ? 'Rejected' : 'Rejected'; + updateTask(TaskOperation.REJECT, taskDetails?.id + '', { + comment: updatedComment, + } as unknown as TaskDetails) + .then(() => { + showSuccessToast(t('server.task-closed-successfully')); + onAfterClose?.(); + onUpdateEntityDetails?.(); + }) + .catch((err: AxiosError) => showErrorToast(err)); + }; + + return ( + + )} + {feed.task?.status === ThreadTaskStatus.Open && ( + + )} + + + +
+ + ); +}; + +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 index 879c2178da5e..eb31858b43b4 100644 --- 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 @@ -28,3 +28,160 @@ margin-right: 4px; } } + +// .task-feed-message-container-new{ +// .task-feed-message{ +// &:hover { +// .ant-typography { +// color: @primary-color; +// } +// } +// } + +// } +// .task-feed-card-new{ +// border-radius: 8px; +// margin: 10px 0px; +// background: #f5f5f5; + +// width: 100%; +// border: none; +// padding: 20px; + +// &.active { +// border-left: 4px solid @info-color; +// background-color: #E6F1FE; +// } +// .task-feed-card-header{ +// background-color: red; +// display: flex; +// width: 100%; +// .task-feed-card-title{ +// background-color: green; +// width: 100%; + +// } +// } + +// } +// .text-wrap { +// word-break: break-word; +// white-space: normal; +// overflow-wrap: break-word; +// text-align: left; +// } +// .task-feed-message{ +// color: @primary-color; +// } + +.task-feed-card-v1-new { + padding: 20px; + background: #f4f4f5; + border-radius: 12px; + margin: 10px 0px; + + &.active { + border-left: 4px solid @primary-color; + background: #e6f1fe; + } + .task-created-by-text { + color: @primary-color; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + } + .task-timestamp-text { + color: #717680; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + } + + .task-feed-card-footer { + margin-top: 2px; + .posts-length { + color: #71717a; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; + } + .action-buttons { + display: flex; + gap: 8px; + } + + .approve-btn { + background-color: @primary-color; + border-color: @primary-color; + color: @white; + border-radius: 8px; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 20px; + letter-spacing: 0.1px; + padding: 8px 16px 8px 8px; + } + + .reject-btn { + border-color: @primary-color; + color: @primary-color; + border-radius: 8px; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 20px; + letter-spacing: 0.1px; + padding: 8px 16px 8px 8px; + } + + .approve-btn .anticon, + .reject-btn .anticon { + font-size: 14px; + margin-left: 8px; + } + } + + .assignee-item { + margin: 0; + margin-right: 4px; + } +} +// .task-feed-message-container-new { +// .task-feed-message-new { +// height: 100%; +// display: flex; +// align-items: flex-start; +// flex-wrap: wrap; +// &:hover { +// .ant-typography { +// color: @primary-color; +// } +// } +// } +// } +.text-wrap { + word-wrap: break-word; + white-space: normal; +} +.task-feed-desc-diff { + background-color: @white; + margin-top: 14px; +} +.task-card-assignee { + margin-left: 18px; +} +.task-feed-card-v1-new .task-feed-card-footer .approve-btn.ant-btn:hover, +.task-feed-card-v1-new .task-feed-card-footer .approve-btn.ant-btn:focus, +.task-feed-card-v1-new .task-feed-card-footer .reject-btn.ant-btn:focus { + background-color: @primary-color !important; + color: @white !important; +} +.task-feed-card-v1-new .task-feed-card-footer .reject-btn.ant-btn:hover, +.task-feed-card-v1-new .task-feed-card-footer .reject-btn.ant-btn:focus { + background-color: @white !important; + color: @primary-color !important; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/activity-feed-new.less b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/activity-feed-new.less new file mode 100644 index 000000000000..563b8bdb670d --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/activity-feed-new.less @@ -0,0 +1,68 @@ +/* + * 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 url('../../../styles/variables.less'); + +.feed-explore-heading { + background-color: @grey-1; +} + +.activity-feed-tab { + display: flex; + gap: 16px; + width: 100%; + width: 100%; + overflow-x: hidden; + + .custom-menu.ant-menu-root.ant-menu-inline { + .ant-menu-item { + height: 40px; + } + } + + .center-container { + flex: 1; + min-width: 0; + height: @profile-entity-details-tab-height; + overflow-y: scroll; + border-radius: 12px; + background-color: @white; + } + .right-container { + flex: 1; + min-width: 0; + + border-left: @global-border; + height: @profile-entity-details-tab-height; + overflow-y: scroll; + background-color: @white; + border-radius: 12px; + + .activity-feed-card-container.ant-btn:hover { + color: inherit; + background-color: @white; + } + } + + .left-container { + flex: 0 0 @left-side-panel-width; + border-right: @global-border !important; + } +} + +.activity-feed-task { + background-color: @grey-1; +} + +.count-loader.ant-skeleton-element .ant-skeleton-button-sm { + width: 72px; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.tsx b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.tsx index 3ae7f649474e..b651a1f2e8f6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/BlockEditor.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2023 Collate. + * Copyright 2025 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 @@ -48,15 +48,13 @@ const BlockEditor = forwardRef( const { i18n } = useTranslation(); const editorSlots = useRef(null); - // this hook to initialize the editor + // Custom editor hook to initialize and update editor const editor = useCustomEditor({ ...EDITOR_OPTIONS, extensions, onUpdate({ editor }) { const htmlContent = editor.getHTML(); - const backendFormat = formatContent(htmlContent, 'server'); - onChange?.(backendFormat); }, editorProps: { @@ -68,19 +66,17 @@ const BlockEditor = forwardRef( autofocus: autoFocus, }); - // this hook to expose the editor instance + // Expose the editor instance using useImperativeHandle useImperativeHandle(ref, () => ({ editor, })); - // this effect to handle the content change + // Set content whenever it changes useEffect(() => { if (isNil(editor) || editor.isDestroyed || content === undefined) { return; } - // We use setTimeout to avoid any flushSync console errors as - // mentioned here https://github.com/ueberdosis/tiptap/issues/3764#issuecomment-1546854730 setTimeout(() => { if (content !== undefined) { const htmlContent = formatContent(content, 'client'); @@ -89,7 +85,7 @@ const BlockEditor = forwardRef( }); }, [content, editor]); - // this effect to handle the editable state + // Handle editable state change useEffect(() => { if ( isNil(editor) || @@ -99,26 +95,42 @@ const BlockEditor = forwardRef( return; } - // We use setTimeout to avoid any flushSync console errors as - // mentioned here https://github.com/ueberdosis/tiptap/issues/3764#issuecomment-1546854730 setTimeout(() => editor.setEditable(editable)); }, [editable, editor]); - // this effect to handle the RTL and LTR direction + // Handle RTL/LTR direction changes useEffect(() => { const editorWrapper = document.getElementById('block-editor-wrapper'); if (!editorWrapper) { return; } editorWrapper.setAttribute('dir', i18n.dir()); - // text align right if rtl - if (i18n.dir() === 'rtl') { - editorWrapper.style.textAlign = 'right'; - } else { - editorWrapper.style.textAlign = 'left'; - } + editorWrapper.style.textAlign = i18n.dir() === 'rtl' ? 'right' : 'left'; }, [i18n]); + const updatePTags = () => { + const pTags = document.querySelectorAll('div.tiptap p'); + + pTags.forEach((pTag) => { + // Check if

tag is empty or contains only a
+ const hasOnlyTrailingBreak = + pTag.children.length === 1 && + pTag.children[0].tagName === 'BR' && + pTag.children[0].classList.contains('ProseMirror-trailingBreak'); + + if (pTag.textContent?.trim() === '' || hasOnlyTrailingBreak) { + pTag.remove(); + } + }); + }; + + // Call the updatePTags function whenever editor content changes + useEffect(() => { + if (editor) { + updatePTags(); + } + }, [editor?.getHTML()]); // Trigger whenever editor's HTML content is updated + return (

{ + const { getEntityPermission } = usePermissionProvider(); + const [additionalInfo, setAdditionalInfo] = useState< + Record + >({}); + const [charts, setCharts] = useState([]); + const [entityPermissions, setEntityPermissions] = + useState(null); + const { isTourPage } = useTourProvider(); + + const entityInfo = useMemo( + () => getEntityOverview(entityType, dataAsset, additionalInfo), + [dataAsset, additionalInfo] + ); + + const entityDetails = useMemo(() => { + return getEntityChildDetails( + entityType, + entityType === EntityType.DASHBOARD + ? ({ ...dataAsset, charts } as Dashboard) + : dataAsset, + highlights + ); + }, [dataAsset, entityType, highlights]); + + const isEntityDeleted = useMemo(() => dataAsset.deleted, [dataAsset]); + + const fetchIncidentCount = useCallback(async () => { + if ( + dataAsset?.fullyQualifiedName && + (entityPermissions?.ViewAll || entityPermissions?.ViewDataProfile) + ) { + try { + const { paging } = await getListTestCaseIncidentStatus({ + limit: 0, + latest: true, + originEntityFQN: dataAsset?.fullyQualifiedName, + startTs: getEpochMillisForPastDays( + PROFILER_FILTER_RANGE.last30days.days + ), + endTs: getCurrentMillis(), + }); + + setAdditionalInfo({ + incidentCount: paging.total, + }); + } catch (error) { + setAdditionalInfo({ + incidentCount: 0, + }); + } + } + }, [dataAsset?.fullyQualifiedName, entityPermissions]); + + const fetchChartsDetails = useCallback(async () => { + try { + const chartDetails = await fetchCharts((dataAsset as Dashboard).charts); + + setCharts(chartDetails); + } catch (err) { + // Error + } + }, [dataAsset]); + + const fetchEntityBasedDetails = () => { + switch (entityType) { + case EntityType.TABLE: + fetchIncidentCount(); + + break; + case EntityType.DASHBOARD: + fetchChartsDetails(); + + break; + default: + break; + } + }; + + const init = useCallback(async () => { + if (dataAsset.id && !isTourPage) { + const permissions = await getEntityPermission( + ResourceEntity.TABLE, + dataAsset.id + ); + setEntityPermissions(permissions); + fetchEntityBasedDetails(); + } else { + setEntityPermissions(null); + } + }, [dataAsset, isTourPage, isEntityDeleted, getEntityPermission]); + + const commonEntitySummaryInfo = useMemo(() => { + switch (entityType) { + case EntityType.API_COLLECTION: + case EntityType.API_ENDPOINT: + case EntityType.API_SERVICE: + case EntityType.CHART: + case EntityType.CONTAINER: + case EntityType.DASHBOARD: + case EntityType.DASHBOARD_DATA_MODEL: + case EntityType.DASHBOARD_SERVICE: + case EntityType.DATABASE: + case EntityType.DATABASE_SCHEMA: + case EntityType.DATABASE_SERVICE: + case EntityType.MESSAGING_SERVICE: + case EntityType.METRIC: + case EntityType.MLMODEL: + case EntityType.MLMODEL_SERVICE: + case EntityType.PIPELINE: + case EntityType.PIPELINE_SERVICE: + case EntityType.SEARCH_INDEX: + case EntityType.SEARCH_SERVICE: + case EntityType.STORAGE_SERVICE: + case EntityType.STORED_PROCEDURE: + case EntityType.TABLE: + case EntityType.TOPIC: + return ( + <> + + + + + + {entityType === EntityType.TABLE && ( + + )} + + + + ); + case EntityType.GLOSSARY_TERM: + case EntityType.TAG: + case EntityType.DATA_PRODUCT: + default: + return null; + } + }, [entityType, dataAsset, entityInfo, componentType]); + + useEffect(() => { + init(); + }, [dataAsset.id]); + + return ( + +
+ {commonEntitySummaryInfo} + + {entityDetails} +
+
+ ); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx index 49172594814b..cdb47b1654dc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx @@ -15,11 +15,13 @@ import { Button, Col, Divider, Row, Space, Tooltip, Typography } from 'antd'; import ButtonGroup from 'antd/lib/button/button-group'; import { AxiosError } from 'axios'; import { capitalize, get, isEmpty } from 'lodash'; +import QueryString from 'qs'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link, useHistory } from 'react-router-dom'; import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg'; import { ReactComponent as IconExternalLink } from '../../../assets/svg/external-links.svg'; +import { ReactComponent as RedAlertIcon } from '../../../assets/svg/ic-alert-red.svg'; import { ReactComponent as TaskOpenIcon } from '../../../assets/svg/ic-open-task.svg'; import { ReactComponent as ShareIcon } from '../../../assets/svg/ic-share.svg'; import { ReactComponent as StarFilledIcon } from '../../../assets/svg/ic-star-filled.svg'; @@ -36,18 +38,23 @@ import { getEntityDetailsPath, } from '../../../constants/constants'; import { SERVICE_TYPES } from '../../../constants/Services.constant'; +import { TAG_START_WITH } from '../../../constants/Tag.constants'; import { useTourProvider } from '../../../context/TourProvider/TourProvider'; import { EntityTabs, EntityType, TabSpecificField, } from '../../../enums/entity.enum'; +import { LineageLayer } from '../../../generated/configuration/lineageSettings'; import { Container } from '../../../generated/entity/data/container'; +import { Metric } from '../../../generated/entity/data/metric'; import { Table } from '../../../generated/entity/data/table'; import { Thread } from '../../../generated/entity/feed/thread'; +import { useApplicationStore } from '../../../hooks/useApplicationStore'; import { useClipboard } from '../../../hooks/useClipBoard'; import { SearchSourceAlias } from '../../../interface/search.interface'; import { getActiveAnnouncement } from '../../../rest/feedsAPI'; +import { getDataQualityLineage } from '../../../rest/lineageAPI'; import { getContainerByName } from '../../../rest/storageAPI'; import { getDataAssetsHeaderInfo } from '../../../utils/DataAssetsHeader.utils'; import entityUtilClassBase from '../../../utils/EntityUtilClassBase'; @@ -57,17 +64,9 @@ import { getEntityVoteStatus, } from '../../../utils/EntityUtils'; import serviceUtilClassBase from '../../../utils/ServiceUtilClassBase'; +import tableClassBase from '../../../utils/TableClassBase'; import { getTierTags } from '../../../utils/TableUtils'; import { showErrorToast } from '../../../utils/ToastUtils'; - -import QueryString from 'qs'; -import { ReactComponent as RedAlertIcon } from '../../../assets/svg/ic-alert-red.svg'; -import { TAG_START_WITH } from '../../../constants/Tag.constants'; -import { LineageLayer } from '../../../generated/configuration/lineageSettings'; -import { Metric } from '../../../generated/entity/data/metric'; -import { useApplicationStore } from '../../../hooks/useApplicationStore'; -import { getDataQualityLineage } from '../../../rest/lineageAPI'; -import tableClassBase from '../../../utils/TableClassBase'; import AnnouncementCard from '../../common/EntityPageInfos/AnnouncementCard/AnnouncementCard'; import AnnouncementDrawer from '../../common/EntityPageInfos/AnnouncementDrawer/AnnouncementDrawer'; import ManageButton from '../../common/EntityPageInfos/ManageButton/ManageButton'; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.component.tsx index c0368a36f756..bf99e3e77ba6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.component.tsx @@ -17,37 +17,12 @@ import { cloneDeep, get } from 'lodash'; import { EntityDetailUnion } from 'Models'; import React, { useEffect, useMemo, useState } from 'react'; import { EntityType } from '../../../enums/entity.enum'; -import { APIEndpoint } from '../../../generated/entity/data/apiEndpoint'; -import { Container } from '../../../generated/entity/data/container'; -import { Dashboard } from '../../../generated/entity/data/dashboard'; -import { DashboardDataModel } from '../../../generated/entity/data/dashboardDataModel'; -import { Metric } from '../../../generated/entity/data/metric'; -import { Mlmodel } from '../../../generated/entity/data/mlmodel'; -import { Pipeline } from '../../../generated/entity/data/pipeline'; -import { SearchIndex } from '../../../generated/entity/data/searchIndex'; -import { StoredProcedure } from '../../../generated/entity/data/storedProcedure'; -import { Table } from '../../../generated/entity/data/table'; -import { Topic } from '../../../generated/entity/data/topic'; import { TagLabel } from '../../../generated/type/tagLabel'; import entityUtilClassBase from '../../../utils/EntityUtilClassBase'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityTags, -} from '../../../utils/EntityUtils'; import searchClassBase from '../../../utils/SearchClassBase'; import serviceUtilClassBase from '../../../utils/ServiceUtilClassBase'; import TitleBreadcrumb from '../../common/TitleBreadcrumb/TitleBreadcrumb.component'; -import APIEndpointSummary from '../../Explore/EntitySummaryPanel/APIEndpointSummary/APIEndpointSummary'; -import ContainerSummary from '../../Explore/EntitySummaryPanel/ContainerSummary/ContainerSummary.component'; -import DashboardSummary from '../../Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.component'; -import DataModelSummary from '../../Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.component'; -import MetricSummary from '../../Explore/EntitySummaryPanel/MetricSummary/MetricSummary'; -import MlModelSummary from '../../Explore/EntitySummaryPanel/MlModelSummary/MlModelSummary.component'; -import PipelineSummary from '../../Explore/EntitySummaryPanel/PipelineSummary/PipelineSummary.component'; -import SearchIndexSummary from '../../Explore/EntitySummaryPanel/SearchIndexSummary/SearchIndexSummary.component'; -import StoredProcedureSummary from '../../Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component'; -import TableSummary from '../../Explore/EntitySummaryPanel/TableSummary/TableSummary.component'; -import TopicSummary from '../../Explore/EntitySummaryPanel/TopicSummary/TopicSummary.component'; +import { DataAssetSummaryPanel } from '../../DataAssetSummaryPanel/DataAssetSummaryPanel'; import EntityHeaderTitle from '../EntityHeaderTitle/EntityHeaderTitle.component'; import './entity-info-drawer.less'; import { LineageDrawerProps } from './EntityInfoDrawer.interface'; @@ -82,115 +57,6 @@ const EntityInfoDrawer = ({ ) : null; }, [selectedNode]); - const tags = useMemo( - () => - getEntityTags(selectedNode.entityType ?? EntityType.TABLE, entityDetail), - [entityDetail, selectedNode] - ); - - const summaryComponent = useMemo(() => { - switch (selectedNode.entityType) { - case EntityType.TABLE: - return ( - - ); - - case EntityType.TOPIC: - return ( - - ); - - case EntityType.DASHBOARD: - return ( - - ); - - case EntityType.PIPELINE: - return ( - - ); - - case EntityType.MLMODEL: - return ( - - ); - case EntityType.CONTAINER: - return ( - - ); - - case EntityType.DASHBOARD_DATA_MODEL: - return ( - - ); - - case EntityType.STORED_PROCEDURE: - return ( - - ); - - case EntityType.SEARCH_INDEX: - return ( - - ); - - case EntityType.API_ENDPOINT: - return ( - - ); - - case EntityType.METRIC: - return ( - - ); - - default: - return null; - } - }, [entityDetail, tags, selectedNode]); - useEffect(() => { const node = cloneDeep(selectedNode); // Since selectedNode is a source object, modify the tags to contain tier information @@ -243,7 +109,10 @@ const EntityInfoDrawer = ({ }> -
{summaryComponent}
+ ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTab.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTab.interface.ts index d26d838cc672..9d8789319bbe 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTab.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTab.interface.ts @@ -22,6 +22,7 @@ export type TaskTabProps = { hasGlossaryReviewer?: boolean; onUpdateEntityDetails?: () => void; onAfterClose?: () => void; + handlePanelResize?: () => void; } & ( | TableTaskTabProps | { columns?: undefined; entityType: Exclude } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx new file mode 100644 index 000000000000..4a75bbcdad02 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx @@ -0,0 +1,1273 @@ +/* + * 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 Icon, { DownOutlined } from '@ant-design/icons'; +import { + Button, + Col, + Divider, + Dropdown, + Form, + Input, + MenuProps, + Row, + Select, + Space, + Tooltip, + Typography, +} from 'antd'; +import { useForm } from 'antd/lib/form/Form'; +import Modal from 'antd/lib/modal/Modal'; +import { AxiosError } from 'axios'; +import classNames from 'classnames'; +import { compare } from 'fast-json-patch'; +import { + isEmpty, + isEqual, + isUndefined, + last, + startCase, + unionBy, +} from 'lodash'; +import { MenuInfo } from 'rc-menu/lib/interface'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { useTranslation } from 'react-i18next'; +import { useHistory } from 'react-router-dom'; +import { ReactComponent as AssigneesIcon } from '../../../../assets/svg/assignees.svg'; +import { ReactComponent as CloseTabIcon } from '../../../../assets/svg/close-tab.svg'; +import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg'; +import { ReactComponent as TaskCloseIcon } from '../../../../assets/svg/ic-close-task.svg'; +import { ReactComponent as TaskOpenIcon } from '../../../../assets/svg/ic-open-task.svg'; +import { ReactComponent as AddColored } from '../../../../assets/svg/plus-colored.svg'; +import { ReactComponent as UserIcon } from '../../../../assets/svg/user-profile.svg'; + +import { DE_ACTIVE_COLOR } from '../../../../constants/constants'; +import { TaskOperation } from '../../../../constants/Feeds.constants'; +import { TASK_TYPES } from '../../../../constants/Task.constant'; +import { usePermissionProvider } from '../../../../context/PermissionProvider/PermissionProvider'; +import { ResourceEntity } from '../../../../context/PermissionProvider/PermissionProvider.interface'; +import { TaskType } from '../../../../generated/api/feed/createThread'; +import { ResolveTask } from '../../../../generated/api/feed/resolveTask'; +import { CreateTestCaseResolutionStatus } from '../../../../generated/api/tests/createTestCaseResolutionStatus'; +import { + TaskDetails, + ThreadTaskStatus, +} from '../../../../generated/entity/feed/thread'; +import { Operation } from '../../../../generated/entity/policies/policy'; +import { + TestCaseFailureReasonType, + TestCaseResolutionStatusTypes, +} from '../../../../generated/tests/testCaseResolutionStatus'; +import { TagLabel } from '../../../../generated/type/tagLabel'; +import { useAuth } from '../../../../hooks/authHooks'; +import { useApplicationStore } from '../../../../hooks/useApplicationStore'; +import { + FieldProp, + FieldTypes, +} from '../../../../interface/FormUtils.interface'; +import Assignees from '../../../../pages/TasksPage/shared/Assignees'; +import DescriptionTask from '../../../../pages/TasksPage/shared/DescriptionTask'; +import TagsTask from '../../../../pages/TasksPage/shared/TagsTask'; +import { + Option, + TaskAction, + TaskActionMode, +} from '../../../../pages/TasksPage/TasksPage.interface'; +import { updateTask, updateThread } from '../../../../rest/feedsAPI'; +import { postTestCaseIncidentStatus } from '../../../../rest/incidentManagerAPI'; +import { getNameFromFQN } from '../../../../utils/CommonUtils'; +import EntityLink from '../../../../utils/EntityLink'; +import { getEntityFQN } from '../../../../utils/FeedUtils'; +import { getField } from '../../../../utils/formUtils'; +import { checkPermission } from '../../../../utils/PermissionsUtils'; +import { getErrorText } from '../../../../utils/StringsUtils'; +import { + fetchOptions, + generateOptions, + getTaskDetailPath, + GLOSSARY_TASK_ACTION_LIST, + INCIDENT_TASK_ACTION_LIST, + isDescriptionTask, + isTagsTask, + TASK_ACTION_COMMON_ITEM, + TASK_ACTION_LIST, +} from '../../../../utils/TasksUtils'; +import { showErrorToast, showSuccessToast } from '../../../../utils/ToastUtils'; +import { EditorContentRef } from '../../../ActivityFeed/ActivityFeedEditor/ActivityFeedEditor'; +import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider'; +import InlineEdit from '../../../common/InlineEdit/InlineEdit.component'; +import EntityPopOverCard from '../../../common/PopOverCard/EntityPopOverCard'; +import './task-tab-new.less'; +// import '../../../ActivityFeed/ActivityFeedTab/activity-feed-tab-new.less'; +import DescriptionTaskNew from '../../../../pages/TasksPage/shared/DescriptionTaskNew'; +import CommentCard from '../../../ActivityFeed/ActivityFeedCardNew/ReplyCard.component'; +import ActivityFeedEditorNew from '../../../ActivityFeed/ActivityFeedEditor/ActivityFeedEditorNew'; +import { OwnerLabelNew } from '../../../common/OwnerLabel/OwnerLabelNew.component'; +import ProfilePicture from '../../../common/ProfilePicture/ProfilePicture'; +import TaskTabIncidentManagerHeaderNew from '../TaskTabIncidentManagerHeader/TasktabIncidentManagerHeaderNew'; +import { TaskTabProps } from './TaskTab.interface'; + +export const TaskTabNew = ({ + taskThread, + owners = [], + entityType, + hasGlossaryReviewer, + ...rest +}: TaskTabProps) => { + const editorRef = useRef(); + const history = useHistory(); + const [assigneesForm] = useForm(); + const { currentUser } = useApplicationStore(); + const updatedAssignees = Form.useWatch('assignees', assigneesForm); + const { permissions } = usePermissionProvider(); + const { task: taskDetails } = taskThread; + + const entityFQN = useMemo( + () => getEntityFQN(taskThread.about) ?? '', + [taskThread.about] + ); + + const isEntityDetailsAvailable = useMemo( + () => !isUndefined(entityFQN) && !isUndefined(entityType), + [entityFQN, entityType] + ); + + const { t } = useTranslation(); + const [form] = Form.useForm(); + const { isAdminUser } = useAuth(); + const { + postFeed, + updateEntityThread, + fetchUpdatedThread, + updateTestCaseIncidentStatus, + testCaseResolutionStatus, + initialAssignees: usersList, + } = useActivityFeedProvider(); + + const isTaskDescription = isDescriptionTask(taskDetails?.type as TaskType); + + const isTaskTags = isTagsTask(taskDetails?.type as TaskType); + + const showAddSuggestionButton = useMemo(() => { + const taskType = taskDetails?.type ?? ('' as TaskType); + const parsedSuggestion = [ + TaskType.UpdateDescription, + TaskType.RequestDescription, + ].includes(taskType) + ? taskDetails?.suggestion + : JSON.parse(taskDetails?.suggestion || '[]'); + + return ( + [TaskType.RequestTag, TaskType.RequestDescription].includes(taskType) && + isEmpty(parsedSuggestion) + ); + }, [taskDetails]); + + const noSuggestionTaskMenuOptions = useMemo(() => { + let label; + + if (taskThread.task?.newValue) { + label = t('label.add-suggestion'); + } else if (isTaskTags) { + label = t('label.add-entity', { + entity: t('label.tag-plural'), + }); + } else { + label = t('label.add-entity', { + entity: t('label.description'), + }); + } + + return [ + { + label, + key: TaskActionMode.EDIT, + icon: AddColored, + }, + ...TASK_ACTION_COMMON_ITEM, + ]; + }, [isTaskTags, taskThread.task?.newValue]); + + const isTaskTestCaseResult = + taskDetails?.type === TaskType.RequestTestCaseFailureResolution; + + const isTaskGlossaryApproval = taskDetails?.type === TaskType.RequestApproval; + + const latestAction = useMemo(() => { + const resolutionStatus = last(testCaseResolutionStatus); + + if (isTaskTestCaseResult) { + switch (resolutionStatus?.testCaseResolutionStatusType) { + case TestCaseResolutionStatusTypes.Assigned: + return INCIDENT_TASK_ACTION_LIST[1]; + + default: + return INCIDENT_TASK_ACTION_LIST[0]; + } + } else if (isTaskGlossaryApproval) { + return GLOSSARY_TASK_ACTION_LIST[0]; + } else if (showAddSuggestionButton) { + return noSuggestionTaskMenuOptions[0]; + } else { + return TASK_ACTION_LIST[0]; + } + }, [ + showAddSuggestionButton, + testCaseResolutionStatus, + isTaskGlossaryApproval, + isTaskTestCaseResult, + noSuggestionTaskMenuOptions, + ]); + + const [taskAction, setTaskAction] = useState(latestAction); + const [isActionLoading, setIsActionLoading] = useState(false); + const isTaskClosed = isEqual(taskDetails?.status, ThreadTaskStatus.Closed); + const [showEditTaskModel, setShowEditTaskModel] = useState(false); + const [comment, setComment] = useState(''); + const [isEditAssignee, setIsEditAssignee] = useState(false); + const [options, setOptions] = useState([]); + const [showFeedEditor, setShowFeedEditor] = useState(false); + const [isAssigneeLoading, setIsAssigneeLoading] = useState(false); + const { initialAssignees, assigneeOptions } = useMemo(() => { + const initialAssignees = generateOptions(taskDetails?.assignees ?? []); + const assigneeOptions = unionBy( + [...initialAssignees, ...generateOptions(usersList)], + 'value' + ); + + return { initialAssignees, assigneeOptions }; + }, [taskDetails, usersList]); + + const taskColumnName = useMemo(() => { + const columnName = EntityLink.getTableColumnName(taskThread.about) ?? ''; + + if (columnName) { + return ( + + {columnName} {t('label.in-lowercase')} + + ); + } + + return null; + }, [taskThread.about]); + + const isOwner = owners?.some((owner) => isEqual(owner.id, currentUser?.id)); + const isCreator = isEqual(taskThread.createdBy, currentUser?.name); + + const checkIfUserPartOfTeam = useCallback( + (teamId: string): boolean => { + return Boolean(currentUser?.teams?.find((team) => teamId === team.id)); + }, + [currentUser] + ); + + const isAssignee = taskDetails?.assignees?.some((assignee) => + isEqual(assignee.id, currentUser?.id) + ); + + const isPartOfAssigneeTeam = taskDetails?.assignees?.some((assignee) => + assignee.type === 'team' ? checkIfUserPartOfTeam(assignee.id) : false + ); + + const getFormattedMenuOptions = (options: TaskAction[]) => { + return options.map((item) => ({ + ...item, + icon: , + })); + }; + + const handleTaskLinkClick = () => { + history.push({ + pathname: getTaskDetailPath(taskThread), + }); + }; + + const taskLinkTitleElement = useMemo( + () => + isEntityDetailsAvailable && !isUndefined(taskDetails) ? ( + + + + ) : null, + [ + isEntityDetailsAvailable, + entityFQN, + entityType, + taskDetails, + handleTaskLinkClick, + ] + ); + + const updateTaskData = (data: TaskDetails | ResolveTask) => { + if (!taskDetails?.id) { + return; + } + updateTask(TaskOperation.RESOLVE, taskDetails?.id + '', data) + .then(() => { + showSuccessToast(t('server.task-resolved-successfully')); + rest.onAfterClose?.(); + rest.onUpdateEntityDetails?.(); + }) + .catch((err: AxiosError) => + showErrorToast(getErrorText(err, t('server.unexpected-error'))) + ); + }; + + const onGlossaryTaskResolve = (status = 'approved') => { + const newValue = isTaskGlossaryApproval ? status : taskDetails?.suggestion; + const data = { newValue: newValue }; + updateTaskData(data as TaskDetails); + }; + + const onTaskResolve = () => { + if (!isTaskGlossaryApproval && isEmpty(taskDetails?.suggestion)) { + showErrorToast( + t('message.field-text-is-required', { + fieldText: isTaskTags + ? t('label.tag-plural') + : t('label.description'), + }) + ); + + return; + } + if (isTaskTags) { + const tagsData = { + newValue: taskDetails?.suggestion || '[]', + }; + + updateTaskData(tagsData as TaskDetails); + } else { + const newValue = isTaskGlossaryApproval + ? 'approved' + : taskDetails?.suggestion; + const data = { newValue: newValue }; + updateTaskData(data as TaskDetails); + } + }; + + const onEditAndSuggest = ({ + description, + updatedTags, + testCaseFailureReason, + testCaseFailureComment, + }: { + description: string; + updatedTags: TagLabel[]; + testCaseFailureReason: TestCaseFailureReasonType; + testCaseFailureComment: string; + }) => { + let data = {} as ResolveTask; + if (isTaskTags) { + data = { + newValue: JSON.stringify(updatedTags) || '[]', + }; + } else { + if (isTaskTestCaseResult) { + data = { + newValue: testCaseFailureComment, + testCaseFQN: entityFQN, + testCaseFailureReason, + }; + } else { + data = { newValue: description }; + } + } + + updateTaskData(data as ResolveTask); + }; + + /** + * + * @returns True if has access otherwise false + */ + const hasEditAccess = + isAdminUser || + isAssignee || + (!hasGlossaryReviewer && isOwner) || + (Boolean(isPartOfAssigneeTeam) && !isCreator); + + const onSave = () => { + postFeed(comment, taskThread?.id ?? '') + .catch(() => { + // ignore since error is displayed in toast in the parent promise. + // Added block for sonar code smell + }) + .finally(() => { + editorRef.current?.clearEditorValue(); + setShowFeedEditor(false); + }); + }; + + const handleMenuItemClick: MenuProps['onClick'] = (info) => { + if (info.key === TaskActionMode.EDIT) { + setShowEditTaskModel(true); + } else if (info.key === TaskActionMode.CLOSE) { + onTaskReject(); + } else { + onTaskResolve(); + } + setTaskAction( + [ + ...TASK_ACTION_LIST, + ...GLOSSARY_TASK_ACTION_LIST, + ...INCIDENT_TASK_ACTION_LIST, + ].find((action) => action.key === info.key) ?? TASK_ACTION_LIST[0] + ); + }; + + const onTaskReject = () => { + if (!isTaskGlossaryApproval && isEmpty(comment)) { + showErrorToast(t('server.task-closed-without-comment')); + + return; + } + + const updatedComment = isTaskGlossaryApproval ? 'Rejected' : comment; + updateTask(TaskOperation.REJECT, taskDetails?.id + '', { + comment: updatedComment, + } as unknown as TaskDetails) + .then(() => { + showSuccessToast(t('server.task-closed-successfully')); + rest.onAfterClose?.(); + rest.onUpdateEntityDetails?.(); + }) + .catch((err: AxiosError) => showErrorToast(err)); + }; + + const onTestCaseIncidentAssigneeUpdate = async () => { + setIsActionLoading(true); + const testCaseIncident: CreateTestCaseResolutionStatus = { + testCaseResolutionStatusType: TestCaseResolutionStatusTypes.Assigned, + testCaseReference: entityFQN, + testCaseResolutionStatusDetails: { + assignee: { + id: updatedAssignees[0].value, + name: updatedAssignees[0].name, + displayName: updatedAssignees[0].displayName, + type: updatedAssignees[0].type, + }, + }, + }; + try { + const response = await postTestCaseIncidentStatus(testCaseIncident); + updateTestCaseIncidentStatus([...testCaseResolutionStatus, response]); + fetchUpdatedThread(taskThread.id).finally(() => { + setIsEditAssignee(false); + }); + } catch (error) { + showErrorToast(error as AxiosError); + } finally { + setIsActionLoading(false); + } + }; + + const onTestCaseIncidentResolve = async ({ + testCaseFailureReason, + testCaseFailureComment, + }: { + testCaseFailureReason: TestCaseFailureReasonType; + testCaseFailureComment: string; + }) => { + setIsActionLoading(true); + const testCaseIncident: CreateTestCaseResolutionStatus = { + testCaseResolutionStatusType: TestCaseResolutionStatusTypes.Resolved, + testCaseReference: entityFQN, + testCaseResolutionStatusDetails: { + resolvedBy: { + id: currentUser?.id ?? '', + name: currentUser?.name ?? '', + type: 'user', + }, + testCaseFailureReason, + testCaseFailureComment, + }, + }; + try { + const response = await postTestCaseIncidentStatus(testCaseIncident); + updateTestCaseIncidentStatus([...testCaseResolutionStatus, response]); + rest.onAfterClose?.(); + setShowEditTaskModel(false); + } catch (error) { + showErrorToast( + getErrorText(error as AxiosError, t('server.unexpected-error')) + ); + } finally { + setIsActionLoading(false); + } + }; + + const handleTaskMenuClick = (info: MenuInfo) => { + setTaskAction( + INCIDENT_TASK_ACTION_LIST.find((action) => action.key === info.key) ?? + INCIDENT_TASK_ACTION_LIST[0] + ); + switch (info.key) { + case TaskActionMode.RE_ASSIGN: + setIsEditAssignee(true); + + break; + case TaskActionMode.RESOLVE: + setShowEditTaskModel(true); + + break; + } + }; + + const onTestCaseTaskDropdownClick = () => { + if (taskAction.key === TaskActionMode.RESOLVE) { + setShowEditTaskModel(true); + } else { + handleTaskMenuClick({ key: taskAction.key } as MenuInfo); + } + }; + + const handleGlossaryTaskMenuClick = (info: MenuInfo) => { + setTaskAction( + GLOSSARY_TASK_ACTION_LIST.find((action) => action.key === info.key) ?? + GLOSSARY_TASK_ACTION_LIST[0] + ); + switch (info.key) { + case TaskActionMode.RESOLVE: + onTaskResolve(); + + break; + + case TaskActionMode.CLOSE: + onGlossaryTaskResolve('rejected'); + + break; + } + }; + + const handleNoSuggestionMenuItemClick: MenuProps['onClick'] = (info) => { + if (info.key === TaskActionMode.EDIT) { + setShowEditTaskModel(true); + } else { + onTaskReject(); + } + setTaskAction( + noSuggestionTaskMenuOptions.find((action) => action.key === info.key) ?? + noSuggestionTaskMenuOptions[0] + ); + }; + + const onTaskDropdownClick = () => { + if (taskAction.key === TaskActionMode.RESOLVE) { + handleMenuItemClick({ key: taskAction.key } as MenuInfo); + } else { + onTaskReject(); + } + }; + + const onNoSuggestionTaskDropdownClick = () => { + if (taskAction.key === TaskActionMode.EDIT) { + handleNoSuggestionMenuItemClick({ key: taskAction.key } as MenuInfo); + } else { + onTaskReject(); + } + }; + + const renderCommentButton = useMemo(() => { + return ( + + ); + }, [comment, onSave]); + + const approvalWorkflowActions = useMemo(() => { + const hasApprovalAccess = + isAssignee || (Boolean(isPartOfAssigneeTeam) && !isCreator); + + return ( + + + } + menu={{ + items: getFormattedMenuOptions(GLOSSARY_TASK_ACTION_LIST), + selectable: true, + selectedKeys: [taskAction.key], + onClick: handleGlossaryTaskMenuClick, + }} + overlayClassName="task-action-dropdown" + onClick={onTaskDropdownClick}> + {taskAction.label} + + + + {/* {renderCommentButton} */} + + ); + }, [ + taskAction, + isAssignee, + isCreator, + isPartOfAssigneeTeam, + renderCommentButton, + handleGlossaryTaskMenuClick, + onTaskDropdownClick, + ]); + + const testCaseResultFlow = useMemo(() => { + const editPermission = checkPermission( + Operation.EditAll, + ResourceEntity.TEST_CASE, + permissions + ); + const hasApprovalAccess = isAssignee || isCreator || editPermission; + + return ( +
+ } + loading={isActionLoading} + menu={{ + items: getFormattedMenuOptions(INCIDENT_TASK_ACTION_LIST), + selectable: true, + selectedKeys: [taskAction.key], + onClick: handleTaskMenuClick, + disabled: !hasApprovalAccess, + }} + overlayClassName="task-action-dropdown" + onClick={onTestCaseTaskDropdownClick}> + {taskAction.label} + + {/* {renderCommentButton} */} +
+ ); + }, [ + taskDetails, + isAssignee, + isPartOfAssigneeTeam, + taskAction, + renderCommentButton, + ]); + + const actionButtons = useMemo(() => { + if (isTaskGlossaryApproval) { + return approvalWorkflowActions; + } + + if (isTaskTestCaseResult) { + return testCaseResultFlow; + } + + return ( + + {isCreator && !hasEditAccess && ( + + )} + {hasEditAccess && ( + <> + {showAddSuggestionButton ? ( +
+ } + menu={{ + items: getFormattedMenuOptions(noSuggestionTaskMenuOptions), + selectable: true, + selectedKeys: [taskAction.key], + onClick: handleNoSuggestionMenuItemClick, + }} + overlayClassName="task-action-dropdown" + onClick={onNoSuggestionTaskDropdownClick}> + {taskAction.label} + +
+ ) : ( + } + menu={{ + items: getFormattedMenuOptions(TASK_ACTION_LIST), + selectable: true, + selectedKeys: [taskAction.key], + onClick: handleMenuItemClick, + }} + overlayClassName="task-action-dropdown" + onClick={() => + taskAction.key === TaskActionMode.EDIT + ? handleMenuItemClick({ key: taskAction.key } as MenuInfo) + : onTaskResolve() + }> + {taskAction.label} + + )} + + )} + {/* {renderCommentButton} */} +
+ ); + }, [ + onTaskReject, + taskDetails, + onTaskResolve, + handleMenuItemClick, + taskAction, + isTaskClosed, + isTaskGlossaryApproval, + showAddSuggestionButton, + isCreator, + approvalWorkflowActions, + testCaseResultFlow, + isTaskTestCaseResult, + renderCommentButton, + handleNoSuggestionMenuItemClick, + onNoSuggestionTaskDropdownClick, + ]); + + const initialFormValue = useMemo(() => { + if (isTaskDescription) { + const description = + taskDetails?.suggestion ?? taskDetails?.oldValue ?? ''; + + return { description }; + } else { + const updatedTags = JSON.parse( + taskDetails?.suggestion ?? taskDetails?.oldValue ?? '[]' + ); + + return { updatedTags }; + } + }, [taskDetails, isTaskDescription]); + + const handleAssigneeUpdate = async () => { + setIsAssigneeLoading(true); + const updatedTaskThread = { + ...taskThread, + task: { + ...taskThread.task, + assignees: updatedAssignees.map((assignee: Option) => ({ + id: assignee.value, + type: assignee.type, + })), + }, + }; + try { + const patch = compare(taskThread, updatedTaskThread); + const data = await updateThread(taskThread.id, patch); + setIsEditAssignee(false); + updateEntityThread(data); + } catch (error) { + showErrorToast(error as AxiosError); + } finally { + setIsAssigneeLoading(false); + } + }; + + useEffect(() => { + assigneesForm.setFieldValue('assignees', initialAssignees); + setOptions(assigneeOptions); + }, [initialAssignees, assigneeOptions]); + + useEffect(() => { + setTaskAction(latestAction); + }, [latestAction]); + + const taskHeader = isTaskTestCaseResult ? ( + + ) : ( +
+
+ {isEditAssignee ? ( +
+ + { + setIsEditAssignee(false); + assigneesForm.setFieldValue('assignees', initialAssignees); + }} + onSave={() => assigneesForm.submit()}> + 0} + options={options} + value={updatedAssignees} + onChange={(values) => + assigneesForm.setFieldValue('assignees', values) + } + onSearch={(query) => + fetchOptions({ + query, + setOptions, + currentUserId: currentUser?.id, + initialOptions: assigneeOptions, + }) + } + /> + + +
+ ) : ( + <> + + + + + {t('label.created-by')}:{' '} + + + + + {taskThread.createdBy} + + + + + {t('label.assignee-plural')}:{' '} + + + + + {(isCreator || hasEditAccess) && + !isTaskClosed && + owners.length === 0 ? ( +
+
+ ); + + const descriptionField: FieldProp = useMemo( + () => ({ + name: 'testCaseFailureComment', + required: true, + label: t('label.comment'), + id: 'root/description', + type: FieldTypes.DESCRIPTION, + rules: [ + { + required: true, + message: t('label.field-required', { + field: t('label.comment'), + }), + }, + ], + props: { + 'data-testid': 'description', + initialValue: '', + placeHolder: t('message.write-your-text', { + text: t('label.comment'), + }), + }, + }), + [] + ); + const ActionRequired = () => { + return ( +
+ + + {t('label.action-required')} + + + {actionButtons} +
+ ); + }; + + return ( + + + + + {taskLinkTitleElement} + + + {taskHeader} + + {isTaskDescription && ( + form.setFieldValue('description', value)} + /> + )} + + {isTaskTags && ( + form.setFieldValue('updatedTags', value)} + /> + )} + {taskThread.task?.status === ThreadTaskStatus.Open && ActionRequired()} + {/*
+ {taskThread?.posts?.map((reply) => ( + + + ))} +
*/} + + +
+ + {t('label.comment')} + + + {showFeedEditor ? ( + + ) : ( +
+
+ +
+ + setShowFeedEditor(true)} + /> +
+ )} + + {taskThread?.posts && taskThread?.posts?.length > 0 && ( + + {taskThread?.posts + ?.slice() + .sort((a, b) => (b.postTs as number) - (a.postTs as number)) + .map((reply) => ( + + ))} + + )} +
+ + + + {isTaskTestCaseResult ? ( + setShowEditTaskModel(false)} + onOk={form.submit}> +
+ + + + {getField(descriptionField)} +
+
+ ) : ( + { + form.resetFields(); + setShowEditTaskModel(false); + }} + onOk={form.submit}> +
+ {isTaskTags ? ( + + form.setFieldValue('updatedTags', value)} + /> + + ) : ( + + form.setFieldValue('description', value)} + /> + + )} +
+
+ )} + {isTaskTestCaseResult && ( + setIsEditAssignee(false)} + onOk={assigneesForm.submit}> +
+ + + assigneesForm.setFieldValue('assignees', values) + } + onSearch={(query) => + fetchOptions({ + query, + setOptions, + initialOptions: assigneeOptions, + }) + } + /> + +
+
+ )} + +
+ ); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/task-tab-new.less b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/task-tab-new.less new file mode 100644 index 000000000000..a6843e8a7f34 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTab/task-tab-new.less @@ -0,0 +1,163 @@ +/* + * 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 (reference) url('../../../../styles/variables.less'); + +.assignees-edit-input { + .ant-space-item:first-child { + width: 100%; + } +} + +.task-action-button { + button { + height: 32px; + border-radius: 8px; + span { + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 20px; + } + &:hover { + color: #1570ef !important; + } + + svg { + width: 12px; + height: 12px; + font-weight: 600; + } + &:first-child { + border-right: none; + padding-right: 0; + } + &:last-child { + border-left: none; + } + } +} + +.task-action-dropdown { + .anticon svg { + fill: none; + } + ul { + padding: 4px 0; + border-radius: 8px; + + li { + font-size: 14px; + font-weight: 500; + line-height: 20px; + padding: 10px 16px; + cursor: pointer; + border-bottom: none; + &:hover { + background: #f5faff; + color: #1570ef !important; + .anticon svg path { + stroke: #1570ef; + } + } + + &.ant-dropdown-menu-item-selected { + background: #f5faff; + color: #1570ef !important; + .anticon svg path { + stroke: #1570ef !important; + } + } + + &:last-child { + border-bottom: none; + } + } + } +} + +.task-details-id { + color: #175cd3; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; +} +.task-details-entity-link { + color: #535862; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; +} + +.reserve-right-sidebar { + .task-cta-buttons { + // Right side padding 20 + 64 width of sidebar + padding-right: 84px; + } +} + +//user profile page +.action-required-card { + display: flex; + align-items: center; + border-radius: 12px; + border: 0.8px solid #dfdfdf; + background: linear-gradient( + 90deg, + rgba(85, 155, 238, 0.25) 0%, + rgba(96, 167, 251, 0.25) 19%, + rgba(245, 245, 245, 0.25) 100% + ); + padding: 16px; + margin: 24px 0px; + box-shadow: none; +} + +.action-required-text { + flex-grow: 1; + font-size: 16px; + font-weight: 400; + color: #373e44; +} + +.action-buttons { + display: flex; + gap: 8px; +} + +.approve-button { + background-color: #2f74eb; + border-color: #2f74eb; + border-radius: 8px; + font-weight: 500; +} + +.approve-button:hover { + background-color: #1f67e9; + border-color: #1f67e9; +} + +.reject-button { + background-color: #d93025; + border-color: #d93025; + color: white; + border-radius: 8px; + font-weight: 500; +} + +.reject-button:hover { + background-color: #b1271c; + border-color: #b1271c; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/TaskTabIncidentManagerHeader.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/TaskTabIncidentManagerHeader.component.tsx index e58c79f86b31..d6e486b7a4a6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/TaskTabIncidentManagerHeader.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/TaskTabIncidentManagerHeader.component.tsx @@ -10,18 +10,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Col, Row, Space, Steps, Typography } from 'antd'; -import { isEmpty, isUndefined, last, toLower } from 'lodash'; +import { Col, Row, Space, Steps, Tag, Typography } from 'antd'; +import { last, toLower } from 'lodash'; import React, { ReactNode, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants'; +import { ReactComponent as Clock } from '../../../../assets/svg/clock.svg'; +import { ReactComponent as Status } from '../../../../assets/svg/status.svg'; +import { ReactComponent as User } from '../../../../assets/svg/user.svg'; +import { + ICON_DIMENSION_USER_PAGE, + NO_DATA_PLACEHOLDER, +} from '../../../../constants/constants'; import { TEST_CASE_STATUS } from '../../../../constants/TestSuite.constant'; import { Thread } from '../../../../generated/entity/feed/thread'; import { TestCaseResolutionStatusTypes } from '../../../../generated/tests/testCaseResolutionStatus'; import { formatDateTime } from '../../../../utils/date-time/DateTimeUtils'; import { getEntityName } from '../../../../utils/EntityUtils'; import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider'; -import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; import RichTextEditorPreviewerV1 from '../../../common/RichTextEditor/RichTextEditorPreviewerV1'; import Severity from '../../../DataQuality/IncidentManager/Severity/Severity.component'; import './task-tab-incident-manager-header.style.less'; @@ -125,29 +130,38 @@ const TaskTabIncidentManagerHeader = ({ thread }: { thread: Thread }) => { />
- - -
- - {`${t('label.assignee')}: `} - - {isUndefined(thread.task?.assignees) || - isEmpty(thread.task?.assignees) ? ( - NO_DATA_PLACEHOLDER - ) : ( - - )} -
-
- - {`${t('label.created-by')}: `} - - -
-
- + + + {/* Created Time */} + + + {t('label.created-date')} + + + {/* Dec 4, 2024 10:45 AM */} + + + {/* Created By */} + + + {t('label.created-by')} + + + {/* */} + + {thread.createdBy} + + + {/* Status */} + + + {t('label.status')} + + + {/* In Review */} + + +
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/TasktabIncidentManagerHeaderNew.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/TasktabIncidentManagerHeaderNew.tsx new file mode 100644 index 000000000000..8ba78131f9f9 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/TasktabIncidentManagerHeaderNew.tsx @@ -0,0 +1,225 @@ +/* + * 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, Steps, Typography } from 'antd'; +import { last, toLower } from 'lodash'; +import React, { ReactNode, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactComponent as AssigneesIcon } from '../../../../assets/svg/assignees.svg'; +import { ReactComponent as UserIcon } from '../../../../assets/svg/user-profile.svg'; +import { NO_DATA_PLACEHOLDER } from '../../../../constants/constants'; +import { TEST_CASE_STATUS } from '../../../../constants/TestSuite.constant'; +import { Thread } from '../../../../generated/entity/feed/thread'; +import { TestCaseResolutionStatusTypes } from '../../../../generated/tests/testCaseResolutionStatus'; +import { formatDateTime } from '../../../../utils/date-time/DateTimeUtils'; +import { getEntityName } from '../../../../utils/EntityUtils'; +import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider'; +import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; +import ProfilePicture from '../../../common/ProfilePicture/ProfilePicture'; +import RichTextEditorPreviewerV1 from '../../../common/RichTextEditor/RichTextEditorPreviewerV1'; +import Severity from '../../../DataQuality/IncidentManager/Severity/Severity.component'; +import './task-tab-incident-manager-header.style.less'; + +const TaskTabIncidentManagerHeaderNew = ({ thread }: { thread: Thread }) => { + const { t } = useTranslation(); + const { testCaseResolutionStatus } = useActivityFeedProvider(); + const testCaseResolutionStepper = useMemo(() => { + const updatedData = [...testCaseResolutionStatus]; + const lastStatusType = last( + testCaseResolutionStatus + )?.testCaseResolutionStatusType; + + if (lastStatusType && TEST_CASE_STATUS[lastStatusType]) { + updatedData.push( + ...TEST_CASE_STATUS[lastStatusType].map((type) => ({ + testCaseResolutionStatusType: type, + })) + ); + } + + return updatedData.map((status) => { + let details: ReactNode = null; + + switch (status.testCaseResolutionStatusType) { + case TestCaseResolutionStatusTypes.ACK: + details = status.updatedBy ? ( + + {`By ${getEntityName(status.updatedBy)} on `} + + ) : null; + + break; + case TestCaseResolutionStatusTypes.Assigned: + details = status.testCaseResolutionStatusDetails?.assignee ? ( + + {`To ${getEntityName( + status.testCaseResolutionStatusDetails?.assignee + )} on `} + + ) : null; + + break; + case TestCaseResolutionStatusTypes.Resolved: + details = status.testCaseResolutionStatusDetails?.resolvedBy ? ( + + {`By ${getEntityName( + status.testCaseResolutionStatusDetails.resolvedBy + )} on `} + + ) : null; + + break; + + default: + break; + } + + return { + className: toLower(status.testCaseResolutionStatusType), + title: ( +
+ + {status.testCaseResolutionStatusType} + + + {details} + {status.updatedAt && ( + + {formatDateTime(status.updatedAt)} + + )} + +
+ ), + key: status.testCaseResolutionStatusType, + }; + }); + }, [testCaseResolutionStatus]); + + const latestTestCaseResolutionStatus = useMemo( + () => last(testCaseResolutionStatus), + [testCaseResolutionStatus] + ); + + const isResolved = + latestTestCaseResolutionStatus?.testCaseResolutionStatusType === + TestCaseResolutionStatusTypes.Resolved; + + return ( + + {/* +
+ +
+ */} + + + + + + {t('label.created-by')} + + + + {' '} + + {thread.createdBy} + + + + + + {`${t('label.assignee-plural')} `} + + + + + + + + {/* */} + + {' '} + {`${t('label.severity')} `} + + + + + + + {isResolved && ( + + {/* */} + {`${t( + 'label.failure-reason' + )}: `} + + )} + {isResolved && ( + + + {latestTestCaseResolutionStatus?.testCaseResolutionStatusDetails + ?.testCaseFailureReason ?? NO_DATA_PLACEHOLDER} + + + )} + {isResolved && ( + + {/* */} + + {' '} + {`${t('label.failure-comment')} `} + + + )} + {isResolved && ( + + + + )} + +
+ +
+ +
+
+ ); +}; + +export default TaskTabIncidentManagerHeaderNew; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/task-tab-incident-manager-header.style.less b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/task-tab-incident-manager-header.style.less index 473d72ce58cc..d92c7291cbf8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/task-tab-incident-manager-header.style.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/Task/TaskTabIncidentManagerHeader/task-tab-incident-manager-header.style.less @@ -18,3 +18,13 @@ padding: 24px; overflow-x: auto; } +.incident-manager-details-label { + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; +} + +.incident-manager-details-label.ant-typography { + color: #535862; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/APICollectionSummary/APICollectionSummary.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/APICollectionSummary/APICollectionSummary.tsx deleted file mode 100644 index a40124e4bdad..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/APICollectionSummary/APICollectionSummary.tsx +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2022 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, Divider, Row } from 'antd'; -import { get } from 'lodash'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { TabSpecificField } from '../../../../enums/entity.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { APICollection } from '../../../../generated/entity/data/apiCollection'; -import { getApiCollectionByFQN } from '../../../../rest/apiCollectionsAPI'; -import { getSortedTagsWithHighlight } from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; - -interface APICollectionSummaryProps { - entityDetails: APICollection; - componentType?: DRAWER_NAVIGATION_OPTIONS; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} - -const APICollectionSummary = ({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - isLoading, - highlights, -}: APICollectionSummaryProps) => { - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.API_COLLECTION, entityDetails), - [entityDetails] - ); - const [apiCollectionDetails, setApiCollectionDetails] = - useState(entityDetails); - - const isExplore = useMemo( - () => componentType === DRAWER_NAVIGATION_OPTIONS.explore, - [componentType] - ); - - const ownerDetails = useMemo(() => { - const owners = entityDetails.owners; - - return { - value: , - }; - }, [entityDetails, apiCollectionDetails]); - - const fetchApiCollectionDetails = useCallback(async () => { - try { - const res = await getApiCollectionByFQN( - entityDetails.fullyQualifiedName ?? '', - { - fields: [TabSpecificField.TAGS, TabSpecificField.OWNERS], - } - ); - - setApiCollectionDetails({ ...res }); - } catch (error) { - // Error - } - }, [entityDetails]); - - useEffect(() => { - if (entityDetails.service?.type === 'apiService') { - fetchApiCollectionDetails(); - } - }, [entityDetails, componentType]); - - return ( - - <> - - {!isExplore ? ( - - {ownerDetails.value} - - ) : null} - - - - - - - - - ); -}; - -export default APICollectionSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/APIEndpointSummary/APIEndpointSummary.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/APIEndpointSummary/APIEndpointSummary.tsx index eb0f1d5dbfab..7195f0d8b02d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/APIEndpointSummary/APIEndpointSummary.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/APIEndpointSummary/APIEndpointSummary.tsx @@ -11,71 +11,37 @@ * limitations under the License. */ -import { Col, Divider, Radio, RadioChangeEvent, Row, Typography } from 'antd'; -import { get, isEmpty } from 'lodash'; +import { Col, Radio, RadioChangeEvent, Row, Typography } from 'antd'; +import { isEmpty } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { TabSpecificField } from '../../../../enums/entity.enum'; import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; import { APIEndpoint } from '../../../../generated/entity/data/apiEndpoint'; import { getApiEndPointByFQN } from '../../../../rest/apiEndpointsAPI'; -import { - getFormattedEntityData, - getSortedTagsWithHighlight, -} from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; +import { getFormattedEntityData } from '../../../../utils/EntitySummaryPanelUtils'; import { SchemaViewType } from '../../../APIEndpoint/APIEndpointSchema/APIEndpointSchema'; -import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; import SummaryList from '../SummaryList/SummaryList.component'; import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; interface APIEndpointSummaryProps { entityDetails: APIEndpoint; - componentType?: DRAWER_NAVIGATION_OPTIONS; - isLoading?: boolean; highlights?: SearchedDataProps['data'][number]['highlight']; } const APIEndpointSummary = ({ entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - isLoading, highlights, }: APIEndpointSummaryProps) => { const { t } = useTranslation(); - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.API_ENDPOINT, entityDetails), - [entityDetails] - ); - const [apiEndpointDetails, setApiEndpointDetails] = useState(entityDetails); const [viewType, setViewType] = useState( SchemaViewType.REQUEST_SCHEMA ); - const isExplore = useMemo( - () => componentType === DRAWER_NAVIGATION_OPTIONS.explore, - [componentType] - ); - - const ownerDetails = useMemo(() => { - const owners = entityDetails.owners; - - return { - value: , - }; - }, [entityDetails, apiEndpointDetails]); - const { formattedSchemaFieldsData, activeSchema } = useMemo(() => { const activeSchema = viewType === SchemaViewType.REQUEST_SCHEMA @@ -117,59 +83,32 @@ const APIEndpointSummary = ({ if (entityDetails.service?.type === 'apiService') { fetchApiEndpointDetails(); } - }, [entityDetails, componentType]); + }, [entityDetails]); return ( - - <> - - {!isExplore ? ( - - {ownerDetails.value} - - ) : null} - - - - - - - - - - - - - {t('label.request')} - - - {t('label.response')} - - - - - {isEmpty(activeSchema?.schemaFields) ? ( - - - {t('message.no-data-available')} - - - ) : ( - - )} - - - - + + + + + {t('label.request')} + + + {t('label.response')} + + + + + {isEmpty(activeSchema?.schemaFields) ? ( + + + {t('message.no-data-available')} + + + ) : ( + + )} + + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ChartSummary/ChartSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ChartSummary/ChartSummary.component.tsx deleted file mode 100644 index f06ed57a66c1..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ChartSummary/ChartSummary.component.tsx +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2024 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, Divider, Row, Typography } from 'antd'; -import { get } from 'lodash'; -import React, { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { Chart } from '../../../../generated/entity/data/chart'; -import { - getFormattedEntityData, - getSortedTagsWithHighlight, -} from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import SummaryList from '../SummaryList/SummaryList.component'; -import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; - -interface ChartsSummaryProps { - entityDetails: Chart; - highlights?: SearchedDataProps['data'][number]['highlight']; -} - -const ChartSummary = ({ entityDetails, highlights }: ChartsSummaryProps) => { - const { t } = useTranslation(); - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.CHARTS, entityDetails), - [entityDetails] - ); - const formattedDashboardData: BasicEntityInfo[] = useMemo( - () => - getFormattedEntityData( - SummaryEntityType.DASHBOARD, - entityDetails.dashboards - ), - [entityDetails.dashboards] - ); - - return ( - <> - - - - - - - - - - - - - - - {t('label.dashboard-plural')} - - - - - - - - ); -}; - -export default ChartSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ChartSummary/ChartSummary.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ChartSummary/ChartSummary.test.tsx deleted file mode 100644 index 55d4e12cefc4..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ChartSummary/ChartSummary.test.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 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 { act, render, screen } from '@testing-library/react'; -import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { MOCK_CHART_DATA } from '../../../../mocks/Chart.mock'; -import ChartSummary from './ChartSummary.component'; - -jest.mock('../SummaryList/SummaryList.component', () => - jest.fn().mockImplementation(() =>

SummaryList

) -); - -jest.mock('../CommonEntitySummaryInfo/CommonEntitySummaryInfo', () => - jest.fn().mockImplementation(() =>

testCommonEntitySummaryInfo

) -); - -jest.mock( - '../../../common/SummaryTagsDescription/SummaryTagsDescription.component', - () => jest.fn().mockImplementation(() =>

SummaryTagsDescription

) -); - -jest.mock('../../../../utils/EntityUtils', () => ({ - getEntityOverview: jest.fn(), - DRAWER_NAVIGATION_OPTIONS: { - explore: 'explore', - }, -})); - -jest.mock('../../../../utils/EntitySummaryPanelUtils', () => ({ - getSortedTagsWithHighlight: jest.fn(), - getFormattedEntityData: jest.fn(), -})); - -describe('ChartSummary component tests', () => { - it('Component should render properly', async () => { - await act(async () => { - render(, { - wrapper: MemoryRouter, - }); - }); - - expect(screen.getByTestId('charts-header')).toBeInTheDocument(); - expect(screen.getByText('SummaryList')).toBeInTheDocument(); - expect(screen.getByText('SummaryTagsDescription')).toBeInTheDocument(); - expect(screen.getByText('testCommonEntitySummaryInfo')).toBeInTheDocument(); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ContainerSummary/ContainerSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ContainerSummary/ContainerSummary.component.tsx deleted file mode 100644 index 1420e0d7411d..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ContainerSummary/ContainerSummary.component.tsx +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2022 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, Divider, Row, Typography } from 'antd'; -import { get } from 'lodash'; -import React, { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { Container } from '../../../../generated/entity/data/container'; -import { - getFormattedEntityData, - getSortedTagsWithHighlight, -} from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import SummaryList from '../SummaryList/SummaryList.component'; -import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; -import { ContainerSummaryProps } from './ContainerSummary.interface'; - -function ContainerSummary({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - isLoading, - highlights, -}: ContainerSummaryProps) { - const { t } = useTranslation(); - - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.CONTAINERS, entityDetails), - [entityDetails] - ); - - const formattedColumnsData: BasicEntityInfo[] = useMemo( - () => - getFormattedEntityData( - SummaryEntityType.COLUMN, - (entityDetails as Container).dataModel?.columns, - highlights - ), - [entityDetails] - ); - - return ( - - <> - - - - - - - - - - - - - - - {t('label.schema')} - - - - - - - - - ); -} - -export default ContainerSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ContainerSummary/ContainerSummary.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ContainerSummary/ContainerSummary.test.tsx deleted file mode 100644 index 7137d616e0ee..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ContainerSummary/ContainerSummary.test.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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 { act, render, screen } from '@testing-library/react'; -import React from 'react'; -import { - Constraint, - Container, - DataType, - FileFormat, - StorageServiceType, -} from '../../../../generated/entity/data/container'; -import ContainerSummary from './ContainerSummary.component'; - -jest.mock('../SummaryList/SummaryList.component', () => - jest - .fn() - .mockImplementation(() =>
SummaryList
) -); - -const mockEntityDetails: Container = { - id: '63be99e5-8ebf-44b6-8247-0f4faed00798', - name: 'transactions', - fullyQualifiedName: 's3_storage_sample.transactions', - displayName: 'Company Transactions', - description: "Bucket containing all the company's transactions", - version: 0.1, - updatedAt: 1678969800877, - updatedBy: 'admin', - href: 'http://openmetadata-server:8585/api/v1/containers/63be99e5-8ebf-44b6-8247-0f4faed00798', - service: { - id: '7ab99e67-b578-4361-bad2-9076a52b341d', - type: 'storageService', - name: 's3_storage_sample', - fullyQualifiedName: 's3_storage_sample', - deleted: false, - href: 'http://openmetadata-server:8585/api/v1/services/storageServices/7ab99e67-b578-4361-bad2-9076a52b341d', - }, - dataModel: { - isPartitioned: true, - columns: [ - { - name: 'transaction_id', - dataType: DataType.Numeric, - dataTypeDisplay: 'numeric', - description: - 'The ID of the executed transaction. This column is the primary key for this table.', - fullyQualifiedName: 's3_storage_sample.transactions.transaction_id', - tags: [], - constraint: Constraint.PrimaryKey, - ordinalPosition: 1, - }, - { - name: 'merchant', - dataType: DataType.Varchar, - dataLength: 100, - dataTypeDisplay: 'varchar', - description: 'The merchant for this transaction.', - fullyQualifiedName: 's3_storage_sample.transactions.merchant', - tags: [], - ordinalPosition: 2, - }, - { - name: 'transaction_time', - dataType: DataType.Timestamp, - dataTypeDisplay: 'timestamp', - description: 'The time the transaction took place.', - fullyQualifiedName: 's3_storage_sample.transactions.transaction_time', - tags: [], - ordinalPosition: 3, - }, - ], - }, - prefix: '/transactions/', - numberOfObjects: 50, - size: 102400, - fileFormats: [FileFormat.Parquet], - serviceType: StorageServiceType.S3, - deleted: false, - tags: [], - followers: [], -}; - -describe('ContainerSummary component tests', () => { - it('Component should render properly, when loaded in the Explore page.', async () => { - await act(async () => { - render(); - }); - - const numberOfObjects = screen.getByTestId('label.object-plural-value'); - const serviceType = screen.getByTestId('label.service-type-value'); - const colsLength = screen.getByTestId('label.column-plural-value'); - const summaryList = screen.getByTestId('SummaryList'); - - expect(numberOfObjects).toBeInTheDocument(); - expect(serviceType).toBeInTheDocument(); - expect(colsLength).toBeInTheDocument(); - expect(summaryList).toBeInTheDocument(); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.component.tsx deleted file mode 100644 index 224b6f058a2a..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.component.tsx +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2022 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, Divider, Row, Typography } from 'antd'; -import { get } from 'lodash'; -import React, { useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { - Dashboard, - TagLabel, -} from '../../../../generated/entity/data/dashboard'; -import { fetchCharts } from '../../../../utils/DashboardDetailsUtils'; -import { - getFormattedEntityData, - getSortedTagsWithHighlight, -} from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import { ChartType } from '../../../Dashboard/DashboardDetails/DashboardDetails.interface'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import SummaryList from '../SummaryList/SummaryList.component'; -import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; - -interface DashboardSummaryProps { - entityDetails: Dashboard; - componentType?: DRAWER_NAVIGATION_OPTIONS; - tags?: TagLabel[]; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} - -function DashboardSummary({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - tags, - isLoading, - highlights, -}: DashboardSummaryProps) { - const { t } = useTranslation(); - const [charts, setCharts] = useState(); - - const fetchChartsDetails = async () => { - try { - const chartDetails = await fetchCharts(entityDetails.charts); - - const updatedCharts = chartDetails.map((chartItem) => ({ - ...chartItem, - sourceUrl: chartItem.sourceUrl, - })); - - setCharts(updatedCharts); - } catch (err) { - // Error - } - }; - - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.DASHBOARDS, entityDetails), - [entityDetails] - ); - - useEffect(() => { - fetchChartsDetails(); - }, [entityDetails]); - - const formattedChartsData: BasicEntityInfo[] = useMemo( - () => getFormattedEntityData(SummaryEntityType.CHART, charts, highlights), - [charts] - ); - - const formattedDataModelData: BasicEntityInfo[] = useMemo( - () => - getFormattedEntityData( - SummaryEntityType.COLUMN, - entityDetails.dataModels, - highlights - ), - [charts] - ); - - return ( - - <> - - - - - - - - - - - - - - {t('label.chart-plural')} - - - - - - - - - - - - - {t('label.data-model-plural')} - - - - - - - - - ); -} - -export default DashboardSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.test.tsx deleted file mode 100644 index e9117e9acf99..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.test.tsx +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 { act, render, screen } from '@testing-library/react'; -import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils'; -import { - mockDashboardEntityDetails, - mockFetchChartsResponse, -} from '../mocks/DashboardSummary.mock'; -import DashboardSummary from './DashboardSummary.component'; - -jest.mock('../SummaryList/SummaryList.component', () => - jest - .fn() - .mockImplementation(() =>
SummaryList
) -); - -jest.mock('../CommonEntitySummaryInfo/CommonEntitySummaryInfo', () => - jest.fn().mockImplementation(() =>
testCommonEntitySummaryInfo
) -); - -jest.mock('../../../../utils/DashboardDetailsUtils', () => ({ - fetchCharts: jest.fn().mockImplementation(() => mockFetchChartsResponse), -})); - -describe('DashboardSummary component tests', () => { - it('Component should render properly', async () => { - await act(async () => { - render(, { - wrapper: MemoryRouter, - }); - }); - - const commonEntitySummaryInfo = screen.getByText( - 'testCommonEntitySummaryInfo' - ); - const chartsHeader = screen.getByTestId('charts-header'); - const summaryList = screen.getAllByTestId('SummaryList'); - - expect(commonEntitySummaryInfo).toBeInTheDocument(); - expect(chartsHeader).toBeInTheDocument(); - expect(summaryList).toHaveLength(2); - }); - - it('Component should render properly, when loaded in the Lineage page.', async () => { - await act(async () => { - const { debug } = render( - , - { - wrapper: MemoryRouter, - } - ); - - debug(); - }); - - const ownerLabel = screen.queryByTestId('label.owner-label'); - const commonEntitySummaryInfo = screen.getByText( - 'testCommonEntitySummaryInfo' - ); - const tags = screen.getByText('label.tag-plural'); - const description = screen.getByText('label.description'); - const noDataFound = screen.getByText('label.no-data-found'); - - expect(ownerLabel).not.toBeInTheDocument(); - - expect(commonEntitySummaryInfo).toBeInTheDocument(); - - expect(tags).toBeInTheDocument(); - expect(description).toBeInTheDocument(); - expect(noDataFound).toBeInTheDocument(); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.component.tsx deleted file mode 100644 index 32d8e4145ce0..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.component.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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, Divider, Row, Typography } from 'antd'; -import { get, isEmpty } from 'lodash'; -import { default as React, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { DashboardDataModel } from '../../../../generated/entity/data/dashboardDataModel'; -import { - getFormattedEntityData, - getSortedTagsWithHighlight, -} from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import SummaryList from '../SummaryList/SummaryList.component'; -import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; -import { DataModelSummaryProps } from './DataModelSummary.interface'; - -const DataModelSummary = ({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - tags, - isLoading, - highlights, -}: DataModelSummaryProps) => { - const { t } = useTranslation(); - const { columns } = entityDetails; - const [dataModelDetails, setDataModelDetails] = - useState(entityDetails); - - const entityInfo = useMemo( - () => - getEntityOverview(ExplorePageTabs.DASHBOARD_DATA_MODEL, dataModelDetails), - [dataModelDetails] - ); - - const formattedColumnsData: BasicEntityInfo[] = useMemo( - () => getFormattedEntityData(SummaryEntityType.COLUMN, columns, highlights), - [columns, dataModelDetails] - ); - - useEffect(() => { - if (!isEmpty(entityDetails)) { - setDataModelDetails(entityDetails); - } - }, [entityDetails]); - - return ( - - <> - - - - - - - - - - - - - - - {t('label.column-plural')} - - - - - - - - - ); -}; - -export default DataModelSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.interface.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.interface.tsx deleted file mode 100644 index aa403d077633..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.interface.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 { DashboardDataModel } from '../../../../generated/entity/data/dashboardDataModel'; -import { TagLabel } from '../../../../generated/entity/data/table'; -import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; - -export interface DataModelSummaryProps { - entityDetails: DashboardDataModel; - componentType?: DRAWER_NAVIGATION_OPTIONS; - tags?: TagLabel[]; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataProductSummary/DataProductSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataProductSummary/DataProductSummary.component.tsx index ca7c0b0e6ba0..3c28b98d01d8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataProductSummary/DataProductSummary.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataProductSummary/DataProductSummary.component.tsx @@ -10,7 +10,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Col, Divider, Row, Space, Typography } from 'antd'; +import { Col, Row, Space, Typography } from 'antd'; import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { DataProduct } from '../../../../generated/entity/domains/dataProduct'; @@ -34,8 +34,8 @@ const DataProductSummary = ({ return ( - <> - + + - - + - - - + - - - + - - - + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSchemaSummary/DatabaseSchemaSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSchemaSummary/DatabaseSchemaSummary.component.tsx deleted file mode 100644 index a464e4b39e84..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSchemaSummary/DatabaseSchemaSummary.component.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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, Divider, Row } from 'antd'; -import { get } from 'lodash'; -import React, { useMemo } from 'react'; -import SummaryTagsDescription from '../../../../components/common/SummaryTagsDescription/SummaryTagsDescription.component'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { getSortedTagsWithHighlight } from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import { DatabaseSchemaSummaryProps } from './DatabaseSchemaSummary.interface'; - -const DatabaseSchemaSummary = ({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - tags, - isLoading, - highlights, -}: DatabaseSchemaSummaryProps) => { - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.DATABASE_SCHEMA, entityDetails), - [entityDetails] - ); - - return ( - - <> - - - - - - - - - - - - ); -}; - -export default DatabaseSchemaSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSchemaSummary/DatabaseSchemaSummary.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSchemaSummary/DatabaseSchemaSummary.interface.ts deleted file mode 100644 index a8621fa5cebf..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSchemaSummary/DatabaseSchemaSummary.interface.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 { DatabaseSchema } from '../../../../generated/entity/data/databaseSchema'; -import { TagLabel } from '../../../../generated/type/tagLabel'; -import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; - -export interface DatabaseSchemaSummaryProps { - entityDetails: DatabaseSchema; - componentType?: DRAWER_NAVIGATION_OPTIONS; - tags?: TagLabel[]; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSummary/DatabaseSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSummary/DatabaseSummary.component.tsx deleted file mode 100644 index 1c2ea8b27f9f..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSummary/DatabaseSummary.component.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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, Divider, Row, Typography } from 'antd'; -import { get } from 'lodash'; -import React, { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import SummaryTagsDescription from '../../../../components/common/SummaryTagsDescription/SummaryTagsDescription.component'; -import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { - getFormattedEntityData, - getSortedTagsWithHighlight, -} from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import SummaryList from '../SummaryList/SummaryList.component'; -import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; -import { DatabaseSummaryProps } from './DatabaseSummary.interface'; - -const DatabaseSummary = ({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - tags, - isLoading, - highlights, -}: DatabaseSummaryProps) => { - const { t } = useTranslation(); - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.DATABASE, entityDetails), - [entityDetails] - ); - - const formattedSchemaData: BasicEntityInfo[] = useMemo( - () => - getFormattedEntityData( - SummaryEntityType.SCHEMAFIELD, - entityDetails.databaseSchemas, - highlights - ), - [entityDetails] - ); - - return ( - - <> - - - - - - - - - - - - - - - - {t('label.schema')} - - - - - - - - - ); -}; - -export default DatabaseSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSummary/DatabaseSummary.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSummary/DatabaseSummary.interface.ts deleted file mode 100644 index 127621a91316..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DatabaseSummary/DatabaseSummary.interface.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 { Database } from '../../../../generated/entity/data/database'; -import { TagLabel } from '../../../../generated/type/tagLabel'; -import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; - -export interface DatabaseSummaryProps { - entityDetails: Database; - componentType?: DRAWER_NAVIGATION_OPTIONS; - tags?: TagLabel[]; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.component.tsx index 7f135fea60dc..9f597f2bd979 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.component.tsx @@ -11,7 +11,8 @@ * limitations under the License. */ -import { Drawer, Typography } from 'antd'; +import { CloseOutlined } from '@ant-design/icons'; +import { Card, Typography } from 'antd'; import { get } from 'lodash'; import React, { useEffect, useMemo, useState } from 'react'; import { Link, useParams } from 'react-router-dom'; @@ -22,64 +23,23 @@ import { } from '../../../context/PermissionProvider/PermissionProvider.interface'; import { ERROR_PLACEHOLDER_TYPE, SIZE } from '../../../enums/common.enum'; import { EntityType } from '../../../enums/entity.enum'; -import { ExplorePageTabs } from '../../../enums/Explore.enum'; -import { Tag } from '../../../generated/entity/classification/tag'; -import { APICollection } from '../../../generated/entity/data/apiCollection'; -import { APIEndpoint } from '../../../generated/entity/data/apiEndpoint'; -import { Chart } from '../../../generated/entity/data/chart'; -import { Container } from '../../../generated/entity/data/container'; -import { Dashboard } from '../../../generated/entity/data/dashboard'; -import { DashboardDataModel } from '../../../generated/entity/data/dashboardDataModel'; -import { Database } from '../../../generated/entity/data/database'; -import { DatabaseSchema } from '../../../generated/entity/data/databaseSchema'; -import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm'; -import { Metric } from '../../../generated/entity/data/metric'; -import { Mlmodel } from '../../../generated/entity/data/mlmodel'; -import { Pipeline } from '../../../generated/entity/data/pipeline'; -import { SearchIndex } from '../../../generated/entity/data/searchIndex'; -import { StoredProcedure } from '../../../generated/entity/data/storedProcedure'; -import { Table } from '../../../generated/entity/data/table'; -import { Topic } from '../../../generated/entity/data/topic'; -import { DataProduct } from '../../../generated/entity/domains/dataProduct'; -import { APIService } from '../../../generated/entity/services/apiService'; -import { DashboardService } from '../../../generated/entity/services/dashboardService'; -import { DatabaseService } from '../../../generated/entity/services/databaseService'; -import { MessagingService } from '../../../generated/entity/services/messagingService'; -import { MlmodelService } from '../../../generated/entity/services/mlmodelService'; -import { PipelineService } from '../../../generated/entity/services/pipelineService'; -import { SearchService } from '../../../generated/entity/services/searchService'; -import { StorageService } from '../../../generated/entity/services/storageService'; -import { getEntityLinkFromType } from '../../../utils/EntityUtils'; +import { + DRAWER_NAVIGATION_OPTIONS, + getEntityLinkFromType, +} from '../../../utils/EntityUtils'; import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils'; import searchClassBase from '../../../utils/SearchClassBase'; import { stringToHTML } from '../../../utils/StringsUtils'; import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; import Loader from '../../common/Loader/Loader'; -import APICollectionSummary from './APICollectionSummary/APICollectionSummary'; -import APIEndpointSummary from './APIEndpointSummary/APIEndpointSummary'; -import ChartSummary from './ChartSummary/ChartSummary.component'; -import ContainerSummary from './ContainerSummary/ContainerSummary.component'; -import DashboardSummary from './DashboardSummary/DashboardSummary.component'; -import DatabaseSchemaSummary from './DatabaseSchemaSummary/DatabaseSchemaSummary.component'; -import DatabaseSummary from './DatabaseSummary/DatabaseSummary.component'; -import DataModelSummary from './DataModelSummary/DataModelSummary.component'; -import DataProductSummary from './DataProductSummary/DataProductSummary.component'; +import { DataAssetSummaryPanel } from '../../DataAssetSummaryPanel/DataAssetSummaryPanel'; import './entity-summary-panel.less'; import { EntitySummaryPanelProps } from './EntitySummaryPanel.interface'; -import GlossaryTermSummary from './GlossaryTermSummary/GlossaryTermSummary.component'; -import MetricSummary from './MetricSummary/MetricSummary'; -import MlModelSummary from './MlModelSummary/MlModelSummary.component'; -import PipelineSummary from './PipelineSummary/PipelineSummary.component'; -import SearchIndexSummary from './SearchIndexSummary/SearchIndexSummary.component'; -import ServiceSummary from './ServiceSummary/ServiceSummary.component'; -import StoredProcedureSummary from './StoredProcedureSummary/StoredProcedureSummary.component'; -import TableSummary from './TableSummary/TableSummary.component'; -import TagsSummary from './TagsSummary/TagsSummary.component'; -import TopicSummary from './TopicSummary/TopicSummary.component'; export default function EntitySummaryPanel({ entityDetails, highlights, + handleClosePanel, }: EntitySummaryPanelProps) { const { tab } = useParams<{ tab: string }>(); const { getEntityPermission } = usePermissionProvider(); @@ -133,205 +93,19 @@ export default function EntitySummaryPanel({ } const type = get(entityDetails, 'details.entityType') ?? EntityType.TABLE; const entity = entityDetails.details; - switch (type) { - case EntityType.TABLE: - return ( - - ); - - case EntityType.TOPIC: - return ( - - ); - - case EntityType.DASHBOARD: - return ( - - ); - - case EntityType.CHART: - return ( - - ); - - case EntityType.PIPELINE: - return ( - - ); - - case EntityType.MLMODEL: - return ( - - ); - - case EntityType.CONTAINER: - return ( - - ); - - case EntityType.STORED_PROCEDURE: - return ( - - ); - - case EntityType.DASHBOARD_DATA_MODEL: - return ( - - ); - - case EntityType.GLOSSARY_TERM: - return ; - - case EntityType.TAG: - return ; - - case EntityType.DATA_PRODUCT: - return ; - case EntityType.SEARCH_INDEX: - return ( - - ); - - case EntityType.DATABASE: - return ( - - ); - - case EntityType.DATABASE_SCHEMA: - return ( - - ); - - case EntityType.DATABASE_SERVICE: - return ( - - ); - case EntityType.MESSAGING_SERVICE: - return ( - - ); - case EntityType.DASHBOARD_SERVICE: - return ( - - ); - case EntityType.PIPELINE_SERVICE: - return ( - - ); - - case EntityType.MLMODEL_SERVICE: - return ( - - ); - - case EntityType.STORAGE_SERVICE: - return ( - - ); - - case EntityType.SEARCH_SERVICE: - return ( - - ); - case EntityType.API_SERVICE: - return ( - - ); - case EntityType.API_ENDPOINT: - return ( - - ); - case EntityType.API_COLLECTION: - return ( - - ); - - case EntityType.METRIC: - return ( - - ); - - default: - return searchClassBase.getEntitySummaryComponent(entity); - } + return ( + + ); }, [tab, entityDetails, viewPermission, isPermissionLoading]); const entityLink = useMemo( @@ -340,14 +114,15 @@ export default function EntitySummaryPanel({ ); return ( - + } title={ viewPermission && ( ) - } - width="100%"> + }> {summaryComponent} - + ); } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/GlossaryTermSummary/GlossaryTermSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/GlossaryTermSummary/GlossaryTermSummary.component.tsx index 8c3d11da14cc..266a86b72a7b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/GlossaryTermSummary/GlossaryTermSummary.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/GlossaryTermSummary/GlossaryTermSummary.component.tsx @@ -11,7 +11,7 @@ * limitations under the License. */ -import { Col, Divider, Row, Space, Typography } from 'antd'; +import { Col, Row, Space, Typography } from 'antd'; import { isEmpty } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -81,8 +81,11 @@ function GlossaryTermSummary({ return ( - <> - + + - - - + - - - + - + ); } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/MetricSummary/MetricSummary.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/MetricSummary/MetricSummary.tsx deleted file mode 100644 index ee307c1071c1..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/MetricSummary/MetricSummary.tsx +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2022 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, Divider, Row } from 'antd'; -import { get, isEmpty } from 'lodash'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { TabSpecificField } from '../../../../enums/entity.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { Metric } from '../../../../generated/entity/data/metric'; -import { getMetricByFqn } from '../../../../rest/metricsAPI'; -import { getSortedTagsWithHighlight } from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import MetricExpression from '../../../Metric/MetricExpression/MetricExpression'; -import RelatedMetrics from '../../../Metric/RelatedMetrics/RelatedMetrics'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; - -interface MetricSummaryProps { - entityDetails: Metric; - componentType?: DRAWER_NAVIGATION_OPTIONS; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} - -const MetricSummary = ({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - isLoading, - highlights, -}: MetricSummaryProps) => { - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.METRIC, entityDetails), - [entityDetails] - ); - - const [metricDetails, setMetricDetails] = useState(entityDetails); - - const ownerDetails = useMemo(() => { - const owners = entityDetails.owners; - - return { - value: , - }; - }, [entityDetails, metricDetails]); - - const fetchMetricDetails = useCallback(async () => { - if (isEmpty(entityDetails.fullyQualifiedName)) { - return; - } - - try { - const res = await getMetricByFqn(entityDetails.fullyQualifiedName ?? '', { - fields: [TabSpecificField.TAGS, TabSpecificField.OWNERS], - }); - - setMetricDetails({ ...res }); - } catch (error) { - // Error - } - }, [entityDetails, componentType]); - - useEffect(() => { - fetchMetricDetails(); - }, [entityDetails, componentType]); - - return ( - - <> - - - {ownerDetails.value} - - - - - - - - - - - - - - - - - - - - - ); -}; - -export default MetricSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/MlModelSummary/MlModelSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/MlModelSummary/MlModelSummary.component.tsx deleted file mode 100644 index 515c6e084b86..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/MlModelSummary/MlModelSummary.component.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2022 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, Divider, Row, Typography } from 'antd'; -import { get } from 'lodash'; -import React, { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { Mlmodel, TagLabel } from '../../../../generated/entity/data/mlmodel'; -import { - getFormattedEntityData, - getSortedTagsWithHighlight, -} from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import SummaryList from '../SummaryList/SummaryList.component'; -import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; - -interface MlModelSummaryProps { - entityDetails: Mlmodel; - componentType?: DRAWER_NAVIGATION_OPTIONS; - tags?: TagLabel[]; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} - -function MlModelSummary({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - tags, - isLoading, - highlights, -}: MlModelSummaryProps) { - const { t } = useTranslation(); - - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.MLMODELS, entityDetails), - [entityDetails] - ); - - const formattedFeaturesData: BasicEntityInfo[] = useMemo( - () => - getFormattedEntityData( - SummaryEntityType.MLFEATURE, - entityDetails.mlFeatures, - highlights - ), - [entityDetails] - ); - - return ( - - <> - - - - - - - - - - - - - - {t('label.feature-plural')} - - - - - - - - - ); -} - -export default MlModelSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/MlModelSummary/MlModelSummary.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/MlModelSummary/MlModelSummary.test.tsx deleted file mode 100644 index 198cc54195ed..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/MlModelSummary/MlModelSummary.test.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 { render, screen } from '@testing-library/react'; -import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { - mockMlModelEntityDetails, - mockMlModelEntityDetails1, -} from '../mocks/MlModelSummary.mock'; -import MlModelSummary from './MlModelSummary.component'; - -jest.mock('../SummaryList/SummaryList.component', () => - jest - .fn() - .mockImplementation(() =>
SummaryList
) -); - -describe('MlModelSummary component tests', () => { - it('Component should render properly', () => { - render(, { - wrapper: MemoryRouter, - }); - - const algorithmLabel = screen.getByTestId('label.algorithm-label'); - const targetLabel = screen.getByTestId('label.target-label'); - const serverLabel = screen.getByTestId('label.server-label'); - const dashboardLabel = screen.getByTestId('label.dashboard-label'); - const algorithmValue = screen.getByTestId('label.algorithm-value'); - const targetValue = screen.getByTestId('label.target-value'); - const serverValue = screen.getByTestId('label.server-value'); - const dashboardValue = screen.getByTestId('label.dashboard-value'); - - expect(algorithmLabel).toBeInTheDocument(); - expect(targetLabel).toBeInTheDocument(); - expect(serverLabel).toBeInTheDocument(); - expect(dashboardLabel).toBeInTheDocument(); - expect(algorithmValue).toContainHTML('Neural Network'); - expect(targetValue).toContainHTML('ETA_time'); - expect(serverValue).toContainHTML('http://my-server.ai'); - expect(dashboardValue).toBeInTheDocument(); - }); - - it('Fields with no data should display "-" in value', () => { - render(, { - wrapper: MemoryRouter, - }); - - const algorithmLabel = screen.getByTestId('label.algorithm-label'); - const targetLabel = screen.queryByTestId('label.target-label'); - const serverLabel = screen.queryByTestId('label.server-label'); - const dashboardLabel = screen.queryByTestId('label.dashboard-label'); - const algorithmValue = screen.getByTestId('label.algorithm-value'); - const targetValue = screen.getByTestId('label.target-value'); - const serverValue = screen.getByTestId('label.server-value'); - const dashboardValue = screen.getByTestId('label.dashboard-value'); - - expect(algorithmLabel).toBeInTheDocument(); - expect(targetLabel).toBeInTheDocument(); - expect(serverLabel).toBeInTheDocument(); - expect(dashboardLabel).toBeInTheDocument(); - expect(algorithmValue).toContainHTML('Time Series'); - expect(targetValue).toContainHTML('-'); - expect(serverValue).toContainHTML('-'); - expect(dashboardValue).toContainHTML('-'); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/PipelineSummary/PipelineSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/PipelineSummary/PipelineSummary.component.tsx deleted file mode 100644 index 225ec3beb251..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/PipelineSummary/PipelineSummary.component.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2022 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, Divider, Row, Typography } from 'antd'; -import { get } from 'lodash'; -import React, { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { Pipeline, TagLabel } from '../../../../generated/entity/data/pipeline'; -import { - getFormattedEntityData, - getSortedTagsWithHighlight, -} from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import SummaryList from '../SummaryList/SummaryList.component'; -import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; - -interface PipelineSummaryProps { - entityDetails: Pipeline; - componentType?: DRAWER_NAVIGATION_OPTIONS; - tags?: TagLabel[]; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} - -function PipelineSummary({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - tags, - isLoading, - highlights, -}: PipelineSummaryProps) { - const { t } = useTranslation(); - - const formattedTasksData: BasicEntityInfo[] = useMemo( - () => - getFormattedEntityData( - SummaryEntityType.TASK, - entityDetails.tasks, - highlights - ), - [entityDetails] - ); - - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.PIPELINES, entityDetails), - [entityDetails] - ); - - return ( - - <> - - - - - - - - - - - - - - {t('label.task-plural')} - - - - - - - - - ); -} - -export default PipelineSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/PipelineSummary/PipelineSummary.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/PipelineSummary/PipelineSummary.test.tsx deleted file mode 100644 index c74d68abdf38..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/PipelineSummary/PipelineSummary.test.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 { render, screen } from '@testing-library/react'; -import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils'; -import { mockPipelineEntityDetails } from '../mocks/PipelineSummary.mock'; -import PipelineSummary from './PipelineSummary.component'; - -jest.mock('../SummaryList/SummaryList.component', () => - jest - .fn() - .mockImplementation(() =>
SummaryList
) -); - -jest.mock('../CommonEntitySummaryInfo/CommonEntitySummaryInfo', () => - jest.fn().mockImplementation(() =>
testCommonEntitySummaryInfo
) -); - -describe('PipelineSummary component tests', () => { - it('Component should render properly, when loaded in the Explore page.', () => { - render(, { - wrapper: MemoryRouter, - }); - - const commonEntitySummaryInfo = screen.getByText( - 'testCommonEntitySummaryInfo' - ); - const tasksHeader = screen.getByTestId('tasks-header'); - const summaryList = screen.getByTestId('SummaryList'); - - expect(commonEntitySummaryInfo).toBeInTheDocument(); - expect(tasksHeader).toBeInTheDocument(); - expect(summaryList).toBeInTheDocument(); - }); - - it('Component should render properly, when loaded in the Lineage page.', async () => { - render( - , - { - wrapper: MemoryRouter, - } - ); - - const descriptionHeader = screen.getAllByTestId('description-header'); - const tags = screen.getByText('label.tag-plural'); - const noTags = screen.getByText('label.no-tags-added'); - const commonEntitySummaryInfo = screen.getByText( - 'testCommonEntitySummaryInfo' - ); - - const viewerContainer = screen.getByTestId('viewer-container'); - const summaryList = screen.getByTestId('SummaryList'); - const ownerLabel = screen.queryByTestId('label.owner-label'); - - expect(ownerLabel).not.toBeInTheDocument(); - - expect(descriptionHeader[0]).toBeInTheDocument(); - expect(tags).toBeInTheDocument(); - expect(commonEntitySummaryInfo).toBeInTheDocument(); - expect(noTags).toBeInTheDocument(); - - expect(summaryList).toBeInTheDocument(); - expect(viewerContainer).toBeInTheDocument(); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SearchIndexSummary/SearchIndexSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SearchIndexSummary/SearchIndexSummary.component.tsx deleted file mode 100644 index dc69bb0b388f..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SearchIndexSummary/SearchIndexSummary.component.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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, Divider, Row, Typography } from 'antd'; -import { get, isEmpty } from 'lodash'; -import { default as React, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { SearchIndex } from '../../../../generated/entity/data/searchIndex'; -import { - getFormattedEntityData, - getSortedTagsWithHighlight, -} from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import SummaryList from '../SummaryList/SummaryList.component'; -import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; -import { SearchIndexSummaryProps } from './SearchIndexSummary.interface'; - -function SearchIndexSummary({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - tags, - isLoading, - highlights, -}: SearchIndexSummaryProps) { - const { t } = useTranslation(); - const [searchIndexDetails, setSearchIndexDetails] = - useState(entityDetails); - - const isExplore = useMemo( - () => componentType === DRAWER_NAVIGATION_OPTIONS.explore, - [componentType] - ); - - const { fields } = searchIndexDetails; - - const formattedFieldsData: BasicEntityInfo[] = useMemo( - () => getFormattedEntityData(SummaryEntityType.FIELD, fields, highlights), - [fields, searchIndexDetails] - ); - - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.SEARCH_INDEX, searchIndexDetails), - [searchIndexDetails] - ); - - useEffect(() => { - if (!isEmpty(entityDetails)) { - setSearchIndexDetails(entityDetails); - } - }, [entityDetails]); - - return ( - - <> - {!isExplore ? ( - <> - - - - - - - - - - - - ) : null} - - {isExplore ? ( - <> - - - - ) : null} - - - - {t('label.field-plural')} - - - - - - - - - ); -} - -export default SearchIndexSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SearchIndexSummary/SearchIndexSummary.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SearchIndexSummary/SearchIndexSummary.interface.ts deleted file mode 100644 index a3d6879e9984..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SearchIndexSummary/SearchIndexSummary.interface.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2022 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 { - SearchIndex, - TagLabel, -} from '../../../../generated/entity/data/searchIndex'; -import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; - -export interface SearchIndexSummaryProps { - entityDetails: SearchIndex; - componentType?: DRAWER_NAVIGATION_OPTIONS; - tags?: TagLabel[]; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SearchIndexSummary/SearchIndexSummary.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SearchIndexSummary/SearchIndexSummary.test.tsx deleted file mode 100644 index 676474058dba..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SearchIndexSummary/SearchIndexSummary.test.tsx +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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 { act, render, screen } from '@testing-library/react'; -import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { - LabelType, - State, - TagSource, -} from '../../../../generated/type/tagLabel'; -import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils'; -import { mockSearchIndexEntityDetails } from '../mocks/SearchIndexSummary.mock'; -import SearchIndexSummary from './SearchIndexSummary.component'; - -jest.mock('../SummaryList/SummaryList.component', () => - jest - .fn() - .mockImplementation(() =>
SummaryList
) -); - -describe('SearchIndexSummary component tests', () => { - it('Component should render properly, when loaded in the Explore page.', async () => { - await act(async () => { - render( - , - { - wrapper: MemoryRouter, - } - ); - }); - - const fieldsHeader = screen.getByTestId('fields-header'); - const tagsHeader = screen.getByTestId('tags-header'); - const summaryList = screen.getByTestId('SummaryList'); - const tag1 = screen.getByText('PersonalData.Personal'); - const tag2 = screen.getByText('PII.Sensitive'); - - expect(fieldsHeader).toBeInTheDocument(); - expect(tagsHeader).toBeInTheDocument(); - expect(summaryList).toBeInTheDocument(); - expect(tag1).toBeInTheDocument(); - expect(tag2).toBeInTheDocument(); - }); - - it('Component should render properly, when loaded in the Lineage page.', async () => { - await act(async () => { - render( - , - { - wrapper: MemoryRouter, - } - ); - }); - - const descriptionHeader = screen.getByTestId('description-header'); - const fieldsHeader = screen.getByTestId('fields-header'); - const ownerLabel = screen.queryByTestId('label.owner-label'); - const tierLabel = screen.getByText('label.tier'); - const serviceLabel = screen.getByText('label.service'); - const tierValue = screen.getByTestId('label.tier-value'); - const serviceValue = screen.getByText('testES'); - const summaryList = screen.getByTestId('SummaryList'); - - expect(ownerLabel).not.toBeInTheDocument(); - expect(descriptionHeader).toBeInTheDocument(); - expect(fieldsHeader).toBeInTheDocument(); - expect(tierLabel).toBeInTheDocument(); - expect(serviceLabel).toBeInTheDocument(); - expect(tierValue).toContainHTML('-'); - expect(serviceValue).toBeInTheDocument(); - expect(summaryList).toBeInTheDocument(); - }); - - it('No data placeholder should be displayed in case of no tags', async () => { - await act(async () => { - render( - - ); - }); - - const tagsHeader = screen.getByTestId('tags-header'); - const noTagsPlaceholder = screen.getByText('label.no-tags-added'); - - expect(tagsHeader).toBeInTheDocument(); - expect(noTagsPlaceholder).toBeInTheDocument(); - }); - - it('Tier should be displayed in tags section on explore page', async () => { - await act(async () => { - render( - , - { - wrapper: MemoryRouter, - } - ); - }); - - const tagsHeader = screen.getByTestId('tags-header'); - const tier = screen.getByText('Tier1'); - const noTagsPlaceholder = screen.queryByText('label.no-tags-added'); - - expect(tagsHeader).toBeInTheDocument(); - expect(tier).toBeInTheDocument(); - expect(noTagsPlaceholder).toBeNull(); - }); - - it('Tier should not be displayed in tags section on Lineage page', async () => { - await act(async () => { - render( - , - { - wrapper: MemoryRouter, - } - ); - }); - - const tagsHeader = screen.getByText('label.tag-plural'); - const tierLabel = screen.getByText('label.tier'); - const tierValue = screen.getByText('Tier1'); - const noTagsPlaceholder = screen.getByText('label.no-tags-added'); - - expect(tagsHeader).toBeInTheDocument(); - expect(tierLabel).toBeInTheDocument(); - expect(tierValue).toBeInTheDocument(); - expect(noTagsPlaceholder).toBeInTheDocument(); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ServiceSummary/ServiceSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ServiceSummary/ServiceSummary.component.tsx deleted file mode 100644 index 3e2b50553e2a..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ServiceSummary/ServiceSummary.component.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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, Divider, Row } from 'antd'; -import { get } from 'lodash'; -import React, { useMemo } from 'react'; -import SummaryTagsDescription from '../../../../components/common/SummaryTagsDescription/SummaryTagsDescription.component'; -import { getSortedTagsWithHighlight } from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import { ServiceSummaryProps } from './ServiceSummary.interface'; - -const ServiceSummary = ({ - type, - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - tags, - isLoading, - highlights, -}: ServiceSummaryProps) => { - const entityInfo = useMemo( - () => getEntityOverview(type, entityDetails), - [entityDetails] - ); - - return ( - - <> - - - - - - - - - - - - ); -}; - -export default ServiceSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ServiceSummary/ServiceSummary.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ServiceSummary/ServiceSummary.interface.ts deleted file mode 100644 index 29172499782f..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/ServiceSummary/ServiceSummary.interface.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { TagLabel } from '../../../../generated/type/tagLabel'; -import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; -import { EntityServiceUnion } from '../../ExplorePage.interface'; - -export interface ServiceSummaryProps { - type: ExplorePageTabs; - entityDetails: EntityServiceUnion; - componentType?: DRAWER_NAVIGATION_OPTIONS; - tags?: TagLabel[]; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx deleted file mode 100644 index f120d3a7583e..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.component.tsx +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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, Divider, Row, Typography } from 'antd'; -import { get, isObject } from 'lodash'; -import React, { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { CSMode } from '../../../../enums/codemirror.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; -import { StoredProcedureCodeObject } from '../../../../generated/entity/data/storedProcedure'; -import { getSortedTagsWithHighlight } from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import SchemaEditor from '../../../Database/SchemaEditor/SchemaEditor'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import { StoredProcedureSummaryProps } from './StoredProcedureSummary.interface'; - -const StoredProcedureSummary = ({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - tags, - isLoading, - highlights, -}: StoredProcedureSummaryProps) => { - const { t } = useTranslation(); - - const entityInfo = useMemo( - () => getEntityOverview(ExplorePageTabs.STORED_PROCEDURE, entityDetails), - [entityDetails] - ); - - return ( - - <> - - - - - - - - - - - - {isObject(entityDetails.storedProcedureCode) && ( - - - - {t('label.code')} - - - - - - - )} - - - ); -}; - -export default StoredProcedureSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.interface.ts deleted file mode 100644 index 966625bcfe58..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/StoredProcedureSummary/StoredProcedureSummary.interface.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 { StoredProcedure } from '../../../../generated/entity/data/storedProcedure'; -import { TagLabel } from '../../../../generated/type/tagLabel'; -import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; - -export interface StoredProcedureSummaryProps { - entityDetails: StoredProcedure; - componentType?: DRAWER_NAVIGATION_OPTIONS; - tags?: TagLabel[]; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; -} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SummaryList/SummaryListItems/SummaryListItems.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SummaryList/SummaryListItems/SummaryListItems.component.tsx index 88fb1b75e24a..8a4810d2a17c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SummaryList/SummaryListItems/SummaryListItems.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SummaryList/SummaryListItems/SummaryListItems.component.tsx @@ -17,6 +17,7 @@ import { useTranslation } from 'react-i18next'; import { MAX_CHAR_LIMIT_ENTITY_SUMMARY } from '../../../../../constants/constants'; import { getTagValue } from '../../../../../utils/CommonUtils'; import { prepareConstraintIcon } from '../../../../../utils/TableUtils'; +import AppBadge from '../../../../common/Badge/Badge.component'; import RichTextEditorPreviewerV1 from '../../../../common/RichTextEditor/RichTextEditorPreviewerV1'; import TagsViewer from '../../../../Tag/TagsViewer/TagsViewer'; import { SummaryListItemProps } from './SummaryListItems.interface'; @@ -48,9 +49,13 @@ function SummaryListItem({ {entityDetails.title} {entityDetails.type && ( - {`(${entityDetails.type})`} + )} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SummaryList/summary-list.less b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SummaryList/summary-list.less index 357f19be5578..193ec5257ae6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SummaryList/summary-list.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/SummaryList/summary-list.less @@ -15,7 +15,7 @@ .summary-list-collapse { &.ant-collapse { - background-color: @grey-1; + background-color: @white; margin-bottom: 16px; padding: 16px; .ant-collapse-item { @@ -38,7 +38,7 @@ } .summary-list-item-container { - background-color: @grey-1; + background-color: @white; margin-bottom: 16px; padding: 16px; border-radius: 5px; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.component.tsx index ac44fa6d5a7d..131c459f1902 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.component.tsx @@ -11,8 +11,8 @@ * limitations under the License. */ -import { Col, Divider, Row, Typography } from 'antd'; -import { get, isEmpty, isUndefined } from 'lodash'; +import { Col, Row, Typography } from 'antd'; +import { isUndefined } from 'lodash'; import { default as React, useCallback, @@ -23,58 +23,31 @@ import { import { useTranslation } from 'react-i18next'; import { ROUTES } from '../../../../constants/constants'; import { mockTablePermission } from '../../../../constants/mockTourData.constants'; -import { PROFILER_FILTER_RANGE } from '../../../../constants/profiler.constant'; import { usePermissionProvider } from '../../../../context/PermissionProvider/PermissionProvider'; import { OperationPermission, ResourceEntity, } from '../../../../context/PermissionProvider/PermissionProvider.interface'; -import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; -import { ExplorePageTabs } from '../../../../enums/Explore.enum'; import { Table } from '../../../../generated/entity/data/table'; import { TestSummary } from '../../../../generated/tests/testCase'; import useCustomLocation from '../../../../hooks/useCustomLocation/useCustomLocation'; -import { getListTestCaseIncidentStatus } from '../../../../rest/incidentManagerAPI'; import { getLatestTableProfileByFqn } from '../../../../rest/tableAPI'; import { getTestCaseExecutionSummary } from '../../../../rest/testAPI'; -import { - getCurrentMillis, - getEpochMillisForPastDays, -} from '../../../../utils/date-time/DateTimeUtils'; -import { - getFormattedEntityData, - getSortedTagsWithHighlight, -} from '../../../../utils/EntitySummaryPanelUtils'; -import { - DRAWER_NAVIGATION_OPTIONS, - getEntityOverview, -} from '../../../../utils/EntityUtils'; import { DEFAULT_ENTITY_PERMISSION } from '../../../../utils/PermissionsUtils'; -import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; -import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component'; -import CommonEntitySummaryInfo from '../CommonEntitySummaryInfo/CommonEntitySummaryInfo'; -import SummaryList from '../SummaryList/SummaryList.component'; -import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; import './table-summary.less'; import { TableProfileDetails, TableSummaryProps, } from './TableSummary.interface'; -function TableSummary({ - entityDetails, - componentType = DRAWER_NAVIGATION_OPTIONS.explore, - tags, - isLoading, - highlights, -}: TableSummaryProps) { +function TableSummary({ entityDetails }: TableSummaryProps) { const { t } = useTranslation(); const location = useCustomLocation(); const isTourPage = location.pathname.includes(ROUTES.TOUR); const { getEntityPermission } = usePermissionProvider(); const [profileData, setProfileData] = useState(); - const [incidentCount, setIncidentCount] = useState(0); + const [testSuiteSummary, setTestSuiteSummary] = useState(); const [tablePermissions, setTablePermissions] = useState( DEFAULT_ENTITY_PERMISSION @@ -106,26 +79,6 @@ function TableSummary({ } }; - const fetchIncidentCount = async () => { - if (tableDetails?.fullyQualifiedName) { - try { - const { paging } = await getListTestCaseIncidentStatus({ - limit: 0, - latest: true, - originEntityFQN: tableDetails?.fullyQualifiedName, - startTs: getEpochMillisForPastDays( - PROFILER_FILTER_RANGE.last30days.days - ), - endTs: getCurrentMillis(), - }); - - setIncidentCount(paging.total); - } catch (error) { - setIncidentCount(0); - } - } - }; - const fetchProfilerData = useCallback(async () => { try { const { profile, tableConstraints } = await getLatestTableProfileByFqn( @@ -190,25 +143,6 @@ function TableSummary({ ); }, [tableDetails, testSuiteSummary, viewProfilerPermission]); - const entityInfo = useMemo( - () => - getEntityOverview(ExplorePageTabs.TABLES, tableDetails, { - incidentCount, - }), - [tableDetails, incidentCount] - ); - - const formattedColumnsData: BasicEntityInfo[] = useMemo( - () => - getFormattedEntityData( - SummaryEntityType.COLUMN, - tableDetails.columns, - highlights, - tableDetails.tableConstraints - ), - [tableDetails] - ); - const init = useCallback(async () => { if (tableDetails.id && !isTourPage) { const tablePermission = await getEntityPermission( @@ -225,7 +159,6 @@ function TableSummary({ if (shouldFetchProfilerData) { fetchProfilerData(); fetchAllTests(); - fetchIncidentCount(); } } else { setTablePermissions(mockTablePermission as OperationPermission); @@ -244,61 +177,19 @@ function TableSummary({ }, [tableDetails.id]); return ( - - <> - - - - - - - - - - - - {t('label.profiler-amp-data-quality')} - - - {profilerSummary} - - - - - - - - - - - {t('label.schema')} - - - - - - - - + + + + {t('label.profiler-amp-data-quality')} + + + {profilerSummary} + ); } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.interface.ts index 207ca159ff9f..bb657152b258 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TableSummary/TableSummary.interface.ts @@ -11,16 +11,10 @@ * limitations under the License. */ -import { Table, TagLabel } from '../../../../generated/entity/data/table'; -import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils'; -import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface'; +import { Table } from '../../../../generated/entity/data/table'; export interface TableSummaryProps { entityDetails: Table; - componentType?: DRAWER_NAVIGATION_OPTIONS; - tags?: TagLabel[]; - isLoading?: boolean; - highlights?: SearchedDataProps['data'][number]['highlight']; } export interface TableProfileDetails { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TagsSummary/TagsSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TagsSummary/TagsSummary.component.tsx index bba441d3d673..b9cdb577731c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TagsSummary/TagsSummary.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TagsSummary/TagsSummary.component.tsx @@ -68,7 +68,7 @@ function TagsSummary({ entityDetails, isLoading }: TagsSummaryProps) { return ( - + (entityDetails); - - const isExplore = useMemo( - () => componentType === DRAWER_NAVIGATION_OPTIONS.explore, - [componentType] - ); - - const entityInfo = useMemo( - () => - getEntityOverview(ExplorePageTabs.TOPICS, { - ...topicDetails, - ...entityDetails, - }), - [topicDetails, entityDetails] - ); - - const ownerDetails = useMemo(() => { - const owners = entityDetails.owners; - - return { - value: , - }; - }, [entityDetails, topicDetails]); - - const fetchExtraTopicInfo = useCallback(async () => { - try { - const res = await getTopicByFqn(entityDetails.fullyQualifiedName ?? '', { - fields: [TabSpecificField.OWNERS, TabSpecificField.TAGS], - }); - - const { partitions, messageSchema } = res; - - setTopicDetails({ ...entityDetails, partitions, messageSchema }); - } catch (error) { - // Error - } - }, [entityDetails]); - - const formattedSchemaFieldsData: BasicEntityInfo[] = useMemo( - () => - getFormattedEntityData( - SummaryEntityType.SCHEMAFIELD, - topicDetails.messageSchema?.schemaFields, - highlights - ), - [topicDetails] - ); - - useEffect(() => { - if (entityDetails.service?.type === 'messagingService') { - fetchExtraTopicInfo(); - } - }, [entityDetails, componentType]); - - return ( - - <> - - {!isExplore ? ( - - {ownerDetails.value} - - ) : null} - - - - - - - - - - - - - {t('label.schema')} - - - - {isEmpty(topicDetails?.messageSchema?.schemaFields) ? ( - - - {t('message.no-data-available')} - - - ) : ( - - )} - - - - - ); -} - -export default TopicSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TopicSummary/TopicSummary.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TopicSummary/TopicSummary.test.tsx deleted file mode 100644 index 488db69421d8..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/TopicSummary/TopicSummary.test.tsx +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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 { act, render, screen } from '@testing-library/react'; -import React from 'react'; -import { getTopicByFqn } from '../../../../rest/topicsAPI'; -import { - mockTopicByFqnResponse, - mockTopicEntityDetails, -} from '../mocks/TopicSummary.mock'; -import TopicSummary from './TopicSummary.component'; - -jest.mock('../SummaryList/SummaryList.component', () => - jest - .fn() - .mockImplementation(() =>
SummaryList
) -); - -jest.mock('../../../../rest/topicsAPI', () => ({ - getTopicByFqn: jest - .fn() - .mockImplementation(() => Promise.resolve(mockTopicByFqnResponse)), -})); - -jest.mock( - '../../../../components/common/SummaryTagsDescription/SummaryTagsDescription.component', - () => jest.fn().mockImplementation(() =>

SummaryTagDescription

) -); - -jest.mock( - '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component', - () => jest.fn().mockImplementation(({ children }) => <>{children}) -); - -describe('TopicSummary component tests', () => { - it('Component should render properly', async () => { - await act(async () => { - render(); - }); - - const partitionsLabel = screen.getByTestId('label.partition-plural-label'); - const replicationFactorLabel = screen.getByTestId( - 'label.replication-factor-label' - ); - const retentionSizeLabel = screen.getByTestId('label.retention-size-label'); - const cleanUpPoliciesLabel = screen.getByTestId( - 'label.clean-up-policy-plural-label' - ); - const maxMessageSizeLabel = screen.getByTestId( - 'label.max-message-size-label' - ); - - const partitionsValue = screen.getByTestId('label.partition-plural-value'); - const replicationFactorValue = screen.getByTestId( - 'label.replication-factor-value' - ); - const retentionSizeValue = screen.getByTestId('label.retention-size-value'); - const cleanUpPoliciesValue = screen.getByTestId( - 'label.clean-up-policy-plural-value' - ); - const maxMessageSizeValue = screen.getByTestId( - 'label.max-message-size-value' - ); - const schemaHeader = screen.getByTestId('schema-header'); - const summaryList = screen.getByTestId('SummaryList'); - - expect(partitionsLabel).toBeInTheDocument(); - expect(replicationFactorLabel).toBeInTheDocument(); - expect(retentionSizeLabel).toBeInTheDocument(); - expect(cleanUpPoliciesLabel).toBeInTheDocument(); - expect(maxMessageSizeLabel).toBeInTheDocument(); - expect(partitionsValue).toContainHTML('-'); - expect(replicationFactorValue).toContainHTML('4'); - expect(retentionSizeValue).toContainHTML('1018.83 MB'); - expect(cleanUpPoliciesValue).toContainHTML('delete'); - expect(maxMessageSizeValue).toContainHTML('208 Bytes'); - expect(schemaHeader).toBeInTheDocument(); - expect(summaryList).toBeInTheDocument(); - }); - - it('No data message should be shown in case no schemaFields are available in topic details', async () => { - (getTopicByFqn as jest.Mock).mockImplementation(() => - Promise.resolve({ ...mockTopicEntityDetails, messageSchema: {} }) - ); - - await act(async () => { - render(); - }); - - const summaryList = screen.queryByTestId('SummaryList'); - const noDataMessage = screen.queryByTestId('no-data-message'); - - expect(summaryList).toBeNull(); - expect(noDataMessage).toBeInTheDocument(); - }); - - it('In case any topic field is not present, "-" should be displayed in place of value', async () => { - (getTopicByFqn as jest.Mock).mockImplementationOnce(() => - Promise.reject({}) - ); - await act(async () => { - render(); - }); - - const partitionsValue = screen.getByTestId('label.partition-plural-value'); - - expect(partitionsValue).toContainHTML('-'); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx index 579be93c4fd4..94402ae23bf0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -12,7 +12,7 @@ * limitations under the License. */ -import { PlusOutlined } from '@ant-design/icons'; +import Icon, { PlusOutlined } from '@ant-design/icons'; import { Button, Checkbox, @@ -22,6 +22,7 @@ import { notification, Row, Skeleton, + Space, Tooltip, Typography, } from 'antd'; @@ -593,7 +594,7 @@ const AssetsTabs = forwardRef( const assetListing = useMemo( () => data.length ? ( -
+
{data.map(({ _source, _id = '' }) => ( { return ( -
- {activeEntity && permissions.Create && data.length > 0 && ( + activeEntity && + permissions.Create && + data.length > 0 && ( +
onSelectAll(e.target.checked)}> @@ -704,8 +707,8 @@ const AssetsTabs = forwardRef( field: t('label.all'), })} - )} -
+
+ ) ); }, [ activeFilter, @@ -723,10 +726,10 @@ const AssetsTabs = forwardRef( const layout = useMemo(() => { return ( - <> + {assetsHeader} {assetListing} - + ); }, [assetsHeader, assetListing, selectedCard]); @@ -823,24 +826,15 @@ const AssetsTabs = forwardRef( return ( <>
- {assetCount > 0 && ( - - -
- -
- - -
- - {quickFilterQuery && ( - - {t('label.clear-entity', { - entity: '', - })} - - )} -
- -
- )} - - {isLoading || isCountLoading ? ( - - - - + + + + + {selectedFilter.length > 0 && ( + +
+ + {quickFilterQuery && ( + + {t('label.clear-entity', { + entity: '', + })} + + )} +
+ + )} + + )} + {isLoading || isCountLoading ? ( - + + + + + -
- ) : ( - layout - )} + ) : ( + layout + )} +
- {!(isLoading || isCountLoading) && ( + {!(isLoading || isCountLoading) && permissions?.EditAll && (
0, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less index 3a68d6e4e089..56cc7de0aa5b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less @@ -74,6 +74,17 @@ .assets-tab-container { .explore-search-card { + border-radius: 12px; + border-left: 4px solid transparent; + opacity: 0.95; + background: #f5f5f5; + &.highlight-card { + border-color: #2081f0; + } + margin: 0 0 20px; + &:last-child { + margin-bottom: 0; + } .service-icon { height: 16px; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/ProfileEditModal/ProfileEditModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ProfileEditModal/ProfileEditModal.tsx new file mode 100644 index 000000000000..741fb21bd435 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ProfileEditModal/ProfileEditModal.tsx @@ -0,0 +1,162 @@ +/* + * Copyright 2022 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 { Button, Input, Modal, Typography } from 'antd'; +import { AxiosError } from 'axios'; +import React, { FunctionComponent, useCallback, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +// import { showErrorToast } from '../../../utils/ToastUtils'; +// import RichTextEditor from '../../common/RichTextEditor/RichTextEditor'; +import { isEmpty } from 'lodash'; +import { User } from '../../../generated/entity/teams/user'; +import { getBackendFormat, HTMLToMarkdown } from '../../../utils/FeedUtils'; +import { showErrorToast } from '../../../utils/ToastUtils'; +import { EditorContentRef } from '../ModalWithMarkdownEditor/ModalWithMarkdownEditor.interface'; +import './profile-edit-modal.less'; +// import { +// EditorContentRef, +// ModalWithMarkdownEditorProps, +// } from './ModalWithMarkdownEditor.interface'; + +interface ProfileEditModalProps { + userData: User; + header: string; + value: string; + placeholder: string; + onSave?: (editorValue: any, displayName: any) => void; + onCancel?: () => void; + visible: boolean; + updateUserDetails: (data: Partial, key: keyof User) => Promise; +} + +export const ProfileEditModal: FunctionComponent = ({ + userData, + header, + placeholder, + value, + onSave, + onCancel, + visible, + updateUserDetails, +}: ProfileEditModalProps) => { + const { t } = useTranslation(); + const [isLoading, setIsLoading] = useState(false); + const [displayName, setDisplayName] = useState(userData.displayName); + const markdownRef = useRef({} as EditorContentRef); + const [editorValue, setEditorValue] = useState(''); + const handleDisplayNameSave = useCallback(async () => { + if (userData.displayName !== displayName) { + // Compare correctly + setIsLoading(true); + try { + await updateUserDetails( + { displayName: isEmpty(displayName) ? undefined : displayName }, + 'displayName' + ); + } catch (error) { + showErrorToast(error as AxiosError); + } finally { + setIsLoading(false); + } + } + }, [displayName, updateUserDetails, userData.displayName]); + const handleDescriptionChange = useCallback( + async (description: string) => { + await updateUserDetails({ description }, 'description'); + }, + [updateUserDetails] + ); + const handleSaveData = async () => { + setIsLoading(true); + try { + const content = markdownRef.current?.getEditorContent?.()?.trim() ?? ''; + await updateUserDetails({ displayName }, 'displayName'); + onSave?.(editorValue, displayName); + } catch (error) { + showErrorToast(error as AxiosError); + } finally { + setIsLoading(false); + } + }; + + const onDisplayNameChange = useCallback( + (e: React.ChangeEvent) => setDisplayName(e.target.value), + [] + ); + const onChangeHandler = (value: string) => { + const markdown = HTMLToMarkdown.turndown(value); + const backendFormat = getBackendFormat(markdown); + setEditorValue(markdown); + }; + + return ( + + {t('label.cancel')} + , + , + ]} + maskClosable={false} + open={visible} + title={ + + {t('label.edit-profile')} + + } + onCancel={onCancel}> + + {t('label.display-name')} + + + {/* + {t('label.description')} + + */} + + ); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/ProfileEditModal/profile-edit-modal.less b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ProfileEditModal/profile-edit-modal.less new file mode 100644 index 000000000000..eb7fec078b16 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ProfileEditModal/profile-edit-modal.less @@ -0,0 +1,82 @@ +/* + * Copyright 2025 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 (reference) url('../../../styles/variables.less'); +.profile-edit-modal { + max-width: 599px; + display: flex; + max-width: 598.752px; + flex-direction: column; + align-items: center; + gap: 20px; + border-radius: 10px; + background: @white; + + font-family: Inter, sans-serif; + .modal-label { + color: #52525b; + font-family: Inter, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 22px; + } + .display-name-edit-input { + border-radius: 4px; + border: 1px solid #dde3ea; + padding: 8px; + color: 757575; + .ant-input { + color: #757575; + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: normal; + + &::placeholder { + color: #757575; + } + } + } + .ant-modal-footer { + border-top: none; + padding-top: 6px; + } + .modal-header { + color: rgba(0, 0, 0, 0.85); + font-family: Inter, sans-serif; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 24px; + } + .cancel-name-edit-btn { + background-color: @white; + color: @primary-color; + box-shadow: none; + } + .editor-container { + border-radius: 4px; + border: none; + } +} +.profile-edit-modal .ant-btn.cancel-name-edit-btn:hover { + background-color: inherit !important; + color: @primary-color !important; + box-shadow: none !important; +} +.profile-edit-modal .ant-btn.save-updated-name-btn:hover { + background-color: @primary-color !important; + color: @white !important; + box-shadow: none !important; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaSelectableList/PersonaSelectableList.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaSelectableList/PersonaSelectableList.component.tsx index 1695b05198ac..858655f04e6a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaSelectableList/PersonaSelectableList.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaSelectableList/PersonaSelectableList.component.tsx @@ -10,15 +10,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Button, Popover, Space, Tooltip, Typography } from 'antd'; +import { Button, Popover, Select, Space, Tooltip, Typography } from 'antd'; +import classNames from 'classnames'; import { t } from 'i18next'; -import React, { useCallback, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg'; -import { - DE_ACTIVE_COLOR, - PAGE_SIZE_LARGE, -} from '../../../../constants/constants'; +import { ReactComponent as PersonaIcon } from '../../../../assets/svg/persona (2).svg'; +import { ReactComponent as ClosePopoverIcon } from '../../../../assets/svg/popover-close.svg'; +import { ReactComponent as SavePopoverIcon } from '../../../../assets/svg/popover-save.svg'; +import { ReactComponent as EditIcon } from '../../../../assets/svg/user-profile-edit.svg'; + +import { PAGE_SIZE_LARGE } from '../../../../constants/constants'; import { EntityType } from '../../../../enums/entity.enum'; import { EntityReference } from '../../../../generated/entity/type'; import { getAllPersonas } from '../../../../rest/PersonaAPI'; @@ -26,7 +28,6 @@ import { getEntityName, getEntityReferenceListFromEntities, } from '../../../../utils/EntityUtils'; -import { SelectableList } from '../../../common/SelectableList/SelectableList.component'; import { PersonaSelectableListProps } from './PersonaSelectableList.interface'; export const PersonaListItemRenderer = (props: EntityReference) => { @@ -42,6 +43,7 @@ export const PersonaListItemRenderer = (props: EntityReference) => { ); }; + export const PersonaSelectableList = ({ hasPermission, selectedPersonas = [], @@ -50,12 +52,18 @@ export const PersonaSelectableList = ({ popoverProps, multiSelect = false, personaList, + isDefaultPersona, }: PersonaSelectableListProps) => { const [popupVisible, setPopupVisible] = useState(false); const { t } = useTranslation(); const [allPersona, setAllPersona] = useState( personaList ?? [] ); + const [isSelectOpen, setIsSelectOpen] = useState(false); + const [isSaving, setIsSaving] = useState(false); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [currentlySelectedPersonas, setCurrentlySelectedPersonas] = + useState([]); const fetchOptions = async (searchText: string, after?: string) => { if (searchText) { @@ -93,66 +101,151 @@ export const PersonaSelectableList = ({ } } }; + const [selectOptions, setSelectOptions] = useState([]); - const handleUpdate = useCallback( - async (users: EntityReference[]) => { - if (multiSelect) { - await (onUpdate as (users: EntityReference[]) => Promise)(users); - } else { - await (onUpdate as (users: EntityReference) => Promise)(users[0]); - } + const loadOptions = async () => { + const { data } = await fetchOptions(''); + setSelectOptions(data); + }; - setPopupVisible(false); - }, - [onUpdate] - ); + useEffect(() => { + loadOptions(); + }, []); + + const handlePersonaUpdate = () => { + setIsSaving(true); + + Promise.resolve( + onUpdate( + isDefaultPersona + ? currentlySelectedPersonas[0] + : currentlySelectedPersonas + ) + ).finally(() => { + setIsSaving(false); + setPopupVisible(false); // Close popover after saving + }); + }; if (!hasPermission) { return null; } + const handleDropdownChange = (visible: boolean) => { + setIsSelectOpen(visible); + }; + const handleCloseEditTeam = () => { + setPopupVisible(false); + }; return ( - // Used Button to stop click propagation event anywhere in the form to parent User.component collapsible panel -
+
} open={popupVisible} - overlayClassName="domain-select-popover" - placement="bottomRight" + overlayClassName="profile-edit-popover-card" + placement="bottomLeft" showArrow={false} + style={{ borderRadius: '12px' }} trigger="click" - onOpenChange={setPopupVisible} - {...popoverProps}> + onOpenChange={setPopupVisible}> {children ?? ( -
); const ownerLink = ( void; + hasPermission?: boolean; + ownerDisplayName?: ReactNode[]; + placeHolder?: string; + maxVisibleOwners?: number; + multiple?: { + user: boolean; + team: boolean; + }; + tooltipText?: string; + avatarSize?: number; +}) => { + const { t } = useTranslation(); + const [showAllOwners, setShowAllOwners] = useState(false); + + const ownerElements = useMemo(() => { + const hasOwners = owners && owners.length > 0; + const visibleOwners = showAllOwners + ? owners + : owners.slice(0, maxVisibleOwners); + const remainingOwnersCount = owners.length - maxVisibleOwners; + const remainingCountLabel = `+ ${remainingOwnersCount}`; + + return ( +
+ {hasOwners ? ( +
owner?.inherited)) }, + className + )}> + + {visibleOwners.map((owner) => ( + + ))} + {remainingOwnersCount > 0 && ( + + {t('label.plus-symbol')} + {remainingOwnersCount} + + )} + + {/* {visibleOwners.map((owner, index) => { + const displayName = getEntityName(owner); + const profilePicture = + owner.type === OwnerType.TEAM ? ( + + ) : ( +
+ +
+ ); + + const ownerLink = ( + + {ownerDisplayName?.[index] ?? displayName} + + ); + + const inheritedIcon = owner?.inherited ? ( + + + + ) : null; + + return ( +
+
+ {profilePicture} +
+ {ownerLink} + {inheritedIcon && ( +
{inheritedIcon}
+ )} +
+ ); + })} */} + {/* {remainingOwnersCount > 0 && ( + + )} */} +
+ ) : ( +
+
+ +
+ + {placeHolder ?? + t('label.no-entity', { entity: t('label.owner-plural') })} + +
+ )} + {onUpdate && ( + { + onUpdate(updatedUsers); + }} + /> + )} +
+ ); + }, [ + owners, + className, + onUpdate, + hasPermission, + showAllOwners, + maxVisibleOwners, + placeHolder, + t, + ownerDisplayName, + ]); + + return ownerElements; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/owner-label.less b/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/owner-label.less index 14fcaab6447b..bc42f6967463 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/owner-label.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/owner-label.less @@ -38,3 +38,11 @@ text-decoration: underline; } } +.extra-avatar { + background-color: #f9f5ff; + color: #7f56d9; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 18px; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/RichTextEditor.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/RichTextEditor.tsx index f661b74c1029..672ca4ef6cea 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/RichTextEditor.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/RichTextEditor.tsx @@ -52,7 +52,10 @@ const RichTextEditor = forwardRef( })); return ( -
+
= ({ showReadMoreBtn = true, maxLength = DESCRIPTION_MAX_PREVIEW_CHARACTERS, isDescriptionExpanded = false, - reducePreviewLineClass, }) => { const { t, i18n } = useTranslation(); const [content, setContent] = useState(''); - const [readMore, setReadMore] = useState(false); const handleReadMoreToggle = () => setReadMore((pre) => !pre); @@ -47,18 +44,6 @@ const RichTextEditorPreviewerV1: FC = ({ [enableSeeMoreVariant, markdown, maxLength] ); - /** - * if hasReadMore is true then value will be based on read more state - * else value will be content - */ - const viewerValue = useMemo(() => { - if (hasReadMore) { - return readMore ? content : `${getTrimmedContent(content, maxLength)}...`; - } - - return content; - }, [hasReadMore, readMore, maxLength, content]); - useEffect(() => { setContent(formatContent(markdown, 'client')); }, [markdown]); @@ -83,18 +68,19 @@ const RichTextEditorPreviewerV1: FC = ({ className={classNames( 'markdown-parser', textVariant, - readMore ? '' : reducePreviewLineClass + readMore ? '' : 'text-clamp-2' )} data-testid="markdown-parser"> - +
{hasReadMore && showReadMoreBtn && ( )}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/rich-text-editor-previewerV1.less b/openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/rich-text-editor-previewerV1.less index 32831c1628a8..4200cafe0451 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/rich-text-editor-previewerV1.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/RichTextEditor/rich-text-editor-previewerV1.less @@ -18,18 +18,19 @@ margin: 0px; line-height: 0px; height: 16px; + color: #175cd3 !important; } .ant-btn:focus, .ant-btn:hover { - color: @primary-color; + color: #175cd3 !important; } &.text-grey-muted { .markdown-parser { .om-block-editor { p { - color: @grey-4; + color: #535862 !important; } ul li::before { background-color: @grey-4; @@ -49,3 +50,26 @@ } } } +// .block-editor-wrapper .tiptap.ProseMirror p:first-child{ +// display: none; +// } +// .block-editor-wrapper .tiptap.ProseMirror p:last-child{ +// display: none; +// } +// .block-editor-wrapper .tiptap.ProseMirror p:has(span:empty) { +// display: none; +// } +// .block-editor-wrapper .tiptap.ProseMirror p:has(br.ProseMirror-trailingBreak) { +// display: none; +// } +// p { +// color: #535862 !important; +// } +.text-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + white-space: normal; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/SummaryTagsDescription/SummaryTagsDescription.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/SummaryTagsDescription/SummaryTagsDescription.component.tsx index 87a58c086a76..316a48520f41 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/SummaryTagsDescription/SummaryTagsDescription.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/SummaryTagsDescription/SummaryTagsDescription.component.tsx @@ -10,26 +10,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Col, Divider, Row, Typography } from 'antd'; +import { Col, Row, Typography } from 'antd'; import React from 'react'; import { useTranslation } from 'react-i18next'; import TagsViewer from '../../../components/Tag/TagsViewer/TagsViewer'; import { BasicEntityInfo } from '../../Explore/EntitySummaryPanel/SummaryList/SummaryList.interface'; -import { EntityUnion } from '../../Explore/ExplorePage.interface'; import RichTextEditorPreviewerV1 from '../RichTextEditor/RichTextEditorPreviewerV1'; +export interface EntityWithDescription { + description?: string; +} + const SummaryTagsDescription = ({ tags = [], entityDetail, }: { tags: BasicEntityInfo['tags']; - entityDetail: EntityUnion; + entityDetail: EntityWithDescription; }) => { const { t } = useTranslation(); return ( <> - + - - - + void; + customClassName?: string; } const DescriptionTask: FC = ({ @@ -37,6 +39,7 @@ const DescriptionTask: FC = ({ isTaskActionEdit, hasEditAccess, onChange, + customClassName, }) => { const { task } = taskThread; const { t } = useTranslation(); @@ -91,7 +94,9 @@ const DescriptionTask: FC = ({ }; return ( -
+
{isTaskClosed ? ( getDiffView() diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/shared/DescriptionTaskNew.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/shared/DescriptionTaskNew.tsx new file mode 100644 index 000000000000..7791c3672519 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/shared/DescriptionTaskNew.tsx @@ -0,0 +1,156 @@ +/* + * Copyright 2022 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 { Typography } from 'antd'; +import classNames from 'classnames'; +import { isEqual } from 'lodash'; +import React, { FC, Fragment } from 'react'; +import { useTranslation } from 'react-i18next'; +import RichTextEditor from '../../../components/common/RichTextEditor/RichTextEditor'; +import { + TaskType, + Thread, + ThreadTaskStatus, +} from '../../../generated/entity/feed/thread'; +import { getDescriptionDiff } from '../../../utils/TasksUtils'; +import { DescriptionTabs } from './DescriptionTabs'; +import { DiffViewNew } from './DiffViewNew'; + +interface DescriptionTaskProps { + taskThread: Thread; + isTaskActionEdit: boolean; + hasEditAccess: boolean; + onChange?: (value: string) => void; + customClassName?: string; + showDescTitle?: boolean; +} + +const DescriptionTaskNew: FC = ({ + taskThread, + isTaskActionEdit, + hasEditAccess, + onChange, + customClassName, + showDescTitle = false, +}) => { + const { task } = taskThread; + const { t } = useTranslation(); + + const isRequestDescription = isEqual(task?.type, TaskType.RequestDescription); + + const isUpdateDescription = isEqual(task?.type, TaskType.UpdateDescription); + + const isTaskClosed = isEqual(task?.status, ThreadTaskStatus.Closed); + + const getDiffView = () => { + const oldValue = task?.oldValue; + const newValue = task?.newValue; + if (!oldValue && !newValue) { + return ( +
+ + {t('label.no-entity', { entity: t('label.description') })} + +
+ ); + } else { + return ( + + ); + } + }; + + /** + * + * @returns Suggested description diff + */ + const getSuggestedDescriptionDiff = () => { + const newDescription = task?.suggestion; + const oldDescription = task?.oldValue; + + const diffs = getDescriptionDiff( + oldDescription || '', + newDescription || '' + ); + + return !newDescription && !oldDescription ? ( + + {t('label.no-entity', { entity: t('label.suggestion') })} + + ) : ( + + ); + }; + + return ( +
+ + {isTaskClosed ? ( + getDiffView() + ) : ( +
+ {isRequestDescription && ( +
+ {isTaskActionEdit && hasEditAccess ? ( + + ) : ( +
+ {getSuggestedDescriptionDiff()} +
+ )} +
+ )} + + {isUpdateDescription && ( +
+ {isTaskActionEdit && hasEditAccess ? ( + + ) : ( +
+ {getSuggestedDescriptionDiff()} +
+ )} +
+ )} +
+ )} +
+
+ ); +}; + +export default DescriptionTaskNew; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/shared/DiffViewNew.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/shared/DiffViewNew.tsx new file mode 100644 index 000000000000..885ba301dddc --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/shared/DiffViewNew.tsx @@ -0,0 +1,165 @@ +/* + * Copyright 2022 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 classNames from 'classnames'; +import { Change } from 'diff'; +import { uniqueId } from 'lodash'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Thread, + ThreadTaskStatus, +} from '../../../generated/entity/feed/thread'; + +export const DiffViewNew = ({ + diffArr, + className, + showDescTitle = false, + task, +}: { + diffArr: Change[]; + className?: string; + showDescTitle?: boolean; + task?: Thread; +}) => { + const { t } = useTranslation(); + const [expanded, setExpanded] = useState(false); + + // function stripHtml(html: string) { + // // Remove all HTML tags except + // return html.replace(/<(?!\/?strong\b)[^>]+>/g, '').trim(); + // } + function stripHtml(html: string) { + // Preserve tags and their content, removing all other HTML tags + return html + .replace(/<(?!\/?strong\b)[^>]+>/g, '') // Keep tags, remove others + .replace(/\*\*(.*?)\*\*/g, '$1') // Convert **bold** to bold + .trim(); + } + + const elements = diffArr.map((diff) => { + const diffValue = stripHtml(diff.value); + if (diff.added) { + return ( + + ); + } + if (diff.removed) { + return ( + + ); + } + + return ( + + ); + }); + + return ( +
+ {showDescTitle && ( + {t('label.description')} + )} +
+        {diffArr.length ? (
+          <>
+            
+ {elements} +
+ {!expanded && diffArr.length > 2 && ( + setExpanded(true)}> + {t('label.view-more')} + + )} + {expanded && diffArr.length > 2 && ( + setExpanded(false)}> + {t('label.view-less')} + + )} + + ) : ( + + {t('label.no-diff-available')} + + )} +
+
+ ); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/shared/TagsTask.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/shared/TagsTask.tsx index eb4877cbff5c..1ca6946df63c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/shared/TagsTask.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TasksPage/shared/TagsTask.tsx @@ -30,7 +30,7 @@ interface TagsTaskProps { isTaskActionEdit: boolean; hasEditAccess: boolean; value?: TagLabel[]; - onChange: (newTags: TagLabel[]) => void; + onChange?: (newTags: TagLabel[]) => void; } const TagsTask: FC = ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx index d83d13aac3d0..ebb990afd4c8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx @@ -26,7 +26,8 @@ import { import { useTranslation } from 'react-i18next'; import { useHistory } from 'react-router-dom'; import Loader from '../../components/common/Loader/Loader'; -import Users from '../../components/Settings/Users/Users.component'; +import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1'; +import Users from '../../components/Settings/Users/UsersNew.component'; import { ROUTES } from '../../constants/constants'; import { TabSpecificField } from '../../enums/entity.enum'; import { User } from '../../generated/entity/teams/user'; @@ -187,16 +188,18 @@ const UserPage = () => { } return ( - + + + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/app.less b/openmetadata-ui/src/main/resources/ui/src/styles/app.less index 7939f76e97d3..42ca3b9e1014 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/app.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/app.less @@ -19,7 +19,6 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background: @body-bg-color; - font-family: 'Poppins', sans-serif; font-feature-settings: normal !important; } @@ -128,7 +127,12 @@ a[href].link-text-grey, background: initial; } } - +.remove-button-default-styling:hover { + background: none !important; + box-shadow: none !important; + border: none !important; + color: inherit !important; +} // used in glossary + query right panel, to keep both consistent .right-panel-label { font-size: 14px; @@ -274,6 +278,13 @@ a[href].link-text-grey, margin-right: auto; margin-left: auto; } + +.bg-white { + background-color: @white; +} +.bg-transparent { + background-color: transparent; +} .bg-body-main { background: @body-bg-color; } @@ -287,7 +298,7 @@ a[href].link-text-grey, background-color: @primary-1; } .bg-grey { - background-color: @border-color; + background-color: #f5f5f5; } .bg-grey-1 { background-color: @grey-1; @@ -306,13 +317,6 @@ a[href].link-text-grey, background-color: @grey-1; } -.bg-white { - background-color: @white; -} -.bg-transparent { - background-color: transparent; -} - .activeCategory { border-left: 2px solid @primary-color; background: @primary-1; @@ -610,23 +614,73 @@ a[href].link-text-grey, /* Diff style */ .diff-added { - background: @success-background-color; + // background: @success-background-color; width: fit-content; color: @success-color; * { color: @success-color; } } +.diff-added-new { + font-family: Inter, sans-serif; + text-decoration: none; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + width: fit-content; + color: #027a48; +} -.diff-removed { - text-decoration: line-through; +.diff-removed-new { + font-family: Inter, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; width: fit-content; - color: @text-grey-muted; - * { - color: @text-grey-muted; - } + text-decoration: line-through; + text-decoration-thickness: 1.5px; + text-decoration-color: #f97066; + text-decoration-skip-ink: auto; + color: #f97066; +} +.diff-normal-new { + font-family: Inter, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + color: #535862; +} +.diff-normal-new strong { + font-weight: 600; + color: #000; /* Adjust color if needed */ +} +.clamp-text-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: clip; } +.clamp-text-3 { + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: clip; +} +.text-expand { + font-family: Inter, sans-serif; + color: @primary-color; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + cursor: pointer; +} .diff-description { color: @success-color; } @@ -852,3 +906,56 @@ a[href].link-text-grey, margin-right: 64px; } } +.action-required-card { + display: flex; + align-items: center; + justify-content: space-between; + border-radius: 12px; + border: 0.8px solid #dfdfdf; + padding: 16px; + background: linear-gradient( + 90deg, + rgba(85, 155, 238, 0.25) 0%, + rgba(96, 167, 251, 0.25) 19%, + rgba(245, 245, 245, 0.25) 100% + ); + box-shadow: none; + + .action-required-text { + font-size: 16px; + font-weight: 400; + color: #333; + line-height: 24px; + } + + .action-buttons { + display: flex; + gap: 8px; + margin-left: 200px; + } + + .approve-button { + background-color: #2f74eb; + border-color: #2f74eb; + border-radius: 8px; + font-weight: 500; + } + + .approve-button:hover { + background-color: #1f67e9; + border-color: #1f67e9; + } + + .reject-button { + background-color: #d93025; + border-color: #d93025; + color: white; + border-radius: 8px; + font-weight: 500; + } + + .reject-button:hover { + background-color: #b1271c; + border-color: #b1271c; + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/border.less b/openmetadata-ui/src/main/resources/ui/src/styles/border.less index c8073f6dc58a..6d5e86fd36d6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/border.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/border.less @@ -47,3 +47,7 @@ .border-danger { border: 1px solid @error-color; } + +.border-radius-card { + border-radius: @card-radius; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/fonts.less b/openmetadata-ui/src/main/resources/ui/src/styles/fonts.less index 719409e4d59c..5d443daef419 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/fonts.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/fonts.less @@ -166,3 +166,15 @@ a.ant-typography, .line-height-16 { line-height: 16px; } + +// =============================== New Design System =============================== + +.text-secondary-new { + color: #414651; +} + +.border-secondary-new { + border-color: #d5d7da; +} + +// =============================== End of New Design System =============================== diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/index.ts b/openmetadata-ui/src/main/resources/ui/src/styles/index.ts index c707587ab6ae..9ad3fd7eb7ab 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/index.ts +++ b/openmetadata-ui/src/main/resources/ui/src/styles/index.ts @@ -16,6 +16,15 @@ import '@fontsource/poppins/300.css'; // Font 300 import '@fontsource/poppins/500.css'; // Font 500 import '@fontsource/poppins/600.css'; // Font 600 import '@fontsource/source-code-pro'; // Font 400 + +import '@fontsource/inter'; // Font 400 +import '@fontsource/inter/400.css'; // Font 400 +import '@fontsource/inter/500.css'; // Font 500 +import '@fontsource/inter/600.css'; // Font 600 +import '@fontsource/inter/700.css'; // Font 700 +import '@fontsource/inter/800.css'; // Font 800 +import '@fontsource/inter/900.css'; // Font 900 + import 'react-awesome-query-builder/lib/css/styles.css'; import 'reactflow/dist/base.css'; import 'reactflow/dist/style.css'; diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/position.less b/openmetadata-ui/src/main/resources/ui/src/styles/position.less index bf41a30eb454..994238ddfdc5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/position.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/position.less @@ -124,6 +124,9 @@ .gap-4 { gap: 16px; } +.gap-5 { + gap: 20px; +} .gap-6 { gap: 24px; } 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 108a1cdef543..70b864c50d6a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less @@ -396,6 +396,18 @@ .p-lg { padding: @padding-lg; } + +.p-box { + padding: 20px; +} +.p-x-box { + padding-right: 20px; + padding-left: 20px; +} +.p-y-box { + padding-top: 20px; + padding-bottom: 20px; +} .p-x-xss { padding-right: @padding-xss; padding-left: @padding-xss; diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/variables.less b/openmetadata-ui/src/main/resources/ui/src/styles/variables.less index e1f685b0f902..d6e37172f283 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/variables.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/variables.less @@ -70,11 +70,13 @@ @grey-5: #fbfbfb; @grey-6: #f9f9f9; @grey-7: #9ca3af; +@grey-8: #535862; @text-grey-muted: @grey-4; -@font-size-base: 14px; +@font-size-base: 16px; @box-shadow-base: 0px 2px 10px rgba(0, 0, 0, 0.12); @white: #fff; @border-radius-base: 4px; +@border-radius-xs: 8px; @checkbox-size: 14px; @switch-height: 16px; @switch-sm-height: 12px; @@ -111,6 +113,9 @@ @om-navbar-height: ~'var(--ant-navbar-height)'; @sidebar-width: 60px; +// Font +@font-family: 'Inter', 'Poppins', sans-serif; + // Sizing @page-height: calc(100vh - @om-navbar-height); @left-side-panel-width: 230px; @@ -136,6 +141,10 @@ @explore-page-height: calc(100vh - @om-navbar-height - 49px); @welcome-page-height: calc(100vh - 112px); +@profile-page-sidebar: calc(100vh - @om-navbar-height - 40px); +@profile-entity-details-tab-height: calc(@profile-page-sidebar - 64px); +@user-profile-page-panel-height: calc(100vh - @om-navbar-height - 108px); + // 48px - navbar height @welcome-page-height: calc(100vh - 48px - @om-navbar-height); @@ -176,3 +185,5 @@ @success-bg-color: rgb(from @success-color r g b / 0.1); @warning-bg-color: rgb(from @warning-color r g b / 0.1); @info-bg-color: rgb(from @info-color r g b / 0.1); + +@card-radius: 12px; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx index a5b9e262b7e1..3789f73324f5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx @@ -171,11 +171,12 @@ export const renderDomainLink = ( ) => ( {isUndefined(domainDisplayName) ? getEntityName(domain) : domainDisplayName} diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntitySummaryPanelUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntitySummaryPanelUtils.tsx index 37b8674f0c32..e626e58c600a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntitySummaryPanelUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntitySummaryPanelUtils.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-case-declarations */ /* * Copyright 2023 Collate. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -11,30 +12,54 @@ * limitations under the License. */ -import Icon from '@ant-design/icons/lib/components/Icon'; -import { Typography } from 'antd'; +import Icon from '@ant-design/icons'; +import { Col, Row, Typography } from 'antd'; import { get, isEmpty, isUndefined } from 'lodash'; import React from 'react'; import { Link } from 'react-router-dom'; import { SearchedDataProps } from '../../src/components/SearchedData/SearchedData.interface'; import { ReactComponent as IconExternalLink } from '../assets/svg/external-links.svg'; +import SchemaEditor from '../components/Database/SchemaEditor/SchemaEditor'; +import APIEndpointSummary from '../components/Explore/EntitySummaryPanel/APIEndpointSummary/APIEndpointSummary'; +import DataProductSummary from '../components/Explore/EntitySummaryPanel/DataProductSummary/DataProductSummary.component'; +import GlossaryTermSummary from '../components/Explore/EntitySummaryPanel/GlossaryTermSummary/GlossaryTermSummary.component'; +import SummaryList from '../components/Explore/EntitySummaryPanel/SummaryList/SummaryList.component'; import { BasicEntityInfo, HighlightedTagLabel, } from '../components/Explore/EntitySummaryPanel/SummaryList/SummaryList.interface'; +import TagsSummary from '../components/Explore/EntitySummaryPanel/TagsSummary/TagsSummary.component'; +import MetricExpression from '../components/Metric/MetricExpression/MetricExpression'; +import RelatedMetrics from '../components/Metric/RelatedMetrics/RelatedMetrics'; import { ICON_DIMENSION, NO_DATA_PLACEHOLDER } from '../constants/constants'; import { SummaryListHighlightKeys } from '../constants/EntitySummaryPanelUtils.constant'; +import { CSMode } from '../enums/codemirror.enum'; import { EntityType } from '../enums/entity.enum'; import { SummaryEntityType } from '../enums/EntitySummary.enum'; +import { Tag } from '../generated/entity/classification/tag'; +import { APIEndpoint } from '../generated/entity/data/apiEndpoint'; import { Chart } from '../generated/entity/data/chart'; -import { TagLabel } from '../generated/entity/data/container'; -import { MlFeature } from '../generated/entity/data/mlmodel'; -import { Task } from '../generated/entity/data/pipeline'; -import { Column, TableConstraint } from '../generated/entity/data/table'; -import { Field } from '../generated/entity/data/topic'; +import { Container, TagLabel } from '../generated/entity/data/container'; +import { Dashboard } from '../generated/entity/data/dashboard'; +import { DashboardDataModel } from '../generated/entity/data/dashboardDataModel'; +import { Database } from '../generated/entity/data/database'; +import { GlossaryTerm } from '../generated/entity/data/glossaryTerm'; +import { Metric } from '../generated/entity/data/metric'; +import { MlFeature, Mlmodel } from '../generated/entity/data/mlmodel'; +import { Pipeline, Task } from '../generated/entity/data/pipeline'; +import { SearchIndex } from '../generated/entity/data/searchIndex'; +import { + StoredProcedure, + StoredProcedureCodeObject, +} from '../generated/entity/data/storedProcedure'; +import { Column, Table, TableConstraint } from '../generated/entity/data/table'; +import { Field, Topic } from '../generated/entity/data/topic'; +import { DataProduct } from '../generated/entity/domains/dataProduct'; import { EntityReference } from '../generated/tests/testCase'; import entityUtilClassBase from './EntityUtilClassBase'; import { getEntityName } from './EntityUtils'; +import i18n from './i18next/LocalUtil'; +import searchClassBase from './SearchClassBase'; import { stringToHTML } from './StringsUtils'; const { Text } = Typography; @@ -350,3 +375,281 @@ export const getFormattedEntityData = ( return []; }; + +export const getEntityChildDetails = ( + entityType: EntityType, + entityInfo: SearchedDataProps['data'][number]['_source'], + highlights?: SearchedDataProps['data'][number]['highlight'] +) => { + let childComponent; + let heading; + switch (entityType) { + case EntityType.TABLE: + heading = i18n.t('label.schema'); + childComponent = ( + + ); + + break; + case EntityType.TOPIC: + heading = i18n.t('label.schema'); + childComponent = isEmpty( + (entityInfo as Topic).messageSchema?.schemaFields + ) ? ( + + + {i18n.t('message.no-data-available')} + + + ) : ( + + ); + + break; + case EntityType.PIPELINE: + heading = i18n.t('label.task-plural'); + childComponent = ( + + ); + + break; + case EntityType.DASHBOARD: + const formattedChartsData: BasicEntityInfo[] = getFormattedEntityData( + SummaryEntityType.CHART, + (entityInfo as Dashboard).charts, + highlights + ); + + const formattedDataModelData: BasicEntityInfo[] = getFormattedEntityData( + SummaryEntityType.COLUMN, + (entityInfo as Dashboard).dataModels, + highlights + ); + + return ( + <> + + + + {i18n.t('label.chart-plural')} + + + + + + + + + + + {i18n.t('label.data-model-plural')} + + + + + + + + ); + + case EntityType.MLMODEL: + heading = i18n.t('label.feature-plural'); + childComponent = ( + + ); + + break; + + case EntityType.CONTAINER: + heading = i18n.t('label.schema'); + childComponent = ( + + ); + + break; + + case EntityType.DASHBOARD_DATA_MODEL: + heading = i18n.t('label.column-plural'); + childComponent = ( + + ); + + break; + case EntityType.STORED_PROCEDURE: + heading = i18n.t('label.code'); + childComponent = ( + + ); + + break; + case EntityType.SEARCH_INDEX: + heading = i18n.t('label.field-plural'); + childComponent = ( + + ); + + break; + case EntityType.API_ENDPOINT: + return ( + + ); + + case EntityType.METRIC: + heading = ; + childComponent = ( + + ); + + break; + case EntityType.DATABASE: + heading = i18n.t('label.schema'); + childComponent = ( + + ); + + break; + case EntityType.CHART: + heading = i18n.t('label.dashboard-plural'); + childComponent = ( + + ); + + break; + case EntityType.DATA_PRODUCT: + return ( + + ); + case EntityType.API_SERVICE: + return ( + + ); + case EntityType.GLOSSARY_TERM: + case EntityType.GLOSSARY: + return ( + + ); + case EntityType.TAG: + return ( + + ); + + case EntityType.DATABASE_SERVICE: + case EntityType.MESSAGING_SERVICE: + case EntityType.DASHBOARD_SERVICE: + case EntityType.PIPELINE_SERVICE: + case EntityType.MLMODEL_SERVICE: + case EntityType.SEARCH_SERVICE: + case EntityType.STORAGE_SERVICE: + case EntityType.API_COLLECTION: + case EntityType.DATABASE_SCHEMA: + return null; + default: + return searchClassBase.getEntitySummaryComponent(entityInfo); + } + + return ( + + + + {heading} + + + {childComponent} + + ); +}; 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 e73b0652fb6e..f748fd26f04e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx @@ -29,11 +29,11 @@ import { OwnerLabel } from '../components/common/OwnerLabel/OwnerLabel.component import QueryCount from '../components/common/QueryCount/QueryCount.component'; import { TitleLink } from '../components/common/TitleBreadcrumb/TitleBreadcrumb.interface'; import { DataAssetsWithoutServiceField } from '../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; +import { DataAssetSummaryPanelProps } from '../components/DataAssetSummaryPanel/DataAssetSummaryPanel.interface'; import { TableProfilerTab } from '../components/Database/Profiler/ProfilerDashboard/profilerDashboard.interface'; import { QueryVoteType } from '../components/Database/TableQueries/TableQueries.interface'; import { EntityServiceUnion, - EntityUnion, EntityWithServices, } from '../components/Explore/ExplorePage.interface'; import { @@ -424,7 +424,7 @@ const getTopicOverview = (topicDetails: Topic) => { } = topicDetails; const overview: BasicEntityOverviewInfo[] = [ - ...getCommonOverview({ domain }, false), + ...getCommonOverview({ domain, owners: topicDetails.owners }), { name: i18next.t('label.partition-plural'), value: partitions ?? NO_DATA, @@ -1167,65 +1167,80 @@ const getMetricOverview = (metric: Metric) => { export const getEntityOverview = ( type: string, - entityDetail: EntityUnion, + entityDetail: DataAssetSummaryPanelProps['dataAsset'], additionalInfo?: Record ): Array => { switch (type) { - case ExplorePageTabs.TABLES: { + case ExplorePageTabs.TABLES: + case EntityType.TABLE: { return getTableOverview(entityDetail as Table, additionalInfo); } - case ExplorePageTabs.TOPICS: { + case ExplorePageTabs.TOPICS: + case EntityType.TOPIC: { return getTopicOverview(entityDetail as Topic); } - case ExplorePageTabs.PIPELINES: { + case ExplorePageTabs.PIPELINES: + case EntityType.PIPELINE: { return getPipelineOverview(entityDetail as Pipeline); } - case ExplorePageTabs.DASHBOARDS: { + case ExplorePageTabs.DASHBOARDS: + case EntityType.DASHBOARD: { return getDashboardOverview(entityDetail as Dashboard); } - case ExplorePageTabs.SEARCH_INDEX: { + case ExplorePageTabs.SEARCH_INDEX: + case EntityType.SEARCH_INDEX: { return getSearchIndexOverview(entityDetail as SearchIndexEntity); } - case ExplorePageTabs.MLMODELS: { + case ExplorePageTabs.MLMODELS: + case EntityType.MLMODEL: { return getMlModelOverview(entityDetail as Mlmodel); } - case ExplorePageTabs.CONTAINERS: { + case ExplorePageTabs.CONTAINERS: + case EntityType.CONTAINER: { return getContainerOverview(entityDetail as Container); } - case ExplorePageTabs.CHARTS: { + case ExplorePageTabs.CHARTS: + case EntityType.CHART: { return getChartOverview(entityDetail as Chart); } - case ExplorePageTabs.DASHBOARD_DATA_MODEL: { + case ExplorePageTabs.DASHBOARD_DATA_MODEL: + case EntityType.DASHBOARD_DATA_MODEL: { return getDataModelOverview(entityDetail as DashboardDataModel); } - case ExplorePageTabs.STORED_PROCEDURE: { + case ExplorePageTabs.STORED_PROCEDURE: + case EntityType.STORED_PROCEDURE: { return getStoredProcedureOverview(entityDetail as StoredProcedure); } - case ExplorePageTabs.DATABASE: { + case ExplorePageTabs.DATABASE: + case EntityType.DATABASE: { return getDatabaseOverview(entityDetail as Database); } - case ExplorePageTabs.DATABASE_SCHEMA: { + case ExplorePageTabs.DATABASE_SCHEMA: + case EntityType.DATABASE_SCHEMA: { return getDatabaseSchemaOverview(entityDetail as DatabaseSchema); } - case ExplorePageTabs.API_COLLECTION: { + case ExplorePageTabs.API_COLLECTION: + case EntityType.API_COLLECTION: { return getApiCollectionOverview(entityDetail as APICollection); } - case ExplorePageTabs.API_ENDPOINT: { + case ExplorePageTabs.API_ENDPOINT: + case EntityType.API_ENDPOINT: { return getApiEndpointOverview(entityDetail as APIEndpoint); } - case ExplorePageTabs.METRIC: { + case ExplorePageTabs.METRIC: + case EntityType.METRIC: { return getMetricOverview(entityDetail as Metric); } @@ -1235,7 +1250,14 @@ export const getEntityOverview = ( case ExplorePageTabs.ML_MODEL_SERVICE: case ExplorePageTabs.PIPELINE_SERVICE: case ExplorePageTabs.SEARCH_INDEX_SERVICE: - case ExplorePageTabs.API_SERVICE: { + case ExplorePageTabs.API_SERVICE: + case EntityType.DATABASE_SERVICE: + case EntityType.MESSAGING_SERVICE: + case EntityType.DASHBOARD_SERVICE: + case EntityType.MLMODEL_SERVICE: + case EntityType.PIPELINE_SERVICE: + case EntityType.SEARCH_SERVICE: + case EntityType.API_SERVICE: { return getEntityServiceOverview(entityDetail as EntityServiceUnion); } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx index 9de0ff0f54a3..cfcc5316ee0f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx @@ -304,7 +304,7 @@ export const getEntityDetail = (item: string) => { }; const getEntityLinkList = (message: string) => { - return message.match(entityLinkRegEx); + return message?.match(entityLinkRegEx); }; const getEntityLinkDetail = (item: string) => { @@ -778,7 +778,12 @@ export const getFeedHeaderTextFromCardStyle = ( return ( } + renderElement={ + + } values={{ field: i18next.t( `label.${cardStyle === CardStyle.Tags ? 'tag-plural' : cardStyle}` diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts index e9badb8155ec..b4af1d88850d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts @@ -16,8 +16,9 @@ import i18Next from 'i18next'; import { isEmpty, isEqual, isUndefined } from 'lodash'; import React from 'react'; import { ReactComponent as CancelColored } from '../assets/svg/cancel-colored.svg'; -import { ReactComponent as EditColored } from '../assets/svg/edit-colored.svg'; -import { ReactComponent as SuccessColored } from '../assets/svg/success-colored.svg'; +import { ReactComponent as CloseIcon } from '../assets/svg/close-circle.svg'; +import { ReactComponent as EditSuggestionIcon } from '../assets/svg/edit-suggestion.svg'; +import { ReactComponent as CheckIcon } from '../assets/svg/tick-circle.svg'; import { ActivityFeedTabs } from '../components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface'; import { FQN_SEPARATOR_CHAR } from '../constants/char.constants'; import { @@ -695,26 +696,30 @@ export const TASK_ACTION_LIST: TaskAction[] = [ { label: i18Next.t('label.accept-suggestion'), key: TaskActionMode.VIEW, - icon: SuccessColored, + icon: CheckIcon, }, { - label: i18Next.t('label.edit-amp-accept-suggestion'), + label: i18Next.t('label.edit-suggestion'), key: TaskActionMode.EDIT, - icon: EditColored, + icon: EditSuggestionIcon, + }, + { + label: i18Next.t('label.close'), + key: TaskActionMode.CLOSE, + icon: CloseIcon, }, - ...TASK_ACTION_COMMON_ITEM, ]; export const GLOSSARY_TASK_ACTION_LIST: TaskAction[] = [ { label: i18Next.t('label.approve'), key: TaskActionMode.RESOLVE, - icon: SuccessColored, + icon: CheckIcon, }, { label: i18Next.t('label.reject'), key: TaskActionMode.CLOSE, - icon: CancelColored, + icon: CloseIcon, }, ]; @@ -722,10 +727,12 @@ export const INCIDENT_TASK_ACTION_LIST: TaskAction[] = [ { label: i18Next.t('label.re-assign'), key: TaskActionMode.RE_ASSIGN, + icon: EditSuggestionIcon, }, { label: i18Next.t('label.resolve'), key: TaskActionMode.RESOLVE, + icon: CloseIcon, }, ]; diff --git a/openmetadata-ui/src/main/resources/ui/yarn.lock b/openmetadata-ui/src/main/resources/ui/yarn.lock index c97f2b9b7609..e46138e6e1ae 100644 --- a/openmetadata-ui/src/main/resources/ui/yarn.lock +++ b/openmetadata-ui/src/main/resources/ui/yarn.lock @@ -2451,6 +2451,11 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== +"@fontsource/inter@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-5.1.1.tgz#401803b6ac4c877f5be94088aa89147ed5a2bd85" + integrity sha512-weN3E+rq0Xb3Z93VHJ+Rc7WOQX9ETJPTAJ+gDcaMHtjft67L58sfS65rAjC5tZUXQ2FdZ/V1/sSzCwZ6v05kJw== + "@fontsource/poppins@^5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@fontsource/poppins/-/poppins-5.0.0.tgz#5023921cfd2a167f64060f8029e8f99ce68c32f9"