feat: set contact as primary from UI

This commit is contained in:
Shariq Ansari 2023-11-13 12:21:50 +05:30
parent 36f4ddee20
commit d408d066c7
4 changed files with 99 additions and 52 deletions

View File

@ -22,7 +22,7 @@ def get_deal(name):
deal["contacts"] = frappe.get_all(
"CRM Contacts",
filters={"parenttype": "CRM Deal", "parent": deal.name},
fields=["contact"],
fields=["contact", "is_primary"],
)
return deal

View File

@ -75,4 +75,14 @@ def remove_contact(deal, contact):
deal = frappe.get_cached_doc("CRM Deal", deal)
deal.contacts = [d for d in deal.contacts if d.contact != contact]
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

View 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>

View File

@ -209,37 +209,6 @@
</div>
</template>
</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
v-else-if="field.type === 'date'"
type="date"
@ -307,28 +276,41 @@
size="md"
/>
{{ getContactByName(contact.name).full_name }}
</div>
<div class="flex gap-3">
<FeatherIcon
name="trash-2"
class="h-4 w-4"
@click="removeContact(contact.name)"
<Badge
v-if="contact.is_primary"
class="ml-2"
variant="outline"
label="Primary"
theme="green"
/>
<ExternalLinkIcon
class="h-4 w-4"
</div>
<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="
router.push({
name: 'Contact',
params: { contactId: contact.name },
})
"
/>
<FeatherIcon
name="chevron-right"
class="h-4 w-4 text-gray-900 transition-all duration-300 ease-in-out"
:class="{ 'rotate-90': cOpened }"
@click="cToggle()"
/>
>
<ExternalLinkIcon class="h-4 w-4" />
</Button>
<Button variant="ghost" @click="cToggle()">
<FeatherIcon
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>
<transition
@ -403,6 +385,7 @@ import NoteIcon from '@/components/Icons/NoteIcon.vue'
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import LinkIcon from '@/components/Icons/LinkIcon.vue'
import ExternalLinkIcon from '@/components/Icons/ExternalLinkIcon.vue'
import SuccessIcon from '@/components/Icons/SuccessIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue'
import Toggler from '@/components/Toggler.vue'
import Activities from '@/components/Activities.vue'
@ -430,8 +413,9 @@ import {
Tabs,
Breadcrumbs,
call,
Badge,
} from 'frappe-ui'
import { ref, computed } from 'vue'
import { ref, computed, h } from 'vue'
import { useRouter } from 'vue-router'
const { getUser } = usersStore()
@ -588,6 +572,7 @@ const detailSections = computed(() => {
contacts: deal.data.contacts.map((contact) => {
return {
name: contact.contact,
is_primary: contact.is_primary,
opened: false,
}
}),
@ -598,10 +583,30 @@ const detailSections = computed(() => {
const showContactModal = ref(false)
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', {
deal: props.dealId,
contact: value,
contact,
})
if (d) {
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', {
deal: props.dealId,
contact: value,
contact,
})
if (d) {
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(() => {
return getOrganization(deal.data.organization)
})