feat: batch creation
This commit is contained in:
@@ -12,12 +12,13 @@
|
||||
"@editorjs/editorjs": "^2.29.0",
|
||||
"@editorjs/embed": "^2.7.0",
|
||||
"@editorjs/header": "^2.8.1",
|
||||
"@editorjs/list": "^1.9.0",
|
||||
"@editorjs/image": "^2.9.0",
|
||||
"@editorjs/nested-list": "^1.4.2",
|
||||
"@editorjs/paragraph": "^2.11.3",
|
||||
"chart.js": "^4.4.1",
|
||||
"dayjs": "^1.11.6",
|
||||
"feather-icons": "^4.28.0",
|
||||
"frappe-ui": "^0.1.31",
|
||||
"frappe-ui": "^0.1.35",
|
||||
"lucide-vue-next": "^0.309.0",
|
||||
"markdown-it": "^14.0.0",
|
||||
"pinia": "^2.0.33",
|
||||
|
||||
141
frontend/src/components/BatchCourses.vue
Normal file
141
frontend/src/components/BatchCourses.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="text-xl font-semibold">
|
||||
{{ __('Courses') }}
|
||||
</div>
|
||||
<Button
|
||||
v-if="user.data?.is_moderator"
|
||||
variant="solid"
|
||||
@click="openCourseModal()"
|
||||
>
|
||||
<template #prefix>
|
||||
<Plus class="h-4 w-4" />
|
||||
</template>
|
||||
{{ __('Add Course') }}
|
||||
</Button>
|
||||
</div>
|
||||
<div v-if="courses.data?.length">
|
||||
<ListView
|
||||
:columns="getCoursesColumns()"
|
||||
:rows="courses.data"
|
||||
row-key="name"
|
||||
:options="{ showTooltip: false }"
|
||||
>
|
||||
<ListHeader
|
||||
class="mb-2 grid items-center space-x-4 rounded bg-gray-100 p-2"
|
||||
>
|
||||
<ListHeaderItem :item="item" v-for="item in getCoursesColumns()">
|
||||
<template #prefix="{ item }">
|
||||
<component
|
||||
v-if="item.icon"
|
||||
:is="item.icon"
|
||||
class="h-4 w-4 stroke-1.5 ml-4"
|
||||
/>
|
||||
</template>
|
||||
</ListHeaderItem>
|
||||
</ListHeader>
|
||||
<ListRows>
|
||||
<ListRow :row="row" v-for="row in courses.data">
|
||||
<template #default="{ column, item }">
|
||||
<ListRowItem :item="row[column.key]" :align="column.align">
|
||||
<div>
|
||||
{{ row[column.key] }}
|
||||
</div>
|
||||
</ListRowItem>
|
||||
</template>
|
||||
</ListRow>
|
||||
</ListRows>
|
||||
<ListSelectBanner>
|
||||
<template #actions="{ unselectAll, selections }">
|
||||
<div class="flex gap-2">
|
||||
<Button variant="ghost" @click="removeCourses(selections)">
|
||||
<Trash2 class="h-4 w-4 stroke-1.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</ListSelectBanner>
|
||||
</ListView>
|
||||
</div>
|
||||
<BatchCourseModal
|
||||
v-model="showCourseModal"
|
||||
:batch="batch"
|
||||
v-model:courses="courses"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import BatchCourseModal from '@/components/Modals/BatchCourseModal.vue'
|
||||
import {
|
||||
createResource,
|
||||
Button,
|
||||
ListHeader,
|
||||
ListHeaderItem,
|
||||
ListSelectBanner,
|
||||
ListRow,
|
||||
ListRows,
|
||||
ListView,
|
||||
ListRowItem,
|
||||
} from 'frappe-ui'
|
||||
import { Plus, Trash2 } from 'lucide-vue-next'
|
||||
|
||||
const showCourseModal = ref(false)
|
||||
const user = inject('$user')
|
||||
|
||||
const props = defineProps({
|
||||
batch: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const courses = createResource({
|
||||
url: 'lms.lms.utils.get_batch_courses',
|
||||
params: {
|
||||
batch: props.batch,
|
||||
},
|
||||
cache: ['batchCourses', props.batchName],
|
||||
auto: true,
|
||||
})
|
||||
|
||||
const openCourseModal = () => {
|
||||
showCourseModal.value = true
|
||||
}
|
||||
|
||||
const getCoursesColumns = () => {
|
||||
return [
|
||||
{
|
||||
label: 'Title',
|
||||
key: 'title',
|
||||
},
|
||||
{
|
||||
label: 'Lessons',
|
||||
key: 'lesson_count',
|
||||
},
|
||||
{
|
||||
label: 'Enrollments',
|
||||
key: 'enrollment_count',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const removeCourse = createResource({
|
||||
url: 'frappe.client.delete',
|
||||
makeParams(values) {
|
||||
return {
|
||||
doctype: 'Batch Course',
|
||||
name: values.course,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const removeCourses = (selections) => {
|
||||
console.log(selections)
|
||||
selections.forEach(async (course) => {
|
||||
removeCourse.submit({ course })
|
||||
await setTimeout(1000)
|
||||
})
|
||||
courses.reload()
|
||||
}
|
||||
</script>
|
||||
@@ -73,11 +73,21 @@
|
||||
>
|
||||
{{ __('Enroll Now') }}
|
||||
</Button>
|
||||
<Button v-if="user?.data?.is_moderator" class="w-full mt-2">
|
||||
<span>
|
||||
{{ __('Edit') }}
|
||||
</span>
|
||||
</Button>
|
||||
<router-link
|
||||
v-if="user?.data?.is_moderator"
|
||||
:to="{
|
||||
name: 'BatchCreation',
|
||||
params: {
|
||||
batchName: batch.data.name,
|
||||
},
|
||||
}"
|
||||
>
|
||||
<Button class="w-full mt-2">
|
||||
<span>
|
||||
{{ __('Edit') }}
|
||||
</span>
|
||||
</Button>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
@@ -55,16 +55,14 @@
|
||||
<Button variant="ghost" @click="removeStudents(selections)">
|
||||
<Trash2 class="h-4 w-4 stroke-1.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
label="Unselect all"
|
||||
@click="unselectAll.toString()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</ListSelectBanner>
|
||||
</ListView>
|
||||
</div>
|
||||
<div v-else class="text-sm italic text-gray-600">
|
||||
{{ __('There are no students in this batch.') }}
|
||||
</div>
|
||||
<StudentModal
|
||||
:batch="props.batch"
|
||||
v-model="showStudentModal"
|
||||
|
||||
@@ -103,8 +103,8 @@ import { computed, inject } from 'vue'
|
||||
import { Button, createResource } from 'frappe-ui'
|
||||
import { createToast } from '@/utils/'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
|
||||
const router = useRouter()
|
||||
const user = inject('$user')
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
v-if="title && (outline.data?.length || allowEdit)"
|
||||
class="flex items-center justify-between mb-4"
|
||||
>
|
||||
<div class="text-lg font-semibold">
|
||||
<div class="font-semibold" :class="allowEdit ? 'text-base' : 'text-lg'">
|
||||
{{ __(title) }}
|
||||
</div>
|
||||
<Button v-if="allowEdit" @click="openChapterModal()">
|
||||
<Button size="sm" v-if="allowEdit" @click="openChapterModal()">
|
||||
{{ __('Add Chapter') }}
|
||||
</Button>
|
||||
<!-- <span class="font-medium cursor-pointer" @click="expandAllChapters()">
|
||||
|
||||
99
frontend/src/components/LessonContent.vue
Normal file
99
frontend/src/components/LessonContent.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div v-if="youtube">
|
||||
<iframe
|
||||
class="youtube-video"
|
||||
:src="getYouTubeVideoSource(youtube)"
|
||||
width="100%"
|
||||
height="400"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
||||
<div v-for="block in content.split('\n\n')">
|
||||
<div v-if="block.includes('{{ YouTubeVideo')">
|
||||
<iframe
|
||||
class="youtube-video"
|
||||
:src="getYouTubeVideoSource(block)"
|
||||
width="100%"
|
||||
height="400"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
||||
<div v-else-if="block.includes('{{ Quiz')">
|
||||
<Quiz :quiz="getId(block)" />
|
||||
</div>
|
||||
<div v-else-if="block.includes('{{ Video')">
|
||||
<video controls width="100%" controlsList="nodownload">
|
||||
<source :src="getId(block)" type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
<div v-else-if="block.includes('{{ PDF')">
|
||||
<iframe
|
||||
:src="getPDFSource(block)"
|
||||
width="100%"
|
||||
height="400"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
||||
<div v-else-if="block.includes('{{ Audio')">
|
||||
<audio width="100%" controls controlsList="nodownload">
|
||||
<source :src="getId(block)" type="audio/mp3" />
|
||||
</audio>
|
||||
</div>
|
||||
<div v-else-if="block.includes('{{ Embed')">
|
||||
<iframe
|
||||
width="100%"
|
||||
height="400"
|
||||
:src="getId(block)"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
>
|
||||
</iframe>
|
||||
</div>
|
||||
<div v-else v-html="markdown.render(block)"></div>
|
||||
</div>
|
||||
<div v-if="quizId">
|
||||
<Quiz :quiz="quizId" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import Quiz from '@/components/QuizBlock.vue'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
|
||||
const markdown = new MarkdownIt({
|
||||
html: true,
|
||||
linkify: true,
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
youtube: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
quizId: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
const getYouTubeVideoSource = (block) => {
|
||||
if (block.includes('{{')) {
|
||||
block = getId(block)
|
||||
}
|
||||
return `https://www.youtube.com/embed/${block}`
|
||||
}
|
||||
|
||||
const getPDFSource = (block) => {
|
||||
return `${getId(block)}#toolbar=0`
|
||||
}
|
||||
|
||||
const getId = (block) => {
|
||||
return block.match(/\(["']([^"']+?)["']\)/)[1]
|
||||
}
|
||||
</script>
|
||||
137
frontend/src/components/LessonPlugins.vue
Normal file
137
frontend/src/components/LessonPlugins.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div class="text-lg font-semibold">
|
||||
{{ __('Components') }}
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<div class="">
|
||||
<div class="text-xs text-gray-600 mb-1">
|
||||
{{ __('Select an Editor') }}
|
||||
</div>
|
||||
<Select v-model="currentEditor" :options="getEditorOptions()" />
|
||||
</div>
|
||||
<div class="flex mt-4">
|
||||
<Link
|
||||
v-model="quiz"
|
||||
class="flex-1"
|
||||
doctype="LMS Quiz"
|
||||
:label="__('Select a Quiz')"
|
||||
/>
|
||||
<Button @click="addQuiz()" class="self-end ml-2">
|
||||
<template #icon>
|
||||
<Plus class="h-4 w-4 stroke-1.5" />
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="text-xs text-gray-600 mb-1">
|
||||
{{ __('Add an image, video, pdf or audio.') }}
|
||||
</div>
|
||||
<div class="flex">
|
||||
<FileUploader
|
||||
v-if="!file"
|
||||
:fileTypes="['image/*', 'video/*', 'audio/*', '.pdf']"
|
||||
:validateFile="validateFile"
|
||||
@success="(data) => addFile(data)"
|
||||
>
|
||||
<template v-slot="{ file, progress, uploading, openFileSelector }">
|
||||
<div class="">
|
||||
<Button @click="openFileSelector" :loading="uploading">
|
||||
{{
|
||||
uploading
|
||||
? __('Uploading {0}%').format(progress)
|
||||
: __('Upload an File')
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</FileUploader>
|
||||
<div v-else class="">
|
||||
<div class="flex items-center">
|
||||
<div class="border rounded-md p-2 mr-2">
|
||||
<FileText class="h-4 w-4 stroke-1.5 text-gray-700" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs">
|
||||
{{ file.file_name }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import { FileUploader, Button, Select } from 'frappe-ui'
|
||||
import { Plus, FileText } from 'lucide-vue-next'
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const quiz = ref(null)
|
||||
const file = ref(null)
|
||||
const lessonEditor = ref(null)
|
||||
const instructorEditor = ref(null)
|
||||
const currentEditor = ref('Lesson Content')
|
||||
|
||||
const props = defineProps({
|
||||
editor: {
|
||||
required: true,
|
||||
},
|
||||
notesEditor: {
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const addQuiz = () => {
|
||||
getCurrentEditor().caret.setToLastBlock('end', 0)
|
||||
if (quiz.value) {
|
||||
getCurrentEditor().blocks.insert('quiz', {
|
||||
quiz: quiz.value,
|
||||
})
|
||||
quiz.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const addFile = (data) => {
|
||||
getCurrentEditor().caret.setToLastBlock('end', 0)
|
||||
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)) {
|
||||
return 'Only image and video files are allowed.'
|
||||
}
|
||||
}
|
||||
|
||||
const getEditorOptions = () => {
|
||||
return [
|
||||
{
|
||||
label: 'Lesson Content',
|
||||
value: 'Lesson Content',
|
||||
},
|
||||
{
|
||||
label: 'Instructor Content',
|
||||
value: 'Instructor Content',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const getCurrentEditor = () => {
|
||||
return currentEditor.value == 'Lesson Content'
|
||||
? lessonEditor.value
|
||||
: instructorEditor.value
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.editor, props.notesEditor],
|
||||
([newEditor, newNotesEditor], [oldEditor, oldNotesEditor]) => {
|
||||
lessonEditor.value = newEditor
|
||||
instructorEditor.value = newNotesEditor
|
||||
}
|
||||
)
|
||||
</script>
|
||||
@@ -9,7 +9,7 @@
|
||||
<Plus class="h-4 w-4" />
|
||||
</template>
|
||||
<span>
|
||||
{{ __('Create') }}
|
||||
{{ __('Add Live Class') }}
|
||||
</span>
|
||||
</Button>
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
@@ -88,7 +88,7 @@ const props = defineProps({
|
||||
const liveClasses = createListResource({
|
||||
doctype: 'LMS Live Class',
|
||||
filters: {
|
||||
batch: props.batchName,
|
||||
batch_name: props.batch,
|
||||
date: ['>=', new Date()],
|
||||
},
|
||||
fields: [
|
||||
|
||||
68
frontend/src/components/Modals/BatchCourseModal.vue
Normal file
68
frontend/src/components/Modals/BatchCourseModal.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="show"
|
||||
:options="{
|
||||
title: __('Add a course'),
|
||||
size: 'sm',
|
||||
actions: [
|
||||
{
|
||||
label: __('Submit'),
|
||||
variant: 'solid',
|
||||
onClick: (close) => addCourse(close),
|
||||
},
|
||||
],
|
||||
}"
|
||||
>
|
||||
<template #body-content>
|
||||
<Link doctype="LMS Course" v-model="course" />
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Dialog, createResource } from 'frappe-ui'
|
||||
import { ref, defineModel } from 'vue'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import { showToast } from '@/utils'
|
||||
|
||||
const show = defineModel()
|
||||
const course = ref(null)
|
||||
const courses = defineModel('courses')
|
||||
|
||||
const props = defineProps({
|
||||
batch: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
const createBatchCourse = createResource({
|
||||
url: 'frappe.client.insert',
|
||||
makeParams(values) {
|
||||
return {
|
||||
doc: {
|
||||
doctype: 'Batch Course',
|
||||
parent: props.batch,
|
||||
parenttype: 'LMS Batch',
|
||||
parentfield: 'courses',
|
||||
course: course.value,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const addCourse = (close) => {
|
||||
createBatchCourse.submit(
|
||||
{},
|
||||
{
|
||||
onSuccess() {
|
||||
courses.value.reload()
|
||||
close()
|
||||
course.value = null
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.message[0] || err, 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
@@ -1,192 +0,0 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="show"
|
||||
:options="{
|
||||
title: __('Create a Batch'),
|
||||
size: '3xl',
|
||||
actions: [
|
||||
{
|
||||
label: __('Save'),
|
||||
variant: 'solid',
|
||||
onClick: (close) => createBatch(close),
|
||||
},
|
||||
],
|
||||
}"
|
||||
>
|
||||
<template #body-content>
|
||||
<div>
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.title"
|
||||
:label="__('Title')"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.published"
|
||||
type="checkbox"
|
||||
:label="__('Published')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.start_date"
|
||||
:label="__('Start Date')"
|
||||
type="date"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.end_date"
|
||||
:label="__('End Date')"
|
||||
type="date"
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.start_time"
|
||||
:label="__('Start Time')"
|
||||
type="time"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.end_time"
|
||||
:label="__('End Time')"
|
||||
type="time"
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-4 mt-4 border-t pt-4">
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.seat_count"
|
||||
:label="__('Seat Count')"
|
||||
type="number"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.evaluation_end_date"
|
||||
:label="__('Evaluation End Date')"
|
||||
type="date"
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.medium"
|
||||
:label="__('Medium')"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.category"
|
||||
:label="__('Category')"
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FileUploader
|
||||
v-if="!batch.meta_image"
|
||||
:fileTypes="['image/*']"
|
||||
:validateFile="validateFile"
|
||||
@success="
|
||||
(file) => {
|
||||
batch.meta_image.value = file
|
||||
}
|
||||
"
|
||||
>
|
||||
<template
|
||||
v-slot="{ file, progress, uploading, openFileSelector }"
|
||||
>
|
||||
<div class="mb-4">
|
||||
<Button @click="openFileSelector" :loading="uploading">
|
||||
{{
|
||||
uploading ? `Uploading ${progress}%` : 'Upload an image'
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</FileUploader>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-t pt-4 mb-4">
|
||||
<FormControl
|
||||
v-model="batch.paid_batch"
|
||||
type="checkbox"
|
||||
:label="__('Paid Batch')"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.amount"
|
||||
:label="__('Amount')"
|
||||
type="number"
|
||||
class="my-4"
|
||||
/>
|
||||
<Link
|
||||
doctype="Currency"
|
||||
v-model="batch.currency"
|
||||
:filters="{ enabled: 1 }"
|
||||
:label="__('Currency')"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4 border-y pt-4 mb-4"></div>
|
||||
<FormControl
|
||||
v-model="batch.description"
|
||||
:label="__('Description')"
|
||||
type="textarea"
|
||||
class="mb-4"
|
||||
/>
|
||||
<div>
|
||||
<label class="block text-sm text-gray-600 mb-1">
|
||||
{{ __('Batch Details') }}
|
||||
</label>
|
||||
<TextEditor
|
||||
:content="batch.batch_details"
|
||||
@change="(val) => (batch.batch_details = val)"
|
||||
:editable="true"
|
||||
:fixedMenu="true"
|
||||
editorClass="prose-sm max-w-none border-b border-x bg-gray-100 rounded-b-md py-1 px-2 min-h-[7rem] mb-4"
|
||||
/>
|
||||
</div>
|
||||
<FormControl
|
||||
v-model="batch.batch_details_raw"
|
||||
:label="__('Batch Details Raw')"
|
||||
type="textarea"
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
Dialog,
|
||||
FormControl,
|
||||
TextEditor,
|
||||
FileUploader,
|
||||
Button,
|
||||
} from 'frappe-ui'
|
||||
import { reactive, defineModel } from 'vue'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
|
||||
const show = defineModel()
|
||||
|
||||
const batch = reactive({
|
||||
title: '',
|
||||
published: false,
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
medium: '',
|
||||
category: '',
|
||||
seat_count: 0,
|
||||
evaluation_end_date: '',
|
||||
description: '',
|
||||
batch_details: '',
|
||||
batch_details_raw: '',
|
||||
meta_image: '',
|
||||
paid_batch: false,
|
||||
amount: 0,
|
||||
currency: '',
|
||||
})
|
||||
</script>
|
||||
@@ -3,7 +3,7 @@
|
||||
v-model="show"
|
||||
:options="{
|
||||
title: __('Add a Student'),
|
||||
size: 'xl',
|
||||
size: 'sm',
|
||||
actions: [
|
||||
{
|
||||
label: 'Submit',
|
||||
|
||||
@@ -45,25 +45,7 @@
|
||||
<template #default="{ tab }">
|
||||
<div class="pt-5 px-10 pb-10">
|
||||
<div v-if="tab.label == 'Courses'">
|
||||
<div class="text-xl font-semibold">
|
||||
{{ __('Courses') }}
|
||||
</div>
|
||||
<div
|
||||
class="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 gap-8 mt-5"
|
||||
>
|
||||
<div v-for="course in courses.data">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'CourseDetail',
|
||||
params: {
|
||||
courseName: course.name,
|
||||
},
|
||||
}"
|
||||
>
|
||||
<CourseCard :key="course.name" :course="course" />
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<BatchCourses :batch="batch.data.name" />
|
||||
</div>
|
||||
<div v-else-if="tab.label == 'Dashboard'">
|
||||
<BatchDashboard :batch="batch" :isStudent="isStudent" />
|
||||
@@ -94,9 +76,10 @@
|
||||
</Tabs>
|
||||
</div>
|
||||
<div class="p-5">
|
||||
<div class="text-2xl font-semibold mb-3">
|
||||
<div class="text-2xl font-semibold mb-2">
|
||||
{{ batch.data.title }}
|
||||
</div>
|
||||
<div v-html="batch.data.description" class="leading-5 mb-4"></div>
|
||||
<div class="flex items-center mb-3">
|
||||
<Calendar class="h-4 w-4 stroke-1.5 mr-2 text-gray-700" />
|
||||
<span>
|
||||
@@ -111,7 +94,6 @@
|
||||
{{ formatTime(batch.data.end_time) }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-html="batch.data.description"></div>
|
||||
</div>
|
||||
<AnnouncementModal
|
||||
v-model="showAnnouncementModal"
|
||||
@@ -180,8 +162,8 @@ import {
|
||||
MessageCircle,
|
||||
} from 'lucide-vue-next'
|
||||
import { formatTime } from '@/utils'
|
||||
import CourseCard from '@/components/CourseCard.vue'
|
||||
import BatchDashboard from '@/components/BatchDashboard.vue'
|
||||
import BatchCourses from '@/components/BatchCourses.vue'
|
||||
import LiveClass from '@/components/LiveClass.vue'
|
||||
import BatchStudents from '@/components/BatchStudents.vue'
|
||||
import Assessments from '@/components/Assessments.vue'
|
||||
@@ -213,7 +195,7 @@ const breadcrumbs = computed(() => {
|
||||
let crumbs = [{ label: 'All Batches', route: { name: 'Batches' } }]
|
||||
if (!isStudent.value) {
|
||||
crumbs.push({
|
||||
label: batch.data?.title,
|
||||
label: 'Details',
|
||||
route: {
|
||||
name: 'BatchDetail',
|
||||
params: {
|
||||
@@ -275,15 +257,6 @@ const tabs = computed(() => {
|
||||
return batchTabs
|
||||
})
|
||||
|
||||
const courses = createResource({
|
||||
url: 'lms.lms.utils.get_batch_courses',
|
||||
params: {
|
||||
batch: props.batchName,
|
||||
},
|
||||
cache: ['batchCourses', props.batchName],
|
||||
auto: true,
|
||||
})
|
||||
|
||||
const redirectToLogin = () => {
|
||||
window.location.href = `/login?redirect-to=/batches`
|
||||
}
|
||||
|
||||
390
frontend/src/pages/BatchCreation.vue
Normal file
390
frontend/src/pages/BatchCreation.vue
Normal file
@@ -0,0 +1,390 @@
|
||||
<template>
|
||||
<div class="h-screen text-base">
|
||||
<header
|
||||
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||
>
|
||||
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
||||
<Button variant="solid" @click="saveBatch()">
|
||||
{{ __('Save') }}
|
||||
</Button>
|
||||
</header>
|
||||
<div class="py-5">
|
||||
<div class="container">
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
{{ __('Details') }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-10">
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.title"
|
||||
:label="__('Title')"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.description"
|
||||
:label="__('Description')"
|
||||
type="textarea"
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.published"
|
||||
type="checkbox"
|
||||
:label="__('Published')"
|
||||
/>
|
||||
<FileUploader
|
||||
v-if="!batch.image"
|
||||
class="mt-4"
|
||||
:fileTypes="['image/*']"
|
||||
:validateFile="validateFile"
|
||||
@success="(file) => saveImage(file)"
|
||||
>
|
||||
<template
|
||||
v-slot="{ file, progress, uploading, openFileSelector }"
|
||||
>
|
||||
<div class="mb-4">
|
||||
<Button @click="openFileSelector" :loading="uploading">
|
||||
{{
|
||||
uploading ? `Uploading ${progress}%` : 'Upload an image'
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</FileUploader>
|
||||
<div v-else class="mt-4">
|
||||
<div class="text-xs text-gray-600 mb-1">
|
||||
{{ __('Meta Image') }}
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="border rounded-md p-2 mr-2">
|
||||
<FileText class="h-5 w-5 stroke-1.5 text-gray-700" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span>
|
||||
{{ batch.image.file_name }}
|
||||
</span>
|
||||
<span class="text-sm text-gray-500 mt-1">
|
||||
{{ getFileSize(batch.image.file_size) }}
|
||||
</span>
|
||||
</div>
|
||||
<X
|
||||
@click="removeImage()"
|
||||
class="bg-gray-200 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container border-b mb-5">
|
||||
<div>
|
||||
<label class="block text-sm text-gray-600 mb-1">
|
||||
{{ __('Batch Details') }}
|
||||
</label>
|
||||
<TextEditor
|
||||
:content="batch.batch_details"
|
||||
@change="(val) => (batch.batch_details = val)"
|
||||
:editable="true"
|
||||
:fixedMenu="true"
|
||||
editorClass="prose-sm max-w-none border-b border-x bg-gray-100 rounded-b-md py-1 px-2 min-h-[7rem] mb-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container border-b mb-5">
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
{{ __('Settings') }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-10">
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.start_date"
|
||||
:label="__('Start Date')"
|
||||
type="date"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.end_date"
|
||||
:label="__('End Date')"
|
||||
type="date"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.start_time"
|
||||
:label="__('Start Time')"
|
||||
type="time"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.end_time"
|
||||
:label="__('End Time')"
|
||||
type="time"
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.seat_count"
|
||||
:label="__('Seat Count')"
|
||||
type="number"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.evaluation_end_date"
|
||||
:label="__('Evaluation End Date')"
|
||||
type="date"
|
||||
class="mb-4"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.medium"
|
||||
type="select"
|
||||
:options="[
|
||||
{
|
||||
label: 'Online',
|
||||
value: 'Online',
|
||||
},
|
||||
{
|
||||
label: 'Offline',
|
||||
value: 'Offline',
|
||||
},
|
||||
]"
|
||||
:label="__('Medium')"
|
||||
class="mb-4"
|
||||
/>
|
||||
<Link
|
||||
doctype="LMS Category"
|
||||
:label="__('Category')"
|
||||
v-model="batch.category"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
{{ __('Payment') }}
|
||||
</div>
|
||||
<div>
|
||||
<FormControl
|
||||
v-model="batch.paid_batch"
|
||||
type="checkbox"
|
||||
:label="__('Paid Batch')"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="batch.amount"
|
||||
:label="__('Amount')"
|
||||
type="number"
|
||||
class="my-4"
|
||||
/>
|
||||
<Link
|
||||
doctype="Currency"
|
||||
v-model="batch.currency"
|
||||
:filters="{ enabled: 1 }"
|
||||
:label="__('Currency')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, onMounted, inject, reactive } from 'vue'
|
||||
import {
|
||||
Breadcrumbs,
|
||||
FormControl,
|
||||
FileUploader,
|
||||
Button,
|
||||
TextEditor,
|
||||
createResource,
|
||||
} from 'frappe-ui'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { getFileSize } from '../utils'
|
||||
import { X, FileText } from 'lucide-vue-next'
|
||||
import { showToast } from '../utils'
|
||||
|
||||
const router = useRouter()
|
||||
const user = inject('$user')
|
||||
|
||||
const props = defineProps({
|
||||
batchName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const batch = reactive({
|
||||
title: '',
|
||||
published: false,
|
||||
description: '',
|
||||
batch_details: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
evaluation_end_date: '',
|
||||
seat_count: '',
|
||||
medium: '',
|
||||
category: '',
|
||||
image: null,
|
||||
paid_batch: false,
|
||||
currency: '',
|
||||
amount: 0,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (!user.data) window.location.href = '/login'
|
||||
if (props.batchName != 'new') {
|
||||
batchDetail.reload()
|
||||
}
|
||||
})
|
||||
|
||||
const newBatch = createResource({
|
||||
url: 'frappe.client.insert',
|
||||
makeParams(values) {
|
||||
return {
|
||||
doc: {
|
||||
doctype: 'LMS Batch',
|
||||
meta_image: batch.image.file_url,
|
||||
...batch,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const batchDetail = createResource({
|
||||
url: 'frappe.client.get',
|
||||
makeParams(values) {
|
||||
return {
|
||||
doctype: 'LMS Batch',
|
||||
name: props.batchName,
|
||||
}
|
||||
},
|
||||
onSuccess(data) {
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (Object.hasOwn(batch, key)) batch[key] = data[key]
|
||||
})
|
||||
let checkboxes = ['published', 'paid_batch']
|
||||
for (let idx in checkboxes) {
|
||||
let key = checkboxes[idx]
|
||||
batch[key] = batch[key] ? true : false
|
||||
}
|
||||
if (data.meta_image) imageResource.reload({ image: data.meta_image })
|
||||
},
|
||||
})
|
||||
|
||||
const editBatch = createResource({
|
||||
url: 'frappe.client.set_value',
|
||||
makeParams(values) {
|
||||
return {
|
||||
doctype: 'LMS Batch',
|
||||
name: props.batchName,
|
||||
fieldname: {
|
||||
meta_image: batch.image.file_url,
|
||||
...batch,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const imageResource = createResource({
|
||||
url: 'lms.lms.api.get_file_info',
|
||||
makeParams(values) {
|
||||
return {
|
||||
file_url: values.image,
|
||||
}
|
||||
},
|
||||
auto: false,
|
||||
onSuccess(data) {
|
||||
batch.image = data
|
||||
},
|
||||
})
|
||||
|
||||
const saveBatch = () => {
|
||||
if (batchDetail.data) {
|
||||
editBatchDetails()
|
||||
} else {
|
||||
createNewBatch()
|
||||
}
|
||||
}
|
||||
|
||||
const createNewBatch = () => {
|
||||
newBatch.submit(
|
||||
{},
|
||||
{
|
||||
onSuccess(data) {
|
||||
router.push({
|
||||
name: 'BatchDetail',
|
||||
params: {
|
||||
batchName: data.name,
|
||||
},
|
||||
})
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.messages?.[0] || err, 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const editBatchDetails = () => {
|
||||
editBatch.submit(
|
||||
{},
|
||||
{
|
||||
onSuccess(data) {
|
||||
router.push({
|
||||
name: 'BatchDetail',
|
||||
params: {
|
||||
batchName: data.name,
|
||||
},
|
||||
})
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.messages?.[0] || err, 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const saveImage = (file) => {
|
||||
batch.image = file
|
||||
}
|
||||
|
||||
const removeImage = () => {
|
||||
batch.image = null
|
||||
}
|
||||
|
||||
const validateFile = (file) => {
|
||||
let extension = file.name.split('.').pop().toLowerCase()
|
||||
if (!['jpg', 'jpeg', 'png'].includes(extension)) {
|
||||
return 'Only image file is allowed.'
|
||||
}
|
||||
}
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let crumbs = [
|
||||
{
|
||||
label: 'Batches',
|
||||
route: {
|
||||
name: 'Batches',
|
||||
},
|
||||
},
|
||||
]
|
||||
if (batchDetail.data) {
|
||||
crumbs.push({
|
||||
label: batchDetail.data.title,
|
||||
route: {
|
||||
name: 'BatchDetail',
|
||||
params: {
|
||||
batchName: props.batchName,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
crumbs.push({
|
||||
label: props.batchName == 'new' ? 'New Batch' : 'Edit Batch',
|
||||
route: { name: 'BatchCreation', params: { batchName: props.batchName } },
|
||||
})
|
||||
return crumbs
|
||||
})
|
||||
</script>
|
||||
@@ -45,9 +45,11 @@
|
||||
<BatchOverlay :batch="batch" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-2xl font-semibold mt-10">
|
||||
{{ __('Courses') }}
|
||||
<div v-if="batch.data.courses.length">
|
||||
<div class="flex items-center mt-10">
|
||||
<div class="text-2xl font-semibold">
|
||||
{{ __('Courses') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8 mt-5">
|
||||
<div
|
||||
@@ -78,10 +80,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Breadcrumbs, createResource } from 'frappe-ui'
|
||||
import { Breadcrumbs, createResource, Button } from 'frappe-ui'
|
||||
import { BookOpen, Calendar, Clock } from 'lucide-vue-next'
|
||||
import { formatTime } from '../utils'
|
||||
import { computed, inject } from 'vue'
|
||||
import { computed, inject, ref } from 'vue'
|
||||
import BatchOverlay from '@/components/BatchOverlay.vue'
|
||||
import CourseCard from '@/components/CourseCard.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
/>
|
||||
<div class="flex">
|
||||
<router-link
|
||||
v-if="user.data"
|
||||
:to="{
|
||||
name: 'CreateBatch',
|
||||
name: 'BatchCreation',
|
||||
params: { batchName: 'new' },
|
||||
}"
|
||||
>
|
||||
@@ -88,7 +89,6 @@ import BatchCard from '@/components/BatchCard.vue'
|
||||
import { inject, ref, computed } from 'vue'
|
||||
|
||||
const user = inject('$user')
|
||||
const showBatchModal = ref(false)
|
||||
|
||||
const batches = createListResource({
|
||||
doctype: 'LMS Batch',
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
v-if="courses.data.length == 0 && courses.list.loading"
|
||||
class="p-5 text-base text-gray-700"
|
||||
>
|
||||
Loading Courses...
|
||||
{{ __('Loading Courses...') }}
|
||||
</div>
|
||||
<Tabs
|
||||
v-else
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<template>
|
||||
<div class="h-screen text-base">
|
||||
<header
|
||||
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||
>
|
||||
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
||||
</header>
|
||||
<div>Batch creation</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { Breadcrumbs } from 'frappe-ui'
|
||||
|
||||
const props = defineProps({
|
||||
batchName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let crumbs = [
|
||||
{
|
||||
label: 'Batches',
|
||||
route: {
|
||||
name: 'Batches',
|
||||
},
|
||||
},
|
||||
]
|
||||
crumbs.push({
|
||||
label: props.batchName == 'new' ? 'New Batch' : 'Edit Batch',
|
||||
route: { name: 'CreateBatch', params: { batchName: props.batchName } },
|
||||
})
|
||||
return crumbs
|
||||
})
|
||||
</script>
|
||||
@@ -8,10 +8,10 @@
|
||||
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
||||
<div class="flex items-center">
|
||||
<router-link
|
||||
v-if="courseResource.doc"
|
||||
v-if="courseResource.data"
|
||||
:to="{
|
||||
name: 'CourseDetail',
|
||||
params: { courseName: courseResource.doc.name },
|
||||
params: { courseName: courseResource.data.name },
|
||||
}"
|
||||
>
|
||||
<Button>
|
||||
@@ -30,7 +30,7 @@
|
||||
<div class="mt-5 mb-10">
|
||||
<div class="container mb-5">
|
||||
<div class="text-lg font-semibold mb-4">
|
||||
{{ __('Course Details') }}
|
||||
{{ __('Details') }}
|
||||
</div>
|
||||
<FormControl
|
||||
v-model="course.title"
|
||||
@@ -55,14 +55,10 @@
|
||||
/>
|
||||
</div>
|
||||
<FileUploader
|
||||
v-if="!image"
|
||||
v-if="!course.course_image"
|
||||
:fileTypes="['image/*']"
|
||||
:validateFile="validateFile"
|
||||
@success="
|
||||
(file) => {
|
||||
image = file
|
||||
}
|
||||
"
|
||||
@success="(file) => saveImage(file)"
|
||||
>
|
||||
<template
|
||||
v-slot="{ file, progress, uploading, openFileSelector }"
|
||||
@@ -86,10 +82,10 @@
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span>
|
||||
{{ image.file_name }}
|
||||
{{ course.course_image.file_name }}
|
||||
</span>
|
||||
<span class="text-sm text-gray-500 mt-1">
|
||||
{{ getFileSize(image.file_size) }}
|
||||
{{ getFileSize(course.course_image.file_size) }}
|
||||
</span>
|
||||
</div>
|
||||
<X
|
||||
@@ -104,12 +100,12 @@
|
||||
class="mb-4"
|
||||
/>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-gray-700">
|
||||
<div class="mb-1.5 text-xs text-gray-600">
|
||||
{{ __('Tags') }}
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div
|
||||
v-for="tag in getTags"
|
||||
v-for="tag in course.tags.split(', ')"
|
||||
class="flex items-center bg-gray-100 p-2 rounded-md mr-2"
|
||||
>
|
||||
{{ tag }}
|
||||
@@ -124,7 +120,7 @@
|
||||
</div>
|
||||
<div class="container border-t">
|
||||
<div class="text-lg font-semibold mt-5 mb-4">
|
||||
{{ __('Course Settings') }}
|
||||
{{ __('Settings') }}
|
||||
</div>
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<FormControl
|
||||
@@ -146,7 +142,7 @@
|
||||
</div>
|
||||
<div class="container border-t">
|
||||
<div class="text-lg font-semibold mt-5 mb-4">
|
||||
{{ __('Course Pricing') }}
|
||||
{{ __('Pricing') }}
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<FormControl
|
||||
@@ -172,9 +168,9 @@
|
||||
<div class="border-l px-5 pt-5">
|
||||
<!-- <CreateOutline v-if="courseResource.doc" :course="courseResource.doc"/> -->
|
||||
<CourseOutline
|
||||
v-if="courseResource.doc"
|
||||
:courseName="courseResource.doc.name"
|
||||
:title="courseResource.doc.title"
|
||||
v-if="courseResource.data"
|
||||
:courseName="courseResource.data.name"
|
||||
:title="course.title"
|
||||
:allowEdit="true"
|
||||
/>
|
||||
</div>
|
||||
@@ -192,15 +188,13 @@ import {
|
||||
FileUploader,
|
||||
} from 'frappe-ui'
|
||||
import { inject, onMounted, computed, ref, reactive } from 'vue'
|
||||
import { convertToTitleCase, createToast, getFileSize } from '../utils'
|
||||
import { convertToTitleCase, showToast, getFileSize } from '../utils'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import { FileText, X } from 'lucide-vue-next'
|
||||
import CourseOutline from '@/components/CourseOutline.vue'
|
||||
|
||||
const user = inject('$user')
|
||||
const tags = ref('')
|
||||
const newTag = ref('')
|
||||
const image = ref(null)
|
||||
|
||||
const props = defineProps({
|
||||
courseName: {
|
||||
@@ -208,84 +202,6 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let crumbs = [
|
||||
{
|
||||
label: 'Courses',
|
||||
route: { name: 'Courses' },
|
||||
},
|
||||
]
|
||||
if (courseResource.doc) {
|
||||
crumbs.push({
|
||||
label: courseResource.doc?.title,
|
||||
route: { name: 'CourseDetail', params: { courseName: props.courseName } },
|
||||
})
|
||||
}
|
||||
crumbs.push({
|
||||
label: props.courseName == 'new' ? 'New Course' : 'Edit Course',
|
||||
route: { name: 'CreateCourse', params: { courseName: props.courseName } },
|
||||
})
|
||||
return crumbs
|
||||
})
|
||||
|
||||
const courseResource = createDocumentResource({
|
||||
doctype: 'LMS Course',
|
||||
name: props.courseName,
|
||||
auto: false,
|
||||
onSuccess(data) {
|
||||
tags.value = data.tags
|
||||
imageResource.reload({ image: data.image })
|
||||
Object.assign(course, data)
|
||||
course.published = data.published ? true : false
|
||||
course.upcoming = data.upcoming ? true : false
|
||||
course.disable_self_learning = data.disable_self_learning ? true : false
|
||||
course.paid_course = data.paid_course ? true : false
|
||||
},
|
||||
})
|
||||
|
||||
const imageResource = createResource({
|
||||
url: 'lms.lms.api.get_file_info',
|
||||
makeParams(values) {
|
||||
return {
|
||||
file_url: values.image,
|
||||
}
|
||||
},
|
||||
auto: false,
|
||||
onSuccess(data) {
|
||||
image.value = data
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (!user.data?.is_moderator || !user.data?.is_instructor) {
|
||||
window.location.href = '/login'
|
||||
}
|
||||
if (props.courseName !== 'new') {
|
||||
courseResource.reload()
|
||||
}
|
||||
})
|
||||
|
||||
/* const course = computed(() => {
|
||||
return {
|
||||
title: courseResource.doc?.title || '',
|
||||
short_introduction: courseResource.doc?.short_introduction || '',
|
||||
description: courseResource.doc?.description || '',
|
||||
video_link: courseResource.doc?.video_link || '',
|
||||
course_image: courseResource.doc?.image || null,
|
||||
tags: courseResource.doc?.tags || '',
|
||||
published: courseResource.doc?.published ? true : false,
|
||||
upcoming: courseResource.doc?.upcoming ? true : false,
|
||||
disable_self_learning: courseResource.doc?.disable_self_learning
|
||||
? true
|
||||
: false,
|
||||
course_image: image.value,
|
||||
paid_course: courseResource.doc?.paid_course ? true : false,
|
||||
course_price: courseResource.doc?.course_price || '',
|
||||
currency: courseResource.doc?.currency || '',
|
||||
image: courseResource.doc?.image || null,
|
||||
}
|
||||
}) */
|
||||
|
||||
const course = reactive({
|
||||
title: '',
|
||||
short_introduction: '',
|
||||
@@ -301,10 +217,13 @@ const course = reactive({
|
||||
currency: '',
|
||||
})
|
||||
|
||||
const getTags = computed(() => {
|
||||
return courseResource.doc?.tags
|
||||
? courseResource.doc.tags.split(', ')
|
||||
: tags.value?.split(', ')
|
||||
onMounted(() => {
|
||||
if (!user.data?.is_moderator || !user.data?.is_instructor) {
|
||||
window.location.href = '/login'
|
||||
}
|
||||
if (props.courseName !== 'new') {
|
||||
courseResource.reload()
|
||||
}
|
||||
})
|
||||
|
||||
const courseCreationResource = createResource({
|
||||
@@ -313,24 +232,82 @@ const courseCreationResource = createResource({
|
||||
return {
|
||||
doc: {
|
||||
doctype: 'LMS Course',
|
||||
image: image.value.file_url,
|
||||
image: course.course_image.file_url,
|
||||
...values,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const courseEditResource = createResource({
|
||||
url: 'frappe.client.set_value',
|
||||
auto: false,
|
||||
makeParams(values) {
|
||||
return {
|
||||
doctype: 'LMS Course',
|
||||
name: values.course,
|
||||
fieldname: {
|
||||
image: course.course_image.file_url,
|
||||
...course,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const courseResource = createResource({
|
||||
url: 'frappe.client.get',
|
||||
makeParams(values) {
|
||||
return {
|
||||
doctype: 'LMS Course',
|
||||
name: props.courseName,
|
||||
}
|
||||
},
|
||||
auto: false,
|
||||
onSuccess(data) {
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (Object.hasOwn(course, key)) course[key] = data[key]
|
||||
})
|
||||
let checkboxes = [
|
||||
'published',
|
||||
'upcoming',
|
||||
'disable_self_learning',
|
||||
'paid_course',
|
||||
]
|
||||
for (let idx in checkboxes) {
|
||||
let key = checkboxes[idx]
|
||||
course[key] = course[key] ? true : false
|
||||
}
|
||||
|
||||
if (data.image) imageResource.reload({ image: data.image })
|
||||
},
|
||||
})
|
||||
|
||||
const imageResource = createResource({
|
||||
url: 'lms.lms.api.get_file_info',
|
||||
makeParams(values) {
|
||||
return {
|
||||
file_url: values.image,
|
||||
}
|
||||
},
|
||||
auto: false,
|
||||
onSuccess(data) {
|
||||
course.course_image = data
|
||||
},
|
||||
})
|
||||
|
||||
const getTags = computed(() => {
|
||||
return courseResource.doc?.tags
|
||||
? courseResource.doc.tags.split(', ')
|
||||
: tags.value?.split(', ')
|
||||
})
|
||||
|
||||
const submitCourse = () => {
|
||||
if (courseResource.doc) {
|
||||
courseResource.setValue.submit(
|
||||
if (courseResource.data) {
|
||||
courseEditResource.submit(
|
||||
{
|
||||
image: image.value?.file_url || null,
|
||||
...course.value,
|
||||
course: courseResource.data.name,
|
||||
},
|
||||
{
|
||||
validate() {
|
||||
return validateMandatoryFields()
|
||||
},
|
||||
onSuccess() {
|
||||
showToast('Success', 'Course updated successfully', 'check')
|
||||
},
|
||||
@@ -340,10 +317,7 @@ const submitCourse = () => {
|
||||
}
|
||||
)
|
||||
} else {
|
||||
courseCreationResource.submit(course.value, {
|
||||
validate() {
|
||||
return validateMandatoryFields()
|
||||
},
|
||||
courseCreationResource.submit(course, {
|
||||
onSuccess() {
|
||||
showToast('Success', 'Course created successfully', 'check')
|
||||
},
|
||||
@@ -363,15 +337,12 @@ const validateMandatoryFields = () => {
|
||||
'course_image',
|
||||
]
|
||||
for (const field of mandatory_fields) {
|
||||
if (!course.value[field]) {
|
||||
if (!course[field]) {
|
||||
let fieldLabel = convertToTitleCase(field.split('_').join(' '))
|
||||
return `${fieldLabel} is mandatory`
|
||||
}
|
||||
}
|
||||
if (
|
||||
course.value.paid_course &&
|
||||
(!course.value.course_price || !course.value.currency)
|
||||
) {
|
||||
if (course.paid_course && (!course.course_price || !course.currency)) {
|
||||
return 'Course price and currency are mandatory for paid courses'
|
||||
}
|
||||
}
|
||||
@@ -385,35 +356,44 @@ const validateFile = (file) => {
|
||||
|
||||
const updateTags = () => {
|
||||
if (newTag.value) {
|
||||
tags.value = tags.value ? `${tags.value}, ${newTag.value}` : newTag.value
|
||||
course.tags = course.tags ? `${course.tags}, ${newTag.value}` : newTag.value
|
||||
newTag.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const removeTag = (tag) => {
|
||||
tags.value = tags.value
|
||||
course.tags = course.tags
|
||||
?.split(', ')
|
||||
.filter((t) => t !== tag)
|
||||
.join(', ')
|
||||
newTag.value = ''
|
||||
}
|
||||
|
||||
const showToast = (title, text, icon) => {
|
||||
createToast({
|
||||
title: title,
|
||||
text: text,
|
||||
icon: icon,
|
||||
iconClasses:
|
||||
icon == 'check'
|
||||
? 'bg-green-600 text-white rounded-md p-px'
|
||||
: 'bg-red-600 text-white rounded-md p-px',
|
||||
position: icon == 'check' ? 'bottom-right' : 'top-center',
|
||||
timeout: icon == 'check' ? 5 : 10,
|
||||
})
|
||||
const saveImage = (file) => {
|
||||
course.course_image = file
|
||||
}
|
||||
|
||||
const removeImage = () => {
|
||||
image.value = null
|
||||
course.value.course_image = null
|
||||
course.course_image = null
|
||||
}
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let crumbs = [
|
||||
{
|
||||
label: 'Courses',
|
||||
route: { name: 'Courses' },
|
||||
},
|
||||
]
|
||||
if (courseResource.data) {
|
||||
crumbs.push({
|
||||
label: course.title,
|
||||
route: { name: 'CourseDetail', params: { courseName: props.courseName } },
|
||||
})
|
||||
}
|
||||
crumbs.push({
|
||||
label: props.courseName == 'new' ? 'New Course' : 'Edit Course',
|
||||
route: { name: 'CreateCourse', params: { courseName: props.courseName } },
|
||||
})
|
||||
return crumbs
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="h-screen text-base">
|
||||
<div class="grid grid-cols-[75%,25%] h-full">
|
||||
<div>
|
||||
<div class="border-r">
|
||||
<header
|
||||
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||
>
|
||||
@@ -10,53 +10,56 @@
|
||||
{{ __('Save') }}
|
||||
</Button>
|
||||
</header>
|
||||
<div class="w-5/6 mx-auto py-5">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ __('Lesson Details') }}
|
||||
<div class="py-5">
|
||||
<div class="w-5/6 mx-auto">
|
||||
<FormControl v-model="lesson.title" label="Title" class="mb-4" />
|
||||
<FormControl
|
||||
v-model="lesson.include_in_preview"
|
||||
type="checkbox"
|
||||
label="Include in Preview"
|
||||
/>
|
||||
</div>
|
||||
<div class="border-t mt-4">
|
||||
<div class="w-5/6 mx-auto pt-4">
|
||||
<div
|
||||
class="flex justify-between cursor-pointer"
|
||||
@click="
|
||||
() => {
|
||||
openInstructorEditor = !openInstructorEditor
|
||||
}
|
||||
"
|
||||
>
|
||||
<label class="block font-medium text-gray-600 mb-1">
|
||||
{{ __('Instructor Notes') }}
|
||||
</label>
|
||||
<ChevronRight
|
||||
class="stroke-2 h-5 w-5 text-gray-600"
|
||||
:class="{
|
||||
'rotate-90 transform duration-200': openInstructorEditor,
|
||||
'duration-200': !openInstructorEditor,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-show="openInstructorEditor"
|
||||
id="instructor-notes"
|
||||
class="py-3"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<FormControl v-model="lesson.title" label="Title" class="mb-4" />
|
||||
<FormControl
|
||||
v-model="lesson.include_in_preview"
|
||||
type="checkbox"
|
||||
label="Include in Preview"
|
||||
/>
|
||||
<div class="mt-4">
|
||||
<label class="block text-xs text-gray-600 mb-1">
|
||||
{{ __('Instructor Notes') }}
|
||||
</label>
|
||||
<div
|
||||
id="instructor-notes"
|
||||
class="border rounded-md px-10 py-3"
|
||||
></div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<label class="block text-xs text-gray-600 mb-1">
|
||||
{{ __('Content') }}
|
||||
</label>
|
||||
<div id="content" class="border rounded-md py-3"></div>
|
||||
<div class="border-t mt-4">
|
||||
<div class="w-5/6 mx-auto pt-4">
|
||||
<label class="block font-medium text-gray-600 mb-1">
|
||||
{{ __('Content') }}
|
||||
</label>
|
||||
<div id="content" class="py-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-l px-5 pt-5">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ __('Components') }}
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<div class="flex">
|
||||
<Link
|
||||
v-model="quiz"
|
||||
class="flex-1"
|
||||
doctype="LMS Quiz"
|
||||
:label="__('Select a Quiz')"
|
||||
/>
|
||||
<Button @click="addQuiz()" class="self-end ml-2">
|
||||
<template #icon>
|
||||
<Plus class="h-4 w-4 stroke-1.5" />
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="sticky top-0 p-5">
|
||||
<LessonPlugins :editor="editor" :notesEditor="instructorEditor" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -73,13 +76,14 @@ import {
|
||||
import { computed, reactive, onMounted, inject, ref } from 'vue'
|
||||
import EditorJS from '@editorjs/editorjs'
|
||||
import { createToast } from '../utils'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import { Plus } from 'lucide-vue-next'
|
||||
import LessonPlugins from '@/components/LessonPlugins.vue'
|
||||
import { getEditorTools } from '../utils'
|
||||
import { ChevronRight } from 'lucide-vue-next'
|
||||
|
||||
let editor
|
||||
const editor = ref(null)
|
||||
const instructorEditor = ref(null)
|
||||
const user = inject('$user')
|
||||
const quiz = ref(null)
|
||||
const openInstructorEditor = ref(false)
|
||||
|
||||
const props = defineProps({
|
||||
courseName: {
|
||||
@@ -100,7 +104,8 @@ onMounted(() => {
|
||||
if (!user.data?.is_moderator || !user.data?.is_instructor) {
|
||||
window.location.href = '/login'
|
||||
}
|
||||
editor = renderEditor('content')
|
||||
editor.value = renderEditor('content')
|
||||
instructorEditor.value = renderEditor('instructor-notes')
|
||||
})
|
||||
|
||||
const renderEditor = (holder) => {
|
||||
@@ -132,8 +137,27 @@ const lessonDetails = createResource({
|
||||
lesson[key] = data.lesson[key]
|
||||
})
|
||||
lesson.include_in_preview = data.include_in_preview ? true : false
|
||||
editor.isReady.then(() => {
|
||||
editor.render(JSON.parse(data.lesson.content))
|
||||
editor.value.isReady.then(() => {
|
||||
if (data.lesson.content) {
|
||||
editor.value.render(JSON.parse(data.lesson.content))
|
||||
} else if (data.lesson.body) {
|
||||
let blocks = convertToJSON(data.lesson)
|
||||
editor.value.render({
|
||||
blocks: blocks,
|
||||
})
|
||||
}
|
||||
})
|
||||
instructorEditor.value.isReady.then(() => {
|
||||
if (data.lesson.instructor_content) {
|
||||
instructorEditor.value.render(
|
||||
JSON.parse(data.lesson.instructor_content)
|
||||
)
|
||||
} else if (data.lesson.instructor_notes) {
|
||||
let blocks = convertToJSON(data.lesson)
|
||||
instructorEditor.value.render({
|
||||
blocks: blocks,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -180,30 +204,106 @@ const lessonReference = createResource({
|
||||
},
|
||||
})
|
||||
|
||||
const saveLesson = () => {
|
||||
editor.save().then((outputData) => {
|
||||
lesson.content = JSON.stringify(outputData)
|
||||
if (lessonDetails.data?.lesson) {
|
||||
editLesson.submit(
|
||||
{
|
||||
lesson: lessonDetails.data.lesson.name,
|
||||
const convertToJSON = (lessonData) => {
|
||||
let blocks = []
|
||||
lessonData.body.split('\n').forEach((block) => {
|
||||
if (block.includes('{{ YouTubeVideo')) {
|
||||
let youtubeID = block.match(/\(["']([^"']+?)["']\)/)[1]
|
||||
if (!youtubeID.includes('https://'))
|
||||
youtubeID = `https://www.youtube.com/embed/${youtubeID}`
|
||||
blocks.push({
|
||||
type: 'embed',
|
||||
data: {
|
||||
service: 'youtube',
|
||||
embed: youtubeID,
|
||||
},
|
||||
{
|
||||
validate() {
|
||||
return validateLesson()
|
||||
},
|
||||
onSuccess() {
|
||||
showToast('Success', 'Lesson updated successfully', 'check')
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.message, 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
} else if (block.includes('{{ Quiz')) {
|
||||
let quiz = block.match(/\(["']([^"']+?)["']\)/)[1]
|
||||
blocks.push({
|
||||
type: 'quiz',
|
||||
data: {
|
||||
quiz: quiz,
|
||||
},
|
||||
})
|
||||
} else if (block.includes('{{ Video')) {
|
||||
let video = block.match(/\(["']([^"']+?)["']\)/)[1]
|
||||
blocks.push({
|
||||
type: 'upload',
|
||||
data: {
|
||||
file_url: video,
|
||||
file_type: 'video',
|
||||
},
|
||||
})
|
||||
} else if (block.includes('{{ Audio')) {
|
||||
let audio = block.match(/\(["']([^"']+?)["']\)/)[1]
|
||||
blocks.push({
|
||||
type: 'upload',
|
||||
data: {
|
||||
file_url: audio,
|
||||
file_type: 'audio',
|
||||
},
|
||||
})
|
||||
} else if (block.includes('{{ PDF')) {
|
||||
let pdf = block.match(/\(["']([^"']+?)["']\)/)[1]
|
||||
blocks.push({
|
||||
type: 'upload',
|
||||
data: {
|
||||
file_url: pdf,
|
||||
file_type: 'pdf',
|
||||
},
|
||||
})
|
||||
} else if (block.includes('{{ Embed')) {
|
||||
let embed = block.match(/\(["']([^"']+?)["']\)/)[1]
|
||||
blocks.push({
|
||||
type: 'embed',
|
||||
data: {
|
||||
service: embed.split('|||')[0],
|
||||
embed: embed.split('|||')[1],
|
||||
},
|
||||
})
|
||||
} else if (block.includes('![]')) {
|
||||
let image = block.match(/\((.*?)\)/)[1]
|
||||
blocks.push({
|
||||
type: 'upload',
|
||||
data: {
|
||||
file_url: image,
|
||||
file_type: 'image',
|
||||
},
|
||||
})
|
||||
} else if (block.includes('#')) {
|
||||
let level = (block.match(/#/g) || []).length
|
||||
blocks.push({
|
||||
type: 'header',
|
||||
data: {
|
||||
text: block.replace(/#/g, '').trim(),
|
||||
level: level,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
createNewLesson()
|
||||
blocks.push({
|
||||
type: 'paragraph',
|
||||
data: {
|
||||
text: block,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
return blocks
|
||||
}
|
||||
|
||||
const saveLesson = () => {
|
||||
editor.value.save().then((outputData) => {
|
||||
lesson.content = JSON.stringify(outputData)
|
||||
instructorEditor.value.save().then((outputData) => {
|
||||
lesson.instructor_content = JSON.stringify(outputData)
|
||||
if (lessonDetails.data?.lesson) {
|
||||
editCurrentLesson()
|
||||
} else {
|
||||
createNewLesson()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const createNewLesson = () => {
|
||||
@@ -230,6 +330,25 @@ const createNewLesson = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const editCurrentLesson = () => {
|
||||
editLesson.submit(
|
||||
{
|
||||
lesson: lessonDetails.data.lesson.name,
|
||||
},
|
||||
{
|
||||
validate() {
|
||||
return validateLesson()
|
||||
},
|
||||
onSuccess() {
|
||||
showToast('Success', 'Lesson updated successfully', 'check')
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.message, 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const validateLesson = () => {
|
||||
if (!lesson.title) {
|
||||
return 'Title is required'
|
||||
@@ -239,20 +358,6 @@ const validateLesson = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const addQuiz = () => {
|
||||
if (quiz.value) {
|
||||
editor.blocks.insert(
|
||||
'quiz',
|
||||
{
|
||||
quiz: quiz.value,
|
||||
},
|
||||
{},
|
||||
editor.blocks.getBlocksCount()
|
||||
)
|
||||
quiz.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const showToast = (title, text, icon) => {
|
||||
createToast({
|
||||
title: title,
|
||||
@@ -306,3 +411,16 @@ const breadcrumbs = computed(() => {
|
||||
return crumbs
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
.embed-tool__caption {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ce-toolbar__actions {
|
||||
right: 108%;
|
||||
}
|
||||
|
||||
.ce-block__content {
|
||||
max-width: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
2
frontend/src/pages/JobCreation.vue
Normal file
2
frontend/src/pages/JobCreation.vue
Normal file
@@ -0,0 +1,2 @@
|
||||
<template></template>
|
||||
<script setup></script>
|
||||
@@ -8,12 +8,14 @@
|
||||
:items="[{ label: __('Jobs'), route: { name: 'Jobs' } }]"
|
||||
/>
|
||||
<div class="flex">
|
||||
<Button v-if="user.data?.name" variant="solid">
|
||||
<template #prefix>
|
||||
<Plus class="h-4 w-4" />
|
||||
</template>
|
||||
{{ __('New Job') }}
|
||||
</Button>
|
||||
<router-link v-if="user.data?.name">
|
||||
<Button variant="solid">
|
||||
<template #prefix>
|
||||
<Plus class="h-4 w-4" />
|
||||
</template>
|
||||
{{ __('New Job') }}
|
||||
</Button>
|
||||
</router-link>
|
||||
</div>
|
||||
</header>
|
||||
<div v-if="jobs.data">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
||||
</header>
|
||||
<div class="grid grid-cols-[70%,30%] h-full">
|
||||
<div v-if="lesson.data.no_preview" class="border-r-2 text-center pt-10">
|
||||
<div v-if="lesson.data.no_preview" class="border-r-1 text-center pt-10">
|
||||
<p class="mb-4">
|
||||
{{
|
||||
__(
|
||||
@@ -111,74 +111,38 @@
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="lesson.data.content"
|
||||
v-for="content in JSON.parse(lesson.data.content).blocks"
|
||||
v-if="lesson.data.instructor_content && allowInstructorContent()"
|
||||
class="bg-gray-100 p-3 rounded-md mt-6"
|
||||
>
|
||||
<div class="text-gray-600 font-medium">
|
||||
{{ __('Instructor Notes') }}
|
||||
</div>
|
||||
<div
|
||||
id="instructor-content"
|
||||
class="ProseMirror prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-gray-300 prose-th:border-gray-300 prose-td:relative prose-th:relative prose-th:bg-gray-100 prose-sm max-w-none !whitespace-normal"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="lesson.data.instructor_notes"
|
||||
class="ProseMirror prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-gray-300 prose-th:border-gray-300 prose-td:relative prose-th:relative prose-th:bg-gray-100 prose-sm max-w-none !whitespace-normal mt-6"
|
||||
>
|
||||
<LessonContent :data="lesson.data.instructor_notes" />
|
||||
</div>
|
||||
<div
|
||||
v-if="lesson.data.content"
|
||||
class="ProseMirror prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-gray-300 prose-th:border-gray-300 prose-td:relative prose-th:relative prose-th:bg-gray-100 prose-sm max-w-none !whitespace-normal mt-5"
|
||||
>
|
||||
<div id="editor"></div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="ProseMirror prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-gray-300 prose-th:border-gray-300 prose-td:relative prose-th:relative prose-th:bg-gray-100 prose-sm max-w-none !whitespace-normal mt-6"
|
||||
class="ProseMirror prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-gray-300 prose-th:border-gray-300 prose-td:relative prose-th:relative prose-th:bg-gray-100 prose-sm max-w-none !whitespace-normal mt-5"
|
||||
>
|
||||
<div v-if="lesson.data.youtube">
|
||||
<iframe
|
||||
class="youtube-video"
|
||||
:src="getYouTubeVideoSource(lesson.data.youtube)"
|
||||
width="100%"
|
||||
height="400"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
||||
<div v-for="block in lesson.data.body.split('\n\n\n')">
|
||||
<div v-if="block.includes('{{ YouTubeVideo')">
|
||||
<iframe
|
||||
class="youtube-video"
|
||||
:src="getYouTubeVideoSource(block)"
|
||||
width="100%"
|
||||
height="400"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
||||
<div v-else-if="block.includes('{{ Quiz')">
|
||||
<Quiz :quiz="getId(block)" />
|
||||
</div>
|
||||
<div v-else-if="block.includes('{{ Video')">
|
||||
<video controls width="100%" controlsList="nodownload">
|
||||
<source :src="getId(block)" type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
<div v-else-if="block.includes('{{ PDF')">
|
||||
<iframe
|
||||
:src="getPDFSource(block)"
|
||||
width="100%"
|
||||
height="400"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
||||
<div v-else-if="block.includes('{{ Audio')">
|
||||
<audio width="100%" controls controlsList="nodownload">
|
||||
<source :src="getId(block)" type="audio/mp3" />
|
||||
</audio>
|
||||
</div>
|
||||
<div v-else-if="block.includes('{{ Embed')">
|
||||
<iframe
|
||||
width="100%"
|
||||
height="400"
|
||||
:src="getId(block)"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
>
|
||||
</iframe>
|
||||
</div>
|
||||
<div v-else v-html="markdown.render(block)"></div>
|
||||
</div>
|
||||
<div v-if="lesson.data.quiz_id">
|
||||
<Quiz :quiz="lesson.data.quiz_id" />
|
||||
</div>
|
||||
<LessonContent
|
||||
:content="lesson.data.body"
|
||||
:youtube="lesson.data.youtube"
|
||||
:quizId="lesson.data.quiz_id"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-20">
|
||||
<Discussions
|
||||
@@ -221,21 +185,15 @@ import { computed, watch, ref, inject, createApp } from 'vue'
|
||||
import CourseOutline from '@/components/CourseOutline.vue'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-vue-next'
|
||||
import Discussions from '@/components/Discussions.vue'
|
||||
import Quiz from '@/components/QuizBlock.vue'
|
||||
import { getEditorTools } from '../utils'
|
||||
import EditorJS from '@editorjs/editorjs'
|
||||
import LessonContent from '@/components/LessonContent.vue'
|
||||
|
||||
const user = inject('$user')
|
||||
const route = useRoute()
|
||||
let editor
|
||||
|
||||
const markdown = new MarkdownIt({
|
||||
html: true,
|
||||
linkify: true,
|
||||
})
|
||||
let editor, instructorEditor
|
||||
|
||||
const props = defineProps({
|
||||
courseName: {
|
||||
@@ -264,26 +222,31 @@ const lesson = createResource({
|
||||
},
|
||||
auto: true,
|
||||
onSuccess(data) {
|
||||
console.log(data)
|
||||
if (data.membership)
|
||||
current_lesson.submit({
|
||||
name: data.membership.name,
|
||||
lesson_name: data.name,
|
||||
})
|
||||
renderEditor()
|
||||
if (data.content) editor = renderEditor('editor', data.content)
|
||||
|
||||
if (data.instructor_content)
|
||||
instructorEditor = renderEditor(
|
||||
'instructor-content',
|
||||
data.instructor_content
|
||||
)
|
||||
|
||||
markProgress(data)
|
||||
},
|
||||
})
|
||||
|
||||
const renderEditor = () => {
|
||||
if (lesson.data?.content) {
|
||||
editor = new EditorJS({
|
||||
holder: 'editor',
|
||||
tools: getEditorTools(),
|
||||
data: JSON.parse(lesson.data.content),
|
||||
readOnly: true,
|
||||
})
|
||||
}
|
||||
const renderEditor = (holder, content) => {
|
||||
return new EditorJS({
|
||||
holder: holder,
|
||||
tools: getEditorTools(),
|
||||
data: JSON.parse(content),
|
||||
readOnly: true,
|
||||
defaultBlock: 'embed', // editor adds an empty block at the top, so to avoid that added default block as embed
|
||||
})
|
||||
}
|
||||
|
||||
const markProgress = (data) => {
|
||||
@@ -349,21 +312,6 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
const getYouTubeVideoSource = (block) => {
|
||||
if (block.includes('{{')) {
|
||||
block = getId(block)
|
||||
}
|
||||
return `https://www.youtube.com/embed/${block}`
|
||||
}
|
||||
|
||||
const getPDFSource = (block) => {
|
||||
return `${getId(block)}#toolbar=0`
|
||||
}
|
||||
|
||||
const getId = (block) => {
|
||||
return block.match(/\(["']([^"']+?)["']\)/)[1]
|
||||
}
|
||||
|
||||
const redirectToLogin = () => {
|
||||
window.location.href = `/login?redirect_to=/courses/${props.courseName}/learn/${route.params.chapterNumber}-${route.params.lessonNumber}`
|
||||
}
|
||||
@@ -377,12 +325,14 @@ const allowDiscussions = () => {
|
||||
}
|
||||
|
||||
const allowEdit = () => {
|
||||
if (user.data?.is_instructor) {
|
||||
return true
|
||||
}
|
||||
if (lesson.data?.instructor.includes(user.data?.name)) {
|
||||
return true
|
||||
}
|
||||
if (user.data?.is_moderator) return true
|
||||
if (lesson.data?.instructors.includes(user.data?.name)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
const allowInstructorContent = () => {
|
||||
if (user.data?.is_moderator) return true
|
||||
if (lesson.data?.instructors.includes(user.data?.name)) return true
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
@@ -437,4 +387,12 @@ const allowEdit = () => {
|
||||
text-decoration: underline;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.codex-editor__redactor {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.embed-tool__caption {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -84,8 +84,14 @@ const routes = [
|
||||
},
|
||||
{
|
||||
path: '/batches/:batchName/edit',
|
||||
name: 'CreateBatch',
|
||||
component: () => import('@/pages/CreateBatch.vue'),
|
||||
name: 'BatchCreation',
|
||||
component: () => import('@/pages/BatchCreation.vue'),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/batches/:batchName/edit',
|
||||
name: 'JobCreation',
|
||||
component: () => import('@/pages/JobCreation.vue'),
|
||||
props: true,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -2,10 +2,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 { Upload } from '@/utils/upload'
|
||||
import Header from '@editorjs/header'
|
||||
import Paragraph from '@editorjs/paragraph'
|
||||
import List from '@editorjs/list'
|
||||
import Embed from '@editorjs/embed'
|
||||
import NestedList from '@editorjs/nested-list'
|
||||
|
||||
export function createToast(options) {
|
||||
toast({
|
||||
@@ -69,10 +70,25 @@ export function getFileSize(file_size) {
|
||||
return value
|
||||
}
|
||||
|
||||
export function showToast(title, text, icon) {
|
||||
createToast({
|
||||
title: title,
|
||||
text: text,
|
||||
icon: icon,
|
||||
iconClasses:
|
||||
icon == 'check'
|
||||
? 'bg-green-600 text-white rounded-md p-px'
|
||||
: 'bg-red-600 text-white rounded-md p-px',
|
||||
position: icon == 'check' ? 'bottom-right' : 'top-center',
|
||||
timeout: icon == 'check' ? 5 : 10,
|
||||
})
|
||||
}
|
||||
|
||||
export function getEditorTools() {
|
||||
return {
|
||||
header: Header,
|
||||
quiz: Quiz,
|
||||
upload: Upload,
|
||||
paragraph: {
|
||||
class: Paragraph,
|
||||
inlineToolbar: true,
|
||||
@@ -80,9 +96,15 @@ export function getEditorTools() {
|
||||
preserveBlank: true,
|
||||
},
|
||||
},
|
||||
list: List,
|
||||
list: {
|
||||
class: NestedList,
|
||||
config: {
|
||||
defaultStyle: 'ordered',
|
||||
},
|
||||
},
|
||||
embed: {
|
||||
class: Embed,
|
||||
inlineToolbar: false,
|
||||
config: {
|
||||
services: {
|
||||
youtube: true,
|
||||
|
||||
@@ -4,13 +4,6 @@ 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
|
||||
@@ -42,7 +35,7 @@ export class Quiz {
|
||||
app.mount(this.wrapper)
|
||||
return
|
||||
}
|
||||
return `<div class='border rounded-md p-10 text-center'>
|
||||
return `<div class='border rounded-md p-10 text-center mb-2'>
|
||||
<span class="font-medium">
|
||||
Quiz: ${quiz}
|
||||
</span>
|
||||
|
||||
43
frontend/src/utils/upload.js
Normal file
43
frontend/src/utils/upload.js
Normal file
@@ -0,0 +1,43 @@
|
||||
export class Upload {
|
||||
constructor({ data, api, readOnly }) {
|
||||
this.data = data
|
||||
this.readOnly = readOnly
|
||||
}
|
||||
|
||||
static get isReadOnlySupported() {
|
||||
return true
|
||||
}
|
||||
|
||||
render() {
|
||||
this.wrapper = document.createElement('div')
|
||||
this.wrapper.innerHTML = this.renderUpload(this.data)
|
||||
return this.wrapper
|
||||
}
|
||||
|
||||
renderUpload(file) {
|
||||
if (file.file_type == 'video') {
|
||||
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') {
|
||||
return `<audio controls width='100%' controls controlsList='nodownload' class="mb-4">
|
||||
<source src=${encodeURI(file.file_url)} type='audio/mp3'>
|
||||
</audio>`
|
||||
} else if (file.file_type == 'pdf') {
|
||||
return `<iframe src="${encodeURI(
|
||||
file.file_url
|
||||
)}#toolbar=0" width='100%' height='700px' class="mb-4"></iframe>`
|
||||
} else {
|
||||
return `<img class="mb-4" src=${encodeURI(
|
||||
file.file_url
|
||||
)} width='100%'>`
|
||||
}
|
||||
}
|
||||
|
||||
save(blockContent) {
|
||||
return {
|
||||
file_url: this.data.file_url,
|
||||
file_type: this.data.file_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,6 +154,7 @@ def get_user_info():
|
||||
user["roles"] = frappe.get_roles(user.name)
|
||||
user.is_instructor = "Course Creator" in user.roles
|
||||
user.is_moderator = "Moderator" in user.roles
|
||||
user.is_evaluator = "Batch Evaluator" in user.roles
|
||||
return user
|
||||
|
||||
|
||||
|
||||
@@ -23,9 +23,11 @@
|
||||
"column_break_15",
|
||||
"file_type",
|
||||
"section_break_11",
|
||||
"body",
|
||||
"instructor_notes",
|
||||
"content",
|
||||
"body",
|
||||
"column_break_cjmf",
|
||||
"instructor_content",
|
||||
"instructor_notes",
|
||||
"help_section",
|
||||
"help"
|
||||
],
|
||||
@@ -143,11 +145,20 @@
|
||||
"fieldname": "content",
|
||||
"fieldtype": "Text",
|
||||
"label": "Content"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_cjmf",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "instructor_content",
|
||||
"fieldtype": "Text",
|
||||
"label": "Instructor Content"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-04 19:40:57.359033",
|
||||
"modified": "2024-03-14 14:25:22.464111",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Course Lesson",
|
||||
|
||||
@@ -1339,6 +1339,7 @@ def get_lesson(course, chapter, lesson):
|
||||
"instructor_notes",
|
||||
"course",
|
||||
"content",
|
||||
"instructor_content",
|
||||
],
|
||||
as_dict=True,
|
||||
)
|
||||
@@ -1761,7 +1762,15 @@ def get_lesson_creation_details(course, chapter, lesson):
|
||||
lesson_details = frappe.db.get_value(
|
||||
"Course Lesson",
|
||||
lesson_name,
|
||||
["name", "title", "include_in_preview", "body", "content", "instructor_notes"],
|
||||
[
|
||||
"name",
|
||||
"title",
|
||||
"include_in_preview",
|
||||
"body",
|
||||
"content",
|
||||
"instructor_notes",
|
||||
"instructor_content",
|
||||
],
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
|
||||
@@ -83,4 +83,5 @@ lms.patches.v1_0.create_batch_source
|
||||
lms.patches.v1_0.batch_tabs_settings
|
||||
execute:frappe.delete_doc("Notification", "Assignment Submission Notification")
|
||||
lms.patches.v1_0.change_jobs_url #19-01-2024
|
||||
lms.patches.v1_0.custom_perm_for_discussions #14-01-2024
|
||||
lms.patches.v1_0.custom_perm_for_discussions #14-01-2024
|
||||
lms.patches.v1_0.rename_evaluator_role
|
||||
6
lms/patches/v1_0/rename_evaluator_role.py
Normal file
6
lms/patches/v1_0/rename_evaluator_role.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
if frappe.db.exists("Role", "Class Evaluator"):
|
||||
frappe.rename_doc("Role", "Class Evaluator", "Batch Evaluator")
|
||||
417
yarn.lock
417
yarn.lock
@@ -203,14 +203,19 @@
|
||||
"@lezer/highlight" "^1.0.0"
|
||||
|
||||
"@codemirror/view@^6.0.0", "@codemirror/view@^6.16.0", "@codemirror/view@^6.23.0":
|
||||
version "6.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.25.0.tgz#7bf6cd15d1285e9354a83e39ee35cbf8f3957119"
|
||||
integrity sha512-XnMGOm6qXB8znzCko0N7k97qZayVdvqpA0JebxA5fHtgBjC/XlCPhH9TK92TahsoCKMPQlaTCUep06Dwj/+GXQ==
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.26.0.tgz#ab5a85aa8ebfb953cb5534e07d0a3751f9a3869a"
|
||||
integrity sha512-nSSmzONpqsNzshPOxiKhK203R6BvABepugAe34QfQDbNDslyjkqBuKgrK5ZBvqNXpfxz5iLrlGTmEfhbQyH46A==
|
||||
dependencies:
|
||||
"@codemirror/state" "^6.4.0"
|
||||
style-mod "^4.1.0"
|
||||
w3c-keyname "^2.2.4"
|
||||
|
||||
"@codexteam/icons@^0.0.2":
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.0.2.tgz#9183996a38b75a93506890373a015e3a2a369264"
|
||||
integrity sha512-KdeKj3TwaTHqM3IXd5YjeJP39PBUZTb+dtHjGlf5+b0VgsxYD4qzsZkb11lzopZbAuDsHaZJmAYQ8LFligIT6Q==
|
||||
|
||||
"@codexteam/icons@^0.0.4":
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.0.4.tgz#8b72dcd3f3a1b0d880bdceb2abebd74b46d3ae13"
|
||||
@@ -221,32 +226,37 @@
|
||||
resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.0.5.tgz#d17f39b6a0497c6439f57dd42711817a3dd3679c"
|
||||
integrity sha512-s6H2KXhLz2rgbMZSkRm8dsMJvyUNZsEjxobBEg9ztdrb1B2H3pEzY6iTwI4XUPJWJ3c3qRKwV4TrO3J5jUdoQA==
|
||||
|
||||
"@codexteam/icons@^0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.0.6.tgz#5553ada48dddf5940851ccc142cfe17835c36ad3"
|
||||
integrity sha512-L7Q5PET8PjKcBT5wp7VR+FCjwCi5PUp7rd/XjsgQ0CI5FJz0DphyHGRILMuDUdCW2MQT9NHbVr4QP31vwAkS/A==
|
||||
|
||||
"@codexteam/icons@^0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.3.0.tgz#62380b4053d487a257de443864b5c72dafab95e6"
|
||||
integrity sha512-fJE9dfFdgq8xU+sbsxjH0Kt8Yeatw9xHBJWb77DhRkEXz3OCoIS6hrRC1ewHEryxzIjxD8IyQrRq2f+Gz3BcmA==
|
||||
|
||||
"@docsearch/css@3.5.2", "@docsearch/css@^3.5.2":
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.5.2.tgz#610f47b48814ca94041df969d9fcc47b91fc5aac"
|
||||
integrity sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA==
|
||||
"@docsearch/css@3.6.0", "@docsearch/css@^3.5.2":
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.6.0.tgz#0e9f56f704b3a34d044d15fd9962ebc1536ba4fb"
|
||||
integrity sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==
|
||||
|
||||
"@docsearch/js@^3.5.2":
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@docsearch/js/-/js-3.5.2.tgz#a11cb2e7e62890e9e940283fed6972ecf632629d"
|
||||
integrity sha512-p1YFTCDflk8ieHgFJYfmyHBki1D61+U9idwrLh+GQQMrBSP3DLGKpy0XUJtPjAOPltcVbqsTjiPFfH7JImjUNg==
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@docsearch/js/-/js-3.6.0.tgz#f9e46943449b9092d874944f7a80bcc071004cfb"
|
||||
integrity sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==
|
||||
dependencies:
|
||||
"@docsearch/react" "3.5.2"
|
||||
"@docsearch/react" "3.6.0"
|
||||
preact "^10.0.0"
|
||||
|
||||
"@docsearch/react@3.5.2":
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.5.2.tgz#2e6bbee00eb67333b64906352734da6aef1232b9"
|
||||
integrity sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng==
|
||||
"@docsearch/react@3.6.0":
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.6.0.tgz#b4f25228ecb7fc473741aefac592121e86dd2958"
|
||||
integrity sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==
|
||||
dependencies:
|
||||
"@algolia/autocomplete-core" "1.9.3"
|
||||
"@algolia/autocomplete-preset-algolia" "1.9.3"
|
||||
"@docsearch/css" "3.5.2"
|
||||
"@docsearch/css" "3.6.0"
|
||||
algoliasearch "^4.19.1"
|
||||
|
||||
"@editorjs/checklist@^1.6.0":
|
||||
@@ -273,12 +283,19 @@
|
||||
dependencies:
|
||||
"@codexteam/icons" "^0.0.5"
|
||||
|
||||
"@editorjs/list@^1.9.0":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@editorjs/list/-/list-1.9.0.tgz#5242095d0cee38671d852b02aa02293f54400f2c"
|
||||
integrity sha512-BQEvZW4vi0O0dBvGNljiKxiE89vMSHoM2Tu2OzKUndoj7pY9AxqpgCh1qvwIVsJAlG4Lbt/vBFQilnoStMmI6A==
|
||||
"@editorjs/image@^2.9.0":
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@editorjs/image/-/image-2.9.0.tgz#0c83252d569a0dc3af14c3f7d16b6df033b9c37b"
|
||||
integrity sha512-xItihKJFiWJ06SMtLWQZvzHv4LRPNAFZYaHAXesBFzXvWwUrtVaVMcNSf0eNnw3InrPO3Po1vZRRgpsT+Ya3Bg==
|
||||
dependencies:
|
||||
"@codexteam/icons" "^0.0.4"
|
||||
"@codexteam/icons" "^0.0.6"
|
||||
|
||||
"@editorjs/nested-list@^1.4.2":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@editorjs/nested-list/-/nested-list-1.4.2.tgz#2b47b9c3ee1ce11dec02eae0b176bd4107360847"
|
||||
integrity sha512-qb1dAoJ+bihqmlR3822TC2GuIxEjTCLTZsZVWNces3uJIZ+W4019G3IJKBt/MOOgz4Evzad/RvUEKwPCPe6YOQ==
|
||||
dependencies:
|
||||
"@codexteam/icons" "^0.0.2"
|
||||
|
||||
"@editorjs/paragraph@^2.11.3":
|
||||
version "2.11.3"
|
||||
@@ -680,9 +697,9 @@
|
||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||
|
||||
"@polka/url@^1.0.0-next.24":
|
||||
version "1.0.0-next.24"
|
||||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.24.tgz#58601079e11784d20f82d0585865bb42305c4df3"
|
||||
integrity sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==
|
||||
version "1.0.0-next.25"
|
||||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.25.tgz#f077fdc0b5d0078d30893396ff4827a13f99e817"
|
||||
integrity sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==
|
||||
|
||||
"@popperjs/core@^2.11.2", "@popperjs/core@^2.9.0":
|
||||
version "2.11.8"
|
||||
@@ -694,96 +711,70 @@
|
||||
resolved "https://registry.yarnpkg.com/@remirror/core-constants/-/core-constants-2.0.2.tgz#f05eccdc69e3a65e7d524b52548f567904a11a1a"
|
||||
integrity sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==
|
||||
|
||||
"@remirror/core-helpers@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@remirror/core-helpers/-/core-helpers-3.0.0.tgz#3a35c2346bc23ebc3cee585b7840b5567755c5f1"
|
||||
integrity sha512-tusEgQJIqg4qKj6HSBUFcyRnWnziw3neh4T9wOmsPGHFC3w9kl5KSrDb9UAgE8uX6y32FnS7vJ955mWOl3n50A==
|
||||
dependencies:
|
||||
"@remirror/core-constants" "^2.0.2"
|
||||
"@remirror/types" "^1.0.1"
|
||||
"@types/object.omit" "^3.0.0"
|
||||
"@types/object.pick" "^1.3.2"
|
||||
"@types/throttle-debounce" "^2.1.0"
|
||||
case-anything "^2.1.13"
|
||||
dash-get "^1.0.2"
|
||||
deepmerge "^4.3.1"
|
||||
fast-deep-equal "^3.1.3"
|
||||
make-error "^1.3.6"
|
||||
object.omit "^3.0.0"
|
||||
object.pick "^1.3.0"
|
||||
throttle-debounce "^3.0.1"
|
||||
"@rollup/rollup-android-arm-eabi@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz#b98786c1304b4ff8db3a873180b778649b5dff2b"
|
||||
integrity sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==
|
||||
|
||||
"@remirror/types@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@remirror/types/-/types-1.0.1.tgz#768502497a0fbbc23338a1586b893f729310cf70"
|
||||
integrity sha512-VlZQxwGnt1jtQ18D6JqdIF+uFZo525WEqrfp9BOc3COPpK4+AWCgdnAWL+ho6imWcoINlGjR/+3b6y5C1vBVEA==
|
||||
dependencies:
|
||||
type-fest "^2.19.0"
|
||||
"@rollup/rollup-android-arm64@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz#8833679af11172b1bf1ab7cb3bad84df4caf0c9e"
|
||||
integrity sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==
|
||||
|
||||
"@rollup/rollup-android-arm-eabi@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz#38c3abd1955a3c21d492af6b1a1dca4bb1d894d6"
|
||||
integrity sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==
|
||||
"@rollup/rollup-darwin-arm64@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz#ef02d73e0a95d406e0eb4fd61a53d5d17775659b"
|
||||
integrity sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==
|
||||
|
||||
"@rollup/rollup-android-arm64@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz#3822e929f415627609e53b11cec9a4be806de0e2"
|
||||
integrity sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==
|
||||
"@rollup/rollup-darwin-x64@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz#3ce5b9bcf92b3341a5c1c58a3e6bcce0ea9e7455"
|
||||
integrity sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==
|
||||
|
||||
"@rollup/rollup-darwin-arm64@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz#6c082de71f481f57df6cfa3701ab2a7afde96f69"
|
||||
integrity sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz#3d3d2c018bdd8e037c6bfedd52acfff1c97e4be4"
|
||||
integrity sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==
|
||||
|
||||
"@rollup/rollup-darwin-x64@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz#c34ca0d31f3c46a22c9afa0e944403eea0edcfd8"
|
||||
integrity sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==
|
||||
"@rollup/rollup-linux-arm64-gnu@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz#5fc8cc978ff396eaa136d7bfe05b5b9138064143"
|
||||
integrity sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz#48e899c1e438629c072889b824a98787a7c2362d"
|
||||
integrity sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==
|
||||
"@rollup/rollup-linux-arm64-musl@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz#f2ae7d7bed416ffa26d6b948ac5772b520700eef"
|
||||
integrity sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz#788c2698a119dc229062d40da6ada8a090a73a68"
|
||||
integrity sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz#303d57a328ee9a50c85385936f31cf62306d30b6"
|
||||
integrity sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz#3882a4e3a564af9e55804beeb67076857b035ab7"
|
||||
integrity sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==
|
||||
"@rollup/rollup-linux-x64-gnu@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz#f672f6508f090fc73f08ba40ff76c20b57424778"
|
||||
integrity sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz#0c6ad792e1195c12bfae634425a3d2aa0fe93ab7"
|
||||
integrity sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==
|
||||
"@rollup/rollup-linux-x64-musl@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz#d2f34b1b157f3e7f13925bca3288192a66755a89"
|
||||
integrity sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz#9d62485ea0f18d8674033b57aa14fb758f6ec6e3"
|
||||
integrity sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==
|
||||
"@rollup/rollup-win32-arm64-msvc@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz#8ffecc980ae4d9899eb2f9c4ae471a8d58d2da6b"
|
||||
integrity sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==
|
||||
|
||||
"@rollup/rollup-linux-x64-musl@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz#50e8167e28b33c977c1f813def2b2074d1435e05"
|
||||
integrity sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==
|
||||
"@rollup/rollup-win32-ia32-msvc@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz#a7505884f415662e088365b9218b2b03a88fc6f2"
|
||||
integrity sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz#68d233272a2004429124494121a42c4aebdc5b8e"
|
||||
integrity sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz#366ca62221d1689e3b55a03f4ae12ae9ba595d40"
|
||||
integrity sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz#9ffdf9ed133a7464f4ae187eb9e1294413fab235"
|
||||
integrity sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==
|
||||
"@rollup/rollup-win32-x64-msvc@4.13.0":
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz#6abd79db7ff8d01a58865ba20a63cfd23d9e2a10"
|
||||
integrity sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==
|
||||
|
||||
"@shikijs/core@1.1.7", "@shikijs/core@^1.1.5":
|
||||
version "1.1.7"
|
||||
@@ -1118,27 +1109,12 @@
|
||||
integrity sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==
|
||||
|
||||
"@types/node@*":
|
||||
version "20.11.24"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.24.tgz#cc207511104694e84e9fb17f9a0c4c42d4517792"
|
||||
integrity sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==
|
||||
version "20.11.28"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.28.tgz#4fd5b2daff2e580c12316e457473d68f15ee6f66"
|
||||
integrity sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
"@types/object.omit@^3.0.0":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/object.omit/-/object.omit-3.0.3.tgz#cc52b1d9774c1619b5c6fc50229d087f01eabd68"
|
||||
integrity sha512-xrq4bQTBGYY2cw+gV4PzoG2Lv3L0pjZ1uXStRRDQoATOYW1lCsFQHhQ+OkPhIcQoqLjAq7gYif7D14Qaa6Zbew==
|
||||
|
||||
"@types/object.pick@^1.3.2":
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/object.pick/-/object.pick-1.3.4.tgz#1a38b6e69a35f36ec2dcc8b9f5ffd555c1c4d7fc"
|
||||
integrity sha512-5PjwB0uP2XDp3nt5u5NJAG2DORHIRClPzWT/TTZhJ2Ekwe8M5bA9tvPdi9NO/n2uvu2/ictat8kgqvLfcIE1SA==
|
||||
|
||||
"@types/throttle-debounce@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz#1c3df624bfc4b62f992d3012b84c56d41eab3776"
|
||||
integrity sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==
|
||||
|
||||
"@types/web-bluetooth@^0.0.20":
|
||||
version "0.0.20"
|
||||
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597"
|
||||
@@ -1202,27 +1178,27 @@
|
||||
integrity sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==
|
||||
|
||||
"@vue/devtools-api@^7.0.14":
|
||||
version "7.0.16"
|
||||
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-7.0.16.tgz#aa199ebb597bab697876d9555feb35c98415a782"
|
||||
integrity sha512-fZG2CG8624qphMf4aj59zNHckMx1G3lxODUuyM9USKuLznXCh66TP+tEbPOCcml16hA0GizJ4D8w6F34hrfbcw==
|
||||
version "7.0.17"
|
||||
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-7.0.17.tgz#6251541c3a9f6a4e0cbdef56b2c17d762aa115b0"
|
||||
integrity sha512-UWU9tqzUBv+ttUxYLaQcL5IxSSdF+i6yheFiEtz7mh88YZUYkxpEmT43iKBs3YsC54ROwPD2iZIndnju6PWfOQ==
|
||||
dependencies:
|
||||
"@vue/devtools-kit" "^7.0.16"
|
||||
"@vue/devtools-kit" "^7.0.17"
|
||||
|
||||
"@vue/devtools-kit@^7.0.16":
|
||||
version "7.0.16"
|
||||
resolved "https://registry.yarnpkg.com/@vue/devtools-kit/-/devtools-kit-7.0.16.tgz#83f906078c6a7dc74db4c24859c3989f17c9741f"
|
||||
integrity sha512-IA8SSGiZbNgOi4wLT3mRvd71Q9KE0KvMfGk6haa2GZ6bL2K/xMA8Fvvj3o1maspfUXrGcCXutaqbLqbGx/espQ==
|
||||
"@vue/devtools-kit@^7.0.17":
|
||||
version "7.0.17"
|
||||
resolved "https://registry.yarnpkg.com/@vue/devtools-kit/-/devtools-kit-7.0.17.tgz#d918d1717a4837fd1226e2e0510c78d026e2ddfe"
|
||||
integrity sha512-znPLSOoTP3RnR9fvkq5M+nnpEA+WocybzOo5ID73vYkE0/n0VcfU8Ld0j4AHQjV/omTdAzh6QLpPlUYdIHXg+w==
|
||||
dependencies:
|
||||
"@vue/devtools-shared" "^7.0.16"
|
||||
"@vue/devtools-shared" "^7.0.17"
|
||||
hookable "^5.5.3"
|
||||
mitt "^3.0.1"
|
||||
perfect-debounce "^1.0.0"
|
||||
speakingurl "^14.0.1"
|
||||
|
||||
"@vue/devtools-shared@^7.0.16":
|
||||
version "7.0.16"
|
||||
resolved "https://registry.yarnpkg.com/@vue/devtools-shared/-/devtools-shared-7.0.16.tgz#a18ea93d5aa60ca4e1df83d3116b663582899e09"
|
||||
integrity sha512-Lew4FrGjDjmanaUWSueNE1Rre83k7jQpttc17MaoVw0eARWU5DgZ1F/g9GNUMZXVjbP9rwE+LL3gd9XfXCfkvA==
|
||||
"@vue/devtools-shared@^7.0.17":
|
||||
version "7.0.17"
|
||||
resolved "https://registry.yarnpkg.com/@vue/devtools-shared/-/devtools-shared-7.0.17.tgz#e893d8f55deeffeed35038eddf8ed63466590ee8"
|
||||
integrity sha512-QNg2TMQBFFffRbTKE9NjytXBywGR77p2UMi/gJ0ow58S+1jkAvL8ikU/JnSs9ePvsVtspHX32m2cdfe4DJ4ygw==
|
||||
dependencies:
|
||||
rfdc "^1.3.1"
|
||||
|
||||
@@ -1426,9 +1402,9 @@ balanced-match@^1.0.0:
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
|
||||
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
|
||||
|
||||
birpc@^0.1.1:
|
||||
version "0.1.1"
|
||||
@@ -1483,9 +1459,9 @@ camelcase-css@^2.0.1:
|
||||
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
|
||||
|
||||
caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001591:
|
||||
version "1.0.30001593"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001593.tgz#7cda1d9e5b0cad6ebab4133b1f239d4ea44fe659"
|
||||
integrity sha512-UWM1zlo3cZfkpBysd7AS+z+v007q9G1+fLTUU42rQnY6t2axoogPW/xol6T7juU5EUoOhML4WgBIdG+9yYqAjQ==
|
||||
version "1.0.30001597"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz#8be94a8c1d679de23b22fbd944232aa1321639e6"
|
||||
integrity sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==
|
||||
|
||||
capital-case@^1.0.4:
|
||||
version "1.0.4"
|
||||
@@ -1496,11 +1472,6 @@ capital-case@^1.0.4:
|
||||
tslib "^2.0.3"
|
||||
upper-case-first "^2.0.2"
|
||||
|
||||
case-anything@^2.1.13:
|
||||
version "2.1.13"
|
||||
resolved "https://registry.yarnpkg.com/case-anything/-/case-anything-2.1.13.tgz#0cdc16278cb29a7fcdeb072400da3f342ba329e9"
|
||||
integrity sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==
|
||||
|
||||
chalk@5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385"
|
||||
@@ -1687,11 +1658,6 @@ csstype@^3.1.3:
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
||||
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||
|
||||
dash-get@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/dash-get/-/dash-get-1.0.2.tgz#4c9e9ad5ef04c4bf9d3c9a451f6f7997298dcc7c"
|
||||
integrity sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==
|
||||
|
||||
data-urls@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143"
|
||||
@@ -1725,11 +1691,6 @@ decimal.js@^10.4.2:
|
||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
|
||||
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
|
||||
|
||||
deepmerge@^4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
|
||||
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
|
||||
|
||||
defu@^6.1.2:
|
||||
version "6.1.4"
|
||||
resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479"
|
||||
@@ -1788,9 +1749,9 @@ ee-first@1.1.1:
|
||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||
|
||||
electron-to-chromium@^1.4.668:
|
||||
version "1.4.690"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.690.tgz#dd5145d45c49c08a9a6f7454127e660bdf9a3fa7"
|
||||
integrity sha512-+2OAGjUx68xElQhydpcbqH50hE8Vs2K6TkAeLhICYfndb67CVH0UsZaijmRUE3rHlIxU1u0jxwhgVe6fK3YANA==
|
||||
version "1.4.707"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.707.tgz#77904f87432b8b50b8b8b654ba3940d2bef48d63"
|
||||
integrity sha512-qRq74Mo7ChePOU6GHdfAJ0NREXU8vQTlVlfWz3wNygFay6xrd/fY2J7oGHwrhFeU30OVctGLdTh/FcnokTWpng==
|
||||
|
||||
emoji-regex@^10.3.0:
|
||||
version "10.3.0"
|
||||
@@ -1968,11 +1929,6 @@ extend-shallow@^2.0.1:
|
||||
dependencies:
|
||||
is-extendable "^0.1.0"
|
||||
|
||||
fast-deep-equal@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-glob@^3.3.0:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
|
||||
@@ -2053,10 +2009,10 @@ fraction.js@^4.3.7:
|
||||
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
|
||||
integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==
|
||||
|
||||
frappe-ui@^0.1.31:
|
||||
version "0.1.33"
|
||||
resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.1.33.tgz#ede5bc4dde1ac0f7fbc3b7a4b6e4a1fa7c20fd5e"
|
||||
integrity sha512-scWkhNh70zmFXaPZlMFT+0DhX1CY/JI3hK6ehZBGQcRfX79GVo9ST6JJ+Odv/nxVc7/r4CDBpOPWGdVrCBy+4Q==
|
||||
frappe-ui@^0.1.35:
|
||||
version "0.1.35"
|
||||
resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.1.35.tgz#fde57d5935745e761c7bdf7857c3103d4ef58358"
|
||||
integrity sha512-8oxA4sADc/WPG9yo39L1ZHYCsrITq2Y8K/m73jSIzY+N4Y34qSHUvbg004ylmFkfAhTDEcINqub3vifTElWZQw==
|
||||
dependencies:
|
||||
"@headlessui/vue" "^1.7.14"
|
||||
"@popperjs/core" "^2.11.2"
|
||||
@@ -2167,9 +2123,9 @@ gray-matter@^4.0.3:
|
||||
strip-bom-string "^1.0.0"
|
||||
|
||||
hasown@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa"
|
||||
integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
|
||||
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
|
||||
@@ -2298,13 +2254,6 @@ is-extendable@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
|
||||
integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==
|
||||
|
||||
is-extendable@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
|
||||
integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
|
||||
dependencies:
|
||||
is-plain-object "^2.0.4"
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
@@ -2339,13 +2288,6 @@ is-number@^7.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
is-plain-object@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
||||
integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
|
||||
dependencies:
|
||||
isobject "^3.0.1"
|
||||
|
||||
is-potential-custom-element-name@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
|
||||
@@ -2361,11 +2303,6 @@ isexe@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||
|
||||
isobject@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||
integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
|
||||
|
||||
jackspeak@^2.3.5:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8"
|
||||
@@ -2564,11 +2501,6 @@ magic-string@^0.30.7:
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||
|
||||
make-error@^1.3.6:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||
|
||||
mark.js@8.11.1:
|
||||
version "8.11.1"
|
||||
resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5"
|
||||
@@ -2799,20 +2731,6 @@ object-hash@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
|
||||
integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
|
||||
|
||||
object.omit@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-3.0.0.tgz#0e3edc2fce2ba54df5577ff529f6d97bd8a522af"
|
||||
integrity sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==
|
||||
dependencies:
|
||||
is-extendable "^1.0.0"
|
||||
|
||||
object.pick@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
|
||||
integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==
|
||||
dependencies:
|
||||
isobject "^3.0.1"
|
||||
|
||||
on-finished@~2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||
@@ -3000,9 +2918,9 @@ postcss-selector-parser@6.0.10:
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
postcss-selector-parser@^6.0.11:
|
||||
version "6.0.15"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535"
|
||||
integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==
|
||||
version "6.0.16"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz#3b88b9f5c5abd989ef4e2fc9ec8eedd34b20fb04"
|
||||
integrity sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==
|
||||
dependencies:
|
||||
cssesc "^3.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
@@ -3166,12 +3084,11 @@ prosemirror-tables@^1.3.5:
|
||||
prosemirror-view "^1.13.3"
|
||||
|
||||
prosemirror-trailing-node@^2.0.7:
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.7.tgz#ba782a7929f18bcae650b1c7082a2d10443eab19"
|
||||
integrity sha512-8zcZORYj/8WEwsGo6yVCRXFMOfBo0Ub3hCUvmoWIZYfMP26WqENU0mpEP27w7mt8buZWuGrydBewr0tOArPb1Q==
|
||||
version "2.0.8"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.8.tgz#233ddcbda72de06f9b5d758d2a65a8cac482ea10"
|
||||
integrity sha512-ujRYhSuhQb1Jsarh1IHqb2KoSnRiD7wAMDGucP35DN7j5af6X7B18PfdPIrbwsPTqIAj0fyOvxbuPsWhNvylmA==
|
||||
dependencies:
|
||||
"@remirror/core-constants" "^2.0.2"
|
||||
"@remirror/core-helpers" "^3.0.0"
|
||||
escape-string-regexp "^4.0.0"
|
||||
|
||||
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.7.3, prosemirror-transform@^1.8.0:
|
||||
@@ -3269,25 +3186,25 @@ rollup@^3.27.1:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rollup@^4.2.0:
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.12.0.tgz#0b6d1e5f3d46bbcf244deec41a7421dc54cc45b5"
|
||||
integrity sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.13.0.tgz#dd2ae144b4cdc2ea25420477f68d4937a721237a"
|
||||
integrity sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==
|
||||
dependencies:
|
||||
"@types/estree" "1.0.5"
|
||||
optionalDependencies:
|
||||
"@rollup/rollup-android-arm-eabi" "4.12.0"
|
||||
"@rollup/rollup-android-arm64" "4.12.0"
|
||||
"@rollup/rollup-darwin-arm64" "4.12.0"
|
||||
"@rollup/rollup-darwin-x64" "4.12.0"
|
||||
"@rollup/rollup-linux-arm-gnueabihf" "4.12.0"
|
||||
"@rollup/rollup-linux-arm64-gnu" "4.12.0"
|
||||
"@rollup/rollup-linux-arm64-musl" "4.12.0"
|
||||
"@rollup/rollup-linux-riscv64-gnu" "4.12.0"
|
||||
"@rollup/rollup-linux-x64-gnu" "4.12.0"
|
||||
"@rollup/rollup-linux-x64-musl" "4.12.0"
|
||||
"@rollup/rollup-win32-arm64-msvc" "4.12.0"
|
||||
"@rollup/rollup-win32-ia32-msvc" "4.12.0"
|
||||
"@rollup/rollup-win32-x64-msvc" "4.12.0"
|
||||
"@rollup/rollup-android-arm-eabi" "4.13.0"
|
||||
"@rollup/rollup-android-arm64" "4.13.0"
|
||||
"@rollup/rollup-darwin-arm64" "4.13.0"
|
||||
"@rollup/rollup-darwin-x64" "4.13.0"
|
||||
"@rollup/rollup-linux-arm-gnueabihf" "4.13.0"
|
||||
"@rollup/rollup-linux-arm64-gnu" "4.13.0"
|
||||
"@rollup/rollup-linux-arm64-musl" "4.13.0"
|
||||
"@rollup/rollup-linux-riscv64-gnu" "4.13.0"
|
||||
"@rollup/rollup-linux-x64-gnu" "4.13.0"
|
||||
"@rollup/rollup-linux-x64-musl" "4.13.0"
|
||||
"@rollup/rollup-win32-arm64-msvc" "4.13.0"
|
||||
"@rollup/rollup-win32-ia32-msvc" "4.13.0"
|
||||
"@rollup/rollup-win32-x64-msvc" "4.13.0"
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rope-sequence@^1.3.0:
|
||||
@@ -3423,9 +3340,9 @@ snake-case@^3.0.4:
|
||||
tslib "^2.0.3"
|
||||
|
||||
socket.io-client@^4.5.1, socket.io-client@^4.7.2:
|
||||
version "4.7.4"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.4.tgz#5f0e060ff34ac0a4b4c5abaaa88e0d1d928c64c8"
|
||||
integrity sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==
|
||||
version "4.7.5"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.5.tgz#919be76916989758bdc20eec63f7ee0ae45c05b7"
|
||||
integrity sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==
|
||||
dependencies:
|
||||
"@socket.io/component-emitter" "~3.1.0"
|
||||
debug "~4.3.2"
|
||||
@@ -3604,11 +3521,6 @@ thenify-all@^1.0.0:
|
||||
dependencies:
|
||||
any-promise "^1.0.0"
|
||||
|
||||
throttle-debounce@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
|
||||
integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==
|
||||
|
||||
tippy.js@^6.3.7:
|
||||
version "6.3.7"
|
||||
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"
|
||||
@@ -3660,20 +3572,15 @@ tslib@^2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
|
||||
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
|
||||
|
||||
type-fest@^2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
|
||||
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
|
||||
|
||||
type-fest@^3.0.0:
|
||||
version "3.13.1"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706"
|
||||
integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==
|
||||
|
||||
typescript@^5.0.2:
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
|
||||
integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
|
||||
version "5.4.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.2.tgz#0ae9cebcfae970718474fe0da2c090cad6577372"
|
||||
integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==
|
||||
|
||||
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||
version "1.0.6"
|
||||
@@ -3776,9 +3683,9 @@ vite-node@0.28.4:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
vite@^5.0.11, vite@^5.1.3:
|
||||
version "5.1.4"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.1.4.tgz#14e9d3e7a6e488f36284ef13cebe149f060bcfb6"
|
||||
integrity sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==
|
||||
version "5.1.6"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.1.6.tgz#706dae5fab9e97f57578469eef1405fc483943e4"
|
||||
integrity sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==
|
||||
dependencies:
|
||||
esbuild "^0.19.3"
|
||||
postcss "^8.4.35"
|
||||
@@ -3787,9 +3694,9 @@ vite@^5.0.11, vite@^5.1.3:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
vitepress@^1.0.0-alpha.29:
|
||||
version "1.0.0-rc.44"
|
||||
resolved "https://registry.yarnpkg.com/vitepress/-/vitepress-1.0.0-rc.44.tgz#01bce883761c22de42b9869a95f04bd02cbb8cdb"
|
||||
integrity sha512-tO5taxGI7fSpBK1D8zrZTyJJERlyU9nnt0jHSt3fywfq3VKn977Hg0wUuTkEmwXlFYwuW26+6+3xorf4nD3XvA==
|
||||
version "1.0.0-rc.45"
|
||||
resolved "https://registry.yarnpkg.com/vitepress/-/vitepress-1.0.0-rc.45.tgz#1cb41f53fa084c224dd2d910137ef7b2e8c0c191"
|
||||
integrity sha512-/OiYsu5UKpQKA2c0BAZkfyywjfauDjvXyv6Mo4Ra57m5n4Bxg1HgUGoth1CLH2vwUbR/BHvDA9zOM0RDvgeSVQ==
|
||||
dependencies:
|
||||
"@docsearch/css" "^3.5.2"
|
||||
"@docsearch/js" "^3.5.2"
|
||||
@@ -3950,6 +3857,6 @@ yaml@2.3.4:
|
||||
integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==
|
||||
|
||||
yaml@^2.3.4:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.0.tgz#2376db1083d157f4b3a452995803dbcf43b08140"
|
||||
integrity sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed"
|
||||
integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==
|
||||
|
||||
Reference in New Issue
Block a user