fix: course count on batch dashboard

This commit is contained in:
Jannat Patel
2025-05-26 12:25:16 +05:30
36 changed files with 1234 additions and 585 deletions

View File

@@ -1,12 +1,15 @@
describe("Course Creation", () => {
it("creates a new course", () => {
cy.login();
cy.wait(1000);
cy.wait(500);
cy.visit("/lms/courses");
// Close onboarding modal
cy.closeOnboardingModal();
// Create a course
cy.get("button").contains("New").click();
cy.wait(1000);
cy.wait(500);
cy.url().should("include", "/courses/new/edit");
cy.get("label").contains("Title").type("Test Course");
@@ -96,7 +99,8 @@ describe("Course Creation", () => {
// View Course
cy.wait(1000);
cy.visit("/lms");
cy.wait(500);
cy.closeOnboardingModal();
cy.url().should("include", "/lms/courses");
cy.get(".grid a:first").within(() => {
cy.get("div").contains("Test Course");

View File

@@ -25,6 +25,7 @@
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
import "cypress-file-upload";
import "cypress-real-events";
Cypress.Commands.add("login", (email, password) => {
if (!email) {
@@ -68,3 +69,11 @@ Cypress.Commands.add("paste", { prevSubject: true }, (subject, text) => {
element.dispatchEvent(event);
});
});
Cypress.Commands.add("closeOnboardingModal", () => {
cy.wait(500);
cy.get('[class*="z-50"]')
.find('button:has(svg[class*="feather-x"])')
.realClick();
cy.wait(1000);
});

View File

@@ -47,11 +47,14 @@ declare module 'vue' {
Discussions: typeof import('./src/components/Discussions.vue')['default']
EditCoverImage: typeof import('./src/components/Modals/EditCoverImage.vue')['default']
EditProfile: typeof import('./src/components/Modals/EditProfile.vue')['default']
EmailTemplateModal: typeof import('./src/components/Modals/EmailTemplateModal.vue')['default']
EmailTemplates: typeof import('./src/components/EmailTemplates.vue')['default']
EmptyState: typeof import('./src/components/EmptyState.vue')['default']
EvaluationModal: typeof import('./src/components/Modals/EvaluationModal.vue')['default']
Evaluators: typeof import('./src/components/Evaluators.vue')['default']
Event: typeof import('./src/components/Modals/Event.vue')['default']
ExplanationVideos: typeof import('./src/components/Modals/ExplanationVideos.vue')['default']
FeedbackModal: typeof import('./src/components/Modals/FeedbackModal.vue')['default']
FrappeCloudIcon: typeof import('./src/components/Icons/FrappeCloudIcon.vue')['default']
IconPicker: typeof import('./src/components/Controls/IconPicker.vue')['default']
IndicatorIcon: typeof import('./src/components/Icons/IndicatorIcon.vue')['default']

View File

@@ -39,9 +39,9 @@ const Layout = computed(() => {
}
if (screenSize.width < 640) {
return MobileLayout
} else {
return DesktopLayout
}
return DesktopLayout
})
onMounted(async () => {

View File

@@ -181,7 +181,6 @@
import UserDropdown from '@/components/UserDropdown.vue'
import CollapseSidebar from '@/components/Icons/CollapseSidebar.vue'
import SidebarLink from '@/components/SidebarLink.vue'
import { useStorage } from '@vueuse/core'
import { ref, onMounted, inject, watch, reactive, markRaw, h } from 'vue'
import { getSidebarLinks } from '../utils'
import { usersStore } from '@/stores/user'
@@ -244,6 +243,7 @@ const iconProps = {
onMounted(() => {
addNotifications()
setSidebarLinks()
setUpOnboarding()
socket.on('publish_lms_notifications', (data) => {
unreadNotifications.reload()
})
@@ -388,10 +388,6 @@ const deletePage = (link) => {
)
}
const getSidebarFromStorage = () => {
return useStorage('sidebar_is_collapsed', false)
}
const toggleSidebar = () => {
sidebarStore.isSidebarCollapsed = !sidebarStore.isSidebarCollapsed
localStorage.setItem(
@@ -438,6 +434,7 @@ const steps = reactive([
title: __('Add your first chapter'),
icon: markRaw(h(FolderTree, iconProps)),
completed: false,
dependsOn: 'create_first_course',
onClick: async () => {
minimize.value = true
let course = await getFirstCourse()
@@ -453,6 +450,7 @@ const steps = reactive([
title: __('Add your first lesson'),
icon: markRaw(h(FileText, iconProps)),
completed: false,
dependsOn: 'create_first_chapter',
onClick: async () => {
minimize.value = true
let course = await getFirstCourse()
@@ -471,6 +469,7 @@ const steps = reactive([
title: __('Create your first quiz'),
icon: markRaw(h(CircleHelp, iconProps)),
completed: false,
dependsOn: 'create_first_course',
onClick: () => {
minimize.value = true
router.push({ name: 'Quizzes' })
@@ -502,6 +501,7 @@ const steps = reactive([
title: __('Add students to your batch'),
icon: markRaw(h(UserPlus, iconProps)),
completed: false,
dependsOn: 'create_first_batch',
onClick: async () => {
minimize.value = true
let batch = await getFirstBatch()
@@ -522,6 +522,7 @@ const steps = reactive([
title: __('Add courses to your batch'),
icon: markRaw(h(BookText, iconProps)),
completed: false,
dependsOn: 'create_first_batch',
onClick: async () => {
minimize.value = true
let batch = await getFirstBatch()

View File

@@ -1,44 +1,49 @@
<template>
<div v-if="user.data?.is_student">
<div
v-if="feedbackList.data?.length"
class="bg-surface-blue-2 text-blue-700 p-2 rounded-md mb-5"
>
{{ __('Thank you for providing your feedback!') }}
</div>
<div v-else class="flex justify-between items-center mb-5">
<div class="text-lg font-semibold">
{{ __('Help Us Improve') }}
<div>
<div class="leading-5 mb-4">
<div v-if="readOnly">
{{ __('Thank you for providing your feedback.') }}
<span
@click="showFeedbackForm = !showFeedbackForm"
class="underline cursor-pointer"
>{{ __('Click here') }}</span
>
{{ __('to view your feedback.') }}
</div>
<div v-else>
{{ __('Help us improve by providing your feedback.') }}
</div>
</div>
<Button @click="submitFeedback()">
{{ __('Submit') }}
</Button>
</div>
<div class="space-y-8">
<div class="flex items-center justify-between">
<Rating
v-for="key in ratingKeys"
v-model="feedback[key]"
:label="__(convertToTitleCase(key))"
<div class="space-y-4" :class="showFeedbackForm ? 'block' : 'hidden'">
<div class="space-y-4">
<Rating
v-for="key in ratingKeys"
v-model="feedback[key]"
:label="__(convertToTitleCase(key))"
:readonly="readOnly"
/>
</div>
<FormControl
v-model="feedback.feedback"
type="textarea"
:label="__('Feedback')"
:rows="9"
:readonly="readOnly"
/>
<Button v-if="!readOnly" @click="submitFeedback">
{{ __('Submit Feedback') }}
</Button>
</div>
<FormControl
v-model="feedback.feedback"
type="textarea"
:label="__('Feedback')"
:rows="7"
:readonly="readOnly"
/>
</div>
</div>
<div v-else-if="feedbackList.data?.length">
<div class="text-lg font-semibold mb-5">
{{ __('Average of Feedback Received') }}
<div class="leading-5 text-sm mb-2 mt-5">
{{ __('Average Feedback Received') }}
</div>
<div class="flex items-center justify-between mb-10">
<div class="space-y-4">
<Rating
v-for="key in ratingKeys"
v-model="average[key]"
@@ -47,81 +52,32 @@
/>
</div>
<div class="text-lg font-semibold mb-5">
{{ __('All Feedback') }}
</div>
<ListView
:columns="feedbackColumns"
:rows="feedbackList.data"
row-key="name"
:options="{
showTooltip: false,
rowHeight: 'h-16',
selectable: false,
}"
>
<ListHeader
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
></ListHeader>
<ListRows>
<ListRow
:row="row"
v-for="row in feedbackList.data"
class="group cursor-pointer feedback-list"
>
<template #default="{ column, item }">
<ListRowItem
:item="row[column.key]"
:align="column.align"
class="text-sm"
>
<template #prefix>
<div v-if="column.key == 'member_name'">
<Avatar
class="flex"
:image="row['member_image']"
:label="item"
size="sm"
/>
</div>
</template>
<div v-if="ratingKeys.includes(column.key)">
<Rating v-model="row[column.key]" :readonly="true" />
</div>
<div v-else class="leading-5">
{{ row[column.key] }}
</div>
</ListRowItem>
</template>
</ListRow>
</ListRows>
</ListView>
<Button variant="outline" class="mt-5" @click="showAllFeedback = true">
{{ __('View all feedback') }}
</Button>
</div>
<div v-else class="text-sm italic text-center text-ink-gray-7 mt-5">
<div v-else class="text-ink-gray-7 mt-5 leading-5">
{{ __('No feedback received yet.') }}
</div>
<FeedbackModal
v-if="feedbackList.data?.length"
v-model="showAllFeedback"
:feedbackList="feedbackList.data"
/>
</template>
<script setup>
import { computed, inject, onMounted, reactive, ref, watch } from 'vue'
import { inject, onMounted, reactive, ref, watch } from 'vue'
import { convertToTitleCase } from '@/utils'
import {
Avatar,
Button,
createListResource,
FormControl,
ListView,
ListHeader,
ListRows,
ListRow,
ListRowItem,
Rating,
} from 'frappe-ui'
import { Button, createListResource, FormControl, Rating } from 'frappe-ui'
import FeedbackModal from '@/components/Modals/FeedbackModal.vue'
const user = inject('$user')
const ratingKeys = ['content', 'instructors', 'value']
const readOnly = ref(false)
const average = reactive({})
const feedback = reactive({})
const showFeedbackForm = ref(true)
const showAllFeedback = ref(false)
const props = defineProps({
batch: {
@@ -167,6 +123,7 @@ watch(
if (feedbackList.data.length) {
let data = feedbackList.data
readOnly.value = true
showFeedbackForm.value = false
ratingKeys.forEach((key) => {
average[key] = 0
@@ -201,40 +158,11 @@ const submitFeedback = () => {
{
onSuccess: () => {
feedbackList.reload()
showFeedbackForm.value = false
},
}
)
}
const feedbackColumns = computed(() => {
return [
{
label: 'Member',
key: 'member_name',
width: '10rem',
},
{
label: 'Feedback',
key: 'feedback',
width: '15rem',
},
{
label: 'Content',
key: 'content',
width: '9rem',
},
{
label: 'Instructors',
key: 'instructors',
width: '9rem',
},
{
label: 'Value',
key: 'value',
width: '9rem',
},
]
})
</script>
<style>
.feedback-list > button > div {

View File

@@ -18,11 +18,11 @@
</div>
<div class="overflow-y-auto">
<SettingFields :fields="fields" :data="data.data" />
<div class="flex flex-row-reverse mt-auto">
<Button variant="solid" :loading="saveSettings.loading" @click="update">
{{ __('Update') }}
</Button>
</div>
</div>
<div class="flex flex-row-reverse mt-auto">
<Button variant="solid" :loading="saveSettings.loading" @click="update">
{{ __('Update') }}
</Button>
</div>
</div>
</template>

View File

@@ -1,16 +1,32 @@
<template>
<div class="flex flex-col min-h-0">
<div class="flex items-center justify-between">
<div class="text-xl font-semibold mb-5 text-ink-gray-9">
{{ label }}
<div class="flex flex-col min-h-0 text-base">
<div class="flex items-center justify-between mb-5">
<div class="flex flex-col space-y-2">
<div class="text-xl font-semibold text-ink-gray-9">
{{ label }}
</div>
<div class="text-xs text-ink-gray-5">
{{ __(description) }}
</div>
</div>
<div class="flex items-center space-x-5">
<div
class="flex items-center space-x-1 text-ink-amber-3 border border-outline-amber-1 bg-surface-amber-1 rounded-lg px-2 py-1"
v-if="saving"
>
<LoadingIndicator class="size-2" />
<span class="text-xs">
{{ __('saving...') }}
</span>
</div>
<Button @click="() => showCategoryForm()">
<template #prefix>
<Plus v-if="!showForm" class="h-3 w-3 stroke-1.5" />
<X v-else class="h-3 w-3 stroke-1.5" />
</template>
{{ showForm ? __('Close') : __('New') }}
</Button>
</div>
<Button @click="() => showCategoryForm()">
<template #prefix>
<Plus v-if="!showForm" class="h-3 w-3 stroke-1.5" />
<X v-else class="h-3 w-3 stroke-1.5" />
</template>
{{ showForm ? __('Close') : __('New') }}
</Button>
</div>
<div
@@ -29,13 +45,39 @@
</div>
<div class="overflow-y-scroll">
<div class="text-base space-y-2">
<FormControl
:value="cat.category"
type="text"
v-for="cat in categories.data"
@change.stop="(e) => update(cat.name, e.target.value)"
/>
<div class="divide-y space-y-2">
<div
v-for="(cat, index) in categories.data"
:key="cat.name"
class="pt-2"
>
<div
v-if="editing?.name !== cat.name"
class="flex items-center justify-between group text-sm"
>
<div @dblclick="allowEdit(cat, index)">
{{ cat.category }}
</div>
<Button
variant="ghost"
theme="red"
class="invisible group-hover:visible"
@click="deleteCategory(cat.name)"
>
<template #icon>
<Trash2 class="size-4 stroke-1.5 text-ink-red-4" />
</template>
</Button>
</div>
<FormControl
v-else
:ref="(el) => (editInputRef[index] = el)"
v-model="editedValue"
type="text"
class="w-full"
@keyup.enter="saveChanges(cat.name, editedValue)"
/>
</div>
</div>
</div>
</div>
@@ -44,16 +86,22 @@
import {
Button,
FormControl,
LoadingIndicator,
createListResource,
createResource,
debounce,
toast,
} from 'frappe-ui'
import { Plus, X } from 'lucide-vue-next'
import { Plus, Trash2, X } from 'lucide-vue-next'
import { ref } from 'vue'
import { cleanError } from '@/utils'
const showForm = ref(false)
const category = ref(null)
const categoryInput = ref(null)
const saving = ref(false)
const editing = ref(null)
const editedValue = ref('')
const editInputRef = ref([])
const props = defineProps({
label: {
@@ -72,25 +120,20 @@ const categories = createListResource({
auto: true,
})
const newCategory = createResource({
url: 'frappe.client.insert',
makeParams(values) {
return {
doc: {
doctype: 'LMS Category',
category: category.value,
},
}
},
})
const addCategory = () => {
newCategory.submit(
{},
categories.insert.submit(
{
category: category.value,
},
{
onSuccess(data) {
categories.reload()
category.value = null
showForm.value = false
toast.success(__('Category added successfully'))
},
onError(err) {
toast.error(__(cleanError(err.messages[0]) || 'Unable to add category'))
},
}
)
@@ -115,6 +158,7 @@ const updateCategory = createResource({
})
const update = (name, value) => {
saving.value = true
updateCategory.submit(
{
name: name,
@@ -122,9 +166,51 @@ const update = (name, value) => {
},
{
onSuccess() {
saving.value = false
categories.reload()
editing.value = null
editedValue.value = ''
toast.success(__('Category updated successfully'))
},
onError(err) {
saving.value = false
editing.value = null
editedValue.value = ''
toast.error(
__(cleanError(err.messages[0]) || 'Unable to update category')
)
},
}
)
}
const deleteCategory = (name) => {
saving.value = true
categories.delete.submit(name, {
onSuccess() {
saving.value = false
categories.reload()
toast.success(__('Category deleted successfully'))
},
onError(err) {
saving.value = false
toast.error(
__(cleanError(err.messages[0]) || 'Unable to delete category')
)
},
})
}
const saveChanges = (name, value) => {
saving.value = true
update(name, value)
}
const allowEdit = (cat, index) => {
editing.value = cat
editedValue.value = cat.category
setTimeout(() => {
editInputRef.value[index].$el.querySelector('input').focus()
}, 0)
}
</script>

View File

@@ -116,7 +116,7 @@
v-if="parseInt(course.data.rating) > 0"
class="flex items-center text-ink-gray-9"
>
<Star class="h-4 w-4 stroke-1.5 fill-orange-500 text-gray-50" />
<Star class="size-4 stroke-1.5 fill-yellow-500 text-transparent" />
<span class="ml-2">
{{ course.data.rating }} {{ __('Rating') }}
</span>

View File

@@ -35,14 +35,14 @@
<span class="text-ink-gray-7">
{{ review.creation }}
</span>
<div class="flex mt-2">
<div class="flex mt-2 space-x-1">
<Star
v-for="index in 5"
class="h-5 w-5 text-ink-gray-1 rounded-sm mr-2"
class="size-4 text-transparent rounded-sm"
:class="
index <= Math.ceil(review.rating)
? 'fill-orange-500'
: 'fill-gray-600'
? 'fill-yellow-500'
: 'fill-gray-300'
"
/>
</div>

View File

@@ -0,0 +1,160 @@
<template>
<div class="flex flex-col min-h-0 text-base">
<div class="flex items-center justify-between mb-5">
<div class="flex flex-col space-y-2">
<div class="text-xl font-semibold text-ink-gray-9">
{{ label }}
</div>
<div class="text-xs text-ink-gray-5">
{{ __(description) }}
</div>
</div>
<div class="flex items-center space-x-5">
<Button @click="openTemplateForm('new')">
<template #prefix>
<Plus class="h-3 w-3 stroke-1.5" />
</template>
{{ __('New') }}
</Button>
</div>
</div>
<div v-if="emailTemplates.data?.length" class="overflow-y-scroll">
<ListView
:columns="columns"
:rows="emailTemplates.data"
row-key="name"
:options="{
showTooltip: false,
onRowClick: (row) => {
openTemplateForm(row.name)
},
}"
>
<ListHeader
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
>
<ListHeaderItem :item="item" v-for="item in columns">
<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 emailTemplates.data">
<template #default="{ column, item }">
<ListRowItem :item="row[column.key]" :align="column.align">
<div class="leading-5 text-sm">
{{ row[column.key] }}
</div>
</ListRowItem>
</template>
</ListRow>
</ListRows>
<ListSelectBanner>
<template #actions="{ unselectAll, selections }">
<div class="flex gap-2">
<Button
variant="ghost"
@click="removeTemplate(selections, unselectAll)"
>
<Trash2 class="h-4 w-4 stroke-1.5" />
</Button>
</div>
</template>
</ListSelectBanner>
</ListView>
</div>
</div>
<EmailTemplateModal
v-model="showForm"
v-model:emailTemplates="emailTemplates"
:templateID="selectedTemplate"
/>
</template>
<script setup lang="ts">
import {
Button,
call,
createListResource,
ListView,
ListHeader,
ListHeaderItem,
ListSelectBanner,
ListRows,
ListRow,
ListRowItem,
toast,
} from 'frappe-ui'
import { computed, ref } from 'vue'
import { Plus, Trash2 } from 'lucide-vue-next'
import EmailTemplateModal from '@/components/Modals/EmailTemplateModal.vue'
const props = defineProps({
label: {
type: String,
required: true,
},
description: {
type: String,
default: '',
},
})
const showForm = ref(false)
const readOnlyMode = window.read_only_mode
const selectedTemplate = ref(null)
const emailTemplates = createListResource({
doctype: 'Email Template',
fields: ['name', 'subject', 'use_html', 'response', 'response_html'],
auto: true,
orderBy: 'modified desc',
cache: 'email-templates',
})
const removeTemplate = (selections, unselectAll) => {
call('lms.lms.api.delete_documents', {
doctype: 'Email Template',
documents: Array.from(selections),
})
.then(() => {
emailTemplates.reload()
toast.success(__('Email Templates deleted successfully'))
unselectAll()
})
.catch((err) => {
toast.error(
cleanError(err.messages[0]) || __('Error deleting email templates')
)
})
}
const openTemplateForm = (templateID) => {
if (readOnlyMode) {
return
}
selectedTemplate.value = templateID
showForm.value = true
}
const columns = computed(() => {
return [
{
label: 'Name',
key: 'name',
width: '20rem',
},
{
label: 'Subject',
key: 'subject',
width: '25rem',
},
]
})
</script>

View File

@@ -1,5 +1,5 @@
<template>
<div>
<div class="flex min-h-0 flex-col text-base">
<div class="flex items-center justify-between mb-4">
<div>
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
@@ -39,25 +39,27 @@
</Button>
</div>
<div class="divide-y">
<div
v-for="evaluator in evaluators.data"
@click="openProfile(evaluator.username)"
class="cursor-pointer"
>
<div class="flex items-center justify-between py-3">
<div class="flex items-center space-x-3">
<Avatar
:image="evaluator.user_image"
:label="evaluator.full_name"
size="lg"
/>
<div>
<div class="text-base font-semibold text-ink-gray-9">
{{ evaluator.full_name }}
</div>
<div class="text-xs text-ink-gray-5">
{{ evaluator.evaluator }}
<div class="overflow-y-scroll">
<div class="divide-y">
<div
v-for="evaluator in evaluators.data"
@click="openProfile(evaluator.username)"
class="cursor-pointer"
>
<div class="flex items-center justify-between py-3">
<div class="flex items-center space-x-3">
<Avatar
:image="evaluator.user_image"
:label="evaluator.full_name"
size="lg"
/>
<div>
<div class="text-base font-semibold text-ink-gray-9">
{{ evaluator.full_name }}
</div>
<div class="text-xs text-ink-gray-5">
{{ evaluator.evaluator }}
</div>
</div>
</div>
</div>

View File

@@ -1,60 +1,55 @@
<template>
<div class="flex h-full flex-col">
<div class="flex h-full flex-col relative">
<div class="h-full pb-10" id="scrollContainer">
<slot />
</div>
<div
v-if="sidebarSettings.data"
class="fixed flex items-center justify-around border-t border-outline-gray-2 bottom-0 z-10 w-full bg-surface-white standalone:pb-4"
:style="{
gridTemplateColumns: `repeat(${
sidebarLinks.length + 1
}, minmax(0, 1fr))`,
}"
>
<button
v-for="tab in sidebarLinks"
:key="tab.label"
:class="isVisible(tab) ? 'block' : 'hidden'"
class="flex flex-col items-center justify-center py-3 transition active:scale-95"
@click="handleClick(tab)"
<div class="relative z-20">
<!-- Dropdown menu -->
<div
class="fixed bottom-16 right-2 w-[80%] rounded-md bg-surface-white text-base p-5 space-y-4 shadow-md"
v-if="showMenu"
ref="menu"
>
<component
:is="icons[tab.icon]"
class="h-6 w-6 stroke-1.5"
:class="[isActive(tab) ? 'text-ink-gray-9' : 'text-ink-gray-5']"
/>
</button>
<Popover
trigger="hover"
popoverClass="bottom-28 mx-2"
placement="top-start"
<div
v-for="link in otherLinks"
:key="link.label"
class="flex items-center space-x-2 cursor-pointer"
@click="handleClick(link)"
>
<component
:is="icons[link.icon]"
class="h-4 w-4 stroke-1.5 text-ink-gray-5"
/>
<div>{{ link.label }}</div>
</div>
</div>
<!-- Fixed menu -->
<div
v-if="sidebarSettings.data"
class="fixed bottom-0 left-0 w-full flex items-center justify-around border-t border-outline-gray-2 bg-surface-white standalone:pb-4 z-10"
>
<template #target>
<button
v-for="tab in sidebarLinks"
:key="tab.label"
:class="isVisible(tab) ? 'block' : 'hidden'"
class="flex flex-col items-center justify-center py-3 transition active:scale-95"
@click="handleClick(tab)"
>
<component
:is="icons[tab.icon]"
class="h-6 w-6 stroke-1.5"
:class="[isActive(tab) ? 'text-ink-gray-9' : 'text-ink-gray-5']"
/>
</button>
<button @click="toggleMenu">
<component
:is="icons['List']"
class="h-6 w-6 stroke-1.5 text-ink-gray-5"
/>
</template>
<template #body-main>
<div class="text-base p-5 space-y-4">
<div
v-for="link in otherLinks"
:key="link.label"
class="flex items-center space-x-2"
@click="handleClick(link)"
>
<component
:is="icons[link.icon]"
class="h-4 w-4 stroke-1.5 text-ink-gray-5"
/>
<div>
{{ link.label }}
</div>
</div>
</div>
</template>
</Popover>
</button>
</div>
</div>
</div>
</template>
@@ -64,7 +59,6 @@ import { useRouter } from 'vue-router'
import { watch, ref, onMounted } from 'vue'
import { sessionStore } from '@/stores/session'
import { usersStore } from '@/stores/user'
import { Popover } from 'frappe-ui'
import * as icons from 'lucide-vue-next'
const { logout, user, sidebarSettings } = sessionStore()
@@ -73,26 +67,47 @@ const router = useRouter()
let { userResource } = usersStore()
const sidebarLinks = ref(getSidebarLinks())
const otherLinks = ref([])
const showMenu = ref(false)
const menu = ref(null)
onMounted(() => {
sidebarSettings.reload(
{},
{
onSuccess(data) {
Object.keys(data).forEach((key) => {
if (!parseInt(data[key])) {
sidebarLinks.value = sidebarLinks.value.filter(
(link) => link.label.toLowerCase().split(' ').join('_') !== key
)
}
})
filterLinksToShow(data)
addOtherLinks()
},
}
)
})
const handleOutsideClick = (e) => {
if (menu.value && !menu.value.contains(e.target)) {
showMenu.value = false
}
}
watch(showMenu, (val) => {
if (val) {
setTimeout(() => {
document.addEventListener('click', handleOutsideClick)
}, 0)
} else {
document.removeEventListener('click', handleOutsideClick)
}
})
const filterLinksToShow = (data) => {
Object.keys(data).forEach((key) => {
if (!parseInt(data[key])) {
sidebarLinks.value = sidebarLinks.value.filter(
(link) => link.label.toLowerCase().split(' ').join('_') !== key
)
}
})
}
const addOtherLinks = () => {
if (user) {
otherLinks.value.push({
@@ -122,6 +137,7 @@ watch(userResource, () => {
(userResource.data.is_moderator || userResource.data.is_instructor)
) {
addQuizzes()
addAssignments()
}
})
@@ -133,6 +149,14 @@ const addQuizzes = () => {
})
}
const addAssignments = () => {
otherLinks.value.push({
label: 'Assignments',
icon: 'Pencil',
to: 'Assignments',
})
}
let isActive = (tab) => {
return tab.activeFor?.includes(router.currentRoute.value.name)
}
@@ -158,4 +182,8 @@ const isVisible = (tab) => {
else if (tab.label == 'Log out') return isLoggedIn
else return true
}
const toggleMenu = () => {
showMenu.value = !showMenu.value
}
</script>

View File

@@ -6,7 +6,7 @@
}"
>
<template #body>
<div class="p-5 text-base max-h-[75vh] overflow-y-auto">
<div class="p-5 text-base">
<div class="text-lg text-ink-gray-9 font-semibold mb-5">
{{
assignmentID === 'new'
@@ -14,7 +14,7 @@
: __('Edit Assignment')
}}
</div>
<div class="space-y-4">
<div class="space-y-4 max-h-[75vh] overflow-y-auto">
<FormControl
v-model="assignment.title"
:label="__('Title')"

View File

@@ -0,0 +1,192 @@
<template>
<Dialog
v-model="show"
:options="{
title:
templateID == 'new'
? __('New Email Template')
: __('Edit Email Template'),
size: 'lg',
actions: [
{
label: __('Save'),
variant: 'solid',
onClick: ({ close }) => {
saveTemplate(close)
},
},
],
}"
>
<template #body-content>
<div class="space-y-4">
<FormControl
:label="__('Name')"
v-model="template.name"
type="text"
:required="true"
:placeholder="__('Batch Enrollment Confirmation')"
/>
<FormControl
:label="__('Subject')"
v-model="template.subject"
type="text"
:required="true"
:placeholder="__('Your enrollment in {{ batch_name }} is confirmed')"
/>
<FormControl
:label="__('Use HTML')"
v-model="template.use_html"
type="checkbox"
/>
<FormControl
v-if="template.use_html"
:label="__('Content')"
v-model="template.response_html"
type="textarea"
:required="true"
:rows="10"
:placeholder="
__(
'<p>Dear {{ member_name }},</p>\n\n<p>You have been enrolled in our upcoming batch {{ batch_name }}.</p>\n\n<p>Thanks,</p>\n<p>Frappe Learning</p>'
)
"
/>
<div v-else>
<div class="text-xs text-ink-gray-5 mb-2">
{{ __('Content') }}
<span class="text-ink-red-3">*</span>
</div>
<TextEditor
:content="template.response"
@change="(val) => (template.response = val)"
:editable="true"
:fixedMenu="true"
:placeholder="
__(
'Dear {{ member_name }},\n\nYou have been enrolled in our upcoming batch {{ batch_name }}.\n\nThanks,\nFrappe Learning'
)
"
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem] max-h-[18rem] overflow-y-auto"
/>
</div>
</div>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { call, Dialog, FormControl, TextEditor, toast } from 'frappe-ui'
import { reactive, watch } from 'vue'
import { cleanError } from '@/utils'
const props = defineProps({
templateID: {
type: String,
default: 'new',
},
})
const show = defineModel()
const emailTemplates = defineModel('emailTemplates')
const template = reactive({
name: '',
subject: '',
use_html: false,
response: '',
response_html: '',
})
const saveTemplate = (close) => {
if (props.templateID == 'new') {
createNewTemplate(close)
} else {
updateTemplate(close)
}
}
const createNewTemplate = (close) => {
emailTemplates.value.insert.submit(
{
__newname: template.name,
...template,
},
{
onSuccess() {
emailTemplates.value.reload()
refreshForm(close)
toast.success(__('Email Template created successfully'))
},
onError(err) {
refreshForm(close)
toast.error(
cleanError(err.messages[0]) || __('Error creating email template')
)
},
}
)
}
const updateTemplate = async (close) => {
if (props.templateID != template.name) {
await renameDoc()
}
setValue(close)
}
const setValue = (close) => {
emailTemplates.value.setValue.submit(
{
...template,
name: template.name,
},
{
onSuccess() {
emailTemplates.value.reload()
refreshForm(close)
toast.success(__('Email Template updated successfully'))
},
onError(err) {
refreshForm(close)
toast.error(
cleanError(err.messages[0]) || __('Error updating email template')
)
},
}
)
}
const renameDoc = async () => {
await call('frappe.client.rename_doc', {
doctype: 'Email Template',
old_name: props.templateID,
new_name: template.name,
})
}
watch(
() => props.templateID,
(val) => {
if (val !== 'new') {
emailTemplates.value?.data.forEach((row) => {
if (row.name === val) {
template.name = row.name
template.subject = row.subject
template.use_html = row.use_html
template.response = row.response
template.response_html = row.response_html
}
})
}
},
{ flush: 'post' }
)
const refreshForm = (close) => {
close()
template.name = ''
template.subject = ''
template.use_html = false
template.response = ''
template.response_html = ''
}
</script>

View File

@@ -66,7 +66,7 @@
</Dialog>
</template>
<script setup>
import { Dialog, createResource, Select, FormControl } from 'frappe-ui'
import { Dialog, createResource, Select, FormControl, toast } from 'frappe-ui'
import { reactive, watch, inject } from 'vue'
import { formatTime } from '@/utils/'
@@ -90,7 +90,7 @@ const props = defineProps({
},
})
let evaluation = reactive({
const evaluation = reactive({
course: '',
date: '',
start_time: '',
@@ -139,7 +139,7 @@ function submitEvaluation(close) {
close()
},
onError(err) {
let message = err.messages?.[0] || err
const message = err.messages?.[0] || err
let unavailabilityMessage
if (typeof message === 'string') {
@@ -148,13 +148,13 @@ function submitEvaluation(close) {
unavailabilityMessage = false
}
toast.warning(__('Evaluator is unavailable'))
toast.warning(__(unavailabilityMessage || 'Evaluator is unavailable'))
},
})
}
const getCourses = () => {
let courses = []
const courses = []
for (const course of props.courses) {
if (course.evaluator) {
courses.push({
@@ -164,7 +164,7 @@ const getCourses = () => {
}
}
if (courses.length == 1) {
if (courses.length === 1) {
evaluation.course = courses[0].value
}

View File

@@ -0,0 +1,115 @@
<template>
<Dialog
v-model="show"
:options="{
size: '4xl',
}"
>
<template #body>
<div class="p-5 min-h-[300px]">
<div class="text-lg font-semibold mb-4">
{{ __('Training Feedback') }}
</div>
<ListView
:columns="feedbackColumns"
:rows="feedbackList"
row-key="name"
:options="{
showTooltip: false,
rowHeight: 'h-16',
selectable: false,
}"
>
<ListHeader
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
></ListHeader>
<ListRows>
<ListRow
:row="row"
v-for="row in feedbackList"
class="group feedback-list"
>
<template #default="{ column, item }">
<ListRowItem
:item="row[column.key]"
:align="column.align"
class="text-sm"
>
<template #prefix>
<div v-if="column.key == 'member_name'">
<Avatar
class="flex"
:image="row['member_image']"
:label="item"
size="sm"
/>
</div>
</template>
<div v-if="ratingKeys.includes(column.key)">
<Rating v-model="row[column.key]" :readonly="true" />
</div>
<div v-else class="leading-5">
{{ row[column.key] }}
</div>
</ListRowItem>
</template>
</ListRow>
</ListRows>
</ListView>
</div>
</template>
</Dialog>
</template>
<script setup lang="ts">
import {
Dialog,
ListView,
Avatar,
ListHeader,
ListRows,
ListRow,
ListRowItem,
Rating,
} from 'frappe-ui'
import { reactive, computed } from 'vue'
const show = defineModel()
const ratingKeys = ['content', 'instructors', 'value']
const props = defineProps({
feedbackList: {
type: Array,
required: true,
},
})
const feedbackColumns = computed(() => {
return [
{
label: 'Member',
key: 'member_name',
width: '10rem',
},
{
label: 'Feedback',
key: 'feedback',
width: '15rem',
},
{
label: 'Content',
key: 'content',
width: '9rem',
},
{
label: 'Instructors',
key: 'instructors',
width: '9rem',
},
{
label: 'Value',
key: 'value',
width: '9rem',
},
]
})
</script>

View File

@@ -15,26 +15,20 @@
>
<template #body-content>
<div class="flex flex-col gap-4">
<div>
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Rating') }}
</div>
<Rating v-model="review.rating" />
</div>
<div>
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Review') }}
</div>
<Textarea type="text" size="md" rows="5" v-model="review.review" />
</div>
<Rating v-model="review.rating" :label="__('Rating')" />
<FormControl
:label="__('Review')"
type="textarea"
v-model="review.review"
:rows="5"
/>
</div>
</template>
</Dialog>
</template>
<script setup>
import { Dialog, Textarea, createResource, toast } from 'frappe-ui'
import { Dialog, FormControl, createResource, toast, Rating } from 'frappe-ui'
import { reactive } from 'vue'
import Rating from '@/components/Controls/Rating.vue'
const show = defineModel()
const reviews = defineModel('reloadReviews')

View File

@@ -51,6 +51,11 @@
:label="activeTab.label"
:description="activeTab.description"
/>
<EmailTemplates
v-else-if="activeTab.label === 'Email Templates'"
:label="activeTab.label"
:description="activeTab.description"
/>
<PaymentSettings
v-else-if="activeTab.label === 'Payment Gateway'"
:label="activeTab.label"
@@ -86,6 +91,7 @@ import SidebarLink from '@/components/SidebarLink.vue'
import Members from '@/components/Members.vue'
import Evaluators from '@/components/Evaluators.vue'
import Categories from '@/components/Categories.vue'
import EmailTemplates from '@/components/EmailTemplates.vue'
import BrandSettings from '@/components/BrandSettings.vue'
import PaymentSettings from '@/components/PaymentSettings.vue'
@@ -122,7 +128,7 @@ const tabsStructure = computed(() => {
label: 'Enable Learning Paths',
name: 'enable_learning_paths',
description:
'This will enforce students to go through programs assigned to them in the correct order.',
'This will ensure students follow the assigned programs in order.',
type: 'checkbox',
},
{
@@ -139,11 +145,26 @@ const tabsStructure = computed(() => {
'If enabled, it sends google calendar invite to the student for evaluations.',
type: 'checkbox',
},
{
type: 'Column Break',
},
{
label: 'Batch Confirmation Template',
name: 'batch_confirmation_template',
doctype: 'Email Template',
type: 'Link',
},
{
label: 'Certification Template',
name: 'certification_template',
doctype: 'Email Template',
type: 'Link',
},
{
label: 'Unsplash Access Key',
name: 'unsplash_access_key',
description:
'Optional. If this is set, students can pick a cover image from the unsplash library for their profile page. https://unsplash.com/documentation#getting-started.',
'Allows users to pick a profile cover image from Unsplash. https://unsplash.com/documentation#getting-started.',
type: 'password',
},
],
@@ -160,6 +181,12 @@ const tabsStructure = computed(() => {
description:
'Configure the payment gateway and other payment related settings',
fields: [
{
label: 'Default Currency',
name: 'default_currency',
type: 'Link',
doctype: 'Currency',
},
{
label: 'Payment Gateway',
name: 'payment_gateway',
@@ -167,10 +194,7 @@ const tabsStructure = computed(() => {
doctype: 'Payment Gateway',
},
{
label: 'Default Currency',
name: 'default_currency',
type: 'Link',
doctype: 'Currency',
type: 'Column Break',
},
{
label: 'Apply GST for India',
@@ -207,9 +231,14 @@ const tabsStructure = computed(() => {
},
{
label: 'Categories',
description: 'Manage the members of your learning system',
description: 'Double click to edit the category',
icon: 'Network',
},
{
label: 'Email Templates',
description: 'Manage the email templates for your learning system',
icon: 'MailPlus',
},
],
},
{
@@ -235,28 +264,6 @@ const tabsStructure = computed(() => {
name: 'favicon',
type: 'Upload',
},
{
label: 'Footer Logo',
name: 'footer_logo',
type: 'Upload',
},
{
label: 'Address',
name: 'address',
type: 'textarea',
rows: 2,
},
{
label: 'Footer "Powered By"',
name: 'footer_powered',
type: 'textarea',
rows: 4,
},
{
label: 'Copyright',
name: 'copyright',
type: 'text',
},
],
},
{
@@ -299,24 +306,6 @@ const tabsStructure = computed(() => {
},
],
},
{
label: 'Email Templates',
icon: 'MailPlus',
fields: [
{
label: 'Batch Confirmation Template',
name: 'batch_confirmation_template',
doctype: 'Email Template',
type: 'Link',
},
{
label: 'Certification Template',
name: 'certification_template',
doctype: 'Email Template',
type: 'Link',
},
],
},
{
label: 'Signup',
icon: 'LogIn',

View File

@@ -12,13 +12,13 @@
/> -->
</div>
<div class="overflow-y-scroll">
<div class="flex space-x-4">
<SettingFields :fields="fields" :data="data.doc" class="w-1/2" />
<div class="flex flex-col divide-y">
<SettingFields :fields="fields" :data="data.doc" />
<SettingFields
v-if="paymentGateway.data"
:fields="paymentGateway.data.fields"
:data="paymentGateway.data.data"
class="w-1/2"
class="pt-5 my-0"
/>
</div>
</div>
@@ -60,9 +60,28 @@ const paymentGateway = createResource({
payment_gateway: props.data.doc.payment_gateway,
}
},
transform(data) {
arrangeFields(data.fields)
return data
},
auto: true,
})
const arrangeFields = (fields) => {
fields = fields.sort((a, b) => {
if (a.type === 'Upload' && b.type !== 'Upload') {
return 1
} else if (a.type !== 'Upload' && b.type === 'Upload') {
return -1
}
return 0
})
fields.splice(3, 0, {
type: 'Column Break',
})
}
const saveSettings = createResource({
url: 'frappe.client.set_value',
makeParams(values) {

View File

@@ -6,7 +6,7 @@
<div v-for="(column, index) in columns" :key="index">
<div
class="flex flex-col space-y-5"
:class="columns.length > 1 ? 'w-72' : 'w-full'"
:class="columns.length > 1 ? 'w-[21rem]' : 'w-1/2'"
>
<div v-for="field in column">
<Link
@@ -14,6 +14,7 @@
v-model="data[field.name]"
:doctype="field.doctype"
:label="__(field.label)"
:description="__(field.description)"
/>
<div v-else-if="field.type == 'Code'">
@@ -54,11 +55,11 @@
<div v-else>
<div class="flex items-center text-sm space-x-2">
<div
class="flex items-center justify-center rounded border border-outline-gray-modals bg-white w-[10rem] py-2"
class="flex items-center justify-center rounded border border-outline-gray-1 bg-surface-gray-2 px-20 py-5"
>
<img
:src="data[field.name]?.file_url || data[field.name]"
class="w-[80%] rounded"
class="size-6 rounded"
/>
</div>
<div class="flex flex-col flex-wrap">

View File

@@ -88,56 +88,61 @@
:scrollToBottom="false"
/>
</div>
<div v-else-if="tab.label == 'Feedback'">
<BatchFeedback :batch="batch.data.name" />
</div>
</div>
</template>
</Tabs>
</div>
<div class="p-5">
<div class="text-ink-gray-7 font-semibold mb-4">
{{ __('About this batch') }}:
</div>
<div
v-html="batch.data.description"
class="leading-5 mb-4 text-ink-gray-7"
></div>
<div class="flex items-center avatar-group overlap mb-5">
<div
class="h-6 mr-1"
:class="{
'avatar-group overlap': batch.data.instructors.length > 1,
}"
>
<UserAvatar
v-for="instructor in batch.data.instructors"
:user="instructor"
/>
<div class="mb-10">
<div class="text-ink-gray-7 font-semibold mb-2">
{{ __('About this batch') }}
</div>
<div
v-html="batch.data.description"
class="leading-5 mb-4 text-ink-gray-7"
></div>
<div class="flex items-center avatar-group overlap mb-5">
<div
class="h-6 mr-1"
:class="{
'avatar-group overlap': batch.data.instructors.length > 1,
}"
>
<UserAvatar
v-for="instructor in batch.data.instructors"
:user="instructor"
/>
</div>
<CourseInstructors :instructors="batch.data.instructors" />
</div>
<DateRange
:startDate="batch.data.start_date"
:endDate="batch.data.end_date"
class="mb-3"
/>
<div class="flex items-center mb-4 text-ink-gray-7">
<Clock class="h-4 w-4 stroke-1.5 mr-2" />
<span>
{{ formatTime(batch.data.start_time) }} -
{{ formatTime(batch.data.end_time) }}
</span>
</div>
<div
v-if="batch.data.timezone"
class="flex items-center mb-4 text-ink-gray-7"
>
<Globe class="h-4 w-4 stroke-1.5 mr-2" />
<span>
{{ batch.data.timezone }}
</span>
</div>
<CourseInstructors :instructors="batch.data.instructors" />
</div>
<DateRange
:startDate="batch.data.start_date"
:endDate="batch.data.end_date"
class="mb-3"
/>
<div class="flex items-center mb-4 text-ink-gray-7">
<Clock class="h-4 w-4 stroke-1.5 mr-2" />
<span>
{{ formatTime(batch.data.start_time) }} -
{{ formatTime(batch.data.end_time) }}
</span>
</div>
<div
v-if="batch.data.timezone"
class="flex items-center mb-4 text-ink-gray-7"
>
<Globe class="h-4 w-4 stroke-1.5 mr-2" />
<span>
{{ batch.data.timezone }}
</span>
<div v-if="dayjs().isSameOrAfter(dayjs(batch.data.start_date))">
<div class="text-ink-gray-7 font-semibold mb-2">
{{ __('Feedback') }}
</div>
<BatchFeedback :batch="batch.data?.name" />
</div>
</div>
<AnnouncementModal
@@ -234,6 +239,7 @@ import Discussions from '@/components/Discussions.vue'
import DateRange from '@/components/Common/DateRange.vue'
import BulkCertificates from '@/components/Modals/BulkCertificates.vue'
import BatchFeedback from '@/components/BatchFeedback.vue'
import dayjs from 'dayjs/esm'
const user = inject('$user')
const showAnnouncementModal = ref(false)
@@ -277,11 +283,6 @@ const tabs = computed(() => {
label: 'Discussions',
icon: MessageCircle,
})
batchTabs.push({
label: 'Feedback',
icon: ClipboardPen,
})
return batchTabs
})

View File

@@ -37,14 +37,7 @@
<BatchOverlay :batch="batch" />
</div>
</div>
<!-- <div class="grid lg:grid-cols-[60%,20%] gap-4 lg:gap-20 mt-10">
<div class="order-2 lg:order-none">
</div>
<div class="order-1 lg:order-none">
<BatchOverlay :batch="batch" />
</div>
</div> -->
<BatchOverlay :batch="batch" class="md:hidden mt-5" />
<div v-if="batch.data.courses.length">
<div class="flex items-center mt-10">
<div class="text-2xl font-semibold">

View File

@@ -153,6 +153,11 @@
doctype="Email Template"
:label="__('Email Template')"
v-model="batch.confirmation_email_template"
:onCreate="
(value, close) => {
openSettings('Email Templates', close)
}
"
/>
</div>
<div class="space-y-5">

View File

@@ -20,14 +20,12 @@
</header>
<div class="p-5 pb-10">
<div
v-if="batchCount"
class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:items-center justify-between mb-5"
>
<div class="text-lg text-ink-gray-9 font-semibold">
{{ __('All Batches') }}
</div>
<div
v-if="batches.data?.length || batchCount"
class="flex flex-col space-y-2 lg:space-y-0 lg:flex-row lg:items-center lg:space-x-4"
>
<TabButtons
@@ -115,12 +113,10 @@ const is_student = computed(() => user.data?.is_student)
const currentTab = ref(is_student.value ? 'All' : 'Upcoming')
const orderBy = ref('start_date')
const readOnlyMode = window.read_only_mode
const batchCount = ref(0)
onMounted(() => {
setFiltersFromQuery()
updateBatches()
getBatchCount()
categories.value = [
{
label: '',
@@ -298,14 +294,6 @@ const canCreateBatch = () => {
return false
}
const getBatchCount = () => {
call('frappe.client.get_count', {
doctype: 'LMS Batch',
}).then((data) => {
batchCount.value = data
})
}
const breadcrumbs = computed(() => [
{
label: __('Batches'),

View File

@@ -20,7 +20,7 @@
:text="__('Average Rating')"
class="flex items-center"
>
<Star class="h-5 w-5 text-gray-100 fill-orange-500" />
<Star class="size-4 text-transparent fill-yellow-500" />
<span class="ml-1 text-ink-gray-7">
{{ course.data.rating }}
</span>

View File

@@ -20,14 +20,12 @@
</header>
<div class="p-5 pb-10">
<div
v-if="courseCount"
class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:items-center justify-between mb-5"
>
<div class="text-lg text-ink-gray-9 font-semibold">
{{ __('All Courses') }}
</div>
<div
v-if="courses.data?.length || courseCount"
class="flex flex-col space-y-2 lg:space-y-0 lg:flex-row lg:items-center lg:space-x-4"
>
<TabButtons :buttons="courseTabs" v-model="currentTab" />
@@ -172,6 +170,8 @@ const identifyUserPersona = async () => {
}
const getCourseCount = () => {
if (!user.data) return
call('frappe.client.get_count', {
doctype: 'LMS Course',
}).then((data) => {

View File

@@ -561,3 +561,24 @@ export const openSettings = (category, close) => {
settingsStore.activeTab = category
settingsStore.isSettingsOpen = true
}
export const cleanError = (message) => {
// Remove HTML tags but keep the text within the tags
const cleanMessage = message.replace(/<[^>]+>/g, (match) => {
return match.replace(/<\/?[^>]+(>|$)/g, '')
})
return cleanMessage
.replace(/&nbsp;/g, ' ')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/&amp;/g, '&')
.replace(/&#x60;/g, '`')
.replace(/&#x3D;/g, '=')
.replace(/&#x2F;/g, '/')
.replace(/&#x2C;/g, ',')
.replace(/&#x3B;/g, ';')
.replace(/&#x3A;/g, ':')
}

View File

@@ -356,6 +356,7 @@
"fieldtype": "Section Break"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [
{
@@ -371,8 +372,8 @@
"link_fieldname": "batch_name"
}
],
"modified": "2025-02-18 15:43:18.512504",
"modified_by": "Administrator",
"modified": "2025-05-21 13:30:28.904260",
"modified_by": "sayali@frappe.io",
"module": "LMS",
"name": "LMS Batch",
"owner": "Administrator",
@@ -412,12 +413,22 @@
"role": "Batch Evaluator",
"share": 1,
"write": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "LMS Student",
"share": 1
}
],
"row_format": "Dynamic",
"show_title_field_in_link": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "title",
"track_changes": 1
}
}

View File

@@ -103,7 +103,7 @@ class LMSBatch(Document):
frappe.throw(_("Seat count cannot be negative."))
students = frappe.db.count("LMS Batch Enrollment", {"batch": self.name})
if cint(self.seat_count) < students:
if cint(self.seat_count) and cint(self.seat_count) < students:
frappe.throw(_("There are no seats available in this batch."))
def validate_timetable(self):

View File

@@ -73,10 +73,11 @@
"read_only": 1
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-01-13 19:02:58.259908",
"modified_by": "Administrator",
"modified": "2025-05-21 15:58:51.667270",
"modified_by": "sayali@frappe.io",
"module": "LMS",
"name": "LMS Batch Feedback",
"owner": "Administrator",
@@ -106,7 +107,9 @@
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
"states": [],
"title_field": "member"
}

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Frappe LMS VERSION\n"
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
"POT-Creation-Date: 2025-05-16 16:04+0000\n"
"PO-Revision-Date: 2025-05-16 16:04+0000\n"
"POT-Creation-Date: 2025-05-23 16:04+0000\n"
"PO-Revision-Date: 2025-05-23 16:04+0000\n"
"Last-Translator: jannat@frappe.io\n"
"Language-Team: jannat@frappe.io\n"
"MIME-Version: 1.0\n"
@@ -50,6 +50,10 @@ msgstr ""
msgid "<a href=\"https://docs.frappe.io/learning\">Documentation</a>"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:50
msgid "<p>Dear {{ member_name }},</p>\\n\\n<p>You have been enrolled in our upcoming batch {{ batch_name }}.</p>\\n\\n<p>Thanks,</p>\\n<p>Frappe Learning</p>"
msgstr ""
#. Header text in the LMS Workspace
#: lms/lms/workspace/lms/lms.json
msgid "<span class=\"h4\"><b>Get Started</b></span>"
@@ -77,7 +81,7 @@ msgstr ""
msgid "About"
msgstr ""
#: frontend/src/pages/Batch.vue:100
#: frontend/src/pages/Batch.vue:98
msgid "About this batch"
msgstr ""
@@ -112,7 +116,7 @@ msgstr ""
#: frontend/src/components/Assessments.vue:11
#: frontend/src/components/BatchCourses.vue:11
#: frontend/src/components/BatchStudents.vue:117
#: frontend/src/components/Categories.vue:27
#: frontend/src/components/Categories.vue:43
#: frontend/src/components/Evaluators.vue:38
#: frontend/src/components/LiveClass.vue:11
#: frontend/src/components/Members.vue:44 frontend/src/pages/ProgramForm.vue:30
@@ -146,7 +150,7 @@ msgstr ""
msgid "Add a Student"
msgstr ""
#: frontend/src/components/AppSidebar.vue:557
#: frontend/src/components/AppSidebar.vue:553
msgid "Add a chapter"
msgstr ""
@@ -158,7 +162,7 @@ msgstr ""
msgid "Add a keyword and then press enter"
msgstr ""
#: frontend/src/components/AppSidebar.vue:558
#: frontend/src/components/AppSidebar.vue:554
msgid "Add a lesson"
msgstr ""
@@ -183,11 +187,11 @@ msgstr ""
msgid "Add at least one possible answer for this question: {0}"
msgstr ""
#: frontend/src/components/AppSidebar.vue:522
#: frontend/src/components/AppSidebar.vue:518
msgid "Add courses to your batch"
msgstr ""
#: frontend/src/components/AppSidebar.vue:502
#: frontend/src/components/AppSidebar.vue:498
msgid "Add students to your batch"
msgstr ""
@@ -199,11 +203,11 @@ msgstr ""
msgid "Add your assignment as {0}"
msgstr ""
#: frontend/src/components/AppSidebar.vue:438
#: frontend/src/components/AppSidebar.vue:434
msgid "Add your first chapter"
msgstr ""
#: frontend/src/components/AppSidebar.vue:453
#: frontend/src/components/AppSidebar.vue:449
msgid "Add your first lesson"
msgstr ""
@@ -231,22 +235,18 @@ msgid "Admin"
msgstr ""
#. Name of a role
#: frontend/src/pages/Batches.vue:277 lms/lms/doctype/lms_badge/lms_badge.json
#: frontend/src/pages/Batches.vue:273 lms/lms/doctype/lms_badge/lms_badge.json
msgid "All"
msgstr ""
#: frontend/src/pages/Batches.vue:27
#: frontend/src/pages/Batches.vue:26
msgid "All Batches"
msgstr ""
#: frontend/src/pages/Courses.vue:27 lms/lms/widgets/BreadCrumb.html:3
#: frontend/src/pages/Courses.vue:26 lms/lms/widgets/BreadCrumb.html:3
msgid "All Courses"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:51
msgid "All Feedback"
msgstr ""
#: lms/templates/quiz/quiz.html:141
msgid "All Submissions"
msgstr ""
@@ -286,7 +286,7 @@ msgstr ""
#. Label of the amount (Currency) field in DocType 'LMS Batch'
#. Label of the course_price (Currency) field in DocType 'LMS Course'
#. Label of the amount (Currency) field in DocType 'LMS Payment'
#: frontend/src/pages/BatchForm.vue:250 frontend/src/pages/CourseForm.vue:230
#: frontend/src/pages/BatchForm.vue:255 frontend/src/pages/CourseForm.vue:230
#: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_course/lms_course.json
#: lms/lms/doctype/lms_payment/lms_payment.json
@@ -344,11 +344,11 @@ msgstr ""
msgid "Appears on the course card in the course list"
msgstr ""
#: frontend/src/pages/BatchForm.vue:225
#: frontend/src/pages/BatchForm.vue:230
msgid "Appears when the batch URL is shared on any online platform"
msgstr ""
#: frontend/src/pages/BatchForm.vue:206
#: frontend/src/pages/BatchForm.vue:211
msgid "Appears when the batch URL is shared on socials"
msgstr ""
@@ -383,7 +383,7 @@ msgstr ""
msgid "Apps"
msgstr ""
#: frontend/src/pages/Batches.vue:287
#: frontend/src/pages/Batches.vue:283
msgid "Archived"
msgstr ""
@@ -423,7 +423,7 @@ msgid "Assessment {0} has already been added to this batch."
msgstr ""
#. Label of the show_assessments (Check) field in DocType 'LMS Settings'
#: frontend/src/components/AppSidebar.vue:570
#: frontend/src/components/AppSidebar.vue:566
#: frontend/src/components/Assessments.vue:5
#: frontend/src/components/BatchStudents.vue:68
#: frontend/src/components/BatchStudents.vue:101
@@ -484,7 +484,7 @@ msgstr ""
msgid "Assignment will appear at the bottom of the lesson."
msgstr ""
#: frontend/src/components/AppSidebar.vue:574
#: frontend/src/components/AppSidebar.vue:570
#: frontend/src/pages/Assignments.vue:211 lms/www/lms.py:273
msgid "Assignments"
msgstr ""
@@ -517,15 +517,15 @@ msgstr ""
msgid "Availability updated successfully"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:43
msgid "Average Feedback Received"
msgstr ""
#: frontend/src/components/CourseCard.vue:55
#: frontend/src/pages/CourseDetail.vue:20
msgid "Average Rating"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:38
msgid "Average of Feedback Received"
msgstr ""
#: frontend/src/pages/Lesson.vue:166
msgid "Back to Course"
msgstr ""
@@ -589,6 +589,10 @@ msgstr ""
msgid "Batch Details Raw"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:28
msgid "Batch Enrollment Confirmation"
msgstr ""
#. Name of a role
#: lms/lms/doctype/course_evaluator/course_evaluator.json
#: lms/lms/doctype/lms_batch/lms_batch.json
@@ -653,7 +657,7 @@ msgid "Batch:"
msgstr ""
#. Label of the batches (Check) field in DocType 'LMS Settings'
#: frontend/src/pages/Batches.vue:311 frontend/src/pages/Batches.vue:318
#: frontend/src/pages/Batches.vue:299 frontend/src/pages/Batches.vue:306
#: lms/lms/doctype/lms_settings/lms_settings.json lms/www/lms.py:122
msgid "Batches"
msgstr ""
@@ -752,19 +756,31 @@ msgstr ""
#. Label of the category (Link) field in DocType 'LMS Batch'
#. Label of the category (Data) field in DocType 'LMS Category'
#. Label of the category (Link) field in DocType 'LMS Course'
#: frontend/src/pages/BatchForm.vue:177 frontend/src/pages/Batches.vue:57
#: frontend/src/pages/BatchForm.vue:182 frontend/src/pages/Batches.vue:55
#: frontend/src/pages/CertifiedParticipants.vue:38
#: frontend/src/pages/CourseForm.vue:36 frontend/src/pages/Courses.vue:53
#: frontend/src/pages/CourseForm.vue:36 frontend/src/pages/Courses.vue:51
#: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_category/lms_category.json
#: lms/lms/doctype/lms_course/lms_course.json lms/templates/signup-form.html:22
msgid "Category"
msgstr ""
#: frontend/src/components/Categories.vue:23
#: frontend/src/components/Categories.vue:39
msgid "Category Name"
msgstr ""
#: frontend/src/components/Categories.vue:133
msgid "Category added successfully"
msgstr ""
#: frontend/src/components/Categories.vue:193
msgid "Category deleted successfully"
msgstr ""
#: frontend/src/components/Categories.vue:173
msgid "Category updated successfully"
msgstr ""
#. Label of the certificate (Link) field in DocType 'LMS Enrollment'
#. Label of a shortcut in the LMS Workspace
#: lms/lms/doctype/lms_enrollment/lms_enrollment.json
@@ -804,13 +820,13 @@ msgstr ""
#. Enrollment'
#. Label of a Card Break in the LMS Workspace
#. Label of a Link in the LMS Workspace
#: frontend/src/components/AppSidebar.vue:578
#: frontend/src/components/AppSidebar.vue:574
#: frontend/src/components/CourseCard.vue:115
#: frontend/src/components/Modals/Event.vue:372
#: frontend/src/pages/BatchForm.vue:62 frontend/src/pages/Batches.vue:40
#: frontend/src/pages/BatchForm.vue:62 frontend/src/pages/Batches.vue:38
#: frontend/src/pages/CourseCertification.vue:10
#: frontend/src/pages/CourseCertification.vue:135
#: frontend/src/pages/Courses.vue:36 lms/fixtures/custom_field.json
#: frontend/src/pages/Courses.vue:34 lms/fixtures/custom_field.json
#: lms/lms/doctype/certification/certification.json
#: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_enrollment/lms_enrollment.json
@@ -946,6 +962,10 @@ msgstr ""
msgid "Clearly Defined Role"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:10
msgid "Click here"
msgstr ""
#: frontend/src/components/LessonHelp.vue:30
msgid "Click on the add icon in the editor and select Quiz from the menu. It opens up a dialog, where you can either select a quiz from the list or create a new quiz. When you select the Create New option it redirects you to the quiz creation page."
msgstr ""
@@ -960,7 +980,7 @@ msgstr ""
msgid "Client Secret"
msgstr ""
#: frontend/src/components/Categories.vue:12
#: frontend/src/components/Categories.vue:27
#: frontend/src/components/Evaluators.vue:24
#: frontend/src/components/Members.vue:24
msgid "Close"
@@ -1197,6 +1217,8 @@ msgstr ""
#. Label of the content (Text) field in DocType 'Course Lesson'
#. Label of the content (Rating) field in DocType 'LMS Batch Feedback'
#: frontend/src/components/Modals/EmailTemplateModal.vue:44
#: frontend/src/components/Modals/EmailTemplateModal.vue:57
#: frontend/src/pages/LessonForm.vue:62
#: lms/lms/doctype/course_lesson/course_lesson.json
#: lms/lms/doctype/lms_batch_feedback/lms_batch_feedback.json
@@ -1488,15 +1510,15 @@ msgstr ""
msgid "Create a Live Class"
msgstr ""
#: frontend/src/components/AppSidebar.vue:565
#: frontend/src/components/AppSidebar.vue:561
msgid "Create a batch"
msgstr ""
#: frontend/src/components/AppSidebar.vue:556
#: frontend/src/components/AppSidebar.vue:552
msgid "Create a course"
msgstr ""
#: frontend/src/components/AppSidebar.vue:566
#: frontend/src/components/AppSidebar.vue:562
msgid "Create a live class"
msgstr ""
@@ -1504,15 +1526,15 @@ msgstr ""
msgid "Create an Assignment"
msgstr ""
#: frontend/src/components/AppSidebar.vue:492
#: frontend/src/components/AppSidebar.vue:488
msgid "Create your first batch"
msgstr ""
#: frontend/src/components/AppSidebar.vue:426
#: frontend/src/components/AppSidebar.vue:422
msgid "Create your first course"
msgstr ""
#: frontend/src/components/AppSidebar.vue:471
#: frontend/src/components/AppSidebar.vue:467
msgid "Create your first quiz"
msgstr ""
@@ -1520,18 +1542,18 @@ msgstr ""
msgid "Created"
msgstr ""
#: frontend/src/components/AppSidebar.vue:562
#: frontend/src/components/AppSidebar.vue:558
msgid "Creating a batch"
msgstr ""
#: frontend/src/components/AppSidebar.vue:553
#: frontend/src/components/AppSidebar.vue:549
msgid "Creating a course"
msgstr ""
#. Label of the currency (Link) field in DocType 'LMS Batch'
#. Label of the currency (Link) field in DocType 'LMS Course'
#. Label of the currency (Link) field in DocType 'LMS Payment'
#: frontend/src/pages/BatchForm.vue:257 frontend/src/pages/CourseForm.vue:247
#: frontend/src/pages/BatchForm.vue:262 frontend/src/pages/CourseForm.vue:247
#: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_course/lms_course.json
#: lms/lms/doctype/lms_payment/lms_payment.json
@@ -1543,7 +1565,7 @@ msgstr ""
msgid "Current Lesson"
msgstr ""
#: frontend/src/components/AppSidebar.vue:584
#: frontend/src/components/AppSidebar.vue:580
msgid "Custom Certificate Templates"
msgstr ""
@@ -1623,6 +1645,10 @@ msgstr ""
msgid "Dear "
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:66
msgid "Dear {{ member_name }},\\n\\nYou have been enrolled in our upcoming batch {{ batch_name }}.\\n\\nThanks,\\nFrappe Learning"
msgstr ""
#. Label of the default_currency (Link) field in DocType 'LMS Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Default Currency"
@@ -1803,6 +1829,10 @@ msgstr ""
msgid "Edit Chapter"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:8
msgid "Edit Email Template"
msgstr ""
#: frontend/src/pages/Profile.vue:72
msgid "Edit Profile"
msgstr ""
@@ -1844,11 +1874,23 @@ msgstr ""
msgid "Email Template"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:117
msgid "Email Template created successfully"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:146
msgid "Email Template updated successfully"
msgstr ""
#. Label of the email_templates_tab (Tab Break) field in DocType 'LMS Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Email Templates"
msgstr ""
#: frontend/src/components/EmailTemplates.vue:128
msgid "Email Templates deleted successfully"
msgstr ""
#. Label of the show_emails (Check) field in DocType 'LMS Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Emails"
@@ -1921,7 +1963,7 @@ msgstr ""
msgid "Enroll Now"
msgstr ""
#: frontend/src/pages/Batches.vue:290 frontend/src/pages/Courses.vue:322
#: frontend/src/pages/Batches.vue:286 frontend/src/pages/Courses.vue:322
msgid "Enrolled"
msgstr ""
@@ -1967,10 +2009,22 @@ msgstr ""
msgid "Enter the correct answer"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:122
msgid "Error creating email template"
msgstr ""
#: lms/lms/doctype/lms_batch/lms_batch.py:193
msgid "Error creating live class. Please try again. {0}"
msgstr ""
#: frontend/src/components/EmailTemplates.vue:133
msgid "Error deleting email templates"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:151
msgid "Error updating email template"
msgstr ""
#. Label of a Link in the LMS Workspace
#. Label of a shortcut in the LMS Workspace
#: frontend/src/components/Modals/Event.vue:365 lms/lms/workspace/lms/lms.json
@@ -2043,10 +2097,6 @@ msgstr ""
msgid "Evaluator is required for paid certificates."
msgstr ""
#: frontend/src/components/Modals/EvaluationModal.vue:151
msgid "Evaluator is unavailable"
msgstr ""
#. Label of the event (Select) field in DocType 'LMS Badge'
#. Label of the event (Link) field in DocType 'LMS Live Class'
#: lms/lms/doctype/lms_badge/lms_badge.json
@@ -2135,7 +2185,8 @@ msgid "Featured"
msgstr ""
#. Label of the feedback (Small Text) field in DocType 'LMS Batch Feedback'
#: frontend/src/components/BatchFeedback.vue:29
#: frontend/src/components/BatchFeedback.vue:30
#: frontend/src/pages/Batch.vue:143
#: lms/lms/doctype/lms_batch_feedback/lms_batch_feedback.json
msgid "Feedback"
msgstr ""
@@ -2336,14 +2387,14 @@ msgstr ""
msgid "Help"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:11
msgid "Help Us Improve"
msgstr ""
#: lms/templates/courses_created.html:15
msgid "Help others learn something new by creating a course."
msgstr ""
#: frontend/src/components/BatchFeedback.vue:15
msgid "Help us improve by providing your feedback."
msgstr ""
#: lms/templates/reviews.html:101
msgid "Help us improve our course material."
msgstr ""
@@ -2585,8 +2636,8 @@ msgstr ""
msgid "Interest"
msgstr ""
#: frontend/src/components/AppSidebar.vue:545
#: frontend/src/components/AppSidebar.vue:548
#: frontend/src/components/AppSidebar.vue:541
#: frontend/src/components/AppSidebar.vue:544
msgid "Introduction"
msgstr ""
@@ -2624,7 +2675,7 @@ msgstr ""
msgid "Invite Request"
msgstr ""
#: frontend/src/components/AppSidebar.vue:481
#: frontend/src/components/AppSidebar.vue:477
msgid "Invite your team and students"
msgstr ""
@@ -2656,7 +2707,7 @@ msgstr ""
msgid "Issue Date"
msgstr ""
#: frontend/src/components/AppSidebar.vue:581
#: frontend/src/components/AppSidebar.vue:577
msgid "Issue a Certificate"
msgstr ""
@@ -2957,6 +3008,7 @@ msgstr ""
#: lms/lms/doctype/lms_assignment/lms_assignment.json
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json
#: lms/lms/doctype/lms_badge_assignment/lms_badge_assignment.json
#: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_batch_enrollment/lms_batch_enrollment.json
#: lms/lms/doctype/lms_batch_feedback/lms_batch_feedback.json
#: lms/lms/doctype/lms_certificate/lms_certificate.json
@@ -3116,9 +3168,9 @@ msgid "LiveCode URL"
msgstr ""
#: frontend/src/components/Members.vue:109
#: frontend/src/pages/Assignments.vue:69 frontend/src/pages/Batches.vue:82
#: frontend/src/pages/Assignments.vue:69 frontend/src/pages/Batches.vue:80
#: frontend/src/pages/CertifiedParticipants.vue:100
#: frontend/src/pages/Courses.vue:77
#: frontend/src/pages/Courses.vue:75
#: frontend/src/pages/QuizSubmissionList.vue:39
#: frontend/src/pages/Quizzes.vue:55
msgid "Load More"
@@ -3142,7 +3194,7 @@ msgid "Location Preference"
msgstr ""
#: frontend/src/components/NoPermission.vue:28
#: frontend/src/components/QuizBlock.vue:9 frontend/src/pages/Batch.vue:188
#: frontend/src/components/QuizBlock.vue:9 frontend/src/pages/Batch.vue:193
#: frontend/src/pages/Lesson.vue:53
msgid "Login"
msgstr ""
@@ -3236,7 +3288,7 @@ msgid "Maximum Attempts"
msgstr ""
#. Label of the medium (Select) field in DocType 'LMS Batch'
#: frontend/src/pages/BatchForm.vue:172
#: frontend/src/pages/BatchForm.vue:177
#: lms/lms/doctype/lms_batch/lms_batch.json
msgid "Medium"
msgstr ""
@@ -3418,7 +3470,7 @@ msgstr ""
#. Label of the meta_image (Attach Image) field in DocType 'LMS Batch'
#. Label of the meta_image (Attach Image) field in DocType 'LMS Settings'
#: frontend/src/pages/BatchForm.vue:185
#: frontend/src/pages/BatchForm.vue:190
#: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Meta Image"
@@ -3479,7 +3531,7 @@ msgstr ""
msgid "Monday"
msgstr ""
#: frontend/src/components/AppSidebar.vue:589
#: frontend/src/components/AppSidebar.vue:585
msgid "Monetization"
msgstr ""
@@ -3500,8 +3552,13 @@ msgstr ""
msgid "My calendar"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:24
msgid "Name"
msgstr ""
#. Option for the 'Event' (Select) field in DocType 'LMS Badge'
#: frontend/src/components/Categories.vue:12
#: frontend/src/components/Categories.vue:27
#: frontend/src/components/EmailTemplates.vue:17
#: frontend/src/components/Evaluators.vue:24
#: frontend/src/components/Members.vue:24 frontend/src/pages/Assignments.vue:19
#: frontend/src/pages/Batches.vue:17 frontend/src/pages/Courses.vue:17
@@ -3518,6 +3575,10 @@ msgstr ""
msgid "New Course"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:7
msgid "New Email Template"
msgstr ""
#: frontend/src/pages/Jobs.vue:23
msgid "New Job"
msgstr ""
@@ -3617,7 +3678,7 @@ msgstr ""
msgid "No courses under review"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:101
#: frontend/src/components/BatchFeedback.vue:60
msgid "No feedback received yet."
msgstr ""
@@ -3673,7 +3734,7 @@ msgstr ""
msgid "Not Graded"
msgstr ""
#: frontend/src/components/NoPermission.vue:7 frontend/src/pages/Batch.vue:156
#: frontend/src/components/NoPermission.vue:7 frontend/src/pages/Batch.vue:161
msgid "Not Permitted"
msgstr ""
@@ -3842,7 +3903,7 @@ msgid "Pages"
msgstr ""
#. Label of the paid_batch (Check) field in DocType 'LMS Batch'
#: frontend/src/pages/BatchForm.vue:245
#: frontend/src/pages/BatchForm.vue:250
#: lms/lms/doctype/lms_batch/lms_batch.json
msgid "Paid Batch"
msgstr ""
@@ -4084,7 +4145,7 @@ msgstr ""
msgid "Please login to access the quiz."
msgstr ""
#: frontend/src/components/NoPermission.vue:25 frontend/src/pages/Batch.vue:167
#: frontend/src/components/NoPermission.vue:25 frontend/src/pages/Batch.vue:172
msgid "Please login to access this page."
msgstr ""
@@ -4223,7 +4284,7 @@ msgid "Previous"
msgstr ""
#. Label of the pricing_tab (Tab Break) field in DocType 'LMS Batch'
#: frontend/src/pages/BatchForm.vue:240
#: frontend/src/pages/BatchForm.vue:245
#: lms/lms/doctype/lms_batch/lms_batch.json
msgid "Pricing"
msgstr ""
@@ -4460,7 +4521,7 @@ msgstr ""
msgid "Quiz will appear at the bottom of the lesson."
msgstr ""
#: frontend/src/components/AppSidebar.vue:573
#: frontend/src/components/AppSidebar.vue:569
#: frontend/src/pages/QuizForm.vue:443 frontend/src/pages/Quizzes.vue:142
#: frontend/src/pages/Quizzes.vue:152 lms/www/lms.py:251
msgid "Quizzes"
@@ -4471,7 +4532,7 @@ msgstr ""
#. Label of the rating (Rating) field in DocType 'LMS Course Review'
#: frontend/src/components/CourseCardOverlay.vue:121
#: frontend/src/components/Modals/Event.vue:86
#: frontend/src/components/Modals/ReviewModal.vue:20
#: frontend/src/components/Modals/ReviewModal.vue:18
#: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json
#: lms/lms/doctype/lms_course/lms_course.json
#: lms/lms/doctype/lms_course_review/lms_course_review.json
@@ -4540,7 +4601,7 @@ msgstr ""
msgid "Related Courses"
msgstr ""
#: frontend/src/pages/BatchForm.vue:221 frontend/src/pages/CourseForm.vue:127
#: frontend/src/pages/BatchForm.vue:226 frontend/src/pages/CourseForm.vue:127
msgid "Remove"
msgstr ""
@@ -4578,7 +4639,7 @@ msgstr ""
#. Label of the review (Small Text) field in DocType 'LMS Course Review'
#. Label of a Link in the LMS Workspace
#: frontend/src/components/Modals/ReviewModal.vue:26
#: frontend/src/components/Modals/ReviewModal.vue:20
#: lms/lms/doctype/lms_course_review/lms_course_review.json
#: lms/lms/workspace/lms/lms.json lms/templates/reviews.html:143
msgid "Review"
@@ -4613,7 +4674,7 @@ msgstr ""
msgid "Role updated successfully"
msgstr ""
#: frontend/src/components/AppSidebar.vue:601
#: frontend/src/components/AppSidebar.vue:597
msgid "Roles"
msgstr ""
@@ -4678,6 +4739,7 @@ msgstr ""
#: frontend/src/components/Assignment.vue:46
#: frontend/src/components/Controls/CodeEditor.vue:25
#: frontend/src/components/Modals/AssignmentForm.vue:59
#: frontend/src/components/Modals/EmailTemplateModal.vue:12
#: frontend/src/components/Modals/Event.vue:101
#: frontend/src/components/Modals/Event.vue:129
#: frontend/src/pages/BatchForm.vue:8 frontend/src/pages/CourseForm.vue:17
@@ -4729,7 +4791,7 @@ msgstr ""
msgid "Search by Name"
msgstr ""
#: frontend/src/pages/Batches.vue:47 frontend/src/pages/Courses.vue:43
#: frontend/src/pages/Batches.vue:45 frontend/src/pages/Courses.vue:41
msgid "Search by Title"
msgstr ""
@@ -4796,15 +4858,15 @@ msgstr ""
msgid "Set your Password"
msgstr ""
#: frontend/src/components/AppSidebar.vue:549
#: frontend/src/components/AppSidebar.vue:545
msgid "Setting up"
msgstr ""
#: frontend/src/components/AppSidebar.vue:594
#: frontend/src/components/AppSidebar.vue:590
msgid "Setting up payment gateway"
msgstr ""
#: frontend/src/components/AppSidebar.vue:599
#: frontend/src/components/AppSidebar.vue:595
#: frontend/src/components/Modals/Settings.vue:7
#: frontend/src/pages/BatchForm.vue:46 frontend/src/pages/CourseForm.vue:143
#: frontend/src/pages/ProfileRoles.vue:4 frontend/src/pages/QuizForm.vue:79
@@ -5132,6 +5194,7 @@ msgid "Subgroup"
msgstr ""
#: frontend/src/components/Modals/AnnouncementModal.vue:20
#: frontend/src/components/Modals/EmailTemplateModal.vue:31
msgid "Subject"
msgstr ""
@@ -5156,7 +5219,6 @@ msgstr ""
msgid "Submission by"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:14
#: frontend/src/components/Modals/AssessmentModal.vue:9
#: frontend/src/components/Modals/BatchCourseModal.vue:9
#: frontend/src/components/Modals/EvaluationModal.vue:9
@@ -5168,6 +5230,10 @@ msgstr ""
msgid "Submit"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:35
msgid "Submit Feedback"
msgstr ""
#: frontend/src/pages/PersonaForm.vue:43
msgid "Submit and Continue"
msgstr ""
@@ -5305,8 +5371,8 @@ msgstr ""
msgid "Text"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:7
msgid "Thank you for providing your feedback!"
#: frontend/src/components/BatchFeedback.vue:6
msgid "Thank you for providing your feedback."
msgstr ""
#: lms/templates/emails/lms_course_interest.html:17
@@ -5584,6 +5650,10 @@ msgstr ""
msgid "Total Signups"
msgstr ""
#: frontend/src/components/Modals/FeedbackModal.vue:11
msgid "Training Feedback"
msgstr ""
#. Option for the 'Location Preference' (Select) field in DocType 'User'
#: lms/fixtures/custom_field.json
msgid "Travel"
@@ -5662,7 +5732,7 @@ msgstr ""
msgid "Unlisted"
msgstr ""
#: frontend/src/pages/Batches.vue:288
#: frontend/src/pages/Batches.vue:284
msgid "Unpublished"
msgstr ""
@@ -5684,14 +5754,14 @@ msgstr ""
#. Option for the 'Status' (Select) field in DocType 'Cohort'
#. Option for the 'Status' (Select) field in DocType 'LMS Certificate Request'
#. Label of the upcoming (Check) field in DocType 'LMS Course'
#: frontend/src/pages/Batches.vue:286 frontend/src/pages/CourseForm.vue:162
#: frontend/src/pages/Batches.vue:282 frontend/src/pages/CourseForm.vue:162
#: frontend/src/pages/Courses.vue:312 lms/lms/doctype/cohort/cohort.json
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.json
#: lms/lms/doctype/lms_course/lms_course.json
msgid "Upcoming"
msgstr ""
#: frontend/src/pages/Batch.vue:179
#: frontend/src/pages/Batch.vue:184
msgid "Upcoming Batches"
msgstr ""
@@ -5700,7 +5770,7 @@ msgstr ""
msgid "Upcoming Evaluations"
msgstr ""
#: frontend/src/components/BrandSettings.vue:23
#: frontend/src/components/BrandSettings.vue:24
#: frontend/src/components/PaymentSettings.vue:27
#: frontend/src/components/SettingDetails.vue:23
msgid "Update"
@@ -5710,7 +5780,7 @@ msgstr ""
msgid "Update Password"
msgstr ""
#: frontend/src/pages/BatchForm.vue:202 frontend/src/pages/CourseForm.vue:108
#: frontend/src/pages/BatchForm.vue:207 frontend/src/pages/CourseForm.vue:108
msgid "Upload"
msgstr ""
@@ -5722,6 +5792,10 @@ msgstr ""
msgid "Uploading {0}%"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:38
msgid "Use HTML"
msgstr ""
#. Label of the user (Link) field in DocType 'LMS Job Application'
#. Label of the email (Link) field in DocType 'Cohort Staff'
#. Label of the user (Link) field in DocType 'LMS Course Interest'
@@ -5807,6 +5881,10 @@ msgstr ""
msgid "View Certificate"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:56
msgid "View all feedback"
msgstr ""
#. Label of the visibility (Select) field in DocType 'LMS Batch Old'
#: lms/lms/doctype/lms_batch_old/lms_batch_old.json
msgid "Visibility"
@@ -5945,7 +6023,7 @@ msgstr ""
msgid "You are already enrolled for this course."
msgstr ""
#: frontend/src/pages/Batch.vue:161
#: frontend/src/pages/Batch.vue:166
msgid "You are not a member of this batch. Please checkout our upcoming batches."
msgstr ""
@@ -6095,6 +6173,10 @@ msgstr ""
msgid "Your class on {0} is today"
msgstr ""
#: frontend/src/components/Modals/EmailTemplateModal.vue:35
msgid "Your enrollment in {{ batch_name }} is confirmed"
msgstr ""
#: lms/lms/notification/certificate_request_reminder/certificate_request_reminder.html:3
#: lms/templates/emails/certificate_request_notification.html:3
msgid "Your evaluation for the course {0} has been scheduled on {1} at {2} {3}."
@@ -6209,10 +6291,18 @@ msgstr ""
msgid "ratings"
msgstr ""
#: frontend/src/components/Categories.vue:19
msgid "saving..."
msgstr ""
#: lms/templates/reviews.html:43
msgid "stars"
msgstr ""
#: frontend/src/components/BatchFeedback.vue:12
msgid "to view your feedback."
msgstr ""
#: frontend/src/components/StudentHeatmap.vue:10
msgid "weeks"
msgstr ""

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
"POT-Creation-Date: 2025-05-16 16:04+0000\n"
"PO-Revision-Date: 2025-05-19 16:03\n"
"PO-Revision-Date: 2025-05-24 17:46\n"
"Last-Translator: jannat@frappe.io\n"
"Language-Team: Serbian (Latin)\n"
"MIME-Version: 1.0\n"
@@ -251,7 +251,7 @@ msgstr ""
#: lms/templates/quiz/quiz.html:141
msgid "All Submissions"
msgstr "Svi podneti podaci"
msgstr "Sve podneseno"
#: lms/lms/doctype/lms_quiz/lms_quiz.py:44
msgid "All questions should have the same marks if the limit is set."
@@ -502,7 +502,7 @@ msgstr ""
#: frontend/src/pages/ProfileEvaluator.vue:137
msgid "Authorize Google Calendar Access"
msgstr ""
msgstr "Odobri pristup Google Calendar-u"
#. Option for the 'Event' (Select) field in DocType 'LMS Badge'
#: lms/lms/doctype/lms_badge/lms_badge.json
@@ -687,7 +687,7 @@ msgstr ""
#: frontend/src/components/Modals/EditProfile.vue:75
msgid "Bio"
msgstr ""
msgstr "Biografija"
#. Label of the body (Markdown Editor) field in DocType 'Course Lesson'
#: lms/lms/doctype/course_lesson/course_lesson.json
@@ -765,7 +765,7 @@ msgstr "Kategorija"
#: frontend/src/components/Categories.vue:23
msgid "Category Name"
msgstr ""
msgstr "Naziv kategorije"
#. Label of the certificate (Link) field in DocType 'LMS Enrollment'
#. Label of a shortcut in the LMS Workspace
@@ -886,7 +886,7 @@ msgstr ""
#: frontend/src/components/Quiz.vue:205 lms/templates/quiz/quiz.html:120
msgid "Check"
msgstr ""
msgstr "Označi"
#: lms/templates/emails/mention_template.html:10
msgid "Check Discussion"
@@ -955,12 +955,12 @@ msgstr ""
#. Label of the client_id (Data) field in DocType 'Zoom Settings'
#: lms/lms/doctype/zoom_settings/zoom_settings.json
msgid "Client ID"
msgstr ""
msgstr "ID klijenta"
#. Label of the client_secret (Password) field in DocType 'Zoom Settings'
#: lms/lms/doctype/zoom_settings/zoom_settings.json
msgid "Client Secret"
msgstr ""
msgstr "Tajna klijenta"
#: frontend/src/components/Categories.vue:12
#: frontend/src/components/Evaluators.vue:24
@@ -983,7 +983,7 @@ msgstr ""
#. Label of the code (Code) field in DocType 'LMS Exercise'
#: lms/lms/doctype/lms_exercise/lms_exercise.json
msgid "Code"
msgstr ""
msgstr "Šifra"
#. Name of a DocType
#. Label of the cohort (Link) field in DocType 'Cohort Join Request'
@@ -1186,7 +1186,7 @@ msgstr ""
#. Label of the confirmation_email_template (Link) field in DocType 'LMS Batch'
#: lms/lms/doctype/lms_batch/lms_batch.json
msgid "Confirmation Email Template"
msgstr ""
msgstr "Šablon imejla za potvrdu"
#: lms/lms/doctype/lms_certificate/lms_certificate.py:29
msgid "Congratulations on getting certified!"
@@ -1744,7 +1744,7 @@ msgstr ""
#: lms/lms/doctype/lms_assignment/lms_assignment.json
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json
msgid "Document"
msgstr ""
msgstr "Dokument"
#: lms/templates/emails/payment_reminder.html:11
msgid "Dont miss this opportunity to enhance your skills. Click below to complete your enrollment"
@@ -1794,7 +1794,7 @@ msgstr ""
#: frontend/src/pages/JobDetail.vue:34 frontend/src/pages/Lesson.vue:135
#: frontend/src/pages/Profile.vue:36 frontend/src/pages/Programs.vue:53
msgid "Edit"
msgstr "Izmeni"
msgstr "Uredi"
#: frontend/src/components/Modals/AssignmentForm.vue:14
msgid "Edit Assignment"
@@ -1840,7 +1840,7 @@ msgstr ""
#. Label of the email_sent (Check) field in DocType 'LMS Course Interest'
#: lms/lms/doctype/lms_course_interest/lms_course_interest.json
msgid "Email Sent"
msgstr ""
msgstr "Imejl poslat"
#: frontend/src/pages/BatchForm.vue:154
msgid "Email Template"
@@ -1864,7 +1864,7 @@ msgstr "Zaposleno lice"
#. Label of the enable (Check) field in DocType 'Zoom Settings'
#: lms/lms/doctype/zoom_settings/zoom_settings.json
msgid "Enable"
msgstr ""
msgstr "Omogući"
#: lms/lms/doctype/lms_settings/lms_settings.py:21
msgid "Enable Google API in Google Settings to send calendar invites for evaluations."
@@ -2149,7 +2149,7 @@ msgstr ""
#. Label of the field_to_check (Select) field in DocType 'LMS Badge'
#: lms/lms/doctype/lms_badge/lms_badge.json
msgid "Field To Check"
msgstr ""
msgstr "Polje za proveru"
#. Label of the major (Data) field in DocType 'Education Detail'
#: lms/lms/doctype/education_detail/education_detail.json
@@ -2293,7 +2293,7 @@ msgstr ""
#. Request'
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.json
msgid "Google Meet Link"
msgstr ""
msgstr "Google Meet Link"
#. Label of the grade (Data) field in DocType 'Education Detail'
#: frontend/src/components/Assignment.vue:158
@@ -2475,7 +2475,7 @@ msgstr ""
#: lms/lms/doctype/lms_badge/lms_badge.json
#: lms/lms/doctype/lms_exercise/lms_exercise.json
msgid "Image"
msgstr ""
msgstr "Slika"
#: frontend/src/components/Modals/EditCoverImage.vue:58
#: frontend/src/components/UnsplashImageBrowser.vue:52
@@ -3369,7 +3369,7 @@ msgstr ""
#. Group in LMS Batch Old's connections
#: lms/lms/doctype/lms_batch_old/lms_batch_old.json
msgid "Members"
msgstr ""
msgstr "Članovi"
#. Label of the membership (Select) field in DocType 'LMS Batch Old'
#: lms/lms/doctype/lms_batch_old/lms_batch_old.json
@@ -3659,7 +3659,7 @@ msgstr "Nije dozvoljeno"
#. Submission'
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json
msgid "Not Applicable"
msgstr "Nije primenljivo"
msgstr "Nije primenjivo"
#: lms/templates/assessments.html:48
msgid "Not Attempted"
@@ -3707,7 +3707,7 @@ msgstr ""
#. Label of the sb_00 (Section Break) field in DocType 'Zoom Settings'
#: lms/lms/doctype/zoom_settings/zoom_settings.json
msgid "OAuth Client ID"
msgstr ""
msgstr "OAuth ID klijenta"
#. Option for the 'Location Preference' (Select) field in DocType 'User'
#: lms/fixtures/custom_field.json
@@ -3997,7 +3997,7 @@ msgstr "Na čekanju"
#: frontend/src/pages/QuizSubmissionList.vue:102
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
msgid "Percentage"
msgstr ""
msgstr "Procenat"
#. Option for the 'Grade Type' (Select) field in DocType 'Education Detail'
#: lms/lms/doctype/education_detail/education_detail.json
@@ -4214,7 +4214,7 @@ msgstr ""
#. Label of the image (Attach Image) field in DocType 'LMS Course'
#: lms/lms/doctype/lms_course/lms_course.json
msgid "Preview Image"
msgstr ""
msgstr "Pregled slike"
#: frontend/src/pages/CourseForm.vue:195
msgid "Preview Video"
@@ -4345,7 +4345,7 @@ msgstr ""
#: frontend/src/pages/CourseForm.vue:154
#: lms/lms/doctype/lms_course/lms_course.json
msgid "Published On"
msgstr ""
msgstr "Objavljeno na"
#. Label of the purchased_certificate (Check) field in DocType 'LMS Enrollment'
#: lms/lms/doctype/lms_enrollment/lms_enrollment.json
@@ -4494,7 +4494,7 @@ msgstr ""
#. Timetable'
#: lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.json
msgid "Reference DocName"
msgstr ""
msgstr "Naziv referentnog dokumenta"
#. Label of the reference_doctype (Link) field in DocType 'LMS Batch Timetable'
#. Label of the reference_doctype (Link) field in DocType 'LMS Timetable
@@ -4502,7 +4502,7 @@ msgstr ""
#: lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.json
#: lms/lms/doctype/lms_timetable_legend/lms_timetable_legend.json
msgid "Reference DocType"
msgstr ""
msgstr "DocType referenca"
#. Label of the reference_doctype (Link) field in DocType 'LMS Badge'
#: lms/lms/doctype/lms_badge/lms_badge.json
@@ -4597,7 +4597,7 @@ msgstr ""
#: lms/templates/reviews.html:4
msgid "Reviews"
msgstr ""
msgstr "Pregledi"
#. Label of the role (Select) field in DocType 'Cohort Staff'
#. Label of the role (Select) field in DocType 'LMS Enrollment'
@@ -4707,7 +4707,7 @@ msgstr ""
#. Label of the scope (Select) field in DocType 'Cohort Web Page'
#: lms/lms/doctype/cohort_web_page/cohort_web_page.json
msgid "Scope"
msgstr ""
msgstr "Opseg"
#. Label of the score (Int) field in DocType 'LMS Quiz Submission'
#: frontend/src/pages/QuizSubmission.vue:39
@@ -4878,12 +4878,12 @@ msgstr ""
#. Label of the sidebar_tab (Tab Break) field in DocType 'LMS Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Sidebar"
msgstr ""
msgstr "Bočna traka"
#. Label of the sidebar_items (Table) field in DocType 'LMS Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Sidebar Items"
msgstr ""
msgstr "Stavke bočne trake"
#: lms/lms/user.py:29
msgid "Sign Up is disabled"
@@ -5056,7 +5056,7 @@ msgstr "Stanje"
#: lms/lms/doctype/lms_course/lms_course.json
#: lms/lms/doctype/lms_settings/lms_settings.json lms/www/lms.py:204
msgid "Statistics"
msgstr ""
msgstr "Statistika"
#. Label of the status (Select) field in DocType 'Job Opportunity'
#. Label of the status (Select) field in DocType 'Cohort'
@@ -5135,7 +5135,7 @@ msgstr ""
#: frontend/src/components/Modals/AnnouncementModal.vue:20
msgid "Subject"
msgstr "Predmet"
msgstr "Naslov"
#: frontend/src/components/Modals/AnnouncementModal.vue:93
msgid "Subject is required"
@@ -5305,7 +5305,7 @@ msgstr ""
#: lms/lms/doctype/lms_assignment/lms_assignment.json
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json
msgid "Text"
msgstr ""
msgstr "Tekst"
#: frontend/src/components/BatchFeedback.vue:7
msgid "Thank you for providing your feedback!"
@@ -5638,7 +5638,7 @@ msgstr ""
#: lms/lms/doctype/lms_assignment/lms_assignment.json
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json
msgid "URL"
msgstr ""
msgstr "URL"
#. Label of the unavailability_section (Section Break) field in DocType 'Course
#. Evaluator'
@@ -5741,12 +5741,12 @@ msgstr ""
#. Label of the user_field (Select) field in DocType 'LMS Badge'
#: lms/lms/doctype/lms_badge/lms_badge.json
msgid "User Field"
msgstr ""
msgstr "Korisničko polje"
#. Label of the user_image (Attach Image) field in DocType 'Course Evaluator'
#: lms/lms/doctype/course_evaluator/course_evaluator.json
msgid "User Image"
msgstr ""
msgstr "Slika korisnika"
#. Option for the 'Type' (Select) field in DocType 'LMS Question'
#. Option for the 'Type' (Select) field in DocType 'LMS Quiz Question'
@@ -5812,7 +5812,7 @@ msgstr ""
#. Label of the visibility (Select) field in DocType 'LMS Batch Old'
#: lms/lms/doctype/lms_batch_old/lms_batch_old.json
msgid "Visibility"
msgstr ""
msgstr "Vidljivost"
#: frontend/src/components/BatchOverlay.vue:69
msgid "Visit Batch"
@@ -6157,7 +6157,7 @@ msgstr ""
#: frontend/src/pages/CertifiedParticipants.vue:82
msgid "certificate"
msgstr ""
msgstr "sertifikat"
#: frontend/src/pages/CertifiedParticipants.vue:81
msgid "certificates"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
"POT-Creation-Date: 2025-05-16 16:04+0000\n"
"PO-Revision-Date: 2025-05-19 16:03\n"
"PO-Revision-Date: 2025-05-24 17:46\n"
"Last-Translator: jannat@frappe.io\n"
"Language-Team: Chinese Simplified\n"
"MIME-Version: 1.0\n"
@@ -109,7 +109,7 @@ msgstr "激活"
#: frontend/src/pages/Statistics.vue:16
msgid "Active Members"
msgstr ""
msgstr "活跃成员"
#: frontend/src/components/Assessments.vue:11
#: frontend/src/components/BatchCourses.vue:11
@@ -329,7 +329,7 @@ msgstr "公告发送成功"
#: frontend/src/components/Modals/AnnouncementModal.vue:96
msgid "Announcement is required"
msgstr ""
msgstr "必须填写公告内容。"
#. Label of the answer (Text Editor) field in DocType 'LMS Assignment'
#. Label of the answer (Text Editor) field in DocType 'LMS Assignment
@@ -352,7 +352,7 @@ msgstr "在分享批次链接至网络平台时显示"
#: frontend/src/pages/BatchForm.vue:206
msgid "Appears when the batch URL is shared on socials"
msgstr ""
msgstr "当班级URL在社交平台分享时显示"
#: frontend/src/pages/JobDetail.vue:51
msgid "Apply"
@@ -475,7 +475,7 @@ msgstr "学员{1}的课时{0}作业已存在。"
#: frontend/src/components/Assignment.vue:356
msgid "Assignment submitted successfully"
msgstr ""
msgstr "作业提交成功。"
#: frontend/src/components/Modals/AssignmentForm.vue:138
msgid "Assignment updated successfully"
@@ -517,7 +517,7 @@ msgstr "自动录制"
#: frontend/src/pages/ProfileEvaluator.vue:224
msgid "Availability updated successfully"
msgstr ""
msgstr "可用性更新成功。"
#: frontend/src/components/CourseCard.vue:55
#: frontend/src/pages/CourseDetail.vue:20
@@ -640,7 +640,7 @@ msgstr "批次结束日期不可早于开始日期"
#: lms/lms/api.py:246
msgid "Batch has already started."
msgstr ""
msgstr "班级已开始。"
#: lms/lms/api.py:241
msgid "Batch is sold out."
@@ -648,7 +648,7 @@ msgstr "该批次已满额。"
#: lms/lms/doctype/lms_batch/lms_batch.py:46
msgid "Batch start time cannot be greater than or equal to end time."
msgstr ""
msgstr "班级开始时间不可大于或等于结束时间。"
#: lms/templates/emails/batch_start_reminder.html:10
msgid "Batch:"
@@ -873,7 +873,7 @@ msgstr "章节添加成功"
#: frontend/src/components/CourseOutline.vue:290
msgid "Chapter deleted successfully"
msgstr ""
msgstr "章节删除成功。"
#: frontend/src/components/Modals/ChapterModal.vue:196
msgid "Chapter updated successfully"
@@ -894,7 +894,7 @@ msgstr "查看讨论"
#: frontend/src/components/Modals/AssignmentForm.vue:55
msgid "Check Submissions"
msgstr ""
msgstr "查看提交记录"
#: lms/templates/certificates_section.html:24
msgid "Check out the {0} to know more about certification."
@@ -917,7 +917,7 @@ msgstr "选择所有适用答案"
#: frontend/src/components/Modals/Question.vue:19
msgid "Choose an existing question"
msgstr ""
msgstr "选择现有题目"
#: frontend/src/components/Controls/IconPicker.vue:27
msgid "Choose an icon"
@@ -1068,7 +1068,7 @@ msgstr "评估人评语"
#. Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Common keywords that will be used for all pages"
msgstr ""
msgstr "全站通用关键词"
#. Label of the company (Data) field in DocType 'LMS Job Application'
#. Label of the company (Data) field in DocType 'Work Experience'
@@ -1171,7 +1171,7 @@ msgstr "执行评估"
#: frontend/src/pages/BatchForm.vue:141
msgid "Configurations"
msgstr ""
msgstr "系统配置"
#: frontend/src/components/UserDropdown.vue:180
msgid "Confirm"
@@ -1327,7 +1327,7 @@ msgstr "课程完成"
#: frontend/src/pages/Statistics.vue:31
msgid "Course Completions"
msgstr ""
msgstr "课程完成情况"
#. Name of a role
#: frontend/src/pages/ProfileRoles.vue:26
@@ -1352,7 +1352,7 @@ msgstr "课程描述"
#: frontend/src/pages/Statistics.vue:22
msgid "Course Enrollments"
msgstr ""
msgstr "课程注册记录"
#. Name of a DocType
#: lms/lms/doctype/course_evaluator/course_evaluator.json
@@ -1419,7 +1419,7 @@ msgstr "课程已添加至项目"
#: frontend/src/pages/CourseForm.vue:468
msgid "Course created successfully"
msgstr ""
msgstr "课程创建成功。"
#: frontend/src/pages/CourseForm.vue:489
msgid "Course deleted successfully"
@@ -1431,7 +1431,7 @@ msgstr "课程移动成功"
#: frontend/src/pages/CourseForm.vue:451
msgid "Course updated successfully"
msgstr ""
msgstr "课程更新成功。"
#: lms/lms/doctype/lms_batch/lms_batch.py:54
#: lms/lms/doctype/lms_program/lms_program.py:19
@@ -2047,7 +2047,7 @@ msgstr "付费证书需指定评估人"
#: frontend/src/components/Modals/EvaluationModal.vue:151
msgid "Evaluator is unavailable"
msgstr ""
msgstr "评估人不可用"
#. Label of the event (Select) field in DocType 'LMS Badge'
#. Label of the event (Link) field in DocType 'LMS Live Class'
@@ -2352,7 +2352,7 @@ msgstr "帮助我们改进课程材料"
#: frontend/src/pages/PersonaForm.vue:16
msgid "Help us understand your needs"
msgstr ""
msgstr "请说明您的需求"
#: lms/lms/notification/certificate_request_reminder/certificate_request_reminder.html:1
#: lms/templates/emails/certificate_request_notification.html:1
@@ -2425,7 +2425,7 @@ msgstr "图标"
#. Label of the user_category (Check) field in DocType 'LMS Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Identify User Category"
msgstr ""
msgstr "识别用户类别"
#: frontend/src/components/LessonHelp.vue:11
msgid "If Include in Preview is enabled for a lesson then the lesson will also be accessible to non logged in users."
@@ -3038,19 +3038,19 @@ msgstr "课时标题"
#: frontend/src/pages/LessonForm.vue:414
msgid "Lesson created successfully"
msgstr ""
msgstr "课时创建成功。"
#: frontend/src/components/CourseOutline.vue:217
msgid "Lesson deleted successfully"
msgstr ""
msgstr "课时删除成功。"
#: frontend/src/components/CourseOutline.vue:232
msgid "Lesson moved successfully"
msgstr ""
msgstr "课时移动成功。"
#: frontend/src/pages/LessonForm.vue:438
msgid "Lesson updated successfully"
msgstr ""
msgstr "课时更新成功。"
#. Label of the lessons (Table) field in DocType 'Course Chapter'
#. Group in Course Chapter's connections
@@ -3429,7 +3429,7 @@ msgstr "元图片"
#. Label of the meta_keywords (Small Text) field in DocType 'LMS Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Meta Keywords"
msgstr ""
msgstr "元关键词"
#. Label of the milestone (Check) field in DocType 'LMS Batch Timetable'
#: lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.json
@@ -3637,7 +3637,7 @@ msgstr "该日期无可用时段"
#: frontend/src/components/Modals/AnnouncementModal.vue:90
msgid "No students in this batch"
msgstr ""
msgstr "本班级暂无学员"
#: frontend/src/pages/AssignmentSubmissionList.vue:67
msgid "No submissions"
@@ -4011,7 +4011,7 @@ msgstr "百分比/状态"
#. Label of the persona_captured (Check) field in DocType 'LMS Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Persona Captured"
msgstr ""
msgstr "用户画像已采集"
#: frontend/src/pages/Billing.vue:99
msgid "Phone Number"
@@ -4125,7 +4125,7 @@ msgstr "请选择时区"
#: frontend/src/components/Quiz.vue:497
msgid "Please select an option"
msgstr ""
msgstr "请选择选项"
#: lms/templates/emails/job_report.html:6
msgid "Please take appropriate action at {0}"
@@ -4146,7 +4146,7 @@ msgstr "分数点如70分"
#: frontend/src/components/Modals/Question.vue:62
msgid "Possibilities"
msgstr ""
msgstr "可能性分析"
#: frontend/src/components/Modals/Question.vue:88
msgid "Possibility"
@@ -4613,7 +4613,7 @@ msgstr "角色偏好"
#: frontend/src/pages/ProfileRoles.vue:105
msgid "Role updated successfully"
msgstr ""
msgstr "角色更新成功。"
#: frontend/src/components/AppSidebar.vue:601
msgid "Roles"
@@ -4756,7 +4756,7 @@ msgstr "剩余席位"
#: lms/lms/doctype/lms_batch/lms_batch.py:103
msgid "Seat count cannot be negative."
msgstr ""
msgstr "座位数不可为负数。"
#: frontend/src/components/BatchCard.vue:15
#: frontend/src/components/BatchOverlay.vue:14
@@ -4939,11 +4939,11 @@ msgstr "部分日程时段存在重叠"
#: frontend/src/pages/ProfileEvaluator.vue:201
msgid "Slot added successfully"
msgstr ""
msgstr "时段添加成功。"
#: frontend/src/pages/ProfileEvaluator.vue:240
msgid "Slot deleted successfully"
msgstr ""
msgstr "时段删除成功。"
#. Label of the slug (Data) field in DocType 'Cohort'
#. Label of the slug (Data) field in DocType 'Cohort Subgroup'
@@ -5139,7 +5139,7 @@ msgstr "主题"
#: frontend/src/components/Modals/AnnouncementModal.vue:93
msgid "Subject is required"
msgstr ""
msgstr "必须填写主题"
#: frontend/src/components/Assignment.vue:32
msgid "Submission"
@@ -5172,7 +5172,7 @@ msgstr "提交"
#: frontend/src/pages/PersonaForm.vue:43
msgid "Submit and Continue"
msgstr ""
msgstr "提交并继续"
#: frontend/src/components/Modals/JobApplicationModal.vue:23
msgid "Submit your resume to proceed with your application for this position. Upon submission, it will be shared with the job poster."
@@ -5364,7 +5364,7 @@ msgstr "本作业暂无提交记录"
#: frontend/src/components/EmptyState.vue:11
msgid "There are no {0} currently. Keep an eye out, fresh learning experiences are on the way!"
msgstr ""
msgstr "当前暂无{0},新学习资源即将上线,敬请关注!"
#: lms/templates/course_list.html:14
msgid "There are no {0} on this site."
@@ -5415,11 +5415,11 @@ msgstr "本描述将展示在无元描述的列表及页面中"
#. Settings'
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "This image will be shown on lists and pages that don't have an image by default"
msgstr ""
msgstr "本图片将显示在无默认图片的列表及页面"
#: frontend/src/pages/Lesson.vue:24
msgid "This lesson is locked"
msgstr ""
msgstr "本课时已锁定"
#: frontend/src/pages/Lesson.vue:29
msgid "This lesson is not available for preview. Please enroll in the course to access it."
@@ -5436,7 +5436,7 @@ msgstr "本测验共包含{0}道试题"
#: frontend/src/components/AppSidebar.vue:75
#: frontend/src/components/AppSidebar.vue:115
msgid "This site is being updated. You will not be able to make any changes. Full access will be restored shortly."
msgstr ""
msgstr "系统正在更新中,期间不可进行任何修改操作。完整访问权限将很快恢复。"
#. Option for the 'Day' (Select) field in DocType 'Evaluator Schedule'
#. Option for the 'Day' (Select) field in DocType 'LMS Certificate Request'
@@ -5648,7 +5648,7 @@ msgstr "不可用状态"
#: frontend/src/pages/ProfileEvaluator.vue:259
msgid "Unavailability updated successfully"
msgstr ""
msgstr "不可用状态更新成功"
#: lms/lms/doctype/course_evaluator/course_evaluator.py:29
msgid "Unavailable From Date cannot be greater than Unavailable To Date"
@@ -5856,7 +5856,7 @@ msgstr "网页"
#: frontend/src/components/Modals/PageModal.vue:80
msgid "Web page added to sidebar"
msgstr ""
msgstr "网页已添加至侧边栏"
#. Option for the 'Day' (Select) field in DocType 'Evaluator Schedule'
#. Option for the 'Day' (Select) field in DocType 'LMS Certificate Request'
@@ -5872,7 +5872,7 @@ msgstr "欢迎使用{0}"
#: frontend/src/pages/PersonaForm.vue:32
msgid "What best describes your role?"
msgstr ""
msgstr "请选择最能描述您角色的选项"
#: frontend/src/components/LessonHelp.vue:6
msgid "What does include in preview mean?"
@@ -5880,7 +5880,7 @@ msgstr "「包含在预览中」的含义是什么?"
#: frontend/src/pages/PersonaForm.vue:21
msgid "What is your use case for Frappe Learning?"
msgstr ""
msgstr "您使用Frappe Learning的主要场景是"
#: lms/templates/courses_under_review.html:15
msgid "When a course gets submitted for review, it will be listed here."
@@ -5978,11 +5978,11 @@ msgstr "简历已作为附件发送至本邮箱"
#: frontend/src/pages/ProfileEvaluator.vue:14
msgid "You cannot change the availability when the site is being updated."
msgstr ""
msgstr "系统更新期间不可修改可用性设置。"
#: frontend/src/pages/ProfileRoles.vue:12
msgid "You cannot change the roles in read-only mode."
msgstr ""
msgstr "只读模式下不可修改角色。"
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:116
msgid "You cannot schedule evaluations after {0}."
@@ -6031,7 +6031,7 @@ msgstr "您已评价过本课程"
#: frontend/src/pages/JobDetail.vue:57
msgid "You have applied"
msgstr ""
msgstr "您已申请"
#: frontend/src/components/BatchOverlay.vue:159
msgid "You have been enrolled in this batch"
@@ -6124,7 +6124,7 @@ msgstr "提交内容已保存,讲师将尽快评阅并通知最终结果"
#: frontend/src/pages/Lesson.vue:8
msgid "Zen Mode"
msgstr ""
msgstr "禅模式"
#. Name of a DocType
#: lms/lms/doctype/zoom_settings/zoom_settings.json
@@ -6141,11 +6141,11 @@ msgstr "活动"
#: frontend/src/components/JobCard.vue:26 frontend/src/pages/JobDetail.vue:108
msgid "applicant"
msgstr ""
msgstr "申请人"
#: frontend/src/components/JobCard.vue:26 frontend/src/pages/JobDetail.vue:108
msgid "applicants"
msgstr ""
msgstr "申请人列表"
#: lms/templates/emails/payment_reminder.html:4
msgid "but didnt complete your payment"
@@ -6225,15 +6225,15 @@ msgstr "您可以"
#: frontend/src/pages/Assignments.vue:29
msgid "{0} Assignments"
msgstr ""
msgstr "{0}项作业"
#: frontend/src/pages/Jobs.vue:33
msgid "{0} Open Jobs"
msgstr ""
msgstr "{0}个开放职位"
#: frontend/src/pages/Quizzes.vue:25
msgid "{0} Quizzes"
msgstr ""
msgstr "{0}项测验"
#: lms/lms/api.py:853 lms/lms/api.py:861
msgid "{0} Settings not found"

View File

@@ -26,7 +26,8 @@
"homepage": "https://github.com/frappe/lms#readme",
"devDependencies": {
"cypress": "^13.9.0",
"cypress-file-upload": "^5.0.8"
"cypress-file-upload": "^5.0.8",
"cypress-real-events": "^1.14.0"
},
"dependencies": {
"pre-commit": "^1.2.2"

View File

@@ -2107,6 +2107,11 @@ cypress-file-upload@^5.0.8:
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1"
integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==
cypress-real-events@^1.14.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.14.0.tgz#c5495db50a2bd247f4accde983af7153566945d3"
integrity sha512-XmI8y3OZLh6cjRroPalzzS++iv+pGCaD9G9kfIbtspgv7GVsDt30dkZvSXfgZb4rAN+3pOkMVB7e0j4oXydW7Q==
cypress@^13.9.0:
version "13.17.0"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.17.0.tgz#34c3d68080c4497eace0f353bd1629587a5f600d"