mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-08 16:38:04 +00:00
supported task assignee with owner name in task thread (#17435)
* supported task assigne with owner name in task thread * fix the mis-allignment due to conversation thread present * fix the cypress test and assignee not updating in the task after editing
This commit is contained in:
parent
455e66bc37
commit
def0fcc5eb
@ -19,7 +19,9 @@ import {
|
||||
|
||||
const owner = 'admin';
|
||||
const assignee = 'adam.matthews2';
|
||||
const assigneeDisplayName = 'Adam Matthews';
|
||||
const secondAssignee = 'aaron_johnson0';
|
||||
const secondAssigneeDisplayName = 'Aaron Johnson';
|
||||
|
||||
export type TaskDetails = {
|
||||
assignee?: string;
|
||||
@ -44,9 +46,14 @@ export const verifyTaskDetails = (
|
||||
expect(matches).to.not.be.null;
|
||||
});
|
||||
|
||||
// creator of the task
|
||||
cy.get('[data-testid="owner-link"]').should('contain', owner);
|
||||
|
||||
cy.get(`[data-testid="${taskAssignee ?? assignee}"]`).should('be.visible');
|
||||
// assignee of the task
|
||||
cy.get('[data-testid="task-assignees"] [data-testid="owner-link"]').should(
|
||||
'contain',
|
||||
taskAssignee ?? assigneeDisplayName
|
||||
);
|
||||
};
|
||||
|
||||
export const editAssignee = () => {
|
||||
@ -71,7 +78,14 @@ export const editAssignee = () => {
|
||||
|
||||
verifyResponseStatusCode('@editAssignee', 200);
|
||||
|
||||
cy.get(`[data-testid="${assignee}"]`).should('be.visible');
|
||||
cy.get('[data-testid="task-assignees"] [data-testid="owner-link"]').should(
|
||||
'contain',
|
||||
assigneeDisplayName
|
||||
);
|
||||
cy.get('[data-testid="task-assignees"] [data-testid="owner-link"]').should(
|
||||
'contain',
|
||||
secondAssigneeDisplayName
|
||||
);
|
||||
};
|
||||
|
||||
export const createDescriptionTask = (
|
||||
|
@ -369,9 +369,10 @@ describe('Task flow should work', { tags: 'DataAssets' }, () => {
|
||||
verifyResponseStatusCode('@taskFeed', 200);
|
||||
|
||||
// verify the task details
|
||||
verifyTaskDetails(/#(\d+) Request to update description for/, USER_NAME);
|
||||
|
||||
cy.get(`[data-testid="${USER_NAME}"]`).should('be.visible');
|
||||
verifyTaskDetails(
|
||||
/#(\d+) Request to update description for/,
|
||||
USER_DETAILS.firstName
|
||||
);
|
||||
|
||||
// Accept the description suggestion which is created
|
||||
cy.get('.ant-btn-compact-first-item').contains('Accept Suggestion').click();
|
||||
|
@ -245,6 +245,21 @@ const ActivityFeedProvider = ({ children, user }: Props) => {
|
||||
setEntityThread([...threads]);
|
||||
}, []);
|
||||
|
||||
const updateEntityThread = useCallback(
|
||||
(thread: Thread) => {
|
||||
setEntityThread((prev) => {
|
||||
return prev.map((threadItem) => {
|
||||
if (threadItem.id === thread.id) {
|
||||
return thread;
|
||||
} else {
|
||||
return threadItem;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
[setEntityThread]
|
||||
);
|
||||
|
||||
const deleteFeed = useCallback(
|
||||
async (threadId: string, postId: string, isThread: boolean) => {
|
||||
if (isThread) {
|
||||
@ -457,6 +472,7 @@ const ActivityFeedProvider = ({ children, user }: Props) => {
|
||||
hideDrawer,
|
||||
updateEditorFocus,
|
||||
setActiveThread,
|
||||
updateEntityThread,
|
||||
entityPaging,
|
||||
userId: user ?? currentUser?.id ?? '',
|
||||
testCaseResolutionStatus,
|
||||
@ -481,6 +497,7 @@ const ActivityFeedProvider = ({ children, user }: Props) => {
|
||||
hideDrawer,
|
||||
updateEditorFocus,
|
||||
setActiveThread,
|
||||
updateEntityThread,
|
||||
entityPaging,
|
||||
user,
|
||||
currentUser,
|
||||
|
@ -34,6 +34,7 @@ export interface ActivityFeedProviderContextType {
|
||||
focusReplyEditor: boolean;
|
||||
entityPaging: Paging;
|
||||
setActiveThread: (thread?: Thread) => void;
|
||||
updateEntityThread: (thread: Thread) => void;
|
||||
userId: string;
|
||||
deleteFeed: (
|
||||
threadId: string,
|
||||
|
@ -11,6 +11,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
TaskType,
|
||||
ThreadTaskStatus,
|
||||
} from '../../../generated/entity/feed/thread';
|
||||
|
||||
export const mockThreadData = [
|
||||
{
|
||||
id: '465b2dfb-300e-45f5-a1a6-e19c6225e9e7',
|
||||
@ -26,6 +31,32 @@ export const mockThreadData = [
|
||||
postsCount: 0,
|
||||
posts: [],
|
||||
relativeDay: 'Today',
|
||||
task: {
|
||||
id: 153,
|
||||
type: TaskType.RequestTag,
|
||||
assignees: [
|
||||
{
|
||||
id: 'ef1b9e8a-75ba-4c12-9874-034c135be925',
|
||||
type: 'team',
|
||||
name: 'Sales',
|
||||
fullyQualifiedName: 'Sales',
|
||||
displayName: 'Sales',
|
||||
deleted: false,
|
||||
},
|
||||
{
|
||||
id: '83bd6004-9237-47a1-b917-ef006b3192d7',
|
||||
type: 'user',
|
||||
name: 'aaron_johnson0',
|
||||
fullyQualifiedName: 'aaron_johnson0',
|
||||
displayName: 'Aaron Johnson',
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
status: ThreadTaskStatus.Open,
|
||||
oldValue: '[]',
|
||||
suggestion:
|
||||
'[{"tagFQN":"PII.None","source":"Classification","name":"None","description":"Non PII"}]',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '40c2faec-0159-4d86-9b15-c17f3e1c081b',
|
||||
|
@ -11,7 +11,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { findByTestId, queryByTestId, render } from '@testing-library/react';
|
||||
import {
|
||||
findByTestId,
|
||||
getByText,
|
||||
queryByTestId,
|
||||
render,
|
||||
} from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { mockThreadData } from './ActivityThread.mock';
|
||||
@ -50,6 +55,10 @@ jest.mock('../ActivityFeedCard/FeedCardFooter/FeedCardFooter', () => {
|
||||
return jest.fn().mockReturnValue(<p>FeedCardFooter</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../common/OwnerLabel/OwnerLabel.component', () => ({
|
||||
OwnerLabel: jest.fn().mockReturnValue(<div>OwnerLabel</div>),
|
||||
}));
|
||||
|
||||
describe('Test ActivityThreadList Component', () => {
|
||||
it('Check if it has all child elements', async () => {
|
||||
const { container } = render(
|
||||
@ -145,4 +154,16 @@ describe('Test ActivityThreadList Component', () => {
|
||||
expect(thread2MainMessage).toBeInTheDocument();
|
||||
expect(thread2QuickReplyEditor).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render task assignee when there is task', async () => {
|
||||
const { container } = render(
|
||||
<ActivityThreadList {...mockActivityThreadListProp} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
expect(getByText(container, 'label.assignee-plural:')).toBeInTheDocument();
|
||||
expect(getByText(container, 'OwnerLabel')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -10,7 +10,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Card, Space } from 'antd';
|
||||
import { Card, Typography } from 'antd';
|
||||
import { isEqual } from 'lodash';
|
||||
import React, { FC, Fragment } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -29,7 +29,7 @@ import {
|
||||
} from '../../../generated/entity/feed/thread';
|
||||
import { getFeedListWithRelativeDays } from '../../../utils/FeedUtils';
|
||||
import { getTaskDetailPath } from '../../../utils/TasksUtils';
|
||||
import AssigneeList from '../../common/AssigneeList/AssigneeList';
|
||||
import { OwnerLabel } from '../../common/OwnerLabel/OwnerLabel.component';
|
||||
import ActivityFeedCard from '../ActivityFeedCard/ActivityFeedCard';
|
||||
import FeedCardFooter from '../ActivityFeedCard/FeedCardFooter/FeedCardFooter';
|
||||
import ActivityFeedEditor from '../ActivityFeedEditor/ActivityFeedEditor';
|
||||
@ -166,14 +166,12 @@ const ActivityThreadList: FC<ActivityThreadListProp> = ({
|
||||
</div>
|
||||
) : null}
|
||||
{thread.task && (
|
||||
<Space wrap className="m-y-xs" size={4}>
|
||||
<span className="text-grey-muted">
|
||||
<div className="d-flex m-y-xs gap-2">
|
||||
<Typography.Text className="text-grey-muted">
|
||||
{t('label.assignee-plural')}:{' '}
|
||||
</span>
|
||||
<AssigneeList
|
||||
assignees={thread.task.assignees || []}
|
||||
/>
|
||||
</Space>
|
||||
</Typography.Text>
|
||||
<OwnerLabel owners={thread.task.assignees} />
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</Fragment>
|
||||
|
@ -34,9 +34,9 @@ jest.mock('../ActivityFeedProvider/ActivityFeedProvider', () => ({
|
||||
default: 'ActivityFeedProvider',
|
||||
}));
|
||||
|
||||
jest.mock('../../../components/common/AssigneeList/AssigneeList', () => {
|
||||
return jest.fn().mockImplementation(() => <p>AssigneeList</p>);
|
||||
});
|
||||
jest.mock('../../common/OwnerLabel/OwnerLabel.component', () => ({
|
||||
OwnerLabel: jest.fn().mockReturnValue(<p>OwnerLabel</p>),
|
||||
}));
|
||||
|
||||
jest.mock('../../../components/common/PopOverCard/EntityPopOverCard', () => {
|
||||
return jest.fn().mockImplementation(({ children }) => children);
|
||||
@ -102,4 +102,15 @@ describe('Test TaskFeedCard Component', () => {
|
||||
expect(screen.getByTestId('task-status-icon-open')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('redirect-task-button-link')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should render OwnerLabel when show thread is true', async () => {
|
||||
await act(async () => {
|
||||
render(<TaskFeedCard {...mockProps} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
});
|
||||
|
||||
expect(screen.getByText('label.assignee-plural:')).toBeInTheDocument();
|
||||
expect(screen.getByText('OwnerLabel')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -20,7 +20,6 @@ import { useHistory } from 'react-router-dom';
|
||||
import { ReactComponent as TaskCloseIcon } from '../../../assets/svg/ic-close-task.svg';
|
||||
import { ReactComponent as TaskOpenIcon } from '../../../assets/svg/ic-open-task.svg';
|
||||
import { ReactComponent as ThreadIcon } from '../../../assets/svg/thread.svg';
|
||||
import AssigneeList from '../../../components/common/AssigneeList/AssigneeList';
|
||||
import EntityPopOverCard from '../../../components/common/PopOverCard/EntityPopOverCard';
|
||||
import UserPopOverCard from '../../../components/common/PopOverCard/UserPopOverCard';
|
||||
import {
|
||||
@ -38,6 +37,7 @@ import { getEntityFQN, getEntityType } from '../../../utils/FeedUtils';
|
||||
|
||||
import { TASK_TYPES } from '../../../constants/Task.constant';
|
||||
import { getTaskDetailPath } from '../../../utils/TasksUtils';
|
||||
import { OwnerLabel } from '../../common/OwnerLabel/OwnerLabel.component';
|
||||
import ProfilePicture from '../../common/ProfilePicture/ProfilePicture';
|
||||
import { useActivityFeedProvider } from '../ActivityFeedProvider/ActivityFeedProvider';
|
||||
import ActivityFeedActions from '../Shared/ActivityFeedActions';
|
||||
@ -206,7 +206,7 @@ const TaskFeedCard = ({
|
||||
</Col>
|
||||
{!showThread ? (
|
||||
<Col span={24}>
|
||||
<div className="d-flex items-center p-l-lg gap-2">
|
||||
<div className="d-flex items-start p-l-lg gap-2">
|
||||
{postLength > 0 && (
|
||||
<>
|
||||
<div className="thread-users-profile-pic">
|
||||
@ -228,24 +228,23 @@ const TaskFeedCard = ({
|
||||
className="d-flex items-center thread-count cursor-pointer m-l-xs"
|
||||
onClick={!hidePopover ? showReplies : noop}>
|
||||
<ThreadIcon width={20} />{' '}
|
||||
<span className="text-xs p-l-xss">{postLength}</span>
|
||||
<span className="text-xs p-t-xss p-l-xss">
|
||||
{postLength}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Typography.Text
|
||||
className={
|
||||
className={classNames(
|
||||
'p-t-xss',
|
||||
postLength > 0
|
||||
? 'm-l-sm text-sm text-grey-muted'
|
||||
: 'text-sm text-grey-muted'
|
||||
}>
|
||||
)}>
|
||||
{`${t('label.assignee-plural')}: `}
|
||||
</Typography.Text>
|
||||
<AssigneeList
|
||||
assignees={feed?.task?.assignees || []}
|
||||
className="d-flex gap-1"
|
||||
showUserName={false}
|
||||
/>
|
||||
<OwnerLabel className="p-t-05" owners={feed?.task?.assignees} />
|
||||
</div>
|
||||
</Col>
|
||||
) : null}
|
||||
|
@ -54,10 +54,6 @@ jest.mock('../../../ActivityFeed/ActivityFeedEditor/ActivityFeedEditor', () => {
|
||||
));
|
||||
});
|
||||
|
||||
jest.mock('../../../common/AssigneeList/AssigneeList', () => {
|
||||
return jest.fn().mockImplementation(() => <p>AssigneeList</p>);
|
||||
});
|
||||
|
||||
jest.mock('../../../common/OwnerLabel/OwnerLabel.component', () => ({
|
||||
OwnerLabel: jest.fn().mockImplementation(() => <p>OwnerLabel</p>),
|
||||
}));
|
||||
@ -171,6 +167,16 @@ describe('Test TaskFeedCard component', () => {
|
||||
expect(activityFeedCard).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should render the assignee and creator of task', async () => {
|
||||
render(<TaskTab {...mockProps} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
|
||||
expect(screen.getByText('label.assignee-plural:')).toBeInTheDocument();
|
||||
expect(screen.getByText('label.created-by:')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('OwnerLabel')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should not render task action button to the task owner if task has reviewer', async () => {
|
||||
render(<TaskTab {...mockProps} hasGlossaryReviewer />, {
|
||||
wrapper: MemoryRouter,
|
||||
|
@ -103,7 +103,6 @@ import ActivityFeedEditor, {
|
||||
EditorContentRef,
|
||||
} from '../../../ActivityFeed/ActivityFeedEditor/ActivityFeedEditor';
|
||||
import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
|
||||
import AssigneeList from '../../../common/AssigneeList/AssigneeList';
|
||||
import InlineEdit from '../../../common/InlineEdit/InlineEdit.component';
|
||||
import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component';
|
||||
import EntityPopOverCard from '../../../common/PopOverCard/EntityPopOverCard';
|
||||
@ -144,7 +143,7 @@ export const TaskTab = ({
|
||||
const { isAdminUser } = useAuth();
|
||||
const {
|
||||
postFeed,
|
||||
setActiveThread,
|
||||
updateEntityThread,
|
||||
fetchUpdatedThread,
|
||||
updateTestCaseIncidentStatus,
|
||||
testCaseResolutionStatus,
|
||||
@ -233,7 +232,7 @@ export const TaskTab = ({
|
||||
const [comment, setComment] = useState('');
|
||||
const [isEditAssignee, setIsEditAssignee] = useState<boolean>(false);
|
||||
const [options, setOptions] = useState<Option[]>([]);
|
||||
|
||||
const [isAssigneeLoading, setIsAssigneeLoading] = useState<boolean>(false);
|
||||
const { initialAssignees, assigneeOptions } = useMemo(() => {
|
||||
const initialAssignees = generateOptions(taskDetails?.assignees ?? []);
|
||||
const assigneeOptions = unionBy(
|
||||
@ -773,6 +772,7 @@ export const TaskTab = ({
|
||||
}, [taskDetails, isTaskDescription]);
|
||||
|
||||
const handleAssigneeUpdate = async () => {
|
||||
setIsAssigneeLoading(true);
|
||||
const updatedTaskThread = {
|
||||
...taskThread,
|
||||
task: {
|
||||
@ -787,9 +787,11 @@ export const TaskTab = ({
|
||||
const patch = compare(taskThread, updatedTaskThread);
|
||||
const data = await updateThread(taskThread.id, patch);
|
||||
setIsEditAssignee(false);
|
||||
setActiveThread(data);
|
||||
updateEntityThread(data);
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
} finally {
|
||||
setIsAssigneeLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -806,12 +808,13 @@ export const TaskTab = ({
|
||||
<TaskTabIncidentManagerHeader thread={taskThread} />
|
||||
) : (
|
||||
<div
|
||||
className={classNames('d-flex justify-between', {
|
||||
className={classNames('d-flex justify-between flex-wrap gap-2', {
|
||||
'flex-column': isEditAssignee,
|
||||
})}>
|
||||
<div className={classNames('gap-2', { 'flex-center': !isEditAssignee })}>
|
||||
<div className="d-flex gap-2" data-testid="task-assignees">
|
||||
{isEditAssignee ? (
|
||||
<Form
|
||||
className="w-full"
|
||||
form={assigneesForm}
|
||||
layout="vertical"
|
||||
onFinish={handleAssigneeUpdate}>
|
||||
@ -830,6 +833,7 @@ export const TaskTab = ({
|
||||
<InlineEdit
|
||||
className="assignees-edit-input"
|
||||
direction="horizontal"
|
||||
isLoading={isAssigneeLoading}
|
||||
onCancel={() => {
|
||||
setIsEditAssignee(false);
|
||||
assigneesForm.setFieldValue('assignees', initialAssignees);
|
||||
@ -859,15 +863,12 @@ export const TaskTab = ({
|
||||
<Typography.Text className="text-grey-muted">
|
||||
{t('label.assignee-plural')}:{' '}
|
||||
</Typography.Text>
|
||||
<AssigneeList
|
||||
assignees={taskDetails?.assignees ?? []}
|
||||
showUserName={false}
|
||||
/>
|
||||
<OwnerLabel owners={taskDetails?.assignees} />
|
||||
{(isCreator || hasEditAccess) &&
|
||||
!isTaskClosed &&
|
||||
owners.length === 0 ? (
|
||||
<Button
|
||||
className="flex-center p-0"
|
||||
className="flex-center p-0 h-auto"
|
||||
data-testid="edit-assignees"
|
||||
icon={<EditIcon color={DE_ACTIVE_COLOR} width="14px" />}
|
||||
size="small"
|
||||
@ -878,7 +879,7 @@ export const TaskTab = ({
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className={classNames('gap-2', { 'flex-center': !isEditAssignee })}>
|
||||
<div className="d-flex gap-2">
|
||||
<Typography.Text className="text-grey-muted">
|
||||
{t('label.created-by')}:{' '}
|
||||
</Typography.Text>
|
||||
|
@ -21,7 +21,6 @@ import { TestCaseResolutionStatusTypes } from '../../../../generated/tests/testC
|
||||
import { formatDateTime } from '../../../../utils/date-time/DateTimeUtils';
|
||||
import { getEntityName } from '../../../../utils/EntityUtils';
|
||||
import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
|
||||
import AssigneeList from '../../../common/AssigneeList/AssigneeList';
|
||||
import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component';
|
||||
import RichTextEditorPreviewer from '../../../common/RichTextEditor/RichTextEditorPreviewer';
|
||||
import Severity from '../../../DataQuality/IncidentManager/Severity/Severity.component';
|
||||
@ -136,7 +135,7 @@ const TaskTabIncidentManagerHeader = ({ thread }: { thread: Thread }) => {
|
||||
isEmpty(thread.task?.assignees) ? (
|
||||
NO_DATA_PLACEHOLDER
|
||||
) : (
|
||||
<AssigneeList assignees={thread.task?.assignees ?? []} />
|
||||
<OwnerLabel owners={thread.task?.assignees} />
|
||||
)}
|
||||
</div>
|
||||
<div className="gap-2 flex-center">
|
||||
|
@ -151,9 +151,6 @@ jest.mock('../../../common/OwnerLabel/OwnerLabel.component', () => {
|
||||
.mockImplementation(() => <div>OwnerLabel.component</div>),
|
||||
};
|
||||
});
|
||||
jest.mock('../../../common/AssigneeList/AssigneeList', () => {
|
||||
return jest.fn().mockImplementation(() => <div>AssigneeList.component</div>);
|
||||
});
|
||||
jest.mock(
|
||||
'../../../DataQuality/IncidentManager/Severity/Severity.component',
|
||||
() => {
|
||||
@ -177,10 +174,7 @@ describe('Test TaskTabIncidentManagerHeader component', () => {
|
||||
await screen.findByTestId('task-resolution-steps')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByTestId('failure-reason')).toBeInTheDocument();
|
||||
expect(await screen.findByText('OwnerLabel.component')).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText('AssigneeList.component')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findAllByText('OwnerLabel.component')).toHaveLength(2);
|
||||
expect(await screen.findByText('Severity.component')).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText('RichTextEditorPreviewer.component')
|
||||
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { HTMLAttributes } from 'react';
|
||||
import { EntityReference } from '../../../generated/entity/type';
|
||||
|
||||
export interface AssigneeListProps extends HTMLAttributes<HTMLDivElement> {
|
||||
assignees: EntityReference[];
|
||||
showUserName?: boolean;
|
||||
}
|
||||
|
||||
export enum UserTeam {
|
||||
User = 'user',
|
||||
Team = 'team',
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import UserPopOverCard from '../PopOverCard/UserPopOverCard';
|
||||
import { AssigneeListProps, UserTeam } from './AssigneeList.interface';
|
||||
|
||||
const AssigneeList: FC<AssigneeListProps> = ({
|
||||
assignees,
|
||||
showUserName = true,
|
||||
}) => {
|
||||
return (
|
||||
<div className="d-flex gap-1 flex-wrap">
|
||||
{assignees.map((assignee) => (
|
||||
<UserPopOverCard
|
||||
displayName={getEntityName(assignee)}
|
||||
key={assignee.name}
|
||||
showUserName={showUserName}
|
||||
type={assignee.type as UserTeam}
|
||||
userName={assignee.name ?? ''}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AssigneeList;
|
@ -134,7 +134,7 @@ export const OwnerLabel = ({
|
||||
})}
|
||||
{remainingOwnersCount > 0 && (
|
||||
<Button
|
||||
className="more-owners-button text-xs"
|
||||
className="more-owners-button text-xs h-auto"
|
||||
size="small"
|
||||
type="link"
|
||||
onClick={() => setShowAllOwners(!showAllOwners)}>
|
||||
|
@ -33,6 +33,7 @@ import {
|
||||
TERM_ADMIN,
|
||||
} from '../../../constants/constants';
|
||||
import { TabSpecificField } from '../../../enums/entity.enum';
|
||||
import { OwnerType } from '../../../enums/user.enum';
|
||||
import { EntityReference } from '../../../generated/type/entityReference';
|
||||
import { useApplicationStore } from '../../../hooks/useApplicationStore';
|
||||
import { useUserProfile } from '../../../hooks/user-profile/useUserProfile';
|
||||
@ -40,7 +41,6 @@ import { getUserByName } from '../../../rest/userAPI';
|
||||
import { getNonDeletedTeams } from '../../../utils/CommonUtils';
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import { getUserWithImage } from '../../../utils/UserDataUtils';
|
||||
import { UserTeam } from '../AssigneeList/AssigneeList.interface';
|
||||
import Loader from '../Loader/Loader';
|
||||
import ProfilePicture from '../ProfilePicture/ProfilePicture';
|
||||
|
||||
@ -107,12 +107,12 @@ const UserRoles = React.memo(({ userName }: { userName: string }) => {
|
||||
const PopoverContent = React.memo(
|
||||
({
|
||||
userName,
|
||||
type = UserTeam.User,
|
||||
type = OwnerType.USER,
|
||||
}: {
|
||||
userName: string;
|
||||
type: UserTeam;
|
||||
type: OwnerType;
|
||||
}) => {
|
||||
const isTeam = type === UserTeam.Team;
|
||||
const isTeam = type === OwnerType.TEAM;
|
||||
const [, , user = {}] = useUserProfile({
|
||||
permission: true,
|
||||
name: userName,
|
||||
@ -175,18 +175,18 @@ const PopoverTitle = React.memo(
|
||||
({
|
||||
userName,
|
||||
profilePicture,
|
||||
type = UserTeam.User,
|
||||
type = OwnerType.USER,
|
||||
}: {
|
||||
userName: string;
|
||||
profilePicture: JSX.Element;
|
||||
type: UserTeam;
|
||||
type: OwnerType;
|
||||
}) => {
|
||||
const history = useHistory();
|
||||
|
||||
const [, , userData] = useUserProfile({
|
||||
permission: true,
|
||||
name: userName,
|
||||
isTeam: type === UserTeam.Team,
|
||||
isTeam: type === OwnerType.TEAM,
|
||||
});
|
||||
|
||||
const onTitleClickHandler = (path: string) => {
|
||||
@ -221,7 +221,7 @@ const PopoverTitle = React.memo(
|
||||
interface Props extends HTMLAttributes<HTMLDivElement> {
|
||||
userName: string;
|
||||
displayName?: ReactNode;
|
||||
type?: UserTeam;
|
||||
type?: OwnerType;
|
||||
showUserName?: boolean;
|
||||
showUserProfile?: boolean;
|
||||
profileWidth?: number;
|
||||
@ -231,7 +231,7 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
|
||||
const UserPopOverCard: FC<Props> = ({
|
||||
userName,
|
||||
displayName,
|
||||
type = UserTeam.User,
|
||||
type = OwnerType.USER,
|
||||
showUserName = false,
|
||||
showUserProfile = true,
|
||||
children,
|
||||
@ -241,7 +241,7 @@ const UserPopOverCard: FC<Props> = ({
|
||||
const profilePicture = (
|
||||
<ProfilePicture
|
||||
avatarType="outlined"
|
||||
isTeam={type === UserTeam.Team}
|
||||
isTeam={type === OwnerType.TEAM}
|
||||
name={userName}
|
||||
width={`${profileWidth}`}
|
||||
/>
|
||||
@ -271,7 +271,7 @@ const UserPopOverCard: FC<Props> = ({
|
||||
)}
|
||||
data-testid={userName}
|
||||
to={
|
||||
type === UserTeam.Team
|
||||
type === OwnerType.TEAM
|
||||
? getTeamAndUserDetailsPath(userName)
|
||||
: getUserPath(userName ?? '')
|
||||
}>
|
||||
|
@ -501,6 +501,9 @@
|
||||
.p-t-xs {
|
||||
padding-top: @padding-xs;
|
||||
}
|
||||
.p-t-05 {
|
||||
padding-top: 2px;
|
||||
}
|
||||
.p-t-xss {
|
||||
padding-top: @padding-xss;
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import TurndownService from 'turndown';
|
||||
import { ReactComponent as AddIcon } from '../assets/svg/added-icon.svg';
|
||||
import { ReactComponent as UpdatedIcon } from '../assets/svg/updated-icon.svg';
|
||||
import { MentionSuggestionsItem } from '../components/ActivityFeed/FeedEditor/FeedEditor.interface';
|
||||
import { UserTeam } from '../components/common/AssigneeList/AssigneeList.interface';
|
||||
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
|
||||
import {
|
||||
EntityField,
|
||||
@ -41,6 +40,7 @@ import {
|
||||
} from '../constants/Feeds.constants';
|
||||
import { EntityType, FqnPart, TabSpecificField } from '../enums/entity.enum';
|
||||
import { SearchIndex } from '../enums/search.enum';
|
||||
import { OwnerType } from '../enums/user.enum';
|
||||
import {
|
||||
CardStyle,
|
||||
EntityTestResultSummaryObject,
|
||||
@ -200,7 +200,8 @@ export async function suggestions(
|
||||
ENTITY_URL_MAP[entityType as EntityUrlMapType],
|
||||
hit._source.name
|
||||
),
|
||||
type: hit._index === SearchIndex.USER ? UserTeam.User : UserTeam.Team,
|
||||
type:
|
||||
hit._index === SearchIndex.USER ? OwnerType.USER : OwnerType.TEAM,
|
||||
name: hit._source.name,
|
||||
displayName: hit._source.displayName,
|
||||
};
|
||||
|
@ -18,12 +18,12 @@ import { isUndefined } from 'lodash';
|
||||
import { ServiceTypes } from 'Models';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { UserTeam } from '../components/common/AssigneeList/AssigneeList.interface';
|
||||
import UserPopOverCard from '../components/common/PopOverCard/UserPopOverCard';
|
||||
import RichTextEditorPreviewer from '../components/common/RichTextEditor/RichTextEditorPreviewer';
|
||||
import TagsViewer from '../components/Tag/TagsViewer/TagsViewer';
|
||||
import { NO_DATA_PLACEHOLDER } from '../constants/constants';
|
||||
import { ServiceCategory } from '../enums/service.enum';
|
||||
import { OwnerType } from '../enums/user.enum';
|
||||
import { Database } from '../generated/entity/data/database';
|
||||
import { Pipeline } from '../generated/entity/data/pipeline';
|
||||
import { EntityReference } from '../generated/entity/type';
|
||||
@ -101,7 +101,7 @@ export const getServiceMainTabColumns = (
|
||||
displayName={owner.displayName}
|
||||
key={owner.id}
|
||||
profileWidth={20}
|
||||
type={owner.type as UserTeam}
|
||||
type={owner.type as OwnerType}
|
||||
userName={owner.name ?? ''}
|
||||
/>
|
||||
))
|
||||
|
Loading…
x
Reference in New Issue
Block a user