diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/bottom-arrow.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/bottom-arrow.svg new file mode 100644 index 00000000000..7c82136e0eb --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/bottom-arrow.svg @@ -0,0 +1,11 @@ + + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BotListV1/BotListV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/BotListV1/BotListV1.component.tsx index 502e8da1f00..cd1dd1c2480 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/BotListV1/BotListV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/BotListV1/BotListV1.component.tsx @@ -171,7 +171,7 @@ const BotListV1 = ({ * handle after delete bot action */ const handleDeleteAction = useCallback(async () => { - fetchBots(); + fetchBots(showDeleted); }, [selectedUser]); const handleSearch = (text: string) => { @@ -306,6 +306,7 @@ const BotListV1 = ({ {t('label.cancel')} - <> - {saveState === 'waiting' ? ( - - ) : saveState === 'success' ? ( - - )} - + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.interface.ts index 3992b4d4019..023fab7952c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.interface.ts @@ -11,6 +11,7 @@ * limitations under the License. */ +import { LOADING_STATE } from 'enums/common.enum'; import { CustomProperty, Type } from '../../generated/entity/type'; export interface CustomPropertyTableProp { @@ -19,6 +20,5 @@ export interface CustomPropertyTableProp { updateEntityType: ( customProperties: Type['customProperties'] ) => Promise; + loadingState: LOADING_STATE; } - -export type Operation = 'delete' | 'update' | 'no-operation'; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.test.tsx index bd3df6bfd1c..66830e4cc76 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.test.tsx @@ -18,6 +18,7 @@ import { render, screen, } from '@testing-library/react'; +import { LOADING_STATE } from 'enums/common.enum'; import React from 'react'; import { CustomPropertyTable } from './CustomPropertyTable'; @@ -64,6 +65,7 @@ const mockProp = { hasAccess: true, customProperties: mockProperties, updateEntityType: mockUpdateEntityType, + loadingState: LOADING_STATE.INITIAL, }; describe('Test CustomField Table Component', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.tsx index 58125894055..3ddd34b8a75 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.tsx @@ -10,38 +10,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Table, Tooltip } from 'antd'; +import { Button, Space, Table, Tooltip } from 'antd'; import { ColumnsType } from 'antd/lib/table'; +import { LOADING_STATE, OPERATION } from 'enums/common.enum'; import { isEmpty } from 'lodash'; -import React, { FC, Fragment, useMemo, useState } from 'react'; +import React, { FC, Fragment, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { ReactComponent as IconDelete } from '../../assets/svg/ic-delete.svg'; +import { ReactComponent as IconEdit } from '../../assets/svg/ic-edit.svg'; import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil'; import { CustomProperty } from '../../generated/entity/type'; import { getEntityName } from '../../utils/CommonUtils'; -import SVGIcons, { Icons } from '../../utils/SvgUtils'; import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPreviewer'; import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal'; import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; -import { - CustomPropertyTableProp, - Operation, -} from './CustomPropertyTable.interface'; +import { CustomPropertyTableProp } from './CustomPropertyTable.interface'; export const CustomPropertyTable: FC = ({ customProperties, updateEntityType, hasAccess, + loadingState, }) => { const { t } = useTranslation(); const [selectedProperty, setSelectedProperty] = useState( {} as CustomProperty ); - const [operation, setOperation] = useState('no-operation'); + const [operation, setOperation] = useState(OPERATION.NO_OPERATION); const resetSelectedProperty = () => { setSelectedProperty({} as CustomProperty); - setOperation('no-operation' as Operation); + setOperation(OPERATION.NO_OPERATION); }; const handlePropertyDelete = () => { @@ -49,9 +49,14 @@ export const CustomPropertyTable: FC = ({ (property) => property.name !== selectedProperty.name ); updateEntityType(updatedProperties); - resetSelectedProperty(); }; + useEffect(() => { + if (loadingState === LOADING_STATE.INITIAL) { + resetSelectedProperty(); + } + }, [loadingState]); + const handlePropertyUpdate = async (updatedDescription: string) => { const updatedProperties = customProperties.map((property) => { if (property.name === selectedProperty.name) { @@ -64,8 +69,14 @@ export const CustomPropertyTable: FC = ({ resetSelectedProperty(); }; - const deleteCheck = !isEmpty(selectedProperty) && operation === 'delete'; - const updateCheck = !isEmpty(selectedProperty) && operation === 'update'; + const deleteCheck = useMemo( + () => !isEmpty(selectedProperty) && operation === OPERATION.DELETE, + [selectedProperty, operation] + ); + const updateCheck = useMemo( + () => !isEmpty(selectedProperty) && operation === OPERATION.UPDATE, + [selectedProperty, operation] + ); const tableColumn: ColumnsType = useMemo( () => [ @@ -100,44 +111,36 @@ export const CustomPropertyTable: FC = ({ dataIndex: 'actions', key: 'actions', render: (_, record) => ( -
- - + + - - + + -
+ ), }, ], @@ -164,6 +167,7 @@ export const CustomPropertyTable: FC = ({ header={t('label.delete-property-name', { propertyName: selectedProperty.name, })} + loadingState={loadingState} visible={deleteCheck} onCancel={resetSelectedProperty} onConfirm={handlePropertyDelete} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx index 308ff722331..9df5b0f92a6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/Explore.component.tsx @@ -15,9 +15,10 @@ import { SortAscendingOutlined, SortDescendingOutlined, } from '@ant-design/icons'; -import { Card, Col, Row, Tabs } from 'antd'; +import { Button, Card, Col, Row, Space, Tabs } from 'antd'; import FacetFilter from 'components/common/facetfilter/FacetFilter'; import SearchedData from 'components/searched-data/SearchedData'; +import { SORT_ORDER } from 'enums/common.enum'; import unique from 'fork-ts-checker-webpack-plugin/lib/utils/array/unique'; import { isEmpty, @@ -95,6 +96,18 @@ const Explore: React.FC = ({ setShowSummaryPanel(false); }; + const isAscSortOrder = useMemo( + () => sortOrder === SORT_ORDER.ASC, + [sortOrder] + ); + const sortProps = useMemo( + () => ({ + className: 'text-base text-primary', + 'data-testid': 'last-updated', + }), + [] + ); + const tabItems = useMemo( () => Object.entries(tabsInfo).map(([tabSearchIndex, tabDetail]) => ({ @@ -261,31 +274,28 @@ const Explore: React.FC = ({ items={tabItems} size="small" tabBarExtraContent={ -
+ - -
- {sortOrder === 'asc' ? ( - + + )} -
-
+ + } onChange={(tab) => { tab && onChangeSearchIndex(tab as ExploreSearchIndex); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/SortingDropDown.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/SortingDropDown.tsx index 17629d6da5b..282fe9e73f1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/SortingDropDown.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/SortingDropDown.tsx @@ -11,10 +11,9 @@ * limitations under the License. */ -import { Dropdown } from 'antd'; +import { Dropdown, Space, Typography } from 'antd'; import React from 'react'; -import { normalLink } from '../../utils/styleconstant'; -import { dropdownIcon as DropDownIcon } from '../../utils/svgconstant'; +import { ReactComponent as DropDownIcon } from '../../assets/svg/bottom-arrow.svg'; export interface SortingField { name: string; @@ -43,16 +42,16 @@ const SortingDropDown: React.FC = ({ return ( -
- {label} - -
+ + {label} + +
); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts index bcbe1f9cc2c..85e9dbeb3d7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts @@ -12,6 +12,7 @@ */ import { DefaultOptionType } from 'antd/lib/select'; +import { SORT_ORDER } from 'enums/common.enum'; import { JsonTree } from 'react-awesome-query-builder'; import { SearchIndex } from '../../enums/search.enum'; import { Dashboard } from '../../generated/entity/data/dashboard'; @@ -65,7 +66,7 @@ export interface ExploreProps { onChangeSortValue: (sortValue: string) => void; sortOrder: string; - onChangeSortOder: (sortOder: string) => void; + onChangeSortOder: (sortOder: SORT_ORDER) => void; showDeleted: boolean; onChangeShowDeleted: (showDeleted: boolean) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/ConfirmationModal/ConfirmationModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ConfirmationModal/ConfirmationModal.tsx index 6e37357b78e..07bbfc2745c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/ConfirmationModal/ConfirmationModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/ConfirmationModal/ConfirmationModal.tsx @@ -14,12 +14,13 @@ import { Button, Typography } from 'antd'; import Modal from 'antd/lib/modal/Modal'; import classNames from 'classnames'; +import { LOADING_STATE } from 'enums/common.enum'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmationModalProps } from './ConfirmationModal.interface'; const ConfirmationModal = ({ - loadingState = 'initial', + loadingState = LOADING_STATE.INITIAL, cancelText, confirmText, header, @@ -57,9 +58,12 @@ const ConfirmationModal = ({ className={confirmButtonCss} danger={confirmText === t('label.delete')} data-testid={ - loadingState === 'waiting' ? 'loading-button' : 'save-button' + loadingState === LOADING_STATE.WAITING + ? 'loading-button' + : 'save-button' } key="save-btn" + loading={LOADING_STATE.WAITING === loadingState} type="primary" onClick={onConfirm}> {confirmText} diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/common.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/common.enum.ts index e874b6e7480..6187f8a846f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/enums/common.enum.ts +++ b/openmetadata-ui/src/main/resources/ui/src/enums/common.enum.ts @@ -37,3 +37,14 @@ export enum PROMISE_STATE { FULFILLED = 'fulfilled', REJECTED = 'rejected', } + +export enum OPERATION { + UPDATE = 'update', + DELETE = 'delete', + NO_OPERATION = 'no-operation', +} + +export enum SORT_ORDER { + ASC = 'asc', + DESC = 'desc', +} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CreateUserPage/CreateUserPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CreateUserPage/CreateUserPage.component.tsx index 85245d10b04..f24fd3d7679 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/CreateUserPage/CreateUserPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/CreateUserPage/CreateUserPage.component.tsx @@ -14,6 +14,7 @@ import { AxiosError } from 'axios'; import PageContainerV1 from 'components/containers/PageContainerV1'; import CreateUserComponent from 'components/CreateUser/CreateUser.component'; +import { LOADING_STATE } from 'enums/common.enum'; import _ from 'lodash'; import { observer } from 'mobx-react'; import { LoadingState } from 'Models'; @@ -38,7 +39,7 @@ const CreateUserPage = () => { const { t } = useTranslation(); const [roles, setRoles] = useState>([]); - const [status, setStatus] = useState('initial'); + const [status, setStatus] = useState(LOADING_STATE.INITIAL); const { bot } = useParams<{ bot: string }>(); @@ -69,7 +70,7 @@ const CreateUserPage = () => { fallbackText?: string ) => { showErrorToast(error, fallbackText); - setStatus('initial'); + setStatus(LOADING_STATE.INITIAL); }; const checkBotInUse = async (name: string) => { @@ -87,15 +88,16 @@ const CreateUserPage = () => { * @param userData Data for creating new user */ const handleAddUserSave = async (userData: CreateUser) => { + setStatus(LOADING_STATE.WAITING); if (bot) { const isBotExists = await checkBotInUse(userData.name); if (isBotExists) { + setStatus(LOADING_STATE.INITIAL); showErrorToast( t('message.entity-already-exists', { entity: userData.name }) ); } else { try { - setStatus('waiting'); // Create a user with isBot:true const userResponse = await createUserWithPut({ ...userData, @@ -111,12 +113,12 @@ const CreateUserPage = () => { }); if (botResponse) { - setStatus('success'); + setStatus(LOADING_STATE.SUCCESS); showSuccessToast( t('server.create-entity-success', { entity: t('label.bot') }) ); setTimeout(() => { - setStatus('initial'); + setStatus(LOADING_STATE.INITIAL); goToUserListPage(); }, 500); @@ -134,14 +136,12 @@ const CreateUserPage = () => { } } else { try { - setStatus('waiting'); - const response = await createUser(userData); if (response) { - setStatus('success'); + setStatus(LOADING_STATE.SUCCESS); setTimeout(() => { - setStatus('initial'); + setStatus(LOADING_STATE.WAITING); goToUserListPage(); }, 500); } else { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx index a9965a70377..c688daad17f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx @@ -24,6 +24,7 @@ import { ResourceEntity, } from 'components/PermissionProvider/PermissionProvider.interface'; import SchemaEditor from 'components/schema-editor/SchemaEditor'; +import { LOADING_STATE } from 'enums/common.enum'; import { compare } from 'fast-json-patch'; import { isEmpty, isUndefined } from 'lodash'; import { default as React, useEffect, useMemo, useState } from 'react'; @@ -57,6 +58,8 @@ const CustomEntityDetailV1 = () => { const [selectedEntityTypeDetail, setSelectedEntityTypeDetail] = useState({} as Type); + const [loadingState, setLoadingState] = useState(LOADING_STATE.INITIAL); + const tabAttributePath = ENTITY_PATH[tab.toLowerCase()]; const { getEntityPermission } = usePermissionProvider(); @@ -126,6 +129,7 @@ const CustomEntityDetailV1 = () => { }, [selectedEntityTypeDetail]); const updateEntityType = async (properties: Type['customProperties']) => { + setLoadingState(LOADING_STATE.WAITING); const patch = compare(selectedEntityTypeDetail, { ...selectedEntityTypeDetail, customProperties: properties, @@ -139,6 +143,8 @@ const CustomEntityDetailV1 = () => { })); } catch (error) { showErrorToast(error as AxiosError); + } finally { + setLoadingState(LOADING_STATE.INITIAL); } }; @@ -275,6 +281,7 @@ const CustomEntityDetailV1 = () => { selectedEntityTypeDetail.customProperties || [] } hasAccess={editPermission} + loadingState={loadingState} updateEntityType={updateEntityType} /> diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx index 331ca629700..de1e0a6171f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/explore/ExplorePage.component.tsx @@ -19,6 +19,7 @@ import { SearchHitCounts, UrlParams, } from 'components/Explore/explore.interface'; +import { SORT_ORDER } from 'enums/common.enum'; import { isNil, isString } from 'lodash'; import Qs from 'qs'; import React, { FunctionComponent, useEffect, useMemo, useState } from 'react'; @@ -30,7 +31,6 @@ import AppState from '../../AppState'; import { getExplorePath, PAGE_SIZE } from '../../constants/constants'; import { INITIAL_SORT_FIELD, - INITIAL_SORT_ORDER, tabsInfo, } from '../../constants/explore.constants'; import { SearchIndex } from '../../enums/search.enum'; @@ -57,7 +57,7 @@ const ExplorePage: FunctionComponent = () => { const [sortValue, setSortValue] = useState(INITIAL_SORT_FIELD); - const [sortOrder, setSortOrder] = useState(INITIAL_SORT_ORDER); + const [sortOrder, setSortOrder] = useState(SORT_ORDER.DESC); const [searchHitCounts, setSearchHitCounts] = useState(); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/tags/index.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/tags/index.tsx index f56062b9452..67e26cd76a1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/tags/index.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/tags/index.tsx @@ -628,11 +628,8 @@ const TagsPage = () => {