workarea-reviews 3.1.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.
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