diff --git a/crm/fcrm/doctype/crm_view_settings/crm_view_settings.py b/crm/fcrm/doctype/crm_view_settings/crm_view_settings.py index e0cf53d5..13d08686 100644 --- a/crm/fcrm/doctype/crm_view_settings/crm_view_settings.py +++ b/crm/fcrm/doctype/crm_view_settings/crm_view_settings.py @@ -1,6 +1,7 @@ # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt import json + import frappe from frappe.model.document import Document, get_controller from frappe.utils import parse_json @@ -9,15 +10,16 @@ from frappe.utils import parse_json class CRMViewSettings(Document): pass + @frappe.whitelist() def create(view): view = frappe._dict(view) view.filters = parse_json(view.filters) or {} - view.columns = parse_json(view.columns or '[]') - view.rows = parse_json(view.rows or '[]') - view.kanban_columns = parse_json(view.kanban_columns or '[]') - view.kanban_fields = parse_json(view.kanban_fields or '[]') + view.columns = parse_json(view.columns or "[]") + view.rows = parse_json(view.rows or "[]") + view.kanban_columns = parse_json(view.kanban_columns or "[]") + view.kanban_fields = parse_json(view.kanban_fields or "[]") default_rows = sync_default_rows(view.doctype) view.rows = view.rows + default_rows if default_rows else view.rows @@ -31,7 +33,7 @@ def create(view): doc = frappe.new_doc("CRM View Settings") doc.name = view.label doc.label = view.label - doc.type = view.type or 'list' + doc.type = view.type or "list" doc.icon = view.icon doc.dt = view.doctype doc.user = frappe.session.user @@ -49,6 +51,7 @@ def create(view): doc.insert() return doc + @frappe.whitelist() def update(view): view = frappe._dict(view) @@ -65,7 +68,7 @@ def update(view): doc = frappe.get_doc("CRM View Settings", view.name) doc.label = view.label - doc.type = view.type or 'list' + doc.type = view.type or "list" doc.icon = view.icon doc.route_name = view.route_name or "" doc.load_default_columns = view.load_default_columns or False @@ -81,11 +84,13 @@ def update(view): doc.save() return doc + @frappe.whitelist() def delete(name): if frappe.db.exists("CRM View Settings", name): frappe.delete_doc("CRM View Settings", name) + @frappe.whitelist() def public(name, value): if frappe.session.user != "Administrator" and "Sales Manager" not in frappe.get_roles(): @@ -98,15 +103,18 @@ def public(name, value): doc.user = "" if value else frappe.session.user doc.save() + @frappe.whitelist() def pin(name, value): doc = frappe.get_doc("CRM View Settings", name) doc.pinned = value doc.save() + def remove_duplicates(l): return list(dict.fromkeys(l)) + def sync_default_rows(doctype, type="list"): list = get_controller(doctype) rows = [] @@ -116,6 +124,7 @@ def sync_default_rows(doctype, type="list"): return rows + def sync_default_columns(view): list = get_controller(view.doctype) columns = [] @@ -136,15 +145,22 @@ def sync_default_columns(view): return columns +@frappe.whitelist() +def set_as_default(name=None, type=None, doctype=None): + if not name: + name = type + "_" + doctype + frappe.db.set_single_value("FCRM Settings", "default_view", name) + + @frappe.whitelist() def create_or_update_default_view(view): view = frappe._dict(view) filters = parse_json(view.filters) or {} - columns = parse_json(view.columns or '[]') - rows = parse_json(view.rows or '[]') - kanban_columns = parse_json(view.kanban_columns or '[]') - kanban_fields = parse_json(view.kanban_fields or '[]') + columns = parse_json(view.columns or "[]") + rows = parse_json(view.rows or "[]") + kanban_columns = parse_json(view.kanban_columns or "[]") + kanban_fields = parse_json(view.kanban_fields or "[]") default_rows = sync_default_rows(view.doctype, view.type) rows = rows + default_rows if default_rows else rows @@ -157,17 +173,12 @@ def create_or_update_default_view(view): doc = frappe.db.exists( "CRM View Settings", - { - "dt": view.doctype, - "type": view.type or 'list', - "is_default": True, - "user": frappe.session.user - }, + {"dt": view.doctype, "type": view.type or "list", "is_default": True, "user": frappe.session.user}, ) if doc: doc = frappe.get_doc("CRM View Settings", doc) doc.label = view.label - doc.type = view.type or 'list' + doc.type = view.type or "list" doc.route_name = view.route_name or "" doc.load_default_columns = view.load_default_columns or False doc.filters = json.dumps(filters) @@ -182,10 +193,10 @@ def create_or_update_default_view(view): doc.save() else: doc = frappe.new_doc("CRM View Settings") - label = 'Group By View' if view.type == 'group_by' else 'List View' + label = "Group By View" if view.type == "group_by" else "List View" doc.name = view.label or label doc.label = view.label or label - doc.type = view.type or 'list' + doc.type = view.type or "list" doc.dt = view.doctype doc.user = frappe.session.user doc.route_name = view.route_name or "" @@ -200,4 +211,4 @@ def create_or_update_default_view(view): doc.columns = json.dumps(columns) doc.rows = json.dumps(rows) doc.is_default = True - doc.insert() \ No newline at end of file + doc.insert() diff --git a/frontend/src/components/ViewControls.vue b/frontend/src/components/ViewControls.vue index 12f021ca..56a84669 100644 --- a/frontend/src/components/ViewControls.vue +++ b/frontend/src/components/ViewControls.vue @@ -215,6 +215,7 @@ import QuickFilterField from '@/components/QuickFilterField.vue' import RefreshIcon from '@/components/Icons/RefreshIcon.vue' import EditIcon from '@/components/Icons/EditIcon.vue' import DuplicateIcon from '@/components/Icons/DuplicateIcon.vue' +import CheckIcon from '@/components/Icons/CheckIcon.vue' import PinIcon from '@/components/Icons/PinIcon.vue' import UnpinIcon from '@/components/Icons/UnpinIcon.vue' import ViewModal from '@/components/Modals/ViewModal.vue' @@ -263,7 +264,7 @@ const props = defineProps({ const { brand } = getSettings() const { $dialog } = globalStore() -const { reload: reloadView, getView } = viewsStore() +const { reload: reloadView, getDefaultView, getView } = viewsStore() const { isManager } = usersStore() const list = defineModel() @@ -887,9 +888,20 @@ function updatePageLength(value, loadMore = false) { // View Actions const viewActions = (view) => { - let isDefault = typeof view.name === 'string' + let isStandard = typeof view.name === 'string' let _view = getView(view.name) + if (isStandard) { + _view = getView(null, view.name, props.doctype) + } + + if (!_view) { + _view = { + type: view.name, + dt: props.doctype, + } + } + let actions = [ { group: __('Default Views'), @@ -904,7 +916,15 @@ const viewActions = (view) => { }, ] - if (!isDefault && (!_view.public || isManager())) { + if (!isStandardView(_view, isStandard)) { + actions[0].items.unshift({ + label: __('Set as default'), + icon: () => h(CheckIcon, { class: 'h-4 w-4' }), + onClick: () => setAsDefault(_view), + }) + } + + if (!isStandard && (!_view.public || isManager())) { actions[0].items.push({ label: __('Edit'), icon: () => h(EditIcon, { class: 'h-4 w-4' }), @@ -961,6 +981,18 @@ const viewActions = (view) => { return actions } +function isStandardView(v, isStandard) { + let defaultView = getDefaultView() + + if (!defaultView) return false + + if (isStandard && !v.name) { + return defaultView == v.type + '_' + v.dt + } + + return defaultView == v.name +} + const viewModalObj = ref({}) function createView() { @@ -972,6 +1004,17 @@ function createView() { showViewModal.value = true } +function setAsDefault(v) { + call('crm.fcrm.doctype.crm_view_settings.crm_view_settings.set_as_default', { + name: v.name, + type: v.type, + doctype: v.dt, + }).then(() => { + reloadView() + list.value.reload() + }) +} + function duplicateView(v) { v.label = v.label + __(' (New)') viewModalObj.value = v diff --git a/frontend/src/stores/views.js b/frontend/src/stores/views.js index 3cded530..011a9e38 100644 --- a/frontend/src/stores/views.js +++ b/frontend/src/stores/views.js @@ -42,6 +42,42 @@ export const viewsStore = defineStore('crm-views', (doctype) => { }, }) + function getDefaultView(routeName = false) { + let view = defaultView.data + if (!view) return null + + if (typeof view === 'string' && !isNaN(view)) { + view = parseInt(view) + } + + if (routeName) { + let viewObj = getView(view) || { + type: view.split('_')[0], + dt: view.split('_')[1], + } + + let routeName = viewObj.dt + + if (routeName.startsWith('CRM ')) { + routeName = routeName.slice(4) + } + + if (!routeName.endsWith('s')) { + routeName += 's' + } + + let viewName = viewObj.is_default ? null : viewObj.name + + return { + name: routeName, + type: viewObj.type, + view: viewName, + } + } + + return view + } + function getView(view, type, doctype = null) { type = type || 'list' if (!view && doctype) { @@ -68,6 +104,7 @@ export const viewsStore = defineStore('crm-views', (doctype) => { views, defaultView, standardViews, + getDefaultView, getPinnedViews, getPublicViews, reload,