feat: configure sidebar items
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
:class="isSidebarCollapsed ? 'items-center' : ''"
|
:class="isSidebarCollapsed ? 'items-center' : ''"
|
||||||
>
|
>
|
||||||
<UserDropdown class="p-2" :isCollapsed="isSidebarCollapsed" />
|
<UserDropdown class="p-2" :isCollapsed="isSidebarCollapsed" />
|
||||||
<div class="flex flex-col overflow-y-auto">
|
<div class="flex flex-col overflow-y-auto" v-if="sidebarSettings.data">
|
||||||
<SidebarLink
|
<SidebarLink
|
||||||
v-for="link in sidebarLinks"
|
v-for="link in sidebarLinks"
|
||||||
:link="link"
|
:link="link"
|
||||||
@@ -42,20 +42,31 @@ import UserDropdown from '@/components/UserDropdown.vue'
|
|||||||
import CollapseSidebar from '@/components/Icons/CollapseSidebar.vue'
|
import CollapseSidebar from '@/components/Icons/CollapseSidebar.vue'
|
||||||
import SidebarLink from '@/components/SidebarLink.vue'
|
import SidebarLink from '@/components/SidebarLink.vue'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { ref, onMounted, inject, computed } from 'vue'
|
import { ref, onMounted, inject } from 'vue'
|
||||||
import { getSidebarLinks } from '../utils'
|
import { getSidebarLinks } from '../utils'
|
||||||
import { sessionStore } from '@/stores/session'
|
import { sessionStore } from '@/stores/session'
|
||||||
import { Bell } from 'lucide-vue-next'
|
import { Bell, File } from 'lucide-vue-next'
|
||||||
import { createResource } from 'frappe-ui'
|
import { createResource } from 'frappe-ui'
|
||||||
|
|
||||||
const { user } = sessionStore()
|
const { user } = sessionStore()
|
||||||
const socket = inject('$socket')
|
const socket = inject('$socket')
|
||||||
const unreadCount = ref(0)
|
const unreadCount = ref(0)
|
||||||
|
const sidebarLinks = ref(getSidebarLinks())
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
socket.on('publish_lms_notifications', (data) => {
|
socket.on('publish_lms_notifications', (data) => {
|
||||||
unreadNotifications.reload()
|
unreadNotifications.reload()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
sidebarLinks.value.push({
|
||||||
|
label: 'Notifications',
|
||||||
|
icon: Bell,
|
||||||
|
to: 'Notifications',
|
||||||
|
activeFor: ['Notifications'],
|
||||||
|
count: unreadCount.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const unreadNotifications = createResource({
|
const unreadNotifications = createResource({
|
||||||
@@ -72,22 +83,40 @@ const unreadNotifications = createResource({
|
|||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
unreadCount.value = data
|
unreadCount.value = data
|
||||||
|
sidebarLinks.value = sidebarLinks.value.map((link) => {
|
||||||
|
if (link.label === 'Notifications') {
|
||||||
|
link.count = data
|
||||||
|
}
|
||||||
|
return link
|
||||||
|
})
|
||||||
},
|
},
|
||||||
auto: true,
|
auto: user ? true : false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const sidebarLinks = computed(() => {
|
const sidebarSettings = createResource({
|
||||||
const links = getSidebarLinks()
|
url: 'lms.lms.api.get_sidebar_settings',
|
||||||
if (user) {
|
cache: 'Sidebar Settings',
|
||||||
links.push({
|
auto: true,
|
||||||
label: 'Notifications',
|
onSuccess(data) {
|
||||||
icon: Bell,
|
Object.keys(data).forEach((key) => {
|
||||||
to: 'Notifications',
|
if (!parseInt(data[key])) {
|
||||||
activeFor: ['Notifications'],
|
sidebarLinks.value = sidebarLinks.value.filter(
|
||||||
count: unreadCount.value,
|
(link) => link.label.toLowerCase() !== key
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (data.nav_items) {
|
||||||
|
data.nav_items.forEach((item) => {
|
||||||
|
sidebarLinks.value.push({
|
||||||
|
label: item.label,
|
||||||
|
icon: File,
|
||||||
|
to: item.url,
|
||||||
|
activeFor: [item.label],
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return links
|
console.log(data)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const getSidebarFromStorage = () => {
|
const getSidebarFromStorage = () => {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<span class="grid h-5 w-6 flex-shrink-0 place-items-center">
|
<span class="grid h-5 w-6 flex-shrink-0 place-items-center">
|
||||||
<component
|
<component
|
||||||
:is="link.icon"
|
:is="link.icon"
|
||||||
class="h-5 w-5 stroke-1.5 text-gray-800"
|
class="h-4 w-4 stroke-1.5 text-gray-800"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</slot>
|
</slot>
|
||||||
@@ -55,8 +55,12 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
|
if (props.link.to.includes('/')) {
|
||||||
|
window.location.href = props.link.to
|
||||||
|
} else {
|
||||||
router.push({ name: props.link.to })
|
router.push({ name: props.link.to })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let isActive = computed(() => {
|
let isActive = computed(() => {
|
||||||
return props.link?.activeFor?.includes(router.currentRoute.value.name)
|
return props.link?.activeFor?.includes(router.currentRoute.value.name)
|
||||||
|
|||||||
@@ -416,3 +416,30 @@ def mark_all_as_read():
|
|||||||
|
|
||||||
for notification in notifications:
|
for notification in notifications:
|
||||||
mark_as_read(notification)
|
mark_as_read(notification)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist(allow_guest=True)
|
||||||
|
def get_sidebar_settings():
|
||||||
|
lms_settings = frappe.get_single("LMS Settings")
|
||||||
|
sidebar_items = frappe._dict()
|
||||||
|
|
||||||
|
items = [
|
||||||
|
"courses",
|
||||||
|
"batches",
|
||||||
|
"certified_participants",
|
||||||
|
"jobs",
|
||||||
|
"statistics",
|
||||||
|
"notifications",
|
||||||
|
]
|
||||||
|
for item in items:
|
||||||
|
sidebar_items[item] = lms_settings.get(item)
|
||||||
|
|
||||||
|
if lms_settings.show_navbar_items:
|
||||||
|
nav_items = frappe.get_all(
|
||||||
|
"Top Bar Item",
|
||||||
|
["label", "url"],
|
||||||
|
{"parenttype": "Website Settings", "parentfield": "top_bar_items"},
|
||||||
|
)
|
||||||
|
sidebar_items.nav_items = nav_items
|
||||||
|
|
||||||
|
return sidebar_items
|
||||||
|
|||||||
@@ -38,6 +38,17 @@
|
|||||||
"column_break_12",
|
"column_break_12",
|
||||||
"cookie_policy",
|
"cookie_policy",
|
||||||
"cookie_policy_page",
|
"cookie_policy_page",
|
||||||
|
"sidebar_tab",
|
||||||
|
"items_in_sidebar_section",
|
||||||
|
"courses",
|
||||||
|
"batches",
|
||||||
|
"certified_participants",
|
||||||
|
"column_break_exdz",
|
||||||
|
"jobs",
|
||||||
|
"statistics",
|
||||||
|
"notifications",
|
||||||
|
"section_break_qlss",
|
||||||
|
"show_navbar_items",
|
||||||
"mentor_request_tab",
|
"mentor_request_tab",
|
||||||
"mentor_request_section",
|
"mentor_request_section",
|
||||||
"mentor_request_creation",
|
"mentor_request_creation",
|
||||||
@@ -349,18 +360,78 @@
|
|||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "show_day_view",
|
"fieldname": "show_day_view",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Show Day View in Timetable"
|
"label": "Show day view in timetable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "unsplash_access_key",
|
"fieldname": "unsplash_access_key",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Unsplash Access Key"
|
"label": "Unsplash Access Key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "sidebar_tab",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Sidebar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "courses",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Courses"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "batches",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Batches"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "certified_participants",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Certified Participants"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "jobs",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Jobs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "statistics",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Statistics"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "notifications",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Notifications"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "items_in_sidebar_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Items in Sidebar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_exdz",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_qlss",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "show_navbar_items",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Show Navbar Items"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-04-16 12:18:14.670978",
|
"modified": "2024-05-28 18:29:31.609637",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Settings",
|
"name": "LMS Settings",
|
||||||
|
|||||||
0
lms/lms/doctype/lms_sidebar_item/__init__.py
Normal file
0
lms/lms/doctype/lms_sidebar_item/__init__.py
Normal file
8
lms/lms/doctype/lms_sidebar_item/lms_sidebar_item.js
Normal file
8
lms/lms/doctype/lms_sidebar_item/lms_sidebar_item.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2024, Frappe and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
// frappe.ui.form.on("LMS Sidebar Item", {
|
||||||
|
// refresh(frm) {
|
||||||
|
|
||||||
|
// },
|
||||||
|
// });
|
||||||
67
lms/lms/doctype/lms_sidebar_item/lms_sidebar_item.json
Normal file
67
lms/lms/doctype/lms_sidebar_item/lms_sidebar_item.json
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2024-05-29 16:44:43.778291",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"web_page",
|
||||||
|
"route",
|
||||||
|
"column_break_glmh",
|
||||||
|
"title",
|
||||||
|
"icon"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_glmh",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "icon",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "web_page",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Web Page",
|
||||||
|
"options": "Web Page"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "web_page.route",
|
||||||
|
"fieldname": "route",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Route"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "web_page.title",
|
||||||
|
"fieldname": "title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Title"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2024-05-29 17:14:30.525055",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "LMS Sidebar Item",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "creation",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
9
lms/lms/doctype/lms_sidebar_item/lms_sidebar_item.py
Normal file
9
lms/lms/doctype/lms_sidebar_item/lms_sidebar_item.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2024, Frappe and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class LMSSidebarItem(Document):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2024, Frappe and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestLMSSidebarItem(FrappeTestCase):
|
||||||
|
pass
|
||||||
@@ -646,7 +646,8 @@ def handle_notifications(doc, method):
|
|||||||
|
|
||||||
def create_notification_log(doc, topic):
|
def create_notification_log(doc, topic):
|
||||||
users = []
|
users = []
|
||||||
if topic.reference_doctype == "LMS Course":
|
print(topic.reference_doctype == "Course Lesson")
|
||||||
|
if topic.reference_doctype == "Course Lesson":
|
||||||
course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course")
|
course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course")
|
||||||
course_title = frappe.db.get_value("LMS Course", course, "title")
|
course_title = frappe.db.get_value("LMS Course", course, "title")
|
||||||
instructors = frappe.db.get_all(
|
instructors = frappe.db.get_all(
|
||||||
@@ -689,7 +690,7 @@ def notify_mentions_on_portal(doc, topic):
|
|||||||
|
|
||||||
from_user_name = get_fullname(doc.owner)
|
from_user_name = get_fullname(doc.owner)
|
||||||
|
|
||||||
if topic.reference_doctype == "LMS Course":
|
if topic.reference_doctype == "Course Lesson":
|
||||||
course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course")
|
course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course")
|
||||||
subject = _("{0} mentioned you in a comment in {1}").format(
|
subject = _("{0} mentioned you in a comment in {1}").format(
|
||||||
from_user_name, topic.title
|
from_user_name, topic.title
|
||||||
|
|||||||
@@ -89,3 +89,4 @@ lms.patches.v1_0.change_navbar_urls
|
|||||||
lms.patches.v1_0.set_published_on
|
lms.patches.v1_0.set_published_on
|
||||||
lms.patches.v2_0.fix_progress_percentage
|
lms.patches.v2_0.fix_progress_percentage
|
||||||
lms.patches.v2_0.add_discussion_topic_titles
|
lms.patches.v2_0.add_discussion_topic_titles
|
||||||
|
lms.patches.v2_0.sidebar_settings
|
||||||
15
lms/patches/v2_0/sidebar_settings.py
Normal file
15
lms/patches/v2_0/sidebar_settings.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
fields = [
|
||||||
|
"courses",
|
||||||
|
"batches",
|
||||||
|
"certified_participants",
|
||||||
|
"jobs",
|
||||||
|
"statistics",
|
||||||
|
"notifications",
|
||||||
|
]
|
||||||
|
|
||||||
|
for field in fields:
|
||||||
|
frappe.db.set_single_value("LMS Settings", field, 1)
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<meta name="twitter:title" content="{{ meta.title }}" />
|
<meta name="twitter:title" content="{{ meta.title }}" />
|
||||||
<meta name="twitter:image" content="{{ meta.image }}" />
|
<meta name="twitter:image" content="{{ meta.image }}" />
|
||||||
<meta name="twitter:description" content="{{ meta.description }}" />
|
<meta name="twitter:description" content="{{ meta.description }}" />
|
||||||
<script type="module" crossorigin src="/assets/lms/frontend/assets/index-BCIMZEe3.js"></script>
|
<script type="module" crossorigin src="/assets/lms/frontend/assets/index-DNirc6jJ.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/lms/frontend/assets/frappe-ui-D-bJ8Xgf.js">
|
<link rel="modulepreload" crossorigin href="/assets/lms/frontend/assets/frappe-ui-D-bJ8Xgf.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/lms/frontend/assets/frappe-ui-B1gEXx4C.css">
|
<link rel="stylesheet" crossorigin href="/assets/lms/frontend/assets/frappe-ui-B1gEXx4C.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/lms/frontend/assets/index-B_uDyhcC.css">
|
<link rel="stylesheet" crossorigin href="/assets/lms/frontend/assets/index-B_uDyhcC.css">
|
||||||
|
|||||||
Reference in New Issue
Block a user