diff --git a/packages/web/src/icons.js b/packages/web/src/icons.js index 16796d572..cc1c1f47c 100644 --- a/packages/web/src/icons.js +++ b/packages/web/src/icons.js @@ -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', diff --git a/packages/web/src/impexp/ImportExportConfigurator.js b/packages/web/src/impexp/ImportExportConfigurator.js index 1582aaab0..f79b4077c 100644 --- a/packages/web/src/impexp/ImportExportConfigurator.js +++ b/packages/web/src/impexp/ImportExportConfigurator.js @@ -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 ( - {direction == 'source' && } - {direction == 'target' && } + {direction == 'source' && ( + + <FontIcon icon="icon import" /> Source configuration + + )} + {direction == 'target' && ( + + <FontIcon icon="icon export" /> Target configuration + + )} x.directions.includes(direction))} name={storageTypeField} /> {(storageType == 'database' || storageType == 'query') && ( <> @@ -396,45 +414,51 @@ export default function ImportExportConfigurator({ uploadedFile = undefined, onC schemaNameField="targetSchemaName" /> - - } /> - ( - setFieldValue(`actionType_${row}`, e.target.value)} - /> - )} - /> - ( - setFieldValue(`targetName_${row}`, e.target.value)} - /> - )} - /> - - supportsPreview ? ( - { - if (e.target.checked) setPreviewSource(row); - else setPreviewSource(null); - }} + + + <FontIcon icon="icon tables" /> Map source tables/files + + + } /> + ( + setFieldValue(`actionType_${row}`, e.target.value)} /> - ) : null - } - /> - + )} + /> + ( + setFieldValue(`targetName_${row}`, e.target.value)} + /> + )} + /> + + supportsPreview ? ( + { + if (e.target.checked) setPreviewSource(row); + else setPreviewSource(null); + }} + /> + ) : null + } + /> + + {(sourceList || []).length == 0 && } + ); } diff --git a/packages/web/src/modals/ImportExportModal.js b/packages/web/src/modals/ImportExportModal.js index 0f8334151..c1ba6baf3 100644 --- a/packages/web/src/modals/ImportExportModal.js +++ b/packages/web/src/modals/ImportExportModal.js @@ -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({ }} > - Import/Export + Import/Export {busy && } @@ -166,7 +193,11 @@ export default function ImportExportModal({