fix: branding, course, lesson and certificate creation

This commit is contained in:
Jannat Patel
2024-04-03 22:42:59 +05:30
parent efcdba3a29
commit a46306720b
14 changed files with 84 additions and 23 deletions

View File

@@ -18,7 +18,9 @@
</div>
</div>
<SidebarLink
:label="isSidebarCollapsed ? 'Expand' : 'Collapse'"
:link="{
label: isSidebarCollapsed ? 'Expand' : 'Collapse',
}"
:isCollapsed="isSidebarCollapsed"
@click="isSidebarCollapsed = !isSidebarCollapsed"
class="m-2"

View File

@@ -97,10 +97,6 @@ const addFile = (data) => {
getCurrentEditor().blocks.insert('upload', data)
}
const getBlocksCount = () => {
return getCurrentEditor().blocks.getBlocksCount
}
const validateFile = (file) => {
let extension = file.name.split('.').pop().toLowerCase()
if (!['jpg', 'jpeg', 'png', 'mp4', 'mov', 'mp3'].includes(extension)) {

View File

@@ -11,7 +11,12 @@
: 'hover:bg-gray-200 px-2 w-52'
"
>
<LMSLogo class="w-8 h-8 rounded flex-shrink-0" />
<span
v-if="branding.data?.brand_html"
v-html="branding.data?.brand_html"
class="w-8 h-8 rounded flex-shrink-0"
></span>
<LMSLogo v-else class="w-8 h-8 rounded flex-shrink-0" />
<div
class="flex flex-1 flex-col text-left duration-300 ease-in-out"
:class="
@@ -21,7 +26,10 @@
"
>
<div class="text-base font-medium text-gray-900 leading-none">
Learning
<span v-if="branding.data?.brand_name">
{{ branding.data?.brand_name }}
</span>
<span v-else> Learning </span>
</div>
<div v-if="user" class="mt-1 text-sm text-gray-700 leading-none">
{{ convertToTitleCase(user.split('@')[0]) }}
@@ -49,6 +57,7 @@ import { Dropdown } from 'frappe-ui'
import { ChevronDown, LogIn, LogOut, User } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
import { convertToTitleCase } from '../utils'
import { onMounted } from 'vue'
const router = useRouter()
const props = defineProps({
@@ -58,8 +67,9 @@ const props = defineProps({
},
})
const { logout, user } = sessionStore()
const { logout, user, branding } = sessionStore()
let { isLoggedIn } = sessionStore()
const userDropdownOptions = [
/* {
icon: User,

View File

@@ -187,14 +187,16 @@ import {
FormControl,
FileUploader,
} from 'frappe-ui'
import { inject, onMounted, computed, ref, reactive } from 'vue'
import { inject, onMounted, computed, ref, reactive, watch } from 'vue'
import { convertToTitleCase, showToast, getFileSize } from '../utils'
import Link from '@/components/Controls/Link.vue'
import { FileText, X } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
import CourseOutline from '@/components/CourseOutline.vue'
const user = inject('$user')
const newTag = ref('')
const router = useRouter()
const props = defineProps({
courseName: {
@@ -318,8 +320,12 @@ const submitCourse = () => {
)
} else {
courseCreationResource.submit(course, {
onSuccess() {
onSuccess(data) {
showToast('Success', 'Course created successfully', 'check')
router.push({
name: 'CreateCourse',
params: { courseName: data.name },
})
},
onError(err) {
showToast(err)
@@ -347,6 +353,15 @@ const validateMandatoryFields = () => {
}
}
watch(
() => props.courseName !== 'new',
(newVal) => {
if (newVal) {
courseResource.reload()
}
}
)
const validateFile = (file) => {
let extension = file.name.split('.').pop().toLowerCase()
if (!['jpg', 'jpeg', 'png'].includes(extension)) {

View File

@@ -73,17 +73,19 @@ import {
Button,
createDocumentResource,
} from 'frappe-ui'
import { computed, reactive, onMounted, inject, ref } from 'vue'
import { computed, reactive, onMounted, inject, ref, watch } from 'vue'
import EditorJS from '@editorjs/editorjs'
import { createToast } from '../utils'
import LessonPlugins from '@/components/LessonPlugins.vue'
import { getEditorTools } from '../utils'
import { ChevronRight } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
const editor = ref(null)
const instructorEditor = ref(null)
const user = inject('$user')
const openInstructorEditor = ref(false)
const router = useRouter()
const props = defineProps({
courseName: {
@@ -319,6 +321,7 @@ const createNewLesson = () => {
{
onSuccess() {
showToast('Success', 'Lesson created successfully', 'check')
lessonDetails.reload()
},
}
)

View File

@@ -114,7 +114,10 @@
</span>
</div>
<div
v-if="lesson.data.instructor_content && allowInstructorContent()"
v-if="
lesson.data.instructor_content.blocks?.length &&
allowInstructorContent()
"
class="bg-gray-100 p-3 rounded-md mt-6"
>
<div class="text-gray-600 font-medium">
@@ -233,8 +236,7 @@ const lesson = createResource({
markProgress(data)
if (data.content) editor = renderEditor('editor', data.content)
if (data.instructor_content)
if (data.instructor_content?.blocks?.length)
instructorEditor = renderEditor(
'instructor-content',
data.instructor_content
@@ -253,7 +255,8 @@ const renderEditor = (holder, content) => {
}
const markProgress = (data) => {
if (!data.progress) progress.submit()
console.log(user.data)
if (user.data && !data.progress) progress.submit()
}
const current_lesson = createResource({

View File

@@ -41,10 +41,20 @@ export const sessionStore = defineStore('lms-session', () => {
},
})
const branding = createResource({
url: 'lms.lms.api.get_branding',
auto: true,
cache: true,
onSuccess(data) {
document.querySelector("link[rel='icon']").href = data.favicon
},
})
return {
user,
isLoggedIn,
login,
logout,
branding,
}
})

View File

@@ -117,9 +117,6 @@ export function getEditorTools() {
paragraph: {
class: Paragraph,
inlineToolbar: true,
config: {
preserveBlank: true,
},
},
list: {
class: NestedList,

View File

@@ -15,11 +15,11 @@ export class Upload {
}
renderUpload(file) {
if (file.file_type == 'video') {
if (this.isVideo(file.file_type)) {
return `<video controls width='100%' controls controlsList='nodownload' class="mb-4">
<source src=${encodeURI(file.file_url)} type='video/mp4'>
</video>`
} else if (file.file_type == 'audio') {
} else if (this.isAudio(file.file_type)) {
return `<audio controls width='100%' controls controlsList='nodownload' class="mb-4">
<source src=${encodeURI(file.file_url)} type='audio/mp3'>
</audio>`
@@ -40,4 +40,12 @@ export class Upload {
file_type: this.data.file_type,
}
}
isVideo(type) {
return ['mov', 'mp4', 'avi', 'mkv', 'webm'].includes(type.toLowerCase())
}
isAudio(type) {
return ['mp3', 'wav', 'ogg'].includes(type.toLowerCase())
}
}

View File

@@ -278,3 +278,13 @@ def get_file_info(file_url):
"File", {"file_url": file_url}, ["file_name", "file_size", "file_url"], as_dict=1
)
return file_info
@frappe.whitelist(allow_guest=True)
def get_branding():
"""Get branding details."""
return {
"brand_name": frappe.db.get_single_value("Website Settings", "app_name"),
"brand_html": frappe.db.get_single_value("Website Settings", "brand_html"),
"favicon": frappe.db.get_single_value("Website Settings", "favicon"),
}

View File

@@ -15,7 +15,11 @@ class LMSCertificate(Document):
def after_insert(self):
if not frappe.flags.in_test:
self.send_mail()
outgoing_email_account = frappe.get_cached_value(
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
)
if outgoing_email_account:
self.send_mail()
def send_mail(self):
subject = _("Congratulations on getting certified!")

View File

@@ -161,7 +161,6 @@ def get_lesson_details(chapter):
)
lesson_details.number = f"{chapter.idx}.{row.idx}"
lesson_details.icon = get_lesson_icon(lesson_details.body)
lesson_details.is_complete = get_progress(lesson_details.course, lesson_details.name)
lessons.append(lesson_details)
return lessons

View File

@@ -10,7 +10,7 @@
{{ _("With this certification, you can now showcase your updated skills and share your achievement with your colleagues and on LinkedIn. To access your certificate, please click on the link provided below. Make sure you are logged in to the portal.") }}
</p>
<br>
<a href="/api/method/frappe.utils.print_format.download_pdf?doctype=LMS+Certificate&name={{certificate_name}}&format={{encodeURIComponent(template)}}">{{ _("Certificate Link") }}</a>
<a href="/api/method/frappe.utils.print_format.download_pdf?doctype=LMS+Certificate&name={{certificate_name}}&format={{template | urlencode }}">{{ _("Certificate Link") }}</a>
<br>
<p>
{{ _("Once again, congratulations on this significant accomplishment.")}}

4
yarn.lock Normal file
View File

@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1