feat: update lesson

This commit is contained in:
Jannat Patel
2022-08-09 18:32:29 +05:30
parent 65edd2ce22
commit a4534d8f3e
9 changed files with 504 additions and 334 deletions

View File

@@ -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

View File

@@ -22,6 +22,8 @@
<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) }}">
@@ -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">

View File

@@ -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{

View File

@@ -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");

View File

@@ -33,7 +33,7 @@
</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>
@@ -60,18 +60,22 @@
<div class="common-card-style lesson-content"> <div class="common-card-style lesson-content">
<div class="lesson-title"> <div class="lesson-title">
<!-- Title -->
<div class="course-home-headings title mb-0 {% if membership %} is-member {% endif %} <div class="course-home-headings title mb-0 {% if membership %} is-member {% endif %}
{% if membership or is_instructor %} eligible-for-submission {% endif %}" {% if membership or is_instructor %} eligible-for-submission {% endif %}" id="title"
{% if lesson.edit_mode %} data-placeholder="{{ _('Title') }}" contenteditable="true" {% endif %} {% if lesson.edit_mode %} data-placeholder="{{ _('Title') }}" contenteditable="true" {% endif %}
data-lesson="{{ lesson.name }}" data-course="{{ course.name }}" data-index="{{ lesson_index }}" data-course="{{ course.name }}" data-chapter="{{ chapter }}"
{% if lesson.name %} data-lesson="{{ lesson.name }}" {% endif %}
>{% if lesson.title %}{{ lesson.title }}{% endif %}</div> >{% if lesson.title %}{{ lesson.title }}{% endif %}</div>
<span class="lesson-progress {{ hide if get_progress(course.name, lesson.name) != 'Complete' else ''}}">{{ _("COMPLETED") }}</span> <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 %}
<button class="button is-default button-links ml-auto btn-edit"> {{ _("Edit") }} </button>
{% endif %} {% endif %}
</div> </div>
<!-- Instructors -->
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
{% set ins_len = instructors | length %} {% set ins_len = instructors | length %}
{% for instructor in instructors %} {% for instructor in instructors %}
@@ -97,15 +101,52 @@
<div class="ml-5 course-meta"> {{ frappe.utils.format_date(lesson.creation, "medium") }} </div> <div class="ml-5 course-meta"> {{ frappe.utils.format_date(lesson.creation, "medium") }} </div>
</div> </div>
<!-- Lesson Content -->
<div class="markdown-source lesson-content-card"> <div class="markdown-source lesson-content-card">
{% if membership or lesson.include_in_preview or is_instructor %} {% if membership or lesson.include_in_preview or is_instructor %}
{% if is_instructor and not lesson.include_in_preview %}
<div class="small alert alert-secondary alert-dismissible mt-4 mb-4"> {% 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.") }} {{ _("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">&times;</a> <a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>
</div> </div>
{% endif %} {% 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) }} {{ render_html(lesson.body) }}
{% endif %}
{% else %} {% else %}
<div class=""> <div class="">
<div class="button is-primary pull-right join-batch" data-course="{{ course.name | urlencode }}"> {{ _("Start Learning") }} </div> <div class="button is-primary pull-right join-batch" data-course="{{ course.name | urlencode }}"> {{ _("Start Learning") }} </div>
@@ -114,7 +155,9 @@
{% endif %} {% endif %}
</div> </div>
{% if not lesson.edit_mode %}
{{ pagination(prev_url, next_url) }} {{ pagination(prev_url, next_url) }}
{% endif %}
</div> </div>
{% endmacro %} {% endmacro %}

View File

@@ -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,15 +76,17 @@ 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");
@@ -76,6 +95,7 @@ const enable_check = (e) => {
} }
}; };
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,6 +112,7 @@ 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")
@@ -126,6 +147,7 @@ const mark_progress = (e) => {
move_to_next_lesson(status, e); 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");
@@ -141,6 +163,7 @@ const change_progress_indicators = (status, e) => {
} }
}; };
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");
@@ -148,6 +171,7 @@ const show_certificate_if_course_completed = (data) => {
} }
}; };
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");
@@ -164,10 +188,11 @@ const move_to_next_lesson = (status, e) => {
} }
}; };
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,15 +239,16 @@ 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);
@@ -230,13 +258,14 @@ const parse_options = () => {
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">
@@ -246,9 +275,10 @@ const add_icon = (element, icon) => {
//$(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
@@ -257,6 +287,7 @@ const add_to_local_storage = (quiz_name, current_index, answer, is_correct) => {
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");
@@ -268,9 +299,10 @@ const create_certificate = (e) => {
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")
@@ -283,6 +315,7 @@ const attach_work = (e) => {
} }
}; };
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();
@@ -316,7 +349,8 @@ const upload_file = (file, target) => {
xhr.send(form_data); xhr.send(form_data);
}); });
} };
const create_lesson_work = (file, target) => { const create_lesson_work = (file, target) => {
frappe.call({ frappe.call({
@@ -335,6 +369,7 @@ const create_lesson_work = (file, target) => {
}); });
}; };
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)
@@ -344,7 +379,8 @@ const return_as_dataurl = (files) => {
}) })
); );
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 => {
@@ -368,6 +404,7 @@ const add_files = (files) => {
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");
@@ -376,6 +413,7 @@ const clear_work = (e) => {
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;
@@ -399,6 +437,7 @@ const fetch_assignments = () => {
}); });
}; };
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="![](${file_doc.file_url})"
data-name="${file_doc.file_name}" > ${__("Copy Link")} </a></td>
</tr>
`);
};

View File

@@ -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
} }

View File

@@ -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>

View File

@@ -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`;