1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@@ -83,6 +83,7 @@ declare module 'vue' {
|
||||
QuizBlock: typeof import('./src/components/QuizBlock.vue')['default']
|
||||
QuizInVideo: typeof import('./src/components/Modals/QuizInVideo.vue')['default']
|
||||
Rating: typeof import('./src/components/Controls/Rating.vue')['default']
|
||||
RelatedCourses: typeof import('./src/components/RelatedCourses.vue')['default']
|
||||
ReviewModal: typeof import('./src/components/Modals/ReviewModal.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<FrappeUIProvider>
|
||||
<Layout>
|
||||
<router-view />
|
||||
<div class="text-base">
|
||||
<router-view />
|
||||
</div>
|
||||
</Layout>
|
||||
<Dialogs />
|
||||
</FrappeUIProvider>
|
||||
|
||||
@@ -191,7 +191,7 @@ import {
|
||||
h,
|
||||
onUnmounted,
|
||||
} from 'vue'
|
||||
import { getSidebarLinks } from '../utils'
|
||||
import { getSidebarLinks } from '@/utils'
|
||||
import { usersStore } from '@/stores/user'
|
||||
import { sessionStore } from '@/stores/session'
|
||||
import { useSidebar } from '@/stores/sidebar'
|
||||
|
||||
@@ -70,9 +70,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Badge } from 'frappe-ui'
|
||||
import { formatTime } from '../utils'
|
||||
import { Clock, BookOpen, Globe } from 'lucide-vue-next'
|
||||
import { formatTime } from '@/utils'
|
||||
import { Clock, Globe } from 'lucide-vue-next'
|
||||
import DateRange from '@/components/Common/DateRange.vue'
|
||||
import CourseInstructors from '@/components/CourseInstructors.vue'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { Button, createResource, Tooltip, toast } from 'frappe-ui'
|
||||
import { getCurrentInstance, inject, ref } from 'vue'
|
||||
import { getCurrentInstance, inject, ref, watch } from 'vue'
|
||||
import Draggable from 'vuedraggable'
|
||||
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
|
||||
import {
|
||||
@@ -197,13 +197,22 @@ const props = defineProps({
|
||||
const outline = createResource({
|
||||
url: 'lms.lms.utils.get_course_outline',
|
||||
cache: ['course_outline', props.courseName],
|
||||
params: {
|
||||
course: props.courseName,
|
||||
progress: props.getProgress,
|
||||
makeParams() {
|
||||
return {
|
||||
course: props.courseName,
|
||||
progress: props.getProgress,
|
||||
}
|
||||
},
|
||||
auto: true,
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.courseName,
|
||||
() => {
|
||||
outline.reload()
|
||||
}
|
||||
)
|
||||
|
||||
const deleteLesson = createResource({
|
||||
url: 'lms.lms.api.delete_lesson',
|
||||
makeParams(values) {
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<script setup>
|
||||
import { Star } from 'lucide-vue-next'
|
||||
import { createResource, Button } from 'frappe-ui'
|
||||
import { computed, ref, inject } from 'vue'
|
||||
import { watch, ref, inject } from 'vue'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
import ReviewModal from '@/components/Modals/ReviewModal.vue'
|
||||
|
||||
@@ -101,12 +101,21 @@ const hasReviewed = createResource({
|
||||
const reviews = createResource({
|
||||
url: 'lms.lms.utils.get_reviews',
|
||||
cache: ['course_reviews', props.courseName],
|
||||
params: {
|
||||
course: props.courseName,
|
||||
makeParams() {
|
||||
return {
|
||||
course: props.courseName,
|
||||
}
|
||||
},
|
||||
auto: true,
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.courseName,
|
||||
() => {
|
||||
reviews.reload()
|
||||
}
|
||||
)
|
||||
|
||||
const showReviewModal = ref(false)
|
||||
|
||||
function openReviewModal() {
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { createResource, TextEditor, Button, Dropdown, toast } from 'frappe-ui'
|
||||
import { timeAgo } from '../utils'
|
||||
import { timeAgo } from '@/utils'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
import { ChevronLeft, MoreHorizontal } from 'lucide-vue-next'
|
||||
import { ref, inject, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<script setup>
|
||||
import { createResource, Button } from 'frappe-ui'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
import { singularize, timeAgo } from '../utils'
|
||||
import { singularize, timeAgo } from '@/utils'
|
||||
import { ref, onMounted, inject, onUnmounted } from 'vue'
|
||||
import DiscussionReplies from '@/components/DiscussionReplies.vue'
|
||||
import DiscussionModal from '@/components/Modals/DiscussionModal.vue'
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { getSidebarLinks } from '../utils'
|
||||
import { getSidebarLinks } from '@/utils'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { watch, ref, onMounted } from 'vue'
|
||||
import { sessionStore } from '@/stores/session'
|
||||
|
||||
@@ -3,44 +3,59 @@
|
||||
v-model="show"
|
||||
:options="{
|
||||
title: __('Attendance for Class - {0}').format(live_class?.title),
|
||||
size: 'xl',
|
||||
size: '4xl',
|
||||
}"
|
||||
>
|
||||
<template #body-content>
|
||||
<div class="space-y-5">
|
||||
<div
|
||||
class="grid grid-cols-2 gap-12 text-sm font-semibold text-ink-gray-5 pb-2"
|
||||
>
|
||||
<div>
|
||||
{{ __('Member') }}
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-20">
|
||||
<div>
|
||||
{{ __('Joined at') }}
|
||||
</div>
|
||||
<div class="text-center">
|
||||
{{ __('Left at') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ __('Attended for') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divide-y text-base">
|
||||
<div
|
||||
v-for="participant in participants.data"
|
||||
@click="redirectToProfile(participant.member_username)"
|
||||
class="cursor-pointer text-base w-fit"
|
||||
class="grid grid-cols-2 items-center w-full text-base w-fit py-2"
|
||||
>
|
||||
<Tooltip placement="right">
|
||||
<div class="flex items-center space-x-2">
|
||||
<Avatar
|
||||
:image="participant.member_image"
|
||||
:label="participant.member_name"
|
||||
size="xl"
|
||||
/>
|
||||
<div class="space-y-1">
|
||||
<div class="font-medium">
|
||||
{{ participant.member_name }}
|
||||
</div>
|
||||
<div>
|
||||
{{ participant.member }}
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<Avatar
|
||||
:image="participant.member_image"
|
||||
:label="participant.member_name"
|
||||
size="xl"
|
||||
/>
|
||||
<div class="space-y-1">
|
||||
<div class="font-medium">
|
||||
{{ participant.member_name }}
|
||||
</div>
|
||||
<div>
|
||||
{{ participant.member }}
|
||||
</div>
|
||||
</div>
|
||||
<template #body>
|
||||
<div
|
||||
class="max-w-[30ch] rounded bg-surface-gray-7 px-2 py-1 text-p-xs text-ink-white leading-5 shadow-xl"
|
||||
>
|
||||
{{ dayjs(participant.joined_at).format('HH:mm a') }} -
|
||||
{{ dayjs(participant.left_at).format('HH:mm a') }}
|
||||
<br />
|
||||
{{ __('attended for') }} {{ participant.duration }}
|
||||
{{ __('minutes') }}
|
||||
</div>
|
||||
</template>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-20 text-right">
|
||||
<div>
|
||||
{{ dayjs(participant.joined_at).format('HH:mm a') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ dayjs(participant.left_at).format('HH:mm a') }}
|
||||
</div>
|
||||
<div>{{ participant.duration }} {{ __('minutes') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -4,15 +4,13 @@
|
||||
<div class="text-2xl font-semibold text-ink-gray-9">
|
||||
{{ __('Related Courses') }}
|
||||
</div>
|
||||
<div class="text-sm text-ink-gray-7">
|
||||
{{ relatedCourses.data.length }} {{ __('courses') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4"
|
||||
>
|
||||
<router-link
|
||||
v-for="course in relatedCourses.data"
|
||||
:key="course.name"
|
||||
:to="{ name: 'CourseDetail', params: { courseName: course.name } }"
|
||||
class="cursor-pointer"
|
||||
>
|
||||
@@ -24,11 +22,8 @@
|
||||
|
||||
<script setup>
|
||||
import { createResource } from 'frappe-ui'
|
||||
import CourseCard from '@/components/CourseCard.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { watch } from 'vue'
|
||||
|
||||
const route = useRoute()
|
||||
import CourseCard from '@/components/CourseCard.vue'
|
||||
|
||||
const props = defineProps({
|
||||
courseName: {
|
||||
@@ -40,22 +35,18 @@ const props = defineProps({
|
||||
const relatedCourses = createResource({
|
||||
url: 'lms.lms.utils.get_related_courses',
|
||||
cache: ['related_courses', props.courseName],
|
||||
params: {
|
||||
course: props.courseName,
|
||||
makeParams() {
|
||||
return {
|
||||
course: props.courseName,
|
||||
}
|
||||
},
|
||||
auto: true,
|
||||
})
|
||||
|
||||
watch(
|
||||
() => route.params.courseName,
|
||||
(newCourseName, oldCourseName) => {
|
||||
if (newCourseName && newCourseName !== oldCourseName) {
|
||||
relatedCourses.update({
|
||||
cache: ['related_courses', newCourseName],
|
||||
params: { course: newCourseName },
|
||||
})
|
||||
relatedCourses.reload()
|
||||
}
|
||||
() => props.courseName,
|
||||
() => {
|
||||
relatedCourses.reload()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -116,7 +116,7 @@ import {
|
||||
EllipsisVertical,
|
||||
} from 'lucide-vue-next'
|
||||
import { inject, ref, getCurrentInstance, computed } from 'vue'
|
||||
import { formatTime } from '../utils'
|
||||
import { formatTime } from '@/utils'
|
||||
import { Button, createResource, call } from 'frappe-ui'
|
||||
import EvaluationModal from '@/components/Modals/EvaluationModal.vue'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="quizzes.length && !showQuiz && readOnly"
|
||||
class="bg-surface-blue-2 space-y-1 py-3 px-4 rounded-md text-sm text-ink-blue-3 leading-5"
|
||||
>
|
||||
<div v-if="quizzes.length && !showQuiz && readOnly" class="leading-5">
|
||||
{{
|
||||
__('This video contains {0} {1}:').format(
|
||||
quizzes.length,
|
||||
@@ -12,8 +9,10 @@
|
||||
}}
|
||||
|
||||
<div v-for="(quiz, index) in quizzes" class="pl-3 mt-1">
|
||||
<span> {{ index + 1 }}. {{ quiz.quiz }} </span>
|
||||
{{ __('at {0}').format(formatTimestamp(quiz.time)) }}
|
||||
<span>
|
||||
{{ index + 1 }}. <span class="font-semibold"> {{ quiz.quiz }} </span>
|
||||
</span>
|
||||
{{ __('at {0} minutes').format(formatTimestamp(quiz.time)) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -65,15 +64,28 @@
|
||||
<Pause v-else @click="pauseVideo" class="size-5 text-ink-white" />
|
||||
</template>
|
||||
</Button>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
:max="duration"
|
||||
step="0.1"
|
||||
v-model="currentTime"
|
||||
@input="changeCurrentTime"
|
||||
class="duration-slider w-full h-1"
|
||||
/>
|
||||
|
||||
<div class="relative flex items-center w-full flex-1">
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
:max="duration"
|
||||
step="0.1"
|
||||
v-model="currentTime"
|
||||
@input="changeCurrentTime"
|
||||
class="duration-slider h-1"
|
||||
/>
|
||||
<!-- QUIZ MARKERS -->
|
||||
<div class="absolute top-0 left-0 w-full h-full pointer-events-none">
|
||||
<div
|
||||
v-for="(quiz, index) in quizzes"
|
||||
:key="index"
|
||||
:style="getQuizMarkerStyle(quiz.time)"
|
||||
class="absolute top-0 h-full w-2 bg-surface-amber-3"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="text-sm font-medium">
|
||||
{{ formatSeconds(currentTime) }} / {{ formatSeconds(duration) }}
|
||||
</span>
|
||||
@@ -116,11 +128,27 @@
|
||||
:saveQuizzes="saveQuizzes"
|
||||
:duration="duration"
|
||||
/>
|
||||
<Dialog
|
||||
v-model="showQuizLoader"
|
||||
:options="{
|
||||
size: 'sm',
|
||||
}"
|
||||
>
|
||||
<template #body>
|
||||
<div class="p-5 text-base">
|
||||
{{
|
||||
__(
|
||||
'Complete the upcoming quiz to continue watching the video. The quiz will open in {0} {1}.'
|
||||
).format(quizLoadTimer, quizLoadTimer === 1 ? 'second' : 'seconds')
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ref, onMounted, computed, watch } from 'vue'
|
||||
import { Pause, Maximize, Volume2, VolumeX } from 'lucide-vue-next'
|
||||
import { Button } from 'frappe-ui'
|
||||
import { Button, Dialog } from 'frappe-ui'
|
||||
import { formatSeconds, formatTimestamp } from '@/utils'
|
||||
import Play from '@/components/Icons/Play.vue'
|
||||
import QuizInVideo from '@/components/Modals/QuizInVideo.vue'
|
||||
@@ -133,6 +161,8 @@ let duration = ref(0)
|
||||
let muted = ref(false)
|
||||
const showQuizModal = ref(false)
|
||||
const showQuiz = ref(false)
|
||||
const showQuizLoader = ref(false)
|
||||
const quizLoadTimer = ref(0)
|
||||
const currentQuiz = ref(null)
|
||||
const nextQuiz = ref({})
|
||||
|
||||
@@ -175,12 +205,24 @@ const updateCurrentTime = () => {
|
||||
playing.value = false
|
||||
videoRef.value.onTimeupdate = null
|
||||
currentQuiz.value = nextQuiz.value.quiz
|
||||
showQuiz.value = true
|
||||
quizLoadTimer.value = 7
|
||||
}
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
watch(quizLoadTimer, () => {
|
||||
if (quizLoadTimer.value > 0) {
|
||||
showQuizLoader.value = true
|
||||
setTimeout(() => {
|
||||
quizLoadTimer.value -= 1
|
||||
}, 1000)
|
||||
} else {
|
||||
showQuizLoader.value = false
|
||||
showQuiz.value = true
|
||||
}
|
||||
})
|
||||
|
||||
const resumeVideo = (restart = false) => {
|
||||
showQuiz.value = false
|
||||
currentQuiz.value = null
|
||||
@@ -259,6 +301,13 @@ const toggleFullscreen = () => {
|
||||
videoContainer.value.requestFullscreen()
|
||||
}
|
||||
}
|
||||
|
||||
const getQuizMarkerStyle = (time) => {
|
||||
const percentage = ((time - 7) / Math.ceil(duration.value)) * 100
|
||||
return {
|
||||
left: `${percentage}%`,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -278,11 +327,10 @@ iframe {
|
||||
}
|
||||
|
||||
.duration-slider {
|
||||
flex: 1;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
border-radius: 10px;
|
||||
background-color: theme('colors.gray.100');
|
||||
background-color: theme('colors.gray.600');
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -290,20 +338,20 @@ iframe {
|
||||
width: 2px;
|
||||
border-radius: 50%;
|
||||
-webkit-appearance: none;
|
||||
background-color: theme('colors.gray.500');
|
||||
background-color: theme('colors.white');
|
||||
}
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio: 0) {
|
||||
input[type='range'] {
|
||||
overflow: hidden;
|
||||
width: 150px;
|
||||
width: 100%;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type='range']::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
cursor: pointer;
|
||||
box-shadow: -500px 0 0 500px theme('colors.gray.600');
|
||||
box-shadow: -500px 0 0 500px theme('colors.white');
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -83,12 +83,12 @@
|
||||
:avg_rating="course.data.rating"
|
||||
:membership="course.data.membership"
|
||||
/>
|
||||
<RelatedCourses :courseName="course.data.name" />
|
||||
</div>
|
||||
<div class="hidden md:block">
|
||||
<CourseCardOverlay :course="course" />
|
||||
</div>
|
||||
</div>
|
||||
<RelatedCourses :courseName="course.data.name" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -109,10 +109,8 @@ import CourseReviews from '@/components/CourseReviews.vue'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
import CourseInstructors from '@/components/CourseInstructors.vue'
|
||||
import RelatedCourses from '@/components/RelatedCourses.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const { brand } = sessionStore()
|
||||
const route = useRoute()
|
||||
|
||||
const props = defineProps({
|
||||
courseName: {
|
||||
@@ -124,22 +122,18 @@ const props = defineProps({
|
||||
const course = createResource({
|
||||
url: 'lms.lms.utils.get_course_details',
|
||||
cache: ['course', props.courseName],
|
||||
params: {
|
||||
course: props.courseName,
|
||||
makeParams() {
|
||||
return {
|
||||
course: props.courseName,
|
||||
}
|
||||
},
|
||||
auto: true,
|
||||
})
|
||||
|
||||
watch(
|
||||
() => route.params.courseName,
|
||||
(newCourseName, oldCourseName) => {
|
||||
if (newCourseName && newCourseName !== oldCourseName) {
|
||||
course.update({
|
||||
cache: ['course', newCourseName],
|
||||
params: { course: newCourseName },
|
||||
})
|
||||
course.reload()
|
||||
}
|
||||
() => props.courseName,
|
||||
() => {
|
||||
course.reload()
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -199,6 +199,21 @@
|
||||
)
|
||||
"
|
||||
/>
|
||||
|
||||
<MultiSelect
|
||||
v-model="related_courses"
|
||||
doctype="LMS Course"
|
||||
:label="__('Related Courses')"
|
||||
:filters="{ name: ['!=', courseResource.data?.name] }"
|
||||
:onCreate="
|
||||
(close) => {
|
||||
router.push({
|
||||
name: 'CourseForm',
|
||||
params: { courseName: 'new' },
|
||||
})
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="px-10 pb-5 space-y-5 border-b">
|
||||
@@ -319,6 +334,7 @@ const newTag = ref('')
|
||||
const { brand } = sessionStore()
|
||||
const router = useRouter()
|
||||
const instructors = ref([])
|
||||
const related_courses = ref([])
|
||||
const app = getCurrentInstance()
|
||||
const { updateOnboardingStep } = useOnboarding('learning')
|
||||
const { $dialog } = app.appContext.config.globalProperties
|
||||
@@ -400,6 +416,9 @@ const courseCreationResource = createResource({
|
||||
instructors: instructors.value.map((instructor) => ({
|
||||
instructor: instructor,
|
||||
})),
|
||||
related_courses: related_courses.value.map((course) => ({
|
||||
course: course,
|
||||
})),
|
||||
...values,
|
||||
},
|
||||
}
|
||||
@@ -418,6 +437,9 @@ const courseEditResource = createResource({
|
||||
instructors: instructors.value.map((instructor) => ({
|
||||
instructor: instructor,
|
||||
})),
|
||||
related_courses: related_courses.value.map((course) => ({
|
||||
course: course,
|
||||
})),
|
||||
...course,
|
||||
},
|
||||
}
|
||||
@@ -440,6 +462,11 @@ const courseResource = createResource({
|
||||
data.instructors.forEach((instructor) => {
|
||||
instructors.value.push(instructor.instructor)
|
||||
})
|
||||
} else if (key == 'related_courses') {
|
||||
related_courses.value = []
|
||||
data.related_courses.forEach((course) => {
|
||||
related_courses.value.push(course.course)
|
||||
})
|
||||
} else if (Object.hasOwn(course, key)) course[key] = data[key]
|
||||
})
|
||||
let checkboxes = [
|
||||
|
||||
@@ -72,7 +72,7 @@ const persona = reactive({
|
||||
const submitPersona = () => {
|
||||
let responses = {
|
||||
site: user.data?.sitename,
|
||||
no_of_students: persona.noOfStudents,
|
||||
role: persona.role,
|
||||
use_case: persona.useCase,
|
||||
}
|
||||
call('lms.lms.api.capture_user_persona', {
|
||||
|
||||
@@ -3,6 +3,7 @@ import VideoBlock from '@/components/VideoBlock.vue'
|
||||
import UploadPlugin from '@/components/UploadPlugin.vue'
|
||||
import { h, createApp } from 'vue'
|
||||
import { Upload as UploadIcon } from 'lucide-vue-next'
|
||||
import { createDialog } from '@/utils/dialogs'
|
||||
import translationPlugin from '../translation'
|
||||
|
||||
export class Upload {
|
||||
@@ -54,6 +55,7 @@ export class Upload {
|
||||
},
|
||||
})
|
||||
app.use(translationPlugin)
|
||||
app.config.globalProperties.$dialog = createDialog
|
||||
app.mount(this.wrapper)
|
||||
return
|
||||
} else if (this.isAudio(file.file_type)) {
|
||||
|
||||
Reference in New Issue
Block a user