mirror of
https://github.com/dbgate/dbgate
synced 2024-11-08 04:35:58 +00:00
import export- cancelable, better design
This commit is contained in:
parent
eaf45d8768
commit
801bf05a31
@ -6,8 +6,10 @@ const iconNames = {
|
||||
'icon plus-box': 'mdi mdi-plus-box-outline',
|
||||
'icon invisible-box': 'mdi mdi-minus-box-outline icon-invisible',
|
||||
'icon cloud-upload': 'mdi mdi-cloud-upload',
|
||||
'icon import': 'mdi mdi-file-upload',
|
||||
'icon import': 'mdi mdi-application-import',
|
||||
'icon export': 'mdi mdi-application-export',
|
||||
'icon new-connection': 'mdi mdi-database-plus',
|
||||
'icon tables': 'mdi mdi-table-multiple',
|
||||
|
||||
'icon database': 'mdi mdi-database',
|
||||
'icon server': 'mdi mdi-server',
|
||||
|
@ -36,6 +36,10 @@ const Wrapper = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const SourceListWrapper = styled.div`
|
||||
margin: 10px;
|
||||
`;
|
||||
|
||||
const Column = styled.div`
|
||||
margin: 10px;
|
||||
flex: 1;
|
||||
@ -77,6 +81,12 @@ const ArrowWrapper = styled.div`
|
||||
align-self: center;
|
||||
`;
|
||||
|
||||
const Title = styled.div`
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
margin: 10px 0px;
|
||||
`;
|
||||
|
||||
function getFileFilters(storageType) {
|
||||
const res = [];
|
||||
if (storageType == 'csv') res.push({ name: 'CSV files', extensions: ['csv'] });
|
||||
@ -194,8 +204,16 @@ function SourceTargetConfig({
|
||||
const archiveFiles = useArchiveFiles({ folder: values[archiveFolderField] });
|
||||
return (
|
||||
<Column>
|
||||
{direction == 'source' && <Label theme={theme}>Source configuration</Label>}
|
||||
{direction == 'target' && <Label theme={theme}>Target configuration</Label>}
|
||||
{direction == 'source' && (
|
||||
<Title theme={theme}>
|
||||
<FontIcon icon="icon import" /> Source configuration
|
||||
</Title>
|
||||
)}
|
||||
{direction == 'target' && (
|
||||
<Title theme={theme}>
|
||||
<FontIcon icon="icon export" /> Target configuration
|
||||
</Title>
|
||||
)}
|
||||
<FormReactSelect options={types.filter((x) => x.directions.includes(direction))} name={storageTypeField} />
|
||||
{(storageType == 'database' || storageType == 'query') && (
|
||||
<>
|
||||
@ -396,45 +414,51 @@ export default function ImportExportConfigurator({ uploadedFile = undefined, onC
|
||||
schemaNameField="targetSchemaName"
|
||||
/>
|
||||
</Wrapper>
|
||||
<TableControl rows={sourceList || []}>
|
||||
<TableColumn fieldName="source" header="Source" formatter={(row) => <SourceName name={row} />} />
|
||||
<TableColumn
|
||||
fieldName="action"
|
||||
header="Action"
|
||||
formatter={(row) => (
|
||||
<SelectField
|
||||
options={getActionOptions(row, values, targetDbinfo)}
|
||||
value={values[`actionType_${row}`] || getActionOptions(row, values, targetDbinfo)[0].value}
|
||||
onChange={(e) => setFieldValue(`actionType_${row}`, e.target.value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<TableColumn
|
||||
fieldName="target"
|
||||
header="Target"
|
||||
formatter={(row) => (
|
||||
<TextField
|
||||
value={getTargetName(row, values)}
|
||||
onChange={(e) => setFieldValue(`targetName_${row}`, e.target.value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<TableColumn
|
||||
fieldName="preview"
|
||||
header="Preview"
|
||||
formatter={(row) =>
|
||||
supportsPreview ? (
|
||||
<CheckboxField
|
||||
checked={previewSource == row}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) setPreviewSource(row);
|
||||
else setPreviewSource(null);
|
||||
}}
|
||||
<SourceListWrapper>
|
||||
<Title>
|
||||
<FontIcon icon="icon tables" /> Map source tables/files
|
||||
</Title>
|
||||
<TableControl rows={sourceList || []}>
|
||||
<TableColumn fieldName="source" header="Source" formatter={(row) => <SourceName name={row} />} />
|
||||
<TableColumn
|
||||
fieldName="action"
|
||||
header="Action"
|
||||
formatter={(row) => (
|
||||
<SelectField
|
||||
options={getActionOptions(row, values, targetDbinfo)}
|
||||
value={values[`actionType_${row}`] || getActionOptions(row, values, targetDbinfo)[0].value}
|
||||
onChange={(e) => setFieldValue(`actionType_${row}`, e.target.value)}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</TableControl>
|
||||
)}
|
||||
/>
|
||||
<TableColumn
|
||||
fieldName="target"
|
||||
header="Target"
|
||||
formatter={(row) => (
|
||||
<TextField
|
||||
value={getTargetName(row, values)}
|
||||
onChange={(e) => setFieldValue(`targetName_${row}`, e.target.value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<TableColumn
|
||||
fieldName="preview"
|
||||
header="Preview"
|
||||
formatter={(row) =>
|
||||
supportsPreview ? (
|
||||
<CheckboxField
|
||||
checked={previewSource == row}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) setPreviewSource(row);
|
||||
else setPreviewSource(null);
|
||||
}}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</TableControl>
|
||||
{(sourceList || []).length == 0 && <ErrorInfo message="No source tables/files" icon="img alert" />}
|
||||
</SourceListWrapper>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ import SocketMessagesView from '../query/SocketMessagesView';
|
||||
import RunnerOutputFiles from '../query/RunnerOuputFiles';
|
||||
import useTheme from '../theme/useTheme';
|
||||
import PreviewDataGrid from '../impexp/PreviewDataGrid';
|
||||
import useSocket from '../utility/SocketProvider';
|
||||
import LoadingInfo from '../widgets/LoadingInfo';
|
||||
import { FontIcon } from '../icons';
|
||||
|
||||
const headerHeight = '60px';
|
||||
const footerHeight = '60px';
|
||||
@ -115,8 +118,25 @@ export default function ImportExportModal({
|
||||
const theme = useTheme();
|
||||
const [previewReader, setPreviewReader] = React.useState(0);
|
||||
const targetArchiveFolder = importToArchive ? `import-${moment().format('YYYY-MM-DD-hh-mm-ss')}` : archive;
|
||||
const socket = useSocket();
|
||||
|
||||
const [busy, setBusy] = React.useState(false);
|
||||
|
||||
const handleRunnerDone = React.useCallback(() => {
|
||||
setBusy(false);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (runnerId && socket) {
|
||||
socket.on(`runner-done-${runnerId}`, handleRunnerDone);
|
||||
return () => {
|
||||
socket.off(`runner-done-${runnerId}`, handleRunnerDone);
|
||||
};
|
||||
}
|
||||
}, [runnerId, socket]);
|
||||
|
||||
const handleExecute = async (values) => {
|
||||
if (busy) return;
|
||||
const script = await createImpExpScript(values);
|
||||
|
||||
setExecuteNumber((num) => num + 1);
|
||||
@ -125,6 +145,13 @@ export default function ImportExportModal({
|
||||
const resp = await axios.post('runners/start', { script });
|
||||
runid = resp.data.runid;
|
||||
setRunnerId(runid);
|
||||
setBusy(true);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
axios.post('runners/cancel', {
|
||||
runid: runnerId,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@ -140,7 +167,7 @@ export default function ImportExportModal({
|
||||
}}
|
||||
>
|
||||
<StyledForm>
|
||||
<ModalHeader modalState={modalState}>Import/Export</ModalHeader>
|
||||
<ModalHeader modalState={modalState}>Import/Export {busy && <FontIcon icon="icon loading" />}</ModalHeader>
|
||||
<Wrapper>
|
||||
<ContentWrapper theme={theme}>
|
||||
<ImportExportConfigurator uploadedFile={uploadedFile} onChangePreview={setPreviewReader} />
|
||||
@ -166,7 +193,11 @@ export default function ImportExportModal({
|
||||
</Wrapper>
|
||||
<Footer theme={theme}>
|
||||
<FooterButtons>
|
||||
<FormStyledButton type="submit" value="Run" />
|
||||
{busy ? (
|
||||
<FormStyledButton type="button" value="Cancel" onClick={handleCancel} />
|
||||
) : (
|
||||
<FormStyledButton type="submit" value="Run" />
|
||||
)}
|
||||
<GenerateSctriptButton modalState={modalState} />
|
||||
<FormStyledButton type="button" value="Close" onClick={modalState.close} />
|
||||
</FooterButtons>
|
||||
|
@ -6,6 +6,7 @@ import TableControl, { TableColumn } from '../utility/TableControl';
|
||||
import formatFileSize from '../utility/formatFileSize';
|
||||
import resolveApi from '../utility/resolveApi';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import ErrorInfo from '../widgets/ErrorInfo';
|
||||
|
||||
export default function RunnerOutputFiles({ runnerId, executeNumber }) {
|
||||
const socket = useSocket();
|
||||
@ -31,6 +32,10 @@ export default function RunnerOutputFiles({ runnerId, executeNumber }) {
|
||||
|
||||
const electron = getElectron();
|
||||
|
||||
if (!files || files.length == 0) {
|
||||
return <ErrorInfo message="No output files" icon="img alert" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<TableControl rows={files}>
|
||||
<TableColumn fieldName="name" header="Name" />
|
||||
|
@ -2,6 +2,7 @@ import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import MessagesView from './MessagesView';
|
||||
import useSocket from '../utility/SocketProvider';
|
||||
import ErrorInfo from '../widgets/ErrorInfo';
|
||||
|
||||
export default function SocketMessagesView({
|
||||
eventName,
|
||||
@ -41,6 +42,10 @@ export default function SocketMessagesView({
|
||||
}
|
||||
}, [eventName, socket]);
|
||||
|
||||
if (!displayedMessages || displayedMessages.length == 0) {
|
||||
return <ErrorInfo message="No messages" icon="img alert" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<MessagesView
|
||||
items={displayedMessages}
|
||||
|
Loading…
Reference in New Issue
Block a user