fix: course details interactions

This commit is contained in:
pateljannat
2021-07-08 10:55:03 +05:30
parent 0ed5309b97
commit 27c01b3b0c
13 changed files with 307 additions and 102 deletions

View File

@@ -63,32 +63,35 @@ class Lesson(Document):
return return
@frappe.whitelist() @frappe.whitelist()
def save_progress(lesson, course): def save_progress(lesson, course, status):
if not frappe.db.exists("LMS Batch Membership", if not frappe.db.exists("LMS Batch Membership",
{ {
"member": frappe.session.user, "member": frappe.session.user,
"course": course "course": course
}): }):
return return
if frappe.db.exists("LMS Course Progress", if frappe.db.exists("LMS Course Progress",
{ {
"lesson": lesson, "lesson": lesson,
"owner": frappe.session.user "owner": frappe.session.user,
"course": course
}): }):
return doc = frappe.get_doc("LMS Course Progress",
{
lesson_details = frappe.get_doc("Lesson", lesson) "lesson": lesson,
dynamic_content = find_macros(lesson_details.body) "owner": frappe.session.user,
"course": course
status = "Complete" })
if dynamic_content: doc.status = status
status = "Partially Complete" doc.save(ignore_permissions=True)
else:
frappe.get_doc({ frappe.get_doc({
"doctype": "LMS Course Progress", "doctype": "LMS Course Progress",
"lesson": lesson_details.name, "lesson": lesson,
"status": status "status": status,
}).save(ignore_permissions=True) }).save(ignore_permissions=True)
return "OK"
def update_progress(lesson): def update_progress(lesson):
user = frappe.session.user user = frappe.session.user

View File

@@ -283,6 +283,15 @@ class LMSCourse(Document):
def get_outline(self): def get_outline(self):
return CourseOutline(self) return CourseOutline(self)
def get_progress(self, lesson):
return frappe.db.get_value("LMS Course Progress",
{
"course": self.name,
"owner": frappe.session.user,
"lesson": lesson
},
["status"])
class CourseOutline: class CourseOutline:
def __init__(self, course): def __init__(self, course):
self.course = course self.course = course

View File

@@ -1,21 +1,34 @@
<div> <div>
<div class="small-title chapter-title" data-target="#{{ chapter.get_slugified_chapter_title() }}" <div class="small-title chapter-title" data-target="#{{ chapter.get_slugified_chapter_title() }}"
data-toggle="collapse" aria-expanded="false"> data-toggle="collapse" aria-expanded="false">
<img class="chapter-icon" src="/assets/community/icons/side-arrow.svg"> <img class="chapter-icon" src="/assets/community/icons/side-arrow.svg">
{{ index }}. {{ chapter.title }} {{ index }}. {{ chapter.title }}
</div> </div>
<div class="chapter-content collapse navbar-collapse" id="{{ chapter.get_slugified_chapter_title() }}"> <div class="chapter-content collapse navbar-collapse" id="{{ chapter.get_slugified_chapter_title() }}">
<div class="chapter-description muted-text"> <div class="chapter-description muted-text">
{{ chapter.description }} {{ chapter.description }}
</div> </div>
<div class="lessons"> <div class="lessons">
{% for lesson in chapter.get_lessons() %} {% for lesson in chapter.get_lessons() %}
<div class="lesson-info"> <div class="lesson-info">
{% if show_link or lesson.include_in_preview %} {% if show_link or lesson.include_in_preview %}
<a class="dark-links" <a class="dark-links"
href="{{ course.get_learn_url(course.get_lesson_index(lesson.name)) }}{{course.query_parameter}}" href="{{ course.get_learn_url(course.get_lesson_index(lesson.name)) }}{{course.query_parameter}}"
data-course="{{ course.name }}"> data-course="{{ course.name }}">
{{ lesson.title }}</a> {{ lesson.title }}</a>
{% if show_link %}
<img class="lesson-progress-tick {{ course.get_progress(lesson.name) != 'Complete' and 'hide' }}"
src="/assets/community/icons/white-tick.svg">
{% endif %}
{% else %} {% else %}
<div title="This lesson is not available for preview"> <div title="This lesson is not available for preview">
<span class="dark-links"> <span class="dark-links">
@@ -24,11 +37,14 @@
<i class="fa fa-lock ml-2"></i> <i class="fa fa-lock ml-2"></i>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
</div> </div>
{% if index != course.get_chapters() | length %} {% if index != course.get_chapters() | length %}
<div class="card-divider"></div> <div class="card-divider"></div>
{% endif %} {% endif %}
@@ -36,16 +52,28 @@
<script> <script>
frappe.ready(() => { frappe.ready(() => {
expand_the_first_chapter(); expand_the_first_chapter();
expand_the_active_chapter();
}) })
var expand_the_first_chapter = () => { var expand_the_first_chapter = () => {
var elements = $(".collapse"); var elements = $(".collapse");
elements.each((i, elem) => { elements.each((i, element) => {
if (i <= 1) { if (i <= 1) {
$(elem).addClass("show"); show_section(element);
$(elem).siblings(".chapter-title").children(".chapter-icon").css("transform", "rotate(90deg)");
} }
}); });
} }
var expand_the_active_chapter = () => {
var selector = $(`a[href="${decodeURIComponent(window.location.pathname)}"]`).parent();
if (selector.length) {
show_section(selector.parent().parent());
}
}
var show_section = (element) => {
$(element).addClass("show");
$(element).siblings(".chapter-title").children(".chapter-icon").css("transform", "rotate(90deg)");
}
</script> </script>

View File

@@ -11,4 +11,5 @@
Created {{ course_count }} {{ suffix }} Created {{ course_count }} {{ suffix }}
</div> </div>
{% endif %} {% endif %}
<a class="stretched-link" href="/{{ member.username }}"></a>
</div> </div>

View File

@@ -77,7 +77,7 @@
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<div class="wide-button submit-review is-primary" data-course="{{ course.name | urlencode}}" id="submit-review">Submit</div> <div class="button wide-button submit-review is-primary" data-course="{{ course.name | urlencode}}" id="submit-review">Submit</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -489,6 +489,7 @@ div.custom-checkbox>label>input:checked+img {
padding: 24px; padding: 24px;
background: #E2E6E9; background: #E2E6E9;
border-radius: 12px; border-radius: 12px;
margin-top: 16px;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
@@ -543,8 +544,8 @@ div.custom-checkbox>label>input:checked+img {
@media (max-width: 768px) { @media (max-width: 768px) {
.course-home-page { .course-home-page {
padding: 0px; padding: 0px 0px 30px;
width: 688px; width: 90%;
} }
} }
@@ -605,17 +606,26 @@ div.custom-checkbox>label>input:checked+img {
} }
} }
.wide-button { .button {
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.13), 0px 0px 0.5px rgba(0, 0, 0, 0.5); box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.13), 0px 0px 0.5px rgba(0, 0, 0, 0.5);
border-radius: 6px; border-radius: 6px;
padding: 12px 24px 12px;
margin-right: 16px;
height: 48px;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: fit-content; width: fit-content;
padding: 8px 12px 8px;
font-size: 12px;
line-height: 135%;
letter-spacing: -0.011em;
}
.wide-button {
padding: 12px 24px 12px;
height: 48px;
margin-right: 16px;
font-size: 16px;
line-height: 150%;
} }
@media (max-width: 375px) { @media (max-width: 375px) {
@@ -634,7 +644,7 @@ div.custom-checkbox>label>input:checked+img {
} }
@media (max-width: 375px) { @media (max-width: 375px) {
.is-secondary { .video-preview {
margin-top: 16px; margin-top: 16px;
} }
} }
@@ -712,6 +722,7 @@ div.custom-checkbox>label>input:checked+img {
line-height: 250%; line-height: 250%;
letter-spacing: -0.011em; letter-spacing: -0.011em;
margin-bottom: 2px; margin-bottom: 2px;
padding-left: 10px;
} }
.chapter-content { .chapter-content {
@@ -910,11 +921,24 @@ div.custom-checkbox>label>input:checked+img {
} }
.breadcrumb { .breadcrumb {
margin: 16px 10px 16px; margin: 16px 10px 0px;
} }
.course-details-outline { .course-details-outline {
width: 352px; width: 352px;
margin-top: 16px;
}
@media (max-width: 768px) {
.course-details-outline {
width: 688px;
}
}
@media (max-width: 375px) {
.course-details-outline {
width: 312px;
}
} }
.lesson-content-card { .lesson-content-card {
@@ -922,13 +946,20 @@ div.custom-checkbox>label>input:checked+img {
flex-direction: column; flex-direction: column;
} }
.lesson-content {
width: 736px;
}
.course-content-parent { .course-content-parent {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
flex-wrap: wrap-reverse;
}
@media (max-width: 375px) {
.course-content-parent {
justify-content: center;
}
}
.course-content-parent .course-home-headings {
margin: 0px 0px 16px;
} }
.lesson-pagination { .lesson-pagination {
@@ -936,3 +967,65 @@ div.custom-checkbox>label>input:checked+img {
justify-content: space-between; justify-content: space-between;
margin: 24px 0px 0px; margin: 24px 0px 0px;
} }
.lesson-pagination-parent {
width: 736px;
margin-top: 16px;
}
@media (max-width: 768px) {
.lesson-pagination-parent {
width: 690px;
}
}
@media (max-width: 375px) {
.lesson-pagination-parent {
width: 312px;
}
}
.course-details-page {
padding: 0px 0px 80px;
display: flex;
flex-direction: column;
width: 1120px;
margin: 0 auto;
}
@media (max-width: 768px) {
.course-details-page {
padding: 24px 0px 24px;
width: 90%;
}
}
@media (max-width: 375px) {
.course-details-page {
width: 100%;
}
}
.active-lesson {
background-color: #EBF5FF;
border-radius: 4px;
}
.lesson-progress {
background: #BFDDF7;
padding: 4px 8px 4px;
font-size: 10px;
line-height: 120%;
margin: 0px 10px 20px;
border-radius: 4px;
font-weight: bold;
}
.lesson-progress-tick {
width: 16px;
height: 16px;
background: #4C5A67;
border-radius: 2px;
padding: 2px;
margin: 0px 4px 4px;
}

View File

@@ -0,0 +1,3 @@
<svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.2414 1.09763C11.3639 1.22781 11.3639 1.43886 11.2414 1.56904L4.33944 8.90237C4.21693 9.03254 4.01829 9.03254 3.89577 8.90237L0.758514 5.56904C0.635997 5.43886 0.635997 5.22781 0.758514 5.09763C0.881031 4.96746 1.07967 4.96746 1.20219 5.09763L4.11761 8.19526L10.7977 1.09763C10.9202 0.967456 11.1189 0.967456 11.2414 1.09763Z" fill="white" stroke="white" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 578 B

View File

@@ -21,7 +21,7 @@
{% block content %} {% block content %}
<div class="common-page-style"> <div class="common-page-style">
<div class="course-home-page"> <div class="course-details-page">
{{ widgets.Breadcrumb(course=course, lesson=lesson) }} {{ widgets.Breadcrumb(course=course, lesson=lesson) }}
<div class="course-content-parent"> <div class="course-content-parent">
<div class="course-details-outline"> <div class="course-details-outline">
@@ -40,9 +40,10 @@
{% macro LessonContent(lesson) %} {% macro LessonContent(lesson) %}
<div class="lesson-content"> <div class="lesson-content">
<div class="course-home-headings title {% if course.is_mentor(frappe.session.user) %} is_mentor {% endif %}" <div class="course-home-headings title" data-lesson="{{ lesson.name }}" data-course="{{ course.name }}">
data-lesson="{{ lesson.name }}" data-course="{{ course.name }}" {% if membership%} {{ lesson.title }}
data-membership="{{membership.name}}" {% endif %}>{{ lesson.title }}</div> <span class="lesson-progress {{hide if course.get_progress(lesson.name) != 'Complete' else ''}}">COMPLETED</span>
</div>
{% if membership or lesson.include_in_preview %} {% if membership or lesson.include_in_preview %}
<div class="common-card-style lesson-content-card">{{ lesson.render_html() }}</div> <div class="common-card-style lesson-content-card">{{ lesson.render_html() }}</div>
@@ -52,6 +53,7 @@
<a href="/courses/{{ course.name }}">Checkout Course Details.</a> <a href="/courses/{{ course.name }}">Checkout Course Details.</a>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endmacro %} {% endmacro %}
@@ -59,22 +61,33 @@
<div class="lesson-pagination"> <div class="lesson-pagination">
{% if prev_url %} {% if prev_url %}
<a class="wide-button is-secondary dark-links" href="{{ prev_url }}"> <a class="button is-secondary dark-links" href="{{ prev_url }}">
<img class="mr-2" src="/assets/community/icons/left-arrow.svg"> <img class="mr-2" src="/assets/community/icons/left-arrow.svg">
Prev Prev
</a> </a>
{% endif %} {% endif %}
<div class="wide-button is-primary"> {% if not course.is_mentor(frappe.session.user) and membership %}
{% if course.get_progress(lesson.name) != "Complete" %}
<div class="button is-secondary" id="progress" data-progress="Complete">
Mark as Complete Mark as Complete
</div> </div>
{% else %}
<div class="button is-secondary" id="progress" data-progress="Incomplete">
Mark as Incomplete
</div>
{% endif %}
{% endif %}
{% if next_url %} {% if next_url %}
<a class="wide-button is-secondary dark-links" href="{{ next_url }}"> <a class="button is-primary" href="{{ next_url }}">
Next Next
<img class="ml-2" src="/assets/community/icons/side-arrow.svg"> <img class="ml-2" src="/assets/community/icons/side-arrow-white.svg">
</a> </a>
{% endif %} {% endif %}
</div> </div>
{% endmacro %} {% endmacro %}

View File

@@ -1,28 +1,72 @@
frappe.ready(() => { frappe.ready(() => {
/* Save Lesson Progress */ highlight_active_lesson();
if ($(".title").attr("data-membership") && !$(".title").hasClass("is_mentor")) {
frappe.call({
method: "community.lms.doctype.lesson.lesson.save_progress",
args: {
lesson: $(".title").attr("data-lesson"),
course: $(".title").attr("data-course")
}
})
}
/* Save Current Lesson */ save_current_lesson();
$("#progress").click((e) => {
mark_progress(e);
});
$("#submit-quiz").click((e) => {
submit_quiz(e);
});
$("#try-again").click((e) => {
try_quiz_again(e);
});
})
var highlight_active_lesson = () => {
var selector = $(`a[href="${decodeURIComponent(window.location.pathname)}"]`).parent();
if (selector.length) {
selector.addClass('active-lesson');
}
}
var save_current_lesson = () => {
if ($(".title").attr("data-membership")) { if ($(".title").attr("data-membership")) {
frappe.call("community.lms.api.save_current_lesson", { frappe.call("community.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")
}) })
} }
}
/* Submit Quiz */ var mark_progress = (e) => {
$("#submit-quiz").click((e) => { var status = $(e.currentTarget).attr("data-progress");
frappe.call({
method: "community.lms.doctype.lesson.lesson.save_progress",
args: {
lesson: $(".title").attr("data-lesson"),
course: $(".title").attr("data-course"),
status: status
},
callback: (data) => {
if (data.message == "OK") {
change_progress_indicators(status, e);
}
}
})
}
var change_progress_indicators = (status, e) => {
if (status == "Complete") {
$(".lesson-progress").removeClass("hide");
$(".active-lesson .lesson-progress-tick").removeClass("hide");
}
else {
$(".lesson-progress").addClass("hide");
$(".active-lesson .lesson-progress-tick").addClass("hide");
}
var label = status != "Complete" ? "Mark as Complete" : "Mark as Incomplete";
var data_progress = status != "Complete" ? "Complete" : "Incomplete";
$(e.currentTarget).text(label).attr("data-progress", data_progress);
}
var submit_quiz = (e) => {
e.preventDefault(); e.preventDefault();
console.log("click")
var result = []; var result = [];
$('.question').each((i, element) => { $('.question').each((i, element) => {
var options = $(element).find(".option"); var options = $(element).find(".option");
@@ -53,10 +97,9 @@ frappe.ready(() => {
$(".score").text(`Score: ${data.message}/${result.length}`); $(".score").text(`Score: ${data.message}/${result.length}`);
} }
}) })
}) }
/* Try the quiz again */ var try_quiz_again = (e) => {
$("#try-again").click((e) => {
e.preventDefault(); e.preventDefault();
$(":input[type='checkbox']").prop("disabled", false); $(":input[type='checkbox']").prop("disabled", false);
$(":input[type='radio']").prop("disabled", false); $(":input[type='radio']").prop("disabled", false);
@@ -65,5 +108,4 @@ frappe.ready(() => {
$(".score").text(""); $(".score").text("");
$("#submit-quiz").removeClass("hide"); $("#submit-quiz").removeClass("hide");
$("#try-again").addClass("hide"); $("#try-again").addClass("hide");
}) }
})

View File

@@ -27,7 +27,7 @@ def get_common_context(context):
context.members = course.get_mentors(membership.batch) + course.get_students(membership.batch) context.members = course.get_mentors(membership.batch) + course.get_students(membership.batch)
context.member_count = len(context.members) context.member_count = len(context.members)
context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else " " context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else ""
context.livecode_url = get_livecode_url() context.livecode_url = get_livecode_url()
def get_livecode_url(): def get_livecode_url():

View File

@@ -40,7 +40,7 @@
</div> </div>
<div class="course-buttons"> <div class="course-buttons">
{% if not course.disable_self_learning and not membership %} {% if not course.disable_self_learning and not membership %}
<div class="wide-button start-learning is-primary join-batch" data-course="{{ course.name | urlencode }}"> <div class="button wide-button start-learning is-primary join-batch" data-course="{{ course.name | urlencode }}">
Start Learning Start Learning
<img class="ml-2" src="/assets/community/icons/white-arrow.svg" /> <img class="ml-2" src="/assets/community/icons/white-arrow.svg" />
</div> </div>
@@ -49,13 +49,13 @@
{% set lesson_index = course.get_lesson_index(membership.current_lesson) if membership and {% set lesson_index = course.get_lesson_index(membership.current_lesson) if membership and
membership.current_lesson membership.current_lesson
else '1.1' %} else '1.1' %}
<a class="wide-button is-primary" id="continue-learning" <a class="button wide-button is-primary" id="continue-learning"
href="{{ course.get_learn_url(lesson_index) }}{{ course.query_parameter }}"> href="{{ course.get_learn_url(lesson_index) }}{{ course.query_parameter }}">
Continue Learning <img class="ml-2" src="/assets/community/icons/white-arrow.svg" /> Continue Learning <img class="ml-2" src="/assets/community/icons/white-arrow.svg" />
</a> </a>
{% endif %} {% endif %}
{% if course.video_link %} {% if course.video_link %}
<div class="wide-button is-secondary video-preview"> <div class="button wide-button is-secondary video-preview">
Watch Video Preview Watch Video Preview
<img class="ml-2" src="/assets/community/images/play.png" /> <img class="ml-2" src="/assets/community/images/play.png" />
</div> </div>

View File

@@ -132,7 +132,7 @@ var join_course = (e) => {
if (data.message == "OK") { if (data.message == "OK") {
frappe.msgprint(__("You are now a student of this course.")); frappe.msgprint(__("You are now a student of this course."));
setTimeout(function () { setTimeout(function () {
window.location.href = `/courses/${course}/home`; window.location.href = `/courses/${course}/learn/1.1`;
}, 2000); }, 2000);
} }
} }

View File

@@ -65,17 +65,30 @@
</span> </span>
{% endif %} {% endif %}
</div> </div>
{% if course.get_membership(frappe.session.user) %} {% set membership = course.get_membership(frappe.session.user) %}
{% set lesson_index = course.get_lesson_index(membership.current_lesson) if membership and
membership.current_lesson
else '1.1' %}
{% set query_parameter = "?batch=" + membership.batch if membership and membership.batch else "" %}
{% if membership %}
<div class="view-course-link is-primary"> <div class="view-course-link is-primary">
Continue Course <img class="ml-3" src="/assets/community/icons/white-arrow.svg" /> Continue Course <img class="ml-3" src="/assets/community/icons/white-arrow.svg" />
</div> </div>
<a class="stretched-link" href="{{ course.get_learn_url(lesson_index) }}{{ query_parameter }}"></a>
{% else %} {% else %}
<div class="view-course-link"> <div class="view-course-link">
View Course <img class="ml-3" src="/assets/community/icons/black-arrow.svg" /> View Course <img class="ml-3" src="/assets/community/icons/black-arrow.svg" />
</div> </div>
<a class="stretched-link" href="/courses/{{ course.name }}"></a>
{% endif %} {% endif %}
</div> </div>
<a class="stretched-link" href="/courses/{{ course.name }}"></a>
</div> </div>
{% endmacro %} {% endmacro %}