From 7777bd02e3c6b16576f2b0b2ac7848d4b0a96807 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Thu, 27 Apr 2023 10:20:50 +0530 Subject: [PATCH] feat: lesson edit page --- lms/lms/doctype/lms_quiz/lms_quiz.json | 17 +- lms/lms/doctype/lms_quiz/lms_quiz.py | 7 + lms/lms/utils.py | 1 - lms/public/css/style.css | 11 +- lms/public/js/common_functions.js | 13 ++ lms/public/js/editor.js | 10 +- lms/public/js/website.bundle.js | 1 - lms/www/batch/edit.html | 63 ++++-- lms/www/batch/edit.js | 84 ++++++++ lms/www/batch/edit.py | 2 +- lms/www/courses/create.html | 282 +++++++++++++------------ lms/www/courses/create.js | 15 +- lms/www/courses/outline.html | 200 +++++++++++------- lms/www/courses/outline.js | 56 ++++- lms/www/utils.py | 4 +- 15 files changed, 504 insertions(+), 262 deletions(-) create mode 100644 lms/www/batch/edit.js diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.json b/lms/lms/doctype/lms_quiz/lms_quiz.json index a3221040..90390bea 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.json +++ b/lms/lms/doctype/lms_quiz/lms_quiz.json @@ -69,7 +69,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2022-11-15 15:36:39.585488", + "modified": "2023-04-26 17:48:29.664013", "modified_by": "Administrator", "module": "LMS", "name": "LMS Quiz", @@ -86,6 +86,21 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "select": 1, + "share": 1, + "write": 1 } ], "show_title_field_in_link": 1, diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.py b/lms/lms/doctype/lms_quiz/lms_quiz.py index f9b4326a..fa49b0a9 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.py +++ b/lms/lms/doctype/lms_quiz/lms_quiz.py @@ -192,3 +192,10 @@ def check_input_answers(question, answer): return 1 return 0 + + +@frappe.whitelist() +def get_user_quizzes(): + return frappe.get_all( + "LMS Quiz", filters={"owner": frappe.session.user}, fields=["name", "title"] + ) diff --git a/lms/lms/utils.py b/lms/lms/utils.py index a7c0870b..b7574fa3 100644 --- a/lms/lms/utils.py +++ b/lms/lms/utils.py @@ -275,7 +275,6 @@ def get_progress(course, lesson): def render_html(lesson): - print(lesson) youtube = lesson.youtube quiz_id = lesson.quiz_id body = lesson.body diff --git a/lms/public/css/style.css b/lms/public/css/style.css index 41bcf7ca..3a670fb6 100644 --- a/lms/public/css/style.css +++ b/lms/public/css/style.css @@ -22,7 +22,7 @@ } .page-title { - font-size: 1.5rem; + font-size: 1.25rem; font-weight: 600; color: var(--gray-900); line-height: 160%; @@ -93,7 +93,7 @@ } .field-group { - margin-bottom: 2rem; + margin-bottom: 1.5rem; } .field-description { @@ -125,9 +125,8 @@ textarea.field-input { padding: 1rem 0; } -.common-card-style .outline-lesson:last-child { +.common-card-style .outline-lesson:last-of-type { border-bottom: none; - padding-bottom: 0; } .level { @@ -364,12 +363,12 @@ input[type=checkbox] { } .button-links { - color: var(--gray-900); + color: inherit; } .button-links:hover { text-decoration: none; - color: var(--gray-900); + color: inherit; } .icon-background { diff --git a/lms/public/js/common_functions.js b/lms/public/js/common_functions.js index 56306d1a..c0a51a6c 100644 --- a/lms/public/js/common_functions.js +++ b/lms/public/js/common_functions.js @@ -1,5 +1,6 @@ frappe.ready(() => { setup_file_size(); + pin_header(); $(".join-batch").click((e) => { join_course(e); @@ -57,6 +58,18 @@ frappe.ready(() => { }); }); +const pin_header = () => { + const el = document.querySelector(".sticky"); + if (el) { + const observer = new IntersectionObserver( + ([e]) => + e.target.classList.toggle("is-pinned", e.intersectionRatio < 1), + { threshold: [1] } + ); + observer.observe(el); + } +}; + const setSortable = (el) => { new Sortable(el, { group: { diff --git a/lms/public/js/editor.js b/lms/public/js/editor.js index 0d12c157..29a8800d 100644 --- a/lms/public/js/editor.js +++ b/lms/public/js/editor.js @@ -2,9 +2,10 @@ import EditorJS from "@editorjs/editorjs"; import Header from "@editorjs/header"; import List from "@editorjs/list"; -const create_editor_for_short_description = () => { - let editor = new EditorJS({ - holder: "course-description", +let self = this; +const create_editor_for_lesson_content = () => { + self.editor = new EditorJS({ + holder: "lesson-content", tools: { header: { class: Header, @@ -13,3 +14,6 @@ const create_editor_for_short_description = () => { }, }); }; + +create_editor_for_lesson_content(); +console.log(self.editor); diff --git a/lms/public/js/website.bundle.js b/lms/public/js/website.bundle.js index 51b30e76..7d1dfd83 100644 --- a/lms/public/js/website.bundle.js +++ b/lms/public/js/website.bundle.js @@ -1,4 +1,3 @@ import "./profile.js"; import "./common_functions.js"; -import "./editor.js"; import "../../../../frappe/frappe/public/js/frappe/ui/chart.js"; diff --git a/lms/www/batch/edit.html b/lms/www/batch/edit.html index 8281851a..4e301986 100644 --- a/lms/www/batch/edit.html +++ b/lms/www/batch/edit.html @@ -9,17 +9,56 @@ {% endblock %} {% block content %} -
-
-
-
- {{ _("Title") }} -
-
- {{ _("Something short and concise.") }} -
- +
+ {{ Header() }} +
+ {{ CreateLesson() }} +
+
+{% endblock %} + + +{% macro Header() %} +
+
+ + +
+ {{ course.title if course.name else _("Course Outline") }}
-
-{% endblock %} \ No newline at end of file + +{% endmacro %} + +{% macro CreateLesson() %} +
+
+
+
+ {{ _("Title") }} +
+
+ {{ _("Something Short and Concise") }} +
+
+
+ +
+
+ +
+ +
+{% endmacro %} + + +{%- block script %} + {{ super() }} + {{ include_script('controls.bundle.js') }} + +{% endblock %} + diff --git a/lms/www/batch/edit.js b/lms/www/batch/edit.js new file mode 100644 index 00000000..22fce367 --- /dev/null +++ b/lms/www/batch/edit.js @@ -0,0 +1,84 @@ +frappe.ready(() => { + let self = this; + setup_editor(); + fetch_quiz_list(); + + $("#save-lesson").click((e) => { + save_lesson(e); + }); +}); + +const setup_editor = () => { + self.editor = new EditorJS({ + holder: "lesson-content", + tools: { + youtube: YouTubeVideo, + quiz: Quiz, + }, + }); +}; + +const save_lesson = (e) => { + self.editor.save().then((outputData) => { + console.log(outputData); + }); +}; + +class YouTubeVideo { + static get toolbox() { + return { + title: "YouTube Video", + }; + } + + render() { + this.wrapper = document.createElement("div"); + $(this.wrapper).html(`
+
+ +
+
`); + return this.wrapper; + } + + save(block_content) { + return { + youtube: $("#youtube").val(), + }; + } +} + +class Quiz { + static get toolbox() { + return { + title: "Quiz", + }; + } + + render() { + this.wrapper = document.createElement("div"); + $(this.wrapper).html( + `
+ +
` + ); + self.quiz_list.forEach((quiz) => { + $(this.wrapper) + .find("#quiz") + .append(``); + }); + + return this.wrapper; + } +} + +const fetch_quiz_list = () => { + frappe.call({ + method: "lms.lms.doctype.lms_quiz.lms_quiz.get_user_quizzes", + callback: (r) => { + self.quiz_list = r.message; + }, + }); +}; diff --git a/lms/www/batch/edit.py b/lms/www/batch/edit.py index e7199149..6c8d6620 100644 --- a/lms/www/batch/edit.py +++ b/lms/www/batch/edit.py @@ -7,4 +7,4 @@ def get_context(context): chapter_index = frappe.form_dict.get("chapter") lesson_index = frappe.form_dict.get("lesson") lesson_number = f"{chapter_index}.{lesson_index}" - context.lesson = get_current_lesson_details(lesson_number, context) + context.lesson = get_current_lesson_details(lesson_number, context, True) diff --git a/lms/www/courses/create.html b/lms/www/courses/create.html index c3cbe242..c1620f36 100644 --- a/lms/www/courses/create.html +++ b/lms/www/courses/create.html @@ -5,148 +5,156 @@ {% block content %}
- -
-
- - -
{{ _("Course Details") }}
-
-
- + {{ Header() }}
- -
-
-
-
- {{ _("Title") }} -
-
- {{ _("Something Short and Concise") }} -
-
-
- -
-
- -
-
-
- {{ _("Preview Video") }} -
-
- {{ _("A feature video that provides a preview of the course") }} -
-
-
- -
-
- -
-
-
- {{ _("Short Introduction") }} -
-
- {{ _("A one line breif description") }} -
-
-
- -
-
- -
-
-
- {{ _("Tags") }} -
-
- {{ _("Add suitable tags") }} -
-
-
- {% for tag in get_tags(course.name) %} - - {% endfor %} - -
-
- -
- - -
- -
-
-
- {{ _("Course Image") }} -
-
- {{ _("Add an appropriate image") }} -
-
-
- -
- -
- -
-
-
- {{ _("Course Description") }} -
-
- {{ _("Add a detailed description") }} -
-
-
- {% if course.description %} -
- {{ course.description }} -
- {% endif %} -
- -
-
- {{ _("Instructor") }} -
-
- {{ widgets.Avatar(member=member, avatar_class="avatar-medium") }} - - {{ member.full_name }} - -
-
- -
+ {{ CreateCourse() }}
{% endblock %} + +{% macro Header() %} +
+
+ + +
{{ _("Course Details") }}
+
+
+{% endmacro %} + + +{% macro CreateCourse() %} +
+
+
+
+ {{ _("Title") }} +
+
+ {{ _("Something Short and Concise") }} +
+
+
+ +
+
+ +
+
+
+ {{ _("Preview Video") }} +
+
+ {{ _("A feature video that provides a preview of the course") }} +
+
+
+ +
+
+ +
+
+
+ {{ _("Short Introduction") }} +
+
+ {{ _("A one line breif description") }} +
+
+
+ +
+
+ +
+
+
+ {{ _("Tags") }} +
+
+ {{ _("Add suitable tags") }} +
+
+
+ {% for tag in get_tags(course.name) %} + + {% endfor %} + +
+
+ +
+ + +
+ +
+
+
+ {{ _("Course Image") }} +
+
+ {{ _("Add an appropriate image") }} +
+
+
+ +
+ +
+ +
+
+
+ {{ _("Course Description") }} +
+
+ {{ _("Add a detailed description") }} +
+
+
+ {% if course.description %} +
+ {{ course.description }} +
+ {% endif %} +
+ +
+
+ {{ _("Instructor") }} +
+
+ {{ widgets.Avatar(member=member, avatar_class="avatar-medium") }} + + {{ member.full_name }} + +
+
+ +
+{% endmacro %} + + {%- block script %} {{ super() }} {{ include_script('controls.bundle.js') }} diff --git a/lms/www/courses/create.js b/lms/www/courses/create.js index 09324f74..c6706370 100644 --- a/lms/www/courses/create.js +++ b/lms/www/courses/create.js @@ -1,6 +1,4 @@ frappe.ready(() => { - pin_header(); - $(".tags").click((e) => { e.preventDefault(); $("#tags-input").focus(); @@ -94,23 +92,14 @@ const make_editor = () => { ], body: $("#description").get(0), }); + console.log(this.description); this.description.make(); + console.log(this.description); $("#description .form-section:last").removeClass("empty-section"); $("#description .frappe-control").removeClass("hide-control"); $("#description .form-column").addClass("p-0"); }; -const pin_header = () => { - const el = document.querySelector(".sticky"); - const observer = new IntersectionObserver( - ([e]) => - e.target.classList.toggle("is-pinned", e.intersectionRatio < 1), - { threshold: [1] } - ); - - observer.observe(el); -}; - const upload_file = (e) => { new frappe.ui.FileUploader({ disable_file_browser: true, diff --git a/lms/www/courses/outline.html b/lms/www/courses/outline.html index 1fdda8b0..eee6fe9e 100644 --- a/lms/www/courses/outline.html +++ b/lms/www/courses/outline.html @@ -7,102 +7,150 @@ {% block page_content %}
- -
-
- - -
{{ _("Course Outline") }}
-
-
- -
- {{ Outline(chapters) }} + {{ Header() }} +
+ {% if chapters | length %} + {{ Outline(chapters) }} + {% else %} + {{ EmptyState() }} + {% endif %} {{ CreateChapter() }} - {{ EmptyState() }}
{% endblock %} -{% macro Outline(chapters) %} -{% if chapters %} -{% for chapter in chapters %} -
-
-
- - - -
- {{ chapter.title }} -
-
- {% for lesson in get_lessons(course.name, chapter) %} -
- - - - -
- {{ lesson.title }} -
-
- {% endfor %} - - + +
+ {{ course.title if course.name else _("Course Outline") }} +
-
-{% endfor %} -{% endif %} + +{% endmacro %} + + +{% macro Outline(chapters) %} + {% if chapters %} + {% for chapter in chapters %} + {% set chapter_index = loop.index %} + {% set lessons = get_lessons(course.name, chapter) %} + + {% endfor %} + {% endif %} + {% endmacro %} {% macro CreateChapter() %} -
-
-
-
-
- {{ _("Chapter Title") }} -
-
- {{ _("Something Short and Concise") }} -
+
+
{% endmacro %} diff --git a/lms/www/courses/outline.js b/lms/www/courses/outline.js index ae5ec9c1..635b34e5 100644 --- a/lms/www/courses/outline.js +++ b/lms/www/courses/outline.js @@ -1,13 +1,51 @@ frappe.ready(() => { - pin_header(); + $("#add-chapter").click((e) => { + show_chapter_modal(e); + }); + + $(".edit-chapter").click((e) => { + show_chapter_modal(e); + }); + + $("#save-chapter").click((e) => { + save_chapter(e); + }); }); -const pin_header = () => { - const el = document.querySelector(".sticky"); - const observer = new IntersectionObserver( - ([e]) => - e.target.classList.toggle("is-pinned", e.intersectionRatio < 1), - { threshold: [1] } - ); - observer.observe(el); +const show_chapter_modal = (e) => { + e.preventDefault(); + $("#chapter-modal").modal("show"); + let parent = $(e.currentTarget).closest(".chapter-container"); + if (parent) { + $("#chapter-title").val($.trim(parent.find(".chapters-title").text())); + $("#chapter-description").val( + $.trim(parent.find(".chapter-description").text()) + ); + $("#chapter-modal").data("chapter", parent.data("chapter")); + $("#chapter-modal").data("idx", parent.data("idx")); + } +}; + +const save_chapter = (e) => { + let parent = $("#chapter-modal"); + + frappe.call({ + method: "lms.lms.doctype.lms_course.lms_course.save_chapter", + args: { + course: $("#course-outline").data("course"), + title: $("#chapter-title").val(), + chapter_description: $("#chapter-description").val(), + idx: parent.data("idx") || $(".chapter-container").length + 1, + chapter: parent.data("chapter") || null, + }, + callback: (data) => { + frappe.show_alert({ + message: __("Saved"), + indicator: "green", + }); + setTimeout(() => { + window.location.reload(); + }, 1000); + }, + }); }; diff --git a/lms/www/utils.py b/lms/www/utils.py index 3eca7d10..cd8208b7 100644 --- a/lms/www/utils.py +++ b/lms/www/utils.py @@ -44,11 +44,11 @@ def redirect_to_lesson(course, index_="1.1"): raise frappe.Redirect -def get_current_lesson_details(lesson_number, context): +def get_current_lesson_details(lesson_number, context, is_edit=False): details_list = list(filter(lambda x: cstr(x.number) == lesson_number, context.lessons)) if not len(details_list): - if frappe.form_dict.get("edit"): + if is_edit: return None else: redirect_to_lesson(context.course)