Merge pull request #191 from shariquerik/bulk-assign

feat: Bulk Assign & Clear Assignments
This commit is contained in:
Shariq Ansari 2024-05-21 00:16:40 +05:30 committed by GitHub
commit f892861739
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 329 additions and 654 deletions

View File

@ -376,7 +376,7 @@ def get_type(field):
return "read_only"
return field.fieldtype.lower()
def get_assigned_users(doctype, name):
def get_assigned_users(doctype, name, default_assigned_to=None):
assigned_users = frappe.get_all(
"ToDo",
fields=["allocated_to"],
@ -388,7 +388,12 @@ def get_assigned_users(doctype, name):
pluck="allocated_to",
)
return list(set(assigned_users))
users = list(set(assigned_users))
# if users is empty, add default_assigned_to
if not users and default_assigned_to:
users = [default_assigned_to]
return users
@frappe.whitelist()

View File

@ -30,7 +30,7 @@ def get_deal(name):
deal["doctype_fields"], deal["all_fields"] = get_doctype_fields("CRM Deal", name)
deal["doctype"] = "CRM Deal"
deal["_form_script"] = get_form_script('CRM Deal')
deal["_assign"] = get_assigned_users("CRM Deal", deal.name)
deal["_assign"] = get_assigned_users("CRM Deal", deal.name, deal.owner)
return deal
@frappe.whitelist()

View File

@ -18,5 +18,5 @@ def get_lead(name):
lead["doctype_fields"], lead["all_fields"] = get_doctype_fields("CRM Lead", name)
lead["doctype"] = "CRM Lead"
lead["_form_script"] = get_form_script('CRM Lead')
lead["_assign"] = get_assigned_users("CRM Lead", lead.name)
lead["_assign"] = get_assigned_users("CRM Lead", lead.name, lead.owner)
return lead

View File

@ -0,0 +1,205 @@
<template>
<EditValueModal
v-model="showEditModal"
:doctype="doctype"
:selectedValues="selectedValues"
@reload="reload"
/>
<AssignmentModal
v-if="selectedValues"
:docs="selectedValues"
:doctype="doctype"
v-model="showAssignmentModal"
v-model:assignees="bulkAssignees"
@reload="reload"
/>
</template>
<script setup>
import EditValueModal from '@/components/Modals/EditValueModal.vue'
import AssignmentModal from '@/components/Modals/AssignmentModal.vue'
import { setupListActions, createToast } from '@/utils'
import { globalStore } from '@/stores/global'
import { call } from 'frappe-ui'
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
const props = defineProps({
doctype: {
type: String,
default: '',
},
options: {
type: Object,
default: () => ({
hideEdit: false,
hideDelete: false,
hideAssign: false,
}),
},
})
const list = defineModel()
const router = useRouter()
const { $dialog } = globalStore()
const showEditModal = ref(false)
const selectedValues = ref([])
const unselectAllAction = ref(() => {})
function editValues(selections, unselectAll) {
selectedValues.value = selections
showEditModal.value = true
unselectAllAction.value = unselectAll
}
function deleteValues(selections, unselectAll) {
$dialog({
title: __('Delete'),
message: __('Are you sure you want to delete {0} item(s)?', [
selections.size,
]),
variant: 'solid',
theme: 'red',
actions: [
{
label: __('Delete'),
variant: 'solid',
theme: 'red',
onClick: (close) => {
call('frappe.desk.reportview.delete_items', {
items: JSON.stringify(Array.from(selections)),
doctype: props.doctype,
}).then(() => {
createToast({
title: __('Deleted successfully'),
icon: 'check',
iconClasses: 'text-green-600',
})
unselectAll()
list.value.reload()
close()
})
},
},
],
})
}
const showAssignmentModal = ref(false)
const bulkAssignees = ref([])
function assignValues(selections, unselectAll) {
showAssignmentModal.value = true
selectedValues.value = selections
unselectAllAction.value = unselectAll
}
function clearAssignemnts(selections, unselectAll) {
$dialog({
title: __('Clear Assignment'),
message: __('Are you sure you want to clear assignment for {0} item(s)?', [
selections.size,
]),
variant: 'solid',
theme: 'red',
actions: [
{
label: __('Clear Assignment'),
variant: 'solid',
theme: 'red',
onClick: (close) => {
call('frappe.desk.form.assign_to.remove_multiple', {
doctype: props.doctype,
names: JSON.stringify(Array.from(selections)),
ignore_permissions: true,
}).then(() => {
createToast({
title: __('Assignment cleared successfully'),
icon: 'check',
iconClasses: 'text-green-600',
})
reload(unselectAll)
close()
})
},
},
],
})
}
const customBulkActions = ref([])
const customListActions = ref([])
function bulkActions(selections, unselectAll) {
let actions = []
if (!props.options.hideEdit) {
actions.push({
label: __('Edit'),
onClick: () => editValues(selections, unselectAll),
})
}
if (!props.options.hideDelete) {
actions.push({
label: __('Delete'),
onClick: () => deleteValues(selections, unselectAll),
})
}
if (!props.options.hideAssign) {
actions.push({
label: __('Assign To'),
onClick: () => assignValues(selections, unselectAll),
})
actions.push({
label: __('Clear Assignment'),
onClick: () => clearAssignemnts(selections, unselectAll),
})
}
customBulkActions.value.forEach((action) => {
actions.push({
label: __(action.label),
onClick: () =>
action.onClick({
list: list.value,
selections,
unselectAll,
call,
createToast,
$dialog,
router,
}),
})
})
return actions
}
function reload(unselectAll) {
unselectAllAction.value?.()
unselectAll?.()
list.value?.reload()
}
onMounted(() => {
if (!list.value?.data) return
setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
defineExpose({
bulkActions,
customListActions,
})
</script>

View File

@ -80,7 +80,9 @@
</ListRows>
<ListSelectBanner>
<template #actions="{ selections, unselectAll }">
<Dropdown :options="bulkActions(selections, unselectAll)">
<Dropdown
:options="listBulkActionsRef.bulkActions(selections, unselectAll)"
>
<Button icon="more-horizontal" variant="ghost" />
</Dropdown>
</template>
@ -95,8 +97,18 @@
}"
@loadMore="emit('loadMore')"
/>
<ListBulkActions
ref="listBulkActionsRef"
v-model="list"
doctype="CRM Call Log"
:options="{
hideEdit: true,
hideAssign: true,
}"
/>
</template>
<script setup>
import ListBulkActions from '@/components/ListBulkActions.vue'
import {
Avatar,
ListView,
@ -108,12 +120,8 @@ import {
ListFooter,
Tooltip,
Dropdown,
call,
} from 'frappe-ui'
import { setupListActions, createToast } from '@/utils'
import { globalStore } from '@/stores/global'
import { onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { ref, watch } from 'vue'
const props = defineProps({
rows: {
@ -146,89 +154,14 @@ const emit = defineEmits([
const pageLengthCount = defineModel()
const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => {
if (val === old_value) return
emit('updatePageCount', val)
})
function deleteValues(selections, unselectAll) {
$dialog({
title: 'Delete',
message: `Are you sure you want to delete ${selections.size} item${
selections.size > 1 ? 's' : ''
}?`,
variant: 'danger',
actions: [
{
label: 'Delete',
variant: 'solid',
theme: 'red',
onClick: (close) => {
call('frappe.desk.reportview.delete_items', {
items: JSON.stringify(Array.from(selections)),
doctype: 'CRM Call Log',
}).then(() => {
createToast({
title: 'Deleted successfully',
icon: 'check',
iconClasses: 'text-green-600',
})
unselectAll()
list.value.reload()
close()
})
},
},
],
})
}
const customBulkActions = ref([])
const customListActions = ref([])
function bulkActions(selections, unselectAll) {
let actions = [
{
label: 'Delete',
onClick: () => deleteValues(selections, unselectAll),
},
]
customBulkActions.value.forEach((action) => {
actions.push({
label: action.label,
onClick: () =>
action.onClick({
list: list.value,
selections,
unselectAll,
call,
createToast,
$dialog,
router,
}),
})
})
return actions
}
onMounted(() => {
if (!list.value?.data) return
setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
const listBulkActionsRef = ref(null)
defineExpose({
customListActions,
customListActions: listBulkActionsRef.value?.customListActions,
})
</script>

View File

@ -82,7 +82,9 @@
</ListRows>
<ListSelectBanner>
<template #actions="{ selections, unselectAll }">
<Dropdown :options="bulkActions(selections, unselectAll)">
<Dropdown
:options="listBulkActionsRef.bulkActions(selections, unselectAll)"
>
<Button icon="more-horizontal" variant="ghost" />
</Dropdown>
</template>
@ -98,19 +100,18 @@
}"
@loadMore="emit('loadMore')"
/>
<EditValueModal
v-model="showEditModal"
v-model:unselectAll="unselectAllAction"
<ListBulkActions
ref="listBulkActionsRef"
v-model="list"
doctype="Contact"
:selectedValues="selectedValues"
@reload="list.reload()"
:options="{
hideAssign: true,
}"
/>
</template>
<script setup>
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import EditValueModal from '@/components/Modals/EditValueModal.vue'
import { globalStore } from '@/stores/global'
import { setupListActions, createToast } from '@/utils'
import ListBulkActions from '@/components/ListBulkActions.vue'
import {
Avatar,
ListView,
@ -122,10 +123,8 @@ import {
ListFooter,
Tooltip,
Dropdown,
call,
} from 'frappe-ui'
import { ref, watch, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ref, watch } from 'vue'
const props = defineProps({
rows: {
@ -158,87 +157,14 @@ const emit = defineEmits([
const pageLengthCount = defineModel()
const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => {
if (val === old_value) return
emit('updatePageCount', val)
})
const showEditModal = ref(false)
const selectedValues = ref([])
const unselectAllAction = ref(() => {})
function editValues(selections, unselectAll) {
selectedValues.value = selections
showEditModal.value = true
unselectAllAction.value = unselectAll
}
function deleteValues(selections, unselectAll) {
$dialog({
title: __('Delete'),
message: __('Are you sure you want to delete {0} item(s)?', [
selections.size,
]),
variant: 'danger',
actions: [
{
label: __('Delete'),
variant: 'solid',
theme: 'red',
onClick: (close) => {
call('frappe.desk.reportview.delete_items', {
items: JSON.stringify(Array.from(selections)),
doctype: 'Contact',
}).then(() => {
createToast({
title: __('Deleted successfully'),
icon: 'check',
iconClasses: 'text-green-600',
})
unselectAll()
list.value.reload()
close()
})
},
},
],
})
}
const customListActions = ref([])
function bulkActions(selections, unselectAll) {
let actions = [
{
label: __('Edit'),
onClick: () => editValues(selections, unselectAll),
},
{
label: __('Delete'),
onClick: () => deleteValues(selections, unselectAll),
},
]
return actions
}
onMounted(() => {
if (!list.value?.data) return
setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
// customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
const listBulkActionsRef = ref(null)
defineExpose({
customListActions,
customListActions: listBulkActionsRef.value?.customListActions,
})
</script>

View File

@ -114,7 +114,9 @@
</ListRows>
<ListSelectBanner>
<template #actions="{ selections, unselectAll }">
<Dropdown :options="bulkActions(selections, unselectAll)">
<Dropdown
:options="listBulkActionsRef.bulkActions(selections, unselectAll)"
>
<Button icon="more-horizontal" variant="ghost" />
</Dropdown>
</template>
@ -130,20 +132,14 @@
}"
@loadMore="emit('loadMore')"
/>
<EditValueModal
v-model="showEditModal"
v-model:unselectAll="unselectAllAction"
doctype="CRM Deal"
:selectedValues="selectedValues"
@reload="list.reload()"
/>
<ListBulkActions ref="listBulkActionsRef" v-model="list" doctype="CRM Deal" />
</template>
<script setup>
import MultipleAvatar from '@/components/MultipleAvatar.vue'
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import EditValueModal from '@/components/Modals/EditValueModal.vue'
import ListBulkActions from '@/components/ListBulkActions.vue'
import {
Avatar,
ListView,
@ -154,13 +150,9 @@ import {
ListSelectBanner,
ListFooter,
Dropdown,
call,
Tooltip,
} from 'frappe-ui'
import { setupListActions, createToast } from '@/utils'
import { globalStore } from '@/stores/global'
import { onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { ref, watch } from 'vue'
const props = defineProps({
rows: {
@ -193,103 +185,14 @@ const emit = defineEmits([
const pageLengthCount = defineModel()
const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => {
if (val === old_value) return
emit('updatePageCount', val)
})
const showEditModal = ref(false)
const selectedValues = ref([])
const unselectAllAction = ref(() => {})
function editValues(selections, unselectAll) {
selectedValues.value = selections
showEditModal.value = true
unselectAllAction.value = unselectAll
}
function deleteValues(selections, unselectAll) {
$dialog({
title: __('Delete'),
message: __('Are you sure you want to delete {0} item(s)?', [
selections.size,
]),
variant: 'danger',
actions: [
{
label: __('Delete'),
variant: 'solid',
theme: 'red',
onClick: (close) => {
call('frappe.desk.reportview.delete_items', {
items: JSON.stringify(Array.from(selections)),
doctype: 'CRM Deal',
}).then(() => {
createToast({
title: __('Deleted successfully'),
icon: 'check',
iconClasses: 'text-green-600',
})
unselectAll()
list.value.reload()
close()
})
},
},
],
})
}
const customBulkActions = ref([])
const customListActions = ref([])
function bulkActions(selections, unselectAll) {
let actions = [
{
label: __('Edit'),
onClick: () => editValues(selections, unselectAll),
},
{
label: __('Delete'),
onClick: () => deleteValues(selections, unselectAll),
},
]
customBulkActions.value.forEach((action) => {
actions.push({
label: __(action.label),
onClick: () =>
action.onClick({
list: list.value,
selections,
unselectAll,
call,
createToast,
$dialog,
router,
}),
})
})
return actions
}
onMounted(() => {
if (!list.value?.data) return
setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
const listBulkActionsRef = ref(null)
defineExpose({
customListActions,
customListActions: listBulkActionsRef.value?.customListActions,
})
</script>

View File

@ -69,7 +69,7 @@
</ListRows>
<ListSelectBanner>
<template #actions="{ selections, unselectAll }">
<Dropdown :options="bulkActions(selections, unselectAll)">
<Dropdown :options="listBulkActionsRef.bulkActions(selections, unselectAll)">
<Button icon="more-horizontal" variant="ghost" />
</Dropdown>
</template>
@ -84,18 +84,17 @@
}"
@loadMore="emit('loadMore')"
/>
<EditValueModal
v-model="showEditModal"
v-model:unselectAll="unselectAllAction"
<ListBulkActions
ref="listBulkActionsRef"
v-model="list"
doctype="Email Template"
:selectedValues="selectedValues"
@reload="list.reload()"
:options="{
hideAssign: true,
}"
/>
</template>
<script setup>
import EditValueModal from '@/components/Modals/EditValueModal.vue'
import { globalStore } from '@/stores/global'
import { setupListActions, createToast } from '@/utils'
import ListBulkActions from '@/components/ListBulkActions.vue'
import {
ListView,
ListHeader,
@ -105,11 +104,9 @@ import {
ListRowItem,
ListFooter,
Dropdown,
call,
Tooltip,
} from 'frappe-ui'
import { ref, watch, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ref, watch } from 'vue'
const props = defineProps({
rows: {
@ -143,87 +140,14 @@ const emit = defineEmits([
const pageLengthCount = defineModel()
const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => {
if (val === old_value) return
emit('updatePageCount', val)
})
const showEditModal = ref(false)
const selectedValues = ref([])
const unselectAllAction = ref(() => {})
function editValues(selections, unselectAll) {
selectedValues.value = selections
showEditModal.value = true
unselectAllAction.value = unselectAll
}
function deleteValues(selections, unselectAll) {
$dialog({
title: __('Delete'),
message: __('Are you sure you want to delete {0} item(s)?', [
selections.size,
]),
variant: 'danger',
actions: [
{
label: __('Delete'),
variant: 'solid',
theme: 'red',
onClick: (close) => {
call('frappe.desk.reportview.delete_items', {
items: JSON.stringify(Array.from(selections)),
doctype: 'Email Template',
}).then(() => {
createToast({
title: __('Deleted successfully'),
icon: 'check',
iconClasses: 'text-green-600',
})
unselectAll()
list.value.reload()
close()
})
},
},
],
})
}
const customListActions = ref([])
function bulkActions(selections, unselectAll) {
let actions = [
{
label: __('Edit'),
onClick: () => editValues(selections, unselectAll),
},
{
label: __('Delete'),
onClick: () => deleteValues(selections, unselectAll),
},
]
return actions
}
onMounted(() => {
if (!list.value?.data) return
setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
// customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
const listBulkActionsRef = ref(null)
defineExpose({
customListActions,
customListActions: listBulkActionsRef.value?.customListActions,
})
</script>

View File

@ -123,7 +123,7 @@
</ListRows>
<ListSelectBanner>
<template #actions="{ selections, unselectAll }">
<Dropdown :options="bulkActions(selections, unselectAll)">
<Dropdown :options="listBulkActionsRef.bulkActions(selections, unselectAll)">
<Button icon="more-horizontal" variant="ghost" />
</Dropdown>
</template>
@ -139,20 +139,14 @@
}"
@loadMore="emit('loadMore')"
/>
<EditValueModal
v-model="showEditModal"
v-model:unselectAll="unselectAllAction"
doctype="CRM Lead"
:selectedValues="selectedValues"
@reload="list.reload()"
/>
<ListBulkActions ref="listBulkActionsRef" v-model="list" doctype="CRM Lead" />
</template>
<script setup>
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import MultipleAvatar from '@/components/MultipleAvatar.vue'
import EditValueModal from '@/components/Modals/EditValueModal.vue'
import ListBulkActions from '@/components/ListBulkActions.vue'
import {
Avatar,
ListView,
@ -163,13 +157,9 @@ import {
ListRowItem,
ListFooter,
Dropdown,
call,
Tooltip,
} from 'frappe-ui'
import { setupListActions, createToast } from '@/utils'
import { globalStore } from '@/stores/global'
import { onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { ref, watch } from 'vue'
const props = defineProps({
rows: {
@ -202,103 +192,14 @@ const emit = defineEmits([
const pageLengthCount = defineModel()
const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => {
if (val === old_value) return
emit('updatePageCount', val)
})
const showEditModal = ref(false)
const selectedValues = ref([])
const unselectAllAction = ref(() => {})
function editValues(selections, unselectAll) {
selectedValues.value = selections
showEditModal.value = true
unselectAllAction.value = unselectAll
}
function deleteValues(selections, unselectAll) {
$dialog({
title: __('Delete'),
message: __('Are you sure you want to delete {0} item(s)?', [
selections.size,
]),
variant: 'danger',
actions: [
{
label: __('Delete'),
variant: 'solid',
theme: 'red',
onClick: (close) => {
call('frappe.desk.reportview.delete_items', {
items: JSON.stringify(Array.from(selections)),
doctype: 'CRM Lead',
}).then(() => {
createToast({
title: __('Deleted successfully'),
icon: 'check',
iconClasses: 'text-green-600',
})
unselectAll()
list.value.reload()
close()
})
},
},
],
})
}
const customBulkActions = ref([])
const customListActions = ref([])
function bulkActions(selections, unselectAll) {
let actions = [
{
label: __('Edit'),
onClick: () => editValues(selections, unselectAll),
},
{
label: __('Delete'),
onClick: () => deleteValues(selections, unselectAll),
},
]
customBulkActions.value.forEach((action) => {
actions.push({
label: __(action.label),
onClick: () =>
action.onClick({
list: list.value,
selections,
unselectAll,
call,
createToast,
$dialog,
router,
}),
})
})
return actions
}
onMounted(() => {
if (!list.value?.data) return
setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
const listBulkActionsRef = ref(null)
defineExpose({
customListActions,
customListActions: listBulkActionsRef.value?.customListActions,
})
</script>

View File

@ -69,7 +69,9 @@
</ListRows>
<ListSelectBanner>
<template #actions="{ selections, unselectAll }">
<Dropdown :options="bulkActions(selections, unselectAll)">
<Dropdown
:options="listBulkActionsRef.bulkActions(selections, unselectAll)"
>
<Button icon="more-horizontal" variant="ghost" />
</Dropdown>
</template>
@ -84,18 +86,17 @@
}"
@loadMore="emit('loadMore')"
/>
<EditValueModal
v-model="showEditModal"
v-model:unselectAll="unselectAllAction"
<ListBulkActions
ref="listBulkActionsRef"
v-model="list"
doctype="CRM Organization"
:selectedValues="selectedValues"
@reload="list.reload()"
:options="{
hideAssign: true,
}"
/>
</template>
<script setup>
import EditValueModal from '@/components/Modals/EditValueModal.vue'
import { globalStore } from '@/stores/global'
import { setupListActions, createToast } from '@/utils'
import ListBulkActions from '@/components/ListBulkActions.vue'
import {
Avatar,
ListView,
@ -107,10 +108,8 @@ import {
ListFooter,
Tooltip,
Dropdown,
call,
} from 'frappe-ui'
import { ref, watch, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ref, watch } from 'vue'
const props = defineProps({
rows: {
@ -143,87 +142,14 @@ const emit = defineEmits([
const pageLengthCount = defineModel()
const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => {
if (val === old_value) return
emit('updatePageCount', val)
})
const showEditModal = ref(false)
const selectedValues = ref([])
const unselectAllAction = ref(() => {})
function editValues(selections, unselectAll) {
selectedValues.value = selections
showEditModal.value = true
unselectAllAction.value = unselectAll
}
function deleteValues(selections, unselectAll) {
$dialog({
title: __('Delete'),
message: __('Are you sure you want to delete {0} item(s)?', [
selections.size,
]),
variant: 'danger',
actions: [
{
label: __('Delete'),
variant: 'solid',
theme: 'red',
onClick: (close) => {
call('frappe.desk.reportview.delete_items', {
items: JSON.stringify(Array.from(selections)),
doctype: 'CRM Organization',
}).then(() => {
createToast({
title: __('Deleted successfully'),
icon: 'check',
iconClasses: 'text-green-600',
})
unselectAll()
list.value.reload()
close()
})
},
},
],
})
}
const customListActions = ref([])
function bulkActions(selections, unselectAll) {
let actions = [
{
label: __('Edit'),
onClick: () => editValues(selections, unselectAll),
},
{
label: __('Delete'),
onClick: () => deleteValues(selections, unselectAll),
},
]
return actions
}
onMounted(() => {
if (!list.value?.data) return
setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
// customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
const listBulkActionsRef = ref(null)
defineExpose({
customListActions,
customListActions: listBulkActionsRef.value?.customListActions,
})
</script>

View File

@ -82,7 +82,7 @@
</ListRows>
<ListSelectBanner>
<template #actions="{ selections, unselectAll }">
<Dropdown :options="bulkActions(selections, unselectAll)">
<Dropdown :options="listBulkActionsRef.bulkActions(selections, unselectAll)">
<Button icon="more-horizontal" variant="ghost" />
</Dropdown>
</template>
@ -97,22 +97,21 @@
}"
@loadMore="emit('loadMore')"
/>
<EditValueModal
v-model="showEditModal"
v-model:unselectAll="unselectAllAction"
<ListBulkActions
ref="listBulkActionsRef"
v-model="list"
doctype="CRM Task"
:selectedValues="selectedValues"
@reload="list.reload()"
:options="{
hideAssign: true,
}"
/>
</template>
<script setup>
import TaskStatusIcon from '@/components/Icons/TaskStatusIcon.vue'
import TaskPriorityIcon from '@/components/Icons/TaskPriorityIcon.vue'
import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
import EditValueModal from '@/components/Modals/EditValueModal.vue'
import ListBulkActions from '@/components/ListBulkActions.vue'
import { dateFormat } from '@/utils'
import { globalStore } from '@/stores/global'
import { setupListActions, createToast } from '@/utils'
import {
Avatar,
ListView,
@ -123,11 +122,9 @@ import {
ListRowItem,
ListFooter,
Dropdown,
call,
Tooltip,
} from 'frappe-ui'
import { ref, watch, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ref, watch } from 'vue'
const props = defineProps({
rows: {
@ -161,87 +158,14 @@ const emit = defineEmits([
const pageLengthCount = defineModel()
const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => {
if (val === old_value) return
emit('updatePageCount', val)
})
const showEditModal = ref(false)
const selectedValues = ref([])
const unselectAllAction = ref(() => {})
function editValues(selections, unselectAll) {
selectedValues.value = selections
showEditModal.value = true
unselectAllAction.value = unselectAll
}
function deleteValues(selections, unselectAll) {
$dialog({
title: __('Delete'),
message: __('Are you sure you want to delete {0} item(s)?', [
selections.size,
]),
variant: 'danger',
actions: [
{
label: __('Delete'),
variant: 'solid',
theme: 'red',
onClick: (close) => {
call('frappe.desk.reportview.delete_items', {
items: JSON.stringify(Array.from(selections)),
doctype: 'CRM Task',
}).then(() => {
createToast({
title: __('Deleted successfully'),
icon: 'check',
iconClasses: 'text-green-600',
})
unselectAll()
list.value.reload()
close()
})
},
},
],
})
}
const customListActions = ref([])
function bulkActions(selections, unselectAll) {
let actions = [
{
label: __('Edit'),
onClick: () => editValues(selections, unselectAll),
},
{
label: __('Delete'),
onClick: () => deleteValues(selections, unselectAll),
},
]
return actions
}
onMounted(() => {
if (!list.value?.data) return
setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
// customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
const listBulkActionsRef = ref(null)
defineExpose({
customListActions,
customListActions: listBulkActionsRef.value?.customListActions,
})
</script>

View File

@ -27,6 +27,7 @@
value=""
doctype="User"
@change="(option) => addValue(option) && ($refs.input.value = '')"
:placeholder="__('John Doe')"
:hideMe="true"
>
<template #item-prefix="{ option }">
@ -83,8 +84,18 @@ const props = defineProps({
type: Object,
default: null,
},
docs: {
type: Array,
default: () => [],
},
doctype: {
type: String,
default: '',
},
})
const emit = defineEmits(['reload'])
const show = defineModel()
const assignees = defineModel('assignees')
const oldAssignees = ref([])
@ -101,7 +112,7 @@ const removeValue = (value) => {
const owner = computed(() => {
if (!props.doc) return ''
if (props.doc.doctype == 'CRM Lead') return props.doc.lead_owner
if (props.doctype == 'CRM Lead') return props.doc.lead_owner
return props.doc.deal_owner
})
@ -137,7 +148,7 @@ function updateAssignees() {
if (removedAssignees.length) {
for (let a of removedAssignees) {
call('frappe.desk.form.assign_to.remove', {
doctype: props.doc.doctype,
doctype: props.doctype,
name: props.doc.name,
assign_to: a,
})
@ -145,11 +156,23 @@ function updateAssignees() {
}
if (addedAssignees.length) {
call('frappe.desk.form.assign_to.add', {
doctype: props.doc.doctype,
name: props.doc.name,
assign_to: addedAssignees,
})
if (props.docs.size) {
call('frappe.desk.form.assign_to.add_multiple', {
doctype: props.doctype,
name: JSON.stringify(Array.from(props.docs)),
assign_to: addedAssignees,
bulk_assign: true,
re_assign: true,
}).then(() => {
emit('reload')
})
} else {
call('frappe.desk.form.assign_to.add', {
doctype: props.doctype,
name: props.doc.name,
assign_to: addedAssignees,
})
}
}
show.value = false
}

View File

@ -59,7 +59,6 @@ const props = defineProps({
})
const show = defineModel()
const unselectAll = defineModel('unselectAll')
const emit = defineEmits(['reload'])
@ -114,7 +113,6 @@ function updateValues() {
newValue.value = ''
loading.value = false
show.value = false
unselectAll.value()
emit('reload')
})
}
@ -130,6 +128,10 @@ function updateValue(v) {
newValue.value = value
}
function getSelectOptions(options) {
return options.split('\n')
}
function getValueComponent(f) {
const { type, options } = f
if (typeSelect.includes(type) || typeCheck.includes(type)) {
@ -140,6 +142,7 @@ function getValueComponent(f) {
label: o,
value: o,
})),
modelValue: newValue.value,
})
} else if (typeLink.includes(type)) {
if (type == 'Dynamic Link') {

View File

@ -280,6 +280,7 @@
<AssignmentModal
v-if="deal.data"
:doc="deal.data"
doctype="CRM Deal"
v-model="showAssignmentModal"
v-model:assignees="deal.data._assignedTo"
/>

View File

@ -186,6 +186,7 @@
<AssignmentModal
v-if="lead.data"
:doc="lead.data"
doctype="CRM Lead"
v-model="showAssignmentModal"
v-model:assignees="lead.data._assignedTo"
/>