Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
0f92707
fix(course): resolve deletion failure for enrolled courses
raizasafeel Jan 16, 2026
fe1aa3d
feat: configurable frontend base path
NagariaHussain Jan 17, 2026
3b88892
refactor: change global variable to function in hooks
NagariaHussain Jan 17, 2026
5ce8e8c
test: flaky evaluation schedule
NagariaHussain Jan 17, 2026
2abc243
test: refactor course tests and add deletion test
raizasafeel Jan 19, 2026
a8cdc76
Merge branch 'develop' into fix/course-deletion
pateljannat Jan 19, 2026
297ffa5
fix(chapter): index recalculation on deletion
raizasafeel Jan 21, 2026
59e40d5
Merge branch 'develop' into fix/chapter-deletion
pateljannat Jan 22, 2026
9e61982
refactor(tests): extract shared test utilities into BaseTestUtils
raizasafeel Jan 21, 2026
8bf896f
refactor(tests): use BaseTestUtils inheritance for tests
raizasafeel Jan 21, 2026
cc3cb7a
Merge remote-tracking branch 'upstream/develop' into fix/course-deletion
raizasafeel Jan 22, 2026
aeb2724
test: use BaseTestUtils in test_auth for consistent test setup
raizasafeel Jan 23, 2026
0e8b232
feat: course admin dashboard
pateljannat Jan 23, 2026
05d21cf
fix: instructor notes block settings menu overlapped by sidebar
Anexus5919 Jan 23, 2026
49ed082
feat: allow moderators to create new payment from settings
pateljannat Jan 24, 2026
53c77f9
fix: reindex lessons after deletion to prevent stale form state
Anexus5919 Jan 24, 2026
b66b603
fix(MultiSelect): prevent 'Create New' button from overlapping dropdo…
raizasafeel Jan 27, 2026
b50d584
feat: new course modal
pateljannat Jan 28, 2026
ff978f5
Merge pull request #2022 from raizasafeel/fix/multiselect-last-option…
pateljannat Jan 28, 2026
fb0499a
fix(Batch): restrict student list in batch details API
raizasafeel Jan 28, 2026
383850a
fix(quiz): questions list empty on back navigation from test quiz
raizasafeel Jan 28, 2026
9eeb948
Merge pull request #2029 from raizasafeel/fix/quiz-questions-empty-on…
pateljannat Jan 28, 2026
a353635
Merge pull request #2028 from raizasafeel/fix/restrict-batch-student-…
pateljannat Jan 28, 2026
98c5318
feat: new course form slow
pateljannat Jan 29, 2026
d8891b8
fix: brand settings
pateljannat Jan 29, 2026
486e2b4
Merge branch 'develop' of https://github.com/frappe/lms into enroll-s…
pateljannat Jan 29, 2026
f8b6dfc
Merge branch 'develop' into fix/chapter-deletion
raizasafeel Jan 29, 2026
a882432
test: added test for chapter deletion and renumbering
raizasafeel Jan 29, 2026
5970540
feat: lesson completion rate in course dashboard
pateljannat Jan 29, 2026
7fe398f
test: fixed course ui tests based on new flow
pateljannat Jan 29, 2026
7360780
Merge pull request #1998 from raizasafeel/fix/course-deletion
pateljannat Jan 29, 2026
3170c06
Merge pull request #2012 from raizasafeel/fix/chapter-deletion
pateljannat Jan 29, 2026
8896a79
Merge branch 'develop' into enroll-students-in-course-from-ui
pateljannat Jan 29, 2026
933bc58
fix: api permissions
pateljannat Jan 29, 2026
a9a322c
Merge pull request #2015 from pateljannat/enroll-students-in-course-f…
pateljannat Jan 29, 2026
62631da
chore: resolved conflicts
pateljannat Jan 29, 2026
5689fbb
fix: api permissions
pateljannat Jan 29, 2026
dc06ad6
fix: removed tags methods
pateljannat Jan 29, 2026
015f903
fix: api permissions
pateljannat Jan 29, 2026
8be53d9
Merge pull request #2035 from pateljannat/issues-176
pateljannat Jan 29, 2026
6c06a86
fix: reset the value of multiple of only one correct answer in quiz q…
pateljannat Jan 30, 2026
c51eae5
fix: show course progress summary if average progress is greater than 0
pateljannat Jan 30, 2026
4be3484
fix: allow mark as read notification endpoints in auth
pateljannat Jan 30, 2026
bfda88d
chore: resolved conflicts
pateljannat Jan 30, 2026
10a9a52
Merge pull request #2001 from frappe/feat/config-fe-base-path
pateljannat Jan 30, 2026
6d3f7ef
chore: resolved conflicts
pateljannat Jan 30, 2026
4fd3e25
fix: batch access conditions
pateljannat Jan 30, 2026
2c60521
Merge pull request #2018 from Anexus5919/fix-lesson-form-stale-state
pateljannat Jan 30, 2026
ab155ae
Merge pull request #2017 from Anexus5919/fix-instructor-notes-setting…
pateljannat Jan 30, 2026
2a63f78
fix: issue date not coming from backend
vishwajeet-13 Jan 30, 2026
0407f01
Merge pull request #2038 from vishwajeet-13/fix/issue-date
pateljannat Feb 2, 2026
14e6694
fix: permissions cleanup
pateljannat Feb 2, 2026
24a511f
fix: do nor return details of unpublished courses and batches via api
pateljannat Feb 2, 2026
da33e1d
test: course and batch details
pateljannat Feb 2, 2026
ac81d18
fix: batch dashboard chart visibility
pateljannat Feb 2, 2026
fb1e1ec
Merge pull request #2040 from pateljannat/issues-177
pateljannat Feb 2, 2026
df2f2e6
fix(courses): use constant value for filter instead of translated label
raizasafeel Feb 2, 2026
be76268
fix(batches): use constant value for filter instead of translated label
raizasafeel Feb 2, 2026
2755346
Merge branch 'frappe:develop' into fix/chapter-deletion
raizasafeel Feb 2, 2026
58b49e3
fix(batches): order assessments by their index
raizasafeel Feb 2, 2026
737993c
Merge pull request #2043 from raizasafeel/fix/multilanguage-filters
pateljannat Feb 2, 2026
2a4650e
Merge pull request #2044 from raizasafeel/fix/quiz-order
pateljannat Feb 2, 2026
5384b26
Merge pull request #2042 from raizasafeel/fix/chapter-deletion
pateljannat Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ node_modules
package-lock.json
lms/public/frontend
lms/www/lms.html
lms/www/_lms.html
frappe-ui
2 changes: 1 addition & 1 deletion .releaserc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"branches": ["develop"],
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer", {
"preset": "angular"
Expand Down
37 changes: 20 additions & 17 deletions cypress/e2e/course_creation.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ describe("Course Creation", () => {
cy.get("button").contains("Create").click();
cy.get("span").contains("New Course").click();
cy.wait(500);
cy.url().should("include", "/courses/new/edit");

cy.get("label").contains("Title").type("Test Course");
cy.get("label")
Expand All @@ -35,21 +34,6 @@ describe("Course Creation", () => {
});
});

cy.get("label")
.contains("Preview Video")
.type("https://www.youtube.com/embed/-LPmw2Znl2c");
cy.get("[id=tags]").type("Learning{enter}Frappe{enter}ERPNext{enter}");
cy.get("label")
.contains("Category")
.parent()
.within(() => {
cy.get("button").click();
});
cy.get("[id^=headlessui-combobox-option-")
.should("be.visible")
.first()
.click();

/* Instructor */
cy.get("label")
.contains("Instructors")
Expand All @@ -69,13 +53,32 @@ describe("Course Creation", () => {
});
});

cy.button("Create").last().click();

// Edit Course Details
cy.wait(500);
cy.get("label")
.contains("Preview Video")
.type("https://www.youtube.com/embed/-LPmw2Znl2c");
cy.get("[id=tags]").type("Learning{enter}Frappe{enter}ERPNext{enter}");
cy.get("label")
.contains("Category")
.parent()
.within(() => {
cy.get("button").click();
});
cy.get("[id^=headlessui-combobox-option-")
.should("be.visible")
.first()
.click();

cy.get("label").contains("Published").click();
cy.get("label").contains("Published On").type("2021-01-01");
cy.button("Save").click();

// Add Chapter
cy.wait(1000);
cy.button("Add Chapter").click();
cy.button("Add").click();

cy.wait(1000);
cy.get("[data-dismissable-layer]")
Expand Down
2 changes: 1 addition & 1 deletion frontend/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ declare module 'vue' {
CourseCardOverlay: typeof import('./src/components/CourseCardOverlay.vue')['default']
CourseInstructors: typeof import('./src/components/CourseInstructors.vue')['default']
CourseOutline: typeof import('./src/components/CourseOutline.vue')['default']
CourseProgressSummary: typeof import('./src/components/Modals/CourseProgressSummary.vue')['default']
CourseReviews: typeof import('./src/components/CourseReviews.vue')['default']
CreateOutline: typeof import('./src/components/CreateOutline.vue')['default']
DateRange: typeof import('./src/components/Common/DateRange.vue')['default']
Expand Down Expand Up @@ -94,6 +93,7 @@ declare module 'vue' {
NoSidebarLayout: typeof import('./src/components/NoSidebarLayout.vue')['default']
Notes: typeof import('./src/components/Notes/Notes.vue')['default']
NotPermitted: typeof import('./src/components/NotPermitted.vue')['default']
NumberChartGraph: typeof import('./src/components/NumberChartGraph.vue')['default']
PageModal: typeof import('./src/components/Modals/PageModal.vue')['default']
PaymentGatewayDetails: typeof import('./src/components/Settings/PaymentGatewayDetails.vue')['default']
PaymentGateways: typeof import('./src/components/Settings/PaymentGateways.vue')['default']
Expand Down
11 changes: 5 additions & 6 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev": "vite",
"serve": "vite preview",
"build": "vite build --base=/assets/lms/frontend/ && yarn copy-html-entry && yarn copy-colors-json",
"copy-html-entry": "cp ../lms/public/frontend/index.html ../lms/www/lms.html",
"copy-html-entry": "cp ../lms/public/frontend/index.html ../lms/www/_lms.html",
"copy-colors-json": "cp node_modules/frappe-ui/tailwind/colors.json src/utils/frappe-ui-colors.json"
},
"dependencies": {
Expand All @@ -25,16 +25,15 @@
"@editorjs/paragraph": "2.11.3",
"@editorjs/simple-image": "1.6.0",
"@editorjs/table": "2.4.2",
"@vueuse/core": "10.4.1",
"@vueuse/router": "12.7.0",
"@vueuse/core": "^14.1.0",
"ace-builds": "1.36.2",
"apexcharts": "4.3.0",
"chart.js": "4.4.1",
"codemirror": "6.0.1",
"dayjs": "1.11.10",
"dompurify": "3.2.6",
"feather-icons": "4.28.0",
"frappe-ui": "^0.1.256",
"frappe-ui": "^0.1.261",
"highlight.js": "11.11.1",
"lucide-vue-next": "0.383.0",
"markdown-it": "14.0.0",
Expand All @@ -43,11 +42,11 @@
"socket.io-client": "4.7.2",
"thememirror": "2.0.1",
"typescript": "5.7.2",
"vue": "^3.5.0",
"vue": "^3.5.27",
"vue-chartjs": "5.3.0",
"vue-codemirror": "6.1.1",
"vue-draggable-next": "2.2.1",
"vue-router": "4.2.2",
"vue-router": "^4.6.4",
"vue3-apexcharts": "1.8.0",
"vuedraggable": "4.1.0"
},
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/AdminBatchDashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

<AxisChart
v-if="showProgressChart"
class="border"
class="border rounded-lg p-3 min-h-[300px]"
:config="{
data: filteredChartData,
title: __('Batch Summary'),
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/AssessmentPlugin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import { Dialog, FormControl } from 'frappe-ui'
import { nextTick, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { Link } from 'frappe-ui/frappe'
import { getLmsRoute } from '@/utils/basePath'

const show = ref(false)
const quiz = ref(null)
Expand Down Expand Up @@ -94,7 +95,10 @@ const addAssessment = () => {
}

const redirectToForm = () => {
if (props.type == 'quiz') window.open('/lms/quizzes?new=true', '_blank')
else window.open('/lms/assignments?new=true', '_blank')
if (props.type == 'quiz') {
window.open(getLmsRoute('quizzes?new=true'), '_blank')
} else {
window.open(getLmsRoute('assignments?new=true'), '_blank')
}
}
</script>
103 changes: 49 additions & 54 deletions frontend/src/components/Assignment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
</div>

<div class="flex flex-col overflow-y-auto">
<div class="p-5">
<div class="flex items-center justify-between mb-4">
<div class="p-5 space-y-5">
<div class="flex items-center justify-between">
<div class="font-semibold text-ink-gray-9">
{{ __('Submission') }}
</div>
Expand All @@ -53,7 +53,7 @@
!['Pass', 'Fail'].includes(submissionResource.doc?.status) &&
submissionResource.doc?.owner == user.data?.name
"
class="bg-surface-blue-2 text-ink-blue-2 p-3 rounded-md leading-5 text-sm mb-4"
class="bg-surface-blue-2 text-ink-blue-2 p-3 rounded-md leading-5 text-sm"
>
{{ __("You've successfully submitted the assignment.") }}
{{
Expand All @@ -63,12 +63,17 @@
}}
{{ __('Feel free to make edits to your submission if needed.') }}
</div>
<div v-if="showUploader()">
<div class="text-xs text-ink-gray-5 mt-1 mb-2">
{{ __('Add your assignment as {0}').format(assignment.data.type) }}
<div v-if="showUploader()" class="border rounded-lg p-3">
<div class="font-semibold mb-2">
{{ __('Upload Assignment') }}
</div>
<div class="text-ink-gray-5 text-sm mt-1 mb-4">
{{
__('You can only upload {0} files').format(assignment.data.type)
}}
</div>
<FileUploader
v-if="!submissionFile"
v-if="!submissionResource.doc?.assignment_attachment"
:fileTypes="getType()"
:uploadArgs="{
private: true,
Expand All @@ -87,21 +92,24 @@
</template>
</FileUploader>
<div v-else>
<div class="flex text-ink-gray-7">
<div class="border self-start rounded-md p-2 mr-2">
<FileText class="h-5 w-5 stroke-1.5" />
</div>
<div class="flex items-center text-ink-gray-7">
<a
:href="submissionFile.file_url"
:href="submissionResource.doc.assignment_attachment"
target="_blank"
class="flex flex-col cursor-pointer !no-underline"
class="cursor-pointer !no-underline text-sm leading-5"
>
<span class="text-sm leading-5">
{{ submissionFile.file_name }}
</span>
<span class="text-sm text-ink-gray-5 mt-1">
{{ getFileSize(submissionFile.file_size) }}
</span>
<div class="flex items-center">
<div class="border rounded-md p-2 mr-2">
<FileText class="h-5 w-5 stroke-1.5" />
</div>
<span>
{{
submissionResource.doc.assignment_attachment
.split('/')
.pop()
}}
</span>
</div>
</a>
<X
v-if="canModifyAssignment"
Expand Down Expand Up @@ -142,13 +150,13 @@
user.data?.name == submissionResource.doc?.owner &&
submissionResource.doc?.comments
"
class="mt-8 p-3 bg-surface-blue-2 rounded-md"
class="mt-8 p-3 border rounded-lg"
>
<div class="text-sm text-ink-gray-5 font-medium mb-2">
{{ __('Comments by Evaluator') }}:
<div class="text-ink-gray-5 mb-4">
{{ __('Comments by Evaluator') }}
</div>
<div
class="leading-5 text-ink-gray-9"
class="leading-6 text-ink-gray-9"
v-html="submissionResource.doc.comments"
></div>
</div>
Expand Down Expand Up @@ -204,10 +212,8 @@ import {
} from 'frappe-ui'
import { computed, inject, onMounted, onBeforeUnmount, ref, watch } from 'vue'
import { FileText, X } from 'lucide-vue-next'
import { getFileSize } from '@/utils'
import { useRouter } from 'vue-router'

const submissionFile = ref(null)
const answer = ref(null)
const comments = ref(null)
const router = useRouter()
Expand Down Expand Up @@ -266,9 +272,7 @@ const newSubmission = createResource({
assignment: props.assignmentID,
member: user.data?.name,
}
if (showUploader()) {
doc.assignment_attachment = submissionFile.value.file_url
} else {
if (!showUploader()) {
doc.answer = answer.value
}
return {
Expand All @@ -277,19 +281,6 @@ const newSubmission = createResource({
},
})

const imageResource = createResource({
url: 'lms.lms.api.get_file_info',
makeParams(values) {
return {
file_url: values.image,
}
},
auto: false,
onSuccess(data) {
submissionFile.value = data
},
})

const submissionResource = createDocumentResource({
doctype: 'LMS Assignment Submission',
name: props.submissionName,
Expand All @@ -302,11 +293,6 @@ const submissionResource = createDocumentResource({

watch(submissionResource, () => {
if (submissionResource.doc) {
if (submissionResource.doc.assignment_attachment) {
imageResource.reload({
image: submissionResource.doc.assignment_attachment,
})
}
if (submissionResource.doc.answer) {
answer.value = submissionResource.doc.answer
}
Expand All @@ -315,7 +301,10 @@ watch(submissionResource, () => {
}
if (submissionResource.isDirty) {
isDirty.value = true
} else if (showUploader() && !submissionFile.value) {
} else if (
showUploader() &&
!submissionResource.doc.assignment_attachment
) {
isDirty.value = true
} else if (!showUploader() && !answer.value) {
isDirty.value = true
Expand All @@ -325,11 +314,17 @@ watch(submissionResource, () => {
}
})

watch(submissionFile, () => {
if (props.submissionName == 'new' && submissionFile.value) {
isDirty.value = true
watch(
() => submissionResource.doc,
() => {
if (
props.submissionName == 'new' &&
submissionResource.doc?.assignment_attachment
) {
isDirty.value = true
}
}
})
)

const submitAssignment = () => {
if (props.submissionName != 'new') {
Expand All @@ -341,13 +336,13 @@ const submitAssignment = () => {
submissionResource.setValue.submit(
{
...submissionResource.doc,
assignment_attachment: submissionFile.value?.file_url,
evaluator: evaluator,
comments: comments.value,
answer: answer.value,
},
{
onSuccess(data) {
isDirty.value = false
toast.success(__('Changes saved successfully'))
},
}
Expand Down Expand Up @@ -388,7 +383,7 @@ const addNewSubmission = () => {

const saveSubmission = (file) => {
isDirty.value = true
submissionFile.value = file
submissionResource.doc.assignment_attachment = file.file_url
}

const markLessonProgress = () => {
Expand Down Expand Up @@ -439,7 +434,7 @@ const validateFile = (file) => {

const removeSubmission = () => {
isDirty.value = true
submissionFile.value = null
submissionResource.doc.assignment_attachment = ''
}

const canGradeSubmission = computed(() => {
Expand Down
Loading
Loading