diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/TaskFeedCard/TaskFeedCardNew.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/TaskFeedCard/TaskFeedCardNew.component.tsx index 4b866725f00..6aae8280d66 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/TaskFeedCard/TaskFeedCardNew.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/TaskFeedCard/TaskFeedCardNew.component.tsx @@ -18,7 +18,6 @@ import { isEmpty, isEqual, isUndefined, lowerCase } from 'lodash'; import React, { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useHistory } from 'react-router-dom'; -import { ReactComponent as AssigneesIcon } from '../../../assets/svg/ic-assignees.svg'; import { ReactComponent as TaskCloseIcon } from '../../../assets/svg/ic-close-task.svg'; import { ReactComponent as TaskOpenIcon } from '../../../assets/svg/ic-open-task.svg'; import { ReactComponent as ReplyIcon } from '../../../assets/svg/ic-reply-2.svg'; @@ -359,8 +358,8 @@ const TaskFeedCard = ({ ? 'task-card-assignee' : '' }`}> - (false); const [recentComment, setRecentComment] = useState(''); + const shouldEditAssignee = + (isCreator || hasEditAccess) && !isTaskClosed && owners.length === 0; const onSave = () => { postFeed(comment, taskThread?.id ?? '') .catch(() => { @@ -838,6 +839,12 @@ export const TaskTabNew = ({ ); setUsersList(filterData); } catch (error) { + showErrorToast( + error as AxiosError, + t('server.entity-fetch-error', { + entity: t('label.assignee'), + }) + ); setUsersList([]); } }, []); @@ -858,6 +865,10 @@ export const TaskTabNew = ({ setTaskAction(latestAction); }, [latestAction]); + const handleEditClick = () => { + setIsEditAssignee(true); + }; + const taskHeader = isTaskTestCaseResult ? ( ) : ( @@ -937,13 +948,13 @@ export const TaskTabNew = ({ ) : ( <> - + {t('label.assignee-plural')} - + {taskThread?.task?.assignees?.length === 1 ? ( ) : ( )} - {(isCreator || hasEditAccess) && - !isTaskClosed && - owners.length === 0 ? ( - } - size="small" - type="text" - onClick={() => setIsEditAssignee(true)} - /> - ) : null} > )} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/nav-bar.less b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/nav-bar.less index e0df5cf04a6..597f79e5155 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/nav-bar.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/nav-bar.less @@ -10,113 +10,112 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - @import (reference) '../../styles/variables.less'; +@import (reference) '../../styles/variables.less'; - .navbar-container { - display: flex; - align-items: center; - justify-content: space-between; - - > div { - height: 40px; - - .ant-input-wrapper.ant-input-group, - .ant-input-affix-wrapper { - height: 40px; - } - } - - .search-container { - width: 35vw; - } - - .app-user-icon { - display: flex; - justify-content: space-between; - align-items: center; - cursor: pointer; - - .username { - font-weight: 500; - line-height: 21px; - } - } - } - - .domain-dropdown-menu { - max-height: 400px; - overflow-y: auto; - } - - .slide-in-top { - animation: slide-in-top 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; - } - - @keyframes slide-in-top { - 0% { - transform: translate(-50%, 100%); - } - - 100% { - transform: translate(-50%, 0); - } - } - - .ant-btn.domain-nav-btn { - background-color: @white; - border: 1px solid @grey-15; - color: @text-color; - height: 40px; - - .domain-text { - max-width: 15vw; - color: inherit; - } - - &:hover, - &:focus { - background-color: @primary-button-background; - border: 1px solid @alert-info-icon-bg-1; - color: @text-color; - } - - &.domain-active { - color: @primary-color; - border: 1px solid @primary-color; - background-color: @primary-button-background; - } - } - - .ant-alert.ant-alert-info.refresh-alert { - width: 470px; - bottom: 30px; - align-items: center; - z-index: 9999; - margin: 0 auto; - box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.12); - border-radius: 10px; - border: 1px solid @text-color; - background: @text-color; - color: @white; - position: fixed; - left: 50%; - transform: translateX(-50%); - - .ant-alert-message { - color: @white; - } - - .ant-alert-description { - color: @grey-4; - } - - .ant-alert-content { - border-right: 1px solid @white; - } - - .ant-btn { - font-weight: 700; - color: @white; - } - } - +.navbar-container { + display: flex; + align-items: center; + justify-content: space-between; + + > div { + height: 40px; + + .ant-input-wrapper.ant-input-group, + .ant-input-affix-wrapper { + height: 40px; + } + } + + .search-container { + width: 35vw; + } + + .app-user-icon { + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + + .username { + font-weight: 500; + line-height: 21px; + } + } +} + +.domain-dropdown-menu { + max-height: 400px; + overflow-y: auto; +} + +.slide-in-top { + animation: slide-in-top 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; +} + +@keyframes slide-in-top { + 0% { + transform: translate(-50%, 100%); + } + + 100% { + transform: translate(-50%, 0); + } +} + +.ant-btn.domain-nav-btn { + background-color: @white; + border: 1px solid @grey-15; + color: @text-color; + height: 40px; + + .domain-text { + max-width: 15vw; + color: inherit; + } + + &:hover, + &:focus { + background-color: @primary-button-background; + border: 1px solid @alert-info-icon-bg-1; + color: @text-color; + } + + &.domain-active { + color: @primary-color; + border: 1px solid @primary-color; + background-color: @primary-button-background; + } +} + +.ant-alert.ant-alert-info.refresh-alert { + width: 470px; + bottom: 30px; + align-items: center; + z-index: 9999; + margin: 0 auto; + box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.12); + border-radius: 10px; + border: 1px solid @text-color; + background: @text-color; + color: @white; + position: fixed; + left: 50%; + transform: translateX(-50%); + + .ant-alert-message { + color: @white; + } + + .ant-alert-description { + color: @grey-4; + } + + .ant-alert-content { + border-right: 1px solid @white; + } + + .ant-btn { + font-weight: 700; + color: @white; + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerAvtar/OwnerAvatar.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerAvtar/OwnerAvatar.tsx index cbd87051cd1..0fbf5a48e0e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerAvtar/OwnerAvatar.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerAvtar/OwnerAvatar.tsx @@ -13,17 +13,20 @@ import Icon from '@ant-design/icons'; import { Typography } from 'antd'; import React from 'react'; +import { ReactComponent as AssigneesIcon } from '../../../assets/svg/ic-assignees.svg'; import { ReactComponent as IconTeamsGrey } from '../../../assets/svg/teams-grey.svg'; import { OwnerType } from '../../../enums/user.enum'; import { EntityReference } from '../../../generated/entity/data/table'; import { getEntityName } from '../../../utils/EntityUtils'; import ProfilePicture from '../ProfilePicture/ProfilePicture'; import './owner-avtar.less'; + interface OwnerAvatarProps { owner: EntityReference; - isCompactView: boolean; - inheritedIcon?: React.ReactNode; avatarSize?: number; + isCompactView?: boolean; + inheritedIcon?: React.ReactNode; + isAssignee?: boolean; } export const OwnerAvatar: React.FC = ({ @@ -31,9 +34,49 @@ export const OwnerAvatar: React.FC = ({ isCompactView, inheritedIcon, avatarSize = 32, + isAssignee, }) => { const displayName = getEntityName(owner); + if (isAssignee) { + return ( + + {owner.type === OwnerType.TEAM ? ( + + + + {displayName} + + + ) : ( + + + + {inheritedIcon && ( + + {inheritedIcon} + + )} + + )} + + ); + } + return owner.type === OwnerType.TEAM ? ( = ({ @@ -34,6 +35,7 @@ export const OwnerItem: React.FC = ({ className, ownerDisplayName, avatarSize = 32, + isAssignee, }) => { const displayName = getEntityName(owner); const ownerPath = getOwnerPath(owner); @@ -78,6 +80,7 @@ export const OwnerItem: React.FC = ({ @@ -91,6 +94,7 @@ export const OwnerItem: React.FC = ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/OwnerLabel.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/OwnerLabel.component.tsx index 3cb71cc3f90..2c6d1764214 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/OwnerLabel.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/OwnerLabel.component.tsx @@ -11,11 +11,12 @@ * limitations under the License. */ -import { Typography } from 'antd'; +import { Button, Typography } from 'antd'; import classNames from 'classnames'; import { reverse } from 'lodash'; import React, { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg'; import { OwnerType } from '../../../enums/user.enum'; import { EntityReference } from '../../../generated/entity/type'; import { NoOwnerFound } from '../NoOwner/NoOwnerFound'; @@ -41,6 +42,8 @@ export const OwnerLabel = ({ tooltipText, isCompactView = true, // renders owner profile followed by its name avatarSize = 32, + isAssignee = false, + onEditClick, }: OwnerLabelProps) => { const { t } = useTranslation(); const [showAllOwners, setShowAllOwners] = useState(false); @@ -87,9 +90,92 @@ export const OwnerLabel = ({ className, ]); + const showMultipleTypeTeam = owners.filter( + (owner) => owner.type === OwnerType.TEAM + ); + const showMultipleTypeVisibleUser = owners + .filter((owner) => owner.type === OwnerType.USER) + .slice(0, maxVisibleOwners) + .reverse(); + const showMultipleTypeRemainingUser = owners + .filter((owner) => owner.type === OwnerType.USER) + .slice(maxVisibleOwners); + const renderMultipleType = useMemo(() => { + return ( + + + {showMultipleTypeTeam.map((owner, index) => ( + + + + ))} + + + {showMultipleTypeVisibleUser.map((owner, index) => ( + + + + ))} + + {showMultipleTypeRemainingUser.length > 0 && ( + + + + )} + {hasPermission && ( + } + type="text" + onClick={onEditClick} + /> + )} + + + + ); + }, [ + showMultipleTypeTeam, + showMultipleTypeVisibleUser, + showMultipleTypeRemainingUser, + avatarSize, + className, + isCompactView, + ownerDisplayName, + hasPermission, + onEditClick, + isDropdownOpen, + owners, + setIsDropdownOpen, + setShowAllOwners, + showAllOwners, + ]); const ownerElements = useMemo(() => { const hasOwners = owners && owners.length > 0; - // Show all owners when "more" is clicked, regardless of view mode const visibleOwners = showAllOwners ? owners @@ -118,19 +204,23 @@ export const OwnerLabel = ({ ); } + if (isAssignee) { + return renderMultipleType; + } + return ( {ownerElementsNonCompactView} - + void; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/owner-label.less b/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/owner-label.less index a69ad0a467f..c0dd8f423ba 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/owner-label.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/owner-label.less @@ -127,7 +127,3 @@ } } } - -.owner-label-container { - max-width: 148px; -}