feat: set contact as primary from UI
This commit is contained in:
parent
36f4ddee20
commit
d408d066c7
@ -22,7 +22,7 @@ def get_deal(name):
|
|||||||
deal["contacts"] = frappe.get_all(
|
deal["contacts"] = frappe.get_all(
|
||||||
"CRM Contacts",
|
"CRM Contacts",
|
||||||
filters={"parenttype": "CRM Deal", "parent": deal.name},
|
filters={"parenttype": "CRM Deal", "parent": deal.name},
|
||||||
fields=["contact"],
|
fields=["contact", "is_primary"],
|
||||||
)
|
)
|
||||||
|
|
||||||
return deal
|
return deal
|
||||||
|
|||||||
@ -75,4 +75,14 @@ def remove_contact(deal, contact):
|
|||||||
deal = frappe.get_cached_doc("CRM Deal", deal)
|
deal = frappe.get_cached_doc("CRM Deal", deal)
|
||||||
deal.contacts = [d for d in deal.contacts if d.contact != contact]
|
deal.contacts = [d for d in deal.contacts if d.contact != contact]
|
||||||
deal.save()
|
deal.save()
|
||||||
|
return True
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def set_primary_contact(deal, contact):
|
||||||
|
if not frappe.has_permission("CRM Deal", "write", deal):
|
||||||
|
frappe.throw(_("Not allowed to set primary contact for Deal"), frappe.PermissionError)
|
||||||
|
|
||||||
|
deal = frappe.get_cached_doc("CRM Deal", deal)
|
||||||
|
deal.set_primary_contact(contact)
|
||||||
|
deal.save()
|
||||||
return True
|
return True
|
||||||
16
frontend/src/components/Icons/SuccessIcon.vue
Normal file
16
frontend/src/components/Icons/SuccessIcon.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M14 8C14 11.3137 11.3137 14 8 14C4.68629 14 2 11.3137 2 8C2 4.68629 4.68629 2 8 2C11.3137 2 14 4.68629 14 8ZM15 8C15 11.866 11.866 15 8 15C4.13401 15 1 11.866 1 8C1 4.13401 4.13401 1 8 1C11.866 1 15 4.13401 15 8ZM11.2909 5.98482C11.4666 5.77175 11.4363 5.45663 11.2232 5.28096C11.0101 5.1053 10.695 5.13561 10.5193 5.34868L7.07001 9.53239L5.72845 7.79857C5.55946 7.58018 5.24543 7.54012 5.02703 7.70911C4.80863 7.8781 4.76858 8.19214 4.93756 8.41053L6.66217 10.6394C6.7552 10.7596 6.89788 10.831 7.04988 10.8334C7.20188 10.8357 7.3467 10.7688 7.4434 10.6515L11.2909 5.98482Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
@ -209,37 +209,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Autocomplete>
|
</Autocomplete>
|
||||||
<Dropdown
|
|
||||||
v-else-if="field.type === 'dropdown'"
|
|
||||||
:options="
|
|
||||||
statusDropdownOptions(deal.data, 'deal', updateField)
|
|
||||||
"
|
|
||||||
class="w-full flex-1"
|
|
||||||
>
|
|
||||||
<template #default="{ open }">
|
|
||||||
<Button
|
|
||||||
:label="deal.data[field.name]"
|
|
||||||
class="w-full justify-between"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<IndicatorIcon
|
|
||||||
:class="
|
|
||||||
dealStatuses[deal.data[field.name]].color
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #default>{{
|
|
||||||
deal.data[field.name]
|
|
||||||
}}</template>
|
|
||||||
<template #suffix>
|
|
||||||
<FeatherIcon
|
|
||||||
:name="open ? 'chevron-up' : 'chevron-down'"
|
|
||||||
class="h-4 text-gray-600"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</Button>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
<FormControl
|
<FormControl
|
||||||
v-else-if="field.type === 'date'"
|
v-else-if="field.type === 'date'"
|
||||||
type="date"
|
type="date"
|
||||||
@ -307,28 +276,41 @@
|
|||||||
size="md"
|
size="md"
|
||||||
/>
|
/>
|
||||||
{{ getContactByName(contact.name).full_name }}
|
{{ getContactByName(contact.name).full_name }}
|
||||||
</div>
|
<Badge
|
||||||
<div class="flex gap-3">
|
v-if="contact.is_primary"
|
||||||
<FeatherIcon
|
class="ml-2"
|
||||||
name="trash-2"
|
variant="outline"
|
||||||
class="h-4 w-4"
|
label="Primary"
|
||||||
@click="removeContact(contact.name)"
|
theme="green"
|
||||||
/>
|
/>
|
||||||
<ExternalLinkIcon
|
</div>
|
||||||
class="h-4 w-4"
|
<div class="flex items-center">
|
||||||
|
<Dropdown :options="contactOptions(contact)">
|
||||||
|
<Button variant="ghost">
|
||||||
|
<FeatherIcon
|
||||||
|
name="more-horizontal"
|
||||||
|
class="h-4 text-gray-600"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
@click="
|
@click="
|
||||||
router.push({
|
router.push({
|
||||||
name: 'Contact',
|
name: 'Contact',
|
||||||
params: { contactId: contact.name },
|
params: { contactId: contact.name },
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
>
|
||||||
<FeatherIcon
|
<ExternalLinkIcon class="h-4 w-4" />
|
||||||
name="chevron-right"
|
</Button>
|
||||||
class="h-4 w-4 text-gray-900 transition-all duration-300 ease-in-out"
|
<Button variant="ghost" @click="cToggle()">
|
||||||
:class="{ 'rotate-90': cOpened }"
|
<FeatherIcon
|
||||||
@click="cToggle()"
|
name="chevron-right"
|
||||||
/>
|
class="h-4 w-4 text-gray-900 transition-all duration-300 ease-in-out"
|
||||||
|
:class="{ 'rotate-90': cOpened }"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<transition
|
<transition
|
||||||
@ -403,6 +385,7 @@ import NoteIcon from '@/components/Icons/NoteIcon.vue'
|
|||||||
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
|
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
|
||||||
import LinkIcon from '@/components/Icons/LinkIcon.vue'
|
import LinkIcon from '@/components/Icons/LinkIcon.vue'
|
||||||
import ExternalLinkIcon from '@/components/Icons/ExternalLinkIcon.vue'
|
import ExternalLinkIcon from '@/components/Icons/ExternalLinkIcon.vue'
|
||||||
|
import SuccessIcon from '@/components/Icons/SuccessIcon.vue'
|
||||||
import LayoutHeader from '@/components/LayoutHeader.vue'
|
import LayoutHeader from '@/components/LayoutHeader.vue'
|
||||||
import Toggler from '@/components/Toggler.vue'
|
import Toggler from '@/components/Toggler.vue'
|
||||||
import Activities from '@/components/Activities.vue'
|
import Activities from '@/components/Activities.vue'
|
||||||
@ -430,8 +413,9 @@ import {
|
|||||||
Tabs,
|
Tabs,
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
call,
|
call,
|
||||||
|
Badge,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, h } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
@ -588,6 +572,7 @@ const detailSections = computed(() => {
|
|||||||
contacts: deal.data.contacts.map((contact) => {
|
contacts: deal.data.contacts.map((contact) => {
|
||||||
return {
|
return {
|
||||||
name: contact.contact,
|
name: contact.contact,
|
||||||
|
is_primary: contact.is_primary,
|
||||||
opened: false,
|
opened: false,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@ -598,10 +583,30 @@ const detailSections = computed(() => {
|
|||||||
const showContactModal = ref(false)
|
const showContactModal = ref(false)
|
||||||
const _contact = ref({})
|
const _contact = ref({})
|
||||||
|
|
||||||
async function addContact(value) {
|
function contactOptions(contact) {
|
||||||
|
let options = [
|
||||||
|
{
|
||||||
|
label: 'Delete',
|
||||||
|
icon: 'trash-2',
|
||||||
|
onClick: () => removeContact(contact.name),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
if (!contact.is_primary) {
|
||||||
|
options.push({
|
||||||
|
label: 'Set as primary contact',
|
||||||
|
icon: h(SuccessIcon, { class: 'h-4 w-4' }),
|
||||||
|
onClick: () => setPrimaryContact(contact.name),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addContact(contact) {
|
||||||
let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.add_contact', {
|
let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.add_contact', {
|
||||||
deal: props.dealId,
|
deal: props.dealId,
|
||||||
contact: value,
|
contact,
|
||||||
})
|
})
|
||||||
if (d) {
|
if (d) {
|
||||||
await contacts.reload()
|
await contacts.reload()
|
||||||
@ -614,10 +619,10 @@ async function addContact(value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeContact(value) {
|
async function removeContact(contact) {
|
||||||
let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.remove_contact', {
|
let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.remove_contact', {
|
||||||
deal: props.dealId,
|
deal: props.dealId,
|
||||||
contact: value,
|
contact,
|
||||||
})
|
})
|
||||||
if (d) {
|
if (d) {
|
||||||
deal.reload()
|
deal.reload()
|
||||||
@ -630,6 +635,22 @@ async function removeContact(value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setPrimaryContact(contact) {
|
||||||
|
let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.set_primary_contact', {
|
||||||
|
deal: props.dealId,
|
||||||
|
contact,
|
||||||
|
})
|
||||||
|
if (d) {
|
||||||
|
await contacts.reload()
|
||||||
|
deal.reload()
|
||||||
|
createToast({
|
||||||
|
title: 'Primary contact set',
|
||||||
|
icon: 'check',
|
||||||
|
iconClasses: 'text-green-600',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const organization = computed(() => {
|
const organization = computed(() => {
|
||||||
return getOrganization(deal.data.organization)
|
return getOrganization(deal.data.organization)
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user