Add option to customise which columns are exported in export data action
This commit is contained in:
parent
cdefcbafe2
commit
6845f86689
|
@ -1,27 +1,18 @@
|
||||||
<script>
|
<script>
|
||||||
import { Label, Select, Body } from "@budibase/bbui"
|
import { Label, Select, Body, Multiselect } from "@budibase/bbui"
|
||||||
import { findAllMatchingComponents } from "builderStore/componentUtils"
|
import {
|
||||||
|
findAllMatchingComponents,
|
||||||
|
findComponent,
|
||||||
|
} from "builderStore/componentUtils"
|
||||||
import { currentAsset } from "builderStore"
|
import { currentAsset } from "builderStore"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
import {
|
||||||
|
getDatasourceForProvider,
|
||||||
|
getSchemaForDatasource,
|
||||||
|
} from "builderStore/dataBinding"
|
||||||
|
|
||||||
export let parameters
|
export let parameters
|
||||||
|
|
||||||
$: tables = findAllMatchingComponents($currentAsset?.props, component =>
|
|
||||||
component._component.endsWith("table")
|
|
||||||
).map(table => ({
|
|
||||||
label: table._instanceName,
|
|
||||||
value: table._id,
|
|
||||||
}))
|
|
||||||
|
|
||||||
$: tableBlocks = findAllMatchingComponents($currentAsset?.props, component =>
|
|
||||||
component._component.endsWith("tableblock")
|
|
||||||
).map(block => ({
|
|
||||||
label: block._instanceName,
|
|
||||||
value: `${block._id}-table`,
|
|
||||||
}))
|
|
||||||
|
|
||||||
$: componentOptions = tables.concat(tableBlocks)
|
|
||||||
|
|
||||||
const FORMATS = [
|
const FORMATS = [
|
||||||
{
|
{
|
||||||
label: "CSV",
|
label: "CSV",
|
||||||
|
@ -33,6 +24,32 @@
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
$: tables = findAllMatchingComponents($currentAsset?.props, component =>
|
||||||
|
component._component.endsWith("table")
|
||||||
|
).map(table => ({
|
||||||
|
label: table._instanceName,
|
||||||
|
value: table._id,
|
||||||
|
}))
|
||||||
|
$: tableBlocks = findAllMatchingComponents($currentAsset?.props, component =>
|
||||||
|
component._component.endsWith("tableblock")
|
||||||
|
).map(block => ({
|
||||||
|
label: block._instanceName,
|
||||||
|
value: `${block._id}-table`,
|
||||||
|
}))
|
||||||
|
$: componentOptions = tables.concat(tableBlocks)
|
||||||
|
$: columnOptions = getColumnOptions(parameters.tableComponentId)
|
||||||
|
|
||||||
|
const getColumnOptions = tableId => {
|
||||||
|
// Strip block suffix if block component
|
||||||
|
if (tableId?.includes("-")) {
|
||||||
|
tableId = tableId.split("-")[0]
|
||||||
|
}
|
||||||
|
const selectedTable = findComponent($currentAsset?.props, tableId)
|
||||||
|
const datasource = getDatasourceForProvider($currentAsset, selectedTable)
|
||||||
|
const { schema } = getSchemaForDatasource($currentAsset, datasource)
|
||||||
|
return Object.keys(schema || {})
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (!parameters.type) {
|
if (!parameters.type) {
|
||||||
parameters.type = "csv"
|
parameters.type = "csv"
|
||||||
|
@ -53,10 +70,16 @@
|
||||||
<Select
|
<Select
|
||||||
bind:value={parameters.tableComponentId}
|
bind:value={parameters.tableComponentId}
|
||||||
options={componentOptions}
|
options={componentOptions}
|
||||||
|
on:change={() => (parameters.columns = [])}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Label small>Export as</Label>
|
<Label small>Export as</Label>
|
||||||
<Select bind:value={parameters.type} options={FORMATS} />
|
<Select bind:value={parameters.type} options={FORMATS} />
|
||||||
|
<Label small>Export columns</Label>
|
||||||
|
<Multiselect
|
||||||
|
placeholder="All columns"
|
||||||
|
bind:value={parameters.columns}
|
||||||
|
options={columnOptions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -80,7 +103,7 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
column-gap: var(--spacing-xs);
|
column-gap: var(--spacing-xs);
|
||||||
row-gap: var(--spacing-s);
|
row-gap: var(--spacing-s);
|
||||||
grid-template-columns: 70px 1fr;
|
grid-template-columns: 90px 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -270,6 +270,7 @@ const exportDataHandler = async action => {
|
||||||
tableId: selection.tableId,
|
tableId: selection.tableId,
|
||||||
rows: selection.selectedRows,
|
rows: selection.selectedRows,
|
||||||
format: action.parameters.type,
|
format: action.parameters.type,
|
||||||
|
columns: action.parameters.columns,
|
||||||
})
|
})
|
||||||
download(data, `${selection.tableId}.${action.parameters.type}`)
|
download(data, `${selection.tableId}.${action.parameters.type}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -65,12 +65,15 @@ export const buildRowEndpoints = API => ({
|
||||||
* Exports rows.
|
* Exports rows.
|
||||||
* @param tableId the table ID to export the rows from
|
* @param tableId the table ID to export the rows from
|
||||||
* @param rows the array of rows to export
|
* @param rows the array of rows to export
|
||||||
|
* @param format the format to export (csv or json)
|
||||||
|
* @param columns which columns to export (all if undefined)
|
||||||
*/
|
*/
|
||||||
exportRows: async ({ tableId, rows, format }) => {
|
exportRows: async ({ tableId, rows, format, columns }) => {
|
||||||
return await API.post({
|
return await API.post({
|
||||||
url: `/api/${tableId}/rows/exportRows?format=${format}`,
|
url: `/api/${tableId}/rows/exportRows?format=${format}`,
|
||||||
body: {
|
body: {
|
||||||
rows,
|
rows,
|
||||||
|
columns,
|
||||||
},
|
},
|
||||||
parseResponse: async response => {
|
parseResponse: async response => {
|
||||||
return await response.text()
|
return await response.text()
|
||||||
|
|
|
@ -157,7 +157,8 @@ exports.validate = async () => {
|
||||||
exports.exportRows = async ctx => {
|
exports.exportRows = async ctx => {
|
||||||
const { datasourceId } = breakExternalTableId(ctx.params.tableId)
|
const { datasourceId } = breakExternalTableId(ctx.params.tableId)
|
||||||
const db = getAppDB()
|
const db = getAppDB()
|
||||||
let format = ctx.query.format
|
const format = ctx.query.format
|
||||||
|
const { columns } = ctx.request.body
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
if (!datasource || !datasource.entities) {
|
if (!datasource || !datasource.entities) {
|
||||||
ctx.throw(400, "Datasource has not been configured for plus API.")
|
ctx.throw(400, "Datasource has not been configured for plus API.")
|
||||||
|
@ -171,13 +172,27 @@ exports.exportRows = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await exports.search(ctx)
|
let result = await exports.search(ctx)
|
||||||
let headers = Object.keys(result.rows[0])
|
let rows = []
|
||||||
|
|
||||||
|
// Filter data to only specified columns if required
|
||||||
|
if (columns && columns.length) {
|
||||||
|
for (let i = 0; i < result.rows.length; i++) {
|
||||||
|
rows[i] = {}
|
||||||
|
for (let column of columns) {
|
||||||
|
rows[i][column] = result.rows[i][column]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rows = result.rows
|
||||||
|
}
|
||||||
|
|
||||||
|
let headers = Object.keys(rows[0])
|
||||||
const exporter = exporters[format]
|
const exporter = exporters[format]
|
||||||
const filename = `export.${format}`
|
const filename = `export.${format}`
|
||||||
|
|
||||||
// send down the file
|
// send down the file
|
||||||
ctx.attachment(filename)
|
ctx.attachment(filename)
|
||||||
return apiFileReturn(exporter(headers, result.rows))
|
return apiFileReturn(exporter(headers, rows))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchEnrichedRow = async ctx => {
|
exports.fetchEnrichedRow = async ctx => {
|
||||||
|
|
Loading…
Reference in New Issue