Merge pull request #191 from shariquerik/bulk-assign
feat: Bulk Assign & Clear Assignments
This commit is contained in:
commit
f892861739
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
205
frontend/src/components/ListBulkActions.vue
Normal file
205
frontend/src/components/ListBulkActions.vue
Normal 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>
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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') {
|
||||
|
||||
@ -280,6 +280,7 @@
|
||||
<AssignmentModal
|
||||
v-if="deal.data"
|
||||
:doc="deal.data"
|
||||
doctype="CRM Deal"
|
||||
v-model="showAssignmentModal"
|
||||
v-model:assignees="deal.data._assignedTo"
|
||||
/>
|
||||
|
||||
@ -186,6 +186,7 @@
|
||||
<AssignmentModal
|
||||
v-if="lead.data"
|
||||
:doc="lead.data"
|
||||
doctype="CRM Lead"
|
||||
v-model="showAssignmentModal"
|
||||
v-model:assignees="lead.data._assignedTo"
|
||||
/>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user