feat: add/remove contact in deal

This commit is contained in:
Shariq Ansari 2023-11-11 13:35:39 +05:30
parent 1cb017ac9e
commit 50fceef0c3
2 changed files with 106 additions and 12 deletions

View File

@ -1,7 +1,8 @@
# 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
# import frappe import frappe
from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
@ -17,3 +18,23 @@ class CRMDeal(Document):
{ "label": 'Email', "value": 'email' }, { "label": 'Email', "value": 'email' },
{ "label": 'Mobile no', "value": 'mobile_no' }, { "label": 'Mobile no', "value": 'mobile_no' },
] ]
@frappe.whitelist()
def add_contact(deal, contact):
if not frappe.has_permission("CRM Deal", "write", deal):
frappe.throw(_("Not allowed to add contact to Deal"), frappe.PermissionError)
deal = frappe.get_cached_doc("CRM Deal", deal)
deal.append("contacts", {"contact": contact})
deal.save()
return True
@frappe.whitelist()
def remove_contact(deal, contact):
if not frappe.has_permission("CRM Deal", "write", deal):
frappe.throw(_("Not allowed to remove contact from Deal"), frappe.PermissionError)
deal = frappe.get_cached_doc("CRM Deal", deal)
deal.contacts = [d for d in deal.contacts if d.contact != contact]
deal.save()
return True

View File

@ -102,16 +102,44 @@
:class="{ 'border-b': i !== detailSections.length - 1 }" :class="{ 'border-b': i !== detailSections.length - 1 }"
> >
<Toggler :is-opened="section.opened" v-slot="{ opened, toggle }"> <Toggler :is-opened="section.opened" v-slot="{ opened, toggle }">
<div <div class="flex items-center justify-between">
class="flex h-7 max-w-fit cursor-pointer items-center gap-2 pl-2 pr-3 text-base font-semibold leading-5" <div
@click="toggle()" class="flex h-7 max-w-fit cursor-pointer items-center gap-2 pl-2 pr-3 text-base font-semibold leading-5"
> @click="toggle()"
<FeatherIcon >
name="chevron-right" <FeatherIcon
class="h-4 text-gray-900 transition-all duration-300 ease-in-out" name="chevron-right"
:class="{ 'rotate-90': opened }" class="h-4 text-gray-900 transition-all duration-300 ease-in-out"
/> :class="{ 'rotate-90': opened }"
{{ section.label }} />
{{ section.label }}
</div>
<div v-if="section.contacts" class="pr-2">
<Autocomplete
value=""
:options="
contacts.data.map((contact) => {
return {
label: contact.full_name,
value: contact.name,
}
})
"
@change="(e) => addContact(e.value)"
>
<template #target="{ togglePopover }">
<Button
class="h-7 px-3"
label="Add contact"
@click="togglePopover()"
>
<template #prefix>
<FeatherIcon name="plus" class="h-4" />
</template>
</Button>
</template>
</Autocomplete>
</div>
</div> </div>
<transition <transition
enter-active-class="duration-300 ease-in" enter-active-class="duration-300 ease-in"
@ -228,7 +256,11 @@
/> />
</div> </div>
<div v-else> <div v-else>
<div v-for="(contact, i) in section.contacts"> <div
v-if="section.contacts.length"
v-for="(contact, i) in section.contacts"
:key="contact.name"
>
<div <div
class="px-2 pb-2.5" class="px-2 pb-2.5"
:class="[i == 0 ? 'pt-5' : 'pt-2.5']" :class="[i == 0 ? 'pt-5' : 'pt-2.5']"
@ -254,6 +286,11 @@
{{ getContactByName(contact.name).full_name }} {{ getContactByName(contact.name).full_name }}
</div> </div>
<div class="flex gap-3"> <div class="flex gap-3">
<FeatherIcon
name="trash-2"
class="h-4 w-4"
@click="removeContact(contact.name)"
/>
<ExternalLinkIcon <ExternalLinkIcon
class="h-4 w-4" class="h-4 w-4"
@click=" @click="
@ -302,6 +339,9 @@
class="mx-2 h-px border-t border-gray-200" class="mx-2 h-px border-t border-gray-200"
/> />
</div> </div>
<div v-else class="flex justify-center items-center text-base text-gray-600 h-20">
No contacts added
</div>
</div> </div>
</div> </div>
</transition> </transition>
@ -354,6 +394,7 @@ import {
Avatar, Avatar,
Tabs, Tabs,
Breadcrumbs, Breadcrumbs,
call,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
@ -519,6 +560,38 @@ const detailSections = computed(() => {
] ]
}) })
async function addContact(value) {
let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.add_contact', {
deal: props.dealId,
contact: value,
})
if (d) {
deal.reload()
contacts.reload()
createToast({
title: 'Contact added',
icon: 'check',
iconClasses: 'text-green-600',
})
}
}
async function removeContact(value) {
let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.remove_contact', {
deal: props.dealId,
contact: value,
})
if (d) {
deal.reload()
contacts.reload()
createToast({
title: 'Contact removed',
icon: 'check',
iconClasses: 'text-green-600',
})
}
}
const organization = computed(() => { const organization = computed(() => {
return getOrganization(deal.data.organization) return getOrganization(deal.data.organization)
}) })