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(
|
||||
tables.map(table => ({
|
||||
type: "context",
|
||||
runtimeBinding: `${safeState}.${makePropSafe(table._id)}`,
|
||||
runtimeBinding: `${safeState}.${makePropSafe(table._id)}.${makePropSafe(
|
||||
"selectedRows"
|
||||
)}`,
|
||||
readableBinding: `${table._instanceName}.Selected rows`,
|
||||
}))
|
||||
)
|
||||
|
@ -343,7 +345,9 @@ const getSelectedRowsBindings = asset => {
|
|||
bindings = bindings.concat(
|
||||
tableBlocks.map(block => ({
|
||||
type: "context",
|
||||
runtimeBinding: `${safeState}.${makePropSafe(block._id + "-table")}`,
|
||||
runtimeBinding: `${safeState}.${makePropSafe(
|
||||
block._id + "-table"
|
||||
)}.${makePropSafe("selectedRows")}`,
|
||||
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 DuplicateRow } from "./DuplicateRow.svelte"
|
||||
export { default as S3Upload } from "./S3Upload.svelte"
|
||||
export { default as ExportData } from "./ExportData.svelte"
|
||||
|
|
|
@ -80,6 +80,10 @@
|
|||
"value": "publicUrl"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Export Data",
|
||||
"component": "ExportData"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
$: {
|
||||
rowSelectionStore.actions.updateSelection(
|
||||
$component.id,
|
||||
selectedRows.length ? selectedRows[0].tableId : "",
|
||||
selectedRows.map(row => row._id)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 = {
|
||||
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
|
||||
|
|
|
@ -145,5 +145,4 @@ exports.export = async function (ctx) {
|
|||
} catch (err) {
|
||||
ctx.throw(400, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue