From b1789cdcba3a8af8e9c51d1be331b48d27ecfec6 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Fri, 2 Jan 2026 14:42:42 +0530 Subject: [PATCH 01/36] fix: payment validation during course enrollment --- lms/lms/doctype/lms_enrollment/lms_enrollment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lms/lms/doctype/lms_enrollment/lms_enrollment.py b/lms/lms/doctype/lms_enrollment/lms_enrollment.py index 8f139083..77aa7eef 100644 --- a/lms/lms/doctype/lms_enrollment/lms_enrollment.py +++ b/lms/lms/doctype/lms_enrollment/lms_enrollment.py @@ -39,8 +39,8 @@ class LMSEnrollment(Document): payment = frappe.db.exists( "LMS Payment", { - "reference_doctype": "LMS Course", - "reference_docname": self.course, + "payment_for_document_type": "LMS Course", + "payment_for_document": self.course, "member": self.member, "payment_received": True, }, From 0f927071c095f3bc55feeec624e483682faf5912 Mon Sep 17 00:00:00 2001 From: raizasafeel <89463672+raizasafeel@users.noreply.github.com> Date: Fri, 16 Jan 2026 11:35:49 +0530 Subject: [PATCH 02/36] fix(course): resolve deletion failure for enrolled courses --- lms/lms/api.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/lms/lms/api.py b/lms/lms/api.py index b6b91851..87127272 100644 --- a/lms/lms/api.py +++ b/lms/lms/api.py @@ -850,17 +850,18 @@ def get_announcements(batch): @frappe.whitelist() def delete_course(course): - chapters = frappe.get_all("Course Chapter", {"course": course}, pluck="name") + frappe.db.delete("LMS Enrollment", {"course": course}) + frappe.db.delete("LMS Course Progress", {"course": course}) + frappe.db.set_value("LMS Quiz", {"course": course}, "course", None) + frappe.db.set_value("LMS Quiz Submission", {"course": course}, "course", None) - chapter_references = frappe.get_all("Chapter Reference", {"parent": course}, pluck="name") + chapters = frappe.get_all("Course Chapter", {"course": course}, pluck="name") + frappe.db.delete("Chapter Reference", {"parent": course}) for chapter in chapters: lessons = frappe.get_all("Course Lesson", {"chapter": chapter}, pluck="name") - lesson_references = frappe.get_all("Lesson Reference", {"parent": chapter}, pluck="name") - - for lesson in lesson_references: - frappe.delete_doc("Lesson Reference", lesson) + frappe.db.delete("Lesson Reference", {"parent": chapter}) for lesson in lessons: topics = frappe.get_all( @@ -871,21 +872,13 @@ def delete_course(course): for topic in topics: frappe.db.delete("Discussion Reply", {"topic": topic}) - frappe.db.delete("Discussion Topic", topic) frappe.delete_doc("Course Lesson", lesson) - for chapter in chapter_references: - frappe.delete_doc("Chapter Reference", chapter) - for chapter in chapters: frappe.delete_doc("Course Chapter", chapter) - frappe.db.delete("LMS Course Progress", {"course": course}) - frappe.db.delete("LMS Quiz", {"course": course}) - frappe.db.delete("LMS Quiz Submission", {"course": course}) - frappe.db.delete("LMS Enrollment", {"course": course}) frappe.delete_doc("LMS Course", course) From fe1aa3dd4019c654bfe8a35a8681d0b9070d2468 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Sat, 17 Jan 2026 23:04:31 +0530 Subject: [PATCH 03/36] feat: configurable frontend base path Co-authored-by: Suraj Shetty --- .gitignore | 1 + frontend/package.json | 2 +- frontend/src/components/AssessmentPlugin.vue | 8 +++- frontend/src/pages/Batch.vue | 5 ++- frontend/src/pages/Billing.vue | 15 ++++--- frontend/src/pages/Lesson.vue | 5 ++- frontend/src/pages/ProfileAbout.vue | 5 ++- .../ProgrammingExerciseSubmission.vue | 6 ++- frontend/src/router.js | 3 +- frontend/src/utils/assignment.js | 6 ++- frontend/src/utils/basePath.js | 12 ++++++ frontend/src/utils/program.ts | 8 +++- frontend/src/utils/quiz.js | 4 +- frontend/vite.config.js | 2 +- lms/hooks.py | 31 +++++++++----- lms/lms/api.py | 3 +- .../lms_assignment_submission.py | 4 +- lms/lms/doctype/lms_batch/lms_batch.js | 3 +- lms/lms/doctype/lms_batch/lms_batch.py | 5 ++- lms/lms/doctype/lms_course/lms_course.js | 6 ++- lms/lms/doctype/lms_course/lms_course.py | 14 +++++-- .../lms_mentor_request/lms_mentor_request.py | 4 +- lms/lms/doctype/lms_payment/lms_payment.py | 6 ++- lms/lms/page/lms_home/lms_home.js | 3 +- lms/lms/test_utils.py | 3 +- lms/lms/user.py | 3 +- lms/lms/utils.py | 24 +++++++++-- .../course_cards/course_cards.html | 2 +- .../recently_published_courses.html | 4 +- lms/lms/widgets/Avatar.html | 2 +- lms/lms/widgets/CourseCard.html | 4 +- .../emails/assignment_submission.html | 3 +- lms/templates/emails/batch_confirmation.html | 3 +- .../emails/batch_start_reminder.html | 2 +- lms/templates/emails/live_class_reminder.html | 4 +- lms/www/{lms.py => _lms.py} | 40 ++++++++++--------- 36 files changed, 177 insertions(+), 78 deletions(-) create mode 100644 frontend/src/utils/basePath.js rename lms/www/{lms.py => _lms.py} (88%) diff --git a/.gitignore b/.gitignore index 95242ce7..e6c74964 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ node_modules package-lock.json lms/public/frontend lms/www/lms.html +lms/www/_lms.html frappe-ui \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index bead43a6..9bd26413 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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": { diff --git a/frontend/src/components/AssessmentPlugin.vue b/frontend/src/components/AssessmentPlugin.vue index 75e747c5..53b51392 100644 --- a/frontend/src/components/AssessmentPlugin.vue +++ b/frontend/src/components/AssessmentPlugin.vue @@ -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) @@ -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') + } } diff --git a/frontend/src/pages/Batch.vue b/frontend/src/pages/Batch.vue index ad5c47bb..3bd57614 100644 --- a/frontend/src/pages/Batch.vue +++ b/frontend/src/pages/Batch.vue @@ -248,6 +248,7 @@ import DateRange from '@/components/Common/DateRange.vue' import BulkCertificates from '@/components/Modals/BulkCertificates.vue' import BatchFeedback from '@/components/BatchFeedback.vue' import dayjs from 'dayjs/esm' +import { getLmsRoute } from '@/utils/basePath' const user = inject('$user') const showAnnouncementModal = ref(false) @@ -357,7 +358,9 @@ const isStudent = computed(() => { }) const redirectToLogin = () => { - window.location.href = `/login?redirect-to=/lms/batches/${props.batchName}` + window.location.href = `/login?redirect-to=${getLmsRoute( + `batches/${props.batchName}` + )}` } const openAnnouncementModal = () => { diff --git a/frontend/src/pages/Billing.vue b/frontend/src/pages/Billing.vue index 00a98a94..02f9ce11 100644 --- a/frontend/src/pages/Billing.vue +++ b/frontend/src/pages/Billing.vue @@ -207,14 +207,18 @@ :text="access.data.message" :buttonLabel="type == 'course' ? 'Checkout Course' : 'Checkout Batch'" :buttonLink=" - type == 'course' ? `/lms/courses/${name}` : `/lms/batches/${name}` + type == 'course' + ? getLmsRoute(`courses/${name}`) + : getLmsRoute(`batches/${name}`) " />
@@ -235,6 +239,7 @@ import Link from '@/components/Controls/Link.vue' import NotPermitted from '@/components/NotPermitted.vue' import { X } from 'lucide-vue-next' import { useTelemetry } from 'frappe-ui/frappe' +import { getLmsRoute } from '@/utils/basePath' const user = inject('$user') const { brand } = sessionStore() @@ -441,11 +446,11 @@ const changeCurrency = (country) => { const redirectTo = computed(() => { if (props.type == 'course') { - return `/lms/courses/${props.name}` + return getLmsRoute(`courses/${props.name}`) } else if (props.type == 'batch') { - return `/lms/batches/${props.name}` + return getLmsRoute(`batches/${props.name}`) } else if (props.type == 'certificate') { - return `/lms/courses/${props.name}/certification` + return getLmsRoute(`courses/${props.name}/certification`) } }) diff --git a/frontend/src/pages/Lesson.vue b/frontend/src/pages/Lesson.vue index d503b35c..1c658774 100644 --- a/frontend/src/pages/Lesson.vue +++ b/frontend/src/pages/Lesson.vue @@ -378,6 +378,7 @@ import CourseOutline from '@/components/CourseOutline.vue' import UserAvatar from '@/components/UserAvatar.vue' import Notes from '@/components/Notes/Notes.vue' import InlineLessonMenu from '@/components/Notes/InlineLessonMenu.vue' +import { getLmsRoute } from '@/utils/basePath' const user = inject('$user') const socket = inject('$socket') @@ -902,7 +903,9 @@ watch(allowDiscussions, () => { }) const redirectToLogin = () => { - window.location.href = `/login?redirect-to=/lms/courses/${props.courseName}` + window.location.href = `/login?redirect-to=${getLmsRoute( + `courses/${props.courseName}` + )}` } usePageMeta(() => { diff --git a/frontend/src/pages/ProfileAbout.vue b/frontend/src/pages/ProfileAbout.vue index f6497645..d2fb068c 100644 --- a/frontend/src/pages/ProfileAbout.vue +++ b/frontend/src/pages/ProfileAbout.vue @@ -122,6 +122,7 @@ import { X, LinkedinIcon, Twitter } from 'lucide-vue-next' import { sessionStore } from '@/stores/session' import { decodeEntities } from '@/utils' import DOMPurify from 'dompurify' +import { getLmsRoute } from '@/utils/basePath' const dayjs = inject('$dayjs') const { branding } = sessionStore() @@ -158,7 +159,9 @@ const badges = createResource({ const shareOnSocial = (badge, medium) => { let shareUrl const url = encodeURIComponent( - `${window.location.origin}/lms/badges/${badge.badge}/${props.profile.data?.email}` + `${window.location.origin}${getLmsRoute( + `badges/${badge.badge}/${props.profile.data?.email}` + )}` ) const summary = `I am happy to announce that I earned the ${ badge.badge diff --git a/frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmission.vue b/frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmission.vue index d11adcea..71f9563c 100644 --- a/frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmission.vue +++ b/frontend/src/pages/ProgrammingExercises/ProgrammingExerciseSubmission.vue @@ -158,6 +158,7 @@ import { sessionStore } from '@/stores/session' import { useRouter } from 'vue-router' import { openSettings } from '@/utils' import { useSettings } from '@/stores/settings' +import { getLmsRoute } from '@/utils/basePath' const user = inject('$user') const code = ref('') @@ -255,7 +256,10 @@ const updateBoilerPlate = () => { const checkIfUserIsPermitted = (doc: any = null) => { if (!user.data) { - window.location.href = `/login?redirect-to=/lms/programming-exercises/${props.exerciseID}/submission/${props.submissionID}` + const redirectPath = getLmsRoute( + `programming-exercises/${props.exerciseID}/submission/${props.submissionID}` + ) + window.location.href = `/login?redirect-to=${redirectPath}` } if (!doc) return diff --git a/frontend/src/router.js b/frontend/src/router.js index fe6b311d..8894f3b9 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -2,6 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router' import { usersStore } from './stores/user' import { sessionStore } from './stores/session' import { useSettings } from './stores/settings' +import { getLmsBasePath } from './utils/basePath' const routes = [ { @@ -268,7 +269,7 @@ const routes = [ ] let router = createRouter({ - history: createWebHistory('/lms'), + history: createWebHistory(`/${getLmsBasePath()}`), routes, }) diff --git a/frontend/src/utils/assignment.js b/frontend/src/utils/assignment.js index 99f9b101..1f1e894d 100644 --- a/frontend/src/utils/assignment.js +++ b/frontend/src/utils/assignment.js @@ -5,6 +5,7 @@ import translationPlugin from '../translation' import { usersStore } from '@/stores/user' import { call } from 'frappe-ui' import router from '@/router' +import { getLmsRoute } from '@/utils/basePath' export class Assignment { constructor({ data, api, readOnly }) { @@ -53,7 +54,10 @@ export class Assignment { fieldname: ['name'], }).then((data) => { let submission = data.name || 'new' - this.wrapper.innerHTML = `` + const submissionPath = getLmsRoute( + `assignment-submission/${assignment}/${submission}?fromLesson=1` + ) + this.wrapper.innerHTML = `` }) return } diff --git a/frontend/src/utils/basePath.js b/frontend/src/utils/basePath.js new file mode 100644 index 00000000..78782a9b --- /dev/null +++ b/frontend/src/utils/basePath.js @@ -0,0 +1,12 @@ +export function getLmsBasePath() { + return window.lms_path || 'lms' +} + +export function getLmsRoute(path = '') { + const base = getLmsBasePath() + if (!path) { + return base + } + const normalized = path.startsWith('/') ? path.slice(1) : path + return `/${base}/${normalized}` +} diff --git a/frontend/src/utils/program.ts b/frontend/src/utils/program.ts index a2d9b548..22cc48eb 100644 --- a/frontend/src/utils/program.ts +++ b/frontend/src/utils/program.ts @@ -4,6 +4,7 @@ import translationPlugin from '@/translation' import ProgrammingExerciseModal from '@/pages/ProgrammingExercises/ProgrammingExerciseModal.vue'; import { call } from 'frappe-ui'; import { usersStore } from '@/stores/user' +import { getLmsRoute } from '@/utils/basePath' export class Program { @@ -73,7 +74,10 @@ export class Program { fieldname: ['name'], }).then((data: { name: string }) => { let submission = data.name || 'new' - this.wrapper.innerHTML = `` + const submissionPath = getLmsRoute( + `programming-exercises/${exercise}/submission/${submission}?fromLesson=1` + ) + this.wrapper.innerHTML = `` }) return } @@ -100,4 +104,4 @@ export class Program { exercise: this.data.exercise, } } -} \ No newline at end of file +} diff --git a/frontend/src/utils/quiz.js b/frontend/src/utils/quiz.js index add3d680..b84f5286 100644 --- a/frontend/src/utils/quiz.js +++ b/frontend/src/utils/quiz.js @@ -5,6 +5,7 @@ import { usersStore } from '../stores/user' import translationPlugin from '../translation' import { CircleHelp } from 'lucide-vue-next' import router from '@/router' +import { getLmsRoute } from '@/utils/basePath' export class Quiz { constructor({ data, api, readOnly }) { @@ -42,7 +43,8 @@ export class Quiz { renderQuiz(quiz) { if (this.readOnly) { - this.wrapper.innerHTML = `` + const quizPath = getLmsRoute(`quiz/${quiz}?fromLesson=1`) + this.wrapper.innerHTML = `` return } this.wrapper.innerHTML = `
diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 3d08802b..704e5ea1 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -17,7 +17,7 @@ export default defineConfig(async ({ mode }) => { lucideIcons: true, jinjaBootData: true, buildConfig: { - indexHtmlPath: '../lms/www/lms.html', + indexHtmlPath: '../lms/www/_lms.html', }, }), vue(), diff --git a/lms/hooks.py b/lms/hooks.py index aeb2e4b2..8a0fd55e 100644 --- a/lms/hooks.py +++ b/lms/hooks.py @@ -1,3 +1,5 @@ +import frappe + from . import __version__ as app_version app_name = "frappe_lms" @@ -6,11 +8,13 @@ app_publisher = "Frappe" app_description = "Frappe LMS App" app_icon_url = "/assets/lms/images/lms-logo.png" app_icon_title = "Learning" -app_icon_route = "/lms" app_color = "grey" app_email = "jannat@frappe.io" app_license = "AGPL" +lms_path = frappe.conf.get("lms_path") or "lms" +app_icon_route = f"/{lms_path}" + # Includes in # ------------------ @@ -163,7 +167,8 @@ override_whitelisted_methods = { # Add all simple route rules here website_route_rules = [ - {"from_route": "/lms/", "to_route": "lms"}, + {"from_route": f"/{lms_path}/", "to_route": "_lms"}, + {"from_route": f"/{lms_path}", "to_route": "_lms"}, { "from_route": "/courses//", "to_route": "certificate", @@ -172,24 +177,25 @@ website_route_rules = [ website_redirects = [ {"source": "/update-profile", "target": "/edit-profile"}, - {"source": "/courses", "target": "/lms/courses"}, + {"source": "/courses", "target": f"/{lms_path}/courses"}, { "source": r"^/courses/.*$", - "target": "/lms/courses", + "target": f"/{lms_path}/courses", }, - {"source": "/batches", "target": "/lms/batches"}, + {"source": "/batches", "target": f"/{lms_path}/batches"}, { "source": r"/batches/(.*)", - "target": "/lms/batches", + "target": f"/{lms_path}/batches", "match_with_query_string": True, }, - {"source": "/job-openings", "target": "/lms/job-openings"}, + {"source": "/job-openings", "target": f"/{lms_path}/job-openings"}, { "source": r"/job-openings/(.*)", - "target": "/lms/job-openings", + "target": f"/{lms_path}/job-openings", "match_with_query_string": True, }, - {"source": "/statistics", "target": "/lms/statistics"}, + {"source": "/statistics", "target": f"/{lms_path}/statistics"}, + {"source": "_lms", "target": f"/{lms_path}"}, ] update_website_context = [ @@ -203,11 +209,16 @@ jinja = { "lms.lms.utils.get_instructors", "lms.lms.utils.get_lesson_index", "lms.lms.utils.get_lesson_url", + "lms.lms.utils.get_lms_route", "lms.lms.utils.is_instructor", "lms.lms.utils.get_palette", ], "filters": [], } + +extend_bootinfo = [ + "lms.lms.utils.extend_bootinfo", +] ## Specify the additional tabs to be included in the user profile page. ## Each entry must be a subclass of lms.lms.plugins.ProfileTab # profile_tabs = [] @@ -256,7 +267,7 @@ add_to_apps_screen = [ "name": "lms", "logo": "/assets/lms/frontend/learning.svg", "title": "Learning", - "route": "/lms", + "route": f"/{lms_path}", "has_permission": "lms.lms.api.check_app_permission", } ] diff --git a/lms/lms/api.py b/lms/lms/api.py index 984ee519..731c0dde 100644 --- a/lms/lms/api.py +++ b/lms/lms/api.py @@ -28,6 +28,7 @@ from frappe.utils import ( ) from frappe.utils.response import Response +from lms.hooks import lms_path from lms.lms.doctype.course_lesson.course_lesson import save_progress from lms.lms.utils import ( get_average_rating, @@ -1673,7 +1674,7 @@ def get_pwa_manifest(): "name": title, "short_name": title, "description": "Easy to use, 100% open source Learning Management System", - "start_url": "/lms", + "start_url": f"/{lms_path}", "icons": [ { "src": banner_image or "/assets/lms/frontend/manifest/manifest-icon-192.maskable.png", diff --git a/lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.py b/lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.py index 9392b2cf..79a6cd0f 100644 --- a/lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.py +++ b/lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.py @@ -7,6 +7,8 @@ from frappe.desk.doctype.notification_log.notification_log import make_notificat from frappe.model.document import Document from frappe.utils import validate_url +from lms.lms.utils import get_lms_route + class LMSAssignmentSubmission(Document): def validate(self): @@ -72,7 +74,7 @@ class LMSAssignmentSubmission(Document): "document_name": self.name, "from_user": self.evaluator, "type": "Alert", - "link": f"/lms/assignment-submission/{self.assignment}/{self.name}", + "link": get_lms_route(f"assignment-submission/{self.assignment}/{self.name}"), } ) make_notification_logs(notification, [self.member]) diff --git a/lms/lms/doctype/lms_batch/lms_batch.js b/lms/lms/doctype/lms_batch/lms_batch.js index f196c867..7aa70bbc 100644 --- a/lms/lms/doctype/lms_batch/lms_batch.js +++ b/lms/lms/doctype/lms_batch/lms_batch.js @@ -48,8 +48,9 @@ frappe.ui.form.on("LMS Batch", { }, refresh: (frm) => { + const lmsPath = frappe.boot.lms_path || "lms"; frm.add_web_link( - `/lms/batches/details/${frm.doc.name}`, + `/${lmsPath}/batches/details/${frm.doc.name}`, "See on website" ); }, diff --git a/lms/lms/doctype/lms_batch/lms_batch.py b/lms/lms/doctype/lms_batch/lms_batch.py index 19208402..23431626 100644 --- a/lms/lms/doctype/lms_batch/lms_batch.py +++ b/lms/lms/doctype/lms_batch/lms_batch.py @@ -18,6 +18,7 @@ from lms.lms.utils import ( get_instructors, get_lesson_index, get_lesson_url, + get_lms_route, get_quiz_details, update_payment_record, ) @@ -164,7 +165,7 @@ def send_email_notification_for_published_batch(batch): "medium": batch.medium, "timezone": batch.timezone, "instructors": instructors, - "batch_url": f"{frappe.utils.get_url()}/lms/batches/details/{batch.name}", + "batch_url": frappe.utils.get_url(get_lms_route(f"batches/details/{batch.name}")), } frappe.sendmail( @@ -193,7 +194,7 @@ def send_system_notification_for_published_batch(batch): "document_name": batch.name, "from_user": instructors[0] if instructors else None, "type": "Alert", - "link": f"/lms/batches/details/{batch.name}", + "link": get_lms_route(f"batches/details/{batch.name}"), } ) make_notification_logs(notification, students) diff --git a/lms/lms/doctype/lms_course/lms_course.js b/lms/lms/doctype/lms_course/lms_course.js index ab913d32..c4429a2d 100644 --- a/lms/lms/doctype/lms_course/lms_course.js +++ b/lms/lms/doctype/lms_course/lms_course.js @@ -20,7 +20,11 @@ frappe.ui.form.on("LMS Course", { }); }, refresh: (frm) => { - frm.add_web_link(`/lms/courses/${frm.doc.name}`, "See on Website"); + const lmsPath = frappe.boot.lms_path || "lms"; + frm.add_web_link( + `/${lmsPath}/courses/${frm.doc.name}`, + "See on Website" + ); if (!frm.doc.currency) frappe.db diff --git a/lms/lms/doctype/lms_course/lms_course.py b/lms/lms/doctype/lms_course/lms_course.py index 431035a9..15597a80 100644 --- a/lms/lms/doctype/lms_course/lms_course.py +++ b/lms/lms/doctype/lms_course/lms_course.py @@ -9,7 +9,13 @@ from frappe.desk.doctype.notification_log.notification_log import make_notificat from frappe.model.document import Document from frappe.utils import cint, today -from ...utils import generate_slug, get_instructors, update_payment_record, validate_image +from ...utils import ( + generate_slug, + get_instructors, + get_lms_route, + update_payment_record, + validate_image, +) class LMSCourse(Document): @@ -107,7 +113,7 @@ class LMSCourse(Document): subject = self.title + " is available!" args = { "title": self.title, - "course_link": f"/lms/courses/{self.name}", + "course_link": get_lms_route(f"courses/{self.name}"), "app_name": frappe.db.get_single_value("System Settings", "app_name"), "site_url": frappe.utils.get_url(), } @@ -172,7 +178,7 @@ def send_email_notification_for_published_courses(courses): "title": course.title, "short_introduction": course.short_introduction, "instructors": instructors, - "course_url": f"{frappe.utils.get_url()}/lms/courses/{course.name}", + "course_url": frappe.utils.get_url(get_lms_route(f"courses/{course.name}")), } frappe.sendmail( @@ -202,7 +208,7 @@ def send_system_notification_for_published_courses(courses): "document_name": course.name, "from_user": instructors[0] if instructors else None, "type": "Alert", - "link": f"/lms/courses/{course.name}", + "link": get_lms_route(f"courses/{course.name}"), } ) make_notification_logs(notification, students) diff --git a/lms/lms/doctype/lms_mentor_request/lms_mentor_request.py b/lms/lms/doctype/lms_mentor_request/lms_mentor_request.py index 40b5ea44..1fa05439 100644 --- a/lms/lms/doctype/lms_mentor_request/lms_mentor_request.py +++ b/lms/lms/doctype/lms_mentor_request/lms_mentor_request.py @@ -5,6 +5,8 @@ import frappe from frappe import _ from frappe.model.document import Document +from lms.lms.utils import get_lms_route + class LMSMentorRequest(Document): def on_update(self): @@ -37,7 +39,7 @@ class LMSMentorRequest(Document): email_template.response, { "member_name": frappe.db.get_value("User", frappe.session.user, "full_name"), - "course_url": "/lms/courses/" + course_details.slug, + "course_url": get_lms_route(f"courses/{course_details.slug}"), "course": course_details.title, }, ) diff --git a/lms/lms/doctype/lms_payment/lms_payment.py b/lms/lms/doctype/lms_payment/lms_payment.py index f55982e6..e38cccf4 100644 --- a/lms/lms/doctype/lms_payment/lms_payment.py +++ b/lms/lms/doctype/lms_payment/lms_payment.py @@ -7,6 +7,8 @@ from frappe.email.doctype.email_template.email_template import get_email_templat from frappe.model.document import Document from frappe.utils import add_days, flt, nowdate +from lms.lms.utils import get_lms_route + class LMSPayment(Document): pass @@ -76,7 +78,9 @@ def send_mail(payment): "title": frappe.db.get_value( payment.payment_for_document_type, payment.payment_for_document, "title" ), - "link": f"/lms/billing/{ payment.payment_for_document_type.split(' ')[-1].lower() }/{ payment.payment_for_document }", + "link": get_lms_route( + f"billing/{payment.payment_for_document_type.split(' ')[-1].lower()}/{payment.payment_for_document}" + ), } if custom_template: diff --git a/lms/lms/page/lms_home/lms_home.js b/lms/lms/page/lms_home/lms_home.js index 35f91552..f5c5f47b 100644 --- a/lms/lms/page/lms_home/lms_home.js +++ b/lms/lms/page/lms_home/lms_home.js @@ -1,3 +1,4 @@ frappe.pages["lms-home"].on_page_load = function (wrapper) { - window.location.href = "/lms/courses"; + const lmsPath = frappe.boot.lms_path || "lms"; + window.location.href = `/${lmsPath}/courses`; }; diff --git a/lms/lms/test_utils.py b/lms/lms/test_utils.py index c33c6fac..cbfe8640 100644 --- a/lms/lms/test_utils.py +++ b/lms/lms/test_utils.py @@ -2,6 +2,7 @@ import frappe from frappe.tests import UnitTestCase from frappe.utils import add_days, nowdate +from lms.hooks import lms_path from lms.lms.api import get_certified_participants from lms.lms.doctype.lms_certificate.lms_certificate import get_default_certificate_template, is_certified @@ -235,7 +236,7 @@ class TestUtils(UnitTestCase): def test_get_lesson_url(self): lessons = get_lessons(self.course.name) for lesson in lessons: - expected_url = f"/lms/courses/{self.course.name}/learn/{lesson.number}" + expected_url = f"/{lms_path}/courses/{self.course.name}/learn/{lesson.number}" self.assertEqual(get_lesson_url(self.course.name, lesson.number), expected_url) def test_is_instructor(self): diff --git a/lms/lms/user.py b/lms/lms/user.py index 5e31e5d8..d89455ce 100644 --- a/lms/lms/user.py +++ b/lms/lms/user.py @@ -4,6 +4,7 @@ from frappe.model.naming import append_number_if_name_exists from frappe.utils import escape_html, random_string from frappe.website.utils import cleanup_page_name, is_signup_disabled +from lms.hooks import lms_path from lms.lms.utils import get_country_code @@ -88,4 +89,4 @@ def set_country_from_ip(login_manager=None, user=None): def on_login(login_manager): default_app = frappe.db.get_single_value("System Settings", "default_app") if default_app == "lms": - frappe.local.response["home_page"] = "/lms" + frappe.local.response["home_page"] = f"/{lms_path}" diff --git a/lms/lms/utils.py b/lms/lms/utils.py index 9cd9a516..f12acfef 100644 --- a/lms/lms/utils.py +++ b/lms/lms/utils.py @@ -32,6 +32,22 @@ from lms.lms.md import find_macros RE_SLUG_NOTALLOWED = re.compile("[^a-z0-9]+") +def get_lms_path(): + path = frappe.conf.get("lms_path") or "lms" + return path.strip("/") + + +def get_lms_route(path=""): + base = f"/{get_lms_path()}" + if not path: + return base + return f"{base}/{path.lstrip('/')}" + + +def extend_bootinfo(bootinfo): + bootinfo["lms_path"] = get_lms_path() + + def slugify(title, used_slugs=None): """Converts title to a slug. @@ -277,7 +293,7 @@ def get_lesson_index(lesson_name): def get_lesson_url(course, lesson_number): if not lesson_number: return - return f"/lms/courses/{course}/learn/{lesson_number}" + return get_lms_route(f"courses/{course}/learn/{lesson_number}") def get_progress(course, lesson, member=None): @@ -421,7 +437,7 @@ def get_batch_details_for_notification(topic): users = [] batch_title = frappe.db.get_value("LMS Batch", topic.reference_docname, "title") subject = _("New comment in batch {0}").format(batch_title) - link = f"/lms/batches/{topic.reference_docname}" + link = get_lms_route(f"batches/{topic.reference_docname}") instructors = frappe.db.get_all( "Course Instructor", {"parenttype": "LMS Batch", "parent": topic.reference_docname}, @@ -475,7 +491,7 @@ def notify_mentions_on_portal(doc, topic): subject = _("{0} mentioned you in a comment in {1}").format( frappe.bold(from_user_name), frappe.bold(batch_title) ) - link = f"/lms/batches/{topic.reference_docname}#discussions" + link = get_lms_route(f"batches/{topic.reference_docname}#discussions") for user in mentions: notification = frappe._dict( @@ -1295,7 +1311,7 @@ def get_assignment_details(assessment, member): assessment.edit_url = f"/assignments/{assessment.assessment_name}" submission_name = existing_submission if existing_submission else "new-submission" - assessment.url = f"/lms/assignment-submission/{assessment.assessment_name}/{submission_name}" + assessment.url = get_lms_route(f"assignment-submission/{assessment.assessment_name}/{submission_name}") return assessment diff --git a/lms/lms/web_template/course_cards/course_cards.html b/lms/lms/web_template/course_cards/course_cards.html index 8eb3ab8a..d7255473 100644 --- a/lms/lms/web_template/course_cards/course_cards.html +++ b/lms/lms/web_template/course_cards/course_cards.html @@ -11,7 +11,7 @@ {{ widgets.CourseCard(course=course, read_only=False) }} {% endfor %}
- + {{ _("Explore More") }} diff --git a/lms/lms/web_template/recently_published_courses/recently_published_courses.html b/lms/lms/web_template/recently_published_courses/recently_published_courses.html index 448ef3d5..ce2fdcb6 100644 --- a/lms/lms/web_template/recently_published_courses/recently_published_courses.html +++ b/lms/lms/web_template/recently_published_courses/recently_published_courses.html @@ -12,7 +12,7 @@ {% endfor %} - + {{ _("Explore More") }} - \ No newline at end of file + diff --git a/lms/lms/widgets/Avatar.html b/lms/lms/widgets/Avatar.html index ab21f7ba..bfb71327 100644 --- a/lms/lms/widgets/Avatar.html +++ b/lms/lms/widgets/Avatar.html @@ -1,6 +1,6 @@ {% set color = get_palette(member.full_name) %} - + {% if member.user_image %} diff --git a/lms/lms/widgets/CourseCard.html b/lms/lms/widgets/CourseCard.html index de74f2c0..0c541ad2 100644 --- a/lms/lms/widgets/CourseCard.html +++ b/lms/lms/widgets/CourseCard.html @@ -93,7 +93,7 @@ {% endif %} {% endfor %} - + {% if ins_len == 1 %} {{ instructors[0].full_name }} @@ -128,7 +128,7 @@ {% else %} - + {% endif %} {% endif %} diff --git a/lms/templates/emails/assignment_submission.html b/lms/templates/emails/assignment_submission.html index d3942aaa..96ff3d5b 100644 --- a/lms/templates/emails/assignment_submission.html +++ b/lms/templates/emails/assignment_submission.html @@ -4,7 +4,6 @@

{{ _(" Please evaluate and grade it.") }}


` - + {{ _("Open Assignment") }} - diff --git a/lms/templates/emails/batch_confirmation.html b/lms/templates/emails/batch_confirmation.html index d4cc26df..5176e5db 100644 --- a/lms/templates/emails/batch_confirmation.html +++ b/lms/templates/emails/batch_confirmation.html @@ -23,7 +23,7 @@

{{ _("Visit the following link to view your ") }} - {{ _("Batch Details") }} + {{ _("Batch Details") }}

{{ _("If you have any questions or require assistance, feel free to contact us.") }} @@ -32,4 +32,3 @@

{{ _("Best Regards") }}

- diff --git a/lms/templates/emails/batch_start_reminder.html b/lms/templates/emails/batch_start_reminder.html index de240de6..95443e6a 100644 --- a/lms/templates/emails/batch_start_reminder.html +++ b/lms/templates/emails/batch_start_reminder.html @@ -20,7 +20,7 @@


- 👉 {{ _("Visit your batch") }} + 👉 {{ _("Visit your batch") }}


diff --git a/lms/templates/emails/live_class_reminder.html b/lms/templates/emails/live_class_reminder.html index cd1db5ef..20865d7a 100644 --- a/lms/templates/emails/live_class_reminder.html +++ b/lms/templates/emails/live_class_reminder.html @@ -17,7 +17,7 @@


- 👉 {{ _("Visit your batch") }} + 👉 {{ _("Visit your batch") }}


@@ -26,4 +26,4 @@

{{ _("Best Regards") }} -

\ No newline at end of file +

diff --git a/lms/www/lms.py b/lms/www/_lms.py similarity index 88% rename from lms/www/lms.py rename to lms/www/_lms.py index fcda12cd..10a9b796 100644 --- a/lms/www/lms.py +++ b/lms/www/_lms.py @@ -5,6 +5,9 @@ from bs4 import BeautifulSoup from frappe import _ from frappe.utils.telemetry import capture +from lms.hooks import lms_path +from lms.lms.utils import get_lms_route + no_cache = 1 @@ -32,6 +35,7 @@ def get_boot(): "read_only_mode": frappe.flags.read_only, "csrf_token": frappe.sessions.get_csrf_token(), "site_name": frappe.local.site, + "lms_path": lms_path, } ) @@ -85,7 +89,7 @@ def get_meta_from_document(app_path): return { "title": _("Course List"), "keywords": "All Courses, Courses, Learn", - "link": "/courses", + "link": get_lms_route("courses"), } if re.match(r"^courses/.*$", app_path): @@ -94,7 +98,7 @@ def get_meta_from_document(app_path): "title": _("New Course"), "image": frappe.db.get_single_value("Website Settings", "banner_image"), "keywords": "New Course, Create Course", - "link": "/lms/courses/new/edit", + "link": get_lms_route("courses/new/edit"), } course_name = app_path.split("/")[1] course = frappe.db.get_value( @@ -113,14 +117,14 @@ def get_meta_from_document(app_path): "image": course.image, "description": course.description, "keywords": course.tags, - "link": f"/courses/{course_name}", + "link": get_lms_route(f"courses/{course_name}"), } if app_path == "batches": return { "title": _("Batches"), "keywords": "All Batches, Batches, Learn", - "link": "/batches", + "link": get_lms_route("batches"), } if re.match(r"^batches/details/.*$", app_path): batch_name = app_path.split("/")[2] @@ -140,7 +144,7 @@ def get_meta_from_document(app_path): "image": batch.meta_image, "description": batch.batch_details, "keywords": f"{batch.category} {batch.medium}", - "link": f"/batches/details/{batch_name}", + "link": get_lms_route(f"batches/details/{batch_name}"), } if re.match(r"^batches/.*$", app_path): @@ -149,7 +153,7 @@ def get_meta_from_document(app_path): return { "title": _("New Batch"), "keywords": "New Batch, Create Batch", - "link": "/lms/batches/new/edit", + "link": get_lms_route("batches/new/edit"), } batch = frappe.db.get_value( "LMS Batch", @@ -167,14 +171,14 @@ def get_meta_from_document(app_path): "image": batch.meta_image, "description": batch.batch_details, "keywords": f"{batch.category} {batch.medium}", - "link": f"/batches/{batch_name}", + "link": get_lms_route(f"batches/{batch_name}"), } if app_path == "job-openings": return { "title": _("Job Openings"), "keywords": "Job Openings, Jobs, Vacancies", - "link": "/job-openings", + "link": get_lms_route("job-openings"), } if re.match(r"^job-openings/.*$", app_path): @@ -195,14 +199,14 @@ def get_meta_from_document(app_path): "image": job_opening.company_logo, "description": job_opening.description, "keywords": "Job Openings, Jobs, Vacancies", - "link": f"/job-openings/{job_opening_name}", + "link": get_lms_route(f"job-openings/{job_opening_name}"), } if app_path == "statistics": return { "title": _("Statistics"), "keywords": "Enrollment Count, Completion, Signups", - "link": "/statistics", + "link": get_lms_route("statistics"), } if re.match(r"^user/.*$", app_path): @@ -225,7 +229,7 @@ def get_meta_from_document(app_path): "image": user.user_image, "description": user.bio, "keywords": f"{user.full_name}, {user.bio}", - "link": f"/user/{username}", + "link": get_lms_route(f"user/{username}"), } if re.match(r"^badges/.*/.*$", app_path): @@ -242,14 +246,14 @@ def get_meta_from_document(app_path): "image": badge.image, "description": badge.description, "keywords": f"{badge.title}, {badge.description}", - "link": f"/badges/{badgeName}/{email}", + "link": get_lms_route(f"badges/{badgeName}/{email}"), } if app_path == "quizzes": return { "title": _("Quizzes"), "keywords": "Quizzes, interactive quizzes, online quizzes", - "link": "/quizzes", + "link": get_lms_route("quizzes"), } if re.match(r"^quizzes/[^/]+$", app_path): @@ -264,14 +268,14 @@ def get_meta_from_document(app_path): return { "title": quiz.title, "keywords": quiz.title, - "link": f"/quizzes/{quiz_name}", + "link": get_lms_route(f"quizzes/{quiz_name}"), } if app_path == "assignments": return { "title": _("Assignments"), "keywords": "Assignments, interactive assignments, online assignments", - "link": "/assignments", + "link": get_lms_route("assignments"), } if re.match(r"^assignments/[^/]+$", app_path): @@ -286,21 +290,21 @@ def get_meta_from_document(app_path): return { "title": assignment.title, "keywords": assignment.title, - "link": f"/assignments/{assignment_name}", + "link": get_lms_route(f"assignments/{assignment_name}"), } if app_path == "programs": return { "title": _("Programs"), "keywords": "All Programs, Programs, Learn", - "link": "/programs", + "link": get_lms_route("programs"), } if app_path == "certified-participants": return { "title": _("Certified Participants"), "keywords": "All Certified Participants, Certified Participants, Learn, Certification", - "link": "/certified-participants", + "link": get_lms_route("certified-participants"), } return {} From 3b88892905a4930b1263c9f201838a5958e5e16e Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Sat, 17 Jan 2026 23:14:33 +0530 Subject: [PATCH 04/36] refactor: change global variable to function in hooks --- lms/hooks.py | 30 +++++++++++++++++------------- lms/lms/api.py | 4 ++-- lms/lms/test_utils.py | 4 ++-- lms/lms/user.py | 5 ++--- lms/www/_lms.py | 5 ++--- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/lms/hooks.py b/lms/hooks.py index 8a0fd55e..ce08b4bd 100644 --- a/lms/hooks.py +++ b/lms/hooks.py @@ -12,8 +12,12 @@ app_color = "grey" app_email = "jannat@frappe.io" app_license = "AGPL" -lms_path = frappe.conf.get("lms_path") or "lms" -app_icon_route = f"/{lms_path}" + +def get_lms_path(): + return (frappe.conf.get("lms_path") or "lms").strip("/") + + +app_icon_route = f"/{get_lms_path()}" # Includes in # ------------------ @@ -167,8 +171,8 @@ override_whitelisted_methods = { # Add all simple route rules here website_route_rules = [ - {"from_route": f"/{lms_path}/", "to_route": "_lms"}, - {"from_route": f"/{lms_path}", "to_route": "_lms"}, + {"from_route": f"/{get_lms_path()}/", "to_route": "_lms"}, + {"from_route": f"/{get_lms_path()}", "to_route": "_lms"}, { "from_route": "/courses//", "to_route": "certificate", @@ -177,25 +181,25 @@ website_route_rules = [ website_redirects = [ {"source": "/update-profile", "target": "/edit-profile"}, - {"source": "/courses", "target": f"/{lms_path}/courses"}, + {"source": "/courses", "target": f"/{get_lms_path()}/courses"}, { "source": r"^/courses/.*$", - "target": f"/{lms_path}/courses", + "target": f"/{get_lms_path()}/courses", }, - {"source": "/batches", "target": f"/{lms_path}/batches"}, + {"source": "/batches", "target": f"/{get_lms_path()}/batches"}, { "source": r"/batches/(.*)", - "target": f"/{lms_path}/batches", + "target": f"/{get_lms_path()}/batches", "match_with_query_string": True, }, - {"source": "/job-openings", "target": f"/{lms_path}/job-openings"}, + {"source": "/job-openings", "target": f"/{get_lms_path()}/job-openings"}, { "source": r"/job-openings/(.*)", - "target": f"/{lms_path}/job-openings", + "target": f"/{get_lms_path()}/job-openings", "match_with_query_string": True, }, - {"source": "/statistics", "target": f"/{lms_path}/statistics"}, - {"source": "_lms", "target": f"/{lms_path}"}, + {"source": "/statistics", "target": f"/{get_lms_path()}/statistics"}, + {"source": "_lms", "target": f"/{get_lms_path()}"}, ] update_website_context = [ @@ -267,7 +271,7 @@ add_to_apps_screen = [ "name": "lms", "logo": "/assets/lms/frontend/learning.svg", "title": "Learning", - "route": f"/{lms_path}", + "route": f"/{get_lms_path()}", "has_permission": "lms.lms.api.check_app_permission", } ] diff --git a/lms/lms/api.py b/lms/lms/api.py index 731c0dde..faee30ce 100644 --- a/lms/lms/api.py +++ b/lms/lms/api.py @@ -28,7 +28,6 @@ from frappe.utils import ( ) from frappe.utils.response import Response -from lms.hooks import lms_path from lms.lms.doctype.course_lesson.course_lesson import save_progress from lms.lms.utils import ( get_average_rating, @@ -36,6 +35,7 @@ from lms.lms.utils import ( get_course_details, get_instructors, get_lesson_count, + get_lms_route, ) @@ -1674,7 +1674,7 @@ def get_pwa_manifest(): "name": title, "short_name": title, "description": "Easy to use, 100% open source Learning Management System", - "start_url": f"/{lms_path}", + "start_url": get_lms_route(), "icons": [ { "src": banner_image or "/assets/lms/frontend/manifest/manifest-icon-192.maskable.png", diff --git a/lms/lms/test_utils.py b/lms/lms/test_utils.py index cbfe8640..c786a8b7 100644 --- a/lms/lms/test_utils.py +++ b/lms/lms/test_utils.py @@ -2,7 +2,6 @@ import frappe from frappe.tests import UnitTestCase from frappe.utils import add_days, nowdate -from lms.hooks import lms_path from lms.lms.api import get_certified_participants from lms.lms.doctype.lms_certificate.lms_certificate import get_default_certificate_template, is_certified @@ -14,6 +13,7 @@ from .utils import ( get_lesson_index, get_lesson_url, get_lessons, + get_lms_route, get_membership, get_reviews, get_tags, @@ -236,7 +236,7 @@ class TestUtils(UnitTestCase): def test_get_lesson_url(self): lessons = get_lessons(self.course.name) for lesson in lessons: - expected_url = f"/{lms_path}/courses/{self.course.name}/learn/{lesson.number}" + expected_url = get_lms_route(f"courses/{self.course.name}/learn/{lesson.number}") self.assertEqual(get_lesson_url(self.course.name, lesson.number), expected_url) def test_is_instructor(self): diff --git a/lms/lms/user.py b/lms/lms/user.py index d89455ce..d6535977 100644 --- a/lms/lms/user.py +++ b/lms/lms/user.py @@ -4,8 +4,7 @@ from frappe.model.naming import append_number_if_name_exists from frappe.utils import escape_html, random_string from frappe.website.utils import cleanup_page_name, is_signup_disabled -from lms.hooks import lms_path -from lms.lms.utils import get_country_code +from lms.lms.utils import get_country_code, get_lms_route def validate_username_duplicates(doc, method): @@ -89,4 +88,4 @@ def set_country_from_ip(login_manager=None, user=None): def on_login(login_manager): default_app = frappe.db.get_single_value("System Settings", "default_app") if default_app == "lms": - frappe.local.response["home_page"] = f"/{lms_path}" + frappe.local.response["home_page"] = get_lms_route() diff --git a/lms/www/_lms.py b/lms/www/_lms.py index 10a9b796..1aca0b9f 100644 --- a/lms/www/_lms.py +++ b/lms/www/_lms.py @@ -5,8 +5,7 @@ from bs4 import BeautifulSoup from frappe import _ from frappe.utils.telemetry import capture -from lms.hooks import lms_path -from lms.lms.utils import get_lms_route +from lms.lms.utils import get_lms_path, get_lms_route no_cache = 1 @@ -35,7 +34,7 @@ def get_boot(): "read_only_mode": frappe.flags.read_only, "csrf_token": frappe.sessions.get_csrf_token(), "site_name": frappe.local.site, - "lms_path": lms_path, + "lms_path": get_lms_path(), } ) From 5ce8e8c4ffc9e0dbdd5de113e2fe402e27e0d58b Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Sat, 17 Jan 2026 23:22:38 +0530 Subject: [PATCH 05/36] test: flaky evaluation schedule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Earlier logic was flaky because the test calculated the last expected date using a 56‑day window, while the production code builds schedules for 60 days. Those extra 4 days sometimes include another Monday or Wednesday, so the schedule ends later than the test expects. --- .../course_evaluator/test_course_evaluator.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lms/lms/doctype/course_evaluator/test_course_evaluator.py b/lms/lms/doctype/course_evaluator/test_course_evaluator.py index 605bb553..764fdec5 100644 --- a/lms/lms/doctype/course_evaluator/test_course_evaluator.py +++ b/lms/lms/doctype/course_evaluator/test_course_evaluator.py @@ -5,7 +5,10 @@ from frappe.tests import UnitTestCase from frappe.utils import add_days, format_time, getdate -from lms.lms.doctype.course_evaluator.course_evaluator import get_schedule +from lms.lms.doctype.course_evaluator.course_evaluator import ( + get_schedule, + get_schedule_range_end_date, +) from lms.lms.test_utils import TestUtils @@ -52,17 +55,9 @@ class TestCourseEvaluator(UnitTestCase): return first_date def calculated_last_date_of_schedule(self, first_date): - last_day = add_days(getdate(), 56) - offset_monday = (0 - last_day.weekday() + 7) % 7 # 0 for Monday - offset_wednesday = (2 - last_day.weekday() + 7) % 7 # 2 for Wednesday - - if offset_monday < offset_wednesday and offset_monday <= 4: - last_day = add_days(last_day, offset_monday) - elif offset_wednesday <= 4: - last_day = add_days(last_day, offset_wednesday) - else: - last_day = add_days(last_day, min(offset_monday, offset_wednesday) + 7) - + last_day = getdate(get_schedule_range_end_date(getdate(), self.batch.name)) + while last_day.weekday() not in (0, 2): + last_day = add_days(last_day, -1) return last_day def test_unavailability_dates(self): From 2abc243b88a26da115a19b0a3041392dac6ade52 Mon Sep 17 00:00:00 2001 From: raizasafeel <89463672+raizasafeel@users.noreply.github.com> Date: Mon, 19 Jan 2026 11:10:23 +0530 Subject: [PATCH 06/36] test: refactor course tests and add deletion test --- lms/lms/doctype/lms_course/test_lms_course.py | 193 +++++++++++++----- 1 file changed, 147 insertions(+), 46 deletions(-) diff --git a/lms/lms/doctype/lms_course/test_lms_course.py b/lms/lms/doctype/lms_course/test_lms_course.py index 936fb0e9..f1225bf4 100644 --- a/lms/lms/doctype/lms_course/test_lms_course.py +++ b/lms/lms/doctype/lms_course/test_lms_course.py @@ -5,58 +5,63 @@ import unittest import frappe -from .lms_course import LMSCourse +from lms.lms.api import delete_course class TestLMSCourse(unittest.TestCase): - def test_new_course(self): - course = new_course("Test Course") - assert course.title == "Test Course" + def setUp(self): + self.cleanup_items = [] def tearDown(self): - if frappe.db.exists("User", "tester@example.com"): - frappe.delete_doc("User", "tester@example.com") + for item_type, item_name in reversed(self.cleanup_items): + if frappe.db.exists(item_type, item_name): + try: + frappe.delete_doc(item_type, item_name, force=True) + except Exception as e: + frappe.log_error(f"Failed to delete {item_type} {item_name}: {e}") - if frappe.db.exists("LMS Course", "test-course"): - frappe.db.delete("Batch Course", {"course": "test-course"}) - frappe.db.delete("LMS Enrollment", {"course": "test-course"}) - frappe.db.delete("Course Lesson", {"course": "test-course"}) - frappe.db.delete("Course Chapter", {"course": "test-course"}) - frappe.db.delete("LMS Course Mentor Mapping", {"course": "test-course"}) - frappe.db.delete("Course Instructor", {"parent": "test-course"}) - frappe.db.sql("delete from `tabCourse Instructor`") - frappe.delete_doc("LMS Course", "test-course") + def test_new_course(self): + course_name = f"Test Course {frappe.generate_hash()}" + course = self._create_course(course_name) -def new_user(name, email): - user = frappe.db.exists("User", email) - if user: - return frappe.get_doc("User", user) - else: - filters = { - "email": email, - "first_name": name, - "send_welcome_email": False, - } + self.assertEqual(course.title, course_name) + self.assertTrue(frappe.db.exists("LMS Course", course.name)) - doc = frappe.new_doc("User") - doc.update(filters) - doc.save() - return doc + def test_delete_course(self): + course = self._create_course(f"Test Course {frappe.generate_hash()}") + chapter = self._create_chapter(f"Test Chapter {frappe.generate_hash()}", course.name) + lesson = self._create_lesson(f"Test Lesson {frappe.generate_hash()}", chapter.name) + self._create_lesson_reference(chapter.name, lesson.name) + self._create_chapter_reference(course.name, chapter.name) -def new_course(title, additional_filters=None): - course = frappe.db.exists("LMS Course", {"title": title}) - if course: - return frappe.get_doc("LMS Course", course) - else: - create_evaluator() + user_email = f"test_{frappe.generate_hash()}@example.com" + self._create_user("Test Member", user_email) + enrollment = self._create_enrollment(user_email, course.name) + self._create_progress(user_email, course.name, lesson.name) + + delete_course(course.name) + + self.assertFalse(frappe.db.exists("LMS Course", course.name)) + self.assertFalse(frappe.db.exists("Course Chapter", chapter.name)) + self.assertFalse(frappe.db.exists("Course Lesson", lesson.name)) + self.assertFalse(frappe.db.exists("LMS Enrollment", enrollment.name)) + self.assertFalse(frappe.db.exists("LMS Course Progress", {"course": course.name})) + self.assertFalse(frappe.db.exists("Chapter Reference", {"parent": course.name})) + self.assertFalse(frappe.db.exists("Lesson Reference", {"parent": chapter.name})) + + def _ensure_test_user(self): + self._create_evaluator() user = frappe.db.get_value( "User", - { - "user_type": "System User", - }, + {"user_type": "System User"}, ) + return user + + def _create_course(self, title, additional_filters=None): + user = self._ensure_test_user() + filters = { "title": title, "short_introduction": title, @@ -70,15 +75,111 @@ def new_course(title, additional_filters=None): if additional_filters: filters.update(additional_filters) - doc = frappe.new_doc("LMS Course") - doc.update(filters) + course = frappe.new_doc("LMS Course") + course.update(filters) + course.save() + self.cleanup_items.append(("LMS Course", course.name)) + return course + + def _create_chapter(self, title, course): + chapter = frappe.new_doc("Course Chapter") + chapter.update( + { + "title": title, + "course": course, + } + ) + chapter.save() + self.cleanup_items.append(("Course Chapter", chapter.name)) + return chapter + + def _create_lesson(self, title, chapter): + lesson = frappe.new_doc("Course Lesson") + lesson.update( + { + "title": title, + "chapter": chapter, + } + ) + lesson.save() + self.cleanup_items.append(("Course Lesson", lesson.name)) + return lesson + + def _create_lesson_reference(self, chapter, lesson): + lesson_ref = frappe.get_doc( + { + "doctype": "Lesson Reference", + "lesson": lesson, + "parent": chapter, + "parenttype": "Course Chapter", + "parentfield": "lessons", + "idx": 1, + } + ) + lesson_ref.insert() + self.cleanup_items.append(("Lesson Reference", lesson_ref.name)) + return lesson_ref + + def _create_chapter_reference(self, course, chapter): + chapter_ref = frappe.get_doc( + { + "doctype": "Chapter Reference", + "chapter": chapter, + "parent": course, + "parenttype": "LMS Course", + "parentfield": "chapters", + "idx": 1, + } + ) + chapter_ref.insert() + self.cleanup_items.append(("Chapter Reference", chapter_ref.name)) + return chapter_ref + + def _create_user(self, name, email): + user = frappe.db.exists("User", email) + if user: + return frappe.get_doc("User", user) + + doc = frappe.new_doc("User") + doc.update( + { + "email": email, + "first_name": name, + "send_welcome_email": False, + } + ) doc.save() + self.cleanup_items.append(("User", doc.name)) return doc - -def create_evaluator(): - if not frappe.db.exists("Course Evaluator", "evaluator@example.com"): - new_user("Evaluator", "evaluator@example.com") - frappe.get_doc({"doctype": "Course Evaluator", "evaluator": "evaluator@example.com"}).save( - ignore_permissions=True + def _create_enrollment(self, member, course): + enrollment = frappe.new_doc("LMS Enrollment") + enrollment.update( + { + "member": member, + "course": course, + } ) + enrollment.insert() + self.cleanup_items.append(("LMS Enrollment", enrollment.name)) + return enrollment + + def _create_progress(self, member, course, lesson): + progress = frappe.new_doc("LMS Course Progress") + progress.update( + { + "member": member, + "course": course, + "lesson": lesson, + } + ) + progress.insert() + self.cleanup_items.append(("LMS Course Progress", progress.name)) + return progress + + def _create_evaluator(self): + if not frappe.db.exists("Course Evaluator", "evaluator@example.com"): + self._create_user("Evaluator", "evaluator@example.com") + evaluator = frappe.get_doc({"doctype": "Course Evaluator", "evaluator": "evaluator@example.com"}) + evaluator.save(ignore_permissions=True) + self.cleanup_items.append(("Course Evaluator", evaluator.name)) From 338f46ac172838fa0c183c2748b37eb11127e03b Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Tue, 20 Jan 2026 16:57:30 +0530 Subject: [PATCH 07/36] chore: changed frappe dependency range --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 3159040f..1463f1bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,3 +64,6 @@ ignore = [ quote-style = "double" indent-style = "tab" docstring-code-format = true + +[tool.bench.frappe-dependencies] +frappe = ">=15.0.0,<=17.0.0-dev" \ No newline at end of file From 9e61982dcb162c27fa0205e25a4ffd7477d8e664 Mon Sep 17 00:00:00 2001 From: raizasafeel <89463672+raizasafeel@users.noreply.github.com> Date: Wed, 21 Jan 2026 10:26:31 +0530 Subject: [PATCH 08/36] refactor(tests): extract shared test utilities into BaseTestUtils --- lms/lms/doctype/lms_course/test_lms_course.py | 168 ++----------- lms/lms/test_helpers.py | 237 ++++++++++++++++++ lms/lms/test_utils.py | 203 +++------------ lms/test_auth.py | 14 +- 4 files changed, 301 insertions(+), 321 deletions(-) create mode 100644 lms/lms/test_helpers.py diff --git a/lms/lms/doctype/lms_course/test_lms_course.py b/lms/lms/doctype/lms_course/test_lms_course.py index f1225bf4..8319be62 100644 --- a/lms/lms/doctype/lms_course/test_lms_course.py +++ b/lms/lms/doctype/lms_course/test_lms_course.py @@ -1,24 +1,18 @@ # Copyright (c) 2021, FOSS United and Contributors # See license.txt -import unittest - import frappe from lms.lms.api import delete_course +from lms.lms.test_helpers import BaseTestUtils -class TestLMSCourse(unittest.TestCase): +class TestLMSCourse(BaseTestUtils): def setUp(self): - self.cleanup_items = [] - - def tearDown(self): - for item_type, item_name in reversed(self.cleanup_items): - if frappe.db.exists(item_type, item_name): - try: - frappe.delete_doc(item_type, item_name, force=True) - except Exception as e: - frappe.log_error(f"Failed to delete {item_type} {item_name}: {e}") + super().setUp() + self.instructor = self._create_user( + "frappe@example.com", "Frappe", "Admin", ["Moderator", "Course Creator"] + ) def test_new_course(self): course_name = f"Test Course {frappe.generate_hash()}" @@ -31,15 +25,15 @@ class TestLMSCourse(unittest.TestCase): def test_delete_course(self): course = self._create_course(f"Test Course {frappe.generate_hash()}") chapter = self._create_chapter(f"Test Chapter {frappe.generate_hash()}", course.name) - lesson = self._create_lesson(f"Test Lesson {frappe.generate_hash()}", chapter.name) + lesson = self._create_lesson(f"Test Lesson {frappe.generate_hash()}", chapter.name, course.name) - self._create_lesson_reference(chapter.name, lesson.name) - self._create_chapter_reference(course.name, chapter.name) + lesson_ref = self._create_lesson_reference(chapter.name, lesson.name) + chapter_ref = self._create_chapter_reference(course.name, chapter.name) user_email = f"test_{frappe.generate_hash()}@example.com" - self._create_user("Test Member", user_email) + self._create_user(user_email, "Test", "Member", ["LMS Student"]) enrollment = self._create_enrollment(user_email, course.name) - self._create_progress(user_email, course.name, lesson.name) + progress = self._create_progress(user_email, course.name, lesson.name) delete_course(course.name) @@ -51,135 +45,11 @@ class TestLMSCourse(unittest.TestCase): self.assertFalse(frappe.db.exists("Chapter Reference", {"parent": course.name})) self.assertFalse(frappe.db.exists("Lesson Reference", {"parent": chapter.name})) - def _ensure_test_user(self): - self._create_evaluator() - user = frappe.db.get_value( - "User", - {"user_type": "System User"}, - ) - return user - - def _create_course(self, title, additional_filters=None): - user = self._ensure_test_user() - - filters = { - "title": title, - "short_introduction": title, - "description": title, - "video_link": "https://youtu.be/pEbIhUySqbk", - "image": "/assets/lms/images/course-home.png", - "instructors": [{"instructor": user}], - "published": 1, - } - - if additional_filters: - filters.update(additional_filters) - - course = frappe.new_doc("LMS Course") - course.update(filters) - course.save() - self.cleanup_items.append(("LMS Course", course.name)) - return course - - def _create_chapter(self, title, course): - chapter = frappe.new_doc("Course Chapter") - chapter.update( - { - "title": title, - "course": course, - } - ) - chapter.save() - self.cleanup_items.append(("Course Chapter", chapter.name)) - return chapter - - def _create_lesson(self, title, chapter): - lesson = frappe.new_doc("Course Lesson") - lesson.update( - { - "title": title, - "chapter": chapter, - } - ) - lesson.save() - self.cleanup_items.append(("Course Lesson", lesson.name)) - return lesson - - def _create_lesson_reference(self, chapter, lesson): - lesson_ref = frappe.get_doc( - { - "doctype": "Lesson Reference", - "lesson": lesson, - "parent": chapter, - "parenttype": "Course Chapter", - "parentfield": "lessons", - "idx": 1, - } - ) - lesson_ref.insert() - self.cleanup_items.append(("Lesson Reference", lesson_ref.name)) - return lesson_ref - - def _create_chapter_reference(self, course, chapter): - chapter_ref = frappe.get_doc( - { - "doctype": "Chapter Reference", - "chapter": chapter, - "parent": course, - "parenttype": "LMS Course", - "parentfield": "chapters", - "idx": 1, - } - ) - chapter_ref.insert() - self.cleanup_items.append(("Chapter Reference", chapter_ref.name)) - return chapter_ref - - def _create_user(self, name, email): - user = frappe.db.exists("User", email) - if user: - return frappe.get_doc("User", user) - - doc = frappe.new_doc("User") - doc.update( - { - "email": email, - "first_name": name, - "send_welcome_email": False, - } - ) - doc.save() - self.cleanup_items.append(("User", doc.name)) - return doc - - def _create_enrollment(self, member, course): - enrollment = frappe.new_doc("LMS Enrollment") - enrollment.update( - { - "member": member, - "course": course, - } - ) - enrollment.insert() - self.cleanup_items.append(("LMS Enrollment", enrollment.name)) - return enrollment - - def _create_progress(self, member, course, lesson): - progress = frappe.new_doc("LMS Course Progress") - progress.update( - { - "member": member, - "course": course, - "lesson": lesson, - } - ) - progress.insert() - self.cleanup_items.append(("LMS Course Progress", progress.name)) - return progress - - def _create_evaluator(self): - if not frappe.db.exists("Course Evaluator", "evaluator@example.com"): - self._create_user("Evaluator", "evaluator@example.com") - evaluator = frappe.get_doc({"doctype": "Course Evaluator", "evaluator": "evaluator@example.com"}) - evaluator.save(ignore_permissions=True) - self.cleanup_items.append(("Course Evaluator", evaluator.name)) + # remove from cleanup_items list since delete_course already deleted them + self.cleanup_items.remove(("LMS Course", course.name)) + self.cleanup_items.remove(("LMS Enrollment", enrollment.name)) + self.cleanup_items.remove(("LMS Course Progress", progress.name)) + self.cleanup_items.remove(("Chapter Reference", chapter_ref.name)) + self.cleanup_items.remove(("Lesson Reference", lesson_ref.name)) + self.cleanup_items.remove(("Course Chapter", chapter.name)) + self.cleanup_items.remove(("Course Lesson", lesson.name)) diff --git a/lms/lms/test_helpers.py b/lms/lms/test_helpers.py new file mode 100644 index 00000000..31f2a058 --- /dev/null +++ b/lms/lms/test_helpers.py @@ -0,0 +1,237 @@ +import frappe +from frappe.tests import UnitTestCase +from frappe.utils import add_days, nowdate + +from lms.lms.doctype.lms_certificate.lms_certificate import get_default_certificate_template + + +class BaseTestUtils(UnitTestCase): + """ + Base class with helper methods for creating test data. + Subclasses should call super().setUp() and super().tearDown(). + """ + + def setUp(self): + self.cleanup_items = [] + + def tearDown(self): + for item_type, item_name in reversed(self.cleanup_items): + if frappe.db.exists(item_type, item_name): + try: + frappe.delete_doc(item_type, item_name, force=True) + except Exception: + pass + + def _create_user(self, email, first_name, last_name, roles, user_type="Website User"): + if frappe.db.exists("User", email): + return frappe.get_doc("User", email) + + user = frappe.new_doc("User") + user.update( + { + "email": email, + "first_name": first_name, + "last_name": last_name, + "user_type": user_type, + "send_welcome_email": False, + } + ) + for role in roles: + user.append("roles", {"role": role}) + user.save() + self.cleanup_items.append(("User", user.name)) + return user + + def _create_course(self, title="Utility Course", instructor="frappe@example.com"): + existing = frappe.db.exists("LMS Course", {"title": title}) + if existing: + return frappe.get_doc("LMS Course", existing) + + course = frappe.new_doc("LMS Course") + course.update( + { + "title": title, + "short_introduction": "A course to test utilities of Frappe Learning", + "description": "This is a detailed description of the Utility Course.", + "tags": "Frappe,Learning,Utility", + "published": 1, + "instructors": [{"instructor": instructor}], + } + ) + course.save() + self.cleanup_items.append(("LMS Course", course.name)) + return course + + def _create_chapter(self, title, course): + if not title: + title = f"Course Chapter {frappe.generate_hash()}" + + existing = frappe.db.exists("Course Chapter", {"course": course, "title": title}) + if existing: + return frappe.get_doc("Course Chapter", existing) + + chapter = frappe.new_doc("Course Chapter") + chapter.update( + { + "course": course, + "title": title, + } + ) + chapter.save() + self.cleanup_items.append(("Course Chapter", chapter.name)) + return chapter + + def _create_lesson(self, title, chapter, course): + existing = frappe.db.exists("Course Lesson", {"course": course, "title": title}) + if existing: + return frappe.get_doc("Course Lesson", existing) + + lesson = frappe.new_doc("Course Lesson") + lesson.update( + { + "course": course, + "chapter": chapter, + "title": title, + "content": '{"time":1765194986690,"blocks":[{"id":"dkLzbW14ds","type":"markdown","data":{"text":"This is a simple content for the current lesson."}},{"id":"KBwuWPc8rV","type":"markdown","data":{"text":""}}],"version":"2.29.0"}', + } + ) + lesson.save() + self.cleanup_items.append(("Course Lesson", lesson.name)) + return lesson + + def _create_lesson_reference(self, chapter, lesson): + lesson_ref = frappe.get_doc( + { + "doctype": "Lesson Reference", + "lesson": lesson, + "parent": chapter, + "parenttype": "Course Chapter", + "parentfield": "lessons", + "idx": 1, + } + ) + lesson_ref.insert() + self.cleanup_items.append(("Lesson Reference", lesson_ref.name)) + return lesson_ref + + def _create_chapter_reference(self, course, chapter, idx=1): + chapter_ref = frappe.get_doc( + { + "doctype": "Chapter Reference", + "chapter": chapter, + "parent": course, + "parenttype": "LMS Course", + "parentfield": "chapters", + "idx": idx, + } + ) + chapter_ref.insert() + self.cleanup_items.append(("Chapter Reference", chapter_ref.name)) + return chapter_ref + + def _create_enrollment(self, member, course): + existing = frappe.db.exists("LMS Enrollment", {"course": course, "member": member}) + if existing: + return frappe.get_doc("LMS Enrollment", existing) + + enrollment = frappe.new_doc("LMS Enrollment") + enrollment.update({"member": member, "course": course}) + enrollment.insert() + self.cleanup_items.append(("LMS Enrollment", enrollment.name)) + return enrollment + + def _create_progress(self, member, course, lesson): + progress = frappe.new_doc("LMS Course Progress") + progress.update({"member": member, "course": course, "lesson": lesson}) + progress.insert() + self.cleanup_items.append(("LMS Course Progress", progress.name)) + return progress + + def _create_evaluator(self, evaluator_email="frappe@example.com"): + if frappe.db.exists("Course Evaluator", evaluator_email): + return frappe.get_doc("Course Evaluator", evaluator_email) + + evaluator = frappe.new_doc("Course Evaluator") + evaluator.update( + { + "evaluator": evaluator_email, + "schedule": [ + {"day": "Monday", "start_time": "10:00", "end_time": "12:00"}, + {"day": "Wednesday", "start_time": "14:00", "end_time": "16:00"}, + ], + "unavailable_from": add_days(nowdate(), 5), + "unavailable_to": add_days(nowdate(), 12), + } + ) + evaluator.save() + self.cleanup_items.append(("Course Evaluator", evaluator.name)) + return evaluator + + def _create_batch( + self, + course, + instructor="frappe@example.com", + title="Utility Training", + evaluator="frappe@example.com", + ): + existing = frappe.db.exists("LMS Batch", {"title": title}) + if existing: + return frappe.get_doc("LMS Batch", existing) + + batch = frappe.new_doc("LMS Batch") + batch.update( + { + "title": title, + "start_date": nowdate(), + "end_date": add_days(nowdate(), 10), + "start_time": "09:00:00", + "end_time": "11:00:00", + "timezone": "Asia/Kolkata", + "description": "Batch for Utility Course Training", + "batch_details": "This batch is created to test utility functions.", + "evaluation_end_date": add_days(nowdate(), 120), + "instructors": [{"instructor": instructor}], + "courses": [{"course": course, "evaluator": evaluator}], + } + ) + batch.save() + self.cleanup_items.append(("LMS Batch", batch.name)) + return batch + + def _add_rating(self, course, member, rating, review_text): + existing = frappe.db.exists("LMS Course Review", {"course": course, "owner": member}) + if existing: + return frappe.get_doc("LMS Course Review", existing) + + frappe.session.user = member + review_doc = frappe.new_doc("LMS Course Review") + review_doc.update( + { + "course": course, + "rating": rating, + "review": review_text, + } + ) + review_doc.save() + self.cleanup_items.append(("LMS Course Review", review_doc.name)) + frappe.session.user = "Administrator" + return review_doc + + def _create_certificate(self, course, member): + existing = frappe.db.exists("LMS Certificate", {"course": course, "member": member}) + if existing: + return frappe.get_doc("LMS Certificate", existing) + + certificate = frappe.new_doc("LMS Certificate") + certificate.update( + { + "course": course, + "member": member, + "issue_date": frappe.utils.nowdate(), + "template": get_default_certificate_template(), + "published": 1, + } + ) + certificate.save() + self.cleanup_items.append(("LMS Certificate", certificate.name)) + return certificate diff --git a/lms/lms/test_utils.py b/lms/lms/test_utils.py index c33c6fac..69a4301a 100644 --- a/lms/lms/test_utils.py +++ b/lms/lms/test_utils.py @@ -1,11 +1,12 @@ +# Copyright (c) 2021, FOSS United and Contributors +# See license.txt + import frappe -from frappe.tests import UnitTestCase -from frappe.utils import add_days, nowdate from lms.lms.api import get_certified_participants -from lms.lms.doctype.lms_certificate.lms_certificate import get_default_certificate_template, is_certified - -from .utils import ( +from lms.lms.doctype.lms_certificate.lms_certificate import is_certified +from lms.lms.test_helpers import BaseTestUtils +from lms.lms.utils import ( get_average_rating, get_chapters, get_evaluator, @@ -25,132 +26,50 @@ from .utils import ( ) -class TestUtils(UnitTestCase): +class TestLMSUtils(BaseTestUtils): def setUp(self): - self.student1 = self.create_user("student1@example.com", "Ashley", "Smith", ["LMS Student"]) - self.student2 = self.create_user("student2@example.com", "John", "Doe", ["LMS Student"]) - self.admin = self.create_user( + super().setUp() + + self.student1 = self._create_user("student1@example.com", "Ashley", "Smith", ["LMS Student"]) + self.student2 = self._create_user("student2@example.com", "John", "Doe", ["LMS Student"]) + self.admin = self._create_user( "frappe@example.com", "Frappe", "Admin", ["Moderator", "Course Creator", "Batch Evaluator"] ) + self.course = self._create_course() + self._setup_chapters_and_lessons() - self.course = self.create_a_course() - self.add_chapters() - self.add_lessons() + self._create_enrollment(self.student1.email, self.course.name) + self._create_enrollment(self.student2.email, self.course.name) - self.add_enrollment(self.course.name, self.student1.email) - self.add_enrollment(self.course.name, self.student2.email) + self._add_rating(self.course.name, self.student1.email, 0.8, "Good course") + self._add_rating(self.course.name, self.student2.email, 1, "Excellent course") - self.add_rating(self.course.name, self.student1.email, 0.8, "Good course") - self.add_rating(self.course.name, self.student2.email, 1, "Excellent course") + self._create_certificate(self.course.name, self.student1.email) - self.create_certificate(self.course.name, self.student1.email) + self.evaluator = self._create_evaluator() + self.batch = self._create_batch(self.course.name) - self.evaluator = self.create_evaluator() - self.batch = self.create_a_batch() - - def create_a_course(self): - existing_course = frappe.db.exists("LMS Course", {"title": "Utility Course"}) - if existing_course: - return frappe.get_doc("LMS Course", existing_course) - - course = frappe.new_doc("LMS Course") - course.title = "Utility Course" - course.short_introduction = "A course to test utilities of Frappe Learning" - course.description = "This is a detailed description of the Utility Course." - course.tags = "Frappe,Learning,Utility" - course.published = 1 - course.append("instructors", {"instructor": "frappe@example.com"}) - course.save() - return course - - def add_chapters(self): + def _setup_chapters_and_lessons(self): chapters = [] for i in range(1, 4): - chapter = frappe.new_doc("Course Chapter") - chapter.course = self.course.name - chapter.title = f"Chapter {i}" - chapter.save() + chapter = self._create_chapter(f"Chapter {i}", self.course.name) chapters.append(chapter) self.course.reload() for chapter in chapters: - self.course.append("chapters", {"chapter": chapter.name}) + if not any(c.chapter == chapter.name for c in self.course.chapters): + self.course.append("chapters", {"chapter": chapter.name}) self.course.save() - def add_lessons(self): - for chapter in self.course.chapters: - chapterDoc = frappe.get_doc("Course Chapter", chapter.chapter) - lessons = [] + for chapter_ref in self.course.chapters: + chapter_doc = frappe.get_doc("Course Chapter", chapter_ref.chapter) for j in range(1, 3): - lesson = frappe.new_doc("Course Lesson") - lesson.course = self.course.name - lesson.chapter = chapter.chapter - lesson.title = f"Lesson {j} of {chapter.chapter}" - content = '{"time":1765194986690,"blocks":[{"id":"dkLzbW14ds","type":"markdown","data":{"text":"This is a simple content for the current lesson."}},{"id":"KBwuWPc8rV","type":"markdown","data":{"text":""}}],"version":"2.29.0"}' - lesson.content = content - lesson.save() - lessons.append(lesson) + lesson_title = f"Lesson {j} of {chapter_ref.chapter}" + lesson = self._create_lesson(lesson_title, chapter_ref.chapter, self.course.name) - for lesson in lessons: - chapterDoc.append("lessons", {"lesson": lesson.name}) - chapterDoc.save() - - def create_evaluator(self): - if frappe.db.exists("Course Evaluator", "frappe@example.com"): - return frappe.get_doc("Course Evaluator", "frappe@example.com") - - evaluator = frappe.new_doc("Course Evaluator") - evaluator.evaluator = "frappe@example.com" - evaluator.append("schedule", {"day": "Monday", "start_time": "10:00", "end_time": "12:00"}) - evaluator.append("schedule", {"day": "Wednesday", "start_time": "14:00", "end_time": "16:00"}) - evaluator.unavailable_from = add_days(nowdate(), 5) - evaluator.unavailable_to = add_days(nowdate(), 12) - evaluator.save() - return evaluator - - def create_a_batch(self): - existing_batch = frappe.db.exists("LMS Batch", {"title": "Utility Training"}) - if existing_batch: - return frappe.get_doc("LMS Batch", existing_batch) - - batch = frappe.new_doc("LMS Batch") - batch.title = "Utility Training" - batch.start_date = nowdate() - batch.end_date = add_days(batch.start_date, 10) - batch.start_time = "09:00:00" - batch.end_time = "11:00:00" - batch.timezone = "Asia/Kolkata" - batch.description = "Batch for Utility Course Training" - batch.batch_details = "This batch is created to test utility functions." - batch.evaluation_end_date = add_days(nowdate(), 120) - batch.append("instructors", {"instructor": "frappe@example.com"}) - batch.append("courses", {"course": self.course.name, "evaluator": "frappe@example.com"}) - batch.save() - return batch - - def create_user(self, email, first_name, last_name, roles): - if frappe.db.exists("User", email): - return frappe.get_doc("User", email) - else: - user = frappe.new_doc("User") - user.email = email - user.first_name = first_name - user.last_name = last_name - user.user_type = "Website User" - for role in roles: - user.append("roles", {"role": role}) - user.save() - return user - - def create_certificate(self, course_name, member): - certificate = frappe.new_doc("LMS Certificate") - certificate.course = course_name - certificate.member = member - certificate.issue_date = frappe.utils.nowdate() - certificate.template = get_default_certificate_template() - certificate.published = 1 - certificate.save() - return certificate + if not any(l.lesson == lesson.name for l in chapter_doc.lessons): + chapter_doc.append("lessons", {"lesson": lesson.name}) + chapter_doc.save() def test_simple_slugs(self): self.assertEqual(slugify("hello-world"), "hello-world") @@ -161,12 +80,6 @@ class TestUtils(UnitTestCase): self.assertEqual(slugify("Hello World", ["hello-world"]), "hello-world-2") self.assertEqual(slugify("Hello World", ["hello-world", "hello-world-2"]), "hello-world-3") - def add_enrollment(self, course, member): - enrollment = frappe.new_doc("LMS Enrollment") - enrollment.course = course - enrollment.member = member - enrollment.save() - def test_get_membership(self): membership = get_membership(self.course.name, self.student1.email) self.assertIsNotNone(membership) @@ -185,13 +98,6 @@ class TestUtils(UnitTestCase): all_lessons = frappe.db.count("Course Lesson", {"course": self.course.name}) self.assertEqual(len(lessons), all_lessons) - for chapter in self.course.chapters: - chapter_lessons = [lesson for lesson in lessons if lesson.chapter == chapter.chapter] - self.assertEqual(len(chapter_lessons), 2) - for j, lesson in enumerate(chapter_lessons, start=1): - self.assertEqual(lesson.title, f"Lesson {j} of {chapter.chapter}") - self.assertEqual(lesson.number, f"{chapter.idx}-{j}") - def test_get_tags(self): tags = get_tags(self.course.name) expected_tags = ["Frappe", "Learning", "Utility"] @@ -206,27 +112,10 @@ class TestUtils(UnitTestCase): average_rating = get_average_rating(self.course.name) self.assertEqual(average_rating, 4.5) - def add_rating(self, course_name, member, rating, review): - frappe.session.user = member - review = frappe.new_doc("LMS Course Review") - review.course = course_name - review.rating = rating - review.review = review - review.save() - frappe.session.user = "Administrator" - def test_get_reviews(self): reviews = get_reviews(self.course.name) self.assertEqual(len(reviews), 2) - for review in reviews: - if review.rating == 0.8: - self.assertEqual(review.member, self.student1.email) - self.assertEqual(review.review, "Good course") - elif review.rating == 1: - self.assertEqual(review.member, self.student2.email) - self.assertEqual(review.review, "Excellent course") - def test_get_lesson_index(self): lessons = get_lessons(self.course.name) for lesson in lessons: @@ -298,30 +187,16 @@ class TestUtils(UnitTestCase): frappe.db.set_value("User", self.student1.email, "open_to", "") def test_rating_validation(self): - student3 = self.create_user("student3@example.com", "Emily", "Cooper", ["LMS Student"]) + student3 = self._create_user("student3@example.com", "Emily", "Cooper", ["LMS Student"]) with self.assertRaises(frappe.exceptions.ValidationError): - self.add_rating(self.course.name, student3.email, -0.5, "Bad course") + frappe.session.user = student3.email + review = frappe.new_doc("LMS Course Review") + review.course = self.course.name + review.rating = -0.5 + review.review = "Bad course" + review.save() frappe.session.user = "Administrator" - frappe.delete_doc("User", student3.email) def test_get_evaluator(self): evaluator_email = get_evaluator(self.course.name, self.batch.name) self.assertEqual(evaluator_email, self.evaluator.evaluator) - - def tearDown(self): - if frappe.db.exists("LMS Batch", self.batch.name): - frappe.delete_doc("LMS Batch", self.batch.name) - - if frappe.db.exists("LMS Course", self.course.name): - frappe.db.delete("LMS Certificate", {"course": self.course.name}) - frappe.db.delete("LMS Enrollment", {"course": self.course.name}) - frappe.db.delete("LMS Course Review", {"course": self.course.name}) - frappe.db.delete("Course Lesson", {"course": self.course.name}) - frappe.db.delete("Course Chapter", {"course": self.course.name}) - frappe.db.delete("Course Instructor", {"parent": self.course.name}) - frappe.delete_doc("LMS Course", self.course.name) - - frappe.delete_doc("Course Evaluator", self.evaluator.name) - frappe.delete_doc("User", "student1@example.com") - frappe.delete_doc("User", "student2@example.com") - frappe.delete_doc("User", "frappe@example.com") diff --git a/lms/test_auth.py b/lms/test_auth.py index 42569cbd..18e4168a 100644 --- a/lms/test_auth.py +++ b/lms/test_auth.py @@ -1,16 +1,14 @@ import frappe -from frappe.tests import UnitTestCase from frappe.tests.test_api import FrappeAPITestCase -from lms.auth import authenticate -from lms.lms.test_utils import TestUtils +from lms.lms.test_helpers import BaseTestUtils -class TestAuth(FrappeAPITestCase): +class TestAuth(FrappeAPITestCase, BaseTestUtils): def setUp(self): - self.normal_user = TestUtils.create_user( - self, "normal-user@example.com", "Normal", "User", ["LMS Student"] - ) + super().setUp() + BaseTestUtils.setUp(self) + self.normal_user = self._create_user("normal-user@example.com", "Normal", "User", ["LMS Student"]) def test_allowed_path(self): site_url = frappe.utils.get_site_url(frappe.local.site) @@ -33,4 +31,4 @@ class TestAuth(FrappeAPITestCase): self.assertEqual(response.json.get("exc_type"), "PermissionError") def tearDown(self): - frappe.delete_doc("User", self.normal_user.name) + BaseTestUtils.tearDown(self) From 8bf896f766fd8dc1c3df3b493c42b51e30e7879f Mon Sep 17 00:00:00 2001 From: raizasafeel <89463672+raizasafeel@users.noreply.github.com> Date: Wed, 21 Jan 2026 11:13:58 +0530 Subject: [PATCH 09/36] refactor(tests): use BaseTestUtils inheritance for tests --- .../course_evaluator/test_course_evaluator.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lms/lms/doctype/course_evaluator/test_course_evaluator.py b/lms/lms/doctype/course_evaluator/test_course_evaluator.py index 6fd7bf05..e3e7f9af 100644 --- a/lms/lms/doctype/course_evaluator/test_course_evaluator.py +++ b/lms/lms/doctype/course_evaluator/test_course_evaluator.py @@ -1,23 +1,21 @@ # Copyright (c) 2022, Frappe and Contributors # See license.txt -# import frappe -from frappe.tests import UnitTestCase from frappe.utils import add_days, format_time, getdate from lms.lms.doctype.course_evaluator.course_evaluator import get_schedule, get_schedule_range_end_date -from lms.lms.test_utils import TestUtils +from lms.lms.test_helpers import BaseTestUtils -class TestCourseEvaluator(UnitTestCase): +class TestCourseEvaluator(BaseTestUtils): def setUp(self): - self.admin = TestUtils.create_user( - self, "frappe@example.com", "Frappe", "Admin", ["Moderator", "Course Creator", "Batch Evaluator"] + super().setUp() + self.admin = self._create_user( + "frappe@example.com", "Frappe", "Admin", ["Moderator", "Course Creator", "Batch Evaluator"] ) - self.course = TestUtils.create_a_course(self) - - self.evaluator = TestUtils.create_evaluator(self) - self.batch = TestUtils.create_a_batch(self) + self.course = self._create_course() + self.evaluator = self._create_evaluator() + self.batch = self._create_batch(self.course.name) def test_schedule_day_and_time(self): schedule = get_schedule(self.batch.courses[0].course, self.batch.name) From aeb2724e82e5d2cdcc550c2c9ed0ec13852c325e Mon Sep 17 00:00:00 2001 From: raizasafeel <89463672+raizasafeel@users.noreply.github.com> Date: Fri, 23 Jan 2026 12:26:03 +0530 Subject: [PATCH 10/36] test: use BaseTestUtils in test_auth for consistent test setup --- lms/test_auth.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lms/test_auth.py b/lms/test_auth.py index 13bba5d4..7c695924 100644 --- a/lms/test_auth.py +++ b/lms/test_auth.py @@ -1,13 +1,13 @@ import frappe from frappe.tests.test_api import FrappeAPITestCase +from lms.auth import authenticate from lms.lms.test_helpers import BaseTestUtils -class TestAuth(FrappeAPITestCase, BaseTestUtils): +class TestAuth(BaseTestUtils, FrappeAPITestCase): def setUp(self): super().setUp() - BaseTestUtils.setUp(self) self.normal_user = self._create_user("normal-user@example.com", "Normal", "User", ["LMS Student"]) def test_allowed_path(self): @@ -23,4 +23,4 @@ class TestAuth(FrappeAPITestCase, BaseTestUtils): frappe.session.user = "Administrator" def tearDown(self): - BaseTestUtils.tearDown(self) + super().tearDown() From 0e8b232ef1ed1ef11ad936cc63a7f2026db381b8 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Fri, 23 Jan 2026 18:26:09 +0530 Subject: [PATCH 11/36] feat: course admin dashboard --- frontend/components.d.ts | 1 + frontend/package.json | 9 +- frontend/src/components/Controls/Uploader.vue | 2 +- frontend/src/components/CourseCardOverlay.vue | 31 +- .../Modals/CourseProgressSummary.vue | 231 --- frontend/src/components/NumberChartGraph.vue | 20 + frontend/src/components/ProgressBar.vue | 5 +- frontend/src/pages/CourseDetail.vue | 194 --- .../{ => Courses}/CourseCertification.vue | 2 +- .../src/pages/Courses/CourseDashboard.vue | 313 ++++ frontend/src/pages/Courses/CourseDetail.vue | 123 ++ .../pages/Courses/CourseEnrollmentModal.vue | 97 ++ .../src/pages/{ => Courses}/CourseForm.vue | 2 +- frontend/src/pages/Courses/CourseOverview.vue | 102 ++ frontend/src/pages/{ => Courses}/Courses.vue | 3 +- frontend/src/router.js | 8 +- frontend/vite.config.js | 1 + frontend/yarn.lock | 567 ++++--- lms/lms/api.py | 20 +- .../doctype/lms_enrollment/lms_enrollment.py | 13 + yarn.lock | 1374 ----------------- 21 files changed, 971 insertions(+), 2147 deletions(-) delete mode 100644 frontend/src/components/Modals/CourseProgressSummary.vue create mode 100644 frontend/src/components/NumberChartGraph.vue delete mode 100644 frontend/src/pages/CourseDetail.vue rename frontend/src/pages/{ => Courses}/CourseCertification.vue (98%) create mode 100644 frontend/src/pages/Courses/CourseDashboard.vue create mode 100644 frontend/src/pages/Courses/CourseDetail.vue create mode 100644 frontend/src/pages/Courses/CourseEnrollmentModal.vue rename frontend/src/pages/{ => Courses}/CourseForm.vue (99%) create mode 100644 frontend/src/pages/Courses/CourseOverview.vue rename frontend/src/pages/{ => Courses}/Courses.vue (99%) delete mode 100644 yarn.lock diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 2b0e68e0..1292ad68 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -94,6 +94,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/Uploader.vue b/frontend/src/components/Controls/Uploader.vue index 8c83ff4c..75242a21 100644 --- a/frontend/src/components/Controls/Uploader.vue +++ b/frontend/src/components/Controls/Uploader.vue @@ -67,7 +67,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..c8fd01dd 100644 --- a/frontend/src/components/CourseCardOverlay.vue +++ b/frontend/src/components/CourseCardOverlay.vue @@ -56,14 +56,14 @@ - {{ __('Contact the Administrator to enroll for this course.') }} + {{ __('Contact the Administrator to enroll for this course') }} - 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..835d3643 --- /dev/null +++ b/frontend/src/components/NumberChartGraph.vue @@ -0,0 +1,20 @@ + + 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 @@ - - 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..b924a1a5 --- /dev/null +++ b/frontend/src/pages/Courses/CourseDashboard.vue @@ -0,0 +1,313 @@ + + diff --git a/frontend/src/pages/Courses/CourseDetail.vue b/frontend/src/pages/Courses/CourseDetail.vue new file mode 100644 index 00000000..716621d7 --- /dev/null +++ b/frontend/src/pages/Courses/CourseDetail.vue @@ -0,0 +1,123 @@ + + + diff --git a/frontend/src/pages/Courses/CourseEnrollmentModal.vue b/frontend/src/pages/Courses/CourseEnrollmentModal.vue new file mode 100644 index 00000000..b7d2535a --- /dev/null +++ b/frontend/src/pages/Courses/CourseEnrollmentModal.vue @@ -0,0 +1,97 @@ + + diff --git a/frontend/src/pages/CourseForm.vue b/frontend/src/pages/Courses/CourseForm.vue similarity index 99% rename from frontend/src/pages/CourseForm.vue rename to frontend/src/pages/Courses/CourseForm.vue index 65a91ec4..efee464e 100644 --- a/frontend/src/pages/CourseForm.vue +++ b/frontend/src/pages/Courses/CourseForm.vue @@ -309,7 +309,7 @@ import { import { Trash2, X } from 'lucide-vue-next' import { useRouter } from 'vue-router' import { useOnboarding, useTelemetry } from 'frappe-ui/frappe' -import { sessionStore } from '../stores/session' +import { sessionStore } from '../../stores/session' import Link from '@/components/Controls/Link.vue' import CourseOutline from '@/components/CourseOutline.vue' import MultiSelect from '@/components/Controls/MultiSelect.vue' diff --git a/frontend/src/pages/Courses/CourseOverview.vue b/frontend/src/pages/Courses/CourseOverview.vue new file mode 100644 index 00000000..8a67a3eb --- /dev/null +++ b/frontend/src/pages/Courses/CourseOverview.vue @@ -0,0 +1,102 @@ + + diff --git a/frontend/src/pages/Courses.vue b/frontend/src/pages/Courses/Courses.vue similarity index 99% rename from frontend/src/pages/Courses.vue rename to frontend/src/pages/Courses/Courses.vue index 48fc9200..ef59c9ba 100644 --- a/frontend/src/pages/Courses.vue +++ b/frontend/src/pages/Courses/Courses.vue @@ -128,7 +128,7 @@ import { sessionStore } from '@/stores/session' import { canCreateCourse } from '@/utils' import CourseCard from '@/components/CourseCard.vue' import EmptyState from '@/components/EmptyState.vue' -import router from '../router' +import { useRouter } from 'vue-router' const user = inject('$user') const dayjs = inject('$dayjs') @@ -142,6 +142,7 @@ const filters = ref({}) const currentTab = ref('Live') const { brand } = sessionStore() const courseCount = ref(0) +const router = useRouter() onMounted(() => { setFiltersFromQuery() diff --git a/frontend/src/router.js b/frontend/src/router.js index fe6b311d..24b64266 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -12,12 +12,12 @@ const routes = [ { path: '/courses', name: 'Courses', - component: () => import('@/pages/Courses.vue'), + component: () => import('@/pages/Courses/Courses.vue'), }, { path: '/courses/:courseName', name: 'CourseDetail', - component: () => import('@/pages/CourseDetail.vue'), + component: () => import('@/pages/Courses/CourseDetail.vue'), props: true, }, { @@ -29,7 +29,7 @@ const routes = [ { path: '/courses/:courseName/certification', name: 'CourseCertification', - component: () => import('@/pages/CourseCertification.vue'), + component: () => import('@/pages/Courses/CourseCertification.vue'), props: true, }, { @@ -121,7 +121,7 @@ const routes = [ { path: '/courses/:courseName/edit', name: 'CourseForm', - component: () => import('@/pages/CourseForm.vue'), + component: () => import('@/pages/Courses/CourseForm.vue'), props: true, }, { diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 3d08802b..e51a922f 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -5,6 +5,7 @@ import { VitePWA } from 'vite-plugin-pwa' export default defineConfig(async ({ mode }) => { const isDev = mode === 'development' + console.log(mode, isDev) const frappeui = await importFrappeUIPlugin(isDev) const config = { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c6f02e93..66c19b11 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -920,16 +920,16 @@ crelt "^1.0.5" "@codemirror/state@6.x", "@codemirror/state@^6.0.0", "@codemirror/state@^6.4.0", "@codemirror/state@^6.5.0": - version "6.5.3" - resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.5.3.tgz#256e256d466f49ed0879d462031de8bd541e1403" - integrity sha512-MerMzJzlXogk2fxWFU1nKp36bY5orBG59HnPiz0G9nLRebWa0zXuv2siH6PLIHBvv5TH8CkQRqjBs0MlxCZu+A== + version "6.5.4" + resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.5.4.tgz#f5be4b8c0d2310180d5f15a9f641c21ca69faf19" + integrity sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw== dependencies: "@marijn/find-cluster-break" "^1.0.0" "@codemirror/view@6.x", "@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.23.0", "@codemirror/view@^6.27.0", "@codemirror/view@^6.35.0", "@codemirror/view@^6.37.0": - version "6.39.10" - resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.39.10.tgz#ae0dfcb635fd307aa3b800e305c9f46152503dba" - integrity sha512-QfT/PXhiiP76PxMnX0RQVPDQrqfRt9wr9QhInNHnEUu4PWoNS8QwwcIDEneXFChJv22y+Yu/Cz5lFMTPz+h16w== + version "6.39.11" + resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.39.11.tgz#200aebef2074bfbbb7a3d5f0644c1b560d876b39" + integrity sha512-bWdeR8gWM87l4DB/kYSF9A+dVackzDb/V56Tq7QVrQ7rn86W0rgZFtlL3g3pem6AeGcb9NQNoy3ao4WpW4h5tQ== dependencies: "@codemirror/state" "^6.5.0" crelt "^1.0.6" @@ -1400,130 +1400,130 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@rollup/rollup-android-arm-eabi@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz#76e0fef6533b3ce313f969879e61e8f21f0eeb28" - integrity sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg== +"@rollup/rollup-android-arm-eabi@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.56.0.tgz#067cfcd81f1c1bfd92aefe3ad5ef1523549d5052" + integrity sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw== -"@rollup/rollup-android-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz#d3cfc675a40bbdec97bda6d7fe3b3b05f0e1cd93" - integrity sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg== +"@rollup/rollup-android-arm64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.56.0.tgz#85e39a44034d7d4e4fee2a1616f0bddb85a80517" + integrity sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q== -"@rollup/rollup-darwin-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz#eb912b8f59dd47c77b3c50a78489013b1d6772b4" - integrity sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg== +"@rollup/rollup-darwin-arm64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.56.0.tgz#17d92fe98f2cc277b91101eb1528b7c0b6c00c54" + integrity sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w== -"@rollup/rollup-darwin-x64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz#e7d0839fdfd1276a1d34bc5ebbbd0dfd7d0b81a0" - integrity sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ== +"@rollup/rollup-darwin-x64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.56.0.tgz#89ae6c66b1451609bd1f297da9384463f628437d" + integrity sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g== -"@rollup/rollup-freebsd-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz#7ff8118760f7351e48fd0cd3717ff80543d6aac8" - integrity sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg== +"@rollup/rollup-freebsd-arm64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.56.0.tgz#cdbdb9947b26e76c188a31238c10639347413628" + integrity sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ== -"@rollup/rollup-freebsd-x64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz#49d330dadbda1d4e9b86b4a3951b59928a9489a9" - integrity sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw== +"@rollup/rollup-freebsd-x64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.56.0.tgz#9b1458d07b6e040be16ee36d308a2c9520f7f7cc" + integrity sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg== -"@rollup/rollup-linux-arm-gnueabihf@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz#98c5f1f8b9776b4a36e466e2a1c9ed1ba52ef1b6" - integrity sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ== +"@rollup/rollup-linux-arm-gnueabihf@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.56.0.tgz#1d50ded7c965d5f125f5832c971ad5b287befef7" + integrity sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A== -"@rollup/rollup-linux-arm-musleabihf@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz#b9acecd3672e742f70b0c8a94075c816a91ff040" - integrity sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg== +"@rollup/rollup-linux-arm-musleabihf@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.56.0.tgz#53597e319b7e65990d3bc2a5048097384814c179" + integrity sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw== -"@rollup/rollup-linux-arm64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz#7a6ab06651bc29e18b09a50ed1a02bc972977c9b" - integrity sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ== +"@rollup/rollup-linux-arm64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.56.0.tgz#597002909dec198ca4bdccb25f043d32db3d6283" + integrity sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ== -"@rollup/rollup-linux-arm64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz#3c8c9072ba4a4d4ef1156b85ab9a2cbb57c1fad0" - integrity sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA== +"@rollup/rollup-linux-arm64-musl@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.56.0.tgz#286f0e0f799545ce288bdc5a7c777261fcba3d54" + integrity sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA== -"@rollup/rollup-linux-loong64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz#17a7af13530f4e4a7b12cd26276c54307a84a8b0" - integrity sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g== +"@rollup/rollup-linux-loong64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.56.0.tgz#1fab07fa1a4f8d3697735b996517f1bae0ba101b" + integrity sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg== -"@rollup/rollup-linux-loong64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz#5cd7a900fd7b077ecd753e34a9b7ff1157fe70c1" - integrity sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw== +"@rollup/rollup-linux-loong64-musl@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.56.0.tgz#efc2cb143d6c067f95205482afb177f78ed9ea3d" + integrity sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA== -"@rollup/rollup-linux-ppc64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz#03a097e70243ddf1c07b59d3c20f38e6f6800539" - integrity sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw== +"@rollup/rollup-linux-ppc64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.56.0.tgz#e8de8bd3463f96b92b7dfb7f151fd80ffe8a937c" + integrity sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw== -"@rollup/rollup-linux-ppc64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz#a5389873039d4650f35b4fa060d286392eb21a94" - integrity sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw== +"@rollup/rollup-linux-ppc64-musl@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.56.0.tgz#8c508fe28a239da83b3a9da75bcf093186e064b4" + integrity sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg== -"@rollup/rollup-linux-riscv64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz#789e60e7d6e2b76132d001ffb24ba80007fb17d0" - integrity sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw== +"@rollup/rollup-linux-riscv64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.56.0.tgz#ff6d51976e0830732880770a9e18553136b8d92b" + integrity sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew== -"@rollup/rollup-linux-riscv64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz#3556fa88d139282e9a73c337c9a170f3c5fe7aa4" - integrity sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg== +"@rollup/rollup-linux-riscv64-musl@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.56.0.tgz#325fb35eefc7e81d75478318f0deee1e4a111493" + integrity sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ== -"@rollup/rollup-linux-s390x-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz#c085995b10143c16747a67f1a5487512b2ff04b2" - integrity sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg== +"@rollup/rollup-linux-s390x-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.56.0.tgz#37410fabb5d3ba4ad34abcfbe9ba9b6288413f30" + integrity sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ== -"@rollup/rollup-linux-x64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz#9563a5419dd2604841bad31a39ccfdd2891690fb" - integrity sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg== +"@rollup/rollup-linux-x64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.56.0.tgz#8ef907a53b2042068fc03fcc6a641e2b02276eca" + integrity sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw== -"@rollup/rollup-linux-x64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz#691bb06e6269a8959c13476b0cd2aa7458facb31" - integrity sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w== +"@rollup/rollup-linux-x64-musl@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.56.0.tgz#61b9ba09ea219e0174b3f35a6ad2afc94bdd5662" + integrity sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA== -"@rollup/rollup-openbsd-x64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz#223e71224746a59ce6d955bbc403577bb5a8be9d" - integrity sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg== +"@rollup/rollup-openbsd-x64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.56.0.tgz#fc4e54133134c1787d0b016ffdd5aeb22a5effd3" + integrity sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA== -"@rollup/rollup-openharmony-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz#0817e5d8ecbfeb8b7939bf58f8ce3c9dd67fce77" - integrity sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw== +"@rollup/rollup-openharmony-arm64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.56.0.tgz#959ae225b1eeea0cc5b7c9f88e4834330fb6cd09" + integrity sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ== -"@rollup/rollup-win32-arm64-msvc@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz#de56d8f2013c84570ef5fb917aae034abda93e4a" - integrity sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g== +"@rollup/rollup-win32-arm64-msvc@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.56.0.tgz#842acd38869fa1cbdbc240c76c67a86f93444c27" + integrity sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing== -"@rollup/rollup-win32-ia32-msvc@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz#659aff5244312475aeea2c9479a6c7d397b517bf" - integrity sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA== +"@rollup/rollup-win32-ia32-msvc@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.56.0.tgz#7ab654def4042df44cb29f8ed9d5044e850c66d5" + integrity sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg== -"@rollup/rollup-win32-x64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz#2cb09549cbb66c1b979f9238db6dd454cac14a88" - integrity sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg== +"@rollup/rollup-win32-x64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.56.0.tgz#7426cdec1b01d2382ffd5cda83cbdd1c8efb3ca6" + integrity sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ== -"@rollup/rollup-win32-x64-msvc@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz#f79437939020b83057faf07e98365b1fa51c458b" - integrity sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw== +"@rollup/rollup-win32-x64-msvc@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.56.0.tgz#9eec0212732a432c71bde0350bc40b673d15b2db" + integrity sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g== "@socket.io/component-emitter@~3.1.0": version "3.1.2" @@ -1896,9 +1896,9 @@ integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== "@types/node@*": - version "25.0.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-25.0.8.tgz#e54e00f94fe1db2497b3e42d292b8376a2678c8d" - integrity sha512-powIePYMmC3ibL0UJ2i2s0WIbq6cg6UyVFQxSCpaPxxzAaziRfimGivjdF943sSGV6RADVbk0Nvlm5P/FB44Zg== + version "25.0.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.0.10.tgz#4864459c3c9459376b8b75fd051315071c8213e7" + integrity sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg== dependencies: undici-types "~7.16.0" @@ -1919,11 +1919,6 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== -"@types/web-bluetooth@^0.0.17": - version "0.0.17" - resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz#5c9f3c617f64a9735d7b72a7cc671e166d900c40" - integrity sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA== - "@types/web-bluetooth@^0.0.20": version "0.0.20" resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597" @@ -1953,100 +1948,90 @@ resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz#164b36653910d27c130cf6c945b4bd9bde5bcbee" integrity sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA== -"@vue/compiler-core@3.5.26": - version "3.5.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.26.tgz#1a91ea90980528bedff7b1c292690bfb30612485" - integrity sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w== +"@vue/compiler-core@3.5.27": + version "3.5.27" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.27.tgz#ce4402428e26095586eb889c41f6e172eb3960bd" + integrity sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ== dependencies: "@babel/parser" "^7.28.5" - "@vue/shared" "3.5.26" + "@vue/shared" "3.5.27" entities "^7.0.0" estree-walker "^2.0.2" source-map-js "^1.2.1" -"@vue/compiler-dom@3.5.26": - version "3.5.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.26.tgz#66c36b6ed8bdf43236d7188ea332bc9d078eb286" - integrity sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A== +"@vue/compiler-dom@3.5.27": + version "3.5.27" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.27.tgz#32b2bc87f0a652c253986796ace0ed6213093af8" + integrity sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w== dependencies: - "@vue/compiler-core" "3.5.26" - "@vue/shared" "3.5.26" + "@vue/compiler-core" "3.5.27" + "@vue/shared" "3.5.27" -"@vue/compiler-sfc@3.5.26": - version "3.5.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz#fb1c6c4bf9a9e22bb169e039e19437cb6995917a" - integrity sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA== +"@vue/compiler-sfc@3.5.27": + version "3.5.27" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.27.tgz#84651b8816bf8e7d6e62fddd14db86efd6d6f1b6" + integrity sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ== dependencies: "@babel/parser" "^7.28.5" - "@vue/compiler-core" "3.5.26" - "@vue/compiler-dom" "3.5.26" - "@vue/compiler-ssr" "3.5.26" - "@vue/shared" "3.5.26" + "@vue/compiler-core" "3.5.27" + "@vue/compiler-dom" "3.5.27" + "@vue/compiler-ssr" "3.5.27" + "@vue/shared" "3.5.27" estree-walker "^2.0.2" magic-string "^0.30.21" postcss "^8.5.6" source-map-js "^1.2.1" -"@vue/compiler-ssr@3.5.26": - version "3.5.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz#f6e94bccbb5339180779036ddfb614f998a197ea" - integrity sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw== +"@vue/compiler-ssr@3.5.27": + version "3.5.27" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.27.tgz#b480cad09dacf8f3d9c82b9843402f1a803baee7" + integrity sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw== dependencies: - "@vue/compiler-dom" "3.5.26" - "@vue/shared" "3.5.26" + "@vue/compiler-dom" "3.5.27" + "@vue/shared" "3.5.27" -"@vue/devtools-api@^6.5.0": +"@vue/devtools-api@^6.5.0", "@vue/devtools-api@^6.6.4": version "6.6.4" resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343" integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g== -"@vue/reactivity@3.5.26": - version "3.5.26" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.26.tgz#59a1edf566dc80133c1c26c93711c877e8602c48" - integrity sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ== +"@vue/reactivity@3.5.27": + version "3.5.27" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.27.tgz#d870557de1389a27b8abcb7cbfa30978dc69a000" + integrity sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ== dependencies: - "@vue/shared" "3.5.26" + "@vue/shared" "3.5.27" -"@vue/runtime-core@3.5.26": - version "3.5.26" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.26.tgz#3f2c040bcf8018c03a1ab5adb0d788c13c986f0e" - integrity sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q== +"@vue/runtime-core@3.5.27": + version "3.5.27" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.27.tgz#bb43744ed070166c7d581b849ac22b71a9ccf127" + integrity sha512-fxVuX/fzgzeMPn/CLQecWeDIFNt3gQVhxM0rW02Tvp/YmZfXQgcTXlakq7IMutuZ/+Ogbn+K0oct9J3JZfyk3A== dependencies: - "@vue/reactivity" "3.5.26" - "@vue/shared" "3.5.26" + "@vue/reactivity" "3.5.27" + "@vue/shared" "3.5.27" -"@vue/runtime-dom@3.5.26": - version "3.5.26" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.26.tgz#5954848614883948ecc1f631a67b32cc32f81936" - integrity sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ== +"@vue/runtime-dom@3.5.27": + version "3.5.27" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.27.tgz#392513252c7ca7e5277240fdc70b8093449127f5" + integrity sha512-/QnLslQgYqSJ5aUmb5F0z0caZPGHRB8LEAQ1s81vHFM5CBfnun63rxhvE/scVb/j3TbBuoZwkJyiLCkBluMpeg== dependencies: - "@vue/reactivity" "3.5.26" - "@vue/runtime-core" "3.5.26" - "@vue/shared" "3.5.26" + "@vue/reactivity" "3.5.27" + "@vue/runtime-core" "3.5.27" + "@vue/shared" "3.5.27" csstype "^3.2.3" -"@vue/server-renderer@3.5.26": - version "3.5.26" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.26.tgz#269055497fcc75b3984063f866f17c748b565ef4" - integrity sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA== +"@vue/server-renderer@3.5.27": + version "3.5.27" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.27.tgz#8137d0d7ec3b59d5992bb04c553775d209dddba7" + integrity sha512-qOz/5thjeP1vAFc4+BY3Nr6wxyLhpeQgAE/8dDtKo6a6xdk+L4W46HDZgNmLOBUDEkFXV3G7pRiUqxjX0/2zWA== dependencies: - "@vue/compiler-ssr" "3.5.26" - "@vue/shared" "3.5.26" + "@vue/compiler-ssr" "3.5.27" + "@vue/shared" "3.5.27" -"@vue/shared@3.5.26": - version "3.5.26" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.26.tgz#1e02ef2d64aced818cd31d81ce5175711dc90a9f" - integrity sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A== - -"@vueuse/core@10.4.1": - version "10.4.1" - resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.4.1.tgz#fc2c8a83a571c207aaedbe393b22daa6d35123f2" - integrity sha512-DkHIfMIoSIBjMgRRvdIvxsyboRZQmImofLyOHADqiVbQVilP8VVHDhBX2ZqoItOgu7dWa8oXiNnScOdPLhdEXg== - dependencies: - "@types/web-bluetooth" "^0.0.17" - "@vueuse/metadata" "10.4.1" - "@vueuse/shared" "10.4.1" - vue-demi ">=0.14.5" +"@vue/shared@3.5.27": + version "3.5.27" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.27.tgz#33a63143d8fb9ca1b3efbc7ecf9bd0ab05f7e06e" + integrity sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ== "@vueuse/core@^10.11.0", "@vueuse/core@^10.4.1": version "10.11.1" @@ -2068,28 +2053,29 @@ "@vueuse/shared" "12.8.2" vue "^3.5.13" +"@vueuse/core@^14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-14.1.0.tgz#274e98e591a505333b7dfb2bcaf7b4530a10b9c9" + integrity sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw== + dependencies: + "@types/web-bluetooth" "^0.0.21" + "@vueuse/metadata" "14.1.0" + "@vueuse/shared" "14.1.0" + "@vueuse/metadata@10.11.1": version "10.11.1" resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.11.1.tgz#209db7bb5915aa172a87510b6de2ca01cadbd2a7" integrity sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw== -"@vueuse/metadata@10.4.1": - version "10.4.1" - resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.4.1.tgz#9d2ff5c67abf17a8c07865c2413fbd0e92f7b7d7" - integrity sha512-2Sc8X+iVzeuMGHr6O2j4gv/zxvQGGOYETYXEc41h0iZXIRnRbJZGmY/QP8dvzqUelf8vg0p/yEA5VpCEu+WpZg== - "@vueuse/metadata@12.8.2": version "12.8.2" resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-12.8.2.tgz#6cb3a4e97cdcf528329eebc1bda73cd7f64318d3" integrity sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A== -"@vueuse/router@12.7.0": - version "12.7.0" - resolved "https://registry.yarnpkg.com/@vueuse/router/-/router-12.7.0.tgz#b349b66e337057bb489b6d64d2dab044d41ca74d" - integrity sha512-Jp6dIel54oc2nh++zqjY06ipCcTT6YWDCNQ8dSSnqRwx90wIl7w7MQP7Wpp1wrDwXEoqhelfeZf2gjfrkAhq3g== - dependencies: - "@vueuse/shared" "12.7.0" - vue "^3.5.13" +"@vueuse/metadata@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-14.1.0.tgz#70fc2e94775e4a07369f11f86f6f0a465b04a381" + integrity sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA== "@vueuse/shared@10.11.1", "@vueuse/shared@^10.11.0": version "10.11.1" @@ -2098,20 +2084,6 @@ dependencies: vue-demi ">=0.14.8" -"@vueuse/shared@10.4.1": - version "10.4.1" - resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-10.4.1.tgz#d5ce33033c156efb60664b5d6034d6cd4e2f530c" - integrity sha512-vz5hbAM4qA0lDKmcr2y3pPdU+2EVw/yzfRsBdu+6+USGa4PxqSQRYIUC9/NcT06y+ZgaTsyURw2I9qOFaaXHAg== - dependencies: - vue-demi ">=0.14.5" - -"@vueuse/shared@12.7.0": - version "12.7.0" - resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-12.7.0.tgz#0c573789069818a2e25ddae3ab64b536c614537b" - integrity sha512-coLlUw2HHKsm7rPN6WqHJQr18WymN4wkA/3ThFaJ4v4gWGWAQQGK+MJxLuJTBs4mojQiazlVWAKNJNpUWGRkNw== - dependencies: - vue "^3.5.13" - "@vueuse/shared@12.8.2", "@vueuse/shared@^12.5.0": version "12.8.2" resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-12.8.2.tgz#b9e4611d0603629c8e151f982459da394e22f930" @@ -2119,6 +2091,11 @@ dependencies: vue "^3.5.13" +"@vueuse/shared@14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-14.1.0.tgz#49b2face86a9c0c52e20eaf4c732a0223276c11f" + integrity sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw== + "@yr/monotone-cubic-spline@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz#7272d89f8e4f6fb7a1600c28c378cc18d3b577b9" @@ -2288,9 +2265,9 @@ base64-js@^1.3.1: integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== baseline-browser-mapping@^2.9.0: - version "2.9.14" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz#3b6af0bc032445bca04de58caa9a87cfe921cbb3" - integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== + version "2.9.17" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz#9d6019766cd7eba738cb5f32c84b9f937cc87780" + integrity sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ== binary-extensions@^2.0.0: version "2.3.0" @@ -2328,7 +2305,7 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.19.1, browserslist@^4.24.0, browserslist@^4.28.0: +browserslist@^4.19.1, browserslist@^4.24.0, browserslist@^4.28.1: version "4.28.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== @@ -2389,9 +2366,9 @@ camelcase-css@^2.0.1: integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== caniuse-lite@^1.0.30001297, caniuse-lite@^1.0.30001759: - version "1.0.30001764" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz#03206c56469f236103b90f9ae10bcb8b9e1f6005" - integrity sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g== + version "1.0.30001765" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz#4a78d8a797fd4124ebaab2043df942eb091648ee" + integrity sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ== chalk@^4.1.0: version "4.1.2" @@ -2506,16 +2483,16 @@ convert-source-map@^2.0.0: integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== core-js-compat@^3.43.0: - version "3.47.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.47.0.tgz#698224bbdbb6f2e3f39decdda4147b161e3772a3" - integrity sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ== + version "3.48.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.48.0.tgz#7efbe1fc1cbad44008190462217cc5558adaeaa6" + integrity sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q== dependencies: - browserslist "^4.28.0" + browserslist "^4.28.1" core-js@^3.1.3, core-js@^3.26.1: - version "3.47.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.47.0.tgz#436ef07650e191afeb84c24481b298bd60eb4a17" - integrity sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg== + version "3.48.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.48.0.tgz#1f813220a47bbf0e667e3885c36cd6f0593bf14d" + integrity sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ== crelt@^1.0.0, crelt@^1.0.5, crelt@^1.0.6: version "1.0.6" @@ -2689,9 +2666,9 @@ ejs@^3.1.6: jake "^10.8.5" electron-to-chromium@^1.5.263: - version "1.5.267" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" - integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw== + version "1.5.277" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.277.tgz#7164191a07bf32a7e646e68334f402dd60629821" + integrity sha512-wKXFZw4erWmmOz5N/grBoJ2XrNJGDFMu2+W5ACHza5rHtvsqrK4gb6rnLC7XxKB9WlJ+RmyQatuEXmtm86xbnw== engine.io-client@~6.5.2: version "6.5.4" @@ -2726,9 +2703,9 @@ entities@^4.4.0: integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== entities@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-7.0.0.tgz#2ae4e443f3f17d152d3f5b0f79b932c1e59deb7a" - integrity sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ== + version "7.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-7.0.1.tgz#26e8a88889db63417dcb9a1e79a3f1bc92b5976b" + integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA== es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9: version "1.24.1" @@ -2977,10 +2954,10 @@ fraction.js@^4.1.2: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== -frappe-ui@^0.1.256: - version "0.1.256" - resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.1.256.tgz#c14756eda75ca01ada034559e8bd2f91bcfe6dff" - integrity sha512-zj8n6KXpMv/0h1NcaCsjFLP8QBnofDEBJgQa+xECU0/jbq4gSqNhFOkcx788qNL+vmBo9frywTeXwDpl7hUCZA== +frappe-ui@^0.1.261: + version "0.1.261" + resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.1.261.tgz#d6919c713a37ed8a2bdb667707dba9ece4956c6d" + integrity sha512-sEdEAgjAkrTERYWk5HBOQuKa7/xuex/X8/Y/hCYFbEThwwy2ZWmQOCsTNyOCjXAn7lyV49Ues/TW01koIq/ysQ== dependencies: "@floating-ui/vue" "^1.1.6" "@headlessui/vue" "^1.7.14" @@ -3613,9 +3590,9 @@ lodash.sortby@^4.7.0: integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== lodash@^4.17.20: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + version "4.17.23" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" + integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== log-symbols@^4.1.0: version "4.1.0" @@ -4032,9 +4009,9 @@ postcss@^8.4.32, postcss@^8.4.47, postcss@^8.5.6: source-map-js "^1.2.1" prettier@^3.3.2: - version "3.7.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f" - integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA== + version "3.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== pretty-bytes@^5.3.0: version "5.6.0" @@ -4115,9 +4092,9 @@ prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.2.2, prosemirror-keymap@^1.2.3: w3c-keyname "^2.2.0" prosemirror-markdown@^1.13.1: - version "1.13.2" - resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.13.2.tgz#863eb3fd5f57a444e4378174622b562735b1c503" - integrity sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g== + version "1.13.3" + resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.13.3.tgz#cf38e98f10c432b906bfcc7179c2e3ab58f49362" + integrity sha512-3E+Et6cdXIH0EgN2tGYQ+EBT7N4kMiZFsW+hzx+aPtOmADDHWCdd2uUQb7yklJrfUYUOjEEu22BiN6UFgPe4cQ== dependencies: "@types/markdown-it" "^14.0.0" markdown-it "^14.0.0" @@ -4185,16 +4162,16 @@ prosemirror-trailing-node@^3.0.0: escape-string-regexp "^4.0.0" prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.10.2, prosemirror-transform@^1.10.5, prosemirror-transform@^1.7.3: - version "1.10.5" - resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.10.5.tgz#4cf9fe5dcbdbfebd62499f24386e7cec9bc9979b" - integrity sha512-RPDQCxIDhIBb1o36xxwsaeAvivO8VLJcgBtzmOwQ64bMtsVFh5SSuJ6dWSxO1UsHTiTXPCgQm3PDJt7p6IOLbw== + version "1.11.0" + resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz#f5c5050354423dc83c6b083f6f1959ec86a3f9ba" + integrity sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw== dependencies: prosemirror-model "^1.21.0" prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.37.0, prosemirror-view@^1.41.4: - version "1.41.4" - resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.41.4.tgz#4e1b3e90accc0eebe3bddb497a40ce54e4de722d" - integrity sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA== + version "1.41.5" + resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.41.5.tgz#3e152d14af633f2f5a73aba24e6130c63f643b2b" + integrity sha512-UDQbIPnDrjE8tqUBbPmCOZgtd75htE6W3r0JCmY9bL6W1iemDM37MZEKC49d+tdQ0v/CKx4gjxLoLsfkD2NiZA== dependencies: prosemirror-model "^1.20.0" prosemirror-state "^1.0.0" @@ -4395,37 +4372,37 @@ rollup@^2.43.1: fsevents "~2.3.2" rollup@^4.2.0: - version "4.55.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.55.1.tgz#4ec182828be440648e7ee6520dc35e9f20e05144" - integrity sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A== + version "4.56.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.56.0.tgz#65959d13cfbd7e48b8868c05165b1738f0143862" + integrity sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg== dependencies: "@types/estree" "1.0.8" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.55.1" - "@rollup/rollup-android-arm64" "4.55.1" - "@rollup/rollup-darwin-arm64" "4.55.1" - "@rollup/rollup-darwin-x64" "4.55.1" - "@rollup/rollup-freebsd-arm64" "4.55.1" - "@rollup/rollup-freebsd-x64" "4.55.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.55.1" - "@rollup/rollup-linux-arm-musleabihf" "4.55.1" - "@rollup/rollup-linux-arm64-gnu" "4.55.1" - "@rollup/rollup-linux-arm64-musl" "4.55.1" - "@rollup/rollup-linux-loong64-gnu" "4.55.1" - "@rollup/rollup-linux-loong64-musl" "4.55.1" - "@rollup/rollup-linux-ppc64-gnu" "4.55.1" - "@rollup/rollup-linux-ppc64-musl" "4.55.1" - "@rollup/rollup-linux-riscv64-gnu" "4.55.1" - "@rollup/rollup-linux-riscv64-musl" "4.55.1" - "@rollup/rollup-linux-s390x-gnu" "4.55.1" - "@rollup/rollup-linux-x64-gnu" "4.55.1" - "@rollup/rollup-linux-x64-musl" "4.55.1" - "@rollup/rollup-openbsd-x64" "4.55.1" - "@rollup/rollup-openharmony-arm64" "4.55.1" - "@rollup/rollup-win32-arm64-msvc" "4.55.1" - "@rollup/rollup-win32-ia32-msvc" "4.55.1" - "@rollup/rollup-win32-x64-gnu" "4.55.1" - "@rollup/rollup-win32-x64-msvc" "4.55.1" + "@rollup/rollup-android-arm-eabi" "4.56.0" + "@rollup/rollup-android-arm64" "4.56.0" + "@rollup/rollup-darwin-arm64" "4.56.0" + "@rollup/rollup-darwin-x64" "4.56.0" + "@rollup/rollup-freebsd-arm64" "4.56.0" + "@rollup/rollup-freebsd-x64" "4.56.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.56.0" + "@rollup/rollup-linux-arm-musleabihf" "4.56.0" + "@rollup/rollup-linux-arm64-gnu" "4.56.0" + "@rollup/rollup-linux-arm64-musl" "4.56.0" + "@rollup/rollup-linux-loong64-gnu" "4.56.0" + "@rollup/rollup-linux-loong64-musl" "4.56.0" + "@rollup/rollup-linux-ppc64-gnu" "4.56.0" + "@rollup/rollup-linux-ppc64-musl" "4.56.0" + "@rollup/rollup-linux-riscv64-gnu" "4.56.0" + "@rollup/rollup-linux-riscv64-musl" "4.56.0" + "@rollup/rollup-linux-s390x-gnu" "4.56.0" + "@rollup/rollup-linux-x64-gnu" "4.56.0" + "@rollup/rollup-linux-x64-musl" "4.56.0" + "@rollup/rollup-openbsd-x64" "4.56.0" + "@rollup/rollup-openharmony-arm64" "4.56.0" + "@rollup/rollup-win32-arm64-msvc" "4.56.0" + "@rollup/rollup-win32-ia32-msvc" "4.56.0" + "@rollup/rollup-win32-x64-gnu" "4.56.0" + "@rollup/rollup-win32-x64-msvc" "4.56.0" fsevents "~2.3.2" rope-sequence@^1.3.0: @@ -4797,9 +4774,9 @@ tempy@^0.6.0: unique-string "^2.0.0" terser@^5.0.0: - version "5.44.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.44.1.tgz#e391e92175c299b8c284ad6ded609e37303b0a9c" - integrity sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw== + version "5.46.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.46.0.tgz#1b81e560d584bbdd74a8ede87b4d9477b0ff9695" + integrity sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.15.0" @@ -4940,9 +4917,9 @@ uc.micro@^2.0.0, uc.micro@^2.1.0: integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== ufo@^1.6.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.6.2.tgz#aaf4d46b98425b2fb5031abe8d65ca069e93e755" - integrity sha512-heMioaxBcG9+Znsda5Q8sQbWnLJSl98AFDXTO80wELWEzX3hordXsTdxrIfMQoO9IY1MEnoGoPjpoKpMj+Yx0Q== + version "1.6.3" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.6.3.tgz#799666e4e88c122a9659805e30b9dc071c3aed4f" + integrity sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q== unbox-primitive@^1.1.0: version "1.1.0" @@ -5169,7 +5146,7 @@ vue-codemirror@6.1.1: "@codemirror/state" "6.x" "@codemirror/view" "6.x" -vue-demi@*, vue-demi@>=0.13.0, vue-demi@>=0.14.5, vue-demi@>=0.14.8: +vue-demi@*, vue-demi@>=0.13.0, vue-demi@>=0.14.8: version "0.14.10" resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04" integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg== @@ -5179,28 +5156,28 @@ vue-draggable-next@2.2.1: resolved "https://registry.yarnpkg.com/vue-draggable-next/-/vue-draggable-next-2.2.1.tgz#adbe98c74610cca8f4eb63f92042681f96920451" integrity sha512-EAMS1IRHF0kZO0o5PMOinsQsXIqsrKT1hKmbICxG3UEtn7zLFkLxlAtajcCcUTisNvQ6TtCB5COjD9a1raNADw== -vue-router@4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.2.2.tgz#b0097b66d89ca81c0986be03da244c7b32a4fd81" - integrity sha512-cChBPPmAflgBGmy3tBsjeoe3f3VOSG6naKyY5pjtrqLGbNEXdzCigFUHgBvp9e3ysAtFtEx7OLqcSDh/1Cq2TQ== +vue-router@^4.6.4: + version "4.6.4" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.6.4.tgz#a0a9cb9ef811a106d249e4bb9313d286718020d8" + integrity sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg== dependencies: - "@vue/devtools-api" "^6.5.0" + "@vue/devtools-api" "^6.6.4" vue3-apexcharts@1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/vue3-apexcharts/-/vue3-apexcharts-1.8.0.tgz#1984648d966aa91bc4dc3e87fa847f5289f7f1cf" integrity sha512-5tSD4mXTBbIJ9ir+58qHE6oNtIe0RNgqIRYMKpcsIaxkKtwUww4JhvPkpUFlmiW4OJbbdklgjleXq1lfcM4gdA== -vue@^3.5.0, vue@^3.5.13: - version "3.5.26" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.26.tgz#03a0b17311e0e593d34b9358fa249b85e3a6d9fb" - integrity sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA== +vue@^3.5.13, vue@^3.5.27: + version "3.5.27" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.27.tgz#e55fd941b614459ab2228489bc19d1692e05876c" + integrity sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw== dependencies: - "@vue/compiler-dom" "3.5.26" - "@vue/compiler-sfc" "3.5.26" - "@vue/runtime-dom" "3.5.26" - "@vue/server-renderer" "3.5.26" - "@vue/shared" "3.5.26" + "@vue/compiler-dom" "3.5.27" + "@vue/compiler-sfc" "3.5.27" + "@vue/runtime-dom" "3.5.27" + "@vue/server-renderer" "3.5.27" + "@vue/shared" "3.5.27" vuedraggable@4.1.0: version "4.1.0" @@ -5281,9 +5258,9 @@ which-collection@^1.0.2: is-weakset "^2.0.3" which-typed-array@^1.1.16, which-typed-array@^1.1.19: - version "1.1.19" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" - integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + version "1.1.20" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.20.tgz#3fdb7adfafe0ea69157b1509f3a1cd892bd1d122" + integrity sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg== dependencies: available-typed-arrays "^1.0.7" call-bind "^1.0.8" diff --git a/lms/lms/api.py b/lms/lms/api.py index d3f55c17..d51bf0ef 100644 --- a/lms/lms/api.py +++ b/lms/lms/api.py @@ -1640,24 +1640,16 @@ def get_average_course_progress(progress_list): def get_progress_distribution(progressList): distribution = [ { - "category": "0-20%", - "count": len([p for p in progressList if 0 <= p < 20]), + "name": "Just Started (0-30%)", + "value": len([p for p in progressList if 0 <= p < 30]), }, { - "category": "20-40%", - "count": len([p for p in progressList if 20 <= p < 40]), + "name": "In Progress (30-60%)", + "value": len([p for p in progressList if 30 <= p < 60]), }, { - "category": "40-60%", - "count": len([p for p in progressList if 40 <= p < 60]), - }, - { - "category": "60-80%", - "count": len([p for p in progressList if 60 <= p < 80]), - }, - { - "category": "80-100%", - "count": len([p for p in progressList if 80 <= p <= 100]), + "name": "Advanced (60-100%)", + "value": len([p for p in progressList if 60 <= p <= 100]), }, ] diff --git a/lms/lms/doctype/lms_enrollment/lms_enrollment.py b/lms/lms/doctype/lms_enrollment/lms_enrollment.py index 77aa7eef..9145f72e 100644 --- a/lms/lms/doctype/lms_enrollment/lms_enrollment.py +++ b/lms/lms/doctype/lms_enrollment/lms_enrollment.py @@ -9,11 +9,24 @@ from frappe.utils import ceil class LMSEnrollment(Document): def before_insert(self): + self.validate_duplicate_enrollment() self.validate_course_enrollment_eligibility() def on_update(self): update_program_progress(self.member) + def validate_duplicate_enrollment(self): + existing_enrollment = frappe.db.exists( + "LMS Enrollment", + { + "course": self.course, + "member": self.member, + }, + ) + + if existing_enrollment: + frappe.throw(_("Student is already enrolled in this course.")) + def validate_course_enrollment_eligibility(self): course_details = frappe.db.get_value( "LMS Course", diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 814ddcc7..00000000 --- a/yarn.lock +++ /dev/null @@ -1,1374 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@cypress/request@^3.0.9": - version "3.0.9" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.9.tgz#8ed6e08fea0c62998b5552301023af7268f11625" - integrity sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~4.0.4" - http-signature "~1.4.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - performance-now "^2.1.0" - qs "6.14.0" - safe-buffer "^5.1.2" - tough-cookie "^5.0.0" - tunnel-agent "^0.6.0" - uuid "^8.3.2" - -"@cypress/xvfb@^1.2.4": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" - integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== - dependencies: - debug "^3.1.0" - lodash.once "^4.1.1" - -"@types/node@*": - version "25.0.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-25.0.3.tgz#79b9ac8318f373fbfaaf6e2784893efa9701f269" - integrity sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA== - dependencies: - undici-types "~7.16.0" - -"@types/sinonjs__fake-timers@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" - integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== - -"@types/sizzle@^2.3.2": - version "2.3.10" - resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.10.tgz#277a542aff6776d8a9b15f2ac682a663e3e94bbd" - integrity sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww== - -"@types/yauzl@^2.9.1": - version "2.10.3" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" - integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== - dependencies: - "@types/node" "*" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ansi-colors@^4.1.1: - version "4.1.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" - integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== - -ansi-escapes@^4.3.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -arch@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" - integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== - -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - -async@^3.2.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" - integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - -aws4@^1.8.0: - version "1.13.2" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" - integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== - -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - -blob-util@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" - integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== - -bluebird@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -cachedir@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.4.0.tgz#7fef9cf7367233d7c88068fe6e34ed0d355a610d" - integrity sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ== - -call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" - integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - -call-bound@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" - integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== - dependencies: - call-bind-apply-helpers "^1.0.2" - get-intrinsic "^1.3.0" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -check-more-types@^2.24.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" - integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== - -ci-info@^4.1.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.3.1.tgz#355ad571920810b5623e11d40232f443f16f1daa" - integrity sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA== - -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-table3@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.1.tgz#36ce9b7af4847f288d3cdd081fbd09bf7bd237b8" - integrity sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA== - dependencies: - string-width "^4.2.0" - optionalDependencies: - colors "1.4.0" - -cli-truncate@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -colorette@^2.0.16: - version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" - integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== - -colors@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -combined-stream@^1.0.8, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" - integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== - -common-tags@^1.8.0: - version "1.8.2" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" - integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== - -concat-stream@^1.4.7: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.0: - version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -cypress-file-upload@^5.0.8: - version "5.0.8" - resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1" - integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g== - -cypress-real-events@^1.14.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.15.0.tgz#b84ed97455238139ac3d0c8f803991b30f22fc8a" - integrity sha512-in98xxTnnM9Z7lZBvvVozm99PBT2eEOjXRG5LKWyYvQnj9mGWXMiPNpfw7e7WiraBFh7XlXIxnE9Cu5o+52kQQ== - -cypress@^14.5.4: - version "14.5.4" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-14.5.4.tgz#d821fbb6220c3328e7413acc7724b75319c9e64d" - integrity sha512-0Dhm4qc9VatOcI1GiFGVt8osgpPdqJLHzRwcAB5MSD/CAAts3oybvPUPawHyvJZUd8osADqZe/xzMsZ8sDTjXw== - dependencies: - "@cypress/request" "^3.0.9" - "@cypress/xvfb" "^1.2.4" - "@types/sinonjs__fake-timers" "8.1.1" - "@types/sizzle" "^2.3.2" - arch "^2.2.0" - blob-util "^2.0.2" - bluebird "^3.7.2" - buffer "^5.7.1" - cachedir "^2.3.0" - chalk "^4.1.0" - check-more-types "^2.24.0" - ci-info "^4.1.0" - cli-cursor "^3.1.0" - cli-table3 "0.6.1" - commander "^6.2.1" - common-tags "^1.8.0" - dayjs "^1.10.4" - debug "^4.3.4" - enquirer "^2.3.6" - eventemitter2 "6.4.7" - execa "4.1.0" - executable "^4.1.1" - extract-zip "2.0.1" - figures "^3.2.0" - fs-extra "^9.1.0" - getos "^3.2.1" - hasha "5.2.2" - is-installed-globally "~0.4.0" - lazy-ass "^1.6.0" - listr2 "^3.8.3" - lodash "^4.17.21" - log-symbols "^4.0.0" - minimist "^1.2.8" - ospath "^1.2.2" - pretty-bytes "^5.6.0" - process "^0.11.10" - proxy-from-env "1.0.0" - request-progress "^3.0.0" - semver "^7.7.1" - supports-color "^8.1.1" - tmp "~0.2.3" - tree-kill "1.2.2" - untildify "^4.0.0" - yauzl "^2.10.0" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - -dayjs@^1.10.4: - version "1.11.19" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.19.tgz#15dc98e854bb43917f12021806af897c58ae2938" - integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== - -debug@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.1, debug@^4.3.4: - version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -dunder-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" - integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== - dependencies: - call-bind-apply-helpers "^1.0.1" - es-errors "^1.3.0" - gopd "^1.2.0" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -end-of-stream@^1.1.0: - version "1.4.5" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" - integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== - dependencies: - once "^1.4.0" - -enquirer@^2.3.6: - version "2.4.1" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" - integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== - dependencies: - ansi-colors "^4.1.1" - strip-ansi "^6.0.1" - -es-define-property@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" - integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" - integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== - dependencies: - es-errors "^1.3.0" - -es-set-tostringtag@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" - integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== - dependencies: - es-errors "^1.3.0" - get-intrinsic "^1.2.6" - has-tostringtag "^1.0.2" - hasown "^2.0.2" - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -eventemitter2@6.4.7: - version "6.4.7" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" - integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== - -execa@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - -executable@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extract-zip@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== - dependencies: - pend "~1.2.0" - -figures@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - -form-data@~4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" - integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - es-set-tostringtag "^2.1.0" - hasown "^2.0.2" - mime-types "^2.1.12" - -fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" - integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== - dependencies: - call-bind-apply-helpers "^1.0.2" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - function-bind "^1.1.2" - get-proto "^1.0.1" - gopd "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - math-intrinsics "^1.1.0" - -get-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" - integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== - dependencies: - dunder-proto "^1.0.1" - es-object-atoms "^1.0.0" - -get-stream@^5.0.0, get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -getos@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" - integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== - dependencies: - async "^3.2.0" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - -global-dirs@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" - integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== - dependencies: - ini "2.0.0" - -gopd@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" - integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== - -graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-symbols@^1.0.3, has-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" - integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== - -has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -hasha@5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" - integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ== - dependencies: - is-stream "^2.0.0" - type-fest "^0.8.0" - -hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -http-signature@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.4.0.tgz#dee5a9ba2bf49416abc544abd6d967f6a94c8c3f" - integrity sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg== - dependencies: - assert-plus "^1.0.0" - jsprim "^2.0.2" - sshpk "^1.18.0" - -human-signals@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - -ieee754@^1.1.13: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-installed-globally@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" - integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== - dependencies: - global-dirs "^3.0.0" - is-path-inside "^3.0.2" - -is-path-inside@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== - -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - -jsonfile@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62" - integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -jsprim@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" - integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - -lazy-ass@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" - integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== - -listr2@^3.8.3: - version "3.14.0" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" - integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== - dependencies: - cli-truncate "^2.1.0" - colorette "^2.0.16" - log-update "^4.0.0" - p-map "^4.0.0" - rfdc "^1.3.0" - rxjs "^7.5.1" - through "^2.3.8" - wrap-ansi "^7.0.0" - -lodash.once@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== - -lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -log-update@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" - integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== - dependencies: - ansi-escapes "^4.3.0" - cli-cursor "^3.1.0" - slice-ansi "^4.0.0" - wrap-ansi "^6.2.0" - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -math-intrinsics@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" - integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -minimist@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -ms@^2.1.1, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -npm-run-path@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -object-inspect@^1.13.3: - version "1.13.4" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" - integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== - -once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -os-shim@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" - integrity sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A== - -ospath@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" - integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA== - -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - -pify@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - -pre-commit@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.2.2.tgz#dbcee0ee9de7235e57f79c56d7ce94641a69eec6" - integrity sha512-qokTiqxD6GjODy5ETAIgzsRgnBWWQHQH2ghy86PU7mIn/wuWeTwF3otyNQZxWBwVn8XNr8Tdzj/QfUXpH+gRZA== - dependencies: - cross-spawn "^5.0.1" - spawn-sync "^1.0.15" - which "1.2.x" - -pretty-bytes@^5.6.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" - integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== - -proxy-from-env@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" - integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== - -pump@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" - integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -qs@6.14.0: - version "6.14.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" - integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== - dependencies: - side-channel "^1.1.0" - -readable-stream@^2.2.2: - version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -request-progress@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" - integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg== - dependencies: - throttleit "^1.0.0" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -rfdc@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" - integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== - -rxjs@^7.5.1: - version "7.8.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" - integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== - dependencies: - tslib "^2.1.0" - -safe-buffer@^5.0.1, safe-buffer@^5.1.2: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver@^7.7.1: - version "7.7.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" - integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== - dependencies: - shebang-regex "^1.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel-list@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" - integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - -side-channel-map@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" - integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - -side-channel-weakmap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" - integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" - side-channel-map "^1.0.1" - -side-channel@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" - integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== - dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" - side-channel-list "^1.0.0" - side-channel-map "^1.0.1" - side-channel-weakmap "^1.0.2" - -signal-exit@^3.0.2: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -spawn-sync@^1.0.15: - version "1.0.15" - resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" - integrity sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw== - dependencies: - concat-stream "^1.4.7" - os-shim "^0.1.2" - -sshpk@^1.18.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" - integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -throttleit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.1.tgz#304ec51631c3b770c65c6c6f76938b384000f4d5" - integrity sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ== - -through@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -tldts-core@^6.1.86: - version "6.1.86" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.86.tgz#a93e6ed9d505cb54c542ce43feb14c73913265d8" - integrity sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA== - -tldts@^6.1.32: - version "6.1.86" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.86.tgz#087e0555b31b9725ee48ca7e77edc56115cd82f7" - integrity sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ== - dependencies: - tldts-core "^6.1.86" - -tmp@~0.2.3: - version "0.2.5" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" - integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== - -tough-cookie@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" - integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== - dependencies: - tldts "^6.1.32" - -tree-kill@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== - -tslib@^2.1.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^0.8.0: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== - -undici-types@~7.16.0: - version "7.16.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" - integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== - -universalify@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" - integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== - -untildify@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" - integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -which@1.2.x: - version "1.2.14" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" - integrity sha512-16uPglFkRPzgiUXYMi1Jf8Z5EzN1iB4V0ZtMXcHZnwsBtQhhHeCqoWw7tsUY42hJGNDWtUsVLTjakIa5BgAxCw== - dependencies: - isexe "^2.0.0" - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" From 39473f003738811f9b2e479bb7c2062f1ce545b3 Mon Sep 17 00:00:00 2001 From: frappe-pr-bot Date: Fri, 23 Jan 2026 16:05:21 +0000 Subject: [PATCH 12/36] chore: update POT file --- lms/locale/main.pot | 116 ++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/lms/locale/main.pot b/lms/locale/main.pot index 4597128a..2958a63c 100644 --- a/lms/locale/main.pot +++ b/lms/locale/main.pot @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Frappe LMS VERSION\n" "Report-Msgid-Bugs-To: jannat@frappe.io\n" -"POT-Creation-Date: 2026-01-16 16:04+0000\n" -"PO-Revision-Date: 2026-01-16 16:04+0000\n" +"POT-Creation-Date: 2026-01-23 16:05+0000\n" +"PO-Revision-Date: 2026-01-23 16:05+0000\n" "Last-Translator: jannat@frappe.io\n" "Language-Team: jannat@frappe.io\n" "MIME-Version: 1.0\n" @@ -317,7 +317,7 @@ msgid "Administrator" msgstr "" #. Name of a role -#: frontend/src/pages/Batches.vue:319 lms/lms/doctype/lms_badge/lms_badge.json +#: frontend/src/pages/Batches.vue:321 lms/lms/doctype/lms_badge/lms_badge.json msgid "All" msgstr "" @@ -484,7 +484,7 @@ msgstr "" msgid "Apps" msgstr "" -#: frontend/src/pages/Batches.vue:329 +#: frontend/src/pages/Batches.vue:331 msgid "Archived" msgstr "" @@ -751,7 +751,7 @@ msgstr "" #. Label of the batch_name (Link) field in DocType 'LMS Certificate' #. Label of the batch_name (Link) field in DocType 'LMS Certificate Request' #. Label of the batch_name (Link) field in DocType 'LMS Live Class' -#: frontend/src/components/Modals/Event.vue:32 +#: frontend/src/components/Modals/Event.vue:35 #: frontend/src/components/Settings/BadgeForm.vue:195 #: frontend/src/components/Settings/Badges.vue:200 #: frontend/src/components/Settings/Transactions/TransactionDetails.vue:125 @@ -876,7 +876,7 @@ msgid "Batch:" msgstr "" #. Label of the batches (Check) field in DocType 'LMS Settings' -#: frontend/src/pages/Batches.vue:350 frontend/src/pages/Batches.vue:357 +#: frontend/src/pages/Batches.vue:352 frontend/src/pages/Batches.vue:359 #: lms/lms/doctype/lms_settings/lms_settings.json lms/www/lms.py:121 msgid "Batches" msgstr "" @@ -1021,7 +1021,7 @@ msgstr "" msgid "Certificate of Completion" msgstr "" -#: frontend/src/components/Modals/Event.vue:347 +#: frontend/src/components/Modals/Event.vue:353 msgid "Certificate saved successfully" msgstr "" @@ -1041,7 +1041,7 @@ msgstr "" #. Label of a chart in the LMS Workspace #. Label of a Card Break in the LMS Workspace #. Label of a Link in the LMS Workspace -#: frontend/src/components/Modals/Event.vue:411 +#: frontend/src/components/Modals/Event.vue:427 #: frontend/src/components/Sidebar/AppSidebar.vue:550 #: frontend/src/pages/BatchForm.vue:69 frontend/src/pages/Batches.vue:100 #: frontend/src/pages/CourseCertification.vue:10 @@ -1769,7 +1769,7 @@ msgstr "" #: frontend/src/components/Modals/BatchStudentProgress.vue:95 #: frontend/src/pages/BatchDetail.vue:44 #: frontend/src/pages/CourseCertification.vue:127 -#: frontend/src/pages/Courses.vue:365 frontend/src/pages/Courses.vue:372 +#: frontend/src/pages/Courses.vue:364 frontend/src/pages/Courses.vue:371 #: frontend/src/pages/Programs/ProgramForm.vue:49 #: frontend/src/pages/Programs/Programs.vue:35 #: lms/lms/doctype/lms_batch/lms_batch.json @@ -1876,7 +1876,7 @@ msgstr "" msgid "Create your first quiz" msgstr "" -#: frontend/src/pages/Assignments.vue:178 frontend/src/pages/Courses.vue:355 +#: frontend/src/pages/Assignments.vue:178 frontend/src/pages/Courses.vue:354 msgid "Created" msgstr "" @@ -1952,7 +1952,7 @@ msgstr "" #. Label of the date (Date) field in DocType 'LMS Certificate Request' #. Label of the date (Date) field in DocType 'LMS Live Class' #. Label of the date (Date) field in DocType 'Scheduled Flow' -#: frontend/src/components/Modals/Event.vue:40 +#: frontend/src/components/Modals/Event.vue:46 #: frontend/src/components/Modals/LiveClassModal.vue:29 #: lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.json #: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json @@ -2239,7 +2239,7 @@ msgstr "" msgid "Edit Email Template" msgstr "" -#: frontend/src/components/Settings/PaymentGatewayDetails.vue:8 +#: frontend/src/components/Settings/PaymentGatewayDetails.vue:13 msgid "Edit Payment Gateway" msgstr "" @@ -2422,7 +2422,7 @@ msgstr "" msgid "Enroll Now" msgstr "" -#: frontend/src/pages/Batches.vue:332 frontend/src/pages/Courses.vue:358 +#: frontend/src/pages/Batches.vue:334 frontend/src/pages/Courses.vue:357 #: frontend/src/pages/Programs/StudentPrograms.vue:96 msgid "Enrolled" msgstr "" @@ -2524,7 +2524,7 @@ msgstr "" #. Label of a Link in the LMS Workspace #. Label of a shortcut in the LMS Workspace -#: frontend/src/components/Modals/Event.vue:404 lms/lms/workspace/lms/lms.json +#: frontend/src/components/Modals/Event.vue:420 lms/lms/workspace/lms/lms.json msgid "Evaluation" msgstr "" @@ -2549,7 +2549,7 @@ msgstr "" msgid "Evaluation end date cannot be less than the batch end date." msgstr "" -#: frontend/src/components/Modals/Event.vue:286 +#: frontend/src/components/Modals/Event.vue:292 msgid "Evaluation saved successfully" msgstr "" @@ -2662,7 +2662,7 @@ msgstr "" #. Label of the expiry_date (Date) field in DocType 'LMS Certificate' #: frontend/src/components/Modals/BulkCertificates.vue:33 -#: frontend/src/components/Modals/Event.vue:144 +#: frontend/src/components/Modals/Event.vue:150 #: lms/lms/doctype/lms_certificate/lms_certificate.json msgid "Expiry Date" msgstr "" @@ -2693,7 +2693,7 @@ msgstr "" #. Submission' #. Option for the 'Status' (Select) field in DocType 'LMS Certificate #. Evaluation' -#: frontend/src/components/Modals/Event.vue:396 +#: frontend/src/components/Modals/Event.vue:412 #: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json #: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json msgid "Fail" @@ -3207,7 +3207,7 @@ msgstr "" #. Option for the 'Status' (Select) field in DocType 'LMS Certificate #. Evaluation' #. Option for the 'Status' (Select) field in DocType 'LMS Course' -#: frontend/src/components/Modals/Event.vue:388 +#: frontend/src/components/Modals/Event.vue:404 #: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json #: lms/lms/doctype/lms_course/lms_course.json msgid "In Progress" @@ -3351,7 +3351,7 @@ msgstr "" #. Label of the issue_date (Date) field in DocType 'Certification' #. Label of the issue_date (Date) field in DocType 'LMS Certificate' #: frontend/src/components/Modals/BulkCertificates.vue:28 -#: frontend/src/components/Modals/Event.vue:138 +#: frontend/src/components/Modals/Event.vue:144 #: lms/lms/doctype/certification/certification.json #: lms/lms/doctype/lms_certificate/lms_certificate.json msgid "Issue Date" @@ -3454,7 +3454,7 @@ msgstr "" msgid "Join Call" msgstr "" -#: frontend/src/components/Modals/Event.vue:78 +#: frontend/src/components/Modals/Event.vue:84 msgid "Join Meeting" msgstr "" @@ -3894,7 +3894,7 @@ msgstr "" msgid "LinkedIn ID" msgstr "" -#: frontend/src/pages/Courses.vue:341 +#: frontend/src/pages/Courses.vue:340 msgid "Live" msgstr "" @@ -4427,7 +4427,7 @@ msgstr "" #: frontend/src/components/Settings/Members.vue:17 #: frontend/src/components/Settings/PaymentGateways.vue:16 #: frontend/src/components/Settings/ZoomSettings.vue:17 -#: frontend/src/pages/Courses.vue:344 +#: frontend/src/pages/Courses.vue:343 #: frontend/src/pages/Programs/Programs.vue:10 #: lms/lms/doctype/lms_badge/lms_badge.json msgid "New" @@ -4459,7 +4459,7 @@ msgstr "" msgid "New Job Applicant" msgstr "" -#: frontend/src/components/Settings/PaymentGatewayDetails.vue:7 +#: frontend/src/components/Settings/PaymentGatewayDetails.vue:12 msgid "New Payment Gateway" msgstr "" @@ -4776,12 +4776,7 @@ msgstr "" #: frontend/src/components/UserAvatar.vue:11 #: frontend/src/pages/CertifiedParticipants.vue:46 #: frontend/src/pages/Profile.vue:69 -msgid "Open to Opportunities" -msgstr "" - -#. Option for the 'Open to' (Select) field in DocType 'User' -#: lms/fixtures/custom_field.json -msgid "Opportunities" +msgid "Open to Work" msgstr "" #. Label of the option (Data) field in DocType 'LMS Option' @@ -4922,7 +4917,7 @@ msgstr "" #. Submission' #. Option for the 'Status' (Select) field in DocType 'LMS Certificate #. Evaluation' -#: frontend/src/components/Modals/Event.vue:392 +#: frontend/src/components/Modals/Event.vue:408 #: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json #: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json msgid "Pass" @@ -5052,7 +5047,7 @@ msgstr "" #. Option for the 'Status' (Select) field in DocType 'LMS Certificate #. Evaluation' #. Option for the 'Status' (Select) field in DocType 'LMS Mentor Request' -#: frontend/src/components/Modals/Event.vue:384 +#: frontend/src/components/Modals/Event.vue:400 #: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json #: lms/lms/doctype/lms_mentor_request/lms_mentor_request.json msgid "Pending" @@ -5192,7 +5187,7 @@ msgstr "" msgid "Please login to continue with payment." msgstr "" -#: lms/lms/utils.py:2081 +#: lms/lms/utils.py:2082 msgid "Please login to enroll in the program." msgstr "" @@ -5523,7 +5518,7 @@ msgstr "" #. Label of the published (Check) field in DocType 'LMS Course' #. Label of the published (Check) field in DocType 'LMS Program' #: frontend/src/components/Modals/BulkCertificates.vue:51 -#: frontend/src/components/Modals/Event.vue:122 +#: frontend/src/components/Modals/Event.vue:128 #: frontend/src/pages/BatchForm.vue:59 frontend/src/pages/CourseForm.vue:105 #: frontend/src/pages/Programs/ProgramForm.vue:33 #: frontend/src/pages/Programs/StudentPrograms.vue:100 @@ -5691,7 +5686,7 @@ msgstr "" #. Label of the rating (Data) field in DocType 'LMS Course' #. Label of the rating (Rating) field in DocType 'LMS Course Review' #: frontend/src/components/CourseCardOverlay.vue:147 -#: frontend/src/components/Modals/Event.vue:92 +#: frontend/src/components/Modals/Event.vue:98 #: frontend/src/components/Modals/ReviewModal.vue:18 #: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json #: lms/lms/doctype/lms_course/lms_course.json @@ -5927,14 +5922,14 @@ msgstr "" #: frontend/src/components/Modals/AssignmentForm.vue:65 #: frontend/src/components/Modals/EditProfile.vue:121 #: frontend/src/components/Modals/EmailTemplateModal.vue:12 -#: frontend/src/components/Modals/Event.vue:115 -#: frontend/src/components/Modals/Event.vue:151 +#: frontend/src/components/Modals/Event.vue:121 +#: frontend/src/components/Modals/Event.vue:157 #: frontend/src/components/Modals/Question.vue:112 #: frontend/src/components/Modals/ZoomAccountModal.vue:10 #: frontend/src/components/Settings/BadgeAssignmentForm.vue:12 #: frontend/src/components/Settings/BadgeForm.vue:78 #: frontend/src/components/Settings/Coupons/CouponDetails.vue:78 -#: frontend/src/components/Settings/PaymentGatewayDetails.vue:38 +#: frontend/src/components/Settings/PaymentGatewayDetails.vue:41 #: frontend/src/components/Settings/Transactions/TransactionDetails.vue:129 #: frontend/src/pages/BatchForm.vue:14 frontend/src/pages/CourseForm.vue:17 #: frontend/src/pages/JobForm.vue:8 frontend/src/pages/LessonForm.vue:14 @@ -6046,7 +6041,7 @@ msgstr "" msgid "Select Date" msgstr "" -#: frontend/src/components/Settings/PaymentGatewayDetails.vue:22 +#: frontend/src/components/Settings/PaymentGatewayDetails.vue:26 msgid "Select Payment Gateway" msgstr "" @@ -6362,7 +6357,7 @@ msgstr "" #. Label of the status (Select) field in DocType 'LMS Programming Exercise #. Submission' #. Label of the status (Select) field in DocType 'LMS Test Case Submission' -#: frontend/src/components/Modals/Event.vue:99 +#: frontend/src/components/Modals/Event.vue:105 #: frontend/src/components/Settings/Badges.vue:228 #: frontend/src/components/Settings/ZoomSettings.vue:197 #: frontend/src/pages/AssignmentSubmissionList.vue:19 @@ -6473,7 +6468,7 @@ msgstr "" #. Label of the summary (Small Text) field in DocType 'LMS Certificate #. Evaluation' -#: frontend/src/components/Modals/Event.vue:106 +#: frontend/src/components/Modals/Event.vue:112 #: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json msgid "Summary" msgstr "" @@ -6569,7 +6564,7 @@ msgstr "" #. Label of the template (Link) field in DocType 'LMS Certificate' #: frontend/src/components/Modals/BulkCertificates.vue:43 -#: frontend/src/components/Modals/Event.vue:127 +#: frontend/src/components/Modals/Event.vue:133 #: lms/lms/doctype/lms_certificate/lms_certificate.json msgid "Template" msgstr "" @@ -6618,7 +6613,7 @@ msgstr "" msgid "Thanks and Regards" msgstr "" -#: lms/lms/utils.py:2247 +#: lms/lms/utils.py:2248 msgid "The batch does not exist." msgstr "" @@ -6626,7 +6621,7 @@ msgstr "" msgid "The batch you have enrolled for is starting tomorrow. Please be prepared and be on time for the session." msgstr "" -#: lms/lms/utils.py:1747 +#: lms/lms/utils.py:1748 msgid "The coupon code '{0}' is invalid." msgstr "" @@ -6650,7 +6645,7 @@ msgstr "" msgid "The last day to schedule your evaluations is " msgstr "" -#: lms/lms/utils.py:2231 +#: lms/lms/utils.py:2232 msgid "The lesson does not exist." msgstr "" @@ -6658,7 +6653,7 @@ msgstr "" msgid "The slot is already booked by another participant." msgstr "" -#: lms/lms/utils.py:1361 lms/lms/utils.py:1477 lms/lms/utils.py:1944 +#: lms/lms/utils.py:1362 lms/lms/utils.py:1478 lms/lms/utils.py:1945 msgid "The specified batch does not exist." msgstr "" @@ -6724,15 +6719,15 @@ msgstr "" msgid "This class has ended" msgstr "" -#: lms/lms/utils.py:1776 +#: lms/lms/utils.py:1777 msgid "This coupon has expired." msgstr "" -#: lms/lms/utils.py:1779 +#: lms/lms/utils.py:1780 msgid "This coupon has reached its maximum usage limit." msgstr "" -#: lms/lms/utils.py:1788 +#: lms/lms/utils.py:1789 msgid "This coupon is not applicable to this {0}." msgstr "" @@ -6740,7 +6735,7 @@ msgstr "" msgid "This course has:" msgstr "" -#: lms/lms/utils.py:1707 +#: lms/lms/utils.py:1708 msgid "This course is free." msgstr "" @@ -6797,7 +6792,7 @@ msgid "Thursday" msgstr "" #. Label of the time (Time) field in DocType 'LMS Live Class' -#: frontend/src/components/Modals/Event.vue:48 +#: frontend/src/components/Modals/Event.vue:54 #: frontend/src/components/Modals/LiveClassModal.vue:52 #: frontend/src/components/Quiz.vue:58 #: lms/lms/doctype/lms_live_class/lms_live_class.json @@ -6932,7 +6927,7 @@ msgstr "" msgid "To Date" msgstr "" -#: lms/lms/utils.py:1721 +#: lms/lms/utils.py:1722 msgid "To join this batch, please contact the Administrator." msgstr "" @@ -7054,7 +7049,7 @@ msgstr "" msgid "Under Review" msgstr "" -#: frontend/src/pages/Batches.vue:330 frontend/src/pages/Courses.vue:356 +#: frontend/src/pages/Batches.vue:332 frontend/src/pages/Courses.vue:355 msgid "Unpublished" msgstr "" @@ -7075,8 +7070,8 @@ msgstr "" #. Option for the 'Status' (Select) field in DocType 'LMS Certificate Request' #. Label of the upcoming (Check) field in DocType 'LMS Course' -#: frontend/src/pages/Batches.vue:328 frontend/src/pages/CourseForm.vue:117 -#: frontend/src/pages/Courses.vue:347 +#: frontend/src/pages/Batches.vue:330 frontend/src/pages/CourseForm.vue:117 +#: frontend/src/pages/Courses.vue:346 #: lms/lms/doctype/lms_certificate_request/lms_certificate_request.json #: lms/lms/doctype/lms_course/lms_course.json msgid "Upcoming" @@ -7218,7 +7213,7 @@ msgid "View Applications" msgstr "" #: frontend/src/components/CertificationLinks.vue:10 -#: frontend/src/components/Modals/Event.vue:67 +#: frontend/src/components/Modals/Event.vue:73 msgid "View Certificate" msgstr "" @@ -7325,6 +7320,11 @@ msgstr "" msgid "Withdrawn" msgstr "" +#. Option for the 'Open to' (Select) field in DocType 'User' +#: lms/fixtures/custom_field.json +msgid "Work" +msgstr "" + #. Label of the work_environment (Section Break) field in DocType 'User' #: lms/fixtures/custom_field.json msgid "Work Environment" @@ -7425,7 +7425,7 @@ msgstr "" msgid "You cannot enroll in an unpublished course." msgstr "" -#: lms/lms/utils.py:2085 +#: lms/lms/utils.py:2086 msgid "You cannot enroll in an unpublished program." msgstr "" @@ -7441,11 +7441,11 @@ msgstr "" msgid "You cannot schedule evaluations for past slots." msgstr "" -#: lms/lms/utils.py:2259 +#: lms/lms/utils.py:2260 msgid "You do not have access to this batch." msgstr "" -#: lms/lms/utils.py:2242 +#: lms/lms/utils.py:2243 msgid "You do not have access to this course." msgstr "" From 05d21cf81770c5d6a9b297fbf5890f99c625a43e Mon Sep 17 00:00:00 2001 From: Adarsh Singh Date: Sat, 24 Jan 2026 00:25:21 +0530 Subject: [PATCH 13/36] fix: instructor notes block settings menu overlapped by sidebar PR #1987 fixed the "+" (Add block) menu positioning but missed the block settings menu (6-dot "Click to tune" icon) which uses a different parent container (.ce-toolbar__actions instead of .ce-toolbox). This adds the missing selector to fix both menus. Related: #1990 --- frontend/src/pages/LessonForm.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/LessonForm.vue b/frontend/src/pages/LessonForm.vue index 41aaa5eb..64233573 100644 --- a/frontend/src/pages/LessonForm.vue +++ b/frontend/src/pages/LessonForm.vue @@ -665,7 +665,8 @@ iframe { padding: 8px; } -.codex-editor--narrow .ce-toolbox .ce-popover { +.codex-editor--narrow .ce-toolbox .ce-popover, +.codex-editor--narrow .ce-toolbar__actions .ce-popover { right: unset; left: initial; } From 49ed08283101d436c882cb2d782850ffcbae31d8 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Sat, 24 Jan 2026 11:31:37 +0530 Subject: [PATCH 14/36] feat: allow moderators to create new payment from settings --- .../Transactions/TransactionDetails.vue | 156 ++++++++++++++---- .../Settings/Transactions/TransactionList.vue | 20 ++- .../Settings/Transactions/Transactions.vue | 11 +- 3 files changed, 146 insertions(+), 41 deletions(-) diff --git a/frontend/src/components/Settings/Transactions/TransactionDetails.vue b/frontend/src/components/Settings/Transactions/TransactionDetails.vue index 832db3fd..efd2cc88 100644 --- a/frontend/src/components/Settings/Transactions/TransactionDetails.vue +++ b/frontend/src/components/Settings/Transactions/TransactionDetails.vue @@ -1,12 +1,33 @@ 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 @@ diff --git a/frontend/src/pages/Courses/Courses.vue b/frontend/src/pages/Courses/Courses.vue index b8214889..d6fdc168 100644 --- a/frontend/src/pages/Courses/Courses.vue +++ b/frontend/src/pages/Courses/Courses.vue @@ -106,7 +106,11 @@ - + diff --git a/frontend/src/pages/Courses/Courses.vue b/frontend/src/pages/Courses/Courses.vue index d6fdc168..0bd81158 100644 --- a/frontend/src/pages/Courses/Courses.vue +++ b/frontend/src/pages/Courses/Courses.vue @@ -5,7 +5,7 @@ Date: Thu, 29 Jan 2026 16:17:34 +0530 Subject: [PATCH 23/36] test: fixed course ui tests based on new flow --- cypress/e2e/course_creation.cy.js | 37 +-- frontend/components.d.ts | 1 - .../src/pages/Courses/CourseDashboard.vue | 4 +- frontend/src/pages/Courses/NewCourseModal.vue | 236 +++++++++--------- .../lms_course_review/lms_course_review.json | 45 +++- 5 files changed, 184 insertions(+), 139 deletions(-) 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 1292ad68..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'] diff --git a/frontend/src/pages/Courses/CourseDashboard.vue b/frontend/src/pages/Courses/CourseDashboard.vue index a802ae59..f5b75a44 100644 --- a/frontend/src/pages/Courses/CourseDashboard.vue +++ b/frontend/src/pages/Courses/CourseDashboard.vue @@ -20,7 +20,7 @@
-
+
{{ __('Students') }} @@ -120,7 +120,7 @@
-
+
{{ __('Progress Summary') }}
diff --git a/frontend/src/pages/Courses/NewCourseModal.vue b/frontend/src/pages/Courses/NewCourseModal.vue index 46389790..eff45643 100644 --- a/frontend/src/pages/Courses/NewCourseModal.vue +++ b/frontend/src/pages/Courses/NewCourseModal.vue @@ -1,82 +1,82 @@ \ No newline at end of file + diff --git a/lms/lms/doctype/lms_course_review/lms_course_review.json b/lms/lms/doctype/lms_course_review/lms_course_review.json index 3eab43fb..4ac6be0c 100644 --- a/lms/lms/doctype/lms_course_review/lms_course_review.json +++ b/lms/lms/doctype/lms_course_review/lms_course_review.json @@ -38,10 +38,11 @@ "fieldtype": "Column Break" } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-12-21 15:25:16.744558", - "modified_by": "Administrator", + "modified": "2026-01-29 16:10:47.787285", + "modified_by": "sayali@frappe.io", "module": "LMS", "name": "LMS Course Review", "owner": "Administrator", @@ -60,7 +61,6 @@ }, { "create": 1, - "delete": 1, "email": 1, "export": 1, "print": 1, @@ -69,12 +69,49 @@ "role": "LMS Student", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Moderator", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Course Creator", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Batch Evaluator", + "share": 1, + "write": 1 } ], + "row_format": "Dynamic", "search_fields": "course", "sort_field": "modified", "sort_order": "DESC", "states": [], "title_field": "course", "track_changes": 1 -} \ No newline at end of file +} From 933bc58264de01239708bf95a890eeea2ac1093a Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Thu, 29 Jan 2026 19:52:56 +0530 Subject: [PATCH 24/36] fix: api permissions --- frontend/src/components/Assignment.vue | 103 ++++----- frontend/src/components/BatchOverlay.vue | 25 +- frontend/src/components/Controls/Uploader.vue | 9 +- .../src/components/Modals/EditProfile.vue | 117 ++-------- .../src/components/Settings/Evaluators.vue | 5 +- .../src/components/Sidebar/AppSidebar.vue | 15 +- frontend/src/pages/CertifiedParticipants.vue | 9 +- frontend/src/pages/Lesson.vue | 2 + frontend/src/pages/Notifications.vue | 12 +- frontend/src/stores/session.js | 2 + frontend/src/stores/user.js | 1 - frontend/src/utils/index.js | 3 + lms/lms/api.py | 215 +++++++++--------- lms/lms/utils.py | 2 + 14 files changed, 238 insertions(+), 282 deletions(-) diff --git a/frontend/src/components/Assignment.vue b/frontend/src/components/Assignment.vue index 2f4b2745..32697bd6 100644 --- a/frontend/src/components/Assignment.vue +++ b/frontend/src/components/Assignment.vue @@ -26,8 +26,8 @@
-
-
+
+
{{ __('Submission') }}
@@ -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.") }} {{ @@ -63,12 +63,17 @@ }} {{ __('Feel free to make edits to your submission if needed.') }}
-
-
- {{ __('Add your assignment as {0}').format(assignment.data.type) }} +
+
+ {{ __('Upload Assignment') }} +
+
+ {{ + __('You can only upload {0} files').format(assignment.data.type) + }}
-
- -
+
- - {{ submissionFile.file_name }} - - - {{ getFileSize(submissionFile.file_size) }} - +
+
+ +
+ + {{ + submissionResource.doc.assignment_attachment + .split('/') + .pop() + }} + +
-
- {{ __('Comments by Evaluator') }}: +
+ {{ __('Comments by Evaluator') }}
@@ -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() @@ -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 { @@ -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, @@ -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 } @@ -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 @@ -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') { @@ -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')) }, } @@ -388,7 +383,7 @@ const addNewSubmission = () => { const saveSubmission = (file) => { isDirty.value = true - submissionFile.value = file + submissionResource.doc.assignment_attachment = file.file_url } const markLessonProgress = () => { @@ -439,7 +434,7 @@ const validateFile = (file) => { const removeSubmission = () => { isDirty.value = true - submissionFile.value = null + submissionResource.doc.assignment_attachment = '' } const canGradeSubmission = computed(() => { diff --git a/frontend/src/components/BatchOverlay.vue b/frontend/src/components/BatchOverlay.vue index 82a4bcb3..9324d6a9 100644 --- a/frontend/src/components/BatchOverlay.vue +++ b/frontend/src/components/BatchOverlay.vue @@ -1,7 +1,7 @@