Merge branch 'develop' into pot_develop_2025-03-30
This commit is contained in:
commit
899b09ac40
@ -418,16 +418,23 @@ def get_data(
|
|||||||
rows.append(field)
|
rows.append(field)
|
||||||
|
|
||||||
for kc in kanban_columns:
|
for kc in kanban_columns:
|
||||||
column_filters = {column_field: kc.get("name")}
|
# Start with base filters
|
||||||
|
column_filters = []
|
||||||
|
|
||||||
|
# Convert and add the main filters first
|
||||||
|
if filters:
|
||||||
|
base_filters = convert_filter_to_tuple(doctype, filters)
|
||||||
|
column_filters.extend(base_filters)
|
||||||
|
|
||||||
|
# Add the column-specific filter
|
||||||
|
if column_field and kc.get("name"):
|
||||||
|
column_filters.append([doctype, column_field, "=", kc.get("name")])
|
||||||
|
|
||||||
order = kc.get("order")
|
order = kc.get("order")
|
||||||
if (column_field in filters and filters.get(column_field) != kc.get("name")) or kc.get("delete"):
|
if kc.get("delete"):
|
||||||
column_data = []
|
column_data = []
|
||||||
else:
|
else:
|
||||||
column_filters.update(filters.copy())
|
page_length = kc.get("page_length", 20)
|
||||||
page_length = 20
|
|
||||||
|
|
||||||
if kc.get("page_length"):
|
|
||||||
page_length = kc.get("page_length")
|
|
||||||
|
|
||||||
if order:
|
if order:
|
||||||
column_data = get_records_based_on_order(
|
column_data = get_records_based_on_order(
|
||||||
@ -437,26 +444,20 @@ def get_data(
|
|||||||
column_data = frappe.get_list(
|
column_data = frappe.get_list(
|
||||||
doctype,
|
doctype,
|
||||||
fields=rows,
|
fields=rows,
|
||||||
filters=convert_filter_to_tuple(doctype, column_filters),
|
filters=column_filters,
|
||||||
order_by=order_by,
|
order_by=order_by,
|
||||||
page_length=page_length,
|
page_length=page_length,
|
||||||
)
|
)
|
||||||
|
|
||||||
new_filters = filters.copy()
|
|
||||||
new_filters.update({column_field: kc.get("name")})
|
|
||||||
|
|
||||||
all_count = frappe.get_list(
|
all_count = frappe.get_list(
|
||||||
doctype,
|
doctype,
|
||||||
filters=convert_filter_to_tuple(doctype, new_filters),
|
filters=column_filters,
|
||||||
fields="count(*) as total_count",
|
fields="count(*) as total_count",
|
||||||
)[0].total_count
|
)[0].total_count
|
||||||
|
|
||||||
kc["all_count"] = all_count
|
kc["all_count"] = all_count
|
||||||
kc["count"] = len(column_data)
|
kc["count"] = len(column_data)
|
||||||
|
|
||||||
for d in column_data:
|
|
||||||
getCounts(d, doctype)
|
|
||||||
|
|
||||||
if order:
|
if order:
|
||||||
column_data = sorted(
|
column_data = sorted(
|
||||||
column_data,
|
column_data,
|
||||||
|
|||||||
@ -41,13 +41,15 @@
|
|||||||
"fieldname": "from",
|
"fieldname": "from",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "From"
|
"label": "From",
|
||||||
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"options": "Initiated\nRinging\nIn Progress\nCompleted\nFailed\nBusy\nNo Answer\nQueued\nCanceled"
|
"options": "Initiated\nRinging\nIn Progress\nCompleted\nFailed\nBusy\nNo Answer\nQueued\nCanceled",
|
||||||
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "start_time",
|
"fieldname": "start_time",
|
||||||
@ -69,13 +71,15 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
"options": "Incoming\nOutgoing"
|
"options": "Incoming\nOutgoing",
|
||||||
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "to",
|
"fieldname": "to",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "To"
|
"label": "To",
|
||||||
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Call duration in seconds",
|
"description": "Call duration in seconds",
|
||||||
@ -153,7 +157,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-01-22 17:57:59.289548",
|
"modified": "2025-04-01 16:01:54.479309",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "FCRM",
|
"module": "FCRM",
|
||||||
"name": "CRM Call Log",
|
"name": "CRM Call Log",
|
||||||
|
|||||||
@ -19,7 +19,8 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Title"
|
"label": "Title",
|
||||||
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "content",
|
"fieldname": "content",
|
||||||
@ -49,7 +50,7 @@
|
|||||||
"link_fieldname": "note"
|
"link_fieldname": "note"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2024-01-19 21:56:30.123334",
|
"modified": "2025-04-01 15:30:14.742001",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "FCRM",
|
"module": "FCRM",
|
||||||
"name": "FCRM Note",
|
"name": "FCRM Note",
|
||||||
|
|||||||
@ -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-03-30 09:35+0000\n"
|
"POT-Creation-Date: 2025-04-06 09:35+0000\n"
|
||||||
"PO-Revision-Date: 2025-03-30 09:35+0000\n"
|
"PO-Revision-Date: 2025-04-06 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"
|
||||||
@ -20,7 +20,7 @@ msgstr ""
|
|||||||
msgid " (New)"
|
msgid " (New)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/TaskModal.vue:95
|
#: frontend/src/components/Modals/TaskModal.vue:66
|
||||||
#: frontend/src/components/Telephony/TaskPanel.vue:67
|
#: frontend/src/components/Telephony/TaskPanel.vue:67
|
||||||
msgid "01/04/2024 11:30 PM"
|
msgid "01/04/2024 11:30 PM"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -631,8 +631,8 @@ msgstr ""
|
|||||||
msgid "Call using {0}"
|
msgid "Call using {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/NoteModal.vue:43
|
#: frontend/src/components/Modals/NoteModal.vue:30
|
||||||
#: frontend/src/components/Modals/TaskModal.vue:43
|
#: frontend/src/components/Modals/TaskModal.vue:30
|
||||||
msgid "Call with John Doe"
|
msgid "Call with John Doe"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -886,7 +886,7 @@ msgstr ""
|
|||||||
#: crm/fcrm/doctype/fcrm_note/fcrm_note.json
|
#: crm/fcrm/doctype/fcrm_note/fcrm_note.json
|
||||||
#: frontend/src/components/Modals/EmailTemplateModal.vue:61
|
#: frontend/src/components/Modals/EmailTemplateModal.vue:61
|
||||||
#: frontend/src/components/Modals/EmailTemplateModal.vue:74
|
#: frontend/src/components/Modals/EmailTemplateModal.vue:74
|
||||||
#: frontend/src/components/Modals/NoteModal.vue:47
|
#: frontend/src/components/Modals/NoteModal.vue:34
|
||||||
msgid "Content"
|
msgid "Content"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -923,14 +923,14 @@ msgid "Converted successfully"
|
|||||||
msgstr ""
|
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:85
|
||||||
#: frontend/src/components/Modals/ContactModal.vue:37
|
#: frontend/src/components/Modals/ContactModal.vue:37
|
||||||
#: 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
|
||||||
#: frontend/src/components/Modals/NoteModal.vue:8
|
#: frontend/src/components/Modals/NoteModal.vue:6
|
||||||
#: frontend/src/components/Modals/OrganizationModal.vue:37
|
#: frontend/src/components/Modals/OrganizationModal.vue:25
|
||||||
#: frontend/src/components/Modals/TaskModal.vue:8
|
#: frontend/src/components/Modals/TaskModal.vue:6
|
||||||
#: frontend/src/components/Modals/ViewModal.vue:16
|
#: frontend/src/components/Modals/ViewModal.vue:16
|
||||||
#: frontend/src/pages/CallLogs.vue:11 frontend/src/pages/Contacts.vue:13
|
#: frontend/src/pages/CallLogs.vue:11 frontend/src/pages/Contacts.vue:13
|
||||||
#: frontend/src/pages/Contacts.vue:57 frontend/src/pages/Deals.vue:13
|
#: frontend/src/pages/Contacts.vue:57 frontend/src/pages/Deals.vue:13
|
||||||
@ -963,12 +963,12 @@ msgid "Create New"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Activities/Activities.vue:383
|
#: frontend/src/components/Activities/Activities.vue:383
|
||||||
#: frontend/src/components/Modals/NoteModal.vue:18
|
#: frontend/src/components/Modals/NoteModal.vue:15
|
||||||
msgid "Create Note"
|
msgid "Create Note"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Activities/Activities.vue:398
|
#: frontend/src/components/Activities/Activities.vue:398
|
||||||
#: frontend/src/components/Modals/TaskModal.vue:18
|
#: frontend/src/components/Modals/TaskModal.vue:15
|
||||||
msgid "Create Task"
|
msgid "Create Task"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1197,7 +1197,7 @@ msgstr ""
|
|||||||
#. 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_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:35
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1343,7 +1343,7 @@ msgstr ""
|
|||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/CallLogModal.vue:100
|
#: frontend/src/components/Modals/CallLogModal.vue:81
|
||||||
msgid "Edit Call Log"
|
msgid "Edit Call Log"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1363,7 +1363,7 @@ msgstr ""
|
|||||||
msgid "Edit Grid Row Fields Layout"
|
msgid "Edit Grid Row Fields Layout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/NoteModal.vue:18
|
#: frontend/src/components/Modals/NoteModal.vue:15
|
||||||
msgid "Edit Note"
|
msgid "Edit Note"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1371,7 +1371,7 @@ msgstr ""
|
|||||||
msgid "Edit Quick Entry Layout"
|
msgid "Edit Quick Entry Layout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/TaskModal.vue:18
|
#: frontend/src/components/Modals/TaskModal.vue:15
|
||||||
msgid "Edit Task"
|
msgid "Edit Task"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2059,7 +2059,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: frontend/src/components/Filter.vue:75 frontend/src/components/Filter.vue:108
|
#: frontend/src/components/Filter.vue:75 frontend/src/components/Filter.vue:108
|
||||||
#: frontend/src/components/Modals/AssignmentModal.vue:35
|
#: frontend/src/components/Modals/AssignmentModal.vue:35
|
||||||
#: frontend/src/components/Modals/TaskModal.vue:75
|
#: frontend/src/components/Modals/TaskModal.vue:51
|
||||||
#: frontend/src/components/Telephony/TaskPanel.vue:47
|
#: frontend/src/components/Telephony/TaskPanel.vue:47
|
||||||
msgid "John Doe"
|
msgid "John Doe"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2446,7 +2446,7 @@ msgstr ""
|
|||||||
msgid "New Address"
|
msgid "New Address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/CallLogModal.vue:100
|
#: frontend/src/components/Modals/CallLogModal.vue:81
|
||||||
msgid "New Call Log"
|
msgid "New Call Log"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2735,13 +2735,13 @@ msgstr ""
|
|||||||
msgid "Only one {0} can be set as primary."
|
msgid "Only one {0} can be set as primary."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/NoteModal.vue:25
|
#: frontend/src/components/Modals/NoteModal.vue:18
|
||||||
#: frontend/src/components/Modals/TaskModal.vue:25
|
#: frontend/src/components/Modals/TaskModal.vue:18
|
||||||
msgid "Open Deal"
|
msgid "Open Deal"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/NoteModal.vue:26
|
#: frontend/src/components/Modals/NoteModal.vue:19
|
||||||
#: frontend/src/components/Modals/TaskModal.vue:26
|
#: frontend/src/components/Modals/TaskModal.vue:19
|
||||||
msgid "Open Lead"
|
msgid "Open Lead"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3331,7 +3331,7 @@ msgstr ""
|
|||||||
#: frontend/src/components/Controls/GridRowFieldsModal.vue:26
|
#: frontend/src/components/Controls/GridRowFieldsModal.vue:26
|
||||||
#: frontend/src/components/DropdownItem.vue:21
|
#: frontend/src/components/DropdownItem.vue:21
|
||||||
#: 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:85
|
||||||
#: frontend/src/components/Modals/DataFieldsModal.vue:26
|
#: frontend/src/components/Modals/DataFieldsModal.vue:26
|
||||||
#: frontend/src/components/Modals/QuickEntryModal.vue:26
|
#: frontend/src/components/Modals/QuickEntryModal.vue:26
|
||||||
#: frontend/src/components/Modals/SidePanelModal.vue:26
|
#: frontend/src/components/Modals/SidePanelModal.vue:26
|
||||||
@ -3709,8 +3709,8 @@ msgstr ""
|
|||||||
#. Label of the title (Data) field in DocType 'FCRM Note'
|
#. Label of the title (Data) field in DocType 'FCRM Note'
|
||||||
#: crm/fcrm/doctype/crm_task/crm_task.json
|
#: crm/fcrm/doctype/crm_task/crm_task.json
|
||||||
#: crm/fcrm/doctype/fcrm_note/fcrm_note.json
|
#: crm/fcrm/doctype/fcrm_note/fcrm_note.json
|
||||||
#: frontend/src/components/Modals/NoteModal.vue:41
|
#: frontend/src/components/Modals/NoteModal.vue:30
|
||||||
#: frontend/src/components/Modals/TaskModal.vue:41
|
#: frontend/src/components/Modals/TaskModal.vue:30
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3757,8 +3757,8 @@ msgstr ""
|
|||||||
msgid "Tomorrow"
|
msgid "Tomorrow"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/NoteModal.vue:56
|
#: frontend/src/components/Modals/NoteModal.vue:37
|
||||||
#: frontend/src/components/Modals/TaskModal.vue:58
|
#: frontend/src/components/Modals/TaskModal.vue:39
|
||||||
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 ""
|
||||||
|
|
||||||
@ -3860,8 +3860,8 @@ 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:112
|
||||||
#: 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
|
||||||
@ -4144,7 +4144,7 @@ msgstr ""
|
|||||||
msgid "kanban"
|
msgid "kanban"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: crm/api/doc.py:38 crm/api/doc.py:156 crm/api/doc.py:500
|
#: crm/api/doc.py:38 crm/api/doc.py:156 crm/api/doc.py:501
|
||||||
msgid "label"
|
msgid "label"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit 3423aa5b5c38d3a1b143ae8ab08cbde7360f9a7c
|
Subproject commit 29307e4fffaacdbb3d9c5d95c5270b2f245a5607
|
||||||
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@ -156,6 +156,7 @@ declare module 'vue' {
|
|||||||
MobileLayout: typeof import('./src/components/Layouts/MobileLayout.vue')['default']
|
MobileLayout: typeof import('./src/components/Layouts/MobileLayout.vue')['default']
|
||||||
MobileSidebar: typeof import('./src/components/Mobile/MobileSidebar.vue')['default']
|
MobileSidebar: typeof import('./src/components/Mobile/MobileSidebar.vue')['default']
|
||||||
MoneyIcon: typeof import('./src/components/Icons/MoneyIcon.vue')['default']
|
MoneyIcon: typeof import('./src/components/Icons/MoneyIcon.vue')['default']
|
||||||
|
MultiActionButton: typeof import('./src/components/MultiActionButton.vue')['default']
|
||||||
MultipleAvatar: typeof import('./src/components/MultipleAvatar.vue')['default']
|
MultipleAvatar: typeof import('./src/components/MultipleAvatar.vue')['default']
|
||||||
MultiSelectEmailInput: typeof import('./src/components/Controls/MultiSelectEmailInput.vue')['default']
|
MultiSelectEmailInput: typeof import('./src/components/Controls/MultiSelectEmailInput.vue')['default']
|
||||||
MuteIcon: typeof import('./src/components/Icons/MuteIcon.vue')['default']
|
MuteIcon: typeof import('./src/components/Icons/MuteIcon.vue')['default']
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@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.123",
|
||||||
"gemoji": "^8.1.0",
|
"gemoji": "^8.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mime": "^4.0.1",
|
"mime": "^4.0.1",
|
||||||
|
|||||||
@ -373,11 +373,7 @@
|
|||||||
>
|
>
|
||||||
<component :is="emptyTextIcon" class="h-10 w-10" />
|
<component :is="emptyTextIcon" class="h-10 w-10" />
|
||||||
<span>{{ __(emptyText) }}</span>
|
<span>{{ __(emptyText) }}</span>
|
||||||
<Button
|
<MultiActionButton v-if="title == 'Calls'" :options="callActions" />
|
||||||
v-if="title == 'Calls'"
|
|
||||||
:label="__('Make a Call')"
|
|
||||||
@click="makeCall(doc.data.mobile_no)"
|
|
||||||
/>
|
|
||||||
<Button
|
<Button
|
||||||
v-else-if="title == 'Notes'"
|
v-else-if="title == 'Notes'"
|
||||||
:label="__('Create Note')"
|
:label="__('Create Note')"
|
||||||
@ -470,6 +466,7 @@ import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
|
|||||||
import WhatsAppArea from '@/components/Activities/WhatsAppArea.vue'
|
import WhatsAppArea from '@/components/Activities/WhatsAppArea.vue'
|
||||||
import WhatsAppBox from '@/components/Activities/WhatsAppBox.vue'
|
import WhatsAppBox from '@/components/Activities/WhatsAppBox.vue'
|
||||||
import LoadingIndicator from '@/components/Icons/LoadingIndicator.vue'
|
import LoadingIndicator from '@/components/Icons/LoadingIndicator.vue'
|
||||||
|
import MultiActionButton from '@/components/MultiActionButton.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 DotIcon from '@/components/Icons/DotIcon.vue'
|
import DotIcon from '@/components/Icons/DotIcon.vue'
|
||||||
@ -487,7 +484,7 @@ import FilesUploader from '@/components/FilesUploader/FilesUploader.vue'
|
|||||||
import { timeAgo, formatDate, startCase } from '@/utils'
|
import { timeAgo, formatDate, startCase } from '@/utils'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { whatsappEnabled } from '@/composables/settings'
|
import { whatsappEnabled, callEnabled } from '@/composables/settings'
|
||||||
import { capture } from '@/telemetry'
|
import { capture } from '@/telemetry'
|
||||||
import { Button, Tooltip, createResource } from 'frappe-ui'
|
import { Button, Tooltip, createResource } from 'frappe-ui'
|
||||||
import { useElementVisibility } from '@vueuse/core'
|
import { useElementVisibility } from '@vueuse/core'
|
||||||
@ -785,5 +782,23 @@ function scroll(hash) {
|
|||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const callActions = computed(() => {
|
||||||
|
let actions = [
|
||||||
|
{
|
||||||
|
label: __('Create Call Log'),
|
||||||
|
onClick: () => modalRef.value.createCallLog(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Make a Call'),
|
||||||
|
onClick: () => makeCall(doc.data.mobile_no),
|
||||||
|
condition: () => callEnabled.value,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return actions.filter((action) =>
|
||||||
|
action.condition ? action.condition() : true,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
defineExpose({ emailBox, all_activities })
|
defineExpose({ emailBox, all_activities })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -26,16 +26,11 @@
|
|||||||
</template>
|
</template>
|
||||||
<span>{{ __('New Comment') }}</span>
|
<span>{{ __('New Comment') }}</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<MultiActionButton
|
||||||
v-else-if="title == 'Calls'"
|
v-else-if="title == 'Calls'"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
@click="makeCall(doc.data.mobile_no)"
|
:options="callActions"
|
||||||
>
|
/>
|
||||||
<template #prefix>
|
|
||||||
<PhoneIcon class="h-4 w-4" />
|
|
||||||
</template>
|
|
||||||
<span>{{ __('Make a Call') }}</span>
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
v-else-if="title == 'Notes'"
|
v-else-if="title == 'Notes'"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
@ -97,6 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import MultiActionButton from '@/components/MultiActionButton.vue'
|
||||||
import Email2Icon from '@/components/Icons/Email2Icon.vue'
|
import Email2Icon from '@/components/Icons/Email2Icon.vue'
|
||||||
import CommentIcon from '@/components/Icons/CommentIcon.vue'
|
import CommentIcon from '@/components/Icons/CommentIcon.vue'
|
||||||
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
||||||
@ -136,6 +132,11 @@ const defaultActions = computed(() => {
|
|||||||
label: __('New Comment'),
|
label: __('New Comment'),
|
||||||
onClick: () => (props.emailBox.showComment = true),
|
onClick: () => (props.emailBox.showComment = true),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: h(PhoneIcon, { class: 'h-4 w-4' }),
|
||||||
|
label: __('Create Call Log'),
|
||||||
|
onClick: () => props.modalRef.createCallLog(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: h(PhoneIcon, { class: 'h-4 w-4' }),
|
icon: h(PhoneIcon, { class: 'h-4 w-4' }),
|
||||||
label: __('Make a Call'),
|
label: __('Make a Call'),
|
||||||
@ -172,4 +173,24 @@ const defaultActions = computed(() => {
|
|||||||
function getTabIndex(name) {
|
function getTabIndex(name) {
|
||||||
return props.tabs.findIndex((tab) => tab.name === name)
|
return props.tabs.findIndex((tab) => tab.name === name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const callActions = computed(() => {
|
||||||
|
let actions = [
|
||||||
|
{
|
||||||
|
label: __('Create Call Log'),
|
||||||
|
icon: 'plus',
|
||||||
|
onClick: () => props.modalRef.createCallLog(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Make a Call'),
|
||||||
|
icon: h(PhoneIcon, { class: 'h-4 w-4' }),
|
||||||
|
onClick: () => makeCall(doc.data.mobile_no),
|
||||||
|
condition: () => callEnabled.value,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return actions.filter((action) =>
|
||||||
|
action.condition ? action.condition() : true,
|
||||||
|
)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -15,10 +15,16 @@
|
|||||||
:doc="doc.data?.name"
|
:doc="doc.data?.name"
|
||||||
@after="redirect('notes')"
|
@after="redirect('notes')"
|
||||||
/>
|
/>
|
||||||
|
<CallLogModal
|
||||||
|
v-model="showCallLogModal"
|
||||||
|
v-model:callLog="callLog"
|
||||||
|
:options="{ afterInsert: () => activities.reload() }"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import TaskModal from '@/components/Modals/TaskModal.vue'
|
import TaskModal from '@/components/Modals/TaskModal.vue'
|
||||||
import NoteModal from '@/components/Modals/NoteModal.vue'
|
import NoteModal from '@/components/Modals/NoteModal.vue'
|
||||||
|
import CallLogModal from '@/components/Modals/CallLogModal.vue'
|
||||||
import { call } from 'frappe-ui'
|
import { call } from 'frappe-ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
@ -77,6 +83,22 @@ function showNote(n) {
|
|||||||
showNoteModal.value = true
|
showNoteModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call Logs
|
||||||
|
const showCallLogModal = ref(false)
|
||||||
|
const callLog = ref({})
|
||||||
|
|
||||||
|
function createCallLog() {
|
||||||
|
let doctype = props.doctype
|
||||||
|
let docname = props.doc.data?.name
|
||||||
|
callLog.value = {
|
||||||
|
data: {
|
||||||
|
reference_doctype: doctype,
|
||||||
|
reference_docname: docname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
showCallLogModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
// common
|
// common
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -95,5 +117,6 @@ defineExpose({
|
|||||||
deleteTask,
|
deleteTask,
|
||||||
updateTaskStatus,
|
updateTaskStatus,
|
||||||
showNote,
|
showNote,
|
||||||
|
createCallLog,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -100,10 +100,16 @@
|
|||||||
:disabled="true"
|
:disabled="true"
|
||||||
/>
|
/>
|
||||||
<Link
|
<Link
|
||||||
v-else-if="field.fieldtype === 'Link'"
|
v-else-if="
|
||||||
|
['Link', 'Dynamic Link'].includes(field.fieldtype)
|
||||||
|
"
|
||||||
class="text-sm text-ink-gray-8"
|
class="text-sm text-ink-gray-8"
|
||||||
v-model="row[field.fieldname]"
|
v-model="row[field.fieldname]"
|
||||||
:doctype="field.options"
|
:doctype="
|
||||||
|
field.fieldtype == 'Link'
|
||||||
|
? field.options
|
||||||
|
: row[field.options]
|
||||||
|
"
|
||||||
:filters="field.filters"
|
:filters="field.filters"
|
||||||
/>
|
/>
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@ -159,6 +159,7 @@ const options = createResource({
|
|||||||
})
|
})
|
||||||
|
|
||||||
function reload(val) {
|
function reload(val) {
|
||||||
|
if (!props.doctype) return
|
||||||
if (
|
if (
|
||||||
options.data?.length &&
|
options.data?.length &&
|
||||||
val === options.params?.txt &&
|
val === options.params?.txt &&
|
||||||
|
|||||||
@ -59,11 +59,16 @@
|
|||||||
<span class="text-ink-red-3" v-if="field.mandatory">*</span>
|
<span class="text-ink-red-3" v-if="field.mandatory">*</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-1" v-else-if="field.fieldtype === 'Link'">
|
<div
|
||||||
|
class="flex gap-1"
|
||||||
|
v-else-if="['Link', 'Dynamic Link'].includes(field.fieldtype)"
|
||||||
|
>
|
||||||
<Link
|
<Link
|
||||||
class="form-control flex-1 truncate"
|
class="form-control flex-1 truncate"
|
||||||
:value="data[field.fieldname]"
|
:value="data[field.fieldname]"
|
||||||
:doctype="field.options"
|
:doctype="
|
||||||
|
field.fieldtype == 'Link' ? field.options : data[field.options]
|
||||||
|
"
|
||||||
:filters="field.filters"
|
:filters="field.filters"
|
||||||
@change="(v) => (data[field.fieldname] = v)"
|
@change="(v) => (data[field.fieldname] = v)"
|
||||||
:placeholder="getPlaceholder(field)"
|
:placeholder="getPlaceholder(field)"
|
||||||
|
|||||||
@ -1,55 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog v-model="show" :options="dialogOptions">
|
<Dialog v-model="show" :options="dialogOptions">
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
<div class="px-4 pt-5 pb-6 bg-surface-modal sm:px-6">
|
||||||
<div class="mb-5 flex items-center justify-between">
|
<div class="flex items-center justify-between mb-5">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||||
{{ __(dialogOptions.title) || __('Untitled') }}
|
{{ __(dialogOptions.title) || __('Untitled') }}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<Button
|
<Button v-if="isManager() && !isMobileView" variant="ghost" class="w-7" @click="openQuickEntryModal">
|
||||||
v-if="isManager() && !isMobileView"
|
<EditIcon class="w-4 h-4" />
|
||||||
variant="ghost"
|
|
||||||
class="w-7"
|
|
||||||
@click="openQuickEntryModal"
|
|
||||||
>
|
|
||||||
<EditIcon class="h-4 w-4" />
|
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" class="w-7" @click="show = false">
|
<Button variant="ghost" class="w-7" @click="show = false">
|
||||||
<FeatherIcon name="x" class="h-4 w-4" />
|
<FeatherIcon name="x" class="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="tabs.data">
|
<div v-if="tabs.data">
|
||||||
<FieldLayout
|
<FieldLayout :tabs="tabs.data" :data="_callLog" doctype="CRM Call Log" />
|
||||||
:tabs="tabs.data"
|
<ErrorMessage class="mt-8" :message="error" />
|
||||||
:data="_callLog"
|
|
||||||
doctype="CRM Call Log"
|
|
||||||
/>
|
|
||||||
<ErrorMessage class="mt-2" :message="error" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 pb-7 pt-4 sm:px-6">
|
<div class="px-4 pt-4 pb-7 sm:px-6">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<Button
|
<Button class="w-full" v-for="action in dialogOptions.actions" :key="action.label" v-bind="action"
|
||||||
class="w-full"
|
:label="__(action.label)" :loading="loading" />
|
||||||
v-for="action in dialogOptions.actions"
|
|
||||||
:key="action.label"
|
|
||||||
v-bind="action"
|
|
||||||
:label="__(action.label)"
|
|
||||||
:loading="loading"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<QuickEntryModal
|
<QuickEntryModal v-if="showQuickEntryModal" v-model="showQuickEntryModal" doctype="CRM Call Log" />
|
||||||
v-if="showQuickEntryModal"
|
|
||||||
v-model="showQuickEntryModal"
|
|
||||||
doctype="CRM Call Log"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -67,7 +48,7 @@ const props = defineProps({
|
|||||||
options: {
|
options: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: {
|
default: {
|
||||||
afterInsert: () => {},
|
afterInsert: () => { },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -175,6 +156,13 @@ const createCallLog = createResource({
|
|||||||
},
|
},
|
||||||
onError(err) {
|
onError(err) {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
if (err.exc_type == 'MandatoryError') {
|
||||||
|
const errorMessage = err.messages
|
||||||
|
.map(msg => msg.split('Log:')[1].trim())
|
||||||
|
.join(', ')
|
||||||
|
error.value = `These fields are required: ${errorMessage}`
|
||||||
|
return
|
||||||
|
}
|
||||||
error.value = err
|
error.value = err
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,34 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog
|
<Dialog v-model="show" :options="{
|
||||||
v-model="show"
|
size: 'xl',
|
||||||
:options="{
|
actions: [
|
||||||
size: 'xl',
|
{
|
||||||
actions: [
|
label: editMode ? __('Update') : __('Create'),
|
||||||
{
|
variant: 'solid',
|
||||||
label: editMode ? __('Update') : __('Create'),
|
onClick: () => updateNote(),
|
||||||
variant: 'solid',
|
},
|
||||||
onClick: () => updateNote(),
|
],
|
||||||
},
|
}">
|
||||||
],
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<template #body-title>
|
<template #body-title>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||||
{{ editMode ? __('Edit Note') : __('Create Note') }}
|
{{ editMode ? __('Edit Note') : __('Create Note') }}
|
||||||
</h3>
|
</h3>
|
||||||
<Button
|
<Button v-if="_note?.reference_docname" size="sm" :label="_note.reference_doctype == 'CRM Deal'
|
||||||
v-if="_note?.reference_docname"
|
? __('Open Deal')
|
||||||
size="sm"
|
: __('Open Lead')
|
||||||
:label="
|
" @click="redirect()">
|
||||||
_note.reference_doctype == 'CRM Deal'
|
|
||||||
? __('Open Deal')
|
|
||||||
: __('Open Lead')
|
|
||||||
"
|
|
||||||
@click="redirect()"
|
|
||||||
>
|
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<ArrowUpRightIcon class="h-4 w-4" />
|
<ArrowUpRightIcon class="w-4 h-4" />
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -36,27 +27,17 @@
|
|||||||
<template #body-content>
|
<template #body-content>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div>
|
<div>
|
||||||
<FormControl
|
<FormControl ref="title" :label="__('Title')" v-model="_note.title" :placeholder="__('Call with John Doe')"
|
||||||
ref="title"
|
required />
|
||||||
:label="__('Title')"
|
|
||||||
v-model="_note.title"
|
|
||||||
:placeholder="__('Call with John Doe')"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-1.5 text-xs text-ink-gray-5">{{ __('Content') }}</div>
|
<div class="mb-1.5 text-xs text-ink-gray-5">{{ __('Content') }}</div>
|
||||||
<TextEditor
|
<TextEditor variant="outline" ref="content"
|
||||||
variant="outline"
|
|
||||||
ref="content"
|
|
||||||
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
||||||
:bubbleMenu="true"
|
:bubbleMenu="true" :content="_note.content" @change="(val) => (_note.content = val)" :placeholder="__('Took a call with John Doe and discussed the new project.')
|
||||||
:content="_note.content"
|
" />
|
||||||
@change="(val) => (_note.content = val)"
|
|
||||||
:placeholder="
|
|
||||||
__('Took a call with John Doe and discussed the new project.')
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<ErrorMessage class="mt-4" v-if="error" :message="__(error)" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -94,17 +75,12 @@ const router = useRouter()
|
|||||||
|
|
||||||
const { updateOnboardingStep } = useOnboarding('frappecrm')
|
const { updateOnboardingStep } = useOnboarding('frappecrm')
|
||||||
|
|
||||||
|
const error = ref(null)
|
||||||
const title = ref(null)
|
const title = ref(null)
|
||||||
const editMode = ref(false)
|
const editMode = ref(false)
|
||||||
let _note = ref({})
|
let _note = ref({})
|
||||||
|
|
||||||
async function updateNote() {
|
async function updateNote() {
|
||||||
if (
|
|
||||||
props.note.title === _note.value.title &&
|
|
||||||
props.note.content === _note.value.content
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (_note.value.name) {
|
if (_note.value.name) {
|
||||||
let d = await call('frappe.client.set_value', {
|
let d = await call('frappe.client.set_value', {
|
||||||
doctype: 'FCRM Note',
|
doctype: 'FCRM Note',
|
||||||
@ -124,6 +100,12 @@ async function updateNote() {
|
|||||||
reference_doctype: props.doctype,
|
reference_doctype: props.doctype,
|
||||||
reference_docname: props.doc || '',
|
reference_docname: props.doc || '',
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
onError: (err) => {
|
||||||
|
if (err.error.exc_type == 'MandatoryError') {
|
||||||
|
error.value = "Title is mandatory"
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if (d.name) {
|
if (d.name) {
|
||||||
updateOnboardingStep('create_first_note')
|
updateOnboardingStep('create_first_note')
|
||||||
|
|||||||
@ -1,43 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog v-model="show" :options="{ size: 'xl' }">
|
<Dialog v-model="show" :options="{ size: 'xl' }">
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
<div class="px-4 pt-5 pb-6 bg-surface-modal sm:px-6">
|
||||||
<div class="mb-5 flex items-center justify-between">
|
<div class="flex items-center justify-between mb-5">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||||
{{ __('New Organization') }}
|
{{ __('New Organization') }}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<Button
|
<Button v-if="isManager() && !isMobileView" variant="ghost" class="w-7" @click="openQuickEntryModal">
|
||||||
v-if="isManager() && !isMobileView"
|
<EditIcon class="w-4 h-4" />
|
||||||
variant="ghost"
|
|
||||||
class="w-7"
|
|
||||||
@click="openQuickEntryModal"
|
|
||||||
>
|
|
||||||
<EditIcon class="h-4 w-4" />
|
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" class="w-7" @click="show = false">
|
<Button variant="ghost" class="w-7" @click="show = false">
|
||||||
<FeatherIcon name="x" class="h-4 w-4" />
|
<FeatherIcon name="x" class="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<FieldLayout
|
<FieldLayout v-if="tabs.data?.length" :tabs="tabs.data" :data="_organization" doctype="CRM Organization" />
|
||||||
v-if="tabs.data?.length"
|
<ErrorMessage class="mt-8" v-if="error" :message="__(error)" />
|
||||||
:tabs="tabs.data"
|
|
||||||
:data="_organization"
|
|
||||||
doctype="CRM Organization"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 pb-7 pt-4 sm:px-6">
|
<div class="px-4 pt-4 pb-7 sm:px-6">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<Button
|
<Button class="w-full" variant="solid" :label="__('Create')" :loading="loading" @click="createOrganization" />
|
||||||
class="w-full"
|
|
||||||
variant="solid"
|
|
||||||
:label="__('Create')"
|
|
||||||
:loading="loading"
|
|
||||||
@click="createOrganization"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -59,7 +44,7 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: {
|
default: {
|
||||||
redirect: true,
|
redirect: true,
|
||||||
afterInsert: () => {},
|
afterInsert: () => { },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -84,6 +69,7 @@ let _organization = ref({
|
|||||||
})
|
})
|
||||||
|
|
||||||
let doc = ref({})
|
let doc = ref({})
|
||||||
|
const error = ref(null)
|
||||||
|
|
||||||
async function createOrganization() {
|
async function createOrganization() {
|
||||||
const doc = await call('frappe.client.insert', {
|
const doc = await call('frappe.client.insert', {
|
||||||
@ -91,6 +77,12 @@ async function createOrganization() {
|
|||||||
doctype: 'CRM Organization',
|
doctype: 'CRM Organization',
|
||||||
..._organization.value,
|
..._organization.value,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
onError: (err) => {
|
||||||
|
if (err.error.exc_type == 'ValidationError') {
|
||||||
|
error.value = err.error?.messages?.[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
loading.value = false
|
loading.value = false
|
||||||
if (doc.name) {
|
if (doc.name) {
|
||||||
|
|||||||
@ -1,34 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog
|
<Dialog v-model="show" :options="{
|
||||||
v-model="show"
|
size: 'xl',
|
||||||
:options="{
|
actions: [
|
||||||
size: 'xl',
|
{
|
||||||
actions: [
|
label: editMode ? __('Update') : __('Create'),
|
||||||
{
|
variant: 'solid',
|
||||||
label: editMode ? __('Update') : __('Create'),
|
onClick: () => updateTask(),
|
||||||
variant: 'solid',
|
},
|
||||||
onClick: () => updateTask(),
|
],
|
||||||
},
|
}">
|
||||||
],
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<template #body-title>
|
<template #body-title>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||||
{{ editMode ? __('Edit Task') : __('Create Task') }}
|
{{ editMode ? __('Edit Task') : __('Create Task') }}
|
||||||
</h3>
|
</h3>
|
||||||
<Button
|
<Button v-if="task?.reference_docname" size="sm" :label="task.reference_doctype == 'CRM Deal'
|
||||||
v-if="task?.reference_docname"
|
? __('Open Deal')
|
||||||
size="sm"
|
: __('Open Lead')
|
||||||
:label="
|
" @click="redirect()">
|
||||||
task.reference_doctype == 'CRM Deal'
|
|
||||||
? __('Open Deal')
|
|
||||||
: __('Open Lead')
|
|
||||||
"
|
|
||||||
@click="redirect()"
|
|
||||||
>
|
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<ArrowUpRightIcon class="h-4 w-4" />
|
<ArrowUpRightIcon class="w-4 h-4" />
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -36,74 +27,53 @@
|
|||||||
<template #body-content>
|
<template #body-content>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div>
|
<div>
|
||||||
<FormControl
|
<FormControl ref="title" :label="__('Title')" v-model="_task.title" :placeholder="__('Call with John Doe')"
|
||||||
ref="title"
|
required />
|
||||||
:label="__('Title')"
|
|
||||||
v-model="_task.title"
|
|
||||||
:placeholder="__('Call with John Doe')"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-1.5 text-xs text-ink-gray-5">
|
<div class="mb-1.5 text-xs text-ink-gray-5">
|
||||||
{{ __('Description') }}
|
{{ __('Description') }}
|
||||||
</div>
|
</div>
|
||||||
<TextEditor
|
<TextEditor variant="outline" ref="description"
|
||||||
variant="outline"
|
|
||||||
ref="description"
|
|
||||||
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
||||||
:bubbleMenu="true"
|
:bubbleMenu="true" :content="_task.description" @change="(val) => (_task.description = val)" :placeholder="__('Took a call with John Doe and discussed the new project.')
|
||||||
:content="_task.description"
|
" />
|
||||||
@change="(val) => (_task.description = val)"
|
|
||||||
:placeholder="
|
|
||||||
__('Took a call with John Doe and discussed the new project.')
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<Dropdown :options="taskStatusOptions(updateTaskStatus)">
|
<Dropdown :options="taskStatusOptions(updateTaskStatus)">
|
||||||
<Button :label="_task.status" class="w-full justify-between">
|
<Button :label="_task.status" class="justify-between w-full">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<TaskStatusIcon :status="_task.status" />
|
<TaskStatusIcon :status="_task.status" />
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Link
|
<Link class="form-control" :value="getUser(_task.assigned_to).full_name" doctype="User"
|
||||||
class="form-control"
|
@change="(option) => (_task.assigned_to = option)" :placeholder="__('John Doe')" :hideMe="true">
|
||||||
:value="getUser(_task.assigned_to).full_name"
|
<template #prefix>
|
||||||
doctype="User"
|
<UserAvatar class="mr-2 !h-4 !w-4" :user="_task.assigned_to" />
|
||||||
@change="(option) => (_task.assigned_to = option)"
|
</template>
|
||||||
:placeholder="__('John Doe')"
|
<template #item-prefix="{ option }">
|
||||||
:hideMe="true"
|
<UserAvatar class="mr-2" :user="option.value" size="sm" />
|
||||||
>
|
</template>
|
||||||
<template #prefix>
|
<template #item-label="{ option }">
|
||||||
<UserAvatar class="mr-2 !h-4 !w-4" :user="_task.assigned_to" />
|
<Tooltip :text="option.value">
|
||||||
</template>
|
<div class="cursor-pointer text-ink-gray-9">
|
||||||
<template #item-prefix="{ option }">
|
{{ getUser(option.value).full_name }}
|
||||||
<UserAvatar class="mr-2" :user="option.value" size="sm" />
|
</div>
|
||||||
</template>
|
</Tooltip>
|
||||||
<template #item-label="{ option }">
|
</template>
|
||||||
<Tooltip :text="option.value">
|
|
||||||
<div class="cursor-pointer text-ink-gray-9">
|
|
||||||
{{ getUser(option.value).full_name }}
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</template>
|
|
||||||
</Link>
|
</Link>
|
||||||
<DateTimePicker
|
<DateTimePicker class="datepicker w-36" v-model="_task.due_date" :placeholder="__('01/04/2024 11:30 PM')"
|
||||||
class="datepicker w-36"
|
:formatter="(date) => getFormat(date, '', true, true)" input-class="border-none" />
|
||||||
v-model="_task.due_date"
|
|
||||||
:placeholder="__('01/04/2024 11:30 PM')"
|
|
||||||
:formatter="(date) => getFormat(date, '', true, true)"
|
|
||||||
input-class="border-none"
|
|
||||||
/>
|
|
||||||
<Dropdown :options="taskPriorityOptions(updateTaskPriority)">
|
<Dropdown :options="taskPriorityOptions(updateTaskPriority)">
|
||||||
<Button :label="_task.priority" class="w-full justify-between">
|
<Button :label="_task.priority" class="justify-between w-full">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<TaskPriorityIcon :priority="_task.priority" />
|
<TaskPriorityIcon :priority="_task.priority" />
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
<ErrorMessage class="mt-4" v-if="error" :message="__(error)" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -147,6 +117,7 @@ const router = useRouter()
|
|||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
const { updateOnboardingStep } = useOnboarding('frappecrm')
|
const { updateOnboardingStep } = useOnboarding('frappecrm')
|
||||||
|
|
||||||
|
const error = ref(null)
|
||||||
const title = ref(null)
|
const title = ref(null)
|
||||||
const editMode = ref(false)
|
const editMode = ref(false)
|
||||||
const _task = ref({
|
const _task = ref({
|
||||||
@ -200,6 +171,12 @@ async function updateTask() {
|
|||||||
reference_docname: props.doc || null,
|
reference_docname: props.doc || null,
|
||||||
..._task.value,
|
..._task.value,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
onError: (err) => {
|
||||||
|
if (err.error.exc_type == 'MandatoryError') {
|
||||||
|
error.value = "Title is mandatory"
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if (d.name) {
|
if (d.name) {
|
||||||
updateOnboardingStep('create_first_task')
|
updateOnboardingStep('create_first_task')
|
||||||
|
|||||||
71
frontend/src/components/MultiActionButton.vue
Normal file
71
frontend/src/components/MultiActionButton.vue
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Button
|
||||||
|
:variant="$attrs.variant"
|
||||||
|
class="border-0"
|
||||||
|
:label="activeButton.label"
|
||||||
|
:size="$attrs.size"
|
||||||
|
:class="[
|
||||||
|
$attrs.class,
|
||||||
|
showDropdown ? 'rounded-br-none rounded-tr-none' : '',
|
||||||
|
]"
|
||||||
|
@click="() => activeButton.onClick()"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<FeatherIcon
|
||||||
|
v-if="activeButton.icon && typeof activeButton.icon === 'string'"
|
||||||
|
:name="activeButton.icon"
|
||||||
|
class="h-4 w-4"
|
||||||
|
/>
|
||||||
|
<component
|
||||||
|
v-else-if="activeButton.icon"
|
||||||
|
:is="activeButton.icon"
|
||||||
|
class="h-4 w-4"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
<Dropdown
|
||||||
|
v-show="showDropdown"
|
||||||
|
:options="parsedOptions"
|
||||||
|
size="sm"
|
||||||
|
class="flex-1 [&>div>div>div]:w-full"
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
|
<template v-slot="{ togglePopover }">
|
||||||
|
<Button
|
||||||
|
:variant="$attrs.variant"
|
||||||
|
@click="togglePopover"
|
||||||
|
icon="chevron-down"
|
||||||
|
class="!w-6 justify-start rounded-bl-none rounded-tl-none border-0 pr-0 text-xs"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Dropdown } from 'frappe-ui'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const showDropdown = ref(props.options?.length > 1)
|
||||||
|
const activeButton = ref(props.options?.[0] || {})
|
||||||
|
|
||||||
|
const parsedOptions = computed(() => {
|
||||||
|
return (
|
||||||
|
props.options?.map((option) => {
|
||||||
|
return {
|
||||||
|
label: option.label,
|
||||||
|
onClick: () => {
|
||||||
|
activeButton.value = option
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}) || []
|
||||||
|
)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@ -215,10 +215,16 @@
|
|||||||
</template>
|
</template>
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
v-else-if="field.fieldtype === 'Link'"
|
v-else-if="
|
||||||
|
['Link', 'Dynamic Link'].includes(field.fieldtype)
|
||||||
|
"
|
||||||
class="form-control select-text"
|
class="form-control select-text"
|
||||||
:value="data[field.fieldname]"
|
:value="data[field.fieldname]"
|
||||||
:doctype="field.options"
|
:doctype="
|
||||||
|
field.fieldtype == 'Link'
|
||||||
|
? field.options
|
||||||
|
: data[field.options]
|
||||||
|
"
|
||||||
:filters="field.filters"
|
:filters="field.filters"
|
||||||
:placeholder="field.placeholder"
|
:placeholder="field.placeholder"
|
||||||
@change="
|
@change="
|
||||||
|
|||||||
@ -545,7 +545,6 @@ const tabs = computed(() => {
|
|||||||
name: 'Calls',
|
name: 'Calls',
|
||||||
label: __('Calls'),
|
label: __('Calls'),
|
||||||
icon: PhoneIcon,
|
icon: PhoneIcon,
|
||||||
condition: () => callEnabled.value,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Tasks',
|
name: 'Tasks',
|
||||||
|
|||||||
@ -457,9 +457,6 @@ function parseRows(rows, columns = []) {
|
|||||||
}
|
}
|
||||||
} else if (row == '_assign') {
|
} else if (row == '_assign') {
|
||||||
let assignees = JSON.parse(deal._assign || '[]')
|
let assignees = JSON.parse(deal._assign || '[]')
|
||||||
if (!assignees.length && deal.deal_owner) {
|
|
||||||
assignees = [deal.deal_owner]
|
|
||||||
}
|
|
||||||
_rows[row] = assignees.map((user) => ({
|
_rows[row] = assignees.map((user) => ({
|
||||||
name: user,
|
name: user,
|
||||||
image: getUser(user).user_image,
|
image: getUser(user).user_image,
|
||||||
|
|||||||
@ -532,7 +532,6 @@ const tabs = computed(() => {
|
|||||||
name: 'Calls',
|
name: 'Calls',
|
||||||
label: __('Calls'),
|
label: __('Calls'),
|
||||||
icon: PhoneIcon,
|
icon: PhoneIcon,
|
||||||
condition: () => callEnabled.value,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Tasks',
|
name: 'Tasks',
|
||||||
|
|||||||
@ -480,9 +480,6 @@ function parseRows(rows, columns = []) {
|
|||||||
}
|
}
|
||||||
} else if (row == '_assign') {
|
} else if (row == '_assign') {
|
||||||
let assignees = JSON.parse(lead._assign || '[]')
|
let assignees = JSON.parse(lead._assign || '[]')
|
||||||
if (!assignees.length && lead.lead_owner) {
|
|
||||||
assignees = [lead.lead_owner]
|
|
||||||
}
|
|
||||||
_rows[row] = assignees.map((user) => ({
|
_rows[row] = assignees.map((user) => ({
|
||||||
name: user,
|
name: user,
|
||||||
image: getUser(user).user_image,
|
image: getUser(user).user_image,
|
||||||
|
|||||||
@ -2542,10 +2542,10 @@ 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.123:
|
||||||
version "0.1.121"
|
version "0.1.123"
|
||||||
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.123.tgz#501139a103a03e52648d9ee9ea85aa54bc8102e0"
|
||||||
integrity sha512-gvtKKZECPD2MU5X4MwPUKr2hSOs1+s1DA9laP3aPnmH0ukJRSFEhDOyjCMfH9k6ZdAe/vZCIbT4XucxLq/fOEA==
|
integrity sha512-WkTnKZ+n82d9xZ9g9ZQXVkFyKU2wlcfT6/9g8/2biJuXMwmo/80I29EKGb9nrM1Liuj0Wtyg9nsqvfvgktdHbw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@headlessui/vue" "^1.7.14"
|
"@headlessui/vue" "^1.7.14"
|
||||||
"@popperjs/core" "^2.11.2"
|
"@popperjs/core" "^2.11.2"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user