+
+ Choose the table that you would like to export your row selection from.
+
+ Please ensure you have enabled row selection in the table settings
+
+
+
+
+
+
+
+
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/index.js b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/index.js
index 416ebffb1a..6593c9cbd4 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/index.js
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/actions/index.js
@@ -12,3 +12,4 @@ export { default as UpdateState } from "./UpdateState.svelte"
export { default as RefreshDataProvider } from "./RefreshDataProvider.svelte"
export { default as DuplicateRow } from "./DuplicateRow.svelte"
export { default as S3Upload } from "./S3Upload.svelte"
+export { default as ExportData } from "./ExportData.svelte"
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/manifest.json b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/manifest.json
index ecbf0d8065..0f6d3344b2 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/manifest.json
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ButtonActionEditor/manifest.json
@@ -80,6 +80,10 @@
"value": "publicUrl"
}
]
+ },
+ {
+ "name": "Export Data",
+ "component": "ExportData"
}
]
}
\ No newline at end of file
diff --git a/packages/client/package.json b/packages/client/package.json
index 509020a5d1..4bf70e4d56 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -32,6 +32,7 @@
"@spectrum-css/vars": "^3.0.1",
"apexcharts": "^3.22.1",
"dayjs": "^1.10.5",
+ "downloadjs": "1.4.7",
"regexparam": "^1.3.0",
"rollup-plugin-polyfill-node": "^0.8.0",
"shortid": "^2.2.15",
diff --git a/packages/client/src/components/app/table/Table.svelte b/packages/client/src/components/app/table/Table.svelte
index f5f591688f..e8dcd30929 100644
--- a/packages/client/src/components/app/table/Table.svelte
+++ b/packages/client/src/components/app/table/Table.svelte
@@ -42,6 +42,7 @@
$: {
rowSelectionStore.actions.updateSelection(
$component.id,
+ selectedRows.length ? selectedRows[0].tableId : "",
selectedRows.map(row => row._id)
)
}
diff --git a/packages/client/src/stores/rowSelection.js b/packages/client/src/stores/rowSelection.js
index 3d1f2038aa..4561557c6f 100644
--- a/packages/client/src/stores/rowSelection.js
+++ b/packages/client/src/stores/rowSelection.js
@@ -1,20 +1,29 @@
-import { writable } from "svelte/store"
+import { get, writable } from "svelte/store"
const createRowSelectionStore = () => {
const store = writable({})
- function updateSelection(componentId, selectedRows) {
+ function updateSelection(componentId, tableId, selectedRows) {
store.update(state => {
- state[componentId] = [...selectedRows]
+ state[componentId] = { tableId: tableId, selectedRows: selectedRows }
return state
})
}
+ function getSelection(tableId) {
+ const selection = get(store)
+ const componentId = Object.keys(selection).find(
+ componentId => selection[componentId].tableId === tableId
+ )
+ return componentId ? selection[componentId] : {}
+ }
+
return {
subscribe: store.subscribe,
set: store.set,
actions: {
updateSelection,
+ getSelection,
},
}
}
diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js
index fec966725b..5d0f4ff71e 100644
--- a/packages/client/src/utils/buttonActions.js
+++ b/packages/client/src/utils/buttonActions.js
@@ -1,4 +1,5 @@
import { get } from "svelte/store"
+import download from "downloadjs"
import {
routeStore,
builderStore,
@@ -8,6 +9,7 @@ import {
notificationStore,
dataSourceStore,
uploadStore,
+ rowSelectionStore,
} from "stores"
import { API } from "api"
import { ActionTypes } from "constants"
@@ -239,6 +241,26 @@ const s3UploadHandler = async action => {
}
}
+const exportDataHandler = async action => {
+ let selection = rowSelectionStore.actions.getSelection(
+ action.parameters.tableId
+ )
+ if (selection.selectedRows && selection.selectedRows.length > 0) {
+ try {
+ const data = await API.exportRows({
+ tableId: selection.tableId,
+ rows: selection.selectedRows,
+ })
+
+ download(JSON.stringify(data), `export.${action.parameters.type}`)
+ } catch (error) {
+ notificationStore.actions.error("There was an error exporting the data")
+ }
+ } else {
+ notificationStore.actions.error("Please select at least one row")
+ }
+}
+
const handlerMap = {
["Save Row"]: saveRowHandler,
["Duplicate Row"]: duplicateRowHandler,
@@ -254,6 +276,7 @@ const handlerMap = {
["Change Form Step"]: changeFormStepHandler,
["Update State"]: updateStateHandler,
["Upload File to S3"]: s3UploadHandler,
+ ["Export Data"]: exportDataHandler,
}
const confirmTextMap = {
diff --git a/packages/client/yarn.lock b/packages/client/yarn.lock
index 54d3ae755f..405591fd90 100644
--- a/packages/client/yarn.lock
+++ b/packages/client/yarn.lock
@@ -464,6 +464,11 @@ domutils@^2.6.0:
domelementtype "^2.2.0"
domhandler "^4.2.0"
+downloadjs@1.4.7:
+ version "1.4.7"
+ resolved "https://registry.yarnpkg.com/downloadjs/-/downloadjs-1.4.7.tgz#f69f96f940e0d0553dac291139865a3cd0101e3c"
+ integrity sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=
+
electron-to-chromium@^1.3.896:
version "1.3.900"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.900.tgz#5be2c5818a2a012c511b4b43e87b6ab7a296d4f5"
diff --git a/packages/frontend-core/src/api/rows.js b/packages/frontend-core/src/api/rows.js
index 553cf8e0de..1b6efe624f 100644
--- a/packages/frontend-core/src/api/rows.js
+++ b/packages/frontend-core/src/api/rows.js
@@ -60,4 +60,18 @@ export const buildRowEndpoints = API => ({
},
})
},
+
+ /**
+ * Exports rows.
+ * @param tableId the table ID to export the rows from
+ * @param rows the array of rows to export
+ */
+ exportRows: async ({ tableId, rows }) => {
+ return await API.post({
+ url: `/api/${tableId}/rows/exportRows`,
+ body: {
+ rows,
+ },
+ })
+ },
})
diff --git a/packages/server/src/api/controllers/row/external.js b/packages/server/src/api/controllers/row/external.js
index 5f06118acc..b7c7a2bc6e 100644
--- a/packages/server/src/api/controllers/row/external.js
+++ b/packages/server/src/api/controllers/row/external.js
@@ -164,14 +164,15 @@ exports.exportRows = async ctx => {
ctx.request.body = {
query: {
oneOf: {
- [table.primaryDisplay]: ctx.request.body.map(id => breakRowIdField(id)[0])
+ [table.primaryDisplay]: ctx.request.body.map(
+ id => breakRowIdField(id)[0]
+ ),
},
},
}
return exports.search(ctx)
}
-
exports.fetchEnrichedRow = async ctx => {
const id = ctx.params.rowId
const tableId = ctx.params.tableId
diff --git a/packages/server/src/api/controllers/row/index.js b/packages/server/src/api/controllers/row/index.js
index 839c549735..c4a9ac8f06 100644
--- a/packages/server/src/api/controllers/row/index.js
+++ b/packages/server/src/api/controllers/row/index.js
@@ -145,5 +145,4 @@ exports.export = async function (ctx) {
} catch (err) {
ctx.throw(400, err)
}
-
}
diff --git a/packages/server/src/api/controllers/row/internal.js b/packages/server/src/api/controllers/row/internal.js
index faf2d8117c..068a485a8a 100644
--- a/packages/server/src/api/controllers/row/internal.js
+++ b/packages/server/src/api/controllers/row/internal.js
@@ -365,7 +365,7 @@ exports.validate = async ctx => {
exports.exportRows = async ctx => {
const db = getAppDB()
const table = await db.get(ctx.params.tableId)
- const rowIds = ctx.request.body
+ const rowIds = ctx.request.body.rows
let response = (
await db.allDocs({
include_docs: true,
@@ -378,7 +378,6 @@ exports.exportRows = async ctx => {
return rows
}
-
exports.fetchEnrichedRow = async ctx => {
const db = getAppDB()
const tableId = ctx.params.tableId
diff --git a/packages/server/src/api/routes/row.js b/packages/server/src/api/routes/row.js
index 7978bac3c7..272b7d168d 100644
--- a/packages/server/src/api/routes/row.js
+++ b/packages/server/src/api/routes/row.js
@@ -253,25 +253,24 @@ router
)
/**
- * @api {post} /api/:tableId/rows/export Export Rows
- * @apiName Export rows
- * @apiGroup rows
- * @apiPermission table write access
- * @apiDescription This API can export a number of provided rows
- *
- * @apiParam {string} tableId The ID of the table the row is to be deleted from.
- *
- * @apiParam (Body) {object[]} [rows] The row IDs which are to be exported
- *
- * @apiSuccess {object[]|object}
- */
+ * @api {post} /api/:tableId/rows/exportRows Export Rows
+ * @apiName Export rows
+ * @apiGroup rows
+ * @apiPermission table write access
+ * @apiDescription This API can export a number of provided rows
+ *
+ * @apiParam {string} tableId The ID of the table the row is to be deleted from.
+ *
+ * @apiParam (Body) {object[]} [rows] The row IDs which are to be exported
+ *
+ * @apiSuccess {object[]|object}
+ */
.post(
- "/api/:tableId/rows/export",
+ "/api/:tableId/rows/exportRows",
paramResource("tableId"),
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
usage,
rowController.export
)
-
module.exports = router