feat: Recommended Items and Item full page refresh
- Added Optional Recommended Items - Item Full Page minor UI Refresh - Floating wishlist button in item full page - Reviews section UI Refresh
This commit is contained in:
parent
b5e7f04b33
commit
2fec068aff
@ -27,6 +27,8 @@
|
||||
"enable_wishlist",
|
||||
"column_break_22",
|
||||
"enable_reviews",
|
||||
"column_break_23",
|
||||
"enable_recommendations",
|
||||
"section_break_18",
|
||||
"company",
|
||||
"price_list",
|
||||
@ -367,12 +369,22 @@
|
||||
{
|
||||
"fieldname": "column_break_22",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_23",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enable_recommendations",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Recommendations"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-07 21:32:17.363276",
|
||||
"modified": "2021-07-13 16:30:14.715949",
|
||||
"modified_by": "Administrator",
|
||||
"module": "E-commerce",
|
||||
"name": "E Commerce Settings",
|
||||
|
@ -0,0 +1,87 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2021-07-12 20:52:12.503470",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"website_item",
|
||||
"website_item_name",
|
||||
"column_break_2",
|
||||
"item_code",
|
||||
"more_information_section",
|
||||
"route",
|
||||
"column_break_6",
|
||||
"website_item_image",
|
||||
"website_item_thumbnail"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "website_item",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Website Item",
|
||||
"options": "Website Item"
|
||||
},
|
||||
{
|
||||
"fetch_from": "website_item.web_item_name",
|
||||
"fieldname": "website_item_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Website Item Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "more_information_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "More Information"
|
||||
},
|
||||
{
|
||||
"fetch_from": "website_item.route",
|
||||
"fieldname": "route",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Route",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "website_item.image",
|
||||
"fieldname": "website_item_image",
|
||||
"fieldtype": "Attach",
|
||||
"label": "Website Item Image",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_6",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fetch_from": "website_item.thumbnail",
|
||||
"fieldname": "website_item_thumbnail",
|
||||
"fieldtype": "Data",
|
||||
"label": "Website Item Thumbnail",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "website_item.item_code",
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Data",
|
||||
"label": "Item Code"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-13 21:02:19.031652",
|
||||
"modified_by": "Administrator",
|
||||
"module": "E-commerce",
|
||||
"name": "Recommended Items",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class RecommendedItems(Document):
|
||||
pass
|
@ -39,6 +39,8 @@
|
||||
"display_additional_information_section",
|
||||
"show_tabbed_section",
|
||||
"tabs",
|
||||
"recommended_items_section",
|
||||
"recommended_items",
|
||||
"offers_section",
|
||||
"offers",
|
||||
"section_break_6",
|
||||
@ -312,13 +314,25 @@
|
||||
"fieldname": "short_description",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Short Website Description"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "recommended_items_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Recommended Items"
|
||||
},
|
||||
{
|
||||
"fieldname": "recommended_items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Recommended/Similar Items",
|
||||
"options": "Recommended Items"
|
||||
}
|
||||
],
|
||||
"has_web_view": 1,
|
||||
"image_field": "image",
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-11 20:49:45.415421",
|
||||
"modified": "2021-07-12 21:00:04.065803",
|
||||
"modified_by": "Administrator",
|
||||
"module": "E-commerce",
|
||||
"name": "Website Item",
|
||||
@ -373,10 +387,10 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "item_code, item_name ,item_group",
|
||||
"search_fields": "web_item_name, item_code, item_group",
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "item_name",
|
||||
"title_field": "web_item_name",
|
||||
"track_changes": 1
|
||||
}
|
@ -13,6 +13,8 @@ from frappe.website.doctype.website_slideshow.website_slideshow import get_slide
|
||||
|
||||
from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for)
|
||||
from erpnext.e_commerce.doctype.item_review.item_review import get_item_reviews
|
||||
from erpnext.e_commerce.shopping_cart.cart import _set_price_list
|
||||
from erpnext.utilities.product import get_price
|
||||
|
||||
# SEARCH
|
||||
from erpnext.e_commerce.website_item_indexing import (
|
||||
@ -199,6 +201,12 @@ class WebsiteItem(WebsiteGenerator):
|
||||
context.wished = True
|
||||
|
||||
context.user_is_customer = check_if_user_is_customer()
|
||||
|
||||
context.recommended_items = None
|
||||
settings = context.shopping_cart.cart_settings
|
||||
if settings.enable_recommendations:
|
||||
context.recommended_items = self.get_recommended_items(settings)
|
||||
|
||||
return context
|
||||
|
||||
def set_variant_context(self, context):
|
||||
@ -379,6 +387,38 @@ class WebsiteItem(WebsiteGenerator):
|
||||
|
||||
return tab_values
|
||||
|
||||
def get_recommended_items(self, settings):
|
||||
items = frappe.db.sql(f"""
|
||||
select
|
||||
ri.website_item_thumbnail, ri.website_item_name,
|
||||
ri.route, ri.item_code
|
||||
from
|
||||
`tabRecommended Items` ri, `tabWebsite Item` wi
|
||||
where
|
||||
ri.item_code = wi.item_code
|
||||
and ri.parent = '{self.name}'
|
||||
and wi.published = 1
|
||||
order by ri.idx
|
||||
""", as_dict=1)
|
||||
|
||||
if settings.show_price:
|
||||
is_guest = frappe.session.user == "Guest"
|
||||
# Show Price if logged in.
|
||||
# If not logged in and price is hidden for guest, skip price fetch.
|
||||
if is_guest and settings.hide_price_for_guest:
|
||||
return items
|
||||
|
||||
selling_price_list = _set_price_list(settings, None)
|
||||
for item in items:
|
||||
item.price_info = get_price(
|
||||
item.item_code,
|
||||
selling_price_list,
|
||||
settings.default_customer_group,
|
||||
settings.company
|
||||
)
|
||||
|
||||
return items
|
||||
|
||||
def invalidate_cache_for_web_item(doc):
|
||||
"""Invalidate Website Item Group cache and rebuild ItemVariantsCacheManager."""
|
||||
from erpnext.stock.doctype.item.item import invalidate_item_variants_cache_for_website
|
||||
|
@ -11,6 +11,7 @@ WEBSITE_ITEM_NAME_AUTOCOMPLETE = 'website_items_name_dict'
|
||||
WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE = 'website_items_category_dict'
|
||||
|
||||
ALLOWED_INDEXABLE_FIELDS_SET = {
|
||||
'web_item_name',
|
||||
'item_code',
|
||||
'item_name',
|
||||
'item_group',
|
||||
|
@ -176,6 +176,16 @@ $.extend(shopping_cart, {
|
||||
const $btn = $(e.currentTarget);
|
||||
$btn.prop('disabled', true);
|
||||
|
||||
if (frappe.session.user==="Guest") {
|
||||
if (localStorage) {
|
||||
localStorage.setItem("last_visited", window.location.pathname);
|
||||
}
|
||||
frappe.call('erpnext.e_commerce.api.get_guest_redirect_on_action').then((res) => {
|
||||
window.location.href = res.message || "/login";
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$btn.addClass('hidden');
|
||||
$btn.parent().find('.go-to-cart').removeClass('hidden');
|
||||
$btn.parent().find('.go-to-cart-grid').removeClass('hidden');
|
||||
|
@ -81,53 +81,55 @@ $.extend(wishlist, {
|
||||
// 'wish'('like') or 'unwish' item in product listing
|
||||
$('.page_content').on('click', '.like-action, .like-action-list', (e) => {
|
||||
const $btn = $(e.currentTarget);
|
||||
const $wish_icon = $btn.find('.wish-icon');
|
||||
let me = this;
|
||||
|
||||
if (frappe.session.user==="Guest") {
|
||||
if (localStorage) {
|
||||
localStorage.setItem("last_visited", window.location.pathname);
|
||||
}
|
||||
|
||||
this.redirect_guest();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let success_action = function() {
|
||||
e_commerce.wishlist.set_wishlist_count();
|
||||
};
|
||||
|
||||
if ($wish_icon.hasClass('wished')) {
|
||||
// un-wish item
|
||||
$btn.removeClass("like-animate");
|
||||
$btn.addClass("like-action-wished");
|
||||
this.toggle_button_class($wish_icon, 'wished', 'not-wished');
|
||||
|
||||
let args = { item_code: $btn.data('item-code') };
|
||||
let failure_action = function() {
|
||||
me.toggle_button_class($wish_icon, 'not-wished', 'wished');
|
||||
};
|
||||
this.add_remove_from_wishlist("remove", args, success_action, failure_action);
|
||||
} else {
|
||||
// wish item
|
||||
$btn.addClass("like-animate");
|
||||
$btn.addClass("like-action-wished");
|
||||
this.toggle_button_class($wish_icon, 'not-wished', 'wished');
|
||||
|
||||
let args = {
|
||||
item_code: $btn.data('item-code'),
|
||||
price: $btn.data('price'),
|
||||
formatted_price: $btn.data('formatted-price')
|
||||
};
|
||||
let failure_action = function() {
|
||||
me.toggle_button_class($wish_icon, 'wished', 'not-wished');
|
||||
};
|
||||
this.add_remove_from_wishlist("add", args, success_action, failure_action);
|
||||
}
|
||||
this.wishlist_action($btn);
|
||||
});
|
||||
},
|
||||
|
||||
wishlist_action(btn) {
|
||||
const $wish_icon = btn.find('.wish-icon');
|
||||
let me = this;
|
||||
|
||||
if (frappe.session.user==="Guest") {
|
||||
if (localStorage) {
|
||||
localStorage.setItem("last_visited", window.location.pathname);
|
||||
}
|
||||
this.redirect_guest();
|
||||
return;
|
||||
}
|
||||
|
||||
let success_action = function() {
|
||||
e_commerce.wishlist.set_wishlist_count();
|
||||
};
|
||||
|
||||
if ($wish_icon.hasClass('wished')) {
|
||||
// un-wish item
|
||||
btn.removeClass("like-animate");
|
||||
btn.addClass("like-action-wished");
|
||||
this.toggle_button_class($wish_icon, 'wished', 'not-wished');
|
||||
|
||||
let args = { item_code: btn.data('item-code') };
|
||||
let failure_action = function() {
|
||||
me.toggle_button_class($wish_icon, 'not-wished', 'wished');
|
||||
};
|
||||
this.add_remove_from_wishlist("remove", args, success_action, failure_action);
|
||||
} else {
|
||||
// wish item
|
||||
btn.addClass("like-animate");
|
||||
btn.addClass("like-action-wished");
|
||||
this.toggle_button_class($wish_icon, 'not-wished', 'wished');
|
||||
|
||||
let args = {
|
||||
item_code: btn.data('item-code'),
|
||||
price: btn.data('price'),
|
||||
formatted_price: btn.data('formatted-price')
|
||||
};
|
||||
let failure_action = function() {
|
||||
me.toggle_button_class($wish_icon, 'wished', 'not-wished');
|
||||
};
|
||||
this.add_remove_from_wishlist("add", args, success_action, failure_action);
|
||||
}
|
||||
},
|
||||
|
||||
toggle_button_class(button, remove, add) {
|
||||
button.removeClass(remove);
|
||||
button.addClass(add);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
:root {
|
||||
--green-info: #38A160;
|
||||
--product-bg-color: white;
|
||||
}
|
||||
|
||||
body.product-page {
|
||||
@ -289,6 +290,7 @@ body.product-page {
|
||||
|
||||
.product-container {
|
||||
@include card($padding: var(--padding-md));
|
||||
background-color: var(--product-bg-color) !important;
|
||||
min-height: 70vh;
|
||||
|
||||
.product-details {
|
||||
@ -299,6 +301,12 @@ body.product-page {
|
||||
}
|
||||
}
|
||||
|
||||
&.item-main {
|
||||
.product-image {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.expand {
|
||||
max-width: 100% !important; // expand in absence of slideshow
|
||||
}
|
||||
@ -327,9 +335,10 @@ body.product-page {
|
||||
}
|
||||
|
||||
.product-title {
|
||||
font-size: 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.product-description {
|
||||
@ -385,7 +394,7 @@ body.product-page {
|
||||
|
||||
.item-cart {
|
||||
.product-price {
|
||||
font-size: 20px;
|
||||
font-size: 22px;
|
||||
color: var(--text-color);
|
||||
font-weight: 600;
|
||||
|
||||
@ -398,12 +407,94 @@ body.product-page {
|
||||
.no-stock {
|
||||
font-size: var(--text-base);
|
||||
}
|
||||
|
||||
.offers-heading {
|
||||
font-size: 16px !important;
|
||||
color: var(--text-color);
|
||||
.tag-icon {
|
||||
--icon-stroke: var(--gray-500);
|
||||
}
|
||||
}
|
||||
|
||||
.w-30-40 {
|
||||
width: 30%;
|
||||
|
||||
@media (max-width: 992px) {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
// Item Recommendations
|
||||
.recommended-item-section {
|
||||
padding-right: 0;
|
||||
|
||||
.recommendation-header {
|
||||
font-size: 16px;
|
||||
font-weight: 500
|
||||
}
|
||||
|
||||
.recommendation-container {
|
||||
padding: .5rem;
|
||||
min-height: 0px;
|
||||
|
||||
.r-item-image {
|
||||
width: 40%;
|
||||
|
||||
.no-image-r-item {
|
||||
display: flex; justify-content: center;
|
||||
background-color: var(--gray-200);
|
||||
align-items: center;
|
||||
color: var(--gray-400);
|
||||
margin-top: .15rem;
|
||||
border-radius: 6px;
|
||||
height: 100%;
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.r-item-info {
|
||||
font-size: 14px;
|
||||
padding-left: 8px;
|
||||
padding-right: 0;
|
||||
width: 60%;
|
||||
|
||||
a {
|
||||
color: var(--gray-800);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.item-price {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.striked-item-price {
|
||||
font-weight: 500;
|
||||
color: var(--gray-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-code {
|
||||
padding: .5rem 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 14px;
|
||||
.product-item-group {
|
||||
padding-right: .25rem;
|
||||
border-right: solid 1px var(--text-muted);
|
||||
}
|
||||
|
||||
.product-item-code {
|
||||
padding-left: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.item-configurator-dialog {
|
||||
@ -794,6 +885,12 @@ body.product-page {
|
||||
}
|
||||
}
|
||||
|
||||
.like-action-item-fp {
|
||||
visibility: visible !important;
|
||||
position: unset;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.like-animate {
|
||||
animation: expand cubic-bezier(0.04, 0.4, 0.5, 0.95) 1.6s forwards 1;
|
||||
}
|
||||
@ -919,16 +1016,63 @@ body.product-page {
|
||||
|
||||
.item-website-specification {
|
||||
font-size: .875rem;
|
||||
.product-title {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
td {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.spec-label {
|
||||
color: var(--gray-600);
|
||||
}
|
||||
|
||||
.spec-content {
|
||||
color: var(--gray-800);
|
||||
}
|
||||
}
|
||||
|
||||
.reviews-full-page {
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
|
||||
.ratings-reviews-section {
|
||||
border-top: 1px solid #E2E6E9;
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
|
||||
.reviews-header {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--gray-800);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.btn-write-review {
|
||||
float: right;
|
||||
padding: .5rem 1rem;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
border: none !important;
|
||||
box-shadow: none;
|
||||
|
||||
color: var(--gray-900);
|
||||
background-color: var(--gray-100);
|
||||
|
||||
&:hover {
|
||||
box-shadow: var(--btn-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
.rating-summary-section {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.rating-summary-title {
|
||||
@ -936,9 +1080,17 @@ body.product-page {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.rating-summary-numbers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
border-right: solid 1px var(--gray-100);
|
||||
}
|
||||
|
||||
.user-review-title {
|
||||
margin-top: 0.15rem;
|
||||
font-size: 16px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@ -952,6 +1104,12 @@ body.product-page {
|
||||
}
|
||||
}
|
||||
|
||||
.ratings-pill {
|
||||
background-color: var(--gray-100);
|
||||
padding: .5rem 1rem;
|
||||
border-radius: 66px;
|
||||
}
|
||||
|
||||
.review {
|
||||
max-width: 80%;
|
||||
line-height: 1.6;
|
||||
@ -961,21 +1119,18 @@ body.product-page {
|
||||
|
||||
.review-signature {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
color: var(--gray-500);
|
||||
font-weight: 400;
|
||||
|
||||
.reviewer {
|
||||
padding-right: 8px;
|
||||
margin-right: 8px;
|
||||
border-right: 1px solid var(--gray-400);
|
||||
color: var(--gray-600);
|
||||
}
|
||||
}
|
||||
|
||||
.rating-progress-bar-section {
|
||||
padding-bottom: 2rem;
|
||||
border-bottom: 1px solid #E2E6E9;
|
||||
margin-right: -10px;
|
||||
|
||||
.rating-bar-title {
|
||||
margin-left: -15px;
|
||||
@ -985,14 +1140,15 @@ body.product-page {
|
||||
margin-bottom: 4px;
|
||||
height: 7px;
|
||||
margin-top: 6px;
|
||||
|
||||
.progress-bar-cosmetic {
|
||||
background-color: var(--gray-600);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.offer-container {
|
||||
border: 1px solid var(--gray-300);
|
||||
border-style: dashed;
|
||||
border-radius: 4px;
|
||||
padding: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@ -1075,3 +1231,12 @@ body.product-page {
|
||||
.font-md {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.in-green {
|
||||
color: var(--green-info) !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mt-minus-2 {
|
||||
margin-top: -2rem;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% extends "templates/web.html" %}
|
||||
{% from "erpnext/templates/includes/macros.html" import recommended_item_row %}
|
||||
|
||||
{% block title %} {{ title }} {% endblock %}
|
||||
|
||||
@ -9,7 +10,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="product-container col-md-12">
|
||||
<div class="product-container item-main">
|
||||
{% from "erpnext/templates/includes/macros.html" import product_image %}
|
||||
<div class="item-content">
|
||||
<div class="product-page-content" itemscope itemtype="http://schema.org/Product">
|
||||
@ -18,33 +19,56 @@
|
||||
{% include "templates/generators/item/item_image.html" %}
|
||||
{% include "templates/generators/item/item_details.html" %}
|
||||
</div>
|
||||
|
||||
<!-- Product Specifications Table Section -->
|
||||
{% if show_tabs and tabs %}
|
||||
<div class="category-tabs">
|
||||
<!-- tabs -->
|
||||
{{ web_block(
|
||||
"Section with Tabs",
|
||||
values=tabs,
|
||||
add_container=0,
|
||||
add_top_padding=0,
|
||||
add_bottom_padding=0
|
||||
) }}
|
||||
</div>
|
||||
{% elif website_specifications %}
|
||||
{% include "templates/generators/item/item_specifications.html"%}
|
||||
{% endif %}
|
||||
|
||||
<!-- Advanced Custom Website Content -->
|
||||
{{ doc.website_content or '' }}
|
||||
|
||||
<!-- Reviews and Comments -->
|
||||
{% if shopping_cart.cart_settings.enable_reviews and not doc.has_variants %}
|
||||
{% include "templates/generators/item/item_reviews.html"%}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Additional Info/Reviews, Recommendations -->
|
||||
<div class="d-flex">
|
||||
{% set show_recommended_items = recommended_items and shopping_cart.cart_settings.enable_recommendations %}
|
||||
{% set info_col = 'col-9' if show_recommended_items else 'col-12' %}
|
||||
|
||||
{% set padding_top = 'pt-0' if (show_tabs and tabs) else '' %}
|
||||
|
||||
<div class="product-container mt-4 {{ padding_top }} {{ info_col }}">
|
||||
<div class="item-content {{ 'mt-minus-2' if (show_tabs and tabs) else '' }}">
|
||||
<div class="product-page-content" itemscope itemtype="http://schema.org/Product">
|
||||
<!-- Product Specifications Table Section -->
|
||||
{% if show_tabs and tabs %}
|
||||
<div class="category-tabs">
|
||||
<!-- tabs -->
|
||||
{{ web_block("Section with Tabs", values=tabs, add_container=0,
|
||||
add_top_padding=0, add_bottom_padding=0)
|
||||
}}
|
||||
</div>
|
||||
{% elif website_specifications %}
|
||||
{% include "templates/generators/item/item_specifications.html"%}
|
||||
{% endif %}
|
||||
|
||||
<!-- Advanced Custom Website Content -->
|
||||
{{ doc.website_content or '' }}
|
||||
|
||||
<!-- Reviews and Comments -->
|
||||
{% if shopping_cart.cart_settings.enable_reviews and not doc.has_variants %}
|
||||
{% include "templates/generators/item/item_reviews.html"%}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recommended Items -->
|
||||
{% if show_recommended_items %}
|
||||
<div class="mt-4 col-3 recommended-item-section">
|
||||
<span class="recommendation-header">Recommended</span>
|
||||
<div class="product-container mt-2 recommendation-container">
|
||||
{% for item in recommended_items %}
|
||||
{{ recommended_item_row(item) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block base_scripts %}
|
||||
|
@ -7,34 +7,39 @@
|
||||
<div class="col-md-12">
|
||||
<!-- Price and Availability -->
|
||||
{% if cart_settings.show_price and product_info.price %}
|
||||
{% set price_info = product_info.price %}
|
||||
{% set price_info = product_info.price %}
|
||||
|
||||
{% if price_info.formatted_mrp %}
|
||||
<small class="formatted-price">
|
||||
M.R.P.:
|
||||
<s>{{ price_info.formatted_mrp }}</s>
|
||||
</small>
|
||||
<small class="ml-2 formatted-price" style="color: #F47A7A; font-weight: 500;">
|
||||
{{ price_info.get("formatted_discount_percent") or price_info.get("formatted_discount_rate")}} OFF
|
||||
</small>
|
||||
{% endif %}
|
||||
<div class="product-price">
|
||||
<!-- Final Price -->
|
||||
{{ price_info.formatted_price_sales_uom }}
|
||||
|
||||
<div class="product-price">
|
||||
{{ price_info.formatted_price_sales_uom }}
|
||||
<small class="formatted-price">({{ price_info.formatted_price }} / {{ product_info.uom }})</small>
|
||||
</div>
|
||||
<!-- Striked Price and Discount -->
|
||||
{% if price_info.formatted_mrp %}
|
||||
<small class="formatted-price">
|
||||
<s>MRP {{ price_info.formatted_mrp }}</s>
|
||||
</small>
|
||||
<small class="ml-1 formatted-price in-green">
|
||||
-{{ price_info.get("formatted_discount_percent") or price_info.get("formatted_discount_rate")}}
|
||||
</small>
|
||||
{% endif %}
|
||||
|
||||
<!-- Price per UOM -->
|
||||
<small class="formatted-price ml-2">
|
||||
({{ price_info.formatted_price }} / {{ product_info.uom }})
|
||||
</small>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ _("UOM") }} : {{ product_info.uom }}
|
||||
{% endif %}
|
||||
|
||||
{% if cart_settings.show_stock_availability %}
|
||||
<div>
|
||||
<div class="mt-2">
|
||||
{% if product_info.in_stock == 0 %}
|
||||
<span class="text-danger no-stock">
|
||||
{{ _('Not in stock') }}
|
||||
</span>
|
||||
{% elif product_info.in_stock == 1 %}
|
||||
<span class="text-success has-stock">
|
||||
<span class="in-green has-stock">
|
||||
{{ _('In stock') }}
|
||||
{% if product_info.show_stock_qty and product_info.stock_qty %}
|
||||
({{ product_info.stock_qty[0][0] }})
|
||||
@ -47,10 +52,15 @@
|
||||
<!-- Offers -->
|
||||
{% if doc.offers %}
|
||||
<br>
|
||||
<h3>Offers</h3>
|
||||
<div class="offers-heading mb-4">
|
||||
<span class="mr-1 tag-icon">
|
||||
<svg class="icon icon-lg"><use href="#icon-tag"></use></svg>
|
||||
</span>
|
||||
<b>Available Offers</b>
|
||||
</div>
|
||||
<div class="offer-container">
|
||||
{% for offer in doc.offers %}
|
||||
<div class="mt-2" style="display: flex;">
|
||||
<div class="mt-2 d-flex">
|
||||
<div class="mr-2" >
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" stroke="var(--yellow-500)" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 15.6213C19 15.2235 19.158 14.842 19.4393 14.5607L20.9393 13.0607C21.5251 12.4749 21.5251 11.5251 20.9393 10.9393L19.4393 9.43934C19.158 9.15804 19 8.7765 19 8.37868V6.5C19 5.67157 18.3284 5 17.5 5H15.6213C15.2235 5 14.842 4.84196 14.5607 4.56066L13.0607 3.06066C12.4749 2.47487 11.5251 2.47487 10.9393 3.06066L9.43934 4.56066C9.15804 4.84196 8.7765 5 8.37868 5H6.5C5.67157 5 5 5.67157 5 6.5V8.37868C5 8.7765 4.84196 9.15804 4.56066 9.43934L3.06066 10.9393C2.47487 11.5251 2.47487 12.4749 3.06066 13.0607L4.56066 14.5607C4.84196 14.842 5 15.2235 5 15.6213V17.5C5 18.3284 5.67157 19 6.5 19H8.37868C8.7765 19 9.15804 19.158 9.43934 19.4393L10.9393 20.9393C11.5251 21.5251 12.4749 21.5251 13.0607 20.9393L14.5607 19.4393C14.842 19.158 15.2235 19 15.6213 19H17.5C18.3284 19 19 18.3284 19 17.5V15.6213Z" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
@ -59,8 +69,8 @@
|
||||
<path d="M15.5 14.5C15.5 15.0523 15.0523 15.5 14.5 15.5C13.9477 15.5 13.5 15.0523 13.5 14.5C13.5 13.9477 13.9477 13.5 14.5 13.5C15.0523 13.5 15.5 13.9477 15.5 14.5Z" fill="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="mr-1">
|
||||
<strong>{{ _(offer.offer_title) }}:</strong>
|
||||
<p class="mr-1 mb-1">
|
||||
{{ _(offer.offer_title) }}:
|
||||
{{ _(offer.offer_subtitle) }}
|
||||
<a class="offer-details" href="#"
|
||||
data-offer-title="{{ offer.offer_title }}" data-offer-id="{{ offer.name }}"
|
||||
@ -74,20 +84,14 @@
|
||||
{% endif %}
|
||||
|
||||
<!-- Add to Cart / View in Cart, Contact Us -->
|
||||
<div class="mt-5 mb-5">
|
||||
<div style="display: flex;" class="mb-4">
|
||||
<div class="mt-6 mb-5">
|
||||
<div class="mb-4 d-flex">
|
||||
<!-- Add to Cart -->
|
||||
{% if product_info.price and (cart_settings.allow_items_not_in_stock or product_info.in_stock) %}
|
||||
<a href="/cart"
|
||||
class="btn btn-light btn-view-in-cart hidden mr-2 font-md"
|
||||
role="button"
|
||||
>
|
||||
<a href="/cart" class="btn btn-light btn-view-in-cart hidden mr-2 font-md" role="button">
|
||||
{{ _("View in Cart") }}
|
||||
</a>
|
||||
<button
|
||||
data-item-code="{{item_code}}"
|
||||
class="btn btn-primary btn-add-to-cart w-50 mr-2"
|
||||
>
|
||||
<button data-item-code="{{item_code}}" class="btn btn-primary btn-add-to-cart mr-2 w-30-40">
|
||||
<span class="mr-2">
|
||||
<svg class="icon icon-md">
|
||||
<use href="#icon-assets"></use>
|
||||
@ -97,41 +101,11 @@
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<!-- Add to Wishlist -->
|
||||
{% if cart_settings.enable_wishlist %}
|
||||
<a href="/wishlist"
|
||||
class="btn btn-view-in-wishlist font-md hidden"
|
||||
role="button"
|
||||
>
|
||||
<span class="mr-2">
|
||||
<svg class="icon icon-md">
|
||||
<use href="#icon-heart"></use>
|
||||
</svg>
|
||||
</span>
|
||||
{{ _("View in Wishlist") }}
|
||||
</a>
|
||||
|
||||
{% set price = product_info.get("price") or {} %}
|
||||
<button
|
||||
data-item-code="{{item_code}}"
|
||||
data-price="{{ price.get('price_list_rate') or 0}}"
|
||||
data-formatted-price="{{ price.get('formatted_price') or 0 }}"
|
||||
class="btn btn-add-to-wishlist font-md"
|
||||
>
|
||||
<span class="mr-2">
|
||||
<svg class="icon icon-md">
|
||||
<use href="#icon-heart"></use>
|
||||
</svg>
|
||||
</span>
|
||||
{{ _("Add to Wishlist") }}
|
||||
</button>
|
||||
<!-- Contact Us -->
|
||||
{% if cart_settings.show_contact_us_button %}
|
||||
{% include "templates/generators/item/item_inquiry.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Contact Us -->
|
||||
{% if cart_settings.show_contact_us_button %}
|
||||
{% include "templates/generators/item/item_inquiry.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -155,28 +129,6 @@
|
||||
});
|
||||
});
|
||||
|
||||
$('.page_content').on('click', '.btn-add-to-wishlist', (e) => {
|
||||
// Bind action on wishlist button
|
||||
const $btn = $(e.currentTarget);
|
||||
$btn.prop('disabled', true);
|
||||
|
||||
let args = {
|
||||
item_code: $btn.data('item-code'),
|
||||
price: $btn.data('price'),
|
||||
formatted_price: $btn.data('formatted-price')
|
||||
};
|
||||
let failure_action = function() {
|
||||
$btn.prop('disabled', false);
|
||||
};
|
||||
let success_action = function() {
|
||||
$btn.prop('disabled', false);
|
||||
e_commerce.wishlist.set_wishlist_count();
|
||||
$('.btn-add-to-wishlist, .btn-view-in-wishlist').toggleClass('hidden');
|
||||
|
||||
};
|
||||
e_commerce.wishlist.add_remove_from_wishlist("add", args, success_action, failure_action);
|
||||
});
|
||||
|
||||
$('.page_content').on('click', '.offer-details', (e) => {
|
||||
// Bind action on More link in Offers
|
||||
const $btn = $(e.currentTarget);
|
||||
|
@ -1,28 +1,65 @@
|
||||
{% set width_class = "expand" if not slides else "" %}
|
||||
{% set cart_settings = shopping_cart.cart_settings %}
|
||||
{% set product_info = shopping_cart.product_info %}
|
||||
{% set price_info = product_info.get('price') or {} %}
|
||||
|
||||
<div class="col-md-7 product-details {{ width_class }}">
|
||||
<!-- title -->
|
||||
<h1 class="product-title" itemprop="name">
|
||||
{{ doc.web_item_name }}
|
||||
</h1>
|
||||
<p class="product-code">
|
||||
<span>{{ _("Item Code") }}:</span>
|
||||
<span itemprop="productID">{{ doc.item_code }}</span>
|
||||
</p>
|
||||
{% if has_variants %}
|
||||
<!-- configure template -->
|
||||
{% include "templates/generators/item/item_configure.html" %}
|
||||
{% else %}
|
||||
<!-- add variant to cart -->
|
||||
{% include "templates/generators/item/item_add_to_cart.html" %}
|
||||
{% endif %}
|
||||
<!-- description -->
|
||||
<div class="product-description" itemprop="description">
|
||||
{% if frappe.utils.strip_html(doc.web_long_description or '') %}
|
||||
{{ doc.web_long_description | safe }}
|
||||
{% elif frappe.utils.strip_html(doc.description or '') %}
|
||||
{{ doc.description | safe }}
|
||||
{% else %}
|
||||
{{ _("No description given") }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<!-- title -->
|
||||
<div class="product-title col-11" itemprop="name">
|
||||
{{ doc.web_item_name }}
|
||||
</div>
|
||||
|
||||
<!-- Wishlist -->
|
||||
{% if cart_settings.enable_wishlist %}
|
||||
<div class="like-action-item-fp like-action {{ 'like-action-wished' if wished else ''}} ml-2"
|
||||
data-item-code="{{ doc.item_code }}"
|
||||
data-price="{{ price_info.get('price_list_rate') if price_info else 0 }}"
|
||||
data-formatted-price="{{ price_info.get('formatted_price') if price_info else 0 }}">
|
||||
<svg class="icon sm">
|
||||
<use class="{{ 'wished' if wished else 'not-wished' }} wish-icon" href="#icon-heart"></use>
|
||||
</svg>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<p class="product-code">
|
||||
<span class="product-item-group">
|
||||
{{ _(doc.item_group) }}
|
||||
</span>
|
||||
<span class="product-item-code">
|
||||
{{ _("Item Code") }}:
|
||||
</span>
|
||||
<span itemprop="productID">{{ doc.item_code }}</span>
|
||||
</p>
|
||||
{% if has_variants %}
|
||||
<!-- configure template -->
|
||||
{% include "templates/generators/item/item_configure.html" %}
|
||||
{% else %}
|
||||
<!-- add variant to cart -->
|
||||
{% include "templates/generators/item/item_add_to_cart.html" %}
|
||||
{% endif %}
|
||||
<!-- description -->
|
||||
<div class="product-description" itemprop="description">
|
||||
{% if frappe.utils.strip_html(doc.web_long_description or '') %}
|
||||
{{ doc.web_long_description | safe }}
|
||||
{% elif frappe.utils.strip_html(doc.description or '') %}
|
||||
{{ doc.description | safe }}
|
||||
{% else %}
|
||||
{{ "" }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% block base_scripts %}
|
||||
<!-- js should be loaded in body! -->
|
||||
<script type="text/javascript" src="/assets/frappe/js/lib/jquery/jquery.min.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
<script>
|
||||
$('.page_content').on('click', '.like-action-item-fp', (e) => {
|
||||
// Bind action on wishlist button
|
||||
const $btn = $(e.currentTarget);
|
||||
e_commerce.wishlist.wishlist_action($btn);
|
||||
});
|
||||
</script>
|
@ -1,7 +1,7 @@
|
||||
{% if shopping_cart and shopping_cart.cart_settings.enabled %}
|
||||
{% set cart_settings = shopping_cart.cart_settings %}
|
||||
{% if cart_settings.show_contact_us_button | int %}
|
||||
<button class="btn btn-inquiry btn-primary-light font-md" data-item-code="{{ doc.name }}">
|
||||
<button class="btn btn-inquiry font-md w-30-40" data-item-code="{{ doc.name }}">
|
||||
{{ _('Contact Us') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
|
@ -1,23 +1,29 @@
|
||||
{% from "erpnext/templates/includes/macros.html" import user_review, ratings_summary %}
|
||||
|
||||
<div class="mt-12 ratings-reviews-section" style="display: flex;">
|
||||
<div class="col-md-4 order-md-1 mt-8" style="max-width: 300px;">
|
||||
{{ ratings_summary(reviews, reviews_per_rating, average_rating, average_whole_rating) }}
|
||||
<div class="mt-4 ratings-reviews-section">
|
||||
<!-- Title and Action -->
|
||||
<div class="w-100 mt-4 mb-2 d-flex">
|
||||
<div class="reviews-header col-9">
|
||||
{{ _("Customer Reviews") }}
|
||||
</div>
|
||||
|
||||
<div class="write-a-review-btn col-3">
|
||||
<!-- Write a Review for legitimate users -->
|
||||
{% if frappe.session.user != "Guest" and user_is_customer %}
|
||||
<button class="btn btn-write-review"
|
||||
data-web-item="{{ doc.name }}">
|
||||
{{ _("Write a Review") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary -->
|
||||
{{ ratings_summary(reviews, reviews_per_rating, average_rating, average_whole_rating, for_summary=True, total_reviews=total_reviews) }}
|
||||
|
||||
<!-- Write a Review for legitimate users -->
|
||||
{% if frappe.session.user != "Guest" and user_is_customer %}
|
||||
<button class="btn btn-light btn-write-review mr-2 mt-4 mb-4 w-100"
|
||||
data-web-item="{{ doc.name }}">
|
||||
{{ _("Write a Review") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Reviews and Comments -->
|
||||
<div class="col-12 order-2 col-md-9 order-md-2 mt-8 ml-16">
|
||||
<h2 class="reviews-header">
|
||||
{{ _("Reviews") }}
|
||||
</h2>
|
||||
<div class="mt-8">
|
||||
{% if reviews %}
|
||||
{{ user_review(reviews) }}
|
||||
|
||||
@ -64,7 +70,7 @@
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
frappe.msgprint({
|
||||
message: __("Thank you for submitting your review"),
|
||||
message: __("Thank you for the review"),
|
||||
title: __("Review Submitted"),
|
||||
indicator: "green"
|
||||
});
|
||||
|
@ -3,13 +3,15 @@
|
||||
<div class="mt-5 item-website-specification">
|
||||
<div class="col-md-11">
|
||||
{% if not show_tabs %}
|
||||
<h3 class="product-title mb-5 mt-8">Product Details</h3>
|
||||
<div class="product-title mb-5 mt-8">
|
||||
Product Details
|
||||
</div>
|
||||
{% endif %}
|
||||
<table class="table table-bordered table-hover">
|
||||
<table class="table">
|
||||
{% for d in website_specifications -%}
|
||||
<tr>
|
||||
<td class="text-muted" style="width: 30%; font-weight: bold;">{{ d.label }}</td>
|
||||
<td>{{ d.description }}</td>
|
||||
<td class="spec-label">{{ d.label }}</td>
|
||||
<td class="spec-content">{{ d.description }}</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
|
@ -7,8 +7,8 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro product_image(website_image, css_class="product-image", alt="") %}
|
||||
<div class="border text-center rounded {{ css_class }}" style="overflow: hidden;">
|
||||
{% macro product_image(website_image, css_class="product-image", alt="", no_border=False) %}
|
||||
<div class="{{ 'border' if not no_border else ''}} text-center rounded {{ css_class }}" style="overflow: hidden;">
|
||||
{% if website_image %}
|
||||
<img itemprop="image" class="website-image h-100 w-100" alt="{{ alt }}" src="{{ frappe.utils.quoted(website_image) | abs_url }}">
|
||||
{% else %}
|
||||
@ -208,9 +208,12 @@
|
||||
</div>
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro ratings_with_title(avg_rating, title, size, rating_header_class) -%}
|
||||
<div style="display: flex;">
|
||||
<div class="rating">
|
||||
{%- macro ratings_with_title(avg_rating, title, size, rating_header_class, for_summary=False) -%}
|
||||
<div class="{{ 'd-flex' if not for_summary else '' }}">
|
||||
<p class="mr-4 {{ rating_header_class }}">
|
||||
<span>{{ title }}</span>
|
||||
</p>
|
||||
<div class="rating {{ 'ratings-pill' if for_summary else ''}}">
|
||||
{% for i in range(1,6) %}
|
||||
{% set fill_class = 'star-click' if i <= avg_rating else '' %}
|
||||
<svg class="icon icon-{{ size }} {{ fill_class }}">
|
||||
@ -218,44 +221,50 @@
|
||||
</svg>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="ml-4 {{ rating_header_class }}">
|
||||
<span>{{ title }}</span>
|
||||
</p>
|
||||
</div>
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro ratings_summary(reviews, reviews_per_rating, average_rating, average_whole_rating)-%}
|
||||
<!-- Ratings Summary -->
|
||||
<h2 class="reviews-header">
|
||||
{{ _("Customer Ratings") }}
|
||||
</h2>
|
||||
|
||||
{% if reviews %}
|
||||
{% set rating_title = frappe.utils.cstr(average_rating) + " " + _("out of 5") %}
|
||||
{{ ratings_with_title(average_whole_rating, rating_title, "lg", "rating-summary-title") }}
|
||||
{% endif %}
|
||||
|
||||
<!-- Rating Progress Bars -->
|
||||
<div class="rating-progress-bar-section">
|
||||
{% for percent in reviews_per_rating %}
|
||||
<div class="mt-4 col-sm-4 small rating-bar-title">
|
||||
{{ loop.index }} star
|
||||
{%- macro ratings_summary(reviews, reviews_per_rating, average_rating, average_whole_rating, for_summary=False, total_reviews=None)-%}
|
||||
<div class="rating-summary-section mt-4">
|
||||
<div class="rating-summary-numbers col-3">
|
||||
<h2 style="font-size: 2rem;">
|
||||
{{ average_rating or 0 }}
|
||||
</h2>
|
||||
<div class="mb-2" style="margin-top: -.5rem;">
|
||||
{{ frappe.utils.cstr(total_reviews) + " " + _("ratings") }}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="progress rating-progress-bar" title="{{ percent }} % of reviews are {{ loop.index }} star">
|
||||
<div class="progress-bar" role="progressbar"
|
||||
aria-valuenow="{{ percent }}"
|
||||
aria-valuemin="0" aria-valuemax="100"
|
||||
style="width: {{ percent }}%; background-color: var(--text-on-green);">
|
||||
|
||||
<!-- Ratings Summary -->
|
||||
{% if reviews %}
|
||||
{% set rating_title = frappe.utils.cstr(average_rating) + " " + _("out of 5") if not for_summary else ''%}
|
||||
{{ ratings_with_title(average_whole_rating, rating_title, "md", "rating-summary-title", for_summary) }}
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-2">{{ frappe.utils.cstr(average_rating or 0) + " " + _("out of 5") }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Rating Progress Bars -->
|
||||
<div class="rating-progress-bar-section col-4 ml-4">
|
||||
{% for percent in reviews_per_rating %}
|
||||
<div class="col-sm-4 small rating-bar-title">
|
||||
{{ loop.index }} star
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="progress rating-progress-bar" title="{{ percent }} % of reviews are {{ loop.index }} star">
|
||||
<div class="progress-bar progress-bar-cosmetic" role="progressbar"
|
||||
aria-valuenow="{{ percent }}"
|
||||
aria-valuemin="0" aria-valuemax="100"
|
||||
style="width: {{ percent }}%;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-1 small">
|
||||
{{ percent }}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-1 small">
|
||||
{{ percent }}%
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{%- endmacro -%}
|
||||
|
||||
@ -264,17 +273,19 @@
|
||||
<div class="user-reviews">
|
||||
{% for review in reviews %}
|
||||
<div class="mb-3 review">
|
||||
{{ ratings_with_title(review.rating, _(review.review_title), "md", "user-review-title") }}
|
||||
{{ ratings_with_title(review.rating, _(review.review_title), "sm", "user-review-title") }}
|
||||
|
||||
<div class="review-signature">
|
||||
<span class="reviewer">{{ _(review.customer) }}</span>
|
||||
<span>{{ review.published_on }}</span>
|
||||
</div>
|
||||
<div class="product-description mb-4 mt-4">
|
||||
<div class="product-description mb-4">
|
||||
<p>
|
||||
{{ _(review.comment) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="review-signature mb-2">
|
||||
<span class="reviewer">{{ _(review.customer) }}</span>
|
||||
<span class="indicator grey" style="--text-on-gray: var(--gray-300);"></span>
|
||||
<span class="reviewer">{{ review.published_on }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@ -347,3 +358,42 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro recommended_item_row(item)-%}
|
||||
<div class="recommended-item mb-6 d-flex">
|
||||
<div class="r-item-image">
|
||||
{% if item.website_item_thumbnail %}
|
||||
{{ product_image(item.website_item_thumbnail, alt="item.website_item_name", no_border=True) }}
|
||||
{% else %}
|
||||
<div class = "no-image-r-item">
|
||||
{{ frappe.utils.get_abbr(item.website_item_name) or "NA" }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="r-item-info">
|
||||
<a href="/{{ item.route or '#'}}" target="_blank">
|
||||
{% set title = item.website_item_name %}
|
||||
{{ title[:70] + "..." if title|len > 70 else title }}
|
||||
</a>
|
||||
|
||||
{% if item.get('price_info') %}
|
||||
{% set price = item.get('price_info') %}
|
||||
<div class="mt-2">
|
||||
<span class="item-price">
|
||||
{{ price.get('formatted_price') or '' }}
|
||||
</span>
|
||||
|
||||
{% if price.get('formatted_mrp') %}
|
||||
<br>
|
||||
<span class="striked-item-price">
|
||||
<s>MRP {{ price.formatted_mrp }}</s>
|
||||
</span>
|
||||
<span class="in-green">
|
||||
- {{ price.get('formatted_discount_percent') or price.get('formatted_discount_rate')}}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{%- endmacro -%}
|
||||
|
@ -4,25 +4,30 @@
|
||||
{% block title %} {{ _("Customer Reviews") }} {% endblock %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="product-container col-md-12">
|
||||
<div style="display: flex;">
|
||||
<div class="col-md-4 order-md-1 mt-8" style="max-width: 300px;">
|
||||
{{ ratings_summary(reviews, reviews_per_rating, average_rating, average_whole_rating) }}
|
||||
<div class="product-container reviews-full-page col-md-12">
|
||||
<!-- Title and Action -->
|
||||
<div class="w-100 mb-6 d-flex">
|
||||
<div class="reviews-header col-9">
|
||||
{{ _("Customer Reviews") }}
|
||||
</div>
|
||||
|
||||
<!-- Write a Review for legitimate users -->
|
||||
{% if frappe.session.user != "Guest" %}
|
||||
<button class="btn btn-light btn-write-review mr-2 mt-4 mb-4 w-100"
|
||||
data-web-item="{{ web_item }}">
|
||||
{{ _("Write a Review") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
<div class="write-a-review-btn col-3">
|
||||
<!-- Write a Review for legitimate users -->
|
||||
{% if frappe.session.user != "Guest" and user_is_customer %}
|
||||
<button class="btn btn-write-review"
|
||||
data-web-item="{{ web_item }}">
|
||||
{{ _("Write a Review") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary -->
|
||||
{{ ratings_summary(reviews, reviews_per_rating, average_rating, average_whole_rating, for_summary=True, total_reviews=total_reviews) }}
|
||||
|
||||
|
||||
<!-- Reviews and Comments -->
|
||||
<div class="col-12 order-2 col-md-9 order-md-2 mt-8 ml-16">
|
||||
<h2 class="reviews-header">
|
||||
{{ _("Reviews") }}
|
||||
</h2>
|
||||
<div class="mt-8">
|
||||
{% if reviews %}
|
||||
{{ user_review(reviews) }}
|
||||
|
||||
@ -40,7 +45,6 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
import frappe
|
||||
from erpnext.e_commerce.doctype.item_review.item_review import get_item_reviews
|
||||
from erpnext.e_commerce.doctype.website_item.website_item import check_if_user_is_customer
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
@ -11,4 +12,5 @@ def get_context(context):
|
||||
if frappe.form_dict and frappe.form_dict.get("item_code"):
|
||||
context.item_code = frappe.form_dict.get("item_code")
|
||||
context.web_item = frappe.db.get_value("Website Item", {"item_code": context.item_code}, "name")
|
||||
context.user_is_customer = check_if_user_is_customer()
|
||||
get_item_reviews(context.web_item, 0, 10, context)
|
||||
|
Loading…
x
Reference in New Issue
Block a user