feat: quiz plugin in lesson
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import { toast } from 'frappe-ui'
|
||||
import { useDateFormat, useTimeAgo } from '@vueuse/core'
|
||||
import { BookOpen, Users, TrendingUp, Briefcase } from 'lucide-vue-next'
|
||||
import { Quiz } from '@/utils/quiz'
|
||||
import Header from '@editorjs/header'
|
||||
import Paragraph from '@editorjs/paragraph'
|
||||
import List from '@editorjs/list'
|
||||
import Embed from '@editorjs/embed'
|
||||
|
||||
export function createToast(options) {
|
||||
toast({
|
||||
@@ -64,6 +69,37 @@ export function getFileSize(file_size) {
|
||||
return value
|
||||
}
|
||||
|
||||
export function getEditorTools() {
|
||||
return {
|
||||
header: Header,
|
||||
quiz: Quiz,
|
||||
paragraph: {
|
||||
class: Paragraph,
|
||||
inlineToolbar: true,
|
||||
config: {
|
||||
preserveBlank: true,
|
||||
},
|
||||
},
|
||||
list: List,
|
||||
embed: {
|
||||
class: Embed,
|
||||
config: {
|
||||
services: {
|
||||
youtube: true,
|
||||
vimeo: true,
|
||||
codepen: true,
|
||||
slides: {
|
||||
regex: /https:\/\/docs\.google\.com\/presentation\/d\/e\/([A-Za-z0-9_-]+)\/pub/,
|
||||
embedUrl:
|
||||
'https://docs.google.com/presentation/d/e/<%= remote_id %>/embed',
|
||||
html: "<iframe width='100%' height='300' frameborder='0' allowfullscreen='true'></iframe>",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function getTimezones() {
|
||||
return [
|
||||
'Pacific/Midway',
|
||||
|
||||
58
frontend/src/utils/quiz.js
Normal file
58
frontend/src/utils/quiz.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import QuizBlock from '@/components/QuizBlock.vue'
|
||||
import { createApp } from 'vue'
|
||||
import { usersStore } from '../stores/user'
|
||||
import translationPlugin from '../translation'
|
||||
|
||||
export class Quiz {
|
||||
static get toolbox() {
|
||||
return {
|
||||
title: 'Quiz',
|
||||
icon: `<img src="/assets/lms/icons/quiz.svg" width="15" height="15">`,
|
||||
}
|
||||
}
|
||||
|
||||
constructor({ data, api, readOnly }) {
|
||||
this.data = data
|
||||
this.readOnly = readOnly
|
||||
}
|
||||
|
||||
static get isReadOnlySupported() {
|
||||
return true
|
||||
}
|
||||
|
||||
render() {
|
||||
this.wrapper = document.createElement('div')
|
||||
if (this.data) {
|
||||
let renderedQuiz = this.renderQuiz(this.data.quiz)
|
||||
if (!this.readOnly) {
|
||||
this.wrapper.innerHTML = renderedQuiz
|
||||
}
|
||||
}
|
||||
return this.wrapper
|
||||
}
|
||||
|
||||
renderQuiz(quiz) {
|
||||
if (this.readOnly) {
|
||||
const app = createApp(QuizBlock, {
|
||||
quiz: quiz, // Pass quiz content as prop
|
||||
})
|
||||
app.use(translationPlugin)
|
||||
const { userResource } = usersStore()
|
||||
app.provide('$user', userResource)
|
||||
app.mount(this.wrapper)
|
||||
return
|
||||
}
|
||||
return `<div class='border rounded-md p-10 text-center'>
|
||||
<span class="font-medium">
|
||||
Quiz: ${quiz}
|
||||
</span>
|
||||
</div>`
|
||||
}
|
||||
|
||||
save(blockContent) {
|
||||
console.log(blockContent)
|
||||
return {
|
||||
quiz: this.data.quiz,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
export default class YouTubeVideo {
|
||||
constructor({ data }) {
|
||||
this.data = data
|
||||
}
|
||||
|
||||
static get toolbox() {
|
||||
return {
|
||||
title: 'YouTube Video',
|
||||
icon: `<img src="/assets/lms/icons/video.svg" width="15" height="15">`,
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
this.wrapper = document.createElement('div')
|
||||
if (this.data && this.data.youtube) {
|
||||
$(this.wrapper).html(this.render_youtube(this.data.youtube))
|
||||
} else {
|
||||
this.render_youtube_dialog()
|
||||
}
|
||||
return this.wrapper
|
||||
}
|
||||
|
||||
render_youtube_dialog() {
|
||||
let me = this
|
||||
let youtubedialog = new frappe.ui.Dialog({
|
||||
title: __('YouTube Video'),
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'youtube',
|
||||
fieldtype: 'Data',
|
||||
label: __('YouTube Video ID'),
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'instructions_section_break',
|
||||
fieldtype: 'Section Break',
|
||||
label: __('Instructions:'),
|
||||
},
|
||||
{
|
||||
fieldname: 'instructions',
|
||||
fieldtype: 'HTML',
|
||||
label: __('Instructions'),
|
||||
options: __(
|
||||
'Enter the YouTube Video ID. The ID is the part of the URL after <code>watch?v=</code>. For example, if the URL is <code>https://www.youtube.com/watch?v=QH2-TGUlwu4</code>, the ID is <code>QH2-TGUlwu4</code>'
|
||||
),
|
||||
},
|
||||
],
|
||||
primary_action_label: __('Insert'),
|
||||
primary_action(values) {
|
||||
youtubedialog.hide()
|
||||
me.youtube = values.youtube
|
||||
$(me.wrapper).html(me.render_youtube(values.youtube))
|
||||
},
|
||||
})
|
||||
youtubedialog.show()
|
||||
}
|
||||
|
||||
render_youtube(youtube) {
|
||||
return `<iframe width="100%" height="400"
|
||||
src="https://www.youtube.com/embed/${youtube}"
|
||||
title="YouTube video player"
|
||||
frameborder="0"
|
||||
style="border-radius: var(--border-radius-lg); margin: 1rem 0;"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen>
|
||||
</iframe>`
|
||||
}
|
||||
|
||||
validate(savedData) {
|
||||
return !savedData.youtube || !savedData.youtube.trim() ? false : true
|
||||
}
|
||||
|
||||
save(block_content) {
|
||||
return {
|
||||
youtube: this.data.youtube || this.youtube,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user