diff --git a/.releaserc b/.releaserc
index 4f5437e0..6c6d1954 100644
--- a/.releaserc
+++ b/.releaserc
@@ -1,5 +1,5 @@
{
- "branches": ["develop"],
+ "branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer", {
"preset": "angular"
diff --git a/cypress/e2e/course_creation.cy.js b/cypress/e2e/course_creation.cy.js
index 6d9fadb1..47a592d9 100644
--- a/cypress/e2e/course_creation.cy.js
+++ b/cypress/e2e/course_creation.cy.js
@@ -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")
@@ -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")
@@ -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]")
diff --git a/frontend/components.d.ts b/frontend/components.d.ts
index 2b0e68e0..2ce38f23 100644
--- a/frontend/components.d.ts
+++ b/frontend/components.d.ts
@@ -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']
@@ -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']
diff --git a/frontend/package.json b/frontend/package.json
index bead43a6..e2b58ba3 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -25,8 +25,7 @@
"@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",
@@ -34,7 +33,7 @@
"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",
@@ -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"
},
diff --git a/frontend/src/components/Controls/MultiSelect.vue b/frontend/src/components/Controls/MultiSelect.vue
index 919fecc8..cd8b7b8a 100644
--- a/frontend/src/components/Controls/MultiSelect.vue
+++ b/frontend/src/components/Controls/MultiSelect.vue
@@ -19,9 +19,16 @@
showOptions = true
}
"
+ @click="
+ (e) => {
+ showOptions = true
+ nextTick(() => {
+ setFocus()
+ })
+ }
+ "
@focus="
() => {
- showOptions = true
if (!filterOptions.data || filterOptions.data.length === 0) {
reload('')
}
@@ -55,7 +62,11 @@
>
- {{ option.description }}
+ {{
+ option.value == option.label
+ ? option.description
+ : option.label
+ }}
{{ option.value }}
@@ -112,7 +123,7 @@ import {
} from '@headlessui/vue'
import { createResource, Popover, Button } from 'frappe-ui'
import { ref, computed, nextTick, useAttrs } from 'vue'
-import { watchDebounced } from '@vueuse/core'
+import { set, watchDebounced } from '@vueuse/core'
import { X, Plus } from 'lucide-vue-next'
const props = defineProps({
@@ -146,18 +157,20 @@ const props = defineProps({
const values = defineModel()
const attrs = useAttrs()
-const emails = ref([])
const search = ref(null)
const error = ref(null)
const query = ref('')
const text = ref('')
const showOptions = ref(false)
+const emit = defineEmits(['update:modelValue'])
const selectedValue = computed({
get: () => query.value || '',
set: (val) => {
query.value = ''
val?.value && addValue(val.value)
+ showOptions.value = false
+ emit('update:modelValue', values.value)
},
})
@@ -229,6 +242,7 @@ const addValue = (value) => {
const removeValue = (value) => {
values.value = values.value.filter((v) => v !== value)
+ emit('update:modelValue', values.value)
}
function setFocus() {
diff --git a/frontend/src/components/Controls/Uploader.vue b/frontend/src/components/Controls/Uploader.vue
index b726753d..7876f0a6 100644
--- a/frontend/src/components/Controls/Uploader.vue
+++ b/frontend/src/components/Controls/Uploader.vue
@@ -72,7 +72,7 @@ const emit = defineEmits<{
const props = withDefaults(
defineProps<{
- modelValue: string
+ modelValue: string | null
label?: string
description?: string
type?: 'image' | 'video'
diff --git a/frontend/src/components/CourseCardOverlay.vue b/frontend/src/components/CourseCardOverlay.vue
index a357a747..1fdd5ef1 100644
--- a/frontend/src/components/CourseCardOverlay.vue
+++ b/frontend/src/components/CourseCardOverlay.vue
@@ -37,7 +37,7 @@
- {{ __('Contact the Administrator to enroll for this course.') }}
+ {{ __('Contact the Administrator to enroll for this course') }}
-
-
-
-
{{ __('This course has:') }}
@@ -168,12 +139,6 @@
-
diff --git a/frontend/src/components/CourseOutline.vue b/frontend/src/components/CourseOutline.vue
index 30df3aa5..ce4a07dc 100644
--- a/frontend/src/components/CourseOutline.vue
+++ b/frontend/src/components/CourseOutline.vue
@@ -15,7 +15,10 @@
{{ __(title) }}
{
close()
router.push({
- name: 'CourseForm',
- params: {
- courseName: 'new',
- },
+ name: 'Courses',
+ query: { newCourse: '1' },
})
}
"
diff --git a/frontend/src/components/Modals/CourseProgressSummary.vue b/frontend/src/components/Modals/CourseProgressSummary.vue
deleted file mode 100644
index fbc9b692..00000000
--- a/frontend/src/components/Modals/CourseProgressSummary.vue
+++ /dev/null
@@ -1,231 +0,0 @@
-
-
-
-
diff --git a/frontend/src/components/NumberChartGraph.vue b/frontend/src/components/NumberChartGraph.vue
new file mode 100644
index 00000000..51c771c3
--- /dev/null
+++ b/frontend/src/components/NumberChartGraph.vue
@@ -0,0 +1,20 @@
+
+
+
+ {{ __(title) }}
+
+
+
+
+
diff --git a/frontend/src/components/ProgressBar.vue b/frontend/src/components/ProgressBar.vue
index eee0e487..dae0f1f5 100644
--- a/frontend/src/components/ProgressBar.vue
+++ b/frontend/src/components/ProgressBar.vue
@@ -1,6 +1,9 @@
-
+
-
+
{{ __(label) }}
-
+
+
+
+
{{ __(description) }}
@@ -19,11 +28,6 @@
-
-
-
diff --git a/frontend/src/components/Settings/Transactions/TransactionList.vue b/frontend/src/components/Settings/Transactions/TransactionList.vue
index 40a2289e..305b24a8 100644
--- a/frontend/src/components/Settings/Transactions/TransactionList.vue
+++ b/frontend/src/components/Settings/Transactions/TransactionList.vue
@@ -1,12 +1,20 @@
-
-
- {{ __(label) }}
-
-
- {{ __(description) }}
+
+
+
+ {{ __(label) }}
+
+
+ {{ __(description) }}
+
+
diff --git a/frontend/src/components/Settings/Transactions/Transactions.vue b/frontend/src/components/Settings/Transactions/Transactions.vue
index 74aeaac4..d2045b8a 100644
--- a/frontend/src/components/Settings/Transactions/Transactions.vue
+++ b/frontend/src/components/Settings/Transactions/Transactions.vue
@@ -1,6 +1,13 @@
+
{
step.value = newStep
if (newData) {
data.value = newData
+ } else {
+ data.value = null
}
}
diff --git a/frontend/src/components/Sidebar/AppSidebar.vue b/frontend/src/components/Sidebar/AppSidebar.vue
index 698e94de..6b103138 100644
--- a/frontend/src/components/Sidebar/AppSidebar.vue
+++ b/frontend/src/components/Sidebar/AppSidebar.vue
@@ -406,9 +406,13 @@ const steps = reactive([
minimize.value = true
let course = await getFirstCourse()
if (course) {
- router.push({ name: 'CourseForm', params: { courseName: course } })
+ router.push({
+ name: 'CourseDetail',
+ params: { courseName: course },
+ hash: '#settings',
+ })
} else {
- router.push({ name: 'CourseForm' })
+ router.push({ name: 'Courses', query: { newCourse: '1' } })
}
},
},
@@ -423,11 +427,12 @@ const steps = reactive([
let course = await getFirstCourse()
if (course) {
router.push({
- name: 'CourseForm',
+ name: 'CourseDetail',
params: { courseName: course },
+ hash: '#settings',
})
} else {
- router.push({ name: 'Courses' })
+ router.push({ name: 'Courses', query: { newCourse: '1' } })
}
},
},
diff --git a/frontend/src/pages/CourseDetail.vue b/frontend/src/pages/CourseDetail.vue
deleted file mode 100644
index 51158cd5..00000000
--- a/frontend/src/pages/CourseDetail.vue
+++ /dev/null
@@ -1,194 +0,0 @@
-
-
-
-
-
-
-
- {{ course.data.title }}
-
-
- {{ course.data.short_introduction }}
-
-
-
-
-
- {{ course.data.rating }}
-
-
-
·
-
-
-
- {{ course.data.enrollment_count_formatted }}
-
-
-
·
-
-
-
-
-
-
-
-
-
- {{ tag }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/src/pages/CourseCertification.vue b/frontend/src/pages/Courses/CourseCertification.vue
similarity index 98%
rename from frontend/src/pages/CourseCertification.vue
rename to frontend/src/pages/Courses/CourseCertification.vue
index 2003a09a..49a6da84 100644
--- a/frontend/src/pages/CourseCertification.vue
+++ b/frontend/src/pages/Courses/CourseCertification.vue
@@ -38,7 +38,7 @@
import { computed, inject, onMounted, ref } from 'vue'
import { Breadcrumbs, call, createResource, usePageMeta } from 'frappe-ui'
import { useRouter } from 'vue-router'
-import { sessionStore } from '../stores/session'
+import { sessionStore } from '../../stores/session'
import UpcomingEvaluations from '@/components/UpcomingEvaluations.vue'
const courseTitle = ref(null)
diff --git a/frontend/src/pages/Courses/CourseDashboard.vue b/frontend/src/pages/Courses/CourseDashboard.vue
new file mode 100644
index 00000000..f5b75a44
--- /dev/null
+++ b/frontend/src/pages/Courses/CourseDashboard.vue
@@ -0,0 +1,397 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ __('Students') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dayjs(row[column.key]).format('DD MMM YYYY') }}
+
+
+ {{ Math.ceil(row[column.key]) }}%
+
+
+ {{ row[column.key].toString() }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ __('Progress Summary') }}
+
+
+
+
+
+
+
+ {{ row.name.split('(')[0] }}
+
+
+
+ {{
+ Math.round((row.value / course.data?.enrollments) * 100)
+ }}%
+
+
+
+
+
+
+
+
+
+ {{ __('Lesson Completion') }}
+
+
+
+
+
+
+ {{ progress.chapter_idx }}.{{ progress.idx }}
+
+
+ {{ progress.title }}
+
+
+
+
+ {{
+ Math.ceil(
+ (progress.completion_count / course.data?.enrollments) *
+ 100
+ )
+ }}%
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/pages/Courses/CourseDetail.vue b/frontend/src/pages/Courses/CourseDetail.vue
new file mode 100644
index 00000000..457ed6cc
--- /dev/null
+++ b/frontend/src/pages/Courses/CourseDetail.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
diff --git a/frontend/src/pages/Courses/CourseEnrollmentModal.vue b/frontend/src/pages/Courses/CourseEnrollmentModal.vue
new file mode 100644
index 00000000..7e69c589
--- /dev/null
+++ b/frontend/src/pages/Courses/CourseEnrollmentModal.vue
@@ -0,0 +1,104 @@
+
+
+
+
diff --git a/frontend/src/pages/CourseForm.vue b/frontend/src/pages/Courses/CourseForm.vue
similarity index 55%
rename from frontend/src/pages/CourseForm.vue
rename to frontend/src/pages/Courses/CourseForm.vue
index 65a91ec4..000b7461 100644
--- a/frontend/src/pages/CourseForm.vue
+++ b/frontend/src/pages/Courses/CourseForm.vue
@@ -1,40 +1,25 @@
-
-
-
-
-
-
+
+
+
+
+
{{ __('Details') }}
@@ -45,6 +30,7 @@
:filters="{ ignore_user_type: 1 }"
:onCreate="(close) => openSettings('Members', close)"
:required="true"
+ @update:modelValue="makeFormDirty()"
/>
@@ -60,8 +46,8 @@
{{ tag }}
@@ -76,21 +62,23 @@
-
+
{{ __('Settings') }}
@@ -101,41 +89,46 @@
>
-
+
{{ __('About the Course') }}
@@ -152,8 +146,13 @@
*
(course.description = val)"
+ :content="courseResource.doc.description"
+ @change="
+ (val) => {
+ courseResource.doc.description = val
+ makeFormDirty()
+ }
+ "
: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-[7rem]"
@@ -161,92 +160,113 @@
-
+
{{ __('Pricing and Certification') }}
-
+
{{ __('Meta Tags') }}
@@ -256,6 +276,7 @@
:label="__('Meta Description')"
type="textarea"
:rows="7"
+ @input="makeFormDirty()"
/>
-
+
@@ -281,10 +303,10 @@
diff --git a/frontend/src/pages/Courses/CourseOverview.vue b/frontend/src/pages/Courses/CourseOverview.vue
new file mode 100644
index 00000000..a2759c07
--- /dev/null
+++ b/frontend/src/pages/Courses/CourseOverview.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+ {{ course.data.title }}
+
+
+ {{ course.data.short_introduction }}
+
+
+
+
+
+ {{ course.data.rating }}
+
+
+
·
+
+
+
+ {{ course.data.enrollment_count_formatted }}
+
+
+
·
+
+
+
+
+
+
+
+
+
+ {{ tag }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/pages/Courses.vue b/frontend/src/pages/Courses/Courses.vue
similarity index 94%
rename from frontend/src/pages/Courses.vue
rename to frontend/src/pages/Courses/Courses.vue
index 48fc9200..0bd81158 100644
--- a/frontend/src/pages/Courses.vue
+++ b/frontend/src/pages/Courses/Courses.vue
@@ -5,7 +5,7 @@
diff --git a/frontend/src/pages/Home/AdminHome.vue b/frontend/src/pages/Home/AdminHome.vue
index e4c6dd22..669dd305 100644
--- a/frontend/src/pages/Home/AdminHome.vue
+++ b/frontend/src/pages/Home/AdminHome.vue
@@ -74,7 +74,7 @@
}}