mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-11 08:43:31 +00:00
Fix(ui): Task assignee new styling (#21049)
* Task asignee new styling * Task asignee new styling * fixed styling of asignee * removed unused prop * replaced position of edit button in tasktab * fixed sonar test * renamed props * fixed comments * fixed comments * fixed alignment issue of owner label * removed zIndex from owner label * removed unnecessery classes
This commit is contained in:
parent
2465ce391e
commit
7fed86cc99
@ -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'
|
||||
: ''
|
||||
}`}>
|
||||
<AssigneesIcon height={20} width={20} />
|
||||
<OwnerLabel
|
||||
isAssignee
|
||||
avatarSize={24}
|
||||
isCompactView={false}
|
||||
owners={feed?.task?.assignees}
|
||||
|
@ -75,7 +75,9 @@
|
||||
color: @primary-heading-color;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.owner-label-container {
|
||||
max-width: 148px;
|
||||
}
|
||||
.domain-link-text.render-domain-lebel-style {
|
||||
color: @grey-700;
|
||||
display: inline-block;
|
||||
|
@ -48,7 +48,6 @@ import React, {
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg';
|
||||
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';
|
||||
@ -435,6 +434,8 @@ export const TaskTabNew = ({
|
||||
const [hasAddedComment, setHasAddedComment] = useState<boolean>(false);
|
||||
const [recentComment, setRecentComment] = useState<string>('');
|
||||
|
||||
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 ? (
|
||||
<TaskTabIncidentManagerHeaderNew thread={taskThread} />
|
||||
) : (
|
||||
@ -937,13 +948,13 @@ export const TaskTabNew = ({
|
||||
</Form>
|
||||
) : (
|
||||
<>
|
||||
<Col className="flex items-center gap-2 text-grey-muted" span={8}>
|
||||
<Col className="flex gap-2 text-grey-muted" span={8}>
|
||||
<AssigneesIcon height={16} />
|
||||
<Typography.Text className="incident-manager-details-label @grey-8">
|
||||
{t('label.assignee-plural')}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
<Col className="flex items-center gap-2" span={16}>
|
||||
<Col className="flex gap-2" span={16}>
|
||||
{taskThread?.task?.assignees?.length === 1 ? (
|
||||
<div className="d-flex items-center gap-2">
|
||||
<UserPopOverCard
|
||||
@ -965,24 +976,15 @@ export const TaskTabNew = ({
|
||||
</div>
|
||||
) : (
|
||||
<OwnerLabel
|
||||
isAssignee
|
||||
avatarSize={24}
|
||||
hasPermission={shouldEditAssignee}
|
||||
isCompactView={false}
|
||||
owners={taskThread?.task?.assignees}
|
||||
showLabel={false}
|
||||
onEditClick={handleEditClick}
|
||||
/>
|
||||
)}
|
||||
{(isCreator || hasEditAccess) &&
|
||||
!isTaskClosed &&
|
||||
owners.length === 0 ? (
|
||||
<Button
|
||||
className="flex-center p-0 h-auto"
|
||||
data-testid="edit-assignees"
|
||||
icon={<EditIcon width="14px" />}
|
||||
size="small"
|
||||
type="text"
|
||||
onClick={() => setIsEditAssignee(true)}
|
||||
/>
|
||||
) : null}
|
||||
</Col>
|
||||
</>
|
||||
)}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<OwnerAvatarProps> = ({
|
||||
@ -31,9 +34,49 @@ export const OwnerAvatar: React.FC<OwnerAvatarProps> = ({
|
||||
isCompactView,
|
||||
inheritedIcon,
|
||||
avatarSize = 32,
|
||||
isAssignee,
|
||||
}) => {
|
||||
const displayName = getEntityName(owner);
|
||||
|
||||
if (isAssignee) {
|
||||
return (
|
||||
<div className="flex w-max-full items-center gap-2">
|
||||
{owner.type === OwnerType.TEAM ? (
|
||||
<div className="d-flex gap-2 multi-team-container w-max-full items-center">
|
||||
<Icon
|
||||
className="owner-team-icon"
|
||||
component={AssigneesIcon}
|
||||
data-testid={!isCompactView && getEntityName(owner)}
|
||||
/>
|
||||
<Typography.Text className="text-sm" ellipsis={{ tooltip: true }}>
|
||||
{displayName}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="owner-avatar-icon"
|
||||
data-testid={!isCompactView && getEntityName(owner)}
|
||||
key={owner.id}
|
||||
style={{ flexBasis: `${avatarSize}px` }}>
|
||||
<ProfilePicture
|
||||
displayName={displayName}
|
||||
key="profile-picture"
|
||||
name={owner.name ?? ''}
|
||||
type="circle"
|
||||
width={isCompactView ? '24' : `${avatarSize}`}
|
||||
/>
|
||||
|
||||
{inheritedIcon && (
|
||||
<div className="inherited-icon-styling flex-center">
|
||||
{inheritedIcon}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return owner.type === OwnerType.TEAM ? (
|
||||
<div className="d-flex gap-2 w-max-full items-center">
|
||||
<Icon
|
||||
|
@ -16,6 +16,12 @@
|
||||
color: @grey-700;
|
||||
}
|
||||
|
||||
.multi-team-container {
|
||||
background-color: @grey-9;
|
||||
border-radius: 16px;
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
.avatar-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -26,6 +26,7 @@ interface OwnerItemProps {
|
||||
className?: string;
|
||||
ownerDisplayName?: ReactNode;
|
||||
avatarSize?: number;
|
||||
isAssignee?: boolean;
|
||||
}
|
||||
|
||||
export const OwnerItem: React.FC<OwnerItemProps> = ({
|
||||
@ -34,6 +35,7 @@ export const OwnerItem: React.FC<OwnerItemProps> = ({
|
||||
className,
|
||||
ownerDisplayName,
|
||||
avatarSize = 32,
|
||||
isAssignee,
|
||||
}) => {
|
||||
const displayName = getEntityName(owner);
|
||||
const ownerPath = getOwnerPath(owner);
|
||||
@ -78,6 +80,7 @@ export const OwnerItem: React.FC<OwnerItemProps> = ({
|
||||
<OwnerAvatar
|
||||
avatarSize={avatarSize}
|
||||
inheritedIcon={inheritedIcon}
|
||||
isAssignee={isAssignee}
|
||||
isCompactView={isCompactView}
|
||||
owner={owner}
|
||||
/>
|
||||
@ -91,6 +94,7 @@ export const OwnerItem: React.FC<OwnerItemProps> = ({
|
||||
<OwnerAvatar
|
||||
avatarSize={avatarSize}
|
||||
inheritedIcon={inheritedIcon}
|
||||
isAssignee={isAssignee}
|
||||
isCompactView={isCompactView}
|
||||
owner={owner}
|
||||
/>
|
||||
|
@ -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 (
|
||||
<div className="flex-wrap w-max-full d-flex relative items-center">
|
||||
<div className="flex w-full gap-2 flex-wrap relative">
|
||||
{showMultipleTypeTeam.map((owner, index) => (
|
||||
<div className="w-max-full" key={owner.id}>
|
||||
<OwnerItem
|
||||
avatarSize={avatarSize}
|
||||
className={className}
|
||||
isAssignee={isAssignee}
|
||||
isCompactView={isCompactView}
|
||||
owner={owner}
|
||||
ownerDisplayName={ownerDisplayName?.[index]}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex">
|
||||
<div className="flex relative m-l-xs justify-end flex-row-reverse">
|
||||
{showMultipleTypeVisibleUser.map((owner, index) => (
|
||||
<div className="relative" key={owner.id}>
|
||||
<OwnerItem
|
||||
avatarSize={avatarSize}
|
||||
className={className}
|
||||
isAssignee={isAssignee}
|
||||
isCompactView={isCompactView}
|
||||
owner={owner}
|
||||
ownerDisplayName={ownerDisplayName?.[index]}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{showMultipleTypeRemainingUser.length > 0 && (
|
||||
<div className="m-l-xs">
|
||||
<OwnerReveal
|
||||
avatarSize={isCompactView ? 24 : avatarSize}
|
||||
isCompactView={false}
|
||||
isDropdownOpen={isDropdownOpen}
|
||||
owners={showMultipleTypeRemainingUser}
|
||||
remainingCount={showMultipleTypeRemainingUser.length}
|
||||
setIsDropdownOpen={setIsDropdownOpen}
|
||||
setShowAllOwners={setShowAllOwners}
|
||||
showAllOwners={showAllOwners}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{hasPermission && (
|
||||
<Button
|
||||
className="p-0 flex-center h-auto"
|
||||
data-testid="edit-assignees"
|
||||
icon={<EditIcon width="14px" />}
|
||||
type="text"
|
||||
onClick={onEditClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [
|
||||
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 (
|
||||
<div
|
||||
className={classNames({
|
||||
'owner-label-container d-flex flex-col items-start flex-start':
|
||||
'owner-label-container w-full d-flex flex-col items-start flex-start':
|
||||
!isCompactView,
|
||||
'd-flex owner-label-heading gap-2 items-center': isCompactView,
|
||||
})}
|
||||
data-testid="owner-label">
|
||||
{ownerElementsNonCompactView}
|
||||
<div className="d-flex items-center flex-center">
|
||||
<div className="d-flex w-max-full items-center flex-center">
|
||||
<div
|
||||
className={classNames(
|
||||
'avatar-group w-full d-flex relative items-center m-l-xss',
|
||||
'avatar-group w-full d-flex relative items-center m-l-xss',
|
||||
{
|
||||
'gap-2 flex-wrap': isCompactView,
|
||||
'flex-row-reverse': !isCompactView,
|
||||
|
@ -29,4 +29,6 @@ export interface OwnerLabelProps {
|
||||
tooltipText?: string;
|
||||
isCompactView?: boolean;
|
||||
avatarSize?: number;
|
||||
isAssignee?: boolean;
|
||||
onEditClick?: () => void;
|
||||
}
|
||||
|
@ -127,7 +127,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.owner-label-container {
|
||||
max-width: 148px;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user