Merge pull request #68 from shariquerik/bulk-edit
fix: Bulk Edit from list view
This commit is contained in:
commit
cde43d5fed
@ -341,3 +341,28 @@ def get_assigned_users(doctype, name):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return list(set(assigned_users))
|
return list(set(assigned_users))
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_fields(doctype: str):
|
||||||
|
not_allowed_fieldtypes = list(frappe.model.no_value_fields) + ["Read Only"]
|
||||||
|
fields = frappe.get_meta(doctype).fields
|
||||||
|
|
||||||
|
_fields = []
|
||||||
|
|
||||||
|
for field in fields:
|
||||||
|
if (
|
||||||
|
field.fieldtype not in not_allowed_fieldtypes
|
||||||
|
and not field.hidden
|
||||||
|
and not field.read_only
|
||||||
|
and not field.is_virtual
|
||||||
|
and field.fieldname
|
||||||
|
):
|
||||||
|
_fields.append({
|
||||||
|
"label": field.label,
|
||||||
|
"type": field.fieldtype,
|
||||||
|
"value": field.fieldname,
|
||||||
|
"options": field.options,
|
||||||
|
})
|
||||||
|
|
||||||
|
return _fields
|
||||||
@ -62,7 +62,15 @@
|
|||||||
</ListRowItem>
|
</ListRowItem>
|
||||||
</ListRow>
|
</ListRow>
|
||||||
</ListRows>
|
</ListRows>
|
||||||
<ListSelectBanner />
|
<ListSelectBanner>
|
||||||
|
<template #actions="{ selections, unselectAll }">
|
||||||
|
<Button variant="subtle" label="Edit" @click="editValues(selections, unselectAll)">
|
||||||
|
<template #prefix>
|
||||||
|
<EditIcon class="h-3 w-3" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</ListSelectBanner>
|
||||||
</ListView>
|
</ListView>
|
||||||
<ListFooter
|
<ListFooter
|
||||||
v-if="pageLengthCount"
|
v-if="pageLengthCount"
|
||||||
@ -74,9 +82,18 @@
|
|||||||
}"
|
}"
|
||||||
@loadMore="emit('loadMore')"
|
@loadMore="emit('loadMore')"
|
||||||
/>
|
/>
|
||||||
|
<EditValueModal
|
||||||
|
v-model="showEditModal"
|
||||||
|
v-model:unselectAll="unselectAllAction"
|
||||||
|
doctype="Contact"
|
||||||
|
:selectedValues="selectedValues"
|
||||||
|
@reload="emit('reload')"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
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 {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
ListView,
|
ListView,
|
||||||
@ -87,7 +104,7 @@ import {
|
|||||||
ListRowItem,
|
ListRowItem,
|
||||||
ListFooter,
|
ListFooter,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
rows: {
|
rows: {
|
||||||
@ -108,7 +125,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['loadMore', 'updatePageCount'])
|
const emit = defineEmits(['loadMore', 'updatePageCount', 'reload'])
|
||||||
|
|
||||||
const pageLengthCount = defineModel()
|
const pageLengthCount = defineModel()
|
||||||
|
|
||||||
@ -116,4 +133,14 @@ watch(pageLengthCount, (val, old_value) => {
|
|||||||
if (val === old_value) return
|
if (val === old_value) return
|
||||||
emit('updatePageCount', val)
|
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
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -85,7 +85,15 @@
|
|||||||
</ListRowItem>
|
</ListRowItem>
|
||||||
</ListRow>
|
</ListRow>
|
||||||
</ListRows>
|
</ListRows>
|
||||||
<ListSelectBanner />
|
<ListSelectBanner>
|
||||||
|
<template #actions="{ selections, unselectAll }">
|
||||||
|
<Button variant="subtle" label="Edit" @click="editValues(selections, unselectAll)">
|
||||||
|
<template #prefix>
|
||||||
|
<EditIcon class="h-3 w-3" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</ListSelectBanner>
|
||||||
</ListView>
|
</ListView>
|
||||||
<ListFooter
|
<ListFooter
|
||||||
v-if="pageLengthCount"
|
v-if="pageLengthCount"
|
||||||
@ -97,12 +105,21 @@
|
|||||||
}"
|
}"
|
||||||
@loadMore="emit('loadMore')"
|
@loadMore="emit('loadMore')"
|
||||||
/>
|
/>
|
||||||
|
<EditValueModal
|
||||||
|
v-model="showEditModal"
|
||||||
|
v-model:unselectAll="unselectAllAction"
|
||||||
|
doctype="CRM Deal"
|
||||||
|
:selectedValues="selectedValues"
|
||||||
|
@reload="emit('reload')"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
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 {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
ListView,
|
ListView,
|
||||||
@ -113,7 +130,7 @@ import {
|
|||||||
ListSelectBanner,
|
ListSelectBanner,
|
||||||
ListFooter,
|
ListFooter,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
rows: {
|
rows: {
|
||||||
@ -134,7 +151,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['loadMore', 'updatePageCount'])
|
const emit = defineEmits(['loadMore', 'updatePageCount', 'reload'])
|
||||||
|
|
||||||
const pageLengthCount = defineModel()
|
const pageLengthCount = defineModel()
|
||||||
|
|
||||||
@ -142,4 +159,14 @@ watch(pageLengthCount, (val, old_value) => {
|
|||||||
if (val === old_value) return
|
if (val === old_value) return
|
||||||
emit('updatePageCount', val)
|
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
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -48,12 +48,23 @@
|
|||||||
</ListRows>
|
</ListRows>
|
||||||
<ListSelectBanner>
|
<ListSelectBanner>
|
||||||
<template #actions="{ selections, unselectAll }">
|
<template #actions="{ selections, unselectAll }">
|
||||||
<Button
|
<div class="flex gap-2">
|
||||||
theme="red"
|
<Button
|
||||||
variant="subtle"
|
theme="red"
|
||||||
label="Delete"
|
variant="subtle"
|
||||||
@click="deleteEmailTemplate(selections, unselectAll)"
|
label="Delete"
|
||||||
/>
|
@click="deleteEmailTemplate(selections, unselectAll)"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
label="Edit"
|
||||||
|
@click="editValues(selections, unselectAll)"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<EditIcon class="h-3 w-3" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ListSelectBanner>
|
</ListSelectBanner>
|
||||||
</ListView>
|
</ListView>
|
||||||
@ -66,8 +77,17 @@
|
|||||||
}"
|
}"
|
||||||
@loadMore="emit('loadMore')"
|
@loadMore="emit('loadMore')"
|
||||||
/>
|
/>
|
||||||
|
<EditValueModal
|
||||||
|
v-model="showEditModal"
|
||||||
|
v-model:unselectAll="unselectAllAction"
|
||||||
|
doctype="Email Template"
|
||||||
|
:selectedValues="selectedValues"
|
||||||
|
@reload="emit('reload')"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import EditIcon from '@/components/Icons/EditIcon.vue'
|
||||||
|
import EditValueModal from '@/components/Modals/EditValueModal.vue'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import {
|
import {
|
||||||
ListView,
|
ListView,
|
||||||
@ -79,7 +99,7 @@ import {
|
|||||||
ListFooter,
|
ListFooter,
|
||||||
call,
|
call,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { defineModel, watch } from 'vue'
|
import { ref, defineModel, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
rows: {
|
rows: {
|
||||||
@ -100,7 +120,12 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['loadMore', 'updatePageCount', 'showEmailTemplate', 'reload'])
|
const emit = defineEmits([
|
||||||
|
'loadMore',
|
||||||
|
'updatePageCount',
|
||||||
|
'showEmailTemplate',
|
||||||
|
'reload',
|
||||||
|
])
|
||||||
|
|
||||||
const pageLengthCount = defineModel()
|
const pageLengthCount = defineModel()
|
||||||
|
|
||||||
@ -143,4 +168,14 @@ function deleteEmailTemplate(selections, unselectAll) {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showEditModal = ref(false)
|
||||||
|
const selectedValues = ref([])
|
||||||
|
const unselectAllAction = ref(() => {})
|
||||||
|
|
||||||
|
function editValues(selections, unselectAll) {
|
||||||
|
selectedValues.value = selections
|
||||||
|
showEditModal.value = true
|
||||||
|
unselectAllAction.value = unselectAll
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -94,7 +94,15 @@
|
|||||||
</ListRowItem>
|
</ListRowItem>
|
||||||
</ListRow>
|
</ListRow>
|
||||||
</ListRows>
|
</ListRows>
|
||||||
<ListSelectBanner />
|
<ListSelectBanner>
|
||||||
|
<template #actions="{ selections, unselectAll }">
|
||||||
|
<Button variant="subtle" label="Edit" @click="editValues(selections, unselectAll)">
|
||||||
|
<template #prefix>
|
||||||
|
<EditIcon class="h-3 w-3" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</ListSelectBanner>
|
||||||
</ListView>
|
</ListView>
|
||||||
<ListFooter
|
<ListFooter
|
||||||
v-if="pageLengthCount"
|
v-if="pageLengthCount"
|
||||||
@ -106,12 +114,21 @@
|
|||||||
}"
|
}"
|
||||||
@loadMore="emit('loadMore')"
|
@loadMore="emit('loadMore')"
|
||||||
/>
|
/>
|
||||||
|
<EditValueModal
|
||||||
|
v-model="showEditModal"
|
||||||
|
v-model:unselectAll="unselectAllAction"
|
||||||
|
doctype="CRM Lead"
|
||||||
|
:selectedValues="selectedValues"
|
||||||
|
@reload="emit('reload')"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
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 {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
ListView,
|
ListView,
|
||||||
@ -122,7 +139,7 @@ import {
|
|||||||
ListRowItem,
|
ListRowItem,
|
||||||
ListFooter,
|
ListFooter,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
rows: {
|
rows: {
|
||||||
@ -143,7 +160,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['loadMore', 'updatePageCount'])
|
const emit = defineEmits(['loadMore', 'updatePageCount', 'reload'])
|
||||||
|
|
||||||
const pageLengthCount = defineModel()
|
const pageLengthCount = defineModel()
|
||||||
|
|
||||||
@ -151,4 +168,14 @@ watch(pageLengthCount, (val, old_value) => {
|
|||||||
if (val === old_value) return
|
if (val === old_value) return
|
||||||
emit('updatePageCount', val)
|
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
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -49,7 +49,15 @@
|
|||||||
</ListRowItem>
|
</ListRowItem>
|
||||||
</ListRow>
|
</ListRow>
|
||||||
</ListRows>
|
</ListRows>
|
||||||
<ListSelectBanner />
|
<ListSelectBanner>
|
||||||
|
<template #actions="{ selections, unselectAll }">
|
||||||
|
<Button variant="subtle" label="Edit" @click="editValues(selections, unselectAll)">
|
||||||
|
<template #prefix>
|
||||||
|
<EditIcon class="h-3 w-3" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</ListSelectBanner>
|
||||||
</ListView>
|
</ListView>
|
||||||
<ListFooter
|
<ListFooter
|
||||||
class="border-t px-5 py-2"
|
class="border-t px-5 py-2"
|
||||||
@ -60,8 +68,17 @@
|
|||||||
}"
|
}"
|
||||||
@loadMore="emit('loadMore')"
|
@loadMore="emit('loadMore')"
|
||||||
/>
|
/>
|
||||||
|
<EditValueModal
|
||||||
|
v-model="showEditModal"
|
||||||
|
v-model:unselectAll="unselectAllAction"
|
||||||
|
doctype="CRM Organization"
|
||||||
|
:selectedValues="selectedValues"
|
||||||
|
@reload="emit('reload')"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import EditIcon from '@/components/Icons/EditIcon.vue'
|
||||||
|
import EditValueModal from '@/components/Modals/EditValueModal.vue'
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
ListView,
|
ListView,
|
||||||
@ -72,7 +89,7 @@ import {
|
|||||||
ListRowItem,
|
ListRowItem,
|
||||||
ListFooter,
|
ListFooter,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
rows: {
|
rows: {
|
||||||
@ -93,7 +110,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['loadMore', 'updatePageCount'])
|
const emit = defineEmits(['loadMore', 'updatePageCount', 'reload'])
|
||||||
|
|
||||||
const pageLengthCount = defineModel()
|
const pageLengthCount = defineModel()
|
||||||
|
|
||||||
@ -101,4 +118,14 @@ watch(pageLengthCount, (val, old_value) => {
|
|||||||
if (val === old_value) return
|
if (val === old_value) return
|
||||||
emit('updatePageCount', val)
|
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
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -65,12 +65,23 @@
|
|||||||
</ListRows>
|
</ListRows>
|
||||||
<ListSelectBanner>
|
<ListSelectBanner>
|
||||||
<template #actions="{ selections, unselectAll }">
|
<template #actions="{ selections, unselectAll }">
|
||||||
<Button
|
<div class="flex gap-2">
|
||||||
theme="red"
|
<Button
|
||||||
variant="subtle"
|
theme="red"
|
||||||
label="Delete"
|
variant="subtle"
|
||||||
@click="deleteTask(selections, unselectAll)"
|
label="Delete"
|
||||||
/>
|
@click="deleteTask(selections, unselectAll)"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
label="Edit"
|
||||||
|
@click="editValues(selections, unselectAll)"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<EditIcon class="h-3 w-3" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ListSelectBanner>
|
</ListSelectBanner>
|
||||||
</ListView>
|
</ListView>
|
||||||
@ -83,11 +94,20 @@
|
|||||||
}"
|
}"
|
||||||
@loadMore="emit('loadMore')"
|
@loadMore="emit('loadMore')"
|
||||||
/>
|
/>
|
||||||
|
<EditValueModal
|
||||||
|
v-model="showEditModal"
|
||||||
|
v-model:unselectAll="unselectAllAction"
|
||||||
|
doctype="CRM Task"
|
||||||
|
:selectedValues="selectedValues"
|
||||||
|
@reload="emit('reload')"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import TaskStatusIcon from '@/components/Icons/TaskStatusIcon.vue'
|
import TaskStatusIcon from '@/components/Icons/TaskStatusIcon.vue'
|
||||||
import TaskPriorityIcon from '@/components/Icons/TaskPriorityIcon.vue'
|
import TaskPriorityIcon from '@/components/Icons/TaskPriorityIcon.vue'
|
||||||
import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
|
import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
|
||||||
|
import EditIcon from '@/components/Icons/EditIcon.vue'
|
||||||
|
import EditValueModal from '@/components/Modals/EditValueModal.vue'
|
||||||
import { dateFormat } from '@/utils'
|
import { dateFormat } from '@/utils'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import {
|
import {
|
||||||
@ -102,7 +122,7 @@ import {
|
|||||||
call,
|
call,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { defineModel, watch } from 'vue'
|
import { ref, defineModel, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
rows: {
|
rows: {
|
||||||
@ -166,4 +186,14 @@ function deleteTask(selections, unselectAll) {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showEditModal = ref(false)
|
||||||
|
const selectedValues = ref([])
|
||||||
|
const unselectAllAction = ref(() => {})
|
||||||
|
|
||||||
|
function editValues(selections, unselectAll) {
|
||||||
|
selectedValues.value = selections
|
||||||
|
showEditModal.value = true
|
||||||
|
unselectAllAction.value = unselectAll
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
165
frontend/src/components/Modals/EditValueModal.vue
Normal file
165
frontend/src/components/Modals/EditValueModal.vue
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog v-model="show" :options="{ title: 'Bulk Edit' }">
|
||||||
|
<template #body-content>
|
||||||
|
<div class="mb-4">
|
||||||
|
<div class="mb-1.5 text-sm text-gray-600">Field</div>
|
||||||
|
<Autocomplete
|
||||||
|
:value="field.label"
|
||||||
|
:options="fields.data"
|
||||||
|
@change="(e) => changeField(e)"
|
||||||
|
placeholder="Select Field..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="mb-1.5 text-sm text-gray-600">Value</div>
|
||||||
|
<component
|
||||||
|
:is="getValueComponent(field)"
|
||||||
|
:value="newValue"
|
||||||
|
size="md"
|
||||||
|
@change="(v) => updateValue(v)"
|
||||||
|
placeholder="Value"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #actions>
|
||||||
|
<Button
|
||||||
|
class="w-full"
|
||||||
|
variant="solid"
|
||||||
|
@click="updateValues"
|
||||||
|
:loading="loading"
|
||||||
|
:label="`Update ${recordCount} Records`"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import DatePicker from '@/components/Controls/DatePicker.vue'
|
||||||
|
import Link from '@/components/Controls/Link.vue'
|
||||||
|
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
|
||||||
|
import { FormControl, call, createResource, TextEditor } from 'frappe-ui'
|
||||||
|
import { ref, computed, defineModel, onMounted, h } from 'vue'
|
||||||
|
|
||||||
|
const typeCheck = ['Check']
|
||||||
|
const typeLink = ['Link', 'Dynamic Link']
|
||||||
|
const typeNumber = ['Float', 'Int', 'Currency', 'Percent']
|
||||||
|
const typeSelect = ['Select']
|
||||||
|
const typeEditor = ['Text Editor']
|
||||||
|
const typeDate = ['Date', 'Datetime']
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
doctype: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
selectedValues: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const show = defineModel()
|
||||||
|
const unselectAll = defineModel('unselectAll')
|
||||||
|
|
||||||
|
const emit = defineEmits(['reload'])
|
||||||
|
|
||||||
|
const fields = createResource({
|
||||||
|
url: 'crm.api.doc.get_fields',
|
||||||
|
cache: ['fields', props.doctype],
|
||||||
|
params: {
|
||||||
|
doctype: props.doctype,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (fields.data?.length) return
|
||||||
|
fields.fetch()
|
||||||
|
})
|
||||||
|
|
||||||
|
const recordCount = computed(() => props.selectedValues?.size || 0)
|
||||||
|
|
||||||
|
const field = ref({
|
||||||
|
label: '',
|
||||||
|
type: '',
|
||||||
|
value: '',
|
||||||
|
options: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const newValue = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
function updateValues() {
|
||||||
|
let fieldVal = newValue.value
|
||||||
|
if (field.value.type == 'Check') {
|
||||||
|
fieldVal = fieldVal == 'Yes' ? 1 : 0
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
call(
|
||||||
|
'frappe.desk.doctype.bulk_update.bulk_update.submit_cancel_or_update_docs',
|
||||||
|
{
|
||||||
|
doctype: props.doctype,
|
||||||
|
docnames: Array.from(props.selectedValues),
|
||||||
|
action: 'update',
|
||||||
|
data: {
|
||||||
|
[field.value.value]: fieldVal || null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
field.value = {
|
||||||
|
label: '',
|
||||||
|
type: '',
|
||||||
|
value: '',
|
||||||
|
options: '',
|
||||||
|
}
|
||||||
|
newValue.value = ''
|
||||||
|
loading.value = false
|
||||||
|
show.value = false
|
||||||
|
unselectAll.value()
|
||||||
|
emit('reload')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeField(f) {
|
||||||
|
newValue.value = ''
|
||||||
|
if (!f) return
|
||||||
|
field.value = f
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateValue(v) {
|
||||||
|
let value = v.target ? v.target.value : v
|
||||||
|
newValue.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValueComponent(f) {
|
||||||
|
const { type, options } = f
|
||||||
|
if (typeSelect.includes(type) || typeCheck.includes(type)) {
|
||||||
|
const _options = type == 'Check' ? ['Yes', 'No'] : getSelectOptions(options)
|
||||||
|
return h(FormControl, {
|
||||||
|
type: 'select',
|
||||||
|
options: _options.map((o) => ({
|
||||||
|
label: o,
|
||||||
|
value: o,
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
} else if (typeLink.includes(type)) {
|
||||||
|
if (type == 'Dynamic Link') {
|
||||||
|
return h(FormControl, { type: 'text' })
|
||||||
|
}
|
||||||
|
return h(Link, { class: 'form-control', doctype: options })
|
||||||
|
} else if (typeNumber.includes(type)) {
|
||||||
|
return h(FormControl, { type: 'number' })
|
||||||
|
} else if (typeDate.includes(type)) {
|
||||||
|
return h(DatePicker)
|
||||||
|
} else if (typeEditor.includes(type)) {
|
||||||
|
return h(TextEditor, {
|
||||||
|
variant: 'outline',
|
||||||
|
editorClass:
|
||||||
|
'!prose-sm overflow-auto min-h-[80px] max-h-80 py-1.5 px-2 rounded border border-gray-300 bg-white hover:border-gray-400 hover:shadow-sm focus:bg-white focus:border-gray-500 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-gray-400 text-gray-800 transition-colors',
|
||||||
|
bubbleMenu: true,
|
||||||
|
content: newValue.value,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return h(FormControl, { type: 'text' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -21,6 +21,7 @@
|
|||||||
}"
|
}"
|
||||||
@loadMore="() => loadMore++"
|
@loadMore="() => loadMore++"
|
||||||
@updatePageCount="(count) => (updatedPageCount = count)"
|
@updatePageCount="(count) => (updatedPageCount = count)"
|
||||||
|
@reload="callLogs.reload()"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-else-if="callLogs.data"
|
v-else-if="callLogs.data"
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
}"
|
}"
|
||||||
@loadMore="() => loadMore++"
|
@loadMore="() => loadMore++"
|
||||||
@updatePageCount="(count) => (updatedPageCount = count)"
|
@updatePageCount="(count) => (updatedPageCount = count)"
|
||||||
|
@reload="contacts.reload()"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-else-if="contacts.data"
|
v-else-if="contacts.data"
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
}"
|
}"
|
||||||
@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
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
@loadMore="() => loadMore++"
|
@loadMore="() => loadMore++"
|
||||||
@updatePageCount="(count) => (updatedPageCount = count)"
|
@updatePageCount="(count) => (updatedPageCount = count)"
|
||||||
@showEmailTemplate="showEmailTemplate"
|
@showEmailTemplate="showEmailTemplate"
|
||||||
@reload="() => emailTemplates.reload()"
|
@reload="emailTemplates.reload()"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-else-if="emailTemplates.data"
|
v-else-if="emailTemplates.data"
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
}"
|
}"
|
||||||
@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
|
||||||
|
|||||||
@ -30,6 +30,7 @@
|
|||||||
}"
|
}"
|
||||||
@loadMore="() => loadMore++"
|
@loadMore="() => loadMore++"
|
||||||
@updatePageCount="(count) => (updatedPageCount = count)"
|
@updatePageCount="(count) => (updatedPageCount = count)"
|
||||||
|
@reload="organizations.reload()"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-else-if="organizations.data"
|
v-else-if="organizations.data"
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
@loadMore="() => loadMore++"
|
@loadMore="() => loadMore++"
|
||||||
@updatePageCount="(count) => (updatedPageCount = count)"
|
@updatePageCount="(count) => (updatedPageCount = count)"
|
||||||
@showTask="showTask"
|
@showTask="showTask"
|
||||||
@reload="() => tasks.reload()"
|
@reload="tasks.reload()"
|
||||||
/>
|
/>
|
||||||
<div v-else-if="tasks.data" class="flex h-full items-center justify-center">
|
<div v-else-if="tasks.data" class="flex h-full items-center justify-center">
|
||||||
<div
|
<div
|
||||||
@ -86,6 +86,7 @@ const rows = computed(() => {
|
|||||||
const showTaskModal = ref(false)
|
const showTaskModal = ref(false)
|
||||||
|
|
||||||
const task = ref({
|
const task = ref({
|
||||||
|
name: '',
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
assigned_to: '',
|
assigned_to: '',
|
||||||
@ -99,6 +100,7 @@ const task = ref({
|
|||||||
function showTask(name) {
|
function showTask(name) {
|
||||||
let t = rows.value?.find((row) => row.name === name)
|
let t = rows.value?.find((row) => row.name === name)
|
||||||
task.value = {
|
task.value = {
|
||||||
|
name: t.name,
|
||||||
title: t.title,
|
title: t.title,
|
||||||
description: t.description,
|
description: t.description,
|
||||||
assigned_to: t.assigned_to?.email || '',
|
assigned_to: t.assigned_to?.email || '',
|
||||||
@ -113,6 +115,7 @@ function showTask(name) {
|
|||||||
|
|
||||||
function createTask() {
|
function createTask() {
|
||||||
task.value = {
|
task.value = {
|
||||||
|
name: '',
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
assigned_to: '',
|
assigned_to: '',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user