mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-12 01:08:18 +00:00
* ✨ 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:
parent
e3223f6a20
commit
38b04b76a9
@ -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 |
@ -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,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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}
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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 =
|
||||
|
@ -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}
|
||||
/>
|
||||
|
@ -75,6 +75,7 @@ export interface DatasetDetailsProps {
|
||||
isentityThreadLoading: boolean;
|
||||
feedCount: number;
|
||||
entityFieldThreadCount: EntityFieldThreadCount[];
|
||||
entityFieldTaskCount: EntityFieldThreadCount[];
|
||||
testMode: DatasetTestModeType;
|
||||
tableTestCase: TableTest[];
|
||||
showTestForm: boolean;
|
||||
|
@ -145,6 +145,7 @@ const DatasetDetailsProps = {
|
||||
postFeedHandler: jest.fn(),
|
||||
feedCount: 0,
|
||||
entityFieldThreadCount: [],
|
||||
entityFieldTaskCount: [],
|
||||
showTestForm: false,
|
||||
testMode: 'table' as DatasetTestModeType,
|
||||
handleAddTableTestCase: jest.fn(),
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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) && (
|
||||
|
@ -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}
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
@ -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)}
|
||||
|
@ -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}
|
||||
|
@ -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 =
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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}
|
||||
|
@ -193,6 +193,7 @@ const TourPage = () => {
|
||||
deletePostHandler={handleCountChange}
|
||||
description={mockDatasetData.description}
|
||||
descriptionUpdateHandler={handleCountChange}
|
||||
entityFieldTaskCount={[]}
|
||||
entityFieldThreadCount={[]}
|
||||
entityLineage={mockDatasetData.entityLineage}
|
||||
entityLineageHandler={handleCountChange}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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']
|
||||
);
|
||||
});
|
||||
};
|
||||
|
@ -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' ||
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user