diff --git a/cypress/e2e/batch_creation.cy.js b/cypress/e2e/batch_creation.cy.js index 9ec55c2c..c93bed2b 100644 --- a/cypress/e2e/batch_creation.cy.js +++ b/cypress/e2e/batch_creation.cy.js @@ -27,6 +27,10 @@ describe("Batch Creation", () => { cy.get("input[placeholder='Jane']").type(randomName); cy.get("button").contains("Add").click(); + // Open Settings + cy.get("span").contains("Learning").click(); + cy.get("span").contains("Settings").click(); + // Add evaluator cy.get("[data-dismissable-layer]") .find("span") @@ -155,6 +159,7 @@ describe("Batch Creation", () => { cy.get("button:visible").contains("Manage Batch").click(); /* Add student to batch */ + cy.get("button").contains("Students").click(); cy.get("button").contains("Add").click(); cy.get('div[role="dialog"]').first().find("button").eq(1).click(); cy.get("input[id^='headlessui-combobox-input-v-']").type(randomEmail); diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 1f5ac102..374bf990 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -8,6 +8,7 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + AdminBatchDashboard: typeof import('./src/components/AdminBatchDashboard.vue')['default'] Annoucements: typeof import('./src/components/Annoucements.vue')['default'] AnnouncementModal: typeof import('./src/components/Modals/AnnouncementModal.vue')['default'] Apps: typeof import('./src/components/Apps.vue')['default'] @@ -42,6 +43,10 @@ declare module 'vue' { CollapseSidebar: typeof import('./src/components/Icons/CollapseSidebar.vue')['default'] ColorSwatches: typeof import('./src/components/Controls/ColorSwatches.vue')['default'] ContactUsEmail: typeof import('./src/components/ContactUsEmail.vue')['default'] + CouponDetails: typeof import('./src/components/Settings/Coupons/CouponDetails.vue')['default'] + CouponItems: typeof import('./src/components/Settings/Coupons/CouponItems.vue')['default'] + CouponList: typeof import('./src/components/Settings/Coupons/CouponList.vue')['default'] + Coupons: typeof import('./src/components/Settings/Coupons/Coupons.vue')['default'] CourseCard: typeof import('./src/components/CourseCard.vue')['default'] CourseCardOverlay: typeof import('./src/components/CourseCardOverlay.vue')['default'] CourseInstructors: typeof import('./src/components/CourseInstructors.vue')['default'] @@ -107,8 +112,9 @@ declare module 'vue' { StudentHeatmap: typeof import('./src/components/StudentHeatmap.vue')['default'] StudentModal: typeof import('./src/components/Modals/StudentModal.vue')['default'] Tags: typeof import('./src/components/Tags.vue')['default'] - TransactionDetails: typeof import('./src/components/Settings/TransactionDetails.vue')['default'] - Transactions: typeof import('./src/components/Settings/Transactions.vue')['default'] + TransactionDetails: typeof import('./src/components/Settings/Transactions/TransactionDetails.vue')['default'] + TransactionList: typeof import('./src/components/Settings/Transactions/TransactionList.vue')['default'] + Transactions: typeof import('./src/components/Settings/Transactions/Transactions.vue')['default'] UnsplashImageBrowser: typeof import('./src/components/UnsplashImageBrowser.vue')['default'] UpcomingEvaluations: typeof import('./src/components/UpcomingEvaluations.vue')['default'] Uploader: typeof import('./src/components/Controls/Uploader.vue')['default'] diff --git a/frontend/package.json b/frontend/package.json index efd332cb..dfc3b043 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,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", "ace-builds": "^1.36.2", "apexcharts": "^4.3.0", diff --git a/frontend/src/components/AdminBatchDashboard.vue b/frontend/src/components/AdminBatchDashboard.vue new file mode 100644 index 00000000..934ab995 --- /dev/null +++ b/frontend/src/components/AdminBatchDashboard.vue @@ -0,0 +1,159 @@ + + diff --git a/frontend/src/components/AppSidebar.vue b/frontend/src/components/AppSidebar.vue index b09ec4c3..9334c63e 100644 --- a/frontend/src/components/AppSidebar.vue +++ b/frontend/src/components/AppSidebar.vue @@ -317,54 +317,68 @@ const addNotifications = () => { } const addQuizzes = () => { - if (isInstructor.value || isModerator.value) { - sidebarLinks.value.splice(4, 0, { - label: 'Quizzes', - icon: 'CircleHelp', - to: 'Quizzes', - activeFor: [ - 'Quizzes', - 'QuizForm', - 'QuizSubmissionList', - 'QuizSubmission', - ], - }) - } + if (!isInstructor.value && !isModerator.value) return + + const quizzesLinkExists = sidebarLinks.value.some( + (link) => link.label === 'Quizzes' + ) + if (quizzesLinkExists) return + + sidebarLinks.value.splice(4, 0, { + label: 'Quizzes', + icon: 'CircleHelp', + to: 'Quizzes', + activeFor: ['Quizzes', 'QuizForm', 'QuizSubmissionList', 'QuizSubmission'], + }) } const addAssignments = () => { - if (isInstructor.value || isModerator.value) { - sidebarLinks.value.splice(5, 0, { - label: 'Assignments', - icon: 'Pencil', - to: 'Assignments', - activeFor: [ - 'Assignments', - 'AssignmentForm', - 'AssignmentSubmissionList', - 'AssignmentSubmission', - ], - }) - } + if (!isInstructor.value && !isModerator.value) return + + const assignmentsLinkExists = sidebarLinks.value.some( + (link) => link.label === 'Assignments' + ) + if (assignmentsLinkExists) return + + sidebarLinks.value.splice(5, 0, { + label: 'Assignments', + icon: 'Pencil', + to: 'Assignments', + activeFor: [ + 'Assignments', + 'AssignmentForm', + 'AssignmentSubmissionList', + 'AssignmentSubmission', + ], + }) } const addProgrammingExercises = () => { - if (isInstructor.value || isModerator.value) { - sidebarLinks.value.splice(3, 0, { - label: 'Programming Exercises', - icon: 'Code', - to: 'ProgrammingExercises', - activeFor: [ - 'ProgrammingExercises', - 'ProgrammingExerciseForm', - 'ProgrammingExerciseSubmissions', - 'ProgrammingExerciseSubmission', - ], - }) - } + if (!isInstructor.value && !isModerator.value) return + const programmingExercisesLinkExists = sidebarLinks.value.some( + (link) => link.label === 'Programming Exercises' + ) + if (programmingExercisesLinkExists) return + + sidebarLinks.value.splice(3, 0, { + label: 'Programming Exercises', + icon: 'Code', + to: 'ProgrammingExercises', + activeFor: [ + 'ProgrammingExercises', + 'ProgrammingExerciseForm', + 'ProgrammingExerciseSubmissions', + 'ProgrammingExerciseSubmission', + ], + }) } const addPrograms = async () => { + const programsLinkExists = sidebarLinks.value.some( + (link) => link.label === 'Programs' + ) + if (programsLinkExists) return + let canAddProgram = await checkIfCanAddProgram() if (!canAddProgram) return let activeFor = ['Programs', 'ProgramDetail'] @@ -379,15 +393,21 @@ const addPrograms = async () => { } const addContactUsDetails = () => { - if (settingsStore.contactUsEmail?.data || settingsStore.contactUsURL?.data) { - sidebarLinks.value.push({ - label: 'Contact Us', - icon: settingsStore.contactUsURL?.data ? 'Headset' : 'Mail', - to: settingsStore.contactUsURL?.data - ? settingsStore.contactUsURL.data - : settingsStore.contactUsEmail?.data, - }) - } + if (!settingsStore.contactUsEmail?.data && !settingsStore.contactUsURL?.data) + return + + const contactUsLinkExists = sidebarLinks.value.some( + (link) => link.label === 'Contact Us' + ) + if (contactUsLinkExists) return + + sidebarLinks.value.push({ + label: 'Contact Us', + icon: settingsStore.contactUsURL?.data ? 'Headset' : 'Mail', + to: settingsStore.contactUsURL?.data + ? settingsStore.contactUsURL.data + : settingsStore.contactUsEmail?.data, + }) } const checkIfCanAddProgram = async () => { @@ -399,6 +419,10 @@ const checkIfCanAddProgram = async () => { } const addHome = () => { + const homeLinkExists = sidebarLinks.value.some( + (link) => link.label === 'Home' + ) + if (homeLinkExists) return sidebarLinks.value.unshift({ label: 'Home', icon: 'Home', diff --git a/frontend/src/components/BatchCourses.vue b/frontend/src/components/BatchCourses.vue index 04002f3c..f6e4b8bc 100644 --- a/frontend/src/components/BatchCourses.vue +++ b/frontend/src/components/BatchCourses.vue @@ -1,7 +1,7 @@ diff --git a/frontend/src/components/Settings/Coupons/CouponItems.vue b/frontend/src/components/Settings/Coupons/CouponItems.vue new file mode 100644 index 00000000..967ae390 --- /dev/null +++ b/frontend/src/components/Settings/Coupons/CouponItems.vue @@ -0,0 +1,140 @@ + + diff --git a/frontend/src/components/Settings/Coupons/CouponList.vue b/frontend/src/components/Settings/Coupons/CouponList.vue new file mode 100644 index 00000000..d093167e --- /dev/null +++ b/frontend/src/components/Settings/Coupons/CouponList.vue @@ -0,0 +1,203 @@ + + diff --git a/frontend/src/components/Settings/Coupons/Coupons.vue b/frontend/src/components/Settings/Coupons/Coupons.vue new file mode 100644 index 00000000..1518cae4 --- /dev/null +++ b/frontend/src/components/Settings/Coupons/Coupons.vue @@ -0,0 +1,53 @@ + + diff --git a/frontend/src/components/Settings/Coupons/types.ts b/frontend/src/components/Settings/Coupons/types.ts new file mode 100644 index 00000000..c4d67118 --- /dev/null +++ b/frontend/src/components/Settings/Coupons/types.ts @@ -0,0 +1,30 @@ +export interface Coupon { + name: string; + enabled: boolean; + code: string; + discount_type: 'Percentage' | 'Fixed Amount'; + percentage_discount?: number; + fixed_amount_discount?: number; + expires_on?: string; + description?: string; + usage_limit?: number; + redemptions_count: number; + applicable_items: ApplicableItem[]; +} + +export type ApplicableItem = { + reference_doctype: "LMS Course" | "LMS Batch"; + reference_name: string; + name: string; + parent: string; + parenttype: "LMS Coupon"; + parentfield: "applicable_items"; +} + +export interface Coupons { + data: Coupon[]; + update: (args: { filters: any[] }) => void; + insert: { submit: (params: Coupon, options: { onSuccess: (data: Coupon) => void; onError?: (err: any) => void }) => void }; + setValue: { submit: (params: Coupon, options: { onSuccess: (data: Coupon) => void; onError?: (err: any) => void }) => void }; + reload: () => void; +} \ No newline at end of file diff --git a/frontend/src/components/Settings/SettingFields.vue b/frontend/src/components/Settings/SettingFields.vue index 73f3b023..189a486c 100644 --- a/frontend/src/components/Settings/SettingFields.vue +++ b/frontend/src/components/Settings/SettingFields.vue @@ -147,6 +147,8 @@ const columns = computed(() => { } else { if (field.type == 'checkbox') { field.value = props.data[field.name] ? true : false + } else { + field.value = props.data[field.name] } currentColumn.push(field) } diff --git a/frontend/src/components/Settings/Settings.vue b/frontend/src/components/Settings/Settings.vue index 46666cff..293995f2 100644 --- a/frontend/src/components/Settings/Settings.vue +++ b/frontend/src/components/Settings/Settings.vue @@ -29,7 +29,7 @@
{ template: markRaw(Transactions), description: 'View all your payment transactions', }, + { + label: 'Coupons', + icon: 'Ticket', + template: markRaw(Coupons), + description: 'Manage discount coupons for courses and batches', + }, ], }, { diff --git a/frontend/src/components/Settings/TransactionDetails.vue b/frontend/src/components/Settings/TransactionDetails.vue deleted file mode 100644 index b996b02d..00000000 --- a/frontend/src/components/Settings/TransactionDetails.vue +++ /dev/null @@ -1,152 +0,0 @@ - - diff --git a/frontend/src/components/Settings/Transactions/TransactionDetails.vue b/frontend/src/components/Settings/Transactions/TransactionDetails.vue new file mode 100644 index 00000000..832db3fd --- /dev/null +++ b/frontend/src/components/Settings/Transactions/TransactionDetails.vue @@ -0,0 +1,184 @@ + + diff --git a/frontend/src/components/Settings/Transactions.vue b/frontend/src/components/Settings/Transactions/TransactionList.vue similarity index 82% rename from frontend/src/components/Settings/Transactions.vue rename to frontend/src/components/Settings/Transactions/TransactionList.vue index 0c6904fb..40a2289e 100644 --- a/frontend/src/components/Settings/Transactions.vue +++ b/frontend/src/components/Settings/Transactions/TransactionList.vue @@ -96,17 +96,10 @@
- diff --git a/frontend/src/pages/Batch.vue b/frontend/src/pages/Batch.vue index 032343d2..0654d8c3 100644 --- a/frontend/src/pages/Batch.vue +++ b/frontend/src/pages/Batch.vue @@ -1,12 +1,12 @@