Skip to content

Commit

Permalink
Add ability to select and perform bulk operations on all records
Browse files Browse the repository at this point in the history
This change affects the recordlist and the bulk operations are edit,delete and undelete
  • Loading branch information
KinyaElGrande committed Jun 14, 2023
1 parent 0d47243 commit 89f621c
Show file tree
Hide file tree
Showing 13 changed files with 343 additions and 171 deletions.
90 changes: 61 additions & 29 deletions client/web/compose/src/components/PageBlocks/RecordListBase.vue
Expand Up @@ -72,6 +72,7 @@
:query="query"
:prefilter="prefilter"
:selection="selected"
:selected-all-records-count="selectedAllRecords ? pagination.count : 0"
:processing="processing"
:preselected-fields="fields.map(({ moduleField }) => moduleField)"
class="mr-1 float-left"
Expand Down Expand Up @@ -164,37 +165,37 @@
</div>

<div
class="d-none flex-wrap align-items-center mt-1"
:class="{ 'd-flex': options.selectable && selected.length }"
v-if="options.selectable && selected.length"
class="d-flex align-items-center flex-wrap align-items-center mt-2"
>
<div
class="d-flex align-items-baseline my-auto pt-1 text-nowrap h-100"
>
{{ $t('recordList.selected', { count: selected.length, total: items.length }) }}
<b-button
variant="link"
class="p-0 text-decoration-none"
@click.prevent="handleSelectAllOnPage({ isChecked: false })"
>
({{ $t('recordList.cancelSelection') }})
</b-button>
<div class="mr-1">
{{ selectedRecordsDisplayText }}
</div>

<b-button
size="sm"
variant="outline-light"
class="text-primary border-0"
@click="selectAllRecords()"
>
{{ selectedAllRecords ? $t('recordList.unselectAllRecords') : $t('recordList.selectAllRecords') }}
</b-button>

<div class="d-flex align-items-center ml-auto">
<automation-buttons
class="d-inline m-0 mr-2"
:buttons="options.selectionButtons"
:module="recordListModule"
:extra-event-args="{ selected, filter }"
:extra-event-args="{ selected, filter}"
v-bind="$props"
@refresh="refresh()"
/>

<bulk-edit-modal
v-show="options.bulkRecordEditEnabled && canUpdateSelectedRecords"
v-show="options.bulkRecordEditEnabled && canUpdateSelectedRecords && !showingDeletedRecords"
:module="recordListModule"
:namespace="namespace"
:selected-records="selected"
:selected-records="selectedAllRecords ? [] : selected"
@save="onBulkUpdate()"
/>

Expand Down Expand Up @@ -433,7 +434,7 @@
:extra-options="options"
/>
<div
v-if="options.inlineRecordEditEnabled && field.canEdit"
v-if="options.inlineRecordEditEnabled && field.canEdit && !showingDeletedRecords"
class="inline-actions"
>
<b-button
Expand Down Expand Up @@ -844,6 +845,7 @@ export default {
customPresetFilters: [],
currentCustomPresetFilter: undefined,
showCustomPresetFilterModal: false,
selectedAllRecords: false,
}
},
Expand Down Expand Up @@ -1020,6 +1022,14 @@ export default {
authUserRoles () {
return this.$auth.user.roles
},
selectedRecordsDisplayText () {
const count = this.selectedAllRecords ? (this.options.showTotalCount ? this.pagination.count : undefined) : this.selected.length
const total = this.items.length
const key = this.selectedAllRecords ? 'selectedFromAllPages' : 'selected'
return this.$t(`recordList.${key}`, { count, total })
},
},
watch: {
Expand Down Expand Up @@ -1150,6 +1160,8 @@ export default {
return
}
this.selected.splice(i, 1)
this.selectedAllRecords = false
}
},
Expand All @@ -1174,6 +1186,7 @@ export default {
handleShowDeleted () {
this.showingDeletedRecords = !this.showingDeletedRecords
this.selectedAllRecords = false
this.refresh(true)
},
Expand Down Expand Up @@ -1381,7 +1394,7 @@ export default {
query: {
fields: e.fields,
// url.Make already URL encodes the the values, so the filter shouldn't be encoded
filter: filter,
filter: this.selectedAllRecords ? '' : filter,
jwt: this.$auth.accessToken,
timezone: timezone ? timezone.tzCode : undefined,
},
Expand Down Expand Up @@ -1468,9 +1481,15 @@ export default {
this.selected = this.items.map(({ id }) => id)
} else {
this.selected = []
this.selectedAllRecords = isChecked
}
},
selectAllRecords () {
this.selectedAllRecords = !this.selectedAllRecords
this.handleSelectAllOnPage({ isChecked: this.selectedAllRecords })
},
handleRestoreSelectedRecords () {
if (this.inlineEditing) {
const sel = new Set(this.selected)
Expand All @@ -1482,22 +1501,27 @@ export default {
} else {
const { moduleID, namespaceID } = this.items[0].r
// filter undeletable records from the selected list
const recordIDs = this.items
.filter(({ id, r }) => r.canUndeleteRecord && this.selected.includes(id))
.map(({ id }) => id)
let query = ''
this.processing = true
if (!this.selectedAllRecords) {
// filter deletable records from the selected list
const recordIDs = this.items
.filter(({ id, r }) => r.canUndeleteRecord && this.selected.includes(id))
.map(({ id }) => id)
query = recordIDs.map(r => `recordID='${r}'`).join(' OR ')
}
this.$ComposeAPI
.recordBulkUndelete({ moduleID, namespaceID, recordIDs })
.recordBulkUndelete({ moduleID, namespaceID, query })
.then(() => {
this.refresh(true)
this.toastSuccess(this.$t('notification:record.undeleteBulkSuccess'))
})
.catch(this.toastErrorHandler(this.$t('notification:record.undeleteBulkFailed')))
.finally(() => {
this.processing = false
this.selectedAllRecords = false
})
}
},
Expand All @@ -1521,22 +1545,29 @@ export default {
// same module so this should be safe to do.
const { moduleID, namespaceID } = this.items[0].r
// filter deletable records from the selected list
const recordIDs = this.items
.filter(({ id, r }) => r.canDeleteRecord && selected.includes(id))
.map(({ id }) => id)
let query = ''
if (!this.selectedAllRecords) {
// filter deletable records from the selected list
const recordIDs = this.items
.filter(({ id, r }) => r.canDeleteRecord && selected.includes(id))
.map(({ id }) => id)
query = recordIDs.map(r => `recordID='${r}'`).join(' OR ')
}
this.processing = true
this.$ComposeAPI
.recordBulkDelete({ moduleID, namespaceID, recordIDs })
.recordBulkDelete({ moduleID, namespaceID, query })
.then(() => this.refresh(true))
.then(() => {
this.toastSuccess(this.$t('notification:record.deleteBulkSuccess'))
})
.catch(this.toastErrorHandler(this.$t('notification:record.deleteBulkFailed')))
.finally(() => {
this.processing = false
this.selectedAllRecords = false
})
}
},
Expand Down Expand Up @@ -1762,6 +1793,7 @@ export default {
},
onBulkUpdate () {
this.selectedAllRecords = false
this.refresh(true)
},
Expand Down
Expand Up @@ -232,6 +232,11 @@ export default {
required: false,
default: () => [],
},
selectedAllRecordsCount: {
type: Number,
required: false,
default: 0,
},
filterRangeType: {
type: String,
default: 'all',
Expand Down Expand Up @@ -375,6 +380,10 @@ export default {
getExportableCount () {
// when exporting selection, only selected records are applicable
if (this.rangeType === 'selection') {
if (this.selectedAllRecordsCount !== 0) {
return this.selectedAllRecordsCount
}
return this.selection.length
}
Expand Down
4 changes: 3 additions & 1 deletion client/web/compose/src/mixins/record.js
Expand Up @@ -281,8 +281,10 @@ export default {

const { moduleID, namespaceID } = this.module

const query = records.map(r => `recordID='${r}'`).join(' OR ')

return this
.$ComposeAPI.recordPatch({ moduleID, namespaceID, records, values })
.$ComposeAPI.recordPatch({ moduleID, namespaceID, values, query })
.catch(err => {
const { details = undefined } = err
if (!!details && Array.isArray(details) && details.length > 0) {
Expand Down
12 changes: 6 additions & 6 deletions lib/js/src/api-clients/compose.ts
Expand Up @@ -2185,8 +2185,8 @@ export default class Compose {
const {
namespaceID,
moduleID,
records,
values,
query,
} = (a as KV) || {}
if (!namespaceID) {
throw Error('field namespaceID is empty')
Expand All @@ -2202,8 +2202,8 @@ export default class Compose {
}),
}
cfg.data = {
records,
values,
query,
}
return this.api().request(cfg).then(result => stdResolve(result))
}
Expand All @@ -2221,8 +2221,8 @@ export default class Compose {
const {
namespaceID,
moduleID,
recordIDs,
truncate,
query,
} = (a as KV) || {}
if (!namespaceID) {
throw Error('field namespaceID is empty')
Expand All @@ -2238,8 +2238,8 @@ export default class Compose {
}),
}
cfg.data = {
recordIDs,
truncate,
query,
}
return this.api().request(cfg).then(result => stdResolve(result))
}
Expand Down Expand Up @@ -2329,7 +2329,7 @@ export default class Compose {
const {
namespaceID,
moduleID,
recordIDs,
query,
} = (a as KV) || {}
if (!namespaceID) {
throw Error('field namespaceID is empty')
Expand All @@ -2345,7 +2345,7 @@ export default class Compose {
}),
}
cfg.data = {
recordIDs,
query,
}
return this.api().request(cfg).then(result => stdResolve(result))
}
Expand Down
3 changes: 3 additions & 0 deletions locale/en/corteza-webapp-compose/block.yaml
Expand Up @@ -484,6 +484,9 @@ recordList:
label: Parent field
selectable: Users will be able to select records
selected: '{{count}} of {{total}} records selected'
selectedFromAllPages: 'All {{count}} records from all pages selected'
selectAllRecords: Select all records on all pages
unselectAllRecords: Clear selection
sort:
tooltip: Sort column
showRecords:
Expand Down

0 comments on commit 89f621c

Please sign in to comment.