From e0601c7b389238a326dd348989f582ec2cd5088f Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Thu, 21 Aug 2025 21:16:21 +0530 Subject: [PATCH 1/7] feat: home page --- frontend/src/components/AppSidebar.vue | 2 +- frontend/src/components/LiveClass.vue | 2 +- frontend/src/components/SidebarLink.vue | 2 +- .../src/components/UpcomingEvaluations.vue | 20 +- frontend/src/pages/Home/AdminHome.vue | 75 ++++++ frontend/src/pages/Home/Home.vue | 104 ++++++++ frontend/src/pages/Home/StudentHome.vue | 192 ++++++++++++++ frontend/src/router.js | 6 +- frontend/src/utils/index.js | 6 + lms/lms/utils.py | 242 +++++++++++++++++- 10 files changed, 626 insertions(+), 25 deletions(-) create mode 100644 frontend/src/pages/Home/AdminHome.vue create mode 100644 frontend/src/pages/Home/Home.vue create mode 100644 frontend/src/pages/Home/StudentHome.vue diff --git a/frontend/src/components/AppSidebar.vue b/frontend/src/components/AppSidebar.vue index 2f99bbed..674268cc 100644 --- a/frontend/src/components/AppSidebar.vue +++ b/frontend/src/components/AppSidebar.vue @@ -365,7 +365,7 @@ const addPrograms = async () => { let canAddProgram = await checkIfCanAddProgram() if (!canAddProgram) return let activeFor = ['Programs', 'ProgramDetail'] - let index = 1 + let index = 2 sidebarLinks.value.splice(index, 0, { label: 'Programs', diff --git a/frontend/src/components/LiveClass.vue b/frontend/src/components/LiveClass.vue index 78c032d2..2669f938 100644 --- a/frontend/src/components/LiveClass.vue +++ b/frontend/src/components/LiveClass.vue @@ -52,7 +52,7 @@ {{ formatTime(cls.time) }} - - {{ dayjs(getClassEnd(cls)).format('HH:mm') }} + {{ dayjs(getClassEnd(cls)).format('HH:mm A') }}
- + -
+
{{ __('Upcoming Evaluations') }}
-
+
- + {{ evl.course_title }}
-
- {{ __('Please schedule an evaluation to get certified.') }} +
+ {{ __('Schedule an evaluation to get certified.') }}
course.course), batch: props.batch, }, diff --git a/frontend/src/pages/Home/AdminHome.vue b/frontend/src/pages/Home/AdminHome.vue new file mode 100644 index 00000000..1c4c9f97 --- /dev/null +++ b/frontend/src/pages/Home/AdminHome.vue @@ -0,0 +1,75 @@ + + diff --git a/frontend/src/pages/Home/Home.vue b/frontend/src/pages/Home/Home.vue new file mode 100644 index 00000000..3817b9b8 --- /dev/null +++ b/frontend/src/pages/Home/Home.vue @@ -0,0 +1,104 @@ + + diff --git a/frontend/src/pages/Home/StudentHome.vue b/frontend/src/pages/Home/StudentHome.vue new file mode 100644 index 00000000..03c695bd --- /dev/null +++ b/frontend/src/pages/Home/StudentHome.vue @@ -0,0 +1,192 @@ + + diff --git a/frontend/src/router.js b/frontend/src/router.js index a25d12f6..eb187b41 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -3,13 +3,11 @@ import { usersStore } from './stores/user' import { sessionStore } from './stores/session' import { useSettings } from './stores/settings' -let defaultRoute = '/courses' const routes = [ { path: '/', - redirect: { - name: 'Courses', - }, + name: 'Home', + component: () => import('@/pages/Home/Home.vue'), }, { path: '/courses', diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js index eba86850..b4b0fd0e 100644 --- a/frontend/src/utils/index.js +++ b/frontend/src/utils/index.js @@ -403,6 +403,12 @@ export function getUserTimezone() { export function getSidebarLinks() { return [ + { + label: 'Home', + icon: 'Home', + to: 'Home', + activeFor: ['Home'], + }, { label: 'Courses', icon: 'BookOpen', diff --git a/lms/lms/utils.py b/lms/lms/utils.py index 3e1f489b..f9fedcc2 100644 --- a/lms/lms/utils.py +++ b/lms/lms/utils.py @@ -2,6 +2,7 @@ import hashlib import json import re import string +from datetime import datetime, timedelta import frappe import razorpay @@ -39,12 +40,12 @@ def slugify(title, used_slugs=None): If a list of used slugs is specified, it will make sure the generated slug is not one of them. - >>> slugify("Hello World!") - 'hello-world' - >>> slugify("Hello World!", ["hello-world"]) - 'hello-world-2' - >>> slugify("Hello World!", ["hello-world", "hello-world-2"]) - 'hello-world-3' + >>> slugify("Hello World!") + 'hello-world' + >>> slugify("Hello World!", ["hello-world"]) + 'hello-world-2' + >>> slugify("Hello World!", ["hello-world", "hello-world-2"]) + 'hello-world-3' """ if not used_slugs: used_slugs = [] @@ -844,14 +845,22 @@ def get_evaluator(course, batch=None): @frappe.whitelist() -def get_upcoming_evals(student, courses, batch=None): +def get_upcoming_evals(courses=None, batch=None): + if frappe.session.user == "Guest": + return [] + + if not courses: + courses = [] + filters = { - "member": student, - "course": ["in", courses], + "member": frappe.session.user, "date": [">=", frappe.utils.nowdate()], "status": "Upcoming", } + if len(courses) > 0: + filters["course"] = ["in", courses] + if batch: filters["batch_name"] = batch @@ -1127,7 +1136,7 @@ def get_course_details(course): # course_details.is_instructor = is_instructor(course_details.name) if course_details.paid_course or course_details.paid_certificate: """course_details.course_price, course_details.currency = check_multicurrency( - course_details.course_price, course_details.currency, None, course_details.amount_usd + course_details.course_price, course_details.currency, None, course_details.amount_usd )""" course_details.price = fmt_money(course_details.course_price, 0, course_details.currency) @@ -2133,3 +2142,216 @@ def get_related_courses(course): def persona_captured(): frappe.db.set_single_value("LMS Settings", "persona_captured", 1) + + +@frappe.whitelist() +def get_my_courses(): + my_courses = [] + if frappe.session.user == "Guest": + return my_courses + + courses = frappe.get_all( + "LMS Enrollment", + { + "member": frappe.session.user, + }, + order_by="creation desc", + limit=3, + pluck="course", + ) + + for course in courses: + my_courses.append(get_course_details(course)) + + return my_courses + + +@frappe.whitelist() +def get_my_batches(): + my_batches = [] + if frappe.session.user == "Guest": + return my_batches + + batches = frappe.get_all( + "LMS Batch Enrollment", + { + "member": frappe.session.user, + }, + order_by="creation desc", + limit=4, + pluck="batch", + ) + + for batch in batches: + batch_details = get_batch_details(batch) + if batch_details: + my_batches.append(batch_details) + + return my_batches + + +@frappe.whitelist() +def get_my_live_classes(): + my_live_classes = [] + if frappe.session.user == "Guest": + return my_live_classes + + batches = frappe.get_all( + "LMS Batch Enrollment", + { + "member": frappe.session.user, + }, + order_by="creation desc", + pluck="batch", + ) + + live_class_details = frappe.get_all( + "LMS Live Class", + filters={ + "date": [">=", getdate()], + "batch_name": ["in", batches], + }, + fields=[ + "name", + "title", + "description", + "time", + "date", + "duration", + "attendees", + "start_url", + "join_url", + "owner", + ], + limit=2, + order_by="date", + ) + + if len(live_class_details): + for live_class in live_class_details: + live_class.course_title = frappe.db.get_value("LMS Course", live_class.course, "title") + + my_live_classes.append(live_class) + + return my_live_classes + + +@frappe.whitelist() +def get_created_courses(): + created_courses = [] + if frappe.session.user == "Guest": + return created_courses + + courses = frappe.get_all( + "Course Instructor", + { + "instructor": frappe.session.user, + "parenttype": "LMS Course", + }, + pluck="parent", + limit=3, + order_by="creation desc", + ) + + for course in courses: + course_details = get_course_details(course) + created_courses.append(course_details) + + return created_courses + + +@frappe.whitelist() +def get_created_batches(): + created_batches = [] + if frappe.session.user == "Guest": + return created_batches + + batches = frappe.get_all( + "Course Instructor", + {"instructor": frappe.session.user, "parenttype": "LMS Batch"}, + pluck="parent", + limit=4, + order_by="creation asc", + ) + + for batch in batches: + batch_details = get_batch_details(batch) + created_batches.append(batch_details) + + return created_batches + + +@frappe.whitelist() +def get_streak_info(): + if frappe.session.user == "Guest": + return {} + + course_dates = frappe.get_all( + "LMS Course Progress", + { + "member": frappe.session.user, + }, + pluck="creation", + ) + + quiz_dates = frappe.get_all( + "LMS Quiz Submission", + { + "member": frappe.session.user, + }, + pluck="creation", + ) + + assignment_dates = frappe.get_all( + "LMS Assignment Submission", + { + "member": frappe.session.user, + }, + pluck="creation", + ) + + programming_exercise_dates = frappe.get_all( + "LMS Programming Exercise Submission", + { + "member": frappe.session.user, + }, + pluck="creation", + ) + + all_dates = set(course_dates + quiz_dates + assignment_dates + programming_exercise_dates) + all_dates = sorted(all_dates) + + streak = 0 + max_streak = 0 + prev_day = None + + for d in all_dates: + # skip weekends entirely + print(d.weekday()) + if d.weekday() in (5, 6): # Saturday=5, Sunday=6 + continue + + if prev_day: + # if yesterday (excluding weekend gaps) + expected = prev_day + timedelta(days=1) + print(expected.weekday()) + while expected.weekday() in (5, 6): # skip Sat/Sun + expected += timedelta(days=1) + + if d == expected: + streak += 1 + else: + streak = 1 + else: + streak = 1 + + max_streak = max(max_streak, streak) + prev_day = d + return 1 + """ return { + "current_streak": streak, + "max_streak": max_streak, + "last_activity_date": prev_day.strftime("%Y-%m-%d") if prev_day else None, + "total_days_active": len(all_dates), + "total_days_in_month": (getdate() - getdate(getdate().year, getdate().month, 1)).days + 1, + } """ From b8708382b1dc9e64a237ce166caf29b41b8aa544 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Fri, 22 Aug 2025 16:47:25 +0530 Subject: [PATCH 2/7] feat: Admin Home --- frontend/src/components/AppSidebar.vue | 10 ++ frontend/src/pages/Home/AdminHome.vue | 167 +++++++++++++++++++++++- frontend/src/pages/Home/Home.vue | 111 ++++++++++------ frontend/src/pages/Home/StudentHome.vue | 5 - frontend/src/router.js | 2 + frontend/src/stores/session.js | 2 +- frontend/src/stores/settings.js | 1 - frontend/src/utils/index.js | 6 - lms/lms/utils.py | 117 ++++++++++++++--- 9 files changed, 341 insertions(+), 80 deletions(-) diff --git a/frontend/src/components/AppSidebar.vue b/frontend/src/components/AppSidebar.vue index 674268cc..b39a24ff 100644 --- a/frontend/src/components/AppSidebar.vue +++ b/frontend/src/components/AppSidebar.vue @@ -383,6 +383,15 @@ const checkIfCanAddProgram = async () => { return programs.enrolled.length > 0 || programs.published.length > 0 } +const addHome = () => { + sidebarLinks.value.unshift({ + label: 'Home', + icon: 'Home', + to: 'Home', + activeFor: ['Home'], + }) +} + const openPageModal = (link) => { showPageModal.value = true pageToEdit.value = link @@ -634,6 +643,7 @@ watch(userResource, () => { if (userResource.data) { isModerator.value = userResource.data.is_moderator isInstructor.value = userResource.data.is_instructor + addHome() addPrograms() addProgrammingExercises() addQuizzes() diff --git a/frontend/src/pages/Home/AdminHome.vue b/frontend/src/pages/Home/AdminHome.vue index 1c4c9f97..c027b996 100644 --- a/frontend/src/pages/Home/AdminHome.vue +++ b/frontend/src/pages/Home/AdminHome.vue @@ -3,7 +3,7 @@
- {{ __('Courses by me') }} + {{ __('Courses Created') }}
- {{ __('Batches by me') }} + {{ __('Upcoming Batches') }}
+ +
+
+
+ {{ __('Upcoming Evaluations') }} +
+
+
+
+ {{ evaluation.course_title }} +
+
+
+ + + {{ dayjs(evaluation.date).format('DD MMMM YYYY') }} + +
+
+ + + {{ formatTime(evaluation.start_time) }} + +
+
+ + + {{ evaluation.member_name }} + +
+
+
+
+
+
+
+ {{ __('Upcoming Live Classes') }} +
+
+
+
+ {{ cls.title }} +
+
+ {{ cls.description }} +
+
+
+ + + {{ dayjs(cls.date).format('DD MMMM YYYY') }} + +
+
+ + + {{ formatTime(cls.time) }} - + {{ dayjs(getClassEnd(cls)).format('HH:mm A') }} + +
+ + +
+ + + {{ __('Ended') }} + +
+
+
+
+
+
+
diff --git a/frontend/src/pages/Home/Home.vue b/frontend/src/pages/Home/Home.vue index 3817b9b8..1ca0fc64 100644 --- a/frontend/src/pages/Home/Home.vue +++ b/frontend/src/pages/Home/Home.vue @@ -11,45 +11,25 @@ {{ __('Hey') }}, {{ user.data?.full_name }} 👋
- - {{ __('Manage your courses and batches at a glance') }} - - - - {{ - __('You have {0} upcoming live classes').format( - myLiveClasses.data.length - ) - }} - - - {{ __(' and {0} evaluation').format(evalCount) }} - - - {{ __(' scheduled.') }} - - - - {{ - __('You have {0} upcoming live classes.').format( - myLiveClasses.data.length - ) - }} - - - {{ __('You have {0} evaluations scheduled.').format(evalCount) }} - - - {{ __('Resume where you left off') }} - + {{ subtitle }}
+
+ 🔥 + + {{ streakInfo.data?.current_streak }} + +
- +
@@ -77,16 +57,6 @@ onMounted(() => { }) }) -const myLiveClasses = createResource({ - url: 'lms.lms.utils.get_my_live_classes', - auto: true, -}) - -const tabs = [ - { label: __('Student'), value: 'student' }, - { label: __('Instructor'), value: 'instructor' }, -] - const isAdmin = computed(() => { return ( user.data?.is_moderator || @@ -95,6 +65,63 @@ const isAdmin = computed(() => { ) }) +const myLiveClasses = createResource({ + url: 'lms.lms.utils.get_my_live_classes', + auto: !isAdmin.value ? true : false, +}) + +const adminLiveClasses = createResource({ + url: 'lms.lms.utils.get_admin_live_classes', + auto: isAdmin.value ? true : false, +}) + +const adminEvals = createResource({ + url: 'lms.lms.utils.get_admin_evals', + auto: isAdmin.value ? true : false, +}) + +const streakInfo = createResource({ + url: 'lms.lms.utils.get_streak_info', + auto: true, +}) + +const subtitle = computed(() => { + if (isAdmin.value) { + if (adminLiveClasses.data?.length > 0 && adminEvals.data?.length > 0) { + return __( + 'You have {0} upcoming live classes and {1} evaluations scheduled.' + ).format(adminLiveClasses.data.length, adminEvals.data.length) + } else if (adminLiveClasses.data?.length > 0) { + return __('You have {0} upcoming live classes.').format( + adminLiveClasses.data.length + ) + } else if (adminEvals.data?.length > 0) { + return __('You have {0} evaluations scheduled.').format( + adminEvals.data.length + ) + } + return __('Manage your courses and batches at a glance') + } else { + if (myLiveClasses.data?.length > 0 && evalCount.value > 0) { + return __( + 'You have {0} upcoming live classes and {1} evaluations scheduled.' + ).format(myLiveClasses.data.length, evalCount.value) + } else if (myLiveClasses.data?.length > 0) { + return __('You have {0} upcoming live classes.').format( + myLiveClasses.data.length + ) + } else if (evalCount.value > 0) { + return __('You have {0} evaluations scheduled.').format(evalCount.value) + } + return __('Resume where you left off') + } +}) + +const tabs = [ + { label: __('Student'), value: 'student' }, + { label: __('Instructor'), value: 'instructor' }, +] + usePageMeta(() => { return { title: __('Home'), diff --git a/frontend/src/pages/Home/StudentHome.vue b/frontend/src/pages/Home/StudentHome.vue index 03c695bd..744af3c1 100644 --- a/frontend/src/pages/Home/StudentHome.vue +++ b/frontend/src/pages/Home/StudentHome.vue @@ -159,11 +159,6 @@ const myBatches = createResource({ auto: true, }) -const streakInfo = createResource({ - url: 'lms.lms.utils.get_streak_info', - auto: true, -}) - const getClassEnd = (cls: { date: string; time: string; duration: number }) => { const classStart = new Date(`${cls.date}T${cls.time}`) return new Date(classStart.getTime() + cls.duration * 60000) diff --git a/frontend/src/router.js b/frontend/src/router.js index eb187b41..473f1776 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -258,6 +258,8 @@ router.beforeEach(async (to, from, next) => { } if (!isLoggedIn) { + if (to.name == 'Home') router.push({ name: 'Courses' }) + await allowGuestAccess.promise if (!allowGuestAccess.data) { window.location.href = '/login' diff --git a/frontend/src/stores/session.js b/frontend/src/stores/session.js index 17697079..04faec5d 100644 --- a/frontend/src/stores/session.js +++ b/frontend/src/stores/session.js @@ -61,7 +61,7 @@ export const sessionStore = defineStore('lms-session', () => { field: 'livecode_url', }, cache: 'livecodeURL', - auto: true, + auto: user.value ? true : false, }) return { diff --git a/frontend/src/stores/settings.js b/frontend/src/stores/settings.js index 68c3cd06..a29d9d52 100644 --- a/frontend/src/stores/settings.js +++ b/frontend/src/stores/settings.js @@ -4,7 +4,6 @@ import { createResource } from 'frappe-ui' import { sessionStore } from './session' export const useSettings = defineStore('settings', () => { - const { isLoggedIn } = sessionStore() const isSettingsOpen = ref(false) const activeTab = ref(null) diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js index b4b0fd0e..eba86850 100644 --- a/frontend/src/utils/index.js +++ b/frontend/src/utils/index.js @@ -403,12 +403,6 @@ export function getUserTimezone() { export function getSidebarLinks() { return [ - { - label: 'Home', - icon: 'Home', - to: 'Home', - activeFor: ['Home'], - }, { label: 'Courses', icon: 'BookOpen', diff --git a/lms/lms/utils.py b/lms/lms/utils.py index f9fedcc2..a14d0326 100644 --- a/lms/lms/utils.py +++ b/lms/lms/utils.py @@ -2242,17 +2242,22 @@ def get_created_courses(): if frappe.session.user == "Guest": return created_courses - courses = frappe.get_all( - "Course Instructor", - { - "instructor": frappe.session.user, - "parenttype": "LMS Course", - }, - pluck="parent", - limit=3, - order_by="creation desc", + CourseInstructor = frappe.qb.DocType("Course Instructor") + Course = frappe.qb.DocType("LMS Course") + + query = ( + frappe.qb.from_(CourseInstructor) + .join(Course) + .on(CourseInstructor.parent == Course.name) + .select(Course.name) + .where(CourseInstructor.instructor == frappe.session.user) + .orderby(Course.published_on, order=frappe.qb.desc) + .limit(3) ) + results = query.run(as_dict=True) + courses = [row["name"] for row in results] + for course in courses: course_details = get_course_details(course) created_courses.append(course_details) @@ -2266,14 +2271,23 @@ def get_created_batches(): if frappe.session.user == "Guest": return created_batches - batches = frappe.get_all( - "Course Instructor", - {"instructor": frappe.session.user, "parenttype": "LMS Batch"}, - pluck="parent", - limit=4, - order_by="creation asc", + CourseInstructor = frappe.qb.DocType("Course Instructor") + Batch = frappe.qb.DocType("LMS Batch") + + query = ( + frappe.qb.from_(CourseInstructor) + .join(Batch) + .on(CourseInstructor.parent == Batch.name) + .select(Batch.name) + .where(CourseInstructor.instructor == frappe.session.user) + .where(Batch.start_date >= getdate()) + .orderby(Batch.start_date, order=frappe.qb.asc) + .limit(4) ) + results = query.run(as_dict=True) + batches = [row["name"] for row in results] + for batch in batches: batch_details = get_batch_details(batch) created_batches.append(batch_details) @@ -2281,6 +2295,70 @@ def get_created_batches(): return created_batches +@frappe.whitelist() +def get_admin_live_classes(): + if frappe.session.user == "Guest": + return [] + + CourseInstructor = frappe.qb.DocType("Course Instructor") + LMSLiveClass = frappe.qb.DocType("LMS Live Class") + + query = ( + frappe.qb.from_(CourseInstructor) + .join(LMSLiveClass) + .on(CourseInstructor.parent == LMSLiveClass.batch_name) + .select( + LMSLiveClass.name, + LMSLiveClass.title, + LMSLiveClass.description, + LMSLiveClass.time, + LMSLiveClass.date, + LMSLiveClass.duration, + LMSLiveClass.attendees, + LMSLiveClass.start_url, + LMSLiveClass.join_url, + LMSLiveClass.owner, + ) + .where(CourseInstructor.instructor == frappe.session.user) + .where(LMSLiveClass.date >= getdate()) + .orderby(LMSLiveClass.date, order=frappe.qb.asc) + .limit(4) + ) + results = query.run(as_dict=True) + return results + + +@frappe.whitelist() +def get_admin_evals(): + if frappe.session.user == "Guest": + return [] + + evals = frappe.get_all( + "LMS Certificate Request", + { + "evaluator": frappe.session.user, + "date": [">=", getdate()], + }, + [ + "name", + "date", + "start_time", + "course", + "evaluator", + "google_meet_link", + "member", + "member_name", + ], + limit=4, + order_by="date asc", + ) + + for evaluation in evals: + evaluation.course_title = frappe.db.get_value("LMS Course", evaluation.course, "title") + + return evals + + @frappe.whitelist() def get_streak_info(): if frappe.session.user == "Guest": @@ -2347,11 +2425,8 @@ def get_streak_info(): max_streak = max(max_streak, streak) prev_day = d - return 1 - """ return { + + return { "current_streak": streak, "max_streak": max_streak, - "last_activity_date": prev_day.strftime("%Y-%m-%d") if prev_day else None, - "total_days_active": len(all_dates), - "total_days_in_month": (getdate() - getdate(getdate().year, getdate().month, 1)).days + 1, - } """ + } From d575bfa0fb1334cf1f6ab2343aeccad072337c5e Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Mon, 25 Aug 2025 09:19:03 +0530 Subject: [PATCH 3/7] feat: streak details --- cypress/e2e/course_creation.cy.js | 2 +- frontend/src/pages/Home/Home.vue | 9 +++- frontend/src/pages/Home/Streak.vue | 79 ++++++++++++++++++++++++++++++ lms/lms/utils.py | 6 +-- 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 frontend/src/pages/Home/Streak.vue diff --git a/cypress/e2e/course_creation.cy.js b/cypress/e2e/course_creation.cy.js index a48a868b..f50acd03 100644 --- a/cypress/e2e/course_creation.cy.js +++ b/cypress/e2e/course_creation.cy.js @@ -98,7 +98,7 @@ describe("Course Creation", () => { // View Course cy.wait(1000); - cy.visit("/lms"); + cy.visit("/lms/courses"); cy.closeOnboardingModal(); cy.url().should("include", "/lms/courses"); diff --git a/frontend/src/pages/Home/Home.vue b/frontend/src/pages/Home/Home.vue index 1ca0fc64..c5a8216a 100644 --- a/frontend/src/pages/Home/Home.vue +++ b/frontend/src/pages/Home/Home.vue @@ -16,7 +16,11 @@
-
+
🔥 {{ streakInfo.data?.current_streak }} @@ -32,6 +36,7 @@ />
+ diff --git a/lms/lms/utils.py b/lms/lms/utils.py index a14d0326..0ffb24f3 100644 --- a/lms/lms/utils.py +++ b/lms/lms/utils.py @@ -2400,7 +2400,7 @@ def get_streak_info(): all_dates = sorted(all_dates) streak = 0 - max_streak = 0 + longest_streak = 0 prev_day = None for d in all_dates: @@ -2423,10 +2423,10 @@ def get_streak_info(): else: streak = 1 - max_streak = max(max_streak, streak) + longest_streak = max(longest_streak, streak) prev_day = d return { "current_streak": streak, - "max_streak": max_streak, + "longest_streak": longest_streak, } From 0713b6b419eccadb52b47bc6486653c75d8b307e Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Mon, 25 Aug 2025 15:33:05 +0530 Subject: [PATCH 4/7] fix: streak logic --- frontend/src/pages/Home/AdminHome.vue | 213 ++++++++++++++---------- frontend/src/pages/Home/Streak.vue | 3 +- frontend/src/pages/Home/StudentHome.vue | 12 +- frontend/vite.config.js | 2 +- lms/lms/utils.py | 159 ++++++++++++------ 5 files changed, 237 insertions(+), 152 deletions(-) diff --git a/frontend/src/pages/Home/AdminHome.vue b/frontend/src/pages/Home/AdminHome.vue index c027b996..22be0957 100644 --- a/frontend/src/pages/Home/AdminHome.vue +++ b/frontend/src/pages/Home/AdminHome.vue @@ -54,108 +54,136 @@
+
-
-
-
- {{ __('Upcoming Evaluations') }} -
-
-
-
- {{ evaluation.course_title }} +
+ +
+ {{ __('No courses created') }} +
+
+ {{ + __( + 'There are no courses currently. Create your first course to get started!' + ) + }} +
+ + + +
+ +
+
+
+ {{ __('Upcoming Evaluations') }} +
+
+
+
+ {{ evaluation.course_title }} +
+
+
+ + + {{ dayjs(evaluation.date).format('DD MMMM YYYY') }} +
-
-
- - - {{ dayjs(evaluation.date).format('DD MMMM YYYY') }} - -
-
- - - {{ formatTime(evaluation.start_time) }} - -
-
- - - {{ evaluation.member_name }} - -
+
+ + + {{ formatTime(evaluation.start_time) }} + +
+
+ + + {{ evaluation.member_name }} +
-
-
- {{ __('Upcoming Live Classes') }} -
-
-
-
- {{ cls.title }} +
+
+
+ {{ __('Upcoming Live Classes') }} +
+
+
+
+ {{ cls.title }} +
+
+ {{ cls.description }} +
+
+
+ + + {{ dayjs(cls.date).format('DD MMMM YYYY') }} +
-
- {{ cls.description }} +
+ + + {{ formatTime(cls.time) }} - + {{ dayjs(getClassEnd(cls)).format('HH:mm A') }} +
-
-
- + + +
+ - {{ dayjs(cls.date).format('DD MMMM YYYY') }} + {{ __('Ended') }}
-
- - - {{ formatTime(cls.time) }} - - {{ dayjs(getClassEnd(cls)).format('HH:mm A') }} - -
- - -
- - - {{ __('Ended') }} - -
-
-
+
@@ -164,7 +192,7 @@