Merge branch 'frappe:develop' into fix/payment

This commit is contained in:
Raizaaa
2026-03-05 02:07:41 +05:30
committed by GitHub
21 changed files with 251 additions and 334 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ declare module 'vue' {
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']
GoogleMeetAccountModal: typeof import('./src/components/Modals/GoogleMeetAccountModal.vue')['default']
GoogleMeetAccountModal: typeof import('./src/components/Settings/GoogleMeetAccountModal.vue')['default']
GoogleMeetSettings: typeof import('./src/components/Settings/GoogleMeetSettings.vue')['default']
IconPicker: typeof import('./src/components/Controls/IconPicker.vue')['default']
IndicatorIcon: typeof import('./src/components/Icons/IndicatorIcon.vue')['default']
+2 -2
View File
@@ -3,13 +3,13 @@
<div class="space-y-2">
<div class="flex items-center text-sm font-medium space-x-2">
<span>
{{ __('What does include in preview mean?') }}
{{ __('What are Instructor Notes?') }}
</span>
</div>
<div class="text-xs text-ink-gray-5 mb-1 leading-5">
{{
__(
'If Include in Preview is enabled for a lesson then the lesson will also be accessible to non logged in users.'
'Instructor Notes are private notes that only instructors can see. They can be used to provide additional context or guidance for the lesson.'
)
}}
</div>
@@ -16,7 +16,12 @@
>
<template #body-content>
<div class="space-y-4 text-base">
<FormControl label="Title" v-model="chapter.title" :required="true" />
<FormControl
label="Title"
v-model="chapter.title"
:required="true"
autocomplete="off"
/>
<Switch
size="sm"
:label="__('SCORM Package')"
@@ -1,9 +1,14 @@
<template>
<div class="flex flex-col h-full">
<div class="flex flex-col h-full text-p-base">
<div>
<div class="flex items-center justify-between">
<div class="font-semibold mb-1 text-ink-gray-9">
{{ __(label) }}
<div class="space-y-2">
<div class="font-semibold text-xl text-ink-gray-9">
{{ __(label) }}
</div>
<div class="text-ink-gray-6 leading-5">
{{ __(description) }}
</div>
</div>
<div class="space-x-2">
<Badge
@@ -21,9 +26,6 @@
</Button>
</div>
</div>
<div class="text-xs text-ink-gray-5">
{{ __(description) }}
</div>
</div>
<div class="overflow-y-auto">
<SettingFields :sections="sections" :data="branding.data" />
@@ -2,7 +2,7 @@
<div class="flex min-h-0 flex-col text-base">
<div class="flex items-center justify-between mb-5">
<div>
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
<div class="text-xl font-semibold mb-2 text-ink-gray-9">
{{ __(label) }}
</div>
<div class="text-ink-gray-6 leading-5">
@@ -2,7 +2,7 @@
<div class="flex min-h-0 flex-col text-base">
<div class="flex items-center justify-between">
<div>
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
<div class="text-xl font-semibold mb-2 text-ink-gray-9">
{{ __(label) }}
</div>
<div class="text-ink-gray-6 leading-5">
@@ -10,7 +10,7 @@
</div>
</div>
<div class="flex item-center space-x-2">
<Button variant="solid" @click="() => (showForm = !showForm)">
<Button @click="() => (showForm = !showForm)">
<template #prefix>
<Plus class="size-4 stroke-1.5" />
</template>
+2 -2
View File
@@ -2,7 +2,7 @@
<div class="flex min-h-0 flex-col text-base">
<div class="flex items-center justify-between">
<div>
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
<div class="text-xl font-semibold mb-2 text-ink-gray-9">
{{ __(label) }}
</div>
<div class="text-ink-gray-6 leading-5">
@@ -10,7 +10,7 @@
</div>
</div>
<div class="flex item-center space-x-2">
<Button variant="solid" @click="() => (showForm = !showForm)">
<Button @click="() => (showForm = !showForm)">
<template #prefix>
<Plus class="size-4 stroke-1.5" />
</template>
@@ -2,7 +2,7 @@
<div class="flex min-h-0 flex-col text-base">
<div class="flex items-center justify-between mb-5">
<div>
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
<div class="text-xl font-semibold mb-2 text-ink-gray-9">
{{ __(label) }}
</div>
<div class="text-ink-gray-6 leading-5">
@@ -2,10 +2,13 @@
<div class="flex flex-col h-full text-base overflow-y-hidden">
<div class="">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center space-x-2">
<div class="flex flex-col space-y-2">
<div class="text-xl font-semibold leading-none text-ink-gray-9">
{{ __(label) }}
</div>
<div class="text-ink-gray-6 leading-5">
{{ __(description) }}
</div>
</div>
<div class="space-x-2">
<Badge
@@ -19,9 +22,6 @@
</Button>
</div>
</div>
<div class="text-ink-gray-6 leading-5">
{{ __(description) }}
</div>
</div>
<SettingFields :sections="sections" :data="data.doc" />
+47 -35
View File
@@ -269,41 +269,6 @@ const tabsStructure = computed(() => {
},
],
},
],
},
{
label: 'Lists',
hideLabel: false,
items: [
{
label: 'Members',
description:
'Add new members or manage roles and permissions of existing members',
icon: 'UserRoundPlus',
template: markRaw(Members),
},
{
label: 'Evaluators',
description: '',
icon: 'UserCheck',
description:
'Add new evaluators or check the slots existing evaluators',
template: markRaw(Evaluators),
},
{
label: 'Zoom Accounts',
description:
'Manage zoom accounts to conduct live classes from batches',
icon: 'Video',
template: markRaw(ZoomSettings),
},
{
label: 'Google Meet Accounts',
description:
'Manage Google Meet accounts to conduct live classes from batches',
icon: 'Video',
template: markRaw(GoogleMeetSettings),
},
{
label: 'Badges',
description:
@@ -325,6 +290,27 @@ const tabsStructure = computed(() => {
},
],
},
{
label: 'Users',
hideLabel: false,
items: [
{
label: 'Members',
description:
'Add new members or manage roles and permissions of existing members',
icon: 'User',
template: markRaw(Members),
},
{
label: 'Evaluators',
description: '',
icon: 'UserCircle2',
description:
'Add new evaluators or check the slots of existing evaluators',
template: markRaw(Evaluators),
},
],
},
{
label: 'Payment',
hideLabel: false,
@@ -395,6 +381,26 @@ const tabsStructure = computed(() => {
},
],
},
{
label: 'Conferencing',
hideLabel: false,
items: [
{
label: 'Zoom',
description:
'Manage zoom accounts to conduct live classes from batches',
icon: 'Video',
template: markRaw(ZoomSettings),
},
{
label: 'Google Meet',
description:
'Manage Google Meet accounts to conduct live classes from batches',
icon: 'Presentation',
template: markRaw(GoogleMeetSettings),
},
],
},
{
label: 'Customize',
hideLabel: false,
@@ -402,6 +408,8 @@ const tabsStructure = computed(() => {
{
label: 'Branding',
icon: 'Blocks',
description:
'Customize the brand name and logo to make the application your own',
template: markRaw(BrandSettings),
sections: [
{
@@ -490,6 +498,8 @@ const tabsStructure = computed(() => {
{
label: 'Signup',
icon: 'LogIn',
description:
'Manage the settings related to user signup and registration',
sections: [
{
columns: [
@@ -525,6 +535,8 @@ const tabsStructure = computed(() => {
{
label: 'SEO',
icon: 'Search',
description:
'Manage the SEO settings to improve your website ranking on search engines',
sections: [
{
columns: [
@@ -2,7 +2,7 @@
<div class="flex min-h-0 flex-col text-base">
<div class="flex items-center justify-between mb-5">
<div>
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
<div class="text-xl font-semibold mb-2 text-ink-gray-9">
{{ __(label) }}
</div>
<div class="text-ink-gray-6 leading-5">
+2 -2
View File
@@ -507,11 +507,11 @@ const conferencingOptions = computed(() => {
const mediumOptions = computed(() => {
return [
{
label: 'Online',
label: __('Online'),
value: 'Online',
},
{
label: 'Offline',
label: __('Offline'),
value: 'Offline',
},
]
@@ -8,81 +8,86 @@
>
<template #body-content>
<div class="text-base">
<div class="grid grid-cols-2 gap-10">
<div class="space-y-5">
<FormControl
v-model="batch.title"
:label="__('Title')"
:required="true"
/>
<FormControl
v-model="batch.start_date"
:label="__('Start Date')"
type="date"
:required="true"
/>
<FormControl
v-model="batch.end_date"
:label="__('End Date')"
type="date"
:required="true"
/>
<Link
doctype="LMS Category"
v-model="batch.category"
:label="__('Category')"
:allowCreate="true"
:onCreate="
() => {
openSettings('Categories')
show = false
}
"
/>
</div>
<div class="space-y-5">
<FormControl
v-model="batch.start_time"
:label="__('Start Time')"
type="time"
:required="true"
/>
<FormControl
v-model="batch.end_time"
:label="__('End Time')"
type="time"
:required="true"
/>
<FormControl
v-model="batch.timezone"
:label="__('Timezone')"
:required="true"
/>
<FormControl
v-model="batch.seat_count"
:label="__('Seat Count')"
type="number"
:required="false"
/>
</div>
<div class="grid grid-cols-3 gap-5">
<FormControl
v-model="batch.title"
:label="__('Title')"
:required="true"
/>
<FormControl
v-model="batch.start_date"
:label="__('Start Date')"
type="date"
:required="true"
/>
<FormControl
v-model="batch.end_date"
:label="__('End Date')"
type="date"
:required="true"
/>
<FormControl
v-model="batch.start_time"
:label="__('Start Time')"
type="time"
:required="true"
/>
<FormControl
v-model="batch.end_time"
:label="__('End Time')"
type="time"
:required="true"
/>
<FormControl
v-model="batch.timezone"
:label="__('Timezone')"
:required="true"
/>
<Link
doctype="LMS Category"
v-model="batch.category"
:label="__('Category')"
:allowCreate="true"
:onCreate="
() => {
openSettings('Categories')
show = false
}
"
/>
<FormControl
v-model="batch.seat_count"
:label="__('Seat Count')"
type="number"
:required="false"
/>
<FormControl
v-model="batch.medium"
type="select"
:options="mediumOptions"
:label="__('Medium')"
class="mb-4"
/>
</div>
<div class="space-y-5 border-t mt-5 pt-5">
<MultiSelect
v-model="batch.instructors"
doctype="Course Evaluator"
:label="__('Instructors')"
:required="true"
:onCreate="(close: () => void) => openSettings('Evaluators', close)"
:filters="{ ignore_user_type: 1 }"
/>
<FormControl
v-model="batch.description"
:label="__('Description')"
type="textarea"
:required="true"
:rows="4"
/>
<div class="grid grid-cols-2 gap-5">
<MultiSelect
v-model="batch.instructors"
doctype="Course Evaluator"
:label="__('Instructors')"
:required="true"
:onCreate="(close: () => void) => openSettings('Evaluators', close)"
:filters="{ ignore_user_type: 1 }"
/>
<FormControl
v-model="batch.description"
:label="__('Description')"
type="textarea"
:required="true"
:rows="4"
/>
</div>
<div class="">
<div class="mb-1.5 text-sm text-ink-gray-5">
{{ __('Batch Details') }}
@@ -93,7 +98,7 @@
@change="(val: string) => (batch.batch_details = val)"
:editable="true"
:fixedMenu="true"
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[10rem]"
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[10rem] max-h-[14rem] overflow-auto"
/>
</div>
</div>
@@ -111,7 +116,7 @@
<script setup lang="ts">
import { Button, Dialog, FormControl, TextEditor, toast } from 'frappe-ui'
import { useOnboarding, useTelemetry } from 'frappe-ui/frappe'
import { ref, inject, onMounted, onBeforeUnmount } from 'vue'
import { computed, inject, onMounted, onBeforeUnmount, ref } from 'vue'
import { useRouter } from 'vue-router'
import { cleanError, openSettings, sanitizeHTML, escapeHTML } from '@/utils'
import Link from '@/components/Controls/Link.vue'
@@ -139,6 +144,7 @@ type Batch = {
instructors: string[]
category: string | null
seat_count: number
medium: string | null
}
const batch = ref<Batch>({
@@ -153,6 +159,7 @@ const batch = ref<Batch>({
instructors: [],
category: null,
seat_count: 0,
medium: null,
})
const validateFields = () => {
@@ -227,4 +234,17 @@ onBeforeUnmount(() => {
data: batch.value,
})
})
const mediumOptions = computed(() => {
return [
{
label: __('Online'),
value: 'Online',
},
{
label: __('Offline'),
value: 'Offline',
},
]
})
</script>
@@ -13,6 +13,7 @@
v-model="course.title"
:label="__('Title')"
:required="true"
autocomplete="off"
/>
<Link
doctype="LMS Category"
@@ -57,7 +58,7 @@
@change="(val: string) => (course.description = val)"
:editable="true"
:fixedMenu="true"
editorClass="prose-sm max-w-none border-b border-x border-outline-gray-modals bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[10rem]"
editorClass="prose-sm max-w-none border-b border-x border-outline-gray-modals bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[10rem] max-h-[17rem] overflow-auto"
/>
</div>
</div>
@@ -87,6 +88,7 @@ const router = useRouter()
const { capture } = useTelemetry()
const { updateOnboardingStep } = useOnboarding('learning')
const user = inject<any>('$user')
const courseCreated = ref(false)
const props = defineProps<{
courses: any
@@ -139,6 +141,7 @@ const saveCourse = (close: () => void = () => {}) => {
toast.success(__('Course created successfully'))
close()
capture('course_created')
courseCreated.value = true
router.push({
name: 'CourseDetail',
params: { courseName: data.name },
@@ -178,8 +181,10 @@ onMounted(() => {
onBeforeUnmount(() => {
window.removeEventListener('keydown', keyboardShortcut)
capture('course_form_closed', {
data: course.value,
})
if (!courseCreated.value) {
capture('course_form_closed', {
data: course.value,
})
}
})
</script>
+13 -7
View File
@@ -15,17 +15,22 @@
</Button>
</header>
<div class="py-5">
<div class="w-5/6 mx-auto">
<div class="grid grid-cols-2 gap-5 w-5/6 mx-auto">
<FormControl
v-model="lesson.title"
label="Title"
:label="__('Title')"
class="mb-4"
:required="true"
autocomplete="off"
/>
<FormControl
<Switch
v-model="lesson.include_in_preview"
type="checkbox"
label="Include in Preview"
:label="__('Include in Preview')"
:description="
__(
'If enabled, the lesson will also be accessible to users who are not enrolled in the course.'
)
"
/>
</div>
<div class="border-t mt-4">
@@ -83,6 +88,7 @@ import {
Button,
createResource,
FormControl,
Switch,
usePageMeta,
toast,
} from 'frappe-ui'
@@ -708,8 +714,8 @@ iframe {
height: 15px;
}
.ce-popover--opened > .ce-popover__container {
max-height: unset;
.ce-popover--opened {
max-height: unset !important;
}
.cdx-search-field__icon svg {
+1 -1
View File
@@ -126,6 +126,7 @@ export function getEditorTools() {
defaultStyle: 'ordered',
},
},
upload: Upload,
table: {
class: Table,
inlineToolbar: true,
@@ -133,7 +134,6 @@ export function getEditorTools() {
quiz: Quiz,
assignment: Assignment,
program: Program,
upload: Upload,
markdown: {
class: Markdown,
inlineToolbar: true,