mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-07-26 19:00:02 +00:00
chore(ui): UI improvements (#10277)
* chore(ui): UI improvements * fix unit test issue * changes as per comments
This commit is contained in:
parent
61c896964e
commit
ef30577ace
@ -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 |
@ -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}
|
||||||
|
@ -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>
|
||||||
|
@ -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';
|
|
||||||
|
@ -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', () => {
|
||||||
|
@ -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}
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -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}
|
||||||
|
@ -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',
|
||||||
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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>
|
||||||
|
@ -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>();
|
||||||
|
|
||||||
|
@ -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)}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user