workarea-reviews 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.eslintignore +2 -0
  4. data/.eslintrc +24 -0
  5. data/.eslintrc.json +35 -0
  6. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  7. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  8. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  9. data/.github/workflows/ci.yml +60 -0
  10. data/.gitignore +15 -0
  11. data/.rubocop.yml +3 -0
  12. data/.scss-lint.yml +188 -0
  13. data/.stylelintrc.json +8 -0
  14. data/.yardopts +1 -0
  15. data/CHANGELOG.md +745 -0
  16. data/CODE_OF_CONDUCT.md +3 -0
  17. data/CONTRIBUTING.md +3 -0
  18. data/Gemfile +8 -0
  19. data/LICENSE +52 -0
  20. data/README.md +107 -0
  21. data/Rakefile +53 -0
  22. data/app/assets/images/workarea/admin/icons/star.svg +1 -0
  23. data/app/assets/images/workarea/storefront/icons/empty_star.svg +1 -0
  24. data/app/assets/images/workarea/storefront/icons/half_star.svg +1 -0
  25. data/app/assets/images/workarea/storefront/icons/star.svg +1 -0
  26. data/app/assets/javascripts/workarea/storefront/reviews/modules/product_review_ajax_submit.js +39 -0
  27. data/app/assets/javascripts/workarea/storefront/reviews/modules/product_reviews_sort_menus.js +82 -0
  28. data/app/assets/javascripts/workarea/storefront/reviews/modules/rating_buttons.js +103 -0
  29. data/app/assets/javascripts/workarea/storefront/reviews/templates/sort_by_property.jst.ejs +11 -0
  30. data/app/assets/stylesheets/workarea/storefront/reviews/components/_rating.scss +28 -0
  31. data/app/assets/stylesheets/workarea/storefront/reviews/components/_reviews.scss +59 -0
  32. data/app/assets/stylesheets/workarea/storefront/reviews/components/_reviews_aggregate.scss +38 -0
  33. data/app/assets/stylesheets/workarea/storefront/reviews/components/_write_review.scss +63 -0
  34. data/app/controllers/workarea/admin/catalog_products_controller.decorator +9 -0
  35. data/app/controllers/workarea/admin/reports_controller.decorator +10 -0
  36. data/app/controllers/workarea/admin/reviews_controller.rb +51 -0
  37. data/app/controllers/workarea/storefront/review_requests_controller.rb +50 -0
  38. data/app/controllers/workarea/storefront/reviews_controller.rb +53 -0
  39. data/app/helpers/workarea/admin/reviews_helper.rb +13 -0
  40. data/app/helpers/workarea/storefront/reviews_helper.rb +22 -0
  41. data/app/helpers/workarea/storefront/reviews_schema_org_helper.rb +34 -0
  42. data/app/mailers/workarea/admin/status_report_mailer.decorator +8 -0
  43. data/app/mailers/workarea/storefront/review_mailer.rb +23 -0
  44. data/app/models/workarea/catalog/product.decorator +15 -0
  45. data/app/models/workarea/fulfillment.decorator +11 -0
  46. data/app/models/workarea/insights/most_active_reviewers.rb +34 -0
  47. data/app/models/workarea/insights/most_reviewed_products.rb +33 -0
  48. data/app/models/workarea/insights/top_rated_products.rb +33 -0
  49. data/app/models/workarea/review.rb +124 -0
  50. data/app/models/workarea/review/request.rb +62 -0
  51. data/app/models/workarea/search/admin/review.rb +72 -0
  52. data/app/models/workarea/search/storefront/product.decorator +9 -0
  53. data/app/models/workarea/sort.decorator +17 -0
  54. data/app/queries/workarea/reports/reviews_by_product.rb +86 -0
  55. data/app/queries/workarea/reports/reviews_by_user.rb +71 -0
  56. data/app/queries/workarea/review_request_params.rb +47 -0
  57. data/app/queries/workarea/review_summary.rb +11 -0
  58. data/app/queries/workarea/search/admin_reviews.rb +40 -0
  59. data/app/queries/workarea/search/category_browse.decorator +9 -0
  60. data/app/queries/workarea/search/product_search.decorator +9 -0
  61. data/app/seeds/workarea/review_seeds.rb +49 -0
  62. data/app/services/workarea/create_review.rb +99 -0
  63. data/app/view_models/workarea/admin/dashboards/reports_view_model.decorator +11 -0
  64. data/app/view_models/workarea/admin/product_view_model.decorator +7 -0
  65. data/app/view_models/workarea/admin/reports/reviews_by_product_view_model.rb +27 -0
  66. data/app/view_models/workarea/admin/review_view_model.rb +23 -0
  67. data/app/view_models/workarea/admin/reviews_search_view_model.rb +46 -0
  68. data/app/view_models/workarea/storefront/product_view_model.decorator +13 -0
  69. data/app/view_models/workarea/storefront/review_view_model.rb +14 -0
  70. data/app/views/workarea/admin/activities/_review_create.html.haml +12 -0
  71. data/app/views/workarea/admin/activities/_review_destroy.html.haml +10 -0
  72. data/app/views/workarea/admin/activities/_review_update.html.haml +10 -0
  73. data/app/views/workarea/admin/dashboards/_reviews_by_product_card.html.haml +17 -0
  74. data/app/views/workarea/admin/insights/_most_active_reviewers.html.haml +22 -0
  75. data/app/views/workarea/admin/insights/_most_reviewed_products.html.haml +22 -0
  76. data/app/views/workarea/admin/insights/_top_rated_products.html.haml +22 -0
  77. data/app/views/workarea/admin/reports/reviews_by_product.html.haml +44 -0
  78. data/app/views/workarea/admin/reviews/_aux_navigation.html.haml +5 -0
  79. data/app/views/workarea/admin/reviews/_catalog_products_aux_link.html.haml +5 -0
  80. data/app/views/workarea/admin/reviews/_dashboard_navigation.html.haml +1 -0
  81. data/app/views/workarea/admin/reviews/_menu.html.haml +3 -0
  82. data/app/views/workarea/admin/reviews/_summary.html.haml +12 -0
  83. data/app/views/workarea/admin/reviews/edit.html.haml +77 -0
  84. data/app/views/workarea/admin/reviews/index.html.haml +74 -0
  85. data/app/views/workarea/admin/status_report_mailer/_reviews.html.haml +5 -0
  86. data/app/views/workarea/storefront/products/_rating.html.haml +9 -0
  87. data/app/views/workarea/storefront/products/_reviews.html.haml +23 -0
  88. data/app/views/workarea/storefront/products/_reviews_aggregate.html.haml +22 -0
  89. data/app/views/workarea/storefront/products/_reviews_summary.html.haml +2 -0
  90. data/app/views/workarea/storefront/review_mailer/review_request.html.haml +24 -0
  91. data/app/views/workarea/storefront/review_requests/show.html.haml +45 -0
  92. data/app/views/workarea/storefront/reviews/new.html.haml +68 -0
  93. data/app/views/workarea/storefront/style_guides/_reviews_product_summary_docs.html.haml +13 -0
  94. data/app/views/workarea/storefront/style_guides/components/_reviews.html.haml +17 -0
  95. data/app/workers/workarea/create_review_requests.rb +33 -0
  96. data/app/workers/workarea/schedule_review_requests.rb +44 -0
  97. data/app/workers/workarea/send_review_requests.rb +12 -0
  98. data/app/workers/workarea/update_product_review_data.rb +26 -0
  99. data/bin/rails +18 -0
  100. data/config/initializers/append_points.rb +75 -0
  101. data/config/initializers/assets.rb +1 -0
  102. data/config/initializers/configuration.rb +31 -0
  103. data/config/initializers/rack_attack.rb +11 -0
  104. data/config/initializers/recaptcha.rb +19 -0
  105. data/config/initializers/scheduled_jobs.rb +8 -0
  106. data/config/locales/en.yml +150 -0
  107. data/config/routes.rb +25 -0
  108. data/lib/tasks/verify.rake +16 -0
  109. data/lib/workarea/mailer_previews/storefront/review_mailer_preview.rb +10 -0
  110. data/lib/workarea/reviews.rb +13 -0
  111. data/lib/workarea/reviews/engine.rb +16 -0
  112. data/lib/workarea/reviews/version.rb +5 -0
  113. data/package.json +9 -0
  114. data/test/dummy/Rakefile +6 -0
  115. data/test/dummy/app/assets/config/manifest.js +4 -0
  116. data/test/dummy/app/assets/images/.keep +0 -0
  117. data/test/dummy/app/assets/javascripts/application.js +13 -0
  118. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  119. data/test/dummy/app/controllers/application_controller.rb +3 -0
  120. data/test/dummy/app/controllers/concerns/.keep +0 -0
  121. data/test/dummy/app/helpers/application_helper.rb +2 -0
  122. data/test/dummy/app/jobs/application_job.rb +2 -0
  123. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  124. data/test/dummy/app/models/concerns/.keep +0 -0
  125. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  126. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  127. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  128. data/test/dummy/bin/bundle +3 -0
  129. data/test/dummy/bin/rails +4 -0
  130. data/test/dummy/bin/rake +4 -0
  131. data/test/dummy/bin/setup +34 -0
  132. data/test/dummy/bin/update +29 -0
  133. data/test/dummy/config.ru +5 -0
  134. data/test/dummy/config/application.rb +25 -0
  135. data/test/dummy/config/boot.rb +5 -0
  136. data/test/dummy/config/cable.yml +9 -0
  137. data/test/dummy/config/environment.rb +5 -0
  138. data/test/dummy/config/environments/development.rb +55 -0
  139. data/test/dummy/config/environments/production.rb +86 -0
  140. data/test/dummy/config/environments/test.rb +43 -0
  141. data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
  142. data/test/dummy/config/initializers/assets.rb +11 -0
  143. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  144. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  145. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  146. data/test/dummy/config/initializers/inflections.rb +16 -0
  147. data/test/dummy/config/initializers/mime_types.rb +4 -0
  148. data/test/dummy/config/initializers/new_framework_defaults.rb +21 -0
  149. data/test/dummy/config/initializers/session_store.rb +5 -0
  150. data/test/dummy/config/initializers/workarea.rb +5 -0
  151. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  152. data/test/dummy/config/locales/en.yml +23 -0
  153. data/test/dummy/config/puma.rb +47 -0
  154. data/test/dummy/config/routes.rb +5 -0
  155. data/test/dummy/config/secrets.yml +22 -0
  156. data/test/dummy/config/spring.rb +6 -0
  157. data/test/dummy/db/seeds.rb +2 -0
  158. data/test/dummy/lib/assets/.keep +0 -0
  159. data/test/dummy/log/.keep +0 -0
  160. data/test/factories/reviews.rb +32 -0
  161. data/test/fixtures/reviews.csv +5 -0
  162. data/test/helpers/workarea/storefront/reviews_helper_test.rb +20 -0
  163. data/test/integration/workarea/admin/reviews_integration_test.rb +37 -0
  164. data/test/integration/workarea/storefront/review_requests_integration_test.rb +63 -0
  165. data/test/integration/workarea/storefront/reviews_integration_test.rb +82 -0
  166. data/test/javascripts/fixtures/product_review_ajax_submit.html.haml +4 -0
  167. data/test/javascripts/product_review_ajax_submit_spec.js +28 -0
  168. data/test/javascripts/spec_helper.js +23 -0
  169. data/test/models/workarea/insights/most_active_reviewers_test.rb +32 -0
  170. data/test/models/workarea/insights/most_reviewed_products_test.rb +32 -0
  171. data/test/models/workarea/insights/top_rated_products_test.rb +49 -0
  172. data/test/models/workarea/review/request_test.rb +18 -0
  173. data/test/models/workarea/review_test.rb +105 -0
  174. data/test/models/workarea/search/storefront/product_test.decorator +14 -0
  175. data/test/queries/workarea/reports/reviews_by_product_test.rb +104 -0
  176. data/test/queries/workarea/reports/reviews_by_user_test.rb +102 -0
  177. data/test/queries/workarea/review_request_params_test.rb +64 -0
  178. data/test/services/workarea/create_review_test.rb +131 -0
  179. data/test/system/workarea/admin/review_system_test.rb +96 -0
  180. data/test/system/workarea/admin/reviews_by_product_system_test.rb +60 -0
  181. data/test/system/workarea/storefront/review_request_system_test.rb +34 -0
  182. data/test/system/workarea/storefront/review_system_test.rb +113 -0
  183. data/test/teaspoon_env.rb +10 -0
  184. data/test/test_helper.rb +11 -0
  185. data/test/workers/workarea/create_review_requests_test.rb +38 -0
  186. data/test/workers/workarea/schedule_review_requests_test.rb +115 -0
  187. data/test/workers/workarea/send_review_requests_test.rb +30 -0
  188. data/workarea-reviews.gemspec +22 -0
  189. data/yarn.lock +3265 -0
  190. metadata +265 -0
@@ -0,0 +1,86 @@
1
+ module Workarea
2
+ module Reports
3
+ class ReviewsByProduct
4
+ include Report
5
+
6
+ self.reporting_class = Review
7
+ self.sort_fields = %w(reviews verified average_rating weighted_average_rating)
8
+
9
+ def aggregation
10
+ [filter_results, project_used_fields, group_by_product, project_averages]
11
+ end
12
+
13
+ def filter_results
14
+ {
15
+ '$match' => {
16
+ 'created_at' => { '$gte' => starts_at, '$lte' => ends_at },
17
+ **approval_query
18
+ }
19
+ }
20
+ end
21
+
22
+ def project_used_fields
23
+ {
24
+ '$project' => {
25
+ 'product_id' => 1,
26
+ 'rating' => 1,
27
+ 'verified' => 1
28
+ }
29
+ }
30
+ end
31
+
32
+ def group_by_product
33
+ {
34
+ '$group' => {
35
+ '_id' => '$product_id',
36
+ 'reviews' => { '$sum' => 1 },
37
+ 'verified' => { '$sum' => { '$cond' => { 'if' => '$verified', 'then' => 1, 'else' => 0 } } },
38
+ 'rating_tally' => { '$sum' => '$rating' },
39
+ 'rated_5' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 5] }, 'then' => 1, 'else' => 0 } } },
40
+ 'rated_4' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 4] }, 'then' => 1, 'else' => 0 } } },
41
+ 'rated_3' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 3] }, 'then' => 1, 'else' => 0 } } },
42
+ 'rated_2' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 2] }, 'then' => 1, 'else' => 0 } } },
43
+ 'rated_1' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 1] }, 'then' => 1, 'else' => 0 } } }
44
+ }
45
+ }
46
+ end
47
+
48
+ def project_averages
49
+ {
50
+ '$project' => {
51
+ 'product_id' => 1,
52
+ 'reviews' => 1,
53
+ 'verified' => 1,
54
+ 'average_rating' => { '$divide' => ['$rating_tally', '$reviews'] },
55
+ 'weighted_average_rating' => {
56
+ '$divide' => [
57
+ {
58
+ '$sum' => [
59
+ { '$multiply' => [{ '$sum' => [2, '$rated_5'] }, 5] },
60
+ { '$multiply' => [{ '$sum' => [2, '$rated_4'] }, 4] },
61
+ { '$multiply' => [{ '$sum' => [2, '$rated_3'] }, 3] },
62
+ { '$multiply' => [{ '$sum' => [2, '$rated_2'] }, 2] },
63
+ { '$multiply' => [{ '$sum' => [2, '$rated_1'] }, 1] }
64
+ ]
65
+ },
66
+ { '$sum' => [10, '$rated_5', '$rated_4', '$rated_3', '$rated_2', '$rated_1'] }
67
+ ]
68
+ }
69
+ }
70
+ }
71
+ end
72
+
73
+ private
74
+
75
+ def approval_query
76
+ if params[:results_filter] == 'approved'
77
+ { approved: true }
78
+ elsif params[:results_filter] == 'unapproved'
79
+ { approved: false }
80
+ else
81
+ {}
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,71 @@
1
+ module Workarea
2
+ module Reports
3
+ class ReviewsByUser
4
+ include Report
5
+
6
+ self.reporting_class = Review
7
+ self.sort_fields = %w(reviews verified average_rating activity_score)
8
+
9
+ def aggregation
10
+ [filter_results, project_used_fields, group_by_email, project_fields]
11
+ end
12
+
13
+ def filter_results
14
+ {
15
+ '$match' => {
16
+ 'created_at' => { '$gte' => starts_at, '$lte' => ends_at },
17
+ **approval_query
18
+ }
19
+ }
20
+ end
21
+
22
+ def project_used_fields
23
+ {
24
+ '$project' => {
25
+ 'email' => 1,
26
+ 'user_id' => 1,
27
+ 'rating' => 1,
28
+ 'verified' => 1
29
+ }
30
+ }
31
+ end
32
+
33
+ def group_by_email
34
+ {
35
+ '$group' => {
36
+ '_id' => '$email',
37
+ 'user_id' => { '$max' => '$user_id' },
38
+ 'reviews' => { '$sum' => 1 },
39
+ 'verified' => { '$sum' => { '$cond' => { 'if' => '$verified', 'then' => 1, 'else' => 0 } } },
40
+ 'rating_tally' => { '$sum' => '$rating' }
41
+ }
42
+ }
43
+ end
44
+
45
+ def project_fields
46
+ {
47
+ '$project' => {
48
+ 'email' => 1,
49
+ 'user_id' => 1,
50
+ 'reviews' => 1,
51
+ 'verified' => 1,
52
+ 'average_rating' => { '$divide' => ['$rating_tally', '$reviews'] },
53
+ 'activity_score' => { '$sum' => ['$reviews', { '$multiply' => ['$verified', 0.75] }] }
54
+ }
55
+ }
56
+ end
57
+
58
+ private
59
+
60
+ def approval_query
61
+ if params[:results_filter] == 'approved'
62
+ { approved: true }
63
+ elsif params[:results_filter] == 'unapproved'
64
+ { approved: false }
65
+ else
66
+ {}
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,47 @@
1
+ module Workarea
2
+ class ReviewRequestParams
3
+ attr_reader :request, :params
4
+
5
+ def initialize(request, params)
6
+ @request = request
7
+ @params = params
8
+ end
9
+
10
+ def to_h
11
+ params.merge(
12
+ product_id: @request.product_id,
13
+ user_id: @request.user_id,
14
+ email: @request.email,
15
+ user_info: user_info,
16
+ first_name: first_name,
17
+ last_name: last_name,
18
+ verified: true
19
+ )
20
+ end
21
+
22
+ private
23
+
24
+ def user_info
25
+ return user.public_info if user&.public_info.present?
26
+
27
+ [first_name.first, last_name.first].join
28
+ end
29
+
30
+ def first_name
31
+ user&.first_name || billing_address.first_name
32
+ end
33
+
34
+ def last_name
35
+ user&.last_name || billing_address.last_name
36
+ end
37
+
38
+ def user
39
+ return nil unless @request.user_id.present?
40
+ @user ||= User.find(@request.user_id)
41
+ end
42
+
43
+ def billing_address
44
+ @billing_address ||= Payment.find(@request.order_id).address
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ module Workarea
2
+ class ReviewSummary
3
+ def pending_reviews_count
4
+ @pending_reviews_count ||= Review.pending.count
5
+ end
6
+
7
+ def recent_reviews_count
8
+ @recent_reviews_count ||= Review.where(:created_at.gte => 1.day.ago).count
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ module Workarea
2
+ module Search
3
+ class AdminReviews
4
+ include Query
5
+ include AdminIndexSearch
6
+ include AdminSorting
7
+ include Pagination
8
+
9
+ document Search::Admin
10
+
11
+ def initialize(params = {})
12
+ super(params.merge(type: 'review'))
13
+ end
14
+
15
+ def fields
16
+ super + %w(review_text)
17
+ end
18
+
19
+ def facets
20
+ super + [
21
+ TermsFacet.new(self, 'state'),
22
+ TermsFacet.new(self, 'rating'),
23
+ TermsFacet.new(self, 'verification')
24
+ ]
25
+ end
26
+
27
+ def sort
28
+ result = super || []
29
+
30
+ if params[:sort] == Sort.highest_rating.to_s
31
+ result.prepend(Sort.highest_rating.field => Sort.highest_rating.direction)
32
+ elsif params[:sort] == Sort.lowest_rating.to_s
33
+ result.prepend(Sort.lowest_rating.field => Sort.lowest_rating.direction)
34
+ end
35
+
36
+ result
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,9 @@
1
+ module Workarea
2
+ decorate Search::CategoryBrowse, with: :reviews do
3
+ class_methods do
4
+ def available_sorts
5
+ super.tap { |sorts| sorts << Sort.top_rated }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Workarea
2
+ decorate Search::ProductSearch, with: :reviews do
3
+ class_methods do
4
+ def available_sorts
5
+ super.tap { |sorts| sorts << Sort.top_rated }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,49 @@
1
+ module Workarea
2
+ class ReviewSeeds
3
+ def perform
4
+ puts 'Adding reviews...'
5
+
6
+ Sidekiq::Callbacks.disable do
7
+ create_reviews_for_catalog
8
+ create_review_request_email_content
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def create_reviews_for_catalog
15
+ Workarea::Catalog::Product.all.each_by(100) do |product|
16
+ reviews = Array.new(rand(10)) { create_review(product) }
17
+ next unless reviews.size > 0
18
+
19
+ Workarea::Review.collection.insert_many(reviews.map(&:as_document))
20
+ UpdateProductReviewData.perform_async(product.id)
21
+ end
22
+ end
23
+
24
+ def create_review(product)
25
+ Workarea::Review.new(
26
+ product_id: product.id,
27
+ user_id: BSON::ObjectId.new,
28
+ rating: rand(5) + 1,
29
+ title: Faker::Lorem.sentence,
30
+ body: Faker::Lorem.paragraph,
31
+ approved: [true, false].sample,
32
+ user_info: Faker::Internet.user_name,
33
+ verified: [true, false].sample,
34
+ created_at: rand(45).days.ago,
35
+ updated_at: Time.current
36
+ )
37
+ end
38
+
39
+ def create_review_request_email_content
40
+ Content::Email.create!(
41
+ type: 'review_request',
42
+ content: <<~HTML
43
+ <h1>Let us know what you think!</h1>
44
+ <p>Your feedback is important to us.</p>
45
+ HTML
46
+ )
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,99 @@
1
+ module Workarea
2
+ class CreateReview
3
+ include ActiveModel::Validations
4
+
5
+ validate :valid_review
6
+ validates :first_name, :last_name, presence: true
7
+
8
+ attr_reader :review, :user, :params
9
+
10
+ def self.for(product: nil, user: nil, params: {})
11
+ review = Review.new(
12
+ product_id: product.try(:id),
13
+ user_id: user.try(:id)
14
+ )
15
+
16
+ new(review, user: user, params: params)
17
+ end
18
+
19
+ def initialize(review, user: nil, params: {})
20
+ @review = review
21
+ @user = user
22
+ @params = params
23
+ end
24
+
25
+ def save
26
+ update_user_attributes
27
+ update_attributes
28
+
29
+ return false unless valid?
30
+ review.save.tap { |result| update_review_requests if result }
31
+ end
32
+
33
+ def verified?
34
+ user_order_ids_with_product.any?
35
+ end
36
+
37
+ private
38
+
39
+ def user_order_ids_with_product
40
+ return [] unless user.present?
41
+
42
+ @user_users_with_product ||=
43
+ Order.placed
44
+ .where(user_id: user.id.to_s)
45
+ .where('items.product_id' => review.product_id)
46
+ .pluck(:id)
47
+ end
48
+
49
+ def update_user_attributes
50
+ return if !user.present? || user.public_info.present?
51
+ user.update_attributes(params.slice(:first_name, :last_name))
52
+ end
53
+
54
+ def update_attributes
55
+ review.assign_attributes(
56
+ params
57
+ .slice(:rating, :body, :title)
58
+ .merge(
59
+ user_info: user_info,
60
+ first_name: first_name,
61
+ last_name: last_name,
62
+ email: email,
63
+ verified: verified?
64
+ )
65
+ )
66
+ end
67
+
68
+ def update_review_requests
69
+ return unless user_order_ids_with_product.any?
70
+ Review::Request.cancel_for_orders!(user_order_ids_with_product)
71
+ end
72
+
73
+ def user_info
74
+ return user.public_info if user&.public_info.present?
75
+
76
+ "#{first_name&.first} #{last_name&.first}".strip
77
+ end
78
+
79
+ def first_name
80
+ params[:first_name].presence || user&.first_name
81
+ end
82
+
83
+ def last_name
84
+ params[:last_name].presence || user&.last_name
85
+ end
86
+
87
+ def email
88
+ params[:email].presence || user&.email
89
+ end
90
+
91
+ def valid_review
92
+ unless review.valid?
93
+ review.errors.each do |attr, error|
94
+ errors.add(attr, error)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,11 @@
1
+ module Workarea
2
+ decorate Admin::Dashboards::ReportsViewModel, with: :reviews do
3
+ def reviews_by_product
4
+ @reviews_by_product ||=
5
+ Admin::Reports::ReviewsByProductViewModel.wrap(
6
+ Workarea::Reports::ReviewsByProduct.new(options),
7
+ options
8
+ )
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ decorate Admin::ProductViewModel, with: :reviews do
3
+ def pending_reviews_count
4
+ Review.pending.where(product_id: model.id).count
5
+ end
6
+ end
7
+ end