Refactor : Refactor Table Entity Conversation and Task Flow Logic #5734 (#5736)

*  Refactor : Refactor Conversation and Task Flow Logic  #5734

* Fix emoji multiple API call issue

* Fix table query editor issue

* Addressing review comments
This commit is contained in:
Sachin Chaurasiya 2022-06-29 17:32:29 +05:30 committed by GitHub
parent e3223f6a20
commit 38b04b76a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 409 additions and 206 deletions

View File

@ -0,0 +1,8 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.2778 2.16615H9.95472V1.57424C9.95472 1.38273 9.74582 1.29569 9.55432 1.29569H8.52718C8.28346 0.599316 7.67412 0.251127 6.97774 0.251127C6.28903 0.225235 5.66259 0.647534 5.4283 1.29569H4.41856C4.22705 1.29569 4.03554 1.38273 4.03554 1.57424V2.16615H2.7124C1.92832 2.17451 1.28685 2.79305 1.25 3.5763V12.4269C1.25 13.1929 1.94638 13.75 2.7124 13.75H11.2778C12.0439 13.75 12.7402 13.1929 12.7402 12.4269V3.57633C12.7034 2.79305 12.0619 2.17451 11.2778 2.16615ZM4.73189 1.99207H5.68942C5.85656 1.97168 5.99082 1.84452 6.02021 1.67871C6.12331 1.22972 6.51722 0.907418 6.97774 0.895289C7.43399 0.909118 7.82197 1.23241 7.91784 1.67871C7.94906 1.85025 8.09218 1.97904 8.26603 1.99207H9.25838V3.38483H4.73189V1.99207ZM12.0439 12.4269C12.0439 12.8099 11.6608 13.0537 11.2778 13.0537H2.7124C2.32939 13.0537 1.94638 12.8099 1.94638 12.4269V3.57633C1.9819 3.17766 2.31219 2.86986 2.7124 2.86256H4.03551V3.75045C4.05391 3.94552 4.22285 4.09144 4.41852 4.08124H9.55428C9.75355 4.09214 9.9278 3.94822 9.95469 3.75045V2.86253H11.2778C11.678 2.86986 12.0083 3.17763 12.0438 3.5763L12.0439 12.4269Z" fill="#7147E8" stroke="#7147E8" stroke-width="0.2"/>
<path d="M5.66293 9.52502C5.55676 9.4131 5.3805 9.40679 5.2666 9.51088L4.36072 10.3743L3.97856 9.97795C3.87239 9.86603 3.69613 9.85974 3.58223 9.9638C3.47259 10.0787 3.47259 10.2594 3.58223 10.3743L4.16254 10.9688C4.21277 11.025 4.28532 11.0561 4.3607 11.0537C4.43536 11.0526 4.50659 11.0221 4.55885 10.9688L5.66288 9.92135C5.77233 9.82095 5.77964 9.65081 5.67921 9.54139C5.67407 9.53567 5.66862 9.53022 5.66293 9.52502Z" fill="#7147E8" stroke="#7147E8" stroke-width="0.2"/>
<path d="M10.2427 9.99927H7.36768C7.2296 9.99927 7.11768 10.1671 7.11768 10.3742C7.11768 10.5813 7.2296 10.7492 7.36768 10.7492H10.2427C10.3808 10.7492 10.4927 10.5813 10.4927 10.3742C10.4927 10.1671 10.3808 9.99927 10.2427 9.99927Z" fill="#7147E8" stroke="#7147E8" stroke-width="0.2"/>
<path d="M5.66293 6.52575C5.55676 6.41384 5.3805 6.40752 5.2666 6.51161L4.36072 7.37501L3.97856 6.97868C3.87239 6.86676 3.69613 6.86045 3.58223 6.96454C3.47259 7.07941 3.47259 7.26014 3.58223 7.37501L4.16254 7.96949C4.21277 8.02574 4.28532 8.05684 4.3607 8.05443C4.43536 8.05337 4.50659 8.02284 4.55885 7.96949L5.66288 6.92209C5.77233 6.82168 5.77964 6.65154 5.67921 6.54212C5.67407 6.5364 5.66862 6.53096 5.66293 6.52575Z" fill="#7147E8" stroke="#7147E8" stroke-width="0.2"/>
<path d="M10.2427 7H7.36768C7.2296 7 7.11768 7.16788 7.11768 7.37497C7.11768 7.58207 7.2296 7.74995 7.36768 7.74995H10.2427C10.3808 7.74995 10.4927 7.58207 10.4927 7.37497C10.4927 7.16788 10.3808 7 10.2427 7Z" fill="#7147E8" stroke="#7147E8" stroke-width="0.2"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -66,11 +66,13 @@ export const getFeedsWithFilter: Function = (
};
export const getFeedCount: Function = (
entityLink?: string
entityLink?: string,
type?: ThreadType
): Promise<AxiosResponse> => {
return APIClient.get(`/feed/count`, {
params: {
entityLink: entityLink,
type,
},
});
};

View File

@ -20,6 +20,7 @@ import { ConfirmState } from '../ActivityFeedCard/ActivityFeedCard.interface';
export interface ActivityThreadPanelProp
extends HTMLAttributes<HTMLDivElement> {
threadLink: string;
threadType?: ThreadType;
open?: boolean;
postFeedHandler: (value: string, id: string) => void;
createThread: (data: CreateThread) => void;

View File

@ -13,8 +13,10 @@
import { Tabs } from 'antd';
import classNames from 'classnames';
import { isEqual } from 'lodash';
import React, { FC, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { PanelTab } from '../../../constants/feed.constants';
import { ThreadType } from '../../../generated/entity/feed/thread';
import FeedPanelOverlay from '../ActivityFeedPanel/FeedPanelOverlay';
import { ActivityThreadPanelProp } from './ActivityThreadPanel.interface';
@ -29,14 +31,21 @@ const ActivityThreadPanel: FC<ActivityThreadPanelProp> = ({
createThread,
deletePostHandler,
updateThreadHandler,
threadType,
}) => {
const { TabPane } = Tabs;
const [activeTab, setActiveTab] = useState<string>('1');
const [activeTab, setActiveTab] = useState<PanelTab>(PanelTab.TASKS);
const onTabChange = (key: string) => {
setActiveTab(key);
setActiveTab(key as PanelTab);
};
useEffect(() => {
if (isEqual(threadType, ThreadType.Conversation)) {
setActiveTab(PanelTab.CONVERSATIONS);
}
}, [threadType]);
useEffect(() => {
document.body.style.overflow = 'hidden';
}, []);
@ -60,7 +69,7 @@ const ActivityThreadPanel: FC<ActivityThreadPanelProp> = ({
activeKey={activeTab}
className="ant-tabs-custom-line ant-tabs-custom-threadpanel"
onChange={onTabChange}>
<TabPane key="1" tab="Tasks">
<TabPane key={PanelTab.TASKS} tab="Tasks">
<ActivityThreadPanelBody
createThread={createThread}
deletePostHandler={deletePostHandler}
@ -72,7 +81,7 @@ const ActivityThreadPanel: FC<ActivityThreadPanelProp> = ({
onTabChange={onTabChange}
/>
</TabPane>
<TabPane key="2" tab="Conversations">
<TabPane key={PanelTab.CONVERSATIONS} tab="Conversations">
<ActivityThreadPanelBody
createThread={createThread}
deletePostHandler={deletePostHandler}

View File

@ -18,7 +18,10 @@ import { isEqual, isUndefined } from 'lodash';
import React, { FC, Fragment, RefObject, useEffect, useState } from 'react';
import AppState from '../../../AppState';
import { getAllFeeds } from '../../../axiosAPIs/feedsAPI';
import { confirmStateInitialValue } from '../../../constants/feed.constants';
import {
confirmStateInitialValue,
PanelTab,
} from '../../../constants/feed.constants';
import { observerOptions } from '../../../constants/Mydata.constants';
import { Thread, ThreadType } from '../../../generated/entity/feed/thread';
import { Paging } from '../../../generated/type/paging';
@ -200,7 +203,9 @@ const ActivityThreadPanelBody: FC<ActivityThreadPanelBodyProp> = ({
useAfterMount(() => {
if (threadType === ThreadType.Task && !isThreadLoading) {
isEqual(threads.length, 0) && onTabChange && onTabChange('2');
isEqual(threads.length, 0) &&
onTabChange &&
onTabChange(PanelTab.CONVERSATIONS);
}
}, [threads, isThreadLoading]);

View File

@ -20,6 +20,7 @@ import { Link } from 'react-router-dom';
import AppState from '../../AppState';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { getTeamAndUserDetailsPath } from '../../constants/constants';
import { EntityField } from '../../constants/feed.constants';
import { observerOptions } from '../../constants/Mydata.constants';
import { SettledStatus } from '../../enums/axios.enum';
import { EntityType } from '../../enums/entity.enum';
@ -497,7 +498,7 @@ const DashboardDetails = ({
<Description
description={description}
entityFieldThreads={getEntityFieldThreadCounts(
'description',
EntityField.DESCRIPTION,
entityFieldThreadCount
)}
entityFqn={dashboardFQN}

View File

@ -17,6 +17,7 @@ import { ExtraInfo } from 'Models';
import React, { FC, useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { EntityField } from '../../constants/feed.constants';
import { OwnerType } from '../../enums/user.enum';
import { ChangeDescription } from '../../generated/entity/data/dashboard';
import { TagLabel } from '../../generated/type/tagLabel';
@ -68,7 +69,7 @@ const DashboardVersion: FC<DashboardVersionProp> = ({
const getDashboardDescription = () => {
const descriptionDiff = getDiffByFieldName(
'description',
EntityField.DESCRIPTION,
changeDescription
);
const oldDescription =

View File

@ -24,6 +24,7 @@ import React, {
import AppState from '../../AppState';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { getTeamAndUserDetailsPath, ROUTES } from '../../constants/constants';
import { EntityField } from '../../constants/feed.constants';
import { observerOptions } from '../../constants/Mydata.constants';
import { CSMode } from '../../enums/codemirror.enum';
import { EntityType, FqnPart } from '../../enums/entity.enum';
@ -34,6 +35,7 @@ import {
TableJoins,
TypeUsedToReturnUsageDetailsOfAnEntity,
} from '../../generated/entity/data/table';
import { ThreadType } from '../../generated/entity/feed/thread';
import { EntityReference } from '../../generated/type/entityReference';
import { Paging } from '../../generated/type/paging';
import { LabelType, State } from '../../generated/type/tagLabel';
@ -138,6 +140,7 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
fetchFeedHandler,
handleExtentionUpdate,
updateThreadHandler,
entityFieldTaskCount,
}: DatasetDetailsProps) => {
const [isEdit, setIsEdit] = useState(false);
const [followersCount, setFollowersCount] = useState(0);
@ -152,6 +155,9 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
});
const [threadLink, setThreadLink] = useState<string>('');
const [threadType, setThreadType] = useState<ThreadType>(
ThreadType.Conversation
);
const [selectedField, setSelectedField] = useState<string>('');
const [elementRef, isInView] = useInfiniteScroll(observerOptions);
@ -527,8 +533,11 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
};
};
const onThreadLinkSelect = (link: string) => {
const onThreadLinkSelect = (link: string, threadType?: ThreadType) => {
setThreadLink(link);
if (threadType) {
setThreadType(threadType);
}
};
const onThreadPanelClose = () => {
@ -615,8 +624,12 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
<div className="tw-col-span-3 tw--ml-5">
<Description
description={description}
entityFieldTasks={getEntityFieldThreadCounts(
EntityField.DESCRIPTION,
entityFieldTaskCount
)}
entityFieldThreads={getEntityFieldThreadCounts(
'description',
EntityField.DESCRIPTION,
entityFieldThreadCount
)}
entityFqn={datasetFQN}
@ -647,8 +660,12 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
FQN_SEPARATOR_CHAR
)}
columns={columns}
entityFieldTasks={getEntityFieldThreadCounts(
EntityField.COLUMNS,
entityFieldTaskCount
)}
entityFieldThreads={getEntityFieldThreadCounts(
'columns',
EntityField.COLUMNS,
entityFieldThreadCount
)}
entityFqn={datasetFQN}
@ -818,6 +835,7 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
open={Boolean(threadLink)}
postFeedHandler={postFeedHandler}
threadLink={threadLink}
threadType={threadType}
updateThreadHandler={updateThreadHandler}
onCancel={onThreadPanelClose}
/>

View File

@ -75,6 +75,7 @@ export interface DatasetDetailsProps {
isentityThreadLoading: boolean;
feedCount: number;
entityFieldThreadCount: EntityFieldThreadCount[];
entityFieldTaskCount: EntityFieldThreadCount[];
testMode: DatasetTestModeType;
tableTestCase: TableTest[];
showTestForm: boolean;

View File

@ -145,6 +145,7 @@ const DatasetDetailsProps = {
postFeedHandler: jest.fn(),
feedCount: 0,
entityFieldThreadCount: [],
entityFieldTaskCount: [],
showTestForm: false,
testMode: 'table' as DatasetTestModeType,
handleAddTableTestCase: jest.fn(),

View File

@ -16,6 +16,7 @@ import { cloneDeep, isEqual, isUndefined } from 'lodash';
import { ExtraInfo } from 'Models';
import React, { useEffect, useState } from 'react';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { EntityField } from '../../constants/feed.constants';
import { FqnPart } from '../../enums/entity.enum';
import { OwnerType } from '../../enums/user.enum';
import {
@ -138,7 +139,7 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
const getTableDescription = () => {
const descriptionDiff = getDiffByFieldName(
'description',
EntityField.DESCRIPTION,
changeDescription
);
const oldDescription =
@ -159,7 +160,10 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
const updatedColumns = (): Table['columns'] => {
const colList = cloneDeep(currentVersionData.columns);
const columnsDiff = getDiffByFieldName('columns', changeDescription);
const columnsDiff = getDiffByFieldName(
EntityField.COLUMNS,
changeDescription
);
const changedColName = getChangeColName(
columnsDiff?.added?.name ??
columnsDiff?.deleted?.name ??
@ -171,7 +175,7 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
columnsDiff?.added?.name ??
columnsDiff?.deleted?.name ??
columnsDiff?.updated?.name,
'description'
EntityField.DESCRIPTION
)
) {
const oldDescription =
@ -249,7 +253,7 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
return colList ?? [];
} else {
const columnsDiff = getDiffByFieldName(
'columns',
EntityField.COLUMNS,
changeDescription,
true
);

View File

@ -13,6 +13,7 @@
import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Popover } from 'antd';
import classNames from 'classnames';
import { cloneDeep, isNil, isUndefined, lowerCase } from 'lodash';
import { EntityFieldThreads, EntityTags, TagOption } from 'Models';
@ -22,6 +23,7 @@ import { useExpanded, useTable } from 'react-table';
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { getTableDetailsPath } from '../../constants/constants';
import { EntityField } from '../../constants/feed.constants';
import { SettledStatus } from '../../enums/axios.enum';
import { EntityType, FqnPart } from '../../enums/entity.enum';
import {
@ -31,6 +33,7 @@ import {
JoinedWith,
Table,
} from '../../generated/entity/data/table';
import { ThreadType } from '../../generated/entity/feed/thread';
import { Operation } from '../../generated/entity/policies/accessControl/rule';
import { TestCaseStatus } from '../../generated/tests/tableTest';
import { LabelType, State, TagLabel } from '../../generated/type/tagLabel';
@ -43,7 +46,6 @@ import {
} from '../../utils/CommonUtils';
import { ENTITY_LINK_SEPARATOR } from '../../utils/EntityUtils';
import { getFieldThreadElement } from '../../utils/FeedElementUtils';
import { getThreadValue } from '../../utils/FeedUtils';
import {
fetchGlossaryTerms,
getGlossaryTermlist,
@ -77,8 +79,9 @@ interface Props {
isReadOnly?: boolean;
entityFqn?: string;
entityFieldThreads?: EntityFieldThreads[];
entityFieldTasks?: EntityFieldThreads[];
onUpdate?: (columns: ModifiedTableColumn[]) => void;
onThreadLinkSelect?: (value: string) => void;
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
onEntityFieldSelect?: (value: string) => void;
}
@ -94,6 +97,7 @@ const EntityTable = ({
onThreadLinkSelect,
entityFqn,
tableConstraints,
entityFieldTasks,
}: Props) => {
const { isAdminUser, userPermissions } = useAuth();
const { isAuthDisabled } = useAuthContext();
@ -356,7 +360,13 @@ const EntityTable = ({
return searchedValue;
};
/* eslint-disable-next-line */
const checkPermission = () =>
isAdminUser ||
hasEditAccess ||
isAuthDisabled ||
userPermissions[Operation.UpdateDescription];
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const getColumnName = (cell: any) => {
const fqn = cell?.row?.original?.fullyQualifiedName || '';
const columnName = getPartialNameFromTableFQN(fqn, [FqnPart.NestedColumn]);
@ -367,9 +377,9 @@ const EntityTable = ({
: columnName;
};
/* eslint-disable-next-line */
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const onRequestDescriptionHandler = (cell: any) => {
const field = 'columns';
const field = EntityField.COLUMNS;
const value = getColumnName(cell);
history.push(
getRequestDescriptionPath(
@ -381,6 +391,20 @@ const EntityTable = ({
);
};
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const onUpdateDescriptionHandler = (cell: any) => {
const field = EntityField.COLUMNS;
const value = getColumnName(cell);
history.push(
getUpdateDescriptionPath(
EntityType.TABLE,
entityFqn as string,
field,
value
)
);
};
const prepareConstraintIcon = (
columnName: string,
columnConstraint?: string
@ -399,28 +423,41 @@ const EntityTable = ({
}
};
/* eslint-disable-next-line */
const handleUpdate = (column: Column, index: number, cell: any) => {
const check =
isAdminUser ||
hasEditAccess ||
isAuthDisabled ||
userPermissions[Operation.UpdateDescription];
if (check) {
handleEditColumn(column, index);
} else {
const field = 'columns';
const value = getColumnName(cell);
const handleUpdate = (column: Column, index: number) => {
handleEditColumn(column, index);
};
history.push(
getUpdateDescriptionPath(
EntityType.TABLE,
entityFqn as string,
field,
value
)
);
}
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const getRequestDescriptionElement = (cell: any) => {
const hasDescription = Boolean(cell.value);
return (
<button
className="tw-w-8 tw-h-8 tw-mr-1 tw-flex-none link-text focus:tw-outline-none tw-opacity-0 group-hover:tw-opacity-100"
data-testid="request-description"
onClick={() =>
hasDescription
? onUpdateDescriptionHandler(cell)
: onRequestDescriptionHandler(cell)
}>
<Popover
destroyTooltipOnHide
content={
hasDescription
? 'Request update description'
: 'Request description'
}
overlayClassName="ant-popover-request-description"
trigger="hover"
zIndex={9999}>
<SVGIcons
alt="request-description"
icon={Icons.REQUEST}
width="16px"
/>
</Popover>
</button>
);
};
useEffect(() => {
@ -675,60 +712,52 @@ const EntityTable = ({
</span>
)}
</div>
{!isReadOnly ? (
<Fragment>
<button
className="tw-self-start tw-w-8 tw-h-auto tw-opacity-0 tw-ml-1 group-hover:tw-opacity-100 focus:tw-outline-none"
onClick={() =>
handleUpdate(row.original, row.id, cell)
}>
<SVGIcons
alt="edit"
icon="icon-edit"
title="Edit"
width="12px"
/>
</button>
{isNil(
getThreadValue(
getColumnName(cell),
'description',
entityFieldThreads as EntityFieldThreads[]
)
) && !cell.value ? (
<button
className="focus:tw-outline-none tw-ml-1 tw-opacity-0 group-hover:tw-opacity-100 tw--mt-2"
data-testid="request-description"
onClick={() =>
onRequestDescriptionHandler(cell)
}>
<PopOver
position="top"
title="Request description"
trigger="mouseenter">
<div className="tw-flex tw--mt-1.5">
{!isReadOnly ? (
<Fragment>
{checkPermission() && (
<button
className="tw-self-start tw-w-8 tw-h-8 tw-opacity-0 tw-ml-1 group-hover:tw-opacity-100 focus:tw-outline-none tw-flex-none"
onClick={() =>
handleUpdate(row.original, row.id)
}>
<SVGIcons
alt="request-description"
className="tw-mt-2.5"
icon={Icons.REQUEST}
alt="edit"
icon="icon-edit"
title="Edit"
width="14px"
/>
</PopOver>
</button>
) : null}
{getFieldThreadElement(
getColumnName(cell),
'description',
entityFieldThreads as EntityFieldThreads[],
onThreadLinkSelect,
EntityType.TABLE,
entityFqn,
`columns${ENTITY_LINK_SEPARATOR}${getColumnName(
cell
)}${ENTITY_LINK_SEPARATOR}description`,
Boolean(cell.value)
)}
</Fragment>
) : null}
</button>
)}
{getRequestDescriptionElement(cell)}
{getFieldThreadElement(
getColumnName(cell),
EntityField.DESCRIPTION,
entityFieldThreads as EntityFieldThreads[],
onThreadLinkSelect,
EntityType.TABLE,
entityFqn,
`columns${ENTITY_LINK_SEPARATOR}${getColumnName(
cell
)}${ENTITY_LINK_SEPARATOR}description`,
Boolean(cell.value)
)}
{getFieldThreadElement(
getColumnName(cell),
EntityField.DESCRIPTION,
entityFieldTasks as EntityFieldThreads[],
onThreadLinkSelect,
EntityType.TABLE,
entityFqn,
`columns${ENTITY_LINK_SEPARATOR}${getColumnName(
cell
)}${ENTITY_LINK_SEPARATOR}description`,
Boolean(cell.value),
ThreadType.Task
)}
</Fragment>
) : null}
</div>
</div>
</div>
{checkIfJoinsAvailable(row.original.name) && (

View File

@ -17,6 +17,7 @@ import React, { RefObject, useCallback, useEffect, useState } from 'react';
import AppState from '../../AppState';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { getTeamAndUserDetailsPath } from '../../constants/constants';
import { EntityField } from '../../constants/feed.constants';
import { observerOptions } from '../../constants/Mydata.constants';
import { EntityType } from '../../enums/entity.enum';
import { OwnerType } from '../../enums/user.enum';
@ -392,7 +393,7 @@ const PipelineDetails = ({
<Description
description={description}
entityFieldThreads={getEntityFieldThreadCounts(
'description',
EntityField.DESCRIPTION,
entityFieldThreadCount
)}
entityFqn={pipelineFQN}

View File

@ -17,6 +17,7 @@ import { ExtraInfo } from 'Models';
import React, { FC, useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { EntityField } from '../../constants/feed.constants';
import { OwnerType } from '../../enums/user.enum';
import { ChangeDescription } from '../../generated/entity/data/pipeline';
import { TagLabel } from '../../generated/type/tagLabel';
@ -68,7 +69,7 @@ const PipelineVersion: FC<PipelineVersionProp> = ({
const getPipelineDescription = () => {
const descriptionDiff = getDiffByFieldName(
'description',
EntityField.DESCRIPTION,
changeDescription
);
const oldDescription =

View File

@ -16,17 +16,20 @@ import { Button, Popover } from 'antd';
import classNames from 'classnames';
import { observer } from 'mobx-react';
import PropTypes from 'prop-types';
import React, { useMemo } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import AppState from '../../AppState';
import { REACTION_LIST } from '../../constants/reactions.constant';
import { ReactionOperation } from '../../enums/reactions.enum';
import useImage from '../../hooks/useImage';
const Emoji = ({ reaction, reactionList, onReactionSelect }) => {
// get reaction object based on cureent reaction
const [reactionType, setReactionType] = useState(reaction);
const [isClicked, setIsClicked] = useState(false);
// get reaction object based on cureent reactionType
const reactionObject = useMemo(
() => REACTION_LIST.find((value) => value.reaction === reaction),
[reaction]
() => REACTION_LIST.find((value) => value.reaction === reactionType),
[reactionType]
);
const { image } = useImage(`emojis/${reactionObject.reaction}.png`);
@ -45,10 +48,13 @@ const Emoji = ({ reaction, reactionList, onReactionSelect }) => {
const userList = reactionList.map((reactionItem) => reactionItem.user.name);
const handleOnClick = () => {
const operation = isReacted
? ReactionOperation.REMOVE
: ReactionOperation.ADD;
onReactionSelect(reactionObject.reaction, operation);
if (!isClicked) {
const operation = isReacted
? ReactionOperation.REMOVE
: ReactionOperation.ADD;
onReactionSelect(reactionObject.reaction, operation);
setIsClicked(true);
}
};
const popoverContent = (
@ -56,10 +62,15 @@ const Emoji = ({ reaction, reactionList, onReactionSelect }) => {
className="tw-w-44 tw-break-normal tw-m-0 tw-p-0"
data-testid="popover-content">
{`${userList.join(', ')}`}{' '}
<span className="tw-font-semibold">{`reacted with ${reaction} emoji`}</span>
<span className="tw-font-semibold">{`reacted with ${reactionType} emoji`}</span>
</p>
);
useEffect(() => {
setReactionType(reaction);
setIsClicked(false);
}, [reaction]);
return (
<Popover
destroyTooltipOnHide

View File

@ -19,6 +19,7 @@ import {
Table,
TableData,
} from '../../generated/entity/data/table';
import { ThreadType } from '../../generated/entity/feed/thread';
import Searchbar from '../common/searchbar/Searchbar';
import EntityTable from '../EntityTable/EntityTable.component';
@ -33,7 +34,8 @@ type Props = {
isReadOnly?: boolean;
entityFqn?: string;
entityFieldThreads?: EntityFieldThreads[];
onThreadLinkSelect?: (value: string) => void;
entityFieldTasks?: EntityFieldThreads[];
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
onEntityFieldSelect?: (value: string) => void;
onUpdate?: (columns: Table['columns']) => void;
};
@ -51,6 +53,7 @@ const SchemaTab: FunctionComponent<Props> = ({
isReadOnly = false,
entityFqn,
tableConstraints,
entityFieldTasks,
}: Props) => {
const [searchText, setSearchText] = useState('');
@ -75,6 +78,7 @@ const SchemaTab: FunctionComponent<Props> = ({
<div className="col-sm-12">
<EntityTable
columnName={columnName}
entityFieldTasks={entityFieldTasks}
entityFieldThreads={entityFieldThreads}
entityFqn={entityFqn}
hasEditAccess={Boolean(hasEditAccess)}

View File

@ -22,6 +22,7 @@ import React, {
import AppState from '../../AppState';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { getTeamAndUserDetailsPath } from '../../constants/constants';
import { EntityField } from '../../constants/feed.constants';
import { observerOptions } from '../../constants/Mydata.constants';
import { EntityType } from '../../enums/entity.enum';
import { OwnerType } from '../../enums/user.enum';
@ -407,7 +408,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
<Description
description={description}
entityFieldThreads={getEntityFieldThreadCounts(
'description',
EntityField.DESCRIPTION,
entityFieldThreadCount
)}
entityFqn={topicFQN}

View File

@ -16,6 +16,7 @@ import { isUndefined } from 'lodash';
import { ExtraInfo } from 'Models';
import React, { FC, useEffect, useState } from 'react';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { EntityField } from '../../constants/feed.constants';
import { OwnerType } from '../../enums/user.enum';
import { ChangeDescription } from '../../generated/entity/data/topic';
import { TagLabel } from '../../generated/type/tagLabel';
@ -99,7 +100,7 @@ const TopicVersion: FC<TopicVersionProp> = ({
const getTableDescription = () => {
const descriptionDiff = getDiffByFieldName(
'description',
EntityField.DESCRIPTION,
changeDescription
);
const oldDescription =

View File

@ -13,6 +13,7 @@
import { EntityFieldThreads } from 'Models';
import { Table } from '../../../generated/entity/data/table';
import { ThreadType } from '../../../generated/entity/feed/thread';
export interface DescriptionProps {
entityName?: string;
@ -26,7 +27,8 @@ export interface DescriptionProps {
entityType?: string;
entityFqn?: string;
entityFieldThreads?: EntityFieldThreads[];
onThreadLinkSelect?: (value: string) => void;
entityFieldTasks?: EntityFieldThreads[];
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void;
onDescriptionEdit?: () => void;
onCancel?: () => void;
onDescriptionUpdate?: (value: string) => void;

View File

@ -11,14 +11,15 @@
* limitations under the License.
*/
import { Popover } from 'antd';
import classNames from 'classnames';
import { isUndefined } from 'lodash';
import { EntityFieldThreads } from 'Models';
import React, { FC, Fragment } from 'react';
import { useHistory } from 'react-router-dom';
import { useAuthContext } from '../../../authentication/auth-provider/AuthProvider';
import { TITLE_FOR_UPDATE_DESCRIPTION } from '../../../constants/constants';
import { EntityType } from '../../../enums/entity.enum';
import { EntityField } from '../../../constants/feed.constants';
import { ThreadType } from '../../../generated/entity/feed/thread';
import { Operation } from '../../../generated/entity/policies/accessControl/rule';
import { useAuth } from '../../../hooks/authHooks';
import { getEntityFeedLink } from '../../../utils/EntityUtils';
@ -26,11 +27,8 @@ import SVGIcons, { Icons } from '../../../utils/SvgUtils';
import {
getRequestDescriptionPath,
getUpdateDescriptionPath,
TASK_ENTITIES,
} from '../../../utils/TasksUtils';
import { ModalWithMarkdownEditor } from '../../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
import NonAdminAction from '../non-admin-action/NonAdminAction';
import PopOver from '../popover/PopOver';
import RichTextEditorPreviewer from '../rich-text-editor/RichTextEditorPreviewer';
import { DescriptionProps } from './Description.interface';
@ -50,6 +48,7 @@ const Description: FC<DescriptionProps> = ({
onEntityFieldSelect,
entityType,
entityFqn,
entityFieldTasks,
}) => {
const history = useHistory();
@ -57,6 +56,7 @@ const Description: FC<DescriptionProps> = ({
const { isAuthDisabled } = useAuthContext();
const thread = entityFieldThreads?.[0];
const tasks = entityFieldTasks?.[0];
const handleRequestDescription = () => {
history.push(
@ -71,45 +71,44 @@ const Description: FC<DescriptionProps> = ({
};
const checkPermission = () => {
if (!isAuthDisabled && !isAdminUser) {
return Boolean(
hasEditAccess || userPermissions[Operation.UpdateDescription]
);
}
return true;
return (
isAdminUser ||
Boolean(hasEditAccess) ||
userPermissions[Operation.UpdateDescription] ||
isAuthDisabled
);
};
const handleUpdate = () => {
if (checkPermission()) {
onDescriptionEdit && onDescriptionEdit();
} else if (TASK_ENTITIES.includes(entityType as EntityType)) {
handleUpdateDescription();
}
onDescriptionEdit && onDescriptionEdit();
};
const RequestDescriptionEl = ({
descriptionThread,
}: {
descriptionThread?: EntityFieldThreads;
}) => {
return isUndefined(descriptionThread) &&
onEntityFieldSelect &&
!description?.trim() ? (
const RequestDescriptionEl = () => {
const hasDescription = Boolean(description.trim());
return onEntityFieldSelect ? (
<button
className="focus:tw-outline-none tw-ml-2 tw--mt-6"
className="tw-w-8 tw-h-8 tw-mr-1 tw-flex-none link-text focus:tw-outline-none"
data-testid="request-description"
onClick={handleRequestDescription}>
<PopOver
position="top"
title="Request description"
trigger="mouseenter">
onClick={
hasDescription ? handleUpdateDescription : handleRequestDescription
}>
<Popover
destroyTooltipOnHide
content={
hasDescription
? 'Request update description'
: 'Request description'
}
overlayClassName="ant-popover-request-description"
trigger="hover"
zIndex={9999}>
<SVGIcons
alt="request-description"
className="tw-mt-2"
icon={Icons.REQUEST}
width="16px"
/>
</PopOver>
</Popover>
</button>
) : null;
};
@ -120,8 +119,8 @@ const Description: FC<DescriptionProps> = ({
descriptionThread?: EntityFieldThreads;
}) => {
return !isUndefined(descriptionThread) ? (
<p
className="link-text tw-ml-2 tw-w-8 tw-h-8 tw-flex-none"
<button
className="tw-w-8 tw-h-8 tw-mr-2 tw-flex-none link-text focus:tw-outline-none"
data-testid="description-thread"
onClick={() => onThreadLinkSelect?.(descriptionThread.entityLink)}>
<span className="tw-flex">
@ -131,48 +130,61 @@ const Description: FC<DescriptionProps> = ({
{descriptionThread.count}
</span>
</span>
</p>
</button>
) : (
<Fragment>
{description?.trim() && onThreadLinkSelect ? (
<p
className="link-text tw-flex-none tw-ml-2"
<button
className="tw-w-8 tw-h-8 tw-mr-2 tw-flex-none link-text focus:tw-outline-none"
data-testid="start-description-thread"
onClick={() =>
onThreadLinkSelect?.(
getEntityFeedLink(entityType, entityFqn, 'description')
getEntityFeedLink(
entityType,
entityFqn,
EntityField.DESCRIPTION
)
)
}>
<SVGIcons alt="comments" icon={Icons.COMMENT_PLUS} width="20px" />
</p>
</button>
) : null}
</Fragment>
);
};
const getDescriptionTaskElement = () => {
return !isUndefined(tasks) ? (
<button
className="tw-w-8 tw-h-8 tw-mr-2 tw-flex-none link-text focus:tw-outline-none"
data-testid="description-task"
onClick={() => onThreadLinkSelect?.(tasks.entityLink, ThreadType.Task)}>
<span className="tw-flex">
<SVGIcons alt="tasks" icon={Icons.TASK_ICON} width="16px" />{' '}
<span className="tw-ml-1" data-testid="description-tasks-count">
{' '}
{tasks.count}
</span>
</span>
</button>
) : null;
};
const DescriptionActions = () => {
return !isReadOnly ? (
<div
className={classNames(
'tw-w-5 tw-min-w-max tw-flex',
description?.trim() ? 'tw-pl-1' : ''
)}>
<NonAdminAction
isOwner={
checkPermission() ||
TASK_ENTITIES.includes(entityType as EntityType)
}
title={TITLE_FOR_UPDATE_DESCRIPTION}>
<div className={classNames('tw-w-5 tw-min-w-max tw-flex tw--mt-0.5')}>
{checkPermission() && (
<button
className="focus:tw-outline-none tw-self-baseline"
className="tw-w-7 tw-h-8 tw-flex-none focus:tw-outline-none"
data-testid="edit-description"
onClick={handleUpdate}>
<SVGIcons alt="edit" icon="icon-edit" title="Edit" width="16px" />
</button>
</NonAdminAction>
)}
<RequestDescriptionEl descriptionThread={thread} />
<RequestDescriptionEl />
<DescriptionThreadEl descriptionThread={thread} />
{getDescriptionTaskElement()}
</div>
) : null;
};

View File

@ -15,6 +15,7 @@ import classNames from 'classnames';
import { isUndefined } from 'lodash';
import { EntityFieldThreads } from 'Models';
import React, { Fragment } from 'react';
import { EntityField } from '../../../constants/feed.constants';
import { Table } from '../../../generated/entity/data/table';
import { Operation } from '../../../generated/entity/policies/accessControl/rule';
import { getHtmlForNonAdminAction } from '../../../utils/CommonUtils';
@ -130,7 +131,7 @@ const DescriptionV1 = ({
<button
className="focus:tw-outline-none tw-ml-2 tw--mt-6"
data-testid="request-description"
onClick={() => onEntityFieldSelect?.('description')}>
onClick={() => onEntityFieldSelect?.(EntityField.DESCRIPTION)}>
<PopOver
position="top"
title="Request description"
@ -168,7 +169,11 @@ const DescriptionV1 = ({
data-testid="start-description-thread"
onClick={() =>
onThreadLinkSelect?.(
getEntityFeedLink(entityType, entityFqn, 'description')
getEntityFeedLink(
entityType,
entityFqn,
EntityField.DESCRIPTION
)
)
}>
<SVGIcons

View File

@ -62,3 +62,15 @@ export enum TaskOperation {
RESOLVE = 'resolve',
REJECT = 'close',
}
export enum PanelTab {
TASKS = 'tasks',
CONVERSATIONS = 'conversations',
}
export enum EntityField {
DESCRIPTION = 'description',
COLUMNS = 'columns',
TAGS = 'tags',
TASKS = 'tasks',
}

View File

@ -56,6 +56,7 @@ import {
getServiceDetailsPath,
getTeamAndUserDetailsPath,
} from '../../constants/constants';
import { EntityField } from '../../constants/feed.constants';
import { observerOptions } from '../../constants/Mydata.constants';
import { EntityType, FqnPart, TabSpecificField } from '../../enums/entity.enum';
import { ServiceCategory } from '../../enums/service.enum';
@ -657,7 +658,7 @@ const DatabaseSchemaPage: FunctionComponent = () => {
blurWithBodyBG
description={description}
entityFieldThreads={getEntityFieldThreadCounts(
'description',
EntityField.DESCRIPTION,
entityFieldThreadCount
)}
entityFqn={databaseSchemaFQN}

View File

@ -27,7 +27,6 @@ import { useHistory, useParams } from 'react-router-dom';
import AppState from '../../AppState';
import {
getAllFeeds,
getFeedCount,
postFeedById,
postThread,
} from '../../axiosAPIs/feedsAPI';
@ -90,6 +89,7 @@ import {
getCurrentUserId,
getEntityMissingError,
getEntityName,
getFeedCounts,
getFields,
getPartialNameFromTableFQN,
} from '../../utils/CommonUtils';
@ -174,6 +174,9 @@ const DatasetDetailsPage: FunctionComponent = () => {
const [entityFieldThreadCount, setEntityFieldThreadCount] = useState<
EntityFieldThreadCount[]
>([]);
const [entityFieldTaskCount, setEntityFieldTaskCount] = useState<
EntityFieldThreadCount[]
>([]);
// Data Quality tab state
const [testMode, setTestMode] = useState<DatasetTestModeType>('table');
@ -490,23 +493,13 @@ const DatasetDetailsPage: FunctionComponent = () => {
}, [activeTab]);
const getEntityFeedCount = () => {
getFeedCount(getEntityFeedLink(EntityType.TABLE, tableFQN))
.then((res: AxiosResponse) => {
if (res.data) {
setFeedCount(res.data.totalCount);
setEntityFieldThreadCount(res.data.counts);
} else {
showErrorToast(
jsonData['api-error-messages']['fetch-entity-feed-count-error']
);
}
})
.catch((err: AxiosError) => {
showErrorToast(
err,
jsonData['api-error-messages']['fetch-entity-feed-count-error']
);
});
getFeedCounts(
EntityType.TABLE,
tableFQN,
setEntityFieldThreadCount,
setEntityFieldTaskCount,
setFeedCount
);
};
const saveUpdatedTableData = (updatedData: Table): Promise<AxiosResponse> => {
@ -1030,6 +1023,7 @@ const DatasetDetailsPage: FunctionComponent = () => {
deleted={deleted}
description={description}
descriptionUpdateHandler={descriptionUpdateHandler}
entityFieldTaskCount={entityFieldTaskCount}
entityFieldThreadCount={entityFieldThreadCount}
entityLineage={entityLineage}
entityLineageHandler={entityLineageHandler}

View File

@ -31,6 +31,7 @@ import ProfilePicture from '../../../components/common/ProfilePicture/ProfilePic
import RichTextEditor from '../../../components/common/rich-text-editor/RichTextEditor';
import TitleBreadcrumb from '../../../components/common/title-breadcrumb/title-breadcrumb.component';
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
import { EntityField } from '../../../constants/feed.constants';
import { EntityType } from '../../../enums/entity.enum';
import {
CreateThread,
@ -99,7 +100,7 @@ const RequestDescription = () => {
const back = () => history.goBack();
const getColumnDetails = useCallback(() => {
if (!isNil(field) && !isNil(value) && field === 'columns') {
if (!isNil(field) && !isNil(value) && field === EntityField.COLUMNS) {
const column = getSanitizeValue.split(FQN_SEPARATOR_CHAR).slice(-1);
const columnObject = getColumnObject(column[0], entityData.columns || []);
@ -127,7 +128,7 @@ const RequestDescription = () => {
if (field && value) {
return `${field}${ENTITY_LINK_SEPARATOR}${value}${ENTITY_LINK_SEPARATOR}description`;
} else {
return 'description';
return EntityField.DESCRIPTION;
}
};

View File

@ -29,6 +29,7 @@ import { postThread } from '../../../axiosAPIs/feedsAPI';
import ProfilePicture from '../../../components/common/ProfilePicture/ProfilePicture';
import TitleBreadcrumb from '../../../components/common/title-breadcrumb/title-breadcrumb.component';
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
import { EntityField } from '../../../constants/feed.constants';
import { EntityType } from '../../../enums/entity.enum';
import {
CreateThread,
@ -104,7 +105,7 @@ const UpdateDescription = () => {
}, [field, entityData]);
const getColumnDetails = useCallback(() => {
if (!isNil(field) && !isNil(value) && field === 'columns') {
if (!isNil(field) && !isNil(value) && field === EntityField.COLUMNS) {
return (
<div data-testid="column-details">
<p className="tw-font-semibold">Column Details</p>
@ -136,7 +137,7 @@ const UpdateDescription = () => {
if (field && value) {
return `${field}${ENTITY_LINK_SEPARATOR}${value}${ENTITY_LINK_SEPARATOR}description`;
} else {
return 'description';
return EntityField.DESCRIPTION;
}
};

View File

@ -61,6 +61,7 @@ import {
PAGE_SIZE,
pagingObject,
} from '../../constants/constants';
import { EntityField } from '../../constants/feed.constants';
import { observerOptions } from '../../constants/Mydata.constants';
import { EntityType, TabSpecificField } from '../../enums/entity.enum';
import { ServiceCategory } from '../../enums/service.enum';
@ -674,7 +675,7 @@ const DatabaseDetails: FunctionComponent = () => {
blurWithBodyBG
description={description}
entityFieldThreads={getEntityFieldThreadCounts(
'description',
EntityField.DESCRIPTION,
entityFieldThreadCount
)}
entityFqn={databaseFQN}

View File

@ -193,6 +193,7 @@ const TourPage = () => {
deletePostHandler={handleCountChange}
description={mockDatasetData.description}
descriptionUpdateHandler={handleCountChange}
entityFieldTaskCount={[]}
entityFieldThreadCount={[]}
entityLineage={mockDatasetData.entityLineage}
entityLineageHandler={handleCountChange}

View File

@ -1132,6 +1132,7 @@ code {
}
.ant-popover-feed > .ant-popover-content > .ant-popover-inner,
.ant-popover-card > .ant-popover-content > .ant-popover-inner,
.ant-popover-request-description > .ant-popover-content > .ant-popover-inner,
.ant-popover-feed-reactions > .ant-popover-content > .ant-popover-inner {
border-radius: 6px;
border: 1px solid #dde3ea;
@ -1341,3 +1342,7 @@ div.ant-typography-ellipsis-custom {
color: #7147e8;
background-color: #7147e825;
}
.table-query-editor pre.CodeMirror-line {
padding-right: 60px !important;
}

View File

@ -11,9 +11,11 @@
* limitations under the License.
*/
import { AxiosError, AxiosResponse } from 'axios';
import classNames from 'classnames';
import { capitalize, isEmpty, isNull, isUndefined } from 'lodash';
import {
EntityFieldThreadCount,
RecentlySearched,
RecentlySearchedData,
RecentlyViewed,
@ -22,6 +24,7 @@ import {
import React, { FormEvent } from 'react';
import { reactLocalStorage } from 'reactjs-localstorage';
import AppState from '../AppState';
import { getFeedCount } from '../axiosAPIs/feedsAPI';
import { Button } from '../components/buttons/Button/Button';
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
import {
@ -36,12 +39,15 @@ import {
} from '../constants/regex.constants';
import { EntityType, FqnPart, TabSpecificField } from '../enums/entity.enum';
import { Ownership } from '../enums/mydata.enum';
import { ThreadType } from '../generated/entity/feed/thread';
import { EntityReference, User } from '../generated/entity/teams/user';
import { getTitleCase } from './EntityUtils';
import jsonData from '../jsons/en';
import { getEntityFeedLink, getTitleCase } from './EntityUtils';
import Fqn from './Fqn';
import { getExplorePathWithInitFilters } from './RouterUtils';
import { serviceTypeLogo } from './ServiceUtils';
import SVGIcons, { Icons } from './SvgUtils';
import { showErrorToast } from './ToastUtils';
export const arraySorterByKey = (
key: string,
@ -631,3 +637,48 @@ export const getExploreLinkByFilter = (
export const replaceSpaceWith_ = (text: string) => {
return text.replace(/\s/g, '_');
};
export const getFeedCounts = (
entityType: string,
entityFQN: string,
conversationCallback: (
value: React.SetStateAction<EntityFieldThreadCount[]>
) => void,
taskCallback: (value: React.SetStateAction<EntityFieldThreadCount[]>) => void,
entityCallback: (value: React.SetStateAction<number>) => void
) => {
getFeedCount(
getEntityFeedLink(entityType, entityFQN),
ThreadType.Conversation
)
.then((res: AxiosResponse) => {
if (res.data) {
entityCallback(res.data.totalCount);
conversationCallback(res.data.counts);
} else {
throw jsonData['api-error-messages']['fetch-entity-feed-count-error'];
}
})
.catch((err: AxiosError) => {
showErrorToast(
err,
jsonData['api-error-messages']['fetch-entity-feed-count-error']
);
});
getFeedCount(getEntityFeedLink(entityType, entityFQN), ThreadType.Task)
.then((res: AxiosResponse) => {
if (res.data) {
entityCallback(res.data.totalCount);
taskCallback(res.data.counts);
} else {
throw jsonData['api-error-messages']['fetch-entity-feed-count-error'];
}
})
.catch((err: AxiosError) => {
showErrorToast(
err,
jsonData['api-error-messages']['fetch-entity-feed-count-error']
);
});
};

View File

@ -26,6 +26,7 @@ import {
DESCRIPTIONLENGTH,
getTeamAndUserDetailsPath,
} from '../constants/constants';
import { EntityField } from '../constants/feed.constants';
import { ChangeType } from '../enums/entity.enum';
import { Column } from '../generated/entity/data/table';
import {
@ -301,7 +302,7 @@ export const feedSummaryFromatter = (
);
break;
} else if (fieldChange?.name?.endsWith('description')) {
} else if (fieldChange?.name?.endsWith(EntityField.DESCRIPTION)) {
summary = (
<p key={uniqueId()}>
{`${
@ -321,7 +322,7 @@ export const feedSummaryFromatter = (
);
break;
} else if (fieldChange?.name === 'columns') {
} else if (fieldChange?.name === EntityField.COLUMNS) {
const length = value?.length ?? 0;
summary = (
<p key={uniqueId()}>
@ -419,7 +420,7 @@ export const feedSummaryFromatter = (
break;
}
case fieldChange?.name === 'description': {
case fieldChange?.name === EntityField.DESCRIPTION: {
summary = (
<p key={uniqueId()}>
{`${
@ -523,7 +524,7 @@ export const summaryFormatter = (fieldChange: FieldChange) => {
? fieldChange?.oldValue
: '{}'
);
if (fieldChange.name === 'columns') {
if (fieldChange.name === EntityField.COLUMNS) {
return `columns ${value?.map((val: any) => val?.name).join(', ')}`;
} else if (
fieldChange.name === 'tags' ||

View File

@ -11,10 +11,11 @@
* limitations under the License.
*/
import { isEmpty, isUndefined } from 'lodash';
import { isEmpty, isEqual, isUndefined } from 'lodash';
import { EntityFieldThreads } from 'Models';
import React, { Fragment } from 'react';
import { entityUrlMap } from '../constants/feed.constants';
import { ThreadType } from '../generated/entity/feed/thread';
import { EntityReference } from '../generated/entity/teams/user';
import { getEntityFeedLink } from './EntityUtils';
import { getThreadField } from './FeedUtils';
@ -24,11 +25,12 @@ export const getFieldThreadElement = (
columnName: string,
columnField: string,
entityFieldThreads: EntityFieldThreads[],
onThreadLinkSelect?: (value: string) => void,
onThreadLinkSelect?: (value: string, threadType?: ThreadType) => void,
entityType?: string,
entityFqn?: string,
entityField?: string,
flag = true
flag = true,
threadType?: ThreadType
) => {
let threadValue: EntityFieldThreads = {} as EntityFieldThreads;
@ -39,27 +41,36 @@ export const getFieldThreadElement = (
}
});
const isTaskType = isEqual(threadType, ThreadType.Task);
return !isEmpty(threadValue) ? (
<p
className="link-text tw-w-8 tw-h-8 tw-flex-none"
<button
className="link-text tw-self-start tw-w-8 tw-h-8 tw-flex-none tw-mx-1"
data-testid="field-thread"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onThreadLinkSelect?.(threadValue.entityLink);
onThreadLinkSelect?.(
threadValue.entityLink,
isTaskType ? ThreadType.Task : ThreadType.Conversation
);
}}>
<span className="tw-flex">
<SVGIcons alt="comments" icon={Icons.COMMENT} width="20px" />
<SVGIcons
alt="comments"
icon={isTaskType ? Icons.TASK_ICON : Icons.COMMENT}
width={isTaskType ? '16px' : '20px'}
/>
<span className="tw-ml-1" data-testid="field-thread-count">
{threadValue.count}
</span>
</span>
</p>
</button>
) : (
<Fragment>
{entityType && entityFqn && entityField && flag ? (
<p
className="link-text tw-self-start tw-w-8 tw-h-8 tw-opacity-0 tw-ml-1 group-hover:tw-opacity-100 tw-flex-none"
{entityType && entityFqn && entityField && flag && !isTaskType ? (
<button
className="link-text tw-self-start tw-w-8 tw-h-8 tw-flex-none tw-mx-1 tw-opacity-0 group-hover:tw-opacity-100"
data-testid="start-field-thread"
onClick={(e) => {
e.preventDefault();
@ -69,7 +80,7 @@ export const getFieldThreadElement = (
);
}}>
<SVGIcons alt="comments" icon={Icons.COMMENT_PLUS} width="20px" />
</p>
</button>
) : null}
</Fragment>
);

View File

@ -144,6 +144,7 @@ import IconTableGrey from '../assets/svg/table-grey.svg';
import IconTable from '../assets/svg/table.svg';
import IconTagGrey from '../assets/svg/tag-grey.svg';
import IconTag from '../assets/svg/tag.svg';
import IconTaskColor from '../assets/svg/Task-ic.svg';
import IconTeamsGrey from '../assets/svg/teams-grey.svg';
import IconTerns from '../assets/svg/terms.svg';
import IconTier from '../assets/svg/tier.svg';
@ -313,6 +314,7 @@ export const Icons = {
STAR: 'ic-star',
MENTIONS: 'ic-mentions',
COMMENT_GREY: 'ic-comment-grey',
TASK_ICON: 'task-icon',
};
const SVGIcons: FunctionComponent<Props> = ({
@ -890,6 +892,10 @@ const SVGIcons: FunctionComponent<Props> = ({
case Icons.ALERT_BELL:
IconComponent = IconAlertBell;
break;
case Icons.TASK_ICON:
IconComponent = IconTaskColor;
break;
case Icons.TASK: