Merge pull request #36 from shariquerik/make-lead-deal-status-link-field
This commit is contained in:
commit
d3a8336f28
@ -111,10 +111,10 @@
|
|||||||
{
|
{
|
||||||
"default": "Qualification",
|
"default": "Qualification",
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"options": "Qualification\nDemo/Making\nProposal/Quotation\nNegotiation\nReady to Close\nWon\nLost",
|
"options": "CRM Deal Status",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
@ -138,7 +138,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-11-24 16:50:07.177125",
|
"modified": "2023-11-29 11:31:46.968519",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "FCRM",
|
"module": "FCRM",
|
||||||
"name": "CRM Deal",
|
"name": "CRM Deal",
|
||||||
|
|||||||
0
crm/fcrm/doctype/crm_deal_status/__init__.py
Normal file
0
crm/fcrm/doctype/crm_deal_status/__init__.py
Normal file
8
crm/fcrm/doctype/crm_deal_status/crm_deal_status.js
Normal file
8
crm/fcrm/doctype/crm_deal_status/crm_deal_status.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
// frappe.ui.form.on("CRM Deal Status", {
|
||||||
|
// refresh(frm) {
|
||||||
|
|
||||||
|
// },
|
||||||
|
// });
|
||||||
62
crm/fcrm/doctype/crm_deal_status/crm_deal_status.json
Normal file
62
crm/fcrm/doctype/crm_deal_status/crm_deal_status.json
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"autoname": "field:deal_status",
|
||||||
|
"creation": "2023-11-29 11:24:55.543387",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"deal_status",
|
||||||
|
"color",
|
||||||
|
"position"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"default": "gray",
|
||||||
|
"fieldname": "color",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Color",
|
||||||
|
"options": "black\ngray\nblue\ngreen\nred\npink\norange\namber\nyellow\ncyan\nteal\nviolet\npurple"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "deal_status",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Status",
|
||||||
|
"reqd": 1,
|
||||||
|
"unique": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "position",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Position"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2023-11-29 12:52:03.070218",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "FCRM",
|
||||||
|
"name": "CRM Deal Status",
|
||||||
|
"naming_rule": "By fieldname",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
9
crm/fcrm/doctype/crm_deal_status/crm_deal_status.py
Normal file
9
crm/fcrm/doctype/crm_deal_status/crm_deal_status.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class CRMDealStatus(Document):
|
||||||
|
pass
|
||||||
9
crm/fcrm/doctype/crm_deal_status/test_crm_deal_status.py
Normal file
9
crm/fcrm/doctype/crm_deal_status/test_crm_deal_status.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestCRMDealStatus(FrappeTestCase):
|
||||||
|
pass
|
||||||
@ -74,10 +74,10 @@
|
|||||||
{
|
{
|
||||||
"default": "Open",
|
"default": "Open",
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"options": "Open\nContacted\nNurture\nQualified\nUnqualified\nJunk",
|
"options": "CRM Lead Status",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
@ -201,7 +201,7 @@
|
|||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-11-24 16:38:36.695965",
|
"modified": "2023-11-29 11:31:08.555096",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "FCRM",
|
"module": "FCRM",
|
||||||
"name": "CRM Lead",
|
"name": "CRM Lead",
|
||||||
|
|||||||
0
crm/fcrm/doctype/crm_lead_status/__init__.py
Normal file
0
crm/fcrm/doctype/crm_lead_status/__init__.py
Normal file
8
crm/fcrm/doctype/crm_lead_status/crm_lead_status.js
Normal file
8
crm/fcrm/doctype/crm_lead_status/crm_lead_status.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
// frappe.ui.form.on("CRM Lead Status", {
|
||||||
|
// refresh(frm) {
|
||||||
|
|
||||||
|
// },
|
||||||
|
// });
|
||||||
63
crm/fcrm/doctype/crm_lead_status/crm_lead_status.json
Normal file
63
crm/fcrm/doctype/crm_lead_status/crm_lead_status.json
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"autoname": "field:lead_status",
|
||||||
|
"creation": "2023-11-29 11:09:53.678414",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"lead_status",
|
||||||
|
"color",
|
||||||
|
"position"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"default": "gray",
|
||||||
|
"fieldname": "color",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Color",
|
||||||
|
"options": "black\ngray\nblue\ngreen\nred\npink\norange\namber\nyellow\ncyan\nteal\nviolet\npurple"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "lead_status",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Status",
|
||||||
|
"reqd": 1,
|
||||||
|
"unique": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "position",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Position"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2023-11-29 12:52:25.641581",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "FCRM",
|
||||||
|
"name": "CRM Lead Status",
|
||||||
|
"naming_rule": "By fieldname",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
9
crm/fcrm/doctype/crm_lead_status/crm_lead_status.py
Normal file
9
crm/fcrm/doctype/crm_lead_status/crm_lead_status.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class CRMLeadStatus(Document):
|
||||||
|
pass
|
||||||
9
crm/fcrm/doctype/crm_lead_status/test_crm_lead_status.py
Normal file
9
crm/fcrm/doctype/crm_lead_status/test_crm_lead_status.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestCRMLeadStatus(FrappeTestCase):
|
||||||
|
pass
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"charts": [],
|
"charts": [],
|
||||||
"content": "[{\"id\":\"1nr6UkvDiL\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h5\\\"><b>PORTAL</b></span>\",\"col\":12}},{\"id\":\"1hyi8SysUY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"CRM Portal Page\",\"col\":3}},{\"id\":\"ktENiGaqXQ\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"VgeWLYOuAS\",\"type\":\"paragraph\",\"data\":{\"text\":\"<b>SHORTCUTS</b>\",\"col\":12}},{\"id\":\"A66FpG-K3T\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leads\",\"col\":3}},{\"id\":\"n9b6N5ebOj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Deals\",\"col\":3}},{\"id\":\"sGHTXrludH\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Organizations\",\"col\":3}},{\"id\":\"uXZNCdqxy0\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Contacts\",\"col\":3}}]",
|
"content": "[{\"id\":\"1nr6UkvDiL\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h5\\\"><b>PORTAL</b></span>\",\"col\":12}},{\"id\":\"1hyi8SysUY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"CRM Portal Page\",\"col\":3}},{\"id\":\"ktENiGaqXQ\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"VgeWLYOuAS\",\"type\":\"paragraph\",\"data\":{\"text\":\"<b>SHORTCUTS</b>\",\"col\":12}},{\"id\":\"A66FpG-K3T\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leads\",\"col\":3}},{\"id\":\"n9b6N5ebOj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Deals\",\"col\":3}},{\"id\":\"sGHTXrludH\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Organizations\",\"col\":3}},{\"id\":\"uXZNCdqxy0\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Contacts\",\"col\":3}},{\"id\":\"TZ7cULX3Tk\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"zpySv0nGVQ\",\"type\":\"paragraph\",\"data\":{\"text\":\"<b>META</b>\",\"col\":12}},{\"id\":\"fa-uKzobpp\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Lead Statuses\",\"col\":3}},{\"id\":\"hxoZghUHP2\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Deal Statuses\",\"col\":3}},{\"id\":\"HbgghUpc8N\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Lead Sources\",\"col\":3}},{\"id\":\"8cPs7Fohb4\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Industries\",\"col\":3}}]",
|
||||||
"creation": "2023-11-27 13:55:17.090361",
|
"creation": "2023-11-27 13:55:17.090361",
|
||||||
"custom_blocks": [],
|
"custom_blocks": [],
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
@ -13,8 +13,8 @@
|
|||||||
"is_hidden": 0,
|
"is_hidden": 0,
|
||||||
"label": "Frappe CRM",
|
"label": "Frappe CRM",
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-11-27 14:29:01.217327",
|
"modified": "2023-11-29 13:37:56.731645",
|
||||||
"modified_by": "shariq@frappe.io",
|
"modified_by": "Administrator",
|
||||||
"module": "FCRM",
|
"module": "FCRM",
|
||||||
"name": "Frappe CRM",
|
"name": "Frappe CRM",
|
||||||
"number_cards": [],
|
"number_cards": [],
|
||||||
@ -40,6 +40,14 @@
|
|||||||
"type": "URL",
|
"type": "URL",
|
||||||
"url": "/crm"
|
"url": "/crm"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"color": "Grey",
|
||||||
|
"doc_view": "List",
|
||||||
|
"label": "Lead Statuses",
|
||||||
|
"link_to": "CRM Lead Status",
|
||||||
|
"stats_filter": "[]",
|
||||||
|
"type": "DocType"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"color": "Grey",
|
"color": "Grey",
|
||||||
"doc_view": "List",
|
"doc_view": "List",
|
||||||
@ -48,6 +56,22 @@
|
|||||||
"stats_filter": "[]",
|
"stats_filter": "[]",
|
||||||
"type": "DocType"
|
"type": "DocType"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"color": "Grey",
|
||||||
|
"doc_view": "List",
|
||||||
|
"label": "Deal Statuses",
|
||||||
|
"link_to": "CRM Deal Status",
|
||||||
|
"stats_filter": "[]",
|
||||||
|
"type": "DocType"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Grey",
|
||||||
|
"doc_view": "List",
|
||||||
|
"label": "Lead Sources",
|
||||||
|
"link_to": "CRM Lead Source",
|
||||||
|
"stats_filter": "[]",
|
||||||
|
"type": "DocType"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"color": "Grey",
|
"color": "Grey",
|
||||||
"doc_view": "List",
|
"doc_view": "List",
|
||||||
@ -63,6 +87,14 @@
|
|||||||
"link_to": "Contact",
|
"link_to": "Contact",
|
||||||
"stats_filter": "[]",
|
"stats_filter": "[]",
|
||||||
"type": "DocType"
|
"type": "DocType"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Grey",
|
||||||
|
"doc_view": "List",
|
||||||
|
"label": "Industries",
|
||||||
|
"link_to": "CRM Industry",
|
||||||
|
"stats_filter": "[]",
|
||||||
|
"type": "DocType"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Frappe CRM"
|
"title": "Frappe CRM"
|
||||||
|
|||||||
@ -70,8 +70,8 @@ website_route_rules = [
|
|||||||
# Installation
|
# Installation
|
||||||
# ------------
|
# ------------
|
||||||
|
|
||||||
# before_install = "crm.install.before_install"
|
before_install = "crm.install.before_install"
|
||||||
# after_install = "crm.install.after_install"
|
after_install = "crm.install.after_install"
|
||||||
|
|
||||||
# Uninstallation
|
# Uninstallation
|
||||||
# ------------
|
# ------------
|
||||||
|
|||||||
93
crm/install.py
Normal file
93
crm/install.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# MIT License. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def before_install():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def after_install():
|
||||||
|
add_default_lead_statuses()
|
||||||
|
add_default_deal_statuses()
|
||||||
|
frappe.db.commit()
|
||||||
|
|
||||||
|
def add_default_lead_statuses():
|
||||||
|
statuses = {
|
||||||
|
"Open": {
|
||||||
|
"color": "gray",
|
||||||
|
"position": 1,
|
||||||
|
},
|
||||||
|
"Contacted": {
|
||||||
|
"color": "orange",
|
||||||
|
"position": 2,
|
||||||
|
},
|
||||||
|
"Nurture": {
|
||||||
|
"color": "blue",
|
||||||
|
"position": 3,
|
||||||
|
},
|
||||||
|
"Qualified": {
|
||||||
|
"color": "green",
|
||||||
|
"position": 4,
|
||||||
|
},
|
||||||
|
"Unqualified": {
|
||||||
|
"color": "red",
|
||||||
|
"position": 5,
|
||||||
|
},
|
||||||
|
"Junk": {
|
||||||
|
"color": "purple",
|
||||||
|
"position": 6,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for status in statuses:
|
||||||
|
if frappe.db.exists("CRM Lead Status", status):
|
||||||
|
continue
|
||||||
|
|
||||||
|
doc = frappe.new_doc("CRM Lead Status")
|
||||||
|
doc.lead_status = status
|
||||||
|
doc.color = statuses[status]["color"]
|
||||||
|
doc.position = statuses[status]["position"]
|
||||||
|
doc.insert()
|
||||||
|
|
||||||
|
def add_default_deal_statuses():
|
||||||
|
statuses = {
|
||||||
|
"Qualification": {
|
||||||
|
"color": "gray",
|
||||||
|
"position": 1,
|
||||||
|
},
|
||||||
|
"Demo/Making": {
|
||||||
|
"color": "orange",
|
||||||
|
"position": 2,
|
||||||
|
},
|
||||||
|
"Proposal/Quotation": {
|
||||||
|
"color": "blue",
|
||||||
|
"position": 3,
|
||||||
|
},
|
||||||
|
"Negotiation": {
|
||||||
|
"color": "yellow",
|
||||||
|
"position": 4,
|
||||||
|
},
|
||||||
|
"Ready to Close": {
|
||||||
|
"color": "purple",
|
||||||
|
"position": 5,
|
||||||
|
},
|
||||||
|
"Won": {
|
||||||
|
"color": "green",
|
||||||
|
"position": 6,
|
||||||
|
},
|
||||||
|
"Lost": {
|
||||||
|
"color": "red",
|
||||||
|
"position": 7,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for status in statuses:
|
||||||
|
if frappe.db.exists("CRM Deal Status", status):
|
||||||
|
continue
|
||||||
|
|
||||||
|
doc = frappe.new_doc("CRM Deal Status")
|
||||||
|
doc.deal_status = status
|
||||||
|
doc.color = statuses[status]["color"]
|
||||||
|
doc.position = statuses[status]["position"]
|
||||||
|
doc.insert()
|
||||||
@ -11,7 +11,7 @@
|
|||||||
v-model="newDeal[field.name]"
|
v-model="newDeal[field.name]"
|
||||||
>
|
>
|
||||||
<template v-if="field.name == 'status'" #prefix>
|
<template v-if="field.name == 'status'" #prefix>
|
||||||
<IndicatorIcon :class="dealStatuses[newDeal[field.name]].color" />
|
<IndicatorIcon :class="getDealStatus(newDeal[field.name]).color" />
|
||||||
</template>
|
</template>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl
|
<FormControl
|
||||||
@ -45,7 +45,7 @@
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
v-else-if="field.type === 'dropdown'"
|
v-else-if="field.type === 'dropdown'"
|
||||||
:options="statusDropdownOptions(newDeal, 'deal')"
|
:options="statusOptions('deal')"
|
||||||
class="w-full flex-1"
|
class="w-full flex-1"
|
||||||
>
|
>
|
||||||
<template #default="{ open }">
|
<template #default="{ open }">
|
||||||
@ -55,7 +55,7 @@
|
|||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<IndicatorIcon
|
<IndicatorIcon
|
||||||
:class="dealStatuses[newDeal[field.name]].color"
|
:class="getDealStatus(newDeal[field.name]).color"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #default>{{ newDeal[field.name] }}</template>
|
<template #default>{{ newDeal[field.name] }}</template>
|
||||||
@ -89,11 +89,13 @@ import UserAvatar from '@/components/UserAvatar.vue'
|
|||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { dealStatuses, statusDropdownOptions, activeAgents } from '@/utils'
|
import { statusesStore } from '@/stores/statuses'
|
||||||
|
import { activeAgents } from '@/utils'
|
||||||
import { FormControl, Button, Dropdown, FeatherIcon } from 'frappe-ui'
|
import { FormControl, Button, Dropdown, FeatherIcon } from 'frappe-ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
|
const { getDealStatus, statusOptions } = statusesStore()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
newDeal: {
|
newDeal: {
|
||||||
@ -170,7 +172,7 @@ const allFields = [
|
|||||||
label: 'Status',
|
label: 'Status',
|
||||||
name: 'status',
|
name: 'status',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: statusDropdownOptions(props.newDeal, 'deal'),
|
options: statusOptions('deal'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Deal Owner',
|
label: 'Deal Owner',
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
v-model="newLead[field.name]"
|
v-model="newLead[field.name]"
|
||||||
>
|
>
|
||||||
<template v-if="field.name == 'status'" #prefix>
|
<template v-if="field.name == 'status'" #prefix>
|
||||||
<IndicatorIcon :class="leadStatuses[newLead[field.name]].color" />
|
<IndicatorIcon :class="getLeadStatus(newLead[field.name]).color" />
|
||||||
</template>
|
</template>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl
|
<FormControl
|
||||||
@ -45,7 +45,7 @@
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
v-else-if="field.type === 'dropdown'"
|
v-else-if="field.type === 'dropdown'"
|
||||||
:options="statusDropdownOptions(newLead)"
|
:options="statusOptions('lead')"
|
||||||
class="w-full flex-1"
|
class="w-full flex-1"
|
||||||
>
|
>
|
||||||
<template #default="{ open }">
|
<template #default="{ open }">
|
||||||
@ -55,7 +55,7 @@
|
|||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<IndicatorIcon
|
<IndicatorIcon
|
||||||
:class="leadStatuses[newLead[field.name]].color"
|
:class="getLeadStatus(newLead[field.name]).color"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #default>{{ newLead[field.name] }}</template>
|
<template #default>{{ newLead[field.name] }}</template>
|
||||||
@ -89,11 +89,13 @@ import UserAvatar from '@/components/UserAvatar.vue'
|
|||||||
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { leadStatuses, statusDropdownOptions, activeAgents } from '@/utils'
|
import { statusesStore } from '@/stores/statuses'
|
||||||
|
import { activeAgents } from '@/utils'
|
||||||
import { FormControl, Button, Dropdown, FeatherIcon } from 'frappe-ui'
|
import { FormControl, Button, Dropdown, FeatherIcon } from 'frappe-ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
|
const { getLeadStatus, statusOptions } = statusesStore()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
newLead: {
|
newLead: {
|
||||||
@ -170,7 +172,7 @@ const allFields = [
|
|||||||
label: 'Status',
|
label: 'Status',
|
||||||
name: 'status',
|
name: 'status',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: statusDropdownOptions(props.newLead),
|
options: statusOptions('lead'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Lead Owner',
|
label: 'Lead Owner',
|
||||||
|
|||||||
@ -143,7 +143,7 @@
|
|||||||
import LayoutHeader from '@/components/LayoutHeader.vue'
|
import LayoutHeader from '@/components/LayoutHeader.vue'
|
||||||
import DurationIcon from '@/components/Icons/DurationIcon.vue'
|
import DurationIcon from '@/components/Icons/DurationIcon.vue'
|
||||||
import NoteModal from '@/components/Modals/NoteModal.vue'
|
import NoteModal from '@/components/Modals/NoteModal.vue'
|
||||||
import { dateFormat, timeAgo, dateTooltipFormat } from '@/utils'
|
import { dateFormat, timeAgo, dateTooltipFormat, secondsToDuration } from '@/utils'
|
||||||
import {
|
import {
|
||||||
TextEditor,
|
TextEditor,
|
||||||
Avatar,
|
Avatar,
|
||||||
@ -156,7 +156,6 @@ import {
|
|||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { contactsStore } from '@/stores/contacts'
|
import { contactsStore } from '@/stores/contacts'
|
||||||
import { secondsToDuration } from '@/utils'
|
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
|||||||
@ -237,18 +237,18 @@ import {
|
|||||||
dateTooltipFormat,
|
dateTooltipFormat,
|
||||||
timeAgo,
|
timeAgo,
|
||||||
formatNumberIntoCurrency,
|
formatNumberIntoCurrency,
|
||||||
dealStatuses,
|
|
||||||
leadStatuses,
|
|
||||||
} from '@/utils'
|
} from '@/utils'
|
||||||
import { usersStore } from '@/stores/users.js'
|
import { usersStore } from '@/stores/users.js'
|
||||||
import { contactsStore } from '@/stores/contacts.js'
|
import { contactsStore } from '@/stores/contacts.js'
|
||||||
import { organizationsStore } from '@/stores/organizations.js'
|
import { organizationsStore } from '@/stores/organizations.js'
|
||||||
|
import { statusesStore } from '@/stores/statuses'
|
||||||
import { ref, computed, h } from 'vue'
|
import { ref, computed, h } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const { getContactByName, contacts } = contactsStore()
|
const { getContactByName, contacts } = contactsStore()
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
const { getOrganization } = organizationsStore()
|
const { getOrganization } = organizationsStore()
|
||||||
|
const { getLeadStatus, getDealStatus } = statusesStore()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
contactId: {
|
contactId: {
|
||||||
@ -390,7 +390,7 @@ function getLeadRowObject(lead) {
|
|||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
label: lead.status,
|
label: lead.status,
|
||||||
color: leadStatuses[lead.status]?.color,
|
color: getLeadStatus(lead.status)?.color,
|
||||||
},
|
},
|
||||||
email: lead.email,
|
email: lead.email,
|
||||||
mobile_no: lead.mobile_no,
|
mobile_no: lead.mobile_no,
|
||||||
@ -415,7 +415,7 @@ function getDealRowObject(deal) {
|
|||||||
annual_revenue: formatNumberIntoCurrency(deal.annual_revenue),
|
annual_revenue: formatNumberIntoCurrency(deal.annual_revenue),
|
||||||
status: {
|
status: {
|
||||||
label: deal.status,
|
label: deal.status,
|
||||||
color: dealStatuses[deal.status]?.color,
|
color: getDealStatus(deal.status)?.color,
|
||||||
},
|
},
|
||||||
email: deal.email,
|
email: deal.email,
|
||||||
mobile_no: deal.mobile_no,
|
mobile_no: deal.mobile_no,
|
||||||
|
|||||||
@ -18,13 +18,11 @@
|
|||||||
<UserAvatar class="mr-2" :user="option.email" size="sm" />
|
<UserAvatar class="mr-2" :user="option.email" size="sm" />
|
||||||
</template>
|
</template>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<Dropdown
|
<Dropdown :options="statusOptions('deal', updateField)">
|
||||||
:options="statusDropdownOptions(deal.data, 'deal', updateField)"
|
|
||||||
>
|
|
||||||
<template #default="{ open }">
|
<template #default="{ open }">
|
||||||
<Button :label="deal.data.status">
|
<Button :label="deal.data.status">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<IndicatorIcon :class="dealStatuses[deal.data.status].color" />
|
<IndicatorIcon :class="getDealStatus(deal.data.status).color" />
|
||||||
</template>
|
</template>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<FeatherIcon
|
<FeatherIcon
|
||||||
@ -163,7 +161,9 @@
|
|||||||
:image="getContactByName(contact.name).image"
|
:image="getContactByName(contact.name).image"
|
||||||
size="md"
|
size="md"
|
||||||
/>
|
/>
|
||||||
<div class="truncate">{{ getContactByName(contact.name).full_name }}</div>
|
<div class="truncate">
|
||||||
|
{{ getContactByName(contact.name).full_name }}
|
||||||
|
</div>
|
||||||
<Badge
|
<Badge
|
||||||
v-if="contact.is_primary"
|
v-if="contact.is_primary"
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
@ -272,16 +272,11 @@ import ContactModal from '@/components/Modals/ContactModal.vue'
|
|||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import Section from '@/components/Section.vue'
|
import Section from '@/components/Section.vue'
|
||||||
import SectionFields from '@/components/SectionFields.vue'
|
import SectionFields from '@/components/SectionFields.vue'
|
||||||
import {
|
import { openWebsite, createToast, activeAgents } from '@/utils'
|
||||||
dealStatuses,
|
|
||||||
statusDropdownOptions,
|
|
||||||
openWebsite,
|
|
||||||
createToast,
|
|
||||||
activeAgents,
|
|
||||||
} from '@/utils'
|
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { contactsStore } from '@/stores/contacts'
|
import { contactsStore } from '@/stores/contacts'
|
||||||
import { organizationsStore } from '@/stores/organizations'
|
import { organizationsStore } from '@/stores/organizations'
|
||||||
|
import { statusesStore } from '@/stores/statuses'
|
||||||
import {
|
import {
|
||||||
createResource,
|
createResource,
|
||||||
FeatherIcon,
|
FeatherIcon,
|
||||||
@ -300,6 +295,7 @@ import { useRouter } from 'vue-router'
|
|||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
const { getContactByName, contacts } = contactsStore()
|
const { getContactByName, contacts } = contactsStore()
|
||||||
const { organizations, getOrganization } = organizationsStore()
|
const { organizations, getOrganization } = organizationsStore()
|
||||||
|
const { statusOptions, getDealStatus } = statusesStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Filter doctype="CRM Deal" />
|
<Filter doctype="CRM Deal" />
|
||||||
<SortBy doctype="CRM Deal" />
|
<SortBy doctype="CRM Deal" />
|
||||||
<ViewSettings doctype="CRM Deal" v-model="deals"/>
|
<ViewSettings doctype="CRM Deal" v-model="deals" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DealsListView v-if="deals.data" :rows="rows" :columns="deals.data.columns" />
|
<DealsListView v-if="deals.data" :rows="rows" :columns="deals.data.columns" />
|
||||||
@ -62,11 +62,11 @@ import Filter from '@/components/Filter.vue'
|
|||||||
import ViewSettings from '@/components/ViewSettings.vue'
|
import ViewSettings from '@/components/ViewSettings.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { organizationsStore } from '@/stores/organizations'
|
import { organizationsStore } from '@/stores/organizations'
|
||||||
|
import { statusesStore } from '@/stores/statuses'
|
||||||
import { useOrderBy } from '@/composables/orderby'
|
import { useOrderBy } from '@/composables/orderby'
|
||||||
import { useFilter } from '@/composables/filter'
|
import { useFilter } from '@/composables/filter'
|
||||||
import { useDebounceFn } from '@vueuse/core'
|
import { useDebounceFn } from '@vueuse/core'
|
||||||
import {
|
import {
|
||||||
dealStatuses,
|
|
||||||
dateFormat,
|
dateFormat,
|
||||||
dateTooltipFormat,
|
dateTooltipFormat,
|
||||||
timeAgo,
|
timeAgo,
|
||||||
@ -87,6 +87,7 @@ const breadcrumbs = [{ label: 'Deals', route: { name: 'Deals' } }]
|
|||||||
|
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
const { getOrganization } = organizationsStore()
|
const { getOrganization } = organizationsStore()
|
||||||
|
const { getDealStatus } = statusesStore()
|
||||||
const { get: getOrderBy } = useOrderBy()
|
const { get: getOrderBy } = useOrderBy()
|
||||||
const { getArgs, storage } = useFilter()
|
const { getArgs, storage } = useFilter()
|
||||||
|
|
||||||
@ -149,7 +150,7 @@ const rows = computed(() => {
|
|||||||
} else if (row == 'status') {
|
} else if (row == 'status') {
|
||||||
_rows[row] = {
|
_rows[row] = {
|
||||||
label: deal.status,
|
label: deal.status,
|
||||||
color: dealStatuses[deal.status]?.color,
|
color: getDealStatus(deal.status)?.color,
|
||||||
}
|
}
|
||||||
} else if (row == 'deal_owner') {
|
} else if (row == 'deal_owner') {
|
||||||
_rows[row] = {
|
_rows[row] = {
|
||||||
|
|||||||
@ -18,13 +18,11 @@
|
|||||||
<UserAvatar class="mr-2" :user="option.email" size="sm" />
|
<UserAvatar class="mr-2" :user="option.email" size="sm" />
|
||||||
</template>
|
</template>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<Dropdown
|
<Dropdown :options="statusOptions('lead', updateField)">
|
||||||
:options="statusDropdownOptions(lead.data, 'lead', updateField)"
|
|
||||||
>
|
|
||||||
<template #default="{ open }">
|
<template #default="{ open }">
|
||||||
<Button :label="lead.data.status">
|
<Button :label="lead.data.status">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<IndicatorIcon :class="leadStatuses[lead.data.status].color" />
|
<IndicatorIcon :class="getLeadStatus(lead.data.status).color" />
|
||||||
</template>
|
</template>
|
||||||
<template #suffix
|
<template #suffix
|
||||||
><FeatherIcon
|
><FeatherIcon
|
||||||
@ -179,16 +177,11 @@ import UserAvatar from '@/components/UserAvatar.vue'
|
|||||||
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
import OrganizationModal from '@/components/Modals/OrganizationModal.vue'
|
||||||
import Section from '@/components/Section.vue'
|
import Section from '@/components/Section.vue'
|
||||||
import SectionFields from '@/components/SectionFields.vue'
|
import SectionFields from '@/components/SectionFields.vue'
|
||||||
import {
|
import { openWebsite, createToast, activeAgents } from '@/utils'
|
||||||
leadStatuses,
|
|
||||||
statusDropdownOptions,
|
|
||||||
openWebsite,
|
|
||||||
createToast,
|
|
||||||
activeAgents,
|
|
||||||
} from '@/utils'
|
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { contactsStore } from '@/stores/contacts'
|
import { contactsStore } from '@/stores/contacts'
|
||||||
import { organizationsStore } from '@/stores/organizations'
|
import { organizationsStore } from '@/stores/organizations'
|
||||||
|
import { statusesStore } from '@/stores/statuses'
|
||||||
import {
|
import {
|
||||||
createResource,
|
createResource,
|
||||||
FileUploader,
|
FileUploader,
|
||||||
@ -208,6 +201,7 @@ import { useRouter } from 'vue-router'
|
|||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
const { contacts } = contactsStore()
|
const { contacts } = contactsStore()
|
||||||
const { organizations, getOrganization } = organizationsStore()
|
const { organizations, getOrganization } = organizationsStore()
|
||||||
|
const { statusOptions, getLeadStatus } = statusesStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@ -29,14 +29,10 @@
|
|||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Filter doctype="CRM Lead" />
|
<Filter doctype="CRM Lead" />
|
||||||
<SortBy doctype="CRM Lead" />
|
<SortBy doctype="CRM Lead" />
|
||||||
<ViewSettings doctype="CRM Lead" v-model="leads"/>
|
<ViewSettings doctype="CRM Lead" v-model="leads" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<LeadsListView
|
<LeadsListView v-if="leads.data" :rows="rows" :columns="leads.data.columns" />
|
||||||
v-if="leads.data"
|
|
||||||
:rows="rows"
|
|
||||||
:columns="leads.data.columns"
|
|
||||||
/>
|
|
||||||
<Dialog
|
<Dialog
|
||||||
v-model="showNewDialog"
|
v-model="showNewDialog"
|
||||||
:options="{
|
:options="{
|
||||||
@ -65,10 +61,11 @@ import Filter from '@/components/Filter.vue'
|
|||||||
import ViewSettings from '@/components/ViewSettings.vue'
|
import ViewSettings from '@/components/ViewSettings.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { organizationsStore } from '@/stores/organizations'
|
import { organizationsStore } from '@/stores/organizations'
|
||||||
|
import { statusesStore } from '@/stores/statuses'
|
||||||
import { useOrderBy } from '@/composables/orderby'
|
import { useOrderBy } from '@/composables/orderby'
|
||||||
import { useFilter } from '@/composables/filter'
|
import { useFilter } from '@/composables/filter'
|
||||||
import { useDebounceFn } from '@vueuse/core'
|
import { useDebounceFn } from '@vueuse/core'
|
||||||
import { leadStatuses, dateFormat, dateTooltipFormat, timeAgo } from '@/utils'
|
import { dateFormat, dateTooltipFormat, timeAgo } from '@/utils'
|
||||||
import {
|
import {
|
||||||
FeatherIcon,
|
FeatherIcon,
|
||||||
Dialog,
|
Dialog,
|
||||||
@ -84,6 +81,7 @@ const breadcrumbs = [{ label: 'Leads', route: { name: 'Leads' } }]
|
|||||||
|
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
const { getOrganization } = organizationsStore()
|
const { getOrganization } = organizationsStore()
|
||||||
|
const { getLeadStatus } = statusesStore()
|
||||||
const { get: getOrderBy } = useOrderBy()
|
const { get: getOrderBy } = useOrderBy()
|
||||||
const { getArgs, storage } = useFilter()
|
const { getArgs, storage } = useFilter()
|
||||||
|
|
||||||
@ -154,7 +152,7 @@ const rows = computed(() => {
|
|||||||
} else if (row == 'status') {
|
} else if (row == 'status') {
|
||||||
_rows[row] = {
|
_rows[row] = {
|
||||||
label: lead.status,
|
label: lead.status,
|
||||||
color: leadStatuses[lead.status]?.color,
|
color: getLeadStatus(lead.status)?.color,
|
||||||
}
|
}
|
||||||
} else if (row == 'lead_owner') {
|
} else if (row == 'lead_owner') {
|
||||||
_rows[row] = {
|
_rows[row] = {
|
||||||
|
|||||||
@ -248,16 +248,15 @@ import CameraIcon from '@/components/Icons/CameraIcon.vue'
|
|||||||
import LeadsIcon from '@/components/Icons/LeadsIcon.vue'
|
import LeadsIcon from '@/components/Icons/LeadsIcon.vue'
|
||||||
import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
||||||
import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
|
import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
|
||||||
|
import { usersStore } from '@/stores/users'
|
||||||
import { organizationsStore } from '@/stores/organizations.js'
|
import { organizationsStore } from '@/stores/organizations.js'
|
||||||
|
import { statusesStore } from '@/stores/statuses'
|
||||||
import {
|
import {
|
||||||
dateFormat,
|
dateFormat,
|
||||||
dateTooltipFormat,
|
dateTooltipFormat,
|
||||||
timeAgo,
|
timeAgo,
|
||||||
leadStatuses,
|
|
||||||
dealStatuses,
|
|
||||||
formatNumberIntoCurrency,
|
formatNumberIntoCurrency,
|
||||||
} from '@/utils'
|
} from '@/utils'
|
||||||
import { usersStore } from '@/stores/users'
|
|
||||||
import { h, computed, ref } from 'vue'
|
import { h, computed, ref } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -268,6 +267,7 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const { organizations, getOrganization } = organizationsStore()
|
const { organizations, getOrganization } = organizationsStore()
|
||||||
|
const { getLeadStatus, getDealStatus } = statusesStore()
|
||||||
const showOrganizationModal = ref(false)
|
const showOrganizationModal = ref(false)
|
||||||
const detailMode = ref(false)
|
const detailMode = ref(false)
|
||||||
|
|
||||||
@ -454,7 +454,7 @@ function getLeadRowObject(lead) {
|
|||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
label: lead.status,
|
label: lead.status,
|
||||||
color: leadStatuses[lead.status]?.color,
|
color: getLeadStatus(lead.status)?.color,
|
||||||
},
|
},
|
||||||
email: lead.email,
|
email: lead.email,
|
||||||
mobile_no: lead.mobile_no,
|
mobile_no: lead.mobile_no,
|
||||||
@ -479,7 +479,7 @@ function getDealRowObject(deal) {
|
|||||||
annual_revenue: formatNumberIntoCurrency(deal.annual_revenue),
|
annual_revenue: formatNumberIntoCurrency(deal.annual_revenue),
|
||||||
status: {
|
status: {
|
||||||
label: deal.status,
|
label: deal.status,
|
||||||
color: dealStatuses[deal.status]?.color,
|
color: getDealStatus(deal.status)?.color,
|
||||||
},
|
},
|
||||||
email: deal.email,
|
email: deal.email,
|
||||||
mobile_no: deal.mobile_no,
|
mobile_no: deal.mobile_no,
|
||||||
|
|||||||
79
frontend/src/stores/statuses.js
Normal file
79
frontend/src/stores/statuses.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { createListResource } from 'frappe-ui'
|
||||||
|
import { reactive, h } from 'vue'
|
||||||
|
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
|
||||||
|
|
||||||
|
export const statusesStore = defineStore('crm-statuses', () => {
|
||||||
|
let leadStatusesByName = reactive({})
|
||||||
|
let dealStatusesByName = reactive({})
|
||||||
|
|
||||||
|
const leadStatuses = createListResource({
|
||||||
|
doctype: 'CRM Lead Status',
|
||||||
|
fields: ['name', 'color', 'position'],
|
||||||
|
orderBy: 'position asc',
|
||||||
|
cache: 'lead-statuses',
|
||||||
|
initialData: [],
|
||||||
|
auto: true,
|
||||||
|
transform(statuses) {
|
||||||
|
for (let status of statuses) {
|
||||||
|
status.color =
|
||||||
|
status.color == 'black'
|
||||||
|
? '!text-gray-900'
|
||||||
|
: `!text-${status.color}-600`
|
||||||
|
leadStatusesByName[status.name] = status
|
||||||
|
}
|
||||||
|
return statuses
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const dealStatuses = createListResource({
|
||||||
|
doctype: 'CRM Deal Status',
|
||||||
|
fields: ['name', 'color', 'position'],
|
||||||
|
orderBy: 'position asc',
|
||||||
|
cache: 'deal-statuses',
|
||||||
|
initialData: [],
|
||||||
|
auto: true,
|
||||||
|
transform(statuses) {
|
||||||
|
for (let status of statuses) {
|
||||||
|
status.color =
|
||||||
|
status.color == 'black'
|
||||||
|
? '!text-gray-900'
|
||||||
|
: `!text-${status.color}-600`
|
||||||
|
dealStatusesByName[status.name] = status
|
||||||
|
}
|
||||||
|
return statuses
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function getLeadStatus(name) {
|
||||||
|
return leadStatusesByName[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDealStatus(name) {
|
||||||
|
return dealStatusesByName[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusOptions(doctype, action) {
|
||||||
|
let statusesByName =
|
||||||
|
doctype == 'deal' ? dealStatusesByName : leadStatusesByName
|
||||||
|
let options = []
|
||||||
|
for (const status in statusesByName) {
|
||||||
|
options.push({
|
||||||
|
label: statusesByName[status].name,
|
||||||
|
icon: () => h(IndicatorIcon, { class: statusesByName[status].color }),
|
||||||
|
onClick: () => {
|
||||||
|
action && action('status', statusesByName[status].name)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
leadStatuses,
|
||||||
|
dealStatuses,
|
||||||
|
getLeadStatus,
|
||||||
|
getDealStatus,
|
||||||
|
statusOptions,
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
|
|
||||||
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 { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
@ -24,67 +23,6 @@ export function timeAgo(date) {
|
|||||||
|
|
||||||
export const dateTooltipFormat = 'ddd, MMM D, YYYY h:mm A'
|
export const dateTooltipFormat = 'ddd, MMM D, YYYY h:mm A'
|
||||||
|
|
||||||
export const leadStatuses = {
|
|
||||||
Open: { label: 'Open', color: '!text-gray-600' },
|
|
||||||
Contacted: {
|
|
||||||
label: 'Contacted',
|
|
||||||
color: '!text-orange-600',
|
|
||||||
},
|
|
||||||
Nurture: {
|
|
||||||
label: 'Nurture',
|
|
||||||
color: '!text-blue-600',
|
|
||||||
},
|
|
||||||
Qualified: {
|
|
||||||
label: 'Qualified',
|
|
||||||
color: '!text-green-600',
|
|
||||||
},
|
|
||||||
Unqualified: {
|
|
||||||
label: 'Unqualified',
|
|
||||||
color: '!text-red-600',
|
|
||||||
},
|
|
||||||
Junk: { label: 'Junk', color: '!text-purple-600' },
|
|
||||||
}
|
|
||||||
|
|
||||||
export const dealStatuses = {
|
|
||||||
Qualification: {
|
|
||||||
label: 'Qualification',
|
|
||||||
color: '!text-gray-600',
|
|
||||||
},
|
|
||||||
'Demo/Making': {
|
|
||||||
label: 'Demo/Making',
|
|
||||||
color: '!text-orange-600',
|
|
||||||
},
|
|
||||||
'Proposal/Quotation': {
|
|
||||||
label: 'Proposal/Quotation',
|
|
||||||
color: '!text-blue-600',
|
|
||||||
},
|
|
||||||
Negotiation: {
|
|
||||||
label: 'Negotiation',
|
|
||||||
color: '!text-yellow-600',
|
|
||||||
},
|
|
||||||
'Ready to Close': {
|
|
||||||
label: 'Ready to Close',
|
|
||||||
color: '!text-purple-600',
|
|
||||||
},
|
|
||||||
Won: { label: 'Won', color: '!text-green-600' },
|
|
||||||
Lost: { label: 'Lost', color: '!text-red-600' },
|
|
||||||
}
|
|
||||||
|
|
||||||
export function statusDropdownOptions(data, doctype, action) {
|
|
||||||
let statuses = doctype == 'deal' ? dealStatuses : leadStatuses
|
|
||||||
let options = []
|
|
||||||
for (const status in statuses) {
|
|
||||||
options.push({
|
|
||||||
label: statuses[status].label,
|
|
||||||
icon: () => h(IndicatorIcon, { class: statuses[status].color }),
|
|
||||||
onClick: () => {
|
|
||||||
action && action('status', statuses[status].label)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
export function taskStatusOptions(action, data) {
|
export function taskStatusOptions(action, data) {
|
||||||
return ['Backlog', 'Todo', 'In Progress', 'Done', 'Canceled'].map(
|
return ['Backlog', 'Todo', 'In Progress', 'Done', 'Canceled'].map(
|
||||||
(status) => {
|
(status) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user