workarea-reviews 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +20 -0
- data/.eslintignore +2 -0
- data/.eslintrc +24 -0
- data/.eslintrc.json +35 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/workflows/ci.yml +60 -0
- data/.gitignore +15 -0
- data/.rubocop.yml +3 -0
- data/.scss-lint.yml +188 -0
- data/.stylelintrc.json +8 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +745 -0
- data/CODE_OF_CONDUCT.md +3 -0
- data/CONTRIBUTING.md +3 -0
- data/Gemfile +8 -0
- data/LICENSE +52 -0
- data/README.md +107 -0
- data/Rakefile +53 -0
- data/app/assets/images/workarea/admin/icons/star.svg +1 -0
- data/app/assets/images/workarea/storefront/icons/empty_star.svg +1 -0
- data/app/assets/images/workarea/storefront/icons/half_star.svg +1 -0
- data/app/assets/images/workarea/storefront/icons/star.svg +1 -0
- data/app/assets/javascripts/workarea/storefront/reviews/modules/product_review_ajax_submit.js +39 -0
- data/app/assets/javascripts/workarea/storefront/reviews/modules/product_reviews_sort_menus.js +82 -0
- data/app/assets/javascripts/workarea/storefront/reviews/modules/rating_buttons.js +103 -0
- data/app/assets/javascripts/workarea/storefront/reviews/templates/sort_by_property.jst.ejs +11 -0
- data/app/assets/stylesheets/workarea/storefront/reviews/components/_rating.scss +28 -0
- data/app/assets/stylesheets/workarea/storefront/reviews/components/_reviews.scss +59 -0
- data/app/assets/stylesheets/workarea/storefront/reviews/components/_reviews_aggregate.scss +38 -0
- data/app/assets/stylesheets/workarea/storefront/reviews/components/_write_review.scss +63 -0
- data/app/controllers/workarea/admin/catalog_products_controller.decorator +9 -0
- data/app/controllers/workarea/admin/reports_controller.decorator +10 -0
- data/app/controllers/workarea/admin/reviews_controller.rb +51 -0
- data/app/controllers/workarea/storefront/review_requests_controller.rb +50 -0
- data/app/controllers/workarea/storefront/reviews_controller.rb +53 -0
- data/app/helpers/workarea/admin/reviews_helper.rb +13 -0
- data/app/helpers/workarea/storefront/reviews_helper.rb +22 -0
- data/app/helpers/workarea/storefront/reviews_schema_org_helper.rb +34 -0
- data/app/mailers/workarea/admin/status_report_mailer.decorator +8 -0
- data/app/mailers/workarea/storefront/review_mailer.rb +23 -0
- data/app/models/workarea/catalog/product.decorator +15 -0
- data/app/models/workarea/fulfillment.decorator +11 -0
- data/app/models/workarea/insights/most_active_reviewers.rb +34 -0
- data/app/models/workarea/insights/most_reviewed_products.rb +33 -0
- data/app/models/workarea/insights/top_rated_products.rb +33 -0
- data/app/models/workarea/review.rb +124 -0
- data/app/models/workarea/review/request.rb +62 -0
- data/app/models/workarea/search/admin/review.rb +72 -0
- data/app/models/workarea/search/storefront/product.decorator +9 -0
- data/app/models/workarea/sort.decorator +17 -0
- data/app/queries/workarea/reports/reviews_by_product.rb +86 -0
- data/app/queries/workarea/reports/reviews_by_user.rb +71 -0
- data/app/queries/workarea/review_request_params.rb +47 -0
- data/app/queries/workarea/review_summary.rb +11 -0
- data/app/queries/workarea/search/admin_reviews.rb +40 -0
- data/app/queries/workarea/search/category_browse.decorator +9 -0
- data/app/queries/workarea/search/product_search.decorator +9 -0
- data/app/seeds/workarea/review_seeds.rb +49 -0
- data/app/services/workarea/create_review.rb +99 -0
- data/app/view_models/workarea/admin/dashboards/reports_view_model.decorator +11 -0
- data/app/view_models/workarea/admin/product_view_model.decorator +7 -0
- data/app/view_models/workarea/admin/reports/reviews_by_product_view_model.rb +27 -0
- data/app/view_models/workarea/admin/review_view_model.rb +23 -0
- data/app/view_models/workarea/admin/reviews_search_view_model.rb +46 -0
- data/app/view_models/workarea/storefront/product_view_model.decorator +13 -0
- data/app/view_models/workarea/storefront/review_view_model.rb +14 -0
- data/app/views/workarea/admin/activities/_review_create.html.haml +12 -0
- data/app/views/workarea/admin/activities/_review_destroy.html.haml +10 -0
- data/app/views/workarea/admin/activities/_review_update.html.haml +10 -0
- data/app/views/workarea/admin/dashboards/_reviews_by_product_card.html.haml +17 -0
- data/app/views/workarea/admin/insights/_most_active_reviewers.html.haml +22 -0
- data/app/views/workarea/admin/insights/_most_reviewed_products.html.haml +22 -0
- data/app/views/workarea/admin/insights/_top_rated_products.html.haml +22 -0
- data/app/views/workarea/admin/reports/reviews_by_product.html.haml +44 -0
- data/app/views/workarea/admin/reviews/_aux_navigation.html.haml +5 -0
- data/app/views/workarea/admin/reviews/_catalog_products_aux_link.html.haml +5 -0
- data/app/views/workarea/admin/reviews/_dashboard_navigation.html.haml +1 -0
- data/app/views/workarea/admin/reviews/_menu.html.haml +3 -0
- data/app/views/workarea/admin/reviews/_summary.html.haml +12 -0
- data/app/views/workarea/admin/reviews/edit.html.haml +77 -0
- data/app/views/workarea/admin/reviews/index.html.haml +74 -0
- data/app/views/workarea/admin/status_report_mailer/_reviews.html.haml +5 -0
- data/app/views/workarea/storefront/products/_rating.html.haml +9 -0
- data/app/views/workarea/storefront/products/_reviews.html.haml +23 -0
- data/app/views/workarea/storefront/products/_reviews_aggregate.html.haml +22 -0
- data/app/views/workarea/storefront/products/_reviews_summary.html.haml +2 -0
- data/app/views/workarea/storefront/review_mailer/review_request.html.haml +24 -0
- data/app/views/workarea/storefront/review_requests/show.html.haml +45 -0
- data/app/views/workarea/storefront/reviews/new.html.haml +68 -0
- data/app/views/workarea/storefront/style_guides/_reviews_product_summary_docs.html.haml +13 -0
- data/app/views/workarea/storefront/style_guides/components/_reviews.html.haml +17 -0
- data/app/workers/workarea/create_review_requests.rb +33 -0
- data/app/workers/workarea/schedule_review_requests.rb +44 -0
- data/app/workers/workarea/send_review_requests.rb +12 -0
- data/app/workers/workarea/update_product_review_data.rb +26 -0
- data/bin/rails +18 -0
- data/config/initializers/append_points.rb +75 -0
- data/config/initializers/assets.rb +1 -0
- data/config/initializers/configuration.rb +31 -0
- data/config/initializers/rack_attack.rb +11 -0
- data/config/initializers/recaptcha.rb +19 -0
- data/config/initializers/scheduled_jobs.rb +8 -0
- data/config/locales/en.yml +150 -0
- data/config/routes.rb +25 -0
- data/lib/tasks/verify.rake +16 -0
- data/lib/workarea/mailer_previews/storefront/review_mailer_preview.rb +10 -0
- data/lib/workarea/reviews.rb +13 -0
- data/lib/workarea/reviews/engine.rb +16 -0
- data/lib/workarea/reviews/version.rb +5 -0
- data/package.json +9 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/config/manifest.js +4 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +34 -0
- data/test/dummy/bin/update +29 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/config/application.rb +25 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +9 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +55 -0
- data/test/dummy/config/environments/production.rb +86 -0
- data/test/dummy/config/environments/test.rb +43 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/new_framework_defaults.rb +21 -0
- data/test/dummy/config/initializers/session_store.rb +5 -0
- data/test/dummy/config/initializers/workarea.rb +5 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/puma.rb +47 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/db/seeds.rb +2 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/factories/reviews.rb +32 -0
- data/test/fixtures/reviews.csv +5 -0
- data/test/helpers/workarea/storefront/reviews_helper_test.rb +20 -0
- data/test/integration/workarea/admin/reviews_integration_test.rb +37 -0
- data/test/integration/workarea/storefront/review_requests_integration_test.rb +63 -0
- data/test/integration/workarea/storefront/reviews_integration_test.rb +82 -0
- data/test/javascripts/fixtures/product_review_ajax_submit.html.haml +4 -0
- data/test/javascripts/product_review_ajax_submit_spec.js +28 -0
- data/test/javascripts/spec_helper.js +23 -0
- data/test/models/workarea/insights/most_active_reviewers_test.rb +32 -0
- data/test/models/workarea/insights/most_reviewed_products_test.rb +32 -0
- data/test/models/workarea/insights/top_rated_products_test.rb +49 -0
- data/test/models/workarea/review/request_test.rb +18 -0
- data/test/models/workarea/review_test.rb +105 -0
- data/test/models/workarea/search/storefront/product_test.decorator +14 -0
- data/test/queries/workarea/reports/reviews_by_product_test.rb +104 -0
- data/test/queries/workarea/reports/reviews_by_user_test.rb +102 -0
- data/test/queries/workarea/review_request_params_test.rb +64 -0
- data/test/services/workarea/create_review_test.rb +131 -0
- data/test/system/workarea/admin/review_system_test.rb +96 -0
- data/test/system/workarea/admin/reviews_by_product_system_test.rb +60 -0
- data/test/system/workarea/storefront/review_request_system_test.rb +34 -0
- data/test/system/workarea/storefront/review_system_test.rb +113 -0
- data/test/teaspoon_env.rb +10 -0
- data/test/test_helper.rb +11 -0
- data/test/workers/workarea/create_review_requests_test.rb +38 -0
- data/test/workers/workarea/schedule_review_requests_test.rb +115 -0
- data/test/workers/workarea/send_review_requests_test.rb +30 -0
- data/workarea-reviews.gemspec +22 -0
- data/yarn.lock +3265 -0
- metadata +265 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Storefront
|
3
|
+
class ReviewMailer < Storefront::ApplicationMailer
|
4
|
+
include TransactionalMailer
|
5
|
+
|
6
|
+
def review_request(request_id)
|
7
|
+
@request = Review::Request.find(request_id)
|
8
|
+
@content = Content::Email.find_content('review_request')
|
9
|
+
@product = Storefront::ProductViewModel.wrap(
|
10
|
+
Catalog::Product.find(@request.product_id)
|
11
|
+
)
|
12
|
+
|
13
|
+
mail(
|
14
|
+
to: @request.email,
|
15
|
+
subject: t(
|
16
|
+
'workarea.storefront.email.review_request.subject',
|
17
|
+
product: @product.name
|
18
|
+
)
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Workarea
|
2
|
+
decorate Catalog::Product, with: :reviews do
|
3
|
+
decorated do
|
4
|
+
# @!attribute total_reviews
|
5
|
+
# @return [Integer] the total number of approved reviews
|
6
|
+
#
|
7
|
+
field :total_reviews, type: Integer, default: 0
|
8
|
+
|
9
|
+
# @!attribute average_rating
|
10
|
+
# @return [Float] the average rating of approved reviews
|
11
|
+
#
|
12
|
+
field :average_rating, type: Float
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Workarea
|
2
|
+
decorate Fulfillment, with: :reviews do
|
3
|
+
def ship_items(tracking_number, shipped_items)
|
4
|
+
super.tap do |result|
|
5
|
+
if result && Workarea.config.send_transactional_emails
|
6
|
+
ScheduleReviewRequests.perform_async(id, shipped_items)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Insights
|
3
|
+
class MostActiveReviewers < Base
|
4
|
+
class << self
|
5
|
+
def dashboards
|
6
|
+
%w(people marketing)
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate_monthly!
|
10
|
+
results = generate_results
|
11
|
+
create!(results: results) if results.present?
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate_results
|
15
|
+
report
|
16
|
+
.results
|
17
|
+
.select { |r| r['reviews'] > 0 }
|
18
|
+
.take(Workarea.config.insights_users_list_max_results)
|
19
|
+
.map { |result| result.merge(email: result['_id']) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def report
|
23
|
+
Reports::ReviewsByUser.new(
|
24
|
+
starts_at: beginning_of_last_month,
|
25
|
+
ends_at: end_of_last_month,
|
26
|
+
sort_by: 'activity_score',
|
27
|
+
sort_direction: 'desc',
|
28
|
+
results_filter: 'approved'
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Insights
|
3
|
+
class MostReviewedProducts < Base
|
4
|
+
class << self
|
5
|
+
def dashboards
|
6
|
+
%w(marketing)
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate_monthly!
|
10
|
+
results = generate_results
|
11
|
+
create!(results: results) if results.present?
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate_results
|
15
|
+
report
|
16
|
+
.results
|
17
|
+
.select { |r| r['reviews'] > 0 }
|
18
|
+
.take(Workarea.config.insights_products_list_max_results)
|
19
|
+
.map { |result| result.merge(product_id: result['_id']) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def report
|
23
|
+
Reports::ReviewsByProduct.new(
|
24
|
+
starts_at: beginning_of_last_month,
|
25
|
+
ends_at: end_of_last_month,
|
26
|
+
sort_by: 'reviews',
|
27
|
+
sort_direction: 'desc'
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Insights
|
3
|
+
class TopRatedProducts < Base
|
4
|
+
class << self
|
5
|
+
def dashboards
|
6
|
+
%w(marketing)
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate_monthly!
|
10
|
+
results = generate_results
|
11
|
+
create!(results: results) if results.present?
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate_results
|
15
|
+
report
|
16
|
+
.results
|
17
|
+
.select { |r| r['reviews'] > 0 }
|
18
|
+
.take(Workarea.config.insights_products_list_max_results)
|
19
|
+
.map { |result| result.merge(product_id: result['_id']) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def report
|
23
|
+
Reports::ReviewsByProduct.new(
|
24
|
+
starts_at: beginning_of_last_month,
|
25
|
+
ends_at: end_of_last_month,
|
26
|
+
sort_by: 'weighted_average_rating',
|
27
|
+
sort_direction: 'desc'
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Workarea
|
2
|
+
class Review
|
3
|
+
include ApplicationDocument
|
4
|
+
|
5
|
+
field :user_id, type: String
|
6
|
+
field :user_info, type: String
|
7
|
+
field :first_name, type: String
|
8
|
+
field :last_name, type: String
|
9
|
+
field :email, type: String
|
10
|
+
field :product_id, type: String
|
11
|
+
field :rating, type: Integer
|
12
|
+
field :title, type: String
|
13
|
+
field :body, type: String
|
14
|
+
field :approved, type: Boolean, default: false
|
15
|
+
field :verified, type: Boolean, default: false
|
16
|
+
|
17
|
+
index(product_id: 1, approved: 1)
|
18
|
+
index(approved: 1)
|
19
|
+
index(created_at: 1)
|
20
|
+
|
21
|
+
validates :product_id, presence: true
|
22
|
+
validates :user_info, presence: true
|
23
|
+
validates :body, presence: true
|
24
|
+
validates :rating, presence: true,
|
25
|
+
numericality: {
|
26
|
+
greater_than_or_equal_to: 1,
|
27
|
+
less_than_or_equal_to: 5,
|
28
|
+
only_integer: true
|
29
|
+
}
|
30
|
+
|
31
|
+
validate :user_must_have_spent_money_to_create_review
|
32
|
+
|
33
|
+
before_save :ensure_title
|
34
|
+
|
35
|
+
scope :by_product, ->(id) { where(product_id: id) }
|
36
|
+
scope :approved, -> { where(approved: true) }
|
37
|
+
scope :pending, -> { where(approved: false) }
|
38
|
+
|
39
|
+
def self.sorts
|
40
|
+
[ Workarea::Sort.pending,
|
41
|
+
Workarea::Sort.newest,
|
42
|
+
Workarea::Sort.modified,
|
43
|
+
Workarea::Sort.title,
|
44
|
+
Workarea::Sort.rating ]
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get {Mongoid::Criteria} for the reviews for a certain product.
|
48
|
+
# Optionally allow including unapproved reviews (for the admin).
|
49
|
+
#
|
50
|
+
# @param [String] product_id
|
51
|
+
# @param [optional, Boolean] allow_unapproved
|
52
|
+
# @return [Mongoid::Criteria]
|
53
|
+
#
|
54
|
+
def self.find_for_product(product_id, allow_unapproved = false)
|
55
|
+
criteria = by_product(product_id)
|
56
|
+
allow_unapproved ? criteria : criteria.approved
|
57
|
+
end
|
58
|
+
|
59
|
+
# Lookup aggregate stats on product reviews for a product.
|
60
|
+
#
|
61
|
+
# @param [String] product_id
|
62
|
+
# @return [Hash]
|
63
|
+
#
|
64
|
+
def self.find_single_aggregates(product_id)
|
65
|
+
stats = by_product(product_id).approved.aggregates(:rating)
|
66
|
+
{ count: stats['count'] || 0, average: stats['avg'] || 0 }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Lookup aggregate review stats on multiple products.
|
70
|
+
# Used for show stats on browse/detail.
|
71
|
+
#
|
72
|
+
# @param [Array<String>] product_ids
|
73
|
+
# @return [Hash] results Keys are product IDs, values are stats Hash
|
74
|
+
#
|
75
|
+
def self.find_aggregates(*product_ids)
|
76
|
+
product_ids = Array(product_ids).flatten
|
77
|
+
|
78
|
+
if product_ids.one?
|
79
|
+
find_single_aggregates(product_ids.first)
|
80
|
+
else
|
81
|
+
product_ids.inject({}) do |memo, product_id|
|
82
|
+
memo[product_id] = find_single_aggregates(product_id)
|
83
|
+
memo
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Find sort score for a product. Used in the search index for sorting by
|
89
|
+
# best reviewed.
|
90
|
+
#
|
91
|
+
# Balances a small number of ratings with the uncertainty of only having a few ratings.
|
92
|
+
# See: http://masanjin.net/blog/how-to-rank-products-based-on-user-input
|
93
|
+
#
|
94
|
+
# @param [String] product_id
|
95
|
+
# @return [Float]
|
96
|
+
#
|
97
|
+
def self.find_sorting_score(product_id)
|
98
|
+
reviews = find_for_product(product_id).to_a
|
99
|
+
count = (1..5).each_with_object({}) do |i, memo|
|
100
|
+
memo[i] = reviews.select { |r| r.rating == i }.length + 2
|
101
|
+
end
|
102
|
+
|
103
|
+
count.sum { |rating, count| rating * count }.to_f / count.values.sum
|
104
|
+
end
|
105
|
+
|
106
|
+
def anonymous?
|
107
|
+
!user_id.present?
|
108
|
+
end
|
109
|
+
|
110
|
+
def ensure_title
|
111
|
+
self.title = body.truncate(50) if title.blank?
|
112
|
+
end
|
113
|
+
|
114
|
+
def user_must_have_spent_money_to_create_review
|
115
|
+
if Workarea.config.require_purchase_to_post_review
|
116
|
+
user = User.find(user_id)
|
117
|
+
if user.total_spent.to_f.zero?
|
118
|
+
err_msg = I18n.t('workarea.storefront.reviews.total_spent_validation')
|
119
|
+
errors.add(:user, err_msg)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Workarea
|
2
|
+
class Review
|
3
|
+
class Request
|
4
|
+
include ApplicationDocument
|
5
|
+
include UrlToken
|
6
|
+
|
7
|
+
field :order_id, type: String
|
8
|
+
field :user_id, type: String
|
9
|
+
field :product_id, type: String
|
10
|
+
|
11
|
+
field :email, type: String
|
12
|
+
|
13
|
+
field :send_after, type: DateTime
|
14
|
+
field :sent_at, type: DateTime
|
15
|
+
field :completed_at, type: DateTime
|
16
|
+
field :canceled_at, type: DateTime
|
17
|
+
|
18
|
+
index(order_id: 1)
|
19
|
+
index(send_after: 1, sent_at: 1, canceled_at: 1)
|
20
|
+
|
21
|
+
index(
|
22
|
+
{ created_at: 1 },
|
23
|
+
{ expire_after_seconds: Workarea.config.review_request_ttl }
|
24
|
+
)
|
25
|
+
|
26
|
+
scope :by_order, ->(order_id) { where(order_id: order_id) }
|
27
|
+
scope :ready_to_send, ->(from = Time.current) do
|
28
|
+
where(:send_after.lte => from, :sent_at => nil, :canceled_at => nil)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.cancel_for_orders!(order_ids)
|
32
|
+
self.in(order_id: Array.wrap(order_ids))
|
33
|
+
.where(completed_at: nil)
|
34
|
+
.update_all(canceled_at: Time.current)
|
35
|
+
end
|
36
|
+
|
37
|
+
def scheduled?
|
38
|
+
send_after.present?
|
39
|
+
end
|
40
|
+
|
41
|
+
def sent?
|
42
|
+
sent_at.present?
|
43
|
+
end
|
44
|
+
|
45
|
+
def completed?
|
46
|
+
completed_at.present? || canceled?
|
47
|
+
end
|
48
|
+
|
49
|
+
def canceled?
|
50
|
+
canceled_at.present?
|
51
|
+
end
|
52
|
+
|
53
|
+
def complete!
|
54
|
+
update_attributes(completed_at: Time.current)
|
55
|
+
end
|
56
|
+
|
57
|
+
def send!
|
58
|
+
update_attributes(sent_at: Time.current)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Search
|
3
|
+
class Admin
|
4
|
+
class Review < Search::Admin
|
5
|
+
def type
|
6
|
+
'review'
|
7
|
+
end
|
8
|
+
|
9
|
+
def status
|
10
|
+
'active'
|
11
|
+
end
|
12
|
+
|
13
|
+
def facets
|
14
|
+
super.merge(
|
15
|
+
state: state,
|
16
|
+
rating: model.rating,
|
17
|
+
verification: verification
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def jump_to_text
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def name
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def jump_to_position
|
30
|
+
6
|
31
|
+
end
|
32
|
+
|
33
|
+
def search_text
|
34
|
+
[model.product_id, model.user_id]
|
35
|
+
end
|
36
|
+
|
37
|
+
def review_text
|
38
|
+
[model.title, model.body, product&.name].compact
|
39
|
+
end
|
40
|
+
|
41
|
+
def state
|
42
|
+
if model.approved?
|
43
|
+
I18n.t('workarea.admin.reviews.approved')
|
44
|
+
else
|
45
|
+
I18n.t('workarea.admin.reviews.pending')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def verification
|
50
|
+
if model.verified?
|
51
|
+
I18n.t('workarea.admin.reviews.verified')
|
52
|
+
else
|
53
|
+
I18n.t('workarea.admin.reviews.unverified')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def as_document
|
58
|
+
super.merge(
|
59
|
+
review_text: review_text,
|
60
|
+
rating: model.rating
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def product
|
67
|
+
@product ||= Catalog::Product.find(model.product_id) rescue nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Workarea
|
2
|
+
decorate Sort, with: :reviews do
|
3
|
+
class_methods do
|
4
|
+
def top_rated
|
5
|
+
new(I18n.t('workarea.sorts.top_rated'), :top_rated, :'sorts.rating', :desc)
|
6
|
+
end
|
7
|
+
|
8
|
+
def highest_rating
|
9
|
+
new(I18n.t('workarea.sorts.highest_rating'), :highest_rating, :rating, :desc)
|
10
|
+
end
|
11
|
+
|
12
|
+
def lowest_rating
|
13
|
+
new(I18n.t('workarea.sorts.lowest_rating'), :lowest_rating, :rating, :asc)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|