From 79d9f31db79f6bd3194fbbb9d36fe6e3492224f4 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Wed, 2 Aug 2023 18:08:42 +0530 Subject: [PATCH] feat: paid courses --- lms/lms/doctype/lms_course/lms_course.js | 15 ++-- lms/lms/doctype/lms_course/lms_course.json | 79 +++++------------ .../doctype/lms_settings/lms_settings.json | 51 ++++++++--- lms/lms/widgets/CourseCard.html | 30 ++++--- lms/lms/widgets/NoPreviewModal.html | 2 +- lms/patches.txt | 3 +- .../v1_0/paid_certificate_to_paid_course.py | 20 +++++ lms/public/css/style.css | 13 ++- lms/public/js/common_functions.js | 6 +- lms/templates/reviews_cta.html | 2 +- lms/www/batch/learn.html | 2 +- lms/www/courses/course.html | 85 ++----------------- lms/www/courses/course.py | 12 +-- lms/www/courses/index.py | 5 +- 14 files changed, 138 insertions(+), 187 deletions(-) create mode 100644 lms/patches/v1_0/paid_certificate_to_paid_course.py diff --git a/lms/lms/doctype/lms_course/lms_course.js b/lms/lms/doctype/lms_course/lms_course.js index 07bd2d30..71c6be3d 100644 --- a/lms/lms/doctype/lms_course/lms_course.js +++ b/lms/lms/doctype/lms_course/lms_course.js @@ -11,14 +11,6 @@ frappe.ui.form.on("LMS Course", { }; }); - frm.set_query("instructor", "instructors", function () { - return { - filters: { - ignore_user_type: 1, - }, - }; - }); - frm.set_query("course", "related_courses", function () { return { filters: { @@ -29,5 +21,12 @@ frappe.ui.form.on("LMS Course", { }, refresh: (frm) => { frm.add_web_link(`/courses/${frm.doc.name}`, "See on Website"); + + if (!frm.doc.currency) + frappe.db + .get_single_value("LMS Settings", "default_currency") + .then((value) => { + frm.set_value("currency", value); + }); }, }); diff --git a/lms/lms/doctype/lms_course/lms_course.json b/lms/lms/doctype/lms_course/lms_course.json index 11e45195..ba362ff7 100644 --- a/lms/lms/doctype/lms_course/lms_course.json +++ b/lms/lms/doctype/lms_course/lms_course.json @@ -32,19 +32,18 @@ "description", "chapters", "related_courses", + "pricing_section", + "paid_course", + "currency", + "course_price", "certification_section", "enable_certification", "expiry", - "section_break_23", + "max_attempts", + "column_break_rxww", "grant_certificate_after", "evaluator", - "column_break_26", - "max_attempts", - "duration", - "pricing_section", - "paid_certificate", - "currency", - "price_certificate" + "duration" ], "fields": [ { @@ -170,13 +169,6 @@ "fieldname": "column_break_12", "fieldtype": "Column Break" }, - { - "default": "0", - "depends_on": "enable_certification", - "fieldname": "paid_certificate", - "fieldtype": "Check", - "label": "Paid Certificate" - }, { "depends_on": "enable_certification", "fieldname": "grant_certificate_after", @@ -193,20 +185,12 @@ "options": "Course Evaluator" }, { - "depends_on": "eval: doc.grant_certificate_after == \"Evaluation\"", "fieldname": "pricing_section", "fieldtype": "Section Break", "label": "Pricing" }, { - "depends_on": "paid_certificate", - "fieldname": "price_certificate", - "fieldtype": "Currency", - "label": "Certificate Price", - "mandatory_depends_on": "paid_certificate" - }, - { - "depends_on": "paid_certificate", + "depends_on": "paid_course", "fieldname": "currency", "fieldtype": "Link", "label": "Currency", @@ -228,11 +212,21 @@ "options": "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12" }, { - "fieldname": "section_break_23", - "fieldtype": "Section Break" + "default": "0", + "fieldname": "paid_course", + "fieldtype": "Check", + "label": "Paid Course" }, { - "fieldname": "column_break_26", + "depends_on": "paid_course", + "fieldname": "course_price", + "fieldtype": "Currency", + "label": "Course Price", + "option": "currency", + "mandatory_depends_on": "paid_certificate" + }, + { + "fieldname": "column_break_rxww", "fieldtype": "Column Break" } ], @@ -260,39 +254,12 @@ } ], "make_attachments_public": 1, - "modified": "2023-05-11 17:08:19.763405", + "modified": "2023-08-02 12:07:26.354119", "modified_by": "Administrator", "module": "LMS", "name": "LMS Course", "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "select": 1, - "share": 1, - "write": 1 - }, - { - "create": 1, - "email": 1, - "export": 1, - "if_owner": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "All", - "select": 1, - "share": 1, - "write": 1 - } - ], + "permissions": [], "search_fields": "title", "show_title_field_in_link": 1, "sort_field": "creation", diff --git a/lms/lms/doctype/lms_settings/lms_settings.json b/lms/lms/doctype/lms_settings/lms_settings.json index f3ed9073..132cfb5d 100644 --- a/lms/lms/doctype/lms_settings/lms_settings.json +++ b/lms/lms/doctype/lms_settings/lms_settings.json @@ -6,16 +6,21 @@ "engine": "InnoDB", "field_order": [ "default_home", - "send_calendar_invite_for_evaluations", - "allow_student_progress", - "column_break_zdel", - "is_onboarding_complete", "force_profile_completion", - "section_break_szgq", - "search_placeholder", - "portal_course_creation", - "column_break_2", + "is_onboarding_complete", + "column_break_zdel", "livecode_url", + "course_settings_section", + "search_placeholder", + "column_break_iqxy", + "portal_course_creation", + "section_break_szgq", + "send_calendar_invite_for_evaluations", + "column_break_2", + "allow_student_progress", + "payment_section", + "default_currency", + "column_break_cfcv", "signup_settings_tab", "signup_settings_section", "terms_of_use", @@ -37,6 +42,7 @@ "default": "https://livecode.dev.fossunited.org", "fieldname": "livecode_url", "fieldtype": "Data", + "hidden": 1, "label": "LiveCode URL" }, { @@ -163,7 +169,8 @@ }, { "fieldname": "section_break_szgq", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Class Settings" }, { "fieldname": "signup_settings_tab", @@ -180,12 +187,36 @@ "fieldname": "allow_student_progress", "fieldtype": "Check", "label": "Allow students to see each others progress in class" + }, + { + "fieldname": "payment_section", + "fieldtype": "Section Break", + "label": "Payment" + }, + { + "fieldname": "default_currency", + "fieldtype": "Link", + "label": "Default Currency", + "options": "Currency" + }, + { + "fieldname": "column_break_cfcv", + "fieldtype": "Column Break" + }, + { + "fieldname": "course_settings_section", + "fieldtype": "Section Break", + "label": "Course Settings" + }, + { + "fieldname": "column_break_iqxy", + "fieldtype": "Column Break" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-04-17 12:54:44.706101", + "modified": "2023-08-02 12:30:50.897156", "modified_by": "Administrator", "module": "LMS", "name": "LMS Settings", diff --git a/lms/lms/widgets/CourseCard.html b/lms/lms/widgets/CourseCard.html index 486471c9..e050ec0e 100644 --- a/lms/lms/widgets/CourseCard.html +++ b/lms/lms/widgets/CourseCard.html @@ -59,19 +59,12 @@ {{ frappe.utils.flt(avg_rating, frappe.get_system_settings("float_precision") or 3) }} {% endif %} - - {% if course.paid_certificate %} -
- - - - - {{ format_amount(course.price_certificate, course.currency) }} - -
- {% endif %} -
{{ course.title }}
+ +
+ {{ course.title }} +
+
{{ course.short_introduction }}
@@ -87,7 +80,8 @@ {% endif %} {% if read_only %} diff --git a/lms/lms/widgets/NoPreviewModal.html b/lms/lms/widgets/NoPreviewModal.html index 0d9c5767..c6f06aa5 100644 --- a/lms/lms/widgets/NoPreviewModal.html +++ b/lms/lms/widgets/NoPreviewModal.html @@ -30,7 +30,7 @@ {{ _("Notify me when available") }} {% elif show_start_learing_cta(course, membership) %} - {% endif %} diff --git a/lms/patches.txt b/lms/patches.txt index f0d194df..bcabe386 100644 --- a/lms/patches.txt +++ b/lms/patches.txt @@ -60,4 +60,5 @@ lms.patches.v1_0.create_class_evaluator_role execute:frappe.permissions.reset_perms("LMS Class") execute:frappe.permissions.reset_perms("Course Evaluator") execute:frappe.permissions.reset_perms("LMS Certificate Request") -execute:frappe.permissions.reset_perms("LMS Certificate Evaluation") \ No newline at end of file +execute:frappe.permissions.reset_perms("LMS Certificate Evaluation") +lms.patches.v1_0.paid_certificate_to_paid_course \ No newline at end of file diff --git a/lms/patches/v1_0/paid_certificate_to_paid_course.py b/lms/patches/v1_0/paid_certificate_to_paid_course.py new file mode 100644 index 00000000..88ec708b --- /dev/null +++ b/lms/patches/v1_0/paid_certificate_to_paid_course.py @@ -0,0 +1,20 @@ +import frappe + + +def execute(): + courses = frappe.get_all( + "LMS Course", + {"paid_certificate": ["is", "set"]}, + ["name", "price_certificate", "currency"], + ) + + for course in courses: + frappe.db.set_value( + "LMS Course", + course.name, + { + "paid_course": 1, + "course_price": course.price_certificate, + "currency": course.currency, + }, + ) diff --git a/lms/public/css/style.css b/lms/public/css/style.css index 7037a6f6..4c8bcf68 100644 --- a/lms/public/css/style.css +++ b/lms/public/css/style.css @@ -414,11 +414,13 @@ input[type=checkbox] { } .course-card-footer { - margin-top: auto; + display: flex; + justify-content: space-between; + margin-top: auto; } -.course-card-footer .avatar-group { - display: inherit; +.course-price { + font-weight: 700; } .view-course-link { @@ -551,6 +553,11 @@ input[type=checkbox] { } } +.course-card-instructors { + display: flex; + align-items: center; +} + .course-card-wide-content { display: flex; flex-direction: column; diff --git a/lms/public/js/common_functions.js b/lms/public/js/common_functions.js index 2eeb43ae..b39acd21 100644 --- a/lms/public/js/common_functions.js +++ b/lms/public/js/common_functions.js @@ -2,8 +2,8 @@ frappe.ready(() => { setup_file_size(); pin_header(); - $(".join-batch").click((e) => { - join_course(e); + $(".enroll-in-course").click((e) => { + enroll_in_course(e); }); $(".notify-me").click((e) => { @@ -68,7 +68,7 @@ const file_size = (value) => { return value; }; -const join_course = (e) => { +const enroll_in_course = (e) => { e.preventDefault(); let course = $(e.currentTarget).attr("data-course"); if (frappe.session.user == "Guest") { diff --git a/lms/templates/reviews_cta.html b/lms/templates/reviews_cta.html index 163e1f6c..73bd63a7 100644 --- a/lms/templates/reviews_cta.html +++ b/lms/templates/reviews_cta.html @@ -7,7 +7,7 @@ {{ _("Write a review") }} {% elif show_start_learing_cta(course, membership) %} -
+
{{ _("Start Learning") }}
{% endif %} diff --git a/lms/www/batch/learn.html b/lms/www/batch/learn.html index 23003702..edc10f04 100644 --- a/lms/www/batch/learn.html +++ b/lms/www/batch/learn.html @@ -135,7 +135,7 @@ {{ render_html(lesson) }} {% else %} - {% set course_link = "" + _('here') + "" %} + {% set course_link = "" + _('here') + "" %}
{{ _("There is no preview available for this lesson. Please join the course to access it. diff --git a/lms/www/courses/course.html b/lms/www/courses/course.html index c969dd2e..c5ee4d8d 100644 --- a/lms/www/courses/course.html +++ b/lms/www/courses/course.html @@ -15,7 +15,7 @@ {{ Description(course) }} {{ widgets.CourseOutline(course=course, membership=membership, is_user_interested=is_user_interested) }} {% if course.status == "Approved" and not frappe.utils.cint(course.upcoming) %} - {% include "lms/templates/reviews.html" %} + {% include "lms/templates/reviews.html" %} {% endif %}
@@ -136,18 +136,8 @@ {{ get_lessons(course.name, None, False) }} {{ _("Lessons") }} - {% if course.enable_certification %} -
- - - - {{ _("Get Certified") }} -
- {% endif %} - - {{ SlotModal(course) }} {% endmacro %} @@ -231,8 +221,13 @@ {{ _("Continue Learning") }} + {% elif course.paid_course %} + + {{ _("Buy This Course") }} + + {% elif show_start_learing_cta(course, membership) %} -
+
{{ _("Start Learning") }}
{% endif %} @@ -245,11 +240,6 @@ {{ _("Get Certificate") }} - {% elif eligible_for_evaluation %} - - {{ _("Apply for Certificate") }} - - {% elif course.grant_certificate_after == "Completion" and progress == 100 %}
{{ _("Get Certificate") }} @@ -279,16 +269,6 @@ {{ _("You have opted to be notified for this course. You will receive an email when the course becomes available.") }}
- {% if certificate_request and not certificate %} -

- - {{ _("Evaluation On: ") }} - - {{ _("{0} at {1}").format(frappe.utils.format_date(certificate_request.date, "medium"), - frappe.utils.format_time(certificate_request.start_time, "short")) }} -

- {% endif %} - {% if course.status == "Under Review" and is_instructor(course.name) %}
{{ _("This course is currently under review. Once the review is complete, the System Admins will publish it on the website.") }} @@ -301,54 +281,3 @@

{% endif %} {% endmacro %} - - - -{% macro SlotModal(course) %} - -{% endmacro %} - diff --git a/lms/www/courses/course.py b/lms/www/courses/course.py index 3af669ed..b8e3f5ac 100644 --- a/lms/www/courses/course.py +++ b/lms/www/courses/course.py @@ -52,13 +52,10 @@ def set_course_context(context, course_name): "disable_self_learning", "status", "video_link", - "enable_certification", - "grant_certificate_after", - "paid_certificate", - "price_certificate", + "paid_course", + "course_price", "currency", - "max_attempts", - "duration", + "grant_certificate_after", ], as_dict=True, ) @@ -79,7 +76,7 @@ def set_course_context(context, course_name): frappe.db.get_value( "LMS Course", csr.course, - ["name", "upcoming", "title", "image", "enable_certification"], + ["name", "upcoming", "title", "image"], as_dict=True, ) ) @@ -94,7 +91,6 @@ def set_course_context(context, course_name): context.certificate = is_certified(course.name) eval_details = get_evaluation_details(course.name) context.eligible_for_evaluation = eval_details.eligible - context.certificate_request = eval_details.request context.no_of_attempts = eval_details.no_of_attempts if context.course.upcoming: context.is_user_interested = get_user_interest(context.course.name) diff --git a/lms/www/courses/index.py b/lms/www/courses/index.py index 844d485f..006c7cc3 100644 --- a/lms/www/courses/index.py +++ b/lms/www/courses/index.py @@ -45,9 +45,8 @@ def get_courses(): "title", "short_introduction", "image", - "enable_certification", - "paid_certificate", - "price_certificate", + "paid_course", + "course_price", "currency", ], )