Merge branch 'frappe:develop' into translation

This commit is contained in:
Shariq Ansari 2024-04-15 14:40:11 +05:30 committed by GitHub
commit 3a6514243d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 257 additions and 54 deletions

View File

@ -240,7 +240,8 @@ def get_list_data(
"views": get_views(doctype), "views": get_views(doctype),
"total_count": len(frappe.get_list(doctype, filters=filters)), "total_count": len(frappe.get_list(doctype, filters=filters)),
"row_count": len(data), "row_count": len(data),
"form_script": get_form_script(doctype) "form_script": get_form_script(doctype),
"list_script": get_form_script(doctype, "List"),
} }

View File

@ -4,9 +4,6 @@ from frappe.query_builder import Order
@frappe.whitelist() @frappe.whitelist()
def get_notifications(): def get_notifications():
if frappe.session.user == "Guest":
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
Notification = frappe.qb.DocType("CRM Notification") Notification = frappe.qb.DocType("CRM Notification")
query = ( query = (
frappe.qb.from_(Notification) frappe.qb.from_(Notification)
@ -46,9 +43,6 @@ def get_notifications():
@frappe.whitelist() @frappe.whitelist()
def mark_as_read(user=None, comment=None): def mark_as_read(user=None, comment=None):
if frappe.session.user == "Guest":
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
user = user or frappe.session.user user = user or frappe.session.user
filters = {"to_user": user, "read": False} filters = {"to_user": user, "read": False}
if comment: if comment:

View File

@ -1,11 +1,8 @@
import frappe import frappe
@frappe.whitelist(allow_guest=True) @frappe.whitelist()
def get_users(): def get_users():
if frappe.session.user == "Guest":
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
users = frappe.qb.get_query( users = frappe.qb.get_query(
"User", "User",
fields=["name", "email", "enabled", "user_image", "full_name", "user_type"], fields=["name", "email", "enabled", "user_image", "full_name", "user_type"],
@ -24,9 +21,6 @@ def get_users():
@frappe.whitelist() @frappe.whitelist()
def get_contacts(): def get_contacts():
if frappe.session.user == "Guest":
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
contacts = frappe.get_all( contacts = frappe.get_all(
"Contact", "Contact",
fields=[ fields=[
@ -66,9 +60,6 @@ def get_contacts():
@frappe.whitelist() @frappe.whitelist()
def get_lead_contacts(): def get_lead_contacts():
if frappe.session.user == "Guest":
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
lead_contacts = frappe.get_all( lead_contacts = frappe.get_all(
"CRM Lead", "CRM Lead",
fields=[ fields=[
@ -88,9 +79,6 @@ def get_lead_contacts():
@frappe.whitelist() @frappe.whitelist()
def get_organizations(): def get_organizations():
if frappe.session.user == "Guest":
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
organizations = frappe.qb.get_query( organizations = frappe.qb.get_query(
"CRM Organization", "CRM Organization",
fields=['*'], fields=['*'],

View File

@ -4,9 +4,6 @@ from pypika import Criterion
@frappe.whitelist() @frappe.whitelist()
def get_views(doctype): def get_views(doctype):
if frappe.session.user == "Guest":
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
View = frappe.qb.DocType("CRM View Settings") View = frappe.qb.DocType("CRM View Settings")
query = ( query = (
frappe.qb.from_(View) frappe.qb.from_(View)

View File

@ -93,7 +93,7 @@ def get_call_log(name):
if c: if c:
return [c.full_name, c.image] return [c.full_name, c.image]
return [None, None] return [None, None]
def get_lead_contact(number): def get_lead_contact(number):
l = frappe.db.get_value("CRM Lead", {"mobile_no": number, "converted": 0}, ["lead_name", "image"], as_dict=True) l = frappe.db.get_value("CRM Lead", {"mobile_no": number, "converted": 0}, ["lead_name", "image"], as_dict=True)
if l: if l:

View File

@ -1,8 +1,38 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt // For license information, please see license.txt
// frappe.ui.form.on("CRM Form Script", { frappe.ui.form.on("CRM Form Script", {
// refresh(frm) { refresh(frm) {
frm.set_query("dt", {
filters: {
istable: 0,
},
});
},
view(frm) {
let has_form_boilerplate = frm.doc.script.includes(
"function setupForm("
);
let has_list_boilerplate = frm.doc.script.includes(
"function setupList("
);
// }, if (frm.doc.view == "Form" && !has_form_boilerplate) {
// }); frm.doc.script = `
function setupForm({ doc }) {
return {
actions: [],
}
}`.trim();
}
if (frm.doc.view == "List" && !has_list_boilerplate) {
frm.doc.script = `
function setupList({ list }) {
return {
actions: [],
bulk_actions: [],
}
}`.trim();
}
},
});

View File

@ -7,6 +7,7 @@
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"dt", "dt",
"view",
"column_break_gboh", "column_break_gboh",
"enabled", "enabled",
"section_break_xeox", "section_break_xeox",
@ -36,16 +37,26 @@
"label": "Enabled" "label": "Enabled"
}, },
{ {
"default": "function setupForm({ doc }) {\n return {\n actions: []\n }\n}", "default": "function setupForm({ doc }) {\n return {\n actions: [],\n }\n}",
"documentation_url": "https://docs.frappe.io/crm/custom-actions",
"fieldname": "script", "fieldname": "script",
"fieldtype": "Code", "fieldtype": "Code",
"label": "Script", "label": "Script",
"options": "JS" "options": "JS"
},
{
"default": "Form",
"fieldname": "view",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Apply To",
"options": "Form\nList",
"set_only_once": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2024-01-19 21:52:46.078626", "modified": "2024-04-10 18:27:17.617602",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "FCRM", "module": "FCRM",
"name": "CRM Form Script", "name": "CRM Form Script",

View File

@ -14,6 +14,7 @@ class CRMFormScript(Document):
if self.dt and self.enabled: if self.dt and self.enabled:
filters = { filters = {
"dt": self.dt, "dt": self.dt,
"view": self.view,
"enabled": 1, "enabled": 1,
} }
if self.name: if self.name:
@ -27,13 +28,14 @@ class CRMFormScript(Document):
frappe.DuplicateEntryError, frappe.DuplicateEntryError,
) )
def get_form_script(dt): def get_form_script(dt, view="Form"):
"""Returns the script for the given doctype""" """Returns the form script for the given doctype"""
FormScript = frappe.qb.DocType("CRM Form Script") FormScript = frappe.qb.DocType("CRM Form Script")
query = ( query = (
frappe.qb.from_(FormScript) frappe.qb.from_(FormScript)
.select("script") .select("script")
.where(FormScript.dt == dt) .where(FormScript.dt == dt)
.where(FormScript.view == view)
.where(FormScript.enabled == 1) .where(FormScript.enabled == 1)
.limit(1) .limit(1)
) )

View File

@ -327,7 +327,7 @@ function getValSelect(f) {
if (fieldtype == 'Dynamic Link') { if (fieldtype == 'Dynamic Link') {
return h(FormControl, { type: 'text' }) return h(FormControl, { type: 'text' })
} }
return h(Link, { class: 'form-control', doctype: options }) return h(Link, { class: 'form-control', doctype: options, value: f.value })
} else if (typeNumber.includes(fieldtype)) { } else if (typeNumber.includes(fieldtype)) {
return h(FormControl, { type: 'number' }) return h(FormControl, { type: 'number' })
} else if (typeDate.includes(fieldtype) && operator == 'between') { } else if (typeDate.includes(fieldtype) && operator == 'between') {
@ -437,8 +437,6 @@ function updateOperator(event, filter) {
function isSameTypeOperator(oldOperator, newOperator) { function isSameTypeOperator(oldOperator, newOperator) {
let textOperators = [ let textOperators = [
'like',
'not like',
'equals', 'equals',
'not equals', 'not equals',
'in', 'in',

View File

@ -103,7 +103,7 @@ import {
Dropdown, Dropdown,
call, call,
} from 'frappe-ui' } from 'frappe-ui'
import { setupBulkActions, createToast } from '@/utils' import { setupListActions, createToast } from '@/utils'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { onMounted, ref, watch } from 'vue' import { onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
@ -181,6 +181,7 @@ function deleteValues(selections, unselectAll) {
} }
const customBulkActions = ref([]) const customBulkActions = ref([])
const customListActions = ref([])
function bulkActions(selections, unselectAll) { function bulkActions(selections, unselectAll) {
let actions = [ let actions = [
@ -209,7 +210,18 @@ function bulkActions(selections, unselectAll) {
onMounted(() => { onMounted(() => {
if (!list.value?.data) return if (!list.value?.data) return
setupBulkActions(list.value.data) setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
customBulkActions.value = list.value?.data?.bulkActions || [] customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
defineExpose({
customListActions,
}) })
</script> </script>

View File

@ -103,7 +103,7 @@
import PhoneIcon from '@/components/Icons/PhoneIcon.vue' import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import EditValueModal from '@/components/Modals/EditValueModal.vue' import EditValueModal from '@/components/Modals/EditValueModal.vue'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { createToast } from '@/utils' import { setupListActions, createToast } from '@/utils'
import { import {
Avatar, Avatar,
ListView, ListView,
@ -117,7 +117,8 @@ import {
Dropdown, Dropdown,
call, call,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, watch } from 'vue' import { ref, watch, onMounted } from 'vue'
import { useRouter } from 'vue-router'
const props = defineProps({ const props = defineProps({
rows: { rows: {
@ -150,6 +151,8 @@ const emit = defineEmits([
const pageLengthCount = defineModel() const pageLengthCount = defineModel()
const list = defineModel('list') const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore() const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => { watch(pageLengthCount, (val, old_value) => {
@ -199,6 +202,8 @@ function deleteValues(selections, unselectAll) {
}) })
} }
const customListActions = ref([])
function bulkActions(selections, unselectAll) { function bulkActions(selections, unselectAll) {
let actions = [ let actions = [
{ {
@ -212,4 +217,21 @@ function bulkActions(selections, unselectAll) {
] ]
return actions 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 || []
})
defineExpose({
customListActions,
})
</script> </script>

View File

@ -146,7 +146,7 @@ import {
call, call,
Tooltip, Tooltip,
} from 'frappe-ui' } from 'frappe-ui'
import { setupBulkActions, createToast } from '@/utils' import { setupListActions, createToast } from '@/utils'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { onMounted, ref, watch } from 'vue' import { onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
@ -234,6 +234,7 @@ function deleteValues(selections, unselectAll) {
} }
const customBulkActions = ref([]) const customBulkActions = ref([])
const customListActions = ref([])
function bulkActions(selections, unselectAll) { function bulkActions(selections, unselectAll) {
let actions = [ let actions = [
@ -266,7 +267,18 @@ function bulkActions(selections, unselectAll) {
onMounted(() => { onMounted(() => {
if (!list.value?.data) return if (!list.value?.data) return
setupBulkActions(list.value.data) setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
customBulkActions.value = list.value?.data?.bulkActions || [] customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
defineExpose({
customListActions,
}) })
</script> </script>

View File

@ -85,7 +85,7 @@
<script setup> <script setup>
import EditValueModal from '@/components/Modals/EditValueModal.vue' import EditValueModal from '@/components/Modals/EditValueModal.vue'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { createToast } from '@/utils' import { setupListActions, createToast } from '@/utils'
import { import {
ListView, ListView,
ListHeader, ListHeader,
@ -98,7 +98,8 @@ import {
call, call,
Tooltip, Tooltip,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, watch } from 'vue' import { ref, watch, onMounted } from 'vue'
import { useRouter } from 'vue-router'
const props = defineProps({ const props = defineProps({
rows: { rows: {
@ -132,6 +133,8 @@ const emit = defineEmits([
const pageLengthCount = defineModel() const pageLengthCount = defineModel()
const list = defineModel('list') const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore() const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => { watch(pageLengthCount, (val, old_value) => {
@ -181,6 +184,8 @@ function deleteValues(selections, unselectAll) {
}) })
} }
const customListActions = ref([])
function bulkActions(selections, unselectAll) { function bulkActions(selections, unselectAll) {
let actions = [ let actions = [
{ {
@ -194,4 +199,21 @@ function bulkActions(selections, unselectAll) {
] ]
return actions 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 || []
})
defineExpose({
customListActions,
})
</script> </script>

View File

@ -155,7 +155,7 @@ import {
call, call,
Tooltip, Tooltip,
} from 'frappe-ui' } from 'frappe-ui'
import { setupBulkActions, createToast } from '@/utils' import { setupListActions, createToast } from '@/utils'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { onMounted, ref, watch } from 'vue' import { onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
@ -243,6 +243,7 @@ function deleteValues(selections, unselectAll) {
} }
const customBulkActions = ref([]) const customBulkActions = ref([])
const customListActions = ref([])
function bulkActions(selections, unselectAll) { function bulkActions(selections, unselectAll) {
let actions = [ let actions = [
@ -275,7 +276,18 @@ function bulkActions(selections, unselectAll) {
onMounted(() => { onMounted(() => {
if (!list.value?.data) return if (!list.value?.data) return
setupBulkActions(list.value.data) setupListActions(list.value.data, {
list: list.value,
call,
createToast,
$dialog,
router,
})
customBulkActions.value = list.value?.data?.bulkActions || [] customBulkActions.value = list.value?.data?.bulkActions || []
customListActions.value = list.value?.data?.listActions || []
})
defineExpose({
customListActions,
}) })
</script> </script>

View File

@ -88,7 +88,7 @@
<script setup> <script setup>
import EditValueModal from '@/components/Modals/EditValueModal.vue' import EditValueModal from '@/components/Modals/EditValueModal.vue'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { createToast } from '@/utils' import { setupListActions, createToast } from '@/utils'
import { import {
Avatar, Avatar,
ListView, ListView,
@ -102,7 +102,8 @@ import {
Dropdown, Dropdown,
call, call,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, watch } from 'vue' import { ref, watch, onMounted } from 'vue'
import { useRouter } from 'vue-router'
const props = defineProps({ const props = defineProps({
rows: { rows: {
@ -135,6 +136,8 @@ const emit = defineEmits([
const pageLengthCount = defineModel() const pageLengthCount = defineModel()
const list = defineModel('list') const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore() const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => { watch(pageLengthCount, (val, old_value) => {
@ -184,6 +187,8 @@ function deleteValues(selections, unselectAll) {
}) })
} }
const customListActions = ref([])
function bulkActions(selections, unselectAll) { function bulkActions(selections, unselectAll) {
let actions = [ let actions = [
{ {
@ -197,4 +202,21 @@ function bulkActions(selections, unselectAll) {
] ]
return actions 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 || []
})
defineExpose({
customListActions,
})
</script> </script>

View File

@ -106,7 +106,7 @@ import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
import EditValueModal from '@/components/Modals/EditValueModal.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 { createToast } from '@/utils' import { setupListActions, createToast } from '@/utils'
import { import {
Avatar, Avatar,
ListView, ListView,
@ -120,7 +120,8 @@ import {
call, call,
Tooltip, Tooltip,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, watch } from 'vue' import { ref, watch, onMounted } from 'vue'
import { useRouter } from 'vue-router'
const props = defineProps({ const props = defineProps({
rows: { rows: {
@ -154,6 +155,8 @@ const emit = defineEmits([
const pageLengthCount = defineModel() const pageLengthCount = defineModel()
const list = defineModel('list') const list = defineModel('list')
const router = useRouter()
const { $dialog } = globalStore() const { $dialog } = globalStore()
watch(pageLengthCount, (val, old_value) => { watch(pageLengthCount, (val, old_value) => {
@ -203,6 +206,8 @@ function deleteValues(selections, unselectAll) {
}) })
} }
const customListActions = ref([])
function bulkActions(selections, unselectAll) { function bulkActions(selections, unselectAll) {
let actions = [ let actions = [
{ {
@ -216,4 +221,21 @@ function bulkActions(selections, unselectAll) {
] ]
return actions 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 || []
})
defineExpose({
customListActions,
})
</script> </script>

View File

@ -3,6 +3,12 @@
<template #left-header> <template #left-header>
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header>
<CustomActions
v-if="callLogsListView?.customListActions"
:actions="callLogsListView.customListActions"
/>
</template>
</LayoutHeader> </LayoutHeader>
<ViewControls <ViewControls
ref="viewControls" ref="viewControls"
@ -13,6 +19,7 @@
doctype="CRM Call Log" doctype="CRM Call Log"
/> />
<CallLogsListView <CallLogsListView
ref="callLogsListView"
v-if="callLogs.data && rows.length" v-if="callLogs.data && rows.length"
v-model="callLogs.data.page_length_count" v-model="callLogs.data.page_length_count"
v-model:list="callLogs" v-model:list="callLogs"
@ -43,6 +50,7 @@
</template> </template>
<script setup> <script setup>
import CustomActions from '@/components/CustomActions.vue'
import PhoneIcon from '@/components/Icons/PhoneIcon.vue' import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import ViewControls from '@/components/ViewControls.vue' import ViewControls from '@/components/ViewControls.vue'
@ -63,6 +71,8 @@ const { getContact, getLeadContact } = contactsStore()
const breadcrumbs = [{ label: 'Call Logs', route: { name: 'Call Logs' } }] const breadcrumbs = [{ label: 'Call Logs', route: { name: 'Call Logs' } }]
const callLogsListView = ref(null)
// callLogs data is loaded in the ViewControls component // callLogs data is loaded in the ViewControls component
const callLogs = ref({}) const callLogs = ref({})
const loadMore = ref(1) const loadMore = ref(1)

View File

@ -4,6 +4,10 @@
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header> <template #right-header>
<CustomActions
v-if="contactsListView?.customListActions"
:actions="contactsListView.customListActions"
/>
<Button variant="solid" label="Create" @click="showContactModal = true"> <Button variant="solid" label="Create" @click="showContactModal = true">
<template #prefix><FeatherIcon name="plus" class="h-4" /></template> <template #prefix><FeatherIcon name="plus" class="h-4" /></template>
</Button> </Button>
@ -18,6 +22,7 @@
doctype="Contact" doctype="Contact"
/> />
<ContactsListView <ContactsListView
ref="contactsListView"
v-if="contacts.data && rows.length" v-if="contacts.data && rows.length"
v-model="contacts.data.page_length_count" v-model="contacts.data.page_length_count"
v-model:list="contacts" v-model:list="contacts"
@ -52,6 +57,7 @@
</template> </template>
<script setup> <script setup>
import CustomActions from '@/components/CustomActions.vue'
import ContactsIcon from '@/components/Icons/ContactsIcon.vue' import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import ContactModal from '@/components/Modals/ContactModal.vue' import ContactModal from '@/components/Modals/ContactModal.vue'
@ -87,6 +93,8 @@ const breadcrumbs = computed(() => {
return items return items
}) })
const contactsListView = ref(null)
// contacts data is loaded in the ViewControls component // contacts data is loaded in the ViewControls component
const contacts = ref({}) const contacts = ref({})
const loadMore = ref(1) const loadMore = ref(1)

View File

@ -4,6 +4,10 @@
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header> <template #right-header>
<CustomActions
v-if="dealsListView?.customListActions"
:actions="dealsListView.customListActions"
/>
<Button variant="solid" label="Create" @click="showNewDialog = true"> <Button variant="solid" label="Create" @click="showNewDialog = true">
<template #prefix><FeatherIcon name="plus" class="h-4" /></template> <template #prefix><FeatherIcon name="plus" class="h-4" /></template>
</Button> </Button>
@ -18,6 +22,7 @@
doctype="CRM Deal" doctype="CRM Deal"
/> />
<DealsListView <DealsListView
ref="dealsListView"
v-if="deals.data && rows.length" v-if="deals.data && rows.length"
v-model="deals.data.page_length_count" v-model="deals.data.page_length_count"
v-model:list="deals" v-model:list="deals"
@ -65,6 +70,7 @@
</template> </template>
<script setup> <script setup>
import CustomActions from '@/components/CustomActions.vue'
import DealsIcon from '@/components/Icons/DealsIcon.vue' import DealsIcon from '@/components/Icons/DealsIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import DealsListView from '@/components/ListViews/DealsListView.vue' import DealsListView from '@/components/ListViews/DealsListView.vue'
@ -92,6 +98,8 @@ const { getDealStatus } = statusesStore()
const router = useRouter() const router = useRouter()
const dealsListView = ref(null)
// deals data is loaded in the ViewControls component // deals data is loaded in the ViewControls component
const deals = ref({}) const deals = ref({})
const loadMore = ref(1) const loadMore = ref(1)

View File

@ -4,6 +4,10 @@
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header> <template #right-header>
<CustomActions
v-if="emailTemplatesListView?.customListActions"
:actions="emailTemplatesListView.customListActions"
/>
<Button variant="solid" label="Create" @click="showEmailTemplateModal = true"> <Button variant="solid" label="Create" @click="showEmailTemplateModal = true">
<template #prefix><FeatherIcon name="plus" class="h-4" /></template> <template #prefix><FeatherIcon name="plus" class="h-4" /></template>
</Button> </Button>
@ -18,6 +22,7 @@
doctype="Email Template" doctype="Email Template"
/> />
<EmailTemplatesListView <EmailTemplatesListView
ref="emailTemplatesListView"
v-if="emailTemplates.data && rows.length" v-if="emailTemplates.data && rows.length"
v-model="emailTemplates.data.page_length_count" v-model="emailTemplates.data.page_length_count"
v-model:list="emailTemplates" v-model:list="emailTemplates"
@ -54,6 +59,7 @@
</template> </template>
<script setup> <script setup>
import CustomActions from '@/components/CustomActions.vue'
import EmailIcon from '@/components/Icons/EmailIcon.vue' import EmailIcon from '@/components/Icons/EmailIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import ViewControls from '@/components/ViewControls.vue' import ViewControls from '@/components/ViewControls.vue'
@ -67,6 +73,8 @@ const breadcrumbs = [
{ label: 'Email Templates', route: { name: 'Email Templates' } }, { label: 'Email Templates', route: { name: 'Email Templates' } },
] ]
const emailTemplatesListView = ref(null)
// emailTemplates data is loaded in the ViewControls component // emailTemplates data is loaded in the ViewControls component
const emailTemplates = ref({}) const emailTemplates = ref({})
const loadMore = ref(1) const loadMore = ref(1)

View File

@ -4,6 +4,10 @@
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header> <template #right-header>
<CustomActions
v-if="leadsListView?.customListActions"
:actions="leadsListView.customListActions"
/>
<Button variant="solid" label="Create" @click="showNewDialog = true"> <Button variant="solid" label="Create" @click="showNewDialog = true">
<template #prefix><FeatherIcon name="plus" class="h-4" /></template> <template #prefix><FeatherIcon name="plus" class="h-4" /></template>
</Button> </Button>
@ -19,6 +23,7 @@
:filters="{ converted: 0 }" :filters="{ converted: 0 }"
/> />
<LeadsListView <LeadsListView
ref="leadsListView"
v-if="leads.data && rows.length" v-if="leads.data && rows.length"
v-model="leads.data.page_length_count" v-model="leads.data.page_length_count"
v-model:list="leads" v-model:list="leads"
@ -66,6 +71,7 @@
</template> </template>
<script setup> <script setup>
import CustomActions from '@/components/CustomActions.vue'
import LeadsIcon from '@/components/Icons/LeadsIcon.vue' import LeadsIcon from '@/components/Icons/LeadsIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import LeadsListView from '@/components/ListViews/LeadsListView.vue' import LeadsListView from '@/components/ListViews/LeadsListView.vue'
@ -87,6 +93,8 @@ const { getLeadStatus } = statusesStore()
const router = useRouter() const router = useRouter()
const leadsListView = ref(null)
// leads data is loaded in the ViewControls component // leads data is loaded in the ViewControls component
const leads = ref({}) const leads = ref({})
const loadMore = ref(1) const loadMore = ref(1)

View File

@ -4,6 +4,10 @@
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header> <template #right-header>
<CustomActions
v-if="organizationsListView?.customListActions"
:actions="organizationsListView.customListActions"
/>
<Button <Button
variant="solid" variant="solid"
label="Create" label="Create"
@ -22,6 +26,7 @@
doctype="CRM Organization" doctype="CRM Organization"
/> />
<OrganizationsListView <OrganizationsListView
ref="organizationsListView"
v-if="organizations.data && rows.length" v-if="organizations.data && rows.length"
v-model="organizations.data.page_length_count" v-model="organizations.data.page_length_count"
v-model:list="organizations" v-model:list="organizations"
@ -55,6 +60,7 @@
<OrganizationModal v-model="showOrganizationModal" /> <OrganizationModal v-model="showOrganizationModal" />
</template> </template>
<script setup> <script setup>
import CustomActions from '@/components/CustomActions.vue'
import OrganizationsIcon from '@/components/Icons/OrganizationsIcon.vue' import OrganizationsIcon from '@/components/Icons/OrganizationsIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import OrganizationModal from '@/components/Modals/OrganizationModal.vue' import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
@ -72,6 +78,7 @@ import { useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()
const organizationsListView = ref(null)
const showOrganizationModal = ref(false) const showOrganizationModal = ref(false)
const currentOrganization = computed(() => { const currentOrganization = computed(() => {

View File

@ -4,6 +4,10 @@
<Breadcrumbs :items="breadcrumbs" /> <Breadcrumbs :items="breadcrumbs" />
</template> </template>
<template #right-header> <template #right-header>
<CustomActions
v-if="tasksListView?.customListActions"
:actions="tasksListView.customListActions"
/>
<Button variant="solid" label="Create" @click="createTask"> <Button variant="solid" label="Create" @click="createTask">
<template #prefix><FeatherIcon name="plus" class="h-4" /></template> <template #prefix><FeatherIcon name="plus" class="h-4" /></template>
</Button> </Button>
@ -18,6 +22,7 @@
doctype="CRM Task" doctype="CRM Task"
/> />
<TasksListView <TasksListView
ref="tasksListView"
v-if="tasks.data && rows.length" v-if="tasks.data && rows.length"
v-model="tasks.data.page_length_count" v-model="tasks.data.page_length_count"
v-model:list="tasks" v-model:list="tasks"
@ -47,6 +52,7 @@
</template> </template>
<script setup> <script setup>
import CustomActions from '@/components/CustomActions.vue'
import EmailIcon from '@/components/Icons/EmailIcon.vue' import EmailIcon from '@/components/Icons/EmailIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import ViewControls from '@/components/ViewControls.vue' import ViewControls from '@/components/ViewControls.vue'
@ -61,6 +67,8 @@ const breadcrumbs = [{ label: 'Tasks', route: { name: 'Tasks' } }]
const { getUser } = usersStore() const { getUser } = usersStore()
const tasksListView = ref(null)
// tasks data is loaded in the ViewControls component // tasks data is loaded in the ViewControls component
const tasks = ref({}) const tasks = ref({})
const loadMore = ref(1) const loadMore = ref(1)

View File

@ -131,11 +131,12 @@ export function setupCustomActions(data, obj) {
data._customActions = formScript?.actions || [] data._customActions = formScript?.actions || []
} }
export function setupBulkActions(data, obj = {}) { export function setupListActions(data, obj = {}) {
if (!data.form_script) return [] if (!data.list_script) return []
let script = new Function(data.form_script + '\nreturn setupForm')() let script = new Function(data.list_script + '\nreturn setupList')()
let formScript = script(obj) let listScript = script(obj)
data.bulkActions = formScript?.bulk_actions || [] data.listActions = listScript?.actions || []
data.bulkActions = listScript?.bulk_actions || []
} }
export function errorMessage(title, message) { export function errorMessage(title, message) {