mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-08 08:31:37 +00:00
Assets improvements part 2 (#14255)
* fix: make filters consistent * disable if no selection * fix: hide notification * fix: failed status data * localization * fix notification loading * remove close icon here * fix: cypress * update error alert design * fix alert styling * fix: minor issues * fix: redirect link of data product * fix: minor suggestion prompt * complete user flow * fix: glossary cypress * update alert view * fix tags manipulation * fix: domain reload fixes Co-Authored-By: Ashish Gupta <ashish@getcollate.io> * fix: minor loading issue on delete * fix: add admin check for activity feed Co-Authored-By: Ashish Gupta <ashish@getcollate.io> * fix: minor css fixes * fix: localisation * fix: minor issue * miner cypress fix * add mlmodelservice as mlmodel parent * fix: added null checks --------- Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com> Co-authored-by: Ashish Gupta <ashish@getcollate.io> Co-authored-by: Shailesh Parmar <shailesh.parmar.webdev@gmail.com> Co-authored-by: 07Himank <himank07mehta@gmail.com>
This commit is contained in:
parent
a76308bf77
commit
a215f03a28
@ -273,7 +273,8 @@ public class SearchRepository {
|
||||
if (changeDescription != null) {
|
||||
Pair<String, Map<String, Object>> updates = getInheritedFieldChanges(changeDescription);
|
||||
Pair<String, String> parentMatch;
|
||||
if (updates.getValue().get("type").toString().equalsIgnoreCase("domain")
|
||||
if (!updates.getValue().isEmpty()
|
||||
&& updates.getValue().get("type").toString().equalsIgnoreCase("domain")
|
||||
&& (entityType.equalsIgnoreCase(Entity.DATABASE_SERVICE)
|
||||
|| entityType.equalsIgnoreCase(Entity.DASHBOARD_SERVICE)
|
||||
|| entityType.equalsIgnoreCase(Entity.MESSAGING_SERVICE)
|
||||
|
@ -207,7 +207,7 @@
|
||||
"indexName": "mlmodel_service_search_index",
|
||||
"indexMappingFile": "/elasticsearch/%s/mlmodel_service_index_mapping.json",
|
||||
"alias": "mlModelService",
|
||||
"parentAliases": ["all"]
|
||||
"parentAliases": ["all","mlModelService"]
|
||||
},
|
||||
"testCaseResolutionStatus": {
|
||||
"indexName": "test_case_resolution_status_search_index",
|
||||
|
@ -307,6 +307,7 @@ export const NEW_GLOSSARY = {
|
||||
reviewer: 'Aaron Johnson',
|
||||
addReviewer: true,
|
||||
tag: 'PersonalData.Personal',
|
||||
isMutually: true,
|
||||
};
|
||||
export const NEW_GLOSSARY_1 = {
|
||||
name: 'Cypress Product%Glossary',
|
||||
|
@ -116,7 +116,11 @@ const createGlossary = (glossaryData) => {
|
||||
.should('be.visible')
|
||||
.type(glossaryData.description);
|
||||
|
||||
cy.get('[data-testid="mutually-exclusive-button"]').scrollIntoView().click();
|
||||
if (glossaryData.isMutually) {
|
||||
cy.get('[data-testid="mutually-exclusive-button"]')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
}
|
||||
|
||||
if (glossaryData.tag) {
|
||||
// Add tag
|
||||
@ -311,10 +315,12 @@ const removeAssetsFromGlossaryTerm = (glossaryTerm, glossary) => {
|
||||
cy.get('[data-testid="delete-button"]').click();
|
||||
cy.get("[data-testid='save-button']").click();
|
||||
|
||||
interceptURL('GET', '/api/v1/search/query*', 'assetTab');
|
||||
cy.get('[data-testid="overview"]').click();
|
||||
cy.get('[data-testid="assets"]').click();
|
||||
|
||||
// go assets tab
|
||||
goToAssetsTab(glossaryTerm.name, glossaryTerm.fullyQualifiedName, true);
|
||||
verifyResponseStatusCode('@assetTab', 200);
|
||||
verifyResponseStatusCode('@searchAssets', 200);
|
||||
|
||||
checkAssetsCount(glossaryTerm.assets.length - (index + 1));
|
||||
});
|
||||
};
|
||||
@ -719,7 +725,7 @@ describe('Glossary page should work properly', () => {
|
||||
|
||||
const ProductTerms = Object.values(NEW_GLOSSARY_1_TERMS);
|
||||
ProductTerms.forEach((term) =>
|
||||
createGlossaryTerm(term, NEW_GLOSSARY_1, 'Approved')
|
||||
createGlossaryTerm(term, NEW_GLOSSARY_1, 'Approved', false)
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -131,7 +131,7 @@ describe('Postgres Ingestion', () => {
|
||||
|
||||
cy.get('#root\\/filterCondition')
|
||||
.scrollIntoView()
|
||||
.type(`s.query like '%${tableName}%'`);
|
||||
.type(`s.query like '%%${tableName}%%'`);
|
||||
cy.get('[data-testid="submit-btn"]')
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
|
@ -40,6 +40,7 @@ import {
|
||||
ThreadTaskStatus,
|
||||
ThreadType,
|
||||
} from '../../../generated/entity/feed/thread';
|
||||
import { useAuth } from '../../../hooks/authHooks';
|
||||
import { useElementInView } from '../../../hooks/useElementInView';
|
||||
import { getAllFeeds, getFeedCount } from '../../../rest/feedsAPI';
|
||||
import { getCountBadge, getEntityDetailLink } from '../../../utils/CommonUtils';
|
||||
@ -81,6 +82,7 @@ export const ActivityFeedTab = ({
|
||||
});
|
||||
const { subTab: activeTab = ActivityFeedTabs.ALL } =
|
||||
useParams<{ subTab: ActivityFeedTabs }>();
|
||||
const { isAdminUser } = useAuth();
|
||||
const [taskFilter, setTaskFilter] = useState<TaskFilter>('open');
|
||||
const [allCount, setAllCount] = useState(0);
|
||||
const [tasksCount, setTasksCount] = useState(0);
|
||||
@ -162,7 +164,7 @@ export const ActivityFeedTab = ({
|
||||
undefined,
|
||||
undefined,
|
||||
ThreadType.Task,
|
||||
FeedFilter.OWNER,
|
||||
isAdminUser ? undefined : FeedFilter.OWNER,
|
||||
undefined,
|
||||
userId
|
||||
).then((res) => {
|
||||
@ -178,7 +180,7 @@ export const ActivityFeedTab = ({
|
||||
undefined,
|
||||
undefined,
|
||||
ThreadType.Conversation,
|
||||
FeedFilter.OWNER,
|
||||
isAdminUser ? undefined : FeedFilter.OWNER,
|
||||
undefined,
|
||||
userId
|
||||
).then((res) => {
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
import { Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Loader from '../../components/Loader/Loader';
|
||||
@ -239,7 +240,7 @@ const Suggestions = ({
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
if (options.length === 0 && !isTourOpen) {
|
||||
if (options.length === 0 && !isTourOpen && !isEmpty(searchText)) {
|
||||
return (
|
||||
<Typography.Text>
|
||||
<Transi18next
|
||||
|
@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Divider, Dropdown, Menu, Typography } from 'antd';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SearchIndex } from '../../enums/search.enum';
|
||||
import {
|
||||
QueryFieldInterface,
|
||||
QueryFieldValueInterface,
|
||||
} from '../../pages/ExplorePage/ExplorePage.interface';
|
||||
import { getAssetsPageQuickFilters } from '../../utils/AdvancedSearchUtils';
|
||||
import { getSelectedValuesFromQuickFilter } from '../../utils/Explore.utils';
|
||||
import { ExploreQuickFilterField } from '../Explore/ExplorePage.interface';
|
||||
import ExploreQuickFilters from '../Explore/ExploreQuickFilters';
|
||||
import { AssetFiltersProps } from './AssetFilters.interface';
|
||||
|
||||
const AssetFilters = ({
|
||||
filterData,
|
||||
type,
|
||||
aggregations,
|
||||
onQuickFilterChange,
|
||||
quickFilterQuery,
|
||||
}: AssetFiltersProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [filters, setFilters] = useState<ExploreQuickFilterField[]>([]);
|
||||
const [selectedFilter, setSelectedFilter] = useState<string[]>([]);
|
||||
const [selectedQuickFilters, setSelectedQuickFilters] = useState<
|
||||
ExploreQuickFilterField[]
|
||||
>([]);
|
||||
|
||||
const handleMenuClick = ({ key }: { key: string }) => {
|
||||
setSelectedFilter((prevSelected) => [...prevSelected, key]);
|
||||
};
|
||||
|
||||
const menu = useMemo(
|
||||
() => (
|
||||
<Menu selectedKeys={selectedFilter} onClick={handleMenuClick}>
|
||||
{filters.map((filter) => (
|
||||
<Menu.Item key={filter.key}>{filter.label}</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
),
|
||||
[selectedFilter, filters]
|
||||
);
|
||||
|
||||
const handleQuickFiltersChange = (data: ExploreQuickFilterField[]) => {
|
||||
const must: QueryFieldInterface[] = [];
|
||||
data.forEach((filter) => {
|
||||
if (!isEmpty(filter.value)) {
|
||||
const should: QueryFieldValueInterface[] = [];
|
||||
if (filter.value) {
|
||||
filter.value.forEach((filterValue) => {
|
||||
const term: Record<string, string> = {};
|
||||
term[filter.key] = filterValue.key;
|
||||
should.push({ term });
|
||||
});
|
||||
}
|
||||
|
||||
must.push({
|
||||
bool: { should },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const quickFilterQuery = isEmpty(must)
|
||||
? undefined
|
||||
: {
|
||||
query: { bool: { must } },
|
||||
};
|
||||
|
||||
onQuickFilterChange?.(quickFilterQuery);
|
||||
};
|
||||
|
||||
const handleQuickFiltersValueSelect = useCallback(
|
||||
(field: ExploreQuickFilterField) => {
|
||||
setSelectedQuickFilters((pre) => {
|
||||
const data = pre.map((preField) => {
|
||||
if (preField.key === field.key) {
|
||||
return field;
|
||||
} else {
|
||||
return preField;
|
||||
}
|
||||
});
|
||||
|
||||
handleQuickFiltersChange(data);
|
||||
|
||||
return data;
|
||||
});
|
||||
},
|
||||
[setSelectedQuickFilters]
|
||||
);
|
||||
|
||||
const clearFilters = useCallback(() => {
|
||||
setSelectedQuickFilters((pre) => {
|
||||
const data = pre.map((preField) => {
|
||||
return { ...preField, value: [] };
|
||||
});
|
||||
|
||||
handleQuickFiltersChange(data);
|
||||
|
||||
return data;
|
||||
});
|
||||
}, [handleQuickFiltersChange, setSelectedQuickFilters]);
|
||||
|
||||
useEffect(() => {
|
||||
if (filterData?.length === 0) {
|
||||
const dropdownItems = getAssetsPageQuickFilters(type);
|
||||
|
||||
setFilters(
|
||||
dropdownItems.map((item) => ({
|
||||
...item,
|
||||
value: getSelectedValuesFromQuickFilter(item, dropdownItems),
|
||||
}))
|
||||
);
|
||||
} else {
|
||||
setFilters(filterData ?? []);
|
||||
}
|
||||
}, [filterData, type]);
|
||||
|
||||
useEffect(() => {
|
||||
const updatedQuickFilters = filters
|
||||
.filter((filter) => selectedFilter.includes(filter.key))
|
||||
.map((selectedFilterItem) => {
|
||||
const originalFilterItem = selectedQuickFilters?.find(
|
||||
(filter) => filter.key === selectedFilterItem.key
|
||||
);
|
||||
|
||||
return originalFilterItem || selectedFilterItem;
|
||||
});
|
||||
|
||||
const newItems = updatedQuickFilters.filter(
|
||||
(item) =>
|
||||
!selectedQuickFilters.some(
|
||||
(existingItem) => item.key === existingItem.key
|
||||
)
|
||||
);
|
||||
|
||||
if (newItems.length > 0) {
|
||||
setSelectedQuickFilters((prevSelected) => [...prevSelected, ...newItems]);
|
||||
}
|
||||
}, [selectedFilter, selectedQuickFilters, filters]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dropdown overlay={menu} trigger={['click']}>
|
||||
<Button icon={<PlusOutlined />} size="small" type="primary" />
|
||||
</Dropdown>
|
||||
{selectedQuickFilters.length > 0 && (
|
||||
<>
|
||||
<Divider className="m-x-md h-6" type="vertical" />
|
||||
<div className="d-flex justify-between flex-1">
|
||||
<ExploreQuickFilters
|
||||
aggregations={aggregations}
|
||||
fields={selectedQuickFilters}
|
||||
index={SearchIndex.ALL}
|
||||
onFieldValueSelect={(data) =>
|
||||
handleQuickFiltersValueSelect?.(data)
|
||||
}
|
||||
/>
|
||||
{quickFilterQuery && (
|
||||
<Typography.Text
|
||||
className="text-primary self-center cursor-pointer"
|
||||
onClick={clearFilters}>
|
||||
{t('label.clear-entity', {
|
||||
entity: '',
|
||||
})}
|
||||
</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AssetFilters;
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Aggregations } from '../../interface/search.interface';
|
||||
import { QueryFilterInterface } from '../../pages/ExplorePage/ExplorePage.interface';
|
||||
import { ExploreQuickFilterField } from '../Explore/ExplorePage.interface';
|
||||
import { AssetsOfEntity } from '../Glossary/GlossaryTerms/tabs/AssetsTabs.interface';
|
||||
|
||||
export interface AssetFiltersProps {
|
||||
filterData?: ExploreQuickFilterField[];
|
||||
defaultFilter?: string[];
|
||||
aggregations?: Aggregations;
|
||||
onQuickFilterChange?: (query?: QueryFilterInterface) => void;
|
||||
type: AssetsOfEntity;
|
||||
quickFilterQuery?: QueryFilterInterface;
|
||||
}
|
@ -10,9 +10,27 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Button, Checkbox, List, Modal, Space, Typography } from 'antd';
|
||||
import {
|
||||
CheckOutlined,
|
||||
CloseOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
PlusOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Checkbox,
|
||||
Divider,
|
||||
Dropdown,
|
||||
List,
|
||||
Modal,
|
||||
Space,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { ItemType } from 'antd/lib/menu/hooks/useItems';
|
||||
import { AxiosError } from 'axios';
|
||||
import classNames from 'classnames';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { EntityDetailUnion } from 'Models';
|
||||
import VirtualList from 'rc-virtual-list';
|
||||
import {
|
||||
@ -20,10 +38,11 @@ import {
|
||||
UIEventHandler,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PAGE_SIZE_MEDIUM } from '../../../constants/constants';
|
||||
import { ERROR_COLOR, PAGE_SIZE_MEDIUM } from '../../../constants/constants';
|
||||
import { SearchIndex } from '../../../enums/search.enum';
|
||||
import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm';
|
||||
import { DataProduct } from '../../../generated/entity/domains/dataProduct';
|
||||
@ -33,7 +52,11 @@ import {
|
||||
Status,
|
||||
} from '../../../generated/type/bulkOperationResult';
|
||||
import { Aggregations } from '../../../interface/search.interface';
|
||||
import { QueryFilterInterface } from '../../../pages/ExplorePage/ExplorePage.interface';
|
||||
import {
|
||||
QueryFieldInterface,
|
||||
QueryFieldValueInterface,
|
||||
QueryFilterInterface,
|
||||
} from '../../../pages/ExplorePage/ExplorePage.interface';
|
||||
import {
|
||||
addAssetsToDataProduct,
|
||||
getDataProductByName,
|
||||
@ -53,11 +76,11 @@ import {
|
||||
import { getCombinedQueryFilterObject } from '../../../utils/ExplorePage/ExplorePageUtils';
|
||||
import { getEncodedFqn } from '../../../utils/StringsUtils';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import AssetFilters from '../../AssetFilters/AssetFilters.component';
|
||||
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
|
||||
import Searchbar from '../../common/SearchBarComponent/SearchBar.component';
|
||||
import TableDataCardV2 from '../../common/TableDataCardV2/TableDataCardV2';
|
||||
import { ExploreQuickFilterField } from '../../Explore/ExplorePage.interface';
|
||||
import ExploreQuickFilters from '../../Explore/ExploreQuickFilters';
|
||||
import { AssetsOfEntity } from '../../Glossary/GlossaryTerms/tabs/AssetsTabs.interface';
|
||||
import Loader from '../../Loader/Loader';
|
||||
import { SearchedDataProps } from '../../SearchedData/SearchedData.interface';
|
||||
@ -103,6 +126,20 @@ export const AssetSelectionModal = ({
|
||||
},
|
||||
})
|
||||
);
|
||||
const [selectedFilter, setSelectedFilter] = useState<string[]>([]);
|
||||
const [filters, setFilters] = useState<ExploreQuickFilterField[]>([]);
|
||||
|
||||
const handleMenuClick = ({ key }: { key: string }) => {
|
||||
setSelectedFilter((prevSelected) => [...prevSelected, key]);
|
||||
};
|
||||
|
||||
const filterMenu: ItemType[] = useMemo(() => {
|
||||
return filters.map((filter) => ({
|
||||
key: filter.key,
|
||||
label: filter.label,
|
||||
onClick: handleMenuClick,
|
||||
}));
|
||||
}, [filters]);
|
||||
|
||||
const fetchEntities = useCallback(
|
||||
async ({
|
||||
@ -153,10 +190,14 @@ export const AssetSelectionModal = ({
|
||||
useEffect(() => {
|
||||
const dropdownItems = getAssetsPageQuickFilters(type);
|
||||
|
||||
setSelectedQuickFilters(
|
||||
setFilters(
|
||||
dropdownItems.map((item) => ({
|
||||
...item,
|
||||
value: getSelectedValuesFromQuickFilter(item, dropdownItems),
|
||||
value: getSelectedValuesFromQuickFilter(
|
||||
item,
|
||||
dropdownItems,
|
||||
undefined // pass in state variable
|
||||
),
|
||||
}))
|
||||
);
|
||||
}, [type]);
|
||||
@ -213,6 +254,7 @@ export const AssetSelectionModal = ({
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
setIsSaveLoading(true);
|
||||
setFailedStatus(undefined);
|
||||
if (!activeEntity) {
|
||||
return;
|
||||
}
|
||||
@ -283,6 +325,29 @@ export const AssetSelectionModal = ({
|
||||
mergeFilters();
|
||||
}, [quickFilterQuery, queryFilter]);
|
||||
|
||||
useEffect(() => {
|
||||
const updatedQuickFilters = filters
|
||||
.filter((filter) => selectedFilter.includes(filter.key))
|
||||
.map((selectedFilterItem) => {
|
||||
const originalFilterItem = selectedQuickFilters?.find(
|
||||
(filter) => filter.key === selectedFilterItem.key
|
||||
);
|
||||
|
||||
return originalFilterItem || selectedFilterItem;
|
||||
});
|
||||
|
||||
const newItems = updatedQuickFilters.filter(
|
||||
(item) =>
|
||||
!selectedQuickFilters.some(
|
||||
(existingItem) => item.key === existingItem.key
|
||||
)
|
||||
);
|
||||
|
||||
if (newItems.length > 0) {
|
||||
setSelectedQuickFilters((prevSelected) => [...prevSelected, ...newItems]);
|
||||
}
|
||||
}, [selectedFilter, selectedQuickFilters, filters]);
|
||||
|
||||
const onScroll: UIEventHandler<HTMLElement> = useCallback(
|
||||
(e) => {
|
||||
const scrollHeight =
|
||||
@ -353,6 +418,66 @@ export const AssetSelectionModal = ({
|
||||
[failedStatus]
|
||||
);
|
||||
|
||||
const handleQuickFiltersChange = (data: ExploreQuickFilterField[]) => {
|
||||
const must: QueryFieldInterface[] = [];
|
||||
data.forEach((filter) => {
|
||||
if (!isEmpty(filter.value)) {
|
||||
const should: QueryFieldValueInterface[] = [];
|
||||
if (filter.value) {
|
||||
filter.value.forEach((filterValue) => {
|
||||
const term: Record<string, string> = {};
|
||||
term[filter.key] = filterValue.key;
|
||||
should.push({ term });
|
||||
});
|
||||
}
|
||||
|
||||
must.push({
|
||||
bool: { should },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const quickFilterQuery = isEmpty(must)
|
||||
? undefined
|
||||
: {
|
||||
query: { bool: { must } },
|
||||
};
|
||||
|
||||
setQuickFilterQuery(quickFilterQuery);
|
||||
};
|
||||
|
||||
const handleQuickFiltersValueSelect = useCallback(
|
||||
(field: ExploreQuickFilterField) => {
|
||||
setSelectedQuickFilters((pre) => {
|
||||
const data = pre.map((preField) => {
|
||||
if (preField.key === field.key) {
|
||||
return field;
|
||||
} else {
|
||||
return preField;
|
||||
}
|
||||
});
|
||||
|
||||
handleQuickFiltersChange(data);
|
||||
|
||||
return data;
|
||||
});
|
||||
},
|
||||
[setSelectedQuickFilters]
|
||||
);
|
||||
|
||||
const clearFilters = useCallback(() => {
|
||||
setQuickFilterQuery(undefined);
|
||||
setSelectedQuickFilters((pre) => {
|
||||
const data = pre.map((preField) => {
|
||||
return { ...preField, value: [] };
|
||||
});
|
||||
|
||||
handleQuickFiltersChange(data);
|
||||
|
||||
return data;
|
||||
});
|
||||
}, [setQuickFilterQuery, handleQuickFiltersChange, setSelectedQuickFilters]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
destroyOnClose
|
||||
@ -362,12 +487,23 @@ export const AssetSelectionModal = ({
|
||||
data-testid="asset-selection-modal"
|
||||
footer={
|
||||
<div className="d-flex justify-between">
|
||||
<div>
|
||||
{selectedItems && selectedItems.size > 1 && (
|
||||
<Typography.Text>
|
||||
<div className="d-flex items-center gap-2">
|
||||
{selectedItems && selectedItems.size >= 1 && (
|
||||
<Typography.Text className="gap-2">
|
||||
<CheckOutlined className="text-success m-r-xs" />
|
||||
{selectedItems.size} {t('label.selected-lowercase')}
|
||||
</Typography.Text>
|
||||
)}
|
||||
{failedStatus?.failedRequest &&
|
||||
failedStatus.failedRequest.length > 0 && (
|
||||
<>
|
||||
<Divider className="m-x-xss" type="vertical" />
|
||||
<Typography.Text type="danger">
|
||||
<CloseOutlined className="m-r-xs" />
|
||||
{failedStatus.failedRequest.length} {t('label.error')}
|
||||
</Typography.Text>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@ -376,7 +512,7 @@ export const AssetSelectionModal = ({
|
||||
</Button>
|
||||
<Button
|
||||
data-testid="save-btn"
|
||||
disabled={isLoading}
|
||||
disabled={!selectedItems?.size || isLoading}
|
||||
loading={isSaveLoading}
|
||||
type="primary"
|
||||
onClick={onSaveAction}>
|
||||
@ -391,6 +527,16 @@ export const AssetSelectionModal = ({
|
||||
width={675}
|
||||
onCancel={onCancel}>
|
||||
<Space className="w-full h-full" direction="vertical" size={16}>
|
||||
<div className="d-flex items-center gap-3">
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: filterMenu,
|
||||
selectedKeys: selectedFilter,
|
||||
}}
|
||||
trigger={['click']}>
|
||||
<Button icon={<PlusOutlined />} size="small" type="primary" />
|
||||
</Dropdown>
|
||||
<div className="flex-1">
|
||||
<Searchbar
|
||||
removeMargin
|
||||
showClearSearch
|
||||
@ -400,16 +546,54 @@ export const AssetSelectionModal = ({
|
||||
searchValue={search}
|
||||
onSearch={setSearch}
|
||||
/>
|
||||
|
||||
<div className="d-flex items-center">
|
||||
<AssetFilters
|
||||
aggregations={aggregations}
|
||||
filterData={selectedQuickFilters}
|
||||
quickFilterQuery={quickFilterQuery}
|
||||
type={type}
|
||||
onQuickFilterChange={(data) => setQuickFilterQuery(data)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedQuickFilters && selectedQuickFilters.length > 0 && (
|
||||
<div className="d-flex items-center">
|
||||
<div className="d-flex justify-between flex-1">
|
||||
<ExploreQuickFilters
|
||||
aggregations={aggregations}
|
||||
fields={selectedQuickFilters}
|
||||
index={SearchIndex.ALL}
|
||||
showDeleted={false}
|
||||
onFieldValueSelect={handleQuickFiltersValueSelect}
|
||||
/>
|
||||
{quickFilterQuery && (
|
||||
<Typography.Text
|
||||
className="p-r-xss text-primary self-center cursor-pointer"
|
||||
onClick={clearFilters}>
|
||||
{t('label.clear-entity', {
|
||||
entity: '',
|
||||
})}
|
||||
</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{failedStatus?.failedRequest && failedStatus.failedRequest.length > 0 && (
|
||||
<Alert
|
||||
closable
|
||||
className="w-full"
|
||||
description={
|
||||
<Typography.Text className="text-grey-muted">
|
||||
{t('message.validation-error-assets')}
|
||||
</Typography.Text>
|
||||
}
|
||||
message={
|
||||
<div className="d-flex items-center gap-3">
|
||||
<ExclamationCircleOutlined
|
||||
style={{ color: ERROR_COLOR, fontSize: '24px' }}
|
||||
/>
|
||||
<Typography.Text className="font-semibold text-sm">
|
||||
{t('label.validation-error-plural')}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
}
|
||||
type="error"
|
||||
/>
|
||||
)}
|
||||
|
||||
{items.length > 0 && (
|
||||
<div className="border p-xs">
|
||||
@ -435,7 +619,8 @@ export const AssetSelectionModal = ({
|
||||
<div
|
||||
className={classNames({
|
||||
'm-y-sm border-danger rounded-4': isError,
|
||||
})}>
|
||||
})}
|
||||
key={item.id}>
|
||||
<TableDataCardV2
|
||||
openEntityInNewPage
|
||||
showCheckboxes
|
||||
@ -449,11 +634,19 @@ export const AssetSelectionModal = ({
|
||||
source={{ ...item, tags: [] }}
|
||||
/>
|
||||
{isError && (
|
||||
<div className="p-x-sm p-b-sm">
|
||||
<Typography.Text type="danger">
|
||||
<>
|
||||
<div className="p-x-sm">
|
||||
<Divider className="m-t-0 m-y-sm " />
|
||||
</div>
|
||||
<div className="d-flex gap-3 p-x-sm p-b-sm">
|
||||
<ExclamationCircleOutlined
|
||||
style={{ color: ERROR_COLOR, fontSize: '24px' }}
|
||||
/>
|
||||
<Typography.Text className="break-all">
|
||||
{errorMessage}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -14,7 +14,7 @@
|
||||
@import url('../../../styles/variables.less');
|
||||
|
||||
.asset-selection-model-card.table-data-card-container {
|
||||
box-shadow: none;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.asset-selection-model-card.table-data-card-container:hover {
|
||||
box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.13);
|
||||
|
@ -14,6 +14,7 @@ import { Col, Row, Space, Tag, Typography } from 'antd';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { ReactComponent as EditIcon } from '../../assets/svg/edit-new.svg';
|
||||
import { ReactComponent as DataProductIcon } from '../../assets/svg/ic-data-product.svg';
|
||||
import DataProductSelectForm from '../../components/DataProductSelectForm/DataProductsSelectForm';
|
||||
@ -27,6 +28,8 @@ import { DataProduct } from '../../generated/entity/domains/dataProduct';
|
||||
import { EntityReference } from '../../generated/entity/type';
|
||||
import { fetchDataProductsElasticSearch } from '../../rest/dataProductAPI';
|
||||
import { getEntityName } from '../../utils/EntityUtils';
|
||||
import { getDataProductsDetailsPath } from '../../utils/RouterUtils';
|
||||
import { getEncodedFqn } from '../../utils/StringsUtils';
|
||||
|
||||
interface DataProductsContainerProps {
|
||||
showHeader?: boolean;
|
||||
@ -44,6 +47,7 @@ const DataProductsContainer = ({
|
||||
onSave,
|
||||
}: DataProductsContainerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const [isEditMode, setIsEditMode] = useState(false);
|
||||
|
||||
const handleAddClick = () => {
|
||||
@ -70,6 +74,10 @@ const DataProductsContainer = ({
|
||||
[activeDomain]
|
||||
);
|
||||
|
||||
const redirectLink = useCallback((fqn) => {
|
||||
history.push(getDataProductsDetailsPath(getEncodedFqn(fqn)));
|
||||
}, []);
|
||||
|
||||
const handleSave = async (dataProducts: DataProduct[]) => {
|
||||
await onSave?.(dataProducts);
|
||||
setIsEditMode(false);
|
||||
@ -107,7 +115,8 @@ const DataProductsContainer = ({
|
||||
return (
|
||||
<Tag
|
||||
className="tag-chip tag-chip-content"
|
||||
key={`dp-tags-${product.fullyQualifiedName}`}>
|
||||
key={`dp-tags-${product.fullyQualifiedName}`}
|
||||
onClick={() => redirectLink(product.fullyQualifiedName)}>
|
||||
<div className="d-flex w-full">
|
||||
<div className="d-flex items-center p-x-xs w-full gap-1">
|
||||
<DataProductIcon
|
||||
|
@ -83,6 +83,7 @@ import {
|
||||
} from '../../../utils/RouterUtils';
|
||||
import {
|
||||
escapeESReservedCharacters,
|
||||
getDecodedFqn,
|
||||
getEncodedFqn,
|
||||
} from '../../../utils/StringsUtils';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
@ -111,7 +112,7 @@ const DomainDetailsPage = ({
|
||||
tab: activeTab,
|
||||
version,
|
||||
} = useParams<{ fqn: string; tab: string; version: string }>();
|
||||
const domainFqn = fqn ? decodeURIComponent(fqn) : '';
|
||||
const domainFqn = fqn ? getDecodedFqn(fqn) : '';
|
||||
const assetTabRef = useRef<AssetsTabRef>(null);
|
||||
const dataProductsTabRef = useRef<DataProductsTabRef>(null);
|
||||
const [domainPermission, setDomainPermission] = useState<OperationPermission>(
|
||||
@ -512,7 +513,7 @@ const DomainDetailsPage = ({
|
||||
fetchDomainPermission();
|
||||
fetchDomainAssets();
|
||||
fetchDataProducts();
|
||||
}, [fqn]);
|
||||
}, [domain.fullyQualifiedName]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -173,11 +173,15 @@ const ExploreSearchCard: React.FC<ExploreSearchCardProps> = forwardRef<
|
||||
<Row gutter={[8, 8]}>
|
||||
{showCheckboxes && (
|
||||
<Col flex="25px">
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
className="assets-checkbox"
|
||||
onChange={(e) => onCheckboxChange?.(e.target.checked)}
|
||||
onChange={(e) => {
|
||||
onCheckboxChange?.(e.target.checked);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
)}
|
||||
{!hideBreadcrumbs && (
|
||||
|
@ -36,7 +36,10 @@ import { getCountBadge, getFeedCounts } from '../../../utils/CommonUtils';
|
||||
import { getEntityVersionByField } from '../../../utils/EntityVersionUtils';
|
||||
import { getQueryFilterToExcludeTerm } from '../../../utils/GlossaryUtils';
|
||||
import { getGlossaryTermsVersionsPath } from '../../../utils/RouterUtils';
|
||||
import { escapeESReservedCharacters } from '../../../utils/StringsUtils';
|
||||
import {
|
||||
escapeESReservedCharacters,
|
||||
getEncodedFqn,
|
||||
} from '../../../utils/StringsUtils';
|
||||
import { ActivityFeedTab } from '../../ActivityFeed/ActivityFeedTab/ActivityFeedTab.component';
|
||||
import { AssetSelectionModal } from '../../Assets/AssetsSelectionModal/AssetSelectionModal';
|
||||
import { CustomPropertyTable } from '../../common/CustomPropertyTable/CustomPropertyTable';
|
||||
@ -258,13 +261,14 @@ const GlossaryTermsV1 = ({
|
||||
const fetchGlossaryTermAssets = async () => {
|
||||
if (glossaryTerm) {
|
||||
try {
|
||||
const encodedFqn = getEncodedFqn(
|
||||
escapeESReservedCharacters(glossaryTerm.fullyQualifiedName)
|
||||
);
|
||||
const res = await searchData(
|
||||
'',
|
||||
1,
|
||||
0,
|
||||
`(tags.tagFQN:"${escapeESReservedCharacters(
|
||||
glossaryTerm.fullyQualifiedName
|
||||
)}")`,
|
||||
`(tags.tagFQN:"${encodedFqn}")`,
|
||||
'',
|
||||
'',
|
||||
SearchIndex.ALL
|
||||
|
@ -14,11 +14,11 @@
|
||||
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Affix,
|
||||
Button,
|
||||
Checkbox,
|
||||
Col,
|
||||
Dropdown,
|
||||
Menu,
|
||||
MenuProps,
|
||||
notification,
|
||||
Row,
|
||||
@ -136,6 +136,7 @@ const AssetsTabs = forwardRef(
|
||||
const [itemCount, setItemCount] = useState<Record<EntityType, number>>(
|
||||
{} as Record<EntityType, number>
|
||||
);
|
||||
const [assetRemoving, setAssetRemoving] = useState(false);
|
||||
|
||||
const [activeFilter, _] = useState<SearchIndex[]>([]);
|
||||
const { fqn } = useParams<{ fqn: string }>();
|
||||
@ -173,8 +174,9 @@ const AssetsTabs = forwardRef(
|
||||
Domain | DataProduct | GlossaryTerm
|
||||
>();
|
||||
|
||||
const [selectedItems, setSelectedItems] =
|
||||
useState<Map<string, EntityDetailUnion>>();
|
||||
const [selectedItems, setSelectedItems] = useState<
|
||||
Map<string, EntityDetailUnion>
|
||||
>(new Map());
|
||||
const [aggregations, setAggregations] = useState<Aggregations>();
|
||||
const [selectedFilter, setSelectedFilter] = useState<string[]>([]); // Contains menu selection
|
||||
const [selectedQuickFilters, setSelectedQuickFilters] = useState<
|
||||
@ -193,16 +195,13 @@ const AssetsTabs = forwardRef(
|
||||
setSelectedFilter((prevSelected) => [...prevSelected, key]);
|
||||
};
|
||||
|
||||
const filterMenu = useMemo(
|
||||
() => (
|
||||
<Menu selectedKeys={selectedFilter} onClick={handleMenuClick}>
|
||||
{filters.map((filter) => (
|
||||
<Menu.Item key={filter.key}>{filter.label}</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
),
|
||||
[selectedFilter, filters]
|
||||
);
|
||||
const filterMenu: ItemType[] = useMemo(() => {
|
||||
return filters.map((filter) => ({
|
||||
key: filter.key,
|
||||
label: filter.label,
|
||||
onClick: handleMenuClick,
|
||||
}));
|
||||
}, [filters]);
|
||||
|
||||
const queryParam = useMemo(() => {
|
||||
const encodedFqn = getEncodedFqn(escapeESReservedCharacters(entityFqn));
|
||||
@ -425,6 +424,7 @@ const AssetsTabs = forwardRef(
|
||||
|
||||
return () => {
|
||||
onAssetClick?.(undefined);
|
||||
hideNotification();
|
||||
};
|
||||
}, []);
|
||||
|
||||
@ -434,15 +434,6 @@ const AssetsTabs = forwardRef(
|
||||
}
|
||||
}, [entityFqn]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedItems) {
|
||||
hideNotification();
|
||||
if (selectedItems.size > 1) {
|
||||
openNotification();
|
||||
}
|
||||
}
|
||||
}, [selectedItems]);
|
||||
|
||||
const assetErrorPlaceHolder = useMemo(() => {
|
||||
if (!isEmpty(activeFilter)) {
|
||||
return (
|
||||
@ -713,6 +704,8 @@ const AssetsTabs = forwardRef(
|
||||
return;
|
||||
}
|
||||
|
||||
setAssetRemoving(true);
|
||||
|
||||
try {
|
||||
const entities = [...(assetsData?.values() ?? [])].map((item) => {
|
||||
return getEntityReferenceFromEntity(
|
||||
@ -760,6 +753,7 @@ const AssetsTabs = forwardRef(
|
||||
} finally {
|
||||
setShowDeleteModal(false);
|
||||
onRemoveAsset?.();
|
||||
setAssetRemoving(false);
|
||||
hideNotification();
|
||||
setSelectedItems(new Map()); // Reset selected items
|
||||
}
|
||||
@ -784,31 +778,6 @@ const AssetsTabs = forwardRef(
|
||||
setSelectedQuickFilters,
|
||||
]);
|
||||
|
||||
const openNotification = () => {
|
||||
notification.warning({
|
||||
key: 'asset-tab-notification-key',
|
||||
message: (
|
||||
<div className="d-flex items-center justify-between">
|
||||
{selectedItems && selectedItems.size > 1 && (
|
||||
<Typography.Text className="text-white">
|
||||
{selectedItems.size} {t('label.items-selected-lowercase')}
|
||||
</Typography.Text>
|
||||
)}
|
||||
<Button
|
||||
danger
|
||||
data-testid="delete-all-button"
|
||||
type="primary"
|
||||
onClick={deleteSelectedItems}>
|
||||
{t('label.delete')}
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
placement: 'bottom',
|
||||
className: 'asset-tab-delete-notification',
|
||||
duration: 0,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchAssets({
|
||||
index: isEmpty(activeFilter) ? [SearchIndex.ALL] : activeFilter,
|
||||
@ -861,7 +830,7 @@ const AssetsTabs = forwardRef(
|
||||
refreshAssets() {
|
||||
fetchAssets({
|
||||
index: isEmpty(activeFilter) ? [SearchIndex.ALL] : activeFilter,
|
||||
page: currentPage,
|
||||
page: 1,
|
||||
});
|
||||
fetchCountsByEntity();
|
||||
},
|
||||
@ -885,12 +854,18 @@ const AssetsTabs = forwardRef(
|
||||
return (
|
||||
<div
|
||||
className={classNames('assets-tab-container p-md')}
|
||||
data-testid="table-container">
|
||||
data-testid="table-container"
|
||||
id="asset-tab">
|
||||
{assetCount > 0 && (
|
||||
<Row className="filters-row gap-2 p-l-lg">
|
||||
<Col span={18}>
|
||||
<div className="d-flex items-center gap-3">
|
||||
<Dropdown overlay={filterMenu} trigger={['click']}>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: filterMenu,
|
||||
selectedKeys: selectedFilter,
|
||||
}}
|
||||
trigger={['click']}>
|
||||
<Button icon={<PlusOutlined />} size="small" type="primary" />
|
||||
</Dropdown>
|
||||
<div className="flex-1">
|
||||
@ -952,10 +927,36 @@ const AssetsTabs = forwardRef(
|
||||
header={t('label.remove-entity', {
|
||||
entity: getEntityName(assetToDelete) + '?',
|
||||
})}
|
||||
isLoading={assetRemoving}
|
||||
visible={showDeleteModal}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
onConfirm={() => onAssetRemove(assetToDelete ? [assetToDelete] : [])}
|
||||
/>
|
||||
|
||||
{selectedItems.size > 1 && (
|
||||
<Affix
|
||||
className={classNames('asset-tab-delete-notification', {
|
||||
visible: selectedItems.size > 1,
|
||||
})}
|
||||
offsetBottom={20}
|
||||
target={() =>
|
||||
document.getElementById('asset-tab') || document.body
|
||||
}>
|
||||
<div className="d-flex items-center justify-between">
|
||||
<Typography.Text className="text-white">
|
||||
{selectedItems.size} {t('label.items-selected-lowercase')}
|
||||
</Typography.Text>
|
||||
<Button
|
||||
danger
|
||||
data-testid="delete-all-button"
|
||||
loading={assetRemoving}
|
||||
type="primary"
|
||||
onClick={deleteSelectedItems}>
|
||||
{t('label.delete')}
|
||||
</Button>
|
||||
</div>
|
||||
</Affix>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -83,10 +83,6 @@ const GlossaryOverviewTab = ({
|
||||
}
|
||||
}, [selectedData, isVersionView]);
|
||||
|
||||
const handleTagsUpdate = async (updatedTags: TagLabel[]) => {
|
||||
setTagsUpdating(updatedTags);
|
||||
};
|
||||
|
||||
const tags = useMemo(
|
||||
() =>
|
||||
isVersionView
|
||||
@ -98,14 +94,16 @@ const GlossaryOverviewTab = ({
|
||||
[isVersionView, selectedData]
|
||||
);
|
||||
|
||||
const handleTagsUpdate = async (updatedTags: TagLabel[]) => {
|
||||
setTagsUpdating(updatedTags);
|
||||
};
|
||||
|
||||
const handleGlossaryTagUpdateValidationConfirm = async () => {
|
||||
if (selectedData) {
|
||||
await onUpdate({
|
||||
...selectedData,
|
||||
tags: tagsUpdatating,
|
||||
});
|
||||
|
||||
setTagsUpdating(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -81,20 +81,24 @@
|
||||
}
|
||||
|
||||
.asset-tab-delete-notification {
|
||||
background-color: @text-color !important;
|
||||
box-shadow: none !important;
|
||||
.ant-notification-notice-icon {
|
||||
display: none;
|
||||
}
|
||||
.ant-notification-notice-close {
|
||||
color: @white !important;
|
||||
left: 24px;
|
||||
top: 22px;
|
||||
width: 20px;
|
||||
}
|
||||
.ant-notification-notice-message {
|
||||
padding-right: 0 !important;
|
||||
margin-left: 20px !important;
|
||||
font-size: 14px !important;
|
||||
&.visible {
|
||||
.ant-affix {
|
||||
bottom: 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-affix {
|
||||
border-radius: 10px;
|
||||
background: #292929;
|
||||
height: 64px !important;
|
||||
width: 350px !important;
|
||||
bottom: -64px !important;
|
||||
left: 40%;
|
||||
transform: translateX(-60%);
|
||||
transition: bottom ease-in 0.5s;
|
||||
|
||||
& > div {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,3 +19,11 @@ export interface GlossaryUpdateConfirmationModalProps {
|
||||
onCancel: () => void;
|
||||
updatedTags: TagLabel[];
|
||||
}
|
||||
|
||||
export enum UpdateState {
|
||||
INITIAL,
|
||||
VALIDATING,
|
||||
FAILED,
|
||||
UPDATATING,
|
||||
SUCESS,
|
||||
}
|
||||
|
@ -11,8 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import Icon from '@ant-design/icons';
|
||||
import { Button, Modal, Space, Typography } from 'antd';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Alert, Button, Modal, Progress, Space, Typography } from 'antd';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
@ -31,7 +30,10 @@ import {
|
||||
getEntityName,
|
||||
} from '../../../utils/EntityUtils';
|
||||
import Table from '../../common/Table/Table';
|
||||
import { GlossaryUpdateConfirmationModalProps } from './GlossaryUpdateConfirmationModal.interface';
|
||||
import {
|
||||
GlossaryUpdateConfirmationModalProps,
|
||||
UpdateState,
|
||||
} from './GlossaryUpdateConfirmationModal.interface';
|
||||
|
||||
export const GlossaryUpdateConfirmationModal = ({
|
||||
glossaryTerm,
|
||||
@ -41,13 +43,12 @@ export const GlossaryUpdateConfirmationModal = ({
|
||||
}: GlossaryUpdateConfirmationModalProps) => {
|
||||
const [failedStatus, setFailedStatus] = useState<BulkOperationResult>();
|
||||
const [tagError, setTagError] = useState<{ code: number; message: string }>();
|
||||
const [tagAdditionConfirmation, setTagAdditionConfirmation] = useState(false);
|
||||
const [validating, setValidating] = useState(false);
|
||||
const [updateState, setUpdateState] = useState(UpdateState.INITIAL);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleUpdateConfirmation = async () => {
|
||||
setTagAdditionConfirmation(true);
|
||||
setValidating(true);
|
||||
setUpdateState(UpdateState.VALIDATING);
|
||||
|
||||
try {
|
||||
// dryRun validations so that we can list failures if any
|
||||
const res = await validateTagAddtionToGlossary(
|
||||
@ -55,16 +56,24 @@ export const GlossaryUpdateConfirmationModal = ({
|
||||
true
|
||||
);
|
||||
|
||||
if (res.status && res.status === Status.Success) {
|
||||
if (res.status === Status.Success) {
|
||||
setUpdateState(UpdateState.UPDATATING);
|
||||
try {
|
||||
await onValidationSuccess();
|
||||
setUpdateState(UpdateState.SUCESS);
|
||||
} catch (err) {
|
||||
// Error
|
||||
} finally {
|
||||
setTimeout(onCancel, 500);
|
||||
}
|
||||
} else {
|
||||
setUpdateState(UpdateState.FAILED);
|
||||
setFailedStatus(res);
|
||||
}
|
||||
} catch (err) {
|
||||
// error
|
||||
setTagError(err.response?.data);
|
||||
} finally {
|
||||
setValidating(false);
|
||||
setUpdateState(UpdateState.FAILED);
|
||||
}
|
||||
};
|
||||
|
||||
@ -96,14 +105,15 @@ export const GlossaryUpdateConfirmationModal = ({
|
||||
];
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
centered
|
||||
open
|
||||
closable={false}
|
||||
closeIcon={null}
|
||||
footer={
|
||||
tagAdditionConfirmation && (
|
||||
const progress =
|
||||
updateState === UpdateState.VALIDATING
|
||||
? 10
|
||||
: updateState === UpdateState.UPDATATING
|
||||
? 60
|
||||
: 100;
|
||||
|
||||
const data = useMemo(() => {
|
||||
const footer = (
|
||||
<div className="d-flex justify-between">
|
||||
<Typography.Text type="secondary">
|
||||
{failedStatus?.numberOfRowsFailed &&
|
||||
@ -111,40 +121,19 @@ export const GlossaryUpdateConfirmationModal = ({
|
||||
</Typography.Text>
|
||||
<Button onClick={onCancel}>{t('label.cancel')}</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
title={
|
||||
tagAdditionConfirmation
|
||||
? t('message.glossary-tag-update-modal-title')
|
||||
: undefined
|
||||
}
|
||||
width={tagAdditionConfirmation ? 750 : undefined}
|
||||
onCancel={onCancel}>
|
||||
{tagAdditionConfirmation || validating ? (
|
||||
<div className="d-flex flex-column gap-2">
|
||||
{!isEmpty(failedStatus?.failedRequest) && !validating && (
|
||||
<>
|
||||
<Table
|
||||
bordered
|
||||
columns={tagsColumn}
|
||||
dataSource={failedStatus?.failedRequest}
|
||||
loading={validating}
|
||||
pagination={{
|
||||
pageSize: 5,
|
||||
showSizeChanger: true,
|
||||
}}
|
||||
rowKey={(record) => record.request.id}
|
||||
/>
|
||||
<Typography.Text italic className="m-t-sm" type="secondary">
|
||||
{t('message.glossary-tag-assignement-help-message')}
|
||||
</Typography.Text>
|
||||
</>
|
||||
)}
|
||||
{tagError?.code === ClientErrors.BAD_REQUEST && (
|
||||
<Typography.Text type="danger">{tagError.message}</Typography.Text>
|
||||
)}
|
||||
);
|
||||
|
||||
const progressBar = (
|
||||
<div className="text-center">
|
||||
<Progress percent={progress} status="normal" type="circle" />
|
||||
</div>
|
||||
) : (
|
||||
);
|
||||
|
||||
switch (updateState) {
|
||||
case UpdateState.INITIAL:
|
||||
return {
|
||||
footer: null,
|
||||
content: (
|
||||
<div className="d-flex items-center flex-column gap-2">
|
||||
<Icon
|
||||
className="m-b-lg"
|
||||
@ -156,21 +145,99 @@ export const GlossaryUpdateConfirmationModal = ({
|
||||
</Typography.Title>
|
||||
<Typography.Text className="text-center">
|
||||
{t('message.glossary-tag-update-description')}{' '}
|
||||
<span className="font-medium">{getEntityName(glossaryTerm)}</span>
|
||||
<span className="font-medium">
|
||||
{getEntityName(glossaryTerm)}
|
||||
</span>
|
||||
</Typography.Text>
|
||||
<div className="m-t-lg">
|
||||
<Space size={8}>
|
||||
<Button onClick={onCancel}>{t('label.no-comma-cancel')}</Button>
|
||||
<Button
|
||||
loading={validating}
|
||||
type="primary"
|
||||
onClick={handleUpdateConfirmation}>
|
||||
<Button onClick={onCancel}>
|
||||
{t('label.no-comma-cancel')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={handleUpdateConfirmation}>
|
||||
{t('label.yes-comma-confirm')}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
case UpdateState.VALIDATING:
|
||||
return {
|
||||
content: progressBar,
|
||||
footer: footer,
|
||||
};
|
||||
case UpdateState.FAILED:
|
||||
return {
|
||||
content: (
|
||||
<div className="d-flex flex-column gap-2">
|
||||
{failedStatus && (
|
||||
<>
|
||||
<Table
|
||||
bordered
|
||||
columns={tagsColumn}
|
||||
dataSource={failedStatus?.failedRequest ?? []}
|
||||
pagination={{
|
||||
pageSize: 5,
|
||||
showSizeChanger: true,
|
||||
}}
|
||||
rowKey={(record) => record.request?.id}
|
||||
/>
|
||||
<Alert
|
||||
className="m-t-sm"
|
||||
message={t('message.glossary-tag-assignement-help-message')}
|
||||
type="warning"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{tagError?.code === ClientErrors.BAD_REQUEST && (
|
||||
<Alert message={tagError.message} type="warning" />
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
footer: (
|
||||
<div className="d-flex justify-between">
|
||||
<Typography.Text type="secondary">
|
||||
{failedStatus?.numberOfRowsFailed &&
|
||||
`${failedStatus.numberOfRowsFailed} ${t('label.failed')}`}
|
||||
</Typography.Text>
|
||||
<Button onClick={onCancel}>{t('label.cancel')}</Button>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
case UpdateState.UPDATATING:
|
||||
return {
|
||||
content: progressBar,
|
||||
footer: <Button onClick={onCancel}>{t('label.cancel')}</Button>,
|
||||
};
|
||||
case UpdateState.SUCESS:
|
||||
return {
|
||||
content: progressBar,
|
||||
footer: <Button onClick={onCancel}>{t('label.cancel')}</Button>,
|
||||
};
|
||||
}
|
||||
}, [updateState, failedStatus]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
centered
|
||||
open
|
||||
closable={false}
|
||||
closeIcon={null}
|
||||
footer={data.footer}
|
||||
title={
|
||||
[
|
||||
UpdateState.VALIDATING,
|
||||
UpdateState.FAILED,
|
||||
UpdateState.UPDATATING,
|
||||
UpdateState.SUCESS,
|
||||
].includes(updateState)
|
||||
? t('message.glossary-tag-update-modal-title')
|
||||
: undefined
|
||||
}
|
||||
width={updateState === UpdateState.FAILED ? 750 : undefined}
|
||||
onCancel={onCancel}>
|
||||
{data.content}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
import { Col, Form, Row, Space, Tooltip, Typography } from 'antd';
|
||||
import { DefaultOptionType } from 'antd/lib/select';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isEmpty, isEqual } from 'lodash';
|
||||
import { EntityTags } from 'Models';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -104,7 +104,7 @@ const TagsContainerV2 = ({
|
||||
const handleSave = async (data: DefaultOptionType | DefaultOptionType[]) => {
|
||||
const updatedTags = (data as DefaultOptionType[]).map((tag) => {
|
||||
let tagData: EntityTags = {
|
||||
tagFQN: tag.value,
|
||||
tagFQN: typeof tag === 'string' ? tag : tag.value,
|
||||
source: tagType,
|
||||
};
|
||||
|
||||
@ -115,13 +115,16 @@ const TagsContainerV2 = ({
|
||||
displayName: tag.data?.displayName,
|
||||
description: tag.data?.description,
|
||||
style: tag.data?.style,
|
||||
labelType: tag.data?.labelType,
|
||||
};
|
||||
}
|
||||
|
||||
return tagData;
|
||||
});
|
||||
|
||||
if (onSelectionChange) {
|
||||
const newTags = updatedTags.map((t) => t.tagFQN);
|
||||
|
||||
if (onSelectionChange && !isEqual(selectedTagsInternal, newTags)) {
|
||||
await onSelectionChange([
|
||||
...updatedTags,
|
||||
...((isGlossaryType
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
export const PRIMERY_COLOR = '#0968da';
|
||||
export const SECONDARY_COLOR = '#B02AAC';
|
||||
export const INFO_COLOR = '#2196f3';
|
||||
export const ERROR_COLOR = '#ff4c3b';
|
||||
export const LITE_GRAY_COLOR = '#DBE0EB';
|
||||
export const TEXT_BODY_COLOR = '#37352F';
|
||||
export const TEXT_GREY_MUTED = '#757575';
|
||||
|
@ -396,6 +396,7 @@
|
||||
"entity-service": "{{entity}}-Dienst",
|
||||
"entity-type-plural": "{{entity}}-Typen",
|
||||
"entity-version-detail-plural": "Details zu {{entity}}-Versionen",
|
||||
"error": "Error",
|
||||
"event-publisher-plural": "Ereignisveröffentlicher",
|
||||
"event-type": "Ereignistyp",
|
||||
"every": "Jede/r/s",
|
||||
@ -1133,6 +1134,7 @@
|
||||
"username-or-email": "Benutzername oder E-Mail",
|
||||
"valid-condition": "Gültige Bedingung",
|
||||
"validating-condition": "Bedingung validieren...",
|
||||
"validation-error-plural": "Validation Errors!",
|
||||
"value": "Wert",
|
||||
"value-count": "Wertanzahl",
|
||||
"value-plural": "Werte",
|
||||
@ -1628,6 +1630,7 @@
|
||||
"user-mentioned-in-comment": "{{user}} hat Sie in einem Kommentar erwähnt.",
|
||||
"user-verified-successfully": "Benutzer erfolgreich verifiziert",
|
||||
"valid-url-endpoint": "Endpunkte sollten gültige URLs sein",
|
||||
"validation-error-assets": "Please examine all your assets that are being added",
|
||||
"version-released-try-now": "{{version}} Veröffentlicht <0>Sehen Sie, was es Neues gibt!</0>",
|
||||
"view-deleted-entity": "Alle gelöschten {{entity}}, die zu diesem {{parent}} gehören, anzeigen.",
|
||||
"view-sample-data-entity": "Um Musterdaten anzuzeigen, führen Sie {{entity}} aus. Bitte beachten Sie dieses Dokument, um die <0>{{entity}}</0> zu planen.",
|
||||
|
@ -396,6 +396,7 @@
|
||||
"entity-service": "{{entity}} Service",
|
||||
"entity-type-plural": "{{entity}} Type",
|
||||
"entity-version-detail-plural": "{{entity}} Version Details",
|
||||
"error": "Error",
|
||||
"event-publisher-plural": "Event Publishers",
|
||||
"event-type": "Event Type",
|
||||
"every": "Every",
|
||||
@ -1133,6 +1134,7 @@
|
||||
"username-or-email": "Username or Email",
|
||||
"valid-condition": "Valid condition",
|
||||
"validating-condition": "Validating the condition...",
|
||||
"validation-error-plural": "Validation Errors!",
|
||||
"value": "Value",
|
||||
"value-count": "Value Count",
|
||||
"value-plural": "Values",
|
||||
@ -1628,6 +1630,7 @@
|
||||
"user-mentioned-in-comment": "{{user}} mentioned you in a comment.",
|
||||
"user-verified-successfully": "User Verified Successfully",
|
||||
"valid-url-endpoint": "Endpoints should be valid URL",
|
||||
"validation-error-assets": "Please examine all your assets that are being added",
|
||||
"version-released-try-now": "{{version}} Released <0>See What's New!</0>",
|
||||
"view-deleted-entity": "View All the Deleted {{entity}}, which come under this {{parent}}.",
|
||||
"view-sample-data-entity": "To view Sample Data, run the {{entity}}. Please refer to this doc to schedule the <0>{{entity}}</0>",
|
||||
|
@ -396,6 +396,7 @@
|
||||
"entity-service": "Servicio de {{entity}}",
|
||||
"entity-type-plural": "{{entity}} Type",
|
||||
"entity-version-detail-plural": "{{entity}} Version Details",
|
||||
"error": "Error",
|
||||
"event-publisher-plural": "Publicadores de eventos",
|
||||
"event-type": "Tipo de evento",
|
||||
"every": "Cada",
|
||||
@ -1133,6 +1134,7 @@
|
||||
"username-or-email": "Nombre de usuario o correo electrónico",
|
||||
"valid-condition": "Condición válida",
|
||||
"validating-condition": "Validando la condición...",
|
||||
"validation-error-plural": "Validation Errors!",
|
||||
"value": "Valor",
|
||||
"value-count": "Recuento de valor",
|
||||
"value-plural": "Valores",
|
||||
@ -1628,6 +1630,7 @@
|
||||
"user-mentioned-in-comment": "{{user}} te mencionó en un comentario.",
|
||||
"user-verified-successfully": "Usuario verificado con éxito",
|
||||
"valid-url-endpoint": "Los terminales deben ser URL válidas",
|
||||
"validation-error-assets": "Please examine all your assets that are being added",
|
||||
"version-released-try-now": "{{version}} Released <0>See What's New!</0>",
|
||||
"view-deleted-entity": "Ver todas las {{entity}} eliminadas que se encuentran bajo este {{parent}}.",
|
||||
"view-sample-data-entity": "To view Sample Data, run the {{entity}}. Please refer to this doc to schedule the <0>{{entity}}</0>",
|
||||
|
@ -396,6 +396,7 @@
|
||||
"entity-service": "Service de {{entity}}",
|
||||
"entity-type-plural": "{{entity}} Types",
|
||||
"entity-version-detail-plural": "Détails des Versions de {{entity}}",
|
||||
"error": "Error",
|
||||
"event-publisher-plural": "Publicateurs d'Événements",
|
||||
"event-type": "Type d'Événement",
|
||||
"every": "Chaque",
|
||||
@ -1133,6 +1134,7 @@
|
||||
"username-or-email": "Nom d'Utilisateur ou Email",
|
||||
"valid-condition": "Condition Valide",
|
||||
"validating-condition": "Validation de la Condition...",
|
||||
"validation-error-plural": "Validation Errors!",
|
||||
"value": "Valeur",
|
||||
"value-count": "Décompte de la Valeur",
|
||||
"value-plural": "Valeurs",
|
||||
@ -1628,6 +1630,7 @@
|
||||
"user-mentioned-in-comment": "{{user}} vous a mentionné dans un commentaire.",
|
||||
"user-verified-successfully": "L'utilisateur a été vérifié avec succès",
|
||||
"valid-url-endpoint": "Les points de terminaison doivent être une URL valide",
|
||||
"validation-error-assets": "Please examine all your assets that are being added",
|
||||
"version-released-try-now": "{{version}} publié essayez maintenant !",
|
||||
"view-deleted-entity": "Afficher toutes les {{entity}} supprimées qui relèvent de ce {{parent}}.",
|
||||
"view-sample-data-entity": "Pour afficher des données d'exemple, exécutez {{entity}}. Veuillez vous référer à ce document pour planifier {{entity}}.",
|
||||
|
@ -396,6 +396,7 @@
|
||||
"entity-service": "{{entity}}サービス",
|
||||
"entity-type-plural": "{{entity}} Type",
|
||||
"entity-version-detail-plural": "{{entity}} Version Details",
|
||||
"error": "Error",
|
||||
"event-publisher-plural": "イベントの作成者",
|
||||
"event-type": "イベントの種類",
|
||||
"every": "Every",
|
||||
@ -1133,6 +1134,7 @@
|
||||
"username-or-email": "ユーザ名あるいはEmailアドレス",
|
||||
"valid-condition": "正しい条件",
|
||||
"validating-condition": "条件を検証中...",
|
||||
"validation-error-plural": "Validation Errors!",
|
||||
"value": "値",
|
||||
"value-count": "値の数",
|
||||
"value-plural": "値",
|
||||
@ -1628,6 +1630,7 @@
|
||||
"user-mentioned-in-comment": "{{user}}がコメントであなたにメンションしました。",
|
||||
"user-verified-successfully": "ユーザ認証に成功しました",
|
||||
"valid-url-endpoint": "エンドポイントは有効なURLでなければなりません",
|
||||
"validation-error-assets": "Please examine all your assets that are being added",
|
||||
"version-released-try-now": "{{version}} Released <0>See What's New!</0>",
|
||||
"view-deleted-entity": "View All the Deleted {{entity}}, which come under this {{parent}}.",
|
||||
"view-sample-data-entity": "To view Sample Data, run the {{entity}}. Please refer to this doc to schedule the <0>{{entity}}</0>",
|
||||
|
@ -396,6 +396,7 @@
|
||||
"entity-service": "Serviço de {{entity}}",
|
||||
"entity-type-plural": "Tipo de {{entity}}",
|
||||
"entity-version-detail-plural": "Detalhes da Versão de {{entity}}",
|
||||
"error": "Error",
|
||||
"event-publisher-plural": "Publicadores de Eventos",
|
||||
"event-type": "Tipo de Evento",
|
||||
"every": "Cada",
|
||||
@ -1133,6 +1134,7 @@
|
||||
"username-or-email": "Nome de Usuário ou Email",
|
||||
"valid-condition": "Condição Válida",
|
||||
"validating-condition": "Validando a condição...",
|
||||
"validation-error-plural": "Validation Errors!",
|
||||
"value": "Valor",
|
||||
"value-count": "Contagem de Valor",
|
||||
"value-plural": "Valores",
|
||||
@ -1628,6 +1630,7 @@
|
||||
"user-mentioned-in-comment": "{{user}} mencionou você em um comentário.",
|
||||
"user-verified-successfully": "Usuário verificado com sucesso",
|
||||
"valid-url-endpoint": "Os endpoints devem ser URLs válidas",
|
||||
"validation-error-assets": "Please examine all your assets that are being added",
|
||||
"version-released-try-now": "{{version}} Lançada <0>Veja o que há de novo!</0>",
|
||||
"view-deleted-entity": "Visualize todos os {{entity}} excluídos, que fazem parte deste {{parent}}.",
|
||||
"view-sample-data-entity": "Para visualizar os Dados de Amostra, execute o {{entity}}. Consulte este documento para agendar o <0>{{entity}}</0>",
|
||||
|
@ -396,6 +396,7 @@
|
||||
"entity-service": "Сервис {{entity}}",
|
||||
"entity-type-plural": "Тип {{entity}}",
|
||||
"entity-version-detail-plural": "{{entity}} Version Details",
|
||||
"error": "Error",
|
||||
"event-publisher-plural": "Издатели события",
|
||||
"event-type": "Тип события",
|
||||
"every": "Каждый",
|
||||
@ -1133,6 +1134,7 @@
|
||||
"username-or-email": "Имя пользователя или Email",
|
||||
"valid-condition": "Действительное условие",
|
||||
"validating-condition": "Проверка условия...",
|
||||
"validation-error-plural": "Validation Errors!",
|
||||
"value": "Значение",
|
||||
"value-count": "Количество значений",
|
||||
"value-plural": "Значения",
|
||||
@ -1628,6 +1630,7 @@
|
||||
"user-mentioned-in-comment": "{{user}} упомянул вас в комментарии.",
|
||||
"user-verified-successfully": "Пользователь успешно проверен",
|
||||
"valid-url-endpoint": "Конечные точки должны быть действительным URL",
|
||||
"validation-error-assets": "Please examine all your assets that are being added",
|
||||
"version-released-try-now": "Выпущена {{версия}} <0>Посмотрите, что нового!</0>",
|
||||
"view-deleted-entity": "Просмотреть все удаленные {{entity}}, относящиеся к этому {{parent}}.",
|
||||
"view-sample-data-entity": "Чтобы просмотреть образцы данных, запустите {{entity}}. Пожалуйста, обратитесь к этому документу, чтобы запланировать <0>{{entity}}</0>",
|
||||
|
@ -396,6 +396,7 @@
|
||||
"entity-service": "{{entity}}服务",
|
||||
"entity-type-plural": "{{entity}}类型",
|
||||
"entity-version-detail-plural": "{{entity}}版本详情",
|
||||
"error": "Error",
|
||||
"event-publisher-plural": "事件发布者",
|
||||
"event-type": "事件类型",
|
||||
"every": "每个",
|
||||
@ -1133,6 +1134,7 @@
|
||||
"username-or-email": "用户名或电子邮箱",
|
||||
"valid-condition": "有效条件",
|
||||
"validating-condition": "正在验证条件...",
|
||||
"validation-error-plural": "Validation Errors!",
|
||||
"value": "值",
|
||||
"value-count": "值数量",
|
||||
"value-plural": "值",
|
||||
@ -1628,6 +1630,7 @@
|
||||
"user-mentioned-in-comment": "{{user}}在评论中提到了您",
|
||||
"user-verified-successfully": "用户验证成功",
|
||||
"valid-url-endpoint": "端点应该是有效的 URL",
|
||||
"validation-error-assets": "Please examine all your assets that are being added",
|
||||
"version-released-try-now": "查看新版本 {{version}} 的新特性!",
|
||||
"view-deleted-entity": "查看所有被删除的{{entity}},隶属于该{{parent}}",
|
||||
"view-sample-data-entity": "要查看样本数据,请运行{{entity}}。您还可以前往查看任务调度相关的信息<0>{{entity}}</0>。",
|
||||
|
@ -305,9 +305,12 @@ const GlossaryPage = () => {
|
||||
.finally(() => setDeleteStatus(LOADING_STATE.INITIAL));
|
||||
};
|
||||
|
||||
const handleAssetClick = (asset?: EntityDetailsObjectInterface) => {
|
||||
const handleAssetClick = useCallback(
|
||||
(asset?: EntityDetailsObjectInterface) => {
|
||||
setPreviewAsset(asset);
|
||||
};
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return <Loader />;
|
||||
|
@ -15,15 +15,20 @@ import { Status } from '../generated/entity/applications/appRunRecord';
|
||||
import { PipelineState } from '../generated/entity/services/ingestionPipelines/ingestionPipeline';
|
||||
|
||||
export const getStatusTypeForApplication = (status: Status) => {
|
||||
if (status === Status.Failed) {
|
||||
switch (status) {
|
||||
case Status.Failed:
|
||||
return StatusType.Failure;
|
||||
} else if (status === Status.Success) {
|
||||
return StatusType.Success;
|
||||
} else if (status === Status.Running) {
|
||||
return StatusType.Warning;
|
||||
}
|
||||
|
||||
case Status.Success:
|
||||
case Status.Completed:
|
||||
return StatusType.Success;
|
||||
|
||||
case Status.Running:
|
||||
return StatusType.Warning;
|
||||
|
||||
default:
|
||||
return StatusType.Failure;
|
||||
}
|
||||
};
|
||||
|
||||
export const getStatusFromPipelineState = (status: PipelineState) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user