[REF] website_slides: improve course frontend page
PURPOSE
Improve onboarding and user experience and course frontend
SPECIFICATIONS
Overall improvements and onboarding :
* new and improved default images for both course types;
* preview / free preview badge update;
* copy writing and spacing update;
* allow to specifically choose a slide to promote. Display promoted slide is
not optional anymore as configured in backend;
* display a sample of lessons if no lesson in a course;
LINKS
Task ID-2241513
PR #55698
This commit is contained in:
committed by
Thibault Delavallée
parent
2e7c398fb3
commit
255bb3abf3
@@ -32,6 +32,20 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Tweak "preview" badge: display Free Preview if payment-based course -->
|
||||
<template id="course_slides_list_slide"
|
||||
name="Slide template for a training channel (Sale)"
|
||||
inherit_id="website_slides.course_slides_list_slide">
|
||||
<xpath expr="//a[@t-if='channel.can_upload']/span/span" position="replace">
|
||||
<span t-if="channel.enroll == 'payment'">Free Preview</span>
|
||||
<span t-else="">Preview</span>
|
||||
</xpath>
|
||||
<xpath expr="//t[@t-elif='slide.is_preview and not channel.is_member']/span/span" position="replace">
|
||||
<span t-if="channel.enroll == 'payment'">Free Preview</span>
|
||||
<span t-else="">Preview</span>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- FULLSCREEN -->
|
||||
<template id="slide_fullscreen" inherit_id="website_slides.slide_fullscreen">
|
||||
<xpath expr="//div[hasclass('o_wslides_fs_main')]" position="attributes">
|
||||
|
||||
@@ -406,7 +406,8 @@ class WebsiteSlides(WebsiteProfile):
|
||||
|
||||
# sorting criterion
|
||||
if channel.channel_type == 'documentation':
|
||||
actual_sorting = sorting if sorting and sorting in request.env['slide.slide']._order_by_strategy else channel.promote_strategy
|
||||
default_sorting = 'latest' if channel.promote_strategy in ['specific', 'none', False] else channel.promote_strategy
|
||||
actual_sorting = sorting if sorting and sorting in request.env['slide.slide']._order_by_strategy else default_sorting
|
||||
else:
|
||||
actual_sorting = 'sequence'
|
||||
order = request.env['slide.slide']._order_by_strategy[actual_sorting]
|
||||
@@ -489,7 +490,10 @@ class WebsiteSlides(WebsiteProfile):
|
||||
# of them but unreachable ones won't be clickable (+ slide controller will crash anyway)
|
||||
# documentation mode may display less slides than content by category but overhead of
|
||||
# computation is reasonable
|
||||
values['slide_promoted'] = request.env['slide.slide'].sudo().search(domain, limit=1, order=order)
|
||||
if channel.promote_strategy == 'specific':
|
||||
values['slide_promoted'] = channel.sudo().promoted_slide_id
|
||||
else:
|
||||
values['slide_promoted'] = request.env['slide.slide'].sudo().search(domain, limit=1, order=order)
|
||||
|
||||
limit_category_data = False
|
||||
if channel.channel_type == 'documentation':
|
||||
|
||||
@@ -96,8 +96,8 @@ class Channel(models.Model):
|
||||
description_short = fields.Text('Short Description', translate=True, help="The description that is displayed on the course card")
|
||||
description_html = fields.Html('Detailed Description', translate=tools.html_translate, sanitize_attributes=False, sanitize_form=False)
|
||||
channel_type = fields.Selection([
|
||||
('documentation', 'Documentation'), ('training', 'Training')],
|
||||
string="Course type", default="documentation", required=True)
|
||||
('training', 'Training'), ('documentation', 'Documentation')],
|
||||
string="Course type", default="training", required=True)
|
||||
sequence = fields.Integer(default=10, help='Display order')
|
||||
user_id = fields.Many2one('res.users', string='Responsible', default=lambda self: self.env.uid)
|
||||
color = fields.Integer('Color Index', default=0, help='Used to decorate kanban view')
|
||||
@@ -115,8 +115,17 @@ class Channel(models.Model):
|
||||
promote_strategy = fields.Selection([
|
||||
('latest', 'Latest Published'),
|
||||
('most_voted', 'Most Voted'),
|
||||
('most_viewed', 'Most Viewed')],
|
||||
string="Featured Content", default='latest', required=True)
|
||||
('most_viewed', 'Most Viewed'),
|
||||
('specific', 'Specific'),
|
||||
('none', 'None')],
|
||||
string="Promoted Content", default='latest', required=False,
|
||||
help='Depending the promote strategy, a slide will appear on the top of the course\'s page :\n'
|
||||
' * Latest Published : the slide created last.\n'
|
||||
' * Most Voted : the slide which have to most vote.\n'
|
||||
' * Most Viewed ; the slide which have been viewed the most.\n'
|
||||
' * Specific : You choose the slide to appear.\n'
|
||||
' * None : There won\'t be any slide showing.\n')
|
||||
promoted_slide_id = fields.Many2one('slide.slide', string='Promoted Slide')
|
||||
access_token = fields.Char("Security Token", copy=False, default=_default_access_token)
|
||||
nbr_presentation = fields.Integer('Presentations', compute='_compute_slides_statistics', store=True)
|
||||
nbr_document = fields.Integer('Documents', compute='_compute_slides_statistics', store=True)
|
||||
@@ -131,7 +140,7 @@ class Channel(models.Model):
|
||||
rating_avg_stars = fields.Float("Rating Average (Stars)", compute='_compute_rating_stats', digits=(16, 1), compute_sudo=True)
|
||||
# configuration
|
||||
allow_comment = fields.Boolean(
|
||||
"Allow rating on Course", default=False,
|
||||
"Allow rating on Course", default=True,
|
||||
help="If checked it allows members to either:\n"
|
||||
" * like content and post comments on documentation course;\n"
|
||||
" * post comment and review on training course;")
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 65 KiB |
@@ -107,6 +107,10 @@
|
||||
<field name="visibility" widget="radio"/>
|
||||
<field name="promote_strategy" widget="radio"
|
||||
attrs="{'invisible': [('channel_type', '=', 'training')]}"/>
|
||||
<field name="promoted_slide_id"
|
||||
attrs="{'invisible': ['|', ('channel_type', '=', 'training'), ('promote_strategy', '!=', 'specific')],
|
||||
'required': [('channel_type', '!=', 'training'), ('promote_strategy', '=', 'specific')]}"
|
||||
domain="[('channel_id', '=', active_id), ('is_category', '=', False)]"/>
|
||||
</group>
|
||||
</group>
|
||||
<div attrs="{'invisible': [('enroll', '!=', 'invite')]}">
|
||||
|
||||
@@ -118,7 +118,11 @@
|
||||
<!-- ==== Header Left ==== -->
|
||||
<div class="col-12 col-md-4 col-lg-3">
|
||||
<div class="d-flex align-items-end justify-content-around h-100">
|
||||
<div t-field="channel.image_1920" t-options='{"widget": "image", "class": "o_wslides_course_pict d-inline-block mb-2 mt-3 my-md-0"}' class="h-100"/>
|
||||
<div t-if="channel.image_1920" t-field="channel.image_1920" t-options='{"widget": "image", "class": "o_wslides_course_pict d-inline-block mb-2 mt-3 my-md-0"}' class="h-100"/>
|
||||
<div t-else="" class="h-100">
|
||||
<img t-att-src="'/website_slides/static/src/img/channel-%s-default.jpg' % ('training' if channel.channel_type == 'training' else 'documentation')"
|
||||
class="o_wslides_course_pict d-inline-block mb-2 mt-3 my-md-0"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -128,7 +132,7 @@
|
||||
<h1 t-field="channel.name"/>
|
||||
<p class="mb-0 mb-xl-3" t-field="channel.description"/>
|
||||
|
||||
<div t-if="channel.channel_type == 'documentation'" class="my-1">
|
||||
<div t-if="channel.channel_type == 'documentation'" class="d-flex mb-md-5">
|
||||
<button role="button" class="btn text-white pl-0" title="Share Channel"
|
||||
aria-label="Share Channel"
|
||||
data-toggle="modal" t-att-data-target="'#slideChannelShareModal_%s' % channel.id">
|
||||
@@ -206,7 +210,7 @@
|
||||
href="#"
|
||||
t-att-data-channel-id="channel.id"
|
||||
t-att-data-channel-tag-ids="channel.tag_ids.ids">
|
||||
<i class="fa fa-plus mr-1"/><span>Add Tag</span>
|
||||
<span>Add Tag</span>
|
||||
</a>
|
||||
</div>
|
||||
<t t-if="channel.channel_type == 'training'" t-call="website_slides.course_slides_list"/>
|
||||
@@ -386,12 +390,12 @@
|
||||
|
||||
<li t-if="category['total_slides'] or channel.can_publish" t-att-class="'o_wslides_slide_list_category o_wslides_js_list_item mb-2' if category_id else 'mt-4'" t-att-data-slide-id="category_id" t-att-data-category-id="category_id">
|
||||
<div t-att-data-category-id="category_id"
|
||||
t-att-class="'o_wslides_slide_list_category_header position-relative d-flex justify-content-between align-items-center mt8 %s' % ('o_wslides_js_category bg-white shadow-sm border-bottom-0' if category_id and channel.can_upload else 'border-0 py-1')">
|
||||
t-att-class="'o_wslides_slide_list_category_header position-relative d-flex justify-content-between align-items-center mt8 %s %s' % ('bg-white shadow-sm border-bottom-0' if category_id else 'border-0', 'o_wslides_js_category py-0' if channel.can_upload else 'py-2')">
|
||||
<div t-att-class="'d-flex align-items-center pl-3 %s' % ('o_wslides_slides_list_drag' if channel.can_publish else '')">
|
||||
<div t-if="channel.can_publish and category_id" class="o_wslides_slides_list_drag py-2 pr-3">
|
||||
<i class="fa fa-bars"/>
|
||||
</div>
|
||||
<span t-if="category_id" t-att-class="'text-muted' if not channel.can_upload else ''" t-field="category['category'].name"/>
|
||||
<span t-if="category_id" t-field="category['category'].name"/>
|
||||
<small t-if="not category['total_slides'] and category_id" class="ml-1 text-muted"><b>(empty)</b></small>
|
||||
</div>
|
||||
<div t-if="category_id" class="o_text_link d-flex border-left">
|
||||
@@ -422,7 +426,7 @@
|
||||
</t>
|
||||
</ul>
|
||||
<div t-if="channel.can_upload" class="o_wslides_content_actions btn-group">
|
||||
<a class="o_wslides_js_slide_upload mr-1 border btn btn-light bg-white"
|
||||
<a class="o_wslides_js_slide_upload mr-1 border btn btn-primary"
|
||||
role="button"
|
||||
aria-label="Upload Presentation"
|
||||
href="#"
|
||||
@@ -435,10 +439,83 @@
|
||||
href="#" role="button"
|
||||
groups="website_slides.group_website_slides_officer"><i class="fa fa-folder-o mr-1"/><span>Add Section</span></a>
|
||||
</div>
|
||||
<t t-if="not channel.slide_ids and channel.can_publish">
|
||||
<t t-call="website_slides.course_slides_list_sample"/>
|
||||
</t>
|
||||
</div>
|
||||
<div t-field="channel.description_html"/>
|
||||
</template>
|
||||
|
||||
<template id="course_slides_list_sample" name="Course Sample Content">
|
||||
<ul class="list-unstyled mt-3" style="opacity: 50%;">
|
||||
<li class="o_wslides_slide_list_category mb-2">
|
||||
<div class="o_wslides_slide_list_category_header position-relative d-flex justify-content-between align-items-center mt8 bg-white shadow-sm border-bottom-0 py-2">
|
||||
<div class="d-flex align-items-center pl-3">
|
||||
<span class="text-muted">Common tasks for a computer scientist</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-unstyled pb-1 border-top">
|
||||
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
|
||||
<i class="fa fa-file-text py-2 mx-2"/>
|
||||
<div class="text-truncate mr-auto">
|
||||
<span>Asking Question</span>
|
||||
</div>
|
||||
<div class="d-flex flex-row">
|
||||
<span class="badge font-weight-bold px-2 py-1 m-1 badge-warning">
|
||||
<i class="fa fa-fw fa-flag"/> 10 xp
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
|
||||
<i class="fa fa-question-circle py-2 mx-2"/>
|
||||
<div class="text-truncate mr-auto">
|
||||
<span>Asking the right question</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
|
||||
<i class="fa fa-file-pdf-o py-2 mx-2"/>
|
||||
<div class="text-truncate mr-auto">
|
||||
<span>Answering Questions</span>
|
||||
</div>
|
||||
<div class="d-flex flex-row">
|
||||
<span class="badge badge-info badge-arrow-right font-weight-normal px-2 py-1 m-1">New</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="o_wslides_slide_list_category mb-2">
|
||||
<div class="o_wslides_slide_list_category_header position-relative d-flex justify-content-between align-items-center mt8 bg-white shadow-sm border-bottom-0 py-2">
|
||||
<div class="d-flex align-items-center pl-3">
|
||||
<span class="text-muted">Parts of computer science</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-unstyled pb-1 border-top">
|
||||
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
|
||||
<i class="fa fa-file-pdf-o py-2 mx-2"/>
|
||||
<div class="text-truncate mr-auto">
|
||||
<span>Mathematics</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
|
||||
<i class="fa fa-file-pdf-o py-2 mx-2"/>
|
||||
<div class="text-truncate mr-auto">
|
||||
<span>Science</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
|
||||
<i class="fa fa-play py-2 mx-2"/>
|
||||
<div class="text-truncate mr-auto">
|
||||
<span>Logic</span>
|
||||
</div>
|
||||
<div class="d-flex flex-row">
|
||||
<span class="badge badge-success font-weight-normal px-2 py-1 m-1">Preview</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<template id="course_slides_list_slide" name="Slide template for a training channel">
|
||||
<li t-att-index="j" t-att-data-slide-id="slide.id" t-att-data-category-id="category_id" t-attf-class="o_wslides_slides_list_slide o_wslides_js_list_item bg-white-50 border-top-0 d-flex align-items-center pl-2 #{'py-1 pr-2' if not channel.can_upload else ''}">
|
||||
<div t-if="channel.can_publish" class=" o_wslides_slides_list_drag border-right p-2">
|
||||
@@ -458,12 +535,14 @@
|
||||
|
||||
<div class="d-flex flex-row">
|
||||
<a name="o_wslides_list_slide_add_quizz" t-if="channel.can_upload and not slide.question_ids" t-attf-href="/slides/slide/#{slug(slide)}?quiz_quick_create">
|
||||
<span class="badge badge-light badge-hide border font-weight-normal px-2 py-1 m-1"><i class="fa fa-plus mr-1"/>Add Quiz</span>
|
||||
<span class="badge badge-light badge-hide border font-weight-normal px-2 py-1 m-1">Add Quiz</span>
|
||||
</a>
|
||||
<a t-if="channel.can_upload" href="#">
|
||||
<span t-att-data-slide-id="slide.id" t-attf-class="o_wslides_js_slide_toggle_is_preview badge #{'badge-success py-1' if slide.is_preview else 'badge-light badge-hide border'} font-weight-normal px-2 m-1">Free preview</span>
|
||||
<span t-att-data-slide-id="slide.id" t-attf-class="o_wslides_js_slide_toggle_is_preview badge #{'badge-success' if slide.is_preview else 'badge-light badge-hide border'} font-weight-normal px-2 py-1 m-1"><span>Preview</span></span>
|
||||
</a>
|
||||
<span t-elif="slide.is_preview and not channel.is_member" class="badge badge-success font-weight-normal px-2 py-1 m-1">Free preview</span>
|
||||
<t t-elif="slide.is_preview and not channel.is_member">
|
||||
<span class="badge badge-success font-weight-normal px-2 py-1 m-1"><span>Preview</span></span>
|
||||
</t>
|
||||
<span t-if="slide.is_new_slide and not channel_progress[slide.id].get('completed')" class="badge badge-info badge-arrow-right font-weight-normal px-2 py-1 m-1">
|
||||
New
|
||||
</span>
|
||||
@@ -481,7 +560,7 @@
|
||||
</t>
|
||||
<span t-if="channel.can_publish" class="d-none d-md-flex">
|
||||
<a t-if="slide.slide_type == 'webpage'" class="px-2 o_text_link text-primary" target="_blank" t-attf-href="/slides/slide/#{slug(slide)}?enable_editor=1"><span class="fa fa-pencil"/></a>
|
||||
<a t-else="" class="px-2 o_text_link text-primary" target="_blank" t-attf-href="/web#id=#{slide.id}&action=#{slide_action}&model=slide.slide&view_type=form"><span class="fa fa-pencil"/></a>
|
||||
<a t-else="" class="px-2 o_text_link text-primary" target="_blank" t-attf-href="/web#id=#{slide.id}&action=#{slide_action}&model=slide.slide&view_type=form" title="Edit in backend"><span class="fa fa-pencil"/></a>
|
||||
<a href="#" t-att-data-slide-id="slide.id" class="o_text_link text-danger px-2 o_wslides_js_slide_archive"><span class="fa fa-trash"/></a>
|
||||
</span>
|
||||
</div>
|
||||
@@ -490,7 +569,7 @@
|
||||
|
||||
<!-- ======= Documentation Course content: cards / categories======= -->
|
||||
<template id="course_promoted_slide" name="Documentation Course content: promoted slide">
|
||||
<div class="o_wslides_promoted_slide d-none">
|
||||
<div class="o_wslides_promoted_slide">
|
||||
<div t-if="not search and not search_slide_type and slide_promoted" class="container py-1 mb-2">
|
||||
<div class="card flex-column flex-lg-row">
|
||||
<t t-set="image_url" t-value="website.image_url(slide_promoted, 'image_1024')"/>
|
||||
@@ -647,7 +726,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- Featured lesson -->
|
||||
<t t-call="website_slides.course_promoted_slide"/>
|
||||
<t t-if="channel.promote_strategy != 'none'">
|
||||
<t t-call="website_slides.course_promoted_slide"/>
|
||||
</t>
|
||||
|
||||
<div class="container py-2">
|
||||
<t t-if="search">
|
||||
@@ -708,13 +789,14 @@
|
||||
</a>
|
||||
<div t-else="" class="card-img-top border-bottom o_wslides_gradient" t-attf-style="padding-top: 50%;"/>
|
||||
</t>
|
||||
<i t-if="channel_progress[slide.id].get('completed')" class="position-absolute py-1 px-2 h5 fa fa-check-circle text-primary" style="right:0; top:0;"/>
|
||||
|
||||
<div class="card-body p-3">
|
||||
<a t-if="can_access" class="card-title h5 mb-2o_wslides_desc_truncate_2" t-attf-href="/slides/slide/#{slug(slide)}#{query_string}" t-esc="slide.name"/>
|
||||
<span t-else="" class="card-title h5 mb-2 o_wslides_desc_truncate_2 text-muted" t-esc="slide.name"/>
|
||||
<div class="card-subtitle mb-2 text-muted" t-if="slide.is_preview or (not slide.is_published and user.has_group('website_slides.group_website_slides_officer'))">
|
||||
<t t-if="slide.is_preview">
|
||||
<span class="badge badge-info">Free preview</span>
|
||||
<span class="badge badge-info">Preview</span>
|
||||
</t>
|
||||
<t t-if="not slide.is_published and channel.can_publish">
|
||||
<span class="badge badge-danger">Unpublished</span>
|
||||
@@ -761,10 +843,4 @@
|
||||
<i t-if="slide.slide_type == 'quiz'" t-att-class="'fa fa-question-circle %s' % icon_class"></i>
|
||||
</template>
|
||||
|
||||
<template name="Display Promoted Slide" id="display_promoted_slide" inherit_id="website_slides.course_promoted_slide" active="True" customize_show="True">
|
||||
<xpath expr="//div[hasclass('o_wslides_promoted_slide')]" position="attributes">
|
||||
<attribute name="class">o_wslides_promoted_slide</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</data></odoo>
|
||||
|
||||
Reference in New Issue
Block a user