mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-29 11:26:05 +00:00
refactor(ui): task flow (#12248)
* refactor(ui): task flow * fix: task workflow * fix(ui): pass initial value on modal form based on task type * fix(ui): pass proper value in initial form values. --------- Co-authored-by: karanh37 <karanh37@gmail.com>
This commit is contained in:
parent
ba3a1e1ed6
commit
6139c59a87
@ -56,8 +56,6 @@ import { ReactComponent as TaskIcon } from '/assets/svg/ic-task.svg';
|
||||
export const ActivityFeedTab = ({
|
||||
fqn,
|
||||
owner,
|
||||
tags,
|
||||
description,
|
||||
columns,
|
||||
entityType,
|
||||
onUpdateEntityDetails,
|
||||
@ -385,20 +383,16 @@ export const ActivityFeedTab = ({
|
||||
{entityType === EntityType.TABLE ? (
|
||||
<TaskTab
|
||||
columns={columns}
|
||||
description={description}
|
||||
entityType={EntityType.TABLE}
|
||||
owner={owner}
|
||||
tags={tags}
|
||||
task={selectedThread}
|
||||
taskThread={selectedThread}
|
||||
onUpdateEntityDetails={onUpdateEntityDetails}
|
||||
/>
|
||||
) : (
|
||||
<TaskTab
|
||||
description={description}
|
||||
entityType={isUserEntity ? entityTypeTask : entityType}
|
||||
owner={owner}
|
||||
tags={tags}
|
||||
task={selectedThread}
|
||||
taskThread={selectedThread}
|
||||
onUpdateEntityDetails={onUpdateEntityDetails}
|
||||
/>
|
||||
)}
|
||||
|
@ -13,7 +13,6 @@
|
||||
import { EntityType } from 'enums/entity.enum';
|
||||
import { Column } from 'generated/entity/data/table';
|
||||
import { EntityReference } from 'generated/entity/type';
|
||||
import { TagLabel } from 'generated/type/tagLabel';
|
||||
|
||||
export type FeedKeys = 'all' | 'mentions' | 'tasks';
|
||||
|
||||
@ -28,8 +27,6 @@ export interface ActivityFeedTabBasicProps {
|
||||
onFeedUpdate: () => void;
|
||||
onUpdateEntityDetails?: () => void;
|
||||
owner?: EntityReference;
|
||||
tags?: TagLabel[];
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export type ActivityFeedTabProps = ActivityFeedTabBasicProps &
|
||||
|
@ -29,7 +29,6 @@ import ActivityFeedEditor from 'components/ActivityFeed/ActivityFeedEditor/Activ
|
||||
import { useActivityFeedProvider } from 'components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
|
||||
import { OwnerLabel } from 'components/common/OwnerLabel/OwnerLabel.component';
|
||||
import EntityPopOverCard from 'components/common/PopOverCard/EntityPopOverCard';
|
||||
import { FQN_SEPARATOR_CHAR } from 'constants/char.constants';
|
||||
import { TaskOperation } from 'constants/Feeds.constants';
|
||||
import { TaskType } from 'generated/api/feed/createThread';
|
||||
import { TaskDetails, ThreadTaskStatus } from 'generated/entity/feed/thread';
|
||||
@ -43,16 +42,14 @@ import {
|
||||
TaskActionMode,
|
||||
} from 'pages/TasksPage/TasksPage.interface';
|
||||
import { MenuInfo } from 'rc-menu/lib/interface';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { updateTask } from 'rest/feedsAPI';
|
||||
import { getNameFromFQN } from 'utils/CommonUtils';
|
||||
import { ENTITY_LINK_SEPARATOR } from 'utils/EntityUtils';
|
||||
import { getEntityField, getEntityFQN, prepareFeedLink } from 'utils/FeedUtils';
|
||||
import { getEntityFQN, prepareFeedLink } from 'utils/FeedUtils';
|
||||
import { getEntityLink } from 'utils/TableUtils';
|
||||
import {
|
||||
getColumnObject,
|
||||
isDescriptionTask,
|
||||
isTagsTask,
|
||||
TASK_ACTION_LIST,
|
||||
@ -63,15 +60,13 @@ import { ReactComponent as TaskCloseIcon } from '/assets/svg/ic-close-task.svg';
|
||||
import { ReactComponent as TaskOpenIcon } from '/assets/svg/ic-open-task.svg';
|
||||
|
||||
export const TaskTab = ({
|
||||
task,
|
||||
taskThread,
|
||||
owner,
|
||||
entityType,
|
||||
tags,
|
||||
description,
|
||||
...rest
|
||||
}: TaskTabProps) => {
|
||||
const { task: taskDetails } = task;
|
||||
const entityFQN = getEntityFQN(task.about) ?? '';
|
||||
const { task: taskDetails } = taskThread;
|
||||
const entityFQN = getEntityFQN(taskThread.about) ?? '';
|
||||
const entityCheck = !isUndefined(entityFQN) && !isUndefined(entityType);
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
@ -90,27 +85,8 @@ export const TaskTab = ({
|
||||
[AppState.userDetails, AppState.nonSecureUserDetails]
|
||||
);
|
||||
|
||||
const entityField = useMemo(() => {
|
||||
return getEntityField(task.about);
|
||||
}, [task]);
|
||||
|
||||
const columnObject = useMemo(() => {
|
||||
// prepare column from entityField
|
||||
const column = entityField?.split(ENTITY_LINK_SEPARATOR)?.slice(-2)?.[0];
|
||||
|
||||
// prepare column value by replacing double quotes
|
||||
const columnValue = column?.replaceAll(/^"|"$/g, '') || '';
|
||||
|
||||
/**
|
||||
* Get column name by spliting columnValue with FQN Separator
|
||||
*/
|
||||
const columnName = columnValue.split(FQN_SEPARATOR_CHAR).pop();
|
||||
|
||||
return getColumnObject(columnName ?? '', rest.columns || []);
|
||||
}, [task, rest.columns]);
|
||||
|
||||
const isOwner = isEqual(owner?.id, currentUser?.id);
|
||||
const isCreator = isEqual(task.createdBy, currentUser?.name);
|
||||
const isCreator = isEqual(taskThread.createdBy, currentUser?.name);
|
||||
|
||||
const checkIfUserPartOfTeam = useCallback(
|
||||
(teamId: string): boolean => {
|
||||
@ -216,35 +192,13 @@ export const TaskTab = ({
|
||||
|
||||
const hasTaskUpdateAccess = () => hasEditAccess() || isPartOfAssigneeTeam;
|
||||
|
||||
// prepare current tags for update tags task
|
||||
const getCurrentTags = () => {
|
||||
if (!isEmpty(columnObject) && entityField) {
|
||||
return columnObject.tags ?? [];
|
||||
} else {
|
||||
return tags ?? [];
|
||||
}
|
||||
};
|
||||
|
||||
// prepare current description for update description task
|
||||
const currentDescription = () => {
|
||||
if (entityField && !isEmpty(columnObject)) {
|
||||
return columnObject.description || '';
|
||||
} else {
|
||||
return description || '';
|
||||
}
|
||||
};
|
||||
|
||||
const onSave = (message: string) => {
|
||||
postFeed(message, task?.id ?? '').catch(() => {
|
||||
postFeed(message, taskThread?.id ?? '').catch(() => {
|
||||
// ignore since error is displayed in toast in the parent promise.
|
||||
// Added block for sonar code smell
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldValue('description', currentDescription());
|
||||
}, [columnObject, entityField, currentDescription]);
|
||||
|
||||
const handleMenuItemClick: MenuProps['onClick'] = (info) => {
|
||||
if (info.key === TaskActionMode.EDIT) {
|
||||
setShowEditTaskModel(true);
|
||||
@ -276,19 +230,26 @@ export const TaskTab = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const parsedSuggestion = [
|
||||
'RequestDescription',
|
||||
'UpdateDescription',
|
||||
].includes(taskDetails?.type ?? '')
|
||||
? taskDetails?.suggestion
|
||||
: JSON.parse(taskDetails?.suggestion || '[]');
|
||||
|
||||
return (
|
||||
<Space
|
||||
className="m-t-sm items-end w-full"
|
||||
data-testid="task-cta-buttons"
|
||||
size="small">
|
||||
{isCreator && (
|
||||
{(isCreator || hasTaskUpdateAccess()) && (
|
||||
<Button onClick={onTaskReject}>{t('label.close')}</Button>
|
||||
)}
|
||||
{hasTaskUpdateAccess() ? (
|
||||
<>
|
||||
{['RequestDescription', 'RequestTag'].includes(
|
||||
taskDetails?.type ?? ''
|
||||
) && isEmpty(taskDetails?.suggestion) ? (
|
||||
) && isEmpty(parsedSuggestion) ? (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() =>
|
||||
@ -328,6 +289,21 @@ export const TaskTab = ({
|
||||
isCreator,
|
||||
]);
|
||||
|
||||
const initialFormValue = useMemo(() => {
|
||||
if (isTaskDescription) {
|
||||
const description =
|
||||
taskDetails?.suggestion ?? taskDetails?.oldValue ?? '';
|
||||
|
||||
return { description };
|
||||
} else {
|
||||
const updatedTags = JSON.parse(
|
||||
taskDetails?.suggestion ?? taskDetails?.oldValue ?? '[]'
|
||||
);
|
||||
|
||||
return { updatedTags };
|
||||
}
|
||||
}, [taskDetails, isTaskDescription]);
|
||||
|
||||
return (
|
||||
<Row className="p-y-sm p-x-md" gutter={[0, 24]}>
|
||||
<Col className="d-flex items-center" span={24}>
|
||||
@ -362,7 +338,7 @@ export const TaskTab = ({
|
||||
</Typography.Text>
|
||||
<OwnerLabel
|
||||
hasPermission={false}
|
||||
owner={{ name: task.createdBy, type: 'user', id: '' }}
|
||||
owner={{ name: taskThread.createdBy, type: 'user', id: '' }}
|
||||
onUpdate={noop}
|
||||
/>
|
||||
</div>
|
||||
@ -373,35 +349,32 @@ export const TaskTab = ({
|
||||
<DescriptionTask
|
||||
hasEditAccess={hasEditAccess()}
|
||||
isTaskActionEdit={false}
|
||||
suggestion={task.task?.suggestion ?? ''}
|
||||
taskDetail={task}
|
||||
value={currentDescription()}
|
||||
taskThread={taskThread}
|
||||
onChange={(value) => form.setFieldValue('description', value)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isTaskTags && (
|
||||
<TagsTask
|
||||
currentTags={getCurrentTags()}
|
||||
hasEditAccess={hasEditAccess()}
|
||||
isTaskActionEdit={false}
|
||||
task={taskDetails}
|
||||
value={JSON.parse(taskDetails?.suggestion ?? '[]')}
|
||||
onChange={(value) => form.setFieldValue('updatedTags', value)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="m-l-lg">
|
||||
{task?.posts?.map((reply) => (
|
||||
{taskThread?.posts?.map((reply) => (
|
||||
<ActivityFeedCardV1
|
||||
isPost
|
||||
feed={task}
|
||||
feed={taskThread}
|
||||
hidePopover={false}
|
||||
key={reply.id}
|
||||
post={reply}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{task.task?.status === ThreadTaskStatus.Open && (
|
||||
{taskDetails?.status === ThreadTaskStatus.Open && (
|
||||
<ActivityFeedEditor onSave={onSave} onTextChange={setComment} />
|
||||
)}
|
||||
|
||||
@ -414,16 +387,20 @@ export const TaskTab = ({
|
||||
open={showEditTaskModel}
|
||||
title={`${t('label.edit-entity', {
|
||||
entity: t('label.task-lowercase'),
|
||||
})} #${taskDetails?.id} ${task.message}`}
|
||||
})} #${taskDetails?.id} ${taskThread.message}`}
|
||||
width={768}
|
||||
onCancel={() => setShowEditTaskModel(false)}
|
||||
onOk={form.submit}>
|
||||
<Form form={form} layout="vertical" onFinish={onEditAndSuggest}>
|
||||
<Form
|
||||
form={form}
|
||||
initialValues={initialFormValue}
|
||||
layout="vertical"
|
||||
onFinish={onEditAndSuggest}>
|
||||
{isTaskTags ? (
|
||||
<Form.Item
|
||||
data-testid="tags-label"
|
||||
label={t('label.tag-plural')}
|
||||
name="updateTags"
|
||||
name="updatedTags"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@ -431,12 +408,13 @@ export const TaskTab = ({
|
||||
fieldText: t('label.tag-plural'),
|
||||
}),
|
||||
},
|
||||
]}>
|
||||
]}
|
||||
trigger="onChange">
|
||||
<TagsTask
|
||||
isTaskActionEdit
|
||||
currentTags={getCurrentTags()}
|
||||
hasEditAccess={hasEditAccess()}
|
||||
task={taskDetails}
|
||||
onChange={(value) => form.setFieldValue('updatedTags', value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
@ -452,13 +430,11 @@ export const TaskTab = ({
|
||||
}),
|
||||
},
|
||||
]}
|
||||
valuePropName="suggestion">
|
||||
trigger="onTextChange">
|
||||
<DescriptionTask
|
||||
isTaskActionEdit
|
||||
hasEditAccess={hasEditAccess()}
|
||||
suggestion={task.task?.suggestion ?? ''}
|
||||
taskDetail={task}
|
||||
value={currentDescription()}
|
||||
taskThread={taskThread}
|
||||
onChange={(value) => form.setFieldValue('description', value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
@ -14,14 +14,11 @@ import { EntityType } from 'enums/entity.enum';
|
||||
import { Column } from 'generated/entity/data/table';
|
||||
import { Thread } from 'generated/entity/feed/thread';
|
||||
import { EntityReference } from 'generated/entity/type';
|
||||
import { TagLabel } from 'generated/type/tagLabel';
|
||||
|
||||
export type TaskTabProps = {
|
||||
task: Thread;
|
||||
taskThread: Thread;
|
||||
owner?: EntityReference;
|
||||
tags?: TagLabel[];
|
||||
onUpdateEntityDetails?: () => void;
|
||||
description?: string;
|
||||
} & (
|
||||
| TableTaskTabProps
|
||||
| { columns?: undefined; entityType: Exclude<EntityType, EntityType.TABLE> }
|
||||
|
@ -556,11 +556,9 @@ const TableDetailsPageV1 = () => {
|
||||
<ActivityFeedProvider>
|
||||
<ActivityFeedTab
|
||||
columns={tableDetails?.columns}
|
||||
description={tableDetails?.description}
|
||||
entityType={EntityType.TABLE}
|
||||
fqn={tableDetails?.fullyQualifiedName ?? ''}
|
||||
owner={tableDetails?.owner}
|
||||
tags={tableDetails?.tags}
|
||||
onFeedUpdate={getEntityFeedCount}
|
||||
onUpdateEntityDetails={fetchTableDetails}
|
||||
/>
|
||||
|
@ -171,7 +171,7 @@ const UpdateTag = () => {
|
||||
}
|
||||
form.setFieldsValue({
|
||||
title: message.trimEnd(),
|
||||
updateTags: getTags(),
|
||||
updatedTags: getTags(),
|
||||
assignees: defaultAssignee,
|
||||
});
|
||||
}, [entityData]);
|
||||
@ -244,7 +244,7 @@ const UpdateTag = () => {
|
||||
label={t('label.update-entity', {
|
||||
entity: t('label.tag-plural'),
|
||||
})}
|
||||
name="updateTags"
|
||||
name="updatedTags"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
|
@ -21,7 +21,7 @@ import {
|
||||
import DescriptionTask from './DescriptionTask';
|
||||
|
||||
const mockProps = {
|
||||
taskDetail: {
|
||||
taskThread: {
|
||||
id: '9542599e-f2f9-46d1-9fc0-d03620351a0d',
|
||||
type: 'Task',
|
||||
href: 'http://localhost:8585/api/v1/feed/9542599e-f2f9-46d1-9fc0-d03620351a0d',
|
||||
@ -84,8 +84,8 @@ describe('Test Description Task Component', () => {
|
||||
render(
|
||||
<DescriptionTask
|
||||
{...mockProps}
|
||||
taskDetail={{
|
||||
...mockProps.taskDetail,
|
||||
taskThread={{
|
||||
...mockProps.taskThread,
|
||||
task: {
|
||||
id: 5,
|
||||
assignees: [
|
||||
@ -125,8 +125,8 @@ describe('Test Description Task Component', () => {
|
||||
render(
|
||||
<DescriptionTask
|
||||
{...mockProps}
|
||||
taskDetail={{
|
||||
...mockProps.taskDetail,
|
||||
taskThread={{
|
||||
...mockProps.taskThread,
|
||||
task: {
|
||||
id: 5,
|
||||
assignees: [
|
||||
|
@ -25,42 +25,30 @@ import { DescriptionTabs } from './DescriptionTabs';
|
||||
import { DiffView } from './DiffView';
|
||||
|
||||
interface DescriptionTaskProps {
|
||||
taskDetail: Thread;
|
||||
taskThread: Thread;
|
||||
isTaskActionEdit: boolean;
|
||||
hasEditAccess: boolean;
|
||||
suggestion: string;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
const DescriptionTask: FC<DescriptionTaskProps> = ({
|
||||
taskDetail,
|
||||
taskThread,
|
||||
isTaskActionEdit,
|
||||
hasEditAccess,
|
||||
suggestion,
|
||||
value: currentDescription = '',
|
||||
onChange,
|
||||
}) => {
|
||||
const { task } = taskThread;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isRequestDescription = isEqual(
|
||||
taskDetail.task?.type,
|
||||
TaskType.RequestDescription
|
||||
);
|
||||
const isRequestDescription = isEqual(task?.type, TaskType.RequestDescription);
|
||||
|
||||
const isUpdateDescription = isEqual(
|
||||
taskDetail.task?.type,
|
||||
TaskType.UpdateDescription
|
||||
);
|
||||
const isUpdateDescription = isEqual(task?.type, TaskType.UpdateDescription);
|
||||
|
||||
const isTaskClosed = isEqual(
|
||||
taskDetail.task?.status,
|
||||
ThreadTaskStatus.Closed
|
||||
);
|
||||
const isTaskClosed = isEqual(task?.status, ThreadTaskStatus.Closed);
|
||||
|
||||
const getDiffView = () => {
|
||||
const oldValue = taskDetail.task?.oldValue;
|
||||
const newValue = taskDetail.task?.newValue;
|
||||
const oldValue = task?.oldValue;
|
||||
const newValue = task?.newValue;
|
||||
if (!oldValue && !newValue) {
|
||||
return (
|
||||
<div className="tw-border tw-border-main tw-p-2 tw-rounded tw-my-1 tw-mb-3">
|
||||
@ -73,10 +61,7 @@ const DescriptionTask: FC<DescriptionTaskProps> = ({
|
||||
return (
|
||||
<DiffView
|
||||
className="tw-border tw-border-main tw-p-2 tw-rounded tw-my-1 tw-mb-3"
|
||||
diffArr={getDescriptionDiff(
|
||||
taskDetail?.task?.oldValue || '',
|
||||
taskDetail?.task?.newValue || ''
|
||||
)}
|
||||
diffArr={getDescriptionDiff(oldValue ?? '', newValue ?? '')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -87,8 +72,8 @@ const DescriptionTask: FC<DescriptionTaskProps> = ({
|
||||
* @returns Suggested description diff
|
||||
*/
|
||||
const getSuggestedDescriptionDiff = () => {
|
||||
const newDescription = taskDetail?.task?.suggestion;
|
||||
const oldDescription = taskDetail?.task?.oldValue;
|
||||
const newDescription = task?.suggestion;
|
||||
const oldDescription = task?.oldValue;
|
||||
|
||||
const diffs = getDescriptionDiff(
|
||||
oldDescription || '',
|
||||
@ -116,7 +101,7 @@ const DescriptionTask: FC<DescriptionTaskProps> = ({
|
||||
{isTaskActionEdit && hasEditAccess ? (
|
||||
<RichTextEditor
|
||||
height="208px"
|
||||
initialValue={suggestion}
|
||||
initialValue={task?.suggestion ?? ''}
|
||||
placeHolder={t('label.add-entity', {
|
||||
entity: t('label.description'),
|
||||
})}
|
||||
@ -135,8 +120,8 @@ const DescriptionTask: FC<DescriptionTaskProps> = ({
|
||||
<div data-testid="update-description">
|
||||
{isTaskActionEdit && hasEditAccess ? (
|
||||
<DescriptionTabs
|
||||
suggestion={suggestion}
|
||||
value={currentDescription}
|
||||
suggestion={task?.suggestion ?? ''}
|
||||
value={task?.oldValue ?? ''}
|
||||
onChange={onChange}
|
||||
/>
|
||||
) : (
|
||||
|
@ -48,7 +48,7 @@ describe('Test Description Tabs Component', () => {
|
||||
expect(await screen.findByText('New')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should render the component relavant tab component', async () => {
|
||||
it('Should render the component relevant tab component', async () => {
|
||||
render(<TagsTabs {...mockProps} />);
|
||||
|
||||
const tabs = await screen.findAllByRole('tab');
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import { diffArrays } from 'diff';
|
||||
import { TagLabel } from 'generated/type/tagLabel';
|
||||
import React, { FC, Fragment, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@ -19,7 +20,6 @@ import {
|
||||
Thread,
|
||||
ThreadTaskStatus,
|
||||
} from '../../../generated/entity/feed/thread';
|
||||
import { TagLabel } from '../../../generated/type/tagLabel';
|
||||
import { TagsDiffView } from './TagsDiffView';
|
||||
import { TagsTabs } from './TagsTabs';
|
||||
import TagSuggestion from './TagSuggestion';
|
||||
@ -28,18 +28,14 @@ interface TagsTaskProps {
|
||||
task: Thread['task'];
|
||||
isTaskActionEdit: boolean;
|
||||
hasEditAccess: boolean;
|
||||
currentTags: TagLabel[];
|
||||
value?: TagLabel[];
|
||||
onChange?: (newTags: TagLabel[]) => void;
|
||||
onChange: (newTags: TagLabel[]) => void;
|
||||
}
|
||||
|
||||
const TagsTask: FC<TagsTaskProps> = ({
|
||||
value = [],
|
||||
onChange,
|
||||
isTaskActionEdit,
|
||||
hasEditAccess,
|
||||
task,
|
||||
currentTags,
|
||||
onChange,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -105,7 +101,10 @@ const TagsTask: FC<TagsTaskProps> = ({
|
||||
{isRequestTag && (
|
||||
<div data-testid="request-tags">
|
||||
{isTaskActionEdit && hasEditAccess ? (
|
||||
<TagSuggestion value={value} onChange={onChange} />
|
||||
<TagSuggestion
|
||||
value={JSON.parse(suggestion ?? '[]')}
|
||||
onChange={onChange}
|
||||
/>
|
||||
) : (
|
||||
suggestedTagsDiff
|
||||
)}
|
||||
@ -115,8 +114,8 @@ const TagsTask: FC<TagsTaskProps> = ({
|
||||
<div data-testid="update-tags">
|
||||
{isTaskActionEdit && hasEditAccess ? (
|
||||
<TagsTabs
|
||||
tags={currentTags}
|
||||
value={value}
|
||||
tags={JSON.parse(oldValue ?? '[]')}
|
||||
value={JSON.parse(suggestion ?? '[]')}
|
||||
onChange={onChange}
|
||||
/>
|
||||
) : (
|
||||
|
@ -168,6 +168,10 @@ a[href].link-text-grey,
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.whitespace-pre-wrap {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.mx-auto {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
|
Loading…
x
Reference in New Issue
Block a user