feat: allow adding owner on glossary and term creation (#11039)

* feat: allow adding owner on glossary and term creation

* fix: glossary feedback

* fix: glossary button styling

* fix: glossary button styling

* fix: added on cancel for dialog

---------

Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
This commit is contained in:
karanh37 2023-04-14 03:02:06 +05:30 committed by GitHub
parent d9564ef0d5
commit 49edea5fdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 170 additions and 48 deletions

View File

@ -14,11 +14,14 @@
import { PlusOutlined } from '@ant-design/icons';
import { Button, Space, Switch, Typography } from 'antd';
import { UserSelectableList } from 'components/common/UserSelectableList/UserSelectableList.component';
import Tags from 'components/Tag/Tags/tags';
import { UserTag } from 'components/common/UserTag/UserTag.component';
import { UserTagSize } from 'components/common/UserTag/UserTag.interface';
import { UserTeamSelectableList } from 'components/common/UserTeamSelectableList/UserTeamSelectableList.component';
import { cloneDeep, toString } from 'lodash';
import { EntityTags } from 'Models';
import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getEntityName } from 'utils/EntityUtils';
import { ADD_GLOSSARY_ERROR } from '../../constants/Glossary.constant';
import { allowedNameRegEx } from '../../constants/regex.constants';
import { PageLayoutType } from '../../enums/layout.enum';
@ -60,6 +63,7 @@ const AddGlossary = ({
const [tags, setTags] = useState<EntityTags[]>([]);
const [mutuallyExclusive, setMutuallyExclusive] = useState(false);
const [reviewer, setReviewer] = useState<Array<EntityReference>>([]);
const [owner, setOwner] = useState<EntityReference | undefined>();
const getDescription = () => {
return markdownRef.current?.getEditorContent() || '';
@ -68,6 +72,9 @@ const AddGlossary = ({
const handleReviewerSave = (reviewer: EntityReference[]) => {
setReviewer(reviewer);
};
const handleUpdatedOwner = async (owner: EntityReference | undefined) => {
setOwner(owner);
};
const handleValidation = (
event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
@ -93,13 +100,6 @@ const AddGlossary = ({
});
};
const handleReviewerRemove = (
_event: React.MouseEvent<HTMLElement, MouseEvent>,
removedTag: string
) => {
setReviewer((pre) => pre.filter((option) => option.name !== removedTag));
};
const validateForm = () => {
const errMsg = {
name: !name.trim(),
@ -113,6 +113,10 @@ const AddGlossary = ({
const handleSave = () => {
if (validateForm()) {
const selectedOwner = owner || {
id: getCurrentUserId(),
type: 'user',
};
const data: CreateGlossary = {
name: name.trim(),
displayName: (displayName || name).trim(),
@ -120,10 +124,7 @@ const AddGlossary = ({
reviewers:
reviewer.map((d) => toString(d.fullyQualifiedName)).filter(Boolean) ??
[],
owner: {
id: getCurrentUserId(),
type: 'user',
},
owner: selectedOwner,
tags: tags,
mutuallyExclusive,
};
@ -225,9 +226,9 @@ const AddGlossary = ({
</Field>
<Field>
<Space align="end">
<Space align="end" size={12}>
<label
className="tw-form-label m-b-0 tw-mb-1"
className="glossary-form-label tw-form-label m-b-0 tw-mb-1"
data-testid="mutually-exclusive-label"
htmlFor="mutuallyExclusive">
{t('label.mutually-exclusive')}
@ -243,34 +244,71 @@ const AddGlossary = ({
<div>
<div className="tw-flex tw-items-center tw-mt-4">
<span className="w-form-label tw-mr-3">
<span className="glossary-form-label w-form-label tw-mr-3">
{`${t('label.owner')}:`}
</span>
<UserTeamSelectableList
hasPermission
owner={owner}
onUpdate={handleUpdatedOwner}>
<Button
data-testid="add-owner"
icon={
<PlusOutlined
style={{ color: 'white', fontSize: '12px' }}
/>
}
size="small"
type="primary"
/>
</UserTeamSelectableList>
</div>
<div className="tw-my-2" data-testid="owner-container">
{owner && (
<UserTag
id={owner.id}
name={getEntityName(owner)}
size={UserTagSize.small}
/>
)}
</div>
<div className="tw-flex tw-items-center tw-mt-4">
<span className="glossary-form-label w-form-label tw-mr-3">
{`${t('label.reviewer-plural')}:`}
</span>
<UserSelectableList
hasPermission
selectedUsers={reviewer ?? []}
onUpdate={handleReviewerSave}>
<Button data-testid="add-reviewers" size="small" type="primary">
<PlusOutlined style={{ color: 'white' }} />
</Button>
<Button
data-testid="add-reviewers"
icon={
<PlusOutlined
style={{ color: 'white', fontSize: '12px' }}
/>
}
size="small"
type="primary"
/>
</UserSelectableList>
</div>
<div className="tw-my-4" data-testid="reviewers-container">
<Space
wrap
className="tw-my-2"
data-testid="reviewers-container"
size={[8, 8]}>
{Boolean(reviewer.length) &&
reviewer.map((d, index) => {
return (
<Tags
editable
isRemovable
className="tw-bg-gray-200"
<UserTag
id={d.id}
key={index}
removeTag={handleReviewerRemove}
tag={d.name ?? ''}
type="contained"
name={getEntityName(d)}
size={UserTagSize.small}
/>
);
})}
</div>
</Space>
</div>
<div className="flex justify-end">

View File

@ -17,6 +17,7 @@ import classNames from 'classnames';
import { UserSelectableList } from 'components/common/UserSelectableList/UserSelectableList.component';
import { UserTag } from 'components/common/UserTag/UserTag.component';
import { UserTagSize } from 'components/common/UserTag/UserTag.interface';
import { UserTeamSelectableList } from 'components/common/UserTeamSelectableList/UserTeamSelectableList.component';
import Tags from 'components/Tag/Tags/tags';
import { t } from 'i18next';
import { cloneDeep, isEmpty, isUndefined } from 'lodash';
@ -31,7 +32,12 @@ import {
TermReference,
} from '../../generated/entity/data/glossaryTerm';
import { EntityReference } from '../../generated/type/entityReference';
import { errorMsg, isValidUrl, requiredField } from '../../utils/CommonUtils';
import {
errorMsg,
getCurrentUserId,
isValidUrl,
requiredField,
} from '../../utils/CommonUtils';
import SVGIcons from '../../utils/SvgUtils';
import { AddTags } from '../AddTags/add-tags.component';
import RichTextEditor from '../common/rich-text-editor/RichTextEditor';
@ -80,6 +86,7 @@ const AddGlossaryTerm = ({
const [synonyms, setSynonyms] = useState('');
const [mutuallyExclusive, setMutuallyExclusive] = useState(false);
const [references, setReferences] = useState<TermReference[]>([]);
const [owner, setOwner] = useState<EntityReference | undefined>();
useEffect(() => {
if (glossaryData?.reviewers && glossaryData?.reviewers.length) {
@ -104,8 +111,8 @@ const AddGlossaryTerm = ({
setReviewer(reviewer);
};
const handleReviewerRemove = (removedTag: string) => {
setReviewer((pre) => pre.filter((option) => option.name !== removedTag));
const handleUpdatedOwner = async (owner: EntityReference | undefined) => {
setOwner(owner);
};
const handleTermRemove = (
@ -212,6 +219,10 @@ const AddGlossaryTerm = ({
if (validateForm(updatedReference)) {
const updatedName = name.trim();
const selectedOwner = owner || {
id: getCurrentUserId(),
type: 'user',
};
const data: CreateGlossaryTerm = {
name: updatedName,
displayName: (displayName || updatedName).trim(),
@ -226,6 +237,7 @@ const AddGlossaryTerm = ({
mutuallyExclusive,
glossary: glossaryData.name,
tags: tags,
owner: selectedOwner,
};
onSave(data);
@ -361,9 +373,9 @@ const AddGlossaryTerm = ({
<div className="m-t-lg">
<Field>
<Space align="end">
<Space align="end" size={12}>
<label
className="tw-form-label m-b-0"
className="glossary-form-label tw-form-label m-b-0"
data-testid="mutually-exclusive-label"
htmlFor="mutuallyExclusive">
{t('label.mutually-exclusive')}
@ -378,14 +390,18 @@ const AddGlossaryTerm = ({
</Field>
<Field>
<Space align="end" data-testid="references">
<label className="tw-form-label m-b-0">
<Space align="end" data-testid="references" size={12}>
<label className="glossary-form-label tw-form-label m-b-0">
{t('label.reference-plural')}
</label>
<Button
className="tw-h-5 tw-px-2"
data-testid="add-reference"
icon={<PlusOutlined />}
icon={
<PlusOutlined
style={{ color: 'white', fontSize: '12px' }}
/>
}
size="small"
type="primary"
onClick={addReferenceFields}
@ -449,13 +465,15 @@ const AddGlossaryTerm = ({
<Field>
<div className="tw-flex tw-items-center tw-mt-4">
<p className="w-form-label tw-mr-3">
<p className="glossary-form-label w-form-label tw-mr-3">
{t('label.related-term-plural')}
</p>
<Button
className="tw-h-5 tw-px-2"
data-testid="add-related-terms"
icon={<PlusOutlined />}
icon={
<PlusOutlined style={{ color: 'white', fontSize: '12px' }} />
}
size="small"
type="primary"
onClick={() => setShowRelatedTermsModal(true)}
@ -478,9 +496,42 @@ const AddGlossaryTerm = ({
})}
</div>
</Field>
<Field>
<div className="tw-flex tw-items-center tw-mt-4">
<p className="w-form-label tw-mr-3">
<p className="glossary-form-label w-form-label tw-mr-3">{`${t(
'label.owner'
)}`}</p>
<UserTeamSelectableList
hasPermission
owner={owner}
onUpdate={handleUpdatedOwner}>
<Button
className="tw-h-5 tw-px-2"
data-testid="add-owner"
icon={
<PlusOutlined
style={{ color: 'white', fontSize: '12px' }}
/>
}
size="small"
type="primary"
/>
</UserTeamSelectableList>
</div>
<div className="tw-my-2" data-testid="owner-container">
{owner && (
<UserTag
id={owner.id}
name={getEntityName(owner)}
size={UserTagSize.small}
/>
)}
</div>
</Field>
<Field>
<div className="tw-flex tw-items-center tw-mt-4">
<p className="glossary-form-label w-form-label tw-mr-3">
{t('label.reviewer-plural')}
</p>
<UserSelectableList
@ -491,24 +542,25 @@ const AddGlossaryTerm = ({
<Button
className="tw-h-5 tw-px-2"
data-testid="add-reviewers"
icon={<PlusOutlined />}
icon={
<PlusOutlined
style={{ color: 'white', fontSize: '12px' }}
/>
}
size="small"
type="primary"
/>
</UserSelectableList>
</div>
<Space wrap className="tw-my-4" size={[8, 8]}>
<Space wrap className="tw-my-2" size={[8, 8]}>
{Boolean(reviewer.length) &&
reviewer.map((d, index) => {
return (
<UserTag
bordered
closable
id={d.id}
key={index}
name={getEntityName(d)}
size={UserTagSize.small}
onRemove={() => d.name && handleReviewerRemove(d.name)}
/>
);
})}

View File

@ -18,7 +18,11 @@ import { UserSelectableList } from 'components/common/UserSelectableList/UserSel
import { UserTeamSelectableList } from 'components/common/UserTeamSelectableList/UserTeamSelectableList.component';
import TagButton from 'components/TagButton/TagButton.component';
import TagsInput from 'components/TagsInput/TagsInput.component';
import { DE_ACTIVE_COLOR, getUserPath } from 'constants/constants';
import {
DE_ACTIVE_COLOR,
getTeamAndUserDetailsPath,
getUserPath,
} from 'constants/constants';
import { GlossaryTerm } from 'generated/entity/data/glossaryTerm';
import { EntityReference } from 'generated/type/entityReference';
import { t } from 'i18next';
@ -119,7 +123,12 @@ const GlossaryDetailsRightPanel = ({
textClass="text-xs"
width="20"
/>
<Link to={getUserPath(selectedData.owner.name ?? '')}>
<Link
to={
selectedData.owner.type === 'team'
? getTeamAndUserDetailsPath(selectedData.owner.name ?? '')
: getUserPath(selectedData.owner.name ?? '')
}>
{getEntityName(selectedData.owner)}
</Link>
</Space>

View File

@ -43,7 +43,18 @@ const GlossaryTermReferencesModal = ({
};
useEffect(() => {
isVisible ? form.setFieldValue('references', references) : null;
if (isVisible) {
const newRefs =
references.length > 0
? references
: [
{
name: '',
endpoint: '',
},
];
form.setFieldValue('references', newRefs);
}
}, [isVisible]);
return (

View File

@ -47,6 +47,7 @@ const EntityNameModal: React.FC<Props> = ({
return (
<Modal
destroyOnClose
closable={false}
footer={[
<Button key="cancel-btn" type="link" onClick={onCancel}>
{t('label.cancel')}
@ -59,13 +60,15 @@ const EntityNameModal: React.FC<Props> = ({
{t('label.save')}
</Button>,
]}
maskClosable={false}
okText={t('label.save')}
open={visible}
title={
<Typography.Text strong data-testid="header">
{t('label.edit-glossary-name')}
</Typography.Text>
}>
}
onCancel={onCancel}>
<Form form={form} layout="vertical" onFinish={handleSave}>
<Form.Item
label={`${t('label.name')}:`}

View File

@ -168,6 +168,8 @@ export const UserTeamSelectableList = ({
: {
id: updateItems[0].id,
type: activeTab === 'teams' ? EntityType.TEAM : EntityType.USER,
name: updateItems[0].name,
displayName: updateItems[0].displayName,
}
);
setPopupVisible(false);

View File

@ -125,3 +125,7 @@
margin-right: 8px;
}
}
.glossary-form-label {
width: 125px;
}

View File

@ -95,4 +95,7 @@
background: @active-color;
}
}
.expand-cell-empty-icon-container {
display: none !important;
}
}

View File

@ -468,7 +468,7 @@ export function getTableExpandableConfig<T>(
<>
<SVGIcons
alt="icon"
className="m-r-xs"
className="m-r-xs drag-icon"
height={8}
icon={Icons.DRAG}
width={8}