Merge pull request #840 from frappe/main-hotfix

This commit is contained in:
Shariq Ansari 2025-05-20 14:40:25 +05:30 committed by GitHub
commit 3e330431a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 702 additions and 608 deletions

View File

@ -118,6 +118,12 @@ def invite_by_email(emails: str, role: str):
for email in to_invite: for email in to_invite:
frappe.get_doc(doctype="CRM Invitation", email=email, role=role).insert(ignore_permissions=True) frappe.get_doc(doctype="CRM Invitation", email=email, role=role).insert(ignore_permissions=True)
return {
"existing_members": existing_members,
"existing_invites": existing_invites,
"to_invite": to_invite,
}
@frappe.whitelist() @frappe.whitelist()
def get_file_uploader_defaults(doctype: str): def get_file_uploader_defaults(doctype: str):

View File

@ -65,7 +65,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2024-09-16 19:40:19.340948", "modified": "2025-05-19 17:57:24.610295",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "FCRM", "module": "FCRM",
"name": "CRM Form Script", "name": "CRM Form Script",
@ -83,9 +83,19 @@
"role": "Sales Manager", "role": "Sales Manager",
"share": 1, "share": 1,
"write": 1 "write": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"share": 1
} }
], ],
"row_format": "Dynamic",
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [] "states": []
} }

View File

@ -21,7 +21,7 @@ class CRMInvitation(Document):
if frappe.local.dev_server: if frappe.local.dev_server:
print(f"Invite link for {self.email}: {invite_link}") print(f"Invite link for {self.email}: {invite_link}")
title = f"Frappe CRM" title = "Frappe CRM"
template = "crm_invitation" template = "crm_invitation"
frappe.sendmail( frappe.sendmail(
@ -44,12 +44,24 @@ class CRMInvitation(Document):
user = self.create_user_if_not_exists() user = self.create_user_if_not_exists()
user.append_roles(self.role) user.append_roles(self.role)
if self.role == "Sales User":
self.update_module_in_user(user, "FCRM")
user.save(ignore_permissions=True) user.save(ignore_permissions=True)
self.status = "Accepted" self.status = "Accepted"
self.accepted_at = frappe.utils.now() self.accepted_at = frappe.utils.now()
self.save(ignore_permissions=True) self.save(ignore_permissions=True)
def update_module_in_user(self, user, module):
block_modules = frappe.get_all(
"Module Def",
fields=["name as module"],
filters={"name": ["!=", module]},
)
if block_modules:
user.set("block_modules", block_modules)
def create_user_if_not_exists(self): def create_user_if_not_exists(self):
if not frappe.db.exists("User", self.email): if not frappe.db.exists("User", self.email):
first_name = self.email.split("@")[0].title() first_name = self.email.split("@")[0].title()

View File

@ -264,7 +264,7 @@ def create_customer_in_remote_site(customer, erpnext_crm_settings):
@frappe.whitelist() @frappe.whitelist()
def get_crm_form_script(): def get_crm_form_script():
return """ return """
async function setupForm({ doc, call, $dialog, updateField, createToast }) { async function setupForm({ doc, call, $dialog, updateField, toast }) {
let actions = []; let actions = [];
let is_erpnext_integration_enabled = await call("frappe.client.get_single_value", {doctype: "ERPNext CRM Settings", field: "enabled"}); let is_erpnext_integration_enabled = await call("frappe.client.get_single_value", {doctype: "ERPNext CRM Settings", field: "enabled"});
if (!["Lost", "Won"].includes(doc?.status) && is_erpnext_integration_enabled) { if (!["Lost", "Won"].includes(doc?.status) && is_erpnext_integration_enabled) {

View File

@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Frappe CRM VERSION\n" "Project-Id-Version: Frappe CRM VERSION\n"
"Report-Msgid-Bugs-To: shariq@frappe.io\n" "Report-Msgid-Bugs-To: shariq@frappe.io\n"
"POT-Creation-Date: 2025-05-04 09:35+0000\n" "POT-Creation-Date: 2025-05-18 09:35+0000\n"
"PO-Revision-Date: 2025-05-04 09:35+0000\n" "PO-Revision-Date: 2025-05-18 09:35+0000\n"
"Last-Translator: shariq@frappe.io\n" "Last-Translator: shariq@frappe.io\n"
"Language-Team: shariq@frappe.io\n" "Language-Team: shariq@frappe.io\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -176,8 +176,8 @@ msgstr ""
msgid "Actions" msgid "Actions"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:546 frontend/src/pages/Lead.vue:532 #: frontend/src/pages/Deal.vue:545 frontend/src/pages/Lead.vue:531
#: frontend/src/pages/MobileDeal.vue:441 frontend/src/pages/MobileLead.vue:344 #: frontend/src/pages/MobileDeal.vue:440 frontend/src/pages/MobileLead.vue:343
msgid "Activity" msgid "Activity"
msgstr "" msgstr ""
@ -201,7 +201,7 @@ msgstr ""
msgid "Add Filter" msgid "Add Filter"
msgstr "" msgstr ""
#: frontend/src/components/Controls/Grid.vue:243 #: frontend/src/components/Controls/Grid.vue:310
msgid "Add Row" msgid "Add Row"
msgstr "" msgstr ""
@ -282,12 +282,19 @@ msgstr ""
msgid "All" msgid "All"
msgstr "" msgstr ""
#: frontend/src/pages/Contact.vue:633 frontend/src/pages/MobileContact.vue:607 #. Label of the amount (Currency) field in DocType 'CRM Products'
#: frontend/src/pages/MobileOrganization.vue:493 #: crm/fcrm/doctype/crm_products/crm_products.json
#: frontend/src/pages/Organization.vue:532 #: frontend/src/pages/Contact.vue:632 frontend/src/pages/MobileContact.vue:606
#: frontend/src/pages/MobileOrganization.vue:492
#: frontend/src/pages/Organization.vue:531
msgid "Amount" msgid "Amount"
msgstr "" msgstr ""
#. Description of the 'Net Amount' (Currency) field in DocType 'CRM Products'
#: crm/fcrm/doctype/crm_products/crm_products.json
msgid "Amount after discount"
msgstr ""
#. Description of the 'Favicon' (Attach) field in DocType 'FCRM Settings' #. Description of the 'Favicon' (Attach) field in DocType 'FCRM Settings'
#: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json #: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json
msgid "An icon file with .ico extension. Should be 16 x 16 px. Generated using a favicon generator. [favicon-generator.org]" msgid "An icon file with .ico extension. Should be 16 x 16 px. Generated using a favicon generator. [favicon-generator.org]"
@ -347,12 +354,12 @@ msgstr ""
msgid "Are you sure you want to delete this attachment?" msgid "Are you sure you want to delete this attachment?"
msgstr "" msgstr ""
#: frontend/src/pages/Contact.vue:321 frontend/src/pages/MobileContact.vue:292 #: frontend/src/pages/Contact.vue:320 frontend/src/pages/MobileContact.vue:291
msgid "Are you sure you want to delete this contact?" msgid "Are you sure you want to delete this contact?"
msgstr "" msgstr ""
#: frontend/src/pages/MobileOrganization.vue:286 #: frontend/src/pages/MobileOrganization.vue:285
#: frontend/src/pages/Organization.vue:328 #: frontend/src/pages/Organization.vue:327
msgid "Are you sure you want to delete this organization?" msgid "Are you sure you want to delete this organization?"
msgstr "" msgstr ""
@ -400,7 +407,7 @@ msgstr ""
msgid "Assignment rule" msgid "Assignment rule"
msgstr "" msgstr ""
#: frontend/src/components/Controls/GridFieldsEditorModal.vue:171 #: frontend/src/components/Controls/GridFieldsEditorModal.vue:176
msgid "At least one field is required" msgid "At least one field is required"
msgstr "" msgstr ""
@ -413,8 +420,8 @@ msgstr ""
msgid "Attach a file" msgid "Attach a file"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:581 frontend/src/pages/Lead.vue:567 #: frontend/src/pages/Deal.vue:580 frontend/src/pages/Lead.vue:566
#: frontend/src/pages/MobileDeal.vue:477 frontend/src/pages/MobileLead.vue:380 #: frontend/src/pages/MobileDeal.vue:476 frontend/src/pages/MobileLead.vue:379
msgid "Attachments" msgid "Attachments"
msgstr "" msgstr ""
@ -572,6 +579,16 @@ msgstr ""
msgid "CRM Portal Page" msgid "CRM Portal Page"
msgstr "" msgstr ""
#. Name of a DocType
#: crm/fcrm/doctype/crm_product/crm_product.json
msgid "CRM Product"
msgstr ""
#. Name of a DocType
#: crm/fcrm/doctype/crm_products/crm_products.json
msgid "CRM Products"
msgstr ""
#. Name of a DocType #. Name of a DocType
#: crm/fcrm/doctype/crm_service_day/crm_service_day.json #: crm/fcrm/doctype/crm_service_day/crm_service_day.json
msgid "CRM Service Day" msgid "CRM Service Day"
@ -667,8 +684,8 @@ msgstr ""
msgid "Calling..." msgid "Calling..."
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:566 frontend/src/pages/Lead.vue:552 #: frontend/src/pages/Deal.vue:565 frontend/src/pages/Lead.vue:551
#: frontend/src/pages/MobileDeal.vue:461 frontend/src/pages/MobileLead.vue:364 #: frontend/src/pages/MobileDeal.vue:460 frontend/src/pages/MobileLead.vue:363
msgid "Calls" msgid "Calls"
msgstr "" msgstr ""
@ -723,8 +740,8 @@ msgstr ""
msgid "Change image" msgid "Change image"
msgstr "" msgstr ""
#: frontend/src/pages/Lead.vue:245 frontend/src/pages/Lead.vue:271 #: frontend/src/pages/Lead.vue:244 frontend/src/pages/Lead.vue:270
#: frontend/src/pages/MobileLead.vue:110 frontend/src/pages/MobileLead.vue:137 #: frontend/src/pages/MobileLead.vue:109 frontend/src/pages/MobileLead.vue:136
msgid "Choose Existing" msgid "Choose Existing"
msgstr "" msgstr ""
@ -811,8 +828,8 @@ msgstr ""
msgid "Comment" msgid "Comment"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:556 frontend/src/pages/Lead.vue:542 #: frontend/src/pages/Deal.vue:555 frontend/src/pages/Lead.vue:541
#: frontend/src/pages/MobileDeal.vue:451 frontend/src/pages/MobileLead.vue:354 #: frontend/src/pages/MobileDeal.vue:450 frontend/src/pages/MobileLead.vue:353
msgid "Comments" msgid "Comments"
msgstr "" msgstr ""
@ -861,7 +878,7 @@ msgstr ""
#: crm/fcrm/doctype/crm_contacts/crm_contacts.json #: crm/fcrm/doctype/crm_contacts/crm_contacts.json
#: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/doctype/crm_deal/crm_deal.json
#: frontend/src/components/Layouts/AppSidebar.vue:509 #: frontend/src/components/Layouts/AppSidebar.vue:509
#: frontend/src/pages/Lead.vue:267 frontend/src/pages/MobileLead.vue:133 #: frontend/src/pages/Lead.vue:266 frontend/src/pages/MobileLead.vue:132
msgid "Contact" msgid "Contact"
msgstr "" msgstr ""
@ -873,11 +890,11 @@ msgstr ""
msgid "Contact Us" msgid "Contact Us"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:665 frontend/src/pages/MobileDeal.vue:560 #: frontend/src/pages/Deal.vue:664 frontend/src/pages/MobileDeal.vue:559
msgid "Contact added" msgid "Contact added"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:651 frontend/src/pages/MobileDeal.vue:546 #: frontend/src/pages/Deal.vue:650 frontend/src/pages/MobileDeal.vue:545
msgid "Contact already added" msgid "Contact already added"
msgstr "" msgstr ""
@ -889,7 +906,7 @@ msgstr ""
msgid "Contact not found" msgid "Contact not found"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:680 frontend/src/pages/MobileDeal.vue:575 #: frontend/src/pages/Deal.vue:679 frontend/src/pages/MobileDeal.vue:574
msgid "Contact removed" msgid "Contact removed"
msgstr "" msgstr ""
@ -898,8 +915,8 @@ msgstr ""
#. Label of a shortcut in the Frappe CRM Workspace #. Label of a shortcut in the Frappe CRM Workspace
#: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/doctype/crm_deal/crm_deal.json
#: crm/fcrm/workspace/frappe_crm/frappe_crm.json #: crm/fcrm/workspace/frappe_crm/frappe_crm.json
#: frontend/src/pages/Contact.vue:265 frontend/src/pages/MobileContact.vue:236 #: frontend/src/pages/Contact.vue:264 frontend/src/pages/MobileContact.vue:235
#: frontend/src/pages/MobileOrganization.vue:373 #: frontend/src/pages/MobileOrganization.vue:372
msgid "Contacts" msgid "Contacts"
msgstr "" msgstr ""
@ -917,8 +934,8 @@ msgstr ""
#: frontend/src/components/Layouts/AppSidebar.vue:351 #: frontend/src/components/Layouts/AppSidebar.vue:351
#: frontend/src/components/ListBulkActions.vue:70 #: frontend/src/components/ListBulkActions.vue:70
#: frontend/src/pages/Lead.vue:205 frontend/src/pages/MobileLead.vue:49 #: frontend/src/pages/Lead.vue:204 frontend/src/pages/MobileLead.vue:49
#: frontend/src/pages/MobileLead.vue:96 #: frontend/src/pages/MobileLead.vue:95
msgid "Convert" msgid "Convert"
msgstr "" msgstr ""
@ -929,8 +946,8 @@ msgstr ""
#: frontend/src/components/ListBulkActions.vue:62 #: frontend/src/components/ListBulkActions.vue:62
#: frontend/src/components/ListBulkActions.vue:204 #: frontend/src/components/ListBulkActions.vue:204
#: frontend/src/pages/Lead.vue:38 frontend/src/pages/Lead.vue:216 #: frontend/src/pages/Lead.vue:38 frontend/src/pages/Lead.vue:215
#: frontend/src/pages/MobileLead.vue:92 #: frontend/src/pages/MobileLead.vue:91
msgid "Convert to Deal" msgid "Convert to Deal"
msgstr "" msgstr ""
@ -946,6 +963,7 @@ msgstr ""
#: frontend/src/components/Modals/AddressModal.vue:100 #: frontend/src/components/Modals/AddressModal.vue:100
#: frontend/src/components/Modals/CallLogModal.vue:104 #: frontend/src/components/Modals/CallLogModal.vue:104
#: frontend/src/components/Modals/ContactModal.vue:37 #: frontend/src/components/Modals/ContactModal.vue:37
#: frontend/src/components/Modals/CreateDocumentModal.vue:87
#: frontend/src/components/Modals/DealModal.vue:63 #: frontend/src/components/Modals/DealModal.vue:63
#: frontend/src/components/Modals/EmailTemplateModal.vue:9 #: frontend/src/components/Modals/EmailTemplateModal.vue:9
#: frontend/src/components/Modals/LeadModal.vue:34 #: frontend/src/components/Modals/LeadModal.vue:34
@ -985,7 +1003,7 @@ msgstr ""
#: frontend/src/components/Controls/Link.vue:48 #: frontend/src/components/Controls/Link.vue:48
#: frontend/src/components/Modals/EmailTemplateSelectorModal.vue:55 #: frontend/src/components/Modals/EmailTemplateSelectorModal.vue:55
#: frontend/src/components/Modals/WhatsappTemplateSelectorModal.vue:45 #: frontend/src/components/Modals/WhatsappTemplateSelectorModal.vue:45
#: frontend/src/components/SidePanelLayout.vue:125 #: frontend/src/components/SidePanelLayout.vue:135
msgid "Create New" msgid "Create New"
msgstr "" msgstr ""
@ -1053,7 +1071,7 @@ msgstr ""
msgid "Custom statuses" msgid "Custom statuses"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:434 #: frontend/src/pages/Deal.vue:433
msgid "Customer created successfully" msgid "Customer created successfully"
msgstr "" msgstr ""
@ -1067,8 +1085,8 @@ msgstr ""
#: frontend/src/components/Activities/DataFields.vue:6 #: frontend/src/components/Activities/DataFields.vue:6
#: frontend/src/components/Layouts/AppSidebar.vue:539 #: frontend/src/components/Layouts/AppSidebar.vue:539
#: frontend/src/pages/Deal.vue:561 frontend/src/pages/Lead.vue:547 #: frontend/src/pages/Deal.vue:560 frontend/src/pages/Lead.vue:546
#: frontend/src/pages/MobileDeal.vue:456 frontend/src/pages/MobileLead.vue:359 #: frontend/src/pages/MobileDeal.vue:455 frontend/src/pages/MobileLead.vue:358
msgid "Data" msgid "Data"
msgstr "" msgstr ""
@ -1103,21 +1121,21 @@ msgstr ""
msgid "Deal Statuses" msgid "Deal Statuses"
msgstr "" msgstr ""
#: frontend/src/pages/Contact.vue:654 frontend/src/pages/MobileContact.vue:628 #: frontend/src/pages/Contact.vue:653 frontend/src/pages/MobileContact.vue:627
#: frontend/src/pages/MobileOrganization.vue:514 #: frontend/src/pages/MobileOrganization.vue:513
#: frontend/src/pages/Organization.vue:553 #: frontend/src/pages/Organization.vue:552
msgid "Deal owner" msgid "Deal owner"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:474 frontend/src/pages/MobileDeal.vue:363 #: frontend/src/pages/Deal.vue:473 frontend/src/pages/MobileDeal.vue:362
msgid "Deal updated" msgid "Deal updated"
msgstr "" msgstr ""
#. Label of a shortcut in the Frappe CRM Workspace #. Label of a shortcut in the Frappe CRM Workspace
#: crm/fcrm/workspace/frappe_crm/frappe_crm.json #: crm/fcrm/workspace/frappe_crm/frappe_crm.json
#: frontend/src/pages/Deal.vue:506 frontend/src/pages/MobileContact.vue:320 #: frontend/src/pages/Deal.vue:505 frontend/src/pages/MobileContact.vue:319
#: frontend/src/pages/MobileDeal.vue:395 #: frontend/src/pages/MobileDeal.vue:394
#: frontend/src/pages/MobileOrganization.vue:367 #: frontend/src/pages/MobileOrganization.vue:366
msgid "Deals" msgid "Deals"
msgstr "" msgstr ""
@ -1191,21 +1209,21 @@ msgstr ""
#: frontend/src/components/Activities/NoteArea.vue:12 #: frontend/src/components/Activities/NoteArea.vue:12
#: frontend/src/components/Activities/TaskArea.vue:55 #: frontend/src/components/Activities/TaskArea.vue:55
#: frontend/src/components/Activities/TaskArea.vue:63 #: frontend/src/components/Activities/TaskArea.vue:63
#: frontend/src/components/Controls/Grid.vue:238 #: frontend/src/components/Controls/Grid.vue:305
#: frontend/src/components/Kanban/KanbanView.vue:225 #: frontend/src/components/Kanban/KanbanView.vue:225
#: frontend/src/components/ListBulkActions.vue:96 #: frontend/src/components/ListBulkActions.vue:96
#: frontend/src/components/ListBulkActions.vue:104 #: frontend/src/components/ListBulkActions.vue:104
#: frontend/src/components/ListBulkActions.vue:186 #: frontend/src/components/ListBulkActions.vue:186
#: frontend/src/components/ViewControls.vue:1163 #: frontend/src/components/ViewControls.vue:1163
#: frontend/src/components/ViewControls.vue:1174 #: frontend/src/components/ViewControls.vue:1174
#: frontend/src/pages/Contact.vue:105 frontend/src/pages/Contact.vue:324 #: frontend/src/pages/Contact.vue:105 frontend/src/pages/Contact.vue:323
#: frontend/src/pages/MobileContact.vue:81 #: frontend/src/pages/MobileContact.vue:81
#: frontend/src/pages/MobileContact.vue:295 #: frontend/src/pages/MobileContact.vue:294
#: frontend/src/pages/MobileDeal.vue:526 #: frontend/src/pages/MobileDeal.vue:525
#: frontend/src/pages/MobileOrganization.vue:72 #: frontend/src/pages/MobileOrganization.vue:72
#: frontend/src/pages/MobileOrganization.vue:289 #: frontend/src/pages/MobileOrganization.vue:288
#: frontend/src/pages/Notes.vue:40 frontend/src/pages/Organization.vue:83 #: frontend/src/pages/Notes.vue:40 frontend/src/pages/Organization.vue:83
#: frontend/src/pages/Organization.vue:331 frontend/src/pages/Tasks.vue:368 #: frontend/src/pages/Organization.vue:330 frontend/src/pages/Tasks.vue:368
msgid "Delete" msgid "Delete"
msgstr "" msgstr ""
@ -1227,12 +1245,12 @@ msgstr ""
msgid "Delete attachment" msgid "Delete attachment"
msgstr "" msgstr ""
#: frontend/src/pages/Contact.vue:320 frontend/src/pages/MobileContact.vue:291 #: frontend/src/pages/Contact.vue:319 frontend/src/pages/MobileContact.vue:290
msgid "Delete contact" msgid "Delete contact"
msgstr "" msgstr ""
#: frontend/src/pages/MobileOrganization.vue:285 #: frontend/src/pages/MobileOrganization.vue:284
#: frontend/src/pages/Organization.vue:327 #: frontend/src/pages/Organization.vue:326
msgid "Delete organization" msgid "Delete organization"
msgstr "" msgstr ""
@ -1241,8 +1259,10 @@ msgid "Deleted successfully"
msgstr "" msgstr ""
#. Label of the description (Text Editor) field in DocType 'CRM Holiday' #. Label of the description (Text Editor) field in DocType 'CRM Holiday'
#. Label of the description (Text Editor) field in DocType 'CRM Product'
#. Label of the description (Text Editor) field in DocType 'CRM Task' #. Label of the description (Text Editor) field in DocType 'CRM Task'
#: crm/fcrm/doctype/crm_holiday/crm_holiday.json #: crm/fcrm/doctype/crm_holiday/crm_holiday.json
#: crm/fcrm/doctype/crm_product/crm_product.json
#: crm/fcrm/doctype/crm_task/crm_task.json #: crm/fcrm/doctype/crm_task/crm_task.json
#: frontend/src/components/Modals/TaskModal.vue:48 #: frontend/src/components/Modals/TaskModal.vue:48
msgid "Description" msgid "Description"
@ -1256,9 +1276,9 @@ msgstr ""
#. Label of the details (Text Editor) field in DocType 'CRM Lead Source' #. Label of the details (Text Editor) field in DocType 'CRM Lead Source'
#: crm/fcrm/doctype/crm_lead/crm_lead.json #: crm/fcrm/doctype/crm_lead/crm_lead.json
#: crm/fcrm/doctype/crm_lead_source/crm_lead_source.json #: crm/fcrm/doctype/crm_lead_source/crm_lead_source.json
#: frontend/src/pages/MobileContact.vue:315 #: frontend/src/pages/MobileContact.vue:314
#: frontend/src/pages/MobileDeal.vue:435 frontend/src/pages/MobileLead.vue:338 #: frontend/src/pages/MobileDeal.vue:434 frontend/src/pages/MobileLead.vue:337
#: frontend/src/pages/MobileOrganization.vue:362 #: frontend/src/pages/MobileOrganization.vue:361
msgid "Details" msgid "Details"
msgstr "" msgstr ""
@ -1273,11 +1293,26 @@ msgstr ""
msgid "Disable" msgid "Disable"
msgstr "" msgstr ""
#. Label of the disabled (Check) field in DocType 'CRM Product'
#: crm/fcrm/doctype/crm_product/crm_product.json
msgid "Disabled"
msgstr ""
#: frontend/src/components/CommentBox.vue:76 #: frontend/src/components/CommentBox.vue:76
#: frontend/src/components/EmailEditor.vue:158 #: frontend/src/components/EmailEditor.vue:158
msgid "Discard" msgid "Discard"
msgstr "" msgstr ""
#. Label of the discount_percentage (Percent) field in DocType 'CRM Products'
#: crm/fcrm/doctype/crm_products/crm_products.json
msgid "Discount %"
msgstr ""
#. Label of the discount_amount (Currency) field in DocType 'CRM Products'
#: crm/fcrm/doctype/crm_products/crm_products.json
msgid "Discount Amount"
msgstr ""
#. Label of the dt (Link) field in DocType 'CRM Form Script' #. Label of the dt (Link) field in DocType 'CRM Form Script'
#. Label of the dt (Link) field in DocType 'CRM Global Settings' #. Label of the dt (Link) field in DocType 'CRM Global Settings'
#. Label of the dt (Link) field in DocType 'CRM View Settings' #. Label of the dt (Link) field in DocType 'CRM View Settings'
@ -1300,6 +1335,10 @@ msgstr ""
msgid "Document not found" msgid "Document not found"
msgstr "" msgstr ""
#: frontend/src/data/document.js:21
msgid "Document updated successfully"
msgstr ""
#. Option for the 'Status' (Select) field in DocType 'CRM Task' #. Option for the 'Status' (Select) field in DocType 'CRM Task'
#: crm/fcrm/doctype/crm_task/crm_task.json #: crm/fcrm/doctype/crm_task/crm_task.json
msgid "Done" msgid "Done"
@ -1382,7 +1421,7 @@ msgstr ""
msgid "ERPNext is not integrated with the CRM" msgid "ERPNext is not integrated with the CRM"
msgstr "" msgstr ""
#: frontend/src/components/FieldLayout/Field.vue:80 #: frontend/src/components/FieldLayout/Field.vue:91
#: frontend/src/components/FieldLayoutEditor.vue:319 #: frontend/src/components/FieldLayoutEditor.vue:319
#: frontend/src/components/FieldLayoutEditor.vue:345 #: frontend/src/components/FieldLayoutEditor.vue:345
#: frontend/src/components/ListBulkActions.vue:179 #: frontend/src/components/ListBulkActions.vue:179
@ -1454,12 +1493,12 @@ msgstr ""
#: crm/fcrm/doctype/crm_contacts/crm_contacts.json #: crm/fcrm/doctype/crm_contacts/crm_contacts.json
#: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/doctype/crm_deal/crm_deal.json
#: crm/fcrm/doctype/crm_invitation/crm_invitation.json #: crm/fcrm/doctype/crm_invitation/crm_invitation.json
#: crm/fcrm/doctype/crm_lead/crm_lead.json frontend/src/pages/Contact.vue:644 #: crm/fcrm/doctype/crm_lead/crm_lead.json frontend/src/pages/Contact.vue:643
#: frontend/src/pages/MobileContact.vue:618 #: frontend/src/pages/MobileContact.vue:617
#: frontend/src/pages/MobileOrganization.vue:504 #: frontend/src/pages/MobileOrganization.vue:503
#: frontend/src/pages/MobileOrganization.vue:532 #: frontend/src/pages/MobileOrganization.vue:531
#: frontend/src/pages/Organization.vue:543 #: frontend/src/pages/Organization.vue:542
#: frontend/src/pages/Organization.vue:571 #: frontend/src/pages/Organization.vue:570
msgid "Email" msgid "Email"
msgstr "" msgstr ""
@ -1501,8 +1540,8 @@ msgstr ""
msgid "Email template" msgid "Email template"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:551 frontend/src/pages/Lead.vue:537 #: frontend/src/pages/Deal.vue:550 frontend/src/pages/Lead.vue:536
#: frontend/src/pages/MobileDeal.vue:446 frontend/src/pages/MobileLead.vue:349 #: frontend/src/pages/MobileDeal.vue:445 frontend/src/pages/MobileLead.vue:348
msgid "Emails" msgid "Emails"
msgstr "" msgstr ""
@ -1556,7 +1595,7 @@ msgstr ""
msgid "End Time" msgid "End Time"
msgstr "" msgstr ""
#: frontend/src/components/FieldLayout/Field.vue:265 #: frontend/src/components/FieldLayout/Field.vue:324
msgid "Enter {0}" msgid "Enter {0}"
msgstr "" msgstr ""
@ -1574,28 +1613,32 @@ msgstr ""
#: frontend/src/components/Settings/SettingsPage.vue:91 #: frontend/src/components/Settings/SettingsPage.vue:91
#: frontend/src/components/Settings/TelephonySettings.vue:131 #: frontend/src/components/Settings/TelephonySettings.vue:131
#: frontend/src/components/Settings/TelephonySettings.vue:156 #: frontend/src/components/Settings/TelephonySettings.vue:156
#: frontend/src/pages/Lead.vue:633 frontend/src/pages/Lead.vue:643 #: frontend/src/pages/Lead.vue:632 frontend/src/pages/Lead.vue:642
#: frontend/src/pages/MobileLead.vue:438 frontend/src/pages/MobileLead.vue:448 #: frontend/src/pages/MobileLead.vue:437 frontend/src/pages/MobileLead.vue:447
msgid "Error" msgid "Error"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:495 frontend/src/pages/MobileDeal.vue:384 #: frontend/src/pages/Deal.vue:494 frontend/src/pages/MobileDeal.vue:383
msgid "Error Updating Deal" msgid "Error Updating Deal"
msgstr "" msgstr ""
#: frontend/src/pages/Lead.vue:481 frontend/src/pages/MobileLead.vue:287 #: frontend/src/pages/Lead.vue:480 frontend/src/pages/MobileLead.vue:286
msgid "Error Updating Lead" msgid "Error Updating Lead"
msgstr "" msgstr ""
#: frontend/src/pages/Lead.vue:666 #: frontend/src/pages/Lead.vue:665
msgid "Error converting to deal" msgid "Error converting to deal"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:482 frontend/src/pages/MobileDeal.vue:371 #: frontend/src/pages/Deal.vue:481 frontend/src/pages/MobileDeal.vue:370
msgid "Error updating deal" msgid "Error updating deal"
msgstr "" msgstr ""
#: frontend/src/pages/Lead.vue:468 frontend/src/pages/MobileLead.vue:274 #: frontend/src/data/document.js:28
msgid "Error updating document"
msgstr ""
#: frontend/src/pages/Lead.vue:467 frontend/src/pages/MobileLead.vue:273
msgid "Error updating lead" msgid "Error updating lead"
msgstr "" msgstr ""
@ -1702,6 +1745,10 @@ msgstr ""
msgid "Failed to create email account, Invalid credentials" msgid "Failed to create email account, Invalid credentials"
msgstr "" msgstr ""
#: frontend/src/data/script.js:98
msgid "Failed to load form controller: {0}"
msgstr ""
#: crm/integrations/twilio/api.py:152 #: crm/integrations/twilio/api.py:152
msgid "Failed to update Twilio call status" msgid "Failed to update Twilio call status"
msgstr "" msgstr ""
@ -1979,7 +2026,9 @@ msgid "If enabled, records can be created from the incoming emails on this accou
msgstr "" msgstr ""
#. Label of the image (Attach Image) field in DocType 'CRM Lead' #. Label of the image (Attach Image) field in DocType 'CRM Lead'
#. Label of the image (Attach Image) field in DocType 'CRM Product'
#: crm/fcrm/doctype/crm_lead/crm_lead.json #: crm/fcrm/doctype/crm_lead/crm_lead.json
#: crm/fcrm/doctype/crm_product/crm_product.json
msgid "Image" msgid "Image"
msgstr "" msgstr ""
@ -2236,11 +2285,11 @@ msgstr ""
msgid "Last Year" msgid "Last Year"
msgstr "" msgstr ""
#: frontend/src/pages/Contact.vue:659 frontend/src/pages/MobileContact.vue:633 #: frontend/src/pages/Contact.vue:658 frontend/src/pages/MobileContact.vue:632
#: frontend/src/pages/MobileOrganization.vue:519 #: frontend/src/pages/MobileOrganization.vue:518
#: frontend/src/pages/MobileOrganization.vue:547 #: frontend/src/pages/MobileOrganization.vue:546
#: frontend/src/pages/Organization.vue:558 #: frontend/src/pages/Organization.vue:557
#: frontend/src/pages/Organization.vue:586 #: frontend/src/pages/Organization.vue:585
msgid "Last modified" msgid "Last modified"
msgstr "" msgstr ""
@ -2286,13 +2335,13 @@ msgstr ""
msgid "Lead Statuses" msgid "Lead Statuses"
msgstr "" msgstr ""
#: frontend/src/pages/Lead.vue:460 frontend/src/pages/MobileLead.vue:266 #: frontend/src/pages/Lead.vue:459 frontend/src/pages/MobileLead.vue:265
msgid "Lead updated" msgid "Lead updated"
msgstr "" msgstr ""
#. Label of a shortcut in the Frappe CRM Workspace #. Label of a shortcut in the Frappe CRM Workspace
#: crm/fcrm/workspace/frappe_crm/frappe_crm.json #: crm/fcrm/workspace/frappe_crm/frappe_crm.json
#: frontend/src/pages/Lead.vue:492 frontend/src/pages/MobileLead.vue:298 #: frontend/src/pages/Lead.vue:491 frontend/src/pages/MobileLead.vue:297
msgid "Leads" msgid "Leads"
msgstr "" msgstr ""
@ -2347,7 +2396,7 @@ msgstr ""
#: frontend/src/components/Activities/Activities.vue:22 #: frontend/src/components/Activities/Activities.vue:22
#: frontend/src/components/Activities/DataFields.vue:35 #: frontend/src/components/Activities/DataFields.vue:35
#: frontend/src/pages/Deal.vue:177 frontend/src/pages/MobileDeal.vue:111 #: frontend/src/pages/Deal.vue:176 frontend/src/pages/MobileDeal.vue:110
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
@ -2493,9 +2542,9 @@ msgstr ""
msgid "Mobile app installation" msgid "Mobile app installation"
msgstr "" msgstr ""
#: frontend/src/pages/Contact.vue:649 frontend/src/pages/MobileContact.vue:623 #: frontend/src/pages/Contact.vue:648 frontend/src/pages/MobileContact.vue:622
#: frontend/src/pages/MobileOrganization.vue:509 #: frontend/src/pages/MobileOrganization.vue:508
#: frontend/src/pages/Organization.vue:548 #: frontend/src/pages/Organization.vue:547
msgid "Mobile no" msgid "Mobile no"
msgstr "" msgstr ""
@ -2532,16 +2581,30 @@ msgstr ""
#: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json #: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json
#: frontend/src/components/Modals/EmailTemplateModal.vue:24 #: frontend/src/components/Modals/EmailTemplateModal.vue:24
#: frontend/src/components/ViewControls.vue:781 #: frontend/src/components/ViewControls.vue:781
#: frontend/src/pages/MobileOrganization.vue:527 #: frontend/src/pages/MobileOrganization.vue:526
#: frontend/src/pages/Organization.vue:566 #: frontend/src/pages/Organization.vue:565
msgid "Name" msgid "Name"
msgstr "" msgstr ""
#. Label of the naming_series (Select) field in DocType 'CRM Deal' #. Label of the naming_series (Select) field in DocType 'CRM Deal'
#. Label of the naming_series (Select) field in DocType 'CRM Product'
#: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/doctype/crm_deal/crm_deal.json
#: crm/fcrm/doctype/crm_product/crm_product.json
msgid "Naming Series" msgid "Naming Series"
msgstr "" msgstr ""
#. Label of the net_amount (Currency) field in DocType 'CRM Products'
#: crm/fcrm/doctype/crm_products/crm_products.json
msgid "Net Amount"
msgstr ""
#. Label of the net_total (Currency) field in DocType 'CRM Deal'
#. Label of the net_total (Currency) field in DocType 'CRM Lead'
#: crm/fcrm/doctype/crm_deal/crm_deal.json
#: crm/fcrm/doctype/crm_lead/crm_lead.json
msgid "Net Total"
msgstr ""
#: frontend/src/components/Activities/ActivityHeader.vue:82 #: frontend/src/components/Activities/ActivityHeader.vue:82
msgid "New" msgid "New"
msgstr "" msgstr ""
@ -2604,14 +2667,18 @@ msgstr ""
msgid "New WhatsApp Message" msgid "New WhatsApp Message"
msgstr "" msgstr ""
#: frontend/src/pages/Lead.vue:283 frontend/src/pages/MobileLead.vue:150 #: frontend/src/pages/Lead.vue:282 frontend/src/pages/MobileLead.vue:149
msgid "New contact will be created based on the person's details" msgid "New contact will be created based on the person's details"
msgstr "" msgstr ""
#: frontend/src/pages/Lead.vue:258 frontend/src/pages/MobileLead.vue:124 #: frontend/src/pages/Lead.vue:257 frontend/src/pages/MobileLead.vue:123
msgid "New organization will be created based on the data in details section" msgid "New organization will be created based on the data in details section"
msgstr "" msgstr ""
#: frontend/src/components/Modals/CreateDocumentModal.vue:83
msgid "New {0}"
msgstr ""
#: frontend/src/components/Filter.vue:655 #: frontend/src/components/Filter.vue:655
msgid "Next 6 Months" msgid "Next 6 Months"
msgstr "" msgstr ""
@ -2646,7 +2713,7 @@ msgstr ""
msgid "No Answer" msgid "No Answer"
msgstr "" msgstr ""
#: frontend/src/components/Controls/Grid.vue:231 #: frontend/src/components/Controls/Grid.vue:298
msgid "No Data" msgid "No Data"
msgstr "" msgstr ""
@ -2661,7 +2728,7 @@ msgid "No changes made"
msgstr "" msgstr ""
#: frontend/src/components/Modals/SidePanelModal.vue:51 #: frontend/src/components/Modals/SidePanelModal.vue:51
#: frontend/src/pages/Deal.vue:262 frontend/src/pages/MobileDeal.vue:199 #: frontend/src/pages/Deal.vue:261 frontend/src/pages/MobileDeal.vue:198
msgid "No contacts added" msgid "No contacts added"
msgstr "" msgstr ""
@ -2673,7 +2740,7 @@ msgstr ""
msgid "No label" msgid "No label"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:726 #: frontend/src/pages/Deal.vue:725
msgid "No mobile number set" msgid "No mobile number set"
msgstr "" msgstr ""
@ -2686,7 +2753,7 @@ msgstr ""
msgid "No phone number set" msgid "No phone number set"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:721 #: frontend/src/pages/Deal.vue:720
msgid "No primary contact set" msgid "No primary contact set"
msgstr "" msgstr ""
@ -2699,16 +2766,16 @@ msgstr ""
msgid "No website set" msgid "No website set"
msgstr "" msgstr ""
#: frontend/src/components/SidePanelLayout.vue:116 #: frontend/src/components/SidePanelLayout.vue:126
msgid "No {0} Available" msgid "No {0} Available"
msgstr "" msgstr ""
#: frontend/src/pages/CallLogs.vue:56 frontend/src/pages/Contact.vue:165 #: frontend/src/pages/CallLogs.vue:56 frontend/src/pages/Contact.vue:164
#: frontend/src/pages/Contacts.vue:59 frontend/src/pages/Deals.vue:235 #: frontend/src/pages/Contacts.vue:59 frontend/src/pages/Deals.vue:235
#: frontend/src/pages/EmailTemplates.vue:60 frontend/src/pages/Leads.vue:261 #: frontend/src/pages/EmailTemplates.vue:60 frontend/src/pages/Leads.vue:261
#: frontend/src/pages/MobileContact.vue:154 #: frontend/src/pages/MobileContact.vue:153
#: frontend/src/pages/MobileOrganization.vue:143 #: frontend/src/pages/MobileOrganization.vue:142
#: frontend/src/pages/Notes.vue:92 frontend/src/pages/Organization.vue:157 #: frontend/src/pages/Notes.vue:92 frontend/src/pages/Organization.vue:156
#: frontend/src/pages/Organizations.vue:59 frontend/src/pages/Tasks.vue:184 #: frontend/src/pages/Organizations.vue:59 frontend/src/pages/Tasks.vue:184
msgid "No {0} Found" msgid "No {0} Found"
msgstr "" msgstr ""
@ -2777,8 +2844,8 @@ msgstr ""
msgid "Not allowed to set primary contact for Deal" msgid "Not allowed to set primary contact for Deal"
msgstr "" msgstr ""
#: frontend/src/pages/Contact.vue:256 frontend/src/pages/Deal.vue:418 #: frontend/src/pages/Contact.vue:255 frontend/src/pages/Deal.vue:417
#: frontend/src/pages/Lead.vue:426 frontend/src/pages/Organization.vue:245 #: frontend/src/pages/Lead.vue:425 frontend/src/pages/Organization.vue:244
msgid "Not permitted" msgid "Not permitted"
msgstr "" msgstr ""
@ -2788,8 +2855,8 @@ msgstr ""
msgid "Note" msgid "Note"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:576 frontend/src/pages/Lead.vue:562 #: frontend/src/pages/Deal.vue:575 frontend/src/pages/Lead.vue:561
#: frontend/src/pages/MobileDeal.vue:472 frontend/src/pages/MobileLead.vue:375 #: frontend/src/pages/MobileDeal.vue:471 frontend/src/pages/MobileLead.vue:374
msgid "Notes" msgid "Notes"
msgstr "" msgstr ""
@ -2836,10 +2903,10 @@ msgstr ""
msgid "Old Parent" msgid "Old Parent"
msgstr "" msgstr ""
#: frontend/src/pages/Contact.vue:304 frontend/src/pages/Lead.vue:596 #: frontend/src/pages/Contact.vue:303 frontend/src/pages/Lead.vue:595
#: frontend/src/pages/MobileContact.vue:275 #: frontend/src/pages/MobileContact.vue:274
#: frontend/src/pages/MobileOrganization.vue:269 #: frontend/src/pages/MobileOrganization.vue:268
#: frontend/src/pages/Organization.vue:311 #: frontend/src/pages/Organization.vue:310
msgid "Only PNG and JPG images are allowed" msgid "Only PNG and JPG images are allowed"
msgstr "" msgstr ""
@ -2894,13 +2961,13 @@ msgstr ""
#: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/doctype/crm_deal/crm_deal.json
#: crm/fcrm/doctype/crm_lead/crm_lead.json #: crm/fcrm/doctype/crm_lead/crm_lead.json
#: frontend/src/components/Layouts/AppSidebar.vue:510 #: frontend/src/components/Layouts/AppSidebar.vue:510
#: frontend/src/pages/Contact.vue:628 frontend/src/pages/Lead.vue:241 #: frontend/src/pages/Contact.vue:627 frontend/src/pages/Lead.vue:240
#: frontend/src/pages/MobileContact.vue:602 #: frontend/src/pages/MobileContact.vue:601
#: frontend/src/pages/MobileLead.vue:106 #: frontend/src/pages/MobileLead.vue:105
#: frontend/src/pages/MobileOrganization.vue:488 #: frontend/src/pages/MobileOrganization.vue:487
#: frontend/src/pages/MobileOrganization.vue:542 #: frontend/src/pages/MobileOrganization.vue:541
#: frontend/src/pages/Organization.vue:527 #: frontend/src/pages/Organization.vue:526
#: frontend/src/pages/Organization.vue:581 #: frontend/src/pages/Organization.vue:580
msgid "Organization" msgid "Organization"
msgstr "" msgstr ""
@ -2927,15 +2994,15 @@ msgstr ""
msgid "Organization logo" msgid "Organization logo"
msgstr "" msgstr ""
#: frontend/src/pages/MobileOrganization.vue:216 #: frontend/src/pages/MobileOrganization.vue:215
#: frontend/src/pages/Organization.vue:258 #: frontend/src/pages/Organization.vue:257
msgid "Organization updated" msgid "Organization updated"
msgstr "" msgstr ""
#. Label of a shortcut in the Frappe CRM Workspace #. Label of a shortcut in the Frappe CRM Workspace
#: crm/fcrm/workspace/frappe_crm/frappe_crm.json #: crm/fcrm/workspace/frappe_crm/frappe_crm.json
#: frontend/src/pages/MobileOrganization.vue:223 #: frontend/src/pages/MobileOrganization.vue:222
#: frontend/src/pages/Organization.vue:265 #: frontend/src/pages/Organization.vue:264
msgid "Organizations" msgid "Organizations"
msgstr "" msgstr ""
@ -3012,8 +3079,8 @@ msgstr ""
#: crm/fcrm/doctype/crm_deal/crm_deal.json #: crm/fcrm/doctype/crm_deal/crm_deal.json
#: crm/fcrm/doctype/crm_lead/crm_lead.json #: crm/fcrm/doctype/crm_lead/crm_lead.json
#: crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.json #: crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.json
#: frontend/src/pages/MobileOrganization.vue:537 #: frontend/src/pages/MobileOrganization.vue:536
#: frontend/src/pages/Organization.vue:576 #: frontend/src/pages/Organization.vue:575
msgid "Phone" msgid "Phone"
msgstr "" msgstr ""
@ -3055,11 +3122,11 @@ msgstr ""
msgid "Please enter a valid URL" msgid "Please enter a valid URL"
msgstr "" msgstr ""
#: frontend/src/pages/Lead.vue:634 frontend/src/pages/MobileLead.vue:439 #: frontend/src/pages/Lead.vue:633 frontend/src/pages/MobileLead.vue:438
msgid "Please select an existing contact" msgid "Please select an existing contact"
msgstr "" msgstr ""
#: frontend/src/pages/Lead.vue:644 frontend/src/pages/MobileLead.vue:449 #: frontend/src/pages/Lead.vue:643 frontend/src/pages/MobileLead.vue:448
msgid "Please select an existing organization" msgid "Please select an existing organization"
msgstr "" msgstr ""
@ -3074,11 +3141,11 @@ msgstr ""
msgid "Position" msgid "Position"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:206 frontend/src/pages/MobileDeal.vue:143 #: frontend/src/pages/Deal.vue:205 frontend/src/pages/MobileDeal.vue:142
msgid "Primary" msgid "Primary"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:695 frontend/src/pages/MobileDeal.vue:590 #: frontend/src/pages/Deal.vue:694 frontend/src/pages/MobileDeal.vue:589
msgid "Primary contact set" msgid "Primary contact set"
msgstr "" msgstr ""
@ -3104,6 +3171,32 @@ msgstr ""
msgid "Probability" msgid "Probability"
msgstr "" msgstr ""
#. Label of the product_code (Link) field in DocType 'CRM Products'
#: crm/fcrm/doctype/crm_products/crm_products.json
msgid "Product"
msgstr ""
#. Label of the product_code (Data) field in DocType 'CRM Product'
#: crm/fcrm/doctype/crm_product/crm_product.json
msgid "Product Code"
msgstr ""
#. Label of the product_name (Data) field in DocType 'CRM Product'
#. Label of the product_name (Data) field in DocType 'CRM Products'
#: crm/fcrm/doctype/crm_product/crm_product.json
#: crm/fcrm/doctype/crm_products/crm_products.json
msgid "Product Name"
msgstr ""
#. Label of the products_tab (Tab Break) field in DocType 'CRM Deal'
#. Label of the products (Table) field in DocType 'CRM Deal'
#. Label of the products_tab (Tab Break) field in DocType 'CRM Lead'
#. Label of the products (Table) field in DocType 'CRM Lead'
#: crm/fcrm/doctype/crm_deal/crm_deal.json
#: crm/fcrm/doctype/crm_lead/crm_lead.json
msgid "Products"
msgstr ""
#: frontend/src/components/Layouts/AppSidebar.vue:497 #: frontend/src/components/Layouts/AppSidebar.vue:497
#: frontend/src/components/Settings/Settings.vue:85 #: frontend/src/components/Settings/Settings.vue:85
msgid "Profile" msgid "Profile"
@ -3126,6 +3219,11 @@ msgstr ""
msgid "Public view" msgid "Public view"
msgstr "" msgstr ""
#. Label of the qty (Float) field in DocType 'CRM Products'
#: crm/fcrm/doctype/crm_products/crm_products.json
msgid "Quantity"
msgstr ""
#. Option for the 'Status' (Select) field in DocType 'CRM Call Log' #. Option for the 'Status' (Select) field in DocType 'CRM Call Log'
#: crm/fcrm/doctype/crm_call_log/crm_call_log.json #: crm/fcrm/doctype/crm_call_log/crm_call_log.json
msgid "Queued" msgid "Queued"
@ -3149,6 +3247,11 @@ msgstr ""
msgid "Quick entry layout" msgid "Quick entry layout"
msgstr "" msgstr ""
#. Label of the rate (Currency) field in DocType 'CRM Products'
#: crm/fcrm/doctype/crm_products/crm_products.json
msgid "Rate"
msgstr ""
#. Label of the read (Check) field in DocType 'CRM Notification' #. Label of the read (Check) field in DocType 'CRM Notification'
#: crm/fcrm/doctype/crm_notification/crm_notification.json #: crm/fcrm/doctype/crm_notification/crm_notification.json
msgid "Read" msgid "Read"
@ -3213,7 +3316,7 @@ msgstr ""
msgid "Reject" msgid "Reject"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:631 #: frontend/src/pages/Deal.vue:630
msgid "Remove" msgid "Remove"
msgstr "" msgstr ""
@ -3493,7 +3596,7 @@ msgstr ""
msgid "Section" msgid "Section"
msgstr "" msgstr ""
#: frontend/src/components/FieldLayout/Field.vue:263 #: frontend/src/components/FieldLayout/Field.vue:322
msgid "Select {0}" msgid "Select {0}"
msgstr "" msgstr ""
@ -3547,7 +3650,7 @@ msgstr ""
msgid "Set an organization" msgid "Set an organization"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:639 frontend/src/pages/MobileDeal.vue:534 #: frontend/src/pages/Deal.vue:638 frontend/src/pages/MobileDeal.vue:533
msgid "Set as Primary Contact" msgid "Set as Primary Contact"
msgstr "" msgstr ""
@ -3670,6 +3773,11 @@ msgstr ""
msgid "Standard Form Scripts can not be modified, duplicate the Form Script instead." msgid "Standard Form Scripts can not be modified, duplicate the Form Script instead."
msgstr "" msgstr ""
#. Label of the standard_rate (Currency) field in DocType 'CRM Product'
#: crm/fcrm/doctype/crm_product/crm_product.json
msgid "Standard Selling Rate"
msgstr ""
#: frontend/src/components/ViewControls.vue:633 #: frontend/src/components/ViewControls.vue:633
msgid "Standard Views" msgid "Standard Views"
msgstr "" msgstr ""
@ -3708,10 +3816,10 @@ msgstr ""
#: crm/fcrm/doctype/crm_invitation/crm_invitation.json #: crm/fcrm/doctype/crm_invitation/crm_invitation.json
#: crm/fcrm/doctype/crm_lead/crm_lead.json #: crm/fcrm/doctype/crm_lead/crm_lead.json
#: crm/fcrm/doctype/crm_lead_status/crm_lead_status.json #: crm/fcrm/doctype/crm_lead_status/crm_lead_status.json
#: crm/fcrm/doctype/crm_task/crm_task.json frontend/src/pages/Contact.vue:639 #: crm/fcrm/doctype/crm_task/crm_task.json frontend/src/pages/Contact.vue:638
#: frontend/src/pages/MobileContact.vue:613 #: frontend/src/pages/MobileContact.vue:612
#: frontend/src/pages/MobileOrganization.vue:499 #: frontend/src/pages/MobileOrganization.vue:498
#: frontend/src/pages/Organization.vue:538 #: frontend/src/pages/Organization.vue:537
msgid "Status" msgid "Status"
msgstr "" msgstr ""
@ -3772,6 +3880,7 @@ msgstr ""
#: crm/fcrm/doctype/crm_global_settings/crm_global_settings.json #: crm/fcrm/doctype/crm_global_settings/crm_global_settings.json
#: crm/fcrm/doctype/crm_invitation/crm_invitation.json #: crm/fcrm/doctype/crm_invitation/crm_invitation.json
#: crm/fcrm/doctype/crm_notification/crm_notification.json #: crm/fcrm/doctype/crm_notification/crm_notification.json
#: crm/fcrm/doctype/crm_product/crm_product.json
#: crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.json #: crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.json
#: crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.json #: crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.json
#: crm/fcrm/doctype/erpnext_crm_settings/erpnext_crm_settings.json #: crm/fcrm/doctype/erpnext_crm_settings/erpnext_crm_settings.json
@ -3793,8 +3902,8 @@ msgstr ""
msgid "Task" msgid "Task"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:571 frontend/src/pages/Lead.vue:557 #: frontend/src/pages/Deal.vue:570 frontend/src/pages/Lead.vue:556
#: frontend/src/pages/MobileDeal.vue:467 frontend/src/pages/MobileLead.vue:370 #: frontend/src/pages/MobileDeal.vue:466 frontend/src/pages/MobileLead.vue:369
msgid "Tasks" msgid "Tasks"
msgstr "" msgstr ""
@ -3939,11 +4048,25 @@ msgstr ""
msgid "Took a call with John Doe and discussed the new project." msgid "Took a call with John Doe and discussed the new project."
msgstr "" msgstr ""
#. Label of the total (Currency) field in DocType 'CRM Deal'
#. Label of the total (Currency) field in DocType 'CRM Lead'
#: crm/fcrm/doctype/crm_deal/crm_deal.json
#: crm/fcrm/doctype/crm_lead/crm_lead.json
msgid "Total"
msgstr ""
#. Label of the total_holidays (Int) field in DocType 'CRM Holiday List' #. Label of the total_holidays (Int) field in DocType 'CRM Holiday List'
#: crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.json #: crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.json
msgid "Total Holidays" msgid "Total Holidays"
msgstr "" msgstr ""
#. Description of the 'Net Total' (Currency) field in DocType 'CRM Deal'
#. Description of the 'Net Total' (Currency) field in DocType 'CRM Lead'
#: crm/fcrm/doctype/crm_deal/crm_deal.json
#: crm/fcrm/doctype/crm_lead/crm_lead.json
msgid "Total after discount"
msgstr ""
#. Option for the 'Weekly Off' (Select) field in DocType 'CRM Holiday List' #. Option for the 'Weekly Off' (Select) field in DocType 'CRM Holiday List'
#. Option for the 'Workday' (Select) field in DocType 'CRM Service Day' #. Option for the 'Workday' (Select) field in DocType 'CRM Service Day'
#: crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.json #: crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.json
@ -4029,6 +4152,7 @@ msgstr ""
#: frontend/src/components/FieldLayoutEditor.vue:26 #: frontend/src/components/FieldLayoutEditor.vue:26
#: frontend/src/components/Modals/AddressModal.vue:8 #: frontend/src/components/Modals/AddressModal.vue:8
#: frontend/src/components/Modals/CallLogModal.vue:8 #: frontend/src/components/Modals/CallLogModal.vue:8
#: frontend/src/components/Modals/CreateDocumentModal.vue:8
#: frontend/src/components/Section.vue:21 #: frontend/src/components/Section.vue:21
#: frontend/src/components/SidePanelLayoutEditor.vue:19 #: frontend/src/components/SidePanelLayoutEditor.vue:19
msgid "Untitled" msgid "Untitled"
@ -4037,9 +4161,9 @@ msgstr ""
#: frontend/src/components/ColumnSettings.vue:134 #: frontend/src/components/ColumnSettings.vue:134
#: frontend/src/components/Modals/AssignmentModal.vue:17 #: frontend/src/components/Modals/AssignmentModal.vue:17
#: frontend/src/components/Modals/EmailTemplateModal.vue:9 #: frontend/src/components/Modals/EmailTemplateModal.vue:9
#: frontend/src/components/Modals/NoteModal.vue:8 #: frontend/src/components/Modals/NoteModal.vue:6
#: frontend/src/components/Modals/TaskModal.vue:8 #: frontend/src/components/Modals/TaskModal.vue:6
#: frontend/src/components/Settings/GeneralSettings.vue:112 #: frontend/src/components/Settings/GeneralSettings.vue:113
#: frontend/src/components/Settings/ProfileSettings.vue:71 #: frontend/src/components/Settings/ProfileSettings.vue:71
#: frontend/src/components/Settings/SettingsPage.vue:31 #: frontend/src/components/Settings/SettingsPage.vue:31
#: frontend/src/components/Settings/TelephonySettings.vue:70 #: frontend/src/components/Settings/TelephonySettings.vue:70
@ -4134,8 +4258,8 @@ msgstr ""
msgid "Website" msgid "Website"
msgstr "" msgstr ""
#: frontend/src/pages/MobileOrganization.vue:308 #: frontend/src/pages/MobileOrganization.vue:307
#: frontend/src/pages/Organization.vue:354 #: frontend/src/pages/Organization.vue:353
msgid "Website not found" msgid "Website not found"
msgstr "" msgstr ""
@ -4165,8 +4289,8 @@ msgstr ""
#: crm/fcrm/doctype/crm_notification/crm_notification.json #: crm/fcrm/doctype/crm_notification/crm_notification.json
#: frontend/src/components/Layouts/AppSidebar.vue:562 #: frontend/src/components/Layouts/AppSidebar.vue:562
#: frontend/src/components/Settings/Settings.vue:124 #: frontend/src/components/Settings/Settings.vue:124
#: frontend/src/pages/Deal.vue:586 frontend/src/pages/Lead.vue:572 #: frontend/src/pages/Deal.vue:585 frontend/src/pages/Lead.vue:571
#: frontend/src/pages/MobileDeal.vue:482 frontend/src/pages/MobileLead.vue:385 #: frontend/src/pages/MobileDeal.vue:481 frontend/src/pages/MobileLead.vue:384
msgid "WhatsApp" msgid "WhatsApp"
msgstr "" msgstr ""
@ -4421,8 +4545,8 @@ msgstr ""
msgid "{0} assigned a {1} {2} to you" msgid "{0} assigned a {1} {2} to you"
msgstr "" msgstr ""
#: frontend/src/pages/Deal.vue:496 frontend/src/pages/Lead.vue:482 #: frontend/src/pages/Deal.vue:495 frontend/src/pages/Lead.vue:481
#: frontend/src/pages/MobileDeal.vue:385 frontend/src/pages/MobileLead.vue:288 #: frontend/src/pages/MobileDeal.vue:384 frontend/src/pages/MobileLead.vue:287
msgid "{0} is a required field" msgid "{0} is a required field"
msgstr "" msgstr ""
@ -4433,3 +4557,23 @@ msgstr ""
msgid "{0} is an invalid email address" msgid "{0} is an invalid email address"
msgstr "" msgstr ""
#: frontend/src/data/script.js:233
msgid "⚠️ Avoid using \"trigger\" as a field name — it conflicts with the built-in trigger() method."
msgstr ""
#: frontend/src/data/script.js:245
msgid "⚠️ Method \"{0}\" not found in class."
msgstr ""
#: frontend/src/data/script.js:75
msgid "⚠️ No class found for doctype: {0}, it is mandatory to have a class for the parent doctype. it can be empty, but it should be present."
msgstr ""
#: frontend/src/data/script.js:168
msgid "⚠️ No data found for parent field: {0}"
msgstr ""
#: frontend/src/data/script.js:176
msgid "⚠️ No row found for idx: {0} in parent field: {1}"
msgstr ""

View File

@ -1,4 +1,4 @@
<h2>You have been invited to join Frappe CRM</h2> <p>You have been invited to join Frappe CRM</p>
<p> <p>
<a class="btn btn-primary" href="{{ invite_link }}">Accept Invitation</a> <a class="btn btn-primary" href="{{ invite_link }}">Accept Invitation</a>
</p> </p>

@ -1 +1 @@
Subproject commit 3423aa5b5c38d3a1b143ae8ab08cbde7360f9a7c Subproject commit 8b615c0e899d75b99c7d36ec6df97b5d0386b2ca

View File

@ -150,6 +150,7 @@ declare module 'vue' {
ListIcon: typeof import('./src/components/Icons/ListIcon.vue')['default'] ListIcon: typeof import('./src/components/Icons/ListIcon.vue')['default']
ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default'] ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default']
LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default'] LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default']
LucideCalendar: typeof import('~icons/lucide/calendar')['default']
LucidePlus: typeof import('~icons/lucide/plus')['default'] LucidePlus: typeof import('~icons/lucide/plus')['default']
MarkAsDoneIcon: typeof import('./src/components/Icons/MarkAsDoneIcon.vue')['default'] MarkAsDoneIcon: typeof import('./src/components/Icons/MarkAsDoneIcon.vue')['default']
MaximizeIcon: typeof import('./src/components/Icons/MaximizeIcon.vue')['default'] MaximizeIcon: typeof import('./src/components/Icons/MaximizeIcon.vue')['default']
@ -174,6 +175,7 @@ declare module 'vue' {
OrganizationsIcon: typeof import('./src/components/Icons/OrganizationsIcon.vue')['default'] OrganizationsIcon: typeof import('./src/components/Icons/OrganizationsIcon.vue')['default']
OrganizationsListView: typeof import('./src/components/ListViews/OrganizationsListView.vue')['default'] OrganizationsListView: typeof import('./src/components/ListViews/OrganizationsListView.vue')['default']
OutboundCallIcon: typeof import('./src/components/Icons/OutboundCallIcon.vue')['default'] OutboundCallIcon: typeof import('./src/components/Icons/OutboundCallIcon.vue')['default']
Password: typeof import('./src/components/Controls/Password.vue')['default']
PauseIcon: typeof import('./src/components/Icons/PauseIcon.vue')['default'] PauseIcon: typeof import('./src/components/Icons/PauseIcon.vue')['default']
PhoneIcon: typeof import('./src/components/Icons/PhoneIcon.vue')['default'] PhoneIcon: typeof import('./src/components/Icons/PhoneIcon.vue')['default']
PinIcon: typeof import('./src/components/Icons/PinIcon.vue')['default'] PinIcon: typeof import('./src/components/Icons/PinIcon.vue')['default']
@ -208,6 +210,7 @@ declare module 'vue' {
SmileIcon: typeof import('./src/components/Icons/SmileIcon.vue')['default'] SmileIcon: typeof import('./src/components/Icons/SmileIcon.vue')['default']
SortBy: typeof import('./src/components/SortBy.vue')['default'] SortBy: typeof import('./src/components/SortBy.vue')['default']
SortIcon: typeof import('./src/components/Icons/SortIcon.vue')['default'] SortIcon: typeof import('./src/components/Icons/SortIcon.vue')['default']
SquareAsterisk: typeof import('./src/components/Icons/SquareAsterisk.vue')['default']
StepsIcon: typeof import('./src/components/Icons/StepsIcon.vue')['default'] StepsIcon: typeof import('./src/components/Icons/StepsIcon.vue')['default']
SuccessIcon: typeof import('./src/components/Icons/SuccessIcon.vue')['default'] SuccessIcon: typeof import('./src/components/Icons/SuccessIcon.vue')['default']
TableMultiselectInput: typeof import('./src/components/Controls/TableMultiselectInput.vue')['default'] TableMultiselectInput: typeof import('./src/components/Controls/TableMultiselectInput.vue')['default']

View File

@ -2,6 +2,7 @@
"name": "crm-ui", "name": "crm-ui",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.0",
"type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build --base=/assets/crm/frontend/ && yarn copy-html-entry", "build": "vite build --base=/assets/crm/frontend/ && yarn copy-html-entry",
@ -9,9 +10,10 @@
"serve": "vite preview" "serve": "vite preview"
}, },
"dependencies": { "dependencies": {
"@tiptap/extension-paragraph": "^2.12.0",
"@twilio/voice-sdk": "^2.10.2", "@twilio/voice-sdk": "^2.10.2",
"@vueuse/integrations": "^10.3.0", "@vueuse/integrations": "^10.3.0",
"frappe-ui": "^0.1.121", "frappe-ui": "^0.1.145",
"gemoji": "^8.1.0", "gemoji": "^8.1.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mime": "^4.0.1", "mime": "^4.0.1",

View File

@ -1,4 +1,4 @@
module.exports = { export default {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},

View File

@ -1,16 +1,17 @@
<template> <template>
<Layout v-if="session().isLoggedIn"> <FrappeUIProvider>
<router-view /> <Layout v-if="session().isLoggedIn">
</Layout> <router-view />
<Dialogs /> </Layout>
<Toasts /> <Dialogs />
</FrappeUIProvider>
</template> </template>
<script setup> <script setup>
import { Dialogs } from '@/utils/dialogs' import { Dialogs } from '@/utils/dialogs'
import { sessionStore as session } from '@/stores/session' import { sessionStore as session } from '@/stores/session'
import { setTheme } from '@/stores/theme' import { setTheme } from '@/stores/theme'
import { Toasts, setConfig } from 'frappe-ui' import { FrappeUIProvider, setConfig } from 'frappe-ui'
import { computed, defineAsyncComponent, onMounted } from 'vue' import { computed, defineAsyncComponent, onMounted } from 'vue'
const MobileLayout = defineAsyncComponent( const MobileLayout = defineAsyncComponent(

View File

@ -216,6 +216,13 @@
:options="field.options" :options="field.options"
@change="(e) => fieldChange(e.target.value, field, row)" @change="(e) => fieldChange(e.target.value, field, row)"
/> />
<Password
v-else-if="field.fieldtype === 'Password'"
variant="outline"
:value="row[field.fieldname]"
:disabled="Boolean(field.read_only)"
@change="fieldChange($event.target.value, field, row)"
/>
<FormattedInput <FormattedInput
v-else-if="field.fieldtype === 'Int'" v-else-if="field.fieldtype === 'Int'"
class="[&_input]:text-right" class="[&_input]:text-right"
@ -325,6 +332,7 @@
</template> </template>
<script setup> <script setup>
import Password from '@/components/Controls/Password.vue'
import FormattedInput from '@/components/Controls/FormattedInput.vue' import FormattedInput from '@/components/Controls/FormattedInput.vue'
import GridFieldsEditorModal from '@/components/Controls/GridFieldsEditorModal.vue' import GridFieldsEditorModal from '@/components/Controls/GridFieldsEditorModal.vue'
import GridRowFieldsModal from '@/components/Controls/GridRowFieldsModal.vue' import GridRowFieldsModal from '@/components/Controls/GridRowFieldsModal.vue'

View File

@ -58,6 +58,21 @@
class="p-1.5 max-h-[12rem] overflow-y-auto" class="p-1.5 max-h-[12rem] overflow-y-auto"
static static
> >
<div
v-if="!options.length"
class="flex gap-2 rounded px-2 py-1 text-base text-ink-gray-5"
>
<FeatherIcon
v-if="fetchContacts"
name="search"
class="h-4"
/>
{{
fetchContacts
? __('No results found')
: __('Type an email address to add')
}}
</div>
<ComboboxOption <ComboboxOption
v-for="option in options" v-for="option in options"
:key="option.value" :key="option.value"
@ -137,6 +152,10 @@ const props = defineProps({
type: Function, type: Function,
default: (value) => `${value} is an Invalid value`, default: (value) => `${value} is an Invalid value`,
}, },
fetchContacts: {
type: Boolean,
default: true,
},
}) })
const values = defineModel() const values = defineModel()
@ -191,17 +210,19 @@ const filterOptions = createResource({
}) })
const options = computed(() => { const options = computed(() => {
let searchedContacts = filterOptions.data || [] let searchedContacts = props.fetchContacts ? filterOptions.data : []
if (!searchedContacts.length && query.value) { if (!searchedContacts?.length && query.value) {
searchedContacts.push({ searchedContacts.push({
label: query.value, label: query.value,
value: query.value, value: query.value,
}) })
} }
return searchedContacts return searchedContacts || []
}) })
function reload(val) { function reload(val) {
if (!props.fetchContacts) return
filterOptions.update({ filterOptions.update({
params: { txt: val }, params: { txt: val },
}) })

View File

@ -0,0 +1,32 @@
<template>
<FormControl
:type="show ? 'text' : 'password'"
:value="modelValue || value"
v-bind="$attrs"
>
<template #suffix>
<Button v-show="showEye" class="!h-4" @click="show = !show">
<FeatherIcon :name="show ? 'eye-off' : 'eye'" class="h-3" />
</Button>
</template>
</FormControl>
</template>
<script setup>
import { FormControl } from 'frappe-ui'
import { ref, computed } from 'vue'
const props = defineProps({
modelValue: {
type: [String, Number],
default: '',
},
value: {
type: [String, Number],
default: '',
},
})
const show = ref(false)
const showEye = computed(() => {
let v = props.modelValue || props.value
return !v?.includes('*')
})
</script>

View File

@ -136,7 +136,6 @@
<DateTimePicker <DateTimePicker
v-else-if="field.fieldtype === 'Datetime'" v-else-if="field.fieldtype === 'Datetime'"
:value="data[field.fieldname]" :value="data[field.fieldname]"
icon-left=""
:formatter="(date) => getFormat(date, '', true, true)" :formatter="(date) => getFormat(date, '', true, true)"
:placeholder="getPlaceholder(field)" :placeholder="getPlaceholder(field)"
input-class="border-none" input-class="border-none"
@ -144,7 +143,6 @@
/> />
<DatePicker <DatePicker
v-else-if="field.fieldtype === 'Date'" v-else-if="field.fieldtype === 'Date'"
icon-left=""
:value="data[field.fieldname]" :value="data[field.fieldname]"
:formatter="(date) => getFormat(date, '', true)" :formatter="(date) => getFormat(date, '', true)"
:placeholder="getPlaceholder(field)" :placeholder="getPlaceholder(field)"
@ -161,11 +159,18 @@
:description="field.description" :description="field.description"
@change="fieldChange($event.target.value, field)" @change="fieldChange($event.target.value, field)"
/> />
<FormattedInput <Password
v-else-if="['Int'].includes(field.fieldtype)" v-else-if="field.fieldtype === 'Password'"
type="number"
:placeholder="getPlaceholder(field)"
:value="data[field.fieldname]" :value="data[field.fieldname]"
:placeholder="getPlaceholder(field)"
:description="field.description"
@change="fieldChange($event.target.value, field)"
/>
<FormattedInput
v-else-if="field.fieldtype === 'Int'"
type="text"
:placeholder="getPlaceholder(field)"
:value="data[field.fieldname] || '0'"
:disabled="Boolean(field.read_only)" :disabled="Boolean(field.read_only)"
:description="field.description" :description="field.description"
@change="fieldChange($event.target.value, field)" @change="fieldChange($event.target.value, field)"
@ -209,6 +214,7 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import Password from '@/components/Controls/Password.vue'
import FormattedInput from '@/components/Controls/FormattedInput.vue' import FormattedInput from '@/components/Controls/FormattedInput.vue'
import EditIcon from '@/components/Icons/EditIcon.vue' import EditIcon from '@/components/Icons/EditIcon.vue'
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue' import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'

View File

@ -104,7 +104,7 @@
import FilesUploaderArea from '@/components/FilesUploader/FilesUploaderArea.vue' import FilesUploaderArea from '@/components/FilesUploader/FilesUploaderArea.vue'
import FilesUploadHandler from './filesUploaderHandler' import FilesUploadHandler from './filesUploaderHandler'
import { isMobileView } from '@/composables/settings' import { isMobileView } from '@/composables/settings'
import { createToast } from '@/utils' import { toast } from 'frappe-ui'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
const props = defineProps({ const props = defineProps({
@ -165,12 +165,7 @@ function attachFiles() {
function uploadViaWebLink() { function uploadViaWebLink() {
let fileUrl = filesUploaderArea.value.webLink let fileUrl = filesUploaderArea.value.webLink
if (!fileUrl) { if (!fileUrl) {
createToast({ toast.error(__('Please enter a valid URL'))
title: __('Error'),
title: __('Please enter a valid URL'),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
return return
} }
fileUrl = decodeURI(fileUrl) fileUrl = decodeURI(fileUrl)

View File

@ -126,8 +126,13 @@
import FileTextIcon from '@/components/Icons/FileTextIcon.vue' import FileTextIcon from '@/components/Icons/FileTextIcon.vue'
import FileAudioIcon from '@/components/Icons/FileAudioIcon.vue' import FileAudioIcon from '@/components/Icons/FileAudioIcon.vue'
import FileVideoIcon from '@/components/Icons/FileVideoIcon.vue' import FileVideoIcon from '@/components/Icons/FileVideoIcon.vue'
import { createToast, formatDate, convertSize } from '@/utils' import { formatDate, convertSize } from '@/utils'
import { FormControl, CircularProgressBar, createResource } from 'frappe-ui' import {
FormControl,
CircularProgressBar,
createResource,
toast,
} from 'frappe-ui'
import { ref, onMounted, watch, onUnmounted } from 'vue' import { ref, onMounted, watch, onUnmounted } from 'vue'
const props = defineProps({ const props = defineProps({
@ -324,24 +329,18 @@ function checkRestrictions(file) {
if (!isCorrectType) { if (!isCorrectType) {
console.warn('File skipped because of invalid file type', file) console.warn('File skipped because of invalid file type', file)
createToast({ toast.warning(
title: __('File "{0}" was skipped because of invalid file type', [ __('File "{0}" was skipped because of invalid file type', [file.name]),
file.name, )
]),
icon: 'alert-circle',
iconClasses: 'text-orange-600',
})
} }
if (!validFileSize) { if (!validFileSize) {
console.warn('File skipped because of invalid file size', file.size, file) console.warn('File skipped because of invalid file size', file.size, file)
createToast({ toast.warning(
title: __('File "{0}" was skipped because size exceeds {1} MB', [ __('File "{0}" was skipped because size exceeds {1} MB', [
file.name, file.name,
maxFileSize / (1024 * 1024), maxFileSize / (1024 * 1024),
]), ]),
icon: 'alert-circle', )
iconClasses: 'text-orange-600',
})
} }
return isCorrectType && validFileSize return isCorrectType && validFileSize
@ -363,11 +362,7 @@ function showMaxFilesNumberWarning(file, maxNumberOfFiles) {
) )
} }
createToast({ toast.warning(message)
title: message,
icon: 'alert-circle',
iconClasses: 'text-orange-600',
})
} }
function removeFile(name) { function removeFile(name) {

View File

@ -0,0 +1,19 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-square-asterisk-icon lucide-square-asterisk"
>
<rect width="18" height="18" x="3" y="3" rx="2" />
<path d="M12 8v8" />
<path d="m8.5 14 7-4" />
<path d="m8.5 10 7 4" />
</svg>
</template>

View File

@ -150,6 +150,7 @@ import Section from '@/components/Section.vue'
import Email2Icon from '@/components/Icons/Email2Icon.vue' import Email2Icon from '@/components/Icons/Email2Icon.vue'
import PinIcon from '@/components/Icons/PinIcon.vue' import PinIcon from '@/components/Icons/PinIcon.vue'
import UserDropdown from '@/components/UserDropdown.vue' import UserDropdown from '@/components/UserDropdown.vue'
import SquareAsterisk from '@/components/Icons/SquareAsterisk.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'
@ -168,6 +169,8 @@ import {
unreadNotificationsCount, unreadNotificationsCount,
notificationsStore, notificationsStore,
} from '@/stores/notifications' } from '@/stores/notifications'
import { usersStore } from '@/stores/users'
import { sessionStore } from '@/stores/session'
import { showSettings, activeSettingsPage } from '@/composables/settings' import { showSettings, activeSettingsPage } from '@/composables/settings'
import { FeatherIcon, call } from 'frappe-ui' import { FeatherIcon, call } from 'frappe-ui'
import { import {
@ -299,16 +302,18 @@ function getIcon(routeName, icon) {
} }
// onboarding // onboarding
const { user } = sessionStore()
const { users, isManager } = usersStore()
const { isOnboardingStepsCompleted, setUp } = useOnboarding('frappecrm') const { isOnboardingStepsCompleted, setUp } = useOnboarding('frappecrm')
async function getFirstLead() { async function getFirstLead() {
let firstLead = localStorage.getItem('firstLead') let firstLead = localStorage.getItem('firstLead' + user)
if (firstLead) return firstLead if (firstLead) return firstLead
return await call('crm.api.onboarding.get_first_lead') return await call('crm.api.onboarding.get_first_lead')
} }
async function getFirstDeal() { async function getFirstDeal() {
let firstDeal = localStorage.getItem('firstDeal') let firstDeal = localStorage.getItem('firstDeal' + user)
if (firstDeal) return firstDeal if (firstDeal) return firstDeal
return await call('crm.api.onboarding.get_first_deal') return await call('crm.api.onboarding.get_first_deal')
} }
@ -317,6 +322,17 @@ const showIntermediateModal = ref(false)
const currentStep = ref({}) const currentStep = ref({})
const steps = reactive([ const steps = reactive([
{
name: 'setup_your_password',
title: __('Setup your password'),
icon: markRaw(SquareAsterisk),
completed: false,
onClick: () => {
minimize.value = true
showSettings.value = true
activeSettingsPage.value = 'Profile'
},
},
{ {
name: 'create_first_lead', name: 'create_first_lead',
title: __('Create your first lead'), title: __('Create your first lead'),
@ -337,12 +353,14 @@ const steps = reactive([
showSettings.value = true showSettings.value = true
activeSettingsPage.value = 'Invite Members' activeSettingsPage.value = 'Invite Members'
}, },
condition: () => isManager(),
}, },
{ {
name: 'convert_lead_to_deal', name: 'convert_lead_to_deal',
title: __('Convert lead to deal'), title: __('Convert lead to deal'),
icon: markRaw(ConvertIcon), icon: markRaw(ConvertIcon),
completed: false, completed: false,
dependsOn: 'create_first_lead',
onClick: async () => { onClick: async () => {
minimize.value = true minimize.value = true
@ -410,6 +428,7 @@ const steps = reactive([
title: __('Add your first comment'), title: __('Add your first comment'),
icon: markRaw(CommentIcon), icon: markRaw(CommentIcon),
completed: false, completed: false,
dependsOn: 'create_first_lead',
onClick: async () => { onClick: async () => {
minimize.value = true minimize.value = true
let deal = await getFirstDeal() let deal = await getFirstDeal()
@ -430,6 +449,7 @@ const steps = reactive([
title: __('Send email'), title: __('Send email'),
icon: markRaw(EmailIcon), icon: markRaw(EmailIcon),
completed: false, completed: false,
dependsOn: 'create_first_lead',
onClick: async () => { onClick: async () => {
minimize.value = true minimize.value = true
let deal = await getFirstDeal() let deal = await getFirstDeal()
@ -450,6 +470,7 @@ const steps = reactive([
title: __('Change deal status'), title: __('Change deal status'),
icon: markRaw(StepsIcon), icon: markRaw(StepsIcon),
completed: false, completed: false,
dependsOn: 'convert_lead_to_deal',
onClick: async () => { onClick: async () => {
minimize.value = true minimize.value = true
@ -478,7 +499,18 @@ const steps = reactive([
}, },
]) ])
onMounted(() => setUp(steps)) onMounted(async () => {
await users.promise
const filteredSteps = steps.filter((step) => {
if (step.condition) {
return step.condition()
}
return true
})
setUp(filteredSteps)
})
// help center // help center
const articles = ref([ const articles = ref([
@ -517,9 +549,7 @@ const articles = ref([
{ {
title: __('Capturing leads'), title: __('Capturing leads'),
opened: false, opened: false,
subArticles: [ subArticles: [{ name: 'web-form', title: __('Web form') }],
{ name: 'web-form', title: __('Web form') },
],
}, },
{ {
title: __('Views'), title: __('Views'),

View File

@ -19,10 +19,10 @@
<script setup> <script setup>
import EditValueModal from '@/components/Modals/EditValueModal.vue' import EditValueModal from '@/components/Modals/EditValueModal.vue'
import AssignmentModal from '@/components/Modals/AssignmentModal.vue' import AssignmentModal from '@/components/Modals/AssignmentModal.vue'
import { setupListCustomizations, createToast } from '@/utils' import { setupListCustomizations } from '@/utils'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { capture } from '@/telemetry' import { capture } from '@/telemetry'
import { call } from 'frappe-ui' import { call, toast } from 'frappe-ui'
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
@ -75,11 +75,7 @@ function convertToDeal(selections, unselectAll) {
call('crm.fcrm.doctype.crm_lead.crm_lead.convert_to_deal', { call('crm.fcrm.doctype.crm_lead.crm_lead.convert_to_deal', {
lead: name, lead: name,
}).then(() => { }).then(() => {
createToast({ toast.success(__('Converted successfully'))
title: __('Converted successfully'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
list.value.reload() list.value.reload()
unselectAll() unselectAll()
close() close()
@ -110,11 +106,7 @@ function deleteValues(selections, unselectAll) {
items: JSON.stringify(Array.from(selections)), items: JSON.stringify(Array.from(selections)),
doctype: props.doctype, doctype: props.doctype,
}).then(() => { }).then(() => {
createToast({ toast.success(__('Deleted successfully'))
title: __('Deleted successfully'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
unselectAll() unselectAll()
list.value.reload() list.value.reload()
close() close()
@ -154,11 +146,7 @@ function clearAssignemnts(selections, unselectAll) {
names: JSON.stringify(Array.from(selections)), names: JSON.stringify(Array.from(selections)),
ignore_permissions: true, ignore_permissions: true,
}).then(() => { }).then(() => {
createToast({ toast.success(__('Assignment cleared successfully'))
title: __('Assignment cleared successfully'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
reload(unselectAll) reload(unselectAll)
close() close()
}) })
@ -215,7 +203,8 @@ function bulkActions(selections, unselectAll) {
selections, selections,
unselectAll, unselectAll,
call, call,
createToast, createToast: toast.create,
toast,
$dialog, $dialog,
router, router,
}), }),
@ -235,7 +224,8 @@ onMounted(async () => {
let customization = await setupListCustomizations(list.value.data, { let customization = await setupListCustomizations(list.value.data, {
list: list.value, list: list.value,
call, call,
createToast, createToast: toast.create,
toast,
$dialog, $dialog,
$socket, $socket,
router, router,

View File

@ -46,6 +46,7 @@ import EditIcon from '@/components/Icons/EditIcon.vue'
import FieldLayout from '@/components/FieldLayout/FieldLayout.vue' import FieldLayout from '@/components/FieldLayout/FieldLayout.vue'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { statusesStore } from '@/stores/statuses' import { statusesStore } from '@/stores/statuses'
import { sessionStore } from '@/stores/session'
import { isMobileView } from '@/composables/settings' import { isMobileView } from '@/composables/settings'
import { capture } from '@/telemetry' import { capture } from '@/telemetry'
import { createResource } from 'frappe-ui' import { createResource } from 'frappe-ui'
@ -57,6 +58,7 @@ const props = defineProps({
defaults: Object, defaults: Object,
}) })
const { user } = sessionStore()
const { getUser, isManager } = usersStore() const { getUser, isManager } = usersStore()
const { getLeadStatus, statusOptions } = statusesStore() const { getLeadStatus, statusOptions } = statusesStore()
const { updateOnboardingStep } = useOnboarding('frappecrm') const { updateOnboardingStep } = useOnboarding('frappecrm')
@ -169,7 +171,7 @@ function createNewLead() {
show.value = false show.value = false
router.push({ name: 'Lead', params: { leadId: data.name } }) router.push({ name: 'Lead', params: { leadId: data.name } })
updateOnboardingStep('create_first_lead', true, false, () => { updateOnboardingStep('create_first_lead', true, false, () => {
localStorage.setItem('firstLead', data.name) localStorage.setItem('firstLead' + user, data.name)
}) })
}, },
onError(err) { onError(err) {

View File

@ -93,9 +93,8 @@
<script setup> <script setup>
import { computed, reactive, ref } from 'vue' import { computed, reactive, ref } from 'vue'
import { createResource } from 'frappe-ui' import { createResource, toast } from 'frappe-ui'
import CircleAlert from '~icons/lucide/circle-alert' import CircleAlert from '~icons/lucide/circle-alert'
import { createToast } from '@/utils'
import { import {
customProviderFields, customProviderFields,
popularProviderFields, popularProviderFields,
@ -139,11 +138,7 @@ const addEmailRes = createResource({
} }
}, },
onSuccess: () => { onSuccess: () => {
createToast({ toast.success(__('Email account created successfully'))
title: __('Email account created successfully'),
icon: 'check',
iconClasses: 'text-green-600',
})
emit('update:step', 'email-list') emit('update:step', 'email-list')
}, },
onError: () => { onError: () => {

View File

@ -82,7 +82,7 @@
<script setup> <script setup>
import { computed, reactive, ref } from 'vue' import { computed, reactive, ref } from 'vue'
import { call } from 'frappe-ui' import { call, toast } from 'frappe-ui'
import EmailProviderIcon from './EmailProviderIcon.vue' import EmailProviderIcon from './EmailProviderIcon.vue'
import { import {
emailIcon, emailIcon,
@ -92,7 +92,6 @@ import {
validateInputs, validateInputs,
incomingOutgoingFields, incomingOutgoingFields,
} from './emailConfig' } from './emailConfig'
import { createToast } from '@/utils'
import CircleAlert from '~icons/lucide/circle-alert' import CircleAlert from '~icons/lucide/circle-alert'
const props = defineProps({ const props = defineProps({
@ -148,11 +147,7 @@ async function updateAccount() {
const values = updatedEmailAccount const values = updatedEmailAccount
if (!nameChanged && !otherFieldsChanged) { if (!nameChanged && !otherFieldsChanged) {
createToast({ toast.info(__('No changes made'))
title: __('No changes made'),
icon: 'info',
iconClasses: 'text-blue-600',
})
return return
} }
@ -210,11 +205,7 @@ async function callSetValue(values) {
function succesHandler() { function succesHandler() {
emit('update:step', 'email-list') emit('update:step', 'email-list')
createToast({ toast.success(__('Email account updated successfully'))
title: __('Email account updated successfully'),
icon: 'check',
iconClasses: 'text-green-600',
})
} }
function errorHandler() { function errorHandler() {

View File

@ -20,6 +20,7 @@
:error-message=" :error-message="
(value) => __('{0} is an invalid email address', [value]) (value) => __('{0} is an invalid email address', [value])
" "
:fetchContacts="false"
/> />
</div> </div>
<FormControl <FormControl
@ -127,10 +128,17 @@ const inviteByEmail = createResource({
role: role.value, role: role.value,
} }
}, },
onSuccess() { onSuccess(data) {
if (data?.existing_invites?.length) {
error.value = __('Agent with email {0} already exists', [
data.existing_invites.join(', '),
])
} else {
role.value = 'Sales User'
error.value = null
}
invitees.value = [] invitees.value = []
role.value = 'Sales User'
error.value = null
pendingInvitations.reload() pendingInvitations.reload()
updateOnboardingStep('invite_your_team') updateOnboardingStep('invite_your_team')
}, },

View File

@ -57,7 +57,7 @@
v-model="profile.email" v-model="profile.email"
:disabled="true" :disabled="true"
/> />
<FormControl <Password
class="w-full" class="w-full"
label="Set new password" label="Set new password"
v-model="profile.new_password" v-model="profile.new_password"
@ -77,13 +77,15 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import Password from '@/components/Controls/Password.vue'
import ProfileImageEditor from '@/components/Settings/ProfileImageEditor.vue' import ProfileImageEditor from '@/components/Settings/ProfileImageEditor.vue'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { createToast } from '@/utils' import { Dialog, Avatar, createResource, ErrorMessage, toast } from 'frappe-ui'
import { Dialog, Avatar, createResource, ErrorMessage } from 'frappe-ui' import { useOnboarding } from 'frappe-ui/frappe'
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
const { getUser, users } = usersStore() const { getUser, users } = usersStore()
const { updateOnboardingStep } = useOnboarding('frappecrm')
const user = computed(() => getUser() || {}) const user = computed(() => getUser() || {})
@ -95,6 +97,13 @@ const error = ref('')
function updateUser() { function updateUser() {
loading.value = true loading.value = true
let passwordUpdated = false
if (profile.value.new_password) {
passwordUpdated = true
}
const fieldname = { const fieldname = {
first_name: profile.value.first_name, first_name: profile.value.first_name,
last_name: profile.value.last_name, last_name: profile.value.last_name,
@ -111,15 +120,14 @@ function updateUser() {
}, },
auto: true, auto: true,
onSuccess: () => { onSuccess: () => {
if (passwordUpdated) {
updateOnboardingStep('setup_your_password')
}
loading.value = false loading.value = false
error.value = '' error.value = ''
profile.value.new_password = '' profile.value.new_password = ''
showEditProfilePhotoModal.value = false showEditProfilePhotoModal.value = false
createToast({ toast.success(__('Profile updated successfully'))
title: __('Profile updated successfully'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
users.reload() users.reload()
}, },
onError: (err) => { onError: (err) => {

View File

@ -42,9 +42,10 @@ import {
createResource, createResource,
Spinner, Spinner,
Badge, Badge,
toast,
ErrorMessage, ErrorMessage,
} from 'frappe-ui' } from 'frappe-ui'
import { createToast, getRandom } from '@/utils' import { getRandom } from '@/utils'
import { computed } from 'vue' import { computed } from 'vue'
const props = defineProps({ const props = defineProps({
@ -79,20 +80,10 @@ const data = createDocumentResource({
auto: true, auto: true,
setValue: { setValue: {
onSuccess: () => { onSuccess: () => {
createToast({ toast.success(__(props.successMessage))
title: __('Success'),
text: __(props.successMessage),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
}, },
onError: (err) => { onError: (err) => {
createToast({ toast.error(err.message + ': ' + err.messages[0])
title: __('Error'),
text: err.message + ': ' + err.messages[0],
icon: 'x',
iconClasses: 'text-ink-red-4',
})
}, },
}, },
}) })

View File

@ -87,7 +87,8 @@ import {
} from 'frappe-ui' } from 'frappe-ui'
import { defaultCallingMedium } from '@/composables/settings' import { defaultCallingMedium } from '@/composables/settings'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { createToast, getRandom } from '@/utils' import { toast } from 'frappe-ui'
import { getRandom } from '@/utils'
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
const { isManager, isAgent } = usersStore() const { isManager, isAgent } = usersStore()
@ -119,20 +120,10 @@ const twilio = createDocumentResource({
auto: true, auto: true,
setValue: { setValue: {
onSuccess: () => { onSuccess: () => {
createToast({ toast.success(__('Twilio settings updated successfully'))
title: __('Success'),
text: __('Twilio settings updated successfully'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
}, },
onError: (err) => { onError: (err) => {
createToast({ toast.error(err.message + ': ' + err.messages[0])
title: __('Error'),
text: err.message + ': ' + err.messages[0],
icon: 'x',
iconClasses: 'text-ink-red-4',
})
}, },
}, },
}) })
@ -144,20 +135,10 @@ const exotel = createDocumentResource({
auto: true, auto: true,
setValue: { setValue: {
onSuccess: () => { onSuccess: () => {
createToast({ toast.success(__('Exotel settings updated successfully'))
title: __('Success'),
text: __('Exotel settings updated successfully'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
}, },
onError: (err) => { onError: (err) => {
createToast({ toast.error(err.message + ': ' + err.messages[0])
title: __('Error'),
text: err.message + ': ' + err.messages[0],
icon: 'x',
iconClasses: 'text-ink-red-4',
})
}, },
}, },
}) })
@ -294,12 +275,7 @@ async function updateMedium() {
}) })
mediumChanged.value = false mediumChanged.value = false
error.value = '' error.value = ''
createToast({ toast.success(__('Default calling medium updated successfully'))
title: __('Success'),
text: __('Default calling medium updated successfully'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
const error = ref('') const error = ref('')

View File

@ -275,11 +275,20 @@
" "
:disabled="Boolean(field.read_only)" :disabled="Boolean(field.read_only)"
/> />
<Password
v-else-if="field.fieldtype === 'Password'"
class="form-control"
:value="document.doc[field.fieldname]"
:placeholder="field.placeholder"
:debounce="500"
@change.stop="fieldChange($event.target.value, field)"
:disabled="Boolean(field.read_only)"
/>
<FormattedInput <FormattedInput
v-else-if="field.fieldtype === 'Int'" v-else-if="field.fieldtype === 'Int'"
class="form-control" class="form-control"
type="text" type="text"
v-model="document.doc[field.fieldname]" :value="document.doc[field.fieldname] || '0'"
:placeholder="field.placeholder" :placeholder="field.placeholder"
:debounce="500" :debounce="500"
@change.stop="fieldChange($event.target.value, field)" @change.stop="fieldChange($event.target.value, field)"
@ -366,6 +375,7 @@
</template> </template>
<script setup> <script setup>
import Password from '@/components/Controls/Password.vue'
import FormattedInput from '@/components/Controls/FormattedInput.vue' import FormattedInput from '@/components/Controls/FormattedInput.vue'
import Section from '@/components/Section.vue' import Section from '@/components/Section.vue'
import NestedPopover from '@/components/NestedPopover.vue' import NestedPopover from '@/components/NestedPopover.vue'

View File

@ -35,7 +35,9 @@
/> />
<div v-if="isDefaultMedium" class="text-sm text-ink-gray-4"> <div v-if="isDefaultMedium" class="text-sm text-ink-gray-4">
{{ __('You can change the default calling medium from the settings') }} {{
__('You can change the default calling medium from the settings')
}}
</div> </div>
</div> </div>
</div> </div>
@ -51,8 +53,7 @@ import {
defaultCallingMedium, defaultCallingMedium,
} from '@/composables/settings' } from '@/composables/settings'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { createToast } from '@/utils' import { FormControl, call, toast } from 'frappe-ui'
import { FormControl, call } from 'frappe-ui'
import { nextTick, ref, watch } from 'vue' import { nextTick, ref, watch } from 'vue'
const { setMakeCall } = globalStore() const { setMakeCall } = globalStore()
@ -107,13 +108,9 @@ async function setDefaultCallingMedium() {
}) })
defaultCallingMedium.value = callMedium.value defaultCallingMedium.value = callMedium.value
createToast({ toast.success(
title: __('Default calling medium set successfully to {0}', [ __('Default calling medium set successfully to {0}', [callMedium.value]),
callMedium.value, )
]),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
watch( watch(

View File

@ -244,11 +244,10 @@ import NoteIcon from '@/components/Icons/NoteIcon.vue'
import TaskIcon from '@/components/Icons/TaskIcon.vue' import TaskIcon from '@/components/Icons/TaskIcon.vue'
import TaskPanel from '@/components/Telephony/TaskPanel.vue' import TaskPanel from '@/components/Telephony/TaskPanel.vue'
import CountUpTimer from '@/components/CountUpTimer.vue' import CountUpTimer from '@/components/CountUpTimer.vue'
import { createToast } from '@/utils'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { sessionStore } from '@/stores/session' import { sessionStore } from '@/stores/session'
import { useDraggable, useWindowSize } from '@vueuse/core' import { useDraggable, useWindowSize } from '@vueuse/core'
import { TextEditor, Avatar, Button, createResource } from 'frappe-ui' import { TextEditor, Avatar, Button, createResource, toast } from 'frappe-ui'
import { ref, onBeforeUnmount, watch, nextTick } from 'vue' import { ref, onBeforeUnmount, watch, nextTick } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
@ -413,12 +412,7 @@ function makeOutgoingCall(number) {
showSmallCallPopup.value = false showSmallCallPopup.value = false
}, },
onError(err) { onError(err) {
createToast({ toast.error(err.messages[0])
title: 'Error',
text: err.messages[0],
icon: 'x',
iconClasses: 'text-red-600',
})
}, },
}) })
} }

View File

@ -312,11 +312,12 @@ import { globalStore } from '@/stores/global'
import { viewsStore } from '@/stores/views' import { viewsStore } from '@/stores/views'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { getMeta } from '@/stores/meta' import { getMeta } from '@/stores/meta'
import { isEmoji, createToast } from '@/utils' import { isEmoji } from '@/utils'
import { import {
Tooltip, Tooltip,
createResource, createResource,
Dropdown, Dropdown,
toast,
call, call,
FeatherIcon, FeatherIcon,
usePageMeta, usePageMeta,
@ -727,12 +728,7 @@ const updateQuickFilters = createResource({
quickFilters.update({ params: { doctype: props.doctype, cached: false } }) quickFilters.update({ params: { doctype: props.doctype, cached: false } })
quickFilters.reload() quickFilters.reload()
toast.success(__('Quick Filters updated successfully'))
createToast({
title: __('Quick Filters updated successfully'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
}, },
}) })

View File

@ -1,6 +1,6 @@
import { getScript } from '@/data/script' import { getScript } from '@/data/script'
import { createToast, runSequentially } from '@/utils' import { runSequentially } from '@/utils'
import { createDocumentResource } from 'frappe-ui' import { createDocumentResource, toast } from 'frappe-ui'
const documentsCache = {} const documentsCache = {}
const controllersCache = {} const controllersCache = {}
@ -17,19 +17,11 @@ export function useDocument(doctype, docname) {
onSuccess: () => setupFormScript(), onSuccess: () => setupFormScript(),
setValue: { setValue: {
onSuccess: () => { onSuccess: () => {
createToast({ toast.success(__('Document updated successfully'))
title: __('Document updated successfully'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
}, },
onError: (err) => { onError: (err) => {
createToast({ toast.error(__('Error updating document'))
title: __('Error updating document'), console.error(err)
text: err.messages[0],
icon: 'x',
iconClasses: 'text-red-600',
})
}, },
}, },
}) })

View File

@ -1,7 +1,6 @@
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { getMeta } from '@/stores/meta' import { getMeta } from '@/stores/meta'
import { createToast } from '@/utils' import { call, createListResource, toast } from 'frappe-ui'
import { call, createListResource } from 'frappe-ui'
import { reactive } from 'vue' import { reactive } from 'vue'
import router from '@/router' import router from '@/router'
@ -34,7 +33,7 @@ export function getScript(doctype, view = 'Form') {
const { $dialog, $socket, makeCall } = globalStore() const { $dialog, $socket, makeCall } = globalStore()
helpers.createDialog = $dialog helpers.createDialog = $dialog
helpers.createToast = createToast helpers.toast = toast
helpers.socket = $socket helpers.socket = $socket
helpers.router = router helpers.router = router
helpers.call = call helpers.call = call

View File

@ -186,7 +186,7 @@ import CameraIcon from '@/components/Icons/CameraIcon.vue'
import DealsIcon from '@/components/Icons/DealsIcon.vue' import DealsIcon from '@/components/Icons/DealsIcon.vue'
import DealsListView from '@/components/ListViews/DealsListView.vue' import DealsListView from '@/components/ListViews/DealsListView.vue'
import AddressModal from '@/components/Modals/AddressModal.vue' import AddressModal from '@/components/Modals/AddressModal.vue'
import { formatDate, timeAgo, createToast } from '@/utils' import { formatDate, timeAgo } from '@/utils'
import { getView } from '@/utils/view' import { getView } from '@/utils/view'
import { getSettings } from '@/stores/settings' import { getSettings } from '@/stores/settings'
import { getMeta } from '@/stores/meta' import { getMeta } from '@/stores/meta'
@ -204,6 +204,7 @@ import {
createResource, createResource,
usePageMeta, usePageMeta,
Dropdown, Dropdown,
toast,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, computed, h } from 'vue' import { ref, computed, h } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
@ -522,11 +523,7 @@ async function setAsPrimary(field, value) {
}) })
if (d) { if (d) {
contact.reload() contact.reload()
createToast({ toast.success(__('Contact updated'))
title: 'Contact updated',
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -539,11 +536,7 @@ async function createNew(field, value) {
}) })
if (d) { if (d) {
contact.reload() contact.reload()
createToast({ toast.success(__('Contact updated'))
title: 'Contact updated',
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -556,11 +549,7 @@ async function editOption(doctype, name, fieldname, value) {
}) })
if (d) { if (d) {
contact.reload() contact.reload()
createToast({ toast.success(__('Contact updated'))
title: 'Contact updated',
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -570,11 +559,7 @@ async function deleteOption(doctype, name) {
name, name,
}) })
await contact.reload() await contact.reload()
createToast({ toast.success(__('Contact updated'))
title: 'Contact updated',
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
async function updateField(fieldname, value) { async function updateField(fieldname, value) {
@ -584,11 +569,7 @@ async function updateField(fieldname, value) {
fieldname, fieldname,
value, value,
}) })
createToast({ toast.success(__('Contact updated'))
title: 'Contact updated',
icon: 'check',
iconClasses: 'text-ink-green-3',
})
contact.reload() contact.reload()
} }

View File

@ -89,7 +89,7 @@
@click=" @click="
deal.data.email deal.data.email
? openEmailBox() ? openEmailBox()
: _errorMessage(__('No email set')) : toast.error(__('No email set'))
" "
/> />
</Button> </Button>
@ -103,7 +103,7 @@
@click=" @click="
deal.data.website deal.data.website
? openWebsite(deal.data.website) ? openWebsite(deal.data.website)
: _errorMessage(__('No website set')) : toast.error(__('No website set'))
" "
/> />
</Button> </Button>
@ -332,10 +332,8 @@ import SLASection from '@/components/SLASection.vue'
import CustomActions from '@/components/CustomActions.vue' import CustomActions from '@/components/CustomActions.vue'
import { import {
openWebsite, openWebsite,
createToast,
setupAssignees, setupAssignees,
setupCustomizations, setupCustomizations,
errorMessage as _errorMessage,
copyToClipboard, copyToClipboard,
} from '@/utils' } from '@/utils'
import { getView } from '@/utils/view' import { getView } from '@/utils/view'
@ -353,6 +351,7 @@ import {
Breadcrumbs, Breadcrumbs,
call, call,
usePageMeta, usePageMeta,
toast,
} from 'frappe-ui' } from 'frappe-ui'
import { useOnboarding } from 'frappe-ui/frappe' import { useOnboarding } from 'frappe-ui/frappe'
import { ref, computed, h, onMounted, onBeforeUnmount } from 'vue' import { ref, computed, h, onMounted, onBeforeUnmount } from 'vue'
@ -401,8 +400,9 @@ const deal = createResource({
$dialog, $dialog,
$socket, $socket,
router, router,
toast,
updateField, updateField,
createToast, createToast: toast.create,
deleteDoc: deleteDeal, deleteDoc: deleteDeal,
resource: { resource: {
deal, deal,
@ -429,11 +429,7 @@ const organization = createResource({
onMounted(() => { onMounted(() => {
$socket.on('crm_customer_created', () => { $socket.on('crm_customer_created', () => {
createToast({ toast.success(__('Customer created successfully'))
title: __('Customer created successfully'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
}) })
if (deal.data) { if (deal.data) {
@ -469,20 +465,11 @@ function updateDeal(fieldname, value, callback) {
onSuccess: () => { onSuccess: () => {
deal.reload() deal.reload()
reload.value = true reload.value = true
createToast({ toast.success(__('Deal updated'))
title: __('Deal updated'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
callback?.() callback?.()
}, },
onError: (err) => { onError: (err) => {
createToast({ toast.error(__('Error updating deal: {0}', [err.messages?.[0]]))
title: __('Error updating deal'),
text: __(err.messages?.[0]),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
}, },
}) })
} }
@ -490,12 +477,7 @@ function updateDeal(fieldname, value, callback) {
function validateRequired(fieldname, value) { function validateRequired(fieldname, value) {
let meta = deal.data.fields_meta || {} let meta = deal.data.fields_meta || {}
if (meta[fieldname]?.reqd && !value) { if (meta[fieldname]?.reqd && !value) {
createToast({ toast.error(__('{0} is a required field', [meta[fieldname].label]))
title: __('Error Updating Deal'),
text: __('{0} is a required field', [meta[fieldname].label]),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
return true return true
} }
return false return false
@ -646,11 +628,7 @@ function contactOptions(contact) {
async function addContact(contact) { async function addContact(contact) {
if (dealContacts.data?.find((c) => c.name === contact)) { if (dealContacts.data?.find((c) => c.name === contact)) {
createToast({ toast.error(__('Contact already added'))
title: __('Contact already added'),
icon: 'x',
iconClasses: 'text-ink-red-3',
})
return return
} }
@ -660,11 +638,7 @@ async function addContact(contact) {
}) })
if (d) { if (d) {
dealContacts.reload() dealContacts.reload()
createToast({ toast.success(__('Contact added'))
title: __('Contact added'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -675,11 +649,7 @@ async function removeContact(contact) {
}) })
if (d) { if (d) {
dealContacts.reload() dealContacts.reload()
createToast({ toast.success(__('Contact removed'))
title: __('Contact removed'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -690,11 +660,7 @@ async function setPrimaryContact(contact) {
}) })
if (d) { if (d) {
dealContacts.reload() dealContacts.reload()
createToast({ toast.success(__('Primary contact set'))
title: __('Primary contact set'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -717,12 +683,12 @@ function triggerCall() {
let mobile_no = primaryContact.mobile_no || null let mobile_no = primaryContact.mobile_no || null
if (!primaryContact) { if (!primaryContact) {
_errorMessage(__('No primary contact set')) toast.error(__('No primary contact set'))
return return
} }
if (!mobile_no) { if (!mobile_no) {
_errorMessage(__('No mobile number set')) toast.error(__('No mobile number set'))
return return
} }

View File

@ -124,7 +124,7 @@
() => () =>
lead.data.mobile_no lead.data.mobile_no
? makeCall(lead.data.mobile_no) ? makeCall(lead.data.mobile_no)
: _errorMessage(__('No phone number set')) : toast.error(__('No phone number set'))
" "
> >
<PhoneIcon class="h-4 w-4" /> <PhoneIcon class="h-4 w-4" />
@ -139,7 +139,7 @@
@click=" @click="
lead.data.email lead.data.email
? openEmailBox() ? openEmailBox()
: _errorMessage(__('No email set')) : toast.error(__('No email set'))
" "
/> />
</Button> </Button>
@ -153,7 +153,7 @@
@click=" @click="
lead.data.website lead.data.website
? openWebsite(lead.data.website) ? openWebsite(lead.data.website)
: _errorMessage(__('No website set')) : toast.error(__('No website set'))
" "
/> />
</Button> </Button>
@ -344,14 +344,13 @@ import SLASection from '@/components/SLASection.vue'
import CustomActions from '@/components/CustomActions.vue' import CustomActions from '@/components/CustomActions.vue'
import { import {
openWebsite, openWebsite,
createToast,
setupAssignees, setupAssignees,
setupCustomizations, setupCustomizations,
errorMessage as _errorMessage,
copyToClipboard, copyToClipboard,
} from '@/utils' } from '@/utils'
import { getView } from '@/utils/view' import { getView } from '@/utils/view'
import { getSettings } from '@/stores/settings' import { getSettings } from '@/stores/settings'
import { sessionStore } from '@/stores/session'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
import { statusesStore } from '@/stores/statuses' import { statusesStore } from '@/stores/statuses'
@ -373,6 +372,7 @@ import {
Breadcrumbs, Breadcrumbs,
call, call,
usePageMeta, usePageMeta,
toast,
} from 'frappe-ui' } from 'frappe-ui'
import { useOnboarding } from 'frappe-ui/frappe' import { useOnboarding } from 'frappe-ui/frappe'
import { ref, reactive, computed, onMounted, watch } from 'vue' import { ref, reactive, computed, onMounted, watch } from 'vue'
@ -380,6 +380,7 @@ import { useRouter, useRoute } from 'vue-router'
import { useActiveTabManager } from '@/composables/useActiveTabManager' import { useActiveTabManager } from '@/composables/useActiveTabManager'
const { brand } = getSettings() const { brand } = getSettings()
const { user } = sessionStore()
const { isManager } = usersStore() const { isManager } = usersStore()
const { $dialog, $socket, makeCall } = globalStore() const { $dialog, $socket, makeCall } = globalStore()
const { statusOptions, getLeadStatus, getDealStatus } = statusesStore() const { statusOptions, getLeadStatus, getDealStatus } = statusesStore()
@ -413,8 +414,9 @@ const lead = createResource({
$dialog, $dialog,
$socket, $socket,
router, router,
toast,
updateField, updateField,
createToast, createToast: toast.create,
deleteDoc: deleteLead, deleteDoc: deleteLead,
resource: { lead, sections }, resource: { lead, sections },
call, call,
@ -455,20 +457,11 @@ function updateLead(fieldname, value, callback) {
onSuccess: () => { onSuccess: () => {
lead.reload() lead.reload()
reload.value = true reload.value = true
createToast({ toast.success(__('Lead updated successfully'))
title: __('Lead updated'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
callback?.() callback?.()
}, },
onError: (err) => { onError: (err) => {
createToast({ toast.error(err.messages?.[0] || __('Error updating lead'))
title: __('Error updating lead'),
text: __(err.messages?.[0]),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
}, },
}) })
} }
@ -476,12 +469,7 @@ function updateLead(fieldname, value, callback) {
function validateRequired(fieldname, value) { function validateRequired(fieldname, value) {
let meta = lead.data.fields_meta || {} let meta = lead.data.fields_meta || {}
if (meta[fieldname]?.reqd && !value) { if (meta[fieldname]?.reqd && !value) {
createToast({ toast.error(__('{0} is a required field', [meta[fieldname].label]))
title: __('Error Updating Lead'),
text: __('{0} is a required field', [meta[fieldname].label]),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
return true return true
} }
return false return false
@ -628,22 +616,12 @@ const existingOrganization = ref('')
async function convertToDeal() { async function convertToDeal() {
if (existingContactChecked.value && !existingContact.value) { if (existingContactChecked.value && !existingContact.value) {
createToast({ toast.error(__('Please select an existing contact'))
title: __('Error'),
text: __('Please select an existing contact'),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
return return
} }
if (existingOrganizationChecked.value && !existingOrganization.value) { if (existingOrganizationChecked.value && !existingOrganization.value) {
createToast({ toast.error(__('Please select an existing organization'))
title: __('Error'),
text: __('Please select an existing organization'),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
return return
} }
@ -661,12 +639,7 @@ async function convertToDeal() {
existing_contact: existingContact.value, existing_contact: existingContact.value,
existing_organization: existingOrganization.value, existing_organization: existingOrganization.value,
}).catch((err) => { }).catch((err) => {
createToast({ toast.error(__('Error converting to deal: {0}', [err.messages?.[0]]))
title: __('Error converting to deal'),
text: __(err.messages?.[0]),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
}) })
if (_deal) { if (_deal) {
showConvertToDealModal.value = false showConvertToDealModal.value = false
@ -675,7 +648,7 @@ async function convertToDeal() {
existingContact.value = '' existingContact.value = ''
existingOrganization.value = '' existingOrganization.value = ''
updateOnboardingStep('convert_lead_to_deal', true, false, () => { updateOnboardingStep('convert_lead_to_deal', true, false, () => {
localStorage.setItem('firstDeal', _deal) localStorage.setItem('firstDeal' + user, _deal)
}) })
capture('convert_lead_to_deal') capture('convert_lead_to_deal')
router.push({ name: 'Deal', params: { dealId: _deal } }) router.push({ name: 'Deal', params: { dealId: _deal } })

View File

@ -169,7 +169,7 @@ import CameraIcon from '@/components/Icons/CameraIcon.vue'
import DealsIcon from '@/components/Icons/DealsIcon.vue' import DealsIcon from '@/components/Icons/DealsIcon.vue'
import DealsListView from '@/components/ListViews/DealsListView.vue' import DealsListView from '@/components/ListViews/DealsListView.vue'
import AddressModal from '@/components/Modals/AddressModal.vue' import AddressModal from '@/components/Modals/AddressModal.vue'
import { formatDate, timeAgo, createToast } from '@/utils' import { formatDate, timeAgo } from '@/utils'
import { getView } from '@/utils/view' import { getView } from '@/utils/view'
import { getSettings } from '@/stores/settings' import { getSettings } from '@/stores/settings'
import { getMeta } from '@/stores/meta' import { getMeta } from '@/stores/meta'
@ -189,6 +189,7 @@ import {
createResource, createResource,
usePageMeta, usePageMeta,
Dropdown, Dropdown,
toast,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, computed, h } from 'vue' import { ref, computed, h } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
@ -496,11 +497,7 @@ async function setAsPrimary(field, value) {
}) })
if (d) { if (d) {
contact.reload() contact.reload()
createToast({ toast.success(___('Contact updated'))
title: 'Contact updated',
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -513,11 +510,7 @@ async function createNew(field, value) {
}) })
if (d) { if (d) {
contact.reload() contact.reload()
createToast({ toast.success(__('Contact updated'))
title: 'Contact updated',
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -530,11 +523,7 @@ async function editOption(doctype, name, fieldname, value) {
}) })
if (d) { if (d) {
contact.reload() contact.reload()
createToast({ toast.success(__('Contact updated'))
title: 'Contact updated',
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -544,11 +533,7 @@ async function deleteOption(doctype, name) {
name, name,
}) })
await contact.reload() await contact.reload()
createToast({ toast.success(__('Contact updated'))
title: 'Contact updated',
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
async function updateField(fieldname, value) { async function updateField(fieldname, value) {
@ -558,11 +543,7 @@ async function updateField(fieldname, value) {
fieldname, fieldname,
value, value,
}) })
createToast({ toast.success(__('Contact updated'))
title: 'Contact updated',
icon: 'check',
iconClasses: 'text-ink-green-3',
})
contact.reload() contact.reload()
} }

View File

@ -256,7 +256,7 @@ import Link from '@/components/Controls/Link.vue'
import SidePanelLayout from '@/components/SidePanelLayout.vue' import SidePanelLayout from '@/components/SidePanelLayout.vue'
import SLASection from '@/components/SLASection.vue' import SLASection from '@/components/SLASection.vue'
import CustomActions from '@/components/CustomActions.vue' import CustomActions from '@/components/CustomActions.vue'
import { createToast, setupAssignees, setupCustomizations } from '@/utils' import { setupAssignees, setupCustomizations } from '@/utils'
import { getView } from '@/utils/view' import { getView } from '@/utils/view'
import { getSettings } from '@/stores/settings' import { getSettings } from '@/stores/settings'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
@ -278,6 +278,7 @@ import {
Breadcrumbs, Breadcrumbs,
call, call,
usePageMeta, usePageMeta,
toast,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, computed, h, onMounted } from 'vue' import { ref, computed, h, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
@ -314,8 +315,9 @@ const deal = createResource({
$dialog, $dialog,
$socket, $socket,
router, router,
toast,
updateField, updateField,
createToast, createToast: toast.create,
deleteDoc: deleteDeal, deleteDoc: deleteDeal,
resource: { resource: {
deal, deal,
@ -358,20 +360,11 @@ function updateDeal(fieldname, value, callback) {
onSuccess: () => { onSuccess: () => {
deal.reload() deal.reload()
reload.value = true reload.value = true
createToast({ toast.success(__('Deal updated'))
title: __('Deal updated'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
callback?.() callback?.()
}, },
onError: (err) => { onError: (err) => {
createToast({ toast.error(err.messages?.[0] || __('Error updating deal'))
title: __('Error updating deal'),
text: __(err.messages?.[0]),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
}, },
}) })
} }
@ -379,12 +372,7 @@ function updateDeal(fieldname, value, callback) {
function validateRequired(fieldname, value) { function validateRequired(fieldname, value) {
let meta = deal.data.fields_meta || {} let meta = deal.data.fields_meta || {}
if (meta[fieldname]?.reqd && !value) { if (meta[fieldname]?.reqd && !value) {
createToast({ toast.error(__('{0} is a required field', [meta[fieldname].label]))
title: __('Error Updating Deal'),
text: __('{0} is a required field', [meta[fieldname].label]),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
return true return true
} }
return false return false
@ -541,11 +529,7 @@ function contactOptions(contact) {
async function addContact(contact) { async function addContact(contact) {
if (dealContacts.data?.find((c) => c.name === contact)) { if (dealContacts.data?.find((c) => c.name === contact)) {
createToast({ toast.error(__('Contact already added'))
title: __('Contact already added'),
icon: 'x',
iconClasses: 'text-ink-red-3',
})
return return
} }
@ -555,11 +539,7 @@ async function addContact(contact) {
}) })
if (d) { if (d) {
dealContacts.reload() dealContacts.reload()
createToast({ toast.success(__('Contact added'))
title: __('Contact added'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -570,11 +550,7 @@ async function removeContact(contact) {
}) })
if (d) { if (d) {
dealContacts.reload() dealContacts.reload()
createToast({ toast.success(__('Contact removed'))
title: __('Contact removed'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }
@ -585,11 +561,7 @@ async function setPrimaryContact(contact) {
}) })
if (d) { if (d) {
dealContacts.reload() dealContacts.reload()
createToast({ toast.success(__('Primary contact set'))
title: __('Primary contact set'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }

View File

@ -173,7 +173,7 @@ import Link from '@/components/Controls/Link.vue'
import SidePanelLayout from '@/components/SidePanelLayout.vue' import SidePanelLayout from '@/components/SidePanelLayout.vue'
import SLASection from '@/components/SLASection.vue' import SLASection from '@/components/SLASection.vue'
import CustomActions from '@/components/CustomActions.vue' import CustomActions from '@/components/CustomActions.vue'
import { createToast, setupAssignees, setupCustomizations } from '@/utils' import { setupAssignees, setupCustomizations } from '@/utils'
import { getView } from '@/utils/view' import { getView } from '@/utils/view'
import { getSettings } from '@/stores/settings' import { getSettings } from '@/stores/settings'
import { globalStore } from '@/stores/global' import { globalStore } from '@/stores/global'
@ -196,6 +196,7 @@ import {
Breadcrumbs, Breadcrumbs,
call, call,
usePageMeta, usePageMeta,
toast,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, computed, onMounted, watch } from 'vue' import { ref, computed, onMounted, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
@ -225,8 +226,9 @@ const lead = createResource({
$dialog, $dialog,
$socket, $socket,
router, router,
toast,
updateField, updateField,
createToast, createToast: toast.create,
deleteDoc: deleteLead, deleteDoc: deleteLead,
resource: { resource: {
lead, lead,
@ -261,20 +263,11 @@ function updateLead(fieldname, value, callback) {
onSuccess: () => { onSuccess: () => {
lead.reload() lead.reload()
reload.value = true reload.value = true
createToast({ toast.success(__('Lead updated successfully'))
title: __('Lead updated'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
callback?.() callback?.()
}, },
onError: (err) => { onError: (err) => {
createToast({ toast.error(__(err.messages?.[0] || 'Error updating lead'))
title: __('Error updating lead'),
text: __(err.messages?.[0]),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
}, },
}) })
} }
@ -282,12 +275,7 @@ function updateLead(fieldname, value, callback) {
function validateRequired(fieldname, value) { function validateRequired(fieldname, value) {
let meta = lead.data.fields_meta || {} let meta = lead.data.fields_meta || {}
if (meta[fieldname]?.reqd && !value) { if (meta[fieldname]?.reqd && !value) {
createToast({ toast.error(__('{0} is a required field', [meta[fieldname].label]))
title: __('Error Updating Lead'),
text: __('{0} is a required field', [meta[fieldname].label]),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
return true return true
} }
return false return false
@ -433,22 +421,12 @@ const existingOrganization = ref('')
async function convertToDeal() { async function convertToDeal() {
if (existingContactChecked.value && !existingContact.value) { if (existingContactChecked.value && !existingContact.value) {
createToast({ toast.error(__('Please select an existing contact'))
title: __('Error'),
text: __('Please select an existing contact'),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
return return
} }
if (existingOrganizationChecked.value && !existingOrganization.value) { if (existingOrganizationChecked.value && !existingOrganization.value) {
createToast({ toast.error(__('Please select an existing organization'))
title: __('Error'),
text: __('Please select an existing organization'),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
return return
} }

View File

@ -165,7 +165,7 @@ import { globalStore } from '@/stores/global'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { statusesStore } from '@/stores/statuses' import { statusesStore } from '@/stores/statuses'
import { getView } from '@/utils/view' import { getView } from '@/utils/view'
import { formatDate, timeAgo, createToast } from '@/utils' import { formatDate, timeAgo } from '@/utils'
import { import {
Breadcrumbs, Breadcrumbs,
Avatar, Avatar,
@ -179,6 +179,7 @@ import {
createDocumentResource, createDocumentResource,
usePageMeta, usePageMeta,
createResource, createResource,
toast,
} from 'frappe-ui' } from 'frappe-ui'
import { h, computed, ref } from 'vue' import { h, computed, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
@ -207,17 +208,6 @@ const organization = createDocumentResource({
auto: true, auto: true,
}) })
async function updateField(fieldname, value) {
await organization.setValue.submit({
[fieldname]: value,
})
createToast({
title: __('Organization updated'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
}
const breadcrumbs = computed(() => { const breadcrumbs = computed(() => {
let items = [{ label: __('Organizations'), route: { name: 'Organizations' } }] let items = [{ label: __('Organizations'), route: { name: 'Organizations' } }]
@ -302,12 +292,7 @@ async function deleteOrganization() {
} }
function openWebsite() { function openWebsite() {
if (!organization.doc.website) if (!organization.doc.website) toast.error(__('No website found'))
createToast({
title: __('Website not found'),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
else window.open(organization.doc.website, '_blank') else window.open(organization.doc.website, '_blank')
} }

View File

@ -192,7 +192,7 @@ import { globalStore } from '@/stores/global'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { statusesStore } from '@/stores/statuses' import { statusesStore } from '@/stores/statuses'
import { getView } from '@/utils/view' import { getView } from '@/utils/view'
import { formatDate, timeAgo, createToast } from '@/utils' import { formatDate, timeAgo } from '@/utils'
import { import {
Tooltip, Tooltip,
Breadcrumbs, Breadcrumbs,
@ -205,6 +205,7 @@ import {
createDocumentResource, createDocumentResource,
usePageMeta, usePageMeta,
createResource, createResource,
toast,
} from 'frappe-ui' } from 'frappe-ui'
import { h, computed, ref } from 'vue' import { h, computed, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
@ -249,17 +250,6 @@ const organization = createDocumentResource({
}, },
}) })
async function updateField(fieldname, value) {
await organization.setValue.submit({
[fieldname]: value,
})
createToast({
title: __('Organization updated'),
icon: 'check',
iconClasses: 'text-ink-green-3',
})
}
const breadcrumbs = computed(() => { const breadcrumbs = computed(() => {
let items = [{ label: __('Organizations'), route: { name: 'Organizations' } }] let items = [{ label: __('Organizations'), route: { name: 'Organizations' } }]
@ -348,12 +338,7 @@ function website(url) {
} }
function openWebsite() { function openWebsite() {
if (!organization.doc.website) if (!organization.doc.website) toast.error(__('No website found'))
createToast({
title: __('Website not found'),
icon: 'x',
iconClasses: 'text-ink-red-4',
})
else window.open(organization.doc.website, '_blank') else window.open(organization.doc.website, '_blank')
} }

View File

@ -7,13 +7,6 @@ import { getMeta } from '@/stores/meta'
import { toast, dayjsLocal, dayjs } from 'frappe-ui' import { toast, dayjsLocal, dayjs } from 'frappe-ui'
import { h } from 'vue' import { h } from 'vue'
export function createToast(options) {
toast({
position: 'bottom-right',
...options,
})
}
export function formatTime(seconds) { export function formatTime(seconds) {
const days = Math.floor(seconds / (3600 * 24)) const days = Math.floor(seconds / (3600 * 24))
const hours = Math.floor((seconds % (3600 * 24)) / 3600) const hours = Math.floor((seconds % (3600 * 24)) / 3600)
@ -209,34 +202,20 @@ export async function setupListCustomizations(data, obj = {}) {
return { actions, bulkActions } return { actions, bulkActions }
} }
export function errorMessage(title, message) {
createToast({
title: title || 'Error',
text: message,
icon: 'x',
iconClasses: 'text-ink-red-4',
})
}
export function copyToClipboard(text) { export function copyToClipboard(text) {
if (navigator.clipboard && window.isSecureContext) { if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(show_success_alert) navigator.clipboard.writeText(text).then(showSuccessAlert)
} else { } else {
let input = document.createElement('textarea') let input = document.createElement('textarea')
document.body.appendChild(input) document.body.appendChild(input)
input.value = text input.value = text
input.select() input.select()
document.execCommand('copy') document.execCommand('copy')
show_success_alert() showSuccessAlert()
document.body.removeChild(input) document.body.removeChild(input)
} }
function show_success_alert() { function showSuccessAlert() {
createToast({ toast.success(__('Copied to clipboard'))
title: 'Copied to clipboard',
text: text,
icon: 'check',
iconClasses: 'text-ink-green-3',
})
} }
} }

View File

@ -1,5 +1,7 @@
module.exports = { import frappeUIPreset from 'frappe-ui/src/tailwind/preset'
presets: [require('frappe-ui/src/tailwind/preset')],
export default {
presets: [frappeUIPreset],
content: [ content: [
'./index.html', './index.html',
'./src/**/*.{vue,js,ts,jsx,tsx}', './src/**/*.{vue,js,ts,jsx,tsx}',

View File

@ -1,5 +1,6 @@
{ {
"private": true, "private": true,
"type": "module",
"workspaces": ["frontend", "frappe-ui"], "workspaces": ["frontend", "frappe-ui"],
"scripts": { "scripts": {
"postinstall": "cd frontend && yarn install", "postinstall": "cd frontend && yarn install",

View File

@ -1236,6 +1236,11 @@
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.10.3.tgz#7744abd4a954f35265af351f1be9b545e819c66d" resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.10.3.tgz#7744abd4a954f35265af351f1be9b545e819c66d"
integrity sha512-wAG/0/UsLeZLmshWb6rtWNXKJftcmnned91/HLccHVQAuQZ1UWH+wXeQKu/mtodxEO7JcU2mVPR9mLGQkK0McQ== integrity sha512-wAG/0/UsLeZLmshWb6rtWNXKJftcmnned91/HLccHVQAuQZ1UWH+wXeQKu/mtodxEO7JcU2mVPR9mLGQkK0McQ==
"@tiptap/core@^2.11.7":
version "2.12.0"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.12.0.tgz#e0bc9255865db53682e83d843c8fef19596a8899"
integrity sha512-3qX8oGVKFFZzQ0vit+ZolR6AJIATBzmEmjAA0llFhWk4vf3v64p1YcXcJsOBsr5scizJu5L6RYWEFatFwqckRg==
"@tiptap/extension-blockquote@^2.10.3": "@tiptap/extension-blockquote@^2.10.3":
version "2.10.3" version "2.10.3"
resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.10.3.tgz#ee29925930ac9a5b129d3ad262bb45afcc23b318" resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.10.3.tgz#ee29925930ac9a5b129d3ad262bb45afcc23b318"
@ -1268,6 +1273,11 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.10.3.tgz#5ff1b1e563c4eda44677df444c523de1e5258fa4" resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.10.3.tgz#5ff1b1e563c4eda44677df444c523de1e5258fa4"
integrity sha512-yiDVNg22fYkzsFk5kBlDSHcjwVJgajvO/M5fDXA+Hfxwo2oNcG6aJyyHXFe+UaXTVjdkPej0J6kcMKrTMCiFug== integrity sha512-yiDVNg22fYkzsFk5kBlDSHcjwVJgajvO/M5fDXA+Hfxwo2oNcG6aJyyHXFe+UaXTVjdkPej0J6kcMKrTMCiFug==
"@tiptap/extension-code-block@^2.11.9":
version "2.12.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.12.0.tgz#73bceaa90ba54de65ade42db5a28cc299c16158b"
integrity sha512-1D7cYAjgxEFHdfC/35Ooi4GqWKB5sszbW8iI7N16XILNln26xb0d5KflXqYrwr9CN/ZnZoCl2o6YsP7xEObcZA==
"@tiptap/extension-code@^2.10.3": "@tiptap/extension-code@^2.10.3":
version "2.10.3" version "2.10.3"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.10.3.tgz#b9fb04be2d51760f011ec7a060d4e2e3eefe392c" resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.10.3.tgz#b9fb04be2d51760f011ec7a060d4e2e3eefe392c"
@ -1362,6 +1372,11 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.10.3.tgz#128c8fcd46d2e854d214c7f566e6212f2ebff6f1" resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.10.3.tgz#128c8fcd46d2e854d214c7f566e6212f2ebff6f1"
integrity sha512-sNkTX/iN+YoleDiTJsrWSBw9D7c4vsYwnW5y/G5ydfuJMIRQMF78pWSIWZFDRNOMkgK5UHkhu9anrbCFYgBfaA== integrity sha512-sNkTX/iN+YoleDiTJsrWSBw9D7c4vsYwnW5y/G5ydfuJMIRQMF78pWSIWZFDRNOMkgK5UHkhu9anrbCFYgBfaA==
"@tiptap/extension-paragraph@^2.12.0":
version "2.12.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.12.0.tgz#35987cfa842f254e67ecec403441578a1b2189a1"
integrity sha512-QNK5cgewCunWFxpLlbvvoO1rrLgEtNKxiY79fctP9toV+e59R+1i1Q9lXC1O5mOfDgVxCb6uFDMsqmKhFjpPog==
"@tiptap/extension-placeholder@^2.0.3": "@tiptap/extension-placeholder@^2.0.3":
version "2.10.3" version "2.10.3"
resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.10.3.tgz#eeb2b2f1916619e334af7d2a9d6885f77bb4ab78" resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.10.3.tgz#eeb2b2f1916619e334af7d2a9d6885f77bb4ab78"
@ -2267,6 +2282,14 @@ eastasianwidth@^0.2.0:
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
echarts@^5.6.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.6.0.tgz#2377874dca9fb50f104051c3553544752da3c9d6"
integrity sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==
dependencies:
tslib "2.3.0"
zrender "5.6.1"
ejs@^3.1.6: ejs@^3.1.6:
version "3.1.10" version "3.1.10"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b"
@ -2542,15 +2565,18 @@ fraction.js@^4.3.7:
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==
frappe-ui@^0.1.121: frappe-ui@^0.1.145:
version "0.1.121" version "0.1.145"
resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.1.121.tgz#a8d37f300228edfcbb6b4fffb343f0773dcfd933" resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.1.145.tgz#19ec429badf85f3f2c45a85ec13c3c462ec11ee9"
integrity sha512-gvtKKZECPD2MU5X4MwPUKr2hSOs1+s1DA9laP3aPnmH0ukJRSFEhDOyjCMfH9k6ZdAe/vZCIbT4XucxLq/fOEA== integrity sha512-DnnSJREu/EpUAJGNFaXEUF3re0hQMmLBOX/MSW9AsQtnCJwXkO5VbH/dyVHAZjqdb9Do3CNQF33/HB4NibNI8Q==
dependencies: dependencies:
"@floating-ui/vue" "^1.1.6"
"@headlessui/vue" "^1.7.14" "@headlessui/vue" "^1.7.14"
"@popperjs/core" "^2.11.2" "@popperjs/core" "^2.11.2"
"@tailwindcss/forms" "^0.5.3" "@tailwindcss/forms" "^0.5.3"
"@tailwindcss/typography" "^0.5.16" "@tailwindcss/typography" "^0.5.16"
"@tiptap/core" "^2.11.7"
"@tiptap/extension-code-block" "^2.11.9"
"@tiptap/extension-code-block-lowlight" "^2.11.5" "@tiptap/extension-code-block-lowlight" "^2.11.5"
"@tiptap/extension-color" "^2.0.3" "@tiptap/extension-color" "^2.0.3"
"@tiptap/extension-highlight" "^2.0.3" "@tiptap/extension-highlight" "^2.0.3"
@ -2571,12 +2597,16 @@ frappe-ui@^0.1.121:
"@tiptap/vue-3" "^2.0.3" "@tiptap/vue-3" "^2.0.3"
"@vueuse/core" "^10.4.1" "@vueuse/core" "^10.4.1"
dayjs "^1.11.13" dayjs "^1.11.13"
echarts "^5.6.0"
feather-icons "^4.28.0" feather-icons "^4.28.0"
idb-keyval "^6.2.0" idb-keyval "^6.2.0"
lowlight "^3.3.0" lowlight "^3.3.0"
lucide-static "^0.479.0" lucide-static "^0.479.0"
ora "5.4.1" ora "5.4.1"
prettier "^3.3.2" prettier "^3.3.2"
prosemirror-model "^1.25.1"
prosemirror-state "^1.4.3"
prosemirror-view "^1.39.2"
radix-vue "^1.5.3" radix-vue "^1.5.3"
reka-ui "^2.0.2" reka-ui "^2.0.2"
showdown "^2.1.0" showdown "^2.1.0"
@ -3732,6 +3762,13 @@ prosemirror-model@^1.0.0, prosemirror-model@^1.19.0, prosemirror-model@^1.20.0,
dependencies: dependencies:
orderedmap "^2.0.0" orderedmap "^2.0.0"
prosemirror-model@^1.25.1:
version "1.25.1"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.25.1.tgz#aeae9f1ec79fcaa76f6fc619800d91fbcf726870"
integrity sha512-AUvbm7qqmpZa5d9fPKMvH1Q5bqYQvAZWOGRvxsB6iFLyycvC9MwNemNVjHVrWgjaoxAfY8XVg7DbvQ/qxvI9Eg==
dependencies:
orderedmap "^2.0.0"
prosemirror-schema-basic@^1.2.3: prosemirror-schema-basic@^1.2.3:
version "1.2.3" version "1.2.3"
resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.3.tgz#649c349bb21c61a56febf9deb71ac68fca4cedf2" resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.3.tgz#649c349bb21c61a56febf9deb71ac68fca4cedf2"
@ -3792,6 +3829,15 @@ prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, pros
prosemirror-state "^1.0.0" prosemirror-state "^1.0.0"
prosemirror-transform "^1.1.0" prosemirror-transform "^1.1.0"
prosemirror-view@^1.39.2:
version "1.39.3"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.39.3.tgz#54fa4b8ab4fd75ad0075dc6dc0be1745429d5a5c"
integrity sha512-bY/7kg0LzRE7ytR0zRdSMWX3sknEjw68l836ffLPMh0OG3OYnNuBDUSF3v0vjvnzgYjgY9ZH/RypbARURlcMFA==
dependencies:
prosemirror-model "^1.20.0"
prosemirror-state "^1.0.0"
prosemirror-transform "^1.1.0"
punycode.js@^2.3.1: punycode.js@^2.3.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7"
@ -4463,6 +4509,11 @@ ts-interface-checker@^0.1.9:
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
tslib@2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
tslib@^2.0.0, tslib@^2.8.0: tslib@^2.0.0, tslib@^2.8.0:
version "2.8.1" version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
@ -4984,3 +5035,10 @@ yaml@^2.3.4:
version "2.6.1" version "2.6.1"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773"
integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg== integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==
zrender@5.6.1:
version "5.6.1"
resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.6.1.tgz#e08d57ecf4acac708c4fcb7481eb201df7f10a6b"
integrity sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==
dependencies:
tslib "2.3.0"