diff --git a/packages/api/src/controllers/archive.js b/packages/api/src/controllers/archive.js
index 55869487e..c3768e114 100644
--- a/packages/api/src/controllers/archive.js
+++ b/packages/api/src/controllers/archive.js
@@ -1,7 +1,11 @@
const fs = require('fs-extra');
+const stream = require('stream');
+const readline = require('readline');
const path = require('path');
+const { formatWithOptions } = require('util');
const { archivedir } = require('../utility/directories');
const socket = require('../utility/socket');
+const JsonLinesDatastore = require('../utility/JsonLinesDatastore');
module.exports = {
folders_meta: 'get',
@@ -61,4 +65,37 @@ module.exports = {
await fs.rmdir(path.join(archivedir(), folder), { recursive: true });
socket.emitChanged(`archive-folders-changed`);
},
+
+ saveFreeTable_meta: 'post',
+ async saveFreeTable({ folder, file, data }) {
+ const { structure, rows } = data;
+ const fileStream = fs.createWriteStream(path.join(archivedir(), folder, `${file}.jsonl`));
+ await fileStream.write(JSON.stringify(structure) + '\n');
+ for (const row of rows) {
+ await fileStream.write(JSON.stringify(row) + '\n');
+ }
+ await fileStream.close();
+ return true;
+ },
+
+ loadFreeTable_meta: 'post',
+ async loadFreeTable({ folder, file }) {
+ return new Promise((resolve, reject) => {
+ const fileStream = fs.createReadStream(path.join(archivedir(), folder, `${file}.jsonl`));
+ const liner = readline.createInterface({
+ input: fileStream,
+ });
+ let structure = null;
+ const rows = [];
+ liner.on('line', (line) => {
+ const data = JSON.parse(line);
+ if (structure) rows.push(data);
+ else structure = data;
+ });
+ liner.on('close', () => {
+ resolve({ structure, rows });
+ fileStream.close();
+ });
+ });
+ },
};
diff --git a/packages/datalib/src/FreeTableGridDisplay.ts b/packages/datalib/src/FreeTableGridDisplay.ts
index 9652790d8..5b7ac48a4 100644
--- a/packages/datalib/src/FreeTableGridDisplay.ts
+++ b/packages/datalib/src/FreeTableGridDisplay.ts
@@ -14,8 +14,8 @@ export class FreeTableGridDisplay extends GridDisplay {
) {
super(config, setConfig, cache, setCache);
this.columns = this.getDisplayColumns(model);
- this.filterable = true;
- this.sortable = true;
+ this.filterable = false;
+ this.sortable = false;
this.editable = true;
}
diff --git a/packages/web/src/appobj/archiveFileAppObject.js b/packages/web/src/appobj/archiveFileAppObject.js
index 82a6886b6..d6b2e2fd7 100644
--- a/packages/web/src/appobj/archiveFileAppObject.js
+++ b/packages/web/src/appobj/archiveFileAppObject.js
@@ -7,13 +7,46 @@ import { openNewTab } from '../utility/common';
import { filterName } from '@dbgate/datalib';
import axios from '../utility/axios';
+function openArchive(setOpenedTabs, fileName, folderName) {
+ openNewTab(setOpenedTabs, {
+ title: fileName,
+ icon: 'archtable.svg',
+ tooltip: `${folderName}\n${fileName}`,
+ tabComponent: 'ArchiveFileTab',
+ props: {
+ archiveFile: fileName,
+ archiveFolder: folderName,
+ },
+ });
+}
+
function Menu({ data, setOpenedTabs }) {
const handleDelete = () => {
axios.post('archive/delete-file', { file: data.fileName, folder: data.folderName });
// setOpenedTabs((tabs) => tabs.filter((x) => x.tabid != data.tabid));
};
+ const handleOpenRead = () => {
+ openArchive(setOpenedTabs, data.fileName, data.folderName);
+ };
+ const handleOpenWrite = async () => {
+ const resp = await axios.post('archive/load-free-table', { file: data.fileName, folder: data.folderName });
+
+ openNewTab(setOpenedTabs, {
+ title: data.fileName,
+ icon: 'freetable.svg',
+ tabComponent: 'FreeTableTab',
+ props: {
+ initialData: resp.data,
+ archiveFile: data.fileName,
+ archiveFolder: data.folderName,
+ },
+ });
+ };
+
return (
<>
+ Open (readonly)
+ Open in free table editor
Delete
>
);
@@ -24,16 +57,7 @@ const archiveFileAppObject = () => ({ fileName, folderName }, { setOpenedTabs })
// const Icon = (props) => ;
const Icon = ArchiveTableIcon;
const onClick = () => {
- openNewTab(setOpenedTabs, {
- title: fileName,
- icon: 'archtable.svg',
- tooltip: `${folderName}\n${fileName}`,
- tabComponent: 'ArchiveFileTab',
- props: {
- archiveFile: fileName,
- archiveFolder: folderName,
- },
- });
+ openArchive(setOpenedTabs, fileName, folderName);
};
const matcher = (filter) => filterName(filter, fileName);
diff --git a/packages/web/src/freetable/useNewFreeTable.js b/packages/web/src/freetable/useNewFreeTable.js
index 37a667110..9cbb8daf9 100644
--- a/packages/web/src/freetable/useNewFreeTable.js
+++ b/packages/web/src/freetable/useNewFreeTable.js
@@ -1,5 +1,5 @@
import _ from 'lodash';
-import { useSetOpenedTabs, useCurrentDatabase } from '../utility/globalState';
+import { useSetOpenedTabs } from '../utility/globalState';
import { openNewTab } from '../utility/common';
export default function useNewFreeTable() {
@@ -10,6 +10,6 @@ export default function useNewFreeTable() {
title: title || 'Table',
icon: 'freetable.svg',
tabComponent: 'FreeTableTab',
- props: {},
+ props,
});
}
diff --git a/packages/web/src/modals/SaveArchiveModal.js b/packages/web/src/modals/SaveArchiveModal.js
new file mode 100644
index 000000000..511b9312f
--- /dev/null
+++ b/packages/web/src/modals/SaveArchiveModal.js
@@ -0,0 +1,44 @@
+import React from 'react';
+import ModalBase from './ModalBase';
+import { FormTextField, FormSubmit, FormArchiveFolderSelect, FormRow, FormLabel } from '../utility/forms';
+import { Formik, Form } from 'formik';
+import styled from 'styled-components';
+import ModalHeader from './ModalHeader';
+import ModalContent from './ModalContent';
+import ModalFooter from './ModalFooter';
+
+const SelectWrapper = styled.div`
+ width: 150px;
+ position: relative;
+ flex: 1;
+`;
+
+export default function SaveArchiveModal({ file = 'new-table', folder = 'default', modalState, onSave }) {
+ const handleSubmit = async (values) => {
+ const { file, folder } = values;
+ modalState.close();
+ if (onSave) onSave(folder, file);
+ };
+ return (
+
+ Save to archive
+
+
+
+
+ );
+}
diff --git a/packages/web/src/tabs/FreeTableTab.js b/packages/web/src/tabs/FreeTableTab.js
index 89185047d..a54bd35b1 100644
--- a/packages/web/src/tabs/FreeTableTab.js
+++ b/packages/web/src/tabs/FreeTableTab.js
@@ -2,15 +2,21 @@ import React from 'react';
import { createGridCache, createChangeSet, createGridConfig, createFreeTableModel } from '@dbgate/datalib';
import useUndoReducer from '../utility/useUndoReducer';
import usePropsCompare from '../utility/usePropsCompare';
-import { useUpdateDatabaseForTab } from '../utility/globalState';
+import { useSetOpenedTabs, useUpdateDatabaseForTab } from '../utility/globalState';
import TableDataGrid from '../datagrid/TableDataGrid';
import useGridConfig from '../utility/useGridConfig';
import FreeTableGrid from '../freetable/FreeTableGrid';
+import SaveArchiveModal from '../modals/SaveArchiveModal';
+import useModalState from '../modals/useModalState';
+import axios from '../utility/axios';
+import { changeTab } from '../utility/common';
-export default function FreeDataTab({ conid, database, schemaName, pureName, tabVisible, toolbarPortalRef, tabid }) {
+export default function FreeDataTab({ archiveFolder, archiveFile, tabVisible, toolbarPortalRef, tabid, initialData }) {
const [config, setConfig] = useGridConfig(tabid);
- const [modelState, dispatchModel] = useUndoReducer(createFreeTableModel());
+ const [modelState, dispatchModel] = useUndoReducer(initialData || createFreeTableModel());
const storageKey = `tabdata_freetable_${tabid}`;
+ const saveSqlFileModalState = useModalState();
+ const setOpenedTabs = useSetOpenedTabs();
React.useEffect(() => {
const existingData = localStorage.getItem(storageKey);
@@ -25,15 +31,32 @@ export default function FreeDataTab({ conid, database, schemaName, pureName, tab
localStorage.setItem(storageKey, JSON.stringify(modelState.value));
}, [modelState]);
+ const handleSave = async (folder, file) => {
+ await axios.post('archive/save-free-table', { folder, file, data: modelState.value });
+ changeTab(tabid, setOpenedTabs, (tab) => ({
+ ...tab,
+ title: file,
+ props: { archiveFIle: file, archiveFolder: folder },
+ }));
+ };
+
return (
-
+ <>
+ saveSqlFileModalState.open()}
+ />
+
+ >
);
}
diff --git a/packages/web/src/utility/forms.js b/packages/web/src/utility/forms.js
index 9079c3dbe..7020c8238 100644
--- a/packages/web/src/utility/forms.js
+++ b/packages/web/src/utility/forms.js
@@ -196,7 +196,7 @@ export function FormArchiveFilesSelect({ folderName, name }) {
return ;
}
-export function FormArchiveFolderSelect({ name }) {
+export function FormArchiveFolderSelect({ name, ...other }) {
const { setFieldValue } = useFormikContext();
const folders = useArchiveFolders();
const folderOptions = React.useMemo(
@@ -214,6 +214,12 @@ export function FormArchiveFolderSelect({ name }) {
};
return (
-
+
);
}