Merge pull request #57 from shariquerik/public-views

fix: Public Views
This commit is contained in:
Shariq Ansari 2024-01-19 22:05:07 +05:30 committed by GitHub
commit c0004f638b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 353 additions and 74 deletions

View File

@ -3,6 +3,8 @@ from frappe.model.document import get_controller
from frappe.model import no_value_fields
from pypika import Criterion
from crm.api.views import get_views
@frappe.whitelist()
def sort_options(doctype: str):
@ -164,14 +166,6 @@ def get_list_data(
"row_count": len(data),
}
def get_views(doctype):
views = frappe.get_all(
"CRM View Settings",
fields=["*"],
filters={"dt": doctype, "user": frappe.session.user}
)
return views
def get_doctype_fields(doctype):
not_allowed_fieldtypes = [

View File

@ -16,6 +16,10 @@ def get_users():
for user in users:
if frappe.session.user == user.name:
user.session_user = True
user.is_manager = (
"Sales Manager" in frappe.get_roles(user.name) or user.name == "Administrator"
)
return users
@frappe.whitelist()

19
crm/api/views.py Normal file
View File

@ -0,0 +1,19 @@
import frappe
from pypika import Criterion
@frappe.whitelist()
def get_views(doctype):
if frappe.session.user == "Guest":
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
View = frappe.qb.DocType("CRM View Settings")
query = (
frappe.qb.from_(View)
.select("*")
.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

View File

@ -127,7 +127,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-01-18 13:27:19.776926",
"modified": "2024-01-19 21:51:52.173476",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Call Log",
@ -142,7 +142,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -20,7 +20,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-12-13 13:28:38.746199",
"modified": "2024-01-19 21:55:17.952032",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Communication Status",
@ -35,7 +35,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -275,7 +275,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-01-19 17:13:41.931398",
"modified": "2024-01-19 21:43:58.940722",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Deal",
@ -290,7 +290,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -36,7 +36,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-11-29 12:52:03.070218",
"modified": "2024-01-19 21:56:44.552134",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Deal Status",
@ -51,7 +51,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -45,7 +45,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-12-29 12:14:45.706725",
"modified": "2024-01-19 21:52:46.078626",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Form Script",
@ -60,7 +60,7 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -85,7 +85,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-12-14 11:18:27.236817",
"modified": "2024-01-19 21:54:54.809445",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Holiday List",
@ -100,7 +100,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -21,7 +21,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-07-24 19:40:31.980882",
"modified": "2024-01-19 21:57:02.025918",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Industry",
@ -36,7 +36,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -293,7 +293,7 @@
"image_field": "image",
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-01-06 18:44:03.946528",
"modified": "2024-01-19 21:52:28.923346",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Lead",
@ -308,7 +308,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -29,7 +29,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-11-23 13:11:40.743731",
"modified": "2024-01-19 21:56:04.702254",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Lead Source",
@ -44,7 +44,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -37,7 +37,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-11-29 12:52:25.641581",
"modified": "2024-01-19 21:56:16.872924",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Lead Status",
@ -52,7 +52,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -49,7 +49,7 @@
"link_fieldname": "note"
}
],
"modified": "2023-11-23 13:11:39.812793",
"modified": "2024-01-19 21:56:30.123334",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Note",
@ -63,7 +63,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -65,7 +65,7 @@
"image_field": "organization_logo",
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-01-04 18:57:52.519305",
"modified": "2024-01-19 21:53:14.945857",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Organization",
@ -80,7 +80,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -115,7 +115,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-12-15 11:50:29.956775",
"modified": "2024-01-19 21:54:25.831753",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Service Level Agreement",
@ -130,7 +130,7 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -87,7 +87,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-11-23 13:11:39.233502",
"modified": "2024-01-19 21:55:42.654547",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Task",
@ -102,7 +102,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -13,10 +13,10 @@
"section_break_qhaf",
"old_parent",
"parent_crm_territory",
"is_group",
"column_break_pypy",
"lft",
"rgt"
"rgt",
"is_group"
],
"fields": [
{
@ -90,7 +90,7 @@
"index_web_pages_for_search": 1,
"is_tree": 1,
"links": [],
"modified": "2024-01-05 12:26:25.485080",
"modified": "2024-01-19 21:53:53.451891",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Territory",
@ -106,7 +106,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -11,6 +11,7 @@
"dt",
"route_name",
"pinned",
"public",
"columns_tab",
"load_default_columns",
"columns",
@ -97,11 +98,17 @@
"fieldname": "load_default_columns",
"fieldtype": "Check",
"label": "Load Default Columns"
},
{
"default": "0",
"fieldname": "public",
"fieldtype": "Check",
"label": "Public"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-01-03 16:54:52.195687",
"modified": "2024-01-19 21:44:52.285420",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM View Settings",
@ -116,7 +123,19 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -63,6 +63,16 @@ 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 "Sales Manager" not in frappe.get_roles() or frappe.session.user != "Administrator":
frappe.throw("Not permitted", frappe.PermissionError)
doc = frappe.get_doc("CRM View Settings", name)
doc.public = 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)

View File

@ -51,7 +51,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-08-17 22:21:00.606384",
"modified": "2024-01-19 21:57:18.626669",
"modified_by": "Administrator",
"module": "FCRM",
"name": "Twilio Agents",
@ -66,7 +66,7 @@
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Sales Manager",
"share": 1,
"write": 1
}

View File

@ -15,25 +15,54 @@
class="mx-2 my-0.5"
/>
</div>
<div
v-if="isSidebarCollapsed && getPublicViews().length"
class="mx-2 my-2 h-1 border-b"
/>
<div
v-if="getPublicViews().length"
class="px-3 text-base text-gray-600 transition-all duration-300 ease-in-out"
:class="
isSidebarCollapsed
? 'ml-0 h-0 overflow-hidden opacity-0'
: 'ml-2 h-7 w-auto opacity-100 mt-4'
"
>
Public Views
</div>
<div v-if="getPublicViews().length" class="flex flex-col overflow-y-auto">
<SidebarLink
v-for="publicView in getPublicViews()"
:icon="
h(getIcon(publicView.route_name), {
class: 'h-4.5 w-4.5 text-gray-700',
})
"
:label="publicView.label"
:to="{
name: publicView.route_name,
query: { view: publicView.name },
}"
:isCollapsed="isSidebarCollapsed"
class="mx-2 my-0.5"
/>
</div>
<div
v-if="isSidebarCollapsed && getPinnedViews().length"
class="mx-2 my-2 h-1 border-b"
/>
<div
v-if="getPinnedViews().length"
class="flex flex-col overflow-y-auto"
:class="isSidebarCollapsed ? 'mt-0' : 'mt-4'"
class="px-3 text-base text-gray-600 transition-all duration-300 ease-in-out"
:class="
isSidebarCollapsed
? 'ml-0 h-0 overflow-hidden opacity-0'
: 'ml-2 h-7 w-auto opacity-100 mt-4'
"
>
<div
class="h-7 px-3 text-base text-gray-600 transition-all duration-300 ease-in-out"
:class="
isSidebarCollapsed
? 'ml-0 h-0 overflow-hidden opacity-0'
: 'ml-2 w-auto opacity-100'
"
>
Pinned Views
</div>
Pinned Views
</div>
<div v-if="getPinnedViews().length" class="flex flex-col overflow-y-auto">
<SidebarLink
v-for="pinnedView in getPinnedViews()"
:icon="
@ -84,7 +113,7 @@ import { viewsStore } from '@/stores/views'
import { useStorage } from '@vueuse/core'
import { h } from 'vue'
const { getPinnedViews } = viewsStore()
const { getPinnedViews, getPublicViews } = viewsStore()
const links = [
{

View File

@ -18,7 +18,10 @@
</Dropdown>
</div>
<div class="flex items-center gap-2">
<div v-if="viewUpdated" class="flex items-center gap-2 border-r pr-2">
<div
v-if="viewUpdated && (!view.public || isManager())"
class="flex items-center gap-2 border-r pr-2"
>
<Button label="Cancel" @click="cancelChanges" />
<Button
:label="view?.name ? 'Save Changes' : 'Create View'"
@ -76,8 +79,9 @@ import Filter from '@/components/Filter.vue'
import ColumnSettings from '@/components/ColumnSettings.vue'
import { globalStore } from '@/stores/global'
import { viewsStore } from '@/stores/views'
import { usersStore } from '@/stores/users'
import { useDebounceFn } from '@vueuse/core'
import { createResource, Dropdown, call } from 'frappe-ui'
import { createResource, Dropdown, call, FeatherIcon } from 'frappe-ui'
import { computed, ref, defineModel, onMounted, watch, h } from 'vue'
import { useRouter, useRoute } from 'vue-router'
@ -94,6 +98,7 @@ const props = defineProps({
const { $dialog } = globalStore()
const { reload: reloadView, getView } = viewsStore()
const { isManager } = usersStore()
const list = defineModel()
const loadMore = defineModel('loadMore')
@ -123,6 +128,7 @@ const view = ref({
rows: '',
load_default_columns: false,
pinned: false,
public: false,
})
const pageLength = computed(() => list.value?.data?.page_length)
@ -159,6 +165,7 @@ function getParams() {
route_name: _view.route_name,
load_default_columns: _view.row,
pinned: _view.pinned,
public: _view.public,
}
} else {
view.value = {
@ -171,6 +178,7 @@ function getParams() {
route_name: '',
load_default_columns: true,
pinned: false,
public: false,
}
}
@ -249,9 +257,16 @@ const viewsDropdownOptions = computed(() => {
router.push({ ...route, query: { view: view.name } })
}
})
let savedViews = list.value.data.views.filter((v) => !v.pinned)
let publicViews = list.value.data.views.filter((v) => v.public)
let savedViews = list.value.data.views.filter((v) => !v.pinned && !v.public)
let pinnedViews = list.value.data.views.filter((v) => v.pinned)
publicViews.length &&
_views.push({
group: 'Public Views',
items: publicViews,
})
savedViews.length &&
_views.push({
group: 'Saved Views',
@ -338,7 +353,7 @@ const viewActions = computed(() => {
},
]
if (route.query.view) {
if (route.query.view && (!view.value.public || isManager())) {
actions[0].items.push(
{
label: 'Rename',
@ -353,6 +368,18 @@ const viewActions = computed(() => {
}
)
if (route.query.view && isManager()) {
actions[0].items.push({
label: view.value.public ? 'Make Private' : 'Make Public',
icon: () =>
h(FeatherIcon, {
name: view.value.public ? 'lock' : 'unlock',
class: 'h-4 w-4',
}),
onClick: () => publicView(),
})
}
actions.push({
group: 'Delete View',
hideLabel: true,
@ -393,6 +420,16 @@ function renameView() {
showViewModal.value = true
}
function publicView() {
call('crm.fcrm.doctype.crm_view_settings.crm_view_settings.public', {
name: route.query.view,
value: !view.value.public,
}).then(() => {
view.value.public = !view.value.public
reloadView()
})
}
function pinView() {
call('crm.fcrm.doctype.crm_view_settings.crm_view_settings.pin', {
name: route.query.view,

View File

@ -42,8 +42,13 @@ export const usersStore = defineStore('crm-users', () => {
return usersByName[email]
}
function isManager(email) {
return getUser(email).is_manager
}
return {
users,
getUser,
isManager,
}
})

View File

@ -1,29 +1,30 @@
import { defineStore } from 'pinia'
import { usersStore } from '@/stores/users'
import { createListResource } from 'frappe-ui'
import { createResource } from 'frappe-ui'
import { reactive, ref } from 'vue'
export const viewsStore = defineStore('crm-views', () => {
const { getUser } = usersStore()
export const viewsStore = defineStore('crm-views', (doctype) => {
let viewsByName = reactive({})
let pinnedViews = ref([])
let publicViews = ref([])
const views = createListResource({
doctype: 'CRM View Settings',
fields: ['*'],
filters: { user: getUser().email },
cache: 'crm-views',
// Views
const views = createResource({
url: 'crm.api.views.get_views',
params: { doctype: doctype || '' },
cache: "crm-views",
initialData: [],
auto: true,
transform(views) {
pinnedViews.value = []
publicViews.value = []
for (let view of views) {
viewsByName[view.name] = view
if (view.pinned) {
pinnedViews.value?.push(view)
}
if (view.public) {
publicViews.value?.push(view)
}
}
return views
},
@ -42,13 +43,18 @@ export const viewsStore = defineStore('crm-views', () => {
return pinnedViews.value
}
async function reload(wait = false) {
function getPublicViews() {
if (!publicViews.value?.length) return []
return publicViews.value
}
async function reload() {
await views.reload()
}
return {
views,
getPinnedViews,
getPublicViews,
reload,
getView,
}