mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-29 11:26:05 +00:00
chore(ui): wait before udpate the ui for update calls (#15356)
* chore(ui): wait before udpate the ui for update calls * fix some more cases * fix unit tests * fix cypress
This commit is contained in:
parent
da926d1f2d
commit
8c46a4e5cd
@ -332,7 +332,7 @@ const DataProductsDetailsPage = ({
|
||||
}
|
||||
};
|
||||
|
||||
const onStyleSave = (data: Style) => {
|
||||
const onStyleSave = async (data: Style) => {
|
||||
const style: Style = {
|
||||
// if color/iconURL is empty or undefined send undefined
|
||||
color: data.color ? data.color : undefined,
|
||||
@ -343,7 +343,7 @@ const DataProductsDetailsPage = ({
|
||||
style,
|
||||
};
|
||||
|
||||
onUpdate(updatedDetails);
|
||||
await onUpdate(updatedDetails);
|
||||
setIsStyleEditing(false);
|
||||
};
|
||||
|
||||
|
@ -16,7 +16,7 @@ export interface DataProductsDetailsPageProps {
|
||||
dataProduct: DataProduct;
|
||||
isVersionsView?: boolean;
|
||||
onUpdate: (dataProductDetails: DataProduct) => Promise<void>;
|
||||
onDelete: () => void;
|
||||
onDelete: () => Promise<void>;
|
||||
}
|
||||
|
||||
export enum DataProductTabs {
|
||||
|
@ -24,6 +24,7 @@ import { TagLabel } from '../../../../generated/type/tagLabel';
|
||||
import { getEntityName } from '../../../../utils/EntityUtils';
|
||||
import Description from '../../../common/EntityDescription/Description';
|
||||
import Loader from '../../../common/Loader/Loader';
|
||||
import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component';
|
||||
import ProfilePicture from '../../../common/ProfilePicture/ProfilePicture';
|
||||
import { UserTeamSelectableList } from '../../../common/UserTeamSelectableList/UserTeamSelectableList.component';
|
||||
import TagsInput from '../../../TagsInput/TagsInput.component';
|
||||
@ -102,28 +103,7 @@ const TableQueryRightPanel = ({
|
||||
</UserTeamSelectableList>
|
||||
)}
|
||||
</Space>
|
||||
<div data-testid="owner-name-container">
|
||||
{query.owner && getEntityName(query.owner) ? (
|
||||
<Space className="m-r-xss" size={4}>
|
||||
<ProfilePicture
|
||||
displayName={getEntityName(query.owner)}
|
||||
name={query.owner?.name || ''}
|
||||
width="20"
|
||||
/>
|
||||
<Link
|
||||
data-testid="owner-link"
|
||||
to={getUserPath(query.owner.name ?? '')}>
|
||||
{getEntityName(query.owner)}
|
||||
</Link>
|
||||
</Space>
|
||||
) : (
|
||||
<span className="text-grey-muted">
|
||||
{t('label.no-entity', {
|
||||
entity: t('label.owner-lowercase'),
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<OwnerLabel hasPermission={false} owner={query.owner} />
|
||||
</Space>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
@ -62,9 +62,6 @@ describe('TableQueryRightPanel component test', () => {
|
||||
});
|
||||
const owner = await screen.findByTestId('owner-link');
|
||||
|
||||
expect(
|
||||
await screen.findByTestId('owner-name-container')
|
||||
).toBeInTheDocument();
|
||||
expect(owner).toBeInTheDocument();
|
||||
expect(owner.textContent).toEqual(MOCK_QUERIES[0].owner?.displayName);
|
||||
expect(
|
||||
|
@ -312,7 +312,7 @@ const DomainDetailsPage = ({
|
||||
setIsNameEditing(false);
|
||||
};
|
||||
|
||||
const onStyleSave = (data: Style) => {
|
||||
const onStyleSave = async (data: Style) => {
|
||||
const style: Style = {
|
||||
// if color/iconURL is empty or undefined send undefined
|
||||
color: data.color ? data.color : undefined,
|
||||
@ -323,7 +323,7 @@ const DomainDetailsPage = ({
|
||||
style,
|
||||
};
|
||||
|
||||
onUpdate(updatedDetails);
|
||||
await onUpdate(updatedDetails);
|
||||
setIsStyleEditing(false);
|
||||
};
|
||||
|
||||
|
@ -136,7 +136,7 @@ const DocumentationTab = ({
|
||||
await onUpdate(updatedData as Domain | DataProduct);
|
||||
};
|
||||
|
||||
const handleExpertsUpdate = (data: Array<EntityReference>) => {
|
||||
const handleExpertsUpdate = async (data: Array<EntityReference>) => {
|
||||
if (!isEqual(data, domain.experts)) {
|
||||
let updatedDomain = cloneDeep(domain);
|
||||
const oldExperts = data.filter((d) => includes(domain.experts, d));
|
||||
@ -152,7 +152,7 @@ const DocumentationTab = ({
|
||||
...updatedDomain,
|
||||
experts: [...oldExperts, ...newExperts],
|
||||
};
|
||||
onUpdate(updatedDomain);
|
||||
await onUpdate(updatedDomain);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -123,7 +123,7 @@ const EntityVersionTimeLine: React.FC<EntityVersionTimelineProps> = ({
|
||||
|
||||
const versions = useMemo(
|
||||
() =>
|
||||
versionList.versions.map((v, i) => {
|
||||
versionList.versions?.map((v, i) => {
|
||||
const currV = JSON.parse(v);
|
||||
|
||||
const majorVersionChecks = () => {
|
||||
|
@ -24,12 +24,10 @@ import {
|
||||
getFormattedEntityData,
|
||||
getSortedTagsWithHighlight,
|
||||
} from '../../../../utils/EntitySummaryPanelUtils';
|
||||
import {
|
||||
DRAWER_NAVIGATION_OPTIONS,
|
||||
getOwnerNameWithProfilePic,
|
||||
} from '../../../../utils/EntityUtils';
|
||||
import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils';
|
||||
import { bytesToSize } from '../../../../utils/StringsUtils';
|
||||
import { getConfigObject } from '../../../../utils/TopicDetailsUtils';
|
||||
import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component';
|
||||
import SummaryPanelSkeleton from '../../../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component';
|
||||
import SummaryTagsDescription from '../../../common/SummaryTagsDescription/SummaryTagsDescription.component';
|
||||
import { SearchedDataProps } from '../../../SearchedData/SearchedData.interface';
|
||||
@ -75,11 +73,7 @@ function TopicSummary({
|
||||
const owner = entityDetails.owner;
|
||||
|
||||
return {
|
||||
value:
|
||||
getOwnerNameWithProfilePic(owner) ??
|
||||
t('label.no-entity', {
|
||||
entity: t('label.owner'),
|
||||
}),
|
||||
value: <OwnerLabel hasPermission={false} owner={owner} />,
|
||||
url: getTeamAndUserDetailsPath(owner?.name ?? ''),
|
||||
isLink: !isEmpty(owner?.name),
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ export type GlossaryDetailsProps = {
|
||||
termsLoading: boolean;
|
||||
updateGlossary: (value: Glossary) => Promise<void>;
|
||||
updateVote?: (data: VotingDataProps) => Promise<void>;
|
||||
handleGlossaryDelete: (id: string) => void;
|
||||
handleGlossaryDelete: (id: string) => Promise<void>;
|
||||
refreshGlossaryTerms: () => void;
|
||||
onAddGlossaryTerm: (glossaryTerm: GlossaryTerm | undefined) => void;
|
||||
onEditGlossaryTerm: (glossaryTerm: GlossaryTerm) => void;
|
||||
|
@ -32,6 +32,7 @@ import {
|
||||
getDiffByFieldName,
|
||||
getRemovedDiffElement,
|
||||
} from '../../../utils/EntityVersionUtils';
|
||||
import { UserTeam } from '../../common/AssigneeList/AssigneeList.interface';
|
||||
import ProfilePicture from '../../common/ProfilePicture/ProfilePicture';
|
||||
|
||||
interface GlossaryReviewersProps {
|
||||
@ -70,6 +71,7 @@ function GlossaryReviewers({
|
||||
<Space className="m-r-xss" key={reviewer.id} size={4}>
|
||||
<ProfilePicture
|
||||
displayName={getEntityName(reviewer)}
|
||||
isTeam={reviewer.type === UserTeam.Team}
|
||||
name={reviewer.name ?? ''}
|
||||
textClass="text-xs"
|
||||
width="20"
|
||||
|
@ -207,9 +207,9 @@ const GlossaryHeader = ({
|
||||
history.push(path);
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
const handleDelete = async () => {
|
||||
const { id } = selectedData;
|
||||
onDelete(id);
|
||||
await onDelete(id);
|
||||
setIsDelete(false);
|
||||
};
|
||||
|
||||
@ -227,7 +227,7 @@ const GlossaryHeader = ({
|
||||
setIsNameEditing(false);
|
||||
};
|
||||
|
||||
const onStyleSave = (data: Style) => {
|
||||
const onStyleSave = async (data: Style) => {
|
||||
const style: Style = {
|
||||
// if color/iconURL is empty or undefined send undefined
|
||||
color: data.color ? data.color : undefined,
|
||||
@ -238,7 +238,7 @@ const GlossaryHeader = ({
|
||||
style,
|
||||
};
|
||||
|
||||
onUpdate(updatedDetails);
|
||||
await onUpdate(updatedDetails);
|
||||
setIsStyleEditing(false);
|
||||
};
|
||||
|
||||
|
@ -21,8 +21,8 @@ export interface GlossaryHeaderProps {
|
||||
permissions: OperationPermission;
|
||||
selectedData: Glossary | GlossaryTerm;
|
||||
isGlossary: boolean;
|
||||
onUpdate: (data: GlossaryTerm | Glossary) => void | Promise<void>;
|
||||
onDelete: (id: string) => void;
|
||||
onUpdate: (data: GlossaryTerm | Glossary) => Promise<void>;
|
||||
onDelete: (id: string) => Promise<void>;
|
||||
onAssetAdd?: () => void;
|
||||
updateVote?: (data: VotingDataProps) => Promise<void>;
|
||||
onAddGlossaryTerm: (glossaryTerm: GlossaryTerm | undefined) => void;
|
||||
|
@ -12,7 +12,7 @@
|
||||
*/
|
||||
import Icon from '@ant-design/icons/lib/components/Icon';
|
||||
import { Button, Col, Form, Input, Modal, Row } from 'antd';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as IconDelete } from '../../../assets/svg/ic-delete.svg';
|
||||
import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg';
|
||||
@ -22,7 +22,7 @@ interface GlossaryTermReferencesModalProps {
|
||||
references: TermReference[];
|
||||
isVisible: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (values: TermReference[]) => void;
|
||||
onSave: (values: TermReference[]) => Promise<void>;
|
||||
}
|
||||
|
||||
const GlossaryTermReferencesModal = ({
|
||||
@ -33,13 +33,17 @@ const GlossaryTermReferencesModal = ({
|
||||
}: GlossaryTermReferencesModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm<{ references: TermReference[] }>();
|
||||
const [saving, setSaving] = useState<boolean>(false);
|
||||
|
||||
const handleSubmit = async (obj: { references: TermReference[] }) => {
|
||||
try {
|
||||
setSaving(true);
|
||||
await form.validateFields();
|
||||
onSave(obj.references);
|
||||
await onSave(obj.references);
|
||||
} catch (_) {
|
||||
// Nothing here
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -69,8 +73,9 @@ const GlossaryTermReferencesModal = ({
|
||||
<Button
|
||||
data-testid="save-btn"
|
||||
key="save-btn"
|
||||
loading={saving}
|
||||
type="primary"
|
||||
onClick={() => form.submit()}>
|
||||
onClick={form.submit}>
|
||||
{t('label.save')}
|
||||
</Button>,
|
||||
]}
|
||||
|
@ -26,6 +26,7 @@ import { FEED_COUNT_INITIAL_DATA } from '../../../constants/entity.constants';
|
||||
import { EntityField } from '../../../constants/Feeds.constants';
|
||||
import { EntityTabs, EntityType } from '../../../enums/entity.enum';
|
||||
import { SearchIndex } from '../../../enums/search.enum';
|
||||
import { Glossary } from '../../../generated/entity/data/glossary';
|
||||
import {
|
||||
GlossaryTerm,
|
||||
Status,
|
||||
@ -153,8 +154,8 @@ const GlossaryTermsV1 = ({
|
||||
[glossaryTerm, handleGlossaryTermUpdate]
|
||||
);
|
||||
|
||||
const onTermUpdate = async (data: GlossaryTerm) => {
|
||||
await handleGlossaryTermUpdate(data);
|
||||
const onTermUpdate = async (data: GlossaryTerm | Glossary) => {
|
||||
await handleGlossaryTermUpdate(data as GlossaryTerm);
|
||||
getEntityFeedCount();
|
||||
};
|
||||
|
||||
@ -170,7 +171,7 @@ const GlossaryTermsV1 = ({
|
||||
permissions={permissions}
|
||||
selectedData={glossaryTerm}
|
||||
onThreadLinkSelect={onThreadLinkSelect}
|
||||
onUpdate={async (data) => await onTermUpdate(data as GlossaryTerm)}
|
||||
onUpdate={onTermUpdate}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@ -334,7 +335,7 @@ const GlossaryTermsV1 = ({
|
||||
onAddGlossaryTerm={onAddGlossaryTerm}
|
||||
onAssetAdd={() => setAssetModelVisible(true)}
|
||||
onDelete={handleGlossaryTermDelete}
|
||||
onUpdate={(data) => onTermUpdate(data as GlossaryTerm)}
|
||||
onUpdate={onTermUpdate}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
|
@ -21,7 +21,7 @@ export interface GlossaryTermsV1Props {
|
||||
glossaryTerm: GlossaryTerm;
|
||||
childGlossaryTerms: GlossaryTerm[];
|
||||
handleGlossaryTermUpdate: (data: GlossaryTerm) => Promise<void>;
|
||||
handleGlossaryTermDelete: (id: string) => void;
|
||||
handleGlossaryTermDelete: (id: string) => Promise<void>;
|
||||
refreshGlossaryTerms: () => void;
|
||||
onAssetClick?: (asset?: EntityDetailsObjectInterface) => void;
|
||||
isSummaryPanelOpen: boolean;
|
||||
|
@ -47,7 +47,7 @@ interface GlossaryTermReferencesProps {
|
||||
isVersionView?: boolean;
|
||||
glossaryTerm: GlossaryTerm;
|
||||
permissions: OperationPermission;
|
||||
onGlossaryTermUpdate: (glossaryTerm: GlossaryTerm) => void;
|
||||
onGlossaryTermUpdate: (glossaryTerm: GlossaryTerm) => Promise<void>;
|
||||
}
|
||||
|
||||
const GlossaryTermReferences = ({
|
||||
@ -74,7 +74,7 @@ const GlossaryTermReferences = ({
|
||||
references: updatedRef,
|
||||
};
|
||||
|
||||
onGlossaryTermUpdate(updatedGlossaryTerm);
|
||||
await onGlossaryTermUpdate(updatedGlossaryTerm);
|
||||
if (updateState) {
|
||||
setReferences(updatedRef);
|
||||
}
|
||||
@ -85,10 +85,6 @@ const GlossaryTermReferences = ({
|
||||
}
|
||||
};
|
||||
|
||||
const onReferenceModalSave = (values: TermReference[]) => {
|
||||
handleReferencesSave(values);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setReferences(glossaryTerm.references ? glossaryTerm.references : []);
|
||||
}, [glossaryTerm.references]);
|
||||
@ -246,9 +242,7 @@ const GlossaryTermReferences = ({
|
||||
onClose={() => {
|
||||
setIsViewMode(true);
|
||||
}}
|
||||
onSave={(values: TermReference[]) => {
|
||||
onReferenceModalSave(values);
|
||||
}}
|
||||
onSave={handleReferencesSave}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -38,7 +38,7 @@ interface GlossaryTermSynonymsProps {
|
||||
isVersionView?: boolean;
|
||||
permissions: OperationPermission;
|
||||
glossaryTerm: GlossaryTerm;
|
||||
onGlossaryTermUpdate: (glossaryTerm: GlossaryTerm) => void;
|
||||
onGlossaryTermUpdate: (glossaryTerm: GlossaryTerm) => Promise<void>;
|
||||
}
|
||||
|
||||
const GlossaryTermSynonyms = ({
|
||||
@ -49,6 +49,7 @@ const GlossaryTermSynonyms = ({
|
||||
}: GlossaryTermSynonymsProps) => {
|
||||
const [isViewMode, setIsViewMode] = useState<boolean>(true);
|
||||
const [synonyms, setSynonyms] = useState<string[]>([]);
|
||||
const [saving, setSaving] = useState<boolean>(false);
|
||||
|
||||
const getSynonyms = () => (
|
||||
<div className="d-flex flex-wrap">
|
||||
@ -156,15 +157,16 @@ const GlossaryTermSynonyms = ({
|
||||
setIsViewMode(true);
|
||||
};
|
||||
|
||||
const handleSynonymsSave = (newSynonyms: string[]) => {
|
||||
if (!isEqual(newSynonyms, glossaryTerm.synonyms)) {
|
||||
const handleSynonymsSave = async () => {
|
||||
if (!isEqual(synonyms, glossaryTerm.synonyms)) {
|
||||
let updatedGlossaryTerm = cloneDeep(glossaryTerm);
|
||||
updatedGlossaryTerm = {
|
||||
...updatedGlossaryTerm,
|
||||
synonyms: newSynonyms,
|
||||
synonyms,
|
||||
};
|
||||
|
||||
onGlossaryTermUpdate(updatedGlossaryTerm);
|
||||
setSaving(true);
|
||||
await onGlossaryTermUpdate(updatedGlossaryTerm);
|
||||
setSaving(false);
|
||||
}
|
||||
setIsViewMode(true);
|
||||
};
|
||||
@ -217,9 +219,10 @@ const GlossaryTermSynonyms = ({
|
||||
className="w-6 p-x-05"
|
||||
data-testid="save-synonym-btn"
|
||||
icon={<CheckOutlined size={12} />}
|
||||
loading={saving}
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={() => handleSynonymsSave(synonyms)}
|
||||
onClick={handleSynonymsSave}
|
||||
/>
|
||||
</Space>
|
||||
|
||||
|
@ -77,6 +77,7 @@ const RelatedTerms = ({
|
||||
if (!isArray(selectedData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newOptions = uniqWith(
|
||||
options,
|
||||
(arrVal, othVal) => arrVal.id === othVal.id
|
||||
|
@ -23,8 +23,8 @@ export type GlossaryV1Props = {
|
||||
isGlossaryActive: boolean;
|
||||
updateGlossary: (value: Glossary) => Promise<void>;
|
||||
onGlossaryTermUpdate: (value: GlossaryTerm) => Promise<void>;
|
||||
onGlossaryDelete: (id: string) => void | Promise<void>;
|
||||
onGlossaryTermDelete: (id: string) => void | Promise<void>;
|
||||
onGlossaryDelete: (id: string) => Promise<void>;
|
||||
onGlossaryTermDelete: (id: string) => Promise<void>;
|
||||
isVersionsView: boolean;
|
||||
onAssetClick?: (asset?: EntityDetailsObjectInterface) => void;
|
||||
isSummaryPanelOpen: boolean;
|
||||
|
@ -11,7 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { AxiosError } from 'axios';
|
||||
import { noop, toString } from 'lodash';
|
||||
import { toString } from 'lodash';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { LOADING_STATE } from '../../../enums/common.enum';
|
||||
@ -113,8 +113,8 @@ const GlossaryVersion = ({ isGlossary = false }: GlossaryVersionProps) => {
|
||||
isSummaryPanelOpen={false}
|
||||
selectedData={selectedData as Glossary}
|
||||
updateGlossary={() => Promise.resolve()}
|
||||
onGlossaryDelete={noop}
|
||||
onGlossaryTermDelete={noop}
|
||||
onGlossaryDelete={() => Promise.resolve()}
|
||||
onGlossaryTermDelete={() => Promise.resolve()}
|
||||
onGlossaryTermUpdate={() => Promise.resolve()}
|
||||
/>
|
||||
)}
|
||||
|
@ -14,7 +14,7 @@
|
||||
import { HTMLAttributes } from 'react';
|
||||
|
||||
export interface EntityDeleteModalProp extends HTMLAttributes<HTMLDivElement> {
|
||||
onConfirm: () => void;
|
||||
onConfirm: () => Promise<void>;
|
||||
onCancel: () => void;
|
||||
entityName: string;
|
||||
entityType: string;
|
||||
|
@ -30,6 +30,7 @@ const EntityDeleteModal = ({
|
||||
bodyText,
|
||||
}: EntityDeleteModalProp) => {
|
||||
const [name, setName] = useState('');
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
const handleOnChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setName(e.target.value);
|
||||
@ -42,6 +43,12 @@ const EntityDeleteModal = ({
|
||||
[loadingState]
|
||||
);
|
||||
|
||||
const handleSave = async () => {
|
||||
setSaving(true);
|
||||
await onConfirm();
|
||||
setSaving(false);
|
||||
};
|
||||
|
||||
// To remove the entered text in the modal input after modal closed
|
||||
useEffect(() => {
|
||||
setName('');
|
||||
@ -67,9 +74,9 @@ const EntityDeleteModal = ({
|
||||
<Button
|
||||
data-testid={isLoadingWaiting ? 'loading-button' : 'confirm-button'}
|
||||
disabled={!isNameMatching}
|
||||
loading={isLoadingWaiting}
|
||||
loading={saving}
|
||||
type="primary"
|
||||
onClick={onConfirm}>
|
||||
onClick={handleSave}>
|
||||
{t('label.confirm')}
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -11,7 +11,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Form, FormProps, Input, Modal } from 'antd';
|
||||
|
||||
import { isUndefined, omit } from 'lodash';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -23,9 +22,17 @@ import { StyleModalProps, StyleWithInput } from './StyleModal.interface';
|
||||
const StyleModal = ({ open, onCancel, onSubmit, style }: StyleModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const [saving, setSaving] = React.useState<boolean>(false);
|
||||
|
||||
const handleSubmit: FormProps<StyleWithInput>['onFinish'] = (value) => {
|
||||
onSubmit(omit(value, 'colorInput'));
|
||||
const handleSubmit: FormProps<StyleWithInput>['onFinish'] = async (value) => {
|
||||
try {
|
||||
setSaving(true);
|
||||
await onSubmit(omit(value, 'colorInput'));
|
||||
} catch (err) {
|
||||
// Error is handled in parent component
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -34,6 +41,7 @@ const StyleModal = ({ open, onCancel, onSubmit, style }: StyleModalProps) => {
|
||||
okButtonProps={{
|
||||
form: 'style-modal',
|
||||
htmlType: 'submit',
|
||||
loading: saving,
|
||||
}}
|
||||
okText={t('label.submit')}
|
||||
open={open}
|
||||
|
@ -15,7 +15,7 @@ import { Style } from '../../../generated/type/schema';
|
||||
export interface StyleModalProps {
|
||||
open: boolean;
|
||||
style?: Style;
|
||||
onSubmit: (value: Style) => void;
|
||||
onSubmit: (value: Style) => Promise<void>;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
*/
|
||||
import { Button, Popover, Space, Typography } from 'antd';
|
||||
import { t } from 'i18next';
|
||||
import { noop } from 'lodash';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg';
|
||||
@ -46,7 +45,7 @@ export const PersonaListItemRenderer = (props: EntityReference) => {
|
||||
export const PersonaSelectableList = ({
|
||||
hasPermission,
|
||||
selectedPersonas = [],
|
||||
onUpdate = noop,
|
||||
onUpdate,
|
||||
children,
|
||||
popoverProps,
|
||||
multiSelect = false,
|
||||
@ -96,11 +95,11 @@ export const PersonaSelectableList = ({
|
||||
};
|
||||
|
||||
const handleUpdate = useCallback(
|
||||
(users: EntityReference[]) => {
|
||||
async (users: EntityReference[]) => {
|
||||
if (multiSelect) {
|
||||
(onUpdate as (users: EntityReference[]) => void)(users);
|
||||
await (onUpdate as (users: EntityReference[]) => Promise<void>)(users);
|
||||
} else {
|
||||
(onUpdate as (users: EntityReference) => void)(users[0]);
|
||||
await (onUpdate as (users: EntityReference) => Promise<void>)(users[0]);
|
||||
}
|
||||
|
||||
setPopupVisible(false);
|
||||
|
@ -116,18 +116,18 @@ const Ingestion: React.FC<IngestionProps> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (id: string, displayName: string) => {
|
||||
const handleDelete = async (id: string, displayName: string) => {
|
||||
setDeleteSelection({ id, name: displayName, state: 'waiting' });
|
||||
deleteIngestion(id, displayName)
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
setDeleteSelection({ id, name: displayName, state: 'success' });
|
||||
handleCancelConfirmationModal();
|
||||
}, 500);
|
||||
})
|
||||
.catch(() => {
|
||||
try {
|
||||
await deleteIngestion(id, displayName);
|
||||
|
||||
setTimeout(() => {
|
||||
setDeleteSelection({ id, name: displayName, state: 'success' });
|
||||
handleCancelConfirmationModal();
|
||||
});
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
handleCancelConfirmationModal();
|
||||
}
|
||||
};
|
||||
|
||||
const getSearchedIngestions = () => {
|
||||
|
@ -100,7 +100,7 @@ export interface TeamDetailsProp {
|
||||
descriptionHandler: (value: boolean) => void;
|
||||
onDescriptionUpdate: (value: string) => Promise<void>;
|
||||
updateTeamHandler: (data: Team, fetchTeam?: boolean) => Promise<void>;
|
||||
handleAddUser: (data: Array<EntityReference>) => void;
|
||||
handleAddUser: (data: Array<EntityReference>) => Promise<void>;
|
||||
afterDeleteAction: (isSoftDeleted?: boolean) => void;
|
||||
removeUserFromTeam: (id: string) => Promise<void>;
|
||||
handleJoinTeamClick: (id: string, data: Operation[]) => void;
|
||||
|
@ -17,6 +17,6 @@ import { EntityReference } from '../../../../../generated/type/entityReference';
|
||||
export interface UserTabProps {
|
||||
permission: OperationPermission;
|
||||
currentTeam: Team;
|
||||
onAddUser: (data: EntityReference[]) => void;
|
||||
onAddUser: (data: EntityReference[]) => Promise<void>;
|
||||
onRemoveUser: (id: string) => Promise<void>;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
|
||||
import { Button, Col, Form, Row, Space } from 'antd';
|
||||
import { useForm } from 'antd/lib/form/Form';
|
||||
import { DefaultOptionType } from 'antd/lib/select';
|
||||
import React, { useState } from 'react';
|
||||
import AsyncSelectList from '../../common/AsyncSelectList/AsyncSelectList';
|
||||
import './tag-select-fom.style.less';
|
||||
@ -30,15 +31,20 @@ const TagSelectForm = ({
|
||||
const [form] = useForm();
|
||||
const [isSubmitLoading, setIsSubmitLoading] = useState(false);
|
||||
|
||||
const handleSave = async (data: {
|
||||
tags: DefaultOptionType | DefaultOptionType[];
|
||||
}) => {
|
||||
setIsSubmitLoading(true);
|
||||
await onSubmit(data.tags);
|
||||
setIsSubmitLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
initialValues={{ tags: defaultValue }}
|
||||
name="tagsForm"
|
||||
onFinish={(data) => {
|
||||
setIsSubmitLoading(true);
|
||||
onSubmit(data.tags);
|
||||
}}>
|
||||
onFinish={handleSave}>
|
||||
<Row gutter={[0, 8]}>
|
||||
<Col className="gutter-row d-flex justify-end" span={24}>
|
||||
<Space align="center">
|
||||
|
@ -15,41 +15,26 @@ import Icon from '@ant-design/icons/lib/components/Icon';
|
||||
import { Button, Space } from 'antd';
|
||||
import Tooltip, { RenderFunction } from 'antd/lib/tooltip';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
isEmpty,
|
||||
isString,
|
||||
isUndefined,
|
||||
lowerCase,
|
||||
noop,
|
||||
toLower,
|
||||
} from 'lodash';
|
||||
import { isEmpty, isString, isUndefined, lowerCase, toLower } from 'lodash';
|
||||
import { ExtraInfo } from 'Models';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
|
||||
import { ReactComponent as IconExternalLink } from '../../../assets/svg/external-links.svg';
|
||||
import { ReactComponent as DomainIcon } from '../../../assets/svg/ic-domain.svg';
|
||||
import { ReactComponent as IconInfoSecondary } from '../../../assets/svg/icon-info.svg';
|
||||
import { ReactComponent as IconTeamsGrey } from '../../../assets/svg/teams-grey.svg';
|
||||
import { DE_ACTIVE_COLOR } from '../../../constants/constants';
|
||||
import { Tag } from '../../../generated/entity/classification/tag';
|
||||
import { Dashboard } from '../../../generated/entity/data/dashboard';
|
||||
import { Table } from '../../../generated/entity/data/table';
|
||||
import { TagLabel } from '../../../generated/type/tagLabel';
|
||||
import { getTeamsUser } from '../../../utils/CommonUtils';
|
||||
import { useAuthContext } from '../../Auth/AuthProviders/AuthProvider';
|
||||
import ProfilePicture from '../ProfilePicture/ProfilePicture';
|
||||
import TierCard from '../TierCard/TierCard';
|
||||
import { UserSelectableList } from '../UserSelectableList/UserSelectableList.component';
|
||||
import { UserTeamSelectableList } from '../UserTeamSelectableList/UserTeamSelectableList.component';
|
||||
import './entity-summary-details.style.less';
|
||||
|
||||
export interface GetInfoElementsProps {
|
||||
data: ExtraInfo;
|
||||
updateOwner?: (value: Table['owner']) => void;
|
||||
tier?: TagLabel;
|
||||
currentTier?: string;
|
||||
updateTier?: (value?: Tag) => Promise<void>;
|
||||
currentOwner?: Dashboard['owner'];
|
||||
deleted?: boolean;
|
||||
allowTeamOwner?: boolean;
|
||||
@ -69,35 +54,12 @@ const InfoIcon = ({
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
const EntitySummaryDetails = ({
|
||||
data,
|
||||
tier,
|
||||
updateOwner,
|
||||
updateTier,
|
||||
currentOwner,
|
||||
deleted = false,
|
||||
allowTeamOwner = true,
|
||||
}: GetInfoElementsProps) => {
|
||||
const EntitySummaryDetails = ({ data }: GetInfoElementsProps) => {
|
||||
let retVal = <></>;
|
||||
const { t } = useTranslation();
|
||||
const { currentUser } = useAuthContext();
|
||||
const displayVal = data.placeholderText || data.value;
|
||||
|
||||
const ownerDropdown = allowTeamOwner ? (
|
||||
<UserTeamSelectableList
|
||||
hasPermission={Boolean(updateOwner)}
|
||||
owner={currentOwner}
|
||||
onUpdate={updateOwner ?? noop}
|
||||
/>
|
||||
) : (
|
||||
<UserSelectableList
|
||||
hasPermission={Boolean(updateOwner)}
|
||||
multiSelect={false}
|
||||
selectedUsers={currentOwner ? [currentOwner] : []}
|
||||
onUpdate={updateOwner ?? noop}
|
||||
/>
|
||||
);
|
||||
|
||||
const { isEntityDetails, userDetails, isTier, isOwner, isTeamOwner } =
|
||||
useMemo(() => {
|
||||
const userDetails = currentUser ? getTeamsUser(data, currentUser) : {};
|
||||
@ -158,7 +120,6 @@ const EntitySummaryDetails = ({
|
||||
className="d-flex gap-1 items-center"
|
||||
data-testid="owner-link">
|
||||
{t('label.no-entity', { entity: t('label.owner') })}
|
||||
{updateOwner && !deleted ? ownerDropdown : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -169,20 +130,7 @@ const EntitySummaryDetails = ({
|
||||
{
|
||||
retVal =
|
||||
!displayVal || displayVal === '--' ? (
|
||||
<>
|
||||
{t('label.no-entity', { entity: t('label.tier') })}
|
||||
{updateTier && !deleted ? (
|
||||
<TierCard currentTier={tier?.tagFQN} updateTier={updateTier}>
|
||||
<span data-testid="edit-tier">
|
||||
<EditIcon
|
||||
className="cursor-pointer"
|
||||
color={DE_ACTIVE_COLOR}
|
||||
width={14}
|
||||
/>
|
||||
</span>
|
||||
</TierCard>
|
||||
) : null}
|
||||
</>
|
||||
<>{t('label.no-entity', { entity: t('label.tier') })}</>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
@ -289,8 +237,6 @@ const EntitySummaryDetails = ({
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
{/* Edit icon with dropdown */}
|
||||
{(isOwner || isTier) && (updateOwner ? ownerDropdown : null)}
|
||||
</>
|
||||
) : isOwner ? (
|
||||
<>
|
||||
@ -307,8 +253,6 @@ const EntitySummaryDetails = ({
|
||||
{displayVal}
|
||||
</Button>
|
||||
</span>
|
||||
{/* Edit icon with dropdown */}
|
||||
{updateOwner ? ownerDropdown : null}
|
||||
</>
|
||||
) : isTier ? (
|
||||
<Space
|
||||
@ -322,18 +266,6 @@ const EntitySummaryDetails = ({
|
||||
direction="horizontal"
|
||||
title={displayVal as string}>
|
||||
<span data-testid="Tier">{displayVal}</span>
|
||||
|
||||
{updateTier && !deleted ? (
|
||||
<TierCard currentTier={tier?.tagFQN} updateTier={updateTier}>
|
||||
<span data-testid="edit-tier">
|
||||
<EditIcon
|
||||
className="cursor-pointer"
|
||||
color={DE_ACTIVE_COLOR}
|
||||
width={14}
|
||||
/>
|
||||
</span>
|
||||
</TierCard>
|
||||
) : null}
|
||||
</Space>
|
||||
) : (
|
||||
<span>{displayVal}</span>
|
||||
|
@ -260,7 +260,10 @@ export const SelectableList = ({
|
||||
/>
|
||||
}
|
||||
itemLayout="vertical"
|
||||
loading={{ spinning: fetching || updating, indicator: <Loader /> }}
|
||||
loading={{
|
||||
spinning: fetching || updating,
|
||||
indicator: <Loader size="small" />,
|
||||
}}
|
||||
locale={{
|
||||
emptyText: emptyPlaceholderText ?? t('message.no-data-available'),
|
||||
}}
|
||||
|
@ -22,7 +22,7 @@ export interface SelectableListProps {
|
||||
multiSelect?: boolean;
|
||||
selectedItems: EntityReference[];
|
||||
onCancel: () => void;
|
||||
onUpdate: (updatedItems: EntityReference[]) => void | Promise<void>;
|
||||
onUpdate: (updatedItems: EntityReference[]) => Promise<void>;
|
||||
searchPlaceholder?: string;
|
||||
customTagRenderer?: (props: EntityReference) => ReactNode;
|
||||
searchBarDataTestId?: string;
|
||||
|
@ -11,7 +11,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Button, Popover, Tooltip } from 'antd';
|
||||
import { noop } from 'lodash';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
|
||||
@ -35,7 +34,7 @@ import { UserSelectableListProps } from './UserSelectableList.interface';
|
||||
export const UserSelectableList = ({
|
||||
hasPermission,
|
||||
selectedUsers = [],
|
||||
onUpdate = noop,
|
||||
onUpdate,
|
||||
children,
|
||||
popoverProps,
|
||||
multiSelect = true,
|
||||
@ -100,11 +99,11 @@ export const UserSelectableList = ({
|
||||
};
|
||||
|
||||
const handleUpdate = useCallback(
|
||||
(users: EntityReference[]) => {
|
||||
async (users: EntityReference[]) => {
|
||||
if (multiSelect) {
|
||||
(onUpdate as (users: EntityReference[]) => void)(users);
|
||||
await (onUpdate as (users: EntityReference[]) => Promise<void>)(users);
|
||||
} else {
|
||||
(onUpdate as (users: EntityReference) => void)(users[0]);
|
||||
await (onUpdate as (users: EntityReference) => Promise<void>)(users[0]);
|
||||
}
|
||||
setPopupVisible(false);
|
||||
},
|
||||
|
@ -24,10 +24,10 @@ export type UserSelectableListProps =
|
||||
} & (
|
||||
| {
|
||||
multiSelect?: true;
|
||||
onUpdate: (updatedUsers: EntityReference[]) => void;
|
||||
onUpdate: (updatedUsers: EntityReference[]) => Promise<void>;
|
||||
}
|
||||
| {
|
||||
multiSelect: false;
|
||||
onUpdate: (updatedUsers: EntityReference) => void;
|
||||
onUpdate: (updatedUsers: EntityReference) => Promise<void>;
|
||||
}
|
||||
);
|
||||
|
@ -293,35 +293,36 @@ const GlossaryPage = () => {
|
||||
[selectedData]
|
||||
);
|
||||
|
||||
const handleGlossaryTermDelete = (id: string) => {
|
||||
setDeleteStatus(LOADING_STATE.WAITING);
|
||||
deleteGlossaryTerm(id)
|
||||
.then(() => {
|
||||
setDeleteStatus(LOADING_STATE.SUCCESS);
|
||||
showSuccessToast(
|
||||
t('server.entity-deleted-successfully', {
|
||||
entity: t('label.glossary-term'),
|
||||
})
|
||||
);
|
||||
let fqn;
|
||||
if (glossaryFqn) {
|
||||
const fqnArr = Fqn.split(glossaryFqn);
|
||||
fqnArr.pop();
|
||||
fqn = fqnArr.join(FQN_SEPARATOR_CHAR);
|
||||
}
|
||||
setIsLoading(true);
|
||||
history.push(getGlossaryPath(fqn));
|
||||
fetchGlossaryList();
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
t('server.delete-entity-error', {
|
||||
entity: t('label.glossary-term'),
|
||||
})
|
||||
);
|
||||
})
|
||||
.finally(() => setDeleteStatus(LOADING_STATE.INITIAL));
|
||||
const handleGlossaryTermDelete = async (id: string) => {
|
||||
try {
|
||||
setDeleteStatus(LOADING_STATE.WAITING);
|
||||
await deleteGlossaryTerm(id);
|
||||
|
||||
setDeleteStatus(LOADING_STATE.SUCCESS);
|
||||
showSuccessToast(
|
||||
t('server.entity-deleted-successfully', {
|
||||
entity: t('label.glossary-term'),
|
||||
})
|
||||
);
|
||||
let fqn;
|
||||
if (glossaryFqn) {
|
||||
const fqnArr = Fqn.split(glossaryFqn);
|
||||
fqnArr.pop();
|
||||
fqn = fqnArr.join(FQN_SEPARATOR_CHAR);
|
||||
}
|
||||
setIsLoading(true);
|
||||
history.push(getGlossaryPath(fqn));
|
||||
fetchGlossaryList();
|
||||
} catch (err) {
|
||||
showErrorToast(
|
||||
err,
|
||||
t('server.delete-entity-error', {
|
||||
entity: t('label.glossary-term'),
|
||||
})
|
||||
);
|
||||
} finally {
|
||||
setDeleteStatus(LOADING_STATE.INITIAL);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAssetClick = useCallback(
|
||||
|
@ -201,7 +201,7 @@ const IncidentManagerDetailPage = () => {
|
||||
const jsonPatch = compare(data, updatedTestCase);
|
||||
|
||||
if (jsonPatch.length && data.id) {
|
||||
updateTestCase(data.id, jsonPatch);
|
||||
await updateTestCase(data.id, jsonPatch);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import {
|
||||
Database,
|
||||
@ -32,7 +33,7 @@ const mockParams = {
|
||||
};
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
Link: jest.fn().mockImplementation(({ children }) => <div>{children}</div>),
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useParams: jest.fn().mockImplementation(() => mockParams),
|
||||
}));
|
||||
|
||||
@ -147,7 +148,9 @@ const props: ServiceVersionMainTabContentProps = {
|
||||
|
||||
describe('ServiceVersionMainTabContent tests', () => {
|
||||
it('Component should render properly provided proper data', () => {
|
||||
render(<ServiceVersionMainTabContent {...props} />);
|
||||
render(<ServiceVersionMainTabContent {...props} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
|
||||
const entityTable = screen.getByTestId('service-children-table');
|
||||
const entityName = screen.getByText('ecommerce_db');
|
||||
@ -168,7 +171,9 @@ describe('ServiceVersionMainTabContent tests', () => {
|
||||
});
|
||||
|
||||
it('Loader should be displayed if isServiceLoading is true', async () => {
|
||||
render(<ServiceVersionMainTabContent {...props} isServiceLoading />);
|
||||
render(<ServiceVersionMainTabContent {...props} isServiceLoading />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
|
||||
const loader = await screen.findByTestId('skeleton-table');
|
||||
|
||||
|
@ -221,7 +221,7 @@ describe('TestDetailsPageV1 component', () => {
|
||||
});
|
||||
|
||||
expect(getTableDetailsByFQN).toHaveBeenCalledWith('fqn', {
|
||||
fields: `${COMMON_API_FIELDS}`,
|
||||
fields: `${COMMON_API_FIELDS},usageSummary`,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -159,7 +159,7 @@ const TableDetailsPageV1 = () => {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [tableFqn]);
|
||||
}, [tableFqn, viewUsagePermission]);
|
||||
|
||||
const fetchQueryCount = async () => {
|
||||
if (!tableDetails?.id) {
|
||||
|
@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
import { Form, Modal, Typography } from 'antd';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { VALIDATION_MESSAGES } from '../../constants/constants';
|
||||
import {
|
||||
@ -22,7 +22,7 @@ import {
|
||||
import { DEFAULT_FORM_VALUE } from '../../constants/Tags.constant';
|
||||
import { FieldProp, FieldTypes } from '../../interface/FormUtils.interface';
|
||||
import { generateFormFields } from '../../utils/formUtils';
|
||||
import { RenameFormProps } from './TagsPage.interface';
|
||||
import { RenameFormProps, SubmitProps } from './TagsPage.interface';
|
||||
|
||||
const TagsForm = ({
|
||||
visible,
|
||||
@ -40,6 +40,7 @@ const TagsForm = ({
|
||||
}: RenameFormProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
@ -202,6 +203,18 @@ const TagsForm = ({
|
||||
: []),
|
||||
];
|
||||
|
||||
const handleSave = async (data: SubmitProps) => {
|
||||
try {
|
||||
setSaving(true);
|
||||
await onSubmit(data);
|
||||
form.setFieldsValue(DEFAULT_FORM_VALUE);
|
||||
} catch {
|
||||
// Parent will handle the error
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
centered
|
||||
@ -212,7 +225,7 @@ const TagsForm = ({
|
||||
form: 'tags',
|
||||
type: 'primary',
|
||||
htmlType: 'submit',
|
||||
loading: isLoading,
|
||||
loading: isLoading || saving,
|
||||
}}
|
||||
okText={t('label.save')}
|
||||
open={visible}
|
||||
@ -232,10 +245,7 @@ const TagsForm = ({
|
||||
layout="vertical"
|
||||
name="tags"
|
||||
validateMessages={VALIDATION_MESSAGES}
|
||||
onFinish={(data) => {
|
||||
onSubmit(data);
|
||||
form.setFieldsValue(DEFAULT_FORM_VALUE);
|
||||
}}>
|
||||
onFinish={handleSave}>
|
||||
{generateFormFields(formFields)}
|
||||
</Form>
|
||||
</Modal>
|
||||
|
@ -44,7 +44,7 @@ export interface RenameFormProps {
|
||||
onCancel: () => void;
|
||||
header: string;
|
||||
initialValues?: Tag;
|
||||
onSubmit: (value: SubmitProps) => void;
|
||||
onSubmit: (value: SubmitProps) => Promise<void>;
|
||||
showMutuallyExclusive?: boolean;
|
||||
isClassification?: boolean;
|
||||
data?: Classification[];
|
||||
|
@ -267,56 +267,55 @@ const TagsPage = () => {
|
||||
* @param categoryName - tag category name
|
||||
* @param tagId - tag id
|
||||
*/
|
||||
const handleDeleteTag = (tagId: string) => {
|
||||
deleteTag(tagId)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
if (currentClassification) {
|
||||
setDeleteStatus(LOADING_STATE.SUCCESS);
|
||||
setClassifications((prev) =>
|
||||
prev.map((item) => {
|
||||
if (
|
||||
item.fullyQualifiedName ===
|
||||
currentClassification.fullyQualifiedName
|
||||
) {
|
||||
return {
|
||||
...item,
|
||||
termCount: (item.termCount ?? 0) - 1,
|
||||
};
|
||||
}
|
||||
const handleDeleteTag = async (tagId: string) => {
|
||||
try {
|
||||
const res = await deleteTag(tagId);
|
||||
|
||||
return item;
|
||||
})
|
||||
);
|
||||
}
|
||||
classificationDetailsRef.current?.refreshClassificationTags();
|
||||
} else {
|
||||
showErrorToast(
|
||||
t('server.delete-entity-error', {
|
||||
entity: t('label.tag-lowercase'),
|
||||
if (res) {
|
||||
if (currentClassification) {
|
||||
setDeleteStatus(LOADING_STATE.SUCCESS);
|
||||
setClassifications((prev) =>
|
||||
prev.map((item) => {
|
||||
if (
|
||||
item.fullyQualifiedName ===
|
||||
currentClassification.fullyQualifiedName
|
||||
) {
|
||||
return {
|
||||
...item,
|
||||
termCount: (item.termCount ?? 0) - 1,
|
||||
};
|
||||
}
|
||||
|
||||
return item;
|
||||
})
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
classificationDetailsRef.current?.refreshClassificationTags();
|
||||
} else {
|
||||
showErrorToast(
|
||||
err,
|
||||
t('server.delete-entity-error', { entity: t('label.tag-lowercase') })
|
||||
t('server.delete-entity-error', {
|
||||
entity: t('label.tag-lowercase'),
|
||||
})
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeleteTags({ data: undefined, state: false });
|
||||
setDeleteStatus(LOADING_STATE.INITIAL);
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
showErrorToast(
|
||||
err,
|
||||
t('server.delete-entity-error', { entity: t('label.tag-lowercase') })
|
||||
);
|
||||
} finally {
|
||||
setDeleteTags({ data: undefined, state: false });
|
||||
setDeleteStatus(LOADING_STATE.INITIAL);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* It redirects to respective function call based on tag/Classification
|
||||
*/
|
||||
const handleConfirmClick = () => {
|
||||
const handleConfirmClick = async () => {
|
||||
if (deleteTags.data?.id) {
|
||||
setDeleteStatus(LOADING_STATE.WAITING);
|
||||
handleDeleteTag(deleteTags.data.id);
|
||||
await handleDeleteTag(deleteTags.data.id);
|
||||
}
|
||||
};
|
||||
|
||||
@ -523,16 +522,16 @@ const TagsPage = () => {
|
||||
history.push(getTagPath(category.fullyQualifiedName));
|
||||
};
|
||||
|
||||
const handleAddTagSubmit = (data: SubmitProps) => {
|
||||
const handleAddTagSubmit = async (data: SubmitProps) => {
|
||||
const updatedData = omit(data, 'color', 'iconURL');
|
||||
const style = {
|
||||
color: data.color,
|
||||
iconURL: data.iconURL,
|
||||
};
|
||||
if (editTag) {
|
||||
handleUpdatePrimaryTag({ ...editTag, ...updatedData, style });
|
||||
await handleUpdatePrimaryTag({ ...editTag, ...updatedData, style });
|
||||
} else {
|
||||
handleCreatePrimaryTag({ ...updatedData, style });
|
||||
await handleCreatePrimaryTag({ ...updatedData, style });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -25,7 +25,6 @@ import { Bucket, EntityDetailUnion } from 'Models';
|
||||
import React, { Fragment } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { OwnerLabel } from '../components/common/OwnerLabel/OwnerLabel.component';
|
||||
import ProfilePicture from '../components/common/ProfilePicture/ProfilePicture';
|
||||
import QueryCount from '../components/common/QueryCount/QueryCount.component';
|
||||
import { DataAssetsWithoutServiceField } from '../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface';
|
||||
import { QueryVoteType } from '../components/Database/TableQueries/TableQueries.interface';
|
||||
@ -174,21 +173,6 @@ export const getEntityTags = (
|
||||
}
|
||||
};
|
||||
|
||||
export const getOwnerNameWithProfilePic = (
|
||||
owner: EntityReference | undefined
|
||||
) =>
|
||||
owner ? (
|
||||
<div className="flex items-center gap-2">
|
||||
{' '}
|
||||
<ProfilePicture
|
||||
displayName={owner.displayName}
|
||||
name={owner.name ?? ''}
|
||||
width="20"
|
||||
/>
|
||||
<span>{getEntityName(owner)}</span>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const getUsageData = (usageSummary: UsageDetails | undefined) =>
|
||||
!isNil(usageSummary?.weeklyStats?.percentileRank)
|
||||
? getUsagePercentile(usageSummary?.weeklyStats?.percentileRank ?? 0)
|
||||
@ -347,11 +331,7 @@ const getPipelineOverview = (pipelineDetails: Pipeline) => {
|
||||
const overview = [
|
||||
{
|
||||
name: i18next.t('label.owner'),
|
||||
value:
|
||||
getOwnerNameWithProfilePic(owner) ??
|
||||
i18next.t('label.no-entity', {
|
||||
entity: i18next.t('label.owner'),
|
||||
}),
|
||||
value: <OwnerLabel hasPermission={false} owner={owner} />,
|
||||
url: getOwnerValue(owner as EntityReference),
|
||||
isLink: !isEmpty(owner?.name),
|
||||
visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
|
||||
@ -400,11 +380,7 @@ const getDashboardOverview = (dashboardDetails: Dashboard) => {
|
||||
const overview = [
|
||||
{
|
||||
name: i18next.t('label.owner'),
|
||||
value:
|
||||
getOwnerNameWithProfilePic(owner) ??
|
||||
i18next.t('label.no-entity', {
|
||||
entity: i18next.t('label.owner'),
|
||||
}),
|
||||
value: <OwnerLabel hasPermission={false} owner={owner} />,
|
||||
url: getOwnerValue(owner as EntityReference),
|
||||
isLink: !isEmpty(owner?.name),
|
||||
visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
|
||||
@ -455,11 +431,7 @@ export const getSearchIndexOverview = (
|
||||
const overview = [
|
||||
{
|
||||
name: i18next.t('label.owner'),
|
||||
value:
|
||||
getOwnerNameWithProfilePic(owner) ??
|
||||
i18next.t('label.no-entity', {
|
||||
entity: i18next.t('label.owner'),
|
||||
}),
|
||||
value: <OwnerLabel hasPermission={false} owner={owner} />,
|
||||
url: getOwnerValue(owner as EntityReference),
|
||||
isLink: !isEmpty(owner?.name),
|
||||
visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
|
||||
@ -493,11 +465,7 @@ const getMlModelOverview = (mlModelDetails: Mlmodel) => {
|
||||
const overview = [
|
||||
{
|
||||
name: i18next.t('label.owner'),
|
||||
value:
|
||||
getOwnerNameWithProfilePic(owner) ??
|
||||
i18next.t('label.no-entity', {
|
||||
entity: i18next.t('label.owner'),
|
||||
}),
|
||||
value: <OwnerLabel hasPermission={false} owner={owner} />,
|
||||
url: getOwnerValue(owner as EntityReference),
|
||||
isLink: !isEmpty(owner?.name),
|
||||
visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
|
||||
@ -598,11 +566,7 @@ const getDataModelOverview = (dataModelDetails: DashboardDataModel) => {
|
||||
const overview = [
|
||||
{
|
||||
name: i18next.t('label.owner'),
|
||||
value:
|
||||
getOwnerNameWithProfilePic(owner) ??
|
||||
i18next.t('label.no-entity', {
|
||||
entity: i18next.t('label.owner'),
|
||||
}),
|
||||
value: <OwnerLabel hasPermission={false} owner={owner} />,
|
||||
url: getOwnerValue(owner as EntityReference),
|
||||
isLink: !isEmpty(owner?.name),
|
||||
visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
|
||||
@ -675,11 +639,7 @@ const getStoredProcedureOverview = (
|
||||
const overview = [
|
||||
{
|
||||
name: i18next.t('label.owner'),
|
||||
value:
|
||||
getOwnerNameWithProfilePic(owner) ??
|
||||
i18next.t('label.no-entity', {
|
||||
entity: i18next.t('label.owner'),
|
||||
}),
|
||||
value: <OwnerLabel hasPermission={false} owner={owner} />,
|
||||
url: getOwnerValue(owner as EntityReference),
|
||||
isLink: !isEmpty(owner?.name),
|
||||
visible: [DRAWER_NAVIGATION_OPTIONS.lineage],
|
||||
@ -757,11 +717,7 @@ const getDatabaseOverview = (databaseDetails: Database) => {
|
||||
const overview = [
|
||||
{
|
||||
name: i18next.t('label.owner'),
|
||||
value:
|
||||
getOwnerNameWithProfilePic(owner) ??
|
||||
i18next.t('label.no-entity', {
|
||||
entity: i18next.t('label.owner'),
|
||||
}),
|
||||
value: <OwnerLabel hasPermission={false} owner={owner} />,
|
||||
url: getOwnerValue(owner as EntityReference),
|
||||
isLink: !isEmpty(owner?.name),
|
||||
visible: [DRAWER_NAVIGATION_OPTIONS.explore],
|
||||
@ -804,11 +760,7 @@ const getDatabaseSchemaOverview = (databaseSchemaDetails: DatabaseSchema) => {
|
||||
const overview = [
|
||||
{
|
||||
name: i18next.t('label.owner'),
|
||||
value:
|
||||
getOwnerNameWithProfilePic(owner) ??
|
||||
i18next.t('label.no-entity', {
|
||||
entity: i18next.t('label.owner'),
|
||||
}),
|
||||
value: <OwnerLabel hasPermission={false} owner={owner} />,
|
||||
url: getOwnerValue(owner as EntityReference),
|
||||
isLink: !isEmpty(owner?.name),
|
||||
visible: [DRAWER_NAVIGATION_OPTIONS.explore],
|
||||
@ -856,11 +808,7 @@ const getEntityServiceOverview = (serviceDetails: EntityServiceUnion) => {
|
||||
const overview = [
|
||||
{
|
||||
name: i18next.t('label.owner'),
|
||||
value:
|
||||
getOwnerNameWithProfilePic(owner) ??
|
||||
i18next.t('label.no-entity', {
|
||||
entity: i18next.t('label.owner'),
|
||||
}),
|
||||
value: <OwnerLabel hasPermission={false} owner={owner} />,
|
||||
url: getOwnerValue(owner as EntityReference),
|
||||
isLink: !isEmpty(owner?.name),
|
||||
visible: [DRAWER_NAVIGATION_OPTIONS.explore],
|
||||
|
@ -11,7 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Space, Typography } from 'antd';
|
||||
import { Typography } from 'antd';
|
||||
import {
|
||||
ArrayChange,
|
||||
Change,
|
||||
@ -31,15 +31,11 @@ import {
|
||||
} from 'lodash';
|
||||
import React, { Fragment, ReactNode } from 'react';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ReactComponent as IconTeamsGrey } from '../assets/svg/teams-grey.svg';
|
||||
import {
|
||||
ExtentionEntities,
|
||||
ExtentionEntitiesKeys,
|
||||
} from '../components/common/CustomPropertyTable/CustomPropertyTable.interface';
|
||||
import ProfilePicture from '../components/common/ProfilePicture/ProfilePicture';
|
||||
import { FQN_SEPARATOR_CHAR } from '../constants/char.constants';
|
||||
import { getTeamAndUserDetailsPath, getUserPath } from '../constants/constants';
|
||||
import { EntityField } from '../constants/Feeds.constants';
|
||||
import { EntityType } from '../enums/entity.enum';
|
||||
import { Column as ContainerColumn } from '../generated/entity/data/container';
|
||||
@ -522,33 +518,6 @@ export function getEntityTagDiff<
|
||||
return entityList ?? [];
|
||||
}
|
||||
|
||||
export const getOwnerInfo = (owner: EntityReference, ownerLabel: ReactNode) => {
|
||||
const isTeamType = owner.type === 'team';
|
||||
|
||||
return (
|
||||
<Space className="m-r-xss" size={4}>
|
||||
{isTeamType ? (
|
||||
<IconTeamsGrey height={18} width={18} />
|
||||
) : (
|
||||
<ProfilePicture
|
||||
displayName={getEntityName(owner)}
|
||||
name={owner.name ?? ''}
|
||||
textClass="text-xs"
|
||||
width="20"
|
||||
/>
|
||||
)}
|
||||
<Link
|
||||
to={
|
||||
isTeamType
|
||||
? getTeamAndUserDetailsPath(owner.name ?? '')
|
||||
: getUserPath(owner.name ?? '')
|
||||
}>
|
||||
{ownerLabel}
|
||||
</Link>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export const getEntityReferenceDiffFromFieldName = (
|
||||
fieldName: string,
|
||||
changeDescription: ChangeDescription,
|
||||
|
@ -11,14 +11,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Space, Typography } from 'antd';
|
||||
import { Typography } from 'antd';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { t } from 'i18next';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { ServiceTypes } from 'Models';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import ProfilePicture from '../components/common/ProfilePicture/ProfilePicture';
|
||||
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';
|
||||
@ -92,12 +93,14 @@ export const getServiceMainTabColumns = (
|
||||
key: 'owner',
|
||||
render: (owner: ServicePageData['owner']) =>
|
||||
!isUndefined(owner) ? (
|
||||
<Space data-testid="owner-data">
|
||||
<ProfilePicture name={owner.name ?? ''} width="24" />
|
||||
<Typography.Text data-testid={`${owner.name}-owner-name`}>
|
||||
{getEntityName(owner)}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<UserPopOverCard
|
||||
showUserName
|
||||
data-testid="owner-data"
|
||||
displayName={owner.displayName}
|
||||
profileWidth={20}
|
||||
type={owner.type as UserTeam}
|
||||
userName={owner.name ?? ''}
|
||||
/>
|
||||
) : (
|
||||
<Typography.Text data-testid="no-owner-text">--</Typography.Text>
|
||||
),
|
||||
|
Loading…
x
Reference in New Issue
Block a user