1
0
forked from test/crm

Merge pull request #595 from shariquerik/default-view

feat: Default view
This commit is contained in:
Shariq Ansari 2025-02-19 11:53:07 +05:30 committed by GitHub
commit 7101fadd36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 156 additions and 31 deletions

View File

@ -8,9 +8,14 @@ def get_views(doctype):
query = (
frappe.qb.from_(View)
.select("*")
.where(Criterion.any([View.user == '', View.user == frappe.session.user]))
.where(Criterion.any([View.user == "", View.user == frappe.session.user]))
)
if doctype:
query = query.where(View.dt == doctype)
views = query.run(as_dict=True)
return views
return views
@frappe.whitelist()
def get_default_view():
return frappe.db.get_single_value("FCRM Settings", "default_view") or None

View File

@ -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()
doc.insert()

View File

@ -6,6 +6,8 @@
"engine": "InnoDB",
"field_order": [
"defaults_tab",
"default_view",
"column_break_jeeh",
"restore_defaults",
"branding_tab",
"brand_name",
@ -56,12 +58,21 @@
"fieldname": "favicon",
"fieldtype": "Attach",
"label": "Favicon"
},
{
"fieldname": "default_view",
"fieldtype": "Data",
"label": "Default View"
},
{
"fieldname": "column_break_jeeh",
"fieldtype": "Column Break"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-01-19 14:23:05.981355",
"modified": "2025-02-18 17:05:39.440396",
"modified_by": "Administrator",
"module": "FCRM",
"name": "FCRM Settings",

View File

@ -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

View File

@ -1,11 +1,11 @@
import { createRouter, createWebHistory } from 'vue-router'
import { userResource } from '@/stores/user'
import { sessionStore } from '@/stores/session'
import { viewsStore } from '@/stores/views'
const routes = [
{
path: '/',
redirect: { name: 'Leads' },
name: 'Home',
},
{
@ -113,7 +113,17 @@ router.beforeEach(async (to, from, next) => {
isLoggedIn && (await userResource.promise)
if (to.name === 'Home' && isLoggedIn) {
next({ name: 'Leads' })
const { getDefaultView, defaultView } = viewsStore()
await defaultView.promise
let { name, type, view } = getDefaultView(true)
name = name || 'Leads'
if (view) {
next({ name, params: { viewType: type }, query: { view } })
} else {
next({ name, params: { viewType: type } })
}
} else if (!isLoggedIn) {
window.location.href = '/login?redirect-to=/crm'
} else if (to.matched.length === 0) {

View File

@ -6,7 +6,14 @@ export const viewsStore = defineStore('crm-views', (doctype) => {
let viewsByName = reactive({})
let pinnedViews = ref([])
let publicViews = ref([])
let defaultView = ref({})
let standardViews = ref({})
// Default view
const defaultView = createResource({
url: 'crm.api.views.get_default_view',
cache: 'crm-default-view',
auto: true,
})
// Views
const views = createResource({
@ -28,17 +35,53 @@ export const viewsStore = defineStore('crm-views', (doctype) => {
publicViews.value?.push(view)
}
if (view.is_default && view.dt) {
defaultView.value[view.dt + ' ' + view.type] = view
standardViews.value[view.dt + ' ' + view.type] = view
}
}
return views
},
})
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) {
return defaultView.value[doctype + ' ' + type] || null
return standardViews.value[doctype + ' ' + type] || null
}
return viewsByName[view]
}
@ -60,6 +103,8 @@ export const viewsStore = defineStore('crm-views', (doctype) => {
return {
views,
defaultView,
standardViews,
getDefaultView,
getPinnedViews,
getPublicViews,
reload,