Add button action allowing for export of client row selection
This commit is contained in:
parent
49420a6818
commit
ea63b9b065
|
@ -331,7 +331,9 @@ const getSelectedRowsBindings = asset => {
|
||||||
bindings = bindings.concat(
|
bindings = bindings.concat(
|
||||||
tables.map(table => ({
|
tables.map(table => ({
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: `${safeState}.${makePropSafe(table._id)}`,
|
runtimeBinding: `${safeState}.${makePropSafe(table._id)}.${makePropSafe(
|
||||||
|
"selectedRows"
|
||||||
|
)}`,
|
||||||
readableBinding: `${table._instanceName}.Selected rows`,
|
readableBinding: `${table._instanceName}.Selected rows`,
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
@ -343,7 +345,9 @@ const getSelectedRowsBindings = asset => {
|
||||||
bindings = bindings.concat(
|
bindings = bindings.concat(
|
||||||
tableBlocks.map(block => ({
|
tableBlocks.map(block => ({
|
||||||
type: "context",
|
type: "context",
|
||||||
runtimeBinding: `${safeState}.${makePropSafe(block._id + "-table")}`,
|
runtimeBinding: `${safeState}.${makePropSafe(
|
||||||
|
block._id + "-table"
|
||||||
|
)}.${makePropSafe("selectedRows")}`,
|
||||||
readableBinding: `${block._instanceName}.Selected rows`,
|
readableBinding: `${block._instanceName}.Selected rows`,
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
<script>
|
||||||
|
import { Label, Select, Body } from "@budibase/bbui"
|
||||||
|
import { tables } from "stores/backend"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
export let parameters
|
||||||
|
$: tableOptions = $tables.list || []
|
||||||
|
|
||||||
|
const FORMATS = [
|
||||||
|
{
|
||||||
|
label: "CSV",
|
||||||
|
value: "csv",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "JSON",
|
||||||
|
value: "json",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!parameters.type) {
|
||||||
|
parameters.type = "csv"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="root">
|
||||||
|
<Body size="S">
|
||||||
|
Choose the table that you would like to export your row selection from.
|
||||||
|
<br />
|
||||||
|
Please ensure you have enabled row selection in the table settings
|
||||||
|
</Body>
|
||||||
|
|
||||||
|
<div class="params">
|
||||||
|
<Label small>Table</Label>
|
||||||
|
<Select
|
||||||
|
bind:value={parameters.tableId}
|
||||||
|
options={tableOptions}
|
||||||
|
getOptionLabel={option => option.name}
|
||||||
|
getOptionValue={option => option._id}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Label small>Type</Label>
|
||||||
|
<Select bind:value={parameters.type} options={FORMATS} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.root {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(p) {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.params {
|
||||||
|
display: grid;
|
||||||
|
column-gap: var(--spacing-l);
|
||||||
|
row-gap: var(--spacing-s);
|
||||||
|
grid-template-columns: 100px 1fr;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -12,3 +12,4 @@ export { default as UpdateState } from "./UpdateState.svelte"
|
||||||
export { default as RefreshDataProvider } from "./RefreshDataProvider.svelte"
|
export { default as RefreshDataProvider } from "./RefreshDataProvider.svelte"
|
||||||
export { default as DuplicateRow } from "./DuplicateRow.svelte"
|
export { default as DuplicateRow } from "./DuplicateRow.svelte"
|
||||||
export { default as S3Upload } from "./S3Upload.svelte"
|
export { default as S3Upload } from "./S3Upload.svelte"
|
||||||
|
export { default as ExportData } from "./ExportData.svelte"
|
||||||
|
|
|
@ -80,6 +80,10 @@
|
||||||
"value": "publicUrl"
|
"value": "publicUrl"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Export Data",
|
||||||
|
"component": "ExportData"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -32,6 +32,7 @@
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
"apexcharts": "^3.22.1",
|
"apexcharts": "^3.22.1",
|
||||||
"dayjs": "^1.10.5",
|
"dayjs": "^1.10.5",
|
||||||
|
"downloadjs": "1.4.7",
|
||||||
"regexparam": "^1.3.0",
|
"regexparam": "^1.3.0",
|
||||||
"rollup-plugin-polyfill-node": "^0.8.0",
|
"rollup-plugin-polyfill-node": "^0.8.0",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
$: {
|
$: {
|
||||||
rowSelectionStore.actions.updateSelection(
|
rowSelectionStore.actions.updateSelection(
|
||||||
$component.id,
|
$component.id,
|
||||||
|
selectedRows.length ? selectedRows[0].tableId : "",
|
||||||
selectedRows.map(row => row._id)
|
selectedRows.map(row => row._id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,29 @@
|
||||||
import { writable } from "svelte/store"
|
import { get, writable } from "svelte/store"
|
||||||
|
|
||||||
const createRowSelectionStore = () => {
|
const createRowSelectionStore = () => {
|
||||||
const store = writable({})
|
const store = writable({})
|
||||||
|
|
||||||
function updateSelection(componentId, selectedRows) {
|
function updateSelection(componentId, tableId, selectedRows) {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state[componentId] = [...selectedRows]
|
state[componentId] = { tableId: tableId, selectedRows: selectedRows }
|
||||||
return state
|
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 {
|
return {
|
||||||
subscribe: store.subscribe,
|
subscribe: store.subscribe,
|
||||||
set: store.set,
|
set: store.set,
|
||||||
actions: {
|
actions: {
|
||||||
updateSelection,
|
updateSelection,
|
||||||
|
getSelection,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
|
import download from "downloadjs"
|
||||||
import {
|
import {
|
||||||
routeStore,
|
routeStore,
|
||||||
builderStore,
|
builderStore,
|
||||||
|
@ -8,6 +9,7 @@ import {
|
||||||
notificationStore,
|
notificationStore,
|
||||||
dataSourceStore,
|
dataSourceStore,
|
||||||
uploadStore,
|
uploadStore,
|
||||||
|
rowSelectionStore,
|
||||||
} from "stores"
|
} from "stores"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { ActionTypes } from "constants"
|
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 = {
|
const handlerMap = {
|
||||||
["Save Row"]: saveRowHandler,
|
["Save Row"]: saveRowHandler,
|
||||||
["Duplicate Row"]: duplicateRowHandler,
|
["Duplicate Row"]: duplicateRowHandler,
|
||||||
|
@ -254,6 +276,7 @@ const handlerMap = {
|
||||||
["Change Form Step"]: changeFormStepHandler,
|
["Change Form Step"]: changeFormStepHandler,
|
||||||
["Update State"]: updateStateHandler,
|
["Update State"]: updateStateHandler,
|
||||||
["Upload File to S3"]: s3UploadHandler,
|
["Upload File to S3"]: s3UploadHandler,
|
||||||
|
["Export Data"]: exportDataHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmTextMap = {
|
const confirmTextMap = {
|
||||||
|
|
|
@ -464,6 +464,11 @@ domutils@^2.6.0:
|
||||||
domelementtype "^2.2.0"
|
domelementtype "^2.2.0"
|
||||||
domhandler "^4.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:
|
electron-to-chromium@^1.3.896:
|
||||||
version "1.3.900"
|
version "1.3.900"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.900.tgz#5be2c5818a2a012c511b4b43e87b6ab7a296d4f5"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.900.tgz#5be2c5818a2a012c511b4b43e87b6ab7a296d4f5"
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -164,14 +164,15 @@ exports.exportRows = async ctx => {
|
||||||
ctx.request.body = {
|
ctx.request.body = {
|
||||||
query: {
|
query: {
|
||||||
oneOf: {
|
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)
|
return exports.search(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.fetchEnrichedRow = async ctx => {
|
exports.fetchEnrichedRow = async ctx => {
|
||||||
const id = ctx.params.rowId
|
const id = ctx.params.rowId
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
|
|
|
@ -145,5 +145,4 @@ exports.export = async function (ctx) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ctx.throw(400, err)
|
ctx.throw(400, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -365,7 +365,7 @@ exports.validate = async ctx => {
|
||||||
exports.exportRows = async ctx => {
|
exports.exportRows = async ctx => {
|
||||||
const db = getAppDB()
|
const db = getAppDB()
|
||||||
const table = await db.get(ctx.params.tableId)
|
const table = await db.get(ctx.params.tableId)
|
||||||
const rowIds = ctx.request.body
|
const rowIds = ctx.request.body.rows
|
||||||
let response = (
|
let response = (
|
||||||
await db.allDocs({
|
await db.allDocs({
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
@ -378,7 +378,6 @@ exports.exportRows = async ctx => {
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.fetchEnrichedRow = async ctx => {
|
exports.fetchEnrichedRow = async ctx => {
|
||||||
const db = getAppDB()
|
const db = getAppDB()
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
|
|
|
@ -253,25 +253,24 @@ router
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /api/:tableId/rows/export Export Rows
|
* @api {post} /api/:tableId/rows/exportRows Export Rows
|
||||||
* @apiName Export rows
|
* @apiName Export rows
|
||||||
* @apiGroup rows
|
* @apiGroup rows
|
||||||
* @apiPermission table write access
|
* @apiPermission table write access
|
||||||
* @apiDescription This API can export a number of provided rows
|
* @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 {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
|
* @apiParam (Body) {object[]} [rows] The row IDs which are to be exported
|
||||||
*
|
*
|
||||||
* @apiSuccess {object[]|object}
|
* @apiSuccess {object[]|object}
|
||||||
*/
|
*/
|
||||||
.post(
|
.post(
|
||||||
"/api/:tableId/rows/export",
|
"/api/:tableId/rows/exportRows",
|
||||||
paramResource("tableId"),
|
paramResource("tableId"),
|
||||||
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||||
usage,
|
usage,
|
||||||
rowController.export
|
rowController.export
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
Loading…
Reference in New Issue