feat: allow adding custom bulk actions

This commit is contained in:
Shariq Ansari 2024-02-06 19:41:59 +05:30
parent 41cfce7383
commit f0122a2cfd
6 changed files with 89 additions and 20 deletions

View File

@ -4,6 +4,7 @@ from frappe.model import no_value_fields
from pypika import Criterion from pypika import Criterion
from crm.api.views import get_views from crm.api.views import get_views
from crm.fcrm.doctype.crm_form_script.crm_form_script import get_form_script
@frappe.whitelist() @frappe.whitelist()
@ -239,6 +240,7 @@ def get_list_data(
"views": get_views(doctype), "views": get_views(doctype),
"total_count": len(frappe.get_all(doctype, filters=filters)), "total_count": len(frappe.get_all(doctype, filters=filters)),
"row_count": len(data), "row_count": len(data),
"form_script": get_form_script(doctype)
} }

View File

@ -87,11 +87,16 @@
</ListRows> </ListRows>
<ListSelectBanner> <ListSelectBanner>
<template #actions="{ selections, unselectAll }"> <template #actions="{ selections, unselectAll }">
<Button variant="subtle" label="Edit" @click="editValues(selections, unselectAll)"> <Dropdown
<template #prefix> v-if="bulkActions.length"
<EditIcon class="h-3 w-3" /> :options="bulkActions(selections, unselectAll)"
</template> >
</Button> <Button variant="ghost">
<template #icon>
<FeatherIcon name="more-horizontal" class="h-4 w-4" />
</template>
</Button>
</Dropdown>
</template> </template>
</ListSelectBanner> </ListSelectBanner>
</ListView> </ListView>
@ -110,7 +115,7 @@
v-model:unselectAll="unselectAllAction" v-model:unselectAll="unselectAllAction"
doctype="CRM Deal" doctype="CRM Deal"
:selectedValues="selectedValues" :selectedValues="selectedValues"
@reload="emit('reload')" @reload="list.reload()"
/> />
</template> </template>
@ -118,7 +123,6 @@
import MultipleAvatar from '@/components/MultipleAvatar.vue' import MultipleAvatar from '@/components/MultipleAvatar.vue'
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue' import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import PhoneIcon from '@/components/Icons/PhoneIcon.vue' import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import EditIcon from '@/components/Icons/EditIcon.vue'
import EditValueModal from '@/components/Modals/EditValueModal.vue' import EditValueModal from '@/components/Modals/EditValueModal.vue'
import { import {
Avatar, Avatar,
@ -129,8 +133,10 @@ import {
ListRowItem, ListRowItem,
ListSelectBanner, ListSelectBanner,
ListFooter, ListFooter,
Dropdown,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, watch } from 'vue' import { setupBulkActions } from '@/utils'
import { onMounted, ref, watch } from 'vue'
const props = defineProps({ const props = defineProps({
rows: { rows: {
@ -151,9 +157,10 @@ const props = defineProps({
}, },
}) })
const emit = defineEmits(['loadMore', 'updatePageCount', 'reload']) const emit = defineEmits(['loadMore', 'updatePageCount'])
const pageLengthCount = defineModel() const pageLengthCount = defineModel()
const list = defineModel('list')
watch(pageLengthCount, (val, old_value) => { watch(pageLengthCount, (val, old_value) => {
if (val === old_value) return if (val === old_value) return
@ -169,4 +176,27 @@ function editValues(selections, unselectAll) {
showEditModal.value = true showEditModal.value = true
unselectAllAction.value = unselectAll unselectAllAction.value = unselectAll
} }
const customBulkActions = ref([])
function bulkActions(selections, unselectAll) {
let actions = [
{
label: 'Edit',
onClick: () => editValues(selections, unselectAll),
},
]
customBulkActions.value.forEach((action) => {
actions.push({
label: action.label,
onClick: () => action.onClick(selections, unselectAll, list.value),
})
})
return actions
}
onMounted(() => {
setupBulkActions(list.value.data)
customBulkActions.value = list.value?.data?.bulkActions || []
})
</script> </script>

View File

@ -96,11 +96,16 @@
</ListRows> </ListRows>
<ListSelectBanner> <ListSelectBanner>
<template #actions="{ selections, unselectAll }"> <template #actions="{ selections, unselectAll }">
<Button variant="subtle" label="Edit" @click="editValues(selections, unselectAll)"> <Dropdown
<template #prefix> v-if="bulkActions.length"
<EditIcon class="h-3 w-3" /> :options="bulkActions(selections, unselectAll)"
</template> >
</Button> <Button variant="ghost">
<template #icon>
<FeatherIcon name="more-horizontal" class="h-4 w-4" />
</template>
</Button>
</Dropdown>
</template> </template>
</ListSelectBanner> </ListSelectBanner>
</ListView> </ListView>
@ -119,7 +124,7 @@
v-model:unselectAll="unselectAllAction" v-model:unselectAll="unselectAllAction"
doctype="CRM Lead" doctype="CRM Lead"
:selectedValues="selectedValues" :selectedValues="selectedValues"
@reload="emit('reload')" @reload="list.reload()"
/> />
</template> </template>
@ -127,7 +132,6 @@
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue' import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import PhoneIcon from '@/components/Icons/PhoneIcon.vue' import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import MultipleAvatar from '@/components/MultipleAvatar.vue' import MultipleAvatar from '@/components/MultipleAvatar.vue'
import EditIcon from '@/components/Icons/EditIcon.vue'
import EditValueModal from '@/components/Modals/EditValueModal.vue' import EditValueModal from '@/components/Modals/EditValueModal.vue'
import { import {
Avatar, Avatar,
@ -138,8 +142,10 @@ import {
ListSelectBanner, ListSelectBanner,
ListRowItem, ListRowItem,
ListFooter, ListFooter,
Dropdown,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, watch } from 'vue' import { setupBulkActions } from '@/utils'
import { onMounted, ref, watch } from 'vue'
const props = defineProps({ const props = defineProps({
rows: { rows: {
@ -160,9 +166,10 @@ const props = defineProps({
}, },
}) })
const emit = defineEmits(['loadMore', 'updatePageCount', 'reload']) const emit = defineEmits(['loadMore', 'updatePageCount'])
const pageLengthCount = defineModel() const pageLengthCount = defineModel()
const list = defineModel('list')
watch(pageLengthCount, (val, old_value) => { watch(pageLengthCount, (val, old_value) => {
if (val === old_value) return if (val === old_value) return
@ -178,4 +185,27 @@ function editValues(selections, unselectAll) {
showEditModal.value = true showEditModal.value = true
unselectAllAction.value = unselectAll unselectAllAction.value = unselectAll
} }
const customBulkActions = ref([])
function bulkActions(selections, unselectAll) {
let actions = [
{
label: 'Edit',
onClick: () => editValues(selections, unselectAll),
},
]
customBulkActions.value.forEach((action) => {
actions.push({
label: action.label,
onClick: () => action.onClick(selections, unselectAll, list.value),
})
})
return actions
}
onMounted(() => {
setupBulkActions(list.value.data)
customBulkActions.value = list.value?.data?.bulkActions || []
})
</script> </script>

View File

@ -18,6 +18,7 @@
<DealsListView <DealsListView
v-if="deals.data && rows.length" v-if="deals.data && rows.length"
v-model="deals.data.page_length_count" v-model="deals.data.page_length_count"
v-model:list="deals"
:rows="rows" :rows="rows"
:columns="deals.data.columns" :columns="deals.data.columns"
:options="{ :options="{
@ -26,7 +27,6 @@
}" }"
@loadMore="() => loadMore++" @loadMore="() => loadMore++"
@updatePageCount="(count) => (updatedPageCount = count)" @updatePageCount="(count) => (updatedPageCount = count)"
@reload="deals.reload()"
/> />
<div v-else-if="deals.data" class="flex h-full items-center justify-center"> <div v-else-if="deals.data" class="flex h-full items-center justify-center">
<div <div

View File

@ -19,6 +19,7 @@
<LeadsListView <LeadsListView
v-if="leads.data && rows.length" v-if="leads.data && rows.length"
v-model="leads.data.page_length_count" v-model="leads.data.page_length_count"
v-model:list="leads"
:rows="rows" :rows="rows"
:columns="leads.data.columns" :columns="leads.data.columns"
:options="{ :options="{
@ -27,7 +28,6 @@
}" }"
@loadMore="() => loadMore++" @loadMore="() => loadMore++"
@updatePageCount="(count) => (updatedPageCount = count)" @updatePageCount="(count) => (updatedPageCount = count)"
@reload="leads.reload()"
/> />
<div v-else-if="leads.data" class="flex h-full items-center justify-center"> <div v-else-if="leads.data" class="flex h-full items-center justify-center">
<div <div

View File

@ -131,6 +131,13 @@ export function setupCustomActions(data, obj) {
data._customActions = formScript?.actions || [] data._customActions = formScript?.actions || []
} }
export function setupBulkActions(data, obj = {}) {
if (!data.form_script) return []
let script = new Function(data.form_script + '\nreturn setupForm')()
let formScript = script(obj)
data.bulkActions = formScript?.bulk_actions || []
}
export function errorMessage(title, message) { export function errorMessage(title, message) {
createToast({ createToast({
title: title || 'Error', title: title || 'Error',