feat: update lesson
This commit is contained in:
@@ -258,3 +258,36 @@ def save_chapter(course, title, chapter_description, idx, chapter):
|
|||||||
chapter_reference.save(ignore_permissions=True)
|
chapter_reference.save(ignore_permissions=True)
|
||||||
|
|
||||||
return doc.name
|
return doc.name
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def save_lesson(title, body, chapter, preview, idx, lesson):
|
||||||
|
if lesson:
|
||||||
|
doc = frappe.get_doc("Course Lesson", lesson)
|
||||||
|
else:
|
||||||
|
doc = frappe.get_doc({
|
||||||
|
"doctype": "Course Lesson"
|
||||||
|
})
|
||||||
|
|
||||||
|
doc.update({
|
||||||
|
"chapter": chapter,
|
||||||
|
"title": title,
|
||||||
|
"body": body,
|
||||||
|
"include_in_preview": preview
|
||||||
|
})
|
||||||
|
doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
if lesson:
|
||||||
|
lesson_reference = frappe.get_doc("Lesson Reference", {"lesson": lesson})
|
||||||
|
else:
|
||||||
|
lesson_reference = frappe.get_doc({
|
||||||
|
"doctype": "Lesson Reference",
|
||||||
|
"parent": chapter,
|
||||||
|
"parenttype": "Course Chapter",
|
||||||
|
"parentfield": "lessons",
|
||||||
|
"idx": idx
|
||||||
|
})
|
||||||
|
|
||||||
|
lesson_reference.update({"lesson": doc.name})
|
||||||
|
lesson_reference.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
return doc.name
|
||||||
|
|||||||
@@ -22,8 +22,10 @@
|
|||||||
<div class="w-100 chapter-title-main" {% if course.edit_mode %} contenteditable="true" {% endif %} >{{ chapter.title }}</div>
|
<div class="w-100 chapter-title-main" {% if course.edit_mode %} contenteditable="true" {% endif %} >{{ chapter.title }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% set lessons = get_lessons(course.name, chapter) %}
|
||||||
|
|
||||||
<div class="chapter-content {% if not course.edit_mode %} collapse navbar-collapse {% endif %} "
|
<div class="chapter-content {% if not course.edit_mode %} collapse navbar-collapse {% endif %} "
|
||||||
id="{{ get_slugified_chapter_title(chapter.title) }}">
|
id="{{ get_slugified_chapter_title(chapter.title) }}">
|
||||||
|
|
||||||
{% if chapter.description or course.edit_mode %}
|
{% if chapter.description or course.edit_mode %}
|
||||||
<div {% if course.edit_mode %} contenteditable="true" {% endif %} class="chapter-description
|
<div {% if course.edit_mode %} contenteditable="true" {% endif %} class="chapter-description
|
||||||
@@ -35,25 +37,28 @@
|
|||||||
<div class="mt-2 mb-8">
|
<div class="mt-2 mb-8">
|
||||||
<button class="btn btn-sm btn-secondary btn-save-chapter"
|
<button class="btn btn-sm btn-secondary btn-save-chapter"
|
||||||
data-index="{{ loop.index }}" data-chapter="{{ chapter.name }}"> {{ _('Save') }} </button>
|
data-index="{{ loop.index }}" data-chapter="{{ chapter.name }}"> {{ _('Save') }} </button>
|
||||||
<a class="btn btn-sm btn-secondary btn-lesson ml-4" href="/courses/{{ course.name }}/learn/0.0"> {{ _("New Lesson") }} </a>
|
<a class="btn btn-sm btn-secondary btn-lesson ml-4"
|
||||||
|
href="/courses/{{ course.name }}/learn/{{loop.index}}.{{ lessons | length + 1 }}?edit=1"> {{ _("New Lesson") }} </a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% set is_instructor = is_instructor(course.name) %}
|
{% set is_instructor = is_instructor(course.name) %}
|
||||||
<div class="lessons">
|
<div class="lessons">
|
||||||
|
|
||||||
{% for lesson in get_lessons(course.name, chapter) %}
|
{% for lesson in lessons %}
|
||||||
{% set active = membership.current_lesson == lesson.name %}
|
{% set active = membership.current_lesson == lesson.name %}
|
||||||
<div class="lesson-info {% if active and not course.edit_mode %} active-lesson {% endif %}">
|
<div class="lesson-info {% if active and not course.edit_mode %} active-lesson {% endif %}">
|
||||||
|
|
||||||
{% if membership or lesson.include_in_preview %}
|
{% if membership or lesson.include_in_preview %}
|
||||||
<a class="lesson-links" href="{{ get_lesson_url(course.name, lesson.number) }}{{course.query_parameter}}"
|
<a class="lesson-links" href="{{ get_lesson_url(course.name, lesson.number) }}{{course.query_parameter}}"
|
||||||
data-course="{{ course.name }}">
|
data-course="{{ course.name }}">
|
||||||
|
|
||||||
<svg class="icon icon-sm mr-2">
|
<svg class="icon icon-sm mr-2">
|
||||||
<use class="" href="#{{ lesson.icon }}">
|
<use class="" href="#{{ lesson.icon }}">
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span>{{ lesson.title }}</span>
|
<span>{{ lesson.title }}</span>
|
||||||
|
|
||||||
{% if membership %}
|
{% if membership %}
|
||||||
<svg class="icon icon-sm lesson-progress-tick {{ get_progress(course.name, lesson.name) != 'Complete' and 'hide' }}">
|
<svg class="icon icon-sm lesson-progress-tick {{ get_progress(course.name, lesson.name) != 'Complete' and 'hide' }}">
|
||||||
<use class="" href="#icon-green-check">
|
<use class="" href="#icon-green-check">
|
||||||
@@ -63,10 +68,9 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
{% elif is_instructor and not lesson.include_in_preview %}
|
{% elif is_instructor and not lesson.include_in_preview %}
|
||||||
<a class="lesson-links"
|
<a class="lesson-links" data-course="{{ course.name }}"
|
||||||
title="This lesson is not available for preview. As you are the Instructor of the course only you can see it."
|
title="This lesson is not available for preview. As you are the Instructor of the course only you can see it."
|
||||||
href="{{ get_lesson_url(course.name, lesson.number) }}{{course.query_parameter}}"
|
href="{{ get_lesson_url(course.name, lesson.number) }}{% if course.edit_mode %}?edit=1{% endif %}{{course.query_parameter}}">
|
||||||
data-course="{{ course.name }}">
|
|
||||||
|
|
||||||
<svg class="icon icon-sm mr-2">
|
<svg class="icon icon-sm mr-2">
|
||||||
<use class="" href="#icon-lock">
|
<use class="" href="#icon-lock">
|
||||||
|
|||||||
@@ -1421,6 +1421,7 @@ pre {
|
|||||||
.attachments-parent {
|
.attachments-parent {
|
||||||
float: right;
|
float: right;
|
||||||
font-size: var(--text-sm);
|
font-size: var(--text-sm);
|
||||||
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
@@ -1506,10 +1507,8 @@ li {
|
|||||||
border: 1px solid var(--gray-400);
|
border: 1px solid var(--gray-400);
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
}
|
|
||||||
|
|
||||||
[contenteditable] {
|
|
||||||
outline: none;
|
outline: none;
|
||||||
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
[contenteditable]:empty:before{
|
[contenteditable]:empty:before{
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
frappe.ready(() => {
|
frappe.ready(() => {
|
||||||
|
|
||||||
|
setup_vue_and_file_size();
|
||||||
|
|
||||||
$(".join-batch").click((e) => {
|
$(".join-batch").click((e) => {
|
||||||
join_course(e);
|
join_course(e);
|
||||||
});
|
});
|
||||||
@@ -17,6 +20,28 @@ frappe.ready(() => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const setup_vue_and_file_size = () => {
|
||||||
|
frappe.require("/assets/frappe/node_modules/vue/dist/vue.js", () => {
|
||||||
|
Vue.prototype.__ = window.__;
|
||||||
|
Vue.prototype.frappe = window.frappe;
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.provide("frappe.form.formatters");
|
||||||
|
frappe.form.formatters.FileSize = file_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const file_size = (value) => {
|
||||||
|
if(value > 1048576) {
|
||||||
|
value = flt(flt(value) / 1048576, 1) + "M";
|
||||||
|
} else if (value > 1024) {
|
||||||
|
value = flt(flt(value) / 1024, 1) + "K";
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const join_course = (e) => {
|
const join_course = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let course = $(e.currentTarget).attr("data-course");
|
let course = $(e.currentTarget).attr("data-course");
|
||||||
|
|||||||
@@ -25,18 +25,18 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="common-page-style lesson-page">
|
<div class="common-page-style lesson-page">
|
||||||
<div class="container course-details-page">
|
<div class="container course-details-page">
|
||||||
{{ BreadCrumb(course, lesson) }}
|
{{ BreadCrumb(course, lesson) }}
|
||||||
<div class="course-content-parent">
|
<div class="course-content-parent">
|
||||||
<div class="course-details-outline">
|
<div class="course-details-outline">
|
||||||
{{ widgets.CourseOutline(course=course, membership=membership) }}
|
{{ widgets.CourseOutline(course=course, membership=membership) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="lesson-pagination-parent">
|
<div class="lesson-pagination-parent">
|
||||||
{{ LessonContent(lesson) }}
|
{{ LessonContent(lesson) }}
|
||||||
{{ Discussions() }}
|
{% if not lesson.edit_mode %} {{ Discussions() }} {% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -58,63 +58,106 @@
|
|||||||
{% set instructors = get_instructors(course.name) %}
|
{% set instructors = get_instructors(course.name) %}
|
||||||
{% set is_instructor = is_instructor(course.name) %}
|
{% set is_instructor = is_instructor(course.name) %}
|
||||||
<div class="common-card-style lesson-content">
|
<div class="common-card-style lesson-content">
|
||||||
<div class="lesson-title">
|
<div class="lesson-title">
|
||||||
|
|
||||||
<div class="course-home-headings title mb-0 {% if membership %} is-member {% endif %}
|
<!-- Title -->
|
||||||
{% if membership or is_instructor %} eligible-for-submission {% endif %}"
|
<div class="course-home-headings title mb-0 {% if membership %} is-member {% endif %}
|
||||||
{% if lesson.edit_mode %} data-placeholder="{{ _('Title') }}" contenteditable="true" {% endif %}
|
{% if membership or is_instructor %} eligible-for-submission {% endif %}" id="title"
|
||||||
data-lesson="{{ lesson.name }}" data-course="{{ course.name }}"
|
{% if lesson.edit_mode %} data-placeholder="{{ _('Title') }}" contenteditable="true" {% endif %}
|
||||||
> {% if lesson.title %} {{ lesson.title }} {% endif %} </div>
|
data-index="{{ lesson_index }}" data-course="{{ course.name }}" data-chapter="{{ chapter }}"
|
||||||
<span class="lesson-progress {{hide if get_progress(course.name, lesson.name) != 'Complete' else ''}}">{{ _("COMPLETED") }}</span>
|
{% if lesson.name %} data-lesson="{{ lesson.name }}" {% endif %}
|
||||||
|
>{% if lesson.title %}{{ lesson.title }}{% endif %}</div>
|
||||||
|
<span class="lesson-progress {{ hide if get_progress(course.name, lesson.name) != 'Complete' else ''}}">{{ _("COMPLETED") }}</span>
|
||||||
|
|
||||||
{% if is_instructor %}
|
<!-- Edit Button -->
|
||||||
<a class="button is-default button-links ml-auto" href="/lesson?name={{ lesson.name }}"> {{ _("Edit") }} </a>
|
{% if is_instructor and not lesson.edit_mode %}
|
||||||
{% endif %}
|
<button class="button is-default button-links ml-auto btn-edit"> {{ _("Edit") }} </button>
|
||||||
</div>
|
{% endif %}
|
||||||
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
{% set ins_len = instructors | length %}
|
|
||||||
{% for instructor in instructors %}
|
|
||||||
{% if ins_len > 1 and loop.index == 1 %}
|
|
||||||
<div class="avatar-group overlap">
|
|
||||||
{% endif %}
|
|
||||||
{{ widgets.Avatar(member=instructor, avatar_class="avatar-small") }}
|
|
||||||
|
|
||||||
{% if ins_len > 1 and loop.index == ins_len %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<a class="button-links ml-1" href="{{ get_profile_url(instructors[0].username) }}">
|
|
||||||
<span class="course-meta">
|
|
||||||
{% if ins_len == 1 %}
|
|
||||||
{{ instructors[0].full_name }}
|
|
||||||
{% else %}
|
|
||||||
{% set suffix = _("other") if ins_len - 1 == 1 else _("others") %}
|
|
||||||
{{ instructors[0].full_name.split(" ")[0] }} and {{ ins_len - 1 }} {{ suffix }}
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<div class="ml-5 course-meta"> {{ frappe.utils.format_date(lesson.creation, "medium") }} </div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="markdown-source lesson-content-card">
|
<!-- Instructors -->
|
||||||
{% if membership or lesson.include_in_preview or is_instructor %}
|
<div class="d-flex align-items-center">
|
||||||
{% if is_instructor and not lesson.include_in_preview %}
|
{% set ins_len = instructors | length %}
|
||||||
<div class="small alert alert-secondary alert-dismissible mt-4 mb-4">
|
{% for instructor in instructors %}
|
||||||
{{ _("This lesson is not available for preview. As you are the Instructor of the course only you can see it.") }}
|
{% if ins_len > 1 and loop.index == 1 %}
|
||||||
<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>
|
<div class="avatar-group overlap">
|
||||||
</div>
|
{% endif %}
|
||||||
{% endif %}
|
{{ widgets.Avatar(member=instructor, avatar_class="avatar-small") }}
|
||||||
{{ render_html(lesson.body) }}
|
|
||||||
{% else %}
|
|
||||||
<div class="">
|
|
||||||
<div class="button is-primary pull-right join-batch" data-course="{{ course.name | urlencode }}"> {{ _("Start Learning") }} </div>
|
|
||||||
<div class=""> {{ _("This lesson is not available for preview. Please join the course to access it.") }} </div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{ pagination(prev_url, next_url) }}
|
{% if ins_len > 1 and loop.index == ins_len %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<a class="button-links ml-1" href="{{ get_profile_url(instructors[0].username) }}">
|
||||||
|
<span class="course-meta">
|
||||||
|
{% if ins_len == 1 %}
|
||||||
|
{{ instructors[0].full_name }}
|
||||||
|
{% else %}
|
||||||
|
{% set suffix = _("other") if ins_len - 1 == 1 else _("others") %}
|
||||||
|
{{ instructors[0].full_name.split(" ")[0] }} and {{ ins_len - 1 }} {{ suffix }}
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<div class="ml-5 course-meta"> {{ frappe.utils.format_date(lesson.creation, "medium") }} </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Lesson Content -->
|
||||||
|
<div class="markdown-source lesson-content-card">
|
||||||
|
{% if membership or lesson.include_in_preview or is_instructor %}
|
||||||
|
|
||||||
|
{% if is_instructor and not lesson.include_in_preview and not lesson.edit_mode %}
|
||||||
|
<div class="small alert alert-secondary alert-dismissible mb-4">
|
||||||
|
{{ _("This lesson is not available for preview. As you are the Instructor of the course only you can see it.") }}
|
||||||
|
<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if lesson.edit_mode %}
|
||||||
|
|
||||||
|
<div class="attachments-parent">
|
||||||
|
<div class="attachment-controls">
|
||||||
|
<div class="show-attachments" data-toggle="collapse" data-target="#collapse-attachments" aria-expanded="false">
|
||||||
|
<svg class="icon icon-sm">
|
||||||
|
<use class="" href="#icon-attachment">
|
||||||
|
</svg>
|
||||||
|
<span class="attachment-count" data-count="0">0 {{ _("attachments") }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="add-attachment">
|
||||||
|
<span class="button is-secondary">
|
||||||
|
<svg class="icon icon-sm">
|
||||||
|
<use class="" href="#icon-upload">
|
||||||
|
</svg>
|
||||||
|
{{ _("Upload Attachments") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class="attachments common-card-style collapse hide" id="collapse-attachments"></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="d-flex align-items-center mb-4 small">
|
||||||
|
<input {% if lesson.include_in_preview %} checked {% endif %}
|
||||||
|
type="checkbox" id="preview"> {{ _("Show preview of this lesson") }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div contenteditable="true" data-placeholder="{{ _('Enter the lesson content.') }}"
|
||||||
|
id="body"> {% if lesson.body %} {{ lesson.body }} {% endif %} </div>
|
||||||
|
<button class="btn btn-primary btn-md mt-8 btn-lesson pull-right"> {{ _("Save") }} </button>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
{{ render_html(lesson.body) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<div class="">
|
||||||
|
<div class="button is-primary pull-right join-batch" data-course="{{ course.name | urlencode }}"> {{ _("Start Learning") }} </div>
|
||||||
|
<div class=""> {{ _("This lesson is not available for preview. Please join the course to access it.") }} </div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if not lesson.edit_mode %}
|
||||||
|
{{ pagination(prev_url, next_url) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|||||||
@@ -45,12 +45,29 @@ frappe.ready(() => {
|
|||||||
clear_work(e);
|
clear_work(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".btn-lesson").click((e) => {
|
||||||
|
save_lesson(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".add-attachment").click((e) => {
|
||||||
|
show_upload_modal();
|
||||||
|
});
|
||||||
|
|
||||||
$(".btn-start-quiz").click((e) => {
|
$(".btn-start-quiz").click((e) => {
|
||||||
$("#start-banner").addClass("hide");
|
$("#start-banner").addClass("hide");
|
||||||
$("#quiz-form").removeClass("hide");
|
$("#quiz-form").removeClass("hide");
|
||||||
mark_active_question();
|
mark_active_question();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".btn-edit").click((e) => {
|
||||||
|
window.location.href = `${window.location.href}?edit=1`;
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("click", ".copy-link", (e) => {
|
||||||
|
frappe.utils.copy_to_clipboard($(e.currentTarget).data("link"));
|
||||||
|
$(".attachments").collapse("hide");
|
||||||
|
});
|
||||||
|
|
||||||
if ($("#quiz-title").data("max-attempts")) {
|
if ($("#quiz-title").data("max-attempts")) {
|
||||||
window.addEventListener("beforeunload", (e) => {
|
window.addEventListener("beforeunload", (e) => {
|
||||||
e.returnValue = "";
|
e.returnValue = "";
|
||||||
@@ -59,23 +76,26 @@ frappe.ready(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const save_current_lesson = () => {
|
const save_current_lesson = () => {
|
||||||
if ($(".title").hasClass("is-member")) {
|
if ($(".title").hasClass("is-member")) {
|
||||||
frappe.call("lms.lms.api.save_current_lesson", {
|
frappe.call("lms.lms.api.save_current_lesson", {
|
||||||
course_name: $(".title").attr("data-course"),
|
course_name: $(".title").attr("data-course"),
|
||||||
lesson_name: $(".title").attr("data-lesson")
|
lesson_name: $(".title").attr("data-lesson")
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const enable_check = (e) => {
|
const enable_check = (e) => {
|
||||||
if ($(".option:checked").length) {
|
if ($(".option:checked").length) {
|
||||||
$("#check").removeAttr("disabled");
|
$("#check").removeAttr("disabled");
|
||||||
$(".custom-checkbox").removeClass("active-option");
|
$(".custom-checkbox").removeClass("active-option");
|
||||||
$(".option:checked").closest(".custom-checkbox").addClass("active-option");
|
$(".option:checked").closest(".custom-checkbox").addClass("active-option");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const mark_active_question = (e = undefined) => {
|
const mark_active_question = (e = undefined) => {
|
||||||
$(".timer").addClass("hide");
|
$(".timer").addClass("hide");
|
||||||
calculate_and_display_time(100);
|
calculate_and_display_time(100);
|
||||||
@@ -92,82 +112,87 @@ const mark_active_question = (e = undefined) => {
|
|||||||
initialize_timer();
|
initialize_timer();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const mark_progress = (e) => {
|
const mark_progress = (e) => {
|
||||||
/* Prevent default only for Next button anchor tag and not for progress checkbox */
|
/* Prevent default only for Next button anchor tag and not for progress checkbox */
|
||||||
if ($(e.currentTarget).prop("nodeName") != "INPUT")
|
if ($(e.currentTarget).prop("nodeName") != "INPUT")
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const target = $(e.currentTarget).attr("data-progress") ? $(e.currentTarget) : $("input.mark-progress");
|
const target = $(e.currentTarget).attr("data-progress") ? $(e.currentTarget) : $("input.mark-progress");
|
||||||
const current_status = $(".lesson-progress").hasClass("hide") ? "Incomplete": "Complete";
|
const current_status = $(".lesson-progress").hasClass("hide") ? "Incomplete": "Complete";
|
||||||
|
|
||||||
let status = "Incomplete";
|
let status = "Incomplete";
|
||||||
if (target.prop("nodeName") == "INPUT" && target.prop("checked")) {
|
if (target.prop("nodeName") == "INPUT" && target.prop("checked")) {
|
||||||
status = "Complete";
|
status = "Complete";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != current_status) {
|
if (status != current_status) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "lms.lms.doctype.course_lesson.course_lesson.save_progress",
|
method: "lms.lms.doctype.course_lesson.course_lesson.save_progress",
|
||||||
args: {
|
args: {
|
||||||
lesson: $(".title").attr("data-lesson"),
|
lesson: $(".title").attr("data-lesson"),
|
||||||
course: $(".title").attr("data-course"),
|
course: $(".title").attr("data-course"),
|
||||||
status: status
|
status: status
|
||||||
},
|
},
|
||||||
callback: (data) => {
|
callback: (data) => {
|
||||||
change_progress_indicators(status, e);
|
change_progress_indicators(status, e);
|
||||||
show_certificate_if_course_completed(data);
|
show_certificate_if_course_completed(data);
|
||||||
|
move_to_next_lesson(status, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
move_to_next_lesson(status, e);
|
move_to_next_lesson(status, e);
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
move_to_next_lesson(status, e);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const change_progress_indicators = (status, e) => {
|
const change_progress_indicators = (status, e) => {
|
||||||
if (status == "Complete") {
|
if (status == "Complete") {
|
||||||
$(".lesson-progress").removeClass("hide");
|
$(".lesson-progress").removeClass("hide");
|
||||||
$(".active-lesson .lesson-progress-tick").removeClass("hide");
|
$(".active-lesson .lesson-progress-tick").removeClass("hide");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$(".lesson-progress").addClass("hide");
|
$(".lesson-progress").addClass("hide");
|
||||||
$(".active-lesson .lesson-progress-tick").addClass("hide");
|
$(".active-lesson .lesson-progress-tick").addClass("hide");
|
||||||
}
|
}
|
||||||
if (status == "Incomplete" && !$(e.currentTarget).hasClass("next")) {
|
if (status == "Incomplete" && !$(e.currentTarget).hasClass("next")) {
|
||||||
$(e.currentTarget).addClass("hide");
|
$(e.currentTarget).addClass("hide");
|
||||||
$("input.mark-progress").prop("checked", false).closest(".custom-checkbox").removeClass("hide");
|
$("input.mark-progress").prop("checked", false).closest(".custom-checkbox").removeClass("hide");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const show_certificate_if_course_completed = (data) => {
|
const show_certificate_if_course_completed = (data) => {
|
||||||
if (data.message == 100 && !$(".next").attr("data-next") && $("#certification").hasClass("hide")) {
|
if (data.message == 100 && !$(".next").attr("data-next") && $("#certification").hasClass("hide")) {
|
||||||
$("#certification").removeClass("hide");
|
$("#certification").removeClass("hide");
|
||||||
$(".next").addClass("hide");
|
$(".next").addClass("hide");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const move_to_next_lesson = (status, e) => {
|
const move_to_next_lesson = (status, e) => {
|
||||||
if ($(e.currentTarget).hasClass("next") && $(e.currentTarget).attr("data-href")) {
|
if ($(e.currentTarget).hasClass("next") && $(e.currentTarget).attr("data-href")) {
|
||||||
window.location.href = $(e.currentTarget).attr("data-href");
|
window.location.href = $(e.currentTarget).attr("data-href");
|
||||||
}
|
}
|
||||||
else if (status == "Complete") {
|
else if (status == "Complete") {
|
||||||
$("input.mark-progress").closest(".custom-checkbox").addClass("hide");
|
$("input.mark-progress").closest(".custom-checkbox").addClass("hide");
|
||||||
$("div.mark-progress").removeClass("hide");
|
$("div.mark-progress").removeClass("hide");
|
||||||
$(".next").addClass("hide");
|
$(".next").addClass("hide");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$("input.mark-progress").closest(".custom-checkbox").removeClass("hide");
|
$("input.mark-progress").closest(".custom-checkbox").removeClass("hide");
|
||||||
$("div.mark-progress").addClass("hide");
|
$("div.mark-progress").addClass("hide");
|
||||||
$(".next").removeClass("hide");
|
$(".next").removeClass("hide");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const quiz_summary = (e=undefined) => {
|
const quiz_summary = (e=undefined) => {
|
||||||
e && e.preventDefault();
|
e && e.preventDefault();
|
||||||
var quiz_name = $("#quiz-title").text();
|
let quiz_name = $("#quiz-title").text();
|
||||||
var total_questions = $(".question").length;
|
let total_questions = $(".question").length;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "lms.lms.doctype.lms_quiz.lms_quiz.quiz_summary",
|
method: "lms.lms.doctype.lms_quiz.lms_quiz.quiz_summary",
|
||||||
args: {
|
args: {
|
||||||
@@ -175,7 +200,7 @@ const quiz_summary = (e=undefined) => {
|
|||||||
"results": localStorage.getItem(quiz_name)
|
"results": localStorage.getItem(quiz_name)
|
||||||
},
|
},
|
||||||
callback: (data) => {
|
callback: (data) => {
|
||||||
var message = data.message == total_questions ? __("Excellent Work 👏") : __("Better luck next time")
|
let message = data.message == total_questions ? __("Excellent Work 👏") : __("Better luck next time")
|
||||||
$(".question").addClass("hide");
|
$(".question").addClass("hide");
|
||||||
$("#summary").addClass("hide");
|
$("#summary").addClass("hide");
|
||||||
$("#quiz-form").parent().prepend(
|
$("#quiz-form").parent().prepend(
|
||||||
@@ -183,20 +208,22 @@ const quiz_summary = (e=undefined) => {
|
|||||||
<div class="font-weight-bold">${data.message}/${total_questions}</div></div>`);
|
<div class="font-weight-bold">${data.message}/${total_questions}</div></div>`);
|
||||||
$("#try-again").removeClass("hide");
|
$("#try-again").removeClass("hide");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const try_quiz_again = (e) => {
|
const try_quiz_again = (e) => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const check_answer = (e=undefined) => {
|
const check_answer = (e=undefined) => {
|
||||||
e && e.preventDefault();
|
e && e.preventDefault();
|
||||||
clearInterval(self.timer);
|
clearInterval(self.timer);
|
||||||
$(".timer").addClass("hide");
|
$(".timer").addClass("hide");
|
||||||
var quiz_name = $("#quiz-title").text();
|
let quiz_name = $("#quiz-title").text();
|
||||||
var total_questions = $(".question").length;
|
let total_questions = $(".question").length;
|
||||||
var current_index = $(".active-question").attr("data-qt-index");
|
let current_index = $(".active-question").attr("data-qt-index");
|
||||||
|
|
||||||
$(".explanation").removeClass("hide");
|
$(".explanation").removeClass("hide");
|
||||||
$("#check").addClass("hide");
|
$("#check").addClass("hide");
|
||||||
@@ -212,193 +239,205 @@ const check_answer = (e=undefined) => {
|
|||||||
else {
|
else {
|
||||||
$("#next").removeClass("hide");
|
$("#next").removeClass("hide");
|
||||||
}
|
}
|
||||||
var [answer, is_correct] = parse_options();
|
let [answer, is_correct] = parse_options();
|
||||||
add_to_local_storage(quiz_name, current_index, answer, is_correct);
|
add_to_local_storage(quiz_name, current_index, answer, is_correct);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const parse_options = () => {
|
const parse_options = () => {
|
||||||
var answer = [];
|
let answer = [];
|
||||||
var is_correct = [];
|
let is_correct = [];
|
||||||
$(".active-question input").each((i, element) => {
|
$(".active-question input").each((i, element) => {
|
||||||
var correct = parseInt($(element).attr("data-correct"));
|
let correct = parseInt($(element).attr("data-correct"));
|
||||||
if ($(element).prop("checked")) {
|
if ($(element).prop("checked")) {
|
||||||
answer.push(decodeURIComponent($(element).val()));
|
answer.push(decodeURIComponent($(element).val()));
|
||||||
correct && is_correct.push(1);
|
correct && is_correct.push(1);
|
||||||
correct ? add_icon(element, "check") : add_icon(element, "wrong");
|
correct ? add_icon(element, "check") : add_icon(element, "wrong");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
correct && is_correct.push(0);
|
correct && is_correct.push(0);
|
||||||
correct ? add_icon(element, "minus-circle-green") : add_icon(element, "minus-circle");
|
correct ? add_icon(element, "minus-circle-green") : add_icon(element, "minus-circle");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
return [answer, is_correct];
|
return [answer, is_correct];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const add_icon = (element, icon) => {
|
const add_icon = (element, icon) => {
|
||||||
$(element).closest(".custom-checkbox").removeClass("active-option");
|
$(element).closest(".custom-checkbox").removeClass("active-option");
|
||||||
var label = $(element).siblings(".option-text").text();
|
let label = $(element).siblings(".option-text").text();
|
||||||
$(element).siblings(".option-text").html(`
|
$(element).siblings(".option-text").html(`
|
||||||
<div>
|
<div>
|
||||||
<img class="mr-3" src="/assets/lms/icons/${icon}.svg">
|
<img class="mr-3" src="/assets/lms/icons/${icon}.svg">
|
||||||
${label}
|
${label}
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
//$(element).parent().empty().html(`<div class="option-text"><img class="mr-3" src="/assets/lms/icons/${icon}.svg"> ${label}</div>`);
|
//$(element).parent().empty().html(`<div class="option-text"><img class="mr-3" src="/assets/lms/icons/${icon}.svg"> ${label}</div>`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const add_to_local_storage = (quiz_name, current_index, answer, is_correct) => {
|
const add_to_local_storage = (quiz_name, current_index, answer, is_correct) => {
|
||||||
var quiz_stored = JSON.parse(localStorage.getItem(quiz_name));
|
let quiz_stored = JSON.parse(localStorage.getItem(quiz_name));
|
||||||
var quiz_obj = {
|
let quiz_obj = {
|
||||||
"question_index": current_index,
|
"question_index": current_index,
|
||||||
"answer": answer.join(),
|
"answer": answer.join(),
|
||||||
"is_correct": is_correct
|
"is_correct": is_correct
|
||||||
}
|
}
|
||||||
quiz_stored ? quiz_stored.push(quiz_obj) : quiz_stored = [quiz_obj]
|
quiz_stored ? quiz_stored.push(quiz_obj) : quiz_stored = [quiz_obj]
|
||||||
localStorage.setItem(quiz_name, JSON.stringify(quiz_stored))
|
localStorage.setItem(quiz_name, JSON.stringify(quiz_stored))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const create_certificate = (e) => {
|
const create_certificate = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
course = $(".title").attr("data-course");
|
course = $(".title").attr("data-course");
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "lms.lms.doctype.lms_certificate.lms_certificate.create_certificate",
|
method: "lms.lms.doctype.lms_certificate.lms_certificate.create_certificate",
|
||||||
args: {
|
args: {
|
||||||
"course": course
|
"course": course
|
||||||
},
|
},
|
||||||
callback: (data) => {
|
callback: (data) => {
|
||||||
window.location.href = `/courses/${course}/${data.message.name}`;
|
window.location.href = `/courses/${course}/${data.message.name}`;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const attach_work = (e) => {
|
const attach_work = (e) => {
|
||||||
const target = $(e.currentTarget);
|
const target = $(e.currentTarget);
|
||||||
let files = target.siblings(".attach-file").prop("files")
|
let files = target.siblings(".attach-file").prop("files")
|
||||||
if (files && files.length) {
|
if (files && files.length) {
|
||||||
files = add_files(files)
|
files = add_files(files)
|
||||||
return_as_dataurl(files)
|
return_as_dataurl(files)
|
||||||
files.map((file) => {
|
files.map((file) => {
|
||||||
upload_file(file, target);
|
upload_file(file, target);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const upload_file = (file, target) => {
|
const upload_file = (file, target) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let xhr = new XMLHttpRequest();
|
let xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
xhr.onreadystatechange = () => {
|
xhr.onreadystatechange = () => {
|
||||||
if (xhr.readyState == XMLHttpRequest.DONE) {
|
if (xhr.readyState == XMLHttpRequest.DONE) {
|
||||||
if (xhr.status === 200) {
|
if (xhr.status === 200) {
|
||||||
let response = JSON.parse(xhr.responseText)
|
let response = JSON.parse(xhr.responseText)
|
||||||
create_lesson_work(response.message, target);
|
create_lesson_work(response.message, target);
|
||||||
} else if (xhr.status === 403) {
|
} else if (xhr.status === 403) {
|
||||||
let response = JSON.parse(xhr.responseText);
|
let response = JSON.parse(xhr.responseText);
|
||||||
frappe.msgprint(`Not permitted. ${response._error_message || ''}`);
|
frappe.msgprint(`Not permitted. ${response._error_message || ''}`);
|
||||||
|
|
||||||
} else if (xhr.status === 413) {
|
} else if (xhr.status === 413) {
|
||||||
frappe.msgprint('Size exceeds the maximum allowed file size.');
|
frappe.msgprint('Size exceeds the maximum allowed file size.');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
frappe.msgprint(xhr.status === 0 ? 'XMLHttpRequest Error' : `${xhr.status} : ${xhr.statusText}`);
|
frappe.msgprint(xhr.status === 0 ? 'XMLHttpRequest Error' : `${xhr.status} : ${xhr.statusText}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
xhr.open('POST', '/api/method/upload_file', true);
|
||||||
}
|
xhr.setRequestHeader('Accept', 'application/json');
|
||||||
xhr.open('POST', '/api/method/upload_file', true);
|
xhr.setRequestHeader('X-Frappe-CSRF-Token', frappe.csrf_token);
|
||||||
xhr.setRequestHeader('Accept', 'application/json');
|
|
||||||
xhr.setRequestHeader('X-Frappe-CSRF-Token', frappe.csrf_token);
|
|
||||||
|
|
||||||
let form_data = new FormData();
|
let form_data = new FormData();
|
||||||
if (file.file_obj) {
|
if (file.file_obj) {
|
||||||
form_data.append('file', file.file_obj, `${frappe.session.user}-${file.name}`);
|
form_data.append('file', file.file_obj, `${frappe.session.user}-${file.name}`);
|
||||||
form_data.append('folder', `${$(".title").attr("data-lesson")} ${$(".title").attr("data-course")}`)
|
form_data.append('folder', `${$(".title").attr("data-lesson")} ${$(".title").attr("data-course")}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xhr.send(form_data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
xhr.send(form_data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const create_lesson_work = (file, target) => {
|
const create_lesson_work = (file, target) => {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "lms.lms.doctype.lesson_assignment.lesson_assignment.upload_assignment",
|
method: "lms.lms.doctype.lesson_assignment.lesson_assignment.upload_assignment",
|
||||||
args: {
|
args: {
|
||||||
assignment: file.file_url,
|
assignment: file.file_url,
|
||||||
lesson: $(".title").attr("data-lesson"),
|
lesson: $(".title").attr("data-lesson"),
|
||||||
identifier: target.siblings(".attach-file").attr("id")
|
identifier: target.siblings(".attach-file").attr("id")
|
||||||
},
|
},
|
||||||
callback: (data) => {
|
callback: (data) => {
|
||||||
target.siblings(".attach-file").addClass("hide");
|
target.siblings(".attach-file").addClass("hide");
|
||||||
target.siblings(".preview-work").removeClass("hide");
|
target.siblings(".preview-work").removeClass("hide");
|
||||||
target.siblings(".preview-work").find("a").attr("href", file.file_url).text(file.file_name)
|
target.siblings(".preview-work").find("a").attr("href", file.file_url).text(file.file_name)
|
||||||
target.addClass("hide");
|
target.addClass("hide");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const return_as_dataurl = (files) => {
|
const return_as_dataurl = (files) => {
|
||||||
let promises = files.map(file =>
|
let promises = files.map(file =>
|
||||||
frappe.dom.file_to_base64(file.file_obj)
|
frappe.dom.file_to_base64(file.file_obj)
|
||||||
.then(dataurl => {
|
.then(dataurl => {
|
||||||
file.dataurl = dataurl;
|
file.dataurl = dataurl;
|
||||||
this.on_success && this.on_success(file);
|
this.on_success && this.on_success(file);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
const add_files = (files) => {
|
const add_files = (files) => {
|
||||||
files = Array.from(files).map(file => {
|
files = Array.from(files).map(file => {
|
||||||
let is_image = file.type.startsWith('image');
|
let is_image = file.type.startsWith('image');
|
||||||
return {
|
return {
|
||||||
file_obj: file,
|
file_obj: file,
|
||||||
cropper_file: file,
|
cropper_file: file,
|
||||||
crop_box_data: null,
|
crop_box_data: null,
|
||||||
optimize: this.attach_doc_image ? true : false,
|
optimize: this.attach_doc_image ? true : false,
|
||||||
name: file.name,
|
name: file.name,
|
||||||
doc: null,
|
doc: null,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
total: 0,
|
total: 0,
|
||||||
failed: false,
|
failed: false,
|
||||||
request_succeeded: false,
|
request_succeeded: false,
|
||||||
error_message: null,
|
error_message: null,
|
||||||
uploading: false,
|
uploading: false,
|
||||||
private: !is_image
|
private: !is_image
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return files
|
return files
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const clear_work = (e) => {
|
const clear_work = (e) => {
|
||||||
const target = $(e.currentTarget);
|
const target = $(e.currentTarget);
|
||||||
const parent = target.closest(".preview-work");
|
const parent = target.closest(".preview-work");
|
||||||
parent.addClass("hide");
|
parent.addClass("hide");
|
||||||
parent.siblings(".attach-file").removeClass("hide").val(null);
|
parent.siblings(".attach-file").removeClass("hide").val(null);
|
||||||
parent.siblings(".submit-work").removeClass("hide");
|
parent.siblings(".submit-work").removeClass("hide");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const fetch_assignments = () => {
|
const fetch_assignments = () => {
|
||||||
if ($(".attach-file").length <= 0)
|
if ($(".attach-file").length <= 0)
|
||||||
return;
|
return;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "lms.lms.doctype.lesson_assignment.lesson_assignment.get_assignment",
|
method: "lms.lms.doctype.lesson_assignment.lesson_assignment.get_assignment",
|
||||||
args: {
|
args: {
|
||||||
"lesson": $(".title").attr("data-lesson")
|
"lesson": $(".title").attr("data-lesson")
|
||||||
},
|
},
|
||||||
callback: (data) => {
|
callback: (data) => {
|
||||||
if (data.message && data.message.length) {
|
if (data.message && data.message.length) {
|
||||||
const assignments = data.message;
|
const assignments = data.message;
|
||||||
for (let i in assignments) {
|
for (let i in assignments) {
|
||||||
let target = $(`#${assignments[i]["id"]}`);
|
let target = $(`#${assignments[i]["id"]}`);
|
||||||
target.addClass("hide");
|
target.addClass("hide");
|
||||||
target.siblings(".submit-work").addClass("hide");
|
target.siblings(".submit-work").addClass("hide");
|
||||||
target.siblings(".preview-work").removeClass("hide");
|
target.siblings(".preview-work").removeClass("hide");
|
||||||
target.siblings(".preview-work").find("a").attr("href", assignments[i]["assignment"]).text(assignments[i]["file_name"]);
|
target.siblings(".preview-work").find("a").attr("href", assignments[i]["assignment"]).text(assignments[i]["file_name"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const initialize_timer = () => {
|
const initialize_timer = () => {
|
||||||
this.time_left = $(".timer").data("time");
|
this.time_left = $(".timer").data("time");
|
||||||
calculate_and_display_time(100, this.time_left);
|
calculate_and_display_time(100, this.time_left);
|
||||||
@@ -423,6 +462,7 @@ const initialize_timer = () => {
|
|||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const calculate_and_display_time = (percent_time) => {
|
const calculate_and_display_time = (percent_time) => {
|
||||||
$(".timer .progress-bar").attr("aria-valuenow", percent_time);
|
$(".timer .progress-bar").attr("aria-valuenow", percent_time);
|
||||||
$(".timer .progress-bar").attr("aria-valuemax", percent_time);
|
$(".timer .progress-bar").attr("aria-valuemax", percent_time);
|
||||||
@@ -430,3 +470,50 @@ const calculate_and_display_time = (percent_time) => {
|
|||||||
let progress_color = percent_time < 20 ? "red" : "var(--primary-color)";
|
let progress_color = percent_time < 20 ? "red" : "var(--primary-color)";
|
||||||
$(".timer .progress-bar").css("background-color", progress_color);
|
$(".timer .progress-bar").css("background-color", progress_color);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const save_lesson = (e) => {
|
||||||
|
let lesson = $("#title").data("lesson");
|
||||||
|
frappe.call({
|
||||||
|
method: "lms.lms.doctype.lms_course.lms_course.save_lesson",
|
||||||
|
args: {
|
||||||
|
"title": $("#title").text(),
|
||||||
|
"body": $("#body").text(),
|
||||||
|
"chapter": $("#title").data("chapter"),
|
||||||
|
"preview": $("#preview").prop("checked") ? 1 : 0,
|
||||||
|
"idx": $("#title").data("index"),
|
||||||
|
"lesson": lesson ? lesson : ""
|
||||||
|
},
|
||||||
|
callback: (data) => {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const show_upload_modal = () => {
|
||||||
|
new frappe.ui.FileUploader({
|
||||||
|
folder: "Home/Attachments",
|
||||||
|
restrictions: {
|
||||||
|
allowed_file_types: ['image/*']
|
||||||
|
},
|
||||||
|
on_success: (file_doc) => {
|
||||||
|
$(".attachments").append(build_attachment_table(file_doc));
|
||||||
|
let count = $(".attachment-count").data("count") + 1;
|
||||||
|
$(".attachment-count").data("count", count);
|
||||||
|
$(".attachment-count").html(__(`${count} attachments`));
|
||||||
|
$(".attachments").removeClass("hide");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const build_attachment_table = (file_doc) => {
|
||||||
|
return $(`
|
||||||
|
<tr class="attachment-row">
|
||||||
|
<td>${file_doc.file_name}</td>
|
||||||
|
<td class=""><a class="button is-secondary button-links copy-link" data-link=""
|
||||||
|
data-name="${file_doc.file_name}" > ${__("Copy Link")} </a></td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
};
|
||||||
|
|||||||
@@ -10,28 +10,30 @@ def get_context(context):
|
|||||||
chapter_index = frappe.form_dict.get("chapter")
|
chapter_index = frappe.form_dict.get("chapter")
|
||||||
lesson_index = frappe.form_dict.get("lesson")
|
lesson_index = frappe.form_dict.get("lesson")
|
||||||
lesson_number = f"{chapter_index}.{lesson_index}"
|
lesson_number = f"{chapter_index}.{lesson_index}"
|
||||||
print(chapter_index, lesson_index, type(chapter_index), type(lesson_index))
|
context.lesson_index = lesson_index
|
||||||
if (not chapter_index and chapter_index != 0) or (not lesson_index and lesson_index != 0):
|
context.chapter = frappe.db.get_value("Chapter Reference", {
|
||||||
|
"idx": chapter_index,
|
||||||
|
"parent": context.course.name
|
||||||
|
}, "chapter")
|
||||||
|
|
||||||
|
if not chapter_index or not lesson_index:
|
||||||
if context.batch:
|
if context.batch:
|
||||||
index_ = get_lesson_index(context.course, context.batch, frappe.session.user) or "1.1"
|
index_ = get_lesson_index(context.course, context.batch, frappe.session.user) or "1.1"
|
||||||
else:
|
else:
|
||||||
index_ = "1.1"
|
index_ = "1.1"
|
||||||
redirect_to_lesson(context.course, index_)
|
redirect_to_lesson(context.course, index_)
|
||||||
|
|
||||||
if chapter_index == 0 and lesson_index == 0:
|
|
||||||
context.lesson = frappe._dict()
|
|
||||||
context.lesson.edit_mode = True
|
|
||||||
else:
|
|
||||||
set_lesson_context(context, lesson_number)
|
|
||||||
|
|
||||||
|
|
||||||
def set_lesson_context(context, lesson_number):
|
|
||||||
context.lesson = get_current_lesson_details(lesson_number, context)
|
context.lesson = get_current_lesson_details(lesson_number, context)
|
||||||
|
if not context.lesson:
|
||||||
|
context.lessom = frappe._dict()
|
||||||
|
|
||||||
|
if frappe.form_dict.get("edit"):
|
||||||
|
context.lesson.edit_mode = True
|
||||||
|
|
||||||
neighbours = get_neighbours(lesson_number, context.lessons)
|
neighbours = get_neighbours(lesson_number, context.lessons)
|
||||||
context.next_url = get_url(neighbours["next"], context.course)
|
context.next_url = get_url(neighbours["next"], context.course)
|
||||||
context.prev_url = get_url(neighbours["prev"], context.course)
|
context.prev_url = get_url(neighbours["prev"], context.course)
|
||||||
|
meta_info = context.lesson.title + " - " + context.course.title if context.lesson.title else "New Lesson"
|
||||||
meta_info = context.lesson.title + " - " + context.course.title
|
|
||||||
context.metatags = {
|
context.metatags = {
|
||||||
"title": meta_info,
|
"title": meta_info,
|
||||||
"keywords": meta_info,
|
"keywords": meta_info,
|
||||||
@@ -42,7 +44,7 @@ def set_lesson_context(context, lesson_number):
|
|||||||
context.page_context = {
|
context.page_context = {
|
||||||
"course": context.course.name,
|
"course": context.course.name,
|
||||||
"batch": context.get("batch") and context.batch.name,
|
"batch": context.get("batch") and context.batch.name,
|
||||||
"lesson": context.lesson.name,
|
"lesson": context.lesson.name if context.lesson.name else "New Lesson",
|
||||||
"is_member": context.membership is not None
|
"is_member": context.membership is not None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,8 +72,9 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div {% if course.edit_mode %} data-placeholder="{{ _('Title') }}" contenteditable="true" {% endif %} id="title"
|
<div {% if course.edit_mode %} data-placeholder="{{ _('Title') }}" contenteditable="true" {% endif %}
|
||||||
class="course-card-wide-title" data-course="{{ course.name | urlencode }}">{% if course.title %} {{ course.title }} {% endif %}</div>
|
id="title" {% if course.name %} data-course="{{ course.name | urlencode }}" {% endif %}
|
||||||
|
class="course-card-wide-title">{% if course.title %} {{ course.title }} {% endif %}</div>
|
||||||
|
|
||||||
<div {% if course.edit_mode %} contenteditable="true" data-placeholder="{{ _('Short Introduction') }}"
|
<div {% if course.edit_mode %} contenteditable="true" data-placeholder="{{ _('Short Introduction') }}"
|
||||||
{% endif %} id="intro" >{% if course.short_introduction %} {{ course.short_introduction }} {% endif %}</div>
|
{% endif %} id="intro" >{% if course.short_introduction %} {{ course.short_introduction }} {% endif %}</div>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
frappe.ready(() => {
|
frappe.ready(() => {
|
||||||
|
|
||||||
setup_vue_and_file_size();
|
|
||||||
|
|
||||||
hide_wrapped_mentor_cards();
|
hide_wrapped_mentor_cards();
|
||||||
|
|
||||||
$("#cancel-request").click((e) => {
|
$("#cancel-request").click((e) => {
|
||||||
@@ -75,27 +73,6 @@ frappe.ready(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const setup_vue_and_file_size = () => {
|
|
||||||
frappe.require("/assets/frappe/node_modules/vue/dist/vue.js", () => {
|
|
||||||
Vue.prototype.__ = window.__;
|
|
||||||
Vue.prototype.frappe = window.frappe;
|
|
||||||
});
|
|
||||||
|
|
||||||
frappe.provide("frappe.form.formatters");
|
|
||||||
frappe.form.formatters.FileSize = file_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const file_size = (value) => {
|
|
||||||
if(value > 1048576) {
|
|
||||||
value = flt(flt(value) / 1048576, 1) + "M";
|
|
||||||
} else if (value > 1024) {
|
|
||||||
value = flt(flt(value) / 1024, 1) + "K";
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const hide_wrapped_mentor_cards = () => {
|
const hide_wrapped_mentor_cards = () => {
|
||||||
let offset_top_prev;
|
let offset_top_prev;
|
||||||
|
|
||||||
@@ -350,7 +327,6 @@ const add_tag = (e) => {
|
|||||||
|
|
||||||
|
|
||||||
const save_course = (e) => {
|
const save_course = (e) => {
|
||||||
let course = $("#title").data("course");
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "lms.lms.doctype.lms_course.lms_course.save_course",
|
method: "lms.lms.doctype.lms_course.lms_course.save_course",
|
||||||
args: {
|
args: {
|
||||||
@@ -360,7 +336,7 @@ const save_course = (e) => {
|
|||||||
"video_link": $("#video-link").text(),
|
"video_link": $("#video-link").text(),
|
||||||
"image": $("#image").attr("href"),
|
"image": $("#image").attr("href"),
|
||||||
"description": $("#description").text(),
|
"description": $("#description").text(),
|
||||||
"course": course ? course : ""
|
"course": $("#title").data("course")
|
||||||
},
|
},
|
||||||
callback: (data) => {
|
callback: (data) => {
|
||||||
window.location.href = `/courses/${data.message}?edit=1`;
|
window.location.href = `/courses/${data.message}?edit=1`;
|
||||||
|
|||||||
Reference in New Issue
Block a user