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