chore(ui): UI improvements (#10277)

* chore(ui): UI improvements

* fix unit test issue

* changes as per comments
This commit is contained in:
Ashish Gupta 2023-02-23 13:05:10 +05:30 committed by GitHub
parent 61c896964e
commit ef30577ace
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 148 additions and 107 deletions

View File

@ -0,0 +1,11 @@
<svg
aria-hidden="true"
fill="currentColor"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg">
<path
d="M8.00003 11C7.87216 11 7.74416 10.9512 7.64653 10.8535L2.64653 5.85353C2.45116 5.65816 2.45116 5.34178 2.64653 5.14653C2.84191 4.95128 3.15828 4.95116 3.35353 5.14653L8.00003 9.79303L12.6465 5.14653C12.8419 4.95116 13.1583 4.95116 13.3535 5.14653C13.5488 5.34191 13.5489 5.65828 13.3535 5.85353L8.35354 10.8535C8.25591 10.9512 8.12791 11 8.00003 11Z"
strokeWidth="0.4"
/>
</svg>

After

Width:  |  Height:  |  Size: 546 B

View File

@ -171,7 +171,7 @@ const BotListV1 = ({
* handle after delete bot action * handle after delete bot action
*/ */
const handleDeleteAction = useCallback(async () => { const handleDeleteAction = useCallback(async () => {
fetchBots(); fetchBots(showDeleted);
}, [selectedUser]); }, [selectedUser]);
const handleSearch = (text: string) => { const handleSearch = (text: string) => {
@ -306,6 +306,7 @@ const BotListV1 = ({
<DeleteWidgetModal <DeleteWidgetModal
afterDeleteAction={handleDeleteAction} afterDeleteAction={handleDeleteAction}
allowSoftDelete={!showDeleted}
entityId={selectedUser?.id || ''} entityId={selectedUser?.id || ''}
entityName={selectedUser?.displayName || ''} entityName={selectedUser?.displayName || ''}
entityType={EntityType.BOT} entityType={EntityType.BOT}

View File

@ -11,7 +11,6 @@
* limitations under the License. * limitations under the License.
*/ */
import { CheckOutlined } from '@ant-design/icons';
import { import {
Button, Button,
Form, Form,
@ -24,6 +23,7 @@ import {
} from 'antd'; } from 'antd';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { LOADING_STATE } from 'enums/common.enum';
import { isUndefined, trim } from 'lodash'; import { isUndefined, trim } from 'lodash';
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -984,23 +984,14 @@ const CreateUser = ({
<Button data-testid="cancel-user" type="link" onClick={onCancel}> <Button data-testid="cancel-user" type="link" onClick={onCancel}>
{t('label.cancel')} {t('label.cancel')}
</Button> </Button>
<> <Button
{saveState === 'waiting' ? ( data-testid="save-user"
<Button disabled type="primary"> form="create-user-bot-form"
<Loader size="small" type="white" /> htmlType="submit"
</Button> loading={saveState === LOADING_STATE.WAITING}
) : saveState === 'success' ? ( type="primary">
<Button disabled icon={<CheckOutlined />} type="primary" /> {t('label.create')}
) : ( </Button>
<Button
data-testid="save-user"
form="create-user-bot-form"
htmlType="submit"
type="primary">
{t('label.create')}
</Button>
)}
</>
</Space> </Space>
</Form> </Form>
</div> </div>

View File

@ -11,6 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { LOADING_STATE } from 'enums/common.enum';
import { CustomProperty, Type } from '../../generated/entity/type'; import { CustomProperty, Type } from '../../generated/entity/type';
export interface CustomPropertyTableProp { export interface CustomPropertyTableProp {
@ -19,6 +20,5 @@ export interface CustomPropertyTableProp {
updateEntityType: ( updateEntityType: (
customProperties: Type['customProperties'] customProperties: Type['customProperties']
) => Promise<void>; ) => Promise<void>;
loadingState: LOADING_STATE;
} }
export type Operation = 'delete' | 'update' | 'no-operation';

View File

@ -18,6 +18,7 @@ import {
render, render,
screen, screen,
} from '@testing-library/react'; } from '@testing-library/react';
import { LOADING_STATE } from 'enums/common.enum';
import React from 'react'; import React from 'react';
import { CustomPropertyTable } from './CustomPropertyTable'; import { CustomPropertyTable } from './CustomPropertyTable';
@ -64,6 +65,7 @@ const mockProp = {
hasAccess: true, hasAccess: true,
customProperties: mockProperties, customProperties: mockProperties,
updateEntityType: mockUpdateEntityType, updateEntityType: mockUpdateEntityType,
loadingState: LOADING_STATE.INITIAL,
}; };
describe('Test CustomField Table Component', () => { describe('Test CustomField Table Component', () => {

View File

@ -10,38 +10,38 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { Table, Tooltip } from 'antd'; import { Button, Space, Table, Tooltip } from 'antd';
import { ColumnsType } from 'antd/lib/table'; import { ColumnsType } from 'antd/lib/table';
import { LOADING_STATE, OPERATION } from 'enums/common.enum';
import { isEmpty } from 'lodash'; 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 { 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 { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
import { CustomProperty } from '../../generated/entity/type'; import { CustomProperty } from '../../generated/entity/type';
import { getEntityName } from '../../utils/CommonUtils'; import { getEntityName } from '../../utils/CommonUtils';
import SVGIcons, { Icons } from '../../utils/SvgUtils';
import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPreviewer'; import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPreviewer';
import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal'; import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal';
import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
import { import { CustomPropertyTableProp } from './CustomPropertyTable.interface';
CustomPropertyTableProp,
Operation,
} from './CustomPropertyTable.interface';
export const CustomPropertyTable: FC<CustomPropertyTableProp> = ({ export const CustomPropertyTable: FC<CustomPropertyTableProp> = ({
customProperties, customProperties,
updateEntityType, updateEntityType,
hasAccess, hasAccess,
loadingState,
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [selectedProperty, setSelectedProperty] = useState<CustomProperty>( const [selectedProperty, setSelectedProperty] = useState<CustomProperty>(
{} as CustomProperty {} as CustomProperty
); );
const [operation, setOperation] = useState<Operation>('no-operation'); const [operation, setOperation] = useState<OPERATION>(OPERATION.NO_OPERATION);
const resetSelectedProperty = () => { const resetSelectedProperty = () => {
setSelectedProperty({} as CustomProperty); setSelectedProperty({} as CustomProperty);
setOperation('no-operation' as Operation); setOperation(OPERATION.NO_OPERATION);
}; };
const handlePropertyDelete = () => { const handlePropertyDelete = () => {
@ -49,9 +49,14 @@ export const CustomPropertyTable: FC<CustomPropertyTableProp> = ({
(property) => property.name !== selectedProperty.name (property) => property.name !== selectedProperty.name
); );
updateEntityType(updatedProperties); updateEntityType(updatedProperties);
resetSelectedProperty();
}; };
useEffect(() => {
if (loadingState === LOADING_STATE.INITIAL) {
resetSelectedProperty();
}
}, [loadingState]);
const handlePropertyUpdate = async (updatedDescription: string) => { const handlePropertyUpdate = async (updatedDescription: string) => {
const updatedProperties = customProperties.map((property) => { const updatedProperties = customProperties.map((property) => {
if (property.name === selectedProperty.name) { if (property.name === selectedProperty.name) {
@ -64,8 +69,14 @@ export const CustomPropertyTable: FC<CustomPropertyTableProp> = ({
resetSelectedProperty(); resetSelectedProperty();
}; };
const deleteCheck = !isEmpty(selectedProperty) && operation === 'delete'; const deleteCheck = useMemo(
const updateCheck = !isEmpty(selectedProperty) && operation === 'update'; () => !isEmpty(selectedProperty) && operation === OPERATION.DELETE,
[selectedProperty, operation]
);
const updateCheck = useMemo(
() => !isEmpty(selectedProperty) && operation === OPERATION.UPDATE,
[selectedProperty, operation]
);
const tableColumn: ColumnsType<CustomProperty> = useMemo( const tableColumn: ColumnsType<CustomProperty> = useMemo(
() => [ () => [
@ -100,44 +111,36 @@ export const CustomPropertyTable: FC<CustomPropertyTableProp> = ({
dataIndex: 'actions', dataIndex: 'actions',
key: 'actions', key: 'actions',
render: (_, record) => ( render: (_, record) => (
<div className="tw-flex"> <Space align="center" size={14}>
<Tooltip <Tooltip title={!hasAccess && NO_PERMISSION_FOR_ACTION}>
title={hasAccess ? t('label.edit') : NO_PERMISSION_FOR_ACTION}> <Button
<button className="cursor-pointer p-0"
className="tw-cursor-pointer"
data-testid="edit-button" data-testid="edit-button"
disabled={!hasAccess} disabled={!hasAccess}
size="small"
type="text"
onClick={() => { onClick={() => {
setSelectedProperty(record); setSelectedProperty(record);
setOperation('update'); setOperation(OPERATION.UPDATE);
}}> }}>
<SVGIcons <IconEdit name={t('label.edit')} width={16} />
alt="edit" </Button>
icon={Icons.EDIT}
title={t('label.edit')}
width="16px"
/>
</button>
</Tooltip> </Tooltip>
<Tooltip <Tooltip title={!hasAccess && NO_PERMISSION_FOR_ACTION}>
title={hasAccess ? t('label.delete') : NO_PERMISSION_FOR_ACTION}> <Button
<button className="cursor-pointer p-0"
className="tw-cursor-pointer tw-ml-4"
data-testid="delete-button" data-testid="delete-button"
disabled={!hasAccess} disabled={!hasAccess}
size="small"
type="text"
onClick={() => { onClick={() => {
setSelectedProperty(record); setSelectedProperty(record);
setOperation('delete'); setOperation(OPERATION.DELETE);
}}> }}>
<SVGIcons <IconDelete name={t('label.delete')} width={16} />
alt="delete" </Button>
icon={Icons.DELETE}
title={t('label.delete')}
width="16px"
/>
</button>
</Tooltip> </Tooltip>
</div> </Space>
), ),
}, },
], ],
@ -164,6 +167,7 @@ export const CustomPropertyTable: FC<CustomPropertyTableProp> = ({
header={t('label.delete-property-name', { header={t('label.delete-property-name', {
propertyName: selectedProperty.name, propertyName: selectedProperty.name,
})} })}
loadingState={loadingState}
visible={deleteCheck} visible={deleteCheck}
onCancel={resetSelectedProperty} onCancel={resetSelectedProperty}
onConfirm={handlePropertyDelete} onConfirm={handlePropertyDelete}

View File

@ -15,9 +15,10 @@ import {
SortAscendingOutlined, SortAscendingOutlined,
SortDescendingOutlined, SortDescendingOutlined,
} from '@ant-design/icons'; } 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 FacetFilter from 'components/common/facetfilter/FacetFilter';
import SearchedData from 'components/searched-data/SearchedData'; 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 unique from 'fork-ts-checker-webpack-plugin/lib/utils/array/unique';
import { import {
isEmpty, isEmpty,
@ -95,6 +96,18 @@ const Explore: React.FC<ExploreProps> = ({
setShowSummaryPanel(false); 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( const tabItems = useMemo(
() => () =>
Object.entries(tabsInfo).map(([tabSearchIndex, tabDetail]) => ({ Object.entries(tabsInfo).map(([tabSearchIndex, tabDetail]) => ({
@ -261,31 +274,28 @@ const Explore: React.FC<ExploreProps> = ({
items={tabItems} items={tabItems}
size="small" size="small"
tabBarExtraContent={ tabBarExtraContent={
<div className="tw-flex"> <Space align="center" size={4}>
<SortingDropDown <SortingDropDown
fieldList={tabsInfo[searchIndex].sortingFields} fieldList={tabsInfo[searchIndex].sortingFields}
handleFieldDropDown={onChangeSortValue} handleFieldDropDown={onChangeSortValue}
sortField={sortValue} sortField={sortValue}
/> />
<Button
<div className="tw-flex"> className="p-0"
{sortOrder === 'asc' ? ( size="small"
<button onClick={() => onChangeSortOder('desc')}> type="text"
<SortAscendingOutlined onClick={() =>
className="tw-text-base tw-text-primary" onChangeSortOder(
data-testid="last-updated" isAscSortOrder ? SORT_ORDER.DESC : SORT_ORDER.ASC
/> )
</button> }>
{isAscSortOrder ? (
<SortAscendingOutlined {...sortProps} />
) : ( ) : (
<button onClick={() => onChangeSortOder('asc')}> <SortDescendingOutlined {...sortProps} />
<SortDescendingOutlined
className="tw-text-base tw-text-primary"
data-testid="last-updated"
/>
</button>
)} )}
</div> </Button>
</div> </Space>
} }
onChange={(tab) => { onChange={(tab) => {
tab && onChangeSearchIndex(tab as ExploreSearchIndex); tab && onChangeSearchIndex(tab as ExploreSearchIndex);

View File

@ -11,10 +11,9 @@
* limitations under the License. * limitations under the License.
*/ */
import { Dropdown } from 'antd'; import { Dropdown, Space, Typography } from 'antd';
import React from 'react'; import React from 'react';
import { normalLink } from '../../utils/styleconstant'; import { ReactComponent as DropDownIcon } from '../../assets/svg/bottom-arrow.svg';
import { dropdownIcon as DropDownIcon } from '../../utils/svgconstant';
export interface SortingField { export interface SortingField {
name: string; name: string;
@ -43,16 +42,16 @@ const SortingDropDown: React.FC<SortingDropdownProps> = ({
return ( return (
<Dropdown <Dropdown
className="tw-self-end tw-mr-2 tw-cursor-pointer" className="self-end m-r-xs cursor-pointer"
data-testid="dropdown" data-testid="dropdown"
menu={{ menu={{
items, items,
}} }}
trigger={['click']}> trigger={['click']}>
<div className="tw-text-primary" data-testid="dropdown-label"> <Space align="center" data-testid="dropdown-label" size={4}>
<span className="tw-mr-2">{label}</span> <Typography.Text className="text-primary">{label}</Typography.Text>
<DropDownIcon style={{ color: normalLink, margin: '0px' }} /> <DropDownIcon className="text-primary" height={16} width={16} />
</div> </Space>
</Dropdown> </Dropdown>
); );
}; };

View File

@ -12,6 +12,7 @@
*/ */
import { DefaultOptionType } from 'antd/lib/select'; import { DefaultOptionType } from 'antd/lib/select';
import { SORT_ORDER } from 'enums/common.enum';
import { JsonTree } from 'react-awesome-query-builder'; import { JsonTree } from 'react-awesome-query-builder';
import { SearchIndex } from '../../enums/search.enum'; import { SearchIndex } from '../../enums/search.enum';
import { Dashboard } from '../../generated/entity/data/dashboard'; import { Dashboard } from '../../generated/entity/data/dashboard';
@ -65,7 +66,7 @@ export interface ExploreProps {
onChangeSortValue: (sortValue: string) => void; onChangeSortValue: (sortValue: string) => void;
sortOrder: string; sortOrder: string;
onChangeSortOder: (sortOder: string) => void; onChangeSortOder: (sortOder: SORT_ORDER) => void;
showDeleted: boolean; showDeleted: boolean;
onChangeShowDeleted: (showDeleted: boolean) => void; onChangeShowDeleted: (showDeleted: boolean) => void;

View File

@ -14,12 +14,13 @@
import { Button, Typography } from 'antd'; import { Button, Typography } from 'antd';
import Modal from 'antd/lib/modal/Modal'; import Modal from 'antd/lib/modal/Modal';
import classNames from 'classnames'; import classNames from 'classnames';
import { LOADING_STATE } from 'enums/common.enum';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ConfirmationModalProps } from './ConfirmationModal.interface'; import { ConfirmationModalProps } from './ConfirmationModal.interface';
const ConfirmationModal = ({ const ConfirmationModal = ({
loadingState = 'initial', loadingState = LOADING_STATE.INITIAL,
cancelText, cancelText,
confirmText, confirmText,
header, header,
@ -57,9 +58,12 @@ const ConfirmationModal = ({
className={confirmButtonCss} className={confirmButtonCss}
danger={confirmText === t('label.delete')} danger={confirmText === t('label.delete')}
data-testid={ data-testid={
loadingState === 'waiting' ? 'loading-button' : 'save-button' loadingState === LOADING_STATE.WAITING
? 'loading-button'
: 'save-button'
} }
key="save-btn" key="save-btn"
loading={LOADING_STATE.WAITING === loadingState}
type="primary" type="primary"
onClick={onConfirm}> onClick={onConfirm}>
{confirmText} {confirmText}

View File

@ -37,3 +37,14 @@ export enum PROMISE_STATE {
FULFILLED = 'fulfilled', FULFILLED = 'fulfilled',
REJECTED = 'rejected', REJECTED = 'rejected',
} }
export enum OPERATION {
UPDATE = 'update',
DELETE = 'delete',
NO_OPERATION = 'no-operation',
}
export enum SORT_ORDER {
ASC = 'asc',
DESC = 'desc',
}

View File

@ -14,6 +14,7 @@
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import PageContainerV1 from 'components/containers/PageContainerV1'; import PageContainerV1 from 'components/containers/PageContainerV1';
import CreateUserComponent from 'components/CreateUser/CreateUser.component'; import CreateUserComponent from 'components/CreateUser/CreateUser.component';
import { LOADING_STATE } from 'enums/common.enum';
import _ from 'lodash'; import _ from 'lodash';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { LoadingState } from 'Models'; import { LoadingState } from 'Models';
@ -38,7 +39,7 @@ const CreateUserPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [roles, setRoles] = useState<Array<Role>>([]); const [roles, setRoles] = useState<Array<Role>>([]);
const [status, setStatus] = useState<LoadingState>('initial'); const [status, setStatus] = useState<LoadingState>(LOADING_STATE.INITIAL);
const { bot } = useParams<{ bot: string }>(); const { bot } = useParams<{ bot: string }>();
@ -69,7 +70,7 @@ const CreateUserPage = () => {
fallbackText?: string fallbackText?: string
) => { ) => {
showErrorToast(error, fallbackText); showErrorToast(error, fallbackText);
setStatus('initial'); setStatus(LOADING_STATE.INITIAL);
}; };
const checkBotInUse = async (name: string) => { const checkBotInUse = async (name: string) => {
@ -87,15 +88,16 @@ const CreateUserPage = () => {
* @param userData Data for creating new user * @param userData Data for creating new user
*/ */
const handleAddUserSave = async (userData: CreateUser) => { const handleAddUserSave = async (userData: CreateUser) => {
setStatus(LOADING_STATE.WAITING);
if (bot) { if (bot) {
const isBotExists = await checkBotInUse(userData.name); const isBotExists = await checkBotInUse(userData.name);
if (isBotExists) { if (isBotExists) {
setStatus(LOADING_STATE.INITIAL);
showErrorToast( showErrorToast(
t('message.entity-already-exists', { entity: userData.name }) t('message.entity-already-exists', { entity: userData.name })
); );
} else { } else {
try { try {
setStatus('waiting');
// Create a user with isBot:true // Create a user with isBot:true
const userResponse = await createUserWithPut({ const userResponse = await createUserWithPut({
...userData, ...userData,
@ -111,12 +113,12 @@ const CreateUserPage = () => {
}); });
if (botResponse) { if (botResponse) {
setStatus('success'); setStatus(LOADING_STATE.SUCCESS);
showSuccessToast( showSuccessToast(
t('server.create-entity-success', { entity: t('label.bot') }) t('server.create-entity-success', { entity: t('label.bot') })
); );
setTimeout(() => { setTimeout(() => {
setStatus('initial'); setStatus(LOADING_STATE.INITIAL);
goToUserListPage(); goToUserListPage();
}, 500); }, 500);
@ -134,14 +136,12 @@ const CreateUserPage = () => {
} }
} else { } else {
try { try {
setStatus('waiting');
const response = await createUser(userData); const response = await createUser(userData);
if (response) { if (response) {
setStatus('success'); setStatus(LOADING_STATE.SUCCESS);
setTimeout(() => { setTimeout(() => {
setStatus('initial'); setStatus(LOADING_STATE.WAITING);
goToUserListPage(); goToUserListPage();
}, 500); }, 500);
} else { } else {

View File

@ -24,6 +24,7 @@ import {
ResourceEntity, ResourceEntity,
} from 'components/PermissionProvider/PermissionProvider.interface'; } from 'components/PermissionProvider/PermissionProvider.interface';
import SchemaEditor from 'components/schema-editor/SchemaEditor'; import SchemaEditor from 'components/schema-editor/SchemaEditor';
import { LOADING_STATE } from 'enums/common.enum';
import { compare } from 'fast-json-patch'; import { compare } from 'fast-json-patch';
import { isEmpty, isUndefined } from 'lodash'; import { isEmpty, isUndefined } from 'lodash';
import { default as React, useEffect, useMemo, useState } from 'react'; import { default as React, useEffect, useMemo, useState } from 'react';
@ -57,6 +58,8 @@ const CustomEntityDetailV1 = () => {
const [selectedEntityTypeDetail, setSelectedEntityTypeDetail] = const [selectedEntityTypeDetail, setSelectedEntityTypeDetail] =
useState<Type>({} as Type); useState<Type>({} as Type);
const [loadingState, setLoadingState] = useState(LOADING_STATE.INITIAL);
const tabAttributePath = ENTITY_PATH[tab.toLowerCase()]; const tabAttributePath = ENTITY_PATH[tab.toLowerCase()];
const { getEntityPermission } = usePermissionProvider(); const { getEntityPermission } = usePermissionProvider();
@ -126,6 +129,7 @@ const CustomEntityDetailV1 = () => {
}, [selectedEntityTypeDetail]); }, [selectedEntityTypeDetail]);
const updateEntityType = async (properties: Type['customProperties']) => { const updateEntityType = async (properties: Type['customProperties']) => {
setLoadingState(LOADING_STATE.WAITING);
const patch = compare(selectedEntityTypeDetail, { const patch = compare(selectedEntityTypeDetail, {
...selectedEntityTypeDetail, ...selectedEntityTypeDetail,
customProperties: properties, customProperties: properties,
@ -139,6 +143,8 @@ const CustomEntityDetailV1 = () => {
})); }));
} catch (error) { } catch (error) {
showErrorToast(error as AxiosError); showErrorToast(error as AxiosError);
} finally {
setLoadingState(LOADING_STATE.INITIAL);
} }
}; };
@ -275,6 +281,7 @@ const CustomEntityDetailV1 = () => {
selectedEntityTypeDetail.customProperties || [] selectedEntityTypeDetail.customProperties || []
} }
hasAccess={editPermission} hasAccess={editPermission}
loadingState={loadingState}
updateEntityType={updateEntityType} updateEntityType={updateEntityType}
/> />
</div> </div>

View File

@ -19,6 +19,7 @@ import {
SearchHitCounts, SearchHitCounts,
UrlParams, UrlParams,
} from 'components/Explore/explore.interface'; } from 'components/Explore/explore.interface';
import { SORT_ORDER } from 'enums/common.enum';
import { isNil, isString } from 'lodash'; import { isNil, isString } from 'lodash';
import Qs from 'qs'; import Qs from 'qs';
import React, { FunctionComponent, useEffect, useMemo, useState } from 'react'; import React, { FunctionComponent, useEffect, useMemo, useState } from 'react';
@ -30,7 +31,6 @@ import AppState from '../../AppState';
import { getExplorePath, PAGE_SIZE } from '../../constants/constants'; import { getExplorePath, PAGE_SIZE } from '../../constants/constants';
import { import {
INITIAL_SORT_FIELD, INITIAL_SORT_FIELD,
INITIAL_SORT_ORDER,
tabsInfo, tabsInfo,
} from '../../constants/explore.constants'; } from '../../constants/explore.constants';
import { SearchIndex } from '../../enums/search.enum'; import { SearchIndex } from '../../enums/search.enum';
@ -57,7 +57,7 @@ const ExplorePage: FunctionComponent = () => {
const [sortValue, setSortValue] = useState<string>(INITIAL_SORT_FIELD); const [sortValue, setSortValue] = useState<string>(INITIAL_SORT_FIELD);
const [sortOrder, setSortOrder] = useState<string>(INITIAL_SORT_ORDER); const [sortOrder, setSortOrder] = useState<SORT_ORDER>(SORT_ORDER.DESC);
const [searchHitCounts, setSearchHitCounts] = useState<SearchHitCounts>(); const [searchHitCounts, setSearchHitCounts] = useState<SearchHitCounts>();

View File

@ -628,11 +628,8 @@ const TagsPage = () => {
<div className="tw-mb-3"> <div className="tw-mb-3">
<Tooltip <Tooltip
title={ title={
createClassificationPermission !createClassificationPermission &&
? t('label.add-entity', { t('message.no-permission-for-action')
entity: t('label.classification'),
})
: t('message.no-permission-for-action')
}> }>
<Button <Button
block block
@ -665,7 +662,7 @@ const TagsPage = () => {
key={category.name} key={category.name}
onClick={() => onClickClassifications(category)}> onClick={() => onClickClassifications(category)}>
<Typography.Paragraph <Typography.Paragraph
className="ant-typography-ellipsis-custom tag-category label-category self-center w-32" className="ant-typography-ellipsis-custom tag-category label-category self-center"
data-testid="tag-name" data-testid="tag-name"
ellipsis={{ rows: 1, tooltip: true }}> ellipsis={{ rows: 1, tooltip: true }}>
{getEntityName(category as unknown as EntityReference)} {getEntityName(category as unknown as EntityReference)}

View File

@ -89,10 +89,13 @@
gap: 16px; gap: 16px;
} }
// Alignment Items
.align-middle { .align-middle {
vertical-align: middle; vertical-align: middle;
} }
.self-end {
align-self: flex-end;
}
.vertical-align-inherit { .vertical-align-inherit {
vertical-align: inherit; vertical-align: inherit;
} }