mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-26 01:46:26 +00:00
Minor: improve the entity export modal feedback message (#18526)
(cherry picked from commit 305f02c62a86a380f65e6ada7e9e2f1d22052aca)
This commit is contained in:
parent
1995b17a8d
commit
7d3a1b0a9e
@ -12,12 +12,13 @@
|
|||||||
*/
|
*/
|
||||||
import { Form, Input, Modal } from 'antd';
|
import { Form, Input, Modal } from 'antd';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { isString } from 'lodash';
|
import { isString } from 'lodash';
|
||||||
import React, { ReactNode, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { ReactNode, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { getCurrentISODate } from '../../../utils/date-time/DateTimeUtils';
|
import { getCurrentISODate } from '../../../utils/date-time/DateTimeUtils';
|
||||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||||
import Banner, { BannerProps } from '../../common/Banner/Banner';
|
import Banner from '../../common/Banner/Banner';
|
||||||
import {
|
import {
|
||||||
CSVExportJob,
|
CSVExportJob,
|
||||||
CSVExportWebsocketResponse,
|
CSVExportWebsocketResponse,
|
||||||
@ -149,16 +150,6 @@ export const EntityExportModalProvider = ({
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const bannerConfig = useMemo(() => {
|
|
||||||
const isCompleted = csvExportJob?.status === 'COMPLETED';
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: isCompleted ? 'success' : 'error',
|
|
||||||
message: isCompleted ? csvExportJob?.message : csvExportJob?.error,
|
|
||||||
hasJobId: !!csvExportJob?.jobId,
|
|
||||||
};
|
|
||||||
}, [csvExportJob]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityExportModalContext.Provider value={providerValue}>
|
<EntityExportModalContext.Provider value={providerValue}>
|
||||||
<>
|
<>
|
||||||
@ -169,13 +160,13 @@ export const EntityExportModalProvider = ({
|
|||||||
open
|
open
|
||||||
cancelText={t('label.cancel')}
|
cancelText={t('label.cancel')}
|
||||||
closable={false}
|
closable={false}
|
||||||
confirmLoading={downloading}
|
|
||||||
data-testid="export-entity-modal"
|
data-testid="export-entity-modal"
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
okButtonProps={{
|
okButtonProps={{
|
||||||
form: 'export-form',
|
form: 'export-form',
|
||||||
htmlType: 'submit',
|
htmlType: 'submit',
|
||||||
id: 'submit-button',
|
id: 'submit-button',
|
||||||
|
disabled: downloading,
|
||||||
}}
|
}}
|
||||||
okText={t('label.export')}
|
okText={t('label.export')}
|
||||||
title={exportData.title ?? t('label.export')}
|
title={exportData.title ?? t('label.export')}
|
||||||
@ -186,6 +177,7 @@ export const EntityExportModalProvider = ({
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
onFinish={handleExport}>
|
onFinish={handleExport}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
className={classNames({ 'mb-0': !csvExportJob?.jobId })}
|
||||||
label={`${t('label.entity-name', {
|
label={`${t('label.entity-name', {
|
||||||
entity: t('label.file'),
|
entity: t('label.file'),
|
||||||
})}:`}
|
})}:`}
|
||||||
@ -194,11 +186,12 @@ export const EntityExportModalProvider = ({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
{bannerConfig.hasJobId && bannerConfig.message && (
|
{csvExportJob?.jobId && (
|
||||||
<Banner
|
<Banner
|
||||||
className="border-radius"
|
className="border-radius"
|
||||||
message={bannerConfig.message}
|
isLoading={downloading}
|
||||||
type={bannerConfig.type as BannerProps['type']}
|
message={csvExportJob.error ?? csvExportJob.message ?? ''}
|
||||||
|
type={csvExportJob.error ? 'error' : 'success'}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -18,17 +18,14 @@ import {
|
|||||||
} from './EntityExportModalProvider.component';
|
} from './EntityExportModalProvider.component';
|
||||||
import { ExportData } from './EntityExportModalProvider.interface';
|
import { ExportData } from './EntityExportModalProvider.interface';
|
||||||
|
|
||||||
const dummyTeamsCSV = `name*,displayName,description,teamType*,parents*,Owner,isJoinable,defaultRoles,policies
|
const mockExportJob = {
|
||||||
access table only,access table only,,Group,Organization,,true,Only table,
|
jobId: '123456',
|
||||||
Engineering,,,BusinessUnit,Organization,,true,,
|
message: 'Export initiated successfyully',
|
||||||
Finance,,,BusinessUnit,Organization,,true,,
|
};
|
||||||
Legal,,,BusinessUnit,Organization,,true,,
|
|
||||||
Applications,,,Group,Engineering,,true,,
|
|
||||||
`;
|
|
||||||
|
|
||||||
const mockShowModal: ExportData = {
|
const mockShowModal: ExportData = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
onExport: jest.fn().mockImplementation(() => Promise.resolve(dummyTeamsCSV)),
|
onExport: jest.fn().mockImplementation(() => Promise.resolve(mockExportJob)),
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConsumerComponent = () => {
|
const ConsumerComponent = () => {
|
||||||
@ -140,6 +137,7 @@ describe('EntityExportModalProvider component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(mockShowModal.onExport).toHaveBeenCalledWith(mockShowModal.name);
|
expect(mockShowModal.onExport).toHaveBeenCalledWith(mockShowModal.name);
|
||||||
expect(screen.queryByTestId('export-entity-modal')).not.toBeInTheDocument();
|
|
||||||
|
expect(await screen.findByText(mockExportJob.message)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import './banner.less';
|
import './banner.less';
|
||||||
|
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
|
import { Spin } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ReactComponent as ErrorIcon } from '../../../assets/svg/banner/ic-banner-error.svg';
|
import { ReactComponent as ErrorIcon } from '../../../assets/svg/banner/ic-banner-error.svg';
|
||||||
import { ReactComponent as SuccessIcon } from '../../../assets/svg/banner/ic-banner-success.svg';
|
import { ReactComponent as SuccessIcon } from '../../../assets/svg/banner/ic-banner-success.svg';
|
||||||
@ -8,12 +10,23 @@ import { ReactComponent as SuccessIcon } from '../../../assets/svg/banner/ic-ban
|
|||||||
export interface BannerProps extends React.HTMLAttributes<HTMLDivElement> {
|
export interface BannerProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
type: 'success' | 'error';
|
type: 'success' | 'error';
|
||||||
message: string;
|
message: string;
|
||||||
|
isLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Banner: FC<BannerProps> = ({ type, message, className }) => {
|
const Banner: FC<BannerProps> = ({ type, message, className, isLoading }) => {
|
||||||
|
const icon = type === 'success' ? <SuccessIcon /> : <ErrorIcon />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('message-banner-wrapper', type, className)}>
|
<div className={classNames('message-banner-wrapper', type, className)}>
|
||||||
{type === 'success' ? <SuccessIcon /> : <ErrorIcon />}
|
{isLoading ? (
|
||||||
|
<Spin
|
||||||
|
className={`loading-spinner-${type}`}
|
||||||
|
indicator={<LoadingOutlined spin />}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
icon
|
||||||
|
)}
|
||||||
<span className="message-banner-text">{message}</span>
|
<span className="message-banner-text">{message}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -20,4 +20,8 @@
|
|||||||
background-color: @error-bg-color;
|
background-color: @error-bg-color;
|
||||||
color: @error-color;
|
color: @error-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading-spinner-success {
|
||||||
|
color: @success-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user