Add bulk row duplication to tables using throttled save row calls

This commit is contained in:
Andrew Kingston 2024-06-21 08:08:19 +01:00
parent f86c80af32
commit 64cc3efc2a
No known key found for this signature in database
6 changed files with 95 additions and 15 deletions

View File

@ -54,7 +54,10 @@
const newRowIndex = offset ? undefined : 0
let rowToCreate = { ...newRow }
delete rowToCreate._isNewRow
const savedRow = await rows.actions.addRow(rowToCreate, newRowIndex)
const savedRow = await rows.actions.addRow({
row: rowToCreate,
idx: newRowIndex,
})
if (savedRow) {
// Reset state
clear()

View File

@ -21,6 +21,7 @@
notifications,
hasBudibaseIdentifiers,
selectedRowCount,
selectedRows,
} = getContext("grid")
let anchor
@ -51,6 +52,18 @@
}
}
const bulkDuplicate = async () => {
menu.actions.close()
const rowsToDuplicate = Object.keys($selectedRows).map(id => {
return rows.actions.getRow(id)
})
const newRows = await rows.actions.bulkDuplicate(rowsToDuplicate)
if (newRows[0]) {
const column = $stickyColumn?.name || $columns[0].name
$focusedCellId = getCellID(newRows[0]._id, column)
}
}
const copyToClipboard = async value => {
await Helpers.copyToClipboard(value)
$notifications.success("Copied to clipboard")
@ -66,8 +79,8 @@
{#if $menu.multiRowMode}
<MenuItem
icon="Duplicate"
disabled={isNewRow || !$config.canAddRows}
on:click={duplicate}
disabled={!$config.canAddRows}
on:click={bulkDuplicate}
>
Duplicate {$selectedRowCount} rows
</MenuItem>

View File

@ -10,7 +10,10 @@ export const createActions = context => {
}
const saveRow = async row => {
row.tableId = get(datasource)?.tableId
row = {
...row,
tableId: get(datasource)?.tableId,
}
return await API.saveRow(row, SuppressErrors)
}

View File

@ -11,8 +11,11 @@ export const createActions = context => {
const saveRow = async row => {
const $datasource = get(datasource)
row.tableId = $datasource?.tableId
row._viewId = $datasource?.id
row = {
...row,
tableId: $datasource?.tableId,
_viewId: $datasource?.id,
}
return {
...(await API.saveRow(row, SuppressErrors)),
_viewId: row._viewId,

View File

@ -4,6 +4,7 @@ import { NewRowID, RowPageSize } from "../lib/constants"
import { getCellID, parseCellID } from "../lib/utils"
import { tick } from "svelte"
import { Helpers } from "@budibase/bbui"
import { sleep } from "../../../utils/utils"
export const createStores = () => {
const rows = writable([])
@ -274,11 +275,9 @@ export const createActions = context => {
}
// Adds a new row
const addRow = async (row, idx, bubble = false) => {
const addRow = async ({ row, idx, bubble = false, notify = true }) => {
try {
// Create row. Spread row so we can mutate and enrich safely.
let newRow = { ...row }
newRow = await datasource.actions.addRow(newRow)
const newRow = await datasource.actions.addRow(row)
// Update state
if (idx != null) {
@ -291,8 +290,9 @@ export const createActions = context => {
handleNewRows([newRow])
}
// Refresh row to ensure data is in the correct format
if (notify) {
get(notifications).success("Row created successfully")
}
return newRow
} catch (error) {
if (bubble) {
@ -305,17 +305,72 @@ export const createActions = context => {
// Duplicates a row, inserting the duplicate row after the existing one
const duplicateRow = async row => {
let clone = { ...row }
let clone = cleanRow(row)
delete clone._id
delete clone._rev
delete clone.__idx
try {
return await addRow(clone, row.__idx + 1, true)
const duped = await addRow({
row: clone,
idx: row.__idx + 1,
bubble: true,
notify: false,
})
get(notifications).success("Duplicated 1 row")
return duped
} catch (error) {
handleValidationError(row._id, error)
}
}
// Duplicates multiple rows, inserting them after the last source row
const bulkDuplicate = async rowsToDupe => {
// Find index of last row
const $rowLookupMap = get(rowLookupMap)
const index = Math.max(...rowsToDupe.map(row => $rowLookupMap[row._id]))
// Clone and clean rows
const clones = rowsToDupe.map(row => {
let clone = cleanRow(row)
delete clone._id
delete clone._rev
return clone
})
// Create rows
let saved = []
let failed = 0
for (let clone of clones) {
try {
saved.push(await datasource.actions.addRow(clone))
rowCacheMap[saved._id] = true
await sleep(50) // Small sleep to ensure we avoid rate limiting
} catch (error) {
failed++
console.error("Duplicating row failed", error)
}
}
// Add to state
if (saved.length) {
rows.update(state => {
return state.toSpliced(index + 1, 0, ...saved)
})
}
// Notify user
if (saved.length) {
get(notifications).success(
`Duplicated ${saved.length} row${saved.length === 1 ? "" : "s"}`
)
}
if (failed) {
get(notifications).error(
`Failed to duplicate ${failed} row${failed === 1 ? "" : "s"}`
)
}
return saved
}
// Replaces a row in state with the newly defined row, handling updates,
// addition and deletion
const replaceRow = (id, row) => {
@ -541,6 +596,7 @@ export const createActions = context => {
actions: {
addRow,
duplicateRow,
bulkDuplicate,
getRow,
updateValue,
applyRowChanges,

View File

@ -1,6 +1,8 @@
import { makePropSafe as safe } from "@budibase/string-templates"
import { Helpers } from "@budibase/bbui"
export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
/**
* Utility to wrap an async function and ensure all invocations happen
* sequentially.