spree_product_reviews 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/spree/admin/product_reviews_controller.rb +15 -7
- data/app/controllers/spree/admin/review_settings_controller.rb +23 -0
- data/app/controllers/spree/admin/reviews_controller.rb +18 -0
- data/app/controllers/spree/product_reviews_controller.rb +40 -9
- data/app/models/spree_product_reviews/spree/store_decorator.rb +19 -0
- data/app/views/spree/admin/product_reviews/_product_review.html.erb +60 -52
- data/app/views/spree/admin/product_reviews/_table.html.erb +8 -2
- data/app/views/spree/admin/product_reviews/index.html.erb +83 -10
- data/app/views/spree/admin/review_settings/edit.html.erb +259 -0
- data/app/views/spree/admin/reviews/index.html.erb +16 -0
- data/app/views/spree/page_sections/_product_reviews.html.erb +9 -6
- data/app/views/spree/product_reviews/_form.html.erb +1 -3
- data/app/views/spree/product_reviews/new.html.erb +40 -0
- data/app/views/{spree → themes/default/spree}/products/_json_ld.html.erb +1 -1
- data/config/initializers/spree_product_reviews.rb +15 -7
- data/config/locales/en.yml +38 -6
- data/config/routes.rb +11 -3
- data/db/migrate/20250501173852_add_reviews.rb +1 -1
- data/db/migrate/20250501174531_add_review_info_to_products.rb +1 -1
- data/db/migrate/20260105145731_add_spam_to_spree_product_reviews.rb +5 -0
- data/lib/generators/spree_product_reviews/install/install_generator.rb +2 -3
- data/lib/spree_product_reviews/configuration.rb +5 -10
- data/lib/spree_product_reviews/engine.rb +1 -6
- data/lib/spree_product_reviews/version.rb +1 -1
- metadata +11 -8
- data/app/views/themes/default/spree/page_sections/_product_details.html.erb +0 -89
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ec19cfa05a7b060251012fac3924e5566796626a04480bde5e7bd11ea7d5ef63
|
|
4
|
+
data.tar.gz: c82cccb478d74e2d54067bce06124df092c782a378c2c85da18fc4938c39a8d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e2a18db7f6b7fed14cd097c54f6da904e99a1108f3f21adc0e2974bdbe9d9d8bce08a7a5e7b3df7f65939f854cba015ffda8431eddab9507d84a307412dceee
|
|
7
|
+
data.tar.gz: db313f9911b65241c29d9759112eb3c4b77c6981f59f377d530d540644b4b3e0c2994a5ffda249970fddbac8d39c994f9760de5471478eec0ae1c3f0cfd334ca
|
|
@@ -6,7 +6,7 @@ module Spree
|
|
|
6
6
|
before_action :find_product_review, only: [:approve, :edit, :update, :destroy, :attach_image, :purge_images]
|
|
7
7
|
|
|
8
8
|
def index
|
|
9
|
-
# optionally add filtering/sorting
|
|
9
|
+
# optionally add filtering/sorting here
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def update
|
|
@@ -33,7 +33,6 @@ module Spree
|
|
|
33
33
|
redirect_to admin_product_product_reviews_path(@product_review.product)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
# --- Attach a single image ---
|
|
37
36
|
def attach_image
|
|
38
37
|
if params[:file].present?
|
|
39
38
|
@product_review.images.attach(params[:file])
|
|
@@ -41,7 +40,6 @@ module Spree
|
|
|
41
40
|
head :ok
|
|
42
41
|
end
|
|
43
42
|
|
|
44
|
-
# DELETE /admin/products/:product_id/product_reviews/:id/purge_images
|
|
45
43
|
def purge_images
|
|
46
44
|
ids = params[:ids] || []
|
|
47
45
|
@product_review.images.where(id: ids).each(&:purge_later)
|
|
@@ -52,14 +50,24 @@ module Spree
|
|
|
52
50
|
end
|
|
53
51
|
end
|
|
54
52
|
|
|
53
|
+
protected
|
|
54
|
+
|
|
55
|
+
def location_after_destroy
|
|
56
|
+
if request.referer.to_s.include?('/admin/reviews')
|
|
57
|
+
admin_reviews_path
|
|
58
|
+
else
|
|
59
|
+
admin_product_product_reviews_path(@product)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
55
63
|
private
|
|
56
64
|
|
|
57
65
|
def permitted_resource_params
|
|
58
66
|
params.require(:product_review).permit(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
:title,
|
|
68
|
+
:rating,
|
|
69
|
+
:review,
|
|
70
|
+
images: []
|
|
63
71
|
)
|
|
64
72
|
end
|
|
65
73
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
class ReviewSettingsController < Spree::Admin::BaseController
|
|
4
|
+
def edit
|
|
5
|
+
@products = Spree::Product.order(updated_at: :desc).limit(500)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def update
|
|
9
|
+
current_store.set_preference(:review_status_default, params[:review_status_default])
|
|
10
|
+
current_store.set_preference(:block_spam_reviews, params[:block_spam_reviews] == '1')
|
|
11
|
+
current_store.set_preference(:disable_review_links, params[:disable_review_links] == '1')
|
|
12
|
+
current_store.set_preference(:disable_review_emails, params[:disable_review_emails] == '1')
|
|
13
|
+
|
|
14
|
+
current_store.set_preference(:spam_words, params[:spam_words])
|
|
15
|
+
|
|
16
|
+
current_store.save!
|
|
17
|
+
|
|
18
|
+
flash[:success] = Spree.t(:successfully_updated, resource: Spree.t(:review_settings))
|
|
19
|
+
redirect_to edit_admin_review_settings_path
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
class ReviewsController < ResourceController
|
|
4
|
+
# We map this controller to the Spree::ProductReview model
|
|
5
|
+
def model_class
|
|
6
|
+
Spree::ProductReview
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def index
|
|
10
|
+
# Eager load product and user to prevent N+1 queries
|
|
11
|
+
@product_reviews = Spree::ProductReview.accessible_by(current_ability)
|
|
12
|
+
.includes(:product, :user, images_attachments: :blob)
|
|
13
|
+
.order(created_at: :desc)
|
|
14
|
+
.page(params[:page])
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -24,16 +24,53 @@ module Spree
|
|
|
24
24
|
flash[:error] = "You have already reviewed this product."
|
|
25
25
|
redirect_to spree.product_path(@product) and return
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
default_status = (current_store.preferred_review_status_default rescue 'pending')
|
|
29
|
+
should_approve = (default_status == 'approved')
|
|
30
|
+
spam_detected = false
|
|
31
|
+
|
|
32
|
+
if (current_store.preferred_disable_review_links rescue false) && @product_review.review.present?
|
|
33
|
+
if @product_review.review.match?(%r{https?://|www\.|[a-zA-Z0-9]+\.(com|net|org|info|biz)})
|
|
34
|
+
should_approve = false
|
|
35
|
+
spam_detected = true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if (current_store.preferred_disable_review_emails rescue false) && @product_review.review.present?
|
|
40
|
+
if @product_review.review.match?(%r{[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}})
|
|
41
|
+
should_approve = false
|
|
42
|
+
spam_detected = true
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if (current_store.preferred_block_spam_reviews rescue false) && @product_review.review.present?
|
|
47
|
+
custom_words = (current_store.preferred_spam_words || "").split(',').map(&:strip).reject(&:empty?)
|
|
48
|
+
custom_words = %w[casino viagra crypto bitcoin lottery loan investment] if custom_words.empty?
|
|
49
|
+
|
|
50
|
+
full_text = "#{@product_review.title} #{@product_review.review}".downcase
|
|
51
|
+
|
|
52
|
+
if custom_words.any? { |word| full_text.include?(word.downcase) }
|
|
53
|
+
should_approve = false
|
|
54
|
+
spam_detected = true
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
@product_review.approved = should_approve
|
|
59
|
+
@product_review.spam = spam_detected
|
|
27
60
|
|
|
28
61
|
if @product_review.save
|
|
29
|
-
|
|
62
|
+
if @product_review.approved?
|
|
63
|
+
flash[:success] = Spree.t("product_review.flash_messages.create.approved")
|
|
64
|
+
else
|
|
65
|
+
flash[:success] = Spree.t("product_review.flash_messages.create.success")
|
|
66
|
+
end
|
|
30
67
|
redirect_to spree.product_path(@product)
|
|
31
68
|
else
|
|
32
69
|
flash[:error] = Spree.t("product_review.flash_messages.create.failure")
|
|
33
70
|
render :new
|
|
34
71
|
end
|
|
35
72
|
end
|
|
36
|
-
|
|
73
|
+
|
|
37
74
|
def index
|
|
38
75
|
@product_reviews = @product.product_reviews.approved.order(created_at: :desc)
|
|
39
76
|
end
|
|
@@ -41,7 +78,6 @@ module Spree
|
|
|
41
78
|
def destroy
|
|
42
79
|
@product_review = Spree::ProductReview.find(params[:id])
|
|
43
80
|
@product_review.destroy
|
|
44
|
-
# Force CanCanCan to reload abilities for the current user
|
|
45
81
|
session[:_csrf_token] = nil
|
|
46
82
|
redirect_to spree.product_path(@product), notice: Spree.t(:review_deleted)
|
|
47
83
|
end
|
|
@@ -54,17 +90,12 @@ module Spree
|
|
|
54
90
|
|
|
55
91
|
def product_review_params
|
|
56
92
|
params.require(:product_review).permit(
|
|
57
|
-
:rating,
|
|
58
|
-
:review,
|
|
59
|
-
:show_identifier,
|
|
60
|
-
:title,
|
|
61
|
-
images: []
|
|
93
|
+
:rating, :review, :show_identifier, :title, images: []
|
|
62
94
|
)
|
|
63
95
|
end
|
|
64
96
|
|
|
65
97
|
def authenticate_user!
|
|
66
98
|
return if spree_current_user
|
|
67
|
-
|
|
68
99
|
session[:spree_user_return_to] = request.fullpath
|
|
69
100
|
redirect_to spree.login_path, alert: Spree.t(:please_log_in)
|
|
70
101
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module SpreeProductReviews
|
|
2
|
+
module Spree
|
|
3
|
+
module StoreDecorator
|
|
4
|
+
def self.prepended(base)
|
|
5
|
+
base.preference :review_status_default, :string, default: 'pending'
|
|
6
|
+
|
|
7
|
+
# Content Moderation Preferences
|
|
8
|
+
base.preference :block_spam_reviews, :boolean, default: false
|
|
9
|
+
base.preference :disable_review_links, :boolean, default: false
|
|
10
|
+
base.preference :disable_review_emails, :boolean, default: false
|
|
11
|
+
|
|
12
|
+
# New: Custom Spam Words List (Comma separated)
|
|
13
|
+
base.preference :spam_words, :text, default: "casino, viagra, crypto, bitcoin, lottery, loan, investment, free access, free trial, giveaway, make money"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
::Spree::Store.prepend SpreeProductReviews::Spree::StoreDecorator
|
|
@@ -1,79 +1,87 @@
|
|
|
1
|
+
<% global_view ||= false %>
|
|
2
|
+
|
|
1
3
|
<tr id="<%= dom_id(product_review) %>">
|
|
2
|
-
|
|
4
|
+
|
|
5
|
+
<%# --- 1. PRODUCT COLUMN (Only for Global View) --- %>
|
|
6
|
+
<% if global_view %>
|
|
7
|
+
<td>
|
|
8
|
+
<div class="d-flex align-items-center">
|
|
9
|
+
<%# IMAGE CONTAINER: Prevent shrinking %>
|
|
10
|
+
<div class="mr-3" style="flex-shrink: 0;">
|
|
11
|
+
<%= render 'spree/admin/shared/product_image', object: product_review.product, width: 40, height: 40 %>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<%# PRODUCT TITLE: Force wrap, max 2 lines %>
|
|
15
|
+
<%= link_to product_review.product.name,
|
|
16
|
+
spree.edit_admin_product_path(product_review.product),
|
|
17
|
+
class: "text-dark font-weight-bold",
|
|
18
|
+
style: "white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.3;" %>
|
|
19
|
+
</div>
|
|
20
|
+
</td>
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
23
|
+
<%# --- STATUS COLUMN --- %>
|
|
24
|
+
<td class="w-10 cursor-pointer">
|
|
3
25
|
<% if product_review.approved? %>
|
|
4
|
-
<span class="badge badge-
|
|
5
|
-
|
|
26
|
+
<span class="badge badge-success">
|
|
27
|
+
<%= icon('check', class: 'mr-1') %> Approved
|
|
6
28
|
</span>
|
|
7
29
|
<% elsif product_review.approved == false %>
|
|
8
|
-
<span class="badge badge-
|
|
9
|
-
|
|
30
|
+
<span class="badge badge-danger">
|
|
31
|
+
<%= icon('x', class: 'mr-1') %> Disapproved
|
|
10
32
|
</span>
|
|
11
33
|
<% else %>
|
|
12
|
-
<span class="badge badge-
|
|
13
|
-
|
|
34
|
+
<span class="badge badge-warning text-white">
|
|
35
|
+
<%= icon('clock', class: 'mr-1') %> Pending
|
|
14
36
|
</span>
|
|
15
37
|
<% end %>
|
|
16
38
|
</td>
|
|
17
39
|
|
|
18
40
|
<td><%= product_review.user&.email || "Guest" %></td>
|
|
19
41
|
<td><%= product_review.title %></td>
|
|
20
|
-
<td><%= product_review.rating %></td>
|
|
21
|
-
<td><%= l(product_review.created_at, format: :short) %></td>
|
|
22
42
|
<td>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
<div class="text-warning">
|
|
44
|
+
<% product_review.rating.times do %>★<% end %>
|
|
45
|
+
</div>
|
|
46
|
+
</td>
|
|
47
|
+
<td><%= l(product_review.created_at.to_date, format: :short) %></td>
|
|
28
48
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class: "inline-block rounded border border-gray-200 admin-review-thumb" do %>
|
|
35
|
-
<%= image_tag main_app.rails_representation_path(thumb, only_path: true),
|
|
36
|
-
width: 50,
|
|
37
|
-
height: 50,
|
|
38
|
-
class: "rounded",
|
|
39
|
-
loading: "lazy",
|
|
40
|
-
decoding: "async" %>
|
|
41
|
-
<% end %>
|
|
42
|
-
<% end %>
|
|
43
|
-
</div>
|
|
49
|
+
<td>
|
|
50
|
+
<% if product_review.images.attached? %>
|
|
51
|
+
<%= icon('photo', class: 'text-primary') %> <%= product_review.images.count %>
|
|
52
|
+
<% else %>
|
|
53
|
+
<span class="text-muted">-</span>
|
|
44
54
|
<% end %>
|
|
45
55
|
</td>
|
|
46
56
|
|
|
47
57
|
<td class="actions">
|
|
48
58
|
<% if product_review.approved? %>
|
|
49
|
-
<%= button_to
|
|
50
|
-
disapprove_admin_product_product_review_path(
|
|
51
|
-
product_review.product,
|
|
52
|
-
product_review
|
|
53
|
-
),
|
|
59
|
+
<%= button_to disapprove_admin_product_product_review_path(product_review.product, product_review),
|
|
54
60
|
method: :get,
|
|
55
|
-
class: "btn btn-
|
|
61
|
+
class: "btn btn-light btn-sm mr-1",
|
|
62
|
+
title: "Disapprove" do %>
|
|
63
|
+
<%= icon('x', class: 'text-danger') %>
|
|
64
|
+
<% end %>
|
|
56
65
|
<% else %>
|
|
57
|
-
<%= link_to
|
|
58
|
-
approve_admin_product_product_review_path(
|
|
59
|
-
product_review.product,
|
|
60
|
-
product_review
|
|
61
|
-
),
|
|
66
|
+
<%= link_to approve_admin_product_product_review_path(product_review.product, product_review),
|
|
62
67
|
method: :get,
|
|
63
|
-
class: "btn btn-
|
|
68
|
+
class: "btn btn-light btn-sm mr-1",
|
|
69
|
+
title: "Approve" do %>
|
|
70
|
+
<%= icon('check', class: 'text-success') %>
|
|
71
|
+
<% end %>
|
|
64
72
|
<% end %>
|
|
65
73
|
|
|
66
|
-
<% if can? :edit, product_review %>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<% end %>
|
|
74
|
+
<% if can? :edit, product_review %>
|
|
75
|
+
<%= link_to_edit(
|
|
76
|
+
product_review,
|
|
77
|
+
no_text: true,
|
|
78
|
+
url: edit_admin_product_product_review_path(
|
|
79
|
+
product_review.product,
|
|
80
|
+
product_review,
|
|
81
|
+
return_to: request.fullpath
|
|
82
|
+
)
|
|
83
|
+
) %>
|
|
84
|
+
<% end %>
|
|
77
85
|
|
|
78
86
|
<% if can? :destroy, product_review %>
|
|
79
87
|
<%= link_to_delete(
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
<%# Check if we are in the context of a specific product or the global list %>
|
|
2
|
+
<% global_view = @product.nil? %>
|
|
3
|
+
<% colspan = global_view ? 7 : 6 %>
|
|
2
4
|
|
|
3
5
|
<table class="table">
|
|
4
6
|
<thead>
|
|
5
7
|
<tr>
|
|
8
|
+
<% if global_view %>
|
|
9
|
+
<th><%= Spree.t(:product) %></th>
|
|
10
|
+
<% end %>
|
|
6
11
|
<th><%= Spree.t(:status) %></th>
|
|
7
12
|
<th><%= Spree.t(:email) %></th>
|
|
8
13
|
<th><%= Spree.t(:review_title) %></th>
|
|
@@ -16,6 +21,7 @@
|
|
|
16
21
|
<tbody>
|
|
17
22
|
<%= render partial: "spree/admin/product_reviews/product_review",
|
|
18
23
|
collection: @product_reviews,
|
|
24
|
+
locals: { global_view: global_view },
|
|
19
25
|
cached: spree_base_cache_scope %>
|
|
20
26
|
|
|
21
27
|
<% if @product_reviews.empty? %>
|
|
@@ -28,4 +34,4 @@
|
|
|
28
34
|
</tr>
|
|
29
35
|
<% end %>
|
|
30
36
|
</tbody>
|
|
31
|
-
</table>
|
|
37
|
+
</table>
|
|
@@ -1,15 +1,88 @@
|
|
|
1
1
|
<% content_for :page_title do %>
|
|
2
|
-
<%=
|
|
3
|
-
<span><%= Spree.t(:product_reviews) %></span>
|
|
2
|
+
<%= Spree.t(:product_reviews) %>
|
|
4
3
|
<% end %>
|
|
5
4
|
|
|
6
|
-
<%
|
|
7
|
-
|
|
5
|
+
<% if @product_reviews.any? %>
|
|
6
|
+
<div class="table-responsive">
|
|
7
|
+
<table class="table" id="listing_product_reviews">
|
|
8
|
+
<thead>
|
|
9
|
+
<tr>
|
|
10
|
+
<th><%= Spree.t(:product) %></th>
|
|
11
|
+
<th><%= Spree.t(:status) %></th>
|
|
12
|
+
<th><%= Spree.t(:user) %></th>
|
|
13
|
+
<th><%= Spree.t(:review) %></th>
|
|
14
|
+
<th><%= Spree.t(:stars) %></th>
|
|
15
|
+
<th><%= Spree.t(:date) %></th>
|
|
16
|
+
<th data-hook="admin_product_reviews_index_header_actions" class="actions"></th>
|
|
17
|
+
</tr>
|
|
18
|
+
</thead>
|
|
19
|
+
<tbody>
|
|
20
|
+
<% @product_reviews.each do |product_review| %>
|
|
21
|
+
<tr id="<%= dom_id product_review %>">
|
|
22
|
+
|
|
23
|
+
<%# PRODUCT IMAGE & NAME %>
|
|
24
|
+
<td>
|
|
25
|
+
<% if product_review.product %>
|
|
26
|
+
<div class="d-flex align-items-center">
|
|
27
|
+
<div class="mr-3" style="flex-shrink: 0;">
|
|
28
|
+
<% if product_review.product.images.any? %>
|
|
29
|
+
<%= spree_image_tag product_review.product.images.first, width: 40, height: 40 %>
|
|
30
|
+
<% end %>
|
|
31
|
+
</div>
|
|
32
|
+
<%= link_to product_review.product.name, edit_admin_product_path(product_review.product), class: "text-dark font-weight-bold", style: "white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.3;" %>
|
|
33
|
+
</div>
|
|
34
|
+
<% end %>
|
|
35
|
+
</td>
|
|
36
|
+
|
|
37
|
+
<%# STATUS BADGE %>
|
|
38
|
+
<td class="w-10 cursor-pointer">
|
|
39
|
+
<% if product_review.respond_to?(:spam?) && product_review.spam? %>
|
|
40
|
+
<%# ORANGE BADGE FOR SPAM %>
|
|
41
|
+
<span class="badge badge-warning text-white" style="background-color: #fd7e14;">
|
|
42
|
+
<i class="ti ti-alert-triangle mr-1"></i> Spam
|
|
43
|
+
</span>
|
|
44
|
+
<% elsif product_review.approved? %>
|
|
45
|
+
<span class="badge badge-success">
|
|
46
|
+
<i class="ti ti-check mr-1"></i> Approved
|
|
47
|
+
</span>
|
|
48
|
+
<% else %>
|
|
49
|
+
<span class="badge badge-danger">
|
|
50
|
+
<i class="ti ti-x mr-1"></i> Disapproved
|
|
51
|
+
</span>
|
|
52
|
+
<% end %>
|
|
53
|
+
</td>
|
|
8
54
|
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
55
|
+
<td><%= product_review.user&.email || Spree.t(:anonymous) %></td>
|
|
56
|
+
|
|
57
|
+
<td title="<%= product_review.review %>">
|
|
58
|
+
<%= truncate(product_review.review, length: 100) %>
|
|
59
|
+
</td>
|
|
60
|
+
|
|
61
|
+
<td>
|
|
62
|
+
<div class="text-warning">
|
|
63
|
+
<%= "★" * product_review.rating.to_i %>
|
|
64
|
+
</div>
|
|
65
|
+
</td>
|
|
66
|
+
|
|
67
|
+
<td><%= l product_review.created_at.to_date, format: :short_date %></td>
|
|
68
|
+
|
|
69
|
+
<td class="actions" data-hook="admin_product_reviews_index_row_actions">
|
|
70
|
+
<% unless product_review.approved %>
|
|
71
|
+
<%= link_to_with_icon 'check', Spree.t(:approve), approve_admin_product_product_review_path(product_review.product, product_review), no_text: true, class: 'btn btn-light btn-sm mr-1' %>
|
|
72
|
+
<% else %>
|
|
73
|
+
<%= link_to_with_icon 'x', Spree.t(:disapprove), disapprove_admin_product_product_review_path(product_review.product, product_review), no_text: true, class: 'btn btn-light btn-sm mr-1' %>
|
|
74
|
+
<% end %>
|
|
75
|
+
|
|
76
|
+
<%= link_to_edit product_review, url: edit_admin_product_product_review_path(product_review.product, product_review, return_to: request.fullpath), no_text: true, class: 'edit' %>
|
|
77
|
+
<%= link_to_delete product_review, url: admin_product_product_review_path(product_review.product, product_review), no_text: true %>
|
|
78
|
+
</td>
|
|
79
|
+
</tr>
|
|
80
|
+
<% end %>
|
|
81
|
+
</tbody>
|
|
82
|
+
</table>
|
|
83
|
+
</div>
|
|
84
|
+
<% else %>
|
|
85
|
+
<div class="alert alert-info no-objects-found">
|
|
86
|
+
<%= Spree.t(:no_resource_found, resource: Spree.t(:reviews)) %>
|
|
14
87
|
</div>
|
|
15
|
-
|
|
88
|
+
<% end %>
|