From 196eb89154b1f1e465284990410dbf76a20cd889 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 22 Jan 2024 22:37:31 +0530 Subject: [PATCH 1/2] fix: added is_view check to differentiate between standard view --- crm/api/doc.py | 2 +- crm/api/views.py | 4 +++- .../doctype/crm_view_settings/crm_view_settings.json | 9 ++++++++- crm/fcrm/doctype/crm_view_settings/crm_view_settings.py | 2 ++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/crm/api/doc.py b/crm/api/doc.py index 05d5623e..346b563e 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -212,7 +212,7 @@ def get_list_data( "page_length": page_length, "page_length_count": page_length_count, "is_default": is_default, - "views": get_views(doctype), + "views": get_views(doctype, is_view=True), "total_count": frappe.client.get_count(doctype, filters=filters), "row_count": len(data), } diff --git a/crm/api/views.py b/crm/api/views.py index 6a95adbf..ac69b46a 100644 --- a/crm/api/views.py +++ b/crm/api/views.py @@ -3,7 +3,7 @@ from pypika import Criterion @frappe.whitelist() -def get_views(doctype): +def get_views(doctype, is_view=False): if frappe.session.user == "Guest": frappe.throw("Authentication failed", exc=frappe.AuthenticationError) @@ -13,6 +13,8 @@ def get_views(doctype): .select("*") .where(Criterion.any([View.user == '', View.user == frappe.session.user])) ) + if is_view: + query = query.where(View.is_view == True) if doctype: query = query.where(View.dt == doctype) views = query.run(as_dict=True) diff --git a/crm/fcrm/doctype/crm_view_settings/crm_view_settings.json b/crm/fcrm/doctype/crm_view_settings/crm_view_settings.json index d4574d55..a1cd5875 100644 --- a/crm/fcrm/doctype/crm_view_settings/crm_view_settings.json +++ b/crm/fcrm/doctype/crm_view_settings/crm_view_settings.json @@ -12,6 +12,7 @@ "route_name", "pinned", "public", + "is_view", "columns_tab", "load_default_columns", "columns", @@ -104,11 +105,17 @@ "fieldname": "public", "fieldtype": "Check", "label": "Public" + }, + { + "default": "0", + "fieldname": "is_view", + "fieldtype": "Check", + "label": "Is View" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-01-19 21:44:52.285420", + "modified": "2024-01-22 21:44:41.733977", "modified_by": "Administrator", "module": "FCRM", "name": "CRM View Settings", 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 a7eb8595..82fff89c 100644 --- a/crm/fcrm/doctype/crm_view_settings/crm_view_settings.py +++ b/crm/fcrm/doctype/crm_view_settings/crm_view_settings.py @@ -24,6 +24,7 @@ def create(view): doc = frappe.new_doc("CRM View Settings") doc.name = view.label doc.label = view.label + doc.is_view = True doc.dt = view.doctype doc.user = frappe.session.user doc.route_name = view.route_name or "" @@ -49,6 +50,7 @@ def update(view): doc = frappe.get_doc("CRM View Settings", view.name) doc.label = view.label + doc.is_view = True doc.route_name = view.route_name or "" doc.load_default_columns = view.load_default_columns or False doc.filters = json.dumps(filters) From c73da41a7871f6afd272a514b6db1c2f824eda01 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 22 Jan 2024 22:39:04 +0530 Subject: [PATCH 2/2] feat: set default view --- .../crm_view_settings/crm_view_settings.json | 15 ++++++ .../crm_view_settings/crm_view_settings.py | 54 +++++++++++++++++++ frontend/src/components/ViewControls.vue | 32 ++++++++++- frontend/src/router.js | 17 +++++- frontend/src/stores/views.js | 20 ++++++- 5 files changed, 134 insertions(+), 4 deletions(-) diff --git a/crm/fcrm/doctype/crm_view_settings/crm_view_settings.json b/crm/fcrm/doctype/crm_view_settings/crm_view_settings.json index a1cd5875..214d9d0d 100644 --- a/crm/fcrm/doctype/crm_view_settings/crm_view_settings.json +++ b/crm/fcrm/doctype/crm_view_settings/crm_view_settings.json @@ -7,11 +7,13 @@ "field_order": [ "label", "user", + "user_list", "column_break_zacm", "dt", "route_name", "pinned", "public", + "default", "is_view", "columns_tab", "load_default_columns", @@ -29,6 +31,7 @@ "label": "Columns" }, { + "depends_on": "eval: doc.public == false", "fieldname": "user", "fieldtype": "Link", "label": "User", @@ -106,11 +109,23 @@ "fieldtype": "Check", "label": "Public" }, + { + "default": "0", + "fieldname": "default", + "fieldtype": "Check", + "label": "Default" + }, { "default": "0", "fieldname": "is_view", "fieldtype": "Check", "label": "Is View" + }, + { + "depends_on": "eval: doc.public == true", + "fieldname": "user_list", + "fieldtype": "Text", + "label": "User List" } ], "index_web_pages_for_search": 1, 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 82fff89c..9331bcc4 100644 --- a/crm/fcrm/doctype/crm_view_settings/crm_view_settings.py +++ b/crm/fcrm/doctype/crm_view_settings/crm_view_settings.py @@ -81,6 +81,60 @@ def pin(name, value): doc.pinned = value doc.save() +@frappe.whitelist() +def make_default(name, doctype, route_name): + existing_default_view = frappe.db.exists("CRM View Settings", { + "default": True, + "user": frappe.session.user + }) + if existing_default_view: + frappe.db.set_value("CRM View Settings", existing_default_view, "default", False) + else: + public_default = frappe.db.exists("CRM View Settings", { + "default": True, + "public": True + }) + if public_default: + public_default_view = frappe.get_doc("CRM View Settings", public_default) + user_list = json.loads(public_default_view.user_list) + if frappe.session.user in user_list: + updated_user_list = [user for user in user_list if user != frappe.session.user] + if not updated_user_list: + public_default_view.default = False + public_default_view.user_list = None + else: + public_default_view.user_list = json.dumps(updated_user_list) + public_default_view.save() + + if not name and doctype: + exists = frappe.db.exists("CRM View Settings", { + "dt": doctype, + "is_view": False, + "user": frappe.session.user + }) + if not exists: + doc = frappe.new_doc("CRM View Settings") + doc.label = "List View" + doc.default = True + doc.dt = doctype + doc.route_name = route_name + doc.user = frappe.session.user + doc.is_view = False + doc.insert() + else: + frappe.db.set_value("CRM View Settings", exists, "default", True) + elif frappe.db.get_value("CRM View Settings", name, "public"): + doc = frappe.get_doc("CRM View Settings", name) + doc.default = True + doc.user_list = ( + json.dumps([frappe.session.user]) + if not doc.user_list + else json.dumps(json.loads(doc.user_list) + [frappe.session.user]) + ) + doc.save() + else: + frappe.db.set_value("CRM View Settings", name, "default", True) + def remove_duplicates(l): return list(dict.fromkeys(l)) diff --git a/frontend/src/components/ViewControls.vue b/frontend/src/components/ViewControls.vue index 5083aaf0..7fa2a0bc 100644 --- a/frontend/src/components/ViewControls.vue +++ b/frontend/src/components/ViewControls.vue @@ -77,6 +77,7 @@ import ViewModal from '@/components/Modals/ViewModal.vue' import SortBy from '@/components/SortBy.vue' import Filter from '@/components/Filter.vue' import ColumnSettings from '@/components/ColumnSettings.vue' +import { createToast } from '@/utils' import { globalStore } from '@/stores/global' import { viewsStore } from '@/stores/views' import { usersStore } from '@/stores/users' @@ -97,7 +98,7 @@ const props = defineProps({ }) const { $dialog } = globalStore() -const { reload: reloadView, getView } = viewsStore() +const { reload: reloadView, getView, getDefaultView } = viewsStore() const { isManager } = usersStore() const list = defineModel() @@ -353,6 +354,20 @@ const viewActions = computed(() => { }, ] + let defaultView = getDefaultView() + if ( + !defaultView || + (route.query.view && route.query.view != defaultView.name) || + (!route.query.view && + (defaultView.route_name != route.name || defaultView.is_view)) + ) { + actions[0].items.push({ + label: 'Make Default', + icon: () => h(FeatherIcon, { name: 'star', class: 'h-4 w-4' }), + onClick: () => makeDefault(), + }) + } + if (route.query.view && (!view.value.public || isManager())) { actions[0].items.push( { @@ -408,6 +423,21 @@ const viewActions = computed(() => { return actions }) +function makeDefault() { + call('crm.fcrm.doctype.crm_view_settings.crm_view_settings.make_default', { + name: route.query.view || '', + doctype: props.doctype, + route_name: route.name, + }).then(() => { + createToast({ + title: 'Default View Set', + icon: 'check', + iconClasses: 'text-green-600', + }) + reloadView() + }) +} + function duplicateView() { view.value.name = '' view.value.label = getView(route.query.view).label + ' New' diff --git a/frontend/src/router.js b/frontend/src/router.js index b90a00f9..0b43986d 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -1,11 +1,12 @@ import { createRouter, createWebHistory } from 'vue-router' import { usersStore } from '@/stores/users' import { sessionStore } from '@/stores/session' +import { viewsStore } from '@/stores/views' const routes = [ { path: '/', - redirect: '/leads', + name: 'Home', }, { path: '/leads', @@ -92,10 +93,22 @@ let router = createRouter({ router.beforeEach(async (to, from, next) => { const { users } = usersStore() const { isLoggedIn } = sessionStore() + const { getDefaultView } = viewsStore() await users.promise - if (to.name === 'Login' && isLoggedIn) { + if (to.path === '/') { + const defaultView = getDefaultView() + if (defaultView?.route_name) { + if (defaultView.is_view) { + next({ name: defaultView.route_name, query: { view: defaultView.name } }) + } else { + next({ name: defaultView.route_name }) + } + } else { + next({ name: 'Leads' }) + } + } else if (to.name === 'Login' && isLoggedIn) { next({ name: 'Leads' }) } else if (to.name !== 'Login' && !isLoggedIn) { next({ name: 'Login' }) diff --git a/frontend/src/stores/views.js b/frontend/src/stores/views.js index 7db007f9..062be8ff 100644 --- a/frontend/src/stores/views.js +++ b/frontend/src/stores/views.js @@ -1,4 +1,5 @@ import { defineStore } from 'pinia' +import { sessionStore } from '@/stores/session' import { createResource } from 'frappe-ui' import { reactive, ref } from 'vue' @@ -6,12 +7,15 @@ export const viewsStore = defineStore('crm-views', (doctype) => { let viewsByName = reactive({}) let pinnedViews = ref([]) let publicViews = ref([]) + let defaultView = ref(null) + + const { user } = sessionStore() // Views const views = createResource({ url: 'crm.api.views.get_views', params: { doctype: doctype || '' }, - cache: "crm-views", + cache: 'crm-views', initialData: [], auto: true, transform(views) { @@ -25,6 +29,15 @@ export const viewsStore = defineStore('crm-views', (doctype) => { if (view.public) { publicViews.value?.push(view) } + + if ( + (!view.public && view.default) || + (view.public && + view.default && + JSON.parse(view.user_list).includes(user)) + ) { + defaultView.value = view + } } return views }, @@ -48,6 +61,10 @@ export const viewsStore = defineStore('crm-views', (doctype) => { return publicViews.value } + function getDefaultView() { + return defaultView.value + } + async function reload() { await views.reload() } @@ -55,6 +72,7 @@ export const viewsStore = defineStore('crm-views', (doctype) => { return { getPinnedViews, getPublicViews, + getDefaultView, reload, getView, }