@@ -9,16 +9,19 @@
|
||||
<script setup>
|
||||
import { FrappeUIProvider } from 'frappe-ui'
|
||||
import { Dialogs } from '@/utils/dialogs'
|
||||
import { computed, onUnmounted, ref } from 'vue'
|
||||
import { computed, onUnmounted, ref, watch } from 'vue'
|
||||
import { useScreenSize } from './utils/composables'
|
||||
import DesktopLayout from './components/DesktopLayout.vue'
|
||||
import MobileLayout from './components/MobileLayout.vue'
|
||||
import NoSidebarLayout from './components/NoSidebarLayout.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { usersStore } from '@/stores/user'
|
||||
import { posthogSettings } from '@/telemetry'
|
||||
|
||||
const screenSize = useScreenSize()
|
||||
const router = useRouter()
|
||||
const noSidebar = ref(false)
|
||||
const { userResource } = usersStore()
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.query.fromLesson || to.path === '/persona') {
|
||||
@@ -44,4 +47,10 @@ onUnmounted(() => {
|
||||
noSidebar.value = false
|
||||
stopSession()
|
||||
})
|
||||
|
||||
watch(userResource, () => {
|
||||
if (userResource.data) {
|
||||
posthogSettings.reload()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex flex-col justify-between min-h-0">
|
||||
<div class="flex flex-col justify-between h-full">
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="font-semibold mb-1 text-ink-gray-9">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div v-for="(column, index) in columns" :key="index">
|
||||
<div
|
||||
class="flex flex-col space-y-5"
|
||||
:class="columns.length > 1 ? 'w-[21rem]' : 'w-1/2'"
|
||||
:class="columns.length > 1 ? 'w-[21rem]' : 'w-full'"
|
||||
>
|
||||
<div v-for="field in column">
|
||||
<Link
|
||||
@@ -55,11 +55,13 @@
|
||||
<div v-else>
|
||||
<div class="flex items-center text-sm space-x-2">
|
||||
<div
|
||||
class="flex items-center justify-center rounded border border-outline-gray-1 bg-surface-gray-2 px-20 py-5"
|
||||
class="flex items-center justify-center rounded border border-outline-gray-1 bg-surface-gray-2"
|
||||
:class="field.size == 'lg' ? 'px-5 py-5' : 'px-20 py-8'"
|
||||
>
|
||||
<img
|
||||
:src="data[field.name]?.file_url || data[field.name]"
|
||||
class="size-6 rounded"
|
||||
class="rounded"
|
||||
:class="field.size == 'lg' ? 'w-36' : 'size-6'"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col flex-wrap">
|
||||
@@ -101,6 +103,7 @@
|
||||
:rows="field.rows"
|
||||
:options="field.options"
|
||||
:description="field.description"
|
||||
:class="columns.length > 1 ? 'w-full' : 'w-1/2'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -335,6 +335,9 @@ const tabsStructure = computed(() => {
|
||||
description:
|
||||
'New users will have to be manually registered by Admins.',
|
||||
},
|
||||
{
|
||||
type: 'Column Break',
|
||||
},
|
||||
{
|
||||
label: 'Signup Consent HTML',
|
||||
name: 'custom_signup_content',
|
||||
@@ -364,10 +367,14 @@ const tabsStructure = computed(() => {
|
||||
description:
|
||||
'Keywords for search engines to find your website. Separated by commas.',
|
||||
},
|
||||
{
|
||||
type: 'Column Break',
|
||||
},
|
||||
{
|
||||
label: 'Meta Image',
|
||||
name: 'meta_image',
|
||||
type: 'Upload',
|
||||
size: 'lg',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -179,7 +179,7 @@ const columns = computed(() => {
|
||||
key: 'member_name',
|
||||
},
|
||||
{
|
||||
label: __('Enabled'),
|
||||
label: __('Status'),
|
||||
key: 'enabled',
|
||||
align: 'center',
|
||||
},
|
||||
|
||||
@@ -127,6 +127,7 @@ const memberCount = ref(0)
|
||||
const dayjs = inject('$dayjs')
|
||||
|
||||
onMounted(() => {
|
||||
getMemberCount()
|
||||
updateParticipants()
|
||||
})
|
||||
|
||||
@@ -138,11 +139,13 @@ const participants = createListResource({
|
||||
pageLength: 30,
|
||||
})
|
||||
|
||||
const count = call('lms.lms.api.get_count_of_certified_members').then(
|
||||
(data) => {
|
||||
const getMemberCount = () => {
|
||||
call('lms.lms.api.get_count_of_certified_members', {
|
||||
filters: filters.value,
|
||||
}).then((data) => {
|
||||
memberCount.value = data
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const categories = createListResource({
|
||||
doctype: 'LMS Certificate',
|
||||
@@ -157,6 +160,7 @@ const categories = createListResource({
|
||||
|
||||
const updateParticipants = () => {
|
||||
updateFilters()
|
||||
getMemberCount()
|
||||
participants.update({
|
||||
filters: filters.value,
|
||||
})
|
||||
|
||||
@@ -284,7 +284,7 @@ import {
|
||||
} from 'vue'
|
||||
import { Image, Trash2, X } from 'lucide-vue-next'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { capture } from '@/telemetry'
|
||||
import { capture, startRecording, stopRecording } from '@/telemetry'
|
||||
import { useOnboarding } from 'frappe-ui/frappe'
|
||||
import { sessionStore } from '../stores/session'
|
||||
import { openSettings } from '@/utils'
|
||||
@@ -337,6 +337,7 @@ onMounted(() => {
|
||||
courseResource.reload()
|
||||
} else {
|
||||
capture('course_form_opened')
|
||||
startRecording()
|
||||
}
|
||||
window.addEventListener('keydown', keyboardShortcut)
|
||||
})
|
||||
@@ -354,6 +355,7 @@ const keyboardShortcut = (e) => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', keyboardShortcut)
|
||||
stopRecording()
|
||||
})
|
||||
|
||||
const courseCreationResource = createResource({
|
||||
|
||||
@@ -240,7 +240,6 @@ const updateTabFilter = () => {
|
||||
filters.value['live'] = 1
|
||||
} else if (currentTab.value == 'Upcoming') {
|
||||
filters.value['upcoming'] = 1
|
||||
filters.value['published'] = 1
|
||||
} else if (currentTab.value == 'New') {
|
||||
filters.value['published'] = 1
|
||||
filters.value['published_on'] = [
|
||||
@@ -249,6 +248,8 @@ const updateTabFilter = () => {
|
||||
]
|
||||
} else if (currentTab.value == 'Created') {
|
||||
filters.value['created'] = 1
|
||||
} else if (currentTab.value == 'Unpublished') {
|
||||
filters.value['published'] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -318,6 +319,7 @@ const courseTabs = computed(() => {
|
||||
user.data?.is_evaluator
|
||||
) {
|
||||
tabs.push({ label: __('Created') })
|
||||
tabs.push({ label: __('Unpublished') })
|
||||
} else if (user.data) {
|
||||
tabs.push({ label: __('Enrolled') })
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ import EditorJS from '@editorjs/editorjs'
|
||||
import LessonHelp from '@/components/LessonHelp.vue'
|
||||
import { ChevronRight } from 'lucide-vue-next'
|
||||
import { getEditorTools, enablePlyr } from '@/utils'
|
||||
import { capture } from '@/telemetry'
|
||||
import { capture, startRecording, stopRecording } from '@/telemetry'
|
||||
import { useOnboarding } from 'frappe-ui/frappe'
|
||||
|
||||
const { brand } = sessionStore()
|
||||
@@ -131,6 +131,7 @@ onMounted(() => {
|
||||
window.location.href = '/login'
|
||||
}
|
||||
capture('lesson_form_opened')
|
||||
startRecording()
|
||||
editor.value = renderEditor('content')
|
||||
instructorEditor.value = renderEditor('instructor-notes')
|
||||
window.addEventListener('keydown', keyboardShortcut)
|
||||
@@ -226,6 +227,7 @@ const keyboardShortcut = (e) => {
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(autoSaveInterval)
|
||||
window.removeEventListener('keydown', keyboardShortcut)
|
||||
stopRecording()
|
||||
})
|
||||
|
||||
const newLessonResource = createResource({
|
||||
|
||||
@@ -457,7 +457,7 @@ class CountDistinct(DistinctOptionFunction):
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def get_count_of_certified_members():
|
||||
def get_count_of_certified_members(filters=None):
|
||||
Certificate = DocType("LMS Certificate")
|
||||
|
||||
query = (
|
||||
@@ -466,6 +466,14 @@ def get_count_of_certified_members():
|
||||
.where(Certificate.published == 1)
|
||||
)
|
||||
|
||||
if filters:
|
||||
for field, value in filters.items():
|
||||
if field == "category":
|
||||
query = query.where(
|
||||
Certificate.course_title.like(f"%{value}%")
|
||||
| Certificate.batch_title.like(f"%{value}%")
|
||||
)
|
||||
|
||||
result = query.run(as_dict=True)
|
||||
return result[0]["total"] if result else 0
|
||||
|
||||
@@ -691,13 +699,13 @@ def get_categories(doctype, filters):
|
||||
@frappe.whitelist()
|
||||
def get_members(start=0, search=""):
|
||||
"""Get members for the given search term and start index.
|
||||
Args: start (int): Start index for the query.
|
||||
Args: start (int): Start index for the query.
|
||||
<<<<<<< HEAD
|
||||
search (str): Search term to filter the results.
|
||||
search (str): Search term to filter the results.
|
||||
=======
|
||||
search (str): Search term to filter the results.
|
||||
search (str): Search term to filter the results.
|
||||
>>>>>>> 4869bba7bbb2fb38477d6fc29fb3b5838e075577
|
||||
Returns: List of members.
|
||||
Returns: List of members.
|
||||
"""
|
||||
|
||||
filters = {"enabled": 1, "name": ["not in", ["Administrator", "Guest"]]}
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [
|
||||
{
|
||||
@@ -111,7 +112,7 @@
|
||||
"link_fieldname": "chapter"
|
||||
}
|
||||
],
|
||||
"modified": "2025-02-03 15:23:17.125617",
|
||||
"modified": "2025-05-29 12:38:26.266673",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Course Chapter",
|
||||
@@ -151,8 +152,21 @@
|
||||
"role": "Course Creator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Moderator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"search_fields": "title",
|
||||
"show_title_field_in_link": 1,
|
||||
"sort_field": "modified",
|
||||
@@ -160,4 +174,4 @@
|
||||
"states": [],
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@
|
||||
}
|
||||
],
|
||||
"make_attachments_public": 1,
|
||||
"modified": "2025-03-13 16:01:19.105212",
|
||||
"modified": "2025-05-29 12:38:01.002898",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Course",
|
||||
@@ -319,12 +319,25 @@
|
||||
"role": "Course Creator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Moderator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"show_title_field_in_link": 1,
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user