feat: Customer Ratings & Reviews Full Page
- Created macros for repetitive snippets
- Created Customer Reviews full page
- View more button to reveal 10 more reviews at a time
- Common function to get reviews with start and end
diff --git a/erpnext/templates/generators/item/item_reviews.html b/erpnext/templates/generators/item/item_reviews.html
index c271fdb..f6b1831 100644
--- a/erpnext/templates/generators/item/item_reviews.html
+++ b/erpnext/templates/generators/item/item_reviews.html
@@ -1,47 +1,16 @@
-{% from "erpnext/templates/includes/macros.html" import ratings_with_title %}
+{% from "erpnext/templates/includes/macros.html" import user_review, ratings_summary %}
-<div class="mt-8 ratings-reviews-section">
- <!-- Ratings Summary -->
+<div class="mt-8 ratings-reviews-section" style="display: flex;">
<div class="col-md-4 order-md-1 mt-8" style="max-width: 300px;">
- <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
- </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);">
- </div>
- </div>
- </div>
- <div class="col-sm-1 small">
- {{ percent }}%
- </div>
- </div>
- {% endfor %}
- </div>
+ {{ ratings_summary(reviews, reviews_per_rating, average_rating, average_whole_rating) }}
<!-- 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">
+ <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 -->
@@ -50,28 +19,14 @@
{{ _("Reviews") }}
</h2>
{% if reviews %}
- {% for review in reviews %}
- <!-- User review -->
- <div class="mb-3 review">
- {{ ratings_with_title(review.rating, _(review.review_title), "md", "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">
- <p>
- {{ _(review.comment) }}
- </p>
- </div>
- </div>
- {% endfor %}
+ {{ user_review(reviews) }}
{% if total_reviews > 4 %}
<div class="mt-6 mb-6"style="color: var(--primary);">
- <a href="/reviews">{{ _("View all reviews") }}</a>
+ <a href="/customer_reviews?item_code={{ doc.item_code }}">{{ _("View all reviews") }}</a>
</div>
{% endif %}
+
{% else %}
<h6 class="text-muted mt-6">
{{ _("No Reviews") }}
@@ -96,7 +51,6 @@
],
primary_action: function() {
var data = d.get_values();
- $btn.prop('hidden', true);
frappe.call({
method: "erpnext.e_commerce.doctype.item_review.item_review.add_item_review",
args: {
@@ -115,6 +69,7 @@
indicator: "green"
});
d.hide();
+ location.reload();
}
}
});
diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html
index e05bc63..cd29494 100644
--- a/erpnext/templates/includes/macros.html
+++ b/erpnext/templates/includes/macros.html
@@ -236,3 +236,59 @@
</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
+ </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);">
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-1 small">
+ {{ percent }}%
+ </div>
+ </div>
+ {% endfor %}
+</div>
+{%- endmacro -%}
+
+{%- macro user_review(reviews)-%}
+<!-- User Reviews -->
+<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") }}
+
+ <div class="review-signature">
+ <span class="reviewer">{{ _(review.customer) }}</span>
+ <span>{{ review.published_on }}</span>
+ </div>
+ <div class="product-description mb-4 mt-4">
+ <p>
+ {{ _(review.comment) }}
+ </p>
+ </div>
+ </div>
+ {% endfor %}
+</div>
+{%- endmacro -%}
diff --git a/erpnext/templates/pages/customer_reviews.html b/erpnext/templates/pages/customer_reviews.html
new file mode 100644
index 0000000..9d8ba9e
--- /dev/null
+++ b/erpnext/templates/pages/customer_reviews.html
@@ -0,0 +1,45 @@
+{% extends "templates/web.html" %}
+{% from "erpnext/templates/includes/macros.html" import user_review, ratings_summary %}
+
+{% 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) }}
+
+ <!-- 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>
+
+ <!-- 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>
+ {% if reviews %}
+ {{ user_review(reviews) }}
+
+ {% if not reviews | len >= total_reviews %}
+ <button class="btn btn-light btn-view-more mr-2 mt-4 mb-4 w-30"
+ data-web-item="{{ web_item }}">
+ {{ _("View More") }}
+ </button>
+ {% endif %}
+
+ {% else %}
+ <h6 class="text-muted mt-6">
+ {{ _("No Reviews") }}
+ </h6>
+ {% endif %}
+ </div>
+</div>
+</div>
+
+{% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/pages/customer_reviews.js b/erpnext/templates/pages/customer_reviews.js
new file mode 100644
index 0000000..453b96a
--- /dev/null
+++ b/erpnext/templates/pages/customer_reviews.js
@@ -0,0 +1,135 @@
+$(() => {
+ class CustomerReviews {
+ constructor() {
+ this.bind_button_actions();
+ this.start = 0;
+ this.page_length = 10;
+ }
+
+ bind_button_actions() {
+ this.write_review();
+ this.view_more();
+ }
+
+ write_review() {
+ //TODO: make dialog popup on stray page
+ $('.page_content').on('click', '.btn-write-review', (e) => {
+ // Bind action on write a review button
+ const $btn = $(e.currentTarget);
+
+ let d = new frappe.ui.Dialog({
+ title: __("Write a Review"),
+ fields: [
+ {fieldname: "title", fieldtype: "Data", label: "Headline", reqd: 1},
+ {fieldname: "rating", fieldtype: "Rating", label: "Overall Rating", reqd: 1},
+ {fieldtype: "Section Break"},
+ {fieldname: "comment", fieldtype: "Small Text", label: "Your Review"}
+ ],
+ primary_action: function() {
+ let me = this;
+ let data = d.get_values();
+ frappe.call({
+ method: "erpnext.e_commerce.doctype.item_review.item_review.add_item_review",
+ args: {
+ web_item: $btn.attr('data-web-item'),
+ title: data.title,
+ rating: data.rating,
+ comment: data.comment
+ },
+ freeze: true,
+ freeze_message: __("Submitting Review ..."),
+ callback: (r) => {
+ if(!r.exc) {
+ frappe.msgprint({
+ message: __("Thank you for submitting your review"),
+ title: __("Review Submitted"),
+ indicator: "green"
+ });
+ d.hide();
+ location.reload();
+ }
+ }
+ });
+ },
+ primary_action_label: __('Submit')
+ });
+ d.show();
+ });
+ }
+
+ view_more() {
+ $('.page_content').on('click', '.btn-view-more', (e) => {
+ // Bind action on view more button
+ const $btn = $(e.currentTarget);
+ $btn.prop('disabled', true);
+
+ this.start += this.page_length;
+ let me = this;
+
+ frappe.call({
+ method: "erpnext.e_commerce.doctype.item_review.item_review.get_item_reviews",
+ args: {
+ web_item: $btn.attr('data-web-item'),
+ start: me.start,
+ end: me.page_length
+ },
+ callback: (result) => {
+ if(result.message) {
+ let res = result.message;
+ me.get_user_review_html(res.reviews);
+
+ $btn.prop('disabled', false);
+ if (res.total_reviews <= (me.start + me.page_length)) {
+ $btn.hide();
+ }
+
+ }
+ }
+ })
+ });
+
+ }
+
+ get_user_review_html(reviews) {
+ let me = this;
+ let $content = $('.user-reviews');
+
+ reviews.forEach((review) => {
+ $content.append(`
+ <div class="mb-3 review">
+ <div style="display: flex;">
+ <div class="rating">
+ ${me.get_review_stars(review.rating)}
+ </div>
+ <p class="ml-4 user-review-title">
+ <span>${__(review.review_title)}</span>
+ </p>
+ </div>
+ <div class="review-signature">
+ <span class="reviewer">${__(review.customer)}</span>
+ <span>${__(review.published_on)}</span>
+ </div>
+ <div class="product-description mb-4 mt-4">
+ <p>
+ ${__(review.comment)}
+ </p>
+ </div>
+ </div>
+ `);
+ });
+ }
+
+ get_review_stars(rating) {
+ let stars = ``;
+ for(let i = 1; i < 6; i++) {
+ let fill_class = i <= rating ? 'star-click' : '';
+ stars += `<svg class="icon icon-md ${fill_class}">
+ <use href="#icon-star"></use>
+ </svg>`;
+ }
+ return stars;
+ }
+ }
+
+ new CustomerReviews();
+});
\ No newline at end of file
diff --git a/erpnext/templates/pages/customer_reviews.py b/erpnext/templates/pages/customer_reviews.py
new file mode 100644
index 0000000..3bb0142
--- /dev/null
+++ b/erpnext/templates/pages/customer_reviews.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+from __future__ import unicode_literals
+
+no_cache = 1
+
+import frappe
+from erpnext.e_commerce.doctype.item_review.item_review import get_item_reviews
+
+def get_context(context):
+ context.full_page = True
+ context.reviews = None
+ 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")
+ get_item_reviews(context.web_item, 0, 10, context)
diff --git a/erpnext/templates/pages/reviews.html b/erpnext/templates/pages/reviews.html
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/templates/pages/reviews.html
+++ /dev/null