From 1f0430643e4d09301191763b51a657d28540c166 Mon Sep 17 00:00:00 2001 From: Sachin Chaurasiya Date: Sat, 5 Mar 2022 02:07:53 +0530 Subject: [PATCH] Fix #3046 UI: Add support for request description (#3074) --- .../ui/src/assets/img/feedEditor.png | Bin 0 -> 98862 bytes .../resources/ui/src/assets/svg/add-chat.svg | 3 + .../resources/ui/src/assets/svg/comment.svg | 4 + .../ui/src/assets/svg/paper-plane-primary.svg | 3 + .../ui/src/assets/svg/paper-plane.svg | 2 +- .../ui/src/assets/svg/request-icon.svg | 6 + .../resources/ui/src/axiosAPIs/feedsAPI.ts | 4 +- .../ActivityFeedEditor/ActivityFeedEditor.tsx | 20 ++- .../ActivityFeedList/ActivityFeedList.tsx | 65 ++++---- .../ActivityFeedPanel/ActivityFeedPanel.tsx | 7 +- .../ActivityThreadPanel.tsx | 35 ++-- .../NoFeedPlaceholder/NoFeedPlaceholder.tsx | 50 ++++++ .../DashboardDetails.component.tsx | 52 ++++-- .../DashboardDetails.interface.ts | 1 + .../DashboardDetails.test.tsx | 1 + .../DatasetDetails.component.tsx | 31 ++++ .../DatasetDetails/DatasetDetails.test.tsx | 5 +- .../EntityTable/EntityTable.component.tsx | 103 ++++++++---- .../src/components/FeedEditor/FeedEditor.tsx | 7 +- .../RequestDescriptionModal.tsx | 87 ++++++++++ .../ui/src/components/MyData/MyData.test.tsx | 2 +- .../PipelineDetails.component.tsx | 154 +++++++++++++----- .../PipelineDetails.interface.ts | 1 + .../SchemaTab/SchemaTab.component.tsx | 6 + .../TopicDetails/TopicDetails.component.tsx | 53 ++++-- .../TopicDetails/TopicDetails.interface.ts | 1 + .../common/description/Description.tsx | 69 ++++++-- .../common/entityPageInfo/EntityPageInfo.tsx | 31 +++- .../ui/src/constants/feed.constants.ts | 8 + .../DashboardDetailsPage.component.tsx | 25 +-- .../DatasetDetailsPage.component.tsx | 5 +- .../PipelineDetailsPage.component.tsx | 27 +-- .../TopicDetailsPage.component.tsx | 26 +-- .../ui/src/pages/database-details/index.tsx | 97 ++++++++++- .../resources/ui/src/pages/service/index.tsx | 104 +++++++++++- .../pages/tour-page/TourPage.component.tsx | 2 +- .../resources/ui/src/utils/EntityUtils.tsx | 5 +- .../ui/src/utils/EntityVersionUtils.tsx | 11 +- .../ui/src/utils/FeedElementUtils.tsx | 52 +++++- .../main/resources/ui/src/utils/FeedUtils.ts | 46 +++++- .../resources/ui/src/utils/MarkdownUtils.tsx | 7 +- .../main/resources/ui/src/utils/SvgUtils.tsx | 24 +++ 42 files changed, 1005 insertions(+), 237 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/img/feedEditor.png create mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/add-chat.svg create mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/comment.svg create mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/paper-plane-primary.svg create mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/request-icon.svg create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/NoFeedPlaceholder/NoFeedPlaceholder.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Modals/RequestDescriptionModal/RequestDescriptionModal.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/img/feedEditor.png b/openmetadata-ui/src/main/resources/ui/src/assets/img/feedEditor.png new file mode 100644 index 0000000000000000000000000000000000000000..66db5801ecbef45d3fbd25768b4d07d5ebdf9e09 GIT binary patch literal 98862 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 literal 0 HcmV?d00001 diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/add-chat.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/add-chat.svg new file mode 100644 index 00000000000..c1e4505a68c --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/add-chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/comment.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/comment.svg new file mode 100644 index 00000000000..2a8ba9b742b --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/comment.svg @@ -0,0 +1,4 @@ + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/paper-plane-primary.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/paper-plane-primary.svg new file mode 100644 index 00000000000..3894ec6718b --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/paper-plane-primary.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/paper-plane.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/paper-plane.svg index 679e15590c9..0fa829bc13c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/paper-plane.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/paper-plane.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/request-icon.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/request-icon.svg new file mode 100644 index 00000000000..a216d951fcf --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/request-icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/feedsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/feedsAPI.ts index ee8ed3be3af..04e68ef08af 100644 --- a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/feedsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/feedsAPI.ts @@ -12,11 +12,11 @@ */ import { AxiosResponse } from 'axios'; +import { isUndefined } from 'lodash'; import { Post } from 'Models'; +import { FeedFilter } from '../enums/mydata.enum'; import { CreateThread } from '../generated/api/feed/createThread'; import APIClient from './index'; -import { FeedFilter } from '../enums/mydata.enum'; -import { isUndefined } from 'lodash'; export const getAllFeeds: Function = ( entityLink?: string diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditor.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditor.tsx index a8ea49f4f93..03ac74a757b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditor.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditor.tsx @@ -14,7 +14,7 @@ import classNames from 'classnames'; import React, { FC, HTMLAttributes, useRef, useState } from 'react'; import { getBackendFormat, HTMLToMarkdown } from '../../../utils/FeedUtils'; -import SVGIcons from '../../../utils/SvgUtils'; +import SVGIcons, { Icons } from '../../../utils/SvgUtils'; import { Button } from '../../buttons/Button/Button'; import PopOver from '../../common/popover/PopOver'; import FeedEditor from '../../FeedEditor/FeedEditor'; @@ -23,6 +23,7 @@ interface ActivityFeedEditorProp extends HTMLAttributes { onSave?: (value: string) => void; buttonClass?: string; placeHolder?: string; + defaultValue?: string; } type EditorContentRef = { getEditorValue: () => string; @@ -34,6 +35,7 @@ const ActivityFeedEditor: FC = ({ buttonClass = '', onSave, placeHolder, + defaultValue, }) => { const editorRef = useRef(); const [editorValue, setEditorValue] = useState(''); @@ -54,6 +56,7 @@ const ActivityFeedEditor: FC = ({ return (

diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedList/ActivityFeedList.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedList/ActivityFeedList.tsx index fb3fa6e3721..77532fd469e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedList/ActivityFeedList.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedList/ActivityFeedList.tsx @@ -28,11 +28,13 @@ import ActivityFeedCard, { } from '../ActivityFeedCard/ActivityFeedCard'; import ActivityFeedEditor from '../ActivityFeedEditor/ActivityFeedEditor'; import ActivityFeedPanel from '../ActivityFeedPanel/ActivityFeedPanel'; +import NoFeedPlaceholder from '../NoFeedPlaceholder/NoFeedPlaceholder'; interface ActivityFeedListProp extends HTMLAttributes { feedList: EntityThread[]; withSidePanel?: boolean; isEntityFeed?: boolean; + entityName?: string; postFeedHandler?: (value: string, id: string) => void; } interface FeedListSeparatorProp extends HTMLAttributes { @@ -61,7 +63,7 @@ export const FeedListSeparator: FC = ({

{relativeDay ? ( - + {relativeDay} ) : null} @@ -91,8 +93,8 @@ const FeedListBody: FC = ({ from: feed.createdBy, }; const postLength = feed.posts.length; - const replies = feed.postsCount; - const repliedUsers = feed.posts.map((f) => f.from); + const replies = feed.postsCount - 1; + const repliedUsers = feed.posts.map((f) => f.from).slice(1, 3); const lastPost = feed.posts[postLength - 1]; return ( @@ -105,15 +107,11 @@ const FeedListBody: FC = ({ /> {postLength > 0 ? ( -
= ({ onViewMore(); }} /> - - | - -

onThreadIdSelect(feed.id)}> - Reply -

- {selctedThreadId === feed.id ? ( - - ) : null}
+ +

{ + onThreadIdSelect(feed.id); + }}> + Reply +

+ {selctedThreadId === feed.id ? ( + + ) : null}
) : (

= ({ withSidePanel = false, isEntityFeed = false, postFeedHandler, + entityName, }) => { const { updatedFeedList, relativeDays } = getFeedListWithRelativeDays(feedList); @@ -256,11 +259,17 @@ const ActivityFeedList: FC = ({ ) : ( - - <>No conversations found. Try changing the filter. + {entityName ? ( + + ) : ( + + + <>No conversations found. Try changing the filter. + + )} )}

diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/ActivityFeedPanel.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/ActivityFeedPanel.tsx index f4d42db2fdb..53a37cbdf57 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/ActivityFeedPanel.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedPanel/ActivityFeedPanel.tsx @@ -38,6 +38,7 @@ interface FeedPanelHeaderProp extends HTMLAttributes, Pick { entityField: string; + noun?: string; } interface FeedPanelOverlayProp extends HTMLAttributes, @@ -51,12 +52,14 @@ export const FeedPanelHeader: FC = ({ onCancel, entityField, className, + noun, }) => { return (

- Thread on {entityField} + {noun ? noun : 'Conversation'} on{' '} + {entityField}

= ({ {repliesLength > 0 ? (
- {getReplyText(repliesLength)} + {getReplyText(repliesLength, 'reply', 'replies')}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel.tsx index f4d6e5fc51e..1b668e6607f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel.tsx @@ -89,8 +89,10 @@ const ActivityThreadList: FC = ({ from: thread.createdBy, }; const postLength = thread.posts.length; - const replies = thread.postsCount; - const repliedUsers = thread.posts.map((f) => f.from); + const replies = thread.postsCount - 1; + const repliedUsers = thread.posts + .map((f) => f.from) + .slice(1, 3); const lastPost = thread.posts[postLength - 1]; return ( @@ -103,31 +105,31 @@ const ActivityThreadList: FC = ({ /> {postLength > 0 ? ( -
onThreadSelect(thread.id)} /> - - | - -

onThreadIdSelect(thread.id)}> - Reply -

+ +

{ + onThreadIdSelect(thread.id); + }}> + Reply +

) : (

= ({ {repliesLength > 0 ? (

- {getReplyText(repliesLength)} + {getReplyText(repliesLength, 'reply', 'replies')}
@@ -304,6 +306,7 @@ const ActivityThreadPanel: FC = ({ {!isUndefined(selectedThread) ? ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/NoFeedPlaceholder/NoFeedPlaceholder.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/NoFeedPlaceholder/NoFeedPlaceholder.tsx new file mode 100644 index 00000000000..da5aa6cf318 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/NoFeedPlaceholder/NoFeedPlaceholder.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2021 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, { FC, HTMLAttributes } from 'react'; +import EditorImg from '../../../assets/img/feedEditor.png'; +import SVGIcons, { Icons } from '../../../utils/SvgUtils'; + +interface NoFeedPlaceholderProp extends HTMLAttributes { + entityName: string; +} + +const NoFeedPlaceholder: FC = ({ + className, + entityName, +}) => { + return ( +
+ {`There is no activity on the "${entityName}" yet. Start a conversation by clicking + on the `} + + + + {` to collaborate with other users.`} + +
+ editor-image +
+
+ ); +}; + +export default NoFeedPlaceholder; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx index 27605d25ace..7f7b00d203b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx @@ -17,9 +17,10 @@ import { EntityTags } from 'Models'; import React, { useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; import { getTeamDetailsPath } from '../../constants/constants'; +import { EntityType } from '../../enums/entity.enum'; import { Dashboard } from '../../generated/entity/data/dashboard'; import { Operation } from '../../generated/entity/policies/accessControl/rule'; -import { User } from '../../generated/entity/teams/user'; +import { EntityReference, User } from '../../generated/entity/teams/user'; import { LabelType, State, TagLabel } from '../../generated/type/tagLabel'; import { useAuth } from '../../hooks/authHooks'; import { @@ -28,6 +29,8 @@ import { getUserTeams, isEven, } from '../../utils/CommonUtils'; +import { getEntityFeedLink } from '../../utils/EntityUtils'; +import { getDefaultValue } from '../../utils/FeedElementUtils'; import { getEntityFieldThreadCounts } from '../../utils/FeedUtils'; import SVGIcons from '../../utils/SvgUtils'; import { getTagsWithoutTier } from '../../utils/TableUtils'; @@ -43,6 +46,7 @@ import PageContainer from '../containers/PageContainer'; import Entitylineage from '../EntityLineage/EntityLineage.component'; import ManageTabComponent from '../ManageTab/ManageTab.component'; import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; +import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal'; import TagsContainer from '../tags-container/tags-container'; import Tags from '../tags/tags'; import { ChartType, DashboardDetailsProps } from './DashboardDetails.interface'; @@ -86,6 +90,7 @@ const DashboardDetails = ({ feedCount, entityFieldThreadCount, createThread, + dashboardFQN, }: DashboardDetailsProps) => { const { isAuthDisabled } = useAuth(); const [isEdit, setIsEdit] = useState(false); @@ -102,6 +107,14 @@ const DashboardDetails = ({ const [tagList, setTagList] = useState>([]); const [isTagLoading, setIsTagLoading] = useState(false); const [threadLink, setThreadLink] = useState(''); + const [selectedField, setSelectedField] = useState(''); + + const onEntityFieldSelect = (value: string) => { + setSelectedField(value); + }; + const closeRequestModal = () => { + setSelectedField(''); + }; const hasEditAccess = () => { if (owner?.type === 'user') { return owner.id === getCurrentUserId(); @@ -365,7 +378,9 @@ const DashboardDetails = ({ 'tags', entityFieldThreadCount )} + entityFqn={dashboardFQN} entityName={entityName} + entityType={EntityType.DASHBOARD} extraInfo={extraInfo} followHandler={followDashboard} followers={followersCount} @@ -400,7 +415,9 @@ const DashboardDetails = ({ 'description', entityFieldThreadCount )} + entityFqn={dashboardFQN} entityName={entityName} + entityType={EntityType.DASHBOARD} hasEditAccess={hasEditAccess()} isEdit={isEdit} isReadOnly={deleted} @@ -408,6 +425,7 @@ const DashboardDetails = ({ onCancel={onCancel} onDescriptionEdit={onDescriptionEdit} onDescriptionUpdate={onDescriptionUpdate} + onEntityFieldSelect={onEntityFieldSelect} onThreadLinkSelect={onThreadLinkSelect} />
@@ -566,15 +584,6 @@ const DashboardDetails = ({
- {threadLink ? ( - - ) : null} )} {activeTab === 2 && ( @@ -586,6 +595,7 @@ const DashboardDetails = ({ isEntityFeed withSidePanel className="" + entityName={entityName} feedList={entityThread} isLoading={isentityThreadLoading} postFeedHandler={postFeedHandler} @@ -632,6 +642,28 @@ const DashboardDetails = ({ onSave={onChartUpdate} /> )} + {threadLink ? ( + + ) : null} + {selectedField ? ( + + ) : null} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.interface.ts index e77c8ad75f7..70d430fe55e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.interface.ts @@ -36,6 +36,7 @@ export interface ChartType extends Chart { } export interface DashboardDetailsProps { + dashboardFQN: string; version: string; isNodeLoading: LoadingNodeState; lineageLeafNodes: LeafNodes; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.test.tsx index e2f852580dc..d1676d1e2a3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.test.tsx @@ -77,6 +77,7 @@ const DashboardDetailsProps = { feedCount: 0, entityFieldThreadCount: [], createThread: jest.fn(), + dashboardFQN: '', }; jest.mock('../ManageTab/ManageTab.component', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx index 9c3ee4533bc..985767307c6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx @@ -17,6 +17,7 @@ import { ColumnJoins, EntityTags, ExtraInfo } from 'Models'; import React, { useEffect, useState } from 'react'; import { getTeamDetailsPath, ROUTES } from '../../constants/constants'; import { CSMode } from '../../enums/codemirror.enum'; +import { EntityType } from '../../enums/entity.enum'; import { JoinedWith, Table, @@ -32,6 +33,8 @@ import { getTableFQNFromColumnFQN, getUserTeams, } from '../../utils/CommonUtils'; +import { getEntityFeedLink } from '../../utils/EntityUtils'; +import { getDefaultValue } from '../../utils/FeedElementUtils'; import { getEntityFieldThreadCounts } from '../../utils/FeedUtils'; import { getTagsWithoutTier, getUsagePercentile } from '../../utils/TableUtils'; import ActivityFeedList from '../ActivityFeed/ActivityFeedList/ActivityFeedList'; @@ -43,6 +46,7 @@ import PageContainer from '../containers/PageContainer'; import Entitylineage from '../EntityLineage/EntityLineage.component'; import FrequentlyJoinedTables from '../FrequentlyJoinedTables/FrequentlyJoinedTables.component'; import ManageTab from '../ManageTab/ManageTab.component'; +import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal'; import SampleDataTable, { SampleColumns, } from '../SampleDataTable/SampleDataTable.component'; @@ -111,6 +115,14 @@ const DatasetDetails: React.FC = ({ }); const [threadLink, setThreadLink] = useState(''); + const [selectedField, setSelectedField] = useState(''); + + const onEntityFieldSelect = (value: string) => { + setSelectedField(value); + }; + const closeRequestModal = () => { + setSelectedField(''); + }; const setUsageDetails = ( usageSummary: TypeUsedToReturnUsageDetailsOfAnEntity @@ -476,7 +488,9 @@ const DatasetDetails: React.FC = ({ 'description', entityFieldThreadCount )} + entityFqn={datasetFQN} entityName={entityName} + entityType={EntityType.TABLE} hasEditAccess={hasEditAccess()} isEdit={isEdit} isReadOnly={deleted} @@ -484,6 +498,7 @@ const DatasetDetails: React.FC = ({ onCancel={onCancel} onDescriptionEdit={onDescriptionEdit} onDescriptionUpdate={onDescriptionUpdate} + onEntityFieldSelect={onEntityFieldSelect} onThreadLinkSelect={onThreadLinkSelect} />
@@ -505,11 +520,13 @@ const DatasetDetails: React.FC = ({ 'columns', entityFieldThreadCount )} + entityFqn={datasetFQN} hasEditAccess={hasEditAccess()} isReadOnly={deleted} joins={tableJoinData.columnJoins as ColumnJoins[]} owner={owner} sampleData={sampleData} + onEntityFieldSelect={onEntityFieldSelect} onThreadLinkSelect={onThreadLinkSelect} onUpdate={onColumnsUpdate} /> @@ -524,6 +541,19 @@ const DatasetDetails: React.FC = ({ onCancel={onThreadPanelClose} /> ) : null} + {selectedField ? ( + + ) : null} )} {activeTab === 2 && ( @@ -535,6 +565,7 @@ const DatasetDetails: React.FC = ({ isEntityFeed withSidePanel className="" + entityName={entityName} feedList={entityThread} isLoading={isentityThreadLoading} postFeedHandler={postFeedHandler} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx index bdfab01542c..7e9397e3616 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx @@ -118,7 +118,10 @@ jest.mock('../ActivityFeed/ActivityFeedList/ActivityFeedList.tsx', () => { }); jest.mock('../ActivityFeed/ActivityThreadPanel/ActivityThreadPanel.tsx', () => { - return jest.fn().mockReturnValue(

FeedCards

); + return jest.fn().mockReturnValue(

Conversations

); +}); +jest.mock('../ActivityFeed/ActivityFeedEditor/ActivityFeedEditor.tsx', () => { + return jest.fn().mockReturnValue(

FeedEditor

); }); jest.mock('../../utils/CommonUtils', () => ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.component.tsx index a0b930e211a..f87e0db3570 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityTable/EntityTable.component.tsx @@ -12,12 +12,13 @@ */ import classNames from 'classnames'; -import { cloneDeep, isUndefined, lowerCase } from 'lodash'; +import { cloneDeep, isNil, isUndefined, lowerCase } from 'lodash'; import { EntityFieldThreads, EntityTags } from 'Models'; import React, { Fragment, useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; import { useExpanded, useTable } from 'react-table'; import { getTableDetailsPath } from '../../constants/constants'; +import { EntityType } from '../../enums/entity.enum'; import { Column, ColumnJoins, @@ -32,7 +33,8 @@ import { getTableFQNFromColumnFQN, } from '../../utils/CommonUtils'; import { getFieldThreadElement } from '../../utils/FeedElementUtils'; -import SVGIcons from '../../utils/SvgUtils'; +import { getThreadValue } from '../../utils/FeedUtils'; +import SVGIcons, { Icons } from '../../utils/SvgUtils'; import { getConstraintIcon, getDataTypeString, @@ -54,9 +56,11 @@ type Props = { columnName: string; hasEditAccess: boolean; isReadOnly?: boolean; + entityFqn?: string; entityFieldThreads?: EntityFieldThreads[]; onUpdate?: (columns: Table['columns']) => void; onThreadLinkSelect?: (value: string) => void; + onEntityFieldSelect?: (value: string) => void; }; const EntityTable = ({ @@ -69,6 +73,8 @@ const EntityTable = ({ entityFieldThreads, isReadOnly = false, onThreadLinkSelect, + onEntityFieldSelect, + entityFqn, }: Props) => { const columns = React.useMemo( () => [ @@ -465,7 +471,11 @@ const EntityTable = ({ cell.row.cells[0].value, 'tags', entityFieldThreads as EntityFieldThreads[], - onThreadLinkSelect + onThreadLinkSelect, + EntityType.TABLE, + entityFqn, + `columns/${cell.row.cells[0].value}/tags`, + Boolean(cell.value.length) )} )} @@ -485,39 +495,70 @@ const EntityTable = ({ /> ) : ( - No description added + No description added{' '} )} - {getFieldThreadElement( - cell.row.cells[0].value, - 'description', - entityFieldThreads as EntityFieldThreads[], - onThreadLinkSelect - )} {!isReadOnly ? ( - + + + + {isNil( + getThreadValue( + cell.row.cells[0].value, + 'description', + entityFieldThreads as EntityFieldThreads[] + ) + ) && !cell.value ? ( + + ) : null} + {getFieldThreadElement( + cell.row.cells[0].value, + 'description', + entityFieldThreads as EntityFieldThreads[], + onThreadLinkSelect, + EntityType.TABLE, + entityFqn, + `columns/${cell.row.cells[0].value}/description`, + Boolean(cell.value) )} - isOwner={hasEditAccess} - permission={Operation.UpdateDescription} - position="top"> - - + ) : null} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/FeedEditor/FeedEditor.tsx b/openmetadata-ui/src/main/resources/ui/src/components/FeedEditor/FeedEditor.tsx index 99bd535a5fb..f0b92a14c98 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/FeedEditor/FeedEditor.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/FeedEditor/FeedEditor.tsx @@ -21,6 +21,7 @@ import React, { useState, } from 'react'; import ReactQuill, { Quill } from 'react-quill'; +import { EditorPlaceHolder } from '../../constants/feed.constants'; import { HTMLToMarkdown, matcher } from '../../utils/FeedUtils'; import { insertMention, insertRef } from '../../utils/QuillUtils'; import { editorRef } from '../common/rich-text-editor/RichTextEditor.interface'; @@ -71,10 +72,10 @@ const modules = { const FeedEditor = forwardRef( ( - { className, editorClass, placeHolder, onChangeHandler }: FeedEditorProp, + { className, editorClass, onChangeHandler, defaultValue }: FeedEditorProp, ref ) => { - const [value, setValue] = useState(''); + const [value, setValue] = useState(defaultValue ?? ''); const handleOnChange = (value: string) => { setValue(value); @@ -97,7 +98,7 @@ const FeedEditor = forwardRef( { + header: string; + threadLink: string; + defaultValue?: string; + headerClassName?: string; + bodyClassName?: string; + onCancel: () => void; + createThread: (data: CreateThread) => void; +} + +const RequestDescriptionModal: FC = ({ + header, + headerClassName, + onCancel, + createThread, + threadLink, + defaultValue, +}) => { + const onPostThread = (value: string) => { + const currentUser = AppState.userDetails?.name ?? AppState.users[0]?.name; + const data = { + message: value, + from: currentUser, + about: threadLink, + }; + createThread(data); + onCancel(); + }; + + return ( + +
+
+
+

+ {header} +

+
+ + + +
+
+
+ +
+
+
+ ); +}; + +export default RequestDescriptionModal; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/MyData.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/MyData.test.tsx index f49b31bf662..22ad00db70d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/MyData.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/MyData.test.tsx @@ -22,10 +22,10 @@ import { import { SearchResponse } from 'Models'; import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; +import { FeedFilter } from '../../enums/mydata.enum'; import { User } from '../../generated/entity/teams/user'; import { formatDataResponse } from '../../utils/APIUtils'; import MyDataPage from './MyData.component'; -import { FeedFilter } from '../../enums/mydata.enum'; const mockData = { data: { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx index c6a2e9f6c3b..3e47c2b4822 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx @@ -13,13 +13,15 @@ import classNames from 'classnames'; import { compare } from 'fast-json-patch'; +import { isNil } from 'lodash'; import { EntityFieldThreads, EntityTags } from 'Models'; -import React, { useEffect, useState } from 'react'; +import React, { Fragment, useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; import { getTeamDetailsPath } from '../../constants/constants'; +import { EntityType } from '../../enums/entity.enum'; import { Pipeline, Task } from '../../generated/entity/data/pipeline'; import { Operation } from '../../generated/entity/policies/accessControl/rule'; -import { User } from '../../generated/entity/teams/user'; +import { EntityReference, User } from '../../generated/entity/teams/user'; import { LabelType, State } from '../../generated/type/tagLabel'; import { useAuth } from '../../hooks/authHooks'; import { @@ -28,9 +30,13 @@ import { getUserTeams, isEven, } from '../../utils/CommonUtils'; -import { getFieldThreadElement } from '../../utils/FeedElementUtils'; +import { getEntityFeedLink } from '../../utils/EntityUtils'; +import { + getDefaultValue, + getFieldThreadElement, +} from '../../utils/FeedElementUtils'; import { getEntityFieldThreadCounts } from '../../utils/FeedUtils'; -import SVGIcons from '../../utils/SvgUtils'; +import SVGIcons, { Icons } from '../../utils/SvgUtils'; import { getTagsWithoutTier } from '../../utils/TableUtils'; import ActivityFeedList from '../ActivityFeed/ActivityFeedList/ActivityFeedList'; import ActivityThreadPanel from '../ActivityFeed/ActivityThreadPanel/ActivityThreadPanel'; @@ -43,6 +49,7 @@ import PageContainer from '../containers/PageContainer'; import Entitylineage from '../EntityLineage/EntityLineage.component'; import ManageTabComponent from '../ManageTab/ManageTab.component'; import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; +import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal'; import { PipeLineDetailsProp } from './PipelineDetails.interface'; const PipelineDetails = ({ @@ -83,6 +90,7 @@ const PipelineDetails = ({ feedCount, entityFieldThreadCount, createThread, + pipelineFQN, }: PipeLineDetailsProp) => { const { isAuthDisabled } = useAuth(); const [isEdit, setIsEdit] = useState(false); @@ -95,6 +103,15 @@ const PipelineDetails = ({ const [threadLink, setThreadLink] = useState(''); + const [selectedField, setSelectedField] = useState(''); + + const onEntityFieldSelect = (value: string) => { + setSelectedField(value); + }; + const closeRequestModal = () => { + setSelectedField(''); + }; + const hasEditAccess = () => { if (owner?.type === 'user') { return owner.id === getCurrentUserId(); @@ -311,7 +328,9 @@ const PipelineDetails = ({ 'tags', entityFieldThreadCount )} + entityFqn={pipelineFQN} entityName={entityName} + entityType={EntityType.PIPELINE} extraInfo={extraInfo} followHandler={followPipeline} followers={followersCount} @@ -345,7 +364,9 @@ const PipelineDetails = ({ 'description', entityFieldThreadCount )} + entityFqn={pipelineFQN} entityName={entityName} + entityType={EntityType.PIPELINE} hasEditAccess={hasEditAccess()} isEdit={isEdit} isReadOnly={deleted} @@ -353,6 +374,7 @@ const PipelineDetails = ({ onCancel={onCancel} onDescriptionEdit={onDescriptionEdit} onDescriptionUpdate={onDescriptionUpdate} + onEntityFieldSelect={onEntityFieldSelect} onThreadLinkSelect={onThreadLinkSelect} /> @@ -403,40 +425,74 @@ const PipelineDetails = ({ /> ) : ( - No description added + No description added{' '} )} - {getFieldThreadElement( - task.name, - 'description', - getEntityFieldThreadCounts( - 'tasks', - entityFieldThreadCount - ) as EntityFieldThreads[], - onThreadLinkSelect - )} {!deleted && ( - + + + + {!isNil( + getFieldThreadElement( + task.name, + 'description', + getEntityFieldThreadCounts( + 'tasks', + entityFieldThreadCount + ) as EntityFieldThreads[], + onThreadLinkSelect + ) + ) && + onEntityFieldSelect && + !task.description ? ( + + ) : null} + {getFieldThreadElement( + task.name, + 'description', + getEntityFieldThreadCounts( + 'tasks', + entityFieldThreadCount + ) as EntityFieldThreads[], + onThreadLinkSelect, + EntityType.PIPELINE, + pipelineFQN, + `tasks/${task.name}/description`, + Boolean(task.description) )} - isOwner={hasEditAccess()} - permission={Operation.UpdateDescription} - position="top"> - - + )} @@ -453,15 +509,6 @@ const PipelineDetails = ({ )} - {threadLink ? ( - - ) : null} )} {activeTab === 2 && ( @@ -473,6 +520,7 @@ const PipelineDetails = ({ isEntityFeed withSidePanel className="" + entityName={entityName} feedList={entityThread} isLoading={isentityThreadLoading} postFeedHandler={postFeedHandler} @@ -519,6 +567,28 @@ const PipelineDetails = ({ onSave={onTaskUpdate} /> )} + {threadLink ? ( + + ) : null} + {selectedField ? ( + + ) : null} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.interface.ts index 0142ffa7b9b..64c206ecd13 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.interface.ts @@ -33,6 +33,7 @@ import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrum import { Edge, EdgeData } from '../EntityLineage/EntityLineage.interface'; export interface PipeLineDetailsProp { + pipelineFQN: string; version: string; isNodeLoading: LoadingNodeState; lineageLeafNodes: LeafNodes; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx index b502161dff7..c8e19ab1e2a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx @@ -30,8 +30,10 @@ type Props = { columnName: string; hasEditAccess?: boolean; isReadOnly?: boolean; + entityFqn?: string; entityFieldThreads?: EntityFieldThreads[]; onThreadLinkSelect?: (value: string) => void; + onEntityFieldSelect?: (value: string) => void; onUpdate?: (columns: Table['columns']) => void; }; @@ -44,7 +46,9 @@ const SchemaTab: FunctionComponent = ({ owner, entityFieldThreads, onThreadLinkSelect, + onEntityFieldSelect, isReadOnly = false, + entityFqn, }: Props) => { const [searchText, setSearchText] = useState(''); @@ -70,12 +74,14 @@ const SchemaTab: FunctionComponent = ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.component.tsx index 81067a7a97f..5cb0603982a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.component.tsx @@ -14,11 +14,14 @@ import { EntityTags } from 'Models'; import React, { useEffect, useState } from 'react'; import { getTeamDetailsPath } from '../../constants/constants'; +import { EntityType } from '../../enums/entity.enum'; import { Topic } from '../../generated/entity/data/topic'; -import { User } from '../../generated/entity/teams/user'; +import { EntityReference, User } from '../../generated/entity/teams/user'; import { LabelType, State } from '../../generated/type/tagLabel'; import { useAuth } from '../../hooks/authHooks'; import { getCurrentUserId, getUserTeams } from '../../utils/CommonUtils'; +import { getEntityFeedLink } from '../../utils/EntityUtils'; +import { getDefaultValue } from '../../utils/FeedElementUtils'; import { getEntityFieldThreadCounts } from '../../utils/FeedUtils'; import { bytesToSize } from '../../utils/StringsUtils'; import { getTagsWithoutTier } from '../../utils/TableUtils'; @@ -29,6 +32,7 @@ import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo'; import TabsPane from '../common/TabsPane/TabsPane'; import PageContainer from '../containers/PageContainer'; import ManageTabComponent from '../ManageTab/ManageTab.component'; +import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal'; import SchemaEditor from '../schema-editor/SchemaEditor'; import { TopicDetailsProps } from './TopicDetails.interface'; @@ -65,12 +69,22 @@ const TopicDetails: React.FC = ({ feedCount, entityFieldThreadCount, createThread, + topicFQN, }: TopicDetailsProps) => { const { isAuthDisabled } = useAuth(); const [isEdit, setIsEdit] = useState(false); const [followersCount, setFollowersCount] = useState(0); const [isFollowing, setIsFollowing] = useState(false); const [threadLink, setThreadLink] = useState(''); + const [selectedField, setSelectedField] = useState(''); + + const onEntityFieldSelect = (value: string) => { + setSelectedField(value); + }; + const closeRequestModal = () => { + setSelectedField(''); + }; + const hasEditAccess = () => { if (owner?.type === 'user') { return owner.id === getCurrentUserId(); @@ -309,7 +323,9 @@ const TopicDetails: React.FC = ({ 'tags', entityFieldThreadCount )} + entityFqn={topicFQN} entityName={entityName} + entityType={EntityType.TOPIC} extraInfo={extraInfo} followHandler={followTopic} followers={followersCount} @@ -343,7 +359,9 @@ const TopicDetails: React.FC = ({ 'description', entityFieldThreadCount )} + entityFqn={topicFQN} entityName={entityName} + entityType={EntityType.TOPIC} hasEditAccess={hasEditAccess()} isEdit={isEdit} isReadOnly={deleted} @@ -351,6 +369,7 @@ const TopicDetails: React.FC = ({ onCancel={onCancel} onDescriptionEdit={onDescriptionEdit} onDescriptionUpdate={onDescriptionUpdate} + onEntityFieldSelect={onEntityFieldSelect} onThreadLinkSelect={onThreadLinkSelect} /> @@ -359,15 +378,6 @@ const TopicDetails: React.FC = ({
- {threadLink ? ( - - ) : null} )} {activeTab === 2 && ( @@ -379,6 +389,7 @@ const TopicDetails: React.FC = ({ isEntityFeed withSidePanel className="" + entityName={entityName} feedList={entityThread} isLoading={isentityThreadLoading} postFeedHandler={postFeedHandler} @@ -402,6 +413,28 @@ const TopicDetails: React.FC = ({ )} + {threadLink ? ( + + ) : null} + {selectedField ? ( + + ) : null} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.interface.ts index 27235496e31..4d5bf38d47b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/TopicDetails/TopicDetails.interface.ts @@ -24,6 +24,7 @@ import { TagLabel } from '../../generated/type/tagLabel'; import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrumb.interface'; export interface TopicDetailsProps { + topicFQN: string; version?: string; schemaText: string; schemaType: string; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/description/Description.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/description/Description.tsx index 0b0fdd5fc5d..4c5226749c1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/description/Description.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/description/Description.tsx @@ -14,11 +14,12 @@ import classNames from 'classnames'; import { isUndefined } from 'lodash'; import { EntityFieldThreads } from 'Models'; -import React from 'react'; +import React, { Fragment } from 'react'; import { Table } from '../../../generated/entity/data/table'; import { Operation } from '../../../generated/entity/policies/accessControl/rule'; import { getHtmlForNonAdminAction } from '../../../utils/CommonUtils'; -import SVGIcons from '../../../utils/SvgUtils'; +import { getEntityFeedLink } from '../../../utils/EntityUtils'; +import SVGIcons, { Icons } from '../../../utils/SvgUtils'; import { ModalWithMarkdownEditor } from '../../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; import NonAdminAction from '../non-admin-action/NonAdminAction'; import RichTextEditorPreviewer from '../rich-text-editor/RichTextEditorPreviewer'; @@ -32,12 +33,15 @@ type Props = { description: string; isEdit?: boolean; isReadOnly?: boolean; + entityType?: string; + entityFqn?: string; entityFieldThreads?: EntityFieldThreads[]; onThreadLinkSelect?: (value: string) => void; onDescriptionEdit?: () => void; onCancel?: () => void; onDescriptionUpdate?: (value: string) => void; onSuggest?: (value: string) => void; + onEntityFieldSelect?: (value: string) => void; }; const Description = ({ @@ -54,6 +58,9 @@ const Description = ({ entityName, entityFieldThreads, onThreadLinkSelect, + onEntityFieldSelect, + entityType, + entityFqn, }: Props) => { const descriptionThread = entityFieldThreads?.[0]; @@ -78,19 +85,9 @@ const Description = ({ /> ) : ( - No description added + No description added{' '} )} - {!isUndefined(descriptionThread) ? ( -

- onThreadLinkSelect?.(descriptionThread.entityLink) - }> - {descriptionThread.count}{' '} - threads -

- ) : null} {isEdit && ( + {isUndefined(descriptionThread) && + onEntityFieldSelect && + !description?.trim() ? ( + + ) : null} + {!isUndefined(descriptionThread) ? ( +

+ onThreadLinkSelect?.(descriptionThread.entityLink) + }> + + {' '} + {descriptionThread.count} + +

+ ) : ( + + {description?.trim() && onThreadLinkSelect ? ( +

+ onThreadLinkSelect?.( + getEntityFeedLink(entityType, entityFqn, 'description') + ) + }> + +

+ ) : null} +
+ )} ) : null} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.tsx index 7a60fed0b9c..b2f0e219518 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/entityPageInfo/EntityPageInfo.tsx @@ -20,8 +20,8 @@ import { Operation } from '../../../generated/entity/policies/accessControl/rule import { User } from '../../../generated/entity/teams/user'; import { TagLabel } from '../../../generated/type/tagLabel'; import { getHtmlForNonAdminAction } from '../../../utils/CommonUtils'; -import { getInfoElements } from '../../../utils/EntityUtils'; -import SVGIcons from '../../../utils/SvgUtils'; +import { getEntityFeedLink, getInfoElements } from '../../../utils/EntityUtils'; +import SVGIcons, { Icons } from '../../../utils/SvgUtils'; import { getFollowerDetail } from '../../../utils/TableUtils'; import { getTagCategories, getTaglist } from '../../../utils/TagsUtils'; import TagsContainer from '../../tags-container/tags-container'; @@ -46,6 +46,8 @@ type Props = { hasEditAccess?: boolean; followersList: Array; entityName: string; + entityType?: string; + entityFqn?: string; version?: string; isVersionSelected?: boolean; entityFieldThreads?: EntityFieldThreads[]; @@ -75,6 +77,8 @@ const EntityPageInfo = ({ versionHandler, entityFieldThreads, onThreadLinkSelect, + entityFqn, + entityType, }: Props) => { const tagThread = entityFieldThreads?.[0]; const [isEditable, setIsEditable] = useState(false); @@ -407,11 +411,28 @@ const EntityPageInfo = ({ {!isUndefined(tagThread) ? (

onThreadLinkSelect?.(tagThread.entityLink)}> - {tagThread.count} threads + + + {tagThread.count} +

- ) : null} + ) : ( +

+ onThreadLinkSelect?.( + getEntityFeedLink(entityType, entityFqn, 'tags') + ) + }> + +

+ )} )} diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/feed.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/feed.constants.ts index 158063e5ab5..76f9ab63193 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/feed.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/feed.constants.ts @@ -20,3 +20,11 @@ export const hashtagRegEx = /\[#(.+?)?\]\((.+?)?\)/g; export const linkRegEx = /\((.+?\/\/.+?)\/(.+?)\/(.+?)\)/; export const entityLinkRegEx = /<#E\/([^<>]+?)\/([^<>]+?)>/g; export const entityRegex = /<#E\/([^<>]+?)\/([^<>]+?)\|(\[(.+?)?\]\((.+?)?\))>/; + +export const entityUrlMap = { + team: 'teams', + user: 'user', +}; + +export const EditorPlaceHolder = `Use @mention to tag a user or a team. +Use #mention to tag a data asset.`; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DashboardDetailsPage/DashboardDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DashboardDetailsPage/DashboardDetailsPage.component.tsx index c0f02b75567..7fbcf46b002 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DashboardDetailsPage/DashboardDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DashboardDetailsPage/DashboardDetailsPage.component.tsx @@ -141,6 +141,14 @@ const DashboardDetailsPage = () => { }); } }; + const getEntityFeedCount = () => { + getFeedCount(getEntityFeedLink(EntityType.DASHBOARD, dashboardFQN)).then( + (res: AxiosResponse) => { + setFeedCount(res.data.totalCount); + setEntityFieldThreadCount(res.data.counts); + } + ); + }; useEffect(() => { if (dashboardDetailsTabs[activeTab - 1].path !== tab) { @@ -347,6 +355,7 @@ const DashboardDetailsPage = () => { setCurrentVersion(version); setDashboardDetails(res.data); setDescription(description); + getEntityFeedCount(); }) .catch((err: AxiosError) => { const errMsg = @@ -401,6 +410,7 @@ const DashboardDetailsPage = () => { setTier(getTierTags(res.data.tags)); setCurrentVersion(res.data.version); setTags(getTagsWithoutTier(res.data.tags)); + getEntityFeedCount(); }) .catch((err: AxiosError) => { const errMsg = @@ -422,6 +432,7 @@ const DashboardDetailsPage = () => { setCurrentVersion(res.data.version); setOwner(res.data.owner); setTier(getTierTags(res.data.tags)); + getEntityFeedCount(); resolve(); }) .catch((err: AxiosError) => { @@ -540,27 +551,20 @@ const DashboardDetailsPage = () => { }); }; - const getEntityFeedCount = () => { - getFeedCount(getEntityFeedLink(EntityType.DASHBOARD, dashboardFQN)).then( - (res: AxiosResponse) => { - setFeedCount(res.data.totalCount); - setEntityFieldThreadCount(res.data.counts); - } - ); - }; const createThread = (data: CreateThread) => { postThread(data) .then((res: AxiosResponse) => { setEntityThread((pre) => [...pre, res.data]); + getEntityFeedCount(); showToast({ variant: 'success', - body: 'Thread is created successfully', + body: 'Conversation created successfully', }); }) .catch(() => { showToast({ variant: 'error', - body: 'Error while creating thread', + body: 'Error while creating the conversation', }); }); }; @@ -595,6 +599,7 @@ const DashboardDetailsPage = () => { charts={charts} createThread={createThread} dashboardDetails={dashboardDetails} + dashboardFQN={dashboardFQN} dashboardTags={tags} dashboardUrl={dashboardUrl} deleted={deleted} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatasetDetailsPage/DatasetDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatasetDetailsPage/DatasetDetailsPage.component.tsx index f82255030ad..771a3c38969 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatasetDetailsPage/DatasetDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatasetDetailsPage/DatasetDetailsPage.component.tsx @@ -574,15 +574,16 @@ const DatasetDetailsPage: FunctionComponent = () => { postThread(data) .then((res: AxiosResponse) => { setEntityThread((pre) => [...pre, res.data]); + getEntityFeedCount(); showToast({ variant: 'success', - body: 'Thread is created successfully', + body: 'Conversation created successfully', }); }) .catch(() => { showToast({ variant: 'error', - body: 'Error while creating thread', + body: 'Error while creating the conversation', }); }); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx index 95efd95ed63..dee330da27d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx @@ -144,6 +144,15 @@ const PipelineDetailsPage = () => { } }; + const getEntityFeedCount = () => { + getFeedCount(getEntityFeedLink(EntityType.PIPELINE, pipelineFQN)).then( + (res: AxiosResponse) => { + setFeedCount(res.data.totalCount); + setEntityFieldThreadCount(res.data.counts); + } + ); + }; + useEffect(() => { if (pipelineDetailsTabs[activeTab - 1].path !== tab) { setActiveTab(getCurrentPipelineTab(tab)); @@ -331,6 +340,7 @@ const PipelineDetailsPage = () => { setCurrentVersion(version); setPipelineDetails(res.data); setDescription(description); + getEntityFeedCount(); }) .catch((err: AxiosError) => { const errMsg = @@ -350,6 +360,7 @@ const PipelineDetailsPage = () => { setCurrentVersion(res.data.version); setOwner(res.data.owner); setTier(getTierTags(res.data.tags)); + getEntityFeedCount(); resolve(); }) .catch((err: AxiosError) => { @@ -370,6 +381,7 @@ const PipelineDetailsPage = () => { setTier(getTierTags(res.data.tags)); setCurrentVersion(res.data.version); setTags(getTagsWithoutTier(res.data.tags)); + getEntityFeedCount(); }) .catch((err: AxiosError) => { const errMsg = @@ -384,6 +396,7 @@ const PipelineDetailsPage = () => { const onTaskUpdate = (jsonPatch: Array) => { patchPipelineDetails(pipelineId, jsonPatch).then((res: AxiosResponse) => { setTasks(res.data.tasks || []); + getEntityFeedCount(); }); }; @@ -483,27 +496,20 @@ const PipelineDetailsPage = () => { }); }; - const getEntityFeedCount = () => { - getFeedCount(getEntityFeedLink(EntityType.PIPELINE, pipelineFQN)).then( - (res: AxiosResponse) => { - setFeedCount(res.data.totalCount); - setEntityFieldThreadCount(res.data.counts); - } - ); - }; const createThread = (data: CreateThread) => { postThread(data) .then((res: AxiosResponse) => { setEntityThread((pre) => [...pre, res.data]); + getEntityFeedCount(); showToast({ variant: 'success', - body: 'Thread is created successfully', + body: 'Conversation created successfully', }); }) .catch(() => { showToast({ variant: 'error', - body: 'Error while creating thread', + body: 'Error while creating the conversation', }); }); }; @@ -552,6 +558,7 @@ const PipelineDetailsPage = () => { loadNodeHandler={loadNodeHandler} owner={owner} pipelineDetails={pipelineDetails} + pipelineFQN={pipelineFQN} pipelineTags={tags} pipelineUrl={pipelineUrl} postFeedHandler={postFeedHandler} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TopicDetails/TopicDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TopicDetails/TopicDetailsPage.component.tsx index 866d39884ad..f9ad15d4600 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TopicDetails/TopicDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TopicDetails/TopicDetailsPage.component.tsx @@ -115,6 +115,15 @@ const TopicDetailsPage: FunctionComponent = () => { } }; + const getEntityFeedCount = () => { + getFeedCount(getEntityFeedLink(EntityType.TOPIC, topicFQN)).then( + (res: AxiosResponse) => { + setFeedCount(res.data.totalCount); + setEntityFieldThreadCount(res.data.counts); + } + ); + }; + const fetchActivityFeed = () => { setIsentityThreadLoading(true); getAllFeeds(getEntityFeedLink(EntityType.TOPIC, topicFQN)) @@ -273,6 +282,7 @@ const TopicDetailsPage: FunctionComponent = () => { setCurrentVersion(version); setTopicDetails(res.data); setDescription(description); + getEntityFeedCount(); }) .catch((err: AxiosError) => { const errMsg = @@ -292,6 +302,7 @@ const TopicDetailsPage: FunctionComponent = () => { setCurrentVersion(res.data.version); setOwner(res.data.owner); setTier(getTierTags(res.data.tags)); + getEntityFeedCount(); resolve(); }) .catch((err: AxiosError) => { @@ -312,6 +323,7 @@ const TopicDetailsPage: FunctionComponent = () => { setTier(getTierTags(res.data.tags)); setCurrentVersion(res.data.version); setTags(getTagsWithoutTier(res.data.tags)); + getEntityFeedCount(); }) .catch((err: AxiosError) => { const errMsg = @@ -359,27 +371,20 @@ const TopicDetailsPage: FunctionComponent = () => { }); }; - const getEntityFeedCount = () => { - getFeedCount(getEntityFeedLink(EntityType.TOPIC, topicFQN)).then( - (res: AxiosResponse) => { - setFeedCount(res.data.totalCount); - setEntityFieldThreadCount(res.data.counts); - } - ); - }; const createThread = (data: CreateThread) => { postThread(data) .then((res: AxiosResponse) => { setEntityThread((pre) => [...pre, res.data]); + getEntityFeedCount(); showToast({ variant: 'success', - body: 'Thread is created successfully', + body: 'Conversation created successfully', }); }) .catch(() => { showToast({ variant: 'error', - body: 'Error while creating thread', + body: 'Error while creating the conversation', }); }); }; @@ -429,6 +434,7 @@ const TopicDetailsPage: FunctionComponent = () => { tagUpdateHandler={onTagUpdate} tier={tier as TagLabel} topicDetails={topicDetails} + topicFQN={topicFQN} topicTags={tags} unfollowTopicHandler={unfollowTopic} users={AppState.users} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/database-details/index.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/database-details/index.tsx index b7a6e06d07d..38fe8bc4aa6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/database-details/index.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/database-details/index.tsx @@ -16,7 +16,12 @@ import classNames from 'classnames'; import { compare } from 'fast-json-patch'; import { isNil } from 'lodash'; import { observer } from 'mobx-react'; -import { EntityThread, ExtraInfo, Paging } from 'Models'; +import { + EntityFieldThreadCount, + EntityThread, + ExtraInfo, + Paging, +} from 'Models'; import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; import { Link, useHistory, useParams } from 'react-router-dom'; import { default as AppState, default as appState } from '../../AppState'; @@ -28,9 +33,11 @@ import { getAllFeeds, getFeedCount, postFeedById, + postThread, } from '../../axiosAPIs/feedsAPI'; import { getDatabaseTables } from '../../axiosAPIs/tableAPI'; import ActivityFeedList from '../../components/ActivityFeed/ActivityFeedList/ActivityFeedList'; +import ActivityThreadPanel from '../../components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel'; import Description from '../../components/common/description/Description'; import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder'; import NextPrevious from '../../components/common/next-previous/NextPrevious'; @@ -41,6 +48,7 @@ import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/t import PageContainer from '../../components/containers/PageContainer'; import Loader from '../../components/Loader/Loader'; import ManageTabComponent from '../../components/ManageTab/ManageTab.component'; +import RequestDescriptionModal from '../../components/Modals/RequestDescriptionModal/RequestDescriptionModal'; import Tags from '../../components/tags/tags'; import { getDatabaseDetailsPath, @@ -52,8 +60,10 @@ import { } from '../../constants/constants'; import { EntityType, TabSpecificField } from '../../enums/entity.enum'; import { ServiceCategory } from '../../enums/service.enum'; +import { CreateThread } from '../../generated/api/feed/createThread'; import { Database } from '../../generated/entity/data/database'; import { Table } from '../../generated/entity/data/table'; +import { EntityReference } from '../../generated/entity/teams/user'; import useToastContext from '../../hooks/useToastContext'; import { getEntityMissingError, @@ -65,6 +75,8 @@ import { getCurrentDatabaseDetailsTab, } from '../../utils/DatabaseDetailsUtils'; import { getEntityFeedLink, getInfoElements } from '../../utils/EntityUtils'; +import { getDefaultValue } from '../../utils/FeedElementUtils'; +import { getEntityFieldThreadCounts } from '../../utils/FeedUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { getOwnerFromId, getUsagePercentile } from '../../utils/TableUtils'; import { getTableTags } from '../../utils/TagsUtils'; @@ -100,6 +112,12 @@ const DatabaseDetails: FunctionComponent = () => { const [isentityThreadLoading, setIsentityThreadLoading] = useState(false); const [feedCount, setFeedCount] = useState(0); + const [entityFieldThreadCount, setEntityFieldThreadCount] = useState< + EntityFieldThreadCount[] + >([]); + + const [threadLink, setThreadLink] = useState(''); + const [selectedField, setSelectedField] = useState(''); const history = useHistory(); const isMounting = useRef(true); @@ -187,6 +205,35 @@ const DatabaseDetails: FunctionComponent = () => { }); }; + const onThreadLinkSelect = (link: string) => { + setThreadLink(link); + }; + + const onThreadPanelClose = () => { + setThreadLink(''); + }; + + const onEntityFieldSelect = (value: string) => { + setSelectedField(value); + }; + const closeRequestModal = () => { + setSelectedField(''); + }; + + const getEntityFeedCount = () => { + getFeedCount(getEntityFeedLink(EntityType.DATABASE, databaseFQN)) + .then((res: AxiosResponse) => { + setFeedCount(res.data.totalCount); + setEntityFieldThreadCount(res.data.counts); + }) + .catch(() => { + showToast({ + variant: 'error', + body: 'Error while fetching feed count', + }); + }); + }; + const getDetailsByFQN = () => { getDatabaseDetailsByFQN(databaseFQN, ['owner']) .then((res: AxiosResponse) => { @@ -260,6 +307,7 @@ const DatabaseDetails: FunctionComponent = () => { setDatabase(updatedDatabaseDetails); setDescription(updatedHTML); setIsEdit(false); + getEntityFeedCount(); }) .catch((err: AxiosError) => { const errMsg = @@ -367,15 +415,21 @@ const DatabaseDetails: FunctionComponent = () => { }); }); }; - const getEntityFeedCount = () => { - getFeedCount(getEntityFeedLink(EntityType.DATABASE, databaseFQN)) + + const createThread = (data: CreateThread) => { + postThread(data) .then((res: AxiosResponse) => { - setFeedCount(res.data.totalCount); + setEntityThread((pre) => [...pre, res.data]); + getEntityFeedCount(); + showToast({ + variant: 'success', + body: 'Conversation created successfully', + }); }) .catch(() => { showToast({ variant: 'error', - body: 'Error while fetching feed count', + body: 'Error while creating the conversation', }); }); }; @@ -448,11 +502,19 @@ const DatabaseDetails: FunctionComponent = () => {
@@ -605,6 +667,7 @@ const DatabaseDetails: FunctionComponent = () => { isEntityFeed withSidePanel className="" + entityName={databaseName} feedList={entityThread} isLoading={isentityThreadLoading} postFeedHandler={postFeedHandler} @@ -625,6 +688,30 @@ const DatabaseDetails: FunctionComponent = () => { )}
+ {threadLink ? ( + + ) : null} + {selectedField ? ( + + ) : null} )} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx index 98a01708457..b186cb4ca78 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx @@ -15,7 +15,13 @@ import { AxiosError, AxiosResponse } from 'axios'; import classNames from 'classnames'; import { compare } from 'fast-json-patch'; import { isNil, isUndefined } from 'lodash'; -import { EntityThread, ExtraInfo, Paging, ServicesData } from 'Models'; +import { + EntityFieldThreadCount, + EntityThread, + ExtraInfo, + Paging, + ServicesData, +} from 'Models'; import React, { Fragment, FunctionComponent, useEffect, useState } from 'react'; import { Link, useHistory, useParams } from 'react-router-dom'; import AppState from '../../AppState'; @@ -32,11 +38,13 @@ import { getAllFeeds, getFeedCount, postFeedById, + postThread, } from '../../axiosAPIs/feedsAPI'; import { getPipelines } from '../../axiosAPIs/pipelineAPI'; import { getServiceByFQN, updateService } from '../../axiosAPIs/serviceAPI'; import { getTopics } from '../../axiosAPIs/topicsAPI'; import ActivityFeedList from '../../components/ActivityFeed/ActivityFeedList/ActivityFeedList'; +import ActivityThreadPanel from '../../components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel'; import Description from '../../components/common/description/Description'; import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder'; import IngestionError from '../../components/common/error/IngestionError'; @@ -49,6 +57,7 @@ import PageContainer from '../../components/containers/PageContainer'; import Ingestion from '../../components/Ingestion/Ingestion.component'; import Loader from '../../components/Loader/Loader'; import ManageTabComponent from '../../components/ManageTab/ManageTab.component'; +import RequestDescriptionModal from '../../components/Modals/RequestDescriptionModal/RequestDescriptionModal'; import ServiceConfig from '../../components/ServiceConfig/ServiceConfig'; import Tags from '../../components/tags/tags'; import { @@ -59,6 +68,7 @@ import { import { TabSpecificField } from '../../enums/entity.enum'; import { SearchIndex } from '../../enums/search.enum'; import { ServiceCategory } from '../../enums/service.enum'; +import { CreateThread } from '../../generated/api/feed/createThread'; import { Dashboard } from '../../generated/entity/data/dashboard'; import { Database } from '../../generated/entity/data/database'; import { Pipeline } from '../../generated/entity/data/pipeline'; @@ -81,6 +91,8 @@ import { isEven, } from '../../utils/CommonUtils'; import { getEntityFeedLink, getInfoElements } from '../../utils/EntityUtils'; +import { getDefaultValue } from '../../utils/FeedElementUtils'; +import { getEntityFieldThreadCounts } from '../../utils/FeedUtils'; import { getCurrentServiceTab, getIsIngestionEnable, @@ -130,6 +142,19 @@ const ServicePage: FunctionComponent = () => { const [isentityThreadLoading, setIsentityThreadLoading] = useState(false); const [feedCount, setFeedCount] = useState(0); + const [entityFieldThreadCount, setEntityFieldThreadCount] = useState< + EntityFieldThreadCount[] + >([]); + + const [threadLink, setThreadLink] = useState(''); + const [selectedField, setSelectedField] = useState(''); + + const onEntityFieldSelect = (value: string) => { + setSelectedField(value); + }; + const closeRequestModal = () => { + setSelectedField(''); + }; const getCountLabel = () => { switch (serviceName) { @@ -239,6 +264,28 @@ const ServicePage: FunctionComponent = () => { } }; + const onThreadLinkSelect = (link: string) => { + setThreadLink(link); + }; + + const onThreadPanelClose = () => { + setThreadLink(''); + }; + + const getEntityFeedCount = () => { + getFeedCount(getEntityFeedLink(serviceCategory.slice(0, -1), serviceFQN)) + .then((res: AxiosResponse) => { + setFeedCount(res.data.totalCount); + setEntityFieldThreadCount(res.data.counts); + }) + .catch(() => { + showToast({ + variant: 'error', + body: 'Error while fetching feed count', + }); + }); + }; + const getSchemaFromType = (type: AirflowPipeline['pipelineType']) => { switch (type) { case PipelineType.Metadata: @@ -254,7 +301,7 @@ const ServicePage: FunctionComponent = () => { const getAllIngestionWorkflows = (paging?: string) => { setIsloading(true); - getAirflowPipelines(['owner, tags, status'], serviceFQN, '', paging) + getAirflowPipelines(['owner'], serviceFQN, '', paging) .then((res) => { if (res.data.data) { setIngestions(res.data.data); @@ -753,10 +800,12 @@ const ServicePage: FunctionComponent = () => { const onDescriptionUpdate = (updatedHTML: string) => { if (description !== updatedHTML && !isUndefined(serviceDetails)) { - const { id } = serviceDetails; + const { id, ...restDetails } = serviceDetails; const updatedServiceDetails = { - ...serviceDetails, + databaseConnection: restDetails.databaseConnection, + name: restDetails.name, + serviceType: restDetails.serviceType, description: updatedHTML, }; @@ -765,6 +814,7 @@ const ServicePage: FunctionComponent = () => { setDescription(updatedHTML); setServiceDetails(updatedServiceDetails); setIsEdit(false); + getEntityFeedCount(); }) .catch((err: AxiosError) => { const errMsg = err.message || 'Something went wrong!'; @@ -865,15 +915,20 @@ const ServicePage: FunctionComponent = () => { }); }; - const getEntityFeedCount = () => { - getFeedCount(getEntityFeedLink(serviceCategory.slice(0, -1), serviceFQN)) + const createThread = (data: CreateThread) => { + postThread(data) .then((res: AxiosResponse) => { - setFeedCount(res.data.totalCount); + setEntityThread((pre) => [...pre, res.data]); + getEntityFeedCount(); + showToast({ + variant: 'success', + body: 'Conversation created successfully', + }); }) .catch(() => { showToast({ variant: 'error', - body: 'Error while fetching feed count', + body: 'Error while creating the conversation', }); }); }; @@ -922,11 +977,19 @@ const ServicePage: FunctionComponent = () => { @@ -1017,6 +1080,7 @@ const ServicePage: FunctionComponent = () => { isEntityFeed withSidePanel className="" + entityName={serviceFQN} feedList={entityThread} isLoading={isentityThreadLoading} postFeedHandler={postFeedHandler} @@ -1075,6 +1139,30 @@ const ServicePage: FunctionComponent = () => { )} + {threadLink ? ( + + ) : null} + {selectedField ? ( + + ) : null} )} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx index 59a8cb1e97e..e6ff9552d95 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/tour-page/TourPage.component.tsx @@ -30,6 +30,7 @@ import { mockFeedData, mockSearchData as exploreSearchData, } from '../../constants/mockTourData.constants'; +import { FeedFilter } from '../../enums/mydata.enum'; import { CurrentTourPageType } from '../../enums/tour.enum'; import { Table, @@ -39,7 +40,6 @@ import { import { TagLabel } from '../../generated/type/tagLabel'; import { useTour } from '../../hooks/useTour'; import { getSteps } from '../../utils/TourUtils'; -import { FeedFilter } from '../../enums/mydata.enum'; const mockData = { data: { hits: { hits: [] } }, 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 a77667e44a8..b69990241c9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx @@ -471,9 +471,10 @@ export const getInfoElements = (data: ExtraInfo) => { export const getEntityFeedLink: Function = ( type: string, - fqn: string + fqn: string, + field?: string ): string | undefined => { if (isUndefined(type) || isUndefined(fqn)) return undefined; - return `<#E/${type}/${fqn}>`; + return `<#E/${type}/${fqn}${field ? `/${field}` : ''}>`; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx index 1749d5e6254..e56f1dd8311 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx @@ -26,7 +26,7 @@ import { FieldChange, } from '../generated/entity/services/databaseService'; import { TagLabel } from '../generated/type/tagLabel'; -import { Paragraph, UnOrderedList } from './MarkdownUtils'; +import { Paragraph, Span, UnOrderedList } from './MarkdownUtils'; import { isValidJSONString } from './StringsUtils'; import { getEntityLink, getOwnerFromId } from './TableUtils'; @@ -34,7 +34,7 @@ import { getEntityLink, getOwnerFromId } from './TableUtils'; const parseMarkdown = ( content: string, className: string, - isNewLine: boolean + _isNewLine: boolean ) => { return ( void + onThreadLinkSelect?: (value: string) => void, + entityType?: string, + entityFqn?: string, + entityField?: string, + flag = true ) => { let threadValue: EntityFieldThreads = {} as EntityFieldThreads; @@ -33,13 +40,46 @@ export const getFieldThreadElement = ( return !isEmpty(threadValue) ? (

{ e.preventDefault(); e.stopPropagation(); onThreadLinkSelect?.(threadValue.entityLink); }}> - {threadValue.count} threads + + + {threadValue.count} +

- ) : null; + ) : ( + + {entityType && entityFqn && entityField && flag ? ( +

{ + e.preventDefault(); + e.stopPropagation(); + onThreadLinkSelect?.( + getEntityFeedLink(entityType, entityFqn, entityField) + ); + }}> + +

+ ) : null} +
+ ); +}; + +export const getDefaultValue = (owner: EntityReference) => { + const message = 'Can you add a description?'; + if (isUndefined(owner)) { + return `${message}`; + } else { + const name = owner.name; + const displayName = owner.displayName; + const entityType = owner.type; + const mention = `
@${displayName}`; + + return `${mention} ${message}`; + } }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.ts index ef297ea9c70..0e870c0204d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.ts @@ -28,6 +28,7 @@ import { entityLinkRegEx, entityRegex, EntityRegEx, + entityUrlMap, hashtagRegEx, linkRegEx, mentionRegEx, @@ -78,11 +79,15 @@ export const HTMLToMarkdown = new TurndownService({ }, }); -export const getReplyText = (count: number) => { - if (count === 0) return 'Reply in thread'; - if (count === 1) return `${count} Reply`; +export const getReplyText = ( + count: number, + singular?: string, + plural?: string +) => { + if (count === 0) return 'Reply in conversation'; + if (count === 1) return `${count} ${singular ? singular : 'older reply'}`; - return `${count} Replies`; + return `${count} ${plural ? plural : 'older replies'}`; }; export const getEntityFieldThreadCounts = ( @@ -109,6 +114,23 @@ export const getThreadField = (value: string, separator = '/') => { return value.split(separator).slice(-2); }; +export const getThreadValue = ( + columnName: string, + columnField: string, + entityFieldThreads: EntityFieldThreads[] +) => { + let threadValue; + + entityFieldThreads?.forEach((thread) => { + const threadField = getThreadField(thread.entityField); + if (threadField[0] === columnName && threadField[1] === columnField) { + threadValue = thread; + } + }); + + return threadValue; +}; + export async function suggestions(searchTerm: string, mentionChar: string) { if (mentionChar === '@') { let atValues = []; @@ -117,10 +139,14 @@ export async function suggestions(searchTerm: string, mentionChar: string) { const hits = data.data.hits.hits; // eslint-disable-next-line @typescript-eslint/no-explicit-any atValues = hits.map((hit: any) => { + const entityType = hit._source.entity_type; + return { id: hit._id, value: `@${hit._source.display_name}`, - link: `${document.location.protocol}//${document.location.host}/${hit._source.entity_type}/${hit._source.name}`, + link: `${document.location.protocol}//${document.location.host}/${ + entityUrlMap[entityType as keyof typeof entityUrlMap] + }/${hit._source.name}`, }; }); } else { @@ -128,10 +154,14 @@ export async function suggestions(searchTerm: string, mentionChar: string) { const hits = data.data.suggest['table-suggest'][0]['options']; // eslint-disable-next-line @typescript-eslint/no-explicit-any atValues = hits.map((hit: any) => { + const entityType = hit._source.entity_type; + return { id: hit._id, value: `@${hit._source.display_name}`, - link: `${document.location.protocol}//${document.location.host}/${hit._source.entity_type}/${hit._source.name}`, + link: `${document.location.protocol}//${document.location.host}/${ + entityUrlMap[entityType as keyof typeof entityUrlMap] + }/${hit._source.name}`, }; }); } @@ -202,10 +232,12 @@ export const getBackendFormat = (message: string) => { const hashtagList = [...new Set(getHashTagList(message) ?? [])]; const mentionDetails = mentionList.map((m) => getEntityDetail(m) ?? []); const hashtagDetails = hashtagList.map((h) => getEntityDetail(h) ?? []); + const urlEntries = Object.entries(entityUrlMap); mentionList.forEach((m, i) => { const updatedDetails = mentionDetails[i].slice(-2); - const entityLink = `<#E/${updatedDetails[0]}/${updatedDetails[1]}|${m}>`; + const entityType = urlEntries.find((e) => e[1] === updatedDetails[0])?.[0]; + const entityLink = `<#E/${entityType}/${updatedDetails[1]}|${m}>`; updatedMessage = updatedMessage.replaceAll(m, entityLink); }); hashtagList.forEach((h, i) => { diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/MarkdownUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/MarkdownUtils.tsx index 414c1824245..801955ab276 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/MarkdownUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/MarkdownUtils.tsx @@ -15,8 +15,11 @@ import React, { FC, HTMLAttributes } from 'react'; export const Paragraph: FC< HTMLAttributes & { isNewLine: boolean } -> = ({ children, isNewLine = true, ...props }) => - isNewLine ?

{children}

: {children}; +> = ({ children, ...props }) =>

{children}

; + +export const Span: FC< + HTMLAttributes & { isNewLine: boolean } +> = ({ children, ...props }) => {children}; export const UnOrderedList: FC> = ({ children, diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx index 11e5274c69e..cc0df9e5488 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx @@ -18,6 +18,7 @@ import IconGithub from '../assets/img/icon-github.png'; import IconGoogle from '../assets/img/icon-google.png'; import IconOkta from '../assets/img/icon-okta.png'; import IconWelcomePopper from '../assets/img/welcome-popper-icon.png'; +import IconCommentPlus from '../assets/svg/add-chat.svg'; import IconAnnouncementWhite from '../assets/svg/announcements-white.svg'; import IconAnnouncement from '../assets/svg/announcements.svg'; import IconAPI from '../assets/svg/api.svg'; @@ -25,6 +26,7 @@ import IconArrowDownPrimary from '../assets/svg/arrow-down-primary.svg'; import IconArrowRightPrimary from '../assets/svg/arrow-right-primary.svg'; import IconSuccess from '../assets/svg/check.svg'; import IconCheckboxPrimary from '../assets/svg/checkbox-primary.svg'; +import IconComments from '../assets/svg/comment.svg'; import IconConfigColor from '../assets/svg/config-color.svg'; import IconConfig from '../assets/svg/config.svg'; import IconControlMinus from '../assets/svg/control-minus.svg'; @@ -95,6 +97,7 @@ import LogoMonogram from '../assets/svg/logo-monogram.svg'; import Logo from '../assets/svg/logo.svg'; import IconManageColor from '../assets/svg/manage-color.svg'; import IconMinus from '../assets/svg/minus.svg'; +import IconPaperPlanePrimary from '../assets/svg/paper-plane-primary.svg'; import IconPaperPlane from '../assets/svg/paper-plane.svg'; import IconPipelineGrey from '../assets/svg/pipeline-grey.svg'; import IconPipeline from '../assets/svg/pipeline.svg'; @@ -102,6 +105,7 @@ import IconPlus from '../assets/svg/plus.svg'; import IconProfilerColor from '../assets/svg/profiler-color.svg'; import IconProfiler from '../assets/svg/profiler.svg'; import IconHelpCircle from '../assets/svg/question-circle.svg'; +import IconRequest from '../assets/svg/request-icon.svg'; import IconSampleDataColor from '../assets/svg/sample-data-colored.svg'; import IconSampleData from '../assets/svg/sample-data.svg'; import IconSchemaColor from '../assets/svg/schema-color.svg'; @@ -238,6 +242,7 @@ export const Icons = { CONTROLMINUS: 'icon-control-minus', EDITLINEAGECOLOR: 'icon-edit-lineage-color', EDITLINEAGE: 'icon-edit-lineage', + REQUEST: 'icon-request', CHECKBOX_PRIMARY: 'icon-checkbox-primary', ARROW_RIGHT_PRIMARY: 'icon-arrow-right-primary', ARROW_DOWN_PRIMARY: 'icon-arrow-down-primary', @@ -247,6 +252,9 @@ export const Icons = { ICON_UP: 'icon-up', ICON_DOWN: 'icon-down', PAPER_PLANE: 'icon-paper-plane', + PAPER_PLANE_PRIMARY: 'icon-paper-plane-primary', + COMMENT: 'icon-comment', + COMMENT_PLUS: 'icon-comment-plus', }; const SVGIcons: FunctionComponent = ({ @@ -694,6 +702,10 @@ const SVGIcons: FunctionComponent = ({ case Icons.ANNOUNCEMENT: IconComponent = IconAnnouncement; + break; + case Icons.REQUEST: + IconComponent = IconRequest; + break; case Icons.ANNOUNCEMENT_WHITE: IconComponent = IconAnnouncementWhite; @@ -714,6 +726,18 @@ const SVGIcons: FunctionComponent = ({ case Icons.PAPER_PLANE: IconComponent = IconPaperPlane; + break; + case Icons.PAPER_PLANE_PRIMARY: + IconComponent = IconPaperPlanePrimary; + + break; + case Icons.COMMENT: + IconComponent = IconComments; + + break; + case Icons.COMMENT_PLUS: + IconComponent = IconCommentPlus; + break; default: